Spell Static Link

Reaction score
91
Static Link

GUI/JASS/vJASS? vJASS
MUI? Yes
Lagless? Yes
Requires?
Jass NewGen Pack
TimerUtils (any flavour)
TimedEffects
ChanceToProc
Warcraft 1.23b+
Actually, if you want this for 1.23, just use the TimerUtils 1.23 version since it depends on that.

Description:
Links the Hero and a targeted unit. Using the element of lightning, the Hero will create a storm that will periodically blast the foe with static electricity, slowing it briefly and dealing minor damage. If the linked units go too far away from each other or one of them dies the chain breaks.
Level 1 - 30 damage per hit, lasts 5 seconds.
Level 2 - 40 damage per hit, lasts 6 seconds.
Level 3 - 50 damage per hit, lasts 7 seconds.
Level 4 - 60 damage per hit, lasts 8 seconds.

Code:
JASS:

scope StaticLink initializer Init // uses TimerUtils, TimedEffects, ChanceToProc

//==================================================================================
// C O N F I G U R A T I O N     M E N U
// ==================================================================================

globals
// Raw code of the ability Static Link
    private constant integer AID_RAW = 'A000'
// Raw code of the ability Static Link Slow (the one that slows enemies).
    private constant integer AID_SLOW = 'A001'
// Order string for casting the ability Static Link Slow (AID_SLOW).
    private constant string OID_SLOW = "slow"
// Raw code of a generic dummy unit. 
    private constant integer UID_DUMMY = 'u001'
// Raw code of the unit attached to the caster (currently the unit with Unholy Frenzy model).
    private constant integer UID_RAW = 'e000'
// Raw code of the unit attached to the target (currently the unit with the lightning ball model).
    private constant integer UID_TARG_RAW = 'e001'

// Special effect that is going to randomly be created on the target when it gets hit by a lightning bolt
    private constant string SFX_ONE = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
// Attachment point of the above effect (SFX_ONE).
    private constant string AP_ONE = "origin"
// Duration of the special effect. This is used because some effects die instantly without showing
// if I use DestroyEffect(). If your effect has a death animation then you can set this to 0. 
    private constant real EFFECT_ONE_DURATION = 1.15
// Special effect that is going to be created on the target when it gets hit by a lightning bolt.
// Unlike SFX_ONE, this effect is created ALWAYS when the lightning hits, not randomly.
    private constant string SFX_TWO = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
// Attachment point of the above effect (SFX_TWO).
    private constant string AP_TWO = "origin"
// Duration of the special effect. This is used because some effects die instantly without showing
// if I use DestroyEffect(). If your effect has a death animation then you can set this to 0. 
    private constant real EFFECT_TWO_DURATION = 0.65
    
// Duration of the secondary lightning (the one that is periodically created to hit the target).
    private constant real SECONDARY_LIGHTNING_DURATION = 0.45

// Sound string which is used for creating a sound from label (CreateSoundFromLabel).
    private constant string SOUND_FROM_LABEL = "LightningBolt"
    
// Lightning type of the first ("permanent") lightning between the caster and target.
    private constant string LIGHTNING_ONE = "CLSB"
// Lightning type of the secondary lightning that periodically hits the target.
    private constant string LIGHTNING_TWO = "FORK"
    
// Attack type of the damaging.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
    private constant weapontype WEAPON_TYPE = null
endglobals

globals
// Period for moving the lightnings, units and for damaging. Keep it in range 0.02 - 0.05!
    private constant real PERIOD = 0.03125
    
// Values used for ChanceToProc struct.
// Percentage chance to create a secondary lightning.
// Note that the chance is calculated each PERIOD tick!
    private constant real CHANCE_TO_PROC = 0.03 // currently 3%
// Weight of the chance (see ChanceToProc's documentation if you don't understand).
    private constant real WEIGHT = 0.21
endglobals

// Duration of the link.
private function DURATION takes unit cast, integer lvl returns real
    return 5. + (lvl * 1)
endfunction

// Maximum distance between the two linked units. Passing it will break the link
private function MAX_DISTANCE takes unit cast, integer lvl returns real
    return 600.
endfunction

// Damage dealt every time a secondary lightning strikes the target.
private function DAMAGE takes unit cast, integer lvl returns real
    return 20. + (lvl * 10)
endfunction

//==================================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//==================================================================================

globals
    private location loc = Location(0, 0)
    private unit dum = null
    private sound zap = null
endglobals

private function GetUnitZ takes unit u returns real
    call MoveLocation(loc, GetUnitX(u), GetUnitY(u))
    return GetLocationZ(loc) + GetUnitFlyHeight(u)
endfunction

private struct Lightning
    unit cast
    unit targ
    lightning light
    timer tim
    integer ticks
    
    method onDestroy takes nothing returns nothing
        call DestroyLightning(this.light)
        call ReleaseTimer(this.tim)
    endmethod
    
    private static method Callback takes nothing returns nothing
        local thistype this = GetTimerData(GetExpiredTimer())
        call MoveLightningEx(this.light, true, GetUnitX(this.cast), GetUnitY(this.cast), GetUnitZ(this.cast), GetUnitX(this.targ), GetUnitY(this.targ), GetUnitZ(this.targ))
        set this.ticks = this.ticks - 1 
        if this.ticks <= 0 then
            call this.destroy()
        endif
    endmethod
    
    static method create takes unit cast, unit targ returns thistype
        local thistype this = thistype.allocate()
        set this.cast = cast
        set this.targ = targ
        set this.light = AddLightningEx(LIGHTNING_TWO, true, GetUnitX(this.cast), GetUnitY(this.cast), GetUnitZ(this.cast), GetUnitX(this.targ), GetUnitY(this.targ), GetUnitZ(this.targ))
        set this.ticks = R2I(SECONDARY_LIGHTNING_DURATION / PERIOD)
        set this.tim = NewTimer()
        call SetTimerData(this.tim, this)
        call TimerStart(this.tim, PERIOD, true, function thistype.Callback)
        return this
    endmethod
endstruct

private struct Data
    unit cast
    unit targ
    unit sfx
    unit targSfx
    lightning light
    integer ticks
    integer lvl
    timer tim
    Chance ch
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer(this.tim)
        call KillUnit(this.sfx)
        call KillUnit(this.targSfx)
        call DestroyLightning(this.light)
        call this.ch.destroy()
    endmethod
    
    private static method Callback takes nothing returns nothing
        local thistype this = GetTimerData(GetExpiredTimer())
        local real x = GetUnitX(this.cast)
        local real y = GetUnitY(this.cast)
        local real tx = GetUnitX(this.targ)
        local real ty = GetUnitY(this.targ)
        local real dx = tx - x
        local real dy = ty - y
        local real distance = SquareRoot(dx * dx + dy * dy)
        local integer id  
        call SetUnitX(this.sfx, x)
        call SetUnitY(this.sfx, y)
        call SetUnitX(this.targSfx, tx)
        call SetUnitY(this.targSfx, ty)
        call MoveLightningEx(this.light, true, x, y, GetUnitZ(this.cast), tx, ty, GetUnitZ(this.targSfx))
        if this.ch.isProc() then
            call Lightning.create(this.sfx, this.targSfx)
            set zap = CreateSoundFromLabel(SOUND_FROM_LABEL, false, true, true, 0, 0)
            call AttachSoundToUnit(zap, this.targ)
            call SetSoundVolume(zap, 100)
            call StartSound(zap)
            call KillSoundWhenDone(zap)
            
            if GetRandomInt(0, 1) == 0 then // Some randomization, in case it becomes too much...
                call StartTimedEffect(AddSpecialEffectTarget(SFX_ONE, this.targ, AP_ONE), EFFECT_ONE_DURATION)
            endif
            call StartTimedEffect(AddSpecialEffectTarget(SFX_TWO, this.targ, AP_TWO), EFFECT_TWO_DURATION)
            call SetUnitX(dum, tx)
            call SetUnitY(dum, ty)
            call SetUnitOwner(dum, GetOwningPlayer(this.cast), true)
            call SetUnitAbilityLevel(dum, AID_SLOW, this.lvl)
            call IssueTargetOrder(dum, OID_SLOW, this.targ)
            call UnitDamageTarget(this.cast, this.targ, DAMAGE(this.cast, this.lvl), true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
        endif
        set this.ticks = this.ticks - 1
        if distance >= MAX_DISTANCE(this.cast, this.lvl) or this.ticks <= 0 or IsUnitType(this.targ, UNIT_TYPE_DEAD) == true or IsUnitType(this.cast, UNIT_TYPE_DEAD) == true or IsUnitInvisible(this.targ, GetOwningPlayer(this.cast)) then
            call this.destroy()
        endif
    endmethod
    
    static method create takes unit cast, unit targ returns thistype
        local thistype this = thistype.allocate()
        local real x = GetUnitX(cast)
        local real y = GetUnitY(cast)
        local real tx = GetUnitX(targ)
        local real ty = GetUnitY(targ)
        set this.cast = cast
        set this.targ = targ
        set this.lvl = GetUnitAbilityLevel(this.cast, AID_RAW)
        set this.ch = Chance.create(CHANCE_TO_PROC, WEIGHT)
        set this.sfx = CreateUnit(GetOwningPlayer(this.cast), UID_RAW, x, y, GetUnitFacing(this.cast))
        set this.targSfx = CreateUnit(GetOwningPlayer(this.cast), UID_TARG_RAW, tx, ty, GetUnitFacing(this.targ))
        set this.light = AddLightningEx(LIGHTNING_ONE, true, x, y, GetUnitZ(this.cast), tx, ty, GetUnitZ(this.targSfx))
        set this.ticks = R2I(DURATION(this.cast, this.lvl) / PERIOD)
        set this.tim = NewTimer()
        call SetTimerData(this.tim, this)
        call TimerStart(this.tim, PERIOD, true, function thistype.Callback)
        return this
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_RAW
endfunction

private function Actions takes nothing returns nothing
    call Data.create(GetTriggerUnit(), GetSpellTargetUnit())
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    
    set dum = CreateUnit(Player(13), UID_DUMMY, 0., 0., 0.)
    call UnitAddAbility(dum, 'Aloc')
    call UnitAddAbility(dum, AID_SLOW)
endfunction

endscope


http://i39.tinypic.com/1043ols.jpg

ReadMe included in the test map. The reason I used ChanceToProc is that I get really annoyed when multiple lightnings stack over each other when using the generic "is RandomNumBetween1and100 < X ?" and this is the way to limit that, besides I haven't really found any spell/script that uses it so far.
Special thanks to Tinki3 for the testmap.
 

Attachments

  • Static Link v1[1].6.w3x
    61.8 KB · Views: 242

RaiJin

New Member
Reaction score
40
JASS:
private function Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local unit targ = GetSpellTargetUnit()
    local Data d = Data.create(cast, targ)
    call TimerStart(d.tim, PERIOD, true, function Callback)
    set cast = null
    set targ = null
endfunction


can be this

JASS:
private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit(), GetSpellTargetUnit())
    call TimerStart(d.tim, PERIOD, true, function Callback)
endfunction


i dont see the point of making 2 variables then discarding them
 
Reaction score
91
> Picture please
Testmap.

> i dont see the point of making 2 variables then discarding them
I left them if someone wants to edit the spell and add additional stuff. Less stuff to type ...
 
Reaction score
91
"Picture" represents nothing of what the spell does and does not show the effect of a spell no matter what it is... Still, uploaded one. Looks better in-game, I think.

EDIT: I just noticed I forgot to add the special effect creation... Updated.
 
Reaction score
91
Update, uses a global for the dummy unit and the sound and now uses TimedEffects for the effect.

> (Totally needs particle emitters.)
What?
 

Romek

Super Moderator
Reaction score
963
> I left them if someone wants to edit the spell and add additional stuff. Less stuff to type ...
It's not in a configurable function, so it's "not to be touched unless you know what you're doing". And it's unlikely to be edited.
Whoever would edit that would put in their own locals. They don't need some useless ones there... "Just in case."

JASS:
    private static method Callback takes nothing returns nothing
        local timer tim = GetExpiredTimer()
        local Effect e = Effect(GetTimerData(tim))
        call e.destroy()
        set tim = null
    endmethod

Could be:
JASS:
    private static method Callback takes nothing returns nothing
        call Effect(GetTimerData(GetExpiredTimer())).destroy()
    endmethod


JASS:
        local timer tim = GetExpiredTimer()
        local Lightning l = Lightning(GetTimerData(tim))

// ...
        set tim = null

Could be:
JASS:
local Lightning l = Lightning(GetTimerData(GetExpiredTimer()))


Typecasting isn't needed at all with SetTimerData and GetTimerData.

Really, a new variable doesn't have to be made for everything you ever use. :rolleyes:
You do that a lot. It's pointless.
 
Reaction score
91
I made an update while you were writing this stuff so the Effect struct is no longer in there. :)

> You do that a lot. It's pointless.
I guess it is but I like it. Okay, gonna clean it a little...

EDIT: Updated, and I got a better screenshot.
 

Romek

Super Moderator
Reaction score
963
JASS:
private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit(), GetSpellTargetUnit())
    call TimerStart(d.tim, PERIOD, true, function Callback)
endfunction

Could be:
JASS:
private function Actions takes nothing returns nothing
    call TimerStart(Data.create(GetTriggerUnit(), GetSpellTargetUnit()).tim, PERIOD, true, function Callback)
endfunction

Or how about making create take nothing, and using all the event responses within the create method. Then, change the TriggerAddAction line to:
JASS:
call TriggerAddAction(t, function Data.create)


In Callback, why are you even making 'local Lightning l'?
How about a simple "call Lightning.create(..)"? :confused:
I don't see you using that variable other than setting it once.

The configurable functions would be better off taking levels.
If you want to stick to the unit argument too, then do so.
Functions can always take 2 arguments.
Then make 'level' a struct member, and call GetUnitAbilityLevel once, when the struct is created.
 

_whelp

New Member
Reaction score
54
This may not matter but
JASS:
private function DAMAGE takes unit cast, integer lvl returns real
    return 20. + (lvl * 10)
endfunction


The parentheses aren't needed, because

Parentheses
Exponents
Multiplication
Division
Addition
Subtraction
.

Multiplication always goes before addition...
 

Romek

Super Moderator
Reaction score
963
Do parenthesis offend you? :rolleyes:
 

_whelp

New Member
Reaction score
54
Well, no, anyways my post can be deleted and people can give me negative rep if they want...


Anyways this spell is uber cool...
 

_whelp

New Member
Reaction score
54
At school they said it was P, unless this is a different variant of those from other countries...
 

Romek

Super Moderator
Reaction score
963
My school has a different one too.
Not that it matters.

And you wouldn't get -rep for something like that. :p

As for the spell, I'll wait for some more replies. :)
 
Reaction score
91
> As for the spell, I'll wait for some more replies.
Okay. ^^

> Anyways this spell is uber cool...
Ty, I thought it sucked a lot as a concept but I guess some people like it as well.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    How can you tell the difference between real traffic and indexing or AI generation bots?
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top