• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Damage Engine 5.A.0.0

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

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


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

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

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

Features

Legacy Code & Requirements

How it works

How to install/upgrade

Video Guides

FAQs

Thanks


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

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

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

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

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

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

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


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

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

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

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

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

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

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

For versions 3.8 and prior:

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

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

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

Thanks to @Spellbound for several crucial bug reports.


Damage Engine Config

Damage Engine vJass

Damage Engine Lua

Changelog


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

BribeFromTheHive/DamageEngine

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

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

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

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

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

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

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


Cross-Compatibility:

BribeFromTheHive/DamageEngine

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

Damage Engine Demo Map (Map)

Lua Damage Engine 2.0.0.0 (Map)

Reviews
23:20, 11th Jan 2015, BPower Criticism: On the one hand Damage Engine offers extremely high utility to every spell, system and map maker independent of the coding style ( GUI, JASS, vJass, .... ) on the other hand it's very easy to import and...
Level 16
Joined
May 2, 2011
Messages
1,345
Can this force unit to roll some number from its damage dice?
e.g. it force max damage roll (bless) or min damage roll (min).
can it also remove of dice (do basic dmg only)

Edit: also, is it possibile to make spirit link damage pure and not magical? Units that have magic resistance will reduce spirit link damage.
Edit: I think I got it. Best way to fully remove magic resistance abilties (including hero armor resistance), and replace it with conditional trigger which will reduce spell damage ONLY IF its above certain threshold. Normally, spirit link damage redistribution is ver low (between 5-75)
And spells usually do a lot of damage. Min spell damage is warden SS (Except blizzard and flamestrike).

This is buggy fix but simple. If you know your map, you will understand which conditions to use to work around this spirit link.


Though is there straight forward fix?
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Can this force unit to roll some number from its damage dice?
e.g. it force max damage roll (bless) or min damage roll (min).
can it also remove of dice (do basic dmg only)

Not a perfect solution for this. No DDS works with damage prior to the dice/critical strike/searing arrows flat damage increasers because Blizzard's API doesn't allow it.

However, if you detect "IsDamageAttack" and don't use Critical Strike in your map (if you do, you'd have to trigger it) you can reset the damage dice. Blizzard made new natives to detect those figures so you can recalculate it and set DamageEventAmount to the new figure.

Edit: also, is it possibile to make spirit link damage pure and not magical? Units that have magic resistance will reduce spirit link damage.
Edit: I think I got it. Best way to fully remove magic resistance abilties (including hero armor resistance), and replace it with conditional trigger which will reduce spell damage ONLY IF its above certain threshold. Normally, spirit link damage redistribution is ver low (between 5-75)
And spells usually do a lot of damage. Min spell damage is warden SS (Except blizzard and flamestrike).

This is buggy fix but simple. If you know your map, you will understand which conditions to use to work around this spirit link.


Though is there straight forward fix?

Very easy fix here. From a DamageModifierEvent 1.00, check if DamageEventDamageT is Equal to DAMAGE_TYPE_SPIRIT_LINK. If so, change DamageEventAttackT to something else like ATTACK_TYPE_CHAOS. This won't work with ethereal units but will work with the rest.
 
Level 16
Joined
May 2, 2011
Messages
1,345
Very easy fix here. From a DamageModifierEvent 1.00, check if DamageEventDamageT is Equal to DAMAGE_TYPE_SPIRIT_LINK. If so, change DamageEventAttackT to something else like ATTACK_TYPE_CHAOS. This won't work with ethereal units but will work with the rest.

we can make it with magic attacks then. problem will be that original spirit link by passed armor reductions, but iirc even your damage engine gives us this option so that should be fine.
 
Level 3
Joined
Apr 7, 2020
Messages
32
I'm confused as to why this isn't working but I'm still relatively new to this engine:

  • trusty block
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • (DamageEventTarget has buff Trusty Block ) Equal to True
      • DamageEventAmount Less than or equal to 300.00
    • Actions
      • Custom script: set DamageEngine_inception = true
      • Set VariableSet DamageEventAmount = 0.00
      • Special Effect - Create a special effect attached to the overhead of DamageEventTarget using Abilities\Spells\Human\Defend\DefendCaster.mdl
      • Special Effect - Destroy (Last created special effect)
The purpose is to block any damage below 300 while the buff is on.
 
Level 3
Joined
Apr 7, 2020
Messages
32
You don't need "set DamageEngine_inception = true" and I'm not sure why you're using it.

Additionally, setting the DamageEventAmount to 0.00 needs to be done from DamageModifierEvent - 1.00.

When you fully nullify the amount, however, you'll want to set DamageEventOverride to True.
Thank you! I appreciate it.
 
Level 1
Joined
Apr 21, 2020
Messages
2
I hope someone can help me. Everything works fine, but i cant get the "healing event" to work. The DamageEvent dont fires with a negative Damage. I have a fresh (actual 5.5.0) copy from the Engine(Map). I need only a few green Numbers on my map :(.

Damage Tag
Events
Game - DamageEvent becomes Equal to 1.00
Conditions
Actions
Game - Display to (All players) the text: (damage: + (String(DamageEventAmount)))

Works with Damage but you dont get negative numbers on healing.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I hope someone can help me. Everything works fine, but i cant get the "healing event" to work. The DamageEvent dont fires with a negative Damage. I have a fresh (actual 5.5.0) copy from the Engine(Map). I need only a few green Numbers on my map :(.

Damage Tag
Events
Game - DamageEvent becomes Equal to 1.00
Conditions
Actions
Game - Display to (All players) the text: (damage: + (String(DamageEventAmount)))

Works with Damage but you dont get negative numbers on healing.

DamageEvent does not detect healing. In fact, nothing properly does. Heal Event KIND OF detects healing, but it lacks source tracking unless you trigger it.
 
Level 1
Joined
Apr 21, 2020
Messages
2
Thanks for your reply. Then i have to make it :)

The presentation, Screens and Forum answers above, have suggested that this is possible with this DamageEngine 5.5.

Nevertheless its a good "round" work. Thanks for that.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Thanks for your reply. Then i have to make it :)

The presentation, Screens and Forum answers above, have suggested that this is possible with this DamageEngine 5.5.

Nevertheless its a good "round" work. Thanks for that.

Damage Engine just supports converting a damage value into one that heals, but Heal Event (in my signature) allows you to detect when a unit's life jumps from something like a Priest's heal or even detects rapid regeneration like Rejuvenation.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Update to 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.

Here are the highlights:

DamageModifierEvent 2 and 3 are not important anymore (but still work exactly as they did before). However, you can now control MUCH more detail in terms of order. You can even do DamageModifierEvent becomes Equal to 0.50 or 1.50 for an event that runs before, and after (respectively) than the standard 1.00 event. It can be fine tuned to 1.01, 1.02, etc. if you really want to be specific.

DamageModiferEvents at 4.00 and higher will run after armor and Warcraft 3 shields are calculated. This marks no change to the way existing maps employ the event, but allows far greater control over the post-armor process and now you can use crazy numbers like 420.69 (nice) for marking very low priority events.

DamageEvent becomes Equal to 0.00 now registers the 0 damage event, but I've kept 2.00 around for backwards compatibility (because I absolutely appreciate backwards compatibility when possible). Anything not equal to 2.00 or 0.00 will register as a DamageEvent with a user-specified priority (so yes, even 420.69 works here too).

Speaking of, any event previously limited to 1.00 will now allow ANY value ro be registered. So LethalDamageEvent set at 0.00 would have a significantly higher priority than 420.69.

Nice, right?
 
Last edited:
  • Like
Reactions: AGD
Level 1
Joined
May 2, 2020
Messages
5
Hi
first of all, great script, allowed me to do a couple things which seemed either way too complicated to make sure they wouldnt be abusable or outright impossible.

but as always, if a user makes an account just to post; there must be a problem and it seems to be one i couldnt find the solution to after browsing this thread -admittedly not all 60+ pages of it-

im trying to apply extra spell damage on hit while the unit is affected by a specific buff, which is (or seemed at least) easy enough on its own and works most of the time... other than when another unit is dealing damage with immolation (ability based on immolation but same deal, so its just immolation) at the same time; to specify, it doesnt work at all if immolation from another unit is going, they do not have to be dealing damage to the same target.

im sure im just missing some sort of variable i should be keeping track of

(oh, and im working in gui)

5/5 work on the script though, Bribe, really thanks for it!
 

SpasMaster

Hosted Project: SC
Level 23
Joined
Jan 29, 2010
Messages
1,969
im trying to apply extra spell damage on hit
What do you refer to as "on-hit"? Auto-attack? Dealing damage from any source? Dealing damage with a specific spell?
Do you want the original damage instance to be modified or to have it cause a separate damage instance?

other than when another unit is dealing damage with immolation (ability based on immolation but same deal, so its just immolation) at the same time
So if I understand correctly, you want a unit to deal extra damage to units, while that same unit is affected by a buff other than immolation?

I am willing to help you out and give you some direction, but you must be a bit more clear and specific with what you want to do exactly. :p
 
Level 1
Joined
May 2, 2020
Messages
5
What do you refer to as "on-hit"? Auto-attack? Dealing damage from any source? Dealing damage with a specific spell?
Do you want the original damage instance to be modified or to have it cause a separate damage instance?

Hello
ill clarify, and apologies if it wasnt clear at first. then ill also include some findings because i managed to actually overcome the issue a bit earlier today, but in an unconventional way

  • EleMasteryOnhit
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • DamageEventSource Equal to uSham
      • (uSham has buff Elemental Force ) Equal to True
      • IsDamageMelee Equal to True
    • Actions
      • If ((Level of Elemental Mastery for uSham) Equal to 1) then do (Unit - Cause uSham to damage DamageEventTarget, dealing 70.00 damage of attack type Spells and damage type Lightning) else do (Unit - Cause uSham to damage DamageEventTarget, dealing 140.00 damage of attack type Spells and damage type Lightning)
its a plain trigger i know, as im not well versed in damage engine yet, however it works.
the further clarification then, based on the trigger shown: buff on unit from skill -> unit damages something with a regular right-click -> should do 70 or 140 extra spell damage based on level of ability that provides the buff

the ability is there, buff is there, all those conditions are fine.

anyway, as i said the issue has been overcome by reverting my damage engine back to 5.5.0.0 as this worked there, and despite not touching the trigger at all, if i upgrade to 5.6.0.1, weird things start happening

on to those weird things i noticed:
  • the first hit from the unit never fires the trigger, i noticed this while trying to debug. if the unit satisfies all conditions, and attacks, nothing happens. however, starting from the second hit, the trigger fires every single time as it is supposed to, and the bonus damage is applied... until...
  • theres anything else at all doing damage; at first i thought it was only immolate being used somewhere was what was causing the problem, but this proved to be false. i put another 4 completely standard units on the map to attack something to test (2 knights, 2 huntresses), then right-clicked different enemies on all of them. the trigger becomes inconsistent, and sometimes the bonus spell damage is applied when the main unit's attacks connect, sometimes it isnt
  • if immolate is in effect (tested it with stock immolate, only increased the aoe so it can hit more), it fully stops the bonus damage from getting applied
all this weirdness only occurs if im on 5.6.0.1, downgrading to 5.5.0.0 immediately fixes every issue, and everything works perfectly as it should

if theres any insight on how i should change the trigger if i wish to upgrade to 5.6.0.1 because im missing something, some change in functionality, im open to it; otherwise i can just stay on 5.5.0.0, but i wanted to report my findings nonetheless

thank you again
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Finally, a neat QOL improvement is that I have assigned weight to each of the events.

Here are the highlights:

DamageModifierEvent 2 and 3 are not important anymore (but still work exactly as they did before). However, you can now control MUCH more detail in terms of order. You can even do DamageModifierEvent becomes Equal to 0.50 or 1.50 for an event that runs before, and after (respectively) than the standard 1.00 event. It can be fine tuned to 1.01, 1.02, etc. if you really want to be specific.

DamageModiferEvents at 4.00 and higher will run after armor and Warcraft 3 shields are calculated. This marks no change to the way existing maps employ the event, but allows far greater control over the post-armor process and now you can use crazy numbers like 420.69 (nice) for marking very low priority events.

DamageEvent becomes Equal to 0.00 now registers the 0 damage event, but I've kept 2.00 around for backwards compatibility (because I absolutely appreciate backwards compatibility when possible). Anything not equal to 2.00 or 0.00 will register as a DamageEvent with a user-specified priority (so yes, even 420.69 works here too).

Speaking of, any event previously limited to 1.00 will now allow ANY value ro be registered. So LethalDamageEvent set at 0.00 would have a significantly higher priority than 420.69.

Nice, right?
Cool idea :piru:
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hello
ill clarify, and apologies if it wasnt clear at first. then ill also include some findings because i managed to actually overcome the issue a bit earlier today, but in an unconventional way

  • EleMasteryOnhit
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • DamageEventSource Equal to uSham
      • (uSham has buff Elemental Force ) Equal to True
      • IsDamageMelee Equal to True
    • Actions
      • If ((Level of Elemental Mastery for uSham) Equal to 1) then do (Unit - Cause uSham to damage DamageEventTarget, dealing 70.00 damage of attack type Spells and damage type Lightning) else do (Unit - Cause uSham to damage DamageEventTarget, dealing 140.00 damage of attack type Spells and damage type Lightning)
its a plain trigger i know, as im not well versed in damage engine yet, however it works.
the further clarification then, based on the trigger shown: buff on unit from skill -> unit damages something with a regular right-click -> should do 70 or 140 extra spell damage based on level of ability that provides the buff

the ability is there, buff is there, all those conditions are fine.

anyway, as i said the issue has been overcome by reverting my damage engine back to 5.5.0.0 as this worked there, and despite not touching the trigger at all, if i upgrade to 5.6.0.1, weird things start happening

on to those weird things i noticed:
  • the first hit from the unit never fires the trigger, i noticed this while trying to debug. if the unit satisfies all conditions, and attacks, nothing happens. however, starting from the second hit, the trigger fires every single time as it is supposed to, and the bonus damage is applied... until...
  • theres anything else at all doing damage; at first i thought it was only immolate being used somewhere was what was causing the problem, but this proved to be false. i put another 4 completely standard units on the map to attack something to test (2 knights, 2 huntresses), then right-clicked different enemies on all of them. the trigger becomes inconsistent, and sometimes the bonus spell damage is applied when the main unit's attacks connect, sometimes it isnt
  • if immolate is in effect (tested it with stock immolate, only increased the aoe so it can hit more), it fully stops the bonus damage from getting applied
all this weirdness only occurs if im on 5.6.0.1, downgrading to 5.5.0.0 immediately fixes every issue, and everything works perfectly as it should

if theres any insight on how i should change the trigger if i wish to upgrade to 5.6.0.1 because im missing something, some change in functionality, im open to it; otherwise i can just stay on 5.5.0.0, but i wanted to report my findings nonetheless

thank you again
The new patch has much more aggressive gatekeeping for detecting damage from code, so it's likely that your map has at least one runaway instance that sets NextDamageType without actually damaging anything afterward.

I could do some ugly tooling to loosely patch up user errors like this with a periodic timer, but my recommendation instead would be to use the Object Manager (press F11) to find all uses of that integer variable and make sure it's not being used incorrectly somewhere.
 
Level 1
Joined
May 2, 2020
Messages
5
The new patch has much more aggressive gatekeeping for detecting damage from code, so it's likely that your map has at least one runaway instance that sets NextDamageType without actually damaging anything afterward.

I could do some ugly tooling to loosely patch up user errors like this with a periodic timer, but my recommendation instead would be to use the Object Manager (press F11) to find all uses of that integer variable and make sure it's not being used incorrectly somewhere.

Sorry, but i have no idea what you just said to me
either you misunderstood me, or i just dont understand what the post is supposed to mean; and i think its likely the second option, so what integer exactly are you referecing?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Sorry, but i have no idea what you just said to me
either you misunderstood me, or i just dont understand what the post is supposed to mean; and i think its likely the second option, so what integer exactly are you referecing?

The integer "NextDamageType". Check if you use it anywhere in your code. Or if you PM me your map I can check your code for you.
 
Level 1
Joined
May 2, 2020
Messages
5
The integer "NextDamageType". Check if you use it anywhere in your code. Or if you PM me your map I can check your code for you.

No, i do not use that variable anywhere

anyway, i threw together a test map because it took 10 minutes to do to show off what i mean, and i think at this point you could treat this is a legitimate bug report even and not really an issue with my map, unless the trigger i already posted before is somehow faulty.

about this test map, all the modifications i did:
  • in the object editor:
    • changed immolation to 400 aoe
    • created custom grunt unit with 9000 health, fleeing off, attacks off, 0 hp regen
  • in the trigger editor:
    • added dds 5.6.0.1
    • created one global variable named hero
    • created two triggers which are the following

  • init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet hero = Mountain King 0000 <gen>
      • Player - Make Player 1 (Red) treat Player 2 (Blue) as an Enemy
      • Player - Make Player 2 (Blue) treat Player 1 (Red) as an Ally
      • Player - Set the current research level of Priest Adept Training to 2 for Player 1 (Red)
  • dmg
    • Events
      • Game - DamageEvent becomes Equal to 1.00
    • Conditions
      • IsDamageMelee Equal to True
      • DamageEventSource Equal to hero
      • (hero has buff Inner Fire) Equal to True
    • Actions
      • Unit - Cause hero to damage DamageEventTarget, dealing 1500.00 damage of attack type Spells and damage type Magic
there's nothing else that has been done other than what you see in the following video to showcase the issue i've been trying to relentlessly communicate:

ddstest - Streamable
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Update to 5.6.1.0 -

Patchworkon Melee/Ranged detection, recursion tracking. Also added the ability to modify damage from a DamageEvent to both make it easier on beginners as well as open up variety for those who may want to branch out with their damage flow options.


@MichaelSieger this fixes the issue you brought to my attention as I was able to reproduce it, then I was able to find out why and resolve why it was happening.

Due to this being primarily a bug fix, this is recommended for all users on 5.6 and later.
 
Level 1
Joined
May 2, 2020
Messages
5
Update to 5.6.1.0 -

Patchworkon Melee/Ranged detection, recursion tracking. Also added the ability to modify damage from a DamageEvent to both make it easier on beginners as well as open up variety for those who may want to branch out with their damage flow options.


@MichaelSieger this fixes the issue you brought to my attention as I was able to reproduce it, then I was able to find out why and resolve why it was happening.

Due to this being primarily a bug fix, this is recommended for all users on 5.6 and later.

Did some testing again with a bit more than just a very simple trigger, and it seems to be working flawlessly now, thank you
 
Level 12
Joined
May 16, 2020
Messages
660
Hi Bribe,

I'm trying to use your Engine because sometimes I want to set the damage to 0 before it reaches my hero (I understand that your engine can do that, unlike GUI Friendly Damage Detection which "just" recognizes the damage after it is done). Unfortunately I'm getting many conflicts with other libraries / JASS spells which I imported and I don't know how to solve this.

Is this something you can maybe help with?

If yes, attached the map (sorry it's quite big due to reforged and 256x256 icons I imported). Cheers!
 

Attachments

  • Land of Legends v0.0.30 - with Damage Engine.w3m
    18.1 MB · Views: 156

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hi Bribe,

I'm trying to use your Engine because sometimes I want to set the damage to 0 before it reaches my hero (I understand that your engine can do that, unlike GUI Friendly Damage Detection which "just" recognizes the damage after it is done). Unfortunately I'm getting many conflicts with other libraries / JASS spells which I imported and I don't know how to solve this.

Is this something you can maybe help with?

If yes, attached the map (sorry it's quite big due to reforged and 256x256 icons I imported). Cheers!
Hi Ugabunda,

This will depend on whether those others are doing damage processing of their own. If so, you'll need to let me know what systems they are (ie. If they use a different Damage Engine).

I'm unable to use the Test Map feature currently as it keeps defaulting to this weird menu when Warcraft 3 starts and it's rather daunting for me to try to figure out why.
 
Level 12
Joined
May 16, 2020
Messages
660
Hi Ugabunda,

This will depend on whether those others are doing damage processing of their own. If so, you'll need to let me know what systems they are (ie. If they use a different Damage Engine).

I'm unable to use the Test Map feature currently as it keeps defaulting to this weird menu when Warcraft 3 starts and it's rather daunting for me to try to figure out why.

I think this happens precisely because Damage Engine currently conflicts with the others (my previously attached Map also defaults to the menu in WC3 Reforged when I have code errors). I attached a map which opens with the newest Reforged Client (meaning I deleted Damage Engine there). Does this work?

And not sure if this will help, but the current libraries I have in the map are:
- xebasic
- xedamage
- T32
- SimError
- TimerUtils
- Group Utils
- Illusion
- DamageEvent (by Flux)
- DamageModify (by Flux)
- GUI Friendly Damage Detection

The error is:
1. Identifier redeclared : "Damage"
2. ---- (previously declared here)

(I'm sure once you solve these other errors will pop up...)


Let me please know if you are able to open this map - If not I will attach the code from the above libraries individually.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
5.6.2.0 - Fixed a bug with Damage modification and laid groundwork for a bunch of optional vJass compatibility addons.

recommended for all users.

@Ugabunda I have just posted in Flux's thread a compatibility script. You can delete both of those libraries and replace them with my short plugin script.
 
Last edited:
Level 9
Joined
Jul 18, 2005
Messages
319
So... I've encountered a strange bug with 5.6.2.0 (as well as 5.6.1.0). There are instances where using a dummy unit to cast a spell on a target in a "udg_DamageEvent" block completely prevents the damage instance. I don't think I've encountered this issue with Damage Engine 5.4.2.3 based on player reports...

I have attached an MWE with the latest version of Damage Engine + an on-hit slow effect cast by a dummy unit. Notice after a few auto-attacks of applying the on-hit slow effect, damage stops registering on the Peasants.
 

Attachments

  • MWE_Slow.w3x
    54.6 KB · Views: 56

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
So... I've encountered a strange bug with 5.6.2.0 (as well as 5.6.1.0). There are instances where using a dummy unit to cast a spell on a target in a "udg_DamageEvent" block completely prevents the damage instance. I don't think I've encountered this issue with Damage Engine 5.4.2.3 based on player reports...

I have attached an MWE with the latest version of Damage Engine + an on-hit slow effect cast by a dummy unit. Notice after a few auto-attacks of applying the on-hit slow effect, damage stops registering on the Peasants.

Can you please try it from a DamageModifierEvent or AfterDamageEvent and let me know if you get along better with one of those?

@Bribe Do you still have the latest version of damage engine that is compatible with non-Reforged patch?
The link in the description goes no where, even though it is highlighted.

I just fixed that thanks. But I think all you need to do to make it work with 1.31 is add this to the top of the DamageEngine library:

JASS:
function BlzGetEventIsAttack takes nothing returns boolean
   return BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL
endfunction

@Ugabunda make sure Weep's library is replaced with the DamageEngine compatibility version as well.
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
@Ugabunda I have just posted in Flux's thread a compatibility script. You can delete both of those libraries and replace them with my short plugin script.

@Ugabunda make sure Weep's library is replaced with the DamageEngine compatibility version as well.

Thank you! I did the following:

1) C&P Damage Engine to my map
2) Delete DamageEvent (from Flux)
3) Delete DamageModify (from Flux)
4) Delete GUI Friendly Damage Detection
4) Add a trigger function with the compatibility patch for Flux's Damage System
5) Add a trigger function with the compatibility patch for Weep's GDD

Unfortuntely still getting an error, this time it's the "ilusion" library, I just checked it and it's also by Flux:
[vJASS] - [System] Illusion

Error:
Line 3349: Missing requirement: DamageEvent (libraries cannot require scopes)

Probably also needs a compatibility patch, sorry for this...
 

Attachments

  • Illusion.txt
    8.7 KB · Views: 40
Level 20
Joined
Aug 13, 2013
Messages
1,696
Hey Bribe,

I'm hesitating to use this on my next upcoming resources that would surely involve damage detection,
It seems like Damage Engine 3.8 is my only option. However, is the 3.8's API safe to use and would not
cause compatibility issues to reforged users that have Damage Engine 5+ in their maps?

Or there are things that I should consider?
 
Level 9
Joined
Jul 18, 2005
Messages
319
Can you please try it from a DamageModifierEvent or AfterDamageEvent and let me know if you get along better with one of those?

I can confirm in the MWE that only AfterDamageEvent doesn't prevent damage from registering. When I apply the adjustment to the map I'm working on, the FPS drops significantly after a few auto-attacks.

I'll look into figuring out what causes this. Then, I'll incorporate in the MWE.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hey Bribe,

I'm hesitating to use this on my next upcoming resources that would surely involve damage detection,
It seems like Damage Engine 3.8 is my only option. However, is the 3.8's API safe to use and would not
cause compatibility issues to reforged users that have Damage Engine 5+ in their maps?

Or there are things that I should consider?
If you're just using basic things no, but you'd be missing out on the plethora of useful features which have been released since then.

Thank you! I did the following:

1) C&P Damage Engine to my map
2) Delete DamageEvent (from Flux)
3) Delete DamageModify (from Flux)
4) Delete GUI Friendly Damage Detection
4) Add a trigger function with the compatibility patch for Flux's Damage System
5) Add a trigger function with the compatibility patch for Weep's GDD

Unfortuntely still getting an error, this time it's the "ilusion" library, I just checked it and it's also by Flux:
[vJASS] - [System] Illusion

Error:
Line 3349: Missing requirement: DamageEvent (libraries cannot require scopes)

Probably also needs a compatibility patch, sorry for this...

Have the Illusion resource require DamageEngine instead of DamageEvent in that case.
 
Level 12
Joined
May 16, 2020
Messages
660
Have the Illusion resource require DamageEngine instead of DamageEvent in that case.

Ahhh I did the process again and I think I did a mistake before, because I'm no longer getting the "Missing requirement: DamageEvent" error. But, I have now this error...:

"source is not an static member of damage" --- referring to the line
"if IsUnitInGroup(Damage.source, thistype.g) then"

Does your compatibility patch in FLux's page include a patch for "DamageModify" - or is it just for DamageEvent?

In the Illusion Library Flux writes that Illusion needs both DamageModify and DamageEvent, so maybe it's related to this?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Ahhh I did the process again and I think I did a mistake before, because I'm no longer getting the "Missing requirement: DamageEvent" error. But, I have now this error...:

"source is not an static member of damage" --- referring to the line
"if IsUnitInGroup(Damage.source, thistype.g) then"

Does your compatibility patch in FLux's page include a patch for "DamageModify" - or is it just for DamageEvent?

In the Illusion Library Flux writes that Illusion needs both DamageModify and DamageEvent, so maybe it's related to this?

Hmmm, I will have to test that tonight. I'm wondering if I can include both a static member and a dynamic member alike and see if that works.

Otherwise those lines would have to be Damage.index.source/target. You can use this script:

JASS:
library Illusion /*

                         Illusion v1.34
                            by Flux, edited by Bribe to work with Damage Engine 5.6.2.0
       
            Allows easy creation of Illusion with any damage factor.
 
    */ requires DamageEngine /*
    https://www.hiveworkshop.com/threads/damage-engine-5-6-2-0.201016
    Required to manipulate damage given and damage taken by illusions.
 
 
    */ optional Table /*
    If not found, the system will create a hashtable. You cannot create more than 256 hashtable
    per map.

 
    ********************************************************************************
    *************************************** API ************************************
    ********************************************************************************
 
    Illusion.create(player, unitSource, x, y)
        - Create an Illusion based on <unitsource>, owned by <player>, positioned at (<x>, <y>)
   
    this.duration = <timedlife>
        - Add a timer to an illusion.
        - Cannot be overwritten once set.
   
    Illusion.get(unit)
        - Return the 'Illusion instance' based on <unit> parameter.
     
    this.unit
        - Refers to the actual illusion unit
   
    this.damageGiven
        - Determines damage dealt factor.
   
    this.damageTaken
        - Determines damage received factor.
 
 
    CREDITS:
        Bribe         - Table, DamageEngine
   
*/
    //===================================================================
    //========================= CONFIGURATION ===========================
    //===================================================================
    globals
        //Rawcode of Illusion Ability based on "Item Illusions"
        private constant integer ILLUSION_SPELL = 'A028'
   
        private constant integer DUMMY_ID = 'u005'
   
        private constant integer REFRESH_COUNT = 30
        //Dummy unit owner
        private constant player DUMMY_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
    endglobals
    //===================================================================
    //======================= END CONFIGURATION =========================
    //===================================================================
 
    native UnitAlive takes unit u returns boolean
 
    struct Illusion
   
        readonly unit unit
        public real damageTaken
        public real damageGiven
   
        static if LIBRARY_Table then
            private static Table tb
        else
            private static hashtable hash = InitHashtable()
        endif
   
        private static trigger deathTrg = CreateTrigger()
        private static group g = CreateGroup()
        private static timer t = CreateTimer()
        private static integer count = 0
        private static unit dummy
        private static unit illu
   
        static method get takes unit u returns thistype
            static if LIBRARY_Table then
                return thistype.tb[GetHandleId(u)]
            else
                return LoadInteger(thistype.hash, GetHandleId(u), 0)
            endif
        endmethod
   
        private static method reAdd takes nothing returns nothing
            call TriggerRegisterUnitEvent(thistype.deathTrg, GetEnumUnit(), EVENT_UNIT_DEATH)
        endmethod
   
        private static method onDeath takes nothing returns boolean
            call thistype(thistype.get(GetTriggerUnit())).destroy()
            return false
        endmethod
   
        method destroy takes nothing returns nothing
            if UnitAlive(this.unit) then
                call KillUnit(this.unit)
            endif
            static if LIBRARY_Table then
                call thistype.tb.remove(GetHandleId(this.unit))
            else
                call RemoveSavedInteger(thistype.hash, GetHandleId(this.unit), 0)
            endif
            call GroupRemoveUnit(thistype.g, this.unit)
            //Death trigger refresh
            set thistype.count = thistype.count + 1
            if thistype.count >= REFRESH_COUNT then
                call DestroyTrigger(thistype.deathTrg)
                set thistype.deathTrg = CreateTrigger()
                call TriggerAddCondition(thistype.deathTrg, Condition(function thistype.onDeath))
                call ForGroup(thistype.g, function thistype.reAdd)
                set thistype.count = 0
            endif
            set this.unit = null
            call this.deallocate()
        endmethod
   
   
        private static method onDamage takes nothing returns nothing
            //If source is illusion
            if IsUnitInGroup(Damage.index.source, thistype.g) then
                set Damage.index.amount = Damage.index.amount*thistype.get(Damage.index.source).damageGiven
            endif
            //If target is illusion
            if IsUnitInGroup(Damage.index.target, thistype.g) then
                set Damage.index.amount = Damage.index.amount*thistype.get(Damage.index.target).damageTaken
            endif
        endmethod
   
        private static method entered takes nothing returns boolean
            if GetSummoningUnit() == thistype.dummy then
                set thistype.illu = GetSummonedUnit()
            endif
            return false
        endmethod
   
        method operator duration= takes real time returns nothing
            call UnitApplyTimedLife(this.unit, 'BTLF', time)
        endmethod
   
        static method create takes player owner, unit source, real x, real y returns thistype
            local thistype this
            set thistype.illu = null
            //Create the Illusion Unit
            if source != null and UnitAlive(source) then
                call SetUnitX(thistype.dummy, GetUnitX(source))
                call SetUnitY(thistype.dummy, GetUnitY(source))
                call SetUnitOwner(thistype.dummy, GetOwningPlayer(source), false)
                if IssueTargetOrderById(thistype.dummy, 852274, source) then
                    if thistype.illu != null then
                        call SetUnitOwner(thistype.illu, owner, true)
                        if IsUnitType(source, UNIT_TYPE_STRUCTURE) then
                            call SetUnitPosition(thistype.illu, x, y)
                        else
                            call SetUnitX(thistype.illu, x)
                            call SetUnitY(thistype.illu, y)
                        endif
                    else
                        debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] No illusion created")
                        call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                        return 0
                    endif
                else
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] Issued illusion create order failed")
                    call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                    return 0
                endif
                call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
            else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] Source unit dead or non-existing")
                return 0
            endif
            //Initialize struct
            set this = thistype.allocate()
            set this.unit = thistype.illu
            set this.damageTaken = 1.0
            set this.damageGiven = 1.0
            call SetUnitAnimation(thistype.illu, "stand")
            call GroupAddUnit(thistype.g, this.unit)
            call TriggerRegisterUnitEvent(thistype.deathTrg, this.unit, EVENT_UNIT_DEATH)
            static if LIBRARY_Table then
                set thistype.tb[GetHandleId(this.unit)] = this
            else
                call SaveInteger(thistype.hash, GetHandleId(this.unit), 0, this)
            endif
            set thistype.illu = null
            return this
        endmethod
   
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            set thistype.dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, 0, 0, 0)
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SUMMON)
            call TriggerAddCondition(t, Condition(function thistype.entered))
            call TriggerAddCondition(thistype.deathTrg, Condition(function thistype.onDeath))
            call UnitAddAbility(thistype.dummy, ILLUSION_SPELL)
            static if LIBRARY_Table then
                set thistype.tb = Table.create()
            endif
            call RegisterDamageEvent(function thistype.onDamage, "Modifier", 1.00)
        endmethod
   
    endstruct
 
endlibrary
 
Last edited:
Level 12
Joined
May 16, 2020
Messages
660
Hmmm, I will have to test that tonight. I'm wondering if I can include both a static member and a dynamic member alike and see if that works.

Otherwise those lines would have to be Damage.index.source/target. You can use this script:

JASS:
library Illusion /*

                         Illusion v1.34
                            by Flux, edited by Bribe to work with Damage Engine 5.6.2.0
       
            Allows easy creation of Illusion with any damage factor.
 
    */ requires DamageEngine /*
    https://www.hiveworkshop.com/threads/damage-engine-5-6-2-0.201016
    Required to manipulate damage given and damage taken by illusions.
 
 
    */ optional Table /*
    If not found, the system will create a hashtable. You cannot create more than 256 hashtable
    per map.

 
    ********************************************************************************
    *************************************** API ************************************
    ********************************************************************************
 
    Illusion.create(player, unitSource, x, y)
        - Create an Illusion based on <unitsource>, owned by <player>, positioned at (<x>, <y>)
   
    this.duration = <timedlife>
        - Add a timer to an illusion.
        - Cannot be overwritten once set.
   
    Illusion.get(unit)
        - Return the 'Illusion instance' based on <unit> parameter.
     
    this.unit
        - Refers to the actual illusion unit
   
    this.damageGiven
        - Determines damage dealt factor.
   
    this.damageTaken
        - Determines damage received factor.
 
 
    CREDITS:
        Bribe         - Table, DamageEngine
   
*/
    //===================================================================
    //========================= CONFIGURATION ===========================
    //===================================================================
    globals
        //Rawcode of Illusion Ability based on "Item Illusions"
        private constant integer ILLUSION_SPELL = 'A028'
   
        private constant integer DUMMY_ID = 'u005'
   
        private constant integer REFRESH_COUNT = 30
        //Dummy unit owner
        private constant player DUMMY_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
    endglobals
    //===================================================================
    //======================= END CONFIGURATION =========================
    //===================================================================
 
    native UnitAlive takes unit u returns boolean
 
    struct Illusion
   
        readonly unit unit
        public real damageTaken
        public real damageGiven
   
        static if LIBRARY_Table then
            private static Table tb
        else
            private static hashtable hash = InitHashtable()
        endif
   
        private static trigger deathTrg = CreateTrigger()
        private static group g = CreateGroup()
        private static timer t = CreateTimer()
        private static integer count = 0
        private static unit dummy
        private static unit illu
   
        static method get takes unit u returns thistype
            static if LIBRARY_Table then
                return thistype.tb[GetHandleId(u)]
            else
                return LoadInteger(thistype.hash, GetHandleId(u), 0)
            endif
        endmethod
   
        private static method reAdd takes nothing returns nothing
            call TriggerRegisterUnitEvent(thistype.deathTrg, GetEnumUnit(), EVENT_UNIT_DEATH)
        endmethod
   
        private static method onDeath takes nothing returns boolean
            call thistype(thistype.get(GetTriggerUnit())).destroy()
            return false
        endmethod
   
        method destroy takes nothing returns nothing
            if UnitAlive(this.unit) then
                call KillUnit(this.unit)
            endif
            static if LIBRARY_Table then
                call thistype.tb.remove(GetHandleId(this.unit))
            else
                call RemoveSavedInteger(thistype.hash, GetHandleId(this.unit), 0)
            endif
            call GroupRemoveUnit(thistype.g, this.unit)
            //Death trigger refresh
            set thistype.count = thistype.count + 1
            if thistype.count >= REFRESH_COUNT then
                call DestroyTrigger(thistype.deathTrg)
                set thistype.deathTrg = CreateTrigger()
                call TriggerAddCondition(thistype.deathTrg, Condition(function thistype.onDeath))
                call ForGroup(thistype.g, function thistype.reAdd)
                set thistype.count = 0
            endif
            set this.unit = null
            call this.deallocate()
        endmethod
   
   
        private static method onDamage takes nothing returns nothing
            //If source is illusion
            if IsUnitInGroup(Damage.index.source, thistype.g) then
                set Damage.index.amount = Damage.amount*thistype.get(Damage.index.source).damageGiven
            endif
            //If target is illusion
            if IsUnitInGroup(Damage.index.target, thistype.g) then
                set Damage.index.amount = Damage.amount*thistype.get(Damage.index.target).damageTaken
            endif
        endmethod
   
        private static method entered takes nothing returns boolean
            if GetSummoningUnit() == thistype.dummy then
                set thistype.illu = GetSummonedUnit()
            endif
            return false
        endmethod
   
        method operator duration= takes real time returns nothing
            call UnitApplyTimedLife(this.unit, 'BTLF', time)
        endmethod
   
        static method create takes player owner, unit source, real x, real y returns thistype
            local thistype this
            set thistype.illu = null
            //Create the Illusion Unit
            if source != null and UnitAlive(source) then
                call SetUnitX(thistype.dummy, GetUnitX(source))
                call SetUnitY(thistype.dummy, GetUnitY(source))
                call SetUnitOwner(thistype.dummy, GetOwningPlayer(source), false)
                if IssueTargetOrderById(thistype.dummy, 852274, source) then
                    if thistype.illu != null then
                        call SetUnitOwner(thistype.illu, owner, true)
                        if IsUnitType(source, UNIT_TYPE_STRUCTURE) then
                            call SetUnitPosition(thistype.illu, x, y)
                        else
                            call SetUnitX(thistype.illu, x)
                            call SetUnitY(thistype.illu, y)
                        endif
                    else
                        debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] No illusion created")
                        call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                        return 0
                    endif
                else
                    debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] Issued illusion create order failed")
                    call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
                    return 0
                endif
                call SetUnitOwner(thistype.dummy, DUMMY_OWNER, false)
            else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3600, "[Illusion] Source unit dead or non-existing")
                return 0
            endif
            //Initialize struct
            set this = thistype.allocate()
            set this.unit = thistype.illu
            set this.damageTaken = 1.0
            set this.damageGiven = 1.0
            call SetUnitAnimation(thistype.illu, "stand")
            call GroupAddUnit(thistype.g, this.unit)
            call TriggerRegisterUnitEvent(thistype.deathTrg, this.unit, EVENT_UNIT_DEATH)
            static if LIBRARY_Table then
                set thistype.tb[GetHandleId(this.unit)] = this
            else
                call SaveInteger(thistype.hash, GetHandleId(this.unit), 0, this)
            endif
            set thistype.illu = null
            return this
        endmethod
   
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            set thistype.dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, 0, 0, 0)
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SUMMON)
            call TriggerAddCondition(t, Condition(function thistype.entered))
            call TriggerAddCondition(thistype.deathTrg, Condition(function thistype.onDeath))
            call UnitAddAbility(thistype.dummy, ILLUSION_SPELL)
            static if LIBRARY_Table then
                set thistype.tb = Table.create()
            endif
            call RegisterDamageEvent(function thistype.onDamage, "Modifier", 1.00)
        endmethod
   
    endstruct
 
endlibrary

Ok thanks. In case it helps: By replacing the illusion library with the code above, I'm getting now the following error:

"amount is not an static member of Damage" --- for line
set Damage.index.amount = Damage.amount*thistype.get(Damage.index.source).damageGiven
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Ok thanks. In case it helps: By replacing the illusion library with the code above, I'm getting now the following error:

"amount is not an static member of Damage" --- for line
set Damage.index.amount = Damage.amount*thistype.get(Damage.index.source).damageGiven
Sorry! I have fixed that in my post now

EDIT - hot off the press: @Nestharus 's incredibly complex Damage system, shortened into a myriad of wrapper functions (yes, Damage Engine has gotten THAT much better over the years). Granted, I removed a few things from the API because I didn't understand how or why they worked, but it should cover the bulk of usage cases. The enable/disable and specific unit stuff has been deliberately pruned out as there is not really a way to efficiently compensate for that in Damage Engine (and like many things made by Nestharus, efficiency was their only purpose).

JASS:
library DDS requires DamageEngine

//Incorporates much of Nestharus's DDS's API to the best of my understanding of how it works.

private struct S extends array
    static constant integer SPELL    = 0
    static constant integer PHYSICAL = 1
    static constant integer CODE     = 2
endstruct

struct DDS extends array
 
    readonly static S Archetype = 0
 
    static method operator archetype takes nothing returns integer
        if udg_IsDamageCode then
            return DDS.Archetype.CODE
        elseif udg_IsDamageSpell then
            return DDS.Archetype.SPELL
        endif
        return DDS.Archetype.PHYSICAL
    endmethod

    static method operator damageCode= takes integer i returns nothing
        set udg_NextDamageType = udg_DamageTypeCode
    endmethod
    static method operator damageCode takes nothing returns integer
        return GetUnitUserData(udg_DamageEventTarget)
    endmethod

    static method operator source takes nothing returns unit
        return udg_DamageEventSource
    endmethod
    static method operator sourceId takes nothing returns integer
        return GetUnitUserData(udg_DamageEventSource)
    endmethod
 
    static method operator target takes nothing returns unit
        return udg_DamageEventTarget
    endmethod
    static method operator targetId takes nothing returns integer
        return GetUnitUserData(udg_DamageEventTarget)
    endmethod
 
    static method operator damage takes nothing returns real
        return Damage.index.amount
    endmethod
    static method operator damage= takes real r returns nothing
        set Damage.index.amount = r
    endmethod
 
    static method operator damageOriginal takes nothing returns real
        return udg_DamageEventPrevAmt
    endmethod

    static method operator damageModifiedAmount takes nothing returns real
        return Damage.index.amount - udg_DamageEventPrevAmt
    endmethod

    static method operator sourcePlayer takes nothing returns player
        return GetOwningPlayer(Damage.index.source)
    endmethod
 
    static method operator targetPlayer takes nothing returns player
        return GetOwningPlayer(Damage.index.target)
    endmethod
 
endstruct

module DAMAGE_EVENT_INTERFACE
 
  static if not thistype.onDamageBefore.exists and not thistype.onDamageAfter.exists then
  else
    private static method onInit takes nothing returns nothing
      static if thistype.onDamageBefore.exists then
        call RegisterDamageEvent(function thistype.onDamageBefore, "Modifier", 4.0)
      endif
      static if thistype.onDamageAfter.exists then
        call RegisterDamageEvent(function thistype.onDamageAfter, "Modifier", 4.1)
      endif
    endmethod
  endif
 
endmodule

endlibrary

EDIT2: This is probably my last one for the day. It's MOST OF Rising_Dusk's IDDS:

JASS:
library IntuitiveDamageSystem requires DamageEngine
 
globals
    constant integer       DAMAGE_TYPE_ATTACK  = 100
    constant integer       DAMAGE_TYPE_IGNORED = 101
    constant integer       DAMAGE_TYPE_SPELL   = 102
    constant integer       DAMAGE_TYPE_EXTRA   = 103
endglobals

    function IgnoreHigherPriority takes nothing returns nothing
        set udg_DamageEventOverride = true
    endfunction
 
    function SetDamage takes real dmg returns nothing
        set Damage.index.amount = dmg //Unlike with IDDS this WILL actually change the damage dealt
    endfunction
 
    function GetTriggerDamageSource takes nothing returns unit
        return udg_DamageEventSource
    endfunction
    function GetTriggerDamageTarget takes nothing returns unit
        return udg_DamageEventTarget
    endfunction
    function GetTriggerDamageBase takes nothing returns real
        return udg_DamageEventPrevAmt
    endfunction
    function GetTriggerDamage takes nothing returns real
        return Damage.index.amount
    endfunction
 
    function GetTriggerDamageType takes nothing returns integer
        return udg_DamageEventType
    endfunction

    //Any codes which use the below two functions of IDDS need to add the number "2" to the end of the function name for compatibility
    function TriggerRegisterDamageEvent2 takes trigger t, integer priority returns nothing
        call TriggerRegisterDamageEvent(t, "Modifier", 4.00 + priority)
    endfunction
 
    //The below function will actually use both DamageEngine's defined types as well as those of IDDS itself.
    function UnitDamageTargetEx2 takes unit s, unit t, real d, attacktype at, integer dt, boolean ca returns nothing
        if dt == DAMAGE_TYPE_ATTACK then
            set udg_NextDamageIsAttack = true
        endif
        set udg_NextDamageType = dt
        if ca then
            set b = UnitDamageTarget(s, t, d, true, false, at, DAMAGE_TYPE_NORMAL, null)
        elseif dt == DAMAGE_TYPE_IGNORED then
            set b = UnitDamageTarget(s, t, d, true, false, at, DAMAGE_TYPE_UNKNOWN, null)
        else
            set b = UnitDamageTarget(s, t, d, true, false, at, DAMAGE_TYPE_UNIVERSAL, null)
        endif
    endfunction
 
endlibrary

For reference, here's Flux's DamagePackage:

JASS:
library_once DamageEvent requires DamageEngine
endlibrary
library_once DamageModify uses DamageEvent
endlibrary

globals
    constant integer DAMAGE_TYPE_PHYSICAL = 1
    constant integer DAMAGE_TYPE_MAGICAL = 2
endglobals

//! textmacro DAMAGE_EVENT_PLUGIN_01

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 Damage.setupEventRobust(t, "udg_DamageEvent", 1.00, false)
endmethod
static method register takes code c returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddCondition(t, Filter(c))
    call registerTrigger(t)
    set t = null
endmethod

static method registerModifierTrigger takes trigger t returns nothing
    call Damage.setupEventRobust(t, "udg_DamageModifierEvent", 1.00, false)
endmethod
static method registerModifier takes code c returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddCondition(t, Filter(c))
    call registerTrigger(t)
    set t = null
endmethod

//! endtextmacro

FINAL(?) EDIT: Jesus4Lyf's legendary "Damage" system:

JASS:
library Damage requires DamageEngine
 
    public function RegisterEvent takes trigger whichTrigger returns nothing
        call RegisterDamageEventTrigger(whichTrigger, "Modifier", 4.00)
    endfunction
 
    public function RegisterZeroEvent takes trigger whichTrigger returns nothing
        call RegisterDamageEventTrigger(whichTrigger, "", 0.00)
    endfunction
 
    public function GetType takes nothing returns damagetype
        return Damage.index.damageType
    endfunction
 
    public function IsAttack takes nothing returns boolean
        return udg_IsDamageAttack
    endfunction
 
    public function Block takes real amount returns nothing
        set Damage.index.amount = Damage.index.amount - amount
    endfunction
 
    public function BlockAll takes nothing returns nothing
        set Damage.index.amount = 0.00
    endfunction
 
    //! textmacro Damage__DealTypeFunc takes NAME, TYPE
        public function $NAME$ takes unit source, unit target, real amount returns boolean
            return UnitDamageTargetEx(source, target, amount, false, false, null, $TYPE$, null)
        endfunction
        public function Is$NAME$ takes nothing returns boolean
            return Damage.index.damageType == $TYPE$
        endfunction
    //! endtextmacro
 
    //! runtextmacro Damage__DealTypeFunc("Pure","DAMAGE_TYPE_UNIVERSAL")
    //! runtextmacro Damage__DealTypeFunc("Spell","DAMAGE_TYPE_MAGIC")
 
    public function Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns boolean
        return UnitDamageTargetEx(source, target, amount, attack, ranged, whichType, DAMAGE_TYPE_NORMAL, null)
    endfunction
    public function IsPhysical takes nothing returns boolean
        return Damage.index.damageType == DAMAGE_TYPE_NORMAL
    endfunction
 
endlibrary
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Just to give a preview of what's coming next, I've been searching around for other Damage libraries to write compatibility scripts for and - while I've been mostly successfuly - there are a few features that don't work.

1) I will be adding a Damage.enabled "variable" which will actually disable both damage event triggers when set to True and re-enable them when set to False. This will not interrupt the ongoing damage but will ensure the subsequent damage is ignored.

2) I will be adding an ability to un-register triggers (for whoever thinks that's a good idea)

3) I will be adding some more syntax sugar so you don't have to use Damage.index.source/target/amount and can just use Damage.source/target/amount if you want.

And one final mention, I've noticed an interesting feature that's gaining a bit of popularity, and that is to have embedded conditions into the damage library and have branching events off of those. Now this would normally be incredibly difficult to pull off in GUI with so many different events already in the system (DamageModifierEvent, DamageEvent, etc.) so what will I do? Make this a vJass-specific feature...

JUST KIDDING!

I love GUI and would never abandon it! Thanks to the magic of how DamageEngine hooks the GUI events, I bring to you this:

JASS:
EQUAL                 // Any damage type permitted (no change to your code needed, it will work as it always has)
NOT_EQUAL             // Same as including IsDamageCode   Equal to True
GREATER_THAN          // Same as including IsDamageSpell  Equal to True
LESS_THAN             // Same as including IsDamageAttack Equal to True
GREATER_THAN_OR_EQUAL // Same as including IsDamageRanged Equal to True
LESS_THAN_OR_EQUAL    // Same as including IsDamageMelee  Equal to True

This will provide syntax like:

  • Spell Damage Detector
    • Events
      • Game - DamageModifierEvent becomes Greater than 1
    • Conditions
      • -------- No conditions needed if IsDamageSpell was your only condition --------
    • Actions
  • Melee Damage Detector
    • Events
      • Game - DamageEvent becomes Less than or Equal to 1
    • Conditions
      • -------- No conditions needed if IsDamageMelee was your only condition --------
    • Actions
See you guys in the near future with Damage Engine 5.7.0.0 !
 
Last edited:
Level 9
Joined
Jul 18, 2005
Messages
319
Just an update on my investigation on the suspected bug I'm reporting...

So, using a dummy caster to cast a spell within a call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.00 ) block will prevent one of the Damage Engine triggers to not fire ~ which inadvertently prevents the damage (based on the recursion prevention block with the call BlzSetEventDamage(0.)?). If you use the call TimerStart(..., 0., false, ...) delay trick, the damage registers, but an extra Damage Engine trigger seems to be called even though the damage deals no damage.

I'll need to look into why this is happening on Damage Engine 5.6.2.0, as it was not a problem with Damage Engine 5.4.2.3.

(NB: I'm still looking into the recursion issue on my actual map if I were to change it to "udg_AfterDamageEvent". Current version of my MWE attached to this post.)
 

Attachments

  • MWE_Slow_v2.w3x
    55 KB · Views: 215

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Just an update on my investigation on the suspected bug I'm reporting...

So, using a dummy caster to cast a spell within a call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.00 ) block will prevent one of the Damage Engine triggers to not fire ~ which inadvertently prevents the damage (based on the recursion prevention block with the call BlzSetEventDamage(0.)?). If you use the call TimerStart(..., 0., false, ...) delay trick, the damage registers, but an extra Damage Engine trigger seems to be called even though the damage deals no damage.

I'll need to look into why this is happening on Damage Engine 5.6.2.0, as it was not a problem with Damage Engine 5.4.2.3.

(NB: I'm still looking into the recursion issue on my actual map if I were to change it to "udg_AfterDamageEvent". Current version of my MWE attached to this post.)
You have enabled the wrong debug message in Damage Engine. You need to enable the ones that say "first damage event running for " and "second damage event running for "

Additionally, the debug message about levelsDeep will tell you if Damage Engine has a recursion problem or if it's something else.

I've had a look at your map and can find no issues with the code, but Blizzard frustratingly again is not allowing me to use the Test Map function...

EDIT: the second event is caused by the "slow" debuff. It's a zero damage event made by WC3 and uses DAMAGE_TYPE_UNKNOWN.

As far as the recursion is concerned, it must be due to the AfterDamageEvent having a flaw with its recursion de-stacking process. I'll bundle the fix with DamageEngine 5.7 so stay tuned!
 
Last edited:
Level 9
Joined
Jul 18, 2005
Messages
319
EDIT: the second event is caused by the "slow" debuff. It's a zero damage event made by WC3 and uses DAMAGE_TYPE_UNKNOWN.

As far as the recursion is concerned, it must be due to the AfterDamageEvent having a flaw with its recursion de-stacking process. I'll bundle the fix with DamageEngine 5.7 so stay tuned!

That probably explains why the 0 second timer trick works then.

No worries and thanks for looking into it! I'm looking forward to the next release then~

(I do need to get familiar on which set of debug messages to enable to make my bug reporting more precise in the future. So thanks for the tip!)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
That probably explains why the 0 second timer trick works then.

No worries and thanks for looking into it! I'm looking forward to the next release then~

(I do need to get familiar on which set of debug messages to enable to make my bug reporting more precise in the future. So thanks for the tip!)
I have tested this further and cannot find any lag issues when dealing the additional damage from an AfterDamageEvent either. I am not sure what you had been doing that caused the lag but it is not anything I can reproduce.
 
Level 6
Joined
Jul 12, 2017
Messages
139
So I haven't edited map for a while and noticed that the damage engine is updated so I wonder do I need to change anything for my damage trigger?

  • Terror Aura Mannoroth Damage
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • IsDamageSpell Equal to False
      • (Level of Terror Aura (Mannoroth) for DamageEventSource) Greater than or equal to 1
    • Actions
      • Set VariableSet NextDamageType = DamageTypePure
      • Set VariableSet Real1 = (Real((Strength of DamageEventSource (Include bonuses))))
      • Set VariableSet Point1 = (Position of DamageEventTarget)
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units within 250.00 of Point1.) and do (Actions)
        • Loop - Actions
          • Set VariableSet TempUnit = (Picked unit)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (TempUnit belongs to an enemy of (Owner of DamageEventSource).) Equal to True
              • (TempUnit is alive) Equal to True
              • (TempUnit is A structure) Equal to False
            • Then - Actions
              • Unit - Cause DamageEventSource to damage TempUnit, dealing (((Real((Base Damage of DamageEventSource for weapon index 0))) x 0.50) + (Real1 x (0.50 x (Real((Level of Terror Aura (Mannoroth) for DamageEventSource)))))) damage of attack type Magic and damage type Normal
            • Else - Actions
      • Custom script: call RemoveLocation(udg_Point1)
 
Top