• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Hundred Slash 1.0.6

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: deepstrasz
I felt like doing a Blademaster ability so uh...here. Oh, and to the mods and mini-mods, can you point out specifically what you think is wrong and show me how to fix them in this fashion?

My code:
My code blah blah

(Mini) Mod code:
Lol, no. Don't do it like that you noob. Do it like this.

It'll save us both a lot of time as you won't have to keep on reviewing every time I update the map.


A skill only those chosen by the blade can use. The Hero splits into 5 images around the target and proceeds to mercilessly cut them down with blinding speed.

Level 1 - Deals 350 damage.
Level 2 - Deals 500 damage.
Level 3 - Deals 650 damage.



JASS:
scope HundredSlash initializer init
    globals
        private hashtable HundredSlash = InitHashtable()
        private constant integer ABILITY  = 'A000' //Ability ID for the ability.
        private constant integer DUMMY = 'e000' //The Unit ID of the dummy
        private constant string EFFECT_A = "BlackBlink.mdx" //The SFX used when the dummies appear.
        private constant string EFFECT_B = "AncientExplode.mdx" //The SFX used at the end.
        private constant string EFFECT_C = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" //SFX used when the target is being slashed.
        private constant string ATTACHMENT = "chest" //Where EFFECT_C shows up
        private constant real DAMAGE_BASE = 200 //Base damage of the spell
        private constant real DAMAGE_BONUS = 150 //Damage increment per level of the ability.
        private constant attacktype ATTACKTYPE = ATTACK_TYPE_NORMAL //Attack type
        private constant damagetype DAMAGETYPE =  DAMAGE_TYPE_ENHANCED //Damage type
        private constant weapontype WEAPONTYPE = null //The sound that plays during the damage step.
        private constant group HUNDREDGROUP = CreateGroup()
    endglobals

    private function Damage takes integer i returns real
        return DAMAGE_BASE + (I2R(i - 1) * DAMAGE_BONUS)
    endfunction

    //--------------------START OF SPELL-----------------------\\
    private function Callback takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        local unit u = GetEnumUnit()
        local unit uu = LoadUnitHandle(HundredSlash, id, 1)
        local integer count = LoadInteger(HundredSlash, id, 2)
       
        if count <= 40 then
            call SetUnitAnimation(u, "attack")
            call DestroyEffect(AddSpecialEffectTarget(EFFECT_C, uu, ATTACHMENT))
        else
            call DestroyEffect(AddSpecialEffect(EFFECT_A, GetUnitX(u), GetUnitY(u)))
        endif
       
        set t = null
        set u = null
        set uu = null
    endfunction

    private function Loop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        local unit u = LoadUnitHandle(HundredSlash, id, 0)
        local unit uu = LoadUnitHandle(HundredSlash, id, 1)
        local integer count = LoadInteger(HundredSlash, id, 2)
       
        call ForGroup(HUNDREDGROUP, function Callback)
        call SetUnitPropWindow(uu, 0)
       
        if count > 40 then
            call DestroyEffect(AddSpecialEffect(EFFECT_A, GetUnitX(u), GetUnitY(u)))
            call ShowUnit(u, true)
            if GetLocalPlayer() == GetOwningPlayer(u) then
                call SelectUnit(u, true)
            endif
            call SetUnitInvulnerable(u, false)
            call SetUnitInvulnerable(uu, false)
            call DestroyEffect(AddSpecialEffect(EFFECT_B, GetUnitX(uu), GetUnitY(uu)))
            call UnitDamageTarget(u, uu, Damage(GetUnitAbilityLevel(u, ABILITY)), false, false, ATTACKTYPE, DAMAGETYPE, WEAPONTYPE)
            call PauseTimer(t)
            call DestroyTimer(t)
            call FlushChildHashtable(HundredSlash, id)
        endif
       
        call SaveInteger(HundredSlash, id, 2, count + 1)
       
        set t = null
        set u = null
        set uu = null
    endfunction

    private function Actions takes nothing returns nothing
        local timer t = CreateTimer()
        local integer id = GetHandleId(t)
        local unit u = GetTriggerUnit()
        local unit uu = GetSpellTargetUnit()
        local real x = GetUnitX(uu)
        local real y = GetUnitY(uu)
        local integer i = 0
        local real angle = 1.257
        local real xx
        local real yy
        local unit dummy
       
        call DestroyEffect(AddSpecialEffect(EFFECT_A, GetUnitX(u), GetUnitY(u)))
        call SetUnitPropWindow(uu, 0)
        call SetUnitInvulnerable(uu, true)
        call SetUnitInvulnerable(u, true)
        call ShowUnit(u, false)
        loop
            exitwhen i == 5
            set i = i + 1
            set xx = x + 150 * Cos(angle)
            set yy = y + 150 * Sin(angle)
            call DestroyEffect(AddSpecialEffect(EFFECT_A, xx, yy))
            set dummy = CreateUnit(GetOwningPlayer(u), DUMMY, xx, yy, bj_RADTODEG * Atan2(y - yy, x - xx))
            call SetUnitTimeScale(dummy, 5.0)
            call GroupAddUnit(HUNDREDGROUP, dummy)
            set angle = angle + 1.257
            set dummy = null
        endloop
       
        call SaveUnitHandle(HundredSlash, id, 0, u)
        call SaveUnitHandle(HundredSlash, id, 1, uu)
        call SaveInteger(HundredSlash, id, 2, 0)
       
        call TimerStart(t, 0.10, true, function Loop)
       
        set t = null
        set u = null
        set uu = null
    endfunction

    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABILITY then
            call Actions()
        endif
        return false
    endfunction

    //===========================================================================
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, Condition( function Conditions ) )
        set t = null
    endfunction
endscope

Keywords:
Blademaster, Slash, Sword, Green, JASS, MUI, Mirror Image, Damage
Contents

Hundred Slash (Map)

Reviews
12th Dec 2015 IcemanBo: Too long as NeedsFix. Rejected. 01:32, 17th Jan 2014 BPower: Reset the target proper window once the spell is over. Casting the spell twice simultaniously will quadruple played the specialeffects and attack animations...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long as NeedsFix. Rejected.

01:32, 17th Jan 2014
BPower:
  • Reset the target proper window once the spell is over.
  • Casting the spell twice simultaniously will quadruple played the specialeffects and attack animations.
  • The offset for the image creation could be configurable.
  • The maximum count of performed hits could be configurable instead of just 40.
  • The dummy scaling could be configurable
  • private function Damage takes integer i returns real ---> instead of i type level. This is part of the user configuration and should be more distinct
  • It would more realistic if the damage is dealt small periods
 
Level 3
Joined
Jun 3, 2013
Messages
34
No name for this spell yet?.. " 1.0.0 "
[Edit] Oh I see, its "Hundred Slash", my bad...
 
Level 3
Joined
Sep 9, 2009
Messages
658
I suggest using only one timers instead of plenty timers.
I suggest using FoG loop instead of ForGroup :)
I also suggest you to inline the actions into the condition func :)

Anyway I'll rate it later.
I just reviewed the code fastly xD

But...I do use one timer.

FoG requires me to remove the units from the group and reselct them at a later re-iteration. Is FoG still better despite that or is there some method to do this with FoG that I don't know about?

You mean like this?

JASS:
function Sample takes nothing returns boolean
    local unit u = GetTriggerUnit()
    if GetSpellAbilityId() == 'A008' then
        call BJDebugMsg(I2S(GetHandleId(u)))
        return false
        set u = null
    endif
    return false
endfunction

//===========================================================================
function InitTrig_Sample takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Sample ) )
    set t = null
endfunction
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
You're creating one timer for each instance :)
Try to use one timer for everything.

Forget what I told with FoG it is not really needed infact. Was just messing around ^^
I'll look more the code and tell again if it could be better or not.
Infact picking units isn't costly and FoG is faster but it is fine like this.

For the moment switch to vJASS or pure JASS with udgs.
 
Level 3
Joined
Sep 9, 2009
Messages
658
It's because of the very simple that I don't know how to do vJASS. I don't know any links to tutorials about it either. Plus, my only concern was MUI-ness rather than efficiency.

I'd give vJASS a try if you know a link to a good tutorial though.

And you still haven't answered my first question btw. Do you mean per instance of spell cast?
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
Yes I meant per spellcast. Each time you cast the spell there is a new timer.

JASS:
scope HundredSlash initializer init
    globals
        private hashtable HundredSlash = InitHashtable()
        private constant integer ABILITY  = 'A000' //Ability ID for the ability.
        private constant integer DUMMY = 'e000' //The Unit ID of the dummy
        private constant string EFFECT_A = "BlackBlink.mdx" //The SFX used when the dummies appear.
        private constant string EFFECT_B = "AncientExplode.mdx" //The SFX used at the end.
        private constant string EFFECT_C = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" //SFX used when the target is being slashed.
        private constant string ATTACHMENT = "chest" //Where EFFECT_C shows up
        private constant real DAMAGE_BASE = 200 //Base damage of the spell
        private constant real DAMAGE_BONUS = 150 //Damage increment per level of the ability.
        private constant attacktype ATTACKTYPE = ATTACK_TYPE_NORMAL //Attack type
        private constant damagetype DAMAGETYPE =  DAMAGE_TYPE_ENHANCED //Damage type
        private constant weapontype WEAPONTYPE = null //The sound that plays during the damage step.
    endglobals

    private function Damage takes integer i returns real
        return DAMAGE_BASE + (I2R(i - 1) * DAMAGE_BONUS)
    endfunction

    //--------------------START OF SPELL-----------------------\\
    private function Callback takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        local unit u = GetEnumUnit()
        local unit uu = LoadUnitHandle(HundredSlash, id, 1)
        local integer count = LoadInteger(HundredSlash, id, 3)
        
        if count <= 40 then
            call SetUnitAnimation(u, "attack")
            call DestroyEffect(AddSpecialEffectTarget(EFFECT_C, uu, ATTACHMENT))
        else
            call DestroyEffect(AddSpecialEffect(EFFECT_A, GetUnitX(u), GetUnitY(u)))
            call RemoveUnit(u)
        endif
        
        set u = null
        set uu = null
    endfunction

    private function Loop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        local unit u = LoadUnitHandle(HundredSlash, id, 0)
        local unit uu = LoadUnitHandle(HundredSlash, id, 1)
        local group g = LoadGroupHandle(HundredSlash, id, 2)
        local integer count = LoadInteger(HundredSlash, id, 3)
        local unit uuu
        
        call ForGroup(g, function Callback)
        
        if count > 40 then
            call DestroyEffect(AddSpecialEffect(EFFECT_A, GetUnitX(u), GetUnitY(u)))
            call ShowUnit(u, true)
            if GetLocalPlayer() == GetOwningPlayer(u) then
                call SelectUnit(u, true)
            endif
            call SetUnitInvulnerable(u, false)
            call SetUnitInvulnerable(uu, false)
            call PauseUnit(uu, false)
            call DestroyEffect(AddSpecialEffect(EFFECT_B, GetUnitX(uu), GetUnitY(uu)))
            call UnitDamageTarget(u, uu, Damage(GetUnitAbilityLevel(u, ABILITY)), false, false, ATTACKTYPE, DAMAGETYPE, WEAPONTYPE)
            call PauseTimer(t)
            call DestroyTimer(t)
            call DestroyGroup(g)
            call FlushChildHashtable(HundredSlash, id)
        endif
        
        call SaveInteger(HundredSlash, id, 3, count + 1)
        
        set t = null
        set u = null
        set uu = null
        set g = null
    endfunction

    private function Actions takes nothing returns nothing
        local timer t = CreateTimer()
        local integer id = GetHandleId(t)
        local unit u = GetTriggerUnit()
        local unit uu = GetSpellTargetUnit()
        local real x = GetUnitX(uu)
        local real y = GetUnitY(uu)
        local group g = CreateGroup()
        local integer i = 0
        local real angle = 1.257
        local real xx
        local real yy
        local unit uuu
        
        call DestroyEffect(AddSpecialEffect(EFFECT_A, GetUnitX(u), GetUnitY(u)))
        call PauseUnit(uu, true)
        call SetUnitInvulnerable(uu, true)
        call SetUnitInvulnerable(u, true)
        call ShowUnit(u, false)
        loop
            exitwhen i == 5
            set i = i + 1
            set xx = x + 150 * Cos(angle)
            set yy = y + 150 * Sin(angle)
            call DestroyEffect(AddSpecialEffect(EFFECT_A, xx, yy))
            set uuu = CreateUnit(GetOwningPlayer(u), DUMMY, xx, yy, bj_RADTODEG * Atan2(y - yy, x - xx))
            call SetUnitTimeScale(uuu, 5.0)
            call GroupAddUnit(g, uuu)
            set angle = angle + 1.257
            set uuu = null
        endloop
        
        call SaveUnitHandle(HundredSlash, id, 0, u)
        call SaveUnitHandle(HundredSlash, id, 1, uu)
        call SaveGroupHandle(HundredSlash, id, 2, g)
        call SaveInteger(HundredSlash, id, 3, 0)
        
        call TimerStart(t, 0.10, true, function Loop)
        
        set t = null
        set u = null
        set uu = null
        set g = null
    endfunction

    private function Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABILITY then
            call Actions()
        endif
        return false
    endfunction

    //===========================================================================
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, Condition( function Conditions ) )
        set t = null
    endfunction
endscope
With this you'll be fine.
I didn't touch the code just wrapped it into a scope and privated everything which neeed this.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
This is vJass not jass as many others have said.

Library is better to use for systems as they can require other libraries making it easier for the user to manage. Other than that they are very similar.

You need to null timer t in first function.
You never use this in your second function. local unit uuu

Make a global group and use that for the system. Constantly creating and destroying groups is costly performance wise.

Never use pause unit in spells use setunitpropwindow to stop that unit from moving.
 
Level 3
Joined
Sep 9, 2009
Messages
658
Make a global group and use that for the system. Constantly creating and destroying groups is costly performance wise.

Never use pause unit in spells use setunitpropwindow to stop that unit from moving.

Enemy still moves even with setunitpropwindow. This is what I'm doing:
call SetUnitPropWindow(uu, GetUnitFacing(uu))

Putting them in a global makes the dummies get removed at the same time. So how do I do this with a global and the dummies still getting removed at different intervals?
 
Level 15
Joined
Dec 21, 2013
Messages
910
"What's VM?" ----> LOL

By the way, nice spell. Well I'm not too good with JASS so...
I can't review it, rofl.

OT:
@sniper_zero Your avatar,.. Is she the girl from Binbou Gami (anime, I forgot the name, it sounds a kind of that, about a lucky girl who can absorb luck)?
 
Top