scope TimePhase initializer OnInit
/*
TIME PHASE by Shadow Flux
SPELL INFO
Distort space-time continuum allowing you to save your current location, hitpoints and mana at
the time this spell was casted. After a short delay or when 'Return Time' is activated, you will
go back in time at the moment this spell was casted restoring location but can only
recover portion of the hitpoints and mana lost.
NOTES:
- Image and floating text created is only visible to player casting the spell.
- Does not desync when tested in Multiplayer Emulation of JNGP.
*/
native UnitAlive takes unit u returns boolean
//===========================================================
//========= TIME PHASE CONFIGURABLES ========================
//===========================================================
globals
private constant integer TIMEPHASE_ABILITYCODE = 'Atmp' // The Hero Ability Time Phase
private constant integer RETURNTIME_ABILITYCODE = 'Atpr' // Return Time Ability
private constant integer TIMEPHASE_HIDE_ABILITYCODE = 'Ahde' // The Dummy Ability based on Engineering Upgrades that hides Time Phase ability
private constant integer IMAGE_TRANSPARENCY = 160 // How transparent the image is. Max value is 255 which is fully non-transparent
private constant string SPELL_EFFECT_CASTER = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl"
private constant string SPELL_EFFECT_TARGET = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl"
private constant real TEXT_HEIGHT = 150 //height offset of the floating text
private constant real TEXT_SIZE = 0.035
endglobals
private function Duration takes integer level returns real
return 4.5 + 0.5*level
endfunction
//THE PERCENTAGE OF HP RECOVERED UPON RETURNING IN Time
/*Example:
Time Phase (level 1) casted at 1000 HP and after taking damage resulting to 500 HP left, returning
in time, caster will recover (1000-500)*0.6 = 300 resulting to 800 HP.
*/
private function PortionHp takes integer level returns real
return 0.1 + 0.2*level
endfunction
private function PortionMana takes integer level returns real
return 0.1 + 0.2*level
endfunction
//===========================================================
//========= END CONFIGURABLES ===============================
//===========================================================
//===========================================================
//========= Important Spell Variables =======================
//===========================================================
globals
private integer index = -1
private timer period = CreateTimer()
private constant real FPS = 0.0312500
private constant player NEUTRAL_PASSIVE_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
private constant integer TIMEPHASE_ORDERINT = 852589
endglobals
private struct SpellData
unit caster
unit imageUnit
boolean exchange
integer lvl
real delay
real hp
real mana
texttag timeLeft
method destroy takes nothing returns nothing
if index == -1 then
call PauseTimer(period)
endif
call this.deallocate()
endmethod
endstruct
globals
private SpellData array data
endglobals
private function ReturnTime takes integer i returns nothing
local SpellData this
local real x
local real y
local real hp
local real hpRecover
local real mana
local real manaRecover
set this = data[i]
if (UnitAlive(this.caster)) then
set x = GetUnitX(this.imageUnit)
set y = GetUnitY(this.imageUnit)
//SPELL VISUAL EFFECTS
call DestroyEffect(AddSpecialEffect(SPELL_EFFECT_CASTER, GetUnitX(this.caster), GetUnitY(this.caster)))
call DestroyEffect(AddSpecialEffect(SPELL_EFFECT_TARGET, x, y))
//SPELL ACTIONS
call SetUnitFacing(this.caster, GetUnitFacing(this.imageUnit))
call SetUnitPosition(this.caster, x, y)
//HP RECOVER
set hp = GetWidgetLife(this.caster)
set hpRecover = (this.hp - hp)*PortionHp(this.lvl)
if (hpRecover > 0) then
//if saved hp > current hp, recover hp
call SetWidgetLife(this.caster, hp + hpRecover)
else
//if saved hp < current hp, set hp to saved hp
call SetWidgetLife(this.caster, this.hp)
endif
//MANA RECOVER
set mana = GetUnitState(this.caster, UNIT_STATE_MANA)
set manaRecover = (this.mana-mana)*PortionMana(this.lvl)
if (manaRecover > 0) then
//if saved mana > current mana, recover mana
call SetUnitState(this.caster, UNIT_STATE_MANA, mana + manaRecover)
else
//if saved mana < current mana, set mana to saved mana
call SetUnitState(this.caster, UNIT_STATE_MANA, this.mana)
endif
endif
call RemoveUnit(this.imageUnit)
call DestroyTextTag(this.timeLeft)
//RESET ABILITIES
call UnitRemoveAbility(this.caster, RETURNTIME_ABILITYCODE)
call UnitRemoveAbility(this.caster, TIMEPHASE_HIDE_ABILITYCODE)
//CLEANUP
set this.caster = null
set this.imageUnit = null
set this.timeLeft = null
set data[i] = data[index]
set index = index - 1
call this.destroy()
endfunction
//===========================================================
//========= TIME PHASE LOOP =================================
//===========================================================
private function Periodic takes nothing returns nothing
local SpellData this
local integer i = 0
loop
exitwhen i > index
//========== TIME PHASE PERIODIC ACTIONS ============
set this = data[i]
set this.delay = this.delay - FPS
call SetTextTagText(this.timeLeft, R2S(this.delay), TEXT_SIZE)
if this.delay <= 0 then
call ReturnTime(i)
else
//HIDE ABILITY
if (this.exchange) then
call UnitAddAbility(this.caster, RETURNTIME_ABILITYCODE)
call UnitAddAbility(this.caster, TIMEPHASE_HIDE_ABILITYCODE)
call UnitMakeAbilityPermanent(this.caster, true, RETURNTIME_ABILITYCODE)
call UnitMakeAbilityPermanent(this.caster, true, TIMEPHASE_HIDE_ABILITYCODE)
set this.mana = GetUnitState(this.caster, UNIT_STATE_MANA)
set this.exchange = false
endif
endif
//=============================================================
set i = i + 1
endloop
endfunction
//===========================================================
//========= TIME PHASE CASTED ===============================
//===========================================================
private function Casted takes nothing returns boolean
local SpellData this
local integer id
local real x
local real y
if (GetIssuedOrderId() == TIMEPHASE_ORDERINT) then
//STORE SPELL STRUCT VARIABLES
set this = SpellData.create()
set this.caster = GetTriggerUnit()
set this.lvl = GetUnitAbilityLevel(this.caster, TIMEPHASE_ABILITYCODE)
set this.exchange = true
set this.hp = GetWidgetLife(this.caster)
set this.delay = Duration(this.lvl)
//LOCAL VARIABLES
set x = GetUnitX(this.caster)
set y = GetUnitY(this.caster)
//FLOATING TEXT SCRIPTS
set this.timeLeft = CreateTextTag()
call SetTextTagPos(this.timeLeft, x, y, TEXT_HEIGHT)
call SetTextTagColor(this.timeLeft, 80, 175, 250, 230)
call SetTextTagVisibility(this.timeLeft, false)
//IMAGE UNIT
set id = GetUnitTypeId(this.caster)
set this.imageUnit = CreateUnit(NEUTRAL_PASSIVE_PLAYER, id, x, y, GetUnitFacing(this.caster))
//ONLY SHOW THE IMAGE UNIT AND TIME LEFT TO THE CASTER PLAYER
call SetUnitVertexColor(this.imageUnit, 255, 255, 255, 0)
if (GetLocalPlayer() == GetTriggerPlayer()) then
call SetUnitVertexColor(this.imageUnit, 255, 255, 255, IMAGE_TRANSPARENCY)
call SetTextTagVisibility(this.timeLeft, true)
endif
call UnitAddAbility(this.imageUnit, 'Aloc') //make the image unit unselectable
call SetUnitTimeScale(this.imageUnit, 0) //stop image unit animation
call PauseUnit(this.imageUnit, true) //disable image unit from auto-attacking
//MAKE IMAGE UNIT GOES THROUGH CASTER
call SetUnitX(this.imageUnit, x)
call SetUnitY(this.imageUnit, y)
//INDEXING
set index = index + 1
set data[index] = this
if (index == 0) then
call TimerStart(period, FPS, true, function Periodic)
endif
endif
return false
endfunction
//===========================================================
//========= TIME PHASE RETURN ===============================
//===========================================================
private function ReturnTimeActivated takes nothing returns boolean
local SpellData this
local unit u
local integer i = 0
if (GetSpellAbilityId() == RETURNTIME_ABILITYCODE) then
set u = GetTriggerUnit()
loop
set this = data[i]
if this.caster == u then
set u = null
call ReturnTime(i)
exitwhen true
endif
//=============================================================
set i = i + 1
endloop
set u = null
endif
return false
endfunction
private function Disable takes nothing returns nothing
call SetPlayerAbilityAvailable(GetEnumPlayer(), TIMEPHASE_HIDE_ABILITYCODE, false)
endfunction
//===========================================================================
private function OnInit takes nothing returns nothing
local trigger t1 = CreateTrigger( )
local trigger t2 = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t1, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerAddCondition( t1, Condition( function Casted ) )
call TriggerRegisterAnyUnitEventBJ( t2, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition( t2, Condition( function ReturnTimeActivated ) )
set t1 = null
set t2 = null
//for hiding the other ability
call ForForce(bj_FORCE_ALL_PLAYERS, function Disable)
endfunction
endscope