Name | Type | is_array | initial_value |
//TESH.scrollpos=15
//TESH.alwaysfold=0
*==============================================================================*
* Tidal Payback *
*==============================================================================*
* by bowser499 aka [DUOS] *
*==============================================================================*
* *
* Does the charge in the selected direction, leaving a water trail behind and *
* heavily damaging all the nearby units. After reaching the distance, the hero *
* will jump, leaving a geyser in the place where this jump started. The geyser *
* explodes after some time, dealing damage to the nearby units. After the hero *
* lands, he deals damage to the units in the close area aswell. *
* *
*==============================================================================*
* HOW TO IMPORT *
*==============================================================================*
* 1. Copy all Object Editor data: *
* 1.1. Copy 'Tidal Payback' ability. *
* 1.2. Copy 'Tidal Payback Geyser' unit. *
* 1.3. Copy 'Tidal Payback Missile' unit. *
* 2. Copy all Import Manager data. Please do not change any path. *
* 3. Copy 'Tidal Payback' folder to your map. *
* 4. Configure the constants in the trigger according to your data in map. *
* 5. Have fun using the spell! *
*==============================================================================*
* CREDITS *
*==============================================================================*
* BTNBubbles.blp Golden-Drake *
* TidalErruption.mdx Unknown Author *
*==============================================================================*
* ENDING *
*==============================================================================*
* Note that this spell requires JNGP with JassHelper 0.A.2.B. *
* I really appreciate any credits you will place in your maps! *
* Be sure to check my pages: *
* *
* www.hiveworkshop.com/forums/members/bowser499/ *
* simplar.deviantart.com *
* *
* I love comments and reviews!~ *
*==============================================================================*
//TESH.scrollpos=6
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=39
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=179
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 72., 72.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
//TESH.scrollpos=248
//TESH.alwaysfold=0
library TidalPayback initializer Init requires TimerUtils, SpellEffectEvent, TerrainPathability
globals
// CONFIGURABLE CONSTANTS
// General.
private constant integer ABILITY_ID = 'A000' // Tidal Payback Ability Raw Code.
private constant integer MISSILE_ID = 'n000' // Tidal Payback Missile Raw Code.
private constant integer FLYABIL_ID = 'Amrf' // The ability whose bug can be used to make unit flyable.
private constant integer GEYSER_ID = 'n001' // Tidal Payback Geyser Raw Code.
// Running Phase.
private constant string RP_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the run starts.
private constant string RP_EFFECT = "units\\human\\WaterElemental\\WaterElemental.mdl" // The effect during the run.
private constant string RP_DAMAGE_EFFECT = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged during the run.
private constant integer RP_WALK_ANIM_INDEX = 0 // The index of 'walk' animation of the appropriate model.
private constant real RP_DISTANCE = 1000. // The distance which the hero must run.
private constant real RP_DISTANCE_FACTOR = 30. // Run distance increment per level.
private constant real RP_ENEMY_CHECK_AOE = 190. // The range from the caster which is scanned for enemies.
private constant real RP_ENEMY_CHECK_AOE_FACTOR = 20. // Range increment per level.
private constant real RP_TIMER_PERIOD = .03125 // The timer of caster's running timeout.
private constant real RP_DISTANCE_PER_PERIOD = 45. // The distance that caster will pass every RP_TIMER_PERIOD seconds.
private constant real RP_WALK_ANIM_SPEED = 2.2 // The speed of 'walk' animation which is needed to make the effect of running. The value '1' means 100%.
private constant real RP_DAMAGE = 120. // The damage that geyser will deal.
private constant real RP_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype RP_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype RP_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype RP_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Geyser after jump.
private constant string GZ_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the geyser explodes.
private constant string GZ_EXPLODE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect when the geyser explodes.
private constant string GZ_EXPLODE_EFFECT_2 = "TidalErruption.mdx" // The effect when the geyser explodes (2).
private constant string GZ_DAMAGE_EFFECT = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged by geyser.
private constant real GZ_DELAY = 1.7 // The time before geyser will explode.
private constant real GZ_DELAY_DECREASE_FACTOR = .2 // How many can delay decrease with every level of the spell.
private constant real GZ_ENEMY_CHECK_AOE = 140. // The range from geyser in which enemies are considered as caught.
private constant real GZ_ENEMY_CHECK_AOE_FACTOR = 22. // Range increment per level.
private constant real GZ_DAMAGE = 340. // The damage that geyser will deal.
private constant real GZ_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype GZ_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype GZ_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype GZ_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Jumping Phase.
private constant string JP_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the jump starts.
private constant string JP_AFTER_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect after the jump ends.
private constant string JP_AFTER_EFFECT_2 = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl" // The effect after the jump ends (2).
private constant real JP_DISTANCE = 1400. // The distance which the hero must jump.
private constant real JP_DISTANCE_FACTOR = 10. // Jump distance increment per level.
private constant real JP_TIMER_PERIOD = .03125 // The timer of caster's jumping timeout.
private constant real JP_DISTANCE_PER_PERIOD = 35. // The distance that caster will pass every JP_TIMER_PERIOD seconds.
private constant real JP_MAX_FLY_HEIGHT = 400. // The maximal flying height that will be achieved by the jumper.
private constant real JP_ENEMY_CHECK_AOE = 380. // The range from geyser in which enemies are considered as caught.
private constant real JP_ENEMY_CHECK_AOE_FACTOR = 62. // Range increment per level.
private constant real JP_DAMAGE = 340. // The damage that geyser will deal.
private constant real JP_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype JP_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype JP_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype JP_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// HARDCODED STUFF
private group TempGroup = CreateGroup()
endglobals
private function EnumEnemiesConditions takes unit abilityCaster, unit enumUnit returns boolean
return IsUnitEnemy(abilityCaster,GetOwningPlayer(enumUnit)) and not IsUnitType(enumUnit,UNIT_TYPE_DEAD) and not IsUnitType(enumUnit,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(enumUnit,UNIT_TYPE_STRUCTURE)
endfunction
private struct SpellActions
unit abilityCaster = null
integer abilityLevel = 0
real casterX = 0.
real casterY = 0.
real spellTargetX = 0.
real spellTargetY = 0.
real coordAngle = 0.
real distCos = 0.
real distSin = 0.
real distancePassed = 0.
timer loopTimer = null
timer geyserTimer = null
unit geyserDummy = null
effect geyserEffect = null
unit flyingMissile = null
boolean isFlyingUp = true
real propWindow = 0.
integer spellPhase = 1
group damagedUnits = CreateGroup()
static method geyserCallback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real geyserX = GetWidgetX(.geyserDummy)
local real geyserY = GetWidgetY(.geyserDummy)
local unit enumUnit
call GroupEnumUnitsInRange(TempGroup,geyserX,geyserY,GZ_ENEMY_CHECK_AOE+GZ_ENEMY_CHECK_AOE_FACTOR*abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(.abilityCaster,enumUnit) then
call DestroyEffect(AddSpecialEffect(GZ_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
call UnitDamageTarget(.abilityCaster,enumUnit,GZ_DAMAGE+GZ_DAMAGE_FACTOR*abilityLevel,true,false,GZ_ATTACK_TYPE,GZ_DAMAGE_TYPE,GZ_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT,geyserX,geyserY))
call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT_2,geyserX,geyserY))
call DestroyEffect(.geyserEffect)
call RemoveUnit(.geyserDummy)
call ReleaseTimer(.geyserTimer)
set .geyserEffect = null
set .geyserDummy = null
set .geyserTimer = null
endmethod
static method createGeyser takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set .geyserTimer = NewTimerEx(this)
set .geyserDummy = CreateUnit(GetOwningPlayer(.abilityCaster),GEYSER_ID,.casterX,.casterY,0.)
set .geyserEffect = AddSpecialEffect(GZ_BEFORE_EFFECT,.casterX,.casterY)
call TimerStart(.geyserTimer,GZ_DELAY+GZ_DELAY_DECREASE_FACTOR*.abilityLevel,false,function thistype.geyserCallback)
endmethod
static method jumpCallback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real nextX = GetWidgetX(.flyingMissile) + .distCos
local real nextY = GetWidgetY(.flyingMissile) + .distSin
local unit enumUnit
if .distancePassed < JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel then
if .distancePassed == 0. and .isFlyingUp then
if UnitAddAbility(.flyingMissile,FLYABIL_ID) then
call UnitRemoveAbility(.flyingMissile,FLYABIL_ID)
endif
call SetUnitFlyHeight(.flyingMissile,JP_MAX_FLY_HEIGHT,JP_MAX_FLY_HEIGHT / (.5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD)))
endif
if distancePassed >= .5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) and .isFlyingUp then
call SetUnitFlyHeight(.flyingMissile,0.,JP_MAX_FLY_HEIGHT / (.5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD)))
set .isFlyingUp = false
endif
if IsTerrainWalkable(nextX,nextY) then
call SetUnitX(.flyingMissile,nextX)
call SetUnitY(.flyingMissile,nextY)
call SetUnitX(.abilityCaster,nextX)
call SetUnitY(.abilityCaster,nextY)
endif
set .distancePassed = .distancePassed + JP_DISTANCE_PER_PERIOD
else
if IsTerrainWalkable(nextX,nextY) then
call SetUnitX(.abilityCaster,nextX)
call SetUnitY(.abilityCaster,nextY)
else
call SetUnitX(.abilityCaster,GetWidgetX(.flyingMissile))
call SetUnitY(.abilityCaster,GetWidgetY(.flyingMissile))
endif
call SetUnitVertexColor(.abilityCaster,255,255,255,255)
call ShowUnit(.flyingMissile,false)
call RemoveUnit(.flyingMissile)
call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT,nextX,nextY))
call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT_2,nextX,nextY))
call SetUnitPropWindow(.abilityCaster,.propWindow)
call SetUnitTurnSpeed(.abilityCaster,GetUnitDefaultTurnSpeed(.abilityCaster))
call IssueImmediateOrderById(.abilityCaster,851972)
call SetUnitFacing(.abilityCaster,.coordAngle*57.295827)
call SetUnitTimeScale(.abilityCaster,1.)
call GroupEnumUnitsInRange(TempGroup,nextX,nextY,JP_ENEMY_CHECK_AOE+JP_ENEMY_CHECK_AOE_FACTOR*.abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(.abilityCaster,enumUnit) then
call UnitDamageTarget(.abilityCaster,enumUnit,JP_DAMAGE+JP_DAMAGE_FACTOR*.abilityLevel,true,false,JP_ATTACK_TYPE,JP_DAMAGE_TYPE,JP_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call ReleaseTimer(.loopTimer)
call GroupClear(.damagedUnits)
call DestroyGroup(.damagedUnits)
set .damagedUnits = null
set .flyingMissile = null
set .abilityCaster = null
set .loopTimer = null
call .destroy()
endif
endmethod
static method walkCallback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit enumUnit
set .casterX = GetWidgetX(abilityCaster)
set .casterY = GetWidgetY(abilityCaster)
if .distancePassed < RP_DISTANCE+RP_DISTANCE_FACTOR*abilityLevel then
call GroupEnumUnitsInRange(TempGroup,.casterX+.distCos,.casterY+.distSin,RP_ENEMY_CHECK_AOE+RP_ENEMY_CHECK_AOE_FACTOR*.abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(.abilityCaster,enumUnit) and not IsUnitInGroup(enumUnit,.damagedUnits) then
call GroupAddUnit(.damagedUnits,enumUnit)
call DestroyEffect(AddSpecialEffect(RP_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
call UnitDamageTarget(.abilityCaster,enumUnit,RP_DAMAGE+RP_DAMAGE_FACTOR*.abilityLevel,true,false,RP_ATTACK_TYPE,RP_DAMAGE_TYPE,RP_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call SetUnitFacing(.abilityCaster,.coordAngle*57.295827)
if IsTerrainWalkable(.casterX+.distCos,.casterY+.distSin) then
call SetUnitX(.abilityCaster,.casterX+.distCos)
call SetUnitY(.abilityCaster,.casterY+.distSin)
endif
call DestroyEffect(AddSpecialEffect(RP_EFFECT,.casterX,.casterY))
set .distancePassed = .distancePassed + RP_DISTANCE_PER_PERIOD
else
set .spellPhase = 2
call SetUnitVertexColor(.abilityCaster,255,255,255,0)
call .createGeyser()
set .distancePassed = 0.
set .flyingMissile = CreateUnit(GetOwningPlayer(.abilityCaster),MISSILE_ID,.casterX,.casterY,.coordAngle*57.295827)
call PauseTimer(.loopTimer)
call TimerStart(.loopTimer,JP_TIMER_PERIOD,true,function thistype.callback)
endif
endmethod
static method callback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .spellPhase == 1 then
call .walkCallback()
elseif .spellPhase == 2 then
call .jumpCallback()
endif
endmethod
static method create takes unit abilityCaster, real spellTargetX, real spellTargetY returns thistype
local thistype this = thistype.allocate()
set .abilityCaster = abilityCaster
set .abilityLevel = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
set .casterX = GetWidgetX(abilityCaster)
set .casterY = GetWidgetY(abilityCaster)
set .spellTargetX = spellTargetX
set .spellTargetY = spellTargetY
set .coordAngle = Atan2(.spellTargetY-.casterY,.spellTargetX-.casterX)
set .distCos = RP_DISTANCE_PER_PERIOD * Cos(.coordAngle)
set .distSin = RP_DISTANCE_PER_PERIOD * Sin(.coordAngle)
set .propWindow = GetUnitPropWindow(abilityCaster)
set .loopTimer = NewTimerEx(this)
call SetUnitPropWindow(abilityCaster,0.)
call SetUnitTurnSpeed(abilityCaster,0.)
call SetUnitAnimationByIndex(abilityCaster,RP_WALK_ANIM_INDEX)
call TimerStart(.loopTimer,RP_TIMER_PERIOD,true,function thistype.callback)
return this
endmethod
endstruct
private function Actions takes nothing returns boolean
local unit abilityCaster = GetTriggerUnit()
local real casterX = GetWidgetX(abilityCaster)
local real casterY = GetWidgetY(abilityCaster)
local real spellTargetX = GetSpellTargetX()
local real spellTargetY = GetSpellTargetY()
call SetUnitFacingTimed(abilityCaster,Atan2(spellTargetY-casterY,spellTargetX-casterX)*57.295827,0.)
call DestroyEffect(AddSpecialEffect(RP_BEFORE_EFFECT,casterX,casterY))
call SetUnitTimeScale(abilityCaster,RP_WALK_ANIM_SPEED)
call SpellActions.create(abilityCaster,spellTargetX,spellTargetY)
set abilityCaster = null
return false
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectEvent(ABILITY_ID,function Actions)
endfunction
endlibrary
//TESH.scrollpos=243
//TESH.alwaysfold=0
library TidalPayback initializer Init requires TimerUtils, SpellEffectEvent
globals
// CONFIGURABLE CONSTANTS
// General.
private constant integer ABILITY_ID = 'A000' // Tidal Payback Ability Raw Code.
private constant integer MISSILE_ID = 'n000' // Tidal Payback Missile Raw Code.
private constant integer FLYABIL_ID = 'Amrf' // The ability whose bug can be used to make unit flyable.
private constant integer GEYSER_ID = 'n001' // Tidal Payback Geyser Raw Code.
// Running Phase.
private constant string RP_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the run starts.
private constant string RP_EFFECT = "units\\human\\WaterElemental\\WaterElemental.mdl" // The effect during the run.
private constant string RP_DAMAGE_EFFECT = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged during the run.
private constant integer RP_WALK_ANIM_INDEX = 0 // The index of 'walk' animation of the appropriate model.
private constant real RP_DISTANCE = 1000. // The distance which the hero must run.
private constant real RP_DISTANCE_FACTOR = 30. // Run distance increment per level.
private constant real RP_ENEMY_CHECK_AOE = 190. // The range from the caster which is scanned for enemies.
private constant real RP_ENEMY_CHECK_AOE_FACTOR = 20. // Range increment per level.
private constant real RP_TIMER_PERIOD = .03125 // The timer of caster's running timeout.
private constant real RP_DISTANCE_PER_PERIOD = 45. // The distance that caster will pass every RP_TIMER_PERIOD seconds.
private constant real RP_WALK_ANIM_SPEED = 2.2 // The speed of 'walk' animation which is needed to make the effect of running. The value '1' means 100%.
private constant real RP_DAMAGE = 120. // The damage that geyser will deal.
private constant real RP_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype RP_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype RP_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype RP_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Geyser after jump.
private constant string GZ_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the geyser explodes.
private constant string GZ_EXPLODE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect when the geyser explodes.
private constant string GZ_EXPLODE_EFFECT_2 = "TidalErruption.mdx" // The effect when the geyser explodes (2).
private constant string GZ_DAMAGE_EFFECT = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged by geyser.
private constant real GZ_DELAY = 1.7 // The time before geyser will explode.
private constant real GZ_DELAY_DECREASE_FACTOR = .2 // How many can delay decrease with every level of the spell.
private constant real GZ_ENEMY_CHECK_AOE = 140. // The range from geyser in which enemies are considered as caught.
private constant real GZ_ENEMY_CHECK_AOE_FACTOR = 22. // Range increment per level.
private constant real GZ_DAMAGE = 340. // The damage that geyser will deal.
private constant real GZ_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype GZ_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype GZ_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype GZ_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Jumping Phase.
private constant string JP_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the jump starts.
private constant string JP_AFTER_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect after the jump ends.
private constant string JP_AFTER_EFFECT_2 = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl" // The effect after the jump ends (2).
private constant real JP_DISTANCE = 1400. // The distance which the hero must jump.
private constant real JP_DISTANCE_FACTOR = 10. // Jump distance increment per level.
private constant real JP_TIMER_PERIOD = .03125 // The timer of caster's jumping timeout.
private constant real JP_DISTANCE_PER_PERIOD = 35. // The distance that caster will pass every JP_TIMER_PERIOD seconds.
private constant real JP_MAX_FLY_HEIGHT = 400. // The maximal flying height that will be achieved by the jumper.
private constant real JP_ENEMY_CHECK_AOE = 380. // The range from geyser in which enemies are considered as caught.
private constant real JP_ENEMY_CHECK_AOE_FACTOR = 62. // Range increment per level.
private constant real JP_DAMAGE = 340. // The damage that geyser will deal.
private constant real JP_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype JP_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype JP_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype JP_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// HARDCODED STUFF
private group TempGroup = CreateGroup()
endglobals
private function EnumEnemiesConditions takes unit abilityCaster, unit enumUnit returns boolean
return IsUnitEnemy(abilityCaster,GetOwningPlayer(enumUnit)) and not IsUnitType(enumUnit,UNIT_TYPE_DEAD) and not IsUnitType(enumUnit,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(enumUnit,UNIT_TYPE_STRUCTURE)
endfunction
private struct SpellActions
unit abilityCaster = null
integer abilityLevel = 0
real casterX = 0.
real casterY = 0.
real spellTargetX = 0.
real spellTargetY = 0.
real coordAngle = 0.
real distCos = 0.
real distSin = 0.
real distancePassed = 0.
timer timer = null
timer geyserTimer = null
unit geyserDummy = null
effect geyserEffect = null
unit flyingMissile = null
boolean isFlyingUp = true
real propWindow = 0.
integer spellPhase = 1
static method geyserCallback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real geyserX = GetWidgetX(.geyserDummy)
local real geyserY = GetWidgetY(.geyserDummy)
local unit enumUnit
call GroupEnumUnitsInRange(TempGroup,geyserX,geyserY,GZ_ENEMY_CHECK_AOE+GZ_ENEMY_CHECK_AOE_FACTOR*abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(.abilityCaster,enumUnit) then
call DestroyEffect(AddSpecialEffect(GZ_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
call UnitDamageTarget(.abilityCaster,enumUnit,GZ_DAMAGE+GZ_DAMAGE_FACTOR*abilityLevel,true,false,GZ_ATTACK_TYPE,GZ_DAMAGE_TYPE,GZ_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT,geyserX,geyserY))
call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT_2,geyserX,geyserY))
call DestroyEffect(.geyserEffect)
call RemoveUnit(.geyserDummy)
call ReleaseTimer(.geyserTimer)
endmethod
static method createGeyser takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set .geyserTimer = NewTimerEx(this)
set .geyserDummy = CreateUnit(GetOwningPlayer(.abilityCaster),GEYSER_ID,.casterX,.casterY,0.)
set .geyserEffect = AddSpecialEffect(GZ_BEFORE_EFFECT,.casterX,.casterY)
call TimerStart(.geyserTimer,GZ_DELAY+GZ_DELAY_DECREASE_FACTOR*.abilityLevel,false,function thistype.geyserCallback)
endmethod
static method jumpCallback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real nextX = GetWidgetX(.flyingMissile) + .distCos
local real nextY = GetWidgetY(.flyingMissile) + .distSin
local unit enumUnit
if .distancePassed < JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel then
if .distancePassed == 0. and .isFlyingUp then
if UnitAddAbility(.flyingMissile,FLYABIL_ID) then
call UnitRemoveAbility(.flyingMissile,FLYABIL_ID)
endif
call SetUnitFlyHeight(.flyingMissile,JP_MAX_FLY_HEIGHT,JP_MAX_FLY_HEIGHT / (.5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD)))
endif
if distancePassed >= .5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) and .isFlyingUp then
call SetUnitFlyHeight(.flyingMissile,0.,JP_MAX_FLY_HEIGHT / (.5 * (JP_DISTANCE+JP_DISTANCE_FACTOR*.abilityLevel) / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD)))
set .isFlyingUp = false
endif
if RectContainsCoords(bj_mapInitialPlayableArea,nextX,nextY) then
call SetUnitX(.flyingMissile,nextX)
call SetUnitY(.flyingMissile,nextY)
endif
set .distancePassed = .distancePassed + JP_DISTANCE_PER_PERIOD
else
if RectContainsCoords(bj_mapInitialPlayableArea,nextX,nextY) then
call SetUnitX(.abilityCaster,nextX)
call SetUnitY(.abilityCaster,nextY)
else
call SetUnitX(.abilityCaster,GetWidgetX(.flyingMissile))
call SetUnitY(.abilityCaster,GetWidgetY(.flyingMissile))
endif
call ShowUnit(.abilityCaster,true)
call ShowUnit(.flyingMissile,false)
call RemoveUnit(.flyingMissile)
call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT,nextX,nextY))
call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT_2,nextX,nextY))
call SetUnitPropWindow(.abilityCaster,.propWindow)
call SetUnitTurnSpeed(.abilityCaster,GetUnitDefaultTurnSpeed(.abilityCaster))
call IssueImmediateOrderById(.abilityCaster,851972)
call SetUnitFacingTimed(.abilityCaster,.coordAngle*57.295827,0.)
call SetUnitTimeScale(.abilityCaster,1.)
if GetLocalPlayer() == GetOwningPlayer(.abilityCaster) then
call SelectUnit(.abilityCaster,true)
endif
call GroupEnumUnitsInRange(TempGroup,nextX,nextY,JP_ENEMY_CHECK_AOE+JP_ENEMY_CHECK_AOE_FACTOR*.abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(.abilityCaster,enumUnit) then
call UnitDamageTarget(.abilityCaster,enumUnit,JP_DAMAGE+JP_DAMAGE_FACTOR*.abilityLevel,true,false,JP_ATTACK_TYPE,JP_DAMAGE_TYPE,JP_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call ReleaseTimer(.timer)
call .destroy()
endif
endmethod
static method walkCallback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit enumUnit
set .casterX = GetWidgetX(abilityCaster)
set .casterY = GetWidgetY(abilityCaster)
if .distancePassed < RP_DISTANCE+RP_DISTANCE_FACTOR*abilityLevel then
call GroupEnumUnitsInRange(TempGroup,.casterX+.distCos,.casterY+.distSin,RP_ENEMY_CHECK_AOE+RP_ENEMY_CHECK_AOE_FACTOR*.abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(.abilityCaster,enumUnit) then
call DestroyEffect(AddSpecialEffect(RP_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
call UnitDamageTarget(.abilityCaster,enumUnit,RP_DAMAGE+RP_DAMAGE_FACTOR*.abilityLevel,true,false,RP_ATTACK_TYPE,RP_DAMAGE_TYPE,RP_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call SetUnitFacingTimed(.abilityCaster,.coordAngle*57.295827,0.)
if RectContainsCoords(bj_mapInitialPlayableArea,.casterX+.distCos,.casterY+.distSin) then
call SetUnitX(.abilityCaster,.casterX+.distCos)
call SetUnitY(.abilityCaster,.casterY+.distSin)
endif
call DestroyEffect(AddSpecialEffect(RP_EFFECT,.casterX,.casterY))
set .distancePassed = .distancePassed + RP_DISTANCE_PER_PERIOD
else
set .spellPhase = 2
call ShowUnit(.abilityCaster,false)
call .createGeyser()
set .distancePassed = 0.
set .flyingMissile = CreateUnit(GetOwningPlayer(.abilityCaster),MISSILE_ID,.casterX,.casterY,.coordAngle*57.295827)
call PauseTimer(.timer)
call TimerStart(.timer,JP_TIMER_PERIOD,true,function thistype.callback)
endif
endmethod
static method callback takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .spellPhase == 1 then
call .walkCallback()
elseif .spellPhase == 2 then
call .jumpCallback()
endif
endmethod
static method create takes unit abilityCaster, real spellTargetX, real spellTargetY returns thistype
local thistype this = thistype.allocate()
set .abilityCaster = abilityCaster
set .abilityLevel = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
set .casterX = GetWidgetX(abilityCaster)
set .casterY = GetWidgetY(abilityCaster)
set .spellTargetX = spellTargetX
set .spellTargetY = spellTargetY
set .coordAngle = Atan2(.spellTargetY-.casterY,.spellTargetX-.casterX)
set .distCos = RP_DISTANCE_PER_PERIOD * Cos(.coordAngle)
set .distSin = RP_DISTANCE_PER_PERIOD * Sin(.coordAngle)
set .propWindow = GetUnitPropWindow(abilityCaster)
set .timer = NewTimerEx(this)
call SetUnitPropWindow(abilityCaster,0.)
call SetUnitTurnSpeed(abilityCaster,0.)
call SetUnitAnimationByIndex(abilityCaster,RP_WALK_ANIM_INDEX)
call TimerStart(.timer,RP_TIMER_PERIOD,true,function thistype.callback)
return this
endmethod
endstruct
private function Actions takes nothing returns boolean
local unit abilityCaster = GetTriggerUnit()
local real casterX = GetWidgetX(abilityCaster)
local real casterY = GetWidgetY(abilityCaster)
local real spellTargetX = GetSpellTargetX()
local real spellTargetY = GetSpellTargetY()
call SetUnitFacingTimed(abilityCaster,Atan2(spellTargetY-casterY,spellTargetX-casterX)*57.295827,0.)
call DestroyEffect(AddSpecialEffect(RP_BEFORE_EFFECT,casterX,casterY))
call SetUnitTimeScale(abilityCaster,RP_WALK_ANIM_SPEED)
call SpellActions.create(abilityCaster,spellTargetX,spellTargetY)
set abilityCaster = null
return false
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectEvent(ABILITY_ID,function Actions)
endfunction
endlibrary
//TESH.scrollpos=235
//TESH.alwaysfold=0
scope TidalPayback initializer Init
globals
// CONFIGURABLE CONSTANTS
// General.
private constant integer ABILITY_ID = 'A000' // Tidal Payback Ability Raw Code.
private constant integer MISSILE_ID = 'n000' // Tidal Payback Missile Raw Code.
private constant integer FLYABIL_ID = 'Amrf' // The ability whose bug can be used to make unit flyable.
private constant integer GEYSER_ID = 'n001' // Tidal Payback Geyser Raw Code.
// Running Phase.
private constant string RP_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the run starts.
private constant string RP_EFFECT = "units\\human\\WaterElemental\\WaterElemental.mdl" // The effect during the run.
private constant string RP_DAMAGE_EFFECT = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged during the run.
private constant integer RP_WALK_ANIM_INDEX = 0 // The index of 'walk' animation of the appropriate model.
private constant real RP_DISTANCE = 1000. // The distance which the hero must run.
private constant real RP_DISTANCE_FACTOR = 30. // Run distance increment per level.
private constant real RP_ENEMY_CHECK_AOE = 190. // The range from the caster which is scanned for enemies.
private constant real RP_ENEMY_CHECK_AOE_FACTOR = 20. // Range increment per level.
private constant real RP_TIMER_PERIOD = .04 // The timer of caster's running timeout.
private constant real RP_DISTANCE_PER_PERIOD = 45. // The distance that caster will pass every RP_TIMER_PERIOD seconds.
private constant real RP_WALK_ANIM_SPEED = 2.2 // The speed of 'walk' animation which is needed to make the effect of running. The value '1' means 100%.
private constant real RP_DAMAGE = 120. // The damage that geyser will deal.
private constant real RP_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype RP_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype RP_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype RP_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Geyser after jump.
private constant string GZ_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the geyser explodes.
private constant string GZ_EXPLODE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect when the geyser explodes.
private constant string GZ_EXPLODE_EFFECT_2 = "TidalErruption.mdx" // The effect when the geyser explodes (2).
private constant string GZ_DAMAGE_EFFECT = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl" // The effect on the units damaged by geyser.
private constant real GZ_DELAY = 1.7 // The time before geyser will explode.
private constant real GZ_DELAY_DECREASE_FACTOR = .2 // How many can delay decrease with every level of the spell.
private constant real GZ_ENEMY_CHECK_AOE = 140. // The range from geyser in which enemies are considered as caught.
private constant real GZ_ENEMY_CHECK_AOE_FACTOR = 22. // Range increment per level.
private constant real GZ_DAMAGE = 340. // The damage that geyser will deal.
private constant real GZ_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype GZ_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype GZ_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype GZ_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Jumping Phase.
private constant string JP_BEFORE_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect before the jump starts.
private constant string JP_AFTER_EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" // The effect after the jump ends.
private constant string JP_AFTER_EFFECT_2 = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl" // The effect after the jump ends (2).
private constant real JP_DISTANCE = 1400. // The distance which the hero must jump.
private constant real JP_DISTANCE_FACTOR = 10. // Jump distance increment per level.
private constant real JP_TIMER_PERIOD = .04 // The timer of caster's jumping timeout.
private constant real JP_DISTANCE_PER_PERIOD = 35. // The distance that caster will pass every JP_TIMER_PERIOD seconds.
private constant real JP_MAX_FLY_HEIGHT = 400. // The maximal flying height that will be achieved by the jumper.
private constant real JP_ENEMY_CHECK_AOE = 380. // The range from geyser in which enemies are considered as caught.
private constant real JP_ENEMY_CHECK_AOE_FACTOR = 62. // Range increment per level.
private constant real JP_DAMAGE = 340. // The damage that geyser will deal.
private constant real JP_DAMAGE_FACTOR = 42. // Damage increment per level.
private constant attacktype JP_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype JP_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype JP_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// HARDCODED STUFF
private hashtable SpellHash = InitHashtable()
private group TempGroup = CreateGroup()
endglobals
// SUPPORTIVE FUNCTIONS
private function CalcAngleBetweenCoords takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1,x2-x1)
endfunction
// SPELL FUNCTIONS
private function EnumEnemiesConditions takes unit abilityCaster, unit enumUnit returns boolean
return IsUnitEnemy(abilityCaster,GetOwningPlayer(enumUnit)) and not IsUnitType(enumUnit,UNIT_TYPE_DEAD) and not IsUnitType(enumUnit,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(enumUnit,UNIT_TYPE_STRUCTURE)
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == ABILITY_ID
endfunction
private function Timer_Geyser takes nothing returns nothing
local timer delayTimer = GetExpiredTimer()
local integer delayTimerId = GetHandleId(delayTimer)
local unit abilityCaster = LoadUnitHandle (SpellHash,delayTimerId,0)
local effect graphicsBefore = LoadEffectHandle(SpellHash,delayTimerId,1)
local unit geyserDummy = LoadUnitHandle (SpellHash,delayTimerId,2)
local real abilityLevel = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
local real geyserX = GetWidgetX(geyserDummy)
local real geyserY = GetWidgetY(geyserDummy)
local unit enumUnit
call GroupEnumUnitsInRange(TempGroup,geyserX,geyserY,GZ_ENEMY_CHECK_AOE+GZ_ENEMY_CHECK_AOE_FACTOR*abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(abilityCaster,enumUnit) then
call DestroyEffect(AddSpecialEffect(GZ_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
call UnitDamageTarget(abilityCaster,enumUnit,GZ_DAMAGE+GZ_DAMAGE_FACTOR*abilityLevel,true,false,GZ_ATTACK_TYPE,GZ_DAMAGE_TYPE,GZ_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call DestroyEffect(graphicsBefore)
call RemoveUnit(geyserDummy)
call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT,geyserX,geyserY))
call DestroyEffect(AddSpecialEffect(GZ_EXPLODE_EFFECT_2,geyserX,geyserY))
call FlushChildHashtable(SpellHash,delayTimerId)
call DestroyTimer(delayTimer)
set geyserDummy = null
set graphicsBefore = null
set abilityCaster = null
set delayTimer = null
endfunction
private function CreateGeyser takes unit abilityCaster, real x, real y returns nothing
local timer delayTimer = CreateTimer()
local integer delayTimerId = GetHandleId(delayTimer)
local real abilityLevel = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
call SaveUnitHandle (SpellHash,delayTimerId,0,abilityCaster)
call SaveEffectHandle(SpellHash,delayTimerId,1,AddSpecialEffect(GZ_BEFORE_EFFECT,x,y))
call SaveUnitHandle (SpellHash,delayTimerId,2,CreateUnit(GetOwningPlayer(abilityCaster),GEYSER_ID,x,y,0.))
call TimerStart(delayTimer,GZ_DELAY+GZ_DELAY_DECREASE_FACTOR*abilityLevel,false,function Timer_Geyser)
set delayTimer = null
endfunction
private function Timer_Jump takes nothing returns nothing
local timer jumpTimer = GetExpiredTimer()
local integer jumpTimerId = GetHandleId(jumpTimer)
local unit abilityCaster = LoadUnitHandle(SpellHash,jumpTimerId,0)
local real coordAngle = LoadReal (SpellHash,jumpTimerId,1)
local real distancePassed = LoadReal (SpellHash,jumpTimerId,2)
local unit flyingMissile = LoadUnitHandle(SpellHash,jumpTimerId,3)
local boolean isFlyingUp = LoadBoolean (SpellHash,jumpTimerId,4)
local boolean isSelectNeeded = LoadBoolean (SpellHash,jumpTimerId,5)
local real missileX = GetWidgetX(flyingMissile)
local real missileY = GetWidgetY(flyingMissile)
local real abilityLevel = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
local real nextX = missileX + JP_DISTANCE_PER_PERIOD * Cos(coordAngle)
local real nextY = missileY + JP_DISTANCE_PER_PERIOD * Sin(coordAngle)
local real fullDistance = JP_DISTANCE + JP_DISTANCE_FACTOR * abilityLevel
local real flyHeightRate = JP_MAX_FLY_HEIGHT / (.5 * fullDistance / (JP_DISTANCE_PER_PERIOD / JP_TIMER_PERIOD))
local unit enumUnit
if distancePassed < fullDistance then
if distancePassed == 0. and isFlyingUp then
if UnitAddAbility(flyingMissile,FLYABIL_ID) then
call UnitRemoveAbility(flyingMissile,FLYABIL_ID)
endif
call SetUnitFlyHeight(flyingMissile,JP_MAX_FLY_HEIGHT,flyHeightRate)
endif
if distancePassed >= .5 * fullDistance and isFlyingUp then
call SetUnitFlyHeight(flyingMissile,0.,flyHeightRate)
call SaveBoolean(SpellHash,jumpTimerId,4,false)
endif
call SetUnitX(flyingMissile,nextX)
call SetUnitY(flyingMissile,nextY)
call SaveReal(SpellHash,jumpTimerId,2,distancePassed+JP_DISTANCE_PER_PERIOD)
else
call ShowUnit(flyingMissile,false)
call RemoveUnit(flyingMissile)
call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT,nextX,nextY))
call DestroyEffect(AddSpecialEffect(JP_AFTER_EFFECT_2,nextX,nextY))
call SetUnitTimeScale(abilityCaster,1.)
call SetUnitX(abilityCaster,nextX)
call SetUnitY(abilityCaster,nextY)
call ShowUnit(abilityCaster,true)
call PauseUnit(abilityCaster,false)
if GetLocalPlayer() == GetOwningPlayer(abilityCaster) then
call SelectUnit(abilityCaster,isSelectNeeded)
endif
call GroupEnumUnitsInRange(TempGroup,nextX,nextY,JP_ENEMY_CHECK_AOE+JP_ENEMY_CHECK_AOE_FACTOR*abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(abilityCaster,enumUnit) then
call UnitDamageTarget(abilityCaster,enumUnit,JP_DAMAGE+JP_DAMAGE_FACTOR*abilityLevel,true,false,JP_ATTACK_TYPE,JP_DAMAGE_TYPE,JP_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call FlushChildHashtable(SpellHash,jumpTimerId)
call PauseTimer(jumpTimer)
call DestroyTimer(jumpTimer)
endif
set jumpTimer = null
set abilityCaster = null
set flyingMissile = null
endfunction
private function Timer_Walk takes nothing returns nothing
local timer runTimer = GetExpiredTimer()
local integer runTimerId = GetHandleId(runTimer)
local unit abilityCaster = LoadUnitHandle(SpellHash,runTimerId,0)
local real coordAngle = LoadReal (SpellHash,runTimerId,1)
local real distancePassed = LoadReal (SpellHash,runTimerId,2)
local real casterX = GetWidgetX(abilityCaster)
local real casterY = GetWidgetY(abilityCaster)
local real abilityLevel = GetUnitAbilityLevel(abilityCaster,ABILITY_ID)
local real nextX = casterX + RP_DISTANCE_PER_PERIOD * Cos(coordAngle)
local real nextY = casterY + RP_DISTANCE_PER_PERIOD * Sin(coordAngle)
local timer jumpTimer
local integer jumpTimerId
local unit enumUnit
if distancePassed < RP_DISTANCE+RP_DISTANCE_FACTOR*abilityLevel then
call GroupEnumUnitsInRange(TempGroup,nextX,nextY,RP_ENEMY_CHECK_AOE+RP_ENEMY_CHECK_AOE_FACTOR*abilityLevel,null)
loop
set enumUnit = FirstOfGroup(TempGroup)
exitwhen enumUnit == null
if EnumEnemiesConditions(abilityCaster,enumUnit) then
call DestroyEffect(AddSpecialEffect(RP_DAMAGE_EFFECT,GetWidgetX(enumUnit),GetWidgetY(enumUnit)))
call UnitDamageTarget(abilityCaster,enumUnit,RP_DAMAGE+RP_DAMAGE_FACTOR*abilityLevel,true,false,RP_ATTACK_TYPE,RP_DAMAGE_TYPE,RP_WEAPON_TYPE)
endif
call GroupRemoveUnit(TempGroup,enumUnit)
endloop
call SetUnitX(abilityCaster,nextX)
call SetUnitY(abilityCaster,nextY)
call DestroyEffect(AddSpecialEffect(RP_EFFECT,casterX,casterY))
call SaveReal(SpellHash,runTimerId,2,distancePassed+RP_DISTANCE_PER_PERIOD)
else
set jumpTimer = CreateTimer()
set jumpTimerId = GetHandleId(jumpTimer)
call SaveUnitHandle(SpellHash,jumpTimerId,0,abilityCaster)
call SaveReal (SpellHash,jumpTimerId,1,coordAngle)
call SaveReal (SpellHash,jumpTimerId,2,0.)
call SaveUnitHandle(SpellHash,jumpTimerId,3,CreateUnit(GetOwningPlayer(abilityCaster),MISSILE_ID,casterX,casterY,coordAngle*57.295827))
call SaveBoolean (SpellHash,jumpTimerId,4,true)
call SaveBoolean (SpellHash,jumpTimerId,5,IsUnitSelected(abilityCaster,GetOwningPlayer(abilityCaster)))
call ShowUnit(abilityCaster,false)
call TimerStart(jumpTimer,JP_TIMER_PERIOD,true,function Timer_Jump)
call CreateGeyser(abilityCaster,nextX,nextY)
call FlushChildHashtable(SpellHash,runTimerId)
call PauseTimer(runTimer)
call DestroyTimer(runTimer)
set jumpTimer = null
endif
set abilityCaster = null
set runTimer = null
endfunction
private function Actions takes nothing returns nothing
local unit abilityCaster = GetTriggerUnit()
local real casterX = GetWidgetX(abilityCaster)
local real casterY = GetWidgetY(abilityCaster)
local real spellTargetX = GetSpellTargetX()
local real spellTargetY = GetSpellTargetY()
local real coordAngle = CalcAngleBetweenCoords(casterX,casterY,spellTargetX,spellTargetY)
local timer runTimer = CreateTimer()
local integer runTimerId = GetHandleId(runTimer)
call PauseUnit(abilityCaster,true)
call SetUnitFacingTimed(abilityCaster,coordAngle*57.295827,0.)
call DestroyEffect(AddSpecialEffect(RP_BEFORE_EFFECT,casterX,casterY))
call SetUnitTimeScale(abilityCaster,RP_WALK_ANIM_SPEED)
call SetUnitAnimationByIndex(abilityCaster,RP_WALK_ANIM_INDEX)
call SaveUnitHandle(SpellHash,runTimerId,0,abilityCaster)
call SaveReal (SpellHash,runTimerId,1,coordAngle)
call SaveReal (SpellHash,runTimerId,2,0.)
call TimerStart(runTimer,RP_TIMER_PERIOD,true,function Timer_Walk)
set runTimer = null
set abilityCaster = null
endfunction
private function Init takes nothing returns nothing
local trigger AbilityCast = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(AbilityCast,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(AbilityCast,Condition(function Conditions))
call TriggerAddAction(AbilityCast,function Actions)
set AbilityCast = null
endfunction
endscope