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

[Trigger] How to make this spell MUI

Status
Not open for further replies.
Level 11
Joined
Aug 11, 2009
Messages
594
So I am just making a simple spell with some dummy units as special effects, my problem is how to remove the dummy units after 0.5 sec in an MUI way.

If you need some info:
There are 3 types of dummy units, A, B and C.
The spell creates 10 units of type A, 5 units of type B and one unit of type C.

All of them are created instantly and should be removed 0.5 sec after the spell is cast. I have everything done except the removing of the units in a way that works. If you want to see the triggers let me know. And please do it in GUI, I have no knowledge in Jass.

+rep and mighty thanks to some help on this!

P.S I am using a purple purge aswell for one of the dummy units and even in a non-MUI way it wont remove itself after I kill it and remove it. Anyone knows what could be the problem?
 
Level 33
Joined
Mar 27, 2008
Messages
8,035
Just add them Expiration Timer of 0.50 to (Last created unit) (if you're using Looping) and to (Picked unit) (if you put them in a Unit Group or something) or somewhat close than that.
The function is under Unit - Add Expiration Timer.
 
Level 33
Joined
Mar 27, 2008
Messages
8,035
Instead of saving each of them a duration based on variable (and later on we have to keep track each duration remaining and kill it), we should stick to simple method, just use Expiration Timer function, that's all you need.
 
Level 11
Joined
Aug 11, 2009
Messages
594
God i feel so stupid :p Thanks to both of you :) btw do any of you know how to remove lightning effects after 0.5 sec? Does indexing work for that?
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,538
Here's what I have -

JASS:
library TempLightning
    globals
        private constant real FIDELITY=1./30.
        public constant string CHAIN_LIGHTNING_PRIMARY="CLPB"
        public constant string CHAIN_LIGHTNING_SECONDARY="CLSB"
        public constant string DRAIN="DRAB"
        public constant string DRAIN_LIFE="DRAL"
        public constant string DRAIN_MANA="DRAM"
        public constant string FINGER_OF_DEATH="AFOD"
        public constant string FORKED_LIGHTNING="FORK"
        public constant string HEALING_WAVE_PRIMARY="HWPB"
        public constant string HEALING_WAVE_SECONDARY="HWSB"
        public constant string LIGHTNING_ATTACK="CHIM"
        public constant string MAGIC_LEASH="LEAS"
        public constant string MANA_BURN="MBUR"
        public constant string MANA_FLARE="MFPB"
        public constant string SPIRIT_LINK="SPLK"
    endglobals
    
    struct TempLightning
        lightning light
        real timeLeft
        real toFade
        real fadeDuration
        
        private static TempLightning array lightnings
        private static integer dbIndex=-1
        private static timer time=CreateTimer()
        
        private static method p takes nothing returns nothing
            local integer index=0
            local TempLightning tempDat
            loop
                exitwhen index>.dbIndex
                set tempDat=.lightnings[index]
                set tempDat.timeLeft=tempDat.timeLeft-FIDELITY
                set tempDat.toFade=tempDat.toFade-FIDELITY
                if tempDat.toFade<0. then
                    call SetLightningColor(tempDat.light,1.,1.,1.,tempDat.timeLeft/tempDat.fadeDuration)
                endif
                if tempDat.timeLeft<=0. then
                    call DestroyLightning(tempDat.light)
                    call tempDat.destroy()
                    set .lightnings[index]=.lightnings[.dbIndex]
                    set .dbIndex=.dbIndex-1
                    set index=index-1
                    if .dbIndex==-1 then
                        call PauseTimer(.time)
                    endif
                endif
                set index=index+1
            endloop
        endmethod
        
        public static method add takes string model, real x1, real y1, real z1, real x2, real y2, real z2, real duration, real fadeTime returns nothing
            local TempLightning tempDat=TempLightning.create()
            local lightning light=AddLightningEx(model,true,x1,y1,z1,x2,y2,z2)
            set tempDat.light=light
            set tempDat.timeLeft=duration
            set tempDat.toFade=fadeTime
            set tempDat.fadeDuration=duration-fadeTime
            if tempDat.fadeDuration<0. then
                set tempDat.fadeDuration=0.
            endif
            set .dbIndex=.dbIndex+1
            set .lightnings[.dbIndex]=tempDat
            if .dbIndex==0 then
                call TimerStart(.time,FIDELITY,true,function TempLightning.p)
            endif
        endmethod
    endstruct
endlibrary

It just allows you to create a lightning from a 3d point to another 3d point. After fadeTime, the lightning fades out over (duration-fadeTime) seconds.

There is no support for unit to unit or unit to point attachments, and no support for changing lightning color. But I could add that if you wanted.

Use it like this:

  • Custom script: call TempLightning.add(<type>,x1,y1,z1,x2,y2,z2,duration,fadeTime)
Where type is the string code for the lightning, and x1,y1,z1,x2,y2,z2 are the real coordinates of the 2 end points.
 
Level 11
Joined
Aug 11, 2009
Messages
594
If you could add color support that would be awesome, i only use triggered lightning when it needs a different color :)
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,538
How's this?

JASS:
library TempLightning
    globals
        private constant real FIDELITY=1./30.
        public constant string CHAIN_LIGHTNING_PRIMARY="CLPB"
        public constant string CHAIN_LIGHTNING_SECONDARY="CLSB"
        public constant string DRAIN="DRAB"
        public constant string DRAIN_LIFE="DRAL"
        public constant string DRAIN_MANA="DRAM"
        public constant string FINGER_OF_DEATH="AFOD"
        public constant string FORKED_LIGHTNING="FORK"
        public constant string HEALING_WAVE_PRIMARY="HWPB"
        public constant string HEALING_WAVE_SECONDARY="HWSB"
        public constant string LIGHTNING_ATTACK="CHIM"
        public constant string MAGIC_LEASH="LEAS"
        public constant string MANA_BURN="MBUR"
        public constant string MANA_FLARE="MFPB"
        public constant string SPIRIT_LINK="SPLK"
    endglobals
    
    struct TempLightning
        lightning light
        real timeLeft
        real toFade
        real fadeDuration
        real red
        real green
        real blue
        
        private static TempLightning array lightnings
        private static integer dbIndex=-1
        private static timer time=CreateTimer()
        
        private static method p takes nothing returns nothing
            local integer index=0
            local TempLightning tempDat
            loop
                exitwhen index>.dbIndex
                set tempDat=.lightnings[index]
                set tempDat.timeLeft=tempDat.timeLeft-FIDELITY
                set tempDat.toFade=tempDat.toFade-FIDELITY
                if tempDat.toFade<0. then
                    call SetLightningColor(tempDat.light,tempDat.red,tempDat.green,tempDat.blue,tempDat.timeLeft/tempDat.fadeDuration)
                endif
                if tempDat.timeLeft<=0. then
                    call DestroyLightning(tempDat.light)
                    call tempDat.destroy()
                    set .lightnings[index]=.lightnings[.dbIndex]
                    set .dbIndex=.dbIndex-1
                    set index=index-1
                    if .dbIndex==-1 then
                        call PauseTimer(.time)
                    endif
                endif
                set index=index+1
            endloop
        endmethod
        
        public static method add takes string model, real x1, real y1, real z1, real x2, real y2, real z2, real duration, real fadeTime, real red, real green, real blue returns nothing
            local TempLightning tempDat=TempLightning.create()
            local lightning light=AddLightningEx(model,true,x1,y1,z1,x2,y2,z2)
            call SetLightningColor(light,red,green,blue,1.)
            set tempDat.red=red
            set tempDat.green=green
            set tempDat.blue=blue
            set tempDat.light=light
            set tempDat.timeLeft=duration
            set tempDat.toFade=fadeTime
            set tempDat.fadeDuration=duration-fadeTime
            if tempDat.fadeDuration<0. then
                set tempDat.fadeDuration=0.
            endif
            set .dbIndex=.dbIndex+1
            set .lightnings[.dbIndex]=tempDat
            if .dbIndex==0 then
                call TimerStart(.time,FIDELITY,true,function TempLightning.p)
            endif
        endmethod
    endstruct
endlibrary

Now you have to call it like

JASS:
call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit()),50.,GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()),70.,1.,.5,1.,0.,0.)
 
Level 11
Joined
Aug 11, 2009
Messages
594
When I use these the lightnings appear but only as small ones, not between the caster and the target, you know what could be wrong?

Could you also explain how the call string works? Where do I change the fade time etc.

Also, I have a spell which creates a dummy unit which will shoot the lightning at the target. How do I change it from going from triggering unit to last created unit?

Thanks :)
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,538
When I use these the lightnings appear but only as small ones, not between the caster and the target, you know what could be wrong?

Either your event has no target unit, or it has no trigger unit.

Could you also explain how the call string works? Where do I change the fade time etc.

Sure.
JASS:
call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit()),50.,GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()),70.,1.,.5,1.,0.,0.)
The arguments are: <string lightningType>, <real x1>, <real y1>, <real z1>, <real x2>, <real y2>, <real z2>, <real duration>, <real fadeTime>, <real red>, <real green>, <real blue>

the lightning disappears after duration seconds, and begins fading after fadeTime seconds.

So if you want the lightning to immediately begin fading from 100% opacity to 0% opacity over 2 seconds, use "...2.,0..." I hope that makes sense.

Also, I have a spell which creates a dummy unit which will shoot the lightning at the target. How do I change it from going from triggering unit to last created unit?

Instead of getting the coordinates of the trigger unit like this:

GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit())

Get the coordinates of the last created unit like this:

GetUnitX(bj_lastCreatedUnit), GetUnitY(bj_lastCreatedUnit)

Hope that helps!
 
Level 11
Joined
Aug 11, 2009
Messages
594
I am using "unit - starts an effect of an ability" as event and it is à target unit ability, that should have à triggering unit. Or do i need to make à variable?
 
Level 11
Joined
Aug 11, 2009
Messages
594
Here are the triggers for the spell and a screenshot of how it looks ingame.

I copied the lightning system right off the one you posted.

Maybe the problem is I am calling through custom script? Maybe I need to do the whole spell in vJass?
 

Attachments

  • Triggers.jpg
    Triggers.jpg
    122.3 KB · Views: 104
  • LightningError.jpg
    LightningError.jpg
    151.7 KB · Views: 113
Level 11
Joined
Aug 11, 2009
Messages
594
No it should create two lightnings, one from each ball that spawns. But I noticed that the lightnings moved with the camera when I moved it to left/right/up/down or tried to take down the view to see from another angle :/

I am using UTM, perhaps creating normal maps with it does not work? Because this is some really strange bug :/

Here is the triggers btw:
  • Spell 1 Energy Burst
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Energy Burst (Arcane)
    • Actions
      • Set Real_Damage = ((4.00 x (Real((Hero level of (Triggering unit))))) + (1.30 x (Real((Agility of (Triggering unit) (Include bonuses))))))
      • Unit - Create 1 Dummy Unit (Energy Burst) for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by 200.00 towards ((Facing of (Triggering unit)) + 90.00) degrees) facing Default building facing degrees
      • Unit - Add a 0.50 second Generic expiration timer to (Last created unit)
      • Custom script: call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,GetUnitX(bj_lastCreatedUnit),GetUnitY(bj_lastCreatedUnit),50.,GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()),70.,1.,.5,1.,0.,1.)
      • Unit - Create 1 Dummy Unit (Energy Burst) for (Owner of (Triggering unit)) at ((Position of (Triggering unit)) offset by 200.00 towards ((Facing of (Triggering unit)) - 90.00) degrees) facing Default building facing degrees
      • Unit - Add a 0.50 second Generic expiration timer to (Last created unit)
      • Custom script: call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,GetUnitX(bj_lastCreatedUnit),GetUnitY(bj_lastCreatedUnit),50.,GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()),70.,1.,.5,1.,0.,1.)
      • Special Effect - Create a special effect attached to the chest of (Target unit of ability being cast) using Abilities\Spells\Undead\OrbOfDeath\OrbOfDeathMissile.mdl
      • Special Effect - Destroy (Last created special effect)
      • Unit - Cause (Triggering unit) to damage (Target unit of ability being cast), dealing Real_Damage damage of attack type Chaos and damage type Magic
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,538
It's not a bug, and UTM can't affect the behavior of in-game effects (doodads are baked into the terrain).

What's happening is that the z-height we've given the lightning is less than the z-height of the terrain, but lightning is visible even if terrain is "covering" it. That's a global feature in WC3.

To better explain this, you may have noticed this "initial cliff level" option when creating a new map:

I9iyE.png


As you should already know, setting a higher than usual initial cliff level allows you to "dig" deeper with cliffs before reaching "0" (at which point the editor won't let you go any lower with the cliff tool).

After you've created the map, you can see the z-coordinate of a particular point by just placing the mouse somewhere on the heightmap and checking the "point" box in the editor:

YonNY.png


From this example, the height of the whole map is at 384. game units, because I've set the initial cliff level to 5.

So now in your map, we could change the values of 50. and 70. to numbers about 50. and 70. higher than your heightmap, but a much better option would be to use a function to get the z-height of the point and then add 50. and 70. to that.

Current code:

JASS:
call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit()),50.,GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()),70.,1.,.5,1.,0.,0.)

An example of a function to get z-height in jass looks like this:

JASS:
scope example initializer i
    globals
        private location zLoc=Location(0.,0.)
    endglobals
    
    private function getZ takes real x, real y returns real
        call MoveLocation(zLoc,x,y)
        return GetLocationZ(zLoc)
    endfunction
    
    private function i takes nothing returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(),0.,0.,
    endfunction
endscope

And in a jass script handling this lightning library would look like this:

JASS:
...
local unit tU=GetTriggerUnit()
local unit targ=GetSpellTargetUnit()
local real x1=GetUnitX(tU)
local real x2=GetUnitY(targ)
local real y1=GetUnitY(tU)
local real y2=GetUnitY(targ)
local real z1=getZ(x1,y1)+50.
local real z2=getZ(x2,y2)+70.
call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,x1,y1,z1,x2,y2,z2,1.,.5,1.,0.,0.)
set tU=null
set targ=null
...

Which would be ideal because it doesn't reuse the same native (doesn't call GetTriggerUnit() many times, for example) and also keeps the entire TempLightning call in 1 readable line.

But since you're using the Custom Script action, it's nice to have it all in one line, so we're gonna do two things:

1: Put a getZ function inside the TempLightning library:

JASS:
library TempLightning
    globals
        private constant real FIDELITY=1./30.
        private location zLoc=Location(0.,0.)
        public constant string CHAIN_LIGHTNING_PRIMARY="CLPB"
        public constant string CHAIN_LIGHTNING_SECONDARY="CLSB"
        public constant string DRAIN="DRAB"
        public constant string DRAIN_LIFE="DRAL"
        public constant string DRAIN_MANA="DRAM"
        public constant string FINGER_OF_DEATH="AFOD"
        public constant string FORKED_LIGHTNING="FORK"
        public constant string HEALING_WAVE_PRIMARY="HWPB"
        public constant string HEALING_WAVE_SECONDARY="HWSB"
        public constant string LIGHTNING_ATTACK="CHIM"
        public constant string MAGIC_LEASH="LEAS"
        public constant string MANA_BURN="MBUR"
        public constant string MANA_FLARE="MFPB"
        public constant string SPIRIT_LINK="SPLK"
    endglobals
    
    struct TempLightning
        lightning light
        real timeLeft
        real toFade
        real fadeDuration
        real red
        real green
        real blue
        
        private static TempLightning array lightnings
        private static integer dbIndex=-1
        private static timer time=CreateTimer()
        
        private static method p takes nothing returns nothing
            local integer index=0
            local TempLightning tempDat
            loop
                exitwhen index>.dbIndex
                set tempDat=.lightnings[index]
                set tempDat.timeLeft=tempDat.timeLeft-FIDELITY
                set tempDat.toFade=tempDat.toFade-FIDELITY
                if tempDat.toFade<0. then
                    call SetLightningColor(tempDat.light,tempDat.red,tempDat.green,tempDat.blue,tempDat.timeLeft/tempDat.fadeDuration)
                endif
                if tempDat.timeLeft<=0. then
                    call DestroyLightning(tempDat.light)
                    call tempDat.destroy()
                    set .lightnings[index]=.lightnings[.dbIndex]
                    set .dbIndex=.dbIndex-1
                    set index=index-1
                    if .dbIndex==-1 then
                        call PauseTimer(.time)
                    endif
                endif
                set index=index+1
            endloop
        endmethod
        
        public static method add takes string model, real x1, real y1, real z1, real x2, real y2, real z2, real duration, real fadeTime, real red, real green, real blue returns nothing
            local TempLightning tempDat=TempLightning.create()
            local lightning light=AddLightningEx(model,true,x1,y1,z1,x2,y2,z2)
            call SetLightningColor(light,red,green,blue,1.)
            set tempDat.red=red
            set tempDat.green=green
            set tempDat.blue=blue
            set tempDat.light=light
            set tempDat.timeLeft=duration
            set tempDat.toFade=fadeTime
            set tempDat.fadeDuration=duration-fadeTime
            if tempDat.fadeDuration<0. then
                set tempDat.fadeDuration=0.
            endif
            set .dbIndex=.dbIndex+1
            set .lightnings[.dbIndex]=tempDat
            if .dbIndex==0 then
                call TimerStart(.time,FIDELITY,true,function TempLightning.p)
            endif
        endmethod
    endstruct
    
    public function getZ takes real x, real y returns real
        call MoveLocation(zLoc,x,y)
        return GetLocationZ(zLoc)
    endfunction
endlibrary

2: Condense our TempLightning call to one line, using TempLightning_getZ() inside it:

JASS:
call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit()),TempLightning_getZ(GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit())),GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()),TempLightning_getZ(GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit())),1.,.5,1.,0.,0.)

And that should look perfect then :)

I hope now you're beginning to see why JASS users generally hate GUI ;)
 
Level 11
Joined
Aug 11, 2009
Messages
594
Ill check it out once i get home from work in à few hours :)

Yeah, i can clearly understand that :p sorry for putting you through all this and mighty thanks for your help :) i am very grateful
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,538
Ahhh that's a pain in the ass. Let's try this, create 4 variables with the variable editor:

Code:
real trigX
real trigY
real targX
real targY

and set them with 4 custom script actions like this:

JASS:
set udg_trigX=GetUnitX(GetTriggerUnit())
set udg_trigY=GetUnitY(GetTriggerUnit())
set udg_targX=GetUnitX(GetSpellTargetUnit())
set udg_targY=GetUnitY(GetSpellTargetUnit())

and then a 5th custom script to call the TempLightning.add using our new variables:

JASS:
call TempLightning.add(TempLightning_CHAIN_LIGHTNING_SECONDARY,udg_trigX,udg_trigY,TempLightning_getZ(udg_trigX,udg_trigY),udg_targX,udg_targY,TempLightning_getZ(udg_targX,udg_targY),1.,.5,1.,0.,0.)

Hopefully that's short enough now =/
 
Level 11
Joined
Aug 11, 2009
Messages
594
I can try that, I actually got it to work another way :p

Saved triggering unit and target in variables, then in the spell ran another trigger with custom text in which I had your call function (replacing triggering unit etc with the variables instead).

But maybe this way you showed now is more effective than having to create one extra trigger for each spell using lightning?
 
Status
Not open for further replies.
Top