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

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Yes that should still work, however with 5.7 I have included built-in conditions that won't "replace" anything you do however will make things much more efficient if you take advantage of them.

What filters are built in easily into GUI?

Spell, Melee, Ranged, Code, Attack and "anything".

Which ones require custom script?

Unit filtering
Unit types
Buff on unit

All of the above for source and target

Additionally, I've added a filter for DamageEventType and DamageEventAmount (minimum) to the fray.

Having these filters will cut back on trigger evaluations for very conplex maps.

Additionally, coming in a future patch, a DAMAGE_ENGINE_LITE mode which cuts out the pre-armor event introduced with 1.31. This doesn't make it backwards-compatible but DOES make it run a lot less code per event. Busy maps could see a difference in framerate.
 
Level 12
Joined
May 16, 2020
Messages
660
Hi Bribe,

In the introduction to the engine you mention that it is "recursion proof". I guess that means that a damage-order within a trigger shouldn't make the same trigger activate itself and deal the damage 2x (or infitine times) to a unit. However, in the below trigger the damage is dealt twice to enemies.

A bit of explanation on the trigger below:
- I'm using a backstab ability which deals damage when my unit is behind an enemy
- The backstab should be triggered by autoattacks, "normal" spells and "finisher spells"
- A backstab will grant "Combo Points" to the caster. However, these Combo Points should only be granted on auto-attacks and "normal" abilities - not "finisher abilities".
- The way I differentiate between the two types of abilities is with "damage type"; "normal" damage type is used for the "normal" abilities and "enhanced" damage type is used for the finisher
- Currently the enhanced damage is dealt twice however, which I assume shouldn't be possible

So my questions:
- Is this a bug?
- Is there a better way than damage type to tell the engine which abilties you want to include for "extra damage" and which ones you want to exclude?

Thank you!

Edit: In case you have reforged, I attached the map.


  • Backstab with combo
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (DamageEventSource has buff Backstab ) Equal to True
      • (DamageEventTarget belongs to an enemy of (Owner of DamageEventSource).) Equal to True
    • Actions
      • Set VariableSet Backstab_Caster = DamageEventSource
      • Set VariableSet Backstab_Target = DamageEventTarget
      • Set VariableSet Backstab_Point[1] = (Position of Backstab_Caster)
      • Set VariableSet Backstab_Point[2] = (Position of Backstab_Target)
      • Set VariableSet Backstab_Angle = (Angle from Backstab_Point[1] to Backstab_Point[2])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Acos((Cos(((Angle from Backstab_Point[1] to Backstab_Point[2]) - (Facing of Backstab_Target)))))) Less than 90.00
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DamageEventDamageT Equal to DAMAGE_TYPE_ENHANCED
            • Then - Actions
              • Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Enhanced
              • Special Effect - Create a special effect attached to the chest of Backstab_Target using war3mapImported\stampedemissiledeath.mdx
              • Special Effect - Destroy (Last created special effect)
              • Floating Text - Create floating text that reads |cffff0000Backstab!... at Backstab_Point[2] with Z offset 0.00, using font size 6.50, color (100.00%, 100.00%, 0.00%), and 0.00% transparency
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change the lifespan of (Last created floating text) to 0.65 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 0.50 seconds
            • Else - Actions
              • Trigger - Turn on Combo Point Loop <gen>
              • Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Normal
              • Special Effect - Create a special effect attached to the chest of Backstab_Target using war3mapImported\stampedemissiledeath.mdx
              • Special Effect - Destroy (Last created special effect)
              • Floating Text - Create floating text that reads |cffff0000Backstab!... at Backstab_Point[2] with Z offset 0.00, using font size 6.50, color (100.00%, 100.00%, 0.00%), and 0.00% transparency
              • Floating Text - Change (Last created floating text): Disable permanence
              • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
              • Floating Text - Change the lifespan of (Last created floating text) to 0.65 seconds
              • Floating Text - Change the fading age of (Last created floating text) to 0.50 seconds
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ComboPointCounter Less than 5
                • Then - Actions
                  • Set VariableSet ComboPointCounter = (ComboPointCounter + 1)
                  • Floating Text - Create floating text that reads (Combo Points: + (String(ComboPointCounter))) at Backstab_Point[1] with Z offset 0.00, using font size 6.50, color (100.00%, 100.00%, 0.00%), and 0.00% transparency
                  • Floating Text - Change (Last created floating text): Disable permanence
                  • Floating Text - Change the lifespan of (Last created floating text) to 0.90 seconds
                  • Floating Text - Change the fading age of (Last created floating text) to 0.50 seconds
                  • Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
                  • Set VariableSet Backstab_ComboCaster = (Last created unit)
                  • Unit - Add Combo Points Buff to Backstab_ComboCaster
                  • Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
                  • Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
                  • Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
                • Else - Actions
                  • Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
                  • Set VariableSet Backstab_ComboCaster = (Last created unit)
                  • Unit - Add Combo Points Buff to Backstab_ComboCaster
                  • Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
                  • Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
                  • Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
        • Else - Actions
      • Custom script: call RemoveLocation (udg_Backstab_Point[1])
      • Custom script: call RemoveLocation (udg_Backstab_Point[2])
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Based on your description if you add a condition to your trigger "DamageEvenrDamageT equal to DAMAGE_TYPE_NORMAL" then it should work.

You mean like this? Mmm this still causes the damage to deal twice. And it's not exactly what I want, because both DAMAGE_TYPEs (normal and enhanced) should go through if the "behind the enemy" condition applies. But Finisher-abilities (with enhanced damage) should just deal additional backstab damage, while Normal-abilities should do the additional mechanic in the lower code.

I added an image to maybe explain the problem better. Also cleaned the code to have it more visible here.


  • Backstab with combo Copy
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (DamageEventSource has buff Backstab ) Equal to True
      • (DamageEventTarget belongs to an enemy of (Owner of DamageEventSource).) Equal to True
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • Set VariableSet Backstab_Caster = DamageEventSource
      • Set VariableSet Backstab_Target = DamageEventTarget
      • Set VariableSet Backstab_Point[1] = (Position of Backstab_Caster)
      • Set VariableSet Backstab_Point[2] = (Position of Backstab_Target)
      • Set VariableSet Backstab_Angle = (Angle from Backstab_Point[1] to Backstab_Point[2])
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Acos((Cos(((Angle from Backstab_Point[1] to Backstab_Point[2]) - (Facing of Backstab_Target)))))) Less than 105.00
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DamageEventDamageT Equal to DAMAGE_TYPE_ENHANCED
            • Then - Actions
              • Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Normal
            • Else - Actions
              • Trigger - Turn on Combo Point Loop <gen>
              • Unit - Cause Backstab_Caster to damage Backstab_Target, dealing ((Real((Agility of Backstab_Caster (Include bonuses)))) x (0.25 x (Real((Level of Backstab for Backstab_Caster))))) damage of attack type Hero and damage type Normal
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ComboPointCounter Less than 5
                • Then - Actions
                  • Set VariableSet ComboPointCounter = (ComboPointCounter + 1)
                  • Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
                  • Set VariableSet Backstab_ComboCaster = (Last created unit)
                  • Unit - Add Combo Points Buff to Backstab_ComboCaster
                  • Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
                  • Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
                  • Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
                • Else - Actions
                  • Unit - Create 1 Dummy for (Owner of Backstab_Caster) at Backstab_Point[1] facing Default building facing degrees
                  • Set VariableSet Backstab_ComboCaster = (Last created unit)
                  • Unit - Add Combo Points Buff to Backstab_ComboCaster
                  • Unit - Set level of Combo Points Buff for Backstab_ComboCaster to ComboPointCounter
                  • Unit - Order Backstab_ComboCaster to Human Priest - Inner Fire Backstab_Caster
                  • Unit - Add a 1.00 second Generic expiration timer to Backstab_ComboCaster
        • Else - Actions
      • Custom script: call RemoveLocation (udg_Backstab_Point[1])
      • Custom script: call RemoveLocation (udg_Backstab_Point[2])
 

Attachments

  • Illustration.PNG
    Illustration.PNG
    95 KB · Views: 121
Level 3
Joined
Mar 15, 2015
Messages
53
Oh hi bribe, Im currently having trouble with lags everytime an unit casts a DPS spell (i.e. Shadow Strike), I disable DE trigger and the lag gone. Is there anyway to spot what cause it? Im currently using DE ver 3.7, does upgrading to 3.8 help?
TY so much! love your works :vw_love:
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Oh hi bribe, Im currently having trouble with lags everytime an unit casts a DPS spell (i.e. Shadow Strike), I disable DE trigger and the lag gone. Is there anyway to spot what cause it? Im currently using DE ver 3.7, does upgrading to 3.8 help?
TY so much! love your works :vw_love:
Yes use 3.8.

@Ugabunda I'm currently investigating that and will likely end up with restricting recursive damage even more than I currently do.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
5.7 is here! There's a lot to say about it but I don't have a lot of time right now.

Mainly I'd ask everyone to please test it as thoroughly as possible - especially if you've previously reported bugs with the system.

Additionally, I've written in cross-compatibility scripts based on documented API of every other Damage system, but I've not tested these. They should work, however there could be syntax errors I've missed or I might be missing a function/gotten a parameter wrong here or there.
 
Level 12
Joined
May 16, 2020
Messages
660
5.7 is here! There's a lot to say about it but I don't have a lot of time right now.

Mainly I'd ask everyone to please test it as thoroughly as possible - especially if you've previously reported bugs with the system.

Additionally, I've written in cross-compatibility scripts based on documented API of every other Damage system, but I've not tested these. They should work, however there could be syntax errors I've missed or I might be missing a function/gotten a parameter wrong here or there.

Thanks for the update Bribe. I'm getting an error when updating (see attached):

"Undeclared function RegisterDamageEvent. Maybe you meant RegisterDamageEngine"

It's probably related to the "illusion" JASS Code you wrote on the previous page (page 67). Not sure how to update this though...
 

Attachments

  • Error_Message.PNG
    Error_Message.PNG
    121.5 KB · Views: 235

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Thanks for the update Bribe. I'm getting an error when updating (see attached):

"Undeclared function RegisterDamageEvent. Maybe you meant RegisterDamageEngine"

It's probably related to the "illusion" JASS Code you wrote on the previous page (page 67). Not sure how to update this though...
Something I haven't gotten the chance to talk about yet, so I'll go over it now, is that I chose to revert certain vJass API decisions of mine in favor of avoiding naming conflicts with other damage libraries.

However, in your case you would simply go back to using Flux's Illusion while at the same time update Flux's library to the one shown in the Damage Engine compatibility section:

JASS:
library_once DamageEvent requires DamageEngine
// Flux's DamagePackage, made fully Damage Engine compatible. Version 0.1.0.0
// Original: https://www.hiveworkshop.com/threads/damagepackage.287101/
endlibrary
library_once DamageModify uses DamageEvent
endlibrary
globals
    constant integer DAMAGE_TYPE_PHYSICAL = 1
    constant integer DAMAGE_TYPE_MAGICAL = 2
endglobals
//! textmacro DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG
static method operator type takes nothing returns integer
    if udg_IsDamageSpell then
        return DAMAGE_TYPE_MAGICAL
    endif
    return DAMAGE_TYPE_PHYSICAL
endmethod
static method registerTrigger takes trigger t returns nothing
    call DamageTrigger.registerTrigger(t, "", 1.00)
endmethod
static method unregisterTrigger takes trigger t returns nothing
    call DamageTrigger.unregisterTrigger(t, "", 1.00, true)
endmethod
static method register takes code c returns nothing
    call registerTrigger(DamageTrigger[c])
endmethod
static method registerModifierTrigger takes trigger t returns nothing
    call Damage.register(t, "Mod", 4.00)
endmethod
static method unregisterModifierTrigger takes trigger t returns nothing
    call Damage.unregisterTrigger(t, "Mod", 4.00, true)
endmethod
static method registerModifier takes code c returns nothing
    call registerModifierTrigger(DamageTrigger[c])
endmethod
static method lockAmount takes nothing returns nothing
    set udg_DamageEventOverride = true
endmethod
//! endtextmacro
 
Level 6
Joined
Jul 12, 2017
Messages
139
so I wanna update my damage engine 5.4 to latest version but not sure what this really mean.

upload_2020-6-16_5-47-49.png


Do I need to delete variable and old folders and replace it with new one or can still keep it and just add in the new one cuz if I delete it, I'll have to fix all my triggers.
 
Level 20
Joined
May 16, 2012
Messages
635

I think that Bribe meant for users to simply replace the code for the new one and not to create multiple copies of them. Anyway, you can create a backup of your map and then replace the system code for the new one and test, so you will be sure your triggers work as before, and if not report bugs here so Bribe can access them.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
so I wanna update my damage engine 5.4 to latest version but not sure what this really mean.

View attachment 357544

Do I need to delete variable and old folders and replace it with new one or can still keep it and just add in the new one cuz if I delete it, I'll have to fix all my triggers.

You would just delete your existing Damage Engine Config trigger (not the category/folder) and then copy over the 5.7 Damage Enfine Config trigger.

Oh I see you have many copies of that folder. Yes as chopinski said delete the extra ones. I need to improve my import instructions I think.
 
Level 4
Joined
Nov 20, 2006
Messages
70
I'm pretty confused by what the Lua version is.

Latest Damage Engine version is 5.7.0.0, but the download also includes a LUA version map uploaded Aug 6, 2019 as version 1.0.2.3 but titled "Lua Damage Engine based on 5.4.2.3".

Are the features or performance different between the Lua and JASS versions?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I'm pretty confused by what the Lua version is.

Latest Damage Engine version is 5.7.0.0, but the download also includes a LUA version map uploaded Aug 6, 2019 as version 1.0.2.3 but titled "Lua Damage Engine based on 5.4.2.3".

Are the features or performance different between the Lua and JASS versions?
The Lua version is very much out of date at this point as I haven't worked on it in quite some time.

Once I can be sure the bugs and what-nor are resolved with 5.7, I'll get to work on bringing the Lua version up to speed.

Lua doesn't need as much TLC as Damage Engine vJass however as many of the new (potential) performance gains already existed in Lua by default thanks to the way Damage Engine just calls functions instead of evaluating/executing them.
 
Level 9
Joined
Jul 18, 2005
Messages
319
Thanks for the update Bribe!

I can confirm that damage now registers when utilising a dummy to cast a spell within the DamageEvent. I suspect the recursion issue I ran into was likely linked to it... I'll need to see whether I can replicate it with 5.7.0.0 in the near future.
 
Level 12
Joined
May 16, 2020
Messages
660
Something I haven't gotten the chance to talk about yet, so I'll go over it now, is that I chose to revert certain vJass API decisions of mine in favor of avoiding naming conflicts with other damage libraries.

However, in your case you would simply go back to using Flux's Illusion while at the same time update Flux's library to the one shown in the Damage Engine compatibility section:

JASS:
library_once DamageEvent requires DamageEngine
// Flux's DamagePackage, made fully Damage Engine compatible. Version 0.1.0.0
// Original: https://www.hiveworkshop.com/threads/damagepackage.287101/
endlibrary
library_once DamageModify uses DamageEvent
endlibrary
globals
    constant integer DAMAGE_TYPE_PHYSICAL = 1
    constant integer DAMAGE_TYPE_MAGICAL = 2
endglobals
//! textmacro DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG
static method operator type takes nothing returns integer
    if udg_IsDamageSpell then
        return DAMAGE_TYPE_MAGICAL
    endif
    return DAMAGE_TYPE_PHYSICAL
endmethod
static method registerTrigger takes trigger t returns nothing
    call DamageTrigger.registerTrigger(t, "", 1.00)
endmethod
static method unregisterTrigger takes trigger t returns nothing
    call DamageTrigger.unregisterTrigger(t, "", 1.00, true)
endmethod
static method register takes code c returns nothing
    call registerTrigger(DamageTrigger[c])
endmethod
static method registerModifierTrigger takes trigger t returns nothing
    call Damage.register(t, "Mod", 4.00)
endmethod
static method unregisterModifierTrigger takes trigger t returns nothing
    call Damage.unregisterTrigger(t, "Mod", 4.00, true)
endmethod
static method registerModifier takes code c returns nothing
    call registerModifierTrigger(DamageTrigger[c])
endmethod
static method lockAmount takes nothing returns nothing
    set udg_DamageEventOverride = true
endmethod
//! endtextmacro

Mmm another new error unfortunately. I did it twice just to be sure, but this appears:

"unregisterTrigger is not a member of DamageTrigger"

Thanks btw for all the work - I honestly have no idea how you still got the overview over so many different systems and compatibility patches.
 

Attachments

  • Error_JASS.PNG
    Error_JASS.PNG
    46.9 KB · Views: 70

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Mmm another new error unfortunately. I did it twice just to be sure, but this appears:

"unregisterTrigger is not a member of DamageTrigger"

Thanks btw for all the work - I honestly have no idea how you still got the overview over so many different systems and compatibility patches.
Thank you for the report! I have updated each of the compatibility scripts as I noticed they were using placeholder API. You don't need to update the Damage Engine code however.
 
Level 6
Joined
Jul 12, 2017
Messages
139
You would just delete your existing Damage Engine Config trigger (not the category/folder) and then copy over the 5.7 Damage Enfine Config trigger.

Oh I see you have many copies of that folder. Yes as chopinski said delete the extra ones. I need to improve my import instructions I think.

oh so I only need to delete this yeah?

upload_2020-6-19_19-57-37.png


only delete config or two of them?
 
Level 19
Joined
Aug 16, 2007
Messages
881
I'm considering updating, but in terms of performance, would you say 5.7.0.0 is faster than version 5.4.2.3 I'm currently using?

I'm thinking that the more recent versions introduces a lot of overhead due to the GUI <-> vJASS synchronization (I know this can be disabled) and having the Damage Engine compatible with other Damage Engines.

Currently I'm using one function for each Damage Event, which I understand is the best thing to do in terms of performance, I would expect the same thing applies to the recent version in case I would update?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I'm considering updating, but in terms of performance, would you say 5.7.0.0 is faster than version 5.4.2.3 I'm currently using?

I'm thinking that the more recent versions introduces a lot of overhead due to the GUI <-> vJASS synchronization (I know this can be disabled) and having the Damage Engine compatible with other Damage Engines.

Currently I'm using one function for each Damage Event, which I understand is the best thing to do in terms of performance, I would expect the same thing applies to the recent version in case I would update?

Honestly? Any performance gains might be negligible for you. The difference in performance could be plus or minus, depending on if your main triggers will be adapted to take advantage of the new speed gains (for example, if you only detect physical damage or spell damage in some, that would benefit you there).

I've never done benchmarks on any version of DamageEngine, but I run all of my tests on a low-perfomance laptop and it does just fine.
 
Level 12
Joined
May 16, 2020
Messages
660
I see that the engine can recognize DamageTypeCriticalStrike. Is it also possible to somehow easily recongize if a pulvarize has been "cast"?

I thought that maybe I can use a combination of "spell damage", AOE DamageEvent and AOEDamageSource, but not sure if that is a good idea.
 
Level 6
Joined
Sep 5, 2019
Messages
13
Was just testing the new 5.7.0, really excited about the new IsAttack functionality. That's gonna be real convenient for mapmakers. Lol'd at the "One ring to rule them all" reference in the comments.

I believe your "Ranged" trigger example should be "DamageModifierEvent becomes Greater than 'OR EQUAL TO' 1.00".
Right now it's just greater than, and it was confusing me why units weren't taking reduced damage from ranged attacks while moving, but they were from spell damage.

Also, maybe update your "Opening Text" trigger with details on how the Keeper blocks all incoming damage. Not a big deal, it's pretty evident what it does by looking at the "Keeper" trigger.

Keep up the awesome work, Bribe!
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I see that the engine can recognize DamageTypeCriticalStrike. Is it also possible to somehow easily recongize if a pulvarize has been "cast"?

I thought that maybe I can use a combination of "spell damage", AOE DamageEvent and AOEDamageSource, but not sure if that is a good idea.

Pulverize has two "fingerprints":
- DamageEventDamageT Equal to DAMAGE_TYPE_ENHANCED
- DamageEventLevel Greater than 1

If your unit that you want to detect Pulverize from does not deal any other form of Enhanced damage, then you only need the first condition.

Was just testing the new 5.7.0, really excited about the new IsAttack functionality. That's gonna be real convenient for mapmakers. Lol'd at the "One ring to rule them all" reference in the comments.

I believe your "Ranged" trigger example should be "DamageModifierEvent becomes Greater than 'OR EQUAL TO' 1.00".
Right now it's just greater than, and it was confusing me why units weren't taking reduced damage from ranged attacks while moving, but they were from spell damage.

Also, maybe update your "Opening Text" trigger with details on how the Keeper blocks all incoming damage. Not a big deal, it's pretty evident what it does by looking at the "Keeper" trigger.

Keep up the awesome work, Bribe!

Thank you for the feedback and for the bug report! I will update these after I am done with Damage Engine 3A.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I am VERY pleased to announce the release of Damage Engine 3A, which can be found below (strictly a vJass MAJOR upgrade for those whose WarCraft 3 is out of date).

This is akin to Apple releasing a new iPhone 5-sized phone, or re-adding the headphone jack to their models.

I will keep the pastebin link supplied with both 3.8 as well as any future variants of 3A that might arise, but here's the code if you don't want to look too far:

JASS:
//===========================================================================
// Damage Engine 3A.0.0.0 - a new type of Damage Engine for users who don't
// have access to the latest version of WarCraft 3, which incorporates new
// features to inherited from Damage Engine 5.7 by hooking TriggerRegisterVariableEvent.
// However, it requires having JassHelper installed.
// 
// Stuff that doesn't work:
// - Pre-armor modification
// - Damage/Attack/Weapotype detection/modification
// - Armor/defense detection/modification
// - Melee/ranged detection
// - Filters for u.
// - Spirit link won't interact with custom damage.
// - Still needs workarounds for Anti-Magic Shell/Mana Shield/Life Drain/etc.
// 
// Stuff that is changed from how it worked with 3.8:
// - Recursive damage now uses the Damage Engine 5 anti-recursion method. So
//   all recursive damage will be postponed until the sequence has completed.
// - No more need for using ClearDamageEvent
// - No more need to disable the DamageEventTrigger in order to avoid things
//   going recursively.
// 
library DamageEngine
globals
    private timer           alarm           = CreateTimer()
    private boolean         alarmSet        = false
    //Values to track the original pre-spirit Link/defensive damage values
    private Damage          lastInstance    = 0
    private boolean         canKick         = false
    //These variables coincide with Blizzard's "limitop" type definitions so as to enable users (GUI in particular) with some nice performance perks.
    public constant integer FILTER_ATTACK   = 0     //LESS_THAN
    public constant integer FILTER_OTHER    = 2     //EQUAL
    public constant integer FILTER_SPELL    = 4     //GREATER_THAN
    public constant integer FILTER_CODE     = 5     //NOT_EQUAL
    public constant integer FILTER_MAX      = 6
    private integer         eventFilter     = FILTER_OTHER
    private constant integer LIMBO          = 16        //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
    public boolean          inception       = false     //When true, it allows your trigger to potentially go recursive up to LIMBO. However it must be set per-trigger throughout the game and not only once per trigger during map initialization.
    private boolean         dreaming        = false
    private integer         sleepLevel      = 0
    private group           proclusGlobal   = CreateGroup() //track sources of recursion
    private group           fischerMorrow   = CreateGroup() //track targets of recursion
    private boolean         kicking         = false
    private boolean         eventsRun       = false
    private unit            protectUnit     = null
    private real            protectLife     = 0.00
    private boolean         blocked         = false
    private keyword         run
    private keyword         trigFrozen
    private keyword         levelsDeep
    private keyword         inceptionTrig
    private keyword         checkLife
    private keyword         lifeTrigger
endglobals
private function CheckAddUnitToEngine takes unit u returns boolean
    if GetUnitAbilityLevel(u, 'Aloc') > 0 then
    elseif not TriggerEvaluate(gg_trg_Damage_Engine_Config) then
    //Add some more elseifs to rule out stuff you don't want to get registered, such as:
    //elseif IsUnitType(u, UNIT_TYPE_STRUCTURE) then
    else
        return true
    endif
    return false
endfunction
struct DamageTrigger extends array
   
    //The below variables are constant
    readonly static thistype        MOD             = 1
    readonly static thistype        DAMAGE          = 5
    readonly static thistype        ZERO            = 6
    readonly static thistype        AFTER           = 7
    readonly static thistype        AOE             = 9
    private static integer          count           = 9
    static thistype                 lastRegistered  = 0
    private static thistype array   trigIndexStack
    static thistype                 eventIndex = 0
    static boolean array            filters
    readonly string                 eventStr
    readonly real                   weight
    readonly boolean                configured
    boolean                         usingGUI
    //The below variables are private
    private thistype                next
    private trigger                 rootTrig
    boolean                         trigFrozen      //Whether the trigger is currently disabled due to recursion
    integer                         levelsDeep      //How deep the user recursion currently is.
    boolean                         inceptionTrig   //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
    
    static method operator enabled= takes boolean b returns nothing
        if b then
            call EnableTrigger(udg_DamageEventTrigger)
        else
            call DisableTrigger(udg_DamageEventTrigger)
        endif
    endmethod
    static method operator enabled takes nothing returns boolean
        return IsTriggerEnabled(udg_DamageEventTrigger)
    endmethod
   
    static method setGUIFromStruct takes boolean full returns nothing
        set udg_DamageEventAmount       = Damage.index.damage
        set udg_DamageEventType         = Damage.index.userType
        set udg_DamageEventOverride     = Damage.index.override
        if full then
            set udg_DamageEventSource   = Damage.index.sourceUnit
            set udg_DamageEventTarget   = Damage.index.targetUnit
            set udg_DamageEventPrevAmt  = Damage.index.prevAmt
            set udg_IsDamageSpell       = Damage.index.isSpell
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
            //! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
        endif
    endmethod
    static method getVerboseStr takes string eventName returns string
        if eventName == "Modifier" or eventName == "Mod" then
            return "udg_DamageModifierEvent"
        endif
        return "udg_" + eventName + "DamageEvent"
    endmethod
    private static method getStrIndex takes string var, real lbs returns thistype
        local integer root = R2I(lbs)
        if var == "udg_DamageModifierEvent" then
            set root= MOD
        elseif var == "udg_DamageEvent" then
            if root == 2 or root == 0 then
                set root= ZERO
            else
                set root= DAMAGE //Above 0.00 but less than 2.00, generally would just be 1.00
            endif
        elseif var == "udg_AfterDamageEvent" then
            set root    = AFTER
        elseif var == "udg_AOEDamageEvent" then
            set root    = AOE
        else
            set root    = 0
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
            //! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
        endif
        return root
    endmethod
    private method toggleAllFilters takes boolean flag returns nothing
        set filters[this + FILTER_ATTACK]   = flag
        set filters[this + FILTER_OTHER]    = flag
        set filters[this + FILTER_SPELL]    = flag
        set filters[this + FILTER_CODE]     = flag
    endmethod
    method operator filter= takes integer f returns nothing
        set this = this*FILTER_MAX
        if f == FILTER_OTHER then
            call this.toggleAllFilters(true)
        else
            if f == FILTER_ATTACK then
                set filters[this + FILTER_ATTACK]   = true
            else
                set filters[this + f] = true
            endif
        endif
    endmethod
    static method registerVerbose takes trigger whichTrig, string var, real lbs, boolean GUI, integer filt returns thistype
        local thistype index= getStrIndex(var, lbs)
        local thistype i    = 0
        local thistype id   = 0
     
        if index == 0 then
            return 0
        elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
            set filters[lastRegistered*FILTER_MAX + filt] = true //allows GUI to register multiple different types of Damage filters to the same trigger
            return 0
        endif
     
        if trigIndexStack[0] == 0 then
            set count              = count + 1   //List runs from index 10 and up
            set id                 = count
        else
            set id                 = trigIndexStack[0]
            set trigIndexStack[0]  = trigIndexStack[id]
        endif
        set lastRegistered         = id
        set id.filter              = filt
        set id.rootTrig            = whichTrig
        set id.usingGUI            = GUI
        set id.weight              = lbs
        set id.eventStr            = var
     
        loop
            set i = index.next
            exitwhen i == 0 or lbs < i.weight
            set index = i
        endloop         
        set index.next = id
        set id.next    = i
     
        //call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
        return lastRegistered
    endmethod
    static method registerTrigger takes trigger t, string var, real lbs returns thistype
        return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
    endmethod
    private static thistype prev = 0
    static method getIndex takes trigger t, string eventName, real lbs returns thistype
        local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
        loop
            set prev = index
            set index = index.next
            exitwhen index == 0 or index.rootTrig == t
        endloop
        return index
    endmethod
    static method unregister takes trigger t, string eventName, real lbs, boolean reset returns boolean
        local thistype index        = getIndex(t, eventName, lbs)
        if index == 0 then
            return false
        endif
        set prev.next               = index.next
         
        set trigIndexStack[index]   = trigIndexStack[0]
        set trigIndexStack[0]       = index
     
        if reset then
            set index.configured    = false
            set index               = index*FILTER_MAX
            call index.toggleAllFilters(false)
        endif
        return true
    endmethod
    static method damageUnit takes unit u, real life returns nothing
        call SetWidgetLife(u, RMaxBJ(life, 0.41))
        if life <= 0.405 then
            if udg_DamageEventType < 0 then
                call SetUnitExploded(u, true)
            endif
            //Kill the unit
            set DamageTrigger.enabled = false
            call UnitDamageTarget(udg_DamageEventSource, u, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
            set DamageTrigger.enabled = true
        endif
    endmethod
    static method checkLife takes nothing returns boolean
        if protectUnit != null then
            if Damage.lifeTrigger != null then
                call DestroyTrigger(Damage.lifeTrigger)
                set Damage.lifeTrigger = null
            endif
            if GetUnitAbilityLevel(protectUnit, udg_DamageBlockingAbility) > 0 then
                call UnitRemoveAbility(protectUnit, udg_DamageBlockingAbility)
                call SetWidgetLife(protectUnit, protectLife)
            elseif udg_IsDamageSpell or blocked then
                call DamageTrigger.damageUnit(protectUnit, protectLife)
            endif
            if blocked then
                set blocked = false
            endif
            set protectUnit = null
            return true
        endif
        return false
    endmethod
    method run takes nothing returns nothing
        local integer cat = this
        local Damage d = Damage.index
        if cat == MOD or not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
            set dreaming = true
            //call BJDebugMsg("Start of event running")
            loop                                   
                set this = this.next
                exitwhen this == 0
                if cat == MOD then
                    exitwhen d.override or udg_DamageEventOverride
                    exitwhen this.weight >= 4.00 and udg_DamageEventAmount <= 0.00
                endif
                set eventIndex = this
                if not this.trigFrozen and filters[this*FILTER_MAX + eventFilter] and IsTriggerEnabled(this.rootTrig) then
                    //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
                    //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
                    //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
                    //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
                    //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
                    //! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
                 
                    if TriggerEvaluate(this.rootTrig) then
                        call TriggerExecute(this.rootTrig)
                    endif
                    if cat == MOD then
                        //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
                        //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
                        //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
                        //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
                        //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
                        //! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
                        if this.usingGUI then
                            set d.damage        = udg_DamageEventAmount
                            set d.userType      = udg_DamageEventType
                            set d.override      = udg_DamageEventOverride
                        elseif this.next == 0 or this.next.usingGUI then //Might offer a slight performance improvement
                            call setGUIFromStruct(false)
                        endif
                    endif
                    call checkLife()
                endif
            endloop
            //call BJDebugMsg("End of event running")
            set dreaming                                = false
        endif
    endmethod
    
    static method finish takes nothing returns nothing
        if checkLife() and not blocked and udg_DamageEventAmount != 0.00 then
            call DamageTrigger.AFTER.run()
        endif
    endmethod
    
    static trigger array    autoTriggers
    static boolexpr array   autoFuncs
    static integer          autoN = 0
    static method operator [] takes code c returns trigger
        local integer i             = 0
        local boolexpr b            = Filter(c)
        loop
            if i == autoN then
                set autoTriggers[i] = CreateTrigger()
                set autoFuncs[i]    = b
                call TriggerAddCondition(autoTriggers[i], b)
                exitwhen true
            endif
            set i = i + 1
            exitwhen b == autoFuncs[i]
        endloop
        return autoTriggers[i]
    endmethod
endstruct
struct Damage extends array
    readonly unit           sourceUnit    //stores udg_DamageEventSource
    readonly unit           targetUnit    //stores udg_DamageEventTarget
    real                    damage        //stores udg_DamageEventAmount
    readonly real           prevAmt       //stores udg_DamageEventPrevAmt
    integer                 userType      //stores udg_DamageEventType
    readonly boolean        isCode
    readonly boolean        isSpell       //stores udg_IsDamageSpell
    boolean                 override
    readonly static unit    aoeSource   = null
    readonly static Damage  index       = 0
    private static Damage   damageStack = 0
    private static integer  count = 0 //The number of currently-running queued or sequential damage instances
    private Damage          stackRef
    private DamageTrigger   recursiveTrig
   
    static trigger lifeTrigger = null   //private
    static method operator source takes nothing returns unit
        return udg_DamageEventSource
    endmethod
    static method operator target takes nothing returns unit
        return udg_DamageEventTarget
    endmethod
    static method operator amount takes nothing returns real
        return Damage.index.damage
    endmethod
    static method operator amount= takes real r returns nothing
        set Damage.index.damage = r
    endmethod
     
    private static method onAOEEnd takes nothing returns nothing
        if udg_DamageEventAOE > 1 then
            call DamageTrigger.AOE.run()
        endif
        set udg_DamageEventAOE = 0
        set udg_DamageEventLevel = 0
        set udg_EnhancedDamageTarget = null
        set aoeSource = null
        call GroupClear(udg_DamageEventAOEGroup)
    endmethod
    static method finish takes nothing returns nothing
        local Damage i                                  = 0
        local integer exit                              
        if canKick then
            set canKick = false
            set kicking = true
            call DamageTrigger.finish()
            if damageStack != 0 then
                loop
                    set exit                            = damageStack
                    set sleepLevel                      = sleepLevel + 1
                    loop
                        set eventFilter                 = FILTER_CODE
                        set Damage.index                = i.stackRef
                        call DamageTrigger.setGUIFromStruct(true)
                        call DamageTrigger.MOD.run()
                        call DamageTrigger.DAMAGE.run()
                        if udg_DamageEventAmount != 0.00 then
                            call DamageTrigger.damageUnit(udg_DamageEventTarget, GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount)
                            call DamageTrigger.AFTER.run()
                        endif
                        set i                           = i + 1
                        exitwhen i == exit
                    endloop
                    exitwhen i == damageStack
                endloop
                loop
                    set i                               = i - 1
                    set i.stackRef.recursiveTrig.trigFrozen  = false
                    set i.stackRef.recursiveTrig.levelsDeep  = 0
                    exitwhen i == 0                     
                endloop                                 
                set damageStack                         = 0
            endif
            set dreaming                                = false
            set sleepLevel                              = 0
            call GroupClear(proclusGlobal)
            call GroupClear(fischerMorrow)
            set kicking = false
            //call BJDebugMsg("Cleared up the groups")
        endif
    endmethod
    
    private static method wakeUp takes nothing returns nothing
        set alarmSet = false
        set dreaming = false
        set DamageTrigger.enabled = true
        
        call finish()
        call onAOEEnd()
        
        set Damage.count    = 0
        set Damage.index    = 0
        set udg_DamageEventTarget = null
        set udg_DamageEventSource = null
    endmethod
    private static method createLifeTrigger takes unit u, limitop op, real amount returns nothing
        if not blocked then
            set lifeTrigger = CreateTrigger()
            call TriggerAddCondition(lifeTrigger, Filter(function DamageTrigger.finish))
            call TriggerRegisterUnitStateEvent(lifeTrigger, u, UNIT_STATE_LIFE, op, amount)
        endif
        set protectUnit = u
    endmethod
    private method mitigate takes real newAmount, boolean recursive returns nothing
        local real prevLife
        local real life
        local unit u = targetUnit
        local real prevAmount = prevAmt
        set life = GetWidgetLife(u)
        if not isSpell then
            if newAmount != prevAmount then
                set life = life + prevAmount - newAmount
                if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
                    set protectLife = life - prevAmount
                    call UnitAddAbility(u, udg_DamageBlockingAbility)
                endif
                call SetWidgetLife(u, RMaxBJ(life, 0.42))
            endif
            call createLifeTrigger(u, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
        else
            set protectLife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
            set prevLife = life
            if life + prevAmount*0.75 > protectLife then
                set life = RMaxBJ(protectLife - prevAmount/2.00, 1.00)
                call SetWidgetLife(u, life)
                set life = (life + protectLife)/2.00
            else
                set life = life + prevAmount*0.50
            endif
            set protectLife = prevLife - (prevAmount - (prevAmount - newAmount))
            call createLifeTrigger(u, GREATER_THAN, life)
        endif
        set u = null
    endmethod
    private method getSpellAmount takes real amt returns real
        local integer i = 6
        local real mult = 1.00
        set isSpell = amt < 0.00
        if isSpell then
            set amt = -amt
            if IsUnitType(target, UNIT_TYPE_ETHEREAL) and not IsUnitType(target, UNIT_TYPE_HERO) then
                set mult = mult*udg_DAMAGE_FACTOR_ETHEREAL //1.67
            endif
            if GetUnitAbilityLevel(target, 'Aegr') > 0 then
                set mult = mult*udg_DAMAGE_FACTOR_ELUNES //0.80
            endif
            if udg_DmgEvBracers != 0 and IsUnitType(target, UNIT_TYPE_HERO) then
                //Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
                loop
                    set i = i - 1
                    if GetItemTypeId(UnitItemInSlot(target, i)) == udg_DmgEvBracers then
                        set mult = mult*udg_DAMAGE_FACTOR_BRACERS //0.67
                        exitwhen true
                    endif
                    exitwhen i == 0
                endloop
            endif
            return amt*mult
        endif
        return amt
    endmethod
    private method addRecursive takes nothing returns boolean
        if this.damage != 0.00 then
            set this.recursiveTrig = DamageTrigger.eventIndex
            if not this.isCode then 
                set this.isCode = true
            endif
            set inception = inception or DamageTrigger.eventIndex.inceptionTrig
            if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
                if inception and not DamageTrigger.eventIndex.trigFrozen then
                    set DamageTrigger.eventIndex.inceptionTrig = true
                    if DamageTrigger.eventIndex.levelsDeep < sleepLevel then
                        set DamageTrigger.eventIndex.levelsDeep = DamageTrigger.eventIndex.levelsDeep + 1
                        if DamageTrigger.eventIndex.levelsDeep >= LIMBO then
                            set DamageTrigger.eventIndex.trigFrozen = true
                        endif
                    endif
                else
                    set DamageTrigger.eventIndex.trigFrozen = true
                endif
            endif
            set damageStack.stackRef = this
            set damageStack = damageStack + 1
            //call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
            return true
        endif
        set inception = false
        return false
    endmethod
    private static method onDamageResponse takes nothing returns boolean
        local Damage d      = Damage.count + 1
        set Damage.count    = d
        set d.sourceUnit    = GetEventDamageSource()
        set d.targetUnit    = GetTriggerUnit()
        set d.damage        = d.getSpellAmount(GetEventDamage())
        set d.prevAmt       = d.damage
        set d.userType      = udg_NextDamageType
        set d.isCode        = udg_NextDamageType != 0 or udg_NextDamageOverride or dreaming
        set d.override      = udg_NextDamageOverride
        
        set udg_NextDamageOverride      = false
        set udg_NextDamageType          = 0
        
        call finish() //in case the unit state event failed and the 0.00 second timer hasn't yet expired
        if dreaming then
            if d.addRecursive() then
                set blocked = true
                call d.mitigate(0.00, true)
            else
                set Damage.count = d - 1
            endif
            return false
        endif
        
        //Added 25 July 2017 to detect AOE damage or multiple single-target damage
        if alarmSet then
            if d.sourceUnit != aoeSource then
                call onAOEEnd()
                set aoeSource           = d.sourceUnit 
            elseif d.targetUnit == udg_EnhancedDamageTarget then
                set udg_DamageEventLevel= udg_DamageEventLevel + 1
            elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
                set udg_DamageEventAOE  = udg_DamageEventAOE + 1
            endif
        else
            call TimerStart(alarm, 0.00, false, function Damage.wakeUp)
            set alarmSet                = true
            set aoeSource               = d.sourceUnit 
            set udg_EnhancedDamageTarget= d.targetUnit
        endif
        
        set Damage.index = d
        call DamageTrigger.setGUIFromStruct(true)
        call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
        call GroupAddUnit(proclusGlobal, udg_DamageEventSource)
        call GroupAddUnit(fischerMorrow, udg_DamageEventTarget)
        
        if udg_DamageEventAmount == 0.00 then
            call DamageTrigger.ZERO.run()
            set canKick = true
            call finish()
        else
            if d.isCode then
                set eventFilter = FILTER_CODE
            elseif udg_IsDamageSpell then
                set eventFilter = FILTER_SPELL
            else
                set eventFilter = FILTER_ATTACK
            endif
            call DamageTrigger.MOD.run()
            call DamageTrigger.DAMAGE.run()
     
            //The damage amount is finalized.
            call d.mitigate(udg_DamageEventAmount, false)
            set canKick = true
        endif
        return false
    endmethod
    static method createDamageTrigger takes nothing returns nothing //private
        set udg_DamageEventTrigger = CreateTrigger()
        call TriggerAddCondition(udg_DamageEventTrigger, Filter(function thistype.onDamageResponse))
    endmethod
    static method setup takes nothing returns boolean //private
        local integer i = udg_UDex
        local unit u
        if udg_UnitIndexEvent == 1.00 then
            set u = udg_UDexUnits[i]
            if CheckAddUnitToEngine(u) then
                set udg_UnitDamageRegistered[i] = true
                call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
                call UnitAddAbility(u, udg_SpellDamageAbility)
                call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
            endif
            set u = null
        else
            set udg_HideDamageFrom[i] = false
            if udg_UnitDamageRegistered[i] then
                set udg_UnitDamageRegistered[i] = false
                set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
                if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
                    set udg_DamageEventsWasted = 0
           
                    //Rebuild the mass EVENT_UNIT_DAMAGED trigger:
                    call DestroyTrigger(udg_DamageEventTrigger)
                    call createDamageTrigger()
                    set i = udg_UDexNext[0]
                    loop
                        exitwhen i == 0
                        if udg_UnitDamageRegistered[i] then
                            call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
                        endif
                        set i = udg_UDexNext[i]
                    endloop
                endif
            endif
        endif
        return false
    endmethod
   
    //! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
    //! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
    //! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
    //! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
    //! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
    //! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
   
endstruct
public function RegisterFromHook takes trigger whichTrig, string var, limitop op, real value returns nothing
    call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns DamageTrigger
    return DamageTrigger.registerVerbose(whichTrig, DamageTrigger.getVerboseStr(eventName), value, false, f)
endfunction
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns DamageTrigger
    return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns DamageTrigger
    return TriggerRegisterDamageEngineEx(DamageTrigger[c], eventName, value, f)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEngine takes code c, string eventName, real value returns DamageTrigger
    return RegisterDamageEngineEx(c, eventName, value, FILTER_OTHER)
endfunction
endlibrary
function InitTrig_Damage_Engine takes nothing returns nothing
    local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
    local integer i = bj_MAX_PLAYERS //Fixed in 3.8
    
    //Create this trigger with UnitIndexEvents in order to add and remove units
    //as they are created or removed.
    local trigger t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
    call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
    call TriggerAddCondition(t, Filter(function Damage.setup))
    set t = null
    
    //Run the configuration actions to set all configurables:
    call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
    
    //Create trigger for storing all EVENT_UNIT_DAMAGED events.
    call Damage.createDamageTrigger()
 
    //Disable SpellDamageAbility for every player.
    loop
        set i = i - 1
        call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
        exitwhen i == 0
    endloop
 
    //Preload abilities.
    call UnitAddAbility(u, udg_DamageBlockingAbility)
    call UnitAddAbility(u, udg_SpellDamageAbility)
    call RemoveUnit(u)
    set u = null
endfunction
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Updated to 5.7.0.1 which is strictly a performance improvement for the 0 damage event (nothing else). This improves performance even if you aren't using that event, so I will recommend this update to all users.

I've updated the demo map to reflect the aforementioned change requests and also beautified the map a little bit. Also I disabled the Recursion trigger which I only had enabled as a stress test.

I've cleaned up the main post quite a bit into organized tabs.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
I am VERY pleased to announce the release of Damage Engine 3A, which can be found below (strictly a vJass MAJOR upgrade for those whose WarCraft 3 is out of date).
Amazed.

Thanks for supporting legacy editors (like myself that currently mods in 1.30.4),
I'll surely adapt now that I can use your system as updated + (v)JASS for my upcoming resources and this makes me proud of it.

However, I'm in doubt if there's a way we can get the actual unit damage even with "bonuses" without sorting into convoluted ways to do it?
Cause right now I can only think of achieving it by tracking the damage done by the unit on damage event and using a unit indexer to store it.
But that would need a unit to hit some unit to get the damage done which is not convenient right? Any ideas to achieve this flawlessly?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Amazed.

Thanks for supporting legacy editors (like myself that currently mods in 1.30.4),
I'll surely adapt now that I can use your system as updated + (v)JASS for my upcoming resources and this makes me proud of it.

However, I'm in doubt if there's a way we can get the actual unit damage even with "bonuses" without sorting into convoluted ways to do it?
Cause right now I can only think of achieving it by tracking the damage done by the unit on damage event and using a unit indexer to store it.
But that would need a unit to hit some unit to get the damage done which is not convenient right? Any ideas to achieve this flawlessly?

Well in your case you can benefit from the natives introduced in 1.29 for SetEventDamage. I am not sure if EVENT_PLAYER_UNIT_DAMAGED was a thing in 1.29 but if not then there is an easily-modifiable version of 3A I could fix up in your case which would get rid of the dependency on the cheat death ability and other weird HP modification stuff.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
Well in your case you can benefit from the natives introduced in 1.29 for SetEventDamage. I am not sure if EVENT_PLAYER_UNIT_DAMAGED was a thing in 1.29 but if not then there is an easily-modifiable version of 3A I could fix up in your case which would get rid of the dependency on the cheat death ability and other weird HP modification stuff.
What I've meant is to get a unit's attack damage (that includes the bonus damage gained from abilities/items/and such...)
without making the unit hit someone just to track the damage it dealt. Let's say I want to create a projectile ability
that deals the actual unit's physical damage to the target. I don't know if someone already achieved this kind of mechanic
though I'm not sure if they can without sorting into damage event somehow or mapping the bonuses a unit acquired.

I'm aware of the new native GetUnitBaseDamage() but it only works for white damage though.

And I'm not interested of using the new natives yet as I want my resources to be compatible in both sides...
I'm just glad that you've managed to incorporate some DamageEngine 5+ features to 3A variant that is doable without breaking compatibility.
I think I have to live with the dependencies for now as it's up to the user for which DamageEngine version they have in their maps.
As long it's cross-compatible: let's say I'm using this 3A variant's API and a user that have DamageEngine 5+ wouldn't have a problem with it.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
What I've meant is to get a unit's attack damage (that includes the bonus damage gained from abilities/items/and such...)
without making the unit hit someone just to track the damage it dealt. Let's say I want to create a projectile ability
that deals the actual unit's physical damage to the target. I don't know if someone already achieved this kind of mechanic
though I'm not sure if they can without sorting into damage event somehow or mapping the bonuses a unit acquired.

I'm aware of the new native GetUnitBaseDamage() but it only works for white damage though.

And I'm not interested of using the new natives yet as I want my resources to be compatible in both sides...
I'm just glad that you've managed to incorporate some DamageEngine 5+ features to 3A variant that is doable without breaking compatibility.
I think I have to live with the dependencies for now as it's up to the user for which DamageEngine version they have in their maps.
As long it's cross-compatible: let's say I'm using this 3A variant's API and a user that have DamageEngine 5+ wouldn't have a problem with it.

Without the natives for BlzGetUnitDamage/Dice/Sides (I think that's what they are called, I'd have to double check) you cannot determine a unit's base damage. You can get an average DPS from a unit's non-spell attacks over a prolonged fight, however this will not help if the unit had very recently picked up a damage enhancing item.

So if your goal is to be compatible with users while assessing a unit's physical damage in the process, my recommendation is to incorporate two variants in your map and separate which option is enabled via a Static If. This of course requires you to save two copies of your map - one for compatibility and the other with the new features:

Variant 1: Track the unit's non-spell damage per second and use that to determine the ancillary damage from this projectile.
Variant 2: Use the new natives and calculate the unit's base damage with its dice each time (so as to give variety to the damage amount in relation to the swinginess of the damage itself)

Variant 1 is for pre-1.32 users and is much less useful whereas Variant 2 will be much more useful and accurate, but without compatibility with old versions of WC3.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
Without the natives for BlzGetUnitDamage/Dice/Sides (I think that's what they are called, I'd have to double check) you cannot determine a unit's base damage. You can get an average DPS from a unit's non-spell attacks over a prolonged fight, however this will not help if the unit had very recently picked up a damage enhancing item.
I'm thinking of a weird workaround using a modular illusion system
with damage modification + a premade hidden unit that have unlimited
health/regen to take the damage of this illusion and let them perform
this damage event outside the playable map. As hero illusion wouldn't
show in the top-left interface icon except for the minimap hero icon
that could be taken care of. It could be possible for this system to get
the pre-damage done before reductions take place right? I only find
bonus damage from externals like auras for example which the
illusion may not get because it's outside the map. I'll check this out.

Regarding the two variants for compatibility and features,
I've been thinking of implementing static ifs for new natives but
I'm afraid that it would still make the map unopenable because
It's saved on latest editor regardless of codes. Not sure with it,
But I'll consider to take this neat advice to get the advantage and
to remove the limits in expanded range of possibilities we have today.

I can only use 1.31.1 editor as I have it on my desktop. My internet
sucks and can't make it to download 30+ GB but I'll replace this crap
someday and might switch to mod in reforged editor as I'm also
craving for the new natives to incorporate in my spell-making vocabulary.
 
Level 20
Joined
May 16, 2012
Messages
635
I'm thinking of a weird workaround using a modular illusion system
with damage modification + a premade hidden unit that have unlimited
health/regen to take the damage of this illusion and let them perform
this damage event outside the playable map. As hero illusion wouldn't
show in the top-left interface icon except for the minimap hero icon
that could be taken care of. It could be possible for this system to get
the pre-damage done before reductions take place right? I only find
bonus damage from externals like auras for example which the
illusion may not get because it's outside the map. I'll check this out.

Regarding the two variants for compatibility and features,
I've been thinking of implementing static ifs for new natives but
I'm afraid that it would still make the map unopenable because
It's saved on latest editor regardless of codes. Not sure with it,
But I'll consider to take this neat advice to get the advantage and
to remove the limits in expanded range of possibilities we have today.

I can only use 1.31.1 editor as I have it on my desktop. My internet
sucks and can't make it to download 30+ GB but I'll replace this crap
someday and might switch to mod in reforged editor as I'm also
craving for the new natives to incorporate in my spell-making vocabulary.

The problem of your idea will be damage variation with dice and side numbers. This will make calculations imprecise since every time you try to get the amount of damage dealt it might be different because of this 2 values that adds a random aspect to the final result, unless the unit have this numbers nullified. Unfortunately, for now I think the only way to do that precisely would be to keep track of bonusses added and removed until blizzard decides to stop being lazy and actually give us natives that manipulate the bonus values. I somewhat was able to keep track of bonusses using my library New Bonus but that requires that map maker to add and remove everything through the code.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
I think it wouldn't be a problem though, based on 'how it works' tab
Bribe stated there that raw damage is already processed as dice and sides
are already computed... before EVENT_UNIT_DAMAGING runs.

EDIT:

I'm also fine with that as I'm aiming for a natural damage which
the function should return so dices and sides are intented to be part of it...
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I don't know if this is just me, but so far when I've tried using the ArmorPierced feature, I haven't seen it make any difference. Even in the provided examples I don't seem to see any evidence of it ignoring values of armor on the target. Is this a problem other people have been having?

I have double checked the code and this should still be working. Make sure the following applies for you:

Your ArmorPierced value needs to be set *before* DamageModifierEvent 4.00. So 3.999999 or less and you can set that value, anything later or using any other event variable name will fail and not allow that to work.

ArmorPierced also only applies with the Normal damage type, so if you use any other damage type like Universal or Magic then the unit could have -10039423 armor or 39043 armor and the outcome is the same.
 
Level 35
Joined
Feb 5, 2009
Messages
4,551
I have double checked the code and this should still be working. Make sure the following applies for you:

Your ArmorPierced value needs to be set *before* DamageModifierEvent 4.00. So 3.999999 or less and you can set that value, anything later or using any other event variable name will fail and not allow that to work.

ArmorPierced also only applies with the Normal damage type, so if you use any other damage type like Universal or Magic then the unit could have -10039423 armor or 39043 armor and the outcome is the same.

Yeah, I tried that, it was both according to the demonstration provided with the Knight, as well as my own test with a Grunt.

The DamageModifierEvent, following the Knight's example, was "DamageModifierEvent becomes Less than 1.00", which I will try with "Equal to 1.00" instead.

For the Knight the DamageEventType was set to DamageTypeCriticalStrike which I am admittedly unsure as to what that does, but in the reports it seems to indicate that it is dealing Damage_Type_Normal, and I tried setting it as such for the Grunt tests, too, without the DamageTypeCriticalStrike.

Strangely enough, when testing the Grunt against the nearby Ogre Mauler with Reports on, it kept showing the damage the Ogre Mauler was dealing to me, even when I attacked it. For some reason it mixed up their values.

The tests for DamageModiferEvent being equal to 1.00 didn't help unfortunately, sort of did the same thing. I'll provide a screenshot of the report stats I'm looking at.

report dang it.png
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
If I already have 1.31.1, does it mean that I can only use DamageEngine v5.4.2.3?
I've downloaded the latest (v5.7.0.1) but I can't open it. Is it because of the new native
implemented specifically from what you've stated in 5.5+ version of it?

^ It is because the map is saved in reforged editor that makes it unopenable.


Is implementing this function at the top of the (v)JASS script that is v5.7.0.1 would enable
me to use the latest library since I think that's only the reason what makes it incompatible?

JASS:
function BlzGetEventIsAttack takes nothing returns boolean
   return BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL
endfunction
^ Should be implemented inside the DamageEngine library, not just on the top of the script.


How about providing us a demo map of 5.7.0.1 that is saved on 1.31.1 and having that
script already in the top of the library to make the demo map openable somehow... Or it's not possible?

^ I apologize for this one as I've just realized this shouldn't be asked.
This would only force you to downgrade which I think is not necessary for just the sake of it.


There are bugs fixed after DamageEngine v5.4.2.3 that makes me hesitate to use it than the latest one.

Nevermind about this, I've managed to get the latest DamageEngine version work with
1.31.1 editor without problems as of now; by only requiring the user a few minutes to achieve it
by using v5.4.3.2 as a demo map for importing, recreating the new system variables
then implementing the custom function you've just made to replicate the new native into the library.

Now I can use this as a counterpart for 3A in legacy variant and this one on a
reforged variant for my resources as I would like to consider this advice of yours. :)

EDIT:

Edited my post with strikethroughs.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Yeah, I tried that, it was both according to the demonstration provided with the Knight, as well as my own test with a Grunt.

The DamageModifierEvent, following the Knight's example, was "DamageModifierEvent becomes Less than 1.00", which I will try with "Equal to 1.00" instead.

For the Knight the DamageEventType was set to DamageTypeCriticalStrike which I am admittedly unsure as to what that does, but in the reports it seems to indicate that it is dealing Damage_Type_Normal, and I tried setting it as such for the Grunt tests, too, without the DamageTypeCriticalStrike.

Strangely enough, when testing the Grunt against the nearby Ogre Mauler with Reports on, it kept showing the damage the Ogre Mauler was dealing to me, even when I attacked it. For some reason it mixed up their values.

The tests for DamageModiferEvent being equal to 1.00 didn't help unfortunately, sort of did the same thing. I'll provide a screenshot of the report stats I'm looking at.

View attachment 358691
I ran some very quick tests on it (I didn't test it as thoroughly yet) but it seems to still be working. I can see in your screenshot that you are using a slightly older version of my testmap. Are you on the latest version?
 
Level 35
Joined
Feb 5, 2009
Messages
4,551
I ran some very quick tests on it (I didn't test it as thoroughly yet) but it seems to still be working. I can see in your screenshot that you are using a slightly older version of my testmap. Are you on the latest version?

I think I just ran the latest patch now, just in case that was it, but am still encountering the same problem. I've also been using the latest version I can from here, using the non-lua version. I'm not sure why it seems to be behaving in this way for me, am I correct in believing Armor Pierced is supposed to ignore a specific amount of armor? So, for example, if the target has 10 armor, and you're set to pierce 5 armor, it should deal damage as if the target has 5 armor?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I think I just ran the latest patch now, just in case that was it, but am still encountering the same problem. I've also been using the latest version I can from here, using the non-lua version. I'm not sure why it seems to be behaving in this way for me, am I correct in believing Armor Pierced is supposed to ignore a specific amount of armor? So, for example, if the target has 10 armor, and you're set to pierce 5 armor, it should deal damage as if the target has 5 armor?

That is the way it's supposed to work yes. In my brief testing I had debugged the value of the armor prior to being changed and after being changed, and the target's armor was stating that it was being modified by -50 and +50 respectively.

If there is still a problem I can imagine it's due to the way I switched from vJass to GUI and vice versa in the code itself. But if it's not that, I would have to do some more stringent testing such as measuring a unit with no damage dice and then testing it with and without the armor modification to see if there's fluctuation (ie. maybe Blizzard doesn't recognize armor changes anymore if they're done via the DAMAGING event). I hope it's just something simple like my code is wrong, because that's something I can actually fix.
 
Level 35
Joined
Feb 5, 2009
Messages
4,551
That is the way it's supposed to work yes. In my brief testing I had debugged the value of the armor prior to being changed and after being changed, and the target's armor was stating that it was being modified by -50 and +50 respectively.

If there is still a problem I can imagine it's due to the way I switched from vJass to GUI and vice versa in the code itself. But if it's not that, I would have to do some more stringent testing such as measuring a unit with no damage dice and then testing it with and without the armor modification to see if there's fluctuation (ie. maybe Blizzard doesn't recognize armor changes anymore if they're done via the DAMAGING event). I hope it's just something simple like my code is wrong, because that's something I can actually fix.

It's definitely a strange area where admittedly I was, in part, hoping my assessment of what Armor Pierced was supposed to do. One thing I have found strange, too, is when I download the latest version of this mechanic, there's quite a few lines in the GUI that are disabled within active triggers. I'm not sure what causes this, but one of the areas is the Report trigger, where it doesn't seem to want to show the damage reduced or the damage increased.

Perhaps even more bizarrely is that when I have report active, in hindsight it only seems to function when one of my units is being damaged. So, if I deal damage to one of my own units, it shows the report as per the usual, but when I deal damage to the Neutral Hostile Creeps, it doesn't show at all. It sort of just occurred to me that might be why it's doing it.

Also also, I found that the map preferences were set to "default map status" rather than the latest version, which may explain why I was encountering a bizarre scenario where the Kobold Geomancer was dealing bonus damage to my Knight, despite the Knight having Heavy Armor and the Geomancer dealing Piercing damage. Still, even when setting the map preference to latest version, it still seemed to encounter the same issues.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
@Wazzz,
There might be something wrong on your end.

These features that you're having problems with are working fine. (at least in my part)
In this case do these steps first before jumping into that conclusion which may help.

Make sure to re-download. After that replace the script inside of the DamageEngine
inside the demo map with this script instead: (only debugs the piercing armor part)

After that set the value of DamageEventArmorPierced variable to somewhere high (99999) just to
make sure the changed value is noticeable during testing, ensuring that setArmor() is called and:
call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
should work properly just like in my results:
damage-engine-armor-pierced-png.358742
(the knight already killed the geomancer in one blow due to critical strike + 99999 armor being pierced)

Note that only simple debug was made to identify if these involved functions work properly on your end.
If none of these displays, I'm afraid it's on your end IF Bribe can't reproduce the problem you're having.
 
Last edited:
Level 35
Joined
Feb 5, 2009
Messages
4,551
@Wazzz,
There might be something wrong on your end.

These features that you're having problems with are working fine. (at least in my part)
In this case do these steps first before jumping into that conclusion which may help.

Make sure to re-download. After that replace the script inside of the DamageEngine
inside the demo map with this script instead: (only debugs the piercing armor part)

After that set the value of DamageEventArmorPierced variable to somewhere high (99999) just to
make sure the changed value is noticeable during testing, ensuring that setArmor() is called and:
call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
should work properly just like in my results:
damage-engine-armor-pierced-png.358742
(the knight already killed the geomancer in one blow due to critical strike + 99999 armor being pierced)

Note that only simple debug was made to identify if these involved functions work properly on your end.
If none of these displays, I'm afraid it's on your end IF Bribe can't reproduce the problem you're having.

It's a very bizarre occurrence for me, where sometimes the After: shows 5.000 for when the Knight is hit, and other times it shows 0.000 for when the Kobold is hit, but on the Critical Strike it just comes up the same. So it seems for some reason, when I run it, the armor value isn't being changed, and yet it works for other people.

What version are you running? I'm not quite sure why it isn't working for me on the latest version of Warcraft, albeit I am not running it on Public Test Realm or anything like that.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
It's a very bizarre occurrence for me, where sometimes the After: shows 5.000, and other times it shows 0.000
This is not bizarre but normal as setArmor() is called on many events.
Probably it returns 0 because the unit is not referenced on that event.
Anyway that's not the problem... but this part:
but on the Critical Strike it doesn't show anything like that.
What do you mean on Critical Strike it doesn't show? You mean if red text spawns there's no -99999 value displayed?
Can you remove the "random integer" condition on Knight's damage script to simulate this everytime and type report?
You must give detail on this one as seeing that the problem is just on somewhere that specific part. Didn't read in-depth
about the code of Damage Engine as a whole so I can't tell the execution of things here and why they should fail in case...

What version are you running? I'm not quite sure why it isn't working for me on the latest version of Warcraft, albeit I am not running it on Public Test Realm or anything like that.
I'm even using 1.31.1 which is the latest legacy patch before reforged.
If the system is problematic for you, other users that are using reforged should be problematic too.
 
Top