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

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...
Well I haven't done extensive tests but Cleaving Attack does deal DAMAGE_TYPE_ENHANCED on non-primary targets, so it's definitely something to consider. It would have been easier had blizzard offered a boolean for physical and magical damage (I mean, they did it already with Runed Bracers, so why not just give us that function?), but Bribe sounds confident in his next update so I guess I'll wait and see.
Quick Edit: Sure, you could use the new natives to differenciate damage and still use the current methods of detecting spell damage, since they are not incompatible. But you would not be able to know if the damage was spell damage in the new "Damaging" event that happens before damage is reduced (unless that damage is already reduced by the magic reduction ability? That seems to defeat the purpose of the event).
I'd rather we got rid of the Runed Bracers workaround as soon as there's an alternative. If attacktype and damagetype detection offers that possibility, out with the bracers :p
Quick Edit 2: I forgot that units the deal "Normal" damage actually use ATTACK_TYPE_MELEE. So there would be no way for the unit with Cleaving Attack to have ATTACK_TYPE_NORMAL.
Even with ranged attacks?
 
Even with ranged attacks?

I don't see why it would be different with ranged attacks :S

Also, I thought a bit about it, it might be that spells simply use ATTACK_TYPE_NORMAL, and the damage type does not matter? Maybe the magic reduction ability simply reduces damage of attacks with that attack type, and does not care about the damage type at all. I have just tested it, and it seems to be the case. No matter what damage type I use, Damage Engine detects damage of ATTACK_TYPE_NORMAL as spell damage.
 
Last edited:
This version of Damage Engine will go live once 1.31 does - it enables a lot of new features like:

- Differentiate between ranged and melee attacks
- Differentiate between native attack, damage and weapon types
- Get/Modify damage before it's reduced by armor
- Get/Modify damage after it's reduced by armor
- Catch the moment a unit is about to be killed and allow the user to set a minimum HP value for the unit to survive with (as opposed to just editing the damage)

Additional perks:
- No longer uses Unit Indexer
- No longer uses any Object Editor data (so you'll need to revert any changes made to Anti-Magic Shell, Mana Shield or Runed Bracers)
- For those that are upgrading from 3.8, it also no longer needs any modifications to any of the other object editor data like max life.

Let me know how it plays for you all. I've tested it very extensively and I'm confident this will fulfill almost all open Damage Engine requests I've gotten over the years.
 

Attachments

  • Damage Engine v5.0 - PTR.w3x
    45.2 KB · Views: 53
Level 21
Joined
Dec 4, 2007
Messages
1,480
^ Does this mean that the default anti magic shield and mana shield work out of the box now?

And couldn't we already detect if a unit is a ranged/melee attacker via conditions?

Anyway, great update!
 
^ Does this mean that the default anti magic shield and mana shield work out of the box now?

And couldn't we already detect if a unit is a ranged/melee attacker via conditions?

Anyway, great update!

All Object Editor data can be left at their default values.

Like @_Guhun_ said, if a melee hero has its second attack index enabled, or a unit like a Gargoyle which has both enabled at all times, you'll want to know the difference.
 
Level 3
Joined
Oct 9, 2012
Messages
28
So glad to see the update after long! I was researching your 3.8 version yesterday and did some modifications. I was confused today at first: did I download the outdated version?

Could I ask for a change log please? so I don't have to compare line by line with my modified version. I changed the unit indexer to jass, and simplified it a little bit to better understand. If this doesn't change I would just copy the other scripts.

Following is my jass version indexer if anyone interested.

Thanks

Code:
function Trig_Unit_Indexer_Actions takes nothing returns nothing
    call ExecuteFunc("InitializeUnitIndexer")
endfunction
    
function ClearUnitIndex takes nothing returns nothing
    if ( GetUnitUserData(udg_UDexUnits[udg_UDex]) == 0 ) then
        set udg_UnitIndexLock[udg_UDex] = ( udg_UnitIndexLock[udg_UDex] - 1 )
        if ( udg_UnitIndexLock[udg_UDex] == 0 ) then
            set udg_UDexNext[udg_UDexPrev[udg_UDex]] = udg_UDexNext[udg_UDex]
            set udg_UDexPrev[udg_UDexNext[udg_UDex]] = udg_UDexPrev[udg_UDex]
            set udg_UDexPrev[udg_UDex] = 0
            set udg_UnitIndexEvent = 0.00
            set udg_UnitIndexEvent = 2.00
            set udg_UnitIndexEvent = 0.00
            set udg_UDexUnits[udg_UDex] = null
            set udg_UDexNext[udg_UDex] = udg_UDexRecycle
            set udg_UDexRecycle = udg_UDex
        endif
    endif
endfunction
    
function IndexUnit takes nothing returns boolean
    local integer pdex = udg_UDex
    local integer ndex
       
    // -
    // You can customize the following block - if conditions are false the (Matching unit) won't be indexed.
    // -
    if ( udg_UnitIndexerEnabled == true and GetUnitUserData(GetFilterUnit()) == 0 ) then
        set udg_UDexWasted = ( udg_UDexWasted + 1 )
        if ( udg_UDexWasted == 32 ) then
            set udg_UDexWasted = 0
            set udg_UDex = udg_UDexNext[0]
            loop
                exitwhen udg_UDex == 0
                set ndex = udg_UDexNext[udg_UDex]
                call ClearUnitIndex()
                set udg_UDex = ndex
            endloop
        else
        endif
        if ( udg_UDexRecycle == 0 ) then
            set udg_UDex = ( udg_UDexGen + 1 )
            set udg_UDexGen = udg_UDex
        else
            set udg_UDex = udg_UDexRecycle
            set udg_UDexRecycle = udg_UDexNext[udg_UDex]
        endif
        set udg_UDexUnits[udg_UDex] = GetFilterUnit()
        call SetUnitUserData( udg_UDexUnits[udg_UDex], udg_UDex )
        set udg_UDexPrev[udg_UDexNext[0]] = udg_UDex
        set udg_UDexNext[udg_UDex] = udg_UDexNext[0]
        set udg_UDexNext[0] = udg_UDex
        set udg_UnitIndexLock[udg_UDex] = 1
        set udg_UnitIndexEvent = 0.00
        set udg_UnitIndexEvent = 1.00
        set udg_UnitIndexEvent = 0.00
        set udg_UDex = pdex
    endif
    return false
endfunction
    
function InitializeUnitIndexer takes nothing returns nothing
    local integer i = bj_MAX_PLAYER_SLOTS
    local boolexpr b = Filter(function IndexUnit)
    local region re = CreateRegion()
    local trigger t = GetTriggeringTrigger()
    local rect r = GetWorldBounds()
    call RegionAddRect(re, r)
    call TriggerRegisterEnterRegion(t, re, b)
    call TriggerClearActions(t)
    call TriggerAddAction(t, function ClearUnitIndex)
    set udg_UnitIndexerEnabled = true
    loop
        set i = i - 1
        call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
        exitwhen i == 0
    endloop
    call RemoveRect(r)
    set re = null
    set r = null
    set t = null
    set b = null
    set udg_UnitIndexEvent = 3.00
    set udg_UnitIndexEvent = 0.00
endfunction

//===========================================================================
function InitTrig_Unit_Indexer takes nothing returns nothing
    set gg_trg_Unit_Indexer = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Unit_Indexer, function Trig_Unit_Indexer_Actions )
endfunction
 
Detecting code damage would be nice
The only way to possibly do this is if you set NextDamageType right before for each of the Unit - Damage Target natives you have. However, right now I don't have any provisions in the engine to identify for the user if that was even the case.

Can you please let me know what benefits this type of feature would otherwise have?

Is there a function to deal reflect damage without a loop?

Sorry, but what would you need a loop for? The system already knows how to deal with recursive damage, so the only thing I would advise doing is to turn off the trigger that deals the reflected damage, then issue the Unit - Damage Target order, then turn the trigger back on.
 
Level 20
Joined
May 16, 2012
Messages
635
Can you please let me know what benefits this type of feature would otherwise have?

its useful for conditional modifications, thats the only reason i picked PDD Damage Engine instead of yours. Lets say you want to modify an instance of damage dealt by code, instead of by an attack or a spell, is it possible right now? i think the only thing you should add is a custom UnitDamageTarget function that represents a coded damage instance and check for damege type in the OnDamage function before you change the value of the real variables that fire up the events. But honestly, since the addition of native damage event, specially for those who create triggers using Jass/vJass, damage engines are now trivial and only useful until generic detection comes to gui.
 
The advantage Is that if I have 2 units with damage reflection abilities there wont be am infinite loop that destroys the map, there are other ways I can fix it like a noloopgroup so Is no big deal. This system Is better, PDD causes devour to heal the devoured Unit and cannibalize to stop when damaged, also causes poison to always kill and so on. For what I have seen so dar your system has none of it so I will stay with it.
 
its useful for conditional modifications, thats the only reason i picked PDD Damage Engine instead of yours. Lets say you want to modify an instance of damage dealt by code, instead of by an attack or a spell, is it possible right now? i think the only thing you should add is a custom UnitDamageTarget function that represents a coded damage instance and check for damege type in the OnDamage function before you change the value of the real variables that fire up the events. But honestly, since the addition of native damage event, specially for those who create triggers using Jass/vJass, damage engines are now trivial and only useful until generic detection comes to gui.

I agree the function could be added, but I think the easiest way is with NextDamageType before dealing damage. I will look into the options, but you've given me something to consider for the next release (which is coming soon).

I disagree that the engine is not needed - just that the complexity could be reduced. It is ultimately up to the mapmaker to assess how damage modification should be handled between systems. Damage Engine does a lot of analysis for the user to keep things simple, compatible and as intuitive as I can make it.

The advantage Is that if I have 2 units with damage reflection abilities there wont be an infinite loop that destroys the map, there are other ways I can fix it like a noloopgroup so is no big deal. This system Is better, PDD causes devour to heal the devoured Unit and cannibalize to stop when damaged, also causes poison to always kill and so on. For what I have seen so dar your system has none of it so I will stay with it.

In the next update the system will ignore DAMAGE_TYPE_UNKNOWN so you can use that as an alternative to disabling both triggers and then dealing the damage. I found that damage type to be only used in events where you don't want it to - such as when a unit gets hit by a debuff.
 
Level 6
Joined
Dec 9, 2010
Messages
121
Hey Bribe. Great system, I love it and it's very useful to me!

I have one problem with using it. I have some spells in the game that are not supposed to deal any damage. For some reason, those spells are causing the targeted unit to receive 1 Damage every time they're hit with it. This includes many single-target abilities, such as Soul Burn, Firebolt, and Web.

Any idea why this is happening? I'm using the most up to date version of this system, and the most up to date Warcraft III version as well.

Thanks!
 
Level 5
Joined
Dec 25, 2014
Messages
111
Hmm.. bribe, is this a map from ptr? because i had to re-save it to test the map. If i didn't re-save it it would unable to test.
And, this version is still give me recursion error when i test it still in the demo map. I'm not sure to implement it to my map. The 1.31 patch is still bugged so.
 
Hmm.. bribe, is this a map from ptr? because i had to re-save it to test the map. If i didn't re-save it it would unable to test.
And, this version is still give me recursion error when i test it still in the demo map. I'm not sure to implement it to my map. The 1.31 patch is still bugged so.
Make sure you download the one without a title
 
Level 5
Joined
Dec 25, 2014
Messages
111
Make sure you download the one without a title
Oh yeah, i'm testing the one without the title. It's still gives me error while the Archimonde attack, he sometimes kill. It gives me error too while one or more units is damaging each other.
And for the testing problem, is that a problem with registry or what? what should i change in the registry editor?
 
Level 2
Joined
Jun 1, 2019
Messages
10
Hi, still learning the code, so I noticed that all summoned units cannot do damage to hero characters, in fact they heal those characters instead, so my infernal actually becomes useless when fighting a hero, is this supposed to be the case and if so, how can I change that? I understand that the water elemental specifically should heal the Archmage, but I feel like it being extended to all summon types and also against enemy heroes is a bit.... odd...
 
Level 2
Joined
Jun 1, 2019
Messages
10
You're using test triggers, the test map makes summoned units heal.

Remove set damage trigger

Ah ty, woopsie doopsie, still I like the idea of summoned units healing their owners, might have to look at the code and see what I can do with it.
 
The release of 5.1 (and the documentation) is taking a bit longer than planned due to recursive mitigation measures. I've got the recursive damage in a queue now rather than a recursive method, but it is very challenging to get the execution order right as well as to preserve the variables. I've almost got it, but it's been taking up many hours of my time trying to debug it. The goal is to make it MUCH easier and less risky to deal damage from a damage event.
 
Level 3
Joined
May 12, 2016
Messages
51
Wonderful system. I use this on an inhouse map I have with my friends. Tho one minor observation.

If an unindexed unit deals damage, unindexed as in via the Turn Off/Turn On thing with the Unit Indexer that comes with the DDS, the damage dealt by it becomes nonlethal.
e.g. i have a Dummy unit that casts Fan of Knives on a target location. The Dummy, unindexed does deal damage enough to drop the affected nearby enemies to 1 but they are not killed.
 
Wonderful system. I use this on an inhouse map I have with my friends. Tho one minor observation.

If an unindexed unit deals damage, unindexed as in via the Turn Off/Turn On thing with the Unit Indexer that comes with the DDS, the damage dealt by it becomes nonlethal.
e.g. i have a Dummy unit that casts Fan of Knives on a target location. The Dummy, unindexed does deal damage enough to drop the affected nearby enemies to 1 but they are not killed.
I presume you're on Damage Engine version 3.8? So the dummy unit who deals the damage I think is ignored in the system because he has Locust, but I'm not 100% sure. If you send me your map I can look into it and find out.
 
Level 13
Joined
Jul 15, 2007
Messages
763
  • !!!DEPRECATED FEATURE!!! Do not Turn off your trigger prior to dealing damage. Instead, Set DamageEventTrigger = (This trigger), then deal the damage. Alternatively, use DAMAGE_TYPE_UNKNOWN* from the Unit - Damage Target function.
    • * This will ignore the "DamageEvent Equal to 1.00" and only trigger "LethalDamageEvent Equal to 1.00" and "DamageModifierEvent Equal to 4.00".

Maybe I misunderstood this piece of advice, but i remodeled the way I use damage engine to match these new set of rules and the now the map crashes. This one in particular is what is causing my issues as I'm pretty sure the crashes are due to a recursion loop (trigger 1 causes damage, trigger 2 causes damage in response to trigger 1, trigger 1 causes damage in response to trigger 2 etc.). Obviously when I go back to the old way of turning off and on specific triggers I get the new recursion game message "WARNING: Recursion error...".

I have a feeling the issue wouldn't exist if I had one huge block code of damage engine triggers, but unfortunately due to the size of my project it is necessary for performance reasons to have an essential damage engine trigger and then multiple smaller destructible damage engine triggers which can be removed or turned off from the map during run-time.

Are you aware of such an issue?
 
Maybe I misunderstood this piece of advice, but i remodeled the way I use damage engine to match these new set of rules and the now the map crashes. This one in particular is what is causing my issues as I'm pretty sure the crashes are due to a recursion loop (trigger 1 causes damage, trigger 2 causes damage in response to trigger 1, trigger 1 causes damage in response to trigger 2 etc.). Obviously when I go back to the old way of turning off and on specific triggers I get the new recursion game message "WARNING: Recursion error...".

I have a feeling the issue wouldn't exist if I had one huge block code of damage engine triggers, but unfortunately due to the size of my project it is necessary for performance reasons to have an essential damage engine trigger and then multiple smaller destructible damage engine triggers which can be removed or turned off from the map during run-time.

Are you aware of such an issue?

Sorry the documentation is prepared for the 5.1 release which isn't prepared itself yet. I will have it ready before Monday for sure. I've got Spirit Link working and now I'm running final tests and making some last minute adjustments to the way the spirit link damage is displayed (because WC3 screws up the values in the damage sequence).
 
5.1 has just launched.

Edit: Looks like Ralle updated the map parser. The one to download is the second of the two (the one that says it was just updated).

The demo map has been improved a bit thanks to Maker's awesome Arcing TextTag resource.

I have factored in every damage scenario I can think of - including recursive damage mitigation (now extremely easy an requires minimal effort for the user to prevent a crash) and how spirit link interacts with custom shields/modifications.

The two biggest issues, both of which are extremely minor but due to quirks in the game engine, are:

Spirit Link damage is displayed weirdly. The last unit in a chain gets the highest damage dealt to it (visually), even if it wasn't the actual damage recipient. The unit, when monitoring the HP, still only gets a fractional damage applied against it and that bigger amount still goes to the unit that got hit. Fixing this would require creating a complex Spirit Link modification table and handler. It can be done, but integrating such a monstrosity into this system is more than I care to take on. Actually, I just found out that the DAMAGING and DAMAGED events don't stay in sync for this event. I'll make an update in the next 24 hours to make sure everything has been re-tested and to make sure no damage is overlooked.

I'm also considering moving the ShieldDamageEvent into the DAMAGING event, because it occurs to me that shields should take effect before armor types are applied. The LethalDamageEvent should definitely fire after armor, however. I'm also going to move DamageEvent back into that trigger.


Critical Strike text tags just show the pure damage amount, before any user modifications and before any armor/resistances were set. There's no way to fix this without triggering all instances of critical strike (which is very easy to do with this system).
 
Last edited:
Level 2
Joined
Apr 17, 2017
Messages
22
No, it requires nothing anymore. Patch 1.31 is truly next-level awesomeness, and Damage Engine is a testament to that.

Oh that's awesome however i encounter some sort of memory leak when a unit dies on my map. I have a version of your Unit Event System. I'm not sure what causes this. Can you help me out please?

I attached the map im working on. PS: the map I use is for testing purposes only. I would create my own in the future.

Edit: Try Killing any unit to see what I'm saying. TIA
 

Attachments

  • (24)IslandExpedition-Warcraft Conquest Mod.w3x
    2.3 MB · Views: 36
Oh that's awesome however i encounter some sort of memory leak when a unit dies on my map. I have a version of your Unit Event System. I'm not sure what causes this. Can you help me out please?

I attached the map im working on. PS: the map I use is for testing purposes only. I would create my own in the future.

Edit: Try Killing any unit to see what I'm saying. TIA
Without having yet opened your map, I can say I'm familiar with that bug and it was caused by old versions of Unit Event. Update it and see how it goes.
 
Level 2
Joined
Apr 17, 2017
Messages
22
Without having yet opened your map, I can say I'm familiar with that bug and it was caused by old versions of Unit Event. Update it and see how it goes.

the current version of the Unit Event im using is the most recent on the hive and that's v2.5.2.0

I'll double check it tomorrow and let you know.
 
the current version of the Unit Event im using is the most recent on the hive and that's v2.5.2.0

I'll double check it tomorrow and let you know.
I'll look into it tomorrow.

In the meantime, I'm pleased to announce the hotfix to Spirit Link - Damage Engine 5.1.1.0 is here and it reconciles the DamageEventTarget mixup which caused me many hours of grief. Basically, the UNIT_DAMAGED event fires out of sequence for that one, so I had to reset the DamageEventX variables. This fixes it completely.

Now I don't believe anything short of more damage detection natives being added by Blizzard will cause DamageEngine to require further major updates. I'll mostly do optimizations and expand on the documentation at this point.

Please ask questions as they come to you.
 
Level 24
Joined
Feb 9, 2009
Messages
1,787
In case you missed it:

@Bribe
I recall posing this question to you in the past regarding spell detection, I think it was damage level? (I.e. enhanced attack / black arrow) but is there a simpler method to adding attribute damage to the method I posted?

Ex:
EVENT: detect 0 damage event
CON: source with lv1 custom fireball,
ACTION: remove custom buff_statbasedfireball deal X damage

Relying on buffs is rather fickle as if two over lap in rapid succession then the above method will be less accurate.

On another note, I noticed a strange interaction with attempting to make the carrion swarm stat based, the max damage seemed to limit the targets hit despite being 0 damage and default (300 Max damage), but i'm not sure if I was using 4.0.0.0 or earlier...
 
In case you missed it:

@Bribe
I recall posing this question to you in the past regarding spell detection, I think it was damage level? (I.e. enhanced attack / black arrow) but is there a simpler method to adding attribute damage to the method I posted?

Ex:
EVENT: detect 0 damage event
CON: source with lv1 custom fireball,
ACTION: remove custom buff_statbasedfireball deal X damage

Relying on buffs is rather fickle as if two over lap in rapid succession then the above method will be less accurate.

On another note, I noticed a strange interaction with attempting to make the carrion swarm stat based, the max damage seemed to limit the targets hit despite being 0 damage and default (300 Max damage), but i'm not sure if I was using 4.0.0.0 or earlier...

I actually got rid of the zero damage event tbh. It's not included in the documentation though. But it does apply towards DamageEventLevel.

As far as Carrion goes, that sounds like an engine limitation of WC3.
 
Level 24
Joined
Feb 9, 2009
Messages
1,787
I actually got rid of the zero damage event tbh. It's not included in the documentation though. But it does apply towards DamageEventLevel.

As far as Carrion goes, that sounds like an engine limitation of WC3.

I did some more tests and I can't seem to repeat it...

Anywho is there a better way to detect specific hits?
i.e. better than this:
EVENT: detect 0 damage event
CON: source with lv1 custom fireball,
ACTION: remove custom buff_statbasedfireball deal X damage
 
Level 13
Joined
Oct 18, 2013
Messages
691
Thanks for touching up on Damage Event, Bribe.
!!!DEPRECATED FEATURE!!! Do not Turn off your trigger prior to dealing damage. Instead, Set DamageEventTrigger = (This trigger), then deal the damage. Alternatively, use DAMAGE_TYPE_UNKNOWN* from the Unit - Damage Target function.
Trying to think of the pros/cons of using Unknown damage. By quirk of how I wrote the damage handling in my map, nested instances of damage COULDNT proc effects related (heal on Spell Damage > 130) With the new rewrite, is nested damage handling like this possible now?
 
Trying to think of the pros/cons of using Unknown damage. By quirk of how I wrote the damage handling in my map, nested instances of damage COULDNT proc effects related (heal on Spell Damage > 130) With the new rewrite, is nested damage handling like this possible now?

I'll need you to go more in-depth with this. I have a few different ideas in my head of what you're describing, but I would like to first get my understanding right.

Yes ( i seemed to have missed putting that in my blurb trigger), however If they come under rapid succession it starts to become less accurate.

I'm a bit clueless on this one as well. What is failing? The unit not having the buff? Maybe if you share a test scenario where I can reproduce that situation and see what's happening, I'll be able to figure out what's happening there.
 
Level 24
Joined
Feb 9, 2009
Messages
1,787
Maybe if you share a test scenario where I can reproduce that situation and see what's happening, I'll be able to figure out what's happening there.

Here's a test map:

- Press ESC for all 100 peasants to shoot 1 damage fireballs at the caster, the caster is healed to max on ESC press, note the damage build up from the text (works only if you are selecting the unit being damaged.)

- Standing still you can take 100 damage, move to the right erratically and you will reduce the damage.

Make sure you have the blood mage selected before you hit ESC to see the damage buildup.


(ignore the carrion swarm spell! I forgot to remove it!)
 

Attachments

  • fireball spam test for bribe.w3x
    71.5 KB · Views: 33
Level 19
Joined
Aug 16, 2007
Messages
881
I'm currently using Damage Engine 3.8.0.0 and I modified it to allow the setup of multiple Spell Damage Reduction items as the version only supported and checked for the Runic Bracers item.
I also made the items Spell Damage Reduction stackable.

Note: I did not use the "Spell Damage Reduction"-ability for my items; I assigned a real value for each item-type in the config and made the following change in the OnUnitDamage function:
JASS:
function OnUnitDamage takes nothing returns boolean
    local boolean override = udg_DamageEventOverride
    local integer i
    local integer j = 0
    local integer e = udg_DamageEventLevel
    local integer a = udg_DamageEventAOE
    local string s
    local real prevAmount
    local real life
    local real prevLife
    local unit u
    local unit f
    call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
    set i = udg_DmgEvRecursionN - 1 //Had to be moved here due to false recursion tracking
    if i < 0 then
        //Added 25 July 2017 to detect AOE damage or multiple single-target damage
        set u                       = udg_DamageEventTarget
        set f                       = udg_DamageEventSource
    elseif i < 16 then
        set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
        set udg_LastDmgValue[i]     = udg_DamageEventAmount
        set udg_LastDmgSource[i]    = udg_DamageEventSource
        set udg_LastDmgTarget[i]    = udg_DamageEventTarget
        set udg_LastDmgWasSpell[i]  = udg_IsDamageSpell
        set udg_LastDmgPrevType[i]  = udg_DamageEventType
    else
        set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
        set s = s + "Trigger - Turn off (This Trigger)\n"
        set s = s + "Unit - Cause...\n"
        set s = s + "Trigger - Turn on (This Trigger)"
      
        //Delete the next couple of lines to disable the in-game recursion crash warnings
        call ClearTextMessages()
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
        return false
    endif
    set udg_DmgEvRecursionN     = i + 2
    set prevAmount              = GetEventDamage()
    set udg_DamageEventTarget   = GetTriggerUnit()
    set udg_DamageEventSource   = GetEventDamageSource()
  
    set udg_DamageEventAmount   = prevAmount
  
    set udg_DamageEventType     = udg_NextDamageType
    set udg_NextDamageType      = 0
    set udg_DamageEventOverride = udg_NextDamageOverride
    set udg_NextDamageOverride  = false
  
    if i < 0 then
        //Added 25 July 2017 to detect AOE damage or multiple single-target damage
        if udg_DamageEventType == 0 then
            if f == udg_DamageEventSource then
                //Source has damaged more than once
                if IsUnitInGroup(udg_DamageEventTarget, udg_DamageEventAOEGroup) then
                    //Added 5 August 2017 to improve tracking of enhanced damage against, say, Pulverize
                    set udg_DamageEventLevel = udg_DamageEventLevel + 1
                    set udg_EnhancedDamageTarget = udg_DamageEventTarget
                else
                    //Multiple targets hit by this source - flag as AOE
                    set udg_DamageEventAOE = udg_DamageEventAOE + 1
                endif
            else
                //New damage source - unflag everything
                set u = udg_DamageEventSource
                set udg_DamageEventSource = f
                call DmgEvOnAOEEnd()
                set udg_DamageEventSource = u
            endif
            call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
        endif
        if not udg_DmgEvStarted then
            set udg_DmgEvStarted = true
            call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
        endif
    endif
  
    if prevAmount == 0.00 then
        if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
            set udg_DamageEventPrevAmt = 0.00
            set udg_DamageEvent = 0.00
            set udg_DamageEvent = 2.00
            set udg_DamageEvent = 0.00
        endif
        call DmgEvResetVars()
    else
        set u = udg_DamageEventTarget
        set udg_IsDamageSpell = prevAmount < 0.00
        if udg_IsDamageSpell then
            set prevAmount = -udg_DamageEventAmount
            set life = 1.00
            if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
                set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
            endif
            if GetUnitAbilityLevel(u, 'Aegr') > 0 then
                set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
            endif

//================ =============== =============== ===============
// ================= I added this part below =============== ===============
//================ =============== =============== ===============

            loop
                exitwhen(j > udg_DmgEvSpellResistItem_Max)

                if udg_DmgEvSpellResistItem_Type[j] != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
                    //Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
                    set i = 6
                    loop
                        set i = i - 1
                        if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvSpellResistItem_Type[j] then
                            set life = life*udg_DmgEvSpellResistItem_Amount[j]
                            //exitwhen true
                        endif
                        exitwhen i == 0
                    endloop
                endif
                set i = 0
                set j = j + 1
            endloop

//================ =============== =============== ===============
//================ =============== =============== ===============
//================ =============== =============== =============== 

            set udg_DamageEventAmount = prevAmount*life
        endif
        set udg_DamageEventPrevAmt = prevAmount
        set udg_DamageModifierEvent = 0.00
        if not udg_DamageEventOverride then
            set udg_DamageModifierEvent = 1.00
            if not udg_DamageEventOverride then
                set udg_DamageModifierEvent = 2.00
                set udg_DamageModifierEvent = 3.00
            endif
        endif
        set udg_DamageEventOverride = override
        if udg_DamageEventAmount > 0.00 then
            set udg_DamageModifierEvent = 4.00
        endif
        set udg_DamageModifierEvent = 0.00
        if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
            set udg_DamageEvent = 0.00
            set udg_DamageEvent = 1.00
            set udg_DamageEvent = 0.00
        endif
        call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
      
        //All events have run and the damage amount is finalized.
        set life = GetWidgetLife(u)
        set udg_DmgEvTrig = CreateTrigger()
        call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
        if not udg_IsDamageSpell then
            if udg_DamageEventAmount != prevAmount then
                set life = life + prevAmount - udg_DamageEventAmount
                if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
                    set udg_LastDamageHP = life - prevAmount
                    call UnitAddAbility(u, udg_DamageBlockingAbility)
                endif
                call SetWidgetLife(u, RMaxBJ(life, 0.42))
            endif
            call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
        else
            set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
            set prevLife = life
            if life + prevAmount*0.75 > udg_LastDamageHP then
                set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
                call SetWidgetLife(u, life)
                set life = (life + udg_LastDamageHP)/2.00
            else
                set life = life + prevAmount*0.50
            endif
            set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
            call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
        endif
    endif
    set u = null
    set f = null
    return false
endfunction



With Damage Engine 5.1.1.1 the Spell Damage Reduction config and functionality is removed and by default the "Spell Damage Reduction"-ability isn't stackable.

So when I update, I'm not sure how I would implement the previous behaviour (as the entire Damage Engine trigger appears to be changed)?


Keep up the great work!
 
Last edited:
Level 19
Joined
Aug 16, 2007
Messages
881
You would still use your Real Variable comparisons per item ID. The system removed the dependency on hacky Object Editor data, but did not remove the requirement for you to manually register stacking multiple different resistence items or other conditions.

So your code would stay roughly the same in the end.

I'm uncertain where to modify the code in the "Damage Engine"-trigger to replicate the previous behaviour.

However, I guess I could simply modify the damage in a DamageModifierEvent instead, not sure why I should complicate things.

Thanks for the quick reply!
 
Top