• 🏆 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!

Shadow Step v1.3

  • Like
Reactions: WizardYu
A random spell that I made.


Details
- The spell is vJASS
- They should be leak-less and lag-less
- It is MUI, meaning can be cast many times at the same instance
- A shadow-themed blink strike


Implementation
JASS:
//==============================================================================
//                           Shadow Step v1.3
//                              By Ayanami
//==============================================================================
//==============================================================================
//                            REQUIREMENTS
//==============================================================================
//
// - JNGP
// - Alloc
// - SpellEffectEvent
// - Unit Indexer
//
//==============================================================================

//==============================================================================
//                           IMPLEMENTATION
//==============================================================================
//
// 1) Copy the whole "Required Systems" Trigger folder & paste in map
// 2) Save the map, it will take a bit longer than usual
// 3) After saving, close the map and re-open it
// 4) Delete or disable the trigger "Objects"
// 5) Caster Dummy will have been automatically created for you
// 6) Copy all 3 abilities under "Night Elf" & paste in map
// 7) Copy the trigger "Shadow Step"
// 8) Go through all the spell Configurations
//
//==============================================================================


Spell Codes



Melds into the shadows and instantly appears next to the target, leaving a trail of shadows. An illusion is left behind at the original location. For 5 seconds, the hero is able to swap places with the illusion once. Illusion deals 25% damage and takes 200% damage.

Level 1 - 40 damage.
Level 2 - 65 damage.
Level 3 - 90 damage.
Level 4 - 115 damage.

Cast Range: 800
Target Type: Unit
Cooldown: 6 seconds

JASS:
library ShadowStep uses SpellEffectEvent, UnitIndexer

//===========================================================================
//                           CONFIGURABLES                        
//===========================================================================

globals
    private constant integer ABILID = 'ABSS' // raw code of ability "Shadow Step"
    private constant integer DUMABILID1 = 'ASS1' // raw code of ability "Shadow Swap"
    private constant integer DUMABILID2 = 'ASS0' // raw code of ability "Shadow Step (Illusion)"
    private constant integer CASTID = 'cAST' // raw code of unit "Caster Dummy"
    
    private constant real OFFSET = 100. // offset distance from the target
    private constant real PERIOD = 0.1 // update interval for shadow fade and illusion timer
    private constant real SHADOW_DIST = 50. // distance interval where a shadow is created
    private constant real TRANS = 150. // 0 = completely transparent, maximum of 255
    private constant real FADE_DUR = 1. // time for shadows to fade
    private constant real FADE_RATE = TRANS * PERIOD / FADE_DUR // rate of fading
    
    private constant real TURN_RATE = 0.6 // caster's turn rate
    private constant string ANIM = "slam" // animation to be played by shadows
    private constant real ANIM_TIME = 0.5665 // animation time for ANIM
    private constant real ANIM_TIME_SCALE = ANIM_TIME / FADE_DUR // time scale for shadows
    
    private constant attacktype ATK = ATTACK_TYPE_HERO // attack type of damage
    private constant damagetype DMG = DAMAGE_TYPE_NORMAL // damage type of damage
endglobals

// damage dealt
private constant function GetDamage takes integer level returns real
    return 25. * level + 15.
endfunction

// duration of illusion
private constant function GetDuration takes integer level returns real
    return 5.
endfunction

//===========================================================================
//                          END CONFIGURABLES                        
//===========================================================================

globals
    private constant player NEUTRAL_PASSIVE = Player(PLAYER_NEUTRAL_PASSIVE)
endglobals

private struct Fade
    thistype next
    thistype prev
    unit u
    real fade = TRANS
    static timer linkTimer = CreateTimer()
    
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
        
        loop
            set this = this.next
            exitwhen this == 0 
            
            if this.fade <= 0 then
                set this.next.prev = this.prev
                set this.prev.next = this.next
                
                if thistype(0).next == thistype(0) then
                    call PauseTimer(linkTimer)
                endif
                
                call RemoveUnit(this.u)
                call this.deallocate()
            else
                set this.fade = this.fade - FADE_RATE
                call SetUnitVertexColor(this.u, 255, 255, 255, R2I(this.fade))
            endif
        endloop
    endmethod
    
    public static method create takes player p, integer typeid, real x, real y, real a returns thistype
        local thistype this = thistype.allocate()
        
        if thistype(0).next == thistype(0) then
            call TimerStart(linkTimer, PERIOD, true, function thistype.iterate)
        endif
        
        set thistype(0).next.prev = this
        set this.next = thistype(0).next
        set thistype(0).next = this
        set this.prev = thistype(0)
        
        set this.u = CreateUnit(NEUTRAL_PASSIVE, typeid, x, y, a * bj_RADTODEG)
        
        call SetUnitColor(this.u, GetPlayerColor(p))
        call UnitAddAbility(this.u, 'Aloc')
        call SetUnitTimeScale(this.u, ANIM_TIME_SCALE)
        call SetUnitAnimation(this.u, ANIM)
        call SetUnitX(this.u, x)
        call SetUnitY(this.u, y)
        
        return this
    endmethod
endstruct

private struct Data
    thistype next
    thistype prev
    unit u
    unit illu
    unit target
    boolean b = true
    real dur
    real a
    real x
    real y
    static thistype tempData
    static unit castDummy
    static integer array store
    static timer linkTimer = CreateTimer()
    
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
        
        loop
            set this = this.next
            exitwhen this == 0
            
            if this.dur <= 0 or IsUnitType(this.illu, UNIT_TYPE_DEAD) then
                set this.next.prev = this.prev
                set this.prev.next = this.next
                
                if thistype(0).next == thistype(0) then
                    call PauseTimer(linkTimer)
                endif
                
                if this.b then
                    call UnitRemoveAbility(this.u, DUMABILID1)
                    call SetPlayerAbilityAvailable(GetOwningPlayer(this.u), ABILID, true)
                endif
                
                call this.deallocate()
            else
                set this.dur = this.dur - PERIOD
            endif
        endloop
    endmethod
    
    private static method swap takes nothing returns boolean
        local thistype this = store[GetUnitId(GetTriggerUnit())]
        local real a = GetUnitFacing(this.u)
        local real x = GetUnitX(this.u)
        local real y = GetUnitY(this.u)
        local real b = GetUnitFacing(this.illu)
        local real dx = GetUnitX(this.illu)
        local real dy = GetUnitY(this.illu)
        
        set this.b = false
        call SetUnitPathing(this.u, false)
        call SetUnitTurnSpeed(this.u, 99999)
        call SetUnitTurnSpeed(this.illu, 99999)
        call SetUnitX(this.u, dx)
        call SetUnitY(this.u, dy)
        call SetUnitFacing(this.u, b)
        call IssueImmediateOrder(this.u, "stop")
        call SetUnitX(this.illu, x)
        call SetUnitY(this.illu, y)
        call SetUnitFacing(this.illu, a)
        call SetUnitTurnSpeed(this.u, TURN_RATE)
        call SetUnitTurnSpeed(this.illu, TURN_RATE)
        if IsUnitType(this.target, UNIT_TYPE_DEAD) then
            call IssueImmediateOrder(this.illu, "stop")
        else
            call IssueTargetOrder(this.illu, "attack", this.target)
        endif
        call SetUnitPathing(this.u, true)
        call UnitRemoveAbility(this.u, DUMABILID1)
        call SetPlayerAbilityAvailable(GetOwningPlayer(this.u), ABILID, true)
        return false
    endmethod
    
    private static method onSummon takes nothing returns boolean
        if thistype(0).next == thistype(0) then
            call TimerStart(linkTimer, PERIOD, true, function thistype.iterate)
        endif
        
        set thistype(0).next.prev = tempData
        set tempData.next = thistype(0).next
        set thistype(0).next = tempData
        set tempData.prev = thistype(0)
        
        set tempData.illu = GetSummonedUnit()
        call SetUnitX(tempData.illu, tempData.x)
        call SetUnitY(tempData.illu, tempData.y)
        call SetUnitFacing(tempData.illu, tempData.a * bj_RADTODEG)
        call UnitApplyTimedLife(tempData.illu, 'BTLF', tempData.dur)
    
        return false
    endmethod

    private static method onCast takes nothing returns boolean
        local thistype this = thistype.allocate()
        local player p
        local integer i
        local integer typeid
        local real dx
        local real dy
        local real dist
        local real damage
        
        set this.u = GetTriggerUnit()
        set this.target = GetSpellTargetUnit()
        set i = GetUnitAbilityLevel(this.u, ABILID)
        set this.dur = GetDuration(i)
        set damage = GetDamage(i)
        set store[GetUnitId(this.u)] = this
        set p = GetOwningPlayer(this.u)
        set typeid = GetUnitTypeId(this.u)
        set this.x = GetUnitX(this.u)
        set this.y = GetUnitY(this.u)
        set dx = GetUnitX(this.target) - this.x
        set dy = GetUnitY(this.target) - this.y
        set this.a = Atan2(dy, dx)
        set dist = SquareRoot(dx * dx + dy * dy) - OFFSET
        
        set i = R2I(dist / SHADOW_DIST)
        loop
            exitwhen i == 0
            call Fade.create(p, typeid, this.x + SHADOW_DIST * i * Cos(this.a), this.y + SHADOW_DIST * i * Sin(this.a), this.a)
            set i = i - 1
        endloop
        
        set tempData = this
        call SetUnitOwner(castDummy, p, false)
        call IssueTargetOrderById(castDummy, 852274, this.u)
        call SetUnitX(this.u, this.x + dist * Cos(this.a))
        call SetUnitY(this.u, this.y + dist * Sin(this.a))
        call SetUnitFacing(this.u, this.a * bj_RADTODEG)
        call IssueTargetOrder(this.u, "attack", this.target)
        call SetPlayerAbilityAvailable(p, ABILID, false)
        call UnitAddAbility(this.u, DUMABILID1)
        call UnitDamageTarget(this.u, this.target, damage, true, false, ATK, DMG, null)
        
        set p = null
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
    
        call RegisterSpellEffectEvent(ABILID, function thistype.onCast)
        call RegisterSpellEffectEvent(DUMABILID1, function thistype.swap)
        
        set castDummy = CreateUnit(NEUTRAL_PASSIVE, CASTID, 0, 0, 0)
        call UnitAddAbility(castDummy, DUMABILID2)
        call TriggerRegisterUnitEvent(t, castDummy, EVENT_UNIT_SUMMON)
        call TriggerAddCondition(t, Condition(function thistype.onSummon))
        set t = null
    endmethod
endstruct

endlibrary



Credits

Sevion - Alloc
Bribe - SpellEffectEvent
Nestharus - Unit Indexer
Elainiel - Shadow Step Icon



Changelogs

- Initial relase


- Initial transparency configurable


- Fixed USE_DAMAGE not being a static-if
- Removed the linkCount variable
- Got rid of this.dur in Fade struct


- Removed AIDS, Damage and GTrigger
- Now requires Alloc, SpellEffectEvent and Unit Indexer
- Replaced UnitAlive native with IsUnitType


Feedback will be appreciated.

Keywords:
shadow, step, blink, strike
Contents

Shadow Step v1.2 (Map)

Reviews
13 Nov 2011 Bribe: Looks solid. Nice work on the spell. Approved. One last thing - "99999" for unit facing speed is the same as "1" I'm afraid. It doesn't go faster than 1.

Moderator

M

Moderator

13 Nov 2011
Bribe: Looks solid. Nice work on the spell.

Approved.

One last thing - "99999" for unit facing speed is the same as "1" I'm afraid. It doesn't go faster than 1.
 
Level 4
Joined
Apr 11, 2012
Messages
67
can u help me i got this error

Line 5855: Unable to find textmacro: "optional"

set a = Table.create()

please help me
 
Level 3
Joined
May 28, 2019
Messages
39
Is there any way to import this spell to my map and avoid compile errors like this one?
f9144947d0.png

I'm currently using WorldEditor for version 1.31.1
 
Last edited:
Level 3
Joined
May 28, 2019
Messages
39
@Nubius Is JassHelper and vJASS enabled? There should be a menu in the trigger editor named JassHelper, where you can enabled them in the dropdown menu.
Thank you very much for fast reply!

Yeah, both functions are enabled. Nevertheless, I get errors even when I try to press "Save" on the original sample map.
ea408311d9.png
 
Level 18
Joined
Oct 17, 2012
Messages
821
Disable vJASS but keep JassHelper enabled and see if you get the errors again. World Editor on patch 1.31 had a problem with how it integrated JassHelper, so disabling vJASS is actually enabling it.

This is not the case for Reforged World Editor though.
 
Level 3
Joined
May 28, 2019
Messages
39
Disable vJASS but keep JassHelper enabled and see if you get the errors again. World Editor on patch 1.31 had a problem with how it integrated JassHelper, so disabling vJASS is actually enabling it.

This is not the case for Reforged World Editor though.
Unfortunately, it seems like there is no difference in logs.
38594162e7.png
 
Last edited:
Level 18
Joined
Oct 17, 2012
Messages
821
Well, if neither option gets rid of the errors, then your Warcraft 3 installment might just be busted up. I suggest reinstalling Warcraft 3 1.31 from Wc3 Download Archive and see if the problem persists. You will need the cd-key for at least ROC.

On my end, everything is working fine with or without vJASS enabled. I just tested by downloading this spell and saving the map. The map was saved without errors. I got my 1.31 version from there as well.
 
Level 3
Joined
May 28, 2019
Messages
39
Well, if neither option gets rid of the errors, then your Warcraft 3 installment might just be busted up. I suggest reinstalling Warcraft 3 1.31 from Wc3 Download Archive and see if the problem persists. You will need the cd-key for at least ROC.

On my end, everything is working fine with or without vJASS enabled. I just tested by downloading this spell and saving the map. The map was saved without errors. I got my 1.31 version from there as well.
Thank you for your help!

I feel very stupid about it, but I've just tried to launch Reforged editor just to test saving the map. It worked, but now my 1.31.1 editor is not launching because of "Not enough memory resources".

Deleting reforged, reinstalling 1.31.1 didn't help. Seems like there is something in the register entries.

I hope I'll find the solution tomorrow. Don't want to migrate to reforged :(
----------------------UPDATE--------------------------
Well, solution is found. To tell the truth, I'm not sure what exactly have I done, but:
1. "Not enough memory resources" issue is DEFINATELY solved via deleting and recreating proper registry entries.
2. My WC3 version was messed up too so downloading new client from given link helped to solve initial problem. Thank you again! +rep
----------------------UPDATE2---------------------
I've forgotten to tell my thoughts about the spell itself. It's freaknig amazing! Its visual effect fits perfectly to the Blademaster model.

I had to make 2 changes though:
1. remove damage from the spell, because it was the teleport dealing damage to the target, not the attack.
2. when effect starts, I now give my blademaster a special bash ablity for a short period of time. This ability buff (stun) and simple DDS allow me to apply additional damage to the "after-teleport-attack" and then I remove the buff and the ability.

Sorry for my english.
 
Last edited:
Top