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

Damage Engine 5.A.0.0

This bundle is marked as director's cut. It exceeds all expectations and excels in every regard.
Damage Engine

Three GUI Damage systems for the community of The Hive,
Seven vJass Damage systems for the JASS-heads on their pedestals high,
Nine competing Damage systems, doomed to die,
One for Bribe on his dark throne
In the Land of the Hive where the Workshop lies.
One Damage Engine to rule them all, One Damage Engine to find them,
One Damage Engine to bring them all and in cross-compatibility unite them.


Whether you're looking for a simple DDS (Damage Detection System), need to modify damage or even if you want a complex network of damage pre-and post-processing, this resource is for you. Damage Engine is the most adapted DDS in existence to take full advantage of the new damage detection natives, and is constantly evaluated and scrutinized by both the community and myself for performance improvements, bug fixes and new features alike.

What started with humble beginnings to bring a Unit Indexer-based version of DDS to GUI users to improve on the previous standard (at the time it was Weep's GDDS) would eventually evolve to incorporate other aspects of damage systems out there. @looking_for_help had identified an extremely useful technique with negative spell resistance being used to detect spell damage, and for a time his Physical Damage Detection system became the new standard. It wouldn't be until much later when Damage Engine would resurface and use the same technique in order to be more useful for the community.

Fast forward to 2020, and you'll find not only that cross-compatibility with Weep's and LFH's systems are incorporated, but the most popular DDS systems in existence - even vJass ones - are fully supported via stripped-down API wrappers. All of the functionality of prior DDS systems has been infused into Damage Engine 5.7, and as such the transition to a "one size fits all" DDS is complete. I hope you find this to be useful, and I also hope that it will help you in developing your map to be more dynamic what than you had previously thought possible.

Features

Legacy Code & Requirements

How it works

How to install/upgrade

Video Guides

FAQs

Thanks


  • Damage Type, Attack Type, Weapon Type, Armor Type and Defense Type detection and modification to alter/understand the fundamentals of WarCraft 3's damage processing;
  • Access to the view and change the damage amount before and/or after reduction by armor, armor type and shields.
  • Fully cross-compatible with every major DDS - vJass and GUI alike.
  • Correctly distribute/negate Spirit Link damage.
  • Differentiate between Ranged, Melee, Spell or other types of attack.
  • Does not require any Object Editor data nor Unit Indexer.
  • As of 5.4, it is now completely recursion-proof.
  • Other features:
    • Damage Blocking, reducing, amplifying and/or conversion to healing
    • Does not require you to know nor use Jass NewGen Pack nor any custom script
    • Custom DamageType association available to be set before and/or during damage event
    • Detect when multiple targets were hit simultaneously via DamageEventAOE > 1.
    • Detect when the same unit was hit simultaneously by the same source via DamageEventLevel > 1.
  • Detect damage: use the event "OnDamageEventEqual to <any value>". You have access to the following variables for reference:
    • DamageEventSource - the unit dealing the damage
    • DamageEventTarget - the unit receiving the damage
    • DamageEventAmount - the amount of damage the unit will receive
    • DamageEventPrevAmt - the amount of damage prior to being modified by the game engine.
    • DamageEventAttackT - the attack type (Chaos, Spells, Pierce, etc.)
    • DamageEventDamageT - the damage type - for comprehensive info, click here.
    • DamageEventWeaponT - the weapon type determines if an attack plays some kind of sound on attack (ie. Metal Heavy Bash). It is always NONE for spells and almost always NONE for ranged attacks.
    • DamageEventArmorT - the armor type (Flesh, Stone, Ethereal, etc.)
    • DamageEventDefenseT - the defense type (Hero, Fortified, Unarmored, etc.)
    • DamageEventArmorPierced - if DAMAGE_TYPE_NORMAL, how much armor was set to be ignored by the user.
    • IsDamageSpell, IsDamageRanged, IsDamageMelee - determine the source category of damage dealt.
    • IsDamageCode - Determine if the damage was dealt natively or by the user. This can give incorrect readings if the user has not identified to Damage Engine that it is code damage. Therefore I recommend setting NextDamageType prior to dealing damage.
    • DamageEventType - An integer identifier that can be referenced by you for whatever extra differentiation you need. You can also create your own special damage types and add them to the definitions in the Damage Event Config trigger. If you the unit should explode on death by that damage, use a damage type integer less than 0. If you want the damage to ignore all modifications, use DamageTypePure.
  • To change damage before it's processed by the WarCraft 3 engine: use the event "PreDamageEvent Becomes Equal to <any value>". Whether you just want to use one monolithic trigger for all damage modification like I do in the demo map, or use the different modifier events here, is up to you.
  • To interact with damage after it's been factored for armor and resistances, use "ArmorDamageEvent Becomes Equal to <any value>". This is typically useful for custom shields. If you fully block or absorb DamageEventAmount (setting it to 0 or less), this event doesn't run.
  • To set the DamageEventType before dealing triggered Damage, use:
    - Set NextDamageType = DamageTypeWhatever
    - Unit - Cause...
  • You can modify the following variables from a "PreDamageEvent" trigger:
    • DamageEventAmount
    • DamageEventAttackT/DamageT/WeaponT/ArmorT/DefenseT
    • DamageEventArmorPierced
    • DamageEventType
    • DamageEventOverride - You can set this if you want to remind yourself not to modify the damage further. If you use the UNKOWN damage type from a Unit - Damage Target native or set "NextDamageType = DamageTypePure" before that function, damage modification is skipped.
  • To catch a unit the moment before it would die from damage, use LethalDamageEvent Becomes Equal to 1.00. Instead of modifying the DamageEventAmount here, modify LethalDamageHP to let the system know how much life to keep the unit alive with. Or you can just reference that LethalDamageHP value in order to know how much "overkill" damage was dealt.
  • To catch a unit as soon as the damage is applied against its Hit Points, use AfterDamageEvent Equal to 1.00.
  • Usage of the "<Event> becomes EQUAL to <value>" can be extrapolated as per the below:
    • EQUAL works as it always has - will run for any damage.
    • NOT EQUAL only runs for code damage.
    • LESS THAN only runs for damage from attacks but not coded attacks.
    • LESS THAN OR EQUAL only runs for melee attacks but not coded attacks.
    • GREATER THAN OR EQUAL only runs for ranged attacks but not coded attacks.
    • GREATER THAN only runs for Spell damage but not coded spell damage.

  • Damage Engine 5 and higher requires the latest Warcraft 3 patch (currently 1.32).
  • For people who did not update their game to Reforged / have not updated to Reforged yet. Alternatively, adding the below JASS script to the top of the DamageEngine library SHOULD enable it to work too:

    JASS:
    function BlzGetEventIsAttack takes nothing returns boolean
         return BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL
    endfunction
  • I have created a Pastebin for all information pertaining to Damage Engine 3.8, including the link to download it, via: Damage Engine 3.8.0.0 | HIVE.
  • As of 20 June 2020, JNGP users who are still on WarCraft 3 1.26 can benefit from Damage Engine 3A, which integrates many of the design choices added in various stages of Damage Engine 5. This is a special update which only requires the JASS script be replaced (no new GUI variables added). Can be found here: Damage Engine 3A.0.0.0 and 3.8.0.0 | HIVE

1
Unit attacks or casts a spell. The pure damage is assessed at this point - damage dice, evasion, critical strike. There is currently no event to affect or modify these at source.
→ →
2
The projectile or weapon hits the target unit
→ →
3
EVENT_UNIT_DAMAGING is fired before any modifications to the damage are made.
→ ↓
↓ ←
6
User changes to DamageEventAmount and/or to DamageEventDamageT/AttackT/WeaponT are saved into the WC3 engine.
← ←
5
If any recursive damage is detected from any of those events, it is postponed and the current damage will continue first.
← ←
4
Damage Engine deploys the PreDamageEvent
7
WarCraft 3 processes the user damage.
→ →
8
WarCraft 3 then distributes the user damage into Spirit Link (before armor)
→ →
9
WarCraft 3 runs any interrupting events, such as spirit link or defensive damage like Thorns Aura
→ ↓
↓ ←
12
The EVENT_UNIT_DAMAGED event runs. This is the original event we have always had access to. Damage Engine will either keep using the original variables from the DAMAGING event, or if there was Spirit Link/defensive damage it will freshly retrieve the event values from WC3 and only retain DamageEventPrevAmt.
← ←
11
Once any potential recursive WarCraft 3 damage is processed, the armor/mana shield modifications to the damage are performed.
← ←
10
If such events such as Spirit Link were detected, they fire their own EVENT_UNIT_DAMAGING & EVENT_UNIT_DAMAGED, and DamageEngine processes it along the way for the user.
13
If the damage is above zero, ArmorDamageEvent will run. If any recursive damage is detected, it is postponed.
→ →
14
The user can make modification to the damage here with the after-damage amount.
→ →
15
The user can access DamageEventPrevAmount if they want to know the damage amount before user changes/WarCraft 3 changes.
→ ↓
↓ ←
18
The user can specify whether or not to change LethalDamageHP in order to stop the unit from dying.
← ←
17
If the damage is still above zero, check if the damage is lethal. If so, run LethalDamageEvent 1.00. If any recursive damage is detected, it is postponed.
← ←
16
If the user wants the value that DamageEngine used to have with DamageEventPrevAmt (after WarCraft 3 processing but before user changes) they multiply DamageEventPrevAmt x DamageScalingWC3.
19
Once all modification is done, run OnDamageEvent. If any recursive damage is detected, it is postponed.
→ →
20
After a new damage instance is detected, or the 0.00 timer expires, run AfterDamageEvent. If any recursive damage is detected, it is postponed.
→ →
21
Run all potential recursive Unit - Damage Target function calls in chronological order (first in, first out).

How to install Damage Engine:
  1. Use WarCraft 3 Version 1.32
  2. If you're upgrading from 3.8 or prior, please delete the entire "Damage Engine" category from your map.
  3. Copy & Paste the Damage Engine category from the attached map to your own map.
How do I upgrade to the latest Damage Engine?
- Depending on the complexity, you'll either need to re-copy the Damage Engine category or just the Damage Engine script. Generally, you can use this as a guide:

  • Damage Engine _._._.x - only requires copying of the JASS script
  • Damage Engine _._.x._ - generally only needs copying of the JASS script, but read the patch notes in case there are changes you may want to make to your own code in order to utilize the changes.
  • Damage Engine _.x._._ - delete your current Damage Engine category or manually add the missing variables to your Variable Editor, and update your JASS script.
  • Damage Engine x._._._ - this occurs very infrequently. Typically requires changes to the way Damage Engine needs to be installed and used.

Notes about upgrading from Damage Engine 4.0 or prior:
  • Revert any custom Object Editor changes made (such as Life Drain reversal, Spell Damage Reduction inversion, etc).
  • You can safely delete the custom spells "Spell Damage Detection" and "Cheat Death Ability"
  • You can delete the Unit Indexer trigger if you do not use it or would prefer to use a different indexer.
  • You should delete any "Mana Shield fix" or "Finger of Death fix" you may have imported, as well as revert any Object Editor data that was required to make it work, as these are no longer needed.
  • !!!DEPRECATED FEATURE!!! As of 5.4, do not bother to take any recursive damage mitigation - Damage Engine will now handle all of that for you!
  • !!!DEPRECATED FEATURE!!! Do not use "ClearDamageEvent" - it does nothing. Just delete it.
  • !!!DEPRECATED FEATURE!!! Do not use "NextDamageOverride" - set NextDamageType = DamageTypePure instead.
  • !!!CHANGED FEATURE!!! If the system detects code damage and the user did not specify it as any particular DamageEvenType, the system will assign it DamageTypeCode automatically.
  • !!!CHANGED FEATURE!!! DamageModifierEvent 1.00-3.00 now run prior to armor reduction. This enables the user to modify the damage before armor and resistance changes are applied - also before Mana Shield and Anti-Magic shell kick in, so no more need for special triggers.


  • Q: Why am I getting a bunch of 'trigger was disabled' errors when I save my map?
  • A: This issue is not unique to Damage Engine, but to all vJass resources. Blizzard has taken the very confusing decision to make JassHelper 'disabled' by default, meaning that every new map that uses a vJass resource has to manually enable JassHelper. Please see this thread if you want to know where to find the Enable JassHelper option.
    .
  • Q: How can I detect when a unit gets damaged?
  • A: Create a trigger with the event: "Game - Value of Real Variable <DamageEvent> becomes Equal to 1.00".
    • Use the following custom variables to reference the event responses:
      • DamageEventSource - the unit dealing the damage
      • DamageEventTarget - the unit getting damaged
      • DamageEventAmount - how much damage is being dealt
      • DamageEventAttackT - which attack type was used by DamageEventSource to damage DamageEventTarget
      • DamageEventDamageT - which damage type was used to damage the target (ie. UNIVERSAL for ultimate damage, or NORMAL for damage that gets reduced by armor).
      • DamageEventDefenseT - which defense type does the target unit have (ie. Hero, Fortified, Unarmored).
      • DamageEventArmorT - which armor type does the target unit have (ie. Flesh, Ethereal, Stone).
      • DamageEventPrevAmt - what the value of the damage was before being modified by armor, ethereal/item bonuses or user changes.
        .
  • Q: How do I modify the damage that is dealt to a unit?
  • A: Create a trigger with the event: "Game - Value of Real Variable <PreDamageEvent> becomes Equal to <any value>".
    • You can change the following variables to affect the damage that will be dealt:
      • DamageEventAmount - how much damage will be dealt (before armor reductions)
      • DamageEventAttackT - which attack type will be used by DamageEventSource to damage DamageEventTarget
      • DamageEventDamageT - which damage type will be used to damage the target.
      • DamageEventDefenseT - which defense type should the target unit have during this attack.
      • DamageEventArmorT - which armor type should the target unit have during this attack.
      • DamageEventArmorPierced - how much armor value to ignore when dealing this damage (applies to DAMAGE_TYPE_NORMAL only, otherwise all armor is ignored).
        .

  • Q: How do I deal Pure damage to a unit (bypassing armor/skipping user modification)?
  • A: Use the following actions:
    • Set NextDamageType = DamageTypePure
    • Unit - Cause Source to damage Target for Amount using attack type Spells and damage type Universal
      .

  • Q: How do I protect against recursive damage?
  • A: Damage Engine 5.4 and above is completely recursion-proof, using vJass hooks to detect registered Damage Event triggers.
    .

  • Q: I've been using <insert Damage system here>. Can I use those with Damage Engine?
  • A: Better - Damage Engine has integrated cross-compatibility with all other approved Damage systems and even the major ones from outside of Hiveworkhop.
    .

  • Q: Can I cause an attack to 'Miss' its target?
  • A: Kind of. Ranged attacks will still explode on the target, and on-hit effects will still apply, but you can do the following:
    • Use the event "PreDamageEvent becomes Equal to 1.00"
    • Use the following actions:
      • Set DamageEventAmount = 0.00
      • Set DamageEventArmorT = ARMOR_TYPE_NONE
      • Set DamageEventWeaponT = WEAPON_TYPE_NONE
    • Setting the weapon type and armor type to none like the above will stop any on-hit sounds from playing. When the Peasant attacks in the demo map, this is the trick I'm using. Instead of saying "MISSED!" I have the Floating Text saying "FAIL!" because - again - it's not exactly a "miss".

Thank you to Blizzard for continuing to work on this amazing game to give us awesome new natives that have the best possible control over incoming damage. Damage Engine brings that power to GUI. Also, a very special thank you to @KILLCIDE for getting me motivated to start up the 5.0 project in the first place.

Thank you to the users of this system who have helped me mold this project into the stable, powerful form it is in today!

For versions 3.8 and prior:

Thank you @looking_for_help for finding the spell damage detection method used in Damage Engine 3 - it was the greatest find since the undefend bug.

Thanks to Jesus4Lyf and @Nestharus for building the inspiration that originally led me to create DamageEngine.

Thank you Wietlol and looking_for_help for challenging me on this project to integrate solutions to problems I had not been made aware of, such as the importance of an After-Damage Event.

Thanks to @Spellbound for several crucial bug reports.


Damage Engine Config

Damage Engine vJass

Damage Engine Lua

Changelog


  • Damage Engine Config
    • Events
      • Map initialization
      • Game - DamageModifierEvent becomes Greater than 0.00
      • Game - LethalDamageEvent becomes Less than or equal to 0.00
      • Game - DamageEvent becomes Not equal to 0.00
      • Game - AfterDamageEvent becomes Less than 0.00
      • Game - AOEDamageEvent becomes Greater than or equal to 0.00
      • Game - SourceDamageEvent becomes Equal to 0.00
      • Game - PreDamageEvent becomes Equal to 0.00
      • Game - ArmorDamageEvent becomes Equal to 0.00
      • Game - ZeroDamageEvent becomes Equal to 0.00
    • Conditions
    • Actions
      • -------- You can add extra classifications here if you want to differentiate between your triggered damage --------
      • -------- Use DamageTypeExplosive (or any negative value damage type) if you want a unit killed by that damage to explode --------
      • -------- - --------
      • -------- The pre-defined type Code might be set by Damage Engine if Unit - Damage Target is detected and the user didn't define a type of their own. --------
      • -------- "Pure" is especially important because it overrides both the Damage Engine as well as WarCraft 3 damage modification. --------
      • -------- I therefore gave the user "Explosive Pure" in case one wants to combine the functionality of the two. --------
      • -------- - --------
      • Set VariableSet DamageTypePureExplosive = -2
      • Set VariableSet DamageTypeExplosive = -1
      • Set VariableSet DamageTypeCode = 1
      • Set VariableSet DamageTypePure = 2
      • -------- - --------
      • Set VariableSet DamageTypeHeal = 3
      • Set VariableSet DamageTypeBlocked = 4
      • Set VariableSet DamageTypeReduced = 5
      • -------- - --------
      • Set VariableSet DamageTypeCriticalStrike = 6
      • -------- - --------
      • Custom script: call DamageEngine_DebugStr()

BribeFromTheHive/DamageEngine

Lua 1.0.2.3 - Fixed to match adjustment made in vJass version 5.4.2.3.
Lua 1.0.2.2 - Fixed to match adjustment made in vJass version 5.4.2.2.
Lua 1.0.2.1 - Fixed to match adjustment made in vJass version 5.4.2.1.
Lua 1.0.2.0 - Added support for Lua Fast Triggers ([Lua] Ridiculously Fast Triggers). Fixed an issue where the AfterDamageEvent wasn't always timed the correct way like it was in the JASS verion.
Lua 1.0.1.0 - Fixed encapsulation issue and recursion issue with DamageEngine_inception. Now hooks UnitDamageTarget.
Lua 1.0.0.0 - Release based on Damage Engine 5.4.2.0

5.9.0.0 - Added the following clearer event names to make things less confusing for new users:
  • PreDamageEvent - can be used in place of DamageModifierEvent pre-armor modification
  • ArmorDamageEvent - can be used in place of DamageModifierEvent post-armor modification
  • OnDamageEvent - can be used instead of a non-zero DamageEvent
  • ZeroDamageEvent - can be used instead of a zero damage DamageEvent
  • SourceDamageEvent - runs at the same time as AOEDamageEvent, but doesn't need to hit multiple units.
Added "DamageFilterRunChance" - odds for the trigger to BE run (works inversely to DamageFilterFailChance).
Shortened the Configuration trigger so that it focuses primarily on what the user can modify.
Organized all variables into categories to help users better understand what does what.
Installation or updating from a previous version will require re-copying the entire Damage Engine folder.
Updated the demo map's text tag production to include a new custom update for ArcingTextTag, thanks to @Ugabunda and @Kusanagi Kuro.
Side note - the Demo Map's triggers have been heavily cleaned up and will now no longer cause crashes when being imported into a new map.
5.8.0.0 -
  • Added a new functionality to the AOEDamageEvent, which allows it to fire even when only one unit is hit, if the AOEDamageEvent is registered as "Not Equal" instead of "Equal to". This is useful in a very specific scenario where an AfterDamageEvent may not be able to be relied upon to properly deallocate data from a running instance that needs to be able to function when multiple units are hit, but also needs to not fail when only one unit is hit.
  • Added additional filters for GUI users to avoid using trigger conditions in even more scenarios:
    • DamageFilterSource/TargetI (has item)
    • DamageFilterSource/TargetA (has ability)
    • DamageFilterSource/TargetC (has a certain classification, like hero or Tauren)
    • DamageFilterFailChance - odds for the trigger to not be run (ideal for critical strike or evasion)
  • Provided a way for GUI to un-register a Damage Event by setting "RemoveDamageEvent" to true from within their trigger's actions.
  • Moved the CreateTimer/CreateTrigger/CreateGroup calls that had been included in the globals block down to the onInit block as per request of @Ricola3D
  • Minor performance improvements thanks to @BLOKKADE
5.7.1.2 - Fixed an issue with armor penetration sometimes bugging out.
5.7.1.1 - Fixed an issue with the eventFilter not retaining its value during an AOE event. Fixed an issue with eventFilter when USE_MELEE_RANGE was set to false. Both issues reported by @lolreported
5.7.1.0 -
I have fixed several potential points of failure reported by multiple users that stopped Damage Engine from working for the rest of the game. In any case there should no longer be ANY SITUATION where Damage Engine just "stops working", even in ways I can't predict. The most likely issue was with DamageScalingWC3/User having possible 0-division errors.​
Fixed the "configured" issue reported by @lolreported where manual configuration didn't work unless the damage and attack type checks were initialized tto -1.​
In addition to these fixed bugs, I have added several more static ifs so that advanced users have more control over getting rid of features they might not care about.​

5.7.0.3 - Fixed the issue reported by @KitsuneTailsPrower wherein the DamageInterface extension wasn't working. This needed to be fixed both in the Damage Engine source as well as the DamageInterface plugin itself.
5.7.0.2 - Fixed the issue reported by @Wazzz where the armor reduction wasn't taken into consideration. Actually there was a much bigger flaw that I had overlooked that was prompting this.
5.7.0.1 - Improved the logic of the 0 damage event.
5.7.0.0:

  • Usage of the "DamageEvent becomes EQUAL to 1.00" now can be extrapolated further than ever before:
    • EQUAL works as it always has.
    • NOT EQUAL only runs for code damage.
    • LESS THAN only runs for damage from attacks.
    • LESS THAN OR EQUAL only runs for melee attacks.
    • GREATER THAN OR EQUAL only runs for ranged attacks.
    • GREATER THAN only runs for Spell damage.
  • Fully adapted Damage Engine to work with every other major DDS
  • Rewrote the internal script to use vJass structs: Damage and DamageTrigger.
  • Changed some of the vJass API. You can find the API listed in the Damage Engine trigger. Notably:
    • I removed UnitDamageTargetEx. You can replace this with Damage.apply.
      • The reason for this change is because of cross-compatibility. Rising Dusk's IDDS uses a different set of parameters for this function, so I removed it from my library to allow this to work seamlessly.
    • TriggerRegisterDamageEvent -> TriggerRegisterDamageEngine
    • RegisterDamageEvent -> RegisterDamageEngine
      • The reason for the above two changes is, like for UnitDamageTargetEx, because Rising Dusk's IDDS uses them. I don't want to make the same mistake I did with Table's API and preferred to walk back my API choices before things got out of hand again.
  • Various performance tweaks, bug fixes and re-commenting of variables.
  • Recursive damage is now processed more intelligently and possibly be more performance-friendly.
  • Adapted the cross-compatibility libraries for Weep and LFH's systems to use textmacros to insert themselves into Damage Engine (this might have been in the 5.6 update, but these two updates were both tailored to achieve similar goals).
5.6.2.0 - Fixed a bug with Damage modification and laid groundwork for a bunch of optional vJass compatibility addons.
5.6.1.0 - Patchwork on Melee/Ranged detection, recursion tracking. Also added the ability to modify damage from a DamageEvent to make it easier on beginners.
5.6.0.1 - Fixed an issue where the DamageEventType for recursive damage that used NextDamageType would always default to DamageTypeCode.
5.6.0.0
Rewrote a significant amount of the internal code so that struct and GUI syntax both work - and are synchronized with each other. vJass-exclusive users can disable the GUI synchronization if they don't use any GUI damage events in their maps.​
Four new variables must be added to your variable editor:​
  • boolean NextDamageIsAttack
  • boolean NextDamageIsMelee
  • boolean NextDamageIsRanged
  • integer NextDamageWeaponT
Struct syntax should not need too much introduction - Damage.index is the triggering event ID, and the syntax is Damage.index.amount/source/target/etc. To initialize a JASS damage event, use:​
JASS:
function RegisterDamageEvent takes code c, string eventName, real value returns nothing
The string is simplified: "Modifier", "" (for simple DamageEvent), "After", "Lethal", "AOE"​
Finally, a neat QOL improvement is that I have assigned weight to each of the events. When registering an event, you can include numbers different than 1, 2, 3 or 4 (for modification) or just the plain "1" for the others. Now you can set your own sequencing and use 1.5, 3.14 or even -1 to give an even more extreme priority to something. Lower numbers run first, higher ones last.​
5.5.0.0 - Added support for the new native BlzGetEventIsAttack via "IsDamageAttack". Also updated the Config trigger to make importing a bit easier
5.4.2.3 - Fixed a mis-flag of the IsDamageSpell value when not being actual spell damage.
5.4.2.2 - Actually fixed the Cold Arrows issue (division by 0.00001 or something ...somehow... either way, it is fixed).
5.4.2.1 - A fix which should hopefully quell the recent reports of damage events failing in complex situations involving Cold Arrows.
5.4.2.0 - A ton of fixes to repair the issues plaguing Sunken City and anyone else who might be pushing this engine well beyond what I imagined it would be used for. Also added a couple of variables intended to be used to ricochet damage: CONVERTED_ATTACK_TYPE and CONVERTED_DAMAGE_TYPE. Check the demo map for how they can be used in a Unit - Damage Target action.
5.4.1.0 - The "Inception" update. Advanced users can interact with Damage Engine via custom script to set DamageEvent_inception = true to allow their damage to potentially go recursive (to a fixed extent).
5.4.0.1 - Hotfixed that modifiers 2 and 3 weren't running.
5.4.0.0 - By using an innovative approach of hooking TriggerRegisterVariableEvent, I've permanently eliminated all risks of recursion in the engine.
5.3.0.1 - Fixed unexpected behavior with DamageTypePure when it is affected by Anti-Magic Shell or Mana Shield. DamageTypePure now no longer ignores DamageScalingWC3.
5.3.0.0 - Fixed an issue with AfterDamageEvent sometimes being delayed. Added DamageScalingUser to track the ratio of user modified damage, as well as DamageEventArmorPierced which allows the user to define how much armor to ignore when working with DAMAGE_TYPE_NORMAL.
5.2.0.1 - Fixed an issue where with the final unit in a Spirit Link chain or the retaliating damage of Thorns/Carapace would not deploy an AfterDamageEvent. Also fixed an issue where AfterDamageEvent would still fire from DAMAGE_TYPE_UNKNOWN if it was greater than 0. Simply copy over the JASS from this post in order to update your own implementation.
5.2.0.0

  • Now features DamageEventArmorT and DamageEventDefenseT, which pull from the target unit's Object Editor data in order to allow you more complete access to detect and even MODIFY those defenses. Changes must be made in a DamageModifierEvent, and they are reverted as soon as armor is calculated.
  • Re-introduced AfterDamageEvent, which is the moment after the unit's life is changed, but before any recursive damage has run.

5.1.3.1 - Bug fixes and performance improvements. No, really. Fixed an issue with the DAMAGED event running even though it was turned off (who would've guessed that?)
5.1.3.0 - Engine re-tooling to improve accuracy and get Spirit Link/Defensive damage out of hardcoded mode - it will now work even in circumstances I haven't tested for (in case those weren't the only issues). Also fixed the Is Unit Moving resource.
5.1.2.1 - Fixed an issue with Spiked Carapace and Thorns Aura where the melee attacker would not get recorded correctly. This required the same fix as I needed to apply for the initial target in a spirit link chain.
5.1.2.0 - Tweaked recursion and fixed a few bugs. Re-introduced the zero damage event now that patch 1.31.1 brought it back.
5.1.1.1 - Minor tweak to Spirit Link in rare situation.
5.1.1.0 - Fixed issues related to Spirit Link. Now works intuitively as expected.
5.1.0.0 - Crazy improvements - check out the details in "How to upgrade from Damage Engine 4.0 or earlier" and "How to use Damage Engine 5.1"
5.0.0.0 - Oh man. Where do I even start?
  • You can now set/compare DamageEventDamageT, DamageEventAttackT or DamageEventWeaponT
  • No longer needs Unit Indexer nor any custom Object Editor data nor modifications.
  • Requires WarCraft 3 1.31 or higher.
  • Changed vanilla JASS code to vJass to clean up a lot of things such as making certain variables private.
  • Look for more details in "How to upgrade from Damage Engine 4.0 or earlier"

4.0.0.0 - Never officially released, but was the first iteration which used SetEventDamage.
For 3.8.0.0 and prior, see: Damage Engine 3.8.0.0 | HIVE


Cross-Compatibility:

BribeFromTheHive/DamageEngine

Keywords:unit indexer, damage detection, damage event, weep, nestharus, looking_for_help, EVENT_PLAYER_UNIT_DAMAGED, damage engine, spell, physical, EVENT_PLAYER_UNIT_DAMAGED, attacktype, damagetype, weapontype, armortype, defensetype, BlzSetEventDamage
Previews
Contents

Damage Engine Demo Map (Map)

Lua Damage Engine 2.0.0.0 (Map)

Reviews
23:20, 11th Jan 2015, BPower Criticism: On the one hand Damage Engine offers extremely high utility to every spell, system and map maker independent of the coding style ( GUI, JASS, vJass, .... ) on the other hand it's very easy to import and...
Level 12
Joined
May 16, 2020
Messages
660
Thank you! After some testing: Cleave deals ENHANCED damage. That's why it didn't work, as "less than" filters it out.

But another question:
Is "IsDamageAttack" essentially the same as the combination of these 2 conditions:
  • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
  • DamageEventAttackT Equal to ATTACK_TYPE_HERO or ATTACK_TYPE_NORMAL
?


And another thing:
I'm thinking of creating a triggered Cleaving Attack and Critical Strike. If the Critical Strike works based on SetDamage to 2x DamageAmount, would the Cleave also recognize that the damage was adjusted?

I'm wondering this, since both the Crit and the Cleave damage would happen at the same time (in 2 different triggers). So I'm not sure if adjusting the Damage dealt in one trigger would also count for the other. Hope you know what I mean - if not I can elaborate more.

Edit: As feared, the two triggers do not interchange information. To illustrate:
  1. Unit deals damage to an enemy
  2. Both the Cleave and Critical Strike Trigger register the damage event
  3. The Crit trigger adjusts the DamageEventAmount to 2x
  4. However, the Cleave trigger still calculates the cleave amount (40%) based on the INITIAL DamageEvent Amount
--> Can I make the Crit trigger "share" the new DamageEventAmount with the Cleave trigger, so that the Cleave trigger deals more damage when a crit occurs?


Edit: Sorry another question. When multiple targets are hit by Cleave, the (custom) lifesteal ability creates a specialeffect for every unit hit. Can I make it so that it still lifesteals from multiple units, but only creates 1 specialeffect?
(kinda like the heal text tag from your Heal system which creates only 1 text tag instead of multiple ones)
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Thank you! After some testing: Cleave deals ENHANCED damage. That's why it didn't work, as "less than" filters it out.

But another question:
Is "IsDamageAttack" essentially the same as the combination of these 2 conditions:
  • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
  • DamageEventAttackT Equal to ATTACK_TYPE_HERO or ATTACK_TYPE_NORMAL
?


And another thing:
I'm thinking of creating a triggered Cleaving Attack and Critical Strike. If the Critical Strike works based on SetDamage to 2x DamageAmount, would the Cleave also recognize that the damage was adjusted?

I'm wondering this, since both the Crit and the Cleave damage would happen at the same time (in 2 different triggers). So I'm not sure if adjusting the Damage dealt in one trigger would also count for the other. Hope you know what I mean - if not I can elaborate more.

Edit: As feared, the two triggers do not interchange information. To illustrate:
  1. Unit deals damage to an enemy
  2. Both the Cleave and Critical Strike Trigger register the damage event
  3. The Crit trigger adjusts the DamageEventAmount to 2x
  4. However, the Cleave trigger still calculates the cleave amount (40%) based on the INITIAL DamageEvent Amount
--> Can I make the Crit trigger "share" the new DamageEventAmount with the Cleave trigger, so that the Cleave trigger deals more damage when a crit occurs?


Edit: Sorry another question. When multiple targets are hit by Cleave, the (custom) lifesteal ability creates a specialeffect for every unit hit. Can I make it so that it still lifesteals from multiple units, but only creates 1 specialeffect?
(kinda like the heal text tag from your Heal system which creates only 1 text tag instead of multiple ones)
I haven't tested IsDamageAttack all that extensively, but it shouldn't have anything to do with the damage type nor attack type. It's just a boolean passed either through UnitDamageTarget or through the callback of BlzGetDamageIsAttack if such a thing is determined by WC3 to be an attack. Like I said, I'm not sure of all the total circumstances would proc it (I didn't test it like I did the damage types), but I wanted to make sure to enable the API to the user regardless of how well understood it was to me.

Now, the triggered damage might not be interacting with each other because you have the wrong trigger priority. If you are doing addition which you want to apply in a multiplicative manner (that is to say, to ensure that if you multiply the damage earlier in the sequence, you want to apply that earlier multiplication to your new additive bonus) there are two ways to achieve this.

One is to change the priority of your triggers so that the additions are done first, by changing the event number to be a lower value (0.5 runs before 1.0 which runs before 2.0 and so on). The second method is to apply the addition via "value X udg_DamageScalingUser". So instead of DamageEventAmount + 25.00 you would do DamageEventAmount + (25.00 x DamageScalingUser).

To accomplish the one special effect while ignoring the others would be done via checking if DamageEventAOE is Equal to 1. If it's greater than 1, then that generally means that you're no longer on the first target hit. There is a special circumstance which could occur, however, and that's if there was some kind of WarCraft 3-recursive crap going on like Spiked Carapace or Thorns Aura or Spirit Link. In such circumstances, you can check if DamageEventTarget is equal to EnhancedDamageTarget.

However, I think with cleaving attack, you said you're relying on WarCraft 3 to process the cleave for you. I am unsure of how WarCraft 3 processes the cleaving attack damage output - like if this is processed pre-UNIT_EVENT_DAMAGING like Critical Strike, but if so then you're kind of out of luck on relying on WarCraft 3 to do the modification for you.
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Thank you Bribe. I'm kinda at my wits end though. Would you mind looking at my map if you find the time please?

(sorry wall of text)



Problem 1:
How do I change the "event number to a lower value" to change trigger priority?
I did some research and some posts say that the time when a "trigger was created" specifies the trigger priority (triggers created earlier have higher priority, see What is the trigger execute order in Warcraft 3?). But it seems you know a better method...?

To show you exactly what I want, here an illustration:

1621269753401.png


[if you want to test]:
In my map there is a hero in the middle (Phantom Assassin). She has 1 triggered crit ability already learned (200% crit) and a triggered 15% physical lifesteal item in the inventory. In front of her is an item ("Battle Fury") that has 2 triggered abilities: a) increases all physical damage to non-heroes by 18, and b) a Cleave ability that should spread 40% of the adjusted damage (=after the fixed damage and critical strike damage) in a 400 AoE.

However, you will see that a) the +18 fixed damage is increased sequentially for every single cleaved target, and b) critical strike damage increases are not considered... Also, I tried to simulate what I wrote in red above with DamageEventPrevAmt, but for some reason this messes up the damage completely (crits go from 600 to 80).

Involved triggers:
  • Item Triggers > Battle Fury > Battle Fury
  • Item Triggers > Mask of Death > Mask of Death
  • Phantom Assassin > Coup de Grace



Problem 2:
To accomplish the one special effect while ignoring the others would be done via checking if DamageEventAOE is Equal to 1. If it's greater than 1, then that generally means that you're no longer on the first target hit. There is a special circumstance which could occur, however, and that's if there was some kind of WarCraft 3-recursive crap going on like Spiked Carapace or Thorns Aura or Spirit Link. In such circumstances, you can check if DamageEventTarget is equal to EnhancedDamageTarget.

I added the DamageEventAOE is EQUAL to 1 as condition, but then the effect stops completely...

Involved triggers:
  • Item Triggers > Mask of Death > Mask of Death



Problem 3:
After some experimentation, it turns out that IsDamageSpell can recognize spells with AttackT = NORMAL and DamageT = NORMAL. I expected it to only work for AttackT = spells, but apparently it recognizes ALL damage from the action of casting a spell. However, the DamageEventAmount is always 0 (even though damage is done). Do you know if this is a bug?

[if you want to test]:
Select the hero in the top lane, and start casting "Q" --> It will deal normal/normal damage, pass through the "IsDamageSpell" check, but have a 0 DamageEventAmount.

Involved triggers:
  • Item Triggers > Eternal Shroud > Eternal Shroud
  • Bristleback > Quill Spray > QuillSpray Periodic
 

Attachments

  • Land of Legends v0.1.96.w3m
    80.7 MB · Views: 127
Level 3
Joined
Nov 26, 2009
Messages
35
How to make this spell work with this system? Let's say i want to modifiy this 500 damage to get a % damage increase and able to crit.
vJASS:
function Trig_WolfGirlDash_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A005'
endfunction
function WolfGirlQ_Clear takes nothing returns nothing
    local timer timerOne = GetExpiredTimer()
    local unit caster = LoadUnitHandleBJ(1, GetHandleIdBJ(timerOne), udg_HeroesHashTable)
    local unit dummy
    local integer playerId = GetConvertedPlayerId(GetOwningPlayer(caster))
        loop
            set dummy = FirstOfGroup(udg_WolfGirlQDummyGroup[playerId])
            exitwhen dummy == null
            call RemoveUnit(dummy)
            call GroupRemoveUnit(udg_WolfGirlQDummyGroup[playerId], dummy)
        endloop
    call ResetUnitAnimation( caster )
    call FlushChildHashtableBJ(GetHandleIdBJ(timerOne), udg_HeroesHashTable)       
    set caster = null
    call GroupClear(udg_WolfGirlQDummyGroup[playerId])
    call PauseTimer(timerOne)
    call DestroyTimer(timerOne)
    set timerOne = null
endfunction
              

function Trig_WolfDashes takes nothing returns nothing
    local timer timerOne = GetExpiredTimer()
    local unit caster = LoadUnitHandle( udg_HeroesHashTable, GetHandleId(timerOne), 1)
    local unit target
    local real range = 100
    local real aoe = 250
    local real angle = LoadRealBJ(2, GetHandleIdBJ(timerOne), udg_HeroesHashTable)
    local real distance = LoadRealBJ(3, GetHandleIdBJ(timerOne), udg_HeroesHashTable)
    local location movePointSecond
    local real casterY = GetUnitY(caster)
    local real casterX = GetUnitX(caster)
    local group damageTo = CreateGroup()
    local location movePoint
    local integer playerId = GetConvertedPlayerId(GetOwningPlayer(caster))
     if (distance > 0.00 and IsUnitAliveBJ(caster)) then
             set movePointSecond = GetUnitLoc(caster)
             set movePoint = PolarProjectionBJ(movePointSecond, range, angle)
             call CreateNUnitsAtLoc( 1, 'h007', GetOwningPlayer(caster), movePoint, angle )
             call GroupAddUnit(udg_WolfGirlQDummyGroup[playerId], GetLastCreatedUnit())
             call SetUnitPositionLoc( caster, movePoint )
             call RemoveLocation(movePointSecond)
             call RemoveLocation(movePoint)
             set  distance = distance - range
             call SaveRealBJ(distance, 3, GetHandleIdBJ(timerOne), udg_HeroesHashTable )
             call GroupEnumUnitsInRange(damageTo ,casterX, casterY, aoe,  null)
            loop
            set target = FirstOfGroup(damageTo)
            exitwhen target == null
                if ( not IsUnitAlly(target, GetOwningPlayer(caster)) and  not IsUnitInGroup(target, udg_WolfGirlQGroup[playerId])) then
                    call UnitDamageTargetBJ( caster, target, 500, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
                    call GroupAddUnit(udg_WolfGirlQGroup[playerId],target)
                    call GroupRemoveUnit(damageTo ,target)
                else
                    call GroupRemoveUnit(damageTo ,target)
                endif
            endloop
        else
            set movePointSecond = GetUnitLoc(caster)
            call CreateNUnitsAtLoc( 1, 'h008', GetOwningPlayer(caster), movePointSecond, angle )
            call GroupAddUnit(udg_WolfGirlQDummyGroup[playerId], GetLastCreatedUnit())
            call GroupEnumUnitsInRange(damageTo ,casterX, casterY, aoe,  null)
            loop
            set target = FirstOfGroup(damageTo)
            exitwhen target == null
                if (IsUnitAlly(target, GetOwningPlayer(caster)) == false) then
                    call UnitDamageTargetBJ( caster, target, 500, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
                    call GroupRemoveUnit(damageTo ,target)
                else
                    call GroupRemoveUnit(damageTo ,target)
                endif
            endloop
            call PauseUnitBJ( false, caster )
            call UnitRemoveAbility(caster, 'A006' )
            call TimerStart(timerOne, 0.3, false, function WolfGirlQ_Clear  )
            call GroupClear(udg_WolfGirlQGroup[playerId])
        endif
        set caster = null
        call DestroyGroup(damageTo)
endfunction

function Trig_WolfGirlDash_Actions takes nothing returns nothing
    local unit caster = GetSpellAbilityUnit()
    local location casterPoint = GetUnitLoc(caster)
    local location targetPoint = GetSpellTargetLoc()
    local real angle =  AngleBetweenPoints(casterPoint, targetPoint)
    local timer timerOne = CreateTimer()
    local real distance = DistanceBetweenPoints(casterPoint, targetPoint)
    local integer playerId = GetConvertedPlayerId(GetOwningPlayer(caster))
    call UnitAddAbility(caster, 'A006')
    call GroupClear(udg_WolfGirlQGroup[playerId])
    call SaveUnitHandle( udg_HeroesHashTable, GetHandleId(timerOne), 1, caster )
    call SaveRealBJ( angle, 2, GetHandleIdBJ(timerOne), udg_HeroesHashTable )
    call SaveRealBJ( distance, 3, GetHandleIdBJ(timerOne), udg_HeroesHashTable )
    call PauseUnitBJ( true, caster )
    call SetUnitAnimationByIndex ( caster , 18 )
    call TimerStart(timerOne, 0.02, true, function Trig_WolfDashes)
    call RemoveLocation(casterPoint)
    call RemoveLocation(targetPoint)
    set caster = null
endfunction


//===========================================================================
function InitTrig_WolfGirlDash takes nothing returns nothing
    set gg_trg_WolfGirlDash = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_WolfGirlDash, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_WolfGirlDash, Condition( function Trig_WolfGirlDash_Conditions ) )
    call TriggerAddAction( gg_trg_WolfGirlDash, function Trig_WolfGirlDash_Actions )
endfunction
 
Level 12
Joined
May 16, 2020
Messages
660
In the Config it says that I can define my own classifications.

How do I do this? Just add for example a variable "DamageTypePierce = 7" and then use it to recognize some specific damage?

And what is DamageTypeCriticalStrike? Also just to give a specific damage type (=triggered critical strike) a "label" which I can use further?


1623513650836.png
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
In the Config it says that I can define my own classifications.

How do I do this? Just add for example a variable "DamageTypePierce = 7" and then use it to recognize some specific damage?

And what is DamageTypeCriticalStrike? Also just to give a specific damage type (=triggered critical strike) a "label" which I can use further?


View attachment 381410
I've never used classifications before but I imagine you just Set a variable like "DamageEventType" to one of your defined classifications before applying damage. Then in the conditions of your DamageEvent trigger you can check for this -> If DamageEventType Equal to DamageTypeCriticalStrike -> Do stuff.

I may be wrong though but that's the only use case I can think of.
 
Last edited:

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
@DieShadow
So in your code you're dealing damage like this:
vJASS:
call UnitDamageTargetBJ( caster, target, 500, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )

You'll need to make a new trigger that detects damage using one of the desired DamageEvents in order to detect this damage.

The trigger will need some Conditions to determine that the source of this damage came from your dashing spell:
  • Events:
  • Game - DamageEvent becomes Equal to 1.00
  • Conditions:
  • DamageEventSource Equal to YourUnit
  • DamageEventAttackT Equal to ATTACK_TYPE_NORMAL
  • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
  • IsDamageCode Equal to True
  • Actions:
  • Set Variable DamageEventAmount = DamageEventAmount + whatever modifications you want to do to the damage dealt
  • // then you can add in some rng for crits to modify the damage even further / etc
So that Event runs whenever a unit takes damage and you use the Conditions to pinpoint the source of the damage. Once the conditions have been met, you use the DamageEventAmount variable to modify the damage dealt. Note that this is on the latest version of DamageEngine and older versions use the DamageModifierEvent and may be lacking some of these variables. There's a massive amount of information on this page and on other Hive posts so if you dig around you should be able to find answers to most if not all of your questions.

Keep in mind that this trigger could fire for other sources of damage that match these conditions. So if your unit deals coded damage (UnitDamageTargetBJ) with another spell that matches these conditions then that will cause the trigger to run as well. This means that it's very important that you have a Condition that can guarantee that the damage came from the dash ability. This can be handled in multiple ways, one of which is to use a global Boolean variable that is Set to True right before you call "UnitDamageTargetBJ" and set to False immediately after. The results would look like this:
vJASS:
set udg_IsDamageFromDash = true
call UnitDamageTargetBJ( caster, target, 500, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
set udg_IsDamageFromDash = false
  • Events:
  • Game - DamageEvent becomes Equal to 1.00
  • Conditions:
  • IsDamageFromDash Equal to True
  • Actions:
  • // do stuff
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
The beauty of this system and how simple it is just surprises me every time.

It indeed works perfectly. For example, I wanted to create some form of damage which can pierce evasion.

Hence I added this line to config:
  • Set VariableSet DamageTypePierced = 7
Then define a damage with this Type:
  • Events
    • Game - DamageModifierEvent becomes Equal to 0.00
  • Conditions
    • (DamageEventSource has an item of type Monkey King Bar) Equal to True
    • (DamageEventTarget is A structure) Equal to False
    • DamageEventAttackT Equal to ATTACK_TYPE_HERO
    • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
  • Actions
    • Set VariableSet DamageEventType = DamageTypePierced
...And then just filter:
  • Events
    • Game - DamageModifierEvent becomes Less than 0.50
  • Conditions
    • DamageEventType Equal to DamageTypePierced
  • Actions
    • Rest
 
Level 12
Joined
May 16, 2020
Messages
660
Would anyone have a good idea how how to prevent a triggered Critical Strike damage from stacking?

When having multiple sources of Critical Strike, and both trigger on the same attack, the Critical Strike with the higher multiplier takes priority, while the lower one does nothing.

I trigger my Crit the following way:

  • Events
    • Game - DamageModifierEvent becomes Equal to 1.10
  • Conditions
    • (Level of Ability for DamageEventSource) Greater than 0
    • (DamageEventTarget is A structure) Equal to False
    • DamageEventAttackT Equal to ATTACK_TYPE_HERO
    • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Random percentage) Less than or equal to 20.00
      • Then - Actions
        • Set VariableSet DamageEventAmount = (DamageEventAmount x 1.5)
      • Else - Actions

So for example, if the DamageEventSource has another ability (or item) that triggers the Crit at the same time for 3x the DamageEventAmount, I want to only deal 3x damage and ignore the 1.5x damage.

I tried to somehow "compare" the damages, but the problem is that as soon as I refer the DamageEventAmount and adjust it for example to 1.5x, I can no longer access the original DamagEventAmount to see if 3.0x is higher than 1.5x...
 
Level 12
Joined
May 16, 2020
Messages
660
I was hoping to use one of these events, as I think Bribe intended for them to be used exacty in this situation:
  • Detect when multiple targets were hit simultaneously via DamageEventAOE > 1.
  • Detect when the same unit was hit simultaneously by the same source via DamageEventLevel > 1.
However, for me DamageEventAOE and DamageEventLevel are 1 only the very first time a unit attacks a single enemy, and then stay 0 throughout the game (even when then attacking multiple enemies).
 
Level 12
Joined
May 16, 2020
Messages
660
Would someone know why the following trigger is not being recognized as doing AOE damage?
It picks every unit within 300 AOE and damages them, but Damage Engine does not recognize it being an area damage...

  • Soldier Deal AOE damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.20
    • Conditions
      • DamageEventAttackT Equal to ATTACK_TYPE_NORMAL
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
      • (Owner of DamageEventSource) Equal to Player 1 (Red)
    • Actions
      • Game - Display to (All players) the text: Soldier is attacking
      • Set VariableSet TempLoc = (Position of DamageEventTarget)
      • Unit Group - Pick every unit in (Units within 300.00 of TempLoc.) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Picked unit) Not equal to DamageEventTarget
            • Then - Actions
              • Unit - Cause DamageEventSource to damage (Picked unit), dealing 100.00 damage of attack type Spells and damage type Magic
            • Else - Actions

  • Soldier isAOE
    • Events
      • Game - AOEDamageEvent becomes Equal to 1.30
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (String(DamageEventAOE))


Here the above trigger within the test map:
 
Level 21
Joined
Dec 4, 2007
Messages
1,473
^ Isn't aoe damage something else:
  • Unit - Cause (Triggering unit) to damage circular area after 0.00 seconds of radius 500.00 at (Center of (Playable map area)), dealing 100.00 damage of attack type Spells and damage type Normal
because if you pick every single unit and deal damage, that's technically a per unit trigger, not areal
another areal damage would be splash/cleave from the object editor, but those possibly (not too sure here) do not take the primarily attacked unit into account
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Would someone know why the following trigger is not being recognized as doing AOE damage?
It picks every unit within 300 AOE and damages them, but Damage Engine does not recognize it being an area damage...

  • Soldier Deal AOE damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.20
    • Conditions
      • DamageEventAttackT Equal to ATTACK_TYPE_NORMAL
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
      • (Owner of DamageEventSource) Equal to Player 1 (Red)
    • Actions
      • Game - Display to (All players) the text: Soldier is attacking
      • Set VariableSet TempLoc = (Position of DamageEventTarget)
      • Unit Group - Pick every unit in (Units within 300.00 of TempLoc.) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Picked unit) Not equal to DamageEventTarget
            • Then - Actions
              • Unit - Cause DamageEventSource to damage (Picked unit), dealing 100.00 damage of attack type Spells and damage type Magic
            • Else - Actions

  • Soldier isAOE
    • Events
      • Game - AOEDamageEvent becomes Equal to 1.30
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (String(DamageEventAOE))


Here the above trigger within the test map:
Actually this is something I could incorporate into DamageEngine if the user sets a flag to indicate this as AOE, I would have to patch the engine in order to allow it to be detected as such.

Or, as the user above you suggested, using WarCraft 3’s built-in AOE damage function could also work. I’ve never tested it because it has crashed Macs all on its own and no one was using it, but it is worth exploring if not just for the sake of knowledge.
 
Level 12
Joined
May 16, 2020
Messages
660
Thank you both. Isn't the reason why no one is using the built-in AOE damage function because it gives you a lot less control on the targets to be damaged?

Actually this is something I could incorporate into DamageEngine if the user sets a flag to indicate this as AOE, I would have to patch the engine in order to allow it to be detected as such.

Would this be a lot of work?
If there is any other way (besides "built-in AOE damage") to recognize if area damage is done, then I can also experiment with this.

Otherwise this AOE-flag would solve a lot of my problems (for example, not play an effect multiple times when you use a lifesteal item and deal damage as shown above, OR make it that you heal only 25% of AOE damage)
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
I found a bug. Everytime I cast Drunken Haze the system displays "Blocked! 0" on the target. I don't want that text to appear since the ability does no damage, what can I do to fix it?
That doesn't sound like a bug but just how the mechanics of damage detection work. In your case you could probably check if the DamageEventAmount was > 0 to begin with. If it was 0 then you can filter it out from your "Blocked" trigger.
 
Level 6
Joined
Jul 12, 2021
Messages
95
Alright now I see how it is not a bug. Could someone please explain what does the variables DamageScalingUser and DamageScalingWC3 do?


If i make this change it seems to work but I dont know why:

From this:
Untitledd.png


To this:
Untitleddd.png
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
DamageEvent is an event that takes place if the original damage processed by WC3 was a non-zero value. Therefore, it deduces that if the amount is now equal to zero, that a block has taken place.

This did get thrown off a bit in one of the recent patches where they started certain damage at 1 mana and reduced it to 0 (such as the damage you've noticed), however that's merely of a logical issue with this demo text tag script as I still consider it to be primitive and has not been fine-tuned for the game insomuch that the engine itself has..
 
Level 12
Joined
May 16, 2020
Messages
660
Would anyone have a good idea how how to prevent a triggered Critical Strike damage from stacking?

  • Events
    • Game - DamageModifierEvent becomes Equal to 1.10
  • Conditions
    • (Level of Ability for DamageEventSource) Greater than 0
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Random percentage) Less than or equal to 20.00
      • Then - Actions
        • Set VariableSet DamageEventAmount = (DamageEventAmount x 1.5)
      • Else - Actions
So for example, if the DamageEventSource has another ability (or item) that triggers the Crit at the same time for 3x the DamageEventAmount, I want to only deal 3x damage and ignore the 1.5x damage.

@Bribe Sorry to necro this, but would you have a good idea how to prevent critical strikes from running at the same time?

I tried "DamageEventLevel > 1" because I assumed with this I could recognize when an enemy is "hit twice", but I didn't know how to proceed.
Daffa had this idea with a table, where you store the crit values and compare a crit to the last one. But in my eyes you would have difficulties telling this table when to delete the values...
 
@Bribe Sorry to necro this, but would you have a good idea how to prevent critical strikes from running at the same time?

I tried "DamageEventLevel > 1" because I assumed with this I could recognize when an enemy is "hit twice", but I didn't know how to proceed.
Daffa had this idea with a table, where you store the crit values and compare a crit to the last one. But in my eyes you would have difficulties telling this table when to delete the values...
You should consolidate all of them into one trigger where you have full control of what crit takes precedence.
 
Level 12
Joined
May 16, 2020
Messages
660
You should consolidate all of them into one trigger where you have full control of what crit takes precedence.

Thank you, will think how to do it!

Either that or just set DamageEventOverride to True whenever a crit is detected, which was the original reason I created that variable to begin with.

Isn't DamageEventOverride the function that prevents any modification afterwards?
So if I apply this to a "crit system": If crit #1 runs (at 1.5x damage) > DamageEventOverride = True > crit #2 runs (at the same time, but at 3x damage), the 2nd, higher crit will be ignored. Or did you mean something else?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
If you have 2 triggers with the same event, whichever one runs first will skip the other event when using DamageEventOverride.

To control which one happens first, you would just change the EQUAL TO event amount from both being the same into one you want to run second be the first plus at least 0.01.

So you end up with something like:

Event becomes Equal to -9001 //runs first
Event becomes Equal to -9000 //runs second
 
Level 1
Joined
Sep 7, 2021
Messages
3
Hello bribery, I hope you are very well, friend I thank you from the bottom of my heart for having created the damage detection, it has been useful to me, but there is a problem, it is not a bug or something like that, what happens is that I have the Damage Engine version 3.8, and I don't know how to add another type of damage so that it is detected, I mean I want the physical damage to be detected, because if I put Chaos damage, it is detected as physical damage.


¿Could you explain to me how to add another type of damage?, Your help would really help me
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hello bribery, I hope you are very well, friend I thank you from the bottom of my heart for having created the damage detection, it has been useful to me, but there is a problem, it is not a bug or something like that, what happens is that I have the Damage Engine version 3.8, and I don't know how to add another type of damage so that it is detected, I mean I want the physical damage to be detected, because if I put Chaos damage, it is detected as physical damage.


¿Could you explain to me how to add another type of damage?, Your help would really help me
Hello bribery, I hope you are very well, friend I thank you from the bottom of my heart for having created the damage detection, it has been useful to me, but there is a problem, it is not a bug or something like that, what happens is that I have the Damage Engine version 3.8, and I don't know how to add another type of damage so that it is detected, I mean I want the physical damage to be detected, because if I put Chaos damage, it is detected as physical damage.


¿Could you explain to me how to add another type of damage?, Your help would really help me
Damage Engine 3A or 3.8 can only distinguish physical from spell damage, but not discern other native damage types. The only thing you can do is set DamageEventType or NextDamageType in relation to triggered damage.
 
Level 1
Joined
Sep 7, 2021
Messages
3
Damage Engine 3A or 3.8 can only distinguish physical from spell damage, but not discern other native damage types. The only thing you can do is set DamageEventType or NextDamageType in relation to triggered damage.
Wow, I'm really a novice at this, it's my fault for not understanding well how the damage engine works, but give me a bribe for it.


The advantage of the current version of damage engine is that if it can detect different types of damage, I am from Colombia and it really is difficult for me to understand and handle your map, sorry for that too ...


In my map I want to implement that when a hero receives damage from an attack, it increases his armor and damage, but the problem comes from the fact that if he is physically damaged by an ability, his armor also increases and I only want it to be from basic attacks, If you can understand me?
 
Level 21
Joined
Dec 4, 2007
Messages
1,473
Wow, I'm really a novice at this, it's my fault for not understanding well how the damage engine works, but give me a bribe for it.


The advantage of the current version of damage engine is that if it can detect different types of damage, I am from Colombia and it really is difficult for me to understand and handle your map, sorry for that too ...


In my map I want to implement that when a hero receives damage from an attack, it increases his armor and damage, but the problem comes from the fact that if he is physically damaged by an ability, his armor also increases and I only want it to be from basic attacks, If you can understand me?
You should be able to check a boolean condition: is damage attack = true.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
You should be able to check a boolean condition: is damage attack = true.
IsDamageAttack works in the new version, but in the old version he or she will need to use IsDamageSpell == False. Obviously it has the downside of triggering on area of effect physical damage and multi-shot kind of stuff, but that's what makes upgrading to Damage Engine 5 so much better.
 
Level 12
Joined
May 16, 2020
Messages
660
If you have 2 triggers with the same event, whichever one runs first will skip the other event when using DamageEventOverride.

To control which one happens first, you would just change the EQUAL TO event amount from both being the same into one you want to run second be the first plus at least 0.01.

So you end up with something like:

Event becomes Equal to -9001 //runs first
Event becomes Equal to -9000 //runs second

If I understand correctly you suggest to adjust the event for each separate crit trigger, correct? For example:

  • CRIT #1 with 400% multiplier
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.10
  • DamageEventOverride = true
  • CRIT #2 with 300% multiplier
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.11
  • DamageEventOverride = true
  • CRIT #3 with 200% multiplier
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.12
  • DamageEventOverride = true

In this case I want to give priority to crits with HIGHER multipliers, hence they get a lower "ModifierEvent" number.

But then this leaves me with a problem, specifically related to Crit abilities that change their multiplier amount per level.

For example, take a hero that has a Crit ability with 150% multiplier at level 1, 175% at level 2, 200% at level 3. If the hero now also holds a crit item that grants a 180% multiplier, then the ability at level 1 and 2 should be prioritized lower than the event of the item. But once the ability has level 3, it should be prioritized higher than the item. However, when applied to the suggested method, I can only chose if the ability (level 1-3) OR item takes priority when both crit.

Would you see a solution for these cases?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
The most efficient method is to put these all in one trigger as someone suggested.

The logic flow in your case is to create a variable called CritAmount (or whatever you want it to be called) and do:

Set CritAmount = 1.00
——check your crit conditions and change CritAmount to the multiplier ONLY if the multiplier is greater than CritAmount ——
Multiply the damageeventamount by CritAmount.
 
Is there an issue with the latest Lua version of this? When I delete the demo triggers the game crashes as soon as an enemy takes damage. The same goes if I import the system without the demo triggers.

EDIT: Deleted everything in the demo triggers one by one, and it seems the map crashes when the "FastTriggers" script is deleted.
Code:
do
   local cMap = {}
   local aMap = {}
   local lastCondFunc
   local waitFunc
 
   local oldCond = Condition --If you don't want this Condition-overwrite behavior
   --for any particular resource, use Filter() instead of Condition(). This tool
   --is mainly for GUI users & the GUI->script compiled behavior uses Condition().
   function Condition(func)
      lastCondFunc = func
      return oldCond(func)
   end
 
   local oldTAC = TriggerAddCondition
   function TriggerAddCondition(trig, cond)
      if lastCondFunc then
         cMap[trig] = lastCondFunc --map the condition function to the trigger.
         lastCondFunc = nil
         cond = Filter(function()
            local t = GetTriggeringTrigger()
            if cMap[t]() then --Call the triggerconditions manually.
               waitFunc = aMap[t]
               waitFunc() --If this was caused by an event, call the trigger actions manually.
            end
         end)
      end
      return oldTAC(trig, cond) --use the regular event if a boolexpr or Filter
      --was used instead of Condition()
   end
 
   local oldTAA = TriggerAddAction
   function TriggerAddAction(trig, act)
      aMap[trig] = act
      return oldTAA(trig, function()
         waitFunc = aMap[GetTriggeringTrigger()]
         waitFunc() --If this was caused by an event, call the trigger actions manually.
      end)
   end
 
   local oldEval = TriggerEvaluate
   function TriggerEvaluate(trig)
      local f = cMap[trig]
      if f then return f() end
      return oldEval(trig)
   end
 
   local oldExec = TriggerExecute
   function TriggerExecute(trig)
      waitFunc = aMap[trig]
      waitFunc()
   end
 
   function RunTrigger(trig)
      local conds = cMap[trig]
      if IsTriggerEnabled(trig) and not conds or conds() then
         waitFunc = aMap[trig]
         waitFunc()
      end
   end
 
   local skipNext = false
   function EnableWaits()
      if skipNext then
         skipNext = false
         return false
      end
      skipNext = true
      coroutine.resume(coroutine.create(function()
         waitFunc()
      end))
      return true
   end
end
 
Last edited:
It seems like there is an issue with "wait" functions as well, especially when running triggers from other triggers. All actions after a wait will fail to run. Happens to both "wait in game time", and normal "wait" functions.

Deleting the damage engine from the map immediately solved all the wait issues.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
It seems like there is an issue with "wait" functions as well, especially when running triggers from other triggers. All actions after a wait will fail to run. Happens to both "wait in game time", and normal "wait" functions.

Deleting the damage engine from the map immediately solved all the wait issues.
That’s because you can’t use Waits in conjunction with Lua Fast Triggers.
 
Level 6
Joined
Jul 12, 2021
Messages
95
  • Shadow Strike
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • DamageEventAmount Equal to 20.01
    • Actions
      • Custom script: Do Actions
Is it safe to use Damage Engine like the trigger above? I'm assuming that the condition (DamageEventAmount Equal to 20.01) will only occur with this trigger and never from auto-attacks, damaging spells or other sources of damage.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
  • Shadow Strike
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • DamageEventAmount Equal to 20.01
    • Actions
      • Custom script: Do Actions
Is it safe to use Damage Engine like the trigger above? I'm assuming that the condition (DamageEventAmount Equal to 20.01) will only occur with this trigger and never from auto-attacks, damaging spells or other sources of damage.
Why would it exclude those other sources of damage? That trigger says that when damage is dealt, if the damage amount is equal to 20.01 then do stuff. So if a source does 20.01 damage then it will run the actions.
 
Level 6
Joined
Jul 12, 2021
Messages
95
Why would it exclude those other sources of damage? That trigger says that when damage is dealt, if the damage amount is equal to 20.01 then do stuff. So if a source does 20.01 damage then it will run the actions.
Alright, I was thinking that auto-attacks and other sources of damage did not consider decimals, but it seems that it is not the case. Then I guess it is safe to use it like this:

  • Shadow Strike
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • DamageEventAmount Equal to -.01
    • Actions
      • Custom script: Do Actions
Seems safe since auto-attacks never give negative damage. The only sources of negative damage that I can of are custom spells and Damage Engine triggers.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
Alright, I was thinking that auto-attacks and other sources of damage did not consider decimals, but it seems that it is not the case. Then I guess it is safe to use it like this:

  • Shadow Strike
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • DamageEventAmount Equal to -.01
    • Actions
      • Custom script: Do Actions
Seems safe since auto-attacks never give negative damage. The only sources of negative damage that I can of are custom spells and Damage Engine triggers.
There's conditions for filtering these things: IsSpell, IsMelee, IsRanged, IsCode. I don't really understand what a DamageEventAmount of -0.01 would achieve.
 
Level 6
Joined
Jul 12, 2021
Messages
95
There's conditions for filtering these things: IsSpell, IsMelee, IsRanged, IsCode. I don't really understand what a DamageEventAmount of -0.01 would achieve.
I'm glad you asked.

It's for dummy purposes. I have a Shockwave spell that deals -0.01 damage. With the next trigger every unit that is affected by the Shockwave will be stunned and will recieve 100 damage:

  • Untitled Trigger 001 Copy
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • DamageEventAmount Equal to -0.01
    • Actions
      • Unit - Create 1 Mountain King for Neutral Hostile at (Position of DamageEventTarget) facing (Position of DamageEventTarget)
      • Unit - Hide (Last created unit)
      • Hero - Create Storm Bolt Item and give it to (Last created unit)
      • Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
      • Hero - Order (Last created unit) to use (Item carried by (Last created unit) of type Storm Bolt Item) on DamageEventTarget
      • Set VariableSet DamageEventAmount = 100.00
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
I'm glad you asked.

It's for dummy purposes. I have a Shockwave spell that deals -0.01 damage. With the next trigger every unit that is affected by the Shockwave will be stunned and will recieve 100 damage:

  • Untitled Trigger 001 Copy
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • DamageEventAmount Equal to -0.01
    • Actions
      • Unit - Create 1 Mountain King for Neutral Hostile at (Position of DamageEventTarget) facing (Position of DamageEventTarget)
      • Unit - Hide (Last created unit)
      • Hero - Create Storm Bolt Item and give it to (Last created unit)
      • Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
      • Hero - Order (Last created unit) to use (Item carried by (Last created unit) of type Storm Bolt Item) on DamageEventTarget
      • Set VariableSet DamageEventAmount = 100.00
I don't think you can assign a spell to deal negative damage but if it works then go for it.

Question, why aren't you using a normal Dummy unit with the Locust ability? There shouldn't be a need for a hero/item/hiding the unit.
 
Level 6
Joined
Jul 12, 2021
Messages
95
I don't think you can assign a spell to deal negative damage but if it works then go for it.

Question, why aren't you using a normal Dummy unit with the Locust ability? There shouldn't be a need for a hero/item/hiding the unit.
It is possible to assign negative damage (at least in Shockwave). The target gets healed instead of damaged.

I need the dummy to be a hero with an item. If you use a normal dummy the casting time will delay the stun ability. The stun needs to be an item for instant cast.
 

Uncle

Warcraft Moderator
Level 63
Joined
Aug 10, 2018
Messages
6,457
It is possible to assign negative damage (at least in Shockwave). The target gets healed instead of damaged.

I need the dummy to be a hero with an item. If you use a normal dummy the casting time will delay the stun ability. The stun needs to be an item for instant cast.
The normal dummy shouldn't have any issues if it's setup properly. Create your dummy like this:
1: Copy and paste the Locust unit
2: Set Movement Type to None and Speed Base to 0
3: Set the Model to "none" or any blank field
4: Set Attacks Enabled to None
 
Top