Shield Engine 1.2.0

This bundle is marked as pending. It has not been reviewed by a staff member yet.

General

Import & Config

How to use

Code

Changelog

Credits


Purpose

So... Here I was implementing shields in various ways and I thought I might as well do a whole engine with events :grin:

This system is meant to be (or become) a complete engine for shielding that can manage shields logic and events.
This engine avoids recursive calls and will postpone any shield created during events, avoiding variables conflicts.

I'm pretty there are more things that could be added to this system, so feel free to make suggestions.

How does this works ?

Shields can
  • prevent all type of damage or only specific (spell/attack only).
  • block an entire instance of damage or block a set amount of damage
  • block damage before or after damage reduction
  • have a visual effect
  • regen over time


With this engine, a unit can have as many shields as you want. Whenever a unit get a shield, a shield bar will appear.
A shield bar is created per type of damage it can block.

When a unit is taking damage while being shielded, shields will apply in the following order:
1/ Shield that can only take specific damage (only spell/attack) before shields that can absorb all
2/ Shield that nullify one instance of damage before those blocking amount of damage
3/ Order by timeLeft

Events List

Game - PreShieldCreationEvent Becomes Equal to 1.00 - This event fires when a shield is created, but not yet applied. Use this event to apply any modifiers you want. Depending of your config, PreShieldCreationEvent can be 2.00, 3.00 etc., several distinct steps before applying the heal to have a clear separation of modifiers.
Game - ShieldCreatedEvent Becomes Equal to 1.00 - This event runs when a shield has been applied to a unit.
Game - ShieldAbsorbEvent Becomes Equal to 1.00 - This event runs when a shield absorb damage.
Game - ShieldBreakEvent Becomes Equal to 1.00 - This event fires when a shield is broken (its amount reached 0 when taking damage).
Game - ShieldExpiredEvent Becomes Equal to 1.00 - This event runs when a timed shield runs out of time.
Game - ShieldRefreshedEvent Becomes Equal to 1.00 - This event runs when a non stackable shield is applied again to the same target.
Game - ShieldStackedEvent Becomes Equal to 1.00 - This event runs when a stackable shield is applied again to the same target.

Available variables

ShieldName - string - Name of the shield

ShieldSource - unit - Who casted the shield
ShieldWielder - unit - Who is affected by the shield

ShieldProcType - integer - When does the shield apply (before or after damage reduction).
ShieldType - integer - How does the shield block damage (set amount of damage or number of damage instance).
ShieldHpType - integer - What type of damage does the shield absorb (all, only spell, only attack).

ShieldIsStackable - boolean - Can the shield stack with itself

ShieldIsTimed - boolean - Does the shield expires after a set amount of time
ShieldDuration - real - When shield is timed, this is its duration
ShieldTimeLeft - real - When shield is timed, this is the time left before expiration

ShieldHpMax - real - How much damage can the shield absorb. If ShieldType is SHIELD_TYPE_INSTANCE, this represents how much damage instance this shield can block instead.
ShieldCurrentHp - real - How much HPs left
ShieldBreakAtZero - boolean - Does the shield break when its HP reaches 0

ShieldAbsorbedDamage - real - Last amount of damage blocked by this shield.
ShieldAbsorbedDamageSource - unit - Who did the last damage absorbed by this shield

ShieldCanRegen - boolean - Can the shield regen its HP ?
ShieldTimeBeforeRegen - real - How much time without taking damage is needed before starting regen. If set to 0, the shield will always regen
ShieldRegenTime - real - How much time is need for the shield to regen to full (from 0% to 100%).

ShieldBaseMaxHP - real - Initial max HP when shield was casted, before any modification
ShieldPrvMaxHP - real - Max HP at the end of previous step
ShieldBaseDuration - real - Initial duration when shield was casted, before any modification
ShieldPrvDuration - real - Duration at the end of previous step

How to shield using this system ?

See the Import & config tab to install this engine in your map.
See the How to use tab to know how to call this engine.


Requirements

Mandatory - Bribe's GUI Unit Event, Bribe's Damage Engine 5.A.0, Anachron's CustomBar

Import

Make sure to have option "File > Preference > Automatically create unknown variables while pasting trigger data" checked.
If you do not have the requirements in your map yet, copy and paste the "Requirements" folder.
Copy the folder "Shield Engine" and paste it in your map.

Configuration

The following variables can be config:

SHIELD_TICK_INTERVAL - real, default is 0.1 - Set the frequency of update of shield (for regen/duration).

MAX_PRESHIELD_CREATION_EVENTS - integer, default is 4 - How much PreShieldCreationEvent are fired.

SHIELD_DEFAULT_SFX_PATH - string, default points to mana shield effect - Will be used on simplified interface for shield creation as a default models for all shields
SHIELD_DEFAULT_ATTACH_POINT - string, default is "origin" - Attach point of above effect.

The 6 variables below need to be config in ShieldWielder struct onInit method.

SHIELD_COLOR_LEFT - ARGB, default is dark green - Left color of shield bar for the shields absorbing all type of damage
SHIELD_COLOR_RIGHT - ARGB, default is light green - Right color of shield bar for the shields absorbing all type of damage

SPELL_SHIELD_COLOR_LEFT - ARGB, default is dark purple - Left color of shield bar for the shields absorbing spell damage
SPELL_SHIELD_COLOR_RIGHT - ARGB, default is light purple - Right color of shield bar for the shields absorbing spell damage

ATTACK_SHIELD_COLOR_LEFT - ARGB, default is dark brown - Left color of shield bar for the shields absorbing attack damage
ATTACK_SHIELD_COLOR_RIGHT - ARGB, default is light brown - Right color of shield bar for the shields absorbing attack damage



Jass API

GUI Triggers



  • Shield Creation:
    • ShieldEngine.CreateShield
      Create a shield on given unit.
      unit source - Unit that casted the shield
      unit target - Unit that received the shield
      string name - Name of Shield, allows search/stack etc
      string sfxPath - Path to the model of visual effect
      string attachPoint - Where is the visual effect attached to the unit
      integer shieldProcType - When does the shield proc (before or after damage reduction).
      integer shieldType - Does the shield block a complete instance of damage or does it block a damage amount.
      integer shieldHpType - What kind of damage does the shield block.
      boolean isStackable - Does the shield stack with itself (if not, shield will only be refreshed when reapplied).
      boolean isTimed - Does the shield expire after a set time.
      real duration - If shield is timed, how much time does the shield last.
      real HP - How many instance/damage can the shield block.
      boolean breakAtZero - Does the shield break when it HP reaches 0.
      boolean canRegen - Can the shield regen over time.
      real timeBeforeRegen - How many seconds without taking damage before the shield start regenerating.
      real regenTime - How much time the shield would take to regen from 0% to 100%.
    • ShieldEngine.CreateTimedShield
      Simplified version of CreateShield for timed shield, with a lot less parameters.
      unit source,
      unit target,
      string name,
      real duration,
      real HP
    • ShieldEngine.CreateInstanceShield
      Simplified version of CreateShield for instance shield, with a lot less parameters.
      unit source,
      unit target,
      string name,
      real HP
  • Shield Removal/Destruction. Destroying a shield will fire a ShieldBreakEvent. Removing a shield will do it silently.
    • ShieldEngine.RemoveShieldByName
      Remove a given shield on a given unit.
      unit u,
      string name
    • ShieldEngine.RemoveAllShields
      Remove all shields on a given unit.
      unit u
    • ShieldEngine.RemoveShieldsByType
      Remove all shields protecting a given unit based on what type of damage they can absorb.
      unit u,
      integer hpType
    • ShieldEngine.DestroyShieldByName
      Destroy a given shield on a given unit.
      unit u,
      string name
    • ShieldEngine.DestroyShieldsByType
      Destroy all shields on a given unit.
      unit u,
      integer hpType
    • ShieldEngine.DestroyAllShields
      Destroy all shields protecting a given unit based on what type of damage they can absorb.
      unit u
  • Getting unit informations.
    • ShieldEngine.IsUnitShielded
      Tells if a given unit has at least one shield.
      unit u
    • ShieldEngine.UnitHasShield
      Tells if a given unit has a specific shield.
      unit u,
      string name
    • ShieldEngine.UnitHasActiveShield
      Tells if a unit has at least one shield with current HP >= 1.0
      unit u
    • ShieldEngine.UnitHasActiveShieldOfType
      Tells if a unit has at least one shield of given type with current HP >= 1.0
      unit u,
      integer hpType


General rule : When calling a GUI trigger, you need to set some variables that it will use. Those variables are always reset at the end of the trigger, so just check there to know which one are used.

  • Shield Creation.
    To create a shield, run the "Create Shield Complete" trigger.
    This requires all 16 variables to be set. Might create a bunch of simplified triggers in the future.
    327756-0aee04450f19331ece6518e7fe69fdf1.png

  • Remove Shield.
  • Destroy Shield.





FAQ

Why would you have so much different shields list ?
It can look a bit messy, and might complicate things a bit, but this avoid a lot of operations during damage blocking.
With this implementation, you only check for the needed shields on the trigger that will occur the most (except for the ShieldTickLoop that MUST check all shields).

Feel to correct me if I'm wrong and/or you see a more efficient way to implement this.

Complete engine code

vJASS:
/*
    vJass ShieldEngine 1.2.0
    by Marchombre

    Requirements:
    -   GUI Unit Event (by Bribe)
    -   Damage Engine 5.A.0.0 (by Bribe)
    -   CustomBar 1.0 (by dhk_undead_lord / aka Anachron)
*/

/*
    Apply shield in the following order:
    1/ Shield that can only take specific damage (only spell/attack) before shields that can absorb all
    2/ Shield that nullify one instance of damage before those blocking amount of damage
    3/ Order by timeLeft
*/

/*
    ================================================
                JASS API DESCRIPTION     
    ================================================
*/
/*
    These are the functions you can use to interact with the engine:

    ================================================
                    SHIELD CREATION    
    ================================================
  
    ShieldEngine.CreateShield
    |-- Parameters:
        unit source,
        unit target,
        string name,
        string sfxPath,
        string attachPoint,
        integer shieldProcType,
        integer shieldType,
        integer shieldHpType,
        boolean isStackable,
        boolean isTimed,
        real duration,
        real HP,
        boolean breakAtZero,
        boolean canRegen,
        real timeBeforeRegen,
        real regenTime
    |-- Returns: nothing

    ShieldEngine.CreateTimedShield
    |-- Parameters:
        unit source,
        unit target,
        string name,
        real duration,
        real HP
    |-- Returns: nothing

    ShieldEngine.CreateInstanceShield
    |-- Parameters:
        unit source,
        unit target,
        string name,
        real HP
    |-- Returns: nothing

    ================================================
                    SHIELD DESTRUCTION    
    ================================================

    There are two ways of destroying a shield manually. Methods with Remove will do it silently. Methods with destroy will fire a BreakEvent.

    ShieldEngine.RemoveShieldByName
    |-- Parameters:
        unit u,
        string name
    |-- Returns: nothing
  
    ShieldEngine.RemoveAllShields
    |-- Parameters:
        unit u
    |-- Returns: nothing

    ShieldEngine.RemoveShieldsByType
    |-- Parameters:
        unit u,
        integer hpType
    |-- Returns: nothing

    ShieldEngine.DestroyShieldByName
    |-- Parameters:
        unit u,
        string name
    |-- Returns: nothing

    ShieldEngine.DestroyShieldsByType
    |-- Parameters:
        unit u,
        integer hpType
    |-- Returns: nothing

    ShieldEngine.DestroyAllShields
    |-- Parameters:
        unit u
    |-- Returns: nothing

    ================================================
                    UNIT INFORMATION    
    ================================================

    ShieldEngine.IsUnitShielded
    |-- Parameters:
        unit u
    |-- Returns: boolean

    ShieldEngine.UnitHasShield
    |-- Parameters:
        unit u,
        string name
    |-- Returns: boolean

    ShieldEngine.UnitHasActiveShield
    |-- Parameters:
        unit u
    |-- Returns: boolean

    ShieldEngine.UnitHasActiveShieldOfType
    |-- Parameters:
        unit u,
        integer shieldHpType
    |-- Returns: boolean

  
*/
/*
    ================================================
                GUI API DESCRIPTION     
    ================================================
*/
/*
    ================================================
                EVENTS TRIGGER VARIABLES    
    ================================================

    udg_PreShieldCreationEvent
    udg_ShieldCreatedEvent
    udg_ShieldAbsorbEvent
    udg_ShieldExpriredEvent
    udg_ShieldBreakEvent
    udg_ShieldRefreshedEvent
    udg_ShieldStackedEvent

    ================================================
                    EVENTS GETTERS    
    ================================================
 
    // This variables should be used as readonly in GUI
    udg_ShieldName
    udg_ShieldAbsorbedDamage
    udg_ShieldAbsorbedDamageSource
    udg_ShieldProcType
    udg_ShieldHpType
    udg_ShieldBaseMaxHP
    udg_ShieldPrvMaxHP
    udg_ShieldBaseDuration
    udg_ShieldPrvDuration


    ================================================
                    EVENTS VARIABLES
    ================================================

    // This Variables can be read and/or modified during events
    udg_ShieldSource
    udg_ShieldWielder
    udg_ShieldIsStackable
    udg_ShieldIsTimed
    udg_ShieldDuration
    udg_ShieldMaxHP
    udg_ShieldCurrentHP
    udg_ShieldBreakAtZero
    udg_ShieldCanRegen
    udg_ShieldTimeBeforeRegen
    udg_ShieldRegenTime

    ================================================
                    GUI CONSTANTS
    ================================================

    // This variables should be used as readonly in GUI
    udg_SHIELD_PROC_PREREDIC
    udg_SHIELD_PROC_POSTREDUC
    udg_SHIELD_TYPE_HP_BASED
    udg_SHIELD_TYPE_INSTANCE
    udg_SHIELD_HP_TYPE_ALL
    udg_SHIELD_HP_TYPE_SPELL
    udg_SHIELD_HP_TYPE_ATTACK

    ================================================
                    GUI API TRIGGERS
    ================================================

    // This variables are used in GUI triggers to call API

    udg_NextShieldSource
    udg_NextShieldTarget
    udg_NextShieldName
    udg_NextShieldSfxPath
    udg_NextShieldSfxAttachPoint
    udg_NextShieldProcType
    udg_NextShieldType
    udg_NextShieldHpType
    udg_NextShieldIsStackable
    udg_NextShieldIsTimed
    udg_NextShieldDuration
    udg_NextShieldHP
    udg_NextShieldBreakAtZero
    udg_NextShieldCanRegen
    udg_NextShieldTimeBeforeRegen
    udg_NextShieldRegenTime

*/

library ShieldEngine requires DamageEngine, ARGB, CustomBar
    globals
        /*
            ================================================
                            CONFIG VARIABLES      
            ================================================
        */
        //
        private constant real SHIELD_TICK_INTERVAL              = 0.1       // Interval (in second) at which duration is checked and regen is applied.
        private constant integer MAX_PRESHIELD_CREATION_EVENTS  = 4       
      
        /*
            Shield aesthetic
        */

        private constant string SHIELD_DEFAULT_SFX_PATH         = "Abilities\\Spells\\Human\\ManaShield\\ManaShieldCaster.mdl"
        private constant string SHIELD_DEFAULT_ATTACH_POINT     = "origin"

        // Shield Bar colors config is done in ShieldWieder.OnInit (can't be done in globals)
        // SHIELD_HP_TYPE_ALL => Green by default
        private ARGB SHIELD_COLOR_LEFT
        private ARGB SHIELD_COLOR_RIGHT

        // SHIELD_HP_TYPE_SPELL => Purple by default
        private ARGB SPELL_SHIELD_COLOR_LEFT
        private ARGB SPELL_SHIELD_COLOR_RIGHT

        // SHIELD_HP_TYPE_ATTACK => Brown by default
        private ARGB ATTACK_SHIELD_COLOR_LEFT
        private ARGB ATTACK_SHIELD_COLOR_RIGHT
      
        /*
            ================================================
                    SHOULD NOT TOUCH THESE CONSTANTS      
            ================================================
        */
        private constant integer SHIELD_PROC_PREREDUC           = 0     // Shield absorb damage before reductions like armor are applied.
        private constant integer SHIELD_PROC_POSTREDUC          = 1     // Shield absorb damage after reductions are applied.

        private constant integer SHIELD_TYPE_HP_BASED           = 0     // Shield that expire after set amount of damage.
        private constant integer SHIELD_TYPE_INSTANCE           = 1     // Shield that expires after a number of damage instance

        private constant integer SHIELD_HP_TYPE_ALL             = 0     // Absorb all type of damage
        private constant integer SHIELD_HP_TYPE_SPELL           = 1     // Absorb only spell damage
        private constant integer SHIELD_HP_TYPE_ATTACK          = 2     // Absorb only attack damage

        // Used for Firing events
        private constant integer SHIELD_EVENT_CREATED           = 0   
        private constant integer SHIELD_EVENT_ABSORB            = 1
        private constant integer SHIELD_EVENT_BREAK             = 2
        private constant integer SHIELD_EVENT_EXPIRE            = 3
        private constant integer SHIELD_EVENT_REFRESHED         = 4
        private constant integer SHIELD_EVENT_STACKED           = 5

        /*
            ================================================
                            GUI VARIABLES      
            ================================================
        */
        /*
          

        */
    endglobals

    public function interface iChange takes CustomBar cb returns nothing

    struct Shield
        /*
            Per instance variables
        */
        public string name                          // Name of Shield, allows search/stack etc.

        public unit source                          // Unit that casted the shield
        public unit wielder                         // Unit that received the shield

        private string sfxPath                      // Path to the model of visual effect
        public effect visualEffect                  // Visual Effect attached to the unit
        private string attachPoint                  // Visual Effect attach point to the wielder

        public integer shieldProcType               // When does the shield apply
        public integer shieldType                   // How the shield block damage
        public integer shieldHpType                 // What type of damage does the shield absorb
        public boolean isStackable                  // Can this shield stack with itself

        public boolean isTimed                      // Is the shield limited in duration
        public real duration                        // If timed: number of seconds before expiration.
        public real timeLeft                        // If timed: how much time is left before expiration.
      
        public real maxHP                           // If shieldType = SHIELD_TYPE_HP_BASED: Amount of damage the shield can absorb. If shieldType = SHIELD_TYPE_INSTANCE: how many instance of damage does the shield absorb
        public real currentHP                       // Remaining HP
        public boolean breakAtZero                  // Does the shield break when its HPs reach 0
        public real lastAbsorbedDamage              // Store the last amount of damage blocked
        public unit lastAbsorbedDamageSource        // Store the unit who dealt last damage

        public boolean canRegen                     // Can the shield regenerate itself
        public real timeBeforeRegen                 // How long without taking damage before shield regen (in seconds)
        public real regenTime                       // How long should it take for a full regen (in seconds)
        public real regenPerTick                    // How much HP regen per tick

        // Following are to keep track of modifications during Events
        public real baseMaxHP                       // Initial Hp Amount when shield was casted
        public real prvMaxHP                        // Hp amount at the end of last step
        public real baseDuration                    // Initial duration when shield was casted
        public real prvDuration                     // Duration at the end of last step

        public Shield next
        public Shield prev

        public static method create takes /*
            */ unit source, /*
            */ unit target, /*
            */ string name, /*
            */ string sfxPath, /*
            */ string attachPoint, /*
            */ integer shieldProcType, /*
            */ integer shieldType, /*
            */ integer shieldHpType, /*
            */ boolean isStackable, /*
            */ boolean isTimed, /*
            */ real duration, /*
            */ real HP, /*
            */ boolean breakAtZero, /*
            */ boolean canRegen, /*
            */ real timeBeforeRegen, /*
            */ real regenTime /*
        */ returns Shield
            local Shield this = Shield.allocate()

            set this.source = source
            set this.wielder = target

            set this.name = name

            set this.sfxPath = sfxPath
            set this.attachPoint = attachPoint
            set this.visualEffect = null

            set this.shieldProcType = shieldProcType
            set this.shieldType = shieldType
            set this.shieldHpType = shieldHpType
            set this.isStackable = isStackable

            set this.isTimed = isTimed
            set this.duration = duration
            set this.timeLeft = this.duration

            // Set times to a very high amount that will not change to make it easier when sorting shields by duration
            if not this.isTimed then
                set this.duration = 1000000.00
                set this.timeLeft = 1000000.00
            endif

            set this.maxHP = HP
            set this.currentHP = HP
            set this.breakAtZero = breakAtZero

            set this.canRegen = canRegen
            set this.timeBeforeRegen = timeBeforeRegen
            set this.regenTime = regenTime

            if this.canRegen then
                set this.regenPerTick = this.maxHP / (this.regenTime / SHIELD_TICK_INTERVAL)
            else
                set this.regenPerTick = 0.00
            endif

            // Following are to keep track of modifications during Events
            set this.baseMaxHP = this.maxHP
            set this.prvMaxHP = this.maxHP
            set this.baseDuration = this.duration
            set this.prvDuration = this.duration

            set this.next = 0
            set this.next = 0
          
            return this
        endmethod

        public method destroy takes nothing returns nothing
            call DestroyEffect(this.visualEffect)
            call this.deallocate()
        endmethod

        public method CreateVisualEffet takes nothing returns nothing
            if this.visualEffect == null then
               set this.visualEffect = AddSpecialEffectTarget(this.sfxPath, this.wielder, this.attachPoint)
            endif
        endmethod

        public method Refresh takes real duration, real HP returns nothing
            local real prevMaxHP
            local real prevCurrentHP
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(this.wielder)

            set this.duration = RMaxBJ(this.duration, duration)
            set this.timeLeft = RMinBJ(this.duration, this.timeLeft + duration)
          
            set prevMaxHP = this.maxHP
            set prevCurrentHP = this.currentHP
      
            set this.maxHP = RMaxBJ(this.maxHP, HP)
            set this.currentHP = RMinBJ(this.maxHP, this.currentHP + HP)
          
            if this.shieldHpType == SHIELD_HP_TYPE_ALL then
                set wielder.totalMaxShield = wielder.totalMaxShield + (this.maxHP - prevMaxHP)
                set wielder.totalCurrentShield = wielder.totalCurrentShield + (this.currentHP - prevCurrentHP)
            elseif this.shieldHpType == SHIELD_HP_TYPE_SPELL then
                set wielder.totalMaxSpellShield = wielder.totalMaxSpellShield + (this.maxHP - prevMaxHP)
                set wielder.totalCurrentSpellShield = wielder.totalCurrentSpellShield + (this.currentHP - prevCurrentHP)
            elseif this.shieldHpType == SHIELD_HP_TYPE_ATTACK then
                set wielder.totalMaxAttackShield = wielder.totalMaxAttackShield + (this.maxHP - prevMaxHP)
                set wielder.totalCurrentAttackShield = wielder.totalCurrentAttackShield + (this.currentHP - prevCurrentHP)
            endif
          
            call ShieldEvent.FireEvent(SHIELD_EVENT_REFRESHED, this)
        endmethod

        public method Stack takes real duration, real HP returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(this.wielder)

            set this.duration = RMaxBJ(this.duration, duration)
            set this.timeLeft = RMinBJ(this.duration, this.timeLeft + duration)

            set this.maxHP = this.maxHP + HP
            set this.currentHP = this.currentHP + HP

            if this.shieldHpType == SHIELD_HP_TYPE_ALL then
                set wielder.totalMaxShield = wielder.totalMaxShield + HP
                set wielder.totalCurrentShield = wielder.totalCurrentShield + HP
            elseif this.shieldHpType == SHIELD_HP_TYPE_SPELL then
                set wielder.totalMaxSpellShield = wielder.totalMaxSpellShield + HP
                set wielder.totalCurrentSpellShield = wielder.totalCurrentSpellShield + HP
            elseif this.shieldHpType == SHIELD_HP_TYPE_ATTACK then
                set wielder.totalMaxAttackShield = wielder.totalMaxAttackShield + HP
                set wielder.totalCurrentAttackShield = wielder.totalCurrentAttackShield + HP
            endif

            call ShieldEvent.FireEvent(SHIELD_EVENT_STACKED, this)
        endmethod

    endstruct


    struct ShieldWielder
        /*
            Per instance variables
        */
        public unit wielder
        private CustomBar shieldBar
        private CustomBar spellShieldBar
        private CustomBar attackShieldBar

        public Shield firstPreReduc
        public Shield lastPreReduc

        public Shield firstPreReducSpell
        public Shield lastPreReducSpell

        public Shield firstPreReducAttack
        public Shield lastPreReducAttack

        public Shield firstPostReduc
        public Shield lastPostReduc

        public Shield firstPostReducSpell
        public Shield lastPostReducSpell

        public Shield firstPostReducAttack
        public Shield lastPostReducAttack

        public real lastDamageTaken

        public real totalMaxShield
        public real totalCurrentShield

        public real totalMaxSpellShield
        public real totalCurrentSpellShield

        public real totalMaxAttackShield
        public real totalCurrentAttackShield

        public ShieldWielder prev
        public ShieldWielder next

        public static method create takes unit wielder returns ShieldWielder
            local ShieldWielder this = ShieldWielder.allocate()

            set this.wielder                        = wielder
            set this.shieldBar                      = 0
            set this.spellShieldBar                 = 0
            set this.attackShieldBar                = 0

            set this.firstPreReduc                  = 0
            set this.lastPreReduc                   = 0
            set this.firstPreReducSpell             = 0
            set this.lastPreReducSpell              = 0
            set this.firstPreReducAttack            = 0
            set this.lastPreReducAttack             = 0

            set this.firstPostReduc                 = 0
            set this.lastPostReduc                  = 0
            set this.firstPostReducSpell            = 0
            set this.lastPostReducSpell             = 0
            set this.firstPostReducAttack           = 0
            set this.lastPostReducAttack            = 0

            set this.lastDamageTaken                = 0.00

            set this.totalMaxShield                 = 0.00
            set this.totalCurrentShield             = 0.00
            set this.totalMaxSpellShield            = 0.00
            set this.totalCurrentSpellShield        = 0.00
            set this.totalMaxAttackShield           = 0.00
            set this.totalCurrentAttackShield       = 0.00

            set this.prev = 0
            set this.next = 0

            return this
        endmethod

        public method destroy takes nothing returns nothing
            call this.deallocate()
        endmethod

        private static method UpdateShieldBar takes CustomBar cb returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(cb.target)

            set cb.txtBar.percentage = 100.0 * wielder.totalCurrentShield / wielder.totalMaxShield
        endmethod

        private static method UpdateSpellShieldBar takes CustomBar cb returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(cb.target)

            set cb.txtBar.percentage = 100.0 * wielder.totalCurrentSpellShield / wielder.totalMaxSpellShield
        endmethod

        private static method UpdateAttackShieldBar takes CustomBar cb returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(cb.target)

            set cb.txtBar.percentage = 100.0 * wielder.totalCurrentAttackShield / wielder.totalMaxAttackShield
        endmethod

        private static method ShowBarCheck takes player owner, player cur returns boolean
            return IsPlayerAlly(owner, cur) or owner == cur
        endmethod

        private method CreateShieldBar takes integer shieldHpType returns nothing
            local CustomBar cb
            local iChange updateFunction
            local real offsetX
            local real offsetY
            local ARGB colorLeft
            local ARGB colorRight

            if shieldHpType == SHIELD_HP_TYPE_SPELL then
                set cb = this.spellShieldBar
                set updateFunction = ShieldWielder.UpdateSpellShieldBar

                set offsetX = -90.0
                set offsetY = -70.0

                set colorLeft = SPELL_SHIELD_COLOR_LEFT
                set colorRight = SPELL_SHIELD_COLOR_RIGHT
            elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
                set cb = this.attackShieldBar
                set updateFunction = ShieldWielder.UpdateAttackShieldBar

                set offsetX = -90.0
                set offsetY = -40.0

                set colorLeft = ATTACK_SHIELD_COLOR_LEFT
                set colorRight = ATTACK_SHIELD_COLOR_RIGHT
            else
                set cb = this.shieldBar
                set updateFunction = ShieldWielder.UpdateShieldBar
              
                set offsetX = -90.0
                set offsetY = -10.0

                set colorLeft = SHIELD_COLOR_LEFT
                set colorRight = SHIELD_COLOR_RIGHT
            endif

            // create new bar only if there isn't already one
            if cb == 0 then
                set cb = CustomBar.create(this.wielder, updateFunction)
              
                set cb.txtTag.offsetX = offsetX
                set cb.txtTag.offsetY = offsetY

                call cb.txtBar.addGradient(colorLeft, colorRight, 1, 25)

                call this.shieldBar.Show(bj_FORCE_ALL_PLAYERS, ShieldWielder.ShowBarCheck)
      
                call this.shieldBar.showCB()

                if shieldHpType == SHIELD_HP_TYPE_SPELL then
                    set this.spellShieldBar = cb

                elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
                    set this.attackShieldBar = cb

                else
                    set this.shieldBar = cb
                endif
            endif

        endmethod

        public method AdjustBarAfterDamage takes integer shieldHpType, real dmgAmount returns nothing
            if shieldHpType == SHIELD_HP_TYPE_ALL then
                set this.totalCurrentShield = this.totalCurrentShield - dmgAmount
            elseif shieldHpType == SHIELD_HP_TYPE_SPELL then
                set this.totalCurrentSpellShield = this.totalCurrentSpellShield - dmgAmount
            elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
                set this.totalCurrentAttackShield = this.totalCurrentAttackShield - dmgAmount
            endif
        endmethod

        private method AdjustShieldBars takes string how, Shield shield returns nothing
            local CustomBar cb
          
            if shield.shieldHpType == SHIELD_HP_TYPE_SPELL then
                if how =="add" then
                    set this.totalMaxSpellShield        = this.totalMaxSpellShield + shield.maxHP
                    set this.totalCurrentSpellShield    = this.totalCurrentSpellShield + shield.currentHP

                    if this.spellShieldBar == 0 then
                        call this.CreateShieldBar(shield.shieldHpType)
                    endif
                else
                    set this.totalMaxSpellShield        = this.totalMaxSpellShield - shield.maxHP
                    set this.totalCurrentSpellShield    = this.totalCurrentSpellShield - shield.currentHP

                    if this.totalMaxSpellShield <= 0.00 then
                        call this.spellShieldBar.destroy()
                        set this.spellShieldBar = 0
                    endif
                endif
            elseif shield.shieldHpType == SHIELD_HP_TYPE_ATTACK then
                if how =="add" then
                    set this.totalMaxAttackShield       = this.totalMaxAttackShield + shield.maxHP
                    set this.totalCurrentAttackShield   = this.totalCurrentAttackShield + shield.currentHP

                    if this.attackShieldBar <= 0 then
                        call this.CreateShieldBar(shield.shieldHpType)
                    endif
                else
                    set this.totalMaxAttackShield       = this.totalMaxAttackShield - shield.maxHP
                    set this.totalCurrentAttackShield   = this.totalCurrentAttackShield - shield.currentHP

                    if this.totalMaxAttackShield <= 0.00 then
                        call this.attackShieldBar.destroy()
                        set this.attackShieldBar = 0
                    endif
                endif
            else
                if how =="add" then
                    set this.totalMaxShield             = this.totalMaxShield + shield.maxHP
                    set this.totalCurrentShield         = this.totalCurrentShield + shield.currentHP

                    if this.shieldBar == 0 then
                        call this.CreateShieldBar(shield.shieldHpType)
                    endif
                else
                    set this.totalMaxShield             = this.totalMaxShield - shield.maxHP
                    set this.totalCurrentShield         = this.totalCurrentShield - shield.currentHP

                    if this.totalMaxShield <= 0.00 then
                        call this.shieldBar.destroy()
                        set this.shieldBar = 0
                    endif
                endif
            endif
        endmethod

        private method AddFirstShield takes Shield shield returns nothing
            if shield.shieldProcType == SHIELD_PROC_PREREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ALL then
                set this.firstPreReduc = shield
                set this.lastPreReduc = shield
            elseif shield.shieldProcType == SHIELD_PROC_PREREDUC and shield.shieldHpType == SHIELD_HP_TYPE_SPELL then
                set this.firstPreReducSpell = shield
                set this.lastPreReducSpell = shield
            elseif shield.shieldProcType == SHIELD_PROC_PREREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ATTACK then
                set this.firstPreReducAttack = shield
                set this.lastPreReducAttack = shield
            elseif shield.shieldProcType == SHIELD_PROC_POSTREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ALL then
                set this.firstPostReduc = shield
                set this.lastPostReduc = shield
            elseif shield.shieldProcType == SHIELD_PROC_POSTREDUC and shield.shieldHpType == SHIELD_HP_TYPE_SPELL then
                set this.firstPostReducSpell = shield
                set this.lastPostReducSpell = shield
            elseif shield.shieldProcType == SHIELD_PROC_POSTREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ATTACK then
                set this.firstPostReducAttack = shield
                set this.lastPostReducAttack = shield
            endif
        endmethod

        public method AddShield takes Shield shield returns nothing
            local Shield current
            local Shield last
            local Shield array listOfFirst
            local integer array listOfProcType
            local integer array listOfHpType
            local integer i

            set listOfFirst[0]      = this.firstPreReduc
            set listOfFirst[1]      = this.firstPostReduc
            set listOfFirst[2]      = this.firstPreReducAttack
            set listOfFirst[3]      = this.firstPostReducAttack
            set listOfFirst[4]      = this.firstPreReducSpell
            set listOfFirst[5]      = this.firstPostReducSpell

            set listOfProcType[0]   = SHIELD_PROC_PREREDUC
            set listOfProcType[1]   = SHIELD_PROC_POSTREDUC
            set listOfProcType[2]   = SHIELD_PROC_PREREDUC
            set listOfProcType[3]   = SHIELD_PROC_POSTREDUC
            set listOfProcType[4]   = SHIELD_PROC_PREREDUC
            set listOfProcType[5]   = SHIELD_PROC_POSTREDUC

            set listOfHpType[0]     = SHIELD_HP_TYPE_ALL
            set listOfHpType[1]     = SHIELD_HP_TYPE_ALL
            set listOfHpType[2]     = SHIELD_HP_TYPE_ATTACK
            set listOfHpType[3]     = SHIELD_HP_TYPE_ATTACK
            set listOfHpType[4]     = SHIELD_HP_TYPE_SPELL
            set listOfHpType[5]     = SHIELD_HP_TYPE_SPELL

            call shield.CreateVisualEffet()
            call this.AdjustShieldBars("add", shield)

            set i = 0

            loop
                exitwhen i > 5
              
                set current = listOfFirst[i]

                // check if the correct list is empty
                if current == 0 and shield.shieldProcType == listOfProcType[i] and shield.shieldHpType == listOfHpType[i] then
                    call this.AddFirstShield(shield)

                    return
                endif

                loop
                    exitwhen current == 0 or shield.shieldProcType != listOfProcType[i] or shield.shieldHpType != listOfHpType[i]
  
                    if shield.shieldType == current.shieldType then
                        if shield.timeLeft < current.timeLeft then
                            set shield.prev = current.prev
                            set current.prev.next = shield
  
                            set shield.next = current
                            set current.prev = shield
                          
                            return
                        endif
                    elseif shield.shieldType == SHIELD_TYPE_INSTANCE and current.shieldType == SHIELD_TYPE_HP_BASED then
                        set shield.prev = current.prev
                        set current.prev.next = shield
  
                        set shield.next = current
                        set current.prev = shield
  
                        return
                    endif
                  
                    set last = current
                    set current = current.next
                endloop

                // check if it should be inserted at the end of this list
                if shield.shieldProcType == listOfProcType[i] and shield.shieldHpType == listOfHpType[i] then
                    set last.next = shield
                endif
              
                set i = i +1
            endloop

        endmethod

        public method RemoveShield takes Shield shield returns nothing
            if shield == this.firstPreReduc then
                set this.firstPreReduc = shield.next
            elseif shield == this.firstPostReduc then
                set this.firstPostReduc = shield.next
            elseif shield == this.firstPreReducAttack then
                set this.firstPreReducAttack = shield.next
            elseif shield == this.firstPostReducAttack then
                set this.firstPostReducAttack = shield.next
            elseif shield == this.firstPreReducSpell then
                set this.firstPreReducSpell = shield.next
            elseif shield == this.firstPostReducSpell then
                set this.firstPostReducSpell = shield.next
            endif

            if shield == this.lastPreReduc then
                set this.lastPreReduc = shield.prev
            elseif shield == this.lastPostReduc then
                set this.lastPostReduc = shield.prev
            elseif shield == this.lastPreReducAttack then
                set this.lastPreReducAttack = shield.prev
            elseif shield == this.lastPostReducAttack then
                set this.lastPostReducAttack = shield.prev
            elseif shield == this.lastPreReducSpell then
                set this.lastPreReducSpell = shield.prev
            elseif shield == this.lastPostReducSpell then
                set this.lastPostReduc = shield.prev
            endif

            if shield.prev != 0 then
                set shield.prev.next = shield.next
            endif
            if shield.next != 0 then
                set shield.next.prev = shield.prev
            endif
          
            call this.AdjustShieldBars("remove", shield)
            call ShieldEngine.CheckRemoveWielder(this)
        endmethod

        public method FindShieldByNameAndTypes takes string name, integer shieldProcType, integer shieldType, integer shieldHpType returns Shield
            local Shield shield = 0

            if shieldHpType == SHIELD_HP_TYPE_ALL then
                if shieldProcType == SHIELD_PROC_PREREDUC then
                    set shield = this.firstPreReduc
                else
                    set shield = this.firstPostReduc
                endif
            elseif shieldHpType == SHIELD_HP_TYPE_SPELL then
                if shieldProcType == SHIELD_PROC_PREREDUC then
                    set shield = this.firstPreReducSpell
                else
                    set shield = this.firstPostReducSpell
                endif
            elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
                if shieldProcType == SHIELD_PROC_PREREDUC then
                    set shield = this.firstPreReducAttack
                else
                    set shield = this.firstPostReducAttack
                endif
            endif
          
            loop
                exitwhen shield == 0

                if shield.name == name and shield.shieldType == shieldType then
                    return shield
                endif

                set shield = shield.next
            endloop

            return 0
        endmethod

        public method RemoveShieldsByType takes integer hpType returns nothing
            local Shield shield

            if hpType == SHIELD_HP_TYPE_ALL then
                set shield = this.firstPreReduc
            elseif hpType == SHIELD_HP_TYPE_ATTACK then
                set shield = this.firstPreReducAttack
            elseif hpType == SHIELD_HP_TYPE_SPELL then
                set shield = this.firstPreReducSpell
            endif

            loop
                exitwhen shield == 0

                call this.RemoveShield(shield)

                set shield = shield.next
            endloop

            if hpType == SHIELD_HP_TYPE_ALL then
                set shield = this.firstPostReduc
            elseif hpType == SHIELD_HP_TYPE_ATTACK then
                set shield = this.firstPostReducAttack
            elseif hpType == SHIELD_HP_TYPE_SPELL then
                set shield = this.firstPostReducSpell
            endif

            loop
                exitwhen shield == 0

                call this.RemoveShield(shield)

                set shield = shield.next
            endloop
        endmethod

        public method DestroyShieldsByType takes integer hpType returns nothing
            local Shield shield

            if hpType == SHIELD_HP_TYPE_ALL then
                set shield = this.firstPreReduc
            elseif hpType == SHIELD_HP_TYPE_ATTACK then
                set shield = this.firstPreReducAttack
            elseif hpType == SHIELD_HP_TYPE_SPELL then
                set shield = this.firstPreReducSpell
            endif

            loop
                exitwhen shield == 0

                call this.RemoveShield(shield)
                call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)

                set shield = shield.next
            endloop

            if hpType == SHIELD_HP_TYPE_ALL then
                set shield = this.firstPostReduc
            elseif hpType == SHIELD_HP_TYPE_ATTACK then
                set shield = this.firstPostReducAttack
            elseif hpType == SHIELD_HP_TYPE_SPELL then
                set shield = this.firstPostReducSpell
            endif

            loop
                exitwhen shield == 0

                call this.RemoveShield(shield)
                call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)

                set shield = shield.next
            endloop
        endmethod

        public method RemoveShieldByName takes string name returns nothing
            local Shield array listOfFirst
            local Shield shield
            local integer i = 0

            set listOfFirst[0] = this.firstPreReduc
            set listOfFirst[1] = this.firstPostReduc
            set listOfFirst[2] = this.firstPreReducAttack
            set listOfFirst[3] = this.firstPostReducAttack
            set listOfFirst[4] = this.firstPreReducSpell
            set listOfFirst[5] = this.firstPostReducSpell

            loop
                exitwhen i > 5

                set shield = listOfFirst[i]

                loop
                    exitwhen shield == 0

                    if shield.name == name then
                        call this.RemoveShield(shield)
                    endif

                    set shield = shield.next
                endloop

                set i = i +1
            endloop
        endmethod

        public method DestroyShieldByName takes string name returns nothing
            local Shield array listOfFirst
            local Shield shield
            local integer i = 0

            set listOfFirst[0] = this.firstPreReduc
            set listOfFirst[1] = this.firstPostReduc
            set listOfFirst[2] = this.firstPreReducAttack
            set listOfFirst[3] = this.firstPostReducAttack
            set listOfFirst[4] = this.firstPreReducSpell
            set listOfFirst[5] = this.firstPostReducSpell

            loop
                exitwhen i > 5

                set shield = listOfFirst[i]

                loop
                    exitwhen shield == 0

                    if shield.name == name then
                        call this.RemoveShield(shield)
                        call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
                    endif

                    set shield = shield.next
                endloop

                set i = i +1
            endloop
        endmethod

        public method HasShield takes string name returns boolean
            local Shield array listOfFirst
            local Shield shield
            local integer i = 0

            set listOfFirst[0] = this.firstPreReduc
            set listOfFirst[1] = this.firstPostReduc
            set listOfFirst[2] = this.firstPreReducAttack
            set listOfFirst[3] = this.firstPostReducAttack
            set listOfFirst[4] = this.firstPreReducSpell
            set listOfFirst[5] = this.firstPostReducSpell

            loop
                exitwhen i > 5

                set shield = listOfFirst[i]

                loop
                    exitwhen shield == 0

                    if shield.name == name then
                        return true
                    endif

                    set shield = shield.next
                endloop

                set i = i +1
            endloop

            return false
        endmethod

        private static method onInit takes nothing returns nothing
            // Shield Bar color, gradient from left to right. Parameters: Alpha, red, green, blue
            // SHIELD_HP_TYPE_ALL => Green by default
            set SHIELD_COLOR_LEFT                          = ARGB.create(255, 15, 75, 15)
            set SHIELD_COLOR_RIGHT                         = ARGB.create(255, 15, 220, 15)

            // SHIELD_HP_TYPE_SPELL => Purple by default
            set SPELL_SHIELD_COLOR_LEFT                    = ARGB.create(255, 75, 10, 75)
            set SPELL_SHIELD_COLOR_RIGHT                   = ARGB.create(255, 220, 30, 220)

            // SHIELD_HP_TYPE_ATTACK => Brown by default
            set ATTACK_SHIELD_COLOR_LEFT                   = ARGB.create(255, 220, 150, 25)
            set ATTACK_SHIELD_COLOR_RIGHT                  = ARGB.create(255, 150, 75, 25)
        endmethod
    endstruct

    struct ShieldEvent
        /*
            Per instance variables
        */
        private integer eventType
        private Shield shield

        private ShieldEvent next
      
        /*
            Shared variables
        */
        private static ShieldEvent first
        private static ShieldEvent last

        public static method create takes integer eventType, Shield shield returns ShieldEvent
            local ShieldEvent this = ShieldEvent.allocate()

            set this.eventType = eventType
            set this.shield = shield

            set this.next = 0

            return this
        endmethod

        private method destroy takes nothing returns nothing
            call this.deallocate()
        endmethod

        private static method resetEventVariables takes nothing returns nothing
            set udg_ShieldName              = null
          
            set udg_ShieldSource            = null
            set udg_ShieldWielder           = null

            set udg_ShieldProcType          = -1
            set udg_ShieldType              = -1
            set udg_ShieldHpType            = -1

            set udg_ShieldIsStackable       = false
          
            set udg_ShieldIsTimed           = false
            set udg_ShieldDuration          = 0.00
            set udg_ShieldTimeLeft          = 0.00

            set udg_ShieldMaxHP             = 0.00
            set udg_ShieldCurrentHP         = 0.00
            set udg_ShieldBreakAtZero       = false

            set udg_ShieldAbsorbedDamage    = 0.00

            set udg_ShieldCanRegen          = false
            set udg_ShieldTimeBeforeRegen   = 0.00
            set udg_ShieldRegenTime         = 0.00

            set udg_ShieldBaseMaxHP         = 0.00
            set udg_ShieldPrvMaxHP          = 0.00
            set udg_ShieldBaseDuration      = 0.00
            set udg_ShieldPrvDuration       = 0.00
        endmethod

        private method setEventVariables takes nothing returns nothing
            set udg_ShieldName                  = this.shield.name
          
            set udg_ShieldSource                = this.shield.source
            set udg_ShieldWielder               = this.shield.wielder

            set udg_ShieldProcType              = this.shield.shieldProcType
            set udg_ShieldType                  = this.shield.shieldType
            set udg_ShieldHpType                = this.shield.shieldHpType

            set udg_ShieldIsStackable           = this.shield.isStackable
          
            set udg_ShieldIsTimed               = this.shield.isTimed
            set udg_ShieldDuration              = this.shield.duration
            set udg_ShieldTimeLeft              = this.shield.timeLeft

            set udg_ShieldMaxHP                 = this.shield.maxHP
            set udg_ShieldCurrentHP             = this.shield.currentHP
            set udg_ShieldBreakAtZero           = this.shield.breakAtZero

            set udg_ShieldAbsorbedDamage        = this.shield.lastAbsorbedDamage
            set udg_ShieldAbsorbedDamageSource  = this.shield.lastAbsorbedDamageSource

            set udg_ShieldCanRegen              = this.shield.canRegen
            set udg_ShieldTimeBeforeRegen       = this.shield.timeBeforeRegen
            set udg_ShieldRegenTime             = this.shield.regenTime

            set udg_ShieldBaseMaxHP             = this.shield.baseMaxHP
            set udg_ShieldPrvMaxHP              = this.shield.prvMaxHP
            set udg_ShieldBaseDuration          = this.shield.baseDuration
            set udg_ShieldPrvDuration           = this.shield.prvDuration
        endmethod

        private method getEventVariables takes nothing returns nothing
            set this.shield.source              = udg_ShieldSource
            set this.shield.wielder             = udg_ShieldWielder

            set this.shield.isStackable         = udg_ShieldIsStackable
          
            set this.shield.isTimed             = udg_ShieldIsTimed
            set this.shield.duration            = udg_ShieldDuration
            set this.shield.timeLeft            = udg_ShieldTimeLeft

            set this.shield.maxHP               = udg_ShieldMaxHP
            set this.shield.currentHP           = udg_ShieldCurrentHP
            set this.shield.breakAtZero         = udg_ShieldBreakAtZero

            set this.shield.canRegen            = udg_ShieldCanRegen
            set this.shield.timeBeforeRegen     = udg_ShieldTimeBeforeRegen
            set this.shield.regenTime           = udg_ShieldRegenTime

            if this.shield.canRegen then
                set this.shield.regenPerTick = this.shield.maxHP / (this.shield.regenTime / SHIELD_TICK_INTERVAL)
            else
                set this.shield.regenPerTick = 0.00
            endif

            // Stores this step result for reference in next step if needed.
            set this.shield.prvMaxHP            = udg_ShieldMaxHP
            set this.shield.prvDuration         = udg_ShieldDuration         
        endmethod

        public static method FireEvent takes integer eventType, Shield shield returns nothing
            local ShieldEvent shieldEvent = ShieldEvent.create(eventType, shield)

            if first == 0 then
                set first = shieldEvent
                set last = shieldEvent
                call ShieldEvent.Run()
            else
                set last.next = shieldEvent
                set last = last.next
            endif

        endmethod

        private static method Run takes nothing returns nothing
            local ShieldEvent shEvent
            local real ev
            local ShieldWielder wielder

            loop
                exitwhen first == 0
              
                set shEvent = first
                set wielder = ShieldEngine.GetUnitWielder(shEvent.shield.wielder)
              
                if shEvent.eventType == SHIELD_EVENT_CREATED then
                    set ev = 1.00
                    loop
                        exitwhen ev > MAX_PRESHIELD_CREATION_EVENTS
                        // Set GUI vars
                        call shEvent.setEventVariables()

                        // Fire PreCreation event
                        set udg_PreShieldCreationEvent  = 0.00
                        set udg_PreShieldCreationEvent  = ev
                        set udg_PreShieldCreationEvent  = 0.00

                        // Update fields in case event was caught and variables changed.
                        call shEvent.getEventVariables()

                        set ev = ev + 1.00
                    endloop
                  
                    if (not shEvent.shield.isTimed or shEvent.shield.timeLeft > 0.00) /*
                        */ and (not shEvent.shield.breakAtZero or shEvent.shield.currentHP > 0.00) /*
                    */ then

                        // Attach shield to wielder
                        call wielder.AddShield(shEvent.shield)

                        // Set GUI vars
                        call shEvent.setEventVariables()

                        // Fire ShieldCreated event
                        set udg_ShieldCreatedEvent          = 0.00
                        set udg_ShieldCreatedEvent          = 1.00
                        set udg_ShieldCreatedEvent          = 0.00
                    endif
                elseif shEvent.eventType == SHIELD_EVENT_ABSORB then
                    // Set GUI vars
                    call shEvent.setEventVariables()

                    set udg_ShieldAbsorbEvent           = 0.00
                    set udg_ShieldAbsorbEvent           = 1.00
                    set udg_ShieldAbsorbEvent           = 0.00

                    // Update fields in case event was caught and variables changed.
                    call shEvent.getEventVariables()
                elseif shEvent.eventType == SHIELD_EVENT_EXPIRE then
                    // Set GUI vars
                    call shEvent.setEventVariables()

                    set udg_ShieldExpiredEvent          = 0.00
                    set udg_ShieldExpiredEvent          = 1.00
                    set udg_ShieldExpiredEvent          = 0.00

                    call shEvent.shield.destroy()
                elseif shEvent.eventType == SHIELD_EVENT_BREAK then
                    // Set GUI vars
                    call shEvent.setEventVariables()

                    set udg_ShieldBreakEvent            = 0.00
                    set udg_ShieldBreakEvent            = 1.00
                    set udg_ShieldBreakEvent            = 0.00

                    call shEvent.shield.destroy()
                elseif shEvent.eventType == SHIELD_EVENT_REFRESHED then
                    // Set GUI vars
                    call shEvent.setEventVariables()

                    set udg_ShieldRefreshedEvent        = 0.00
                    set udg_ShieldRefreshedEvent        = 1.00
                    set udg_ShieldRefreshedEvent        = 0.00

                    // Update fields in case event was caught and variables changed.
                    call shEvent.getEventVariables()
                elseif shEvent.eventType == SHIELD_EVENT_STACKED then
                    // Set GUI vars
                    call shEvent.setEventVariables()

                    set udg_ShieldStackedEvent          = 0.00
                    set udg_ShieldStackedEvent          = 1.00
                    set udg_ShieldStackedEvent          = 0.00

                    // Update fields in case event was caught and variables changed.
                    call shEvent.getEventVariables()
                endif
                  
                // Resets variables
                call resetEventVariables()

                // Set the next ShieldEvent that will be run
                set first = shEvent.next
              
                // Free resources
                call shEvent.destroy()
            endloop
        endmethod

        private static method onInit takes nothing returns nothing
            set first = 0
            set last = 0
        endmethod
    endstruct

    struct ShieldEngine
        /*
            Shared variables
        */
        private static timer clock
        private static timer tickTimer
        private static trigger preReducTrigger
        private static trigger postReducTrigger
        private static trigger tickTrigger
        private static trigger unregisterTrigger

        private static boolean array isInSystem
        private static ShieldWielder array unitToWielder
        private static ShieldWielder firstWielder
        private static ShieldWielder lastWielder
      
        private static method ApplyShields takes integer procType returns nothing
            local Shield shield //= 0
            local real blockedDmg

            local unit dmgTarget = udg_DamageEventTarget
            local integer uId = GetUnitUserData(dmgTarget)
            local ShieldWielder wielder = unitToWielder[uId]

            local boolean isDmgSpell = udg_IsDamageSpell
            local boolean isDmgAtt = udg_IsDamageAttack

            set wielder.lastDamageTaken = TimerGetElapsed(clock)

            /*
                Loop through shields, applying one by one until either no shield left or no damage left
                Shields order criterias
                1/ Shield that can only take specific damage (only spell/attack) before shields that can absorb all
                2/ Shield that nullify one instance of damage before those blocking amount of damage
                3/ Order by timeLeft
            */

            if procType == SHIELD_PROC_PREREDUC then
                if isDmgSpell then
                    set shield = wielder.firstPreReducSpell
                elseif isDmgAtt then
                    set shield = wielder.firstPreReducAttack
                endif
            elseif procType == SHIELD_PROC_POSTREDUC then
                if isDmgSpell then
                    set shield = wielder.firstPostReducSpell
                elseif isDmgAtt then
                    set shield = wielder.firstPostReducAttack
                endif
            endif

            // Loop through shields against specific damage
            loop
                exitwhen shield == 0 or udg_DamageEventAmount == 0.00
              
                if shield.shieldType == SHIELD_TYPE_INSTANCE and shield.currentHP >= 1.00 then
                    set shield.lastAbsorbedDamage = udg_DamageEventAmount
                    set shield.lastAbsorbedDamageSource = udg_DamageEventSource
                    set udg_DamageEventAmount = 0.00
                  
                    set shield.currentHP = shield.currentHP - 1.00
                    call wielder.AdjustBarAfterDamage(shield.shieldHpType, 1.00)

                    call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)

                    if shield.currentHP == 0 and shield.breakAtZero then
                        call wielder.RemoveShield(shield)
                        call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
                    endif
                elseif shield.shieldType == SHIELD_TYPE_HP_BASED and shield.currentHP >= 0.00 then
                    if shield.currentHP > udg_DamageEventAmount then
                        set shield.currentHP = shield.currentHP - udg_DamageEventAmount
                        call wielder.AdjustBarAfterDamage(shield.shieldHpType, udg_DamageEventAmount)

                        set shield.lastAbsorbedDamage = udg_DamageEventAmount
                        set shield.lastAbsorbedDamageSource = udg_DamageEventSource
                        set udg_DamageEventAmount = 0.00

                        call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
                    else
                        set udg_DamageEventAmount = udg_DamageEventAmount - shield.currentHP
                        call wielder.AdjustBarAfterDamage(shield.shieldHpType, shield.currentHP)

                        set shield.lastAbsorbedDamage = shield.currentHP
                        set shield.lastAbsorbedDamageSource = udg_DamageEventSource
                        set shield.currentHP = 0.00

                        call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)

                        if shield.breakAtZero then
                            call wielder.RemoveShield(shield)
                            call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
                        endif
                    endif
                  
                endif

                set shield = shield.next
            endloop

            if procType == SHIELD_PROC_PREREDUC then
                set shield = wielder.firstPreReduc
            elseif procType == SHIELD_PROC_POSTREDUC then
                set shield = wielder.firstPostReduc
            endif

            // Loop through shields against all type of damage
            loop
                exitwhen shield == 0 or udg_DamageEventAmount == 0.00
              
                if shield.shieldType == SHIELD_TYPE_INSTANCE and shield.currentHP >= 1.00 then
                    set shield.lastAbsorbedDamage = udg_DamageEventAmount
                    set shield.lastAbsorbedDamageSource = udg_DamageEventSource
                    set udg_DamageEventAmount = 0.00
                  
                    set shield.currentHP = shield.currentHP - 1.00
                    call wielder.AdjustBarAfterDamage(shield.shieldHpType, 1.00)

                    call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)

                    if shield.currentHP == 0 and shield.breakAtZero then
                        call wielder.RemoveShield(shield)
                        call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
                    endif
                elseif shield.shieldType == SHIELD_TYPE_HP_BASED and shield.currentHP >= 0.00 then
                    if shield.currentHP > udg_DamageEventAmount then
                        set shield.currentHP = shield.currentHP - udg_DamageEventAmount
                        call wielder.AdjustBarAfterDamage(shield.shieldHpType, udg_DamageEventAmount)

                        set shield.lastAbsorbedDamage = udg_DamageEventAmount
                        set shield.lastAbsorbedDamageSource = udg_DamageEventSource
                        set udg_DamageEventAmount = 0.00

                        call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
                    else
                        set udg_DamageEventAmount = udg_DamageEventAmount - shield.currentHP
                        call wielder.AdjustBarAfterDamage(shield.shieldHpType, shield.currentHP)

                        set shield.lastAbsorbedDamage = shield.currentHP
                        set shield.lastAbsorbedDamageSource = udg_DamageEventSource
                        set shield.currentHP = 0.00

                        call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)

                        if shield.breakAtZero then
                            call wielder.RemoveShield(shield)
                            call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
                        endif
                    endif
                  
                endif

                set shield = shield.next
            endloop
        endmethod

        private static method ApplyShieldPreReduc takes nothing returns nothing
            call ShieldEngine.ApplyShields(SHIELD_PROC_PREREDUC)
        endmethod

        private static method ApplyShieldPostReduc takes nothing returns nothing
            call ShieldEngine.ApplyShields(SHIELD_PROC_POSTREDUC)
        endmethod

        private static method ShieldTickLoop takes nothing returns nothing
            local ShieldWielder wielder = firstWielder
            local Shield shield
            local Shield array listOfFirst
            local integer i

          
            // Update every shield of every shield wielder
            loop
                exitwhen wielder == 0

                set listOfFirst[0] = wielder.firstPreReduc
                set listOfFirst[1] = wielder.firstPostReduc
                set listOfFirst[2] = wielder.firstPreReducAttack
                set listOfFirst[3] = wielder.firstPostReducAttack
                set listOfFirst[4] = wielder.firstPreReducSpell
                set listOfFirst[5] = wielder.firstPostReducSpell
              
                set i = 0

                loop
                    exitwhen i > 5

                    set shield = listOfFirst[i]

                    loop
                        exitwhen shield == 0

                        if shield.canRegen and shield.currentHP != shield.maxHP and (TimerGetElapsed(clock) - wielder.lastDamageTaken > shield.timeBeforeRegen) then
                            set shield.currentHP = RMinBJ(shield.maxHP, shield.currentHP + shield.regenPerTick)

                            if shield.shieldHpType == SHIELD_HP_TYPE_ALL then
                                set wielder.totalCurrentShield = wielder.totalCurrentShield + shield.regenPerTick
                            elseif shield.shieldType == SHIELD_HP_TYPE_SPELL then
                                set wielder.totalCurrentSpellShield = wielder.totalCurrentSpellShield + shield.regenPerTick
                            elseif shield.shieldType == SHIELD_HP_TYPE_ATTACK then
                                set wielder.totalCurrentAttackShield = wielder.totalCurrentAttackShield + shield.regenPerTick
                            endif
                        endif

                        if shield.isTimed then

                            set shield.timeLeft = shield.timeLeft - SHIELD_TICK_INTERVAL
                          
                            if shield.timeLeft <= 0.00 then
                                call wielder.RemoveShield(shield)
                                call ShieldEvent.FireEvent(SHIELD_EVENT_EXPIRE, shield)
                            endif
                        endif

                        set shield = shield.next
                    endloop

                    set i = i +1
                endloop

                set wielder = wielder.next
            endloop
        endmethod

        public static method GetUnitWielder takes unit u returns ShieldWielder
            local integer uId = GetUnitUserData(u)

            if isInSystem[uId] then
                return unitToWielder[uId]
            endif

            return 0
        endmethod

        private static method IsUnitInSytem takes nothing returns boolean
            local integer uId = GetUnitUserData(udg_DamageEventTarget)

            return isInSystem[uId]
        endmethod

        private static method onInit takes nothing returns nothing
            /*
                ================================================
                                    TRIGGERS     
                ================================================
            */

            set clock                   = CreateTimer()
            set tickTimer               = CreateTimer()
            set preReducTrigger         = CreateTrigger()
            set postReducTrigger        = CreateTrigger()
            set tickTrigger             = CreateTrigger()
            set unregisterTrigger       = CreateTrigger()

            call StartTimerBJ(clock, true, 99999.00)
            call StartTimerBJ(tickTimer, true, SHIELD_TICK_INTERVAL)
          
            // Check shields before damage reduction
            call TriggerRegisterVariableEvent(preReducTrigger, "udg_PreDamageEvent", EQUAL, 1.00)
            call TriggerAddAction(preReducTrigger, function ShieldEngine.ApplyShieldPreReduc)
            call TriggerAddCondition(preReducTrigger, function ShieldEngine.IsUnitInSytem)

            // Check shields after damage reduction
            call TriggerRegisterVariableEvent(postReducTrigger, "udg_ArmorDamageEvent", EQUAL, 1.00)
            call TriggerAddAction(postReducTrigger, function ShieldEngine.ApplyShieldPostReduc)
            call TriggerAddCondition(preReducTrigger, function ShieldEngine.IsUnitInSytem)

            // Loop Trigger for duration and regeneration
            call TriggerRegisterTimerExpireEvent(tickTrigger, tickTimer)
            call TriggerAddAction(tickTrigger, function ShieldEngine.ShieldTickLoop)

            // Remove a unit from system if it is unindex by UnitEvent
            call TriggerRegisterVariableEvent(unregisterTrigger, "udg_UnitIndexEvent", EQUAL, 2.00)
            call TriggerRegisterVariableEvent(unregisterTrigger, "udg_DeathEvent", EQUAL, 0.50)
            call TriggerRegisterVariableEvent(unregisterTrigger, "udg_DeathEvent", EQUAL, 1.00)
            call TriggerRegisterVariableEvent(unregisterTrigger, "udg_DeathEvent", EQUAL, 3.00)
            call TriggerAddAction(unregisterTrigger, function ShieldEngine.RemoveUnitFromSystem)

            // Disable triggers until a unit is registered
            call DisableTrigger(preReducTrigger)
            call DisableTrigger(postReducTrigger)
            call DisableTrigger(tickTrigger)

            /*
                ================================================
                            Struct members init      
                ================================================
            */

            set firstWielder = 0
            set lastWielder = 0
          
            /*
                ================================================
                                GUI VARIABLES      
                ================================================
            */

            set udg_SHIELD_PROC_PREREDUC = SHIELD_PROC_PREREDUC
            set udg_SHIELD_PROC_POSTREDUC = SHIELD_PROC_POSTREDUC
  
            set udg_SHIELD_TYPE_HP_BASED = SHIELD_TYPE_HP_BASED
            set udg_SHIELD_TYPE_INSTANCE = SHIELD_TYPE_INSTANCE
  
            set udg_SHIELD_HP_TYPE_ALL = SHIELD_HP_TYPE_ALL
            set udg_SHIELD_HP_TYPE_SPELL = SHIELD_HP_TYPE_SPELL
            set udg_SHIELD_HP_TYPE_ATTACK = SHIELD_HP_TYPE_ATTACK
        endmethod

        public static method RemoveUnitFromSystem takes nothing returns nothing
            local integer uId = udg_UDex
            local ShieldWielder wielder

            if isInSystem[uId] then
                set wielder = unitToWielder[uId]

                call ShieldEngine.RemoveAllShields(wielder.wielder)
            endif
        endmethod

        public static method CheckRemoveWielder takes ShieldWielder wielder returns nothing
          
            if wielder.totalMaxShield == 0.00 /*
                */ and wielder.totalMaxSpellShield == 0.00 /*
                */ and wielder.totalMaxAttackShield == 0.00 /*
            */ then
                if wielder == firstWielder then
                    set firstWielder = wielder.next
                  
                    set isInSystem[GetUnitUserData(wielder.wielder)] = false
                    call wielder.destroy()

                    if firstWielder == 0 then
                        call DisableTrigger(preReducTrigger)
                        call DisableTrigger(postReducTrigger)
                        call DisableTrigger(tickTrigger)
                    endif
                else
                    set wielder.prev.next = wielder.next
                    set wielder.next.prev = wielder.prev

                    set isInSystem[GetUnitUserData(wielder.wielder)] = false
                    call wielder.destroy()

                endif
            endif
        endmethod

        /*
            ================================================
                            SYSTEM API       
            ================================================
        */

        // Tells if a unit is shielded
        public static method IsUnitShielded takes unit u returns boolean
            local integer uId = GetUnitUserData(u)

            return isInSystem[uId]
        endmethod

        // Tells if a unit is shielded AND if current total shield is greater than 0.
        public static method UnitHasActiveShield takes unit u returns boolean
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            return ShieldEngine.UnitHasActiveShieldOfType(u, SHIELD_HP_TYPE_ALL) /*
                */ or ShieldEngine.UnitHasActiveShieldOfType(u, SHIELD_HP_TYPE_ATTACK) /*
                */ or ShieldEngine.UnitHasActiveShieldOfType(u, SHIELD_HP_TYPE_SPELL)
        endmethod

        public static method UnitHasActiveShieldOfType takes unit u, integer shieldHpType returns boolean
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            if wielder == 0 then
                return false
            endif

            if shieldHpType == SHIELD_HP_TYPE_ALL then
                return wielder.totalCurrentShield >= 1.0
            elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
                return wielder.totalCurrentAttackShield >= 1.0
            else
                return wielder.totalCurrentSpellShield >= 1.0
            endif
        endmethod

        // Tells if unit has a specific shield
        public static method UnitHasShield takes unit u, string name returns boolean
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            return wielder.HasShield(name)
        endmethod

        /*
            The following methods remove/destroy shields.
            Removing: No Event if fired, shield disappear without any trace.
            Destroying: Shield Break Event is fired when destroying a shield.
        */
        public static method RemoveShieldByName takes unit u, string name returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            call wielder.RemoveShieldByName(name)
        endmethod

        public static method DestroyShieldByName takes unit u, string name returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            call wielder.DestroyShieldByName(name)
        endmethod

        public static method RemoveShieldsByType takes unit u, integer hpType returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            call wielder.RemoveShieldsByType(hpType)
        endmethod

        public static method RemoveAllShields takes unit u returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            call wielder.RemoveShieldsByType(SHIELD_HP_TYPE_ALL)
            call wielder.RemoveShieldsByType(SHIELD_HP_TYPE_SPELL)
            call wielder.RemoveShieldsByType(SHIELD_HP_TYPE_ATTACK)
        endmethod

        public static method DestroyShieldsByType takes unit u, integer hpType returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
          
            call wielder.DestroyShieldsByType(hpType)
        endmethod

        public static method DestroyAllShields takes unit u returns nothing
            local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)

            call wielder.DestroyShieldsByType(SHIELD_HP_TYPE_ALL)
            call wielder.DestroyShieldsByType(SHIELD_HP_TYPE_SPELL)
            call wielder.DestroyShieldsByType(SHIELD_HP_TYPE_ATTACK)
        endmethod

        /*
            ================================================
                            ENTRY POINT      
            ================================================
        */

        public static method CreateShield takes /*
            */ unit source, /*
            */ unit target, /*
            */ string name, /*
            */ string sfxPath, /*
            */ string attachPoint, /*
            */ integer shieldProcType, /*
            */ integer shieldType, /*
            */ integer shieldHpType, /*
            */ boolean isStackable, /*
            */ boolean isTimed, /*
            */ real duration, /*
            */ real HP, /*
            */ boolean breakAtZero, /*
            */ boolean canRegen, /*
            */ real timeBeforeRegen, /*
            */ real regenTime /*
        */ returns nothing
            local integer uId = GetUnitUserData(target)
            local ShieldWielder wielder
            local Shield shield

            if isInSystem[uId] then
                set wielder = unitToWielder[uId]
            else
                set wielder = ShieldWielder.create(target)
                set unitToWielder[uId] = wielder
                set isInSystem[uId] = true
                if firstWielder == 0 then
                    set firstWielder = wielder
                    set lastWielder = wielder

                    call EnableTrigger(preReducTrigger)
                    call EnableTrigger(postReducTrigger)
                    call EnableTrigger(tickTrigger)
                else
                    set wielder.prev = lastWielder
                    set lastWielder.next = wielder
                    set lastWielder = lastWielder.next
                endif
            endif

            // Check if shield with same name and types exist
            set shield = wielder.FindShieldByNameAndTypes(name, shieldProcType, shieldType, shieldHpType)


            // If didn't exist, create new one
            if shield == 0 then
                set shield = Shield.create(source, target, name, sfxPath, attachPoint, shieldProcType, shieldType, shieldHpType, isStackable, isTimed, duration, HP, breakAtZero, canRegen, timeBeforeRegen, regenTime)
              
                call ShieldEvent.FireEvent(SHIELD_EVENT_CREATED, shield)
            else
                // if both previous shield and new are stackable, then stack, else refresh
                if shield.isStackable and isStackable then
                    call shield.Stack(duration, HP)
                else
                    call shield.Refresh(duration, HP)
                endif
            endif

        endmethod

        /*
            ================================================
                        SIMPLIFIED FUNCTIONS
            ================================================
        */

        public static method CreateTimedShield takes unit source, unit target, string name, real duration, real HP returns nothing
            call ShieldEngine.CreateShield(source, target, name, SHIELD_DEFAULT_SFX_PATH, SHIELD_DEFAULT_ATTACH_POINT, SHIELD_PROC_POSTREDUC, SHIELD_TYPE_HP_BASED, SHIELD_HP_TYPE_ALL, false, true, duration, HP, true, false, 0.0, 0.0)
        endmethod

        public static method CreateInstanceShield takes unit source, unit target, string name, real HP returns nothing
            call ShieldEngine.CreateShield(source, target, name, SHIELD_DEFAULT_SFX_PATH, SHIELD_DEFAULT_ATTACH_POINT, SHIELD_PROC_PREREDUC, SHIELD_TYPE_INSTANCE, SHIELD_HP_TYPE_ALL, false, false, 0.0, HP, true, false, 0.0, 0.0)
        endmethod

    endstruct
endlibrary


Version 1.2.0 (2025-04-14)
Added:
  • GUI variables:
    • ShieldAbsorbedDamageSource - unit - Who did the last damage absorbed by this shield
    • ShieldBaseMaxHP - real - Initial max HP when shield was casted, before any modification
    • ShieldPrvMaxHP - real - Max HP at the end of previous step
    • ShieldBaseDuration - real - Initial duration when shield was casted, before any modification
    • ShieldPrvDuration - real - Duration at the end of previous step
  • Jass Functions:
    • ShieldEngine.UnitHasActiveShield - tells if a unit has at least on shield with current HP >= 1.0
    • ShieldEngine.UnitHasActiveShieldOfType - tells if a unit has at least on shield of given type with current HP >= 1.0
    • ShieldEngine.CreateInstanceShield - Simplified version for ShieldEngine.CreateShield with much less parameters for Instance shields.

Version 1.1.0 (2025-03-31)
Added:
  • API description
  • ShieldEngine.IsUnitShielded : tells if unit has at least one shield attached to them
  • ShieldEngine.UnitHasShield : tells if unit has a shield with given name
  • ShieldEngine.CreateTimedShield : Simplified version for ShieldEngine.CreateShield with much less parameters.
  • Triggers to use remove and destroy shield functions.

Bug fixes:
  • Fixed a bug where shield effect and shield HP bar would not be destroyed for Attack & Spell shields when shield was stacked.
  • Fixed a bug where shield HP bar would not correctly update when shield was stacked.
  • Fixed an issue where some functions would check shieldType instead of shieldProcType or shieldHpType.

Version 1.1.1

Bug fixes:
- Units and their shields are now properly removed from the system if they're unindex by UnitEvent.


Version 1.0.0 (2025-03-28)
First upload.

Credits to Vinz for his Sacred Guard model used in demo map.


Keywords:
shield, shielding, shield event, shield engine, EVENT_PLAYER_UNIT_SHIELDED
Previews
Contents

Shield Engine 1.1.1 (Map)

Shield Engine 1.2.0 (Map)

Level 9
Joined
Nov 18, 2014
Messages
80
Not to burst your bubble but you've got tough competition for a shield system (not mine) already GUI Friendly Shield System 1.00b
Oh, why on earth did it not come up in my search result when I tried to look for something like this ? :confused:2
All I could find was some really outdated systems. Guess I'm blind :grin:

Thanks for linking it, I'll look and see if it covers the same things (and how it's probably more optimized than mine).
 
Level 9
Joined
Nov 18, 2014
Messages
80
Found a few things to fix.


Version 1.1.0


Added:
  • API description
  • ShieldEngine.IsUnitShielded : tells if unit has at least one shield attached to them
  • ShieldEngine.UnitHasShield : tells if unit has a shield with given name
  • ShieldEngine.CreateTimedShield : Simplified version for ShieldEngine.CreateShield with much less parameters.
  • Triggers to use remove and destroy shield functions.

Bug fixes:
  • Fixed a bug where shield effect and shield HP bar would not be destroyed for Attack & Spell shields when shield was stacked.
  • Fixed a bug where shield HP bar would not correctly update when shield was stacked.
  • Fixed an issue where some functions would check shieldType instead of shieldProcType or shieldHpType.

Version 1.1.1

Bug fixes:
- Units and their shields are now properly removed from the system if they're unindex by UnitEvent.

 
Last edited:
Level 9
Joined
Nov 18, 2014
Messages
80
After playing with the system a bit, I added a few things that were needed.
Updated this thread to detail Jass API.

Version 1.2.0

Added:
  • GUI variables:
    • ShieldAbsorbedDamageSource - unit - Who did the last damage absorbed by this shield
    • ShieldBaseMaxHP - real - Initial max HP when shield was casted, before any modification
    • ShieldPrvMaxHP - real - Max HP at the end of previous step
    • ShieldBaseDuration - real - Initial duration when shield was casted, before any modification
    • ShieldPrvDuration - real - Duration at the end of previous step
  • Jass Functions:
    • ShieldEngine.UnitHasActiveShield - tells if a unit has at least one shield with current HP >= 1.0
    • ShieldEngine.UnitHasActiveShieldOfType - tells if a unit has at least one shield of given type with current HP >= 1.0
    • ShieldEngine.CreateInstanceShield - Simplified version for ShieldEngine.CreateShield with much less parameters for Instance shields.
 
Top