• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Trigger] Tower ability "corpse magnet"

Level 2
Joined
Aug 22, 2023
Messages
4
Hello,

creating a Towerdefense - based on the amount of units/corpses in a maze - i would like a tower that: if a creep dies - its corpse would - if in range - fly to the nearest tower called "corpse heap" //preferably +1 a stat(like current health) so that the tower could use an ability like: -X hp -> give a tower +X mana

i'm very new to the mapping community and got no experience in vJass

was wondering if you could help me out
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,595
Hello, you don't need any experience in Jass (or vJass, they're basically the same thing) to create something like this.

Here are the triggers for creating this effect. I also attached a map with all of these triggers which you can easily copy and paste into your own map. Just remember that you'll need to update some of the triggers to work with how you've setup the towers/enemies in your map. Also, the CM_Closest_Distance variable needs to be given a value equal to your desired range (towers within X range of dying enemy).

This trigger detects when you build a Corpse Tower and keeps track of it in a Unit Group variable:
  • Corpse Magnet Tower Built
    • Events
      • Unit - A unit Finishes construction
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Corpse Tower
    • Actions
      • -------- Note: This tracks the Corpse Towers so we can reference them whenever an enemy unit dies: --------
      • Unit Group - Add (Triggering unit) to CM_Tower_Group
      • -------- --------
      • -------- Note: I disable some of the triggers while no Corpse Towers are active on the map: --------
      • Set VariableSet CM_Tower_Count = (CM_Tower_Count + 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CM_Tower_Count Equal to 1
        • Then - Actions
          • Trigger - Turn on Corpse Magnet Get Enemy Corpse <gen>
          • Trigger - Turn on Corpse Magnet Tower Dies <gen>
        • Else - Actions

This trigger detects when a Corpse Tower dies and stops tracking it in our Unit Group variable:
  • Corpse Magnet Tower Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Corpse Tower
      • ((Triggering unit) is in CM_Tower_Group.) Equal to True
    • Actions
      • -------- Note: Now that the tower is dead we no longer need to track it: --------
      • Unit Group - Remove (Triggering unit) from CM_Tower_Group.
      • -------- --------
      • -------- Note: I disable some of the triggers while no Corpse Towers are active on the map: --------
      • Set VariableSet CM_Tower_Count = (CM_Tower_Count - 1)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CM_Tower_Count Equal to 0
        • Then - Actions
          • Trigger - Turn off Corpse Magnet Get Enemy Corpse <gen>
          • Trigger - Turn off Corpse Magnet Tower Dies <gen>
        • Else - Actions

This trigger detects when an enemy unit dies and launches a special corpse Missile at the nearest Corpse Tower (if it finds one):
  • Corpse Magnet Get Enemy Corpse
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Owner of (Triggering unit)) Equal to Player 2 (Blue)
    • Actions
      • Set VariableSet CM_Point[0] = (Position of (Triggering unit))
      • -------- --------
      • -------- Note: This attempts to get us the closest Corpse Magnet Tower: --------
      • Set VariableSet CM_Closest_Corpse_Tower = No unit
      • Set VariableSet CM_Closest_Distance = 9999999.00
      • Unit Group - Pick every unit in CM_Tower_Group and do (Actions)
        • Loop - Actions
          • Set VariableSet CM_Corpse_Tower = (Picked unit)
          • Set VariableSet CM_Point[1] = (Position of CM_Corpse_Tower)
          • Set VariableSet CM_Distance = (Distance between CM_Point[0] and CM_Point[1])
          • Custom script: call RemoveLocation( udg_CM_Point[1] )
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • CM_Distance Less than CM_Closest_Distance
            • Then - Actions
              • Set VariableSet CM_Closest_Corpse_Tower = CM_Corpse_Tower
              • Set VariableSet CM_Closest_Distance = CM_Distance
            • Else - Actions
      • -------- --------
      • -------- Note: If we found a valid Corpse Magnet Tower then launch a custom Missile at it: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • CM_Closest_Corpse_Tower Not equal to No unit
        • Then - Actions
          • -------- SETTING UP MEMBERS --------
          • Set VariableSet MissileStart = CM_Point[0]
          • Set VariableSet MissileStartZ = 50.00
          • Set VariableSet MissileFinishZ = 128.00
          • Set VariableSet MissileModel = Abilities\Weapons\MeatwagonMissile\MeatwagonMissile.mdl
          • Set VariableSet MissileSpeed = 800.00
          • Set VariableSet MissileSource = (Triggering unit)
          • Set VariableSet MissileTarget = CM_Closest_Corpse_Tower
          • Set VariableSet MissileOwner = (Owner of MissileTarget)
          • Set VariableSet MissileVision = 0.00
          • Set VariableSet MissileArc = 0.40
          • Set VariableSet MissileCollision = 50.00
          • -------- SETTING UP EVENTS --------
          • Set VariableSet Missile_onFinish = Corpse Magnet Corpse Impact <gen>
          • -------- LAUNCH THE MISSILE --------
          • Trigger - Run MissileCreate <gen> (ignoring conditions)
        • Else - Actions
          • Custom script: call RemoveLocation( udg_CM_Point[0] )
Note: You need to change the value of CM_Closest_Distance to the Area Of Effect that you want this Corpse Magnet ability to have. So if the enemy needs to be within 900.00 range of a Corpse Tower then you'd do this:
  • Set Variable CM_Closest_Distance = 900.01
I added the .01 because the tower needs to be LESS than that distance away.


This trigger detects when one of our special corpse Missiles reaches a Corpse Tower and then heals it:
  • Corpse Magnet Corpse Impact
    • Events
    • Conditions
      • (MissileTarget is alive) Equal to True
    • Actions
      • -------- Note: You need to use the Missile variables provided by the Missile System in this trigger! --------
      • Unit - Set life of MissileTarget to ((Life of MissileTarget) + 1.00)
      • Special Effect - Create a special effect at MissileFinish using Abilities\Spells\Undead\AnimateDead\AnimateDeadTarget.mdl
      • Special Effect - Destroy (Last created special effect)
My Corpse Magnet triggers use this system: Relativistic Missiles [vJASS][LUA][GUI]

Edit: Updated the Corpse Magnet Tower Dies trigger to use a Condition to prevent a bug related to it dying before finishing construction.
 

Attachments

  • Corpse Magnet 1.w3x
    1 MB · Views: 0
Last edited:
Level 2
Joined
Aug 22, 2023
Messages
4
Hello again,
unfortunatly i am using the 1.28 german version of wc3 so I couldn't use your reccomended missile system. A friend of mine told me to get the "SharpCraft World Editor Extended" and to use the "Relativistic Missiles [vJASS][LUA][GUI]". With your and his information i completed the tower ability.

Differences:
I am using "unit decays" instead of "unit dies" as an event and dont have the "Corpse Magnet Corpse Impact" trigger.


  • CorpseHeapGetEnemyCorpse
    • Ereignisse
      • Einheit - A unit Verfällt
    • Bedingungen
      • ((Owner of (Triggering unit)) Gleich Spieler 11 (Dunkelgrün)) or ((Owner of (Triggering unit)) Gleich Spieler 12 (Braun))
    • Aktionen
      • Set CorpseHeapPoint[0] = (Position of (Triggering unit))
      • Set CorpseHeapClosestCorpseTower = Keine Einheit
      • Set CorpseHeapClosestDistance = 900.01
      • Einheitengruppe - Pick every unit in CorpseHeapTowerGroup and do (Actions)
        • Schleifen - Aktionen
          • Set CorpseHeapCorpseTower = (Picked unit)
          • Set CorpseHeapPoint[1] = (Position of CorpseHeapCorpseTower)
          • Set CorpseHeapDistance = (Distance between CorpseHeapPoint[0] and CorpseHeapPoint[1])
          • Custom script: call RemoveLocation(udg_CorpseHeapPoint[1])
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Bedingungen
              • CorpseHeapDistance Kleiner als CorpseHeapClosestDistance
            • 'THEN'-Aktionen
              • Set CorpseHeapClosestCorpseTower = CorpseHeapCorpseTower
              • Set CorpseHeapClosestDistance = CorpseHeapDistance
            • 'ELSE'-Aktionen
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Bedingungen
          • CorpseHeapClosestCorpseTower Ungleich Keine Einheit
        • 'THEN'-Aktionen
          • Spiel - Display to (All players) the text: CreateMissile
          • Set TempInteger = (CMS_Amount + 1)
          • -------- Hier würden die daten für das Projectil rein --------
          • Set CMS_StartingLocation = (Position of (Triggering unit))
          • Set CMS_StartingHeight = 0.00
          • Set CMS_StartingSpeed = 300.00
          • Set CMS_StartingAcceleration = 200.00
          • Set CMS_StartingSource = (Triggering unit)
          • Set CMS_StartingType = 1
          • Set CMS_StartingMissile = (Triggering unit)
          • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
          • -------- - --------
          • Set CMS_Param_MaxDistance[TempInteger] = ((Distance between (Position of (Triggering unit)) and (Position of CorpseHeapClosestCorpseTower)) - 30.00)
          • Set CMS_Param_TurnRate[TempInteger] = 55.00
          • Set CMS_StartingAngle = (Angle from CMS_StartingLocation to (Position of CorpseHeapClosestCorpseTower))
          • Set CMS_Param_IsHoming[TempInteger] = True
          • Set CMS_Param_Target_Type[TempInteger] = CMS_TARGETTYPE_UNIT
          • Set CMS_Param_Target_Location[TempInteger] = (Position of CorpseHeapClosestCorpseTower)
          • Set CMS_Param_Target_Unit[TempInteger] = CorpseHeapClosestCorpseTower
          • -------- - --------
          • Set CMS_Param_Target_Unit[0] = CorpseHeapClosestCorpseTower
          • Set CMS_Param_WalkingHeight[TempInteger] = 0.00
          • Set CMS_Param_Gravity[TempInteger] = (-45.00 x 0.03)
          • -------- - --------
          • Custom script: call RemoveLocation(udg_TempLocation)
          • -------- - --------
          • Spezialeffekt - Create a special effect attached to the overhead of CorpseHeapClosestCorpseTower using Abilities\Spells\Undead\RaiseSkeletonWarrior\RaiseSkeleton.mdl
          • -------- - --------
        • 'ELSE'-Aktionen
          • Custom script: call RemoveLocation(udg_CorpseHeapPoint[0])
      • Wait 1.00 seconds
      • Einheit - Set life of CorpseHeapClosestCorpseTower to ((Leben of CorpseHeapClosestCorpseTower) + 1.00)
Again thank you a lot for your help, this really saved me alot of time and made me more used to making abilities in the editor <3
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,595
Understood, I'm glad you got it working. But in the future please let people know your version/limitations beforehand. I usually ask but I assumed since you were a new user you would be on the latest patch (that seems to be the case most of the time).

Anyway, I see three issues with your trigger.

The first issue is the use of the Wait action along with a global variable:
  • Wait 1.00 seconds
  • Einheit - Set life of CorpseHeapClosestCorpseTower to ((Leben of CorpseHeapClosestCorpseTower) + 1.00)
You should never reference a global variable after a Wait unless you know for a fact that the variable's value can't change. In this case, if another unit were to Decay within that 1.00 second period, it COULD change the CorpseHeapClosestCorpseTower to a different tower. If that happens, the different tower will get healed twice and the original tower won't get healed at all. Remember, a global variable can only have ONE value at a time.

The easy solution is to use a local variable since those are local to the trigger instance that created them. Alternatively, you could use some kind of indexing method with Array variables.

The second issue is that you're ALWAYS Waiting 1.00 second and trying to Heal to CorpseHeapClosestCorpseTower. That doesn't make sense, you should only Wait and Heal if you actually found a nearby corpse tower. The solution is simple, move those Actions into the THEN part of the trigger where it runs the Actions IF a tower is found:

Here's how you can fix both of these issues:
  • CorpseHeapGetEnemyCorpse
    • Ereignisse
      • Einheit - A unit Verfällt
    • Bedingungen
      • ((Owner of (Triggering unit)) Gleich Spieler 11 (Dunkelgrün)) or ((Owner of (Triggering unit)) Gleich Spieler 12 (Braun))
    • Aktionen
      • Custom script: local unit u = null
      • Set CorpseHeapPoint[0] = (Position of (Triggering unit))
      • Set CorpseHeapClosestCorpseTower = Keine Einheit
      • Set CorpseHeapClosestDistance = 900.01
      • Einheitengruppe - Pick every unit in CorpseHeapTowerGroup and do (Actions)
        • Schleifen - Aktionen
          • Set CorpseHeapCorpseTower = (Picked unit)
          • Set CorpseHeapPoint[1] = (Position of CorpseHeapCorpseTower)
          • Set CorpseHeapDistance = (Distance between CorpseHeapPoint[0] and CorpseHeapPoint[1])
          • Custom script: call RemoveLocation(udg_CorpseHeapPoint[1])
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • 'IF'-Bedingungen
              • CorpseHeapDistance Kleiner als CorpseHeapClosestDistance
            • 'THEN'-Aktionen
              • Set CorpseHeapClosestCorpseTower = CorpseHeapCorpseTower
              • Set CorpseHeapClosestDistance = CorpseHeapDistance
            • 'ELSE'-Aktionen
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • 'IF'-Bedingungen
          • CorpseHeapClosestCorpseTower Ungleich Keine Einheit
        • 'THEN'-Aktionen
          • Spiel - Display to (All players) the text: CreateMissile
          • Set TempInteger = (CMS_Amount + 1)
          • -------- Hier würden die daten für das Projectil rein --------
          • Set CMS_StartingLocation = (Position of (Triggering unit))
          • Set CMS_StartingHeight = 0.00
          • Set CMS_StartingSpeed = 300.00
          • Set CMS_StartingAcceleration = 200.00
          • Set CMS_StartingSource = (Triggering unit)
          • Set CMS_StartingType = 1
          • Set CMS_StartingMissile = (Triggering unit)
          • Custom script: set udg_TempInteger = CMS_CreateMissileEx()
          • -------- - --------
          • Set CMS_Param_MaxDistance[TempInteger] = ((Distance between (Position of (Triggering unit)) and (Position of CorpseHeapClosestCorpseTower)) - 30.00)
          • Set CMS_Param_TurnRate[TempInteger] = 55.00
          • Set CMS_StartingAngle = (Angle from CMS_StartingLocation to (Position of CorpseHeapClosestCorpseTower))
          • Set CMS_Param_IsHoming[TempInteger] = True
          • Set CMS_Param_Target_Type[TempInteger] = CMS_TARGETTYPE_UNIT
          • Set CMS_Param_Target_Location[TempInteger] = (Position of CorpseHeapClosestCorpseTower)
          • Set CMS_Param_Target_Unit[TempInteger] = CorpseHeapClosestCorpseTower
          • -------- - --------
          • Set CMS_Param_Target_Unit[0] = CorpseHeapClosestCorpseTower
          • Set CMS_Param_WalkingHeight[TempInteger] = 0.00
          • Set CMS_Param_Gravity[TempInteger] = (-45.00 x 0.03)
          • -------- - --------
          • Custom script: call RemoveLocation(udg_TempLocation)
          • -------- - --------
          • Spezialeffekt - Create a special effect attached to the overhead of CorpseHeapClosestCorpseTower using Abilities\Spells\Undead\RaiseSkeletonWarrior\RaiseSkeleton.mdl
          • Special Effect - Destroy (Last created special effect)
          • -------- - --------
          • Custom script: set u = udg_CorpseHeapClosestCorpseTower
          • Wait 1.00 seconds
          • Custom script: set udg_CorpseHeapClosestCorpseTower = u
          • Einheit - Set life of CorpseHeapClosestCorpseTower to ((Leben of CorpseHeapClosestCorpseTower) + 1.00)
        • 'ELSE'-Aktionen
          • Custom script: call RemoveLocation(udg_CorpseHeapPoint[0])
      • Custom script: set u = null
Note how I've structured everything. The Actions need to be in those exact positions!

The third issue is a minor one. You're creating a memory leak whenever you do this:
  • Spezialeffekt - Create a special effect attached to the overhead of CorpseHeapClosestCorpseTower using Abilities\Spells\Undead\RaiseSkeletonWarrior\RaiseSkeleton.mdl
You need to Destroy the Special Effect or else it will exist forever:
  • Spezialeffekt - Create a special effect attached to the overhead of CorpseHeapClosestCorpseTower using Abilities\Spells\Undead\RaiseSkeletonWarrior\RaiseSkeleton.mdl
  • Special Effect - Destroy (Last created special effect)
The game assumes that you may want to reuse this Special Effect so it won't destroy it automatically. It also doesn't know when you'd want to destroy it anyway, so it makes sense that the user is in charge of doing this. Memory leaks can create performance issues if too many of them pile up.
 
Last edited:
Level 2
Joined
Aug 22, 2023
Messages
4
"But in the future please let people know your version/limitations beforehand." noted!

On a sidenote the special-effect doesnt seem to work at all (i can't see any animation beeing played)
The unit model is: "Doodads\Ashenvale\Props\ScorchedRemains\ScorchedRemains1.mdl"
  • Spezialeffekt - Create a special effect attached to the overhead of CorpseHeapClosestCorpseTower using Abilities\Spells\Undead\RaiseSkeletonWarrior\RaiseSkeleton.mdl
  • Spezialeffekt - Destroy (Last created special effect)
The special effect is not nessecary but it would be nice to know more about them
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,595
If the model being used doesn't have a proper death animation then it will simply disappear when it's destroyed. For cases like those you would use a Destroy-After-Delay system which gives the model time to finish it's animation before destroying it.

Here's how you can easily create one yourself in two steps.

Step 1: Create a Real variable called Destroy_Effect_Delay

Step 2: Create this trigger:
  • Destroy Effect After Delay
    • Events
    • Conditions
    • Actions
      • Custom script: local effect sfx = GetLastCreatedEffectBJ()
      • Wait Destroy_Effect_Delay seconds
      • Custom script: call DestroyEffect(sfx)
      • Custom script: set sfx = null

Now you can easily use the system like so:
  • Example
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
    • Actions
      • Special Effect - Create a special effect attached to the chest of (Triggering unit) using Abilities\Spells\Human\DivineShield\DivineShieldTarget.mdl
      • Set Destroy_Effect_Delay = 1.00
      • Trigger - Run Destroy Effect After Delay <gen> (ignoring conditions)
That will destroy the special effect after 1.00 second. You don't have to worry about the Wait issue I described before because we're taking advantage of local variables in the Destroy Effect After Delay trigger.
 
Last edited:
Top