//###########################################################################################################
//# Blood Rage #
//# Created by Erkki2 #
//# Required libraries: #
//# Unit Indexer by Bribe #
//# Damage Engine by Bribe #
//# #
//# Import instructions: #
//# 1. Copy the cheat death ability to your map and create an ability for Blood Rage #
//# 2. Check "automatically create unknown variables while pasting trigger data" in map preferences #
//# 3. Copy this trigger and required libraries to your map #
//# 4. Set DamageBlockingAbility in Damage Engine trigger to the ability copied at 1 #
//# 5. Set configurables in this trigger #
//# 6. To modify rage from in your own triggers, call addRage(unit, amount) #
//###########################################################################################################
scope BloodRage
// #################
// # CONFIGURABLES #
// #################
globals
//Raw code of the spell ability
private constant integer ABIL_CODE = 'A000'
//Iteration interval
private constant real TIMEOUT = 0.03125
//How high above unit the bar is
private constant real BAR_HEIGHT = 150.0
//Maximum amount of rage
private constant real MAX_RAGE = 1000.0
//How many characters ("|") the bar has
private constant integer BAR_WIDTH = 150
//Setting this to smaller value will move the bar left, and higher value to tight
private constant real LETTER_WIDTH = 0.4
//Height of the bar
private constant real BAR_FONT_SIZE = 0.015
//Colour settings for the bar
private constant integer BAR_RED = 255
private constant integer BAR_GREEN = 0
private constant integer BAR_BLUE = 0
private constant integer BAR_ALPHA = 255
private constant integer BAR_BACKGROUND_RED = 102
private constant integer BAR_BACKGROUND_GREEN = 0
private constant integer BAR_BACKGROUND_BLUE = 0
private constant integer BAR_BACKGROUND_ALPHA = 255
//Unit will gain (X * damage dealt) rage points when dealing damage
private constant real ATTACK_RAGE_FACTOR = 0.3
//Unit will gain X rage when an allied unit dies nearby
private constant real ALLY_DEATH_RAGE_AMOUNT = 50
//Unit will gain X rage when an enemy unit dies nearby
private constant real ENEMY_DEATH_RAGE_AMOUNT = 30
//Unit will gain rage points when a unit dies within this area
private constant real RAGE_GAIN_AREA = 800
//The speed at which rage depletes
private constant real DEGENERATION_PER_SECOND = 30
//If the unit has not gained rage in x seconds, it will begin to degenerate
private constant real DEGENERATION_DELAY = 10.0
//Damage blocked by rage
private constant real DAMAGE_REDUCTION_FACTOR = 0.5
//Rage lost per point of blocked damage
private constant real RAGE_DRAIN_FACTOR = 1
endglobals
//Rage points required to gain level "level"
private function levelRageAmount takes integer level returns real
return level*250.0
endfunction
//Rage bonus ability for level "level"
private function rageAbility takes integer level returns integer
if level == 1 then
return 'A002'
elseif level == 2 then
return 'A003'
elseif level == 3 then
return 'A004'
endif
return 0
endfunction
//Level of rage bonus ability for level "level".
private function rageBonusLevel takes integer level returns integer
return 1
endfunction
//Rage doesn't deregenerate if current rage is below this amount
private function degenerationTreshold takes unit u returns real
return 100.0*GetUnitAbilityLevel(u, ABIL_CODE)
endfunction
// ############################################################
// # CONFIGURABLES END, Don't touch anything after this line. #
// ############################################################
private struct Spell
private thistype next
private thistype prev
private static timer iterator = CreateTimer()
private static integer count = 0
private unit u
private texttag rageBarTotal
private texttag rageBarCurrent
private real currentRage
private integer rageLevel
private real degenDelay
private method destroy takes nothing returns nothing
call this.deallocate()
set this.next.prev = this.prev
set this.prev.next = this.next
call DestroyTextTag(this.rageBarCurrent)
call DestroyTextTag(this.rageBarTotal)
set this.rageBarCurrent = null
set this.rageBarTotal = null
set this.u = null
set count = count - 1
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
local real x
local real y
local string text
local integer i
local integer treshold
loop
exitwhen this == 0
//Set bar position to unit's position.
//Move bar to left so that its center is at the unit.
set x = GetUnitX(this.u) - BAR_WIDTH*LETTER_WIDTH
set y = GetUnitY(this.u)
call SetTextTagPos(this.rageBarTotal, x, y, BAR_HEIGHT)
call SetTextTagPos(this.rageBarCurrent, x, y, BAR_HEIGHT)
//Set bar to match unit's current rage amount
set text = ""
set i = 0
set treshold = R2I(this.currentRage/MAX_RAGE*I2R(BAR_WIDTH))
loop
exitwhen i >= treshold
set text = text + "|"
set i = i + 1
endloop
call SetTextTagText(this.rageBarCurrent, text, BAR_FONT_SIZE)
//If unit is dead, hide the bar, and if unit is alive, show the bar
call SetTextTagVisibility(rageBarTotal, GetUnitState(this.u, UNIT_STATE_LIFE) > 0)
call SetTextTagVisibility(rageBarCurrent, GetUnitState(this.u, UNIT_STATE_LIFE) > 0)
//If unit's rage level changes, add/remove bonuses accordingly
loop
if this.currentRage >= levelRageAmount(this.rageLevel + 1) /*
*/ and GetUnitAbilityLevel(this.u, ABIL_CODE) > this.rageLevel then
call UnitAddAbility(this.u, rageAbility(this.rageLevel + 1))
call SetUnitAbilityLevel(this.u, rageAbility(this.rageLevel + 1), rageBonusLevel(this.rageLevel))
set this.rageLevel = this.rageLevel + 1
elseif this.currentRage < levelRageAmount(this.rageLevel) then
call UnitRemoveAbility(this.u, rageAbility(this.rageLevel))
set this.rageLevel = this.rageLevel - 1
else
exitwhen true
endif
endloop
//This unit hasn't gained rage in DEGENERATION_DELAY seconds
if this.degenDelay < TIMEOUT then
//Degenerate unit's rage
if this.currentRage - DEGENERATION_PER_SECOND > degenerationTreshold(this.u) then
set this.currentRage = this.currentRage - DEGENERATION_PER_SECOND*TIMEOUT
endif
else
set this.degenDelay = this.degenDelay - TIMEOUT
endif
set this = this.next
endloop
//if there are no spell instances running, pause the timer
if count == 0 then
call PauseTimer(iterator)
endif
endmethod
//Registers a unit to have blood rage
private static method registerUnit takes unit u returns nothing
local thistype this
local string text
local integer i
set this = thistype.allocate()
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set count = count + 1
if count == 1 then
call TimerStart(iterator, TIMEOUT, true, function thistype.periodic)
endif
set this.u = u
set this.currentRage = 0
set this.rageLevel = 0
set this.degenDelay = DEGENERATION_DELAY
set text = ""
set i = 0
loop
exitwhen i >= BAR_WIDTH
set text = text + "|"
set i = i + 1
endloop
set this.rageBarCurrent = CreateTextTag()
call SetTextTagText(this.rageBarCurrent, "", BAR_FONT_SIZE)
call SetTextTagPos(this.rageBarCurrent, GetUnitX(this.u), GetUnitY(this.u), BAR_HEIGHT)
call SetTextTagColor(this.rageBarCurrent, BAR_RED, BAR_GREEN, BAR_BLUE, BAR_ALPHA)
set this.rageBarTotal = CreateTextTag()
call SetTextTagText(this.rageBarTotal, text, BAR_FONT_SIZE)
call SetTextTagPos(this.rageBarTotal, GetUnitX(this.u), GetUnitY(this.u), BAR_HEIGHT)
call SetTextTagColor(this.rageBarTotal, BAR_BACKGROUND_RED, BAR_BACKGROUND_GREEN, BAR_BACKGROUND_BLUE, BAR_BACKGROUND_ALPHA)
endmethod
//Registers units that are indexed
private static method registerUnitsIndexed takes nothing returns boolean
if GetUnitAbilityLevel(udg_UDexUnits[udg_UDex], ABIL_CODE) > 0 then
call thistype.registerUnit(udg_UDexUnits[udg_UDex])
endif
return false
endmethod
//Registers a hero that learns blood rage
private static method registerLearningHero takes nothing returns boolean
if GetUnitAbilityLevel(GetTriggerUnit(), ABIL_CODE) == 1 and (GetLearnedSkill() == ABIL_CODE) then
call thistype.registerUnit(GetTriggerUnit())
endif
return false
endmethod
//Unregisters a unit from blood rage
private static method deindexUnit takes nothing returns boolean
local thistype this = thistype(0).next
loop
exitwhen this == 0
if GetUnitUserData(this.u) == 0 then
call this.destroy()
return false
endif
set this = this.next
endloop
return false
endmethod
//Changes unit's current rage by amount
private method modifyRage takes real amount returns nothing
if this.currentRage + amount <= 0 then
set this.currentRage = 0
elseif currentRage + amount >= MAX_RAGE then
set this.currentRage = MAX_RAGE
else
set this.currentRage = this.currentRage + amount
if amount > 0 then
set this.degenDelay = DEGENERATION_DELAY
endif
endif
endmethod
//This function allows modifying rage from outside this trigger
static method addRage takes unit u, real amount returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
if u == this.u then
call this.modifyRage(amount)
return
endif
set this = this.next
endloop
endmethod
//Adds rage when unit deals damage
private static method addRageAttack takes nothing returns boolean
local thistype this = thistype(0).next
if not (GetUnitAbilityLevel(udg_DamageEventSource, ABIL_CODE) == 0) then
loop
exitwhen this == 0
if udg_DamageEventSource == this.u then
call this.modifyRage(udg_DamageEventAmount*ATTACK_RAGE_FACTOR)
return false
endif
set this = this.next
endloop
endif
return false
endmethod
//Adds rage when a unit dies nearby
private static method addRageDeath takes nothing returns boolean
local real dx
local real dy
local unit dying = GetTriggerUnit()
local thistype this = thistype(0).next
if GetUnitAbilityLevel(dying, 'Aloc') == 0 then
loop
exitwhen this == 0
set dx = GetUnitX(this.u) - GetUnitX(dying)
set dy = GetUnitY(this.u) - GetUnitY(dying)
if SquareRoot(dx*dx + dy*dy) <= RAGE_GAIN_AREA then
if IsUnitAlly(dying, GetOwningPlayer(this.u)) then
call this.modifyRage(ALLY_DEATH_RAGE_AMOUNT)
else
call this.modifyRage(ENEMY_DEATH_RAGE_AMOUNT)
endif
endif
set this = this.next
endloop
endif
return false
endmethod
//Reduces damage taken by units that have blood rage
private static method reduceDamage takes nothing returns boolean
local thistype this = thistype(0).next
local real reduction
if not (GetUnitAbilityLevel(udg_DamageEventTarget, ABIL_CODE) == 0) then
loop
exitwhen this == 0
if udg_DamageEventTarget == this.u then
if this.currentRage > udg_DamageEventAmount*DAMAGE_REDUCTION_FACTOR*RAGE_DRAIN_FACTOR then
set reduction = udg_DamageEventAmount*DAMAGE_REDUCTION_FACTOR
else
set reduction = this.currentRage/RAGE_DRAIN_FACTOR
endif
set this.currentRage = this.currentRage - reduction*RAGE_DRAIN_FACTOR
set udg_DamageEventAmount = udg_DamageEventAmount - reduction
return false
endif
set this = this.next
endloop
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local trigger t2 = CreateTrigger()
local trigger t3 = CreateTrigger()
local trigger t4 = CreateTrigger()
local trigger t5 = CreateTrigger()
local trigger t6 = CreateTrigger()
local integer i = 0
call TriggerRegisterVariableEvent( t, "udg_UnitIndexEvent", EQUAL, 1.00 )
call TriggerAddCondition(t, Condition(function thistype.registerUnitsIndexed))
call TriggerAddCondition(t2, Condition(function thistype.registerLearningHero))
call TriggerAddCondition(t3, Condition(function thistype.deindexUnit))
call TriggerRegisterVariableEvent( t4, "udg_UnitIndexEvent", EQUAL, 2.00 )
call TriggerRegisterVariableEvent( t4, "udg_DamageEvent", EQUAL, 1.00 )
call TriggerAddCondition(t4, Condition(function thistype.addRageAttack))
call TriggerAddCondition(t5, Condition(function thistype.addRageDeath))
call TriggerRegisterVariableEvent( t6, "udg_DamageModifierEvent", EQUAL, 1.00 )
call TriggerAddCondition(t6, Condition(function thistype.reduceDamage))
loop
exitwhen i == 16
call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_HERO_SKILL, null)
call TriggerRegisterPlayerUnitEvent(t5, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
set i = i + 1
endloop
endmethod
endstruct
//This function can be used to modify rage from outside this trigger
function addRage takes unit u, real amount returns nothing
call Spell.addRage(u, amount)
endfunction
endscope