Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AOEDamageEvent | real | No | |
AOEDamageSource | unit | No | |
ARMOR_TYPE_ETHEREAL | integer | No | |
ARMOR_TYPE_FLESH | integer | No | |
ARMOR_TYPE_METAL | integer | No | |
ARMOR_TYPE_NONE | integer | No | |
ARMOR_TYPE_STONE | integer | No | |
ARMOR_TYPE_WOOD | integer | No | |
ArmorTypeDebugStr | string | Yes | |
ATTACK_TYPE_CHAOS | integer | No | |
ATTACK_TYPE_HERO | integer | No | |
ATTACK_TYPE_MAGIC | integer | No | |
ATTACK_TYPE_NORMAL | integer | No | |
ATTACK_TYPE_PIERCE | integer | No | |
ATTACK_TYPE_SIEGE | integer | No | |
ATTACK_TYPE_SPELLS | integer | No | |
AttackTypeDebugStr | string | Yes | |
boss_BM_status | integer | No | |
boss_hero_deaths | integer | No | |
boss_MC_crystal_l | unit | No | |
boss_MC_crystal_r | unit | No | |
boss_MC_status | integer | No | |
boss_MC_transmitter_l | unit | No | |
boss_MC_transmitter_r | unit | No | |
bossAICounter | integer | No | |
bossAIEnabled | boolean | No | |
bossBool1 | boolean | No | |
bossInt1 | integer | No | |
bossOngoing | boolean | No | |
bossRegions | rect | Yes | |
bossTimer1 | timer | No | |
bossTimer2 | timer | No | |
bossUnit | unit | No | |
bossVfx1 | effect | No | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CenterPoint | location | No | |
CheckDeathInList | boolean | Yes | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
cinematic | integer | No | |
ClearDamageEvent | trigger | No | |
CONVERTED_ATTACK_TYPE | attacktype | Yes | |
CONVERTED_DAMAGE_TYPE | damagetype | Yes | |
conveyorEastGroup | group | No | |
conveyorNorthGroup | group | No | |
conveyorSouthGroup | group | No | |
conveyorWestGroup | group | No | |
coreSecretPoints | rect | Yes | |
CP_HiddenItems | item | Yes | |
CP_HiddenItemsIndex | integer | No | |
CP_Item | item | No | |
CP_Point | location | No | |
CP_PointIsWalkable | boolean | No | |
CP_Rect | rect | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DAMAGE_TYPE_ACID | integer | No | |
DAMAGE_TYPE_COLD | integer | No | |
DAMAGE_TYPE_DEATH | integer | No | |
DAMAGE_TYPE_DEFENSIVE | integer | No | |
DAMAGE_TYPE_DEMOLITION | integer | No | |
DAMAGE_TYPE_DISEASE | integer | No | |
DAMAGE_TYPE_DIVINE | integer | No | |
DAMAGE_TYPE_ENHANCED | integer | No | |
DAMAGE_TYPE_FIRE | integer | No | |
DAMAGE_TYPE_FORCE | integer | No | |
DAMAGE_TYPE_LIGHTNING | integer | No | |
DAMAGE_TYPE_MAGIC | integer | No | |
DAMAGE_TYPE_MIND | integer | No | |
DAMAGE_TYPE_NORMAL | integer | No | |
DAMAGE_TYPE_PLANT | integer | No | |
DAMAGE_TYPE_POISON | integer | No | |
DAMAGE_TYPE_SHADOW_STRIKE | integer | No | |
DAMAGE_TYPE_SLOW_POISON | integer | No | |
DAMAGE_TYPE_SONIC | integer | No | |
DAMAGE_TYPE_SPIRIT_LINK | integer | No | |
DAMAGE_TYPE_UNIVERSAL | integer | No | |
DAMAGE_TYPE_UNKNOWN | integer | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventArmorPierced | real | No | |
DamageEventArmorT | integer | No | |
DamageEventAttackT | integer | No | |
DamageEventDamageT | integer | No | |
DamageEventDefenseT | integer | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageEventWeaponT | integer | No | |
DamageFilterAttackT | integer | No | |
DamageFilterDamageT | integer | No | |
DamageFilterMinAmount | real | No | |
DamageFilterSource | unit | No | |
DamageFilterSourceB | integer | No | |
DamageFilterSourceT | integer | No | |
DamageFilterTarget | unit | No | |
DamageFilterTargetB | integer | No | |
DamageFilterTargetT | integer | No | |
DamageFilterType | integer | No | |
DamageModifierEvent | real | No | |
DamageScalingUser | real | No | |
DamageScalingWC3 | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCode | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeDebugStr | string | Yes | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypePure | integer | No | |
DamageTypePureExplosive | integer | No | |
DamageTypeReduced | integer | No | |
dashHasPoint | boolean | No | |
dashTimer | timer | No | |
DeathEvent | real | No | |
DEFENSE_TYPE_DIVINE | integer | No | |
DEFENSE_TYPE_FORTIFIED | integer | No | |
DEFENSE_TYPE_HEAVY | integer | No | |
DEFENSE_TYPE_HERO | integer | No | |
DEFENSE_TYPE_LIGHT | integer | No | |
DEFENSE_TYPE_MEDIUM | integer | No | |
DEFENSE_TYPE_NORMAL | integer | No | |
DEFENSE_TYPE_UNARMORED | integer | No | |
DefenseTypeDebugStr | string | Yes | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
difficulty_hp_boss_m | real | No | |
difficulty_hp_multiplier | real | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
doodadRegions | rect | Yes | |
enemyPatrolPoints | location | Yes | |
EnhancedDamageTarget | unit | No | |
g_int1 | integer | No | |
g_int2 | integer | No | |
g_lightning | lightning | Yes | |
g_visModArr | fogmodifier | Yes | |
heroes_alive | group | No | |
heroes_all | group | No | |
heroes_dead | group | No | |
heroes_hash | hashtable | No | |
HideDamageFrom | boolean | Yes | |
human_players | force | No | |
initial_spawn | rect | Yes | |
is_testing | boolean | No | |
IsDamageAttack | boolean | No | |
IsDamageCode | boolean | No | |
IsDamageMelee | boolean | No | |
IsDamageRanged | boolean | No | |
IsDamageSpell | boolean | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingKnockedBack | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
K2DAmphibious | boolean | Yes | |
K2DAngle | real | Yes | |
K2DBounce | boolean | Yes | |
K2DCollision | real | Yes | |
K2DCos | real | Yes | |
K2DCosD1 | real | Yes | |
K2DCosD2 | real | Yes | |
K2DCosH | real | Yes | |
K2DDebrisKiller | unit | No | |
K2DDestRadius | real | Yes | |
K2DDistanceLeft | real | Yes | |
K2DFlying | boolean | Yes | |
K2DFreeze | boolean | Yes | |
K2DFriction | real | Yes | |
K2DFXModel | string | Yes | |
K2DFXRate | real | Yes | |
K2DFXTimeLeft | real | Yes | |
K2DHeight | real | Yes | |
K2DHeightThreshold | real | Yes | |
K2DImpact | trigger | Yes | |
K2DItem | item | No | |
K2DItemOffset | boolean | No | |
K2DItemsFound | boolean | No | |
K2DKillTrees | boolean | Yes | |
K2DLastX | real | Yes | |
K2DLastY | real | Yes | |
K2DMaxDestRadius | real | No | |
K2DMaxX | real | No | |
K2DMaxY | real | No | |
K2DMinX | real | No | |
K2DMinY | real | No | |
K2DNext | integer | Yes | |
K2DOverride | boolean | Yes | |
K2DPause | boolean | Yes | |
K2DPrev | integer | Yes | |
K2DRadius | integer | Yes | |
K2DRegion | rect | No | |
K2DSimple | boolean | Yes | |
K2DSin | real | Yes | |
K2DSinD1 | real | Yes | |
K2DSinD2 | real | Yes | |
K2DSinH | real | Yes | |
K2DSource | unit | Yes | |
K2DTimeLeft | real | Yes | |
K2DTimeout | real | No | |
K2DTimer | timer | No | |
K2DUnbiasedCollision | boolean | Yes | |
K2DVelocity | real | Yes | |
K2DX | real | No | |
K2DY | real | No | |
KillerOfUnit | unit | Yes | |
Knockback2DAmphibious | boolean | No | |
Knockback2DAngle | real | No | |
Knockback2DBounces | boolean | No | |
Knockback2DCollision | real | No | |
Knockback2DDefaultBounce | boolean | No | |
Knockback2DDefaultDestRadius | real | No | |
Knockback2DDefaultFriction | real | No | |
Knockback2DDefaultFX | string | No | |
Knockback2DDefaultFXRate | real | No | |
Knockback2DDefaultGravity | real | No | |
Knockback2DDefaultKillTrees | boolean | No | |
Knockback2DDefaultPause | boolean | No | |
Knockback2DDestRadius | real | No | |
Knockback2DDistance | real | No | |
Knockback2DFriction | real | No | |
Knockback2DFXRate | real | No | |
Knockback2DGravity | real | No | |
Knockback2DHeight | real | No | |
Knockback2DKillTrees | boolean | No | |
Knockback2DLoopFX | string | No | |
Knockback2DOnImpact | trigger | No | |
Knockback2DOverride | boolean | No | |
Knockback2DPause | boolean | No | |
Knockback2DRobustPathing | integer | No | |
Knockback2DSimple | boolean | No | |
Knockback2DSource | unit | No | |
Knockback2DTime | real | No | |
Knockback2DTreeOrDebris | string | No | |
Knockback2DUnbiasedCollision | boolean | No | |
Knockback2DUnit | unit | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
LethalDamageEvent | real | No | |
LethalDamageHP | real | No | |
LoopInt | integer | No | |
NextDamageIsAttack | boolean | No | |
NextDamageIsMelee | boolean | No | |
NextDamageIsRanged | boolean | No | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
NextDamageWeaponT | integer | No | |
onHeroDeathEvent | real | No | |
Radians_QuarterPi | real | No | |
Radians_QuarterTurn | real | No | |
Radians_Turn | real | No | |
respawn_death_visibility | fogmodifier | Yes | |
respawnCurrentPoint | destructable | Yes | |
spawnedEnemies | group | No | |
spawnedEnemiesHeat | integer | No | |
spawnedEnemiesPower | integer | No | |
spawnedPowerArr | integer | Yes | |
SpellDamageAbility | abilcode | No | |
SummonerOfUnit | unit | Yes | |
t_bool1 | boolean | No | |
t_destructible1 | destructable | No | |
t_group1 | group | No | |
t_int1 | integer | No | |
t_int2 | integer | No | |
t_int3 | integer | No | |
t_loc1 | location | No | |
t_loc2 | location | No | |
t_real1 | real | No | |
t_real2 | real | No | |
t_rect1 | rect | No | |
t_rect2 | rect | No | |
t_string1 | string | No | |
t_unit1 | unit | No | |
t_unittype1 | unitcode | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes | |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
WEAPON_TYPE_AM_CHOP | integer | No | |
WEAPON_TYPE_CH_SLICE | integer | No | |
WEAPON_TYPE_CL_SLICE | integer | No | |
WEAPON_TYPE_CM_SLICE | integer | No | |
WEAPON_TYPE_MH_BASH | integer | No | |
WEAPON_TYPE_MH_CHOP | integer | No | |
WEAPON_TYPE_MH_SLICE | integer | No | |
WEAPON_TYPE_MH_STAB | integer | No | |
WEAPON_TYPE_ML_CHOP | integer | No | |
WEAPON_TYPE_ML_SLICE | integer | No | |
WEAPON_TYPE_MM_BASH | integer | No | |
WEAPON_TYPE_MM_CHOP | integer | No | |
WEAPON_TYPE_MM_SLICE | integer | No | |
WEAPON_TYPE_MM_STAB | integer | No | |
WEAPON_TYPE_NONE | integer | No | |
WEAPON_TYPE_RH_BASH | integer | No | |
WEAPON_TYPE_WH_BASH | integer | No | |
WEAPON_TYPE_WH_SLICE | integer | No | |
WEAPON_TYPE_WL_BASH | integer | No | |
WEAPON_TYPE_WL_SLICE | integer | No | |
WEAPON_TYPE_WL_STAB | integer | No | |
WEAPON_TYPE_WM_BASH | integer | No | |
WEAPON_TYPE_WM_SLICE | integer | No | |
WEAPON_TYPE_WM_STAB | integer | No | |
WeaponTypeDebugStr | string | Yes | |
WorldMaxX | real | No | |
WorldMaxY | real | No |
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
//============================================================================
// 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=28
//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=21
//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
library DummyRecycler /*
// DummyRecycler v1.25
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment dummies for visual effects or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and there are reports that will always leave a permanent tiny bit of
// memory (0.04 KB).
// On average, retrieving a pending Dummy is approximately 4x faster compared
// to creating a new one and recycling a Dummy compared to removing it is
// approximately 1.3x faster.
// Furthermore, if you're using a lot of "Unit has entered map" events,
// using this system will even result to even more better performance
// because retrieving Dummy units does not cause that event to run.
*/ requires /*
nothing
*/ optional Table/*
if not found, this system will use a hashtable. Hashtables are limited to
255 per map.
*/ optional WorldBounds /*
if not found, this system will initialize its own Map Boundaries.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
// function ShowDummy takes unit u, boolean flag returns nothing
// - Shows/hides Dummy Unit without conflicting with the Locust ability.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */
globals
//The rawcode of the Dummy Unit
private constant integer DUMMY_ID = 'h00P'
//The owner of the Dummy Unit
private constant player OWNER = Player(14)
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
private constant integer ANGLES_COUNT = 10
//The number of Dummy units per ANGLES_COUNT. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
private constant integer STORED_UNIT_COUNT = 3
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
private constant integer MAX_DUMMY_COUNT = 100
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum STORED_UNIT_COUNT
private constant integer BORROW_REQUEST = 5
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle.
private constant real ANGLE_TOLERANCE = 10.0
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
private constant boolean HIDE_ON_MAP_CORNER = true
endglobals
//Every time a new dummy unit is retrieved, it will apply this resets
//If it is redundant/you dont need it, remove it.
//! textmacro DUMMY_UNIT_RESET
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
call ShowDummy(bj_lastCreatedUnit, true)
//! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
globals
private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
private real array angle
private integer array count
private integer array countHead
private integer array countNext
private integer array countPrev
private integer array next
private integer array prev
private unit array dummy
private integer upper
private integer lower
private integer lastInstance
private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
endglobals
static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
private module BoundsInit
readonly static real x
readonly static real y
private static method onInit takes nothing returns nothing
local rect map = GetWorldBounds()
set thistype.x = GetRectMaxX(map)
set thistype.y = GetRectMaxY(map)
call RemoveRect(map)
set map = null
endmethod
endmodule
private struct Bounds extends array
implement BoundsInit
endstruct
endif
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable hash = InitHashtable()
endif
private static method onInit takes nothing returns nothing
local real add = 360.0/ANGLES_COUNT
local real a = 0
local integer this = ANGLES_COUNT
local integer head = 0
local integer cHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
set upper = STORED_UNIT_COUNT
set lower = STORED_UNIT_COUNT
static if LIBRARY_Table then
set tb = Table.create()
endif
//Initialize countHeads
loop
exitwhen i < 0
set countNext[cHead] = cHead
set countPrev[cHead] = cHead
set countHead[i] = cHead
set cHead = cHead - 1
set i = i - 1
endloop
set cHead = countHead[STORED_UNIT_COUNT] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen a >= 360
//Initialize head
set next[head] = head
set prev[head] = head
set count[head] = STORED_UNIT_COUNT
set angle[head] = a
//Insert head in the Count List
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
set i = 0
loop
exitwhen i >= STORED_UNIT_COUNT
//Queued Linked List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
endif
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
endif
call PauseUnit(dummy[this], true)
static if LIBRARY_Table then
set tb[GetHandleId(dummy[this])] = this
else
call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
endif
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set a = a + add
endloop
set lastInstance = this
endmethod
endmodule
private struct S extends array
implement M
endstruct
private function GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*ANGLES_COUNT/360.0))
endfunction
function ShowDummy takes unit u, boolean flag returns nothing
if IsUnitHidden(u) == flag then
call ShowUnit(u, flag)
if flag and GetUnitTypeId(u) == DUMMY_ID then
call UnitRemoveAbility(u, 'Aloc')
call UnitAddAbility(u, 'Aloc')
endif
endif
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = GetHead(R2I(facing + FACING_OFFSET))
local integer this = next[head]
local integer cHead
//If there are Dummy Units in the Queue List already facing close to the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
//Remove from the Queue List
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//For double free protection
set next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = dummy[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
//! runtextmacro DUMMY_UNIT_RESET()
//Update Count and Bounds
set count[head] = count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
set count[head] = count[head] + 1
set this = next[countNext[countHead[upper]]]
call SetUnitFacing(dummy[this], angle[head])
//Remove
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//Add to the Current List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
set head = countNext[countHead[upper]]
set count[head] = count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[upper]
if countNext[cHead] == cHead then
set upper = upper - 1
endif
if count[head] < lower then
set lower = count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if dummyCount < MAX_DUMMY_COUNT then
set this = lastInstance
//For double free protection
set next[this] = -1
set dummy[this] = bj_lastCreatedUnit
static if LIBRARY_Table then
set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
else
call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
endif
set lastInstance = lastInstance + 1
endif
set dummyCount = dummyCount + 1
endif
return bj_lastCreatedUnit
endfunction
function RecycleDummy takes unit u returns nothing
static if LIBRARY_Table then
local integer this = S.tb[GetHandleId(u)]
else
local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
endif
local integer head
local integer cHead
//If the unit is a legit Dummy Unit
if this > 0 and next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = countNext[countHead[lower]]
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
//Update Status
call SetUnitFacing(u, angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, OWNER, false)
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
call SetUnitX(u, WorldBounds.maxX)
call SetUnitY(u, WorldBounds.maxY)
else
call SetUnitX(u, Bounds.x)
call SetUnitY(u, Bounds.y)
endif
else
call SetUnitScale(u, 0, 0, 0)
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set count[head] = count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[lower]
if countNext[cHead] == cHead then
set lower = lower + 1
endif
if count[head] > upper then
set upper = count[head]
endif
elseif this == 0 then
call RemoveUnit(u)
debug elseif next[this] != -1 then
debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
endif
endfunction
private function Expires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
static if LIBRARY_Table then
call RecycleDummy(S.tb.unit[id])
call S.tb.unit.remove(id)
else
call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
call FlushChildHashtable(S.hash, id)
endif
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
static if LIBRARY_Table then
set S.tb.unit[GetHandleId(t)] = u
else
call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
endif
call TimerStart(t, time, false, function Expires)
set t = null
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
endfunction
function GetDummyFacingTarget takes unit u returns unit
local real facing = GetRandomReal(0, bj_PI * 2)
local real x = GetUnitX(u) + 50 * Cos(facing + bj_PI)
local real y = GetUnitY(u) + 50 * Sin(facing + bj_PI)
return GetRecycledDummy(x, y, 0, facing)
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS()
endlibrary
library UnitAddAbilityTimed requires TimerUtils, DummyRecycler
//! novjass
-------- API --------
call TimedAbility.create(unit Your Unit, integer Ability ID, real Timeout, boolean Recycle Unit)
/*
If you want to recycle the unit right after the ability is removed, set the boolean
(the final argument) to true
*/
//! endnovjass
struct TimedAbility
unit u
integer abil
boolean recycle
private method destroy takes nothing returns nothing
call this.deallocate()
set this.u = null
set this.abil = 0
set this.recycle = false
endmethod
private static method removeAbil takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call UnitRemoveAbility(this.u, this.abil)
if this.recycle then
call RecycleDummy(this.u)
endif
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method create takes unit source, integer abil_code, real timeout, boolean recycle_source returns TimedAbility
local thistype this = allocate()
local timer t = NewTimerEx(this)
set this.u = source
set this.abil = abil_code
set this.recycle = recycle_source
call TimerStart(t, timeout, false, function thistype.removeAbil)
set t = null
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=4
//TESH.alwaysfold=0
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world = GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set playMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set playMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set playMinX = GetRectMinX(bj_mapInitialPlayableArea)
set playMinY = GetRectMinY(bj_mapInitialPlayableArea)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
readonly static real playMaxX
readonly static real playMaxY
readonly static real playMinX
readonly static real playMinY
implement WorldBoundInit
endstruct
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Alloc ~~ By Sevion ~~ Version 1.09 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Alloc?
// - Alloc implements an intuitive allocation method for array structs
//
// =Pros=
// - Efficient.
// - Simple.
// - Less overhead than regular structs.
//
// =Cons=
// - Must use array structs (hardly a con).
// - Must manually call OnDestroy.
// - Must use Delegates for inheritance.
// - No default values for variables (use onInit instead).
// - No array members (use another Alloc struct as a linked list or type declaration).
//
// Methods:
// - struct.allocate()
// - struct.deallocate()
//
// These methods are used just as they should be used in regular structs.
//
// Modules:
// - Alloc
// Implements the most basic form of Alloc. Includes only create and destroy
// methods.
//
// Details:
// - Less overhead than regular structs
//
// - Use array structs when using Alloc. Put the implement at the top of the struct.
//
// - Alloc operates almost exactly the same as default structs in debug mode with the exception of onDestroy.
//
// How to import:
// - Create a trigger named Alloc.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Nestharus for the method of allocation and suggestions on further merging.
// - Bribe for suggestions like the static if and method names.
// - PurgeandFire111 for some suggestions like the merging of Alloc and AllocX as well as OnDestroy stuff.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Alloc
module Alloc
private static integer instanceCount = 0
private thistype recycle
static method allocate takes nothing returns thistype
local thistype this
if (thistype(0).recycle == 0) then
debug if (instanceCount == 8190) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to allocate too many instances!")
debug return 0
debug endif
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = thistype(0).recycle
set thistype(0).recycle = thistype(0).recycle.recycle
endif
debug set this.recycle = -1
return this
endmethod
method deallocate takes nothing returns nothing
debug if (this.recycle != -1) then
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Alloc ERROR: Attempted to deallocate an invalid instance at [" + I2S(this) + "]!")
debug return
debug endif
set this.recycle = thistype(0).recycle
set thistype(0).recycle = this
endmethod
endmodule
endlibrary
library LineSegmentEnumeration /* v2.2a -- hiveworkshop.com/threads/line-segment-enumeration-v1-1.286552/
Information
¯¯¯¯¯¯¯¯¯¯¯
Allows to enumerate widgets inside a line segment with an offset.
So basicly it will result in a rect, but which is also allowed to be rotated.
Mechanics
¯¯¯¯¯¯¯¯¯
(Issue:)
The problem with normal jass rects is that they aren't defined by 4 points, but only by 4 values: x/y -min/max.
The result is that a jass rect is never rotated, so it's always paralel to the x/y axis.
But when we draw a line from point A to point B we might also create a non-axix-parelel rect, and then
we can't use the normal rect natives from jass anymore to find out if a point is inside the rect.
(Solution:)
To solve this problem the system does following:
jass rect: rectangular defined by 4 values (axis paralel)
custom rect: the real rectangular that is defined by user (not axis parelel)
1. Create a big jass rect that is big enough so we can ensure to enum all widgets that are potentialy inside our custom rect. (Enum_Group)
This Enum_Group will contain all wanted units, but may also contain not wanted units.
2. Construct the custom rect following a form with the same parameters as in this image, but in 2D:
https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Ellipsoide.svg/800px-Ellipsoide.svg.png
3. Loop through Enum_Group and define each widget's coordinates relative to the center of the custom rect as a 2D vector
4. Get the components of the widget's position vector on the local (rotated) x-y axis of the custom rect
5. Check if the projected lengths (absolute value of components) of the widget's position is less than <a> and <b> as described in the
image linked above.
*/
// --- API ---
//! novjass
struct LineSegment
static constant real MAX_UNIT_COLLISION
static method EnumUnitsEx takes group whichgroup, real ax, real ay, real bx, real by, real offset, boolean checkCollision returns nothing
static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
static method EnumDestructables takes real ax, real ay, real bx, real by, real offset returns nothing
// after enumerated destructables you have access to:
static integer DestructableCounter // starts with index "0"
static destructable array Destructable
static method EnumItems takes real ax, real ay, real bx, real by, real offset returns nothing
// after enumerated items you have access to:
static integer ItemCounter // starts with index "0"
static destructable array Item
//! endnovjass
// ==== End API ====
struct LineSegment extends array
public static constant real MAX_UNIT_COLLISION = 197.00
private static constant rect RECT = Rect(0, 0, 0, 0)
private static constant group GROUP = CreateGroup()
private static real ox
private static real oy
private static real dx
private static real dy
private static real da
private static real db
private static real ui
private static real uj
private static real wdx
private static real wdy
private static method PrepareRect takes real ax, real ay, real bx, real by, real offset, real offsetCollision returns nothing
local real maxX
local real maxY
local real minX
local real minY
// get center coordinates of rectangle
set ox = 0.5*(ax + bx)
set oy = 0.5*(ay + by)
// get rectangle major axis as vector
set dx = 0.5*(bx - ax)
set dy = 0.5*(by - ay)
// get half of rectangle length (da) and height (db)
set da = SquareRoot(dx*dx + dy*dy)
set db = offset
// get unit vector of the major axis
set ui = dx/da
set uj = dy/da
// Prepare the bounding Jass Rect
set offset = offset + offsetCollision
if ax > bx then
set maxX = ax + offset
set minX = bx - offset
else
set maxX = bx + offset
set minX = ax - offset
endif
if ay > by then
set maxY = ay + offset
set minY = by - offset
else
set maxY = by + offset
set minY = ay - offset
endif
call SetRect(RECT, minX, minY, maxX, maxY)
endmethod
private static method RotateWidgetCoordinates takes widget w returns nothing
// distance of widget from rectangle center in vector form
set wdx = GetWidgetX(w) - ox
set wdy = GetWidgetY(w) - oy
set dx = wdx*ui + wdy*uj // get the component of above vector in the rect's major axis
set dy = wdx*(-uj) + wdy*ui // get the component of above vector in the rect's transverse axis
endmethod
private static method IsWidgetInRect takes widget w returns boolean
call RotateWidgetCoordinates(w)
// Check if the components above are less than half the length and height of the rectangle
// (Square them to compare absolute values)
return dx*dx <= da*da and dy*dy <= db*db
endmethod
private static method IsUnitInRect takes unit u, boolean checkCollision returns boolean
if checkCollision then
call RotateWidgetCoordinates(u)
// Check if the perpendicular distances of the unit from both axes of the rect are less than
// da and db
return IsUnitInRangeXY(u, ox - dy*uj, oy + dy*ui, RAbsBJ(da)) /*
*/ and IsUnitInRangeXY(u, ox + dx*ui, oy + dx*uj, RAbsBJ(db))
endif
return IsWidgetInRect(u)
endmethod
public static method EnumUnitsEx takes group whichgroup, real ax, real ay, real bx, real by, real offset, boolean checkCollision returns nothing
local unit u
if checkCollision then
call PrepareRect(ax, ay, bx, by, offset, MAX_UNIT_COLLISION)
else
call PrepareRect(ax, ay, bx, by, offset, 0.00)
endif
call GroupEnumUnitsInRect(GROUP, RECT, null)
// enum through all tracked units, and check if it's inside bounds
call GroupClear(whichgroup)
loop
set u = FirstOfGroup(GROUP)
exitwhen u == null
if IsUnitInRect(u, checkCollision) then
call GroupAddUnit(whichgroup, u)
endif
call GroupRemoveUnit(GROUP, u)
endloop
endmethod
public static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
call EnumUnitsEx(whichgroup, ax, ay, bx, by, offset, false)
endmethod
//! textmacro LSE_WIDGET takes TYPE, NAME
public static integer $NAME$Counter = -1
public static $TYPE$ array $NAME$
private static method on$NAME$Filter takes nothing returns nothing
local $TYPE$ t = GetFilter$NAME$()
if IsWidgetInRect(t) then
set $NAME$Counter = $NAME$Counter + 1
set $NAME$[$NAME$Counter] = t
endif
set t = null
endmethod
public static method Enum$NAME$s takes real ax, real ay, real bx, real by, real offset returns nothing
call PrepareRect(ax, ay, bx, by, offset, 0.00)
set $NAME$Counter = -1
call Enum$NAME$sInRect(RECT, Filter(function thistype.on$NAME$Filter), null)
endmethod
//! endtextmacro
//! runtextmacro LSE_WIDGET("destructable", "Destructable")
//! runtextmacro LSE_WIDGET("item", "Item")
endstruct
endlibrary
library MouseUtils
/*
-------------------
MouseUtils
- MyPad
1.3.1
-------------------
----------------------------------------------------------------------------
A simple snippet that allows one to
conveniently use the mouse natives
as they were meant to be...
-------------------
| API |
-------------------
struct UserMouse extends array
static method operator [] (player p) -> thistype
- Returns the player's id + 1
static method getCurEventType() -> integer
- Returns the custom event that got executed.
method operator player -> player
- Returns Player(this - 1)
readonly real mouseX
readonly real mouseY
- Returns the current mouse coordinates.
readonly method operator isMouseClicked -> boolean
- Determines whether any mouse key has been clicked,
and will return true on the first mouse key.
method isMouseButtonClicked(mousebuttontype mouseButton)
- Returns true if the mouse button hasn't been
released yet.
method setMousePos(real x, y) (introduced in 1.0.2.2)
- Sets the mouse position for a given player.
static method registerCode(code c, integer ev) -> triggercondition
- Lets code run upon the execution of a certain event.
- Returns a triggercondition that can be removed later.
static method unregisterCallback(triggercondition trgHndl, integer ev)
- Removes a generated triggercondition from the trigger.
functions:
GetPlayerMouseX(player p) -> real
GetPlayerMouseY(player p) -> real
- Returns the coordinates of the mouse of the player.
OnMouseEvent(code func, integer eventId) -> triggercondition
- See UserMouse.registerCode
GetMouseEventType() -> integer
- See UserMouse.getCurEventType
UnregisterMouseCallback(triggercondition t, integer eventId)
- See UserMouse.unregisterCallback
SetUserMousePos(player p, real x, real y)
- See UserMouse.setMousePos
Unique Global Constants:
IMPL_LOCK (Introduced in v.1.0.2.2)
- Enables or disables the lock option
-------------------
| Credits |
-------------------
- Pyrogasm for pointing out a comparison logic flaw
in operator isMouseClicked.
- Illidan(Evil)X for the useful enum handles that
grant more functionality to this snippet.
- TriggerHappy for the suggestion to include
associated events and callbacks to this snippet.
- Quilnez for pointing out a bug related to the
method isMouseButtonClicked not working as intended
in certain situations.
- Lt_Hawkeye for pointing out a bug related to the
registration of callback functions not working
as intended. (The triggers that would hold these
functions did not exist yet at those moments)
----------------------------------------------------------------------------
*/
// Arbitrary constants
globals
constant integer EVENT_MOUSE_UP = 0x400
constant integer EVENT_MOUSE_DOWN = 0x800
constant integer EVENT_MOUSE_MOVE = 0xC00
// Introduced in v1.0.2.3
// Commented out in v1.0.2.4
// private constant real STARTUP_DELAY = 0.00
// private constant boolean NO_DELAY = false
// Introduced in v1.0.2.2
private constant boolean IMPL_LOCK = true
endglobals
private module Init
private static method invokeTimerInit takes nothing returns nothing
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
call thistype.timerInit()
endmethod
private static method onInit takes nothing returns nothing
set evTrigger[EVENT_MOUSE_UP] = CreateTrigger()
set evTrigger[EVENT_MOUSE_DOWN] = CreateTrigger()
set evTrigger[EVENT_MOUSE_MOVE] = CreateTrigger()
call TimerStart(CreateTimer(), 0.00, false, function thistype.invokeTimerInit)
endmethod
endmodule
struct UserMouse extends array
static if IMPL_LOCK then
// Determines the minimum interval that a mouse move event detector
// will be deactivated. (Globally-based)
// You can configure it to any amount you like.
private static constant real INTERVAL = 0.031250000
// Determines how many times a mouse move event detector can fire
// before being deactivated. (locally-based)
// You can configure this to any integer value. (Preferably positive)
private static constant integer MOUSE_COUNT_MAX = 16
// Determines the amount to be deducted from mouseEventCount
// per INTERVAL. Runs independently of resetTimer
private static constant integer MOUSE_COUNT_LOSS = 8
private static constant boolean IS_INSTANT = (INTERVAL <= 0.)
endif
private static integer currentEventType = 0
private static integer updateCount = 0
private static trigger stateDetector = null
static if IMPL_LOCK and not IS_INSTANT then
private static timer resetTimer = null
private integer mouseEventCount
private timer mouseEventReductor
endif
private static trigger array evTrigger
private static integer array mouseButtonStack
private thistype next
private thistype prev
private thistype resetNext
private thistype resetPrev
private trigger posDetector
private integer mouseClickCount
readonly real mouseX
readonly real mouseY
// Converts the enum type mousebuttontype into an integer
private static method toIndex takes mousebuttontype mouseButton returns integer
return GetHandleId(mouseButton)
endmethod
static method getCurEventType takes nothing returns integer
return currentEventType
endmethod
static method operator [] takes player p returns thistype
if thistype(GetPlayerId(p) + 1).posDetector != null then
return GetPlayerId(p) + 1
endif
return 0
endmethod
method operator player takes nothing returns player
return Player(this - 1)
endmethod
method operator isMouseClicked takes nothing returns boolean
return .mouseClickCount > 0
endmethod
method isMouseButtonClicked takes mousebuttontype mouseButton returns boolean
return UserMouse.mouseButtonStack[(this - 1)*3 + UserMouse.toIndex(mouseButton)] > 0
endmethod
method setMousePos takes integer x, integer y returns nothing
if GetLocalPlayer() == this.player then
call BlzSetMousePos(x, y)
endif
endmethod
static if IMPL_LOCK then
private static method getMouseEventReductor takes timer t returns thistype
local thistype this = thistype(0).next
loop
exitwhen this.mouseEventReductor == t or this == 0
set this = this.next
endloop
return this
endmethod
private static method onMouseUpdateListener takes nothing returns nothing
local thistype this = thistype(0).resetNext
set updateCount = 0
loop
exitwhen this == 0
set updateCount = updateCount + 1
set this.mouseEventCount = 0
call EnableTrigger(this.posDetector)
set this.resetNext.resetPrev = this.resetPrev
set this.resetPrev.resetNext = this.resetNext
set this = this.resetNext
endloop
if updateCount > 0 then
static if not IS_INSTANT then
call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
else
call onMouseUpdateListener()
endif
else
static if not IS_INSTANT then
call TimerStart(resetTimer, 0.00, false, null)
call PauseTimer(resetTimer)
endif
endif
endmethod
private static method onMouseReductListener takes nothing returns nothing
local thistype this = getMouseEventReductor(GetExpiredTimer())
if this.mouseEventCount <= 0 then
call PauseTimer(this.mouseEventReductor)
else
set this.mouseEventCount = IMaxBJ(this.mouseEventCount - MOUSE_COUNT_LOSS, 0)
call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
endif
endmethod
endif
private static method onMouseUpOrDown takes nothing returns nothing
local thistype this = thistype[GetTriggerPlayer()]
local integer index = (this - 1)*3 + UserMouse.toIndex(BlzGetTriggerPlayerMouseButton())
local boolean releaseFlag = false
if GetTriggerEventId() == EVENT_PLAYER_MOUSE_DOWN then
set this.mouseClickCount = IMinBJ(this.mouseClickCount + 1, 3)
set releaseFlag = UserMouse.mouseButtonStack[index] <= 0
set UserMouse.mouseButtonStack[index] = IMinBJ(UserMouse.mouseButtonStack[index] + 1, 1)
if releaseFlag then
set currentEventType = EVENT_MOUSE_DOWN
call TriggerEvaluate(evTrigger[EVENT_MOUSE_DOWN])
endif
else
set this.mouseClickCount = IMaxBJ(this.mouseClickCount - 1, 0)
set releaseFlag = UserMouse.mouseButtonStack[index] > 0
set UserMouse.mouseButtonStack[index] = IMaxBJ(UserMouse.mouseButtonStack[index] - 1, 0)
if releaseFlag then
set currentEventType = EVENT_MOUSE_UP
call TriggerEvaluate(evTrigger[EVENT_MOUSE_UP])
endif
endif
endmethod
private static method onMouseMove takes nothing returns nothing
local thistype this = thistype[GetTriggerPlayer()]
local boolean started = false
set this.mouseX = BlzGetTriggerPlayerMouseX()
set this.mouseY = BlzGetTriggerPlayerMouseY()
static if IMPL_LOCK then
set this.mouseEventCount = this.mouseEventCount + 1
if this.mouseEventCount <= 1 then
call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
endif
endif
set currentEventType = EVENT_MOUSE_MOVE
call TriggerEvaluate(evTrigger[EVENT_MOUSE_MOVE])
static if IMPL_LOCK then
if this.mouseEventCount >= thistype.MOUSE_COUNT_MAX then
call DisableTrigger(this.posDetector)
if thistype(0).resetNext == 0 then
static if not IS_INSTANT then
call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
// Mouse event reductor should be paused
else
set started = true
endif
call PauseTimer(this.mouseEventReductor)
endif
set this.resetNext = 0
set this.resetPrev = this.resetNext.resetPrev
set this.resetPrev.resetNext = this
set this.resetNext.resetPrev = this
if started then
call onMouseUpdateListener()
endif
endif
endif
endmethod
private static method initCallback takes nothing returns nothing
local thistype this = 1
local player p = this.player
static if IMPL_LOCK and not IS_INSTANT then
set resetTimer = CreateTimer()
endif
set stateDetector = CreateTrigger()
call TriggerAddCondition( stateDetector, Condition(function thistype.onMouseUpOrDown))
loop
exitwhen integer(this) > bj_MAX_PLAYER_SLOTS
if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
set this.posDetector = CreateTrigger()
static if IMPL_LOCK and not IS_INSTANT then
set this.mouseEventReductor = CreateTimer()
endif
call TriggerRegisterPlayerEvent( this.posDetector, p, EVENT_PLAYER_MOUSE_MOVE )
call TriggerAddCondition( this.posDetector, Condition(function thistype.onMouseMove))
call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_UP )
call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_DOWN )
endif
set this = this + 1
set p = this.player
endloop
endmethod
private static method timerInit takes nothing returns nothing
call thistype.initCallback()
endmethod
static method registerCode takes code handlerFunc, integer eventId returns triggercondition
return TriggerAddCondition(evTrigger[eventId], Condition(handlerFunc))
endmethod
static method unregisterCallback takes triggercondition whichHandler, integer eventId returns nothing
call TriggerRemoveCondition(evTrigger[eventId], whichHandler)
endmethod
implement Init
endstruct
function GetPlayerMouseX takes player p returns real
return UserMouse[p].mouseX
endfunction
function GetPlayerMouseY takes player p returns real
return UserMouse[p].mouseY
endfunction
function OnMouseEvent takes code func, integer eventId returns triggercondition
return UserMouse.registerCode(func, eventId)
endfunction
function GetMouseEventType takes nothing returns integer
return UserMouse.getCurEventType()
endfunction
function UnregisterMouseCallback takes triggercondition whichHandler, integer eventId returns nothing
call UserMouse.unregisterCallback(whichHandler, eventId)
endfunction
function SetUserMousePos takes player p, integer x, integer y returns nothing
call UserMouse[p].setMousePos(x, y)
endfunction
endlibrary
//===========================================================================
function UnitEventDestroyGroup takes integer i returns nothing
if udg_CargoTransportGroup[i] != null then
call DestroyGroup(udg_CargoTransportGroup[i])
set udg_CargoTransportGroup[i] = null
endif
endfunction
function UnitEventCheckAfter takes nothing returns nothing
local integer i = 0
loop
set i = udg_CheckDeathList[i]
exitwhen i == 0
set udg_UDex = i
if udg_IsUnitNew[i] then
//The unit was just created.
set udg_IsUnitNew[i] = false
set udg_UnitIndexEvent = 1.50 //New event requested by SpellBound to detect when unit fully enters scope.
set udg_UnitIndexEvent = 0.00
elseif udg_IsUnitTransforming[i] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[i] = GetUnitTypeId(udg_UDexUnits[i]) //Set this afterward to give the user extra reference
set udg_IsUnitTransforming[i] = false
call UnitAddAbility(udg_UDexUnits[i], udg_DetectTransformAbility)
elseif udg_IsUnitAlive[i] then
//The unit has started reincarnating.
set udg_IsUnitReincarnating[i] = true
set udg_IsUnitAlive[i] = false
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
endif
set udg_CheckDeathInList[i] = false
endloop
//Empty the list
set udg_CheckDeathList[0] = 0
endfunction
function UnitEventCheckAfterProxy takes integer i returns nothing
if udg_CheckDeathList[0] == 0 then
call TimerStart(udg_CheckDeathTimer, 0.00, false, function UnitEventCheckAfter)
endif
if not udg_CheckDeathInList[i] then
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
set udg_CheckDeathInList[i] = true
endif
endfunction
function UnitEventOnUnload takes nothing returns nothing
local integer i = udg_UDex
call GroupRemoveUnit(udg_CargoTransportGroup[GetUnitUserData(udg_CargoTransportUnit[i])], udg_UDexUnits[i])
set udg_IsUnitBeingUnloaded[i] = true
set udg_CargoEvent = 0.00
set udg_CargoEvent = 2.00
set udg_CargoEvent = 0.00
set udg_IsUnitBeingUnloaded[i] = false
if not IsUnitLoaded(udg_UDexUnits[i]) or IsUnitType(udg_CargoTransportUnit[i], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_CargoTransportUnit[i]) == 0 then
set udg_CargoTransportUnit[i] = null
endif
endfunction
function UnitEventOnDeath takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_UDex != 0 then
set udg_KillerOfUnit[udg_UDex] = GetKillingUnit() //Added 29 May 2017 for GIMLI_2
set udg_IsUnitAlive[udg_UDex] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
set udg_KillerOfUnit[udg_UDex] = null
if udg_CargoTransportUnit[udg_UDex] != null then
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnOrder takes nothing returns boolean
local integer pdex = udg_UDex
local unit u = GetFilterUnit()
local integer i = GetUnitUserData(u)
if i > 0 then
set udg_UDex = i
if GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
if not udg_IsUnitRemoved[i] then
set udg_IsUnitRemoved[i] = true
set udg_IsUnitAlive[i] = false
set udg_SummonerOfUnit[i] = null
//For backwards-compatibility:
set udg_DeathEvent = 0.00
set udg_DeathEvent = 3.00
set udg_DeathEvent = 0.00
//Fire deindex event for UDex:
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 2.00
set udg_UnitIndexEvent = 0.00
set udg_UDexNext[udg_UDexPrev[i]] = udg_UDexNext[i]
set udg_UDexPrev[udg_UDexNext[i]] = udg_UDexPrev[i]
// Recycle the index for later use
set udg_UDexUnits[i] = null
set udg_UDexPrev[i] = udg_UDexLastRecycled
set udg_UDexLastRecycled = i
call UnitEventDestroyGroup(i)
endif
elseif not udg_IsUnitAlive[i] then
if not IsUnitType(u, UNIT_TYPE_DEAD) then
set udg_IsUnitAlive[i] = true
set udg_DeathEvent = 0.00
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
set udg_IsUnitReincarnating[i] = false
endif
elseif IsUnitType(u, UNIT_TYPE_DEAD) then
if udg_IsUnitNew[i] then
//This unit was created as a corpse.
set udg_IsUnitAlive[i] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
elseif udg_CargoTransportUnit[i] == null or not IsUnitType(u, UNIT_TYPE_HERO) then
//The unit may have just started reincarnating.
call UnitEventCheckAfterProxy(i)
endif
elseif GetUnitAbilityLevel(u, udg_DetectTransformAbility) == 0 and not udg_IsUnitTransforming[i] then
set udg_IsUnitTransforming[i] = true
call UnitEventCheckAfterProxy(i) //This block has been updated on 21 July 2017
endif
if udg_CargoTransportUnit[i] != null and not udg_IsUnitBeingUnloaded[i] and not IsUnitLoaded(u) or IsUnitType(u, UNIT_TYPE_DEAD) then
call UnitEventOnUnload()
endif
set udg_UDex = pdex
endif
set u = null
return false
endfunction
function UnitEventOnSummon takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_IsUnitNew[udg_UDex] then
set udg_SummonerOfUnit[udg_UDex] = GetSummoningUnit()
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 0.50
set udg_UnitIndexEvent = 0.00
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnLoad takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = GetUnitUserData(GetTriggerUnit())
local integer index
if i != 0 then
set udg_UDex = i
if udg_CargoTransportUnit[i] != null then
call UnitEventOnUnload()
endif
//Loaded corpses do not issue an order when unloaded, therefore must
//use the enter-region event method taken from Jesus4Lyf's Transport.
if not udg_IsUnitAlive[i] then
call SetUnitX(udg_UDexUnits[i], udg_WorldMaxX)
call SetUnitY(udg_UDexUnits[i], udg_WorldMaxY)
endif
set udg_CargoTransportUnit[i] = GetTransportUnit()
set index = GetUnitUserData(udg_CargoTransportUnit[i])
if udg_CargoTransportGroup[index] == null then
set udg_CargoTransportGroup[index] = CreateGroup()
endif
call GroupAddUnit(udg_CargoTransportGroup[index], udg_UDexUnits[i])
set udg_CargoEvent = 0.00
set udg_CargoEvent = 1.00
set udg_CargoEvent = 0.00
set udg_UDex = pdex
endif
return false
endfunction
function UnitEventEnter takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = udg_UDexLastRecycled
local unit u = GetFilterUnit()
if udg_UnitIndexerEnabled and GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
//Generate a unique integer index for this unit
if i == 0 then
set i = udg_UDexMax + 1
set udg_UDexMax = i
else
set udg_UDexLastRecycled = udg_UDexPrev[i]
endif
//Link index to unit, unit to index
set udg_UDexUnits[i] = u
call SetUnitUserData(u, i)
//For backwards-compatibility, add the unit to a linked list
set udg_UDexNext[i] = udg_UDexNext[0]
set udg_UDexPrev[udg_UDexNext[0]] = i
set udg_UDexNext[0] = i
set udg_UDexPrev[i] = 0
set udg_CheckDeathInList[i] = false
call UnitAddAbility(u, udg_DetectRemoveAbility)
call UnitMakeAbilityPermanent(u, true, udg_DetectRemoveAbility)
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_UnitTypeOf[i] = GetUnitTypeId(u)
set udg_IsUnitNew[i] = true
set udg_IsUnitAlive[i] = true
set udg_IsUnitRemoved[i] = false
set udg_IsUnitReincarnating[i] = false
set udg_IsUnitPreplaced[i] = udg_IsUnitPreplaced[0] //Added 29 May 2017 for Spellbound
call UnitEventCheckAfterProxy(i)
//Fire index event for UDex
set udg_UDex = i
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 1.00
set udg_UnitIndexEvent = 0.00
else
set udg_UDex = GetUnitUserData(u)
if udg_CargoTransportUnit[udg_UDex] != null and not IsUnitLoaded(u) then
//The unit was dead, but has re-entered the map.
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
set u = null
return false
endfunction
//===========================================================================
function UnitEventInit takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS //update to make it work with 1.29
local player p
local trigger t = CreateTrigger()
local trigger load = CreateTrigger()
local trigger death = CreateTrigger()
local trigger summon = CreateTrigger()
local rect r = GetWorldBounds()
local region re = CreateRegion()
local boolexpr enterB = Filter(function UnitEventEnter)
local boolexpr orderB = Filter(function UnitEventOnOrder)
set udg_WorldMaxX = GetRectMaxX(r)
set udg_WorldMaxY = GetRectMaxY(r)
call RegionAddRect(re, r)
call RemoveRect(r)
call UnitEventDestroyGroup(0)
call UnitEventDestroyGroup(1)
set udg_CheckDeathList[0] = 0
set udg_UnitIndexerEnabled = true
call TriggerRegisterEnterRegion(CreateTrigger(), re, enterB)
call TriggerAddCondition(load, Filter(function UnitEventOnLoad))
call TriggerAddCondition(death, Filter(function UnitEventOnDeath))
call TriggerAddCondition(summon, Filter(function UnitEventOnSummon))
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, udg_DetectRemoveAbility, false)
call SetPlayerAbilityAvailable(p, udg_DetectTransformAbility, false)
call TriggerRegisterPlayerUnitEvent(summon, p, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, orderB)
call TriggerRegisterPlayerUnitEvent(death, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(load, p, EVENT_PLAYER_UNIT_LOADED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
set summon = null
set death = null
set load = null
set re = null
set enterB = null
set orderB = null
set p = null
set r = null
set t = null
endfunction
function InitTrig_Unit_Event takes nothing returns nothing
endfunction
library NewBonus requires CooldownReduction, DTCUtils
/* ----------------------- NewBonus v2.2 by Chopinski ----------------------- */
//! novjass
Since ObjectMerger is broken and we still have no means to edit
bonus values (green values) i decided to create a light weight
Bonus library that works in the same way that the original Bonus Mod
by Earth Fury did. NewBonus requires patch 1.30+.
Credits to Earth Fury for the original Bonus idea
How to Import?
Importing bonus mod is really simple. Just copy the 9 abilities with the
prefix "NewBonus" from the Object Editor into your map and match their new raw
code to the bonus types in the global block below. Then create a trigger called
NewBonus, convert it to custom text and paste this code there. You done!
API:
function GetUnitBonus takes unit u, integer bonus_type returns real
-> Returns the specified bonus amount for the unit
-> Example: set amount = GetUnitBonus(GetTriggerUnit(), BONUS_AGILITY)
function SetUnitBonus takes unit u, integer bonus_type, real amount returns real
-> Set the specified bonus type to amount for the unit
-> Example: call SetUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)
function RemoveUnitBonus takes unit u, integer bonus_type returns nothing
-> Removes the Specified bonus type from unit
-> Example: call RemoveUnitBonus(GetTriggerUnit(), BONUS_AGILITY)
function AddUnitBonus takes unit u, integer bonus_type, real amount returns real
-> Add the specified amount for the specified bonus tyte for unit
-> Example: call AddUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* Configuration */
/* --------------------------------------------------------------------------
*/
globals
//The bonus types
//Integer Values
constant integer BONUS_DAMAGE = 1
constant integer BONUS_ARMOR = 2
constant integer BONUS_AGILITY = 3
constant integer BONUS_STRENGTH = 4
constant integer BONUS_INTELLIGENCE = 5
constant integer BONUS_HEALTH = 6
constant integer BONUS_MANA = 7
constant integer BONUS_MOVEMENT_SPEED = 8
constant integer BONUS_SIGHT_RANGE = 9
constant integer BONUS_SPELL_POWER = 42
//Real Values
constant integer BONUS_HEALTH_REGEN = 50
constant integer BONUS_MANA_REGEN = 51
constant integer BONUS_ATTACK_SPEED = 52
constant integer BONUS_MAGIC_RESISTANCE = 53
constant integer BONUS_EVASION_CHANCE = 54
constant integer BONUS_CRITICAL_CHANCE = 55
constant integer BONUS_CRITICAL_DAMAGE = 56
constant integer BONUS_LIFE_STEAL = 57
constant integer BONUS_COOLDOWN_REDUCTION = 62
constant integer BONUS_COOLDOWN_REDUCTION_FLAT = 63
constant integer BONUS_COOLDOWN_OFFSET = 64
constant integer BONUS_SPELL_HASTE = 71 //% more casts. 100 of this = 50% BONUS_COOLDOWN_REDUCTION_FLAT
constant integer BONUS_SPELL_POWER_MULTIPLIER = 72
constant integer BONUS_ARMOR_PEN_FLAT = 73
constant integer BONUS_ARMOR_PEN_PERCENT = 74
//The abilities codes for each bonus
//When pasting the abilities over to your map
//their raw code should match the bonus here
private constant integer DAMAGE_ABILITY = 'Z001'
private constant integer ARMOR_ABILITY = 'Z002'
private constant integer STATS_ABILITY = 'Z003'
private constant integer HEALTH_ABILITY = 'Z004'
private constant integer MANA_ABILITY = 'Z005'
private constant integer HEALTHREGEN_ABILITY = 'Z006'
private constant integer MANAREGEN_ABILITY = 'Z007'
private constant integer ATTACKSPEED_ABILITY = 'Z008'
private constant integer MOVEMENTSPEED_ABILITY = 'Z009'
private constant integer SIGHT_RANGE_ABILITY = 'Z00A'
private constant integer MAGIC_RESISTANCE_ABILITY = 'Z00B'
//private constant integer CRITICAL_STRIKE_ABILITY = 'Z00C'
private constant integer EVASION_ABILITY = 'Z00D'
private constant integer LIFE_STEAL_ABILITY = 'Z00E'
//The abilities fields that are modified. For the sake of readability
private constant abilityintegerlevelfield DAMAGE_FIELD = ABILITY_ILF_ATTACK_BONUS
private constant abilityintegerlevelfield ARMOR_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
private constant abilityintegerlevelfield AGILITY_FIELD = ABILITY_ILF_AGILITY_BONUS
private constant abilityintegerlevelfield STRENGTH_FIELD = ABILITY_ILF_STRENGTH_BONUS_ISTR
private constant abilityintegerlevelfield INTELLIGENCE_FIELD = ABILITY_ILF_INTELLIGENCE_BONUS
private constant abilityintegerlevelfield HEALTH_FIELD = ABILITY_ILF_MAX_LIFE_GAINED
private constant abilityintegerlevelfield MANA_FIELD = ABILITY_ILF_MAX_MANA_GAINED
private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD = ABILITY_ILF_MOVEMENT_SPEED_BONUS
private constant abilityintegerlevelfield SIGHT_RANGE_FIELD = ABILITY_ILF_SIGHT_RANGE_BONUS
private constant abilityreallevelfield HEALTHREGEN_FIELD = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
private constant abilityreallevelfield MANAREGEN_FIELD = ABILITY_RLF_AMOUNT_REGENERATED
private constant abilityreallevelfield ATTACKSPEED_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
private constant abilityreallevelfield MAGIC_RESISTANCE_FIELD = ABILITY_RLF_DAMAGE_REDUCTION_ISR2
private constant abilityreallevelfield CRITICAL_CHANCE_FIELD = ABILITY_RLF_CHANCE_TO_CRITICAL_STRIKE
private constant abilityreallevelfield CRITICAL_DAMAGE_FIELD = ABILITY_RLF_DAMAGE_MULTIPLIER_OCR2
private constant abilityreallevelfield EVASION_FIELD = ABILITY_RLF_CHANCE_TO_EVADE_EEV1
private constant abilityreallevelfield LIFE_STEAL_FIELD = ABILITY_RLF_LIFE_STOLEN_PER_ATTACK
endglobals
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
struct NewBonus
static method SetUnitAbilityBonusI takes unit u, integer abilCode, abilityintegerlevelfield field, integer amount returns integer
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), field, 0, amount) then
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
endif
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), field, 0)
endmethod
static method SetUnitAbilityBonusR takes unit u, integer abilCode, abilityreallevelfield field, real amount returns real
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
if BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), field, 0, amount) then
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
endif
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), field, 0)
endmethod
static method Get takes unit u, integer bonus_type returns integer
if bonus_type == BONUS_DAMAGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, DAMAGE_ABILITY), DAMAGE_FIELD, 0)
elseif bonus_type == BONUS_ARMOR then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, ARMOR_ABILITY), ARMOR_FIELD, 0)
elseif bonus_type == BONUS_HEALTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0)
elseif bonus_type == BONUS_MANA then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0)
elseif bonus_type == BONUS_AGILITY then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), AGILITY_FIELD, 0)
elseif bonus_type == BONUS_STRENGTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), STRENGTH_FIELD, 0)
elseif bonus_type == BONUS_INTELLIGENCE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, STATS_ABILITY), INTELLIGENCE_FIELD, 0)
elseif bonus_type == BONUS_MOVEMENT_SPEED then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MOVEMENTSPEED_ABILITY), MOVEMENTSPEED_FIELD, 0)
elseif bonus_type == BONUS_SIGHT_RANGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, SIGHT_RANGE_ABILITY), SIGHT_RANGE_FIELD, 0)
elseif bonus_type == BONUS_SPELL_POWER then
return GetSpellPowerBaseAmount(u)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method Set takes unit u, integer bonus_type, integer amount returns integer
local real p
if bonus_type == BONUS_DAMAGE then
return SetUnitAbilityBonusI(u, DAMAGE_ABILITY, DAMAGE_FIELD, amount)
elseif bonus_type == BONUS_ARMOR then
return SetUnitAbilityBonusI(u, ARMOR_ABILITY, ARMOR_FIELD, amount)
elseif bonus_type == BONUS_HEALTH then
set p = GetUnitLifePercent(u)
call BlzSetUnitMaxHP(u, (BlzGetUnitMaxHP(u) + amount - Get(u, bonus_type)))
call SetUnitLifePercentBJ(u, p)
return SetUnitAbilityBonusI(u, HEALTH_ABILITY, HEALTH_FIELD, amount)
elseif bonus_type == BONUS_MANA then
set p = GetUnitManaPercent(u)
call BlzSetUnitMaxMana(u, (BlzGetUnitMaxMana(u) + amount - Get(u, bonus_type)))
call SetUnitManaPercentBJ(u, p)
return SetUnitAbilityBonusI(u, MANA_ABILITY, MANA_FIELD, amount)
elseif bonus_type == BONUS_AGILITY then
return SetUnitAbilityBonusI(u, STATS_ABILITY, AGILITY_FIELD, amount)
elseif bonus_type == BONUS_STRENGTH then
return SetUnitAbilityBonusI(u, STATS_ABILITY, STRENGTH_FIELD, amount)
elseif bonus_type == BONUS_INTELLIGENCE then
return SetUnitAbilityBonusI(u, STATS_ABILITY, INTELLIGENCE_FIELD, amount)
elseif bonus_type == BONUS_MOVEMENT_SPEED then
return SetUnitAbilityBonusI(u, MOVEMENTSPEED_ABILITY, MOVEMENTSPEED_FIELD, amount)
elseif bonus_type == BONUS_SIGHT_RANGE then
call BlzSetUnitRealField(u, UNIT_RF_SIGHT_RADIUS, (BlzGetUnitRealField(u, UNIT_RF_SIGHT_RADIUS) + amount - Get(u, bonus_type)))
return SetUnitAbilityBonusI(u, SIGHT_RANGE_ABILITY, SIGHT_RANGE_FIELD, amount)
elseif bonus_type == BONUS_SPELL_POWER then
call SetSpellPowerBaseAmount(u, amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method Add takes unit u, integer bonus_type, integer amount returns integer
local integer current_amount = Get(u, bonus_type)
if amount > 0 and current_amount > 2147483647 - amount then
set amount = 2147483647 - current_amount
elseif amount < 0 and current_amount < -2147483648 - amount then
set amount = -2147483648 - current_amount
endif
call Set(u, bonus_type, (current_amount + amount))
return amount
endmethod
static method GetR takes unit u, integer bonus_type returns real
if bonus_type == BONUS_HEALTH_REGEN then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, HEALTHREGEN_ABILITY), HEALTHREGEN_FIELD, 0)
elseif bonus_type == BONUS_MANA_REGEN then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, MANAREGEN_ABILITY), MANAREGEN_FIELD, 0)
elseif bonus_type == BONUS_ATTACK_SPEED then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, ATTACKSPEED_ABILITY), ATTACKSPEED_FIELD, 0)
elseif bonus_type == BONUS_MAGIC_RESISTANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, MAGIC_RESISTANCE_ABILITY), MAGIC_RESISTANCE_FIELD, 0)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
return GetCriticalHitChance(u)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
return GetCriticalHitDamage(u)
elseif bonus_type == BONUS_EVASION_CHANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, EVASION_ABILITY), EVASION_FIELD, 0)
elseif bonus_type == BONUS_LIFE_STEAL then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, LIFE_STEAL_ABILITY), LIFE_STEAL_FIELD, 0)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION then
return GetUnitCooldownReduction(u)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION_FLAT then
return GetUnitCooldownReductionFlat(u)
elseif bonus_type == BONUS_COOLDOWN_OFFSET then
return GetUnitCooldownOffset(u)
elseif bonus_type == BONUS_SPELL_HASTE then
//Lol Formula for AH
//https://www.reddit.com/r/leagueoflegends/comments/i5m8m6/i_made_a_chart_to_convert_cdr_to_ability_haste/
return 100. * (1. / (1. - GetUnitCooldownReductionFlat(u)) - 1.0)
elseif bonus_type == BONUS_SPELL_POWER_MULTIPLIER then
return GetSpellPowerMultiplier(u)
elseif bonus_type == BONUS_ARMOR_PEN_FLAT then
return ArmorPenetrationFlat.Get(u)
elseif bonus_type == BONUS_ARMOR_PEN_PERCENT then
return ArmorPenetrationPercent.Get(u)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1.
endmethod
static method SetR takes unit u, integer bonus_type, real amount returns real
if bonus_type == BONUS_HEALTH_REGEN then
call SetUnitAbilityBonusR(u, HEALTHREGEN_ABILITY, HEALTHREGEN_FIELD, amount)
elseif bonus_type == BONUS_MANA_REGEN then
call SetUnitAbilityBonusR(u, MANAREGEN_ABILITY, MANAREGEN_FIELD, amount)
elseif bonus_type == BONUS_ATTACK_SPEED then
call SetUnitAbilityBonusR(u, ATTACKSPEED_ABILITY, ATTACKSPEED_FIELD, amount)
elseif bonus_type == BONUS_MAGIC_RESISTANCE then
call SetUnitAbilityBonusR(u, MAGIC_RESISTANCE_ABILITY, MAGIC_RESISTANCE_FIELD, amount)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
call SetCriticalHitChance(u, amount)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
call SetCriticalHitDamage(u, amount)
elseif bonus_type == BONUS_EVASION_CHANCE then
call SetUnitAbilityBonusR(u, EVASION_ABILITY, EVASION_FIELD, amount)
elseif bonus_type == BONUS_LIFE_STEAL then
call SetUnitAbilityBonusR(u, LIFE_STEAL_ABILITY, LIFE_STEAL_FIELD, amount)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION then
call SetUnitCooldownReduction(u, amount)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION_FLAT then
call SetUnitCooldownReductionFlat(u, amount)
elseif bonus_type == BONUS_COOLDOWN_OFFSET then
call SetUnitCooldownOffset(u, amount)
elseif bonus_type == BONUS_SPELL_HASTE then
//Lol Formula for AH
//https://www.reddit.com/r/leagueoflegends/comments/i5m8m6/i_made_a_chart_to_convert_cdr_to_ability_haste/
call SetUnitCooldownReductionFlat(u, 1. - 1. / (1. + amount / 100.))
elseif bonus_type == BONUS_SPELL_POWER_MULTIPLIER then
call SetSpellPowerMultiplier(u, amount)
elseif bonus_type == BONUS_ARMOR_PEN_FLAT then
return ArmorPenetrationFlat.Set(u, amount)
elseif bonus_type == BONUS_ARMOR_PEN_PERCENT then
return ArmorPenetrationPercent.Set(u, amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return amount
endmethod
static method AddR takes unit u, integer bonus_type, real amount returns real
if bonus_type >= BONUS_HEALTH_REGEN and bonus_type <= BONUS_ARMOR_PEN_PERCENT then
//if bonus_type == BONUS_SPELL_HASTE then
// call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Spell haste before=" + R2SW(GetR(u, bonus_type),0,1) + ", adding amount=" + R2SW(amount,0,1))
//endif
call SetR(u, bonus_type, GetR(u, bonus_type) + amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return amount
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* --------------------------------------------------------------------------
*/
function GetUnitBonus takes unit u, integer bonus_type returns real
if bonus_type < BONUS_HEALTH_REGEN then
return I2R(NewBonus.Get(u, bonus_type))
else
return NewBonus.GetR(u, bonus_type)
endif
endfunction
function SetUnitBonus takes unit u, integer bonus_type, real amount returns real
if bonus_type < BONUS_HEALTH_REGEN then
return I2R(NewBonus.Set(u, bonus_type, R2I(amount)))
else
return NewBonus.SetR(u, bonus_type, amount)
endif
endfunction
function RemoveUnitBonus takes unit u, integer bonus_type returns nothing
if bonus_type < BONUS_HEALTH_REGEN then
call NewBonus.Set(u, bonus_type, 0)
else
if bonus_type == BONUS_CRITICAL_DAMAGE then
call NewBonus.SetR(u, bonus_type, 1)
else
call NewBonus.SetR(u, bonus_type, 0)
endif
if bonus_type == BONUS_LIFE_STEAL then
call UnitRemoveAbility(u, LIFE_STEAL_ABILITY)
endif
endif
endfunction
function AddUnitBonus takes unit u, integer bonus_type, real amount returns real
if bonus_type < BONUS_HEALTH_REGEN then
return I2R(NewBonus.Add(u, bonus_type, R2I(amount)))
else
return NewBonus.AddR(u, bonus_type, amount)
endif
endfunction
endlibrary
library NewBonusUtils requires ExtendableBonusSystem, RegisterPlayerUnitEvent
/* ----------------------- NewBonusUtils v2.2 by Chopinski ----------------------- */
//! novjass
Required Library: RegisterPlayerUnitEvent -> www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
API:
function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
-> Add the specified amount for the specified bonus type for unit for a duration
-> Example: call AddUnitBonusTimed(GetTriggerUnit(), BONUS_ARMOR, 13, 10.5)
function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
-> Links the bonus amount specified to a buff or ability. As long as the unit has the buff or
-> the ability represented by the parameter buffId the bonus is not removed.
-> Example: call LinkBonusToBuff(GetTriggerUnit(), BONUS_ARMOR, 10, 'B000')
function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
-> Links the bonus amount specified to an item. As long as the unit has that item the bonus is not removed.
-> Note that it will work for items with the same id, because it takes as parameter the item object.
-> Example: call LinkBonusToItem(GetManipulatingUnit(), BONUS_ARMOR, 10, GetManipulatedItem())
function UnitCopyBonuses takes unit source, unit target returns nothing
-> Copy the source unit bonuses using the Add functionality to the target unit
-> Example: call UnitCopyBonuses(GetTriggerUnit(), GetSummonedUnit())
function UnitMirrorBonuses takes unit source, unit target returns nothing
-> Copy the source unit bonuses using the Set functionality to the target unit
-> Example: call UnitMirrorBonuses(GetTriggerUnit(), GetSummonedUnit())
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private struct NewBonusUtils
static timer timer = CreateTimer()
static integer key = -1
static thistype array array
static integer k = -1
static thistype array items
unit unit
item item
real ticks
integer type
integer buff
real amount
boolean link
method remove takes integer i, boolean isItem returns integer
call AddUnitBonus(unit, type, -amount)
if isItem then
set items[i] = items[k]
set k = k - 1
else
set array[i] = array[key]
set key = key - 1
if key == -1 then
call PauseTimer(timer)
endif
endif
set unit = null
set item = null
call deallocate()
return i - 1
endmethod
static method removeBonusTypeForItem takes item itm, integer bonusToRemove returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > k
set this = items[i]
if item == itm and type == bonusToRemove then
set i = remove(i, true)
endif
set i = i + 1
endloop
endmethod
static method onDrop takes nothing returns nothing
local item itm = GetManipulatedItem()
local integer i = 0
local thistype this
loop
exitwhen i > k
set this = items[i]
if item == itm then
set i = remove(i, true)
endif
set i = i + 1
endloop
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > key
set this = array[i]
if link then
set ticks = ticks - 1
if ticks <= 0 then
set i = remove(i, false)
endif
else
if GetUnitAbilityLevel(unit, buff) == 0 then
set i = remove(i, false)
endif
endif
set i = i + 1
endloop
endmethod
static method linkTimed takes unit u, integer bonus_type, real amount, real duration, boolean link returns nothing
local thistype this = thistype.allocate()
set this.unit = u
set this.type = bonus_type
set this.ticks = duration/0.03125000
set this.link = link
set this.amount = AddUnitBonus(u, bonus_type, amount)
set key = key + 1
set array[key] = this
if key == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
static method linkBuff takes unit u, integer bonus_type, real amount, integer buffId, boolean link returns nothing
local thistype this = thistype.allocate()
set this.unit = u
set this.type = bonus_type
set this.buff = buffId
set this.link = link
set this.amount = AddUnitBonus(u, bonus_type, amount)
set key = key + 1
set array[key] = this
if key == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
static method linkItem takes unit u, integer bonus_type, real amount, item i returns nothing
local thistype this = thistype.allocate()
set this.unit = u
set this.item = i
set this.type = bonus_type
set this.amount = AddUnitBonus(u, bonus_type, amount)
set k = k + 1
set items[k] = this
endmethod
static method copy takes unit source, unit target returns nothing
local integer i = 1
loop
exitwhen i > 17
if GetUnitBonus(source, i) != 0 then
call AddUnitBonus(target, i, GetUnitBonus(source, i))
endif
set i = i + 1
endloop
endmethod
static method mirror takes unit source, unit target returns nothing
local integer i = 1
loop
exitwhen i > 17
call SetUnitBonus(target, i, GetUnitBonus(source, i))
set i = i + 1
endloop
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* -------------------------------------------------------------------------- */
function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
call NewBonusUtils.linkTimed(u, bonus_type, amount, duration, true)
endfunction
function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
call NewBonusUtils.linkBuff(u, bonus_type, amount, buffId, false)
endfunction
function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
call NewBonusUtils.linkItem(u, bonus_type, amount, i)
endfunction
function RemoveSpecificBonusFromItem takes item i, integer bonus_type returns nothing
call NewBonusUtils.removeBonusTypeForItem(i, bonus_type)
endfunction
function UnitCopyBonuses takes unit source, unit target returns nothing
call NewBonusUtils.copy(source, target)
endfunction
function UnitMirrorBonuses takes unit source, unit target returns nothing
call NewBonusUtils.mirror(source, target)
endfunction
endlibrary
//TESH.scrollpos=205
//TESH.alwaysfold=0
function K2DItemCheckXY takes real x, real y returns boolean
call SetItemPosition(udg_K2DItem, x, y)
return GetWidgetX(udg_K2DItem) == x and GetWidgetY(udg_K2DItem) == y
endfunction
function K2DItemCheckAxis takes real x, real y returns boolean
local real x2 = x*udg_K2DRadius[udg_UDex]
local real y2 = y*udg_K2DRadius[udg_UDex]
set x = udg_K2DX + x2
set y = udg_K2DY + y2
if K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
set x = udg_K2DX - x2
set y = udg_K2DY - y2
return K2DItemCheckXY(x, y) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endif
return false
endfunction
function K2DItemCheck takes nothing returns boolean
local boolean result = K2DItemCheckXY(udg_K2DX, udg_K2DY)
//Only perform additional pathing checks if the unit has a larger collision.
if result and udg_Knockback2DRobustPathing > 0 and udg_K2DRadius[udg_UDex] > 0 then
//Check horizontal axis of unit to make sure nothing is going to collide
set result = K2DItemCheckAxis(udg_K2DCosH[udg_UDex], udg_K2DSinH[udg_UDex])
//Check vertical axis of unit to ensure nothing will collide
set result = result and K2DItemCheckAxis(udg_K2DCos[udg_UDex], udg_K2DSin[udg_UDex])
if result and udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
//Check diagonal axis of unit if more thorough pathing is desired
set result = K2DItemCheckAxis(udg_K2DCosD1[udg_UDex], udg_K2DSinD1[udg_UDex])
set result = result and K2DItemCheckAxis(udg_K2DCosD2[udg_UDex], udg_K2DSinD2[udg_UDex])
endif
endif
//Reset item so it won't interfere with the map
call SetItemPosition(udg_K2DItem, udg_K2DMaxX, udg_K2DMaxY)
call SetItemVisible(udg_K2DItem, false)
return result
endfunction
function K2DItemFilter takes nothing returns boolean
//Check for visible items, temporarily hide them and add them to the filter.
if IsItemVisible(GetFilterItem()) then
call SetItemVisible(GetFilterItem(), false)
return true
endif
return false
endfunction
function K2DItemCode takes nothing returns nothing
//Perform the item-pathing check only once, then unhide those filtered items
if not udg_K2DItemsFound then
set udg_K2DItemsFound = true
set udg_K2DItemOffset = K2DItemCheck()
endif
call SetItemVisible(GetEnumItem(), true)
endfunction
function K2DKillDest takes nothing returns nothing
local real x
local real y
//Handle destruction of debris
set bj_destRandomCurrentPick = GetEnumDestructable()
if GetWidgetLife(bj_destRandomCurrentPick) > 0.405 and IssueTargetOrder(udg_K2DDebrisKiller, udg_Knockback2DTreeOrDebris, bj_destRandomCurrentPick) then
set x = GetWidgetX(bj_destRandomCurrentPick) - udg_K2DX
set y = GetWidgetY(bj_destRandomCurrentPick) - udg_K2DY
if x*x + y*y <= udg_K2DDestRadius[udg_UDex] then
call KillDestructable(bj_destRandomCurrentPick)
endif
endif
endfunction
function K2DEnumDests takes nothing returns nothing
call MoveRectTo(udg_K2DRegion, udg_K2DX, udg_K2DY)
if udg_K2DKillTrees[udg_UDex] then
call SetUnitX(udg_K2DDebrisKiller, udg_K2DX)
call SetUnitY(udg_K2DDebrisKiller, udg_K2DY)
call EnumDestructablesInRect(udg_K2DRegion, null, function K2DKillDest)
endif
endfunction
function Knockback2DCheckXY takes real x, real y returns boolean
set udg_K2DX = x + udg_K2DVelocity[udg_UDex]*udg_K2DCos[udg_UDex]
set udg_K2DY = y + udg_K2DVelocity[udg_UDex]*udg_K2DSin[udg_UDex]
if udg_K2DSimple[udg_UDex] then
//A "pull" effect or a missile system does not require complex pathing.
if udg_K2DX <= udg_K2DMaxX and udg_K2DX >= udg_K2DMinX and udg_K2DY <= udg_K2DMaxY and udg_K2DY >= udg_K2DMinY then
call K2DEnumDests()
return true
endif
return false
elseif udg_K2DFlying[udg_UDex] then
return not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLYABILITY)
elseif not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_WALKABILITY) then
call K2DEnumDests()
set udg_K2DItemOffset = false
call EnumItemsInRect(udg_K2DRegion, Filter(function K2DItemFilter), function K2DItemCode)
if udg_K2DItemsFound then
//If items were found, the check was already performed.
set udg_K2DItemsFound = false
else
//Otherwise, perform the check right now.
set udg_K2DItemOffset = K2DItemCheck()
endif
return udg_K2DItemOffset
endif
return udg_K2DAmphibious[udg_UDex] and not IsTerrainPathable(udg_K2DX, udg_K2DY, PATHING_TYPE_FLOATABILITY)
endfunction
function Knockback2DApplyAngle takes real angle returns nothing
set angle = ModuloReal(angle, udg_Radians_Turn)
set udg_K2DCos[udg_UDex] = Cos(angle)
set udg_K2DSin[udg_UDex] = Sin(angle)
set udg_K2DAngle[udg_UDex] = angle
if udg_Knockback2DRobustPathing > 0 then
set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
set udg_K2DCosH[udg_UDex] = Cos(angle)
set udg_K2DSinH[udg_UDex] = Sin(angle)
if udg_Knockback2DRobustPathing == 2 and udg_K2DRadius[udg_UDex] > 16 then
set angle = ModuloReal(angle + udg_Radians_QuarterPi, udg_Radians_Turn)
set udg_K2DCosD1[udg_UDex] = Cos(angle)
set udg_K2DSinD1[udg_UDex] = Sin(angle)
set angle = ModuloReal(angle + udg_Radians_QuarterTurn, udg_Radians_Turn)
set udg_K2DCosD2[udg_UDex] = Cos(angle)
set udg_K2DSinD2[udg_UDex] = Sin(angle)
endif
endif
endfunction
function Knockback2DLooper takes nothing returns nothing
local integer i = 0
local unit u
local real x
local real y
call PauseUnit(udg_K2DDebrisKiller, false)
loop
set i = udg_K2DNext[i]
exitwhen i == 0
set udg_UDex = i
set udg_K2DTimeLeft[i] = udg_K2DTimeLeft[i] - udg_K2DTimeout
set udg_K2DDistanceLeft[i] = udg_K2DDistanceLeft[i] - udg_K2DVelocity[i]
set u = udg_UDexUnits[i]
if udg_K2DTimeLeft[i] > 0.00 then
if udg_K2DTimeLeft[i] < udg_K2DHeightThreshold[i] and udg_K2DHeightThreshold[i] != 0.00 then
call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)/udg_K2DHeightThreshold[i])
set udg_K2DHeightThreshold[i] = 0.00
endif
if udg_K2DPause[i] then
set x = udg_K2DLastX[i]
set y = udg_K2DLastY[i]
else
set x = GetUnitX(u)
set y = GetUnitY(u)
endif
if not Knockback2DCheckXY(x, y) then
if not udg_K2DFreeze[i] and IsTriggerEnabled(udg_K2DImpact[i]) and TriggerEvaluate(udg_K2DImpact[i]) then
call TriggerExecute(udg_K2DImpact[i])
endif
if udg_K2DBounce[i] then
call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_K2DAngle[i] + bj_PI)
if not Knockback2DCheckXY(x, y) then
call Knockback2DApplyAngle(udg_Radians_Turn - udg_K2DAngle[i])
set udg_K2DX = x
set udg_K2DY = y
endif
endif
else
set udg_K2DX = x
set udg_K2DY = y
set udg_K2DFreeze[i] = true
endif
endif
call SetUnitX(u, udg_K2DX)
call SetUnitY(u, udg_K2DY)
set udg_K2DLastX[i] = udg_K2DX
set udg_K2DLastY[i] = udg_K2DY
if udg_K2DFXModel[i] != "" then
set udg_K2DFXTimeLeft[i] = udg_K2DFXTimeLeft[i] - udg_K2DTimeout
if udg_K2DFXTimeLeft[i] <= 0.00 then
set udg_K2DFXTimeLeft[i] = udg_K2DFXRate[i]
if udg_K2DFlying[i] then
call DestroyEffect(AddSpecialEffectTarget(udg_K2DFXModel[i], u, "origin"))
else
call DestroyEffect(AddSpecialEffect(udg_K2DFXModel[i], udg_K2DX, udg_K2DY))
endif
endif
endif
if udg_K2DCollision[i] >= 0.00 then
set udg_Knockback2DSource = u
call GroupEnumUnitsInRange(bj_lastCreatedGroup, udg_K2DX, udg_K2DY, 200.00, null)
call GroupRemoveUnit(bj_lastCreatedGroup, u)
loop
set udg_Knockback2DUnit = FirstOfGroup(bj_lastCreatedGroup)
exitwhen udg_Knockback2DUnit == null
call GroupRemoveUnit(bj_lastCreatedGroup, udg_Knockback2DUnit)
if IsUnitInRange(udg_Knockback2DUnit, u, udg_K2DCollision[i]) and udg_K2DFlying[i] == IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_FLYING) and (not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_STRUCTURE)) and not IsUnitType(udg_Knockback2DUnit, UNIT_TYPE_DEAD) and (udg_K2DUnbiasedCollision[i] or IsUnitAlly(udg_Knockback2DUnit, GetOwningPlayer(u))) and TriggerEvaluate(gg_trg_Knockback_2D) then
set udg_Knockback2DAngle = bj_RADTODEG * Atan2(GetUnitY(udg_Knockback2DUnit) - udg_K2DY, GetUnitX(udg_Knockback2DUnit) - udg_K2DX)
set udg_Knockback2DDistance = udg_K2DDistanceLeft[i]
set udg_Knockback2DBounces = udg_K2DBounce[i]
set udg_Knockback2DCollision = udg_K2DCollision[i]
if udg_K2DHeight[i] != 0.00 then
set udg_Knockback2DHeight = GetUnitFlyHeight(u) - GetUnitDefaultFlyHeight(u)
endif
set udg_Knockback2DLoopFX = udg_K2DFXModel[i]
set udg_Knockback2DTime = udg_K2DTimeLeft[i]
set udg_Knockback2DUnbiasedCollision = udg_K2DUnbiasedCollision[i]
call TriggerExecute(gg_trg_Knockback_2D)
set udg_Knockback2DSource = u //in case of a recursive knockback
endif
endloop
endif
set udg_K2DVelocity[i] = udg_K2DVelocity[i] - udg_K2DFriction[i]
else
call TriggerExecute(gg_trg_Knockback_2D_Destroy)
endif
endloop
set u = null
//Disable dummy after the loop finishes so it doesn't interfere with the map
call PauseUnit(udg_K2DDebrisKiller, true)
endfunction
//===========================================================================
function StartKnockback2DTimer takes nothing returns nothing
call TimerStart(udg_K2DTimer, udg_K2DTimeout, true, function Knockback2DLooper)
endfunction
function InitTrig_Knockback_2D_System takes nothing returns nothing
endfunction
library CooldownReduction requires RegisterPlayerUnitEvent, Table, Alloc
/* ------------------ Cooldown Reduction v1.9 by Chopinski ------------------ */
// Intro
// This library intension in to introduce to warcraft an easy way to
// manipulate abilities cooldowns based on a cooldown reduction value that
// is unique for each unit.
// How it Works?
// When casting an ability, its "new" cooldown is calculated based on the
// amount of cooldown reduction of the casting unit. the formula for
// calculation is:
// Cooldown = (Default Cooldown - Cooldown Offset) * [(1 - source1)*(1 - source2)*...] * (1 - Cooldown Reduction Flat)
// The system also allow negative values for CDR, resulting in increased
// ability cooldown.
// It does not acumulate because the abilities are registered automatically
// on the first cast, saving its base cooldown (Object Editor values) and
// always using this base value for calculation, so you can still edit
// the ability via the editor and the system takes care of the rest.
// How to Import
// simply copy the CooldownReduction folder over to your map, and start
// use the API functions
// Requirements
// CooldownReduction requires RegisterPlayerUnitEvent, Alloc and a Unit Indexer.
// Credits to Magtheridon96 for RegisterPlayerUnitEvent and to Bribe for
// the UnitIndexer. It also requires patch 1.31+.
// RegisterPlayerUnitEvent: www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
// UnitIndexer: www.hiveworkshop.com/threads/gui-unit-indexer-1-4-0-0.197329/#resource-45899
// Alloc: www.hiveworkshop.com/threads/snippet-alloc.192348/
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* Configuration */
/* --------------------------------------------------------------------------
*/
// Use this function to filter out units you dont want to have abilities registered.
// By default dummy units do not trigger the system.
private function UnitFilter takes unit source returns boolean
return GetUnitAbilityLevel(source, 'Aloc') == 0
endfunction
private function AbilityFilter takes integer abilityId returns boolean
return abilityId != 'A04I'
endfunction
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
private module List
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method push takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
private struct AbilityList extends array
implement Alloc
implement List
unit unit
ability ability
Table defaults
integer id
integer levels
method destroy takes nothing returns nothing
local thistype node = this.next
loop
exitwhen node == this
set node.ability = null
call node.defaults.destroy()
call node.pop()
call node.deallocate()
set node = node.next
endloop
call deallocate()
set unit = null
endmethod
method insert takes integer id returns thistype
local thistype node = push(allocate())
local integer i = 0
set node.id = id
set node.ability = BlzGetUnitAbility(unit, id)
set node.levels = BlzGetAbilityIntegerField(node.ability, ABILITY_IF_LEVELS)
set node.defaults = Table.create()
loop
exitwhen i >= node.levels
set node.defaults.real[i] = BlzGetAbilityRealLevelField(node.ability, ABILITY_RLF_COOLDOWN, i)
set i = i + 1
endloop
return node
endmethod
method update takes integer count, real normal, real flat, real offset returns nothing
local thistype node = this.next
local real cooldown
local integer i
loop
exitwhen node == this
set i = 0
loop
exitwhen i >= node.levels
if count > 0 then
set cooldown = ((node.defaults.real[i] - offset) * normal * (1 - flat))
else
set cooldown = ((node.defaults.real[i] - offset) * (1 - flat))
endif
call BlzSetAbilityRealLevelField(node.ability, ABILITY_RLF_COOLDOWN, i, cooldown)
call IncUnitAbilityLevel(unit, node.id)
call DecUnitAbilityLevel(unit, node.id)
set i = i + 1
endloop
set node = node.next
endloop
endmethod
method calculate takes integer id, integer level, real cooldown, integer count, real normal, real flat, real offset returns nothing
if count > 0 then
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, id), ABILITY_RLF_COOLDOWN, level, ((cooldown - offset) * normal * (1 - flat)))
else
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, id), ABILITY_RLF_COOLDOWN, level, ((cooldown - offset) * (1 - flat)))
endif
call IncUnitAbilityLevel(unit, id)
call DecUnitAbilityLevel(unit, id)
endmethod
method simulate takes real cooldown, integer count, real normal, real flat, real offset returns real
local real cd
if count > 0 then
set cd = ((cooldown - offset) * normal * (1 - flat))
else
set cd = ((cooldown - offset) * (1 - flat))
endif
return cd
endmethod
static method create takes unit source returns thistype
local thistype this = thistype(allocate()).init()
set unit = source
return this
endmethod
endstruct
struct CDR
readonly static hashtable hashtable = InitHashtable()
private static AbilityList array n
private static integer array count
readonly static real array normal
readonly static real array flat
readonly static real array offset
private static method getInstance takes unit source returns AbilityList
local integer i = GetUnitUserData(source)
if n[i] == 0 then
set n[i] = AbilityList.create(source)
endif
return n[i]
endmethod
private static method update takes unit u returns nothing
local integer id = GetUnitUserData(u)
local AbilityList list = getInstance(u)
call list.update(count[id], normal[id], flat[id], offset[id])
endmethod
private static method calculate takes unit u returns real
local integer idx = GetUnitUserData(u)
local integer id = GetHandleId(u)
local integer i = 0
local real cdr = 0
local real aux
loop
exitwhen i > count[idx]
set aux = LoadReal(hashtable, id, i)
if i > 0 then
set cdr = cdr * (1 - aux)
else
set cdr = 1 - aux
endif
set i = i + 1
endloop
return cdr
endmethod
static method get takes unit u, integer types returns real
if types == 0 then
return normal[GetUnitUserData(u)]
elseif types == 1 then
return flat[GetUnitUserData(u)]
else
return offset[GetUnitUserData(u)]
endif
endmethod
static method Set takes unit u, real value, integer types returns nothing
if types == 0 then
set normal[GetUnitUserData(u)] = value
elseif types == 1 then
set flat[GetUnitUserData(u)] = value
else
set offset[GetUnitUserData(u)] = value
endif
call update(u)
endmethod
static method add takes unit u, real amount returns nothing
local integer i = GetUnitUserData(u)
if amount != 0 then
call SaveReal(hashtable, GetHandleId(u), count[i], amount)
set normal[i] = calculate(u)
set count[i] = count[i] + 1
call update(u)
endif
endmethod
static method remove takes unit u, real amount returns nothing
local integer idx = GetUnitUserData(u)
local integer id = GetHandleId(u)
local integer i = 0
local real aux
if amount == 0 then
return
endif
loop
exitwhen i > count[idx] - 1
set aux = LoadReal(hashtable, id, i)
if aux == amount then
call RemoveSavedReal(hashtable, id, i)
if i != count[idx] - 1 then
set aux = LoadReal(hashtable, id, count[idx] - 1)
call SaveReal(hashtable, id, i, aux)
call RemoveSavedReal(hashtable, id, count[idx] - 1)
endif
set count[idx] = count[idx] - 1
set normal[idx] = calculate(u)
set i = count[idx] + 1
call update(u)
else
set i = i + 1
endif
endloop
endmethod
static method calculateCooldown takes unit u, integer id, integer level, real cooldown returns nothing
local integer i = GetUnitUserData(u)
local AbilityList list = getInstance(u)
call list.calculate(id, level - 1, cooldown, count[i], normal[i], flat[i], offset[i])
endmethod
static method simulateCooldown takes unit u, real cooldown returns real
local integer i = GetUnitUserData(u)
local AbilityList list = getInstance(u)
return list.simulate(cooldown, count[i], normal[i], flat[i], offset[i])
endmethod
static method register takes unit u, integer id returns nothing
local AbilityList list
local integer i
if UnitFilter(u) and AbilityFilter(id) then
set list = getInstance(u)
set i = GetUnitUserData(u)
if not LoadBoolean(hashtable, list, id) then
call list.insert(id)
call SaveBoolean(hashtable, list, id, true)
if count[i] > 0 or normal[i] != 0 or flat[i] != 0 or offset[i] != 0 then
call update(u)
endif
endif
endif
endmethod
private static method onCast takes nothing returns nothing
call register(GetTriggerUnit(), GetSpellAbilityId())
endmethod
private static method onLevel takes nothing returns nothing
call register(GetTriggerUnit(), GetLearnedSkill())
endmethod
private static method onDeindex takes nothing returns nothing
local unit source = udg_UDexUnits[udg_UDex]
local integer i = GetUnitUserData(source)
local AbilityList list = getInstance(source)
set n[i] = 0
set normal[i] = 0
set flat[i] = 0
set offset[i] = 0
set count[i] = 0
if list != 0 then
call list.destroy()
call FlushChildHashtable(hashtable, list)
endif
call FlushChildHashtable(hashtable, GetHandleId(source))
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Condition(function thistype.onDeindex))
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function thistype.onLevel)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* --------------------------------------------------------------------------
*/
function GetUnitCooldownReduction takes unit u returns real
return 1 - CDR.get(u, 0)
endfunction
function GetUnitCooldownReductionFlat takes unit u returns real
return CDR.get(u, 1)
endfunction
function GetUnitCooldownOffset takes unit u returns real
return CDR.get(u, 2)
endfunction
function SetUnitCooldownReduction takes unit u, real value returns nothing
call CDR.Set(u, value, 0)
endfunction
function SetUnitCooldownReductionFlat takes unit u, real value returns nothing
call CDR.Set(u, value, 1)
endfunction
function SetUnitCooldownOffset takes unit u, real value returns nothing
call CDR.Set(u, value, 2)
endfunction
function UnitAddCooldownReduction takes unit u, real value returns nothing
call CDR.add(u, value)
endfunction
function UnitAddCooldownReductionFlat takes unit u, real value returns nothing
call CDR.Set(u, CDR.get(u, 1) + value, 1)
endfunction
function UnitAddCooldownOffset takes unit u, real value returns nothing
call CDR.Set(u, CDR.get(u, 2) + value, 2)
endfunction
function UnitRemoveCooldownReduction takes unit u, real value returns nothing
call CDR.remove(u, value)
endfunction
function CalculateAbilityCooldown takes unit u, integer id, integer level, real cooldown returns nothing
call CDR.calculateCooldown(u, id, level, cooldown)
endfunction
function SimulateAbilityCooldown takes unit u, real cooldown returns real
return CDR.simulateCooldown(u, cooldown)
endfunction
function RegisterAbility takes unit u, integer id returns nothing
call CDR.register(u, id)
endfunction
endlibrary
library CooldownReductionUtils requires CooldownReduction
/* --------------- Cooldown Reduction Utils v1.8 by Chopinski --------------- */
// Intro
// Utility Library that include a few extra functions to deal with
// Cooldown Reduction
// JASS API
// function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
// -> Add to the amount of cdr of a unit for a given duration. Accepts positive and negative values.
// -> It handles removing the bonus automatically
// function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
// -> Add to the amount of cdr flat of a unit for a given period. Accepts positive and negative values.
// -> It handles removing the bonus automatically
// function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
// -> Add to the amount of cdr offset of a unit for a given period. Accepts positive and negative values.
// -> It handles removing the bonus automatically
// function GetUnitCooldownReductionEx takes unit u returns string
// -> Returns the amount of cdr a unit has as a string factored by 100
// -> example of return: 10.50 -> 0.105 internally.
// function GetUnitCooldownReductionFlatEx takes unit u returns string
// -> Returns the amount of cdr flat a unit has as a string factored by 100
// -> example of return: 10.50 -> 0.105 internally.
// function GetUnitCooldownOffsetEx takes unit u returns string
// -> Returns the amount of cdr offset a unit has as a string
// function GetAbilityTable takes nothing returns hashtable
// -> Returns the hashtable that holds the units default cooldown reduction values.
// -> Use with caution! you might break stuff
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private struct CDRUtils extends CDR
static timer t = CreateTimer()
//----------------------------------------------
static integer didx = -1
static thistype array data
//----------------------------------------------
unit u
real ticks
real amount
integer tipo
method destroy takes nothing returns nothing
if didx == -1 then
call PauseTimer(t)
endif
set .u = null
set .ticks = 0
call .deallocate()
endmethod
private static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
set .ticks = .ticks - 1
if .ticks <= 0 then
if .tipo == 0 then
call remove(.u, .amount)
elseif .tipo == 1 then
call Set(.u, get(.u, 1) - .amount, 1)
else
call Set(.u, get(.u, 2) - .amount, 2)
endif
set data[i] = data[didx]
set didx = didx - 1
set i = i - 1
call .destroy()
endif
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real amount, real duration, integer tipo returns nothing
local thistype this = thistype.allocate()
set .u = u
set .amount = amount
set .tipo = tipo
set .ticks = duration/0.03125000
set didx = didx + 1
set data[didx] = this
if tipo == 0 then
call add(u, amount)
elseif tipo == 1 then
call Set(u, get(u, 1) + amount, 1)
else
call Set(u, get(u, 2) + amount, 2)
endif
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* -------------------------------------------------------------------------- */
function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
call CDRUtils.addTimed(u, value, duration, 0)
endfunction
function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
call CDRUtils.addTimed(u, value, duration, 1)
endfunction
function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
call CDRUtils.addTimed(u, value, duration, 2)
endfunction
function GetUnitCooldownReductionEx takes unit u returns string
return R2SW(CDRUtils.get(u, 0)*100, 1, 2)
endfunction
function GetUnitCooldownReductionFlatEx takes unit u returns string
return R2SW(CDRUtils.get(u, 1)*100, 1, 2)
endfunction
function GetUnitCooldownOffsetEx takes unit u returns string
return R2SW(CDRUtils.get(u, 2), 1, 2)
endfunction
function GetAbilityTable takes nothing returns hashtable
return CDR.hashtable
endfunction
endlibrary
scope MissilesW
private struct Missile extends Missiles
method onHit takes unit hit returns boolean
if hit != .source and UnitAlive(hit) then
call KillUnit(hit)
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
call KillDestructable(dest)
return false
endmethod
endstruct
private struct Test
timer t
unit c
real fx
real fy
real fz
real tx
real ty
integer count
integer i = 1
private static method GetRandomRange takes real radius returns real
local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
if r > 1 then
return (2 - r)*radius
endif
return r*radius
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real maxRange
local real theta
local real radius
local real toX
local real toY
local Missile missile
if count > 0 then
set i = -i
set count = count - 1
set theta = 2*bj_PI*GetRandomReal(0, 1)
set radius = GetRandomRange(350)
set toX = tx + radius*Cos(theta)
set toY = ty + radius*Sin(theta)
set fx = GetUnitX(c)
set fy = GetUnitY(c)
set missile = Missile.create(fx, fy, fz, toX, toY, 0)
set missile.source = c
set missile.model = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
set missile.speed = 800
set missile.collision = 75
set missile.arc = GetRandomReal(40, 45)
set missile.curve = GetRandomReal(5, 20)*i
call missile.launch()
else
call ReleaseTimer(t)
set t = null
set c = null
call deallocate()
endif
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set t = NewTimerEx(this)
set c = GetTriggerUnit()
set fx = GetUnitX(c)
set fy = GetUnitY(c)
set fz = GetUnitFlyHeight(c) + 50
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set count = 50
call TimerStart(.t, 0.1, true, function thistype.onLoop)
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A000', function thistype.onCast)
endmethod
endstruct
endscope
library Missiles requires MissileEffect, TimerUtils, WorldBounds
/* ----------------------- Missiles v2.6 by Chopinski -----------------------
*/
// Thanks and Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits and thanks to AGD and for the effect orientation ideas.
// This version of Missiles requires patch 1.31+
// How to Import:
// 1 - Copy this, MissileEffect and optionaly the MissileUtils libraries to your map
/* ----------------------------------- END ----------------------------------
*/
/* --------------------------------------------------------------------------
*/
/* System */
/* --------------------------------------------------------------------------
*/
globals
// The update period of the system
public constant real PERIOD = 1./40.
// The max amount of Missiles processed in a PERIOD
// You can play around with both these values to find
// your sweet spot. If equal to 0, the system will
// process all missiles at once every period.
public constant real SWEET_SPOT = 150
// the avarage collision size compensation when detecting collisions
private constant real COLLISION_SIZE = 128.
// item size used in z collision
private constant real ITEM_SIZE = 16.
// Raw code of the dummy unit used for vision
private constant integer DUMMY = 'dumi'
// Needed, don't touch.
private location LOC = Location(0., 0.)
endglobals
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onCliff takes nothing returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onTileset takes integer tileset returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onBoundaries takes nothing returns boolean defaults false
method onPause takes nothing returns boolean defaults false
method onResume takes nothing returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
private function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
private function GetUnitZ takes unit u returns real
return GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
endfunction
private function SetUnitZ takes unit u, real z returns nothing
call SetUnitFlyHeight(u, z - GetLocZ(GetUnitX(u), GetUnitY(u)), 0)
endfunction
private function GetMapCliffLevel takes nothing returns integer
return GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY)
endfunction
private struct Pool
private static player player = Player(PLAYER_NEUTRAL_PASSIVE)
private static group group = CreateGroup()
timer timer
unit unit
static method recycle takes unit dummy returns nothing
if GetUnitTypeId(dummy) == DUMMY then
call GroupAddUnit(group, dummy)
call SetUnitX(dummy, WorldBounds.maxX)
call SetUnitY(dummy, WorldBounds.maxY)
call SetUnitOwner(dummy, player, false)
call PauseUnit(dummy, true)
endif
endmethod
static method retrieve takes real x, real y, real z, real face returns unit
if BlzGroupGetSize(group) > 0 then
set bj_lastCreatedUnit = FirstOfGroup(group)
call PauseUnit(bj_lastCreatedUnit, false)
call GroupRemoveUnit(group, bj_lastCreatedUnit)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitZ(bj_lastCreatedUnit, z)
//call BlzSetUnitFacingEx(bj_lastCreatedUnit, face)
else
set bj_lastCreatedUnit = CreateUnit(player, DUMMY, x, y, face)
call SetUnitZ(bj_lastCreatedUnit, z)
call UnitRemoveAbility(bj_lastCreatedUnit, 'Amrf')
endif
return bj_lastCreatedUnit
endmethod
private static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call recycle(unit)
call ReleaseTimer(timer)
set timer = null
set unit = null
call deallocate()
endmethod
static method recycleTimed takes unit dummy, real delay returns nothing
local thistype this
if GetUnitTypeId(dummy) != DUMMY then
debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
else
set this = thistype.allocate()
set timer = NewTimerEx(this)
set unit = dummy
call TimerStart(timer, delay, false, function thistype.onExpire)
endif
endmethod
private static method onInit takes nothing returns nothing
local integer i = 0
local unit u
loop
exitwhen i == SWEET_SPOT
set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
call PauseUnit(u, false)
call GroupAddUnit(group, u)
call UnitRemoveAbility(u, 'Amrf')
set i = i + 1
endloop
set u = null
endmethod
endstruct
private struct Coordinates
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z returns Coordinates
local thistype this = thistype.allocate()
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
/* --------------------------------------------------------------------------
*/
/* System */
/* --------------------------------------------------------------------------
*/
private module OnHit
set o = origin
set h = height
set c = open
set d = o.distance
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(group, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(group)
exitwhen u == null
if not HaveSavedBoolean(table, this, GetHandleId(u)) then
if IsUnitInRangeXY(u, x, y, collision) then
if collideZ then
set dx = GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u)
set dy = BlzGetUnitCollisionSize(u)
if dx + dy >= z - collision and dx <= z + collision then
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
else
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
endif
endif
call GroupRemoveUnit(group, u)
endloop
endif
endif
endmodule
private module OnMissile
if .onMissile.exists then
if allocated and collision > 0 then
set k = 0
loop
exitwhen k > count
set missile = collection[k]
if missile != this then
if not HaveSavedBoolean(table, this, missile) then
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= collision then
call SaveBoolean(table, this, missile, true)
if allocated and .onMissile(missile) then
call terminate()
exitwhen true
endif
endif
endif
endif
set k = k + 1
endloop
endif
endif
endmodule
private module OnDestructable
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision + 64.0
call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(rect, null, function thistype.onDest)
endif
endif
endmodule
private module OnItem
if .onItem.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(rect, x - dx, y - dx, x + dx, y + dx)
call EnumItemsInRect(rect, null, function thistype.onItems)
endif
endif
endmodule
private module OnCliff
if .onCliff.exists then
set dx = GetTerrainCliffLevel(nextX, nextY)
set dy = GetTerrainCliffLevel(x, y)
if dy < dx and z < (dx - GetMapCliffLevel())*bj_CLIFFHEIGHT then
if allocated and .onCliff() then
call terminate()
endif
endif
endif
endmodule
private module OnTerrain
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
endmodule
private module OnTileset
if .onTileset.exists then
set k = GetTerrainType(x, y)
if k != tileset then
if allocated and .onTileset(k) then
call terminate()
endif
endif
set tileset = k
endif
endmodule
private module OnPeriod
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
endmodule
private module OnOrient
// Homing or not
set u = target
if u != null and GetUnitTypeId(u) != 0 then
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + toZ)
set dx = impact.x - nextX
set dy = impact.y - nextY
set a = Atan2(dy, dx)
set travel = o.distance - SquareRoot(dx*dx + dy*dy)
else
set a = o.angle
set target = null
endif
// turn rate
if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
if Sin(a-cA) >= 0 then
set cA = cA + turn
else
set cA = cA - turn
endif
else
set cA = a
endif
set vel = veloc*dilation
set yaw = cA
set s = travel + vel
set veloc = veloc + acceleration
set travel = s
set pitch = o.alpha
set prevX = x
set prevY = y
set prevZ = z
set x = nextX
set y = nextY
set z = nextZ
set nextX = x + vel*Cos(yaw)
set nextY = y + vel*Sin(yaw)
// arc calculation
if h != 0 or o.slope != 0 then
set nextZ = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = yaw + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = yaw + Atan(-((4*c)*(2*s - d))/(d*d))
endif
endmodule
private module OnFinish
if s >= d - 0.0001 then
set finished = true
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
//else
// if travel > 0 and not paused then
// call terminate()
// endif
endif
else
call terminate()
endif
else
if not roll then
call effect.orient(yaw, -pitch, 0)
else
call effect.orient(yaw, -pitch, Atan2(c, h))
endif
endif
endmodule
private module OnBoundaries
if not effect.move(x, y, z) then
if .onBoundaries.exists then
if allocated and .onBoundaries() then
call terminate()
endif
endif
else
if dummy != null then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
endif
endif
endmodule
private module OnPause
set pid = pid + 1
set pkey = pid
set frozen[pid] = this
if .onPause.exists then
if allocated and .onPause() then
call terminate()
endif
endif
endmodule
private module OnResume
local thistype aux
set paused = flag
if not paused and pkey != -1 then
set id = id + 1
set missiles[id] = this
set aux = frozen[pid]
set aux.pkey = pkey
set frozen[pkey] = frozen[pid]
set pid = pid - 1
set pkey = -1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if id == 0 then
call TimerStart(timer, PERIOD, true, function thistype.move)
endif
if .onResume.exists then
if allocated and .onResume() then
call terminate()
else
if finished then
call terminate()
endif
endif
else
if finished then
call terminate()
endif
endif
endif
endmodule
private module OnRemove
local thistype aux
if allocated and launched then
set allocated = false
if pkey != -1 then
set aux = frozen[pid]
set aux.pkey = pkey
set frozen[pkey] = frozen[pid]
set pid = pid - 1
set pkey = -1
endif
if .onRemove.exists then
call .onRemove()
endif
if dummy != null then
call Pool.recycle(dummy)
endif
set aux = collection[count]
set aux.index = index
set collection[index] = collection[count]
set count = count - 1
set index = -1
call origin.destroy()
call impact.destroy()
call effect.destroy()
call reset()
call FlushChildHashtable(table, this)
endif
endmodule
private module Operators
/* -------------------------- Model of the missile --------------------------
*/
method operator model= takes string fx returns nothing
call DestroyEffect(effect.effect)
set effect.path = fx
set effect.effect = AddSpecialEffect(fx, origin.x, origin.y)
call BlzSetSpecialEffectZ(effect.effect, origin.z)
call BlzSetSpecialEffectYaw(effect.effect, cA)
endmethod
method operator model takes nothing returns string
return effect.path
endmethod
/* ----------------------------- Curved movement ----------------------------
*/
method operator curve= takes real value returns nothing
set open = Tan(value*bj_DEGTORAD)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)*bj_RADTODEG
endmethod
/* ----------------------------- Arced Movement -----------------------------
*/
method operator arc= takes real value returns nothing
set height = Tan(value*bj_DEGTORAD)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)*bj_RADTODEG
endmethod
/* ------------------------------ Effect scale ------------------------------
*/
method operator scale= takes real value returns nothing
set effect.size = value
call effect.scale(effect.effect, value)
endmethod
method operator scale takes nothing returns real
return effect.size
endmethod
/* ------------------------------ Missile Speed -----------------------------
*/
method operator speed= takes real newspeed returns nothing
local real d = origin.distance
local real s
local real vel
set veloc = newspeed*PERIOD
set vel = veloc*dilation
set s = travel + vel
set nextX = x + vel*Cos(cA)
set nextY = y + vel*Sin(cA)
if height != 0 or origin.slope != 0 then
set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
set z = nextZ
endif
endmethod
method operator speed takes nothing returns real
return veloc/PERIOD
endmethod
/* ------------------------------- Flight Time ------------------------------
*/
method operator duration= takes real flightTime returns nothing
local real d = origin.distance
local real s
local real vel
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
set time = flightTime
set vel = veloc*dilation
set s = travel + vel
set nextX = x + vel*Cos(cA)
set nextY = y + vel*Sin(cA)
if height != 0 or origin.slope != 0 then
set nextZ = 4*height*s*(d-s)/(d*d) + origin.slope*s + origin.z
set z = nextZ
endif
endmethod
method operator duration takes nothing returns real
return time
endmethod
/* ------------------------------- Sight Range ------------------------------
*/
method operator vision= takes real sightRange returns nothing
set sight = sightRange
if dummy == null then
if owner == null then
if source != null then
set dummy = Pool.retrieve(x, y, z, 0)
call SetUnitOwner(dummy, GetOwningPlayer(source), false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
else
set dummy = Pool.retrieve(x, y, z, 0)
call SetUnitOwner(dummy, owner, false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
else
call SetUnitOwner(dummy, owner, false)
call BlzSetUnitRealField(dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
endif
endmethod
method operator vision takes nothing returns real
return sight
endmethod
/* ------------------------------- Time Scale -------------------------------
*/
method operator timeScale= takes real newTimeScale returns nothing
set effect.timeScale = newTimeScale
endmethod
method operator timeScale takes nothing returns real
return effect.timeScale
endmethod
/* ---------------------------------- Alpha ---------------------------------
*/
method operator alpha= takes integer newAlpha returns nothing
set effect.alpha = newAlpha
endmethod
method operator alpha takes nothing returns integer
return effect.alpha
endmethod
/* ------------------------------ Player Color ------------------------------
*/
method operator playerColor= takes integer playerId returns nothing
set effect.playerColor = playerId
endmethod
method operator playerColor takes nothing returns integer
return effect.playerColor
endmethod
/* -------------------------------- Animation -------------------------------
*/
method operator animation= takes integer animType returns nothing
set effect.animation = animType
endmethod
method operator animation takes nothing returns integer
return effect.animation
endmethod
endmodule
private module Methods
/* --------------------------- Bounce and Deflect ---------------------------
*/
method bounce takes nothing returns nothing
call origin.move(x, y, z - GetLocZ(x, y))
set travel = 0
set finished = false
endmethod
method deflect takes real tx, real ty, real tz returns nothing
local real locZ = GetLocZ(x, y)
set target = null
set toZ = tz
if z < locZ then
set nextX = prevX
set nextY = prevY
set nextZ = prevZ
endif
call impact.move(tx, ty, tz)
call origin.move(x, y, z - locZ)
set travel = 0
set finished = false
endmethod
method deflectTarget takes unit u returns nothing
call deflect(GetUnitX(u), GetUnitY(u), toZ)
set target = u
endmethod
/* ---------------------------- Flush hit targets ---------------------------
*/
method flushAll takes nothing returns nothing
call FlushChildHashtable(table, this)
endmethod
method flush takes widget w returns nothing
if w != null then
call RemoveSavedBoolean(table, this, GetHandleId(w))
endif
endmethod
method hitted takes widget w returns boolean
return HaveSavedBoolean(table, this, GetHandleId(w))
endmethod
/* ----------------------- Missile attachment methods -----------------------
*/
method attach takes string model, real dx, real dy, real dz, real scale returns effect
return effect.attach(model, dx, dy, dz, scale)
endmethod
method detach takes effect attachment returns nothing
if attachment != null then
call effect.detach(attachment)
endif
endmethod
/* ------------------------------ Missile Pause -----------------------------
*/
method pause takes boolean flag returns nothing
implement OnResume
endmethod
/* ---------------------------------- Color ---------------------------------
*/
method color takes integer red, integer green, integer blue returns nothing
call effect.setColor(red, green, blue)
endmethod
/* ---------------------- Destructable collision method ---------------------
*/
static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
local real dz
local real tz
local real dx
local real dy
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
set dx = GetWidgetX(d) - .x
set dy = GetWidgetY(d) - .y
if SquareRoot(dx * dx + dy * dy) < GetDestructableLife(d) + .collision then
if collideZ then
set dz = GetLocZ(GetWidgetX(d), GetWidgetY(d))
set tz = GetDestructableOccluderHeight(d)
if dz + tz >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
endif
endif
set d = null
endmethod
/* -------------------------- Item collision method -------------------------
*/
static method onItems takes nothing returns nothing
local thistype this = temp
local item i = GetEnumItem()
local real dz
if not HaveSavedBoolean(table, this, GetHandleId(i)) then
if collideZ then
set dz = GetLocZ(GetItemX(i), GetItemY(i))
if dz + ITEM_SIZE >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
endif
set i = null
endmethod
/* -------------------------------- Terminate -------------------------------
*/
method terminate takes nothing returns nothing
implement OnRemove
endmethod
endmodule
struct Missiles extends MissileEvents
private static timer timer = CreateTimer()
private static group group = CreateGroup()
private static rect rect = Rect(0., 0., 0., 0.)
private static hashtable table = InitHashtable()
private static integer last = 0
private static thistype temp = 0
private static integer id = -1
private static integer pid = -1
private static thistype array missiles
private static thistype array frozen
private static real dilation = 1
readonly static thistype array collection
readonly static integer count = -1
private real cA
private real height
private real open
private real toZ
private real time
private real sight
private unit dummy
private integer pkey
private integer index
Coordinates impact
Coordinates origin
MissileEffect effect
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real nextX
readonly real nextY
readonly real nextZ
real turn
readonly real veloc
readonly real travel
readonly boolean launched
readonly boolean allocated
readonly boolean finished
readonly boolean paused
readonly integer tileset
unit source
unit target
player owner
boolean collideZ
real collision
real damage
real acceleration
integer data
integer type
boolean roll
implement Operators
implement Methods
/* ------------------------------ Reset members -----------------------------
*/
private method reset takes nothing returns nothing
set launched = false
set finished = false
set collideZ = false
set paused = false
set roll = false
set source = null
set target = null
set owner = null
set dummy = null
set open = 0.
set height = 0.
set veloc = 0.
set acceleration = 0.
set collision = 0.
set damage = 0.
set travel = 0.
set turn = 0.
set time = 0.
set sight = 0.
set data = 0
set type = 0
set tileset = 0
set pkey = -1
set index = -1
endmethod
/* -------------------------- Destroys the missile --------------------------
*/
private method remove takes integer i returns integer
if paused then
implement OnPause
else
implement OnRemove
endif
set missiles[i] = missiles[id]
set id = id - 1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1
endif
if id == -1 then
call PauseTimer(timer)
endif
if not allocated then
call deallocate()
endif
return i - 1
endmethod
/* ---------------------------- Missiles movement ---------------------------
*/
private static method move takes nothing returns nothing
local integer j = 0
local integer i
local integer k
local unit u
local real a
local real d
local real s
local real h
local real c
local real dx
local real dy
local real vel
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
if SWEET_SPOT > 0 then
set i = last
else
set i = 0
endif
loop
exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > id)
set this = missiles[i]
set temp = this
if allocated and not paused then
implement OnHit
implement OnMissile
implement OnDestructable
implement OnItem
implement OnCliff
implement OnTerrain
implement OnTileset
implement OnPeriod
implement OnOrient
implement OnFinish
implement OnBoundaries
else
set i = remove(i)
set j = j - 1
endif
set i = i + 1
set j = j + 1
if i > id and SWEET_SPOT > 0 then
set i = 0
endif
endloop
set last = i
set u = null
endmethod
/* --------------------------- Launch the Missile ---------------------------
*/
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set id = id + 1
set missiles[id] = this
set count = count + 1
set index = count
set collection[count] = this
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (id + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if id == 0 then
call TimerStart(timer, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* --------------------------- Main Creator method --------------------------
*/
static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
local thistype this = thistype.allocate()
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .effect = MissileEffect.create(x, y, origin.z)
call Coordinates.link(origin, impact)
set .allocated = true
set .cA = origin.angle
set .x = x
set .y = y
set .z = impact.z
set .prevX = x
set .prevY = y
set .prevZ = impact.z
set .nextX = x
set .nextY = y
set .nextZ = impact.z
set .toZ = toZ
return this
endmethod
endstruct
endlibrary
library MissileEffect requires WorldBounds, Alloc
/* -------------------- Missile Effect v2.6 by Chopinski -------------------- */
// This is a simple helper library for the Relativistic Missiles system.
// Credits:
// Sevion for the Alloc module
// - www.hiveworkshop.com/threads/snippet-alloc.192348/
// Nestharus for World Bounds Library
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private module LinkedList
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method pushBack takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pushFront takes thistype node returns thistype
set node.prev = this
set node.next = next
set next.prev = node
set next = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
private struct Effect extends array
implement LinkedList
implement Alloc
real x
real y
real z
real size
real yaw
real pitch
real roll
string path
effect effect
method remove takes nothing returns nothing
call DestroyEffect(effect)
call pop()
call deallocate()
set effect = null
endmethod
method insert takes string fxpath, real x, real y, real z, real scale returns thistype
local thistype node = pushBack(allocate())
set node.x = x
set node.y = y
set node.z = z
set node.yaw = 0.
set node.pitch = 0.
set node.roll = 0.
set node.path = fxpath
set node.size = scale
set node.effect = AddSpecialEffect(fxpath, x, y)
call BlzSetSpecialEffectZ(node.effect, z)
call BlzSetSpecialEffectScale(node.effect, scale)
return node
endmethod
static method create takes nothing returns thistype
return thistype(allocate()).init()
endmethod
endstruct
struct MissileEffect
real size
real yaw
real pitch
real roll
real time
integer transparency
integer animtype
integer playercolor
string path
effect effect
Effect attachments
/* -------------------------------- Operators ------------------------------- */
method operator timeScale= takes real newTimeScale returns nothing
set time = newTimeScale
call BlzSetSpecialEffectTimeScale(effect, time)
endmethod
method operator timeScale takes nothing returns real
return time
endmethod
method operator alpha= takes integer newAlpha returns nothing
set transparency = newAlpha
call BlzSetSpecialEffectAlpha(effect, transparency)
endmethod
method operator alpha takes nothing returns integer
return transparency
endmethod
method operator playerColor= takes integer playerId returns nothing
set playercolor = playerId
call BlzSetSpecialEffectColorByPlayer(effect, Player(playerId))
endmethod
method operator playerColor takes nothing returns integer
return playercolor
endmethod
method operator animation= takes integer animType returns nothing
set animtype = animType
call BlzPlaySpecialEffect(effect, ConvertAnimType(animtype))
endmethod
method operator animation takes nothing returns integer
return animtype
endmethod
/* --------------------------------- Methods -------------------------------- */
method scale takes effect sfx, real scale returns nothing
set size = scale
call BlzSetSpecialEffectScale(sfx, scale)
endmethod
method orient takes real yaw, real pitch, real roll returns nothing
local Effect node = attachments.next
set .yaw = yaw
set .pitch = pitch
set .roll = roll
call BlzSetSpecialEffectOrientation(effect, yaw, pitch, roll)
loop
exitwhen node == attachments
set node.yaw = yaw
set node.pitch = pitch
set node.roll = roll
call BlzSetSpecialEffectOrientation(node.effect, yaw, pitch, roll)
set node = node.next
endloop
endmethod
method move takes real x, real y, real z returns boolean
local Effect node = attachments.next
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call BlzSetSpecialEffectPosition(effect, x, y, z)
loop
exitwhen node == attachments
call BlzSetSpecialEffectPosition(node.effect, x - node.x, y - node.y, z - node.z)
set node = node.next
endloop
return true
endif
return false
endmethod
method attach takes string fxpath, real dx, real dy, real dz, real scale returns effect
local Effect node = attachments.insert(fxpath, dx, dy, dz, scale)
call BlzSetSpecialEffectPosition(node.effect, BlzGetLocalSpecialEffectX(effect) - dx, BlzGetLocalSpecialEffectY(effect) - dy, BlzGetLocalSpecialEffectZ(effect) - dz)
return node.effect
endmethod
method detach takes effect sfx returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
if GetHandleId(node.effect) == GetHandleId(sfx) then
call node.remove()
exitwhen true
endif
set node = node.next
endloop
endmethod
method setColor takes integer red, integer green, integer blue returns nothing
call BlzSetSpecialEffectColor(effect, red, green, blue)
endmethod
/* -------------------------- Contructor/Destructor ------------------------- */
method destroy takes nothing returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
call node.remove()
set node = node.next
endloop
call DestroyEffect(effect)
call attachments.deallocate()
set effect = null
set path = null
set size = 1.
call deallocate()
endmethod
static method create takes real x, real y, real z returns thistype
local thistype this = thistype.allocate()
set effect = AddSpecialEffect("", x, y)
set path = ""
set size = 1
set time = 0
set transparency = 0
set animtype = 0
set playercolor = 0
set attachments = Effect.create()
call BlzSetSpecialEffectZ(effect, z)
return this
endmethod
endstruct
endlibrary
library MissileUtils requires Missiles
/* -------------------- Missile Utils v2.6 by Chopinski -------------------- */
// This is a simple Utils library for the Relativistic Missiles system.
// Credits:
// Sevion for the Alloc module
// - www.hiveworkshop.com/threads/snippet-alloc.192348/
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* System */
/* -------------------------------------------------------------------------- */
private module LinkedList
readonly thistype next
readonly thistype prev
method init takes nothing returns thistype
set next = this
set prev = this
return this
endmethod
method pushBack takes thistype node returns thistype
set node.prev = prev
set node.next = this
set prev.next = node
set prev = node
return node
endmethod
method pushFront takes thistype node returns thistype
set node.prev = this
set node.next = next
set next.prev = node
set next = node
return node
endmethod
method pop takes nothing returns nothing
set prev.next = next
set next.prev = prev
endmethod
endmodule
struct MGroup extends array
implement LinkedList
implement Alloc
public Missiles missile
method remove takes nothing returns nothing
call pop()
call deallocate()
endmethod
method insert takes Missiles m returns thistype
local thistype node = pushBack(allocate())
set node.missile = m
return node
endmethod
static method create takes nothing returns thistype
return thistype(allocate()).init()
endmethod
endstruct
struct MissileGroup
MGroup group
integer size
method destroy takes nothing returns nothing
call group.deallocate()
call deallocate()
endmethod
method missileAt takes integer i returns Missiles
local MGroup node = group.next
local integer j = 0
if size > 0 and i <= size - 1 then
loop
exitwhen j == i
set node = node.next
set j = j + 1
endloop
return node.missile
else
return 0
endif
endmethod
method remove takes Missiles missile returns nothing
local MGroup node = group.next
loop
exitwhen node == group
if node.missile == missile then
set size = size - 1
call node.remove()
exitwhen true
endif
set node = node.next
endloop
endmethod
method insert takes Missiles missile returns nothing
set size = size + 1
call group.insert(missile)
endmethod
method clear takes nothing returns nothing
local MGroup node = group.next
loop
exitwhen node == group
call node.remove()
set node = node.next
endloop
set size = 0
endmethod
method contains takes Missiles missile returns boolean
local MGroup node = group.next
local boolean found = false
loop
exitwhen node == group
if node.missile == missile then
set found = true
exitwhen true
endif
set node = node.next
endloop
return found
endmethod
method addGroup takes MissileGroup source returns nothing
local MGroup node = source.group.next
loop
exitwhen node == source.group
if not contains(node.missile) then
call insert(node.missile)
endif
set node = node.next
endloop
endmethod
method removeGroup takes MissileGroup source returns nothing
local MGroup node = source.group.next
loop
exitwhen node == source.group
if contains(node.missile) then
call remove(node.missile)
endif
set node = node.next
endloop
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set group = MGroup.create()
set size = 0
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS API */
/* -------------------------------------------------------------------------- */
function CreateMissileGroup takes nothing returns MissileGroup
return MissileGroup.create()
endfunction
function DestroyMissileGroup takes MissileGroup missiles returns nothing
if missiles != 0 then
call missiles.clear()
call missiles.destroy()
endif
endfunction
function MissileGroupGetSize takes MissileGroup missiles returns integer
if missiles != 0 then
return missiles.size
else
return 0
endif
endfunction
function GroupMissileAt takes MissileGroup missiles, integer position returns Missiles
if missiles != 0 then
return missiles.missileAt(position)
else
return 0
endif
endfunction
function ClearMissileGroup takes MissileGroup missiles returns nothing
if missiles != 0 then
call missiles.clear()
endif
endfunction
function IsMissileInGroup takes Missiles missile, MissileGroup missiles returns boolean
if missiles != 0 and missile != 0 then
if missiles.size > 0 then
return missiles.contains(missile)
else
return false
endif
else
return false
endif
endfunction
function GroupRemoveMissile takes MissileGroup missiles, Missiles missile returns nothing
if missiles != 0 and missile != 0 then
if missiles.size > 0 then
call missiles.remove(missile)
endif
endif
endfunction
function GroupAddMissile takes MissileGroup missiles, Missiles missile returns nothing
if missiles != 0 and missile != 0 then
if not missiles.contains(missile) then
call missiles.insert(missile)
endif
endif
endfunction
function GroupPickRandomMissile takes MissileGroup missiles returns Missiles
if missiles != 0 then
if missiles.size > 0 then
return missiles.missileAt(GetRandomInt(0, missiles.size - 1))
else
return 0
endif
else
return 0
endif
endfunction
function FirstOfMissileGroup takes MissileGroup missiles returns Missiles
if missiles != 0 then
if missiles.size > 0 then
return missiles.group.next.missile
else
return 0
endif
else
return 0
endif
endfunction
function GroupAddMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
if source != 0 and destiny != 0 then
if source.size > 0 and source != destiny then
call destiny.addGroup(source)
endif
endif
endfunction
function GroupRemoveMissileGroup takes MissileGroup source, MissileGroup destiny returns nothing
if source != 0 and destiny != 0 then
if source == destiny then
call source.clear()
elseif source.size > 0 then
call destiny.removeGroup(source)
endif
endif
endfunction
function GroupEnumMissilesOfType takes MissileGroup missiles, integer whichType returns nothing
local integer i
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if missile.type == whichType then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfTypeCounted takes MissileGroup missiles, integer whichType, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if missile.type == whichType then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfPlayer takes MissileGroup missiles, player p returns nothing
local integer i
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if missile.owner == p then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesOfPlayerCounted takes MissileGroup missiles, player p, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if missile.owner == p then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRect takes MissileGroup missiles, rect r returns nothing
local integer i
local Missiles missile
if missiles != 0 and r != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRectCounted takes MissileGroup missiles, rect r, integer amount returns nothing
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and r != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
if GetRectMinX(r) <= missile.x and missile.x <= GetRectMaxX(r) and GetRectMinY(r) <= missile.y and missile.y <= GetRectMaxY(r) then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeOfLoc takes MissileGroup missiles, location loc, real radius returns nothing
local real dx
local real dy
local integer i
local Missiles missile
if missiles != 0 and radius > 0 and loc != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
set dx = missile.x - GetLocationX(loc)
set dy = missile.y - GetLocationY(loc)
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeOfLocCounted takes MissileGroup missiles, location loc, real radius, integer amount returns nothing
local real dx
local real dy
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and radius > 0 and loc != null then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
set dx = missile.x - GetLocationX(loc)
set dy = missile.y - GetLocationY(loc)
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRange takes MissileGroup missiles, real x, real y, real radius returns nothing
local real dx
local real dy
local integer i
local Missiles missile
if missiles != 0 and radius > 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count
set missile = Missiles.collection[i]
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set i = i + 1
endloop
endif
endif
endfunction
function GroupEnumMissilesInRangeCounted takes MissileGroup missiles, real x, real y, real radius, integer amount returns nothing
local real dx
local real dy
local integer i
local integer j = amount
local Missiles missile
if missiles != 0 and radius > 0 then
if Missiles.count > -1 then
set i = 0
if missiles.size > 0 then
call missiles.clear()
endif
loop
exitwhen i > Missiles.count or j == 0
set missile = Missiles.collection[i]
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
call missiles.insert(missile)
endif
set j = j - 1
set i = i + 1
endloop
endif
endif
endfunction
endlibrary
//===========================================================================
//
// Damage Engine 5.7.1.2 - update requires replacing the JASS script.
//
/*
Three GUI Damage systems for the community of The Hive,
Seven vJass Damage systems for the JASS-heads on their pedestals high,
Nine competing Damage systems, doomed to die,
One for Bribe on his dark throne
In the Land of the Hive where the Workshop lies.
One Damage Engine to rule them all, One Damage Engine to find them,
One Damage Engine to bring them all and in cross-compatibility unite them.
*/
//! novjass
JASS API (work in progress - I have a lot of documentation to go through):
struct Damage extends array
readonly static unit source //stores udg_DamageEventSource
readonly static unit target //stores udg_DamageEventTarget
static real amount //stores udg_DamageEventAmount
readonly unit sourceUnit //stores udg_DamageEventSource by index
readonly unit targetUnit //stores udg_DamageEventTarget by index
real damage //stores udg_DamageEventAmount by index
readonly real prevAmt //stores udg_DamageEventPrevAmt by index
attacktype attackType //stores udg_DamageEventAttackT by index
damagetype damageType //stores udg_DamageEventDamageT by index
weapontype weaponType //stores udg_DamageEventWeaponT by index
integer userType //stores udg_DamageEventType by index
readonly integer eFilter //replaces the previous eventFilter variable
readonly boolean isAttack //stores udg_IsDamageAttack by index
readonly boolean isCode //stores udg_IsDamageCode by index
readonly boolean isMelee //stores udg_IsDamageMelee by index
readonly boolean isRanged //stores udg_IsDamageRanged by index
readonly boolean isSpell //stores udg_IsDamageSpell by index
real armorPierced //stores udg_DamageEventArmorPierced by index
integer armorType //stores udg_DamageEventArmorT by index
integer defenseType //stores udg_DamageEventDefenseT by index
static boolean operator enabled
- Set to false to disable the damage event triggers/false to reverse that
static method apply takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns Damage
- Same arguments as "UnitDamageTarget" but has the benefit of being performance-friendly during recursive events.
- Will automatically cause the damage to be registered as Code damage.
static method applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
- A simplified version of the above function that autofills in the booleans, attack type and weapon type.
static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
- A different variation of the above which autofills the "attack" boolean and sets the damagetype to DAMAGE_TYPE_NORMAL.
struct DamageTrigger extends array
method operator filter= takes integer filter returns nothing
// Apply primary filters such as DamageEngine_FILTER_MELEE/RANGED/SPELL which are based off of limitop handles to enable easier access for GUI folks
// Full filter list:
- global integer DamageEngine_FILTER_ATTACK
- global integer DamageEngine_FILTER_MELEE
- global integer DamageEngine_FILTER_OTHER
- global integer DamageEngine_FILTER_RANGED
- global integer DamageEngine_FILTER_SPELL
- global integer DamageEngine_FILTER_CODE
boolean configured //set to True after configuring any filters listed below.
method configure takes nothing returns nothing
// Apply custom filters after setting any desired udg_DamageFilter variables (for GUI).
// Alternatively, vJass users can set these instead. Just be mindful to set the variable
// "configured" to true after settings these.
unit source
unit target
integer sourceType
integer targetType
integer sourceBuff
integer targetBuff
real damageMin
integer attackType
integer damageType
integer userType
//The string in the aruments below requires the following API:
// "" for standard damage event
// "Modifier(or Mod if you prefer)/After/Lethal/AOE" for the others
static method getIndex takes trigger t, string eventName, real value returns integer
static method registerTrigger takes trigger whichTrig, string var, real weight returns nothing
static method unregister takes trigger t, string eventName, real value, boolean reset returns boolean
static method operator [] takes code c returns trigger
// Converts a code argument to a trigger, while checking if the same code had already been registered before.
//The accepted strings here use the same criteria as DamageTrigger.getIndex/registerTrigger/unregister
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns nothing
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns nothing
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns nothing
function RegisterDamageEngine takes code c, string eventName, real value returns nothing
//! endnovjass
//===========================================================================
library DamageEngine
globals
private constant boolean USE_GUI = true //If you don't use any of the GUI events, set to false to slightly improve performance
private constant boolean USE_SCALING = USE_GUI //If you don't need or want to use DamageScalingUser/WC3 then set this to false
private constant boolean USE_EXTRA = true //If you don't use DamageEventLevel or AOEDamageEvent, set this to false
private constant boolean USE_ARMOR_MOD = true //If you do not modify nor detect armor/defense, set this to false
private constant boolean USE_MELEE_RANGE= true //If you do not detect melee nor ranged damage, set this to false
private constant boolean USE_LETHAL = true //If you do not use LethalDamageEvent nor negative damage (explosive) types, set this to false
private constant integer LIMBO = 16 //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
public constant integer TYPE_CODE = 1 //Must be the same as udg_DamageTypeCode, or 0 if you prefer to disable the automatic flag.
public constant integer TYPE_PURE = 2 //Must be the same as udg_DamageTypePure
private constant real DEATH_VAL = 0.405 //In case Blizz ever changes this, it'll be a quick fix here.
private timer alarm = CreateTimer()
private boolean alarmSet = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean canKick = true
private boolean totem = false
private boolean array attacksImmune
private boolean array damagesImmune
//Made global in order to use enable/disable behavior.
private trigger t1 = CreateTrigger()
private trigger t2 = CreateTrigger()
private trigger t3 = CreateTrigger() //Catches, stores recursive events
//These variables coincide with Blizzard's "limitop" type definitions so as to enable users (GUI in particular) with some nice performance perks.
public constant integer FILTER_ATTACK = 0 //LESS_THAN
public constant integer FILTER_MELEE = 1 //LESS_THAN_OR_EQUAL
public constant integer FILTER_OTHER = 2 //EQUAL
public constant integer FILTER_RANGED = 3 //GREATER_THAN_OR_EQUAL
public constant integer FILTER_SPELL = 4 //GREATER_THAN
public constant integer FILTER_CODE = 5 //NOT_EQUAL
public constant integer FILTER_MAX = 6
private integer eventFilter = FILTER_OTHER
public boolean inception = false //When true, it allows your trigger to potentially go recursive up to LIMBO. However it must be set per-trigger throughout the game and not only once per trigger during map initialization.
private boolean dreaming = false
private integer sleepLevel = 0
private group proclusGlobal = CreateGroup() //track sources of recursion
private group fischerMorrow = CreateGroup() //track targets of recursion
private boolean kicking = false
private boolean eventsRun = false
private keyword run
private keyword trigFrozen
private keyword levelsDeep
private keyword inceptionTrig
private boolean hasLethal = false
endglobals
native UnitAlive takes unit u returns boolean
//GUI Vars:
/*
Retained from 3.8 and prior:
----------------------------
unit udg_DamageEventSource
unit udg_DamageEventTarget
unit udg_EnhancedDamageTarget
group udg_DamageEventAOEGroup
integer udg_DamageEventAOE
integer udg_DamageEventLevel
real udg_DamageModifierEvent
real udg_DamageEvent
real udg_AfterDamageEvent
real udg_DamageEventAmount
real udg_DamageEventPrevAmt
real udg_AOEDamageEvent
boolean udg_DamageEventOverride
boolean udg_NextDamageType
boolean udg_DamageEventType
boolean udg_IsDamageSpell
//Added in 5.0:
boolean udg_IsDamageMelee
boolean udg_IsDamageRanged
unit udg_AOEDamageSource
real udg_LethalDamageEvent
real udg_LethalDamageHP
real udg_DamageScalingWC3
integer udg_DamageEventAttackT
integer udg_DamageEventDamageT
integer udg_DamageEventWeaponT
//Added in 5.1:
boolean udg_IsDamageCode
//Added in 5.2:
integer udg_DamageEventArmorT
integer udg_DamageEventDefenseT
//Addded in 5.3:
real DamageEventArmorPierced
real udg_DamageScalingUser
//Added in 5.4.2 to allow GUI users to re-issue the exact same attack and damage type at the attacker.
attacktype array udg_CONVERTED_ATTACK_TYPE
damagetype array udg_CONVERTED_DAMAGE_TYPE
//Added after Reforged introduced the new native BlzGetDamageIsAttack
boolean udg_IsDamageAttack
//Added in 5.6 to give GUI users control over the "IsDamageAttack", "IsDamageRanged" and "DamageEventWeaponT" field
boolean udg_NextDamageIsAttack //The first boolean value in the UnitDamageTarget native
boolean udg_NextDamageIsMelee //Flag the damage classification as melee
boolean udg_NextDamageIsRanged //The second boolean value in the UnitDamageTarget native
integer udg_NextDamageWeaponT //Allows control over damage sound effect
//Added in 5.7 to enable efficient, built-in filtering (see the below "checkConfiguration" method - I recommend commenting-out anything you don't need in your map)
integer udg_DamageFilterAttackT
integer udg_DamageFilterDamageT //filter for a specific attack/damage type
unit udg_DamageFilterSource
unit udg_DamageFilterTarget //filter for a specific source/target
integer udg_DamageFilterSourceT
integer udg_DamageFilterTargetT //unit type of source/target
integer udg_DamageFilterType //which DamageEventType was used
integer udg_DamageFilterSourceB
integer udg_DamageFilterTargetB //if source/target has a buff
real udg_DamageFilterMinAmount //only allow a minimum damage threshold
*/
struct DamageTrigger extends array
//Map-makers should comment-out any booleans they will never need to check for.
method checkConfiguration takes nothing returns boolean
if this.userType != 0 and udg_DamageEventType != this.userType then
elseif this.source != null and this.source != udg_DamageEventSource then
elseif this.target != null and this.target != udg_DamageEventTarget then
elseif this.attackType >= 0 and this.attackType != udg_DamageEventAttackT then
elseif this.damageType >= 0 and this.damageType != udg_DamageEventDamageT then
elseif this.sourceType != 0 and GetUnitTypeId(udg_DamageEventSource) != this.sourceType then
elseif this.targetType != 0 and GetUnitTypeId(udg_DamageEventTarget) != this.targetType then
elseif this.sourceBuff != 0 and GetUnitAbilityLevel(udg_DamageEventSource, this.sourceBuff) == 0 then
elseif this.targetBuff != 0 and GetUnitAbilityLevel(udg_DamageEventTarget, this.targetBuff) == 0 then
elseif udg_DamageEventAmount > this.damageMin then
return true
endif
return false
endmethod
//The below variables are constant
readonly static thistype MOD = 1
readonly static thistype SHIELD = 4
readonly static thistype DAMAGE = 5
readonly static thistype ZERO = 6
readonly static thistype AFTER = 7
readonly static thistype LETHAL = 8
readonly static thistype AOE = 9
private static integer count = 9
static thistype lastRegistered = 0
private static thistype array trigIndexStack
static thistype eventIndex = 0
static boolean array filters
readonly string eventStr
readonly real weight
boolean configured
boolean usingGUI
//The below variables are private
private thistype next
private trigger rootTrig
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer levelsDeep //How deep the user recursion currently is.
boolean inceptionTrig //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
unit source
unit target
integer sourceType
integer targetType
integer sourceBuff
integer targetBuff
real damageMin
integer attackType
integer damageType
integer userType
method configure takes nothing returns nothing
set this.attackType = udg_DamageFilterAttackT
set this.damageType = udg_DamageFilterDamageT
set this.source = udg_DamageFilterSource
set this.target = udg_DamageFilterTarget
set this.sourceType = udg_DamageFilterSourceT
set this.targetType = udg_DamageFilterTargetT
set this.sourceBuff = udg_DamageFilterSourceB
set this.targetBuff = udg_DamageFilterTargetB
set this.userType = udg_DamageFilterType
set this.damageMin = udg_DamageFilterMinAmount
set udg_DamageFilterAttackT =-1
set udg_DamageFilterDamageT =-1
set udg_DamageFilterSource = null
set udg_DamageFilterTarget = null
set udg_DamageFilterSourceT = 0
set udg_DamageFilterTargetT = 0
set udg_DamageFilterType = 0
set udg_DamageFilterSourceB = 0
set udg_DamageFilterTargetB = 0
set udg_DamageFilterMinAmount=0.00
set this.configured = true
endmethod
static method setGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.damage
set udg_DamageEventAttackT = GetHandleId(Damage.index.attackType)
set udg_DamageEventDamageT = GetHandleId(Damage.index.damageType)
set udg_DamageEventWeaponT = GetHandleId(Damage.index.weaponType)
set udg_DamageEventType = Damage.index.userType
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set udg_DamageEventArmorPierced = Damage.index.armorPierced
set udg_DamageEventArmorT = Damage.index.armorType
set udg_DamageEventDefenseT = Damage.index.defenseType
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if full then
set udg_DamageEventSource = Damage.index.sourceUnit
set udg_DamageEventTarget = Damage.index.targetUnit
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageAttack = Damage.index.isAttack
set udg_IsDamageCode = Damage.index.isCode
set udg_IsDamageSpell = Damage.index.isSpell
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set udg_IsDamageMelee = Damage.index.isMelee
set udg_IsDamageRanged = Damage.index.isRanged
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
endmethod
static method setStructFromGUI takes nothing returns nothing
set Damage.index.damage = udg_DamageEventAmount
set Damage.index.attackType = ConvertAttackType(udg_DamageEventAttackT)
set Damage.index.damageType = ConvertDamageType(udg_DamageEventDamageT)
set Damage.index.weaponType = ConvertWeaponType(udg_DamageEventWeaponT)
set Damage.index.userType = udg_DamageEventType
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set Damage.index.armorPierced = udg_DamageEventArmorPierced
set Damage.index.armorType = udg_DamageEventArmorT
set Damage.index.defenseType = udg_DamageEventDefenseT
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endmethod
static method getVerboseStr takes string eventName returns string
if eventName == "Modifier" or eventName == "Mod" then
return "udg_DamageModifierEvent"
endif
return "udg_" + eventName + "DamageEvent"
endmethod
private static method getStrIndex takes string var, real lbs returns thistype
local integer root = R2I(lbs)
if var == "udg_DamageModifierEvent" then
if root >= 4 then
set root= SHIELD //4.00 or higher
else
set root= MOD //Less than 4.00
endif
elseif var == "udg_DamageEvent" then
if root == 2 or root == 0 then
set root= ZERO
else
set root= DAMAGE //Above 0.00 but less than 2.00, generally would just be 1.00
endif
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_LethalDamageEvent" then
set root = LETHAL
elseif var == "udg_AOEDamageEvent" then
set root = AOE
else
set root = 0
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
endif
return root
endmethod
private method toggleAllFilters takes boolean flag returns nothing
set filters[this + FILTER_ATTACK] = flag
set filters[this + FILTER_MELEE] = flag
set filters[this + FILTER_OTHER] = flag
set filters[this + FILTER_RANGED] = flag
set filters[this + FILTER_SPELL] = flag
set filters[this + FILTER_CODE] = flag
endmethod
method operator filter= takes integer f returns nothing
set this = this*FILTER_MAX
if f == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if f == FILTER_ATTACK then
set filters[this + FILTER_ATTACK] = true
set filters[this + FILTER_MELEE] = true
set filters[this + FILTER_RANGED] = true
else
set filters[this + f] = true
endif
endif
endmethod
static method registerVerbose takes trigger whichTrig, string var, real lbs, boolean GUI, integer filt returns thistype
local thistype index= getStrIndex(var, lbs)
local thistype i = 0
local thistype id = 0
if index == 0 then
return 0
elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
set filters[lastRegistered*FILTER_MAX + filt] = true //allows GUI to register multiple different types of Damage filters to the same trigger
return 0
endif
if not hasLethal and index == LETHAL then
set hasLethal = true
endif
if trigIndexStack[0] == 0 then
set count = count + 1 //List runs from index 10 and up
set id = count
else
set id = trigIndexStack[0]
set trigIndexStack[0] = trigIndexStack[id]
endif
set lastRegistered = id
set id.filter = filt
set id.rootTrig = whichTrig
set id.usingGUI = GUI
set id.weight = lbs
set id.eventStr = var
//Next 2 lines added to fix a bug when using manual vJass configuration,
//discovered and solved by lolreported
set id.attackType = -1
set id.damageType = -1
loop
set i = index.next
exitwhen i == 0 or lbs < i.weight
set index = i
endloop
set index.next = id
set id.next = i
//call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
return lastRegistered
endmethod
static method registerTrigger takes trigger t, string var, real lbs returns thistype
return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
endmethod
private static thistype prev = 0
static method getIndex takes trigger t, string eventName, real lbs returns thistype
local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
loop
set prev = index
set index = index.next
exitwhen index == 0 or index.rootTrig == t
endloop
return index
endmethod
static method unregister takes trigger t, string eventName, real lbs, boolean reset returns boolean
local thistype index = getIndex(t, eventName, lbs)
if index == 0 then
return false
endif
set prev.next = index.next
set trigIndexStack[index] = trigIndexStack[0]
set trigIndexStack[0] = index
if reset then
call index.configure()
set index.configured = false
set index = index*FILTER_MAX
call index.toggleAllFilters(false)
endif
return true
endmethod
method run takes nothing returns nothing
local integer cat = this
local Damage d = Damage.index
static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
local boolean structUnset = false
local boolean guiUnset = false
local boolean mod = cat <= DAMAGE
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if dreaming then
return
endif
set dreaming = true
call DisableTrigger(t1)
call DisableTrigger(t2)
call EnableTrigger(t3)
//call BJDebugMsg("Start of event running")
loop
set this = this.next
exitwhen this == 0
exitwhen cat == MOD and (udg_DamageEventOverride or udg_DamageEventType == TYPE_PURE)
exitwhen cat == SHIELD and udg_DamageEventAmount <= 0.00
static if USE_LETHAL then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
exitwhen cat == LETHAL and udg_LethalDamageHP > DEATH_VAL
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set eventIndex = this
if not this.trigFrozen and filters[this*FILTER_MAX + d.eFilter] and IsTriggerEnabled(this.rootTrig) and (not this.configured or this.checkConfiguration()) then
static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if mod then
if this.usingGUI then
if guiUnset then
set guiUnset = false
call setGUIFromStruct(false)
endif
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
elseif structUnset then
set structUnset = false
call setStructFromGUI()
endif
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
//JASS users who do not use actions can modify the below block to just evaluate.
//It should not make any perceptable difference in terms of performance.
if TriggerEvaluate(this.rootTrig) then
call TriggerExecute(this.rootTrig)
endif
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if mod then
if this.usingGUI then
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
if cat != MOD then
set d.damage = udg_DamageEventAmount
else
set structUnset = true
endif
elseif cat != MOD then
set udg_DamageEventAmount = d.damage
else
set guiUnset = true
endif
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
endloop
static if USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if structUnset then
call setStructFromGUI()
endif
if guiUnset then
call setGUIFromStruct(false)
endif
else// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call setGUIFromStruct(false)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
//call BJDebugMsg("End of event running")
call DisableTrigger(t3)
call EnableTrigger(t1)
call EnableTrigger(t2)
set dreaming = false
endmethod
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code c returns trigger
local integer i = 0
local boolexpr b = Filter(c)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
exitwhen true
endif
set i = i + 1
exitwhen b == autoFuncs[i]
endloop
return autoTriggers[i]
endmethod
endstruct
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_05()
struct Damage extends array
readonly unit sourceUnit //stores udg_DamageEventSource
readonly unit targetUnit //stores udg_DamageEventTarget
real damage //stores udg_DamageEventAmount
readonly real prevAmt //stores udg_DamageEventPrevAmt
attacktype attackType //stores udg_DamageEventAttackT
damagetype damageType //stores udg_DamageEventDamageT
weapontype weaponType //stores udg_DamageEventWeaponT
integer userType //stores udg_DamageEventType
readonly boolean isAttack //stores udg_IsDamageAttack
readonly boolean isCode //stores udg_IsDamageCode
readonly boolean isSpell //stores udg_IsDamageSpell
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
readonly boolean isMelee //stores udg_IsDamageMelee
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
readonly boolean isRanged //stores udg_IsDamageRanged
readonly integer eFilter //stores the previous eventFilter variable
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
real armorPierced //stores udg_DamageEventArmorPierced
integer armorType //stores udg_DamageEventArmorT
integer defenseType //stores udg_DamageEventDefenseT
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
readonly static Damage index = 0
private static Damage damageStack = 0
private static Damage prepped = 0
private static integer count = 0 //The number of currently-running queued or sequential damage instances
private Damage stackRef
private DamageTrigger recursiveTrig
private integer prevArmorT
private integer prevDefenseT
static method operator source takes nothing returns unit
return udg_DamageEventSource
endmethod
static method operator target takes nothing returns unit
return udg_DamageEventTarget
endmethod
static method operator amount takes nothing returns real
return Damage.index.damage
endmethod
static method operator amount= takes real r returns nothing
set Damage.index.damage = r
endmethod
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
private method setArmor takes boolean reset returns nothing
local real pierce
local integer at
local integer dt
if reset then
set pierce = udg_DamageEventArmorPierced
set at = Damage.index.prevArmorT
set dt = Damage.index.prevDefenseT
set udg_DamageEventArmorPierced = 0.00
set this.armorPierced = 0.00
else
set pierce = -udg_DamageEventArmorPierced
set at = udg_DamageEventArmorT
set dt = udg_DamageEventDefenseT
endif
if pierce != 0.00 then
call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
endif
if Damage.index.prevArmorT != udg_DamageEventArmorT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, at)
endif
if Damage.index.prevDefenseT != udg_DamageEventDefenseT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, dt)
endif
endmethod
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
private static method onAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
call DamageTrigger.AOE.run()
endif
set udg_DamageEventAOE = 0
set udg_DamageEventLevel = 0
set udg_EnhancedDamageTarget = null
set udg_AOEDamageSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
private static method afterDamage takes nothing returns nothing
if udg_DamageEventPrevAmt != 0.00 and udg_DamageEventDamageT != 0 then
call DamageTrigger.AFTER.run()
set udg_DamageEventDamageT = 0
set udg_DamageEventPrevAmt = 0.00
endif
endmethod
private method doPreEvents takes boolean natural returns boolean
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set this.armorType = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_ARMOR_TYPE)
set this.defenseType = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_DEFENSE_TYPE)
set this.prevArmorT = this.armorType
set this.prevDefenseT = this.defenseType
set this.armorPierced = 0.00
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set Damage.index = this
call DamageTrigger.setGUIFromStruct(true)
call GroupAddUnit(proclusGlobal, udg_DamageEventSource)
call GroupAddUnit(fischerMorrow, udg_DamageEventTarget)
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_05()
if udg_DamageEventAmount != 0.00 then
set udg_DamageEventOverride = udg_DamageEventDamageT == 0
call DamageTrigger.MOD.run()
static if not USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call DamageTrigger.setGUIFromStruct(false)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if natural then
call BlzSetEventAttackType(this.attackType)
call BlzSetEventDamageType(this.damageType)
call BlzSetEventWeaponType(this.weaponType)
call BlzSetEventDamage(udg_DamageEventAmount)
endif
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call this.setArmor(false)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
return false
endif
return true
endmethod
private static method unfreeze takes nothing returns nothing
local Damage i = damageStack
loop
exitwhen i == 0
set i = i - 1
set i.stackRef.recursiveTrig.trigFrozen = false
set i.stackRef.recursiveTrig.levelsDeep = 0
endloop
call EnableTrigger(t1)
call EnableTrigger(t2)
set kicking = false
set damageStack = 0
set prepped = 0
set dreaming = false
set sleepLevel = 0
call GroupClear(proclusGlobal)
call GroupClear(fischerMorrow)
//call BJDebugMsg("Cleared up the groups")
endmethod
static method finish takes nothing returns nothing
local Damage i = 0
local integer exit
if eventsRun then
set eventsRun = false
call afterDamage()
endif
if canKick and not kicking then
if damageStack != 0 then
set kicking = true
loop
set sleepLevel = sleepLevel + 1
set exit = damageStack
loop
set prepped = i.stackRef
if UnitAlive(prepped.targetUnit) then //Added just in case dead units had issues.
call prepped.doPreEvents(false) //don't evaluate the pre-event
if prepped.damage > 0.00 then
call DisableTrigger(t1) //Force only the after armor event to run.
call EnableTrigger(t2) //in case the user forgot to re-enable this
set totem = true
call UnitDamageTarget(prepped.sourceUnit, prepped.targetUnit, prepped.damage, prepped.isAttack, prepped.isRanged, prepped.attackType, prepped.damageType, prepped.weaponType)
else
//No new events run at all in this case
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
endif
if prepped.damage < 0.00 then
//No need for BlzSetEventDamage here
call SetWidgetLife(prepped.targetUnit, GetWidgetLife(prepped.targetUnit) - prepped.damage)
endif
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call prepped.setArmor(true)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
call afterDamage()
endif
set i = i + 1
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
endif
call unfreeze()
endif
endmethod
private static method failsafeClear takes nothing returns nothing
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call Damage.index.setArmor(true)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set canKick = true
set kicking = false
set totem = false
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
set eventsRun = true
endif
call finish()
endmethod
static method operator enabled= takes boolean b returns nothing
if b then
if dreaming then
call EnableTrigger(t3)
else
call EnableTrigger(t1)
call EnableTrigger(t2)
endif
else
if dreaming then
call DisableTrigger(t3)
else
call DisableTrigger(t1)
call DisableTrigger(t2)
endif
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(t1)
endmethod
private static boolean arisen = false
private static method getOutOfBed takes nothing returns nothing
if totem then
call failsafeClear() //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
else
set canKick = true
set kicking = false
call finish()
endif
static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call onAOEEnd()
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set arisen = true
endmethod
private static method wakeUp takes nothing returns nothing
set dreaming = false
set Damage.enabled = true
call ForForce(bj_FORCE_PLAYER[0], function thistype.getOutOfBed) //Moved to a new thread in case of a thread crash
if not arisen then
//call BJDebugMsg("DamageEngine issue: thread crashed!")
call unfreeze()
else
set arisen = false
endif
set Damage.count = 0
set Damage.index = 0
set alarmSet = false
//call BJDebugMsg("Timer wrapped up")
endmethod
private method addRecursive takes nothing returns nothing
if this.damage != 0.00 then
set this.recursiveTrig = DamageTrigger.eventIndex
if not this.isCode then
set this.isCode = true
set this.userType = TYPE_CODE
endif
set inception = inception or DamageTrigger.eventIndex.inceptionTrig
if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
if not inception then
set DamageTrigger.eventIndex.trigFrozen = true
elseif not DamageTrigger.eventIndex.trigFrozen then
set DamageTrigger.eventIndex.inceptionTrig = true
if DamageTrigger.eventIndex.levelsDeep < sleepLevel then
set DamageTrigger.eventIndex.levelsDeep = DamageTrigger.eventIndex.levelsDeep + 1
if DamageTrigger.eventIndex.levelsDeep >= LIMBO then
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
endif
endif
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
endif
set inception = false
endmethod
private static method clearNexts takes nothing returns nothing
set udg_NextDamageIsAttack = false
set udg_NextDamageType = 0
set udg_NextDamageWeaponT = 0
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set udg_NextDamageIsMelee = false
set udg_NextDamageIsRanged = false
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endmethod
static method create takes unit src, unit tgt, real amt, boolean a, attacktype at, damagetype dt, weapontype wt returns Damage
local Damage d = Damage.count + 1
set Damage.count = d
set d.sourceUnit = src
set d.targetUnit = tgt
set d.damage = amt
set d.prevAmt = amt
set d.attackType = at
set d.damageType = dt
set d.weaponType = wt
set d.isAttack = udg_NextDamageIsAttack or a
set d.isSpell = d.attackType == null and not d.isAttack
return d
endmethod
private static method createFromEvent takes nothing returns Damage
local Damage d = create(GetEventDamageSource(), GetTriggerUnit(), GetEventDamage(), BlzGetEventDamageType() == DAMAGE_TYPE_NORMAL, BlzGetEventAttackType(), BlzGetEventDamageType(), BlzGetEventWeaponType())
set d.isCode = udg_NextDamageType != 0 or udg_NextDamageIsAttack or udg_NextDamageIsRanged or udg_NextDamageIsMelee or d.damageType == DAMAGE_TYPE_MIND or udg_NextDamageWeaponT != 0 or (d.damage != 0.00 and d.damageType == DAMAGE_TYPE_UNKNOWN)
if d.isCode then
if udg_NextDamageType != 0 then
set d.userType = udg_NextDamageType
else
set d.userType = TYPE_CODE
endif
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set d.isMelee = udg_NextDamageIsMelee
set d.isRanged = udg_NextDamageIsRanged
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set d.eFilter = FILTER_CODE
if udg_NextDamageWeaponT != 0 then
set d.weaponType = ConvertWeaponType(udg_NextDamageWeaponT)
set udg_NextDamageWeaponT = 0
endif
else
set d.userType = 0
if d.damageType == DAMAGE_TYPE_NORMAL and d.isAttack then
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set d.isMelee = IsUnitType(d.sourceUnit, UNIT_TYPE_MELEE_ATTACKER)
set d.isRanged = IsUnitType(d.sourceUnit, UNIT_TYPE_RANGED_ATTACKER)
if d.isMelee and d.isRanged then
set d.isMelee = d.weaponType != null // Melee units play a sound when damaging
set d.isRanged = not d.isMelee // In the case where a unit is both ranged and melee, the ranged attack plays no sound.
endif
if d.isMelee then
set d.eFilter = FILTER_MELEE
elseif d.isRanged then
set d.eFilter = FILTER_RANGED
else
set d.eFilter = FILTER_ATTACK
endif
else
set d.eFilter = FILTER_ATTACK
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
else
if d.isSpell then
set d.eFilter = FILTER_SPELL
else
set d.eFilter = FILTER_OTHER
endif
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set d.isMelee = false
set d.isRanged = false
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
endif
call clearNexts()
return d
endmethod
private static method onRecursion takes nothing returns boolean //New in 5.7
local Damage d = Damage.createFromEvent()
call d.addRecursive()
call BlzSetEventDamage(0.00)
return false
endmethod
private static method onDamaging takes nothing returns boolean
local Damage d = Damage.createFromEvent()
//call BJDebugMsg("Pre-damage event running for " + GetUnitName(GetTriggerUnit()))
if alarmSet then
if totem then //WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
if d.damageType == DAMAGE_TYPE_SPIRIT_LINK or d.damageType == DAMAGE_TYPE_DEFENSIVE or d.damageType == DAMAGE_TYPE_PLANT then
set totem = false
set lastInstance= Damage.index
set canKick = false
else
call failsafeClear() //Not an overlapping event - just wrap it up
endif
else
call finish() //wrap up any previous damage index
endif
static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if d.sourceUnit != udg_AOEDamageSource then
call onAOEEnd()
set udg_AOEDamageSource = d.sourceUnit
elseif d.targetUnit == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1
elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
else
call TimerStart(alarm, 0.00, false, function Damage.wakeUp)
set alarmSet = true
static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set udg_AOEDamageSource = d.sourceUnit
set udg_EnhancedDamageTarget= d.targetUnit
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
static if USE_EXTRA then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call GroupAddUnit(udg_DamageEventAOEGroup, d.targetUnit)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if d.doPreEvents(true) then
call DamageTrigger.ZERO.run()
set canKick = true
call finish()
endif
set totem = lastInstance == 0 or attacksImmune[udg_DamageEventAttackT] or damagesImmune[udg_DamageEventDamageT] or not IsUnitType(udg_DamageEventTarget, UNIT_TYPE_MAGIC_IMMUNE)
return false
endmethod
private static method onDamaged takes nothing returns boolean
local real r = GetEventDamage()
local Damage d = Damage.index
//call BJDebugMsg("Second damage event running for " + GetUnitName(GetTriggerUnit()))
if prepped > 0 then
set prepped = 0
elseif dreaming or d.prevAmt == 0.00 then
return false
elseif totem then
set totem = false
else
//This should only happen for stuff like Spirit Link or Thorns Aura/Carapace
call afterDamage()
set Damage.index = lastInstance
set lastInstance = 0
set d = Damage.index
set canKick = true
call DamageTrigger.setGUIFromStruct(true)
endif
static if USE_ARMOR_MOD then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call d.setArmor(true)
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
static if USE_SCALING then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if udg_DamageEventAmount != 0.00 and r != 0.00 then
set udg_DamageScalingWC3 = r / udg_DamageEventAmount
elseif udg_DamageEventAmount > 0.00 then
set udg_DamageScalingWC3 = 0.00
else
set udg_DamageScalingWC3 = 1.00
if udg_DamageEventPrevAmt == 0.00 then
set udg_DamageScalingUser = 0.00
else
set udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt
endif
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set udg_DamageEventAmount = r
set d.damage = r
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
if udg_DamageEventAmount > 0.00 then
call DamageTrigger.SHIELD.run()
static if not USE_GUI then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
set udg_DamageEventAmount = d.damage
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
static if USE_LETHAL then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if hasLethal or udg_DamageEventType < 0 then
set udg_LethalDamageHP = GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
if udg_LethalDamageHP <= DEATH_VAL then
if hasLethal then
call DamageTrigger.LETHAL.run()
set udg_DamageEventAmount = GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
set d.damage = udg_DamageEventAmount
endif
if udg_DamageEventType < 0 and udg_LethalDamageHP <= DEATH_VAL then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
endif
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
static if USE_SCALING then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if udg_DamageEventPrevAmt == 0.00 or udg_DamageScalingWC3 == 0.00 then
set udg_DamageScalingUser = 0.00
else
set udg_DamageScalingUser = udg_DamageEventAmount/udg_DamageEventPrevAmt/udg_DamageScalingWC3
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
endif
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
endif
call BlzSetEventDamage(udg_DamageEventAmount)
set eventsRun = true
if udg_DamageEventAmount == 0.00 then
call finish()
endif
return false
endmethod
static method apply takes unit src, unit tgt, real amt, boolean a, boolean r, attacktype at, damagetype dt, weapontype wt returns Damage
local Damage d
if udg_NextDamageType == 0 then
set udg_NextDamageType = TYPE_CODE
endif
if dreaming then
set d = create(src, tgt, amt, a, at, dt, wt)
set d.isCode = true
set d.eFilter = FILTER_CODE
set d.userType = udg_NextDamageType
static if USE_MELEE_RANGE then// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
if not d.isSpell then
set d.isRanged = udg_NextDamageIsRanged or r
set d.isMelee = not d.isRanged
endif
endif// \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ /
call d.addRecursive()
else
call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
set d = Damage.index
call finish()
endif
call clearNexts()
return d
endmethod
static method applySpell takes unit src, unit tgt, real amt, damagetype dt returns Damage
return apply(src, tgt, amt, false, false, null, dt, null)
endmethod
static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
endmethod
//===========================================================================
private static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(t1, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(t1, Filter(function Damage.onDamaging))
call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DAMAGED)
call TriggerAddCondition(t2, Filter(function Damage.onDamaged))
//For recursion
call TriggerRegisterAnyUnitEventBJ(t3, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(t3, Filter(function Damage.onRecursion))
call DisableTrigger(t3)
//For preventing Thorns/Defensive glitch.
//Data gathered from https://www.hiveworkshop.com/threads/repo-in-progress-mapping-damage-types-to-their-abilities.316271/
set attacksImmune[0] = false //ATTACK_TYPE_NORMAL
set attacksImmune[1] = true //ATTACK_TYPE_MELEE
set attacksImmune[2] = true //ATTACK_TYPE_PIERCE
set attacksImmune[3] = true //ATTACK_TYPE_SIEGE
set attacksImmune[4] = false //ATTACK_TYPE_MAGIC
set attacksImmune[5] = true //ATTACK_TYPE_CHAOS
set attacksImmune[6] = true //ATTACK_TYPE_HERO
set damagesImmune[0] = true //DAMAGE_TYPE_UNKNOWN
set damagesImmune[4] = true //DAMAGE_TYPE_NORMAL
set damagesImmune[5] = true //DAMAGE_TYPE_ENHANCED
set damagesImmune[8] = false //DAMAGE_TYPE_FIRE
set damagesImmune[9] = false //DAMAGE_TYPE_COLD
set damagesImmune[10] = false //DAMAGE_TYPE_LIGHTNING
set damagesImmune[11] = true //DAMAGE_TYPE_POISON
set damagesImmune[12] = true //DAMAGE_TYPE_DISEASE
set damagesImmune[13] = false //DAMAGE_TYPE_DIVINE
set damagesImmune[14] = false //DAMAGE_TYPE_MAGIC
set damagesImmune[15] = false //DAMAGE_TYPE_SONIC
set damagesImmune[16] = true //DAMAGE_TYPE_ACID
set damagesImmune[17] = false //DAMAGE_TYPE_FORCE
set damagesImmune[18] = false //DAMAGE_TYPE_DEATH
set damagesImmune[19] = false //DAMAGE_TYPE_MIND
set damagesImmune[20] = false //DAMAGE_TYPE_PLANT
set damagesImmune[21] = false //DAMAGE_TYPE_DEFENSIVE
set damagesImmune[22] = true //DAMAGE_TYPE_DEMOLITION
set damagesImmune[23] = true //DAMAGE_TYPE_SLOW_POISON
set damagesImmune[24] = false //DAMAGE_TYPE_SPIRIT_LINK
set damagesImmune[25] = false //DAMAGE_TYPE_SHADOW_STRIKE
set damagesImmune[26] = true //DAMAGE_TYPE_UNIVERSAL
endmethod
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
endstruct
public function DebugStr takes nothing returns nothing
local integer i = 0
loop
set udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i)
exitwhen i == 6
set i = i + 1
endloop
set i = 0
loop
set udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i)
exitwhen i == 26
set i = i + 1
endloop
set udg_AttackTypeDebugStr[0] = "SPELLS" //ATTACK_TYPE_NORMAL in JASS
set udg_AttackTypeDebugStr[1] = "NORMAL" //ATTACK_TYPE_MELEE in JASS
set udg_AttackTypeDebugStr[2] = "PIERCE"
set udg_AttackTypeDebugStr[3] = "SIEGE"
set udg_AttackTypeDebugStr[4] = "MAGIC"
set udg_AttackTypeDebugStr[5] = "CHAOS"
set udg_AttackTypeDebugStr[6] = "HERO"
set udg_DamageTypeDebugStr[0] = "UNKNOWN"
set udg_DamageTypeDebugStr[4] = "NORMAL"
set udg_DamageTypeDebugStr[5] = "ENHANCED"
set udg_DamageTypeDebugStr[8] = "FIRE"
set udg_DamageTypeDebugStr[9] = "COLD"
set udg_DamageTypeDebugStr[10] = "LIGHTNING"
set udg_DamageTypeDebugStr[11] = "POISON"
set udg_DamageTypeDebugStr[12] = "DISEASE"
set udg_DamageTypeDebugStr[13] = "DIVINE"
set udg_DamageTypeDebugStr[14] = "MAGIC"
set udg_DamageTypeDebugStr[15] = "SONIC"
set udg_DamageTypeDebugStr[16] = "ACID"
set udg_DamageTypeDebugStr[17] = "FORCE"
set udg_DamageTypeDebugStr[18] = "DEATH"
set udg_DamageTypeDebugStr[19] = "MIND"
set udg_DamageTypeDebugStr[20] = "PLANT"
set udg_DamageTypeDebugStr[21] = "DEFENSIVE"
set udg_DamageTypeDebugStr[22] = "DEMOLITION"
set udg_DamageTypeDebugStr[23] = "SLOW_POISON"
set udg_DamageTypeDebugStr[24] = "SPIRIT_LINK"
set udg_DamageTypeDebugStr[25] = "SHADOW_STRIKE"
set udg_DamageTypeDebugStr[26] = "UNIVERSAL"
set udg_WeaponTypeDebugStr[0] = "NONE" //WEAPON_TYPE_WHOKNOWS in JASS
set udg_WeaponTypeDebugStr[1] = "METAL_LIGHT_CHOP"
set udg_WeaponTypeDebugStr[2] = "METAL_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[3] = "METAL_HEAVY_CHOP"
set udg_WeaponTypeDebugStr[4] = "METAL_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[5] = "METAL_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[6] = "METAL_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[7] = "METAL_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[8] = "METAL_HEAVY_BASH"
set udg_WeaponTypeDebugStr[9] = "METAL_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[10] = "METAL_HEAVY_STAB"
set udg_WeaponTypeDebugStr[11] = "WOOD_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[12] = "WOOD_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[13] = "WOOD_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[14] = "WOOD_LIGHT_BASH"
set udg_WeaponTypeDebugStr[15] = "WOOD_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[16] = "WOOD_HEAVY_BASH"
set udg_WeaponTypeDebugStr[17] = "WOOD_LIGHT_STAB"
set udg_WeaponTypeDebugStr[18] = "WOOD_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[19] = "CLAW_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[20] = "CLAW_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[21] = "CLAW_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[22] = "AXE_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[23] = "ROCK_HEAVY_BASH"
set udg_DefenseTypeDebugStr[0] = "LIGHT"
set udg_DefenseTypeDebugStr[1] = "MEDIUM"
set udg_DefenseTypeDebugStr[2] = "HEAVY"
set udg_DefenseTypeDebugStr[3] = "FORTIFIED"
set udg_DefenseTypeDebugStr[4] = "NORMAL" //Typically deals flat damage to all armor types
set udg_DefenseTypeDebugStr[5] = "HERO"
set udg_DefenseTypeDebugStr[6] = "DIVINE"
set udg_DefenseTypeDebugStr[7] = "UNARMORED"
set udg_ArmorTypeDebugStr[0] = "NONE" //ARMOR_TYPE_WHOKNOWS in JASS, added in 1.31
set udg_ArmorTypeDebugStr[1] = "FLESH"
set udg_ArmorTypeDebugStr[2] = "METAL"
set udg_ArmorTypeDebugStr[3] = "WOOD"
set udg_ArmorTypeDebugStr[4] = "ETHEREAL"
set udg_ArmorTypeDebugStr[5] = "STONE"
endfunction
//===========================================================================
//
// Setup of automatic events from GUI and custom ones from JASS alike
//
//===========================================================================
public function RegisterFromHook takes trigger whichTrig, string var, limitop op, real value returns nothing
call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns DamageTrigger
return DamageTrigger.registerVerbose(whichTrig, DamageTrigger.getVerboseStr(eventName), value, false, f)
endfunction
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[c], eventName, value, f)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEngine takes code c, string eventName, real value returns DamageTrigger
return RegisterDamageEngineEx(c, eventName, value, FILTER_OTHER)
endfunction
//For GUI to tap into more powerful vJass event filtering:
//! textmacro DAMAGE_TRIGGER_CONFIG
if not DamageTrigger.eventIndex.configured then
//! endtextmacro
//! textmacro DAMAGE_TRIGGER_CONFIG_END
call DamageTrigger.eventIndex.configure()
if not DamageTrigger.eventIndex.checkConfiguration() then
return
endif
endif
//! endtextmacro
endlibrary
globals
constant integer ABIL_ATTACK_BASIC = 'A004'
constant integer ABIL_ATTACK_SLAM = 'A003'
constant integer ABIL_SPELL_SLAM = 'A005'
constant integer STAT_COUNT = 8
constant integer STAT_INDEX_HP = 0
constant integer STAT_INDEX_HP_REPAIR = 1
constant integer STAT_INDEX_ENERGY = 2
constant integer STAT_INDEX_ENERGY_RECHARGE = 3
constant integer STAT_INDEX_FIREPOWER = 4
constant integer STAT_INDEX_SYSTEM_POWER = 5
constant integer STAT_INDEX_SYSTEM_SPEED = 6
constant integer STAT_INDEX_MOBILITY = 7
constant real BONUS_PER_CORE_HP = 10.
constant real BONUS_PER_CORE_HP_REPAIR = 0.15
constant real BONUS_PER_CORE_ENERGY = 12.
constant real BONUS_PER_CORE_ENERGY_RECHARGE = 0.3
constant real BONUS_PER_CORE_FIREPOWER = 2.
constant real BONUS_PER_CORE_SYSTEM_POWER = 5.
constant real BONUS_PER_CORE_SYSTEM_SPEED = 8.
constant real BONUS_PER_CORE_MOBILITY = 12.
constant real CONVEYOR_SPEED = 3.0
constant string CNOTE = "|c00888888"//Note Color
//location walkablePoint = Location(0.,0.)
rect enumRect = Rect(0., 0., 0., 0.)
//player-id-indexed array
location zTester = Location(0., 0.)
integer array coresAvailable
integer totalCoresCollected = 0
endglobals
library VersionManager
function IsReforged takes nothing returns boolean
return GetLocalizedString("REFORGED") != "REFORGED" //Note: Does not work until after initialization
endfunction
/*
function onInit takes nothing returns nothing
//TODO: init any WC3-version-specific things.
set isReforged = IsReforged()
if isReforged then
call BJDebugMsg("is reforged!!!!!!!!!!!!!!!!!!!!!!!!!!")
else
call BJDebugMsg("----------------- not reforged!")
endif
endfunction
*/
endlibrary
library MetroidvaniaUtils requires ExtendableBonusesBasicBonuses, ExtendableBonusesMetroidvania
function GetZ takes real x, real y returns real
call MoveLocation(zTester, x, y)
return GetLocationZ(zTester)
endfunction
function R2I2S takes real r returns string
return I2S(R2I(r + 0.1))
endfunction
function R2SHumanReadable takes real r returns string
if r > 10. then
return I2S(R2I(r+0.01))
elseif r > 0.99 then
return R2SW(r, 0, 1)
elseif r == 0 then
return R2SW(r, 0, 1)
else
return R2SW(r, 0, 2)
endif
endfunction
function strStartsWith takes string str, string start returns boolean
return start == SubString(str, 0, StringLength(start))
endfunction
function strEndsWith takes string str, string end returns boolean
return end == SubString(str, StringLength(str) - StringLength(end), StringLength(str))
endfunction
function statIndexToBonusId takes integer statIndex returns integer
if statIndex == STAT_INDEX_HP then
return BonusHealth.typeid
elseif statIndex == STAT_INDEX_HP_REPAIR then
return BonusHealthRegen.typeid
elseif statIndex == STAT_INDEX_ENERGY then
return BonusMana.typeid
elseif statIndex == STAT_INDEX_ENERGY_RECHARGE then
return BonusManaRegen.typeid
elseif statIndex == STAT_INDEX_FIREPOWER then
return BonusDamage.typeid
elseif statIndex == STAT_INDEX_SYSTEM_POWER then
return BonusSystemPower.typeid
elseif statIndex == STAT_INDEX_SYSTEM_SPEED then
return BonusSystemSpeed.typeid
elseif statIndex == STAT_INDEX_MOBILITY then
return BonusMovementSpeed.typeid
endif
call BJDebugMsg("Didn't find bonus id for statIndex=" + I2S(statIndex))
return -1
endfunction
function Si2Color takes integer statIndex returns string
if statIndex == STAT_INDEX_HP then
return "|c0096f064"
elseif statIndex == STAT_INDEX_HP_REPAIR then
return "|c0050b478"
elseif statIndex == STAT_INDEX_ENERGY then
return "|c0072AAEE"
elseif statIndex == STAT_INDEX_ENERGY_RECHARGE then
return "|c008064EC"
elseif statIndex == STAT_INDEX_FIREPOWER then
return "|c00FF7800"
elseif statIndex == STAT_INDEX_SYSTEM_POWER then
return "|c00DF00FF"
elseif statIndex == STAT_INDEX_SYSTEM_SPEED then
return "|c00D2B2FF"
elseif statIndex == STAT_INDEX_MOBILITY then
return "|c00EEE8AA"
endif
call BJDebugMsg("Didn't find color id for statIndex=" + I2S(statIndex))
return ""
endfunction
function Si2Name takes integer statIndex returns string
if statIndex == STAT_INDEX_HP then
return "Structural Integrity"
elseif statIndex == STAT_INDEX_HP_REPAIR then
return "Repair Rate"
elseif statIndex == STAT_INDEX_ENERGY then
return "Energy Capacity"
elseif statIndex == STAT_INDEX_ENERGY_RECHARGE then
return "Energy Recharge"
elseif statIndex == STAT_INDEX_FIREPOWER then
return "Firepower"
elseif statIndex == STAT_INDEX_SYSTEM_POWER then
return "Auxiliary Power"
elseif statIndex == STAT_INDEX_SYSTEM_SPEED then
return "System Speed"
elseif statIndex == STAT_INDEX_MOBILITY then
return "Mobility"
endif
call BJDebugMsg("Didn't find color id for statIndex=" + I2S(statIndex))
return ""
endfunction
function statIndexToBonusAmount takes integer statIndex returns real
if statIndex == STAT_INDEX_HP then
return BONUS_PER_CORE_HP
elseif statIndex == STAT_INDEX_HP_REPAIR then
return BONUS_PER_CORE_HP_REPAIR
elseif statIndex == STAT_INDEX_ENERGY then
return BONUS_PER_CORE_ENERGY
elseif statIndex == STAT_INDEX_ENERGY_RECHARGE then
return BONUS_PER_CORE_ENERGY_RECHARGE
elseif statIndex == STAT_INDEX_FIREPOWER then
return BONUS_PER_CORE_FIREPOWER
elseif statIndex == STAT_INDEX_SYSTEM_POWER then
return BONUS_PER_CORE_SYSTEM_POWER
elseif statIndex == STAT_INDEX_SYSTEM_SPEED then
return BONUS_PER_CORE_SYSTEM_SPEED
elseif statIndex == STAT_INDEX_MOBILITY then
return BONUS_PER_CORE_MOBILITY
endif
call BJDebugMsg("Didn't find bonus amount for statIndex=" + I2S(statIndex))
return -1.
endfunction
struct HeroStatsCore
private static integer array coresApplied
//every 3 points spent will increase cost by 1 for all different stats
static method coreCostForNext takes player p, integer statIndex returns integer
return 1 + coresAppliedToStat(p, statIndex) / 3
endmethod
static method coresAppliedToStat takes player p, integer statIndex returns integer
return coresApplied[GetPlayerId(p) * STAT_COUNT + statIndex]
endmethod
static method addCore takes player p, integer numberOfCores returns nothing
local integer i = GetPlayerId(p)
set coresAvailable[i] = coresAvailable[i] + numberOfCores
//Note: You'll probably need to update stats-panel after calling this.
//updateStatsPanel(p)
endmethod
static method available takes player p returns integer
return coresAvailable[GetPlayerId(p)]
endmethod
static method addCoresForAll takes integer numberOfCores returns nothing
call addCore(Player(0), numberOfCores)
call addCore(Player(1), numberOfCores)
call addCore(Player(2), numberOfCores)
call addCore(Player(3), numberOfCores)
call addCore(Player(4), numberOfCores)
call addCore(Player(5), numberOfCores)
set totalCoresCollected = totalCoresCollected + IMaxBJ(0, numberOfCores)
endmethod
static method applyCore takes unit u, integer statIndex returns nothing
local player p = GetOwningPlayer(u)
local integer i = GetPlayerId(p) * STAT_COUNT + statIndex
local integer cost = coreCostForNext(p, statIndex)
if available(p) >= cost then
call addCore(p, -cost)
set coresApplied[i] = coresApplied[i] + 1
call AddUnitBonus(u, statIndexToBonusId(statIndex), statIndexToBonusAmount(statIndex))
call MetroidvaniaController.getMetroidvaniaController(p).updateAbilityDetails()
endif
set p = null
endmethod
endstruct
function setTextTagToPlayerColor takes texttag text, player p returns nothing
if p == Player(0) then
call SetTextTagColor(text, 255, 3, 3, 200)
elseif p == Player(1) then
call SetTextTagColor(text, 0, 66, 255, 200)
elseif p == Player(2) then
call SetTextTagColor(text, 28, 230, 185, 200)
elseif p == Player(3) then
call SetTextTagColor(text, 84, 0, 129, 200)
elseif p == Player(4) then
call SetTextTagColor(text, 255, 252, 1, 200)
//TODO: all, I guess: https://www.hiveworkshop.com/threads/warcraft-iii-color-tags-and-linebreaks.31386/
//Convert hex -> rgb with ctrl+alt+r
endif
endfunction
function GetMissingHp takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetWidgetLife(u)
endfunction
function GetMissingMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA) - GetUnitState(u, UNIT_STATE_MANA)
endfunction
function GetMaxHp takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function GetMaxMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA)
endfunction
function GetHp takes unit u returns real
return GetWidgetLife(u)
endfunction
function GetMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MANA)
endfunction
function GetHpReg takes unit u returns real
return BlzGetUnitRealField(u, UNIT_RF_HIT_POINTS_REGENERATION_RATE) + GetUnitBonus(u, BonusHealthRegen.typeid)
endfunction
function GetManaReg takes unit u returns real
return BlzGetUnitRealField(u, UNIT_RF_MANA_REGENERATION) + GetUnitBonus(u, BonusManaRegen.typeid)
endfunction
function ConsumeMana takes unit u, real amount returns nothing
local texttag text
if amount > 0. then
set text = CreateTextTag()
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, 1.0)
call SetTextTagFadepoint(text, 0.7)
call SetTextTagColor(text, 128, 100, 236, 180)
call SetTextTagPos(text, GetUnitX(u) + GetRandomReal(-18., 18), GetUnitY(u) + GetRandomReal(-18., 18), 72.)
call SetTextTagVelocityBJ(text, 32., GetRandomReal(75., 105.))
call SetTextTagText(text, "-" + R2SHumanReadable(amount), TextTagSize2Height(6))
set text = null
call SetUnitState(u, UNIT_STATE_MANA, GetMana(u) - amount)
endif
endfunction
function RestoreMana takes unit u, real amount returns nothing
local texttag text
if amount > 0. then
set text = CreateTextTag()
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, 1.0)
call SetTextTagFadepoint(text, 0.7)
call SetTextTagColor(text, 128, 100, 236, 180)
call SetTextTagPos(text, GetUnitX(u) + GetRandomReal(-18., 18), GetUnitY(u) + GetRandomReal(-18., 18), 72.)
call SetTextTagVelocityBJ(text, 32., GetRandomReal(75., 105.))
call SetTextTagText(text, "+" + R2SHumanReadable(amount), TextTagSize2Height(6))
set text = null
call SetUnitState(u, UNIT_STATE_MANA, GetMana(u) + amount)
endif
endfunction
function GetDamage takes unit u returns real
return BlzGetUnitBaseDamage(u, 0) + GetUnitBonus(u, BONUS_DAMAGE) + 1.
endfunction
function GetAttackSpeed takes unit u returns real
return GetUnitBonus(u, BONUS_ATTACK_SPEED)
endfunction
function GetUtilProcessingRating takes unit u returns real
return 1.0
endfunction
function GetMovementSpeed takes unit u returns real
return GetUnitMoveSpeed(u)
endfunction
function GetStat takes integer statIndex, unit u returns real
if statIndex == STAT_INDEX_HP then
return GetMaxHp(u)
elseif statIndex == STAT_INDEX_HP_REPAIR then
return GetHpReg(u)
elseif statIndex == STAT_INDEX_ENERGY then
return GetMaxMana(u)
elseif statIndex == STAT_INDEX_ENERGY_RECHARGE then
return GetManaReg(u)
elseif statIndex == STAT_INDEX_FIREPOWER then
return GetDamage(u)
elseif statIndex == STAT_INDEX_SYSTEM_POWER then
return GetUnitBonus(u, BonusSystemPower.typeid)
elseif statIndex == STAT_INDEX_SYSTEM_SPEED then
return GetUnitBonus(u, BonusSystemSpeed.typeid)
elseif statIndex == STAT_INDEX_MOBILITY then
return GetUnitMoveSpeed(u)
endif
call BJDebugMsg("Didn't find stat for statIndex=" + I2S(statIndex))
return -1.
endfunction
function ScalingText takes real value, real percent, integer statIndex returns string
return Si2Color(statIndex) + R2SHumanReadable(value * percent) + " (" + R2SHumanReadable(percent*100.) + "%% of " + Si2Name(statIndex) + ")|r"
endfunction
function playSound3d takes real x, real y, string sound_file_name returns nothing
local sound snd = CreateSound(sound_file_name, false, true, true, 10, 10, "")
local integer sound_duration_msec = GetSoundFileDuration(sound_file_name)
call SetSoundDistances(snd, 600.00, 10000.00)
call SetSoundDistanceCutoff(snd, 3000.00)
call SetSoundPosition(snd, x, y, 0 )
call SetSoundDuration(snd, sound_duration_msec)
call SetSoundVolume(snd, 127)
call StartSound(snd)
call KillSoundWhenDone(snd)
set snd = null
endfunction
function IsHero takes unit u returns boolean
return IsUnitInGroup(u, udg_heroes_all)
endfunction
function GetRandomHero takes nothing returns unit
return BlzGroupUnitAt(udg_heroes_all, GetRandomInt(0, BlzGroupGetSize(udg_heroes_all) - 1))
endfunction
function IsUnitTargetableNoFF takes unit target, player friend returns boolean
return BlzIsUnitSelectable(target) and (not IsUnitAlly(target, friend) or GetPlayerId(GetOwningPlayer(target)) == PLAYER_NEUTRAL_PASSIVE)
endfunction
function AttackMoveTowardsRandomHero takes unit u returns nothing
local unit hero = GetRandomHero()
call IssuePointOrder(u, "attack", GetUnitX(hero), GetUnitY(hero))
set hero = null
endfunction
endlibrary
function damageGroup takes group g, unit source, real damage, damagetype dmgType returns nothing
local unit u
loop
set u = FirstOfGroup(g)
exitwhen u == null
call Damage.applySpell(source, u, damage, dmgType)
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
endfunction
//source, x, y, damage, radius, damage-type
//call damageAoENoFF(udg_t_unit1, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), udg_t_real1, 200., DAMAGE_TYPE_DEMOLITION)
//call damageAoENoFF(GetHero(GetTriggerPlayer()), GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), udg_t_real1, 200., DAMAGE_TYPE_DEMOLITION)
function damageAoENoFFWithResult takes unit source, real x, real y, real damage, real radius, damagetype dmgType, group resultGroup returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 96., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if IsUnitTargetableNoFF(u, owner) and IsUnitInRangeXY(u, x, y, radius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, dmgType)
if resultGroup != null then
call GroupAddUnit(resultGroup, u)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
function damageAoENoFF takes unit source, real x, real y, real damage, real radius, damagetype dmgType returns nothing
call damageAoENoFFWithResult(source, x, y, damage, radius, dmgType, null)
endfunction
function damageAoEWithResult takes unit source, real x, real y, real damage, real radius, damagetype dmgType, group resultGroup returns nothing
local group targetsWithinRadius = CreateGroup()
local player owner = GetOwningPlayer(source)
local unit u
call GroupEnumUnitsInRange(targetsWithinRadius, x, y, radius + 96., null)
loop
set u = FirstOfGroup(targetsWithinRadius)
exitwhen u == null
if UnitAlive(u) and IsUnitInRangeXY(u, x, y, radius) then
//call UnitDamageTarget(source, u, damage, false, false, ATTACK_TYPE_NORMAL, dmgType, null)
call Damage.applySpell(source, u, damage, dmgType)
if resultGroup != null then
call GroupAddUnit(resultGroup, u)
endif
endif
call GroupRemoveUnit(targetsWithinRadius,u)
endloop
call DestroyGroup (targetsWithinRadius)
set owner = null
set targetsWithinRadius = null
endfunction
function damageAoE takes unit source, real x, real y, real damage, real radius, damagetype dmgType returns nothing
call damageAoEWithResult(source, x, y, damage, radius, dmgType, null)
endfunction
//BlzGetAbilityStringField takes ability whichAbility, abilitystringfield whichField returns string
//BlzSetAbilityRealLevelField takes ability whichAbility, abilityreallevelfield whichField, integer level, real value returns boolean
function OrderAbilWithSpeed takes unit u, integer abilityCode, real actionDuration, real animationSpeed, real tx, real ty returns nothing
local ability ab = BlzGetUnitAbility(u, abilityCode)
local string orderStr = BlzGetAbilityStringLevelField(ab, ABILITY_SLF_BASE_ORDER_ID_NCL6, 0)
call BlzSetAbilityRealLevelField(ab, ABILITY_RLF_FOLLOW_THROUGH_TIME, 0, actionDuration)
call SetUnitTimeScale(u, animationSpeed)
call IssuePointOrder(u, orderStr, tx, ty)
set ab = null
endfunction
//set udg_t_string1 = SubString("012345678", 0, 5) -> 01234
function hitDest takes destructable d, integer level returns boolean
local string destName = GetDestructableName(d)
local integer destLevel
if strStartsWith(destName, "Dest") then
set destLevel = S2I(SubString(destName, 4, 5))
if level >= destLevel then
call KillDestructable(d)
return true
endif
endif
return false
endfunction
function destroyEnumDest1 takes nothing returns nothing
call hitDest(GetEnumDestructable(), 1)
endfunction
function destroyDest1 takes real x, real y, real radius returns nothing
call SetRect(enumRect, x - radius, y - radius, x + radius, y + radius)
call EnumDestructablesInRect(enumRect, null, function destroyEnumDest1)
endfunction
function destroyEnumDest2 takes nothing returns nothing
call hitDest(GetEnumDestructable(), 2)
endfunction
function destroyDest2 takes real x, real y, real radius returns nothing
call SetRect(enumRect, x - radius, y - radius, x + radius, y + radius)
call EnumDestructablesInRect(enumRect, null, function destroyEnumDest2)
endfunction
library SimpleDelayedDestroyer requires TimerUtils
//--------------- Effect ------------
struct EffectDestroyer
effect effect
private method destroy takes nothing returns nothing
call DestroyEffect(this.effect)
set this.effect = null
call this.deallocate()
endmethod
private static method destroyFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method destroyLater takes effect e, real delay returns nothing
local thistype this = allocate()
local timer t = NewTimerEx(this)
call TimerStart(t, delay, false, function thistype.destroyFromTimer)
set this.effect = e
set t = null
endmethod
endstruct
function DestroyEffectLater takes effect e, real delay returns nothing
call EffectDestroyer.destroyLater(e, delay)
endfunction
function DestroyLastCreatedEffectLater takes real delay returns nothing
call EffectDestroyer.destroyLater(bj_lastCreatedEffect, delay)
endfunction
//--------------- Lightning ------------
struct LightningDestroyer
lightning effect
private method destroy takes nothing returns nothing
call DestroyLightning(this.effect)
set this.effect = null
call this.deallocate()
endmethod
private static method destroyFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method destroyLater takes lightning e, real delay returns nothing
local thistype this = allocate()
local timer t = NewTimerEx(this)
call TimerStart(t, delay, false, function thistype.destroyFromTimer)
set this.effect = e
set t = null
endmethod
endstruct
function DestroyLightningLater takes lightning e, real delay returns nothing
call LightningDestroyer.destroyLater(e, delay)
endfunction
//--------------- Item ------------
struct ItemDestroyer
item itemToDestroy
private method destroy takes nothing returns nothing
call RemoveItem(this.itemToDestroy)
set this.itemToDestroy = null
call this.deallocate()
endmethod
private static method destroyFromTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.destroy()
call ReleaseTimer(t)
set t = null
endmethod
static method destroyLater takes item i, real delay returns nothing
local thistype this = allocate()
local timer t = NewTimerEx(this)
call TimerStart(t, delay, false, function thistype.destroyFromTimer)
set this.itemToDestroy = i
set t = null
endmethod
endstruct
function DestroyItemLater takes item i, real delay returns nothing
call ItemDestroyer.destroyLater(i, delay)
endfunction
function DestroyLastCreatedItemLater takes real delay returns nothing
call ItemDestroyer.destroyLater(bj_lastCreatedItem, delay)
endfunction
endlibrary
library MetroidvaniaUi requires MetroidvaniaUtils, VersionManager
globals
constant real UI_UPDATE_FREQUENCY = 0.05
constant integer UI_TARGET_DEAD_TICKS_DESELECT = R2I(4.0 / UI_UPDATE_FREQUENCY)
trigger ui_show_ui_tooltip_trig = null
trigger ui_hide_tooltip_trig = null
trigger ui_core_bonus_trig = null
framehandle ui_mainBackDrop = null
framehandle ui_portrait = null
framehandle ui_hpBarFull = null
framehandle ui_hpBarEmpty = null
framehandle ui_hpBarText = null
framehandle ui_energyBarFull = null
framehandle ui_energyBarEmpty = null
framehandle ui_energyBarText = null
framehandle ui_statsBackdrop = null
framehandle ui_targetBackdrop = null
framehandle array ui_frame
string array ui_strings
real ui_stat_text_scale = 1.0
//Even numbers are the icon, odd (+1) is the cooldown indicator
constant integer UI_FRAMES_PER_ABILITY = 3
constant integer UI_ABIL_BTN_OFFSET = 0
constant integer UI_ABIL_ICON_OFFSET = 1
constant integer UI_ABIL_COOLDOWN_OFFSET = 2
constant integer UI_ATTACK = 0
constant integer UI_ATTACK_ALT = UI_ATTACK + UI_FRAMES_PER_ABILITY
constant integer UI_UTIL1 = UI_ATTACK_ALT + UI_FRAMES_PER_ABILITY
constant integer UI_UTIL2 = UI_UTIL1 + UI_FRAMES_PER_ABILITY
constant integer UI_DASH = UI_UTIL2 + UI_FRAMES_PER_ABILITY
constant integer UI_PASSIVE1 = UI_DASH + UI_FRAMES_PER_ABILITY
constant integer UI_PASSIVE2 = UI_PASSIVE1 + UI_FRAMES_PER_ABILITY
constant integer UI_INTERACT = UI_PASSIVE2 + UI_FRAMES_PER_ABILITY
constant integer UI_MAP = UI_INTERACT + 3
constant integer UI_STATS = UI_MAP + 3
//8 stats, 4 frames per stat (Icon, text, icon, button)
constant integer UI_STATS_BASE = 30
constant integer UI_STATS_FRAMES_PER = 4
constant integer UI_STATS_HP = UI_STATS_BASE + 0 * UI_STATS_FRAMES_PER
constant integer UI_STATS_HP_REPAIR = UI_STATS_BASE + 1 * UI_STATS_FRAMES_PER
constant integer UI_STATS_ENERGY = UI_STATS_BASE + 2 * UI_STATS_FRAMES_PER
constant integer UI_STATS_ENERGY_RECHARGE = UI_STATS_BASE + 3 * UI_STATS_FRAMES_PER
constant integer UI_STATS_FIREPOWER = UI_STATS_BASE + 4 * UI_STATS_FRAMES_PER
constant integer UI_STATS_SYSTEM_POWER = UI_STATS_BASE + 5 * UI_STATS_FRAMES_PER
constant integer UI_STATS_SYSTEM_SPEED = UI_STATS_BASE + 6 * UI_STATS_FRAMES_PER
constant integer UI_STATS_MOBILITY = UI_STATS_BASE + 7 * UI_STATS_FRAMES_PER
constant integer UI_STATS_CORES_AVAILABLE = UI_STATS_BASE + 8 * UI_STATS_FRAMES_PER
constant integer UI_TOOLTIP_FRAME = 70
constant integer UI_TOOLTIP_HEADER = UI_TOOLTIP_FRAME + 1
constant integer UI_TOOLTIP_TEXT = UI_TOOLTIP_FRAME + 2
constant integer UI_TOOLTIP_MANA = UI_TOOLTIP_FRAME + 3
constant integer UI_TOOLTIP_COOLDOWN = UI_TOOLTIP_FRAME + 4
constant integer UI_TOOLTIP_MANA_ICON_FRAME = UI_TOOLTIP_FRAME + 5
constant integer UI_TOOLTIP_COOLDOWN_ICON_FRAME = UI_TOOLTIP_FRAME + 6
constant integer UI_TARGET_START = 80
constant integer UI_TARGET_NAME = UI_TARGET_START + 1
constant integer UI_TARGET_HP_ICON = UI_TARGET_START + 2
constant integer UI_TARGET_HP_BAR_EMPTY = UI_TARGET_START + 3
constant integer UI_TARGET_HP_BAR_FULL = UI_TARGET_START + 4
constant integer UI_TARGET_HP_BAR_TEXT = UI_TARGET_START + 5
constant integer UI_TARGET_DAMAGE_ICON = UI_TARGET_START + 6
constant integer UI_TARGET_DAMAGE_TEXT = UI_TARGET_START + 7
constant integer UI_TARGET_MOVEMENT_ICON = UI_TARGET_START + 8
constant integer UI_TARGET_MOVEMENT_TEXT = UI_TARGET_START + 9
constant string ICONS = "Icons\\"
constant string ICON_NONE = "Icons\\v2\\None.blp"
constant string ICON_COOLDOWN = "cooldownBorder.blp"
constant string ICON_INTERACT = "Icons\\Interact.blp"
constant string ICON_MAP = "Icons\\Map.blp"
constant string ICON_STATS_OPEN = "Icons\\StatsOpen.blp"
constant string ICON_STATS_CLOSE = "Icons\\StatsClose.blp"
constant string STATHEX_HP = "UI\\hexv2\\Hp_s.blp"
constant string STATHEX_HP_REPAIR = "UI\\hexv2\\RepairRate.blp"
constant string STATHEX_ENERGY = "UI\\hexv2\\EnergyStorage.blp"
constant string STATHEX_ENERGY_CHARGE = "UI\\hexv2\\EnergyRecharge.blp"
constant string STATHEX_FIREPOWER = "UI\\hexv2\\Firepower.blp"
constant string STATHEX_SYSTEM_POWER = "UI\\hexv2\\SystemEnergy.blp"
constant string STATHEX_SYSTEM_SPEED = "UI\\hexv2\\SystemSpeed.blp"
constant string STATHEX_MOBILITY = "UI\\hexv2\\Mobility.blp"
constant string STAT_BTN_ADD = "Icons\\StatsUpgrade.blp"
constant string STAT_BTN_ADD_DIS = "Icons\\StatsUpgradeDisabled.blp"
constant real UI_STAT_ICON_W = 0.0130
constant real UI_STAT_ICON_H = 0.0150
constant real UI_STAT_ICON_ADD_SIZE = 0.013
constant real UI_STAT_INSET_SIDES = 0.0065
constant real UI_STAT_INSET_TOP = 0.0065
constant integer COOLDOWN_ALPHA = 200
constant real UI_MAIN_LEFT = 0.28
constant real UI_MAIN_RIGHT = 0.52
constant real UI_MAIN_TOP = 0.09
constant real UI_MAIN_BOT = 0.0
constant real UI_MAIN_INSET = 0.009
constant real UI_MAIN_PADDING = 0.002
constant real UI_ICONS_TOP = UI_MAIN_TOP - 0.85 * UI_MAIN_INSET
constant real UI_ICONS_LEFT = UI_MAIN_LEFT + UI_MAIN_INSET
constant real UI_ICONS_SIZE = 0.028
constant real UI_ICONS_EXTRAS_SIZE = 0.0155
constant real UI_ICONS_EXTRAS_RIGHT = UI_MAIN_RIGHT - 0.8 * UI_MAIN_INSET
constant real UI_ICONS_EXTRAS_LEFT = UI_ICONS_EXTRAS_RIGHT - UI_ICONS_EXTRAS_SIZE
constant real UI_STATS_LEFT = UI_MAIN_RIGHT - 0.9 * UI_MAIN_INSET
constant real UI_STATS_RIGHT = UI_MAIN_RIGHT + 0.10
constant real UI_STATS_CENTER = UI_STATS_LEFT + (UI_STATS_RIGHT - UI_STATS_LEFT) / 2.
constant real UI_TARGET_LEFT = UI_MAIN_LEFT - 0.11
constant real UI_TARGET_RIGHT = UI_MAIN_LEFT + UI_MAIN_INSET
constant real UI_BAR_ICON_SIZE_W = 0.0182
constant real UI_BAR_ICON_SIZE_H = 0.0208
constant real UI_BAR_ICON_LEFT = UI_MAIN_LEFT + UI_MAIN_INSET
constant real UI_BAR_ICON_RIGHT = UI_BAR_ICON_LEFT + UI_BAR_ICON_SIZE_W
constant real UI_BAR_LEFT = UI_BAR_ICON_LEFT + UI_BAR_ICON_SIZE_W
constant real UI_BAR_RIGHT = UI_ICONS_EXTRAS_LEFT - 0.5 * UI_MAIN_PADDING
constant real UI_BAR_ENERGY_BOT = UI_MAIN_PADDING * 1.5
constant real UI_BAR_ENERGY_TOP = UI_BAR_ENERGY_BOT + UI_BAR_ICON_SIZE_H
constant real UI_BAR_HP_BOT = UI_BAR_ENERGY_TOP + 0.5 * UI_MAIN_PADDING
constant real UI_BAR_HP_TOP = UI_BAR_HP_BOT + UI_BAR_ICON_SIZE_H
constant real UI_TOOLTIP_BASE_W = 0.14
constant real UI_TOOLTIP_BASE_H = 0.064
constant real UI_TOOLTIP_INSET_TOP = 0.008
constant real UI_TOOLTIP_INSET_SIDES = 0.0045
constant real UI_TOOLTIP_HEADER_HEIGHT = 0.01
constant real UI_TOOLTIP_ICON_W = 0.009
constant real UI_TOOLTIP_ICON_H = 0.01
constant string UI_TOOLTIP_MANA_ICON_PATH = "UI\\hexv2\\EnergyRecharge.blp"
constant string UI_TOOLTIP_COOLDOWN_ICON_PATH = "UI\\hexv2\\SystemSpeed.blp"
integer targetDeadTicks
timer targetDeadTimer
endglobals
function frameToFrameId takes framehandle f returns integer
if f == ui_frame[UI_STATS] then
return UI_STATS
elseif f == ui_frame[UI_MAP] then
return UI_MAP
elseif f == ui_hpBarText then
return 0
elseif f == ui_energyBarText then
return 2
elseif f == ui_frame[UI_STATS_HP] then
return UI_STATS_HP
elseif f == ui_frame[UI_STATS_HP+2] then
return UI_STATS_HP+2
elseif f == ui_frame[UI_STATS_HP_REPAIR] then
return UI_STATS_HP_REPAIR
elseif f == ui_frame[UI_STATS_HP_REPAIR+2] then
return UI_STATS_HP_REPAIR+2
elseif f == ui_frame[UI_STATS_ENERGY] then
return UI_STATS_ENERGY
elseif f == ui_frame[UI_STATS_ENERGY+2] then
return UI_STATS_ENERGY+2
elseif f == ui_frame[UI_STATS_ENERGY_RECHARGE] then
return UI_STATS_ENERGY_RECHARGE
elseif f == ui_frame[UI_STATS_ENERGY_RECHARGE+2] then
return UI_STATS_ENERGY_RECHARGE+2
elseif f == ui_frame[UI_STATS_FIREPOWER] then
return UI_STATS_FIREPOWER
elseif f == ui_frame[UI_STATS_FIREPOWER+2] then
return UI_STATS_FIREPOWER+2
elseif f == ui_frame[UI_STATS_SYSTEM_POWER] then
return UI_STATS_SYSTEM_POWER
elseif f == ui_frame[UI_STATS_SYSTEM_POWER+2] then
return UI_STATS_SYSTEM_POWER+2
elseif f == ui_frame[UI_STATS_SYSTEM_SPEED] then
return UI_STATS_SYSTEM_SPEED
elseif f == ui_frame[UI_STATS_SYSTEM_SPEED+2] then
return UI_STATS_SYSTEM_SPEED+2
elseif f == ui_frame[UI_STATS_MOBILITY] then
return UI_STATS_MOBILITY
elseif f == ui_frame[UI_STATS_MOBILITY+2] then
return UI_STATS_MOBILITY+2
elseif f == ui_frame[UI_STATS_CORES_AVAILABLE] then
return UI_STATS_CORES_AVAILABLE
elseif f == ui_frame[UI_TARGET_DAMAGE_TEXT] then
return UI_TARGET_DAMAGE_TEXT
elseif f == ui_frame[UI_TARGET_MOVEMENT_TEXT] then
return UI_TARGET_MOVEMENT_TEXT
endif
return 0
endfunction
function ui2Stat takes integer uiStatIndex returns integer
if uiStatIndex == UI_STATS_HP then
return STAT_INDEX_HP
elseif uiStatIndex == UI_STATS_HP_REPAIR then
return STAT_INDEX_HP_REPAIR
elseif uiStatIndex == UI_STATS_ENERGY then
return STAT_INDEX_ENERGY
elseif uiStatIndex == UI_STATS_ENERGY_RECHARGE then
return STAT_INDEX_ENERGY_RECHARGE
elseif uiStatIndex == UI_STATS_FIREPOWER then
return STAT_INDEX_FIREPOWER
elseif uiStatIndex == UI_STATS_SYSTEM_POWER then
return STAT_INDEX_SYSTEM_POWER
elseif uiStatIndex == UI_STATS_SYSTEM_SPEED then
return STAT_INDEX_SYSTEM_SPEED
elseif uiStatIndex == UI_STATS_MOBILITY then
return STAT_INDEX_MOBILITY
endif
return -1
endfunction
function ui_statIndexToString takes integer statIndex returns string
return Si2Name(ui2Stat(statIndex))
endfunction
function frameToHeroAction takes framehandle actionButton, player p returns HeroActionBase
if actionButton == ui_frame[UI_ATTACK] then
return MetroidvaniaController.getMetroidvaniaController(p).getAttack()
elseif actionButton == ui_frame[UI_ATTACK_ALT] then
return MetroidvaniaController.getMetroidvaniaController(p).getAttackAlt()
elseif actionButton == ui_frame[UI_UTIL1] then
return MetroidvaniaController.getMetroidvaniaController(p).getUtil1()
elseif actionButton == ui_frame[UI_UTIL2] then
return MetroidvaniaController.getMetroidvaniaController(p).getUtil2()
elseif actionButton == ui_frame[UI_DASH] then
return MetroidvaniaController.getMetroidvaniaController(p).getDash()
elseif actionButton == ui_frame[UI_PASSIVE1] then
//return MetroidvaniaController.getMetroidvaniaController(p).getAttackAlt()
elseif actionButton == ui_frame[UI_PASSIVE2] then
//return MetroidvaniaController.getMetroidvaniaController(p).getAttackAlt()
elseif actionButton == ui_frame[UI_INTERACT] then
return MetroidvaniaController.getMetroidvaniaController(p).getInteract()
endif
return 0
endfunction
//function uiFieldValue takes integer fieldBase,
function setUiVisible takes boolean visibile returns nothing
call BlzFrameSetVisible(ui_mainBackDrop, visibile)
call BlzFrameSetVisible(ui_statsBackdrop, visibile)
call BlzFrameSetVisible(ui_targetBackdrop, visibile)
call BlzFrameSetVisible(ui_hpBarFull, visibile)
call BlzFrameSetVisible(ui_energyBarFull, visibile)
endfunction
function updateApplyCoreBtn takes player p, integer coresAvailable, integer uiStatIndex returns nothing
if coresAvailable >= HeroStatsCore.coreCostForNext(p, ui2Stat(uiStatIndex)) then
call BlzFrameSetTexture(ui_frame[uiStatIndex+3], STAT_BTN_ADD, 0, true)
call BlzFrameSetEnable(ui_frame[uiStatIndex+2], true)
else
call BlzFrameSetTexture(ui_frame[uiStatIndex+3], STAT_BTN_ADD_DIS, 0, true)
call BlzFrameSetEnable(ui_frame[uiStatIndex+2], false)
endif
endfunction
function updateStatsPanel takes player p returns nothing
local unit u
local integer coresAvailable
if p == GetLocalPlayer() then
set u = MetroidvaniaController.getMetroidvaniaController(p).getHero()
set coresAvailable = HeroStatsCore.available(p)
call BlzFrameSetText(ui_frame[UI_STATS_HP], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_HP), u)))
call BlzFrameSetText(ui_frame[UI_STATS_HP_REPAIR], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_HP_REPAIR), u)))
call BlzFrameSetText(ui_frame[UI_STATS_ENERGY], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_ENERGY), u)))
call BlzFrameSetText(ui_frame[UI_STATS_ENERGY_RECHARGE], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_ENERGY_RECHARGE), u)))
call BlzFrameSetText(ui_frame[UI_STATS_FIREPOWER], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_FIREPOWER), u)))
call BlzFrameSetText(ui_frame[UI_STATS_SYSTEM_POWER], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_SYSTEM_POWER), u)))
call BlzFrameSetText(ui_frame[UI_STATS_SYSTEM_SPEED], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_SYSTEM_SPEED), u)))
call BlzFrameSetText(ui_frame[UI_STATS_MOBILITY], R2SHumanReadable(GetStat(ui2Stat(UI_STATS_MOBILITY), u)))
call BlzFrameSetText(ui_frame[UI_STATS_CORES_AVAILABLE], "Cores Available: " + I2S(coresAvailable))
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_HP)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_HP_REPAIR)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_ENERGY)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_ENERGY_RECHARGE)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_FIREPOWER)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_SYSTEM_POWER)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_SYSTEM_SPEED)
call updateApplyCoreBtn(p, coresAvailable, UI_STATS_MOBILITY)
endif
set u = null
endfunction
function updateLocalStatsPanel takes nothing returns nothing
call updateStatsPanel(GetLocalPlayer())
endfunction
//set udg_t_string1 = SubString("012345678", 0, 5) -> 01234
function updateBar takes framehandle barFrame, framehandle textFrame, real max, real curr returns nothing
local integer barValue = R2I(curr / max * 100. + 0.01)
call BlzFrameSetText(textFrame, "|cffffffff" + R2I2S(curr) + "/" + R2I2S(max))
call BlzFrameSetValue(barFrame, barValue)
endfunction
function updateCooldown takes integer uiButtonIndex, real totalCooldown, real remainingCooldown returns nothing
local integer cdValue = R2I(remainingCooldown / totalCooldown * 100. + 0.01)
call BlzFrameSetValue(ui_frame[uiButtonIndex + UI_ABIL_COOLDOWN_OFFSET], cdValue)
endfunction
function clearUiTarget takes MetroidvaniaController controller returns nothing
call BlzFrameSetText(ui_frame[UI_TARGET_NAME], "Target: N/A")
call BlzFrameSetText(ui_frame[UI_TARGET_HP_BAR_TEXT], "N/A")
call BlzFrameSetText(ui_frame[UI_TARGET_DAMAGE_TEXT], "N/A")
call BlzFrameSetText(ui_frame[UI_TARGET_MOVEMENT_TEXT], "N/A")
call BlzFrameSetValue(ui_frame[UI_TARGET_HP_BAR_FULL], 0)
endfunction
function updateTarget takes MetroidvaniaController controller returns nothing
local unit u = controller.getTargetUnit()
call updateBar(ui_frame[UI_TARGET_HP_BAR_FULL], ui_frame[UI_TARGET_HP_BAR_TEXT], GetMaxHp(u), GetHp(u))
call BlzFrameSetText(ui_frame[UI_TARGET_DAMAGE_TEXT], R2I2S(GetDamage(u)))
call BlzFrameSetText(ui_frame[UI_TARGET_MOVEMENT_TEXT], R2I2S(GetUnitMoveSpeed(u)))
if UnitAlive(u) then
set targetDeadTicks = 0
else
set targetDeadTicks = targetDeadTicks + 1
//If dead for more than X ticks, deselect
if targetDeadTicks > UI_TARGET_DEAD_TICKS_DESELECT then
call controller.clearTarget()
call clearUiTarget(controller)
endif
endif
endfunction
function setUiTarget takes unit u returns nothing
call BlzFrameSetText(ui_frame[UI_TARGET_NAME], "Target: " + GetUnitName(u))
call BlzFrameSetText(ui_frame[UI_TARGET_DAMAGE_TEXT], R2I2S(GetDamage(u)))
call BlzFrameSetText(ui_frame[UI_TARGET_MOVEMENT_TEXT], R2I2S(GetUnitMoveSpeed(u)))
set targetDeadTicks = 0
endfunction
function updateTargetField takes MetroidvaniaController controller, integer fieldIndex returns nothing
endfunction
function updateUi takes nothing returns nothing
local player p = GetLocalPlayer()
local MetroidvaniaController controller = MetroidvaniaController.getMetroidvaniaController(p)
local unit u = controller.getHero()
call updateBar(ui_hpBarFull, ui_hpBarText, GetMaxHp(u), GetHp(u))
call updateBar(ui_energyBarFull, ui_energyBarText, GetMaxMana(u), GetMana(u))
if controller.getTargetUnit() != null then
call updateTarget(controller)
endif
set u = null
set p = null
endfunction
//---- UI TOOPTIPS
function updateUITooltip takes player p, framehandle f returns nothing
local integer frameId = frameToFrameId(f)
call BlzFrameSetPoint(ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOM, f, FRAMEPOINT_TOP, 0., UI_MAIN_PADDING)
call BlzFrameSetSize(ui_frame[UI_TOOLTIP_FRAME], UI_TOOLTIP_BASE_W, UI_TOOLTIP_BASE_H - 1.5 * UI_TOOLTIP_ICON_H + 0.0003 * StringLength(ui_strings[frameId+1]))
call BlzFrameSetText(ui_frame[UI_TOOLTIP_HEADER], ui_strings[frameId])
call BlzFrameSetText(ui_frame[UI_TOOLTIP_TEXT], ui_strings[frameId+1])
endfunction
function showUITooltip takes nothing returns nothing
if GetTriggerPlayer() == GetLocalPlayer() then
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_FRAME], true)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_MANA], false)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_MANA_ICON_FRAME], false)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_COOLDOWN], false)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_COOLDOWN_ICON_FRAME], false)
call updateUITooltip(GetTriggerPlayer(), BlzGetTriggerFrame())
endif
endfunction
// ---- ABILITY-TOOLTIP ----
function updateTooltipContent takes string header, string text, real manacost, real cooldown returns nothing
call BlzFrameSetText(ui_frame[UI_TOOLTIP_HEADER], header)
call BlzFrameSetText(ui_frame[UI_TOOLTIP_TEXT], text)
if manacost == 0. then
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_MANA], false)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_MANA_ICON_FRAME], false)
else
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_MANA], true)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_MANA_ICON_FRAME], true)
call BlzFrameSetText(ui_frame[UI_TOOLTIP_MANA], R2SHumanReadable(manacost))
endif
if cooldown == 0. then
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_COOLDOWN], false)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_COOLDOWN_ICON_FRAME], false)
else
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_COOLDOWN], true)
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_COOLDOWN_ICON_FRAME], true)
call BlzFrameSetText(ui_frame[UI_TOOLTIP_COOLDOWN], R2SW(cooldown, 0, 2) + " seconds")
endif
endfunction
function updateTooltipMain takes player p, framehandle frame returns nothing
local HeroActionBase a = frameToHeroAction(frame, p)
local integer tooltipLength
if a != 0 then
set tooltipLength = StringLength(a.abilityText())
call BlzFrameSetPoint(ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOM, frame, FRAMEPOINT_TOP, 0., UI_MAIN_PADDING)
call BlzFrameSetSize(ui_frame[UI_TOOLTIP_FRAME], UI_TOOLTIP_BASE_W, UI_TOOLTIP_BASE_H + 0.000255 * tooltipLength)
call updateTooltipContent(a.abilityName(), a.abilityText(), a.energyCost, a.cooldownDetails.actionCooldown)
else
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_FRAME], false)
endif
endfunction
function updateTooltipTrigFunc takes nothing returns nothing
if GetTriggerPlayer() == GetLocalPlayer() then
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_FRAME], true)
call updateTooltipMain(GetTriggerPlayer(), BlzGetTriggerFrame())
endif
endfunction
function hideTooltipMouseLeaveButton takes nothing returns nothing
//call BJDebugMsg("Hide tooltip! " + I2S(GetRandomInt(0, 999)))
if GetTriggerPlayer() == GetLocalPlayer() then
call BlzFrameSetVisible(ui_frame[UI_TOOLTIP_FRAME], false)
endif
endfunction
function clickUIButton takes nothing returns nothing
local player p = GetTriggerPlayer()
local framehandle f = BlzGetTriggerFrame()
call MetroidvaniaController.getMetroidvaniaController(p).tryToExecuteAction(frameToHeroAction(f, p))
if p == GetLocalPlayer() then
call BlzFrameSetEnable(f, false)
call BlzFrameSetEnable(f, true)
endif
set p = null
set f = null
endfunction
//---- TOOLTIP END ----
//-------------- Toggle Stats Planel ------------
function clickToggleStatsPanel takes nothing returns nothing
call BlzFrameSetEnable(ui_frame[UI_STATS], false)
call BlzFrameSetEnable(ui_frame[UI_STATS], true)
if GetTriggerPlayer() == GetLocalPlayer() then
if BlzFrameIsVisible(ui_statsBackdrop) then
call BlzFrameSetVisible(ui_statsBackdrop, false)
call BlzFrameSetTexture(ui_frame[UI_STATS+2], ICON_STATS_OPEN, 0, true)
else
call updateStatsPanel(GetTriggerPlayer())
call BlzFrameSetVisible(ui_statsBackdrop, true)
call BlzFrameSetTexture(ui_frame[UI_STATS+2], ICON_STATS_CLOSE, 0, true)
endif
endif
endfunction
function addStatFrames takes integer frameBase, string iconPath, integer placementX, integer placementY returns nothing
local real yTop = -UI_STAT_INSET_TOP - (1+placementY) * (UI_STAT_ICON_H + UI_MAIN_PADDING)
local real yBottom = yTop - UI_STAT_ICON_H
local real yCenter = (yTop + yBottom) / 2.0
local integer iconIndex = frameBase+1
local integer textIndex = frameBase
local integer buttonIndex = frameBase+2
local integer btnIconIndex = frameBase+3
//call BlzFrameSetAbsPoint(ui_statsBackdrop, FRAMEPOINT_TOPLEFT, UI_STATS_LEFT, UI_MAIN_TOP)
//call BlzFrameSetAbsPoint(ui_statsBackdrop, FRAMEPOINT_BOTTOMRIGHT, UI_STATS_RIGHT, UI_MAIN_BOT)
local real xBase = UI_STAT_INSET_SIDES
local real xButtonCenter
//--- LEFT COLUMN ---
if placementX == 0 then
set ui_frame[iconIndex] = BlzCreateFrameByType("BACKDROP", "stat" + I2S(frameBase), ui_statsBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[iconIndex], FRAMEPOINT_TOPLEFT, ui_statsBackdrop, FRAMEPOINT_TOPLEFT, xBase, yTop)
call BlzFrameSetPoint(ui_frame[iconIndex], FRAMEPOINT_BOTTOMRIGHT, ui_statsBackdrop, FRAMEPOINT_TOPLEFT, xBase + UI_STAT_ICON_W, yBottom)
call BlzFrameSetTexture(ui_frame[iconIndex], iconPath, 0, true)
set xButtonCenter = - UI_MAIN_PADDING - UI_STAT_ICON_ADD_SIZE / 2.
set xBase = UI_STATS_LEFT + xBase + UI_STAT_ICON_W
set ui_frame[textIndex] = BlzCreateFrameByType("TEXT", "statText" + I2S(frameBase), ui_statsBackdrop, "", 0)
call BlzFrameSetScale(ui_frame[textIndex], ui_stat_text_scale)
call BlzFrameSetAbsPoint(ui_frame[textIndex], FRAMEPOINT_TOPLEFT, xBase, UI_MAIN_TOP + yTop)
call BlzFrameSetAbsPoint(ui_frame[textIndex], FRAMEPOINT_BOTTOMRIGHT, UI_STATS_CENTER + xButtonCenter - UI_STAT_ICON_ADD_SIZE / 2., UI_MAIN_TOP + yBottom)
call BlzFrameSetText(ui_frame[textIndex], "17")
//call BlzFrameSetEnable(ui_frame[textIndex], false)
call BlzFrameSetTextAlignment(ui_frame[textIndex], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
//call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[textIndex], FRAMEEVENT_MOUSE_ENTER)
//call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[textIndex], FRAMEEVENT_MOUSE_LEAVE)
//set xBase = - UI_MAIN_PADDING - UI_STAT_ICON_ADD_SIZE / 2.
set ui_frame[buttonIndex] = BlzCreateFrame("ScriptDialogButton", ui_statsBackdrop, 0, 0)
call BlzFrameSetPoint(ui_frame[buttonIndex], FRAMEPOINT_CENTER, ui_statsBackdrop, FRAMEPOINT_TOP, xButtonCenter, yCenter)
call BlzFrameSetSize(ui_frame[buttonIndex], UI_STAT_ICON_ADD_SIZE, UI_STAT_ICON_ADD_SIZE)
//call BlzFrameSetPoint(ui_frame[buttonIndex], FRAMEPOINT_BOTTOMRIGHT, ui_statsBackdrop, FRAMEPOINT_TOP, xBase + UI_STAT_ICON_H, yBottom)
set ui_frame[btnIconIndex] = BlzCreateFrameByType("BACKDROP", "statPlusBackdrop" + I2S(frameBase), ui_frame[buttonIndex], "", 0)
call BlzFrameSetAllPoints(ui_frame[btnIconIndex], ui_frame[buttonIndex])
call BlzFrameSetTexture(ui_frame[btnIconIndex], STAT_BTN_ADD, 0, true)
else //--- RIGHT COLUMN ---
set xBase = UI_MAIN_PADDING
set ui_frame[iconIndex] = BlzCreateFrameByType("BACKDROP", "stat" + I2S(frameBase), ui_statsBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[iconIndex], FRAMEPOINT_TOPLEFT, ui_statsBackdrop, FRAMEPOINT_TOP, xBase, yTop)
call BlzFrameSetPoint(ui_frame[iconIndex], FRAMEPOINT_BOTTOMRIGHT, ui_statsBackdrop, FRAMEPOINT_TOP, xBase + UI_STAT_ICON_W, yBottom)
call BlzFrameSetTexture(ui_frame[iconIndex], iconPath, 0, true)
set xButtonCenter = -UI_STAT_INSET_SIDES - UI_STAT_ICON_ADD_SIZE * 0.52
set xBase = UI_STATS_CENTER + UI_STAT_ICON_W + UI_MAIN_PADDING
set ui_frame[textIndex] = BlzCreateFrameByType("TEXT", "statText" + I2S(frameBase), ui_statsBackdrop, "", 0)
call BlzFrameSetScale(ui_frame[textIndex], ui_stat_text_scale)
call BlzFrameSetAbsPoint(ui_frame[textIndex], FRAMEPOINT_TOPLEFT, xBase, UI_MAIN_TOP + yTop)
call BlzFrameSetAbsPoint(ui_frame[textIndex], FRAMEPOINT_BOTTOMRIGHT, UI_STATS_RIGHT + xButtonCenter - 0.5 * UI_STAT_ICON_ADD_SIZE, UI_MAIN_TOP + yBottom)
//call BlzFrameSetPoint(ui_frame[textIndex], FRAMEPOINT_TOPLEFT, ui_statsBackdrop, FRAMEPOINT_TOP, xBase, yTop)
//call BlzFrameSetPoint(ui_frame[textIndex], FRAMEPOINT_BOTTOMRIGHT, ui_statsBackdrop, FRAMEPOINT_TOPRIGHT, -UI_STAT_ICON_H - UI_MAIN_PADDING - UI_STAT_INSET_SIDES, yBottom)
call BlzFrameSetText(ui_frame[textIndex], "4711")
//call BlzFrameSetEnable(ui_frame[textIndex], false)
call BlzFrameSetTextAlignment(ui_frame[textIndex], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
//call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[textIndex], FRAMEEVENT_MOUSE_ENTER)
//call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[textIndex], FRAMEEVENT_MOUSE_LEAVE)
set ui_frame[buttonIndex] = BlzCreateFrame("ScriptDialogButton", ui_statsBackdrop, 0, 0)
call BlzFrameSetPoint(ui_frame[buttonIndex], FRAMEPOINT_CENTER, ui_statsBackdrop, FRAMEPOINT_TOPRIGHT, xButtonCenter, yCenter)
call BlzFrameSetSize(ui_frame[buttonIndex], UI_STAT_ICON_ADD_SIZE, UI_STAT_ICON_ADD_SIZE)
//set xBase = -UI_STAT_INSET_SIDES -UI_STAT_ICON_H - UI_MAIN_PADDING
//call BlzFrameSetPoint(ui_frame[frameBase+2], FRAMEPOINT_TOPLEFT, ui_statsBackdrop, FRAMEPOINT_TOPRIGHT, xBase, yTop)
//call BlzFrameSetPoint(ui_frame[frameBase+2], FRAMEPOINT_BOTTOMRIGHT, ui_statsBackdrop, FRAMEPOINT_TOPRIGHT, xBase + UI_STAT_ICON_H, yBottom)
set ui_frame[btnIconIndex] = BlzCreateFrameByType("BACKDROP", "statPlusBackdrop" + I2S(frameBase), ui_frame[buttonIndex], "", 0)
call BlzFrameSetAllPoints(ui_frame[btnIconIndex], ui_frame[buttonIndex])
call BlzFrameSetTexture(ui_frame[btnIconIndex], STAT_BTN_ADD, 0, true)
endif
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[textIndex], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[buttonIndex], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[textIndex], FRAMEEVENT_MOUSE_LEAVE)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[buttonIndex], FRAMEEVENT_MOUSE_LEAVE)
call BlzTriggerRegisterFrameEvent(ui_core_bonus_trig, ui_frame[buttonIndex], FRAMEEVENT_CONTROL_CLICK)
//set ui_frame[i] = BlzCreateFrame("ScriptDialogButton", ui_mainBackDrop, 0, 0)
//call BlzTriggerRegisterFrameEvent(abilityTooltipMouseEnterTrig, ui_frame[frameBase+3], FRAMEEVENT_MOUSE_ENTER)
//call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[frameBase+3], FRAMEEVENT_MOUSE_LEAVE)
//call BlzTriggerRegisterFrameEvent(abilityClickTrig, ui_frame[frameBase+3], FRAMEEVENT_MOUSE_UP)
endfunction
function getPlusButtonText takes integer statIndex, integer coreCost, real bonusPerCore returns string
return "Consume " + I2S(coreCost) + " cores to increase the " + ui_statIndexToString(statIndex) + " by " + R2SHumanReadable(bonusPerCore) + "."
endfunction
function coreBonusButton takes nothing returns nothing
local player p = GetTriggerPlayer()
local framehandle f = BlzGetTriggerFrame()
local integer frameId = frameToFrameId(f) - 2 //button is +2 compared to "base"
local integer statIndex = ui2Stat(frameId)
local integer costForNext
call BlzFrameSetEnable(f, false)
call BlzFrameSetEnable(f, true)
call HeroStatsCore.applyCore(MetroidvaniaController.getMetroidvaniaController(p).getHero(), statIndex)
set costForNext = HeroStatsCore.coreCostForNext(p, statIndex)
if p == GetLocalPlayer() then
set ui_strings[frameId+3] = getPlusButtonText(frameId, costForNext, statIndexToBonusAmount(statIndex))
call updateStatsPanel(p)
call updateApplyCoreBtn(p, HeroStatsCore.available(p), frameId)
call BlzFrameSetText(ui_frame[UI_TOOLTIP_HEADER], ui_strings[frameId+2])
call BlzFrameSetText(ui_frame[UI_TOOLTIP_TEXT], ui_strings[frameId+3])
endif
set f = null
set p = null
endfunction
function hideBaseGui takes nothing returns nothing
call BlzEnableUIAutoPosition(false)
call BlzHideOriginFrames(true)
call BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), FRAMEPOINT_BOTTOM, 0.4, -0.18)
//if IsReforged() then
call BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
//else
call BlzFrameSetAllPoints(BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0))
//endif
endfunction
//---------------------------------------------- MAIN INIT -----------------------
function uiInit takes nothing returns nothing
local framehandle tmp
local integer i = 0
local real xBase
local real yBase
local trigger abilityClickTrig = CreateTrigger()
local trigger statsClickToggleTrig = CreateTrigger()
local trigger abilityTooltipMouseEnterTrig = CreateTrigger()
if IsReforged() then
set ui_stat_text_scale = 0.9
endif
//set ui_stat_text_scale = 0.8
//call BJDebugMsg("ui_stat_text_scale=" + R2S(ui_stat_text_scale))
set ui_show_ui_tooltip_trig = CreateTrigger()
set ui_hide_tooltip_trig = CreateTrigger()
set ui_core_bonus_trig = CreateTrigger()
set targetDeadTimer = CreateTimer()
call TriggerAddAction(abilityTooltipMouseEnterTrig, function updateTooltipTrigFunc)
call TriggerAddAction(ui_show_ui_tooltip_trig, function showUITooltip)
call TriggerAddAction(ui_hide_tooltip_trig, function hideTooltipMouseLeaveButton)
call TriggerAddAction(abilityClickTrig, function clickUIButton)
call TriggerAddAction(statsClickToggleTrig, function clickToggleStatsPanel)
call TriggerAddAction(ui_core_bonus_trig, function coreBonusButton)
set ui_strings[UI_STATS] = "Stats"
set ui_strings[UI_STATS+1] = "Opens/Closes the stats panel."
set ui_strings[UI_MAP] = "Map"
set ui_strings[UI_MAP+1] = "Opens/Closes the map panel. Issuing any order also closes the map."
set ui_strings[0] = ui_statIndexToString(UI_STATS_HP)
set ui_strings[0+1] = "Your current Structural Integrity.|nHits taken lowers it. Upon reaching 0, your current body is destroyed and you will be reassembled at the last activated Emergency Backup Location after a few seconds."
set ui_strings[2] = ui_statIndexToString(UI_STATS_ENERGY)
set ui_strings[2+1] = "Your current Energy. Attacks and abilities consumes it. |nYou passivly regenerate energy based on your " + ui_statIndexToString(UI_STATS_ENERGY_RECHARGE) + "."
set ui_strings[UI_STATS_HP] = ui_strings[0]
set ui_strings[UI_STATS_HP+1] = ui_strings[0+1]
set ui_strings[UI_STATS_HP+2] = "Add " + ui_statIndexToString(UI_STATS_HP)
set ui_strings[UI_STATS_HP+3] = getPlusButtonText(UI_STATS_HP, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_HP)))
set ui_strings[UI_STATS_HP_REPAIR] = ui_statIndexToString(UI_STATS_HP_REPAIR)
set ui_strings[UI_STATS_HP_REPAIR+1] = "Your current repair-rate. Passivly restores " + ui_statIndexToString(UI_STATS_HP) + " per second."
set ui_strings[UI_STATS_HP_REPAIR+2] = "Increase " + ui_statIndexToString(UI_STATS_HP_REPAIR)
set ui_strings[UI_STATS_HP_REPAIR+3] = getPlusButtonText(UI_STATS_HP_REPAIR, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_HP_REPAIR)))
set ui_strings[UI_STATS_ENERGY] = ui_statIndexToString(UI_STATS_ENERGY)
set ui_strings[UI_STATS_ENERGY+1] = "Your Energy Capacity. Attacks and abilities drain some amount of energy. "
set ui_strings[UI_STATS_ENERGY+2] = "Increase " + ui_statIndexToString(UI_STATS_ENERGY)
set ui_strings[UI_STATS_ENERGY+3] = getPlusButtonText(UI_STATS_ENERGY, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_ENERGY)))
set ui_strings[UI_STATS_ENERGY_RECHARGE] = ui_statIndexToString(UI_STATS_ENERGY_RECHARGE)
set ui_strings[UI_STATS_ENERGY_RECHARGE+1] = "Passivly restores Energy per second."
set ui_strings[UI_STATS_ENERGY_RECHARGE+2] = "Increase " + ui_statIndexToString(UI_STATS_ENERGY_RECHARGE)
set ui_strings[UI_STATS_ENERGY_RECHARGE+3] = getPlusButtonText(UI_STATS_ENERGY_RECHARGE, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_ENERGY_RECHARGE)))
set ui_strings[UI_STATS_FIREPOWER] = ui_statIndexToString(UI_STATS_FIREPOWER)
set ui_strings[UI_STATS_FIREPOWER+1] = "Offensive attacks and abilities deal damage based on your " +ui_statIndexToString(UI_STATS_FIREPOWER)+ " and some times also " + ui_statIndexToString(UI_STATS_SYSTEM_POWER) +"."
set ui_strings[UI_STATS_FIREPOWER+2] = "Increase " + ui_statIndexToString(UI_STATS_FIREPOWER)
set ui_strings[UI_STATS_FIREPOWER+3] = getPlusButtonText(UI_STATS_FIREPOWER, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_FIREPOWER)))
set ui_strings[UI_STATS_SYSTEM_POWER] = ui_statIndexToString(UI_STATS_SYSTEM_POWER)
set ui_strings[UI_STATS_SYSTEM_POWER+1] = "Most abilities and some attacks increases their effectivness based on the " + ui_statIndexToString(UI_STATS_SYSTEM_POWER) + "."
set ui_strings[UI_STATS_SYSTEM_POWER+2] = "Increase " + ui_statIndexToString(UI_STATS_SYSTEM_POWER)
set ui_strings[UI_STATS_SYSTEM_POWER+3] = getPlusButtonText(UI_STATS_SYSTEM_POWER, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_SYSTEM_POWER)))
set ui_strings[UI_STATS_SYSTEM_SPEED] = ui_statIndexToString(UI_STATS_SYSTEM_SPEED)
set ui_strings[UI_STATS_SYSTEM_SPEED+1] = "Lowered cooldowns on attacks and abilities. Each point result in 1% more casts if you cast as soon as it is available. In other words: 100 points loweres cooldowns to 50% of their original."
set ui_strings[UI_STATS_SYSTEM_SPEED+2] = "Increase " + ui_statIndexToString(UI_STATS_SYSTEM_SPEED)
set ui_strings[UI_STATS_SYSTEM_SPEED+3] = getPlusButtonText(UI_STATS_SYSTEM_SPEED, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_SYSTEM_SPEED)))
set ui_strings[UI_STATS_MOBILITY] = ui_statIndexToString(UI_STATS_MOBILITY)
set ui_strings[UI_STATS_MOBILITY+1] = "Your movement speed in world-units per second."
set ui_strings[UI_STATS_MOBILITY+2] = "Increase " + ui_statIndexToString(UI_STATS_MOBILITY)
set ui_strings[UI_STATS_MOBILITY+3] = getPlusButtonText(UI_STATS_MOBILITY, 1, statIndexToBonusAmount(ui2Stat(UI_STATS_MOBILITY)))
set ui_strings[UI_STATS_CORES_AVAILABLE] = "Upgrade Cores"
set ui_strings[UI_STATS_CORES_AVAILABLE+1] = "Upgrade cores allows you to enhance your different stats. Every 3 cores applied to a single stat increases its cost by 1."
set ui_strings[UI_TARGET_DAMAGE_TEXT] = "Firepower"
set ui_strings[UI_TARGET_DAMAGE_TEXT+1] = "Typically the damage dealt by a regular attack by this target."
set ui_strings[UI_TARGET_MOVEMENT_TEXT] = "Mobility"
set ui_strings[UI_TARGET_MOVEMENT_TEXT+1] = "Movement Speed in world-units per second for this target."
set ui_portrait = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT, 0)
call BlzFrameSetAbsPoint(ui_portrait, FRAMEPOINT_TOPLEFT, 0.0, 0.0)
call BlzFrameSetAbsPoint(ui_portrait, FRAMEPOINT_BOTTOMRIGHT, -0.1, -0.1)
call BlzFrameSetVisible(ui_portrait, false)
set ui_mainBackDrop = BlzCreateFrameByType("BACKDROP", "ui_mainBackDrop", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetAbsPoint(ui_mainBackDrop, FRAMEPOINT_TOPLEFT, UI_MAIN_LEFT, UI_MAIN_TOP)
call BlzFrameSetAbsPoint(ui_mainBackDrop, FRAMEPOINT_BOTTOMRIGHT, UI_MAIN_RIGHT, UI_MAIN_BOT)
call BlzFrameSetTexture(ui_mainBackDrop, "UI\\ScifiCardX2RotCut.blp", 0, true)
call BlzFrameSetVisible(ui_mainBackDrop, false)
set ui_statsBackdrop = BlzCreateFrameByType("BACKDROP", "ui_statsBackdrop", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetAbsPoint(ui_statsBackdrop, FRAMEPOINT_TOPLEFT, UI_STATS_LEFT, UI_MAIN_TOP)
call BlzFrameSetAbsPoint(ui_statsBackdrop, FRAMEPOINT_BOTTOMRIGHT, UI_STATS_RIGHT, UI_MAIN_BOT)
call BlzFrameSetTexture(ui_statsBackdrop, "UI\\ScifiCardX3Cut.blp", 0, true)
call BlzFrameSetVisible(ui_statsBackdrop, false)
set ui_targetBackdrop = BlzCreateFrameByType("BACKDROP", "ui_targetBackdrop", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
call BlzFrameSetAbsPoint(ui_targetBackdrop, FRAMEPOINT_TOPLEFT, UI_TARGET_LEFT, UI_MAIN_TOP)
call BlzFrameSetAbsPoint(ui_targetBackdrop, FRAMEPOINT_BOTTOMRIGHT, UI_TARGET_RIGHT, UI_MAIN_BOT)
call BlzFrameSetTexture(ui_targetBackdrop, "UI\\ScifiCardX3Cut.blp", 0, true)
call BlzFrameSetVisible(ui_targetBackdrop, false)
//HP BAR
set ui_hpBarEmpty = BlzCreateFrameByType("BACKDROP", "hpEmpty", ui_mainBackDrop, "", 0)
call BlzFrameSetAbsPoint(ui_hpBarEmpty, FRAMEPOINT_TOPLEFT, UI_BAR_LEFT, UI_BAR_HP_TOP)
call BlzFrameSetAbsPoint(ui_hpBarEmpty, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_RIGHT, UI_BAR_HP_BOT)
call BlzFrameSetTexture(ui_hpBarEmpty, "UI\\ScifiBarEmpty.blp", 0, true)
set ui_hpBarFull = BlzCreateFrameByType("SIMPLESTATUSBAR", "hpFull", ui_hpBarEmpty, "", 0)
call BlzFrameSetTexture(ui_hpBarFull, "UI\\ScifiBarFull.blp", 0, true)
call BlzFrameSetAbsPoint(ui_hpBarFull, FRAMEPOINT_TOPLEFT, UI_BAR_LEFT, UI_BAR_HP_TOP)
call BlzFrameSetAbsPoint(ui_hpBarFull, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_RIGHT, UI_BAR_HP_BOT)
call BlzFrameSetValue(ui_hpBarFull, 0)
set ui_hpBarText = BlzCreateFrameByType("TEXT", "hpBarText", ui_mainBackDrop, "", 0)
call BlzFrameSetAbsPoint(ui_hpBarText, FRAMEPOINT_TOPLEFT, UI_BAR_LEFT, UI_BAR_HP_TOP)
call BlzFrameSetAbsPoint(ui_hpBarText, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_RIGHT, UI_BAR_HP_BOT)
call BlzFrameSetText(ui_hpBarText, "|cffffffff100/100|r")
//call BlzFrameSetEnable(ui_hpBarText, false)
call BlzFrameSetScale(ui_hpBarText, 1.00)
call BlzFrameSetTextAlignment(ui_hpBarText, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_hpBarText, FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_hpBarText, FRAMEEVENT_MOUSE_LEAVE)
set tmp = BlzCreateFrameByType("BACKDROP", "ui_hpIcon", ui_mainBackDrop, "", 1)
call BlzFrameSetAbsPoint(tmp, FRAMEPOINT_TOPLEFT, UI_BAR_ICON_LEFT, UI_BAR_HP_TOP)
call BlzFrameSetAbsPoint(tmp, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_ICON_RIGHT, UI_BAR_HP_BOT)
call BlzFrameSetTexture(tmp, "UI\\ScifiHexHp.blp", 0, true)
//ENERGY BAR
set ui_energyBarEmpty = BlzCreateFrameByType("BACKDROP", "", ui_mainBackDrop, "", 1)
call BlzFrameSetAbsPoint(ui_energyBarEmpty, FRAMEPOINT_TOPLEFT, UI_BAR_LEFT, UI_BAR_ENERGY_TOP)
call BlzFrameSetAbsPoint(ui_energyBarEmpty, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_RIGHT, UI_BAR_ENERGY_BOT)
call BlzFrameSetTexture(ui_energyBarEmpty, "UI\\ScifiBarEmpty.blp", 0, true)
set ui_energyBarFull = BlzCreateFrameByType("SIMPLESTATUSBAR", "", ui_energyBarEmpty, "", 0)
call BlzFrameSetTexture(ui_energyBarFull, "UI\\ScifiBarFull.blp", 0, true)
call BlzFrameSetAbsPoint(ui_energyBarFull, FRAMEPOINT_TOPLEFT, UI_BAR_LEFT, UI_BAR_ENERGY_TOP)
call BlzFrameSetAbsPoint(ui_energyBarFull, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_RIGHT, UI_BAR_ENERGY_BOT)
call BlzFrameSetValue(ui_energyBarFull, 0)
set ui_energyBarText = BlzCreateFrameByType("TEXT", "hpBarText", ui_mainBackDrop, "", 0)
call BlzFrameSetAbsPoint(ui_energyBarText, FRAMEPOINT_TOPLEFT, UI_BAR_LEFT, UI_BAR_ENERGY_TOP)
call BlzFrameSetAbsPoint(ui_energyBarText, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_RIGHT, UI_BAR_ENERGY_BOT)
call BlzFrameSetText(ui_energyBarText, "|cffffffff100/100|r")
//call BlzFrameSetEnable(ui_energyBarText, false)
call BlzFrameSetScale(ui_energyBarText, 1.00)
call BlzFrameSetTextAlignment(ui_energyBarText, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_energyBarText, FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_energyBarText, FRAMEEVENT_MOUSE_LEAVE)
set tmp = BlzCreateFrameByType("BACKDROP", "ui_energyIcon", ui_mainBackDrop, "", 1)
call BlzFrameSetAbsPoint(tmp, FRAMEPOINT_TOPLEFT, UI_BAR_ICON_LEFT, UI_BAR_ENERGY_TOP)
call BlzFrameSetAbsPoint(tmp, FRAMEPOINT_BOTTOMRIGHT, UI_BAR_ICON_RIGHT, UI_BAR_ENERGY_BOT)
call BlzFrameSetTexture(tmp, "UI\\ScifiHexEnergy.blp", 0, true)
//Icons
set yBase = UI_ICONS_TOP
loop
exitwhen i > UI_PASSIVE2
set xBase = UI_ICONS_LEFT + (i / UI_FRAMES_PER_ABILITY) * (UI_ICONS_SIZE + 2.1 * UI_MAIN_PADDING)
set ui_frame[i] = BlzCreateFrame("ScriptDialogButton", ui_mainBackDrop, 0, 0)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, xBase, yBase)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, xBase + UI_ICONS_SIZE, yBase - UI_ICONS_SIZE)
call BlzTriggerRegisterFrameEvent(abilityTooltipMouseEnterTrig, ui_frame[i], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_LEAVE)
call BlzTriggerRegisterFrameEvent(abilityClickTrig, ui_frame[i], FRAMEEVENT_MOUSE_UP)
call BlzFrameSetVisible(ui_frame[i], false)
set ui_frame[i+1] = BlzCreateFrameByType("BACKDROP", "icon" + I2S(i), ui_frame[i], "", 0)
call BlzFrameSetAllPoints(ui_frame[i+1], ui_frame[i])
call BlzFrameSetTexture(ui_frame[i+1], ICON_NONE, 0, true)
//call BlzFrameSetVisible(ui_frame[i+1], false)
//call BlzFrameSetAlpha(ui_frame[i], 250)
set ui_frame[i+2] = BlzCreateFrameByType("SIMPLESTATUSBAR", "iconCD + + I2S(i)", ui_frame[i], "", 0)
call BlzFrameSetTexture(ui_frame[i+2], ICONS + ICON_COOLDOWN, 0, true)
call BlzFrameSetAllPoints(ui_frame[i+2], ui_frame[i])
call BlzFrameSetValue(ui_frame[i+2], 0.)
call BlzFrameSetAlpha(ui_frame[i+2], COOLDOWN_ALPHA)
set i = i + UI_FRAMES_PER_ABILITY
endloop
set i = UI_MAP
set yBase = UI_BAR_ENERGY_BOT
set ui_frame[i] = BlzCreateFrame("ScriptDialogButton", ui_mainBackDrop, 0, 0)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, UI_ICONS_EXTRAS_LEFT, yBase + UI_ICONS_EXTRAS_SIZE)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, UI_ICONS_EXTRAS_RIGHT, yBase)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_LEAVE)
set ui_frame[i+2] = BlzCreateFrameByType("BACKDROP", "icon" + I2S(i), ui_frame[i], "", 1)
call BlzFrameSetAllPoints(ui_frame[i+2], ui_frame[i])
call BlzFrameSetTexture(ui_frame[i+2], ICON_MAP, 0, true)
//call BlzTriggerRegisterFrameEvent(TriggerFrame01, Frame01, FRAMEEVENT_CONTROL_CLICK)
//call TriggerAddAction(TriggerFrame01, function Frame01Func)
set ui_frame[i+1] = BlzCreateFrameByType("SIMPLESTATUSBAR", "iconCD + + I2S(i)", ui_frame[i], "", 0)
call BlzFrameSetTexture(ui_frame[i+1], ICONS + ICON_COOLDOWN, 0, true)
call BlzFrameSetAllPoints(ui_frame[i+1], ui_frame[i])
call BlzFrameSetValue(ui_frame[i+1], 0)
call BlzFrameSetAlpha(ui_frame[i+1], COOLDOWN_ALPHA)
set i = UI_INTERACT
set yBase = yBase + UI_ICONS_EXTRAS_SIZE + 0.5 * UI_MAIN_PADDING
set ui_frame[i] = BlzCreateFrame("ScriptDialogButton", ui_mainBackDrop, 0, 0)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, UI_ICONS_EXTRAS_LEFT, yBase + UI_ICONS_EXTRAS_SIZE)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, UI_ICONS_EXTRAS_RIGHT, yBase)
call BlzTriggerRegisterFrameEvent(abilityTooltipMouseEnterTrig, ui_frame[i], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_LEAVE)
call BlzTriggerRegisterFrameEvent(abilityClickTrig, ui_frame[i], FRAMEEVENT_MOUSE_UP)
set ui_frame[i+2] = BlzCreateFrameByType("BACKDROP", "icon" + I2S(i), ui_frame[i], "", 1)
call BlzFrameSetAllPoints(ui_frame[i+2], ui_frame[i])
call BlzFrameSetTexture(ui_frame[i+2], ICON_INTERACT, 0, true)
set ui_frame[i+1] = BlzCreateFrameByType("SIMPLESTATUSBAR", "iconCD + + I2S(i)", ui_frame[i], "", 0)
call BlzFrameSetTexture(ui_frame[i+1], ICONS + ICON_COOLDOWN, 0, true)
call BlzFrameSetAllPoints(ui_frame[i+1], ui_frame[i])
call BlzFrameSetValue(ui_frame[i+1], 0)
call BlzFrameSetAlpha(ui_frame[i+1], COOLDOWN_ALPHA)
set i = UI_STATS
set yBase = yBase + UI_ICONS_EXTRAS_SIZE + 0.5 * UI_MAIN_PADDING
set ui_frame[i] = BlzCreateFrame("ScriptDialogButton", ui_mainBackDrop, 0, 0)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, UI_ICONS_EXTRAS_LEFT, yBase + UI_ICONS_EXTRAS_SIZE)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, UI_ICONS_EXTRAS_RIGHT, yBase)
call BlzTriggerRegisterFrameEvent(statsClickToggleTrig, ui_frame[i], FRAMEEVENT_MOUSE_UP)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_LEAVE)
set ui_frame[i+2] = BlzCreateFrameByType("BACKDROP", "icon" + I2S(i), ui_frame[i], "", 1)
call BlzFrameSetAllPoints(ui_frame[i+2], ui_frame[i])
call BlzFrameSetTexture(ui_frame[i+2], ICON_STATS_CLOSE, 0, true)
set ui_frame[i+1] = BlzCreateFrameByType("SIMPLESTATUSBAR", "iconCD + + I2S(i)", ui_frame[i], "", 0)
call BlzFrameSetTexture(ui_frame[i+1], ICONS + ICON_COOLDOWN, 0, true)
call BlzFrameSetAllPoints(ui_frame[i+1], ui_frame[i])
call BlzFrameSetValue(ui_frame[i+1], 0)
call BlzFrameSetAlpha(ui_frame[i+1], COOLDOWN_ALPHA)
// ---- TOOLTIP ----
set i = UI_TOOLTIP_FRAME
set ui_frame[i] = BlzCreateFrameByType("BACKDROP", "tooltipFrame", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 1)
//call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, UI_MAIN_LEFT, UI_MAIN_TOP + UI_TOOLTIP_BASE_H)
//call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, UI_MAIN_LEFT + UI_TOOLTIP_BASE_W, UI_MAIN_TOP)
//call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, UI_MAIN_LEFT, UI_MAIN_TOP + UI_TOOLTIP_BASE_H)
call BlzFrameSetAbsPoint(ui_frame[i], FRAMEPOINT_BOTTOM, UI_MAIN_LEFT, UI_MAIN_TOP - UI_MAIN_PADDING)
call BlzFrameSetSize(ui_frame[i], UI_TOOLTIP_BASE_W, UI_TOOLTIP_BASE_H)
call BlzFrameSetTexture(ui_frame[i], "UI\\ScifiCardX3.blp", 0, true)
call BlzFrameSetVisible(ui_frame[i], false)
set i = UI_TOOLTIP_HEADER
set ui_frame[i] = BlzCreateFrameByType("TEXT", "tooltipHeader", ui_frame[UI_TOOLTIP_FRAME], "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_TOPLEFT, UI_TOOLTIP_INSET_SIDES, -UI_TOOLTIP_INSET_TOP)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_TOPRIGHT, -UI_TOOLTIP_INSET_SIDES, -(UI_TOOLTIP_HEADER_HEIGHT + UI_TOOLTIP_INSET_TOP))
call BlzFrameSetText(ui_frame[i], "|cffffffffHEADER|r")
call BlzFrameSetEnable(ui_frame[i], false)
call BlzFrameSetScale(ui_frame[i], 1.10)
call BlzFrameSetTextAlignment(ui_frame[i], TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_CENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_ENTER)
set i = UI_TOOLTIP_TEXT
set ui_frame[i] = BlzCreateFrameByType("TEXT", "tooltipBody", ui_frame[UI_TOOLTIP_FRAME], "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_TOP, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_TOP, 0.0, -(UI_TOOLTIP_HEADER_HEIGHT + UI_TOOLTIP_INSET_TOP + 2.5 * UI_MAIN_PADDING))
//call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMRIGHT, -UI_TOOLTIP_INSET, UI_TOOLTIP_INSET)
//call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMRIGHT, -UI_TOOLTIP_INSET, UI_TOOLTIP_INSET)
//call BlzFrameSetText(ui_frame[i], "|cffffffffHello! Some bread thext that, will this line break? Who knows? But I'd better test it.|nAlso With new-|nlines :)|nGoing for a few more chars of texts but still less than 256 chars...|r")
call BlzFrameSetText(ui_frame[i], "Hello!")
call BlzFrameSetSize(ui_frame[i], UI_TOOLTIP_BASE_W - 2. * UI_TOOLTIP_INSET_SIDES, 0.0)
call BlzFrameSetEnable(ui_frame[i], false)
call BlzFrameSetScale(ui_frame[i], 0.90)
//call BlzFrameSetTextAlignment(ui_frame[i], TEXT_JUSTIFY_TOP, TEXT_JUSTIFY_LEFT)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[i], FRAMEEVENT_MOUSE_ENTER)
//MANA
set i = UI_TOOLTIP_MANA
set xBase = -UI_TOOLTIP_INSET_SIDES * 4.75
set ui_frame[i] = BlzCreateFrameByType("TEXT", "tooltipMana", ui_frame[UI_TOOLTIP_FRAME], "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMLEFT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMRIGHT, xBase, UI_TOOLTIP_HEADER_HEIGHT)
call BlzFrameSetText(ui_frame[i], "|cffffffff12|r")
call BlzFrameSetEnable(ui_frame[i], false)
set i = UI_TOOLTIP_MANA_ICON_FRAME
set xBase = xBase - 0.5 * UI_TOOLTIP_INSET_SIDES
set ui_frame[i] = BlzCreateFrameByType("BACKDROP", "tooltipManaIcon", ui_frame[UI_TOOLTIP_FRAME], "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMRIGHT, xBase, UI_TOOLTIP_HEADER_HEIGHT)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMRIGHT, xBase - UI_TOOLTIP_ICON_W, UI_TOOLTIP_HEADER_HEIGHT + UI_TOOLTIP_ICON_H)
call BlzFrameSetTexture(ui_frame[i], UI_TOOLTIP_MANA_ICON_PATH, 0, true)
set i = UI_TOOLTIP_COOLDOWN_ICON_FRAME
set xBase = UI_TOOLTIP_INSET_SIDES * 2.25
set ui_frame[i] = BlzCreateFrameByType("BACKDROP", "tooltipCooldownIcon", ui_frame[UI_TOOLTIP_FRAME], "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMLEFT, xBase + UI_TOOLTIP_ICON_W, UI_TOOLTIP_HEADER_HEIGHT)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMLEFT, xBase, UI_TOOLTIP_HEADER_HEIGHT + UI_TOOLTIP_ICON_H)
call BlzFrameSetTexture(ui_frame[i], UI_TOOLTIP_COOLDOWN_ICON_PATH, 0, true)
set i = UI_TOOLTIP_COOLDOWN
set xBase = xBase + UI_MAIN_PADDING + UI_TOOLTIP_ICON_W + UI_MAIN_PADDING
set ui_frame[i] = BlzCreateFrameByType("TEXT", "tooltipCooldown", ui_frame[UI_TOOLTIP_FRAME], "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMLEFT, ui_frame[UI_TOOLTIP_FRAME], FRAMEPOINT_BOTTOMLEFT, xBase, UI_TOOLTIP_HEADER_HEIGHT)
call BlzFrameSetText(ui_frame[i], "|cffffffff5.35 s|r")
call BlzFrameSetEnable(ui_frame[i], false)
set yBase = -UI_STAT_INSET_TOP
//---------- Setup Stats -----------
set ui_frame[UI_STATS_CORES_AVAILABLE] = BlzCreateFrameByType("TEXT", "statCoresAvailable", ui_statsBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[UI_STATS_CORES_AVAILABLE], FRAMEPOINT_TOPLEFT, ui_statsBackdrop, FRAMEPOINT_TOPLEFT, UI_STAT_INSET_SIDES, yBase)
call BlzFrameSetPoint(ui_frame[UI_STATS_CORES_AVAILABLE], FRAMEPOINT_BOTTOMRIGHT, ui_statsBackdrop, FRAMEPOINT_TOPRIGHT, -UI_STAT_INSET_SIDES, yBase - UI_STAT_ICON_H)
call BlzFrameSetText(ui_frame[UI_STATS_CORES_AVAILABLE], "Cores Available: 3")
//call BlzFrameSetEnable(ui_frame[UI_STATS_CORES_AVAILABLE], false)
call BlzFrameSetScale(ui_frame[UI_STATS_CORES_AVAILABLE], 1.00)
call BlzFrameSetTextAlignment(ui_frame[UI_STATS_CORES_AVAILABLE], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[UI_STATS_CORES_AVAILABLE], FRAMEEVENT_MOUSE_LEAVE)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[UI_STATS_CORES_AVAILABLE], FRAMEEVENT_MOUSE_ENTER)
call addStatFrames(UI_STATS_HP, STATHEX_HP, 0, 0)
call addStatFrames(UI_STATS_HP_REPAIR, STATHEX_HP_REPAIR, 0, 1)
call addStatFrames(UI_STATS_ENERGY, STATHEX_ENERGY, 0, 2)
call addStatFrames(UI_STATS_ENERGY_RECHARGE, STATHEX_ENERGY_CHARGE, 0, 3)
call addStatFrames(UI_STATS_FIREPOWER, STATHEX_FIREPOWER, 1, 0)
call addStatFrames(UI_STATS_SYSTEM_POWER, STATHEX_SYSTEM_POWER, 1, 1)
call addStatFrames(UI_STATS_SYSTEM_SPEED, STATHEX_SYSTEM_SPEED, 1, 2)
call addStatFrames(UI_STATS_MOBILITY, STATHEX_MOBILITY, 1, 3)
//----------------------------------
//------------- SETUP TARGET ---------
//UI_STAT_INSET_SIDES
set yBase = -UI_STAT_INSET_TOP
set ui_frame[UI_TARGET_NAME] = BlzCreateFrameByType("TEXT", "targetName", ui_targetBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[UI_TARGET_NAME], FRAMEPOINT_TOPLEFT, ui_targetBackdrop, FRAMEPOINT_TOPLEFT, UI_MAIN_INSET, yBase)
call BlzFrameSetPoint(ui_frame[UI_TARGET_NAME], FRAMEPOINT_BOTTOMRIGHT, ui_targetBackdrop, FRAMEPOINT_TOPRIGHT, -UI_MAIN_INSET, yBase - UI_STAT_ICON_H)
call BlzFrameSetText(ui_frame[UI_TARGET_NAME], "Target: N/A")
set i = UI_TARGET_HP_BAR_EMPTY
set ui_frame[i] = BlzCreateFrameByType("BACKDROP", "hpEmpty", ui_targetBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_TOPLEFT, ui_targetBackdrop, FRAMEPOINT_TOPLEFT, UI_STAT_INSET_SIDES, yBase - UI_STAT_ICON_H * 1.5)
call BlzFrameSetPoint(ui_frame[i], FRAMEPOINT_BOTTOMRIGHT, ui_targetBackdrop, FRAMEPOINT_TOPRIGHT, -UI_STAT_INSET_SIDES, yBase - UI_STAT_ICON_H * 2.5)
call BlzFrameSetTexture(ui_frame[i], "UI\\ScifiBarEmpty.blp", 0, true)
set ui_frame[UI_TARGET_HP_BAR_FULL] = BlzCreateFrameByType("SIMPLESTATUSBAR", "hpFull", ui_frame[UI_TARGET_HP_BAR_FULL], "", 0)
call BlzFrameSetTexture(ui_frame[UI_TARGET_HP_BAR_FULL], "UI\\ScifiBarFull.blp", 0, true)
call BlzFrameSetAllPoints(ui_frame[UI_TARGET_HP_BAR_FULL], ui_frame[UI_TARGET_HP_BAR_EMPTY])
call BlzFrameSetValue(ui_frame[UI_TARGET_HP_BAR_FULL], 0)
set ui_frame[UI_TARGET_HP_BAR_TEXT] = BlzCreateFrameByType("TEXT", "hpBarText", ui_targetBackdrop, "", 0)
call BlzFrameSetAllPoints(ui_frame[UI_TARGET_HP_BAR_TEXT], ui_frame[UI_TARGET_HP_BAR_EMPTY])
call BlzFrameSetText(ui_frame[UI_TARGET_HP_BAR_TEXT], "|cffffffffN/A|r")
//call BlzFrameSetEnable(ui_frame[UI_TARGET_HP_BAR_TEXT], false)
call BlzFrameSetScale(ui_frame[UI_TARGET_HP_BAR_TEXT], 1.00)
call BlzFrameSetTextAlignment(ui_frame[UI_TARGET_HP_BAR_TEXT], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
//TODO: Add dmg + movement speed
set ui_frame[UI_TARGET_DAMAGE_ICON] = BlzCreateFrameByType("BACKDROP", "stat" + I2S(UI_TARGET_DAMAGE_ICON), ui_targetBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[UI_TARGET_DAMAGE_ICON], FRAMEPOINT_TOPLEFT, ui_frame[UI_TARGET_HP_BAR_FULL], FRAMEPOINT_BOTTOMLEFT, 0., -UI_MAIN_PADDING)
call BlzFrameSetSize(ui_frame[UI_TARGET_DAMAGE_ICON], UI_STAT_ICON_ADD_SIZE, UI_STAT_ICON_ADD_SIZE)
call BlzFrameSetTexture(ui_frame[UI_TARGET_DAMAGE_ICON], STATHEX_FIREPOWER, 0, true)
set ui_frame[UI_TARGET_DAMAGE_TEXT] = BlzCreateFrameByType("TEXT", "statText" + I2S(UI_TARGET_DAMAGE_ICON), ui_targetBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[UI_TARGET_DAMAGE_TEXT], FRAMEPOINT_TOPLEFT, ui_frame[UI_TARGET_DAMAGE_ICON], FRAMEPOINT_TOPRIGHT, UI_MAIN_PADDING, 0.)
call BlzFrameSetPoint(ui_frame[UI_TARGET_DAMAGE_TEXT], FRAMEPOINT_BOTTOM, ui_frame[UI_TARGET_DAMAGE_ICON], FRAMEPOINT_BOTTOM, 0., 0.)
call BlzFrameSetPoint(ui_frame[UI_TARGET_DAMAGE_TEXT], FRAMEPOINT_RIGHT, ui_targetBackdrop, FRAMEPOINT_RIGHT, -UI_MAIN_INSET, 0.)
call BlzFrameSetText(ui_frame[UI_TARGET_DAMAGE_TEXT], "|cffffffffN/A|r")
//call BlzFrameSetEnable(ui_frame[UI_TARGET_HP_BAR_TEXT], false)
call BlzFrameSetScale(ui_frame[UI_TARGET_DAMAGE_TEXT], 1.00)
call BlzFrameSetTextAlignment(ui_frame[UI_TARGET_DAMAGE_TEXT], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_LEFT)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[UI_TARGET_DAMAGE_TEXT], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[UI_TARGET_DAMAGE_TEXT], FRAMEEVENT_MOUSE_LEAVE)
//Movement Speed
set ui_frame[UI_TARGET_MOVEMENT_ICON] = BlzCreateFrameByType("BACKDROP", "stat" + I2S(UI_TARGET_MOVEMENT_ICON), ui_targetBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[UI_TARGET_MOVEMENT_ICON], FRAMEPOINT_TOPLEFT, ui_frame[UI_TARGET_DAMAGE_ICON], FRAMEPOINT_BOTTOMLEFT, 0., -UI_MAIN_PADDING)
call BlzFrameSetSize(ui_frame[UI_TARGET_MOVEMENT_ICON], UI_STAT_ICON_ADD_SIZE, UI_STAT_ICON_ADD_SIZE)
call BlzFrameSetTexture(ui_frame[UI_TARGET_MOVEMENT_ICON], STATHEX_MOBILITY, 0, true)
set ui_frame[UI_TARGET_MOVEMENT_TEXT] = BlzCreateFrameByType("TEXT", "statText" + I2S(UI_TARGET_MOVEMENT_ICON), ui_targetBackdrop, "", 0)
call BlzFrameSetPoint(ui_frame[UI_TARGET_MOVEMENT_TEXT], FRAMEPOINT_TOPLEFT, ui_frame[UI_TARGET_MOVEMENT_ICON], FRAMEPOINT_TOPRIGHT, UI_MAIN_PADDING, 0.)
call BlzFrameSetPoint(ui_frame[UI_TARGET_MOVEMENT_TEXT], FRAMEPOINT_BOTTOM, ui_frame[UI_TARGET_MOVEMENT_ICON], FRAMEPOINT_BOTTOM, 0., 0.)
call BlzFrameSetPoint(ui_frame[UI_TARGET_MOVEMENT_TEXT], FRAMEPOINT_RIGHT, ui_targetBackdrop, FRAMEPOINT_RIGHT, -UI_MAIN_INSET, 0.)
call BlzFrameSetText(ui_frame[UI_TARGET_MOVEMENT_TEXT], "|cffffffffN/A|r")
//call BlzFrameSetEnable(ui_frame[UI_TARGET_HP_BAR_TEXT], false)
call BlzFrameSetScale(ui_frame[UI_TARGET_MOVEMENT_TEXT], 1.00)
call BlzFrameSetTextAlignment(ui_frame[UI_TARGET_MOVEMENT_TEXT], TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_LEFT)
call BlzTriggerRegisterFrameEvent(ui_show_ui_tooltip_trig, ui_frame[UI_TARGET_MOVEMENT_TEXT], FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(ui_hide_tooltip_trig, ui_frame[UI_TARGET_MOVEMENT_TEXT], FRAMEEVENT_MOUSE_LEAVE)
//-----------------------------------------
set tmp = null
set abilityTooltipMouseEnterTrig = null
call TimerStart(CreateTimer(), UI_UPDATE_FREQUENCY, true, function updateUi)
endfunction
endlibrary
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicBottomBorder",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicDialogueText",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicPanel",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicPanelControlFrame",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicPortrait",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicPortraitBackground",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicPortraitCover",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicScenePanel",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicSpeakerText",0), false)
//call BlzFrameSetVisible(BlzGetFrameByName("CinematicTopBorder",0), false)
//call BlzFrameSetVisible(BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT, 0), false)
library MouseUtilsLockCamExtension requires MouseUtils
struct LockCamExtension extends array
private static unit array lockedCamUnit
private static real array relativeX
private static real array relativeY
static method get takes player p returns integer
return GetPlayerId(p)
endmethod
static method onMouseMovement takes nothing returns nothing
local player p = GetTriggerPlayer()
local integer index = get(p)
local unit u = lockedCamUnit[index]
set relativeX[index] = GetPlayerMouseX(p) - GetUnitX(u)
set relativeY[index] = GetPlayerMouseY(p) - GetUnitY(u)
//call BJDebugMsg("OnMouseMovement: " + I2S(GetPlayerId(p)) + ", this=" + I2S(this) + ", x=" + R2S(.relativeX))
set p = null
set u = null
endmethod
static method registerUnit takes unit u returns nothing
local integer index = get(GetOwningPlayer(u))
//call BJDebugMsg("Registering for: " + I2S(GetPlayerId(GetOwningPlayer(u))) + ", " + GetUnitName(u))
set lockedCamUnit[index] = u
endmethod
static method mx takes integer index returns real
return GetUnitX(lockedCamUnit[index]) + relativeX[index]
endmethod
static method my takes integer index returns real
return GetUnitY(lockedCamUnit[index]) + relativeY[index]
endmethod
endstruct
function registerLockCamUnit takes unit u returns nothing
call LockCamExtension.registerUnit(u)
endfunction
function initLockCamExtension takes nothing returns nothing
call UserMouse.registerCode(function LockCamExtension.onMouseMovement, EVENT_MOUSE_MOVE)
endfunction
function GetMouseX takes player p returns real
//call BJDebugMsg("GetMouseX: " + I2S(GetPlayerId(p)))
return LockCamExtension.mx(LockCamExtension.get(p))
endfunction
function GetMouseY takes player p returns real
return LockCamExtension.my(LockCamExtension.get(p))
endfunction
endlibrary
library DestructableUpdates requires Table, TimerUtils, Respawn
globals
constant integer SPAWN_POINT_DEST = 'B016'
constant real WALL_CHECK_RANGE = 550.
constant real WALL_LOW_RANGE = 350.
constant real WALL_LOW_RANGE_SQR = WALL_LOW_RANGE * WALL_LOW_RANGE
constant integer NO_FLAG = 0
constant integer DEAD_FLAG = 1
constant integer LOW_FLAG = 2
constant integer DEAD_LOW_FLAG = 3
constant integer ANIMATING_FLAG = 4
constant string ANIM_HIGH_ALIVE = "stand"
constant string ANIM_LOW_ALIVE = "stand alternate"
constant string ANIM_HIGH_DEAD = "decay"
constant string ANIM_LOW_DEAD = "decay alternate"
endglobals
struct DestructableUpdater
readonly static Table table
private static unit u
private static real ux
private static real uy
private static method setHigh takes destructable d returns nothing
local integer id = GetHandleId(d)
local integer flag = table[id]
if flag == LOW_FLAG then
//LOW and NOT DEAD and not playing animation
call table.integer.remove(id)
call SetDestructableAnimation(d, ANIM_HIGH_ALIVE)
elseif flag == DEAD_LOW_FLAG then
set table[id] = DEAD_FLAG
call SetDestructableAnimation(d, ANIM_HIGH_DEAD)
endif
endmethod
private static method setLow takes destructable d returns nothing
local integer id = GetHandleId(d)
local integer flag = table[id]
//call BJDebugMsg("Set Low: " + GetDestructableName(d) + ", flag=" + I2S(flag))
if flag == NO_FLAG then
set table[id] = LOW_FLAG
call SetDestructableAnimation(d, ANIM_LOW_ALIVE)
elseif flag == DEAD_FLAG then
set table[id] = DEAD_LOW_FLAG
call SetDestructableAnimation(d, ANIM_LOW_DEAD)
endif
endmethod
private static method updateLowerableDestructable takes destructable d returns nothing
local real dx = GetDestructableX(d) - ux
local real y = GetDestructableY(d)
local real dy = y - uy
if y < uy and (dx * dx + dy * dy) < WALL_LOW_RANGE_SQR then
call setLow(d)
else
call setHigh(d)
endif
endmethod
private static method updateDestructable takes nothing returns nothing
local destructable d = GetEnumDestructable()
local integer id = GetHandleId(d)
if not table.has(id) and GetDestructableLife(d) == 0. and GetDestructableMaxLife(d) > 0. then
set table[id] = DEAD_FLAG
endif
if GetDestructableOccluderHeight(d) == 200.00 or strEndsWith(GetDestructableName(d), "lowerable") then
call updateLowerableDestructable(d)
elseif GetDestructableTypeId(d) == SPAWN_POINT_DEST then
call heroRespawnUpdateSpawnPointWithDistanceCheck(u, d)
endif
endmethod
private static method foreachUnit takes nothing returns nothing
set u = GetEnumUnit()
set ux = GetUnitX(u)
set uy = GetUnitY(u)
call SetRect(enumRect, ux - WALL_CHECK_RANGE, uy - WALL_CHECK_RANGE, ux + WALL_CHECK_RANGE, uy + WALL_CHECK_RANGE)
call EnumDestructablesInRect(enumRect, null, function thistype.updateDestructable)
endmethod
private static method updateLoop takes nothing returns nothing
call ForGroup(udg_heroes_alive, function thistype.foreachUnit)
//For some reason did BlzGroupAddGroupFast lagg my game, so I cannot FirstOfGroup-loop here
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), 0.2, true, function thistype.updateLoop)
set table = Table.create()
endmethod
static method resetAnimatingFlag takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetTimerData(t)
call ReleaseTimer(t)
set table[id] = BlzBitAnd(table[id], BlzBitXor(ANIMATING_FLAG, -1))
set t = null
endmethod
endstruct
endlibrary
// ------------ Public functions --------------
function OpenDoor takes destructable d returns nothing
local integer id = GetHandleId(d)
call KillDestructable(d)
//set DestructableUpdater.table[id] = BlzBitOr(DestructableUpdater.table[id], BlzBitOr(DEAD_FLAG, ANIMATING_FLAG))
set DestructableUpdater.table[id] = BlzBitOr(DEAD_FLAG, ANIMATING_FLAG)
call TimerStart(NewTimerEx(id), 1.0, false, function DestructableUpdater.resetAnimatingFlag)
endfunction
function CloseDoor takes destructable d returns nothing
local integer id = GetHandleId(d)
call DestructableRestoreLife(d, GetDestructableMaxLife(d), true)
//set DestructableUpdater.table[id] = BlzBitOr(BlzBitAnd(DestructableUpdater.table[id], BlzBitXor(DEAD_FLAG, -1)), ANIMATING_FLAG)
set DestructableUpdater.table[id] = ANIMATING_FLAG
call TimerStart(NewTimerEx(id), 1.0, false, function DestructableUpdater.resetAnimatingFlag)
endfunction
library MetroidvaniaController initializer onInit requires MouseUtilsLockCamExtension, MetroidvaniaUtils, MetroidvaniaUi
globals
constant oskeytype MC_KEY_ATTACK = OSKEY_Q
constant oskeytype MC_KEY_ATTACK_ALT = OSKEY_W
constant oskeytype MC_KEY_UTIL_1 = OSKEY_E
constant oskeytype MC_KEY_UTIL_2 = OSKEY_R
constant oskeytype MC_KEY_DASH = OSKEY_D
constant oskeytype MC_KEY_INTERACT = OSKEY_F
constant oskeytype MC_KEY_MAP = OSKEY_M
constant integer NO_ACTION = -1
constant integer ACITON_STATE_READY = 1
constant integer ACTION_STATE_DOING = 2
constant integer ACTION_STATE_BACKSWING = 3
constant integer ACTION_STATE_COOLDOWN = 4
constant string TARGET_INDICATOR = "Vfx\\ThompZon\\TargetIndicatorTC5.mdx"
//constant integer UI_ATTACK = 0
//constant integer UI_ATTACK_ALT = 1
//constant integer UI_UTIL_1 = 2
//constant integer UI_UTIL_2 = 3
//constant integer UI_DASH = 4
//constant integer UI_PASSIVE_1 = 5
//constant integer UI_PASSIVE_2 = 6
//constant integer UI_INTERACT = 7
//constant integer UI_MAP = 8
endglobals
private keyword controllerCallback
private interface ActionInterface
method onStart takes nothing returns nothing defaults nothing
method onActionPoint takes nothing returns nothing defaults nothing
method onBackswing takes nothing returns nothing defaults nothing
method onRelease takes nothing returns nothing defaults nothing
method onCooldownFinished takes nothing returns nothing defaults nothing
method animationSpeed takes nothing returns real defaults 1.0
method abilityText takes nothing returns string defaults "TODO"
method abilityName takes nothing returns string defaults "TODO"
//method updateCooldown takes nothing returns nothing defaults nothing
endinterface
struct CooldownDetails extends array
implement Alloc
readonly real actionPoint
readonly real actionDuration
readonly real actionCooldown
public static method create takes real actionPoint, real actionDuration, real actionCooldown returns thistype
local thistype this = thistype.allocate()
set .actionPoint = actionPoint
set .actionDuration = actionDuration
set .actionCooldown = actionCooldown
return this
endmethod
public method printDetails takes nothing returns nothing
call BJDebugMsg("this=" + I2S(this) + ", AP=" + R2S(actionPoint) + ", Dur="+R2S(actionDuration) + ", Cooldown=" + R2S(actionCooldown))
endmethod
public method update takes real actionPoint, real actionDuration, real actionCooldown returns nothing
set .actionPoint = actionPoint
set .actionDuration = actionDuration
set .actionCooldown = actionCooldown
//call BJDebugMsg("--- Updated cooldown details ---")
//call printDetails()
endmethod
public method destroy takes nothing returns nothing
call this.deallocate()
endmethod
endstruct
private module ActionFlow
private static method releaseTimer takes nothing returns nothing
call ReleaseTimer(GetExpiredTimer())
endmethod
private static method updateUi takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real remainingCooldown = TimerGetRemaining(.cooldownTimer)
if remainingCooldown > 0 then
if GetLocalPlayer() == GetOwningPlayer(u) then
call updateCooldown(uiButtonIndex, .cooldownDetails.actionCooldown, remainingCooldown)
endif
else
call ReleaseTimer(GetExpiredTimer())
set .cooldownTimer = null
endif
endmethod
private static method cooldownFinished takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if .onCooldownFinished.exists then
call .onCooldownFinished()
endif
call ReleaseTimer(t)
set t = null
set actionState = ACITON_STATE_READY
call controllerCallback.evaluate(u, this)
endmethod
private static method atBackswing takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if .onBackswing.exists then
call .onBackswing()
endif
call TimerStart(t, cooldownDetails.actionCooldown - cooldownDetails.actionDuration, false, function thistype.cooldownFinished)
set t = null
set actionState = ACTION_STATE_COOLDOWN
call controllerCallback.evaluate(u, this)
endmethod
private static method atActionPoint takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if .onActionPoint.exists then
call .onActionPoint()
endif
call TimerStart(t, cooldownDetails.actionDuration - cooldownDetails.actionPoint, false, function thistype.atBackswing)
set t = null
if GetLocalPlayer() == GetOwningPlayer(u) then
call SelectUnit(u, true)
endif
set actionState = ACTION_STATE_BACKSWING
call controllerCallback.evaluate(u, this)
endmethod
method start takes nothing returns nothing
call ConsumeMana(u, energyCost)
if .onStart.exists then
call .onStart()
endif
set .cooldownTimer = NewTimerEx(this)
call TimerStart(cooldownTimer, cooldownDetails.actionCooldown, false, function thistype.releaseTimer)
call TimerStart(NewTimerEx(this), cooldownDetails.actionPoint, false, function thistype.atActionPoint)
call TimerStart(NewTimerEx(this), 0.02, true, function thistype.updateUi)
set actionState = ACTION_STATE_DOING
if cooldownDetails.actionPoint == 0. then
call controllerCallback.evaluate(u, this)
elseif GetLocalPlayer() == GetOwningPlayer(u) then
call ClearSelection()
endif
endmethod
endmodule
struct HeroActionBase extends ActionInterface
private integer actionState
public boolean isHolding
readonly timer cooldownTimer
readonly unit u
readonly CooldownDetails cooldownDetails
readonly integer uiButtonIndex
real energyCost
boolean ignoreTarget
public method isDoing takes nothing returns boolean
return actionState == ACTION_STATE_DOING
endmethod
public method isBackswing takes nothing returns boolean
return actionState == ACTION_STATE_BACKSWING
endmethod
public method isOnCooldown takes nothing returns boolean
return actionState != ACITON_STATE_READY
endmethod
public method isReady takes nothing returns boolean
return actionState == ACITON_STATE_READY
endmethod
public method isCastable takes nothing returns boolean
return isReady() and GetMana(u) > energyCost
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(u)
endmethod
stub method updateAbilityDetails takes nothing returns nothing
call BJDebugMsg("!!! NO UPDATE FUNCTION IMPLEMENTED !!!")
endmethod
implement ActionFlow
public static method create takes unit u, integer uiIndex, string iconPath returns thistype
local thistype this = thistype.allocate()
set .u = u
set .cooldownDetails = CooldownDetails.create(1.0, 1.0, 1.0)
set .isHolding = false
set .actionState = ACITON_STATE_READY
set .uiButtonIndex = uiIndex
call BlzFrameSetVisible(ui_frame[uiButtonIndex + UI_ABIL_BTN_OFFSET], true)
call BlzFrameSetVisible(ui_frame[uiButtonIndex + UI_ABIL_ICON_OFFSET], true)
call BlzFrameSetTexture(ui_frame[uiButtonIndex + UI_ABIL_ICON_OFFSET], iconPath, 0, true)
call BlzFrameSetValue(ui_frame[uiButtonIndex + UI_ABIL_COOLDOWN_OFFSET], 0)
return this
endmethod
public method destroy takes nothing returns nothing
if cooldownTimer != null then
call ReleaseTimer(cooldownTimer)
set cooldownTimer = null
endif
call cooldownDetails.destroy()
call this.deallocate()
endmethod
endstruct
struct MetroidvaniaController extends array
private static trigger mc_main_down
private static trigger mc_main_up
private static trigger mc_select
private static trigger mc_deselect
private static effect localTargetIndicator
private unit u
private unit target
private boolean isHolding
private timer t
private boolean isDoingAction
private HeroActionBase currentAction
private HeroActionBase queuedAction
private real currentActionTargetX
private real currentActionTargetY
private HeroActionBase attack
private HeroActionBase attackAlt
private HeroActionBase util1
private HeroActionBase util2
private HeroActionBase dash
private HeroActionBase interact
private HeroActionBase map
private method keyToAction takes oskeytype key returns HeroActionBase
if key == MC_KEY_ATTACK then
return attack
elseif key == MC_KEY_ATTACK_ALT then
return attackAlt
elseif key == MC_KEY_UTIL_1 then
return util1
elseif key == MC_KEY_UTIL_2 then
return util2
elseif key == MC_KEY_DASH then
return dash
elseif key == MC_KEY_INTERACT then
return interact
elseif key == MC_KEY_MAP then
return map
else
return NO_ACTION
endif
endmethod
private method updateTargetCoords takes HeroActionBase action returns nothing
if target != null and UnitAlive(target) and not action.ignoreTarget then
set .currentActionTargetX = GetUnitX(target)
set .currentActionTargetY = GetUnitY(target)
else
//call BJDebugMsg("updateTargetCoords: Playerid=" + I2S(GetPlayerId(GetOwningPlayer(.u))))
set .currentActionTargetX = GetMouseX(GetOwningPlayer(.u))
set .currentActionTargetY = GetMouseY(GetOwningPlayer(.u))
endif
endmethod
public method tryToExecuteAction takes HeroActionBase action returns nothing
if not isDoingAction and action > 0 and action.isCastable() then
set currentAction = action
set isDoingAction = true
call updateTargetCoords(action)
call action.start()
endif
endmethod
static method controllerCallback takes unit u, HeroActionBase action returns nothing
local thistype this = thistype(GetPlayerId(GetOwningPlayer(u)))
//Should be at start of "backswing" at earliest, allow queueing actions at start of "backswing"
set isDoingAction = false
if currentAction == action then
if currentAction.isHolding and currentAction.isCastable() then
call updateTargetCoords(currentAction)
call currentAction.start()
elseif queuedAction != NO_ACTION and queuedAction.isCastable() then
call updateTargetCoords(queuedAction)
set currentAction = queuedAction
call currentAction.start()
endif
endif
endmethod
private static method keyPressed takes nothing returns nothing
local thistype this = thistype(GetPlayerId(GetTriggerPlayer()))
local real tx
local real ty
local HeroActionBase action
if UnitAlive(u) then
if not isDoingAction then
set currentAction = keyToAction(BlzGetTriggerPlayerKey())
set queuedAction = NO_ACTION
if currentAction != NO_ACTION then
//call BJDebugMsg("Trying to cast: " + currentAction.abilityName())
set currentAction.isHolding = true
call tryToExecuteAction(currentAction)
else
//call BJDebugMsg("Action is NO_ACTION")
endif
else
//call BJDebugMsg("Doing Action...")
set action = keyToAction(BlzGetTriggerPlayerKey())
if currentAction == action then
set currentAction.isHolding = true
else
set queuedAction = action
endif
endif
else
set currentAction = NO_ACTION
set queuedAction = NO_ACTION
endif
endmethod
private static method keyReleased takes nothing returns nothing
local thistype this = thistype(GetPlayerId(GetTriggerPlayer()))
local HeroActionBase action = keyToAction(BlzGetTriggerPlayerKey())
if action != NO_ACTION then
set action.isHolding = false
call action.onRelease()
endif
endmethod
private static method updateLocalTargetIndicator takes nothing returns nothing
local thistype this = thistype(GetPlayerId(GetLocalPlayer()))
if (target != null) then
call BlzSetSpecialEffectPosition(localTargetIndicator, GetUnitX(target) - 5., GetUnitY(target) - 5., BlzGetLocalUnitZ(target))
call BlzSetSpecialEffectAlpha(localTargetIndicator, 255)
else
call BlzSetSpecialEffectAlpha(localTargetIndicator, 0)
endif
endmethod
private static method selectTargetNearMouse takes nothing returns nothing
local player p = GetTriggerPlayer()
local thistype this = thistype(GetPlayerId(p))
local group g = CreateGroup()
local unit u
local real mx = GetMouseX(p)
local real my = GetMouseY(p)
local real dx
local real dy
local unit selectedUnit = null
local real distanceSqr = 999999.9
local real dSqr
call GroupEnumUnitsInRange(g, mx, my, 100.0, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
set dx = GetUnitX(u) - mx
set dy = GetUnitY(u) - my
set dSqr = dx * dx + dy * dy
if .u != u and UnitAlive(u) and dSqr < distanceSqr then
set distanceSqr = dSqr
set selectedUnit = u
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set target = selectedUnit
if target == null then
if p == GetLocalPlayer() then
call clearUiTarget(this)
endif
else
if p == GetLocalPlayer() then
call setUiTarget(.target)
endif
endif
set selectedUnit = null
set g = null
set p = null
endmethod
private static method onClick takes nothing returns nothing
if UserMouse[GetTriggerPlayer()].isMouseButtonClicked(MOUSE_BUTTON_TYPE_LEFT) then
call selectTargetNearMouse()
endif
endmethod
public method updateAbilityDetails takes nothing returns nothing
call attack.updateAbilityDetails()
call attackAlt.updateAbilityDetails()
call util1.updateAbilityDetails()
call util2.updateAbilityDetails()
call dash.updateAbilityDetails()
call interact.updateAbilityDetails()
call map.updateAbilityDetails()
endmethod
private static method onInit takes nothing returns nothing
local player p
local integer i = 0
local thistype this
set localTargetIndicator = AddSpecialEffect(TARGET_INDICATOR, 0., 0.)
call BlzSetSpecialEffectColorByPlayer(localTargetIndicator, Player(0))
call BlzSetSpecialEffectAlpha(localTargetIndicator, 0)
call TimerStart(CreateTimer(), 0.02, true, function thistype.updateLocalTargetIndicator)
set mc_main_down = CreateTrigger()
set mc_main_up = CreateTrigger()
call TriggerAddCondition( mc_main_down, Condition(function thistype.keyPressed))
call TriggerAddCondition( mc_main_up, Condition(function thistype.keyReleased))
call OnMouseEvent(function thistype.onClick, EVENT_MOUSE_DOWN)
loop
exitwhen i > 4
set p = Player(i)
set this = thistype(i)
set .isDoingAction = false
set .isHolding = false
if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
//BlzTriggerRegisterPlayerKeyEvent takes trigger whichTrigger, player whichPlayer, oskeytype key, integer metaKey, boolean keyDown returns event
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_ATTACK, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_ATTACK, 0, false)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_ATTACK_ALT, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_ATTACK_ALT, 0, false)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_UTIL_1, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_UTIL_1, 0, false)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_UTIL_2, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_UTIL_2, 0, false)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_DASH, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_DASH, 0, false)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_INTERACT, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_INTERACT, 0, false)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_down, p, MC_KEY_MAP, 0, true)
call BlzTriggerRegisterPlayerKeyEvent(mc_main_up, p, MC_KEY_MAP, 0, false)
endif
set i = i + 1
endloop
set p = null
endmethod
public static method setAttack takes player p, HeroActionBase attack returns nothing
local thistype this = thistype(GetPlayerId(p))
set .attack = attack
call updateAbilityDetails()
endmethod
public static method setAttackAlt takes player p, HeroActionBase attackAlt returns nothing
local thistype this = thistype(GetPlayerId(p))
set .attackAlt = attackAlt
call updateAbilityDetails()
endmethod
public static method setUtil1 takes player p, HeroActionBase util1 returns nothing
local thistype this = thistype(GetPlayerId(p))
set .util1 = util1
call updateAbilityDetails()
endmethod
public static method setUtil2 takes player p, HeroActionBase util2 returns nothing
local thistype this = thistype(GetPlayerId(p))
set .util2 = util2
call updateAbilityDetails()
endmethod
public static method setDash takes player p, HeroActionBase dash returns nothing
local thistype this = thistype(GetPlayerId(p))
set .dash = dash
call updateAbilityDetails()
endmethod
public static method setMap takes player p, HeroActionBase map returns nothing
local thistype this = thistype(GetPlayerId(p))
set .map = map
endmethod
public static method setInteract takes player p, HeroActionBase interact returns nothing
local thistype this = thistype(GetPlayerId(p))
set .interact = interact
endmethod
public static method setHero takes player p, unit hero returns nothing
local thistype this = thistype(GetPlayerId(p))
set .u = hero
endmethod
public static method setTarget takes player p, unit target returns nothing
local thistype this = thistype(GetPlayerId(p))
set .target = target
endmethod
public method getAttack takes nothing returns HeroActionBase
return .attack
endmethod
public method getAttackAlt takes nothing returns HeroActionBase
return .attackAlt
endmethod
public method getUtil1 takes nothing returns HeroActionBase
return .util1
endmethod
public method getUtil2 takes nothing returns HeroActionBase
return .util2
endmethod
public method getDash takes nothing returns HeroActionBase
return .dash
endmethod
public method getMap takes nothing returns HeroActionBase
return .map
endmethod
public method getInteract takes nothing returns HeroActionBase
return .interact
endmethod
public method getTargetUnit takes nothing returns unit
return .target
endmethod
public method getHero takes nothing returns unit
return .u
endmethod
public method setTargetUnit takes unit newTarget returns nothing
set .target = newTarget
endmethod
public method clearTarget takes nothing returns nothing
set .target = null
endmethod
//call MetroidvaniaController.disableControl()
public static method disableControl takes nothing returns nothing
call ClearSelection()
endmethod
public static method resumeControl takes nothing returns nothing
call SelectUnit(thistype(GetPlayerId(GetLocalPlayer())).u, true)
endmethod
public static method getTargetX takes player p returns real
//call BJDebugMsg("getTargetX: Playerid=" + I2S(GetPlayerId(p)))
return thistype(GetPlayerId(p)).currentActionTargetX
endmethod
public static method getTargetY takes player p returns real
return thistype(GetPlayerId(p)).currentActionTargetY
endmethod
public static method getMetroidvaniaController takes player p returns MetroidvaniaController
return thistype(GetPlayerId(p))
endmethod
endstruct
function controllerCallback takes unit u, HeroActionBase action returns nothing
call MetroidvaniaController.controllerCallback(u, action)
endfunction
function GetHero takes player p returns unit
return MetroidvaniaController.getMetroidvaniaController(p).getHero()
endfunction
endlibrary
library KnockbackTZ initializer onInit requires MetroidvaniaUtils
globals
private constant real CHECK_RADIUS = 96. //Distance to add to collision to get edge-to-edge collisions, rather than center-to-center
//Time (in seconds) per update
private constant real TICK_RATE = 1./50.
//Speed percent kept per tick
private constant real DEFAULT_FRICTION_PERCENT = 0.996 //~80% of previous second if 1./50. tick-rate
constant real DEFAULT_FRICTION = 0.01 //total length of the friction vector (absolute units per tick)
//Should bounce, using reflection angle from walls, or "continue" "into" the wall
private constant boolean DEFAULT_BOUNCEY = true
private constant real BOUNCE_VELOCITY_LOSS = 0.8
private constant real DEFAULT_COLLISION = 8.
constant real NO_COLLISION = -999.
//Limit of units processed per tick.
//Next tick distance is multiplied by skiped ticks, if skiped due to this limit.
private constant integer MAX_UPDATES_PER_TICK = 50
//Distance difference allowed to be considered "equal". Real default is too small
private constant real WALKABLE_EPSILON = .1
private constant integer EAST = 0
private constant integer NORTH = 1
private constant integer WEST = 2
private constant integer SOUTH = 3
private constant group knockbackUnits = CreateGroup()
location walkablePoint = Location(0.,0.)
endglobals
private keyword StartKnockback
function updateWalkablePoint takes real x, real y returns nothing
call SetItemPosition(udg_CP_Item, x, y)
call MoveLocation(walkablePoint, GetItemX(udg_CP_Item), GetItemY(udg_CP_Item))
call SetItemVisible(udg_CP_Item, false)
endfunction
function isPointWalkable takes real x, real y returns boolean
call updateWalkablePoint(x, y)
return RAbsBJ(x - GetLocationX(walkablePoint)) < WALKABLE_EPSILON and RAbsBJ(y - GetLocationY(walkablePoint)) < WALKABLE_EPSILON
endfunction
function isPointWalableNoUpdate takes real x, real y returns boolean
return x == GetLocationX(walkablePoint) and y == GetLocationY(walkablePoint)
endfunction
function walkablePointsIn takes real x, real y, integer size returns integer
local integer walkablePoints = 0
local integer i = 0
local integer sizeCubed = size * size
local real halfSize = (size / 2) * 32.
local real tx
local real ty
if isPointWalkable(x, y) then
loop
exitwhen i == sizeCubed
set tx = x - halfSize + ModuloInteger(i, size) * 32.
set ty = y - halfSize + (i / size) * 32.
if isPointWalkable(tx, ty) then
set walkablePoints = walkablePoints + 1
endif
set i = i + 1
endloop
endif
return walkablePoints
endfunction
function IsWalkableWithCollisionSize takes real x, real y, real c returns boolean
return isPointWalkable(x, y) and isPointWalkable(x + c, y + c) and isPointWalkable(x - c, y + c) and isPointWalkable(x - c, y - c) and isPointWalkable(x + c, y - c)
endfunction
//313 units over 0.36 seconds
//(313^2)/(0.78*400)
struct KnockbackTZ extends array
//private static group knockbackUnits
private static integer lastProcessed = 0
static timer updTimer
private real vx
private real vy
private real fx
private real fy
private real fPercent
private real timeLeft
private real mass
private boolean massOverridden
private real collision
private boolean bounce
private integer ticksSinceLastUpdate
public method endKnockback takes unit u returns nothing
call GroupRemoveUnit(knockbackUnits, u)
set .timeLeft = 0.
set .vx = 0.
set .vy = 0.
set .fx = 0.
set .fy = 0.
set .collision = 0.
set .fPercent = DEFAULT_FRICTION
set .bounce = DEFAULT_BOUNCEY
set .ticksSinceLastUpdate = 0
if not IsHero(u) and not UnitAlive(u) then
set mass = 0.
set massOverridden = false
endif
if BlzGroupGetSize(knockbackUnits) == 0 then
call PauseTimer(updTimer)
endif
endmethod
public static method doKnockback takes unit u, real angle, real speed, real time, real friction, real frictionPercentOfSpeed, real collider returns thistype
local thistype this = GetUnitUserData(u)
local real speedPerTick = speed * TICK_RATE
local real colSize = BlzGetUnitCollisionSize(u)
if not massOverridden then
set .mass = 10. * colSize
endif
set .timeLeft = RMaxBJ(.timeLeft, time)
set .vx = .vx + Cos(angle) * speedPerTick
set .vy = .vy + Sin(angle) * speedPerTick
set .fx = .fx + Cos(angle) * friction
set .fy = .fy + Sin(angle) * friction
set .fPercent = frictionPercentOfSpeed
set .collision = RMaxBJ(.collision, collider)
if BlzGroupGetSize(knockbackUnits) == 0 then
call StartKnockback.evaluate()
endif
call GroupAddUnit(knockbackUnits, u)
return this
endmethod
public static method doKnockbackSimple takes unit u, real angle, real speed, real time returns thistype
return doKnockback(u, angle, speed, time, DEFAULT_FRICTION, DEFAULT_FRICTION_PERCENT, DEFAULT_COLLISION)
endmethod
public static method unitCollision takes KnockbackTZ sourceKnock, unit source, unit u returns nothing
endmethod
private method checkCollision takes unit source returns nothing
local real x = GetUnitX(source)
local real y = GetUnitY(source)
local real col = BlzGetUnitCollisionSize(source)
local real angle
local KnockbackTZ other
//local real
local unit u
local group g = CreateGroup()
call GroupEnumUnitsInRange(g, x, y, col + .collision + CHECK_RADIUS, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if IsUnitInRange(source, u, .collision) then
set other = GetUnitUserData(u)
if IsUnitInGroup(u, knockbackUnits) then
else
endif
if other.mass > 0. then
endif
set angle = Atan2(GetUnitY(u) - y, GetUnitX(u) - x)
//call doKnockback(u, angle, )
endif
call GroupRemoveUnit(g, u)
endloop
endmethod
private method tickUnit takes unit u returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real col = BlzGetUnitCollisionSize(u)
local real newX
local real newY
local real tickVx
local real tickVy
local integer i = 0
set ticksSinceLastUpdate = ticksSinceLastUpdate + 1
set tickVx = vx * ticksSinceLastUpdate
set tickVy = vy * ticksSinceLastUpdate
set newX = x + tickVx
set newY = y + tickVy
//call BJDebugMsg("Tick vx=" + R2S(tickVx) + ", vy=" + R2S(tickVy) + ", fx=" + R2S(fx) + ", fy=" + R2S(fy))
if not IsWalkableWithCollisionSize(newX, newY, col) and bounce then
if IsWalkableWithCollisionSize(x - tickVx, newY, col) then
set vx = -vx * BOUNCE_VELOCITY_LOSS
set fx = -fx
set vy = vy * BOUNCE_VELOCITY_LOSS
elseif IsWalkableWithCollisionSize(newX, y - tickVy, col) then
set vx = vx * BOUNCE_VELOCITY_LOSS
set vy = -vy * BOUNCE_VELOCITY_LOSS
set fy = -fy
else
set vx = -vx * BOUNCE_VELOCITY_LOSS
set vy = -vy * BOUNCE_VELOCITY_LOSS
set fx = -fx
set fy = -fy
endif
endif
if collision > 0. then
call checkCollision(u)
endif
set newX = x + tickVx
set newY = y + tickVy
call SetUnitX(u, newX)
call SetUnitY(u, newY)
loop
set vx = vx * fPercent - fx
set vy = vy * fPercent - fy
set i = i + 1
exitwhen i == ticksSinceLastUpdate
endloop
set timeLeft = timeLeft - TICK_RATE
set ticksSinceLastUpdate = 0
if timeLeft <= 0. then
call endKnockback(u)
endif
endmethod
private method skipUnit takes unit u returns nothing
set ticksSinceLastUpdate = ticksSinceLastUpdate + 1
endmethod
static method update takes nothing returns nothing
local unit u
local integer i = lastProcessed
local integer processed = 0
local integer groupSize = BlzGroupGetSize(knockbackUnits)
local boolean shouldSetLastIfOverflow = true
//call BJDebugMsg("upd, lastProcessed=" + I2S(lastProcessed) + ", GroupSize=" + I2S(groupSize))
if i >= groupSize then
set i = 0
set lastProcessed = 0
endif
loop
set u = BlzGroupUnitAt(knockbackUnits, i)
if processed < MAX_UPDATES_PER_TICK then
call KnockbackTZ(GetUnitUserData(u)).tickUnit(u)
else
if shouldSetLastIfOverflow then
set shouldSetLastIfOverflow = false
set lastProcessed = i
endif
call KnockbackTZ(GetUnitUserData(u)).skipUnit(u)
endif
set processed = processed + 1
set i = i + 1
if i == groupSize then
set i = 0
endif
exitwhen processed >= groupSize - 1
endloop
endmethod
private static method onInit takes nothing returns nothing
set updTimer = CreateTimer()
endmethod
endstruct
private function StartKnockback takes nothing returns nothing
call TimerStart(KnockbackTZ.updTimer, TICK_RATE, true, function KnockbackTZ.update)
endfunction
endlibrary
library ExtendableBonusSystem
/* ----------------------- ExtendableBonusSystem v0.1 by ThompZon ----------------------- */
//! novjass
Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
using a simple interface. Hopefully the interface will be simple so it is easy to implement
but also this means that the core can upgrade without impacting the extensions!
Inspired by: chopinski New Bonus system
https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/
jassApi
//! endnovjass
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
public interface ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean defaults false
method Get takes unit u returns real
method Set takes unit u, real value returns real
endinterface
struct RealAbilityBonus extends ExtendableBonusPlugin
stub method AbilityCode takes nothing returns integer
call BJDebugMsg("RealAbilityBonus missing AbilityCode!")
return -1
endmethod
stub method Field takes nothing returns abilityreallevelfield
call BJDebugMsg("RealAbilityBonus missing Field!")
return null
endmethod
method Set takes unit u, real value returns real
local integer abilCode = AbilityCode()
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
call BlzSetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, value)
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0)
endmethod
method Get takes unit u returns real
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, AbilityCode()), Field(), 0)
endmethod
endstruct
struct IntegerAbilityBonus extends ExtendableBonusPlugin
stub method AbilityCode takes nothing returns integer
call BJDebugMsg("IntegerAbilityBonus missing AbilityCode!")
return -1
endmethod
stub method Field takes nothing returns abilityintegerlevelfield
call BJDebugMsg("IntegerAbilityBonus missing Field!")
return null
endmethod
method Set takes unit u, real value returns real
local integer abilCode = AbilityCode()
if GetUnitAbilityLevel(u, abilCode) == 0 then
call UnitAddAbility(u, abilCode)
call UnitMakeAbilityPermanent(u, true, abilCode)
endif
call BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0, R2I(value))
call IncUnitAbilityLevel(u, abilCode)
call DecUnitAbilityLevel(u, abilCode)
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, abilCode), Field(), 0))
endmethod
method Get takes unit u returns real
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, AbilityCode()), Field(), 0))
endmethod
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
endstruct
struct ExtendableBonus
private static ExtendableBonusPlugin array registeredBonuses
//private static integer maxTypeId = -1
static method Register takes ExtendableBonusPlugin bonus returns integer
set registeredBonuses[bonus.getType()] = bonus
//set maxTypeId = IMaxBJ(maxTypeId, bonus.getType())
//call BJDebugMsg("maxTypeId=" + I2S(maxTypeId) + ", registedTypeId=" + I2S(bonus.getType()) + ", bonus=" + I2S(bonus))
return bonus.getType()
endmethod
static method GetBonus takes integer typeId returns ExtendableBonusPlugin
return registeredBonuses[typeId]
endmethod
static method Get takes unit u, integer typeId returns real
if (registeredBonuses[typeId] == 0) then
call BJDebugMsg("type is not registered! id=" + I2S(typeId))
return -1.0
else
return registeredBonuses[typeId].Get(u)
endif
endmethod
static method Set takes unit u, integer typeId, real value returns real
if (registeredBonuses[typeId] == 0) then
call BJDebugMsg("type is not registered! id=" + I2S(typeId))
return -1.0
else
//call BJDebugMsg("Setting type=" + I2S(typeId) + " to " + R2S(value))
return registeredBonuses[typeId].Set(u, value)
endif
endmethod
static method Add takes unit u, integer typeId, real value returns real
local ExtendableBonusPlugin currentBonus = GetBonus(typeId)
local real addedValue
if currentBonus.IsIntegerBonus() then
set addedValue = R2I(value)
else
set addedValue = value
endif
call Set(u, typeId, currentBonus.Get(u) + addedValue)
return addedValue
endmethod
endstruct
function GetUnitBonus takes unit u, integer typeId returns real
return ExtendableBonus.Get(u, typeId)
endfunction
function SetUnitBonus takes unit u, integer typeId, real value returns real
return ExtendableBonus.Set(u, typeId, value)
endfunction
function AddUnitBonus takes unit u, integer typeId, real value returns real
return ExtendableBonus.Add(u, typeId, value)
endfunction
function RemoveUnitBonus takes unit u, integer typeId returns nothing
call ExtendableBonus.Set(u, typeId, 0)
endfunction
endlibrary
library ExtendableBonusesBasicBonuses requires ExtendableBonusSystem
/* ----------------------- ExtendableBonusSystemBasics v0.1 by ThompZon ----------------------- */
//! novjass
Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
using a simple interface. Hopefully the interface will be simple so it is easy to implement
but also this means that the core can upgrade without impacting the extensions!
Inspired by: chopinski New Bonus system
https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/
jassApi
//! endnovjass
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
globals
//The abilities codes for each bonus
//When pasting the abilities over to your map
//their raw code should match the bonus here
private constant integer DAMAGE_ABILITY = 'Z001'
private constant integer ARMOR_ABILITY = 'Z002'
private constant integer STATS_ABILITY = 'Z003'
private constant integer HEALTH_ABILITY = 'Z004'
private constant integer MANA_ABILITY = 'Z005'
private constant integer HEALTHREGEN_ABILITY = 'Z006'
private constant integer MANAREGEN_ABILITY = 'Z007'
private constant integer ATTACKSPEED_ABILITY = 'Z008'
private constant integer MOVEMENTSPEED_ABILITY = 'Z009'
private constant integer SIGHT_RANGE_ABILITY = 'Z00A'
//The abilities fields that are modified. For the sake of readability
private constant abilityintegerlevelfield DAMAGE_FIELD = ABILITY_ILF_ATTACK_BONUS
private constant abilityintegerlevelfield ARMOR_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
private constant abilityintegerlevelfield AGILITY_FIELD = ABILITY_ILF_AGILITY_BONUS
private constant abilityintegerlevelfield STRENGTH_FIELD = ABILITY_ILF_STRENGTH_BONUS_ISTR
private constant abilityintegerlevelfield INTELLIGENCE_FIELD = ABILITY_ILF_INTELLIGENCE_BONUS
private constant abilityintegerlevelfield HEALTH_FIELD = ABILITY_ILF_MAX_LIFE_GAINED
private constant abilityintegerlevelfield MANA_FIELD = ABILITY_ILF_MAX_MANA_GAINED
private constant abilityintegerlevelfield MOVEMENTSPEED_FIELD = ABILITY_ILF_MOVEMENT_SPEED_BONUS
private constant abilityintegerlevelfield SIGHT_RANGE_FIELD = ABILITY_ILF_SIGHT_RANGE_BONUS
private constant abilityreallevelfield HEALTHREGEN_FIELD = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
private constant abilityreallevelfield MANAREGEN_FIELD = ABILITY_RLF_AMOUNT_REGENERATED
private constant abilityreallevelfield ATTACKSPEED_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
//These are not required by the system, but they were the way to interact with NewBonus by chopinski
//One could use these or use "BonusDamage.typeid"
integer BONUS_DAMAGE
integer BONUS_ARMOR
integer BONUS_AGILITY
integer BONUS_STRENGTH
integer BONUS_INTELLIGENCE
integer BONUS_HEALTH
integer BONUS_MANA
integer BONUS_MOVEMENT_SPEED
integer BONUS_SIGHT_RANGE
integer BONUS_HEALTH_REGEN
integer BONUS_MANA_REGEN
integer BONUS_ATTACK_SPEED
endglobals
//===== Integer Ability Based =====
struct BonusDamage extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return DAMAGE_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return DAMAGE_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_DAMAGE = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusArmor extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return ARMOR_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return ARMOR_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_ARMOR = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
/*
* Note: One can not use this ability to give negative movement speed bonus! It results in no change of movement speed unless there are positive movement speed already!
*/
struct BonusMovementSpeed extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return MOVEMENTSPEED_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return MOVEMENTSPEED_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_MOVEMENT_SPEED = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusSightRange extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return SIGHT_RANGE_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return SIGHT_RANGE_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_SIGHT_RANGE = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
//Hero stats
struct BonusAgility extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return STATS_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return AGILITY_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_AGILITY = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusStrength extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return STATS_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return STRENGTH_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_STRENGTH = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusIntelligence extends IntegerAbilityBonus
method AbilityCode takes nothing returns integer
return STATS_ABILITY
endmethod
method Field takes nothing returns abilityintegerlevelfield
return INTELLIGENCE_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_INTELLIGENCE = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
//===== Real Ability Based =====
struct BonusAttackSpeed extends RealAbilityBonus
method AbilityCode takes nothing returns integer
return ATTACKSPEED_ABILITY
endmethod
method Field takes nothing returns abilityreallevelfield
return ATTACKSPEED_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_ATTACK_SPEED = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusHealthRegen extends RealAbilityBonus
method AbilityCode takes nothing returns integer
return HEALTHREGEN_ABILITY
endmethod
method Field takes nothing returns abilityreallevelfield
return HEALTHREGEN_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_HEALTH_REGEN = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusManaRegen extends RealAbilityBonus
method AbilityCode takes nothing returns integer
return MANAREGEN_ABILITY
endmethod
method Field takes nothing returns abilityreallevelfield
return MANAREGEN_FIELD
endmethod
static method onInit takes nothing returns nothing
set BONUS_MANA_REGEN = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
//===== Custom handled =====
//I just copy pasted
struct BonusHealth extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0))
endmethod
method Set takes unit u, real value returns real
local real p = GetUnitLifePercent(u)
local integer amount = R2I(value)
call BlzSetUnitMaxHP(u, (BlzGetUnitMaxHP(u) + amount - BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0)))
call SetUnitLifePercentBJ(u, p)
if GetUnitAbilityLevel(u, HEALTH_ABILITY) == 0 then
call UnitAddAbility(u, HEALTH_ABILITY)
call UnitMakeAbilityPermanent(u, true, HEALTH_ABILITY)
endif
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0, amount) then
call IncUnitAbilityLevel(u, HEALTH_ABILITY)
call DecUnitAbilityLevel(u, HEALTH_ABILITY)
endif
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, HEALTH_ABILITY), HEALTH_FIELD, 0))
endmethod
static method onInit takes nothing returns nothing
set BONUS_HEALTH = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusMana extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0))
endmethod
method Set takes unit u, real value returns real
local real p = GetUnitManaPercent(u)
local integer amount = R2I(value)
call BlzSetUnitMaxMana(u, (BlzGetUnitMaxMana(u) + amount - BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0)))
call SetUnitManaPercentBJ(u, p)
if GetUnitAbilityLevel(u, MANA_ABILITY) == 0 then
call UnitAddAbility(u, MANA_ABILITY)
call UnitMakeAbilityPermanent(u, true, MANA_ABILITY)
endif
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0, amount) then
call IncUnitAbilityLevel(u, MANA_ABILITY)
call DecUnitAbilityLevel(u, MANA_ABILITY)
endif
return I2R(BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(u, MANA_ABILITY), MANA_FIELD, 0))
endmethod
static method onInit takes nothing returns nothing
set BONUS_MANA = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
endlibrary
library ExtendableBonusesMetroidvania requires ExtendableBonusSystem, CooldownReduction
/* ----------------------- ExtendableBonusSystemMetroidvania v0.1 by ThompZon ----------------------- */
//! novjass
Inspired by chopinski New Bonus system, I wanted a system that you can add your own bonuses
using a simple interface. Hopefully the interface will be simple so it is easy to implement
but also this means that the core can upgrade without impacting the extensions!
Inspired by: chopinski New Bonus system
https://www.hiveworkshop.com/threads/new-bonus-vjass-lua.324058/
jassApi
//! endnovjass
/* -------------------------------------------------------------------------- */
/* System */
/* --------------------------------------------------------------------------
*/
globals
//These are not required by the system, but they were the way to interact with NewBonus by chopinski
//One could use these or use "BonusDamage.typeid"
integer BONUS_SYSTEM_POWER
integer BONUS_SYSTEM_SPEED //% more casts. 100 of this = 50% BONUS_COOLDOWN_REDUCTION_FLAT
integer array systempower
integer array systemspeed
endglobals
struct BonusSystemPower extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
return I2R(systempower[GetUnitUserData(u)])
endmethod
method Set takes unit u, real value returns real
local integer unitIndex = GetUnitUserData(u)
set systempower[unitIndex] = R2I(value)
return I2R(systempower[unitIndex])
endmethod
static method onInit takes nothing returns nothing
set BONUS_SYSTEM_POWER = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
struct BonusSystemSpeed extends ExtendableBonusSystem_ExtendableBonusPlugin
method IsIntegerBonus takes nothing returns boolean
return true
endmethod
method Get takes unit u returns real
//return 100. * (1. / (1. - GetUnitCooldownReductionFlat(u)) - 1.0)
return I2R(systemspeed[GetUnitUserData(u)])
endmethod
method Set takes unit u, real value returns real
//call SetUnitCooldownReductionFlat(u, 1. - 1. / (1. + value / 100.))
local integer unitIndex = GetUnitUserData(u)
set systemspeed[unitIndex] = R2I(value)
return I2R(systemspeed[unitIndex])
endmethod
static method onInit takes nothing returns nothing
set BONUS_SYSTEM_SPEED = ExtendableBonus.Register(thistype.allocate())
endmethod
endstruct
function GetCDR takes unit u returns real
return 100. / (100. + systemspeed[GetUnitUserData(u)])
endfunction
endlibrary
library Respawn requires MetroidvaniaUtils
globals
texttag array hero_respawn_texttag
integer array hero_respawn_remaining
fogmodifier array hero_respawn_vision
constant real HERO_RESPAWN_DISTANCE_SQR = 150.0 * 150.0
endglobals
function heroRespawnIsSoloOwnerOfSpawnPoint takes integer index returns boolean
local group g = CreateGroup()
local unit u = null
local integer i
local boolean returnValue = true
local destructable spawnPoint = udg_respawnCurrentPoint[index]
call BlzGroupAddGroupFast(udg_heroes_all, g)
loop
set u = FirstOfGroup(g)
exitwhen returnValue or u == null
set i = GetUnitUserData(u)
if i != index and spawnPoint == udg_respawnCurrentPoint[i] then
set returnValue = false
endif
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set g = null
set u = null
set spawnPoint = null
return returnValue
endfunction
function heroRespawnChangeSpawnText takes unit u, real destx, real desty returns nothing
local texttag text = CreateTextTag()
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, 3.5)
call SetTextTagFadepoint(text, 2.5)
call setTextTagToPlayerColor(text, GetOwningPlayer(u))
call SetTextTagPos(text, destx, desty, 64.)
call SetTextTagVelocityBJ(text, 32., 90.)
call SetTextTagText(text, "Emergency Backup|nConnected!", TextTagSize2Height(8))
set text = null
endfunction
function heroRespawnUpdateSpawnPointWithDistanceCheck takes unit u, destructable newSpawnPoint returns nothing
local integer index = GetUnitUserData(u)
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local real destx = GetDestructableX(newSpawnPoint)
local real desty = GetDestructableY(newSpawnPoint)
local real dx = ux - destx
local real dy = uy - desty
local real distanceSqr = dx * dx + dy * dy
if distanceSqr < HERO_RESPAWN_DISTANCE_SQR and udg_respawnCurrentPoint[index] != newSpawnPoint then
if heroRespawnIsSoloOwnerOfSpawnPoint(index) then
call KillDestructable(udg_respawnCurrentPoint[index])
endif
set udg_respawnCurrentPoint[index] = newSpawnPoint
call DestructableRestoreLife(newSpawnPoint, 20.0, true)
call heroRespawnChangeSpawnText(u, destx, desty)
endif
endfunction
function heroRespawnGo takes integer index returns nothing
local unit u = udg_UDexUnits[index]
local destructable spawn = udg_respawnCurrentPoint[index]
local real x = GetDestructableX(spawn)
local real y = GetDestructableY(spawn)
call SetUnitX(u, x)
call SetUnitY(u, y)
call DestroyFogModifier(hero_respawn_vision[index])
call GroupAddUnit(udg_heroes_alive, u)
call GroupRemoveUnit(udg_heroes_dead, u)
if GetLocalPlayer() == GetOwningPlayer(u) then
call SelectUnit(u, true)
endif
set hero_respawn_vision[index] = null
set spawn = null
set u = null
endfunction
function heroRespawnSetupTexttag takes integer index returns nothing
local unit u = udg_UDexUnits[index]
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local texttag text = CreateTextTag()
set hero_respawn_texttag[index] = text
set u = null
call SetTextTagPermanent(text, false)
call SetTextTagLifespan(text, 5.5)
call SetTextTagFadepoint(text, 5.1)
call SetTextTagColor(text, 236, 60, 60, 190)
call SetTextTagPos(text, x, y, 128)
set text = null
endfunction
function heroRespawnTick takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer index = GetTimerData(t)
set hero_respawn_remaining[index] = hero_respawn_remaining[index] - 1
if hero_respawn_remaining[index] == 6 then
call heroRespawnSetupTexttag(index)
endif
if hero_respawn_remaining[index] > 0 then
call SetTextTagText(hero_respawn_texttag[index], "System Reset in:|n" + I2S(hero_respawn_remaining[index]), TextTagSize2Height(8))
else
call heroRespawnGo(index)
call ReleaseTimer(t)
endif
set t = null
endfunction
function heroRespawnLater takes unit u returns nothing
local integer index = GetUnitUserData(u)
local timer t = NewTimerEx(index)
set hero_respawn_remaining[index] = 7
set hero_respawn_vision[index] = CreateFogModifierRadius(GetOwningPlayer(u), FOG_OF_WAR_VISIBLE, GetUnitX(u), GetUnitY(u), 250.0, true, true)
call FogModifierStart(hero_respawn_vision[index])
call TimerStart(t, 1.0, true, function heroRespawnTick)
set t = null
set u = null
endfunction
endlibrary
globals
constant integer BOMB_UNIT_TYPE = 'h002'
constant integer TIMED_LIFE = 'BTLF'
constant string SOUND_DASH1 = "Sounds\\Hero\\SDash1.wav"
constant string SOUND_DASH2 = "Sounds\\Hero\\SDash2.wav"
endglobals
struct HeroAttackBasic extends Missiles
method onFinish takes nothing returns boolean
return true
endmethod
/*
static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
endmethod
*/
method onHit takes unit hit returns boolean
if BlzIsUnitSelectable(hit) and (not IsUnitAlly(hit, owner) or GetPlayerId(GetOwningPlayer(hit)) == PLAYER_NEUTRAL_PASSIVE) then
//set udg_NextDamageType = udg_DamageTypeCode
//call UnitDamageTarget(source, hit, damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
call Damage.applyAttack(source, hit, damage, true, ATTACK_TYPE_HERO, WEAPON_TYPE_WHOKNOWS)
return true
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
if hitDest(dest, 0) or GetDestructableOccluderHeight(dest) > 50.0 and GetDestructableLife(dest) > 0. then
return true
else
return false
endif
endmethod
method onTerrain takes nothing returns boolean
return true
endmethod
method onTileset takes integer tileset returns boolean
if tileset == 'cOc1' then
return true
endif
return false
endmethod
static method fireFrom takes unit u, real x, real y, real tx, real ty, real dmg, real spread, real distance, real speed returns nothing
//local real z = 100.
local real angle = Atan2(ty - y, tx - x) + GetRandomReal(-spread, spread)
local real tx2 = x + distance * Cos(angle)
local real ty2 = y + distance * Sin(angle)
local integer startCliff = GetTerrainCliffLevel(x, y)
local integer endCliff = GetTerrainCliffLevel(tx2, ty2)
local thistype this = thistype.create(x + 24. * Cos(angle), y + 24. * Sin(angle), 100., tx2, ty2, 64. + 128. * (startCliff - endCliff)) //GetZHeight(tx, ty) + 64)
set .source = u
set .model = "Units\\Hero\\BeamMissile.mdx"
set .speed = speed
set .collision = 26.
set .damage = dmg
set .owner = GetOwningPlayer(u)
call launch()
endmethod
static method fire takes unit u, real tx, real ty, real dmg, real spread, real distance, real speed returns nothing
call fireFrom(u, GetUnitX(u), GetUnitY(u), tx, ty, dmg, spread, distance, speed)
endmethod
public static method fireAsHeroActionFire takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer index = GetTimerData(t)
local unit u = udg_UDexUnits[index]
local real tx = MetroidvaniaController.getTargetX(GetOwningPlayer(u))
local real ty = MetroidvaniaController.getTargetY(GetOwningPlayer(u))
local real dmg = GetDamage(u)
call fire(u, tx, ty, dmg, 0.02, 1800., 1400.)
call ReleaseTimer(t)
set u = null
set t = null
endmethod
endstruct
struct HeroAttackBasicAction extends HeroActionBase
private method dmgPercent takes nothing returns real
return 1.0 + GetUnitBonus(u, BonusSystemPower.typeid) * 0.01
endmethod
private method damageCalculation takes nothing returns real
return dmgPercent() * GetDamage(u)
endmethod
public method animationSpeed takes nothing returns real
return 1.0 + 0.01 * GetUnitBonus(u, BonusSystemSpeed.typeid)
endmethod
method updateAbilityDetails takes nothing returns nothing
local real duration = 0.8 * GetCDR(u)
call cooldownDetails.update(0.33 * duration, duration, duration * 1.25)
set energyCost = 3.5
endmethod
method onStart takes nothing returns nothing
local player p = GetOwningPlayer(u)
local real tx = MetroidvaniaController.getTargetX(p)
local real ty = MetroidvaniaController.getTargetY(p)
call OrderAbilWithSpeed(u, ABIL_ATTACK_BASIC, cooldownDetails.actionDuration, animationSpeed(), tx, ty)
set p = null
endmethod
method onActionPoint takes nothing returns nothing
local player p = GetOwningPlayer(u)
local real tx = MetroidvaniaController.getTargetX(p)
local real ty = MetroidvaniaController.getTargetY(p)
local real dmg = damageCalculation()
call HeroAttackBasic.fire(u, tx, ty, dmg, 0.02, 1800., 1400.)
set p = null
endmethod
public static method create takes unit u returns thistype
local thistype this = HeroAttackBasicAction.allocate(u, UI_ATTACK, "Icons\\v2\\PlasmaBolt.blp")
return this
endmethod
method abilityText takes nothing returns string
local string s = "Fires a plama bolt towards targeted unit or mouse pointer. |nDamage: " + ScalingText(GetDamage(u), dmgPercent(), STAT_INDEX_FIREPOWER) + ".|n"
set s = s + CNOTE + Si2Name(STAT_INDEX_FIREPOWER) + "-percent is increased by 1% per " + Si2Name(STAT_INDEX_SYSTEM_POWER)
return s
endmethod
method abilityName takes nothing returns string
return "[Q] Plasma Bolt"
endmethod
//method onBackswing takes nothing returns nothing
// call BJDebugMsg("backswing finished")
//endmethod
//method onCooldownFinished takes nothing returns nothing
// call BJDebugMsg("cooldown finished")
//endmethod
endstruct
struct HeroAttackRapidAction extends HeroActionBase
private method dmgPercent takes nothing returns real
return 1.0 + GetUnitBonus(u, BonusSystemPower.typeid) * 0.01
endmethod
private method damageCalculation takes nothing returns real
return dmgPercent() * GetDamage(u)
endmethod
public method animationSpeed takes nothing returns real
return 1.0 + 0.01 * GetUnitBonus(u, BonusSystemSpeed.typeid)
endmethod
method updateAbilityDetails takes nothing returns nothing
local real duration = 0.8 * GetCDR(u)
call cooldownDetails.update(0.33 * duration, duration, duration * 1.25)
set energyCost = 3.5
endmethod
method onStart takes nothing returns nothing
local player p = GetOwningPlayer(u)
local real tx = MetroidvaniaController.getTargetX(p)
local real ty = MetroidvaniaController.getTargetY(p)
call OrderAbilWithSpeed(u, ABIL_ATTACK_BASIC, cooldownDetails.actionDuration, animationSpeed(), tx, ty)
set p = null
endmethod
method onActionPoint takes nothing returns nothing
local player p = GetOwningPlayer(u)
local real tx = MetroidvaniaController.getTargetX(p)
local real ty = MetroidvaniaController.getTargetY(p)
local real dmg = damageCalculation()
call HeroAttackBasic.fire(u, tx, ty, dmg, 0.02, 1800., 1400.)
set p = null
endmethod
public static method create takes unit u returns thistype
local thistype this = HeroAttackRapidAction.allocate(u, UI_ATTACK, "Icons\\v2\\PlasmaBolt.blp")
return this
endmethod
method abilityText takes nothing returns string
local string s = "Fires a plama bolt towards targeted unit or mouse pointer. |nDamage: " + ScalingText(GetDamage(u), dmgPercent(), STAT_INDEX_FIREPOWER) + ".|n"
set s = s + CNOTE + Si2Name(STAT_INDEX_FIREPOWER) + "-percent is increased by 1% per " + Si2Name(STAT_INDEX_SYSTEM_POWER)
return s
endmethod
method abilityName takes nothing returns string
return "[Q] Plasma Bolt"
endmethod
//method onBackswing takes nothing returns nothing
// call BJDebugMsg("backswing finished")
//endmethod
//method onCooldownFinished takes nothing returns nothing
// call BJDebugMsg("cooldown finished")
//endmethod
endstruct
struct HeroBasicBombAction extends HeroActionBase
private static real baseDmg = 10.
private static real percentDmg = 0.75
private static real percentPower = 1.0
method updateAbilityDetails takes nothing returns nothing
call cooldownDetails.update(0.0, 0.1, 5.0 * GetCDR(u))
set energyCost = 12.0
endmethod
method onStart takes nothing returns nothing
local player p = GetOwningPlayer(u)
local real tx = MetroidvaniaController.getTargetX(p)
local real ty = MetroidvaniaController.getTargetY(p)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real angle = Atan2(ty - y, tx - x)
local real ux = x + 64. * Cos(angle)
local real uy = y + 64. * Sin(angle)
local unit bomb
call updateWalkablePoint(ux, uy)
set bomb = CreateUnit(p, BOMB_UNIT_TYPE, GetLocationX(walkablePoint), GetLocationY(walkablePoint), 0.0)
call UnitApplyTimedLife(bomb, TIMED_LIFE, 2.0)
//call UnitAddAbility(bomb,'Amrf')
//call UnitRemoveAbility(bomb,'Amrf')
//call SetUnitFlyHeight(bomb, 128., 99999.)
set bomb = null
set p = null
endmethod
public static method create takes unit u returns thistype
local thistype this = thistype.allocate(u, UI_UTIL1, "Icons\\v2\\EnergyBomb.blp")
return this
endmethod
private static method destroyDestructable takes nothing returns nothing
endmethod
public static method destroyDebrisInArea takes real x, real y returns nothing
local real ux = x
local real uy = y
call SetRect(enumRect, ux - WALL_CHECK_RANGE, uy - WALL_CHECK_RANGE, ux + WALL_CHECK_RANGE, uy + WALL_CHECK_RANGE)
call EnumDestructablesInRect(enumRect, null, function thistype.destroyDestructable)
endmethod
public static method getDamage takes unit u returns real
return baseDmg + percentDmg * GetDamage(u) + percentPower * GetUnitBonus(u, BonusSystemPower.typeid)
endmethod
method abilityText takes nothing returns string
local real dmg = getDamage(u)
local string s = "Places a bomb at your current location, exploding after 2 seconds, dealing damage and slowing in a 200 unit radius.|nDamage: " + R2I2S(dmg) + " (" + R2I2S(baseDmg) + " + " + ScalingText(GetDamage(u), percentDmg, STAT_INDEX_FIREPOWER)
set s = s + " + " + ScalingText(GetUnitBonus(u, BonusSystemPower.typeid), percentPower, STAT_INDEX_SYSTEM_POWER) + ").|nSlows: 40% for 1.5 seconds.|nThe bomb is able to destroy some webs, rocks and debries."
return s
endmethod
method abilityName takes nothing returns string
return "[E] Energy Bomb"
endmethod
endstruct
struct HeroBasicDash extends HeroActionBase
method percent takes nothing returns real
return 1.1 + 0.005 * GetUnitBonus(u, BonusSystemPower.typeid)
endmethod
method dashedDistance takes nothing returns real
return percent() * GetMovementSpeed(u)
endmethod
method updateAbilityDetails takes nothing returns nothing
call cooldownDetails.update(0.320, 0.360, 2.5 * GetCDR(u))
set energyCost = RMaxBJ(11.0 - R2I(GetUnitBonus(u, BonusSystemPower.typeid)+0.01) / 10, 3.)
endmethod
method dashSpeed takes nothing returns real
return dashedDistance() / cooldownDetails.actionDuration
endmethod
method onStart takes nothing returns nothing
local player p = GetOwningPlayer(u)
local real tx = MetroidvaniaController.getTargetX(p)
local real ty = MetroidvaniaController.getTargetY(p)
local real ux = GetUnitX(u)
local real uy = GetUnitY(u)
local real angle = Atan2(ty - uy, tx - ux)
//0.800 animation-time, we want total time to be somewhat shorter than that, lets go for 0.480s
//Let the speed just increase the distance, not the animation-time
call OrderAbilWithSpeed(u, ABIL_SPELL_SLAM, cooldownDetails.actionDuration, 1.25, tx, ty)
//set udg_Knockback2DTime = cooldownDetails.actionDuration
//set udg_Knockback2DDistance = dashedDistance()
//set udg_Knockback2DCollision = 8.00
//set udg_Knockback2DAngle = Atan2(ty - uy, tx - ux) * bj_RADTODEG
//set udg_Knockback2DUnit = u
//set udg_Knockback2DLoopFX = ""
//call ConditionalTriggerExecute( gg_trg_Knockback_2D )
call KnockbackTZ.doKnockback(u, angle, dashSpeed(), cooldownDetails.actionDuration, 0., 1., 8.)
if GetRandomInt(0,1) == 1 then
call playSound3d(ux, uy, SOUND_DASH1)
else
call playSound3d(ux, uy, SOUND_DASH2)
endif
set p = null
endmethod
public static method create takes unit u returns thistype
local thistype this = thistype.allocate(u, UI_DASH, "Icons\\v2\\Dash.blp")
set .ignoreTarget = true
return this
endmethod
//method onActionPoint takes nothing returns nothing
//endmethod
//method onBackswing takes nothing returns nothing
// call BJDebugMsg("backswing finished")
//endmethod
//method onCooldownFinished takes nothing returns nothing
// call BJDebugMsg("cooldown finished")
//endmethod
method abilityText takes nothing returns string
local string s = "Dashes towards mouse pointer over 0.36 seconds. Dashed distance: " + ScalingText(GetMovementSpeed(u), percent(), STAT_INDEX_MOBILITY) + ".|n" + CNOTE + Si2Name(STAT_INDEX_MOBILITY) + "-percent is increased by 0.5% per "
set s = s + Si2Name(STAT_INDEX_SYSTEM_POWER) + ", and "+Si2Color(STAT_INDEX_ENERGY)+"Energy-cost"+CNOTE+" reduced by 1 per 10 " + Si2Name(STAT_INDEX_SYSTEM_POWER) + " (minimum 3 Energy)"
return s
endmethod
method abilityName takes nothing returns string
return "[D] Dash"
endmethod
endstruct
struct HeroShockBlast extends Missiles
private boolean isEmpowered
method boom takes nothing returns nothing
local group g = CreateGroup()
if isEmpowered then
call damageAoENoFF(source, x, y, damage * 1.5, 250., DAMAGE_TYPE_LIGHTNING)
call destroyDest2(x, y, 275.)
else
call damageAoENoFF(source, x, y, damage, 125., DAMAGE_TYPE_LIGHTNING)
call destroyDest1(x, y, 150.)
endif
call DestroyGroup(g)
set g = null
endmethod
method onFinish takes nothing returns boolean
return true
endmethod
/*
static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
endmethod
*/
method onHit takes unit hit returns boolean
if BlzIsUnitSelectable(hit) and (not IsUnitAlly(hit, owner) or GetPlayerId(GetOwningPlayer(hit)) == PLAYER_NEUTRAL_PASSIVE) then
//set udg_NextDamageType = udg_DamageTypeCode
//call UnitDamageTarget(source, hit, damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
call boom()
return true
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
if hitDest(dest, 1) or GetDestructableOccluderHeight(dest) > 50.0 and GetDestructableLife(dest) > 0. then
return true
else
return false
endif
endmethod
method onTerrain takes nothing returns boolean
return true
endmethod
method onTileset takes integer tileset returns boolean
if tileset == 'cOc1' then
return true
endif
return false
endmethod
static method fireFrom takes unit u, real x, real y, real tx, real ty, real dmg, real spread, real distance, real speed returns nothing
//local real z = 100.
local real angle = Atan2(ty - y, tx - x) + GetRandomReal(-spread, spread)
local real tx2 = x + distance * Cos(angle)
local real ty2 = y + distance * Sin(angle)
local integer startCliff = GetTerrainCliffLevel(x, y)
local integer endCliff = GetTerrainCliffLevel(tx2, ty2)
local thistype this = thistype.create(x + 24. * Cos(angle), y + 24. * Sin(angle), 100., tx2, ty2, 64. + 128. * (startCliff - endCliff)) //GetZHeight(tx, ty) + 64)
set .source = u
set .model = "Units\\Hero\\BeamMissile.mdx"
set .speed = speed
set .collision = 26.
set .damage = dmg
set .owner = GetOwningPlayer(u)
call launch()
endmethod
static method fire takes unit u, real tx, real ty, real dmg, real spread, real distance, real speed returns nothing
call fireFrom(u, GetUnitX(u), GetUnitY(u), tx, ty, dmg, spread, distance, speed)
endmethod
public static method fireAsHeroActionFire takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer index = GetTimerData(t)
local unit u = udg_UDexUnits[index]
local real tx = MetroidvaniaController.getTargetX(GetOwningPlayer(u))
local real ty = MetroidvaniaController.getTargetY(GetOwningPlayer(u))
local real dmg = GetDamage(u)
call fire(u, tx, ty, dmg, 0.02, 1800., 1400.)
call ReleaseTimer(t)
set u = null
set t = null
endmethod
endstruct
globals
constant real INTERACT_RANGE = 160.
constant string INTERACTION_TOGGLEABLE = "i_tog"
constant string INTERACTION_TERMINAL = "i_term"
constant string INTERACTION_TERMINAL_TOGGLABLE = "term_togglable"
constant real INTERACT_TERMINAL_RANGE = 488
endglobals
struct InteractAction extends HeroActionBase
method updateAbilityDetails takes nothing returns nothing
call cooldownDetails.update(0.1, 0.2, 1.0)
endmethod
method onStart takes nothing returns nothing
//TODO: Play a sound
endmethod
private static method processTerminalTogglable takes nothing returns nothing
local destructable d = GetEnumDestructable()
local string name = GetDestructableName(d)
if strEndsWith(name, INTERACTION_TERMINAL_TOGGLABLE) then
if GetDestructableLife(d) > 0 then
call OpenDoor(d)
else
call CloseDoor(d)
endif
endif
endmethod
private static method toggleNearbyTerminalTogglable takes destructable dest returns nothing
local real x = GetDestructableX(dest)
local real y = GetDestructableY(dest)
call SetRect(enumRect, x - INTERACT_TERMINAL_RANGE, y - INTERACT_TERMINAL_RANGE, x + INTERACT_TERMINAL_RANGE, y + INTERACT_TERMINAL_RANGE)
call EnumDestructablesInRect(enumRect, null, function thistype.processTerminalTogglable)
endmethod
private static method processDestructable takes nothing returns nothing
local destructable d = GetEnumDestructable()
local string name = GetDestructableName(d)
local integer len = StringLength(name)
if SubString(name, len - 5, len) == INTERACTION_TOGGLEABLE then
if GetDestructableLife(d) > 0 then
call OpenDoor(d)
else
call CloseDoor(d)
endif
elseif strEndsWith(name, INTERACTION_TERMINAL) and GetDestructableLife(d) > 0. then
call toggleNearbyTerminalTogglable(d)
endif
set d = null
endmethod
method onActionPoint takes nothing returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call SetRect(enumRect, x - INTERACT_RANGE, y - INTERACT_RANGE, x + INTERACT_RANGE, y + INTERACT_RANGE)
call EnumDestructablesInRect(enumRect, null, function thistype.processDestructable)
endmethod
public static method create takes unit u returns thistype
local thistype this = thistype.allocate(u, UI_INTERACT, ICON_INTERACT)
return this
endmethod
method abilityText takes nothing returns string
return "Open or closes nearby smaller doors and interact with computers connected with cables."
endmethod
method abilityName takes nothing returns string
return "[F] Interact"
endmethod
endstruct
globals
constant real MCL_CONVEYOR_TICK_RATE = 0.04
constant real MCL_CONVEYOR_BEAM_DMG_BASE = 21.
real mcl_crystal_l_x
real mcl_crystal_l_y
real mcl_crystal_r_x
real mcl_crystal_r_y
real mcl_transmitter_l_x
real mcl_transmitter_l_y
real mcl_transmitter_r_x
real mcl_transmitter_r_y
lightning mcl_l_beam_static
lightning mcl_r_beam_static
lightning mcl_l_beam_to_boss
lightning mcl_r_beam_to_boss
timer mcl_conveyor_t
integer mcl_conveyor_counter
lightning mcl_conveyor_s
lightning mcl_conveyor_n
integer mcl_missile_counter
endglobals
struct FinalBossHoming extends Missiles
method onFinish takes nothing returns boolean
return true
endmethod
/*
static method applyAttack takes unit src, unit tgt, real amt, boolean ranged, attacktype at, weapontype wt returns Damage
return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
endmethod
*/
method onHit takes unit hit returns boolean
if not IsUnitAlly(hit, owner) then
//set udg_NextDamageType = udg_DamageTypeCode
//call UnitDamageTarget(source, hit, damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null)
call Damage.applyAttack(source, hit, damage, true, ATTACK_TYPE_HERO, WEAPON_TYPE_WHOKNOWS)
return true
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
if GetDestructableOccluderHeight(dest) > 50.0 and GetDestructableLife(dest) > 0. then
return true
else
return false
endif
endmethod
method onTerrain takes nothing returns boolean
return true
endmethod
method onTileset takes integer tileset returns boolean
if tileset == 'cOc1' then
return true
endif
return false
endmethod
static method fire takes boolean isLeft returns nothing
local real x = GetUnitX(udg_bossUnit)
local real y = GetUnitY(udg_bossUnit)
local real bossFacing = GetUnitFacing(udg_bossUnit) * bj_DEGTORAD
local unit target = GetRandomHero()
local real tx = GetUnitX(target)
local real ty = GetUnitY(target)
local thistype this
if isLeft then
set x = x + 44. * Cos(bossFacing + bj_PI / 2.) + 30. * Cos(bossFacing)
set y = y + 44. * Sin(bossFacing + bj_PI / 2.) + 30. * Sin(bossFacing)
else
set x = x - 44. * Cos(bossFacing + bj_PI / 2.) + 30. * Cos(bossFacing)
set y = y - 44. * Sin(bossFacing + bj_PI / 2.) + 30. * Sin(bossFacing)
endif
set this = thistype.create(x, y, 128., tx, ty, 32.)
//
set .source = udg_bossUnit
set .target = target
set .arc = GetRandomReal(14., 24.)
set .curve = GetRandomReal(-7., 7.)
set .model = "Units\\Jab1z\\Mecha-Locust.mdx"
set .speed = GetRandomReal(425., 500.)
set .acceleration = 0.025
set .collision = 26.
set .turn = 0.022
set .damage = 0.75 * GetDamage(udg_bossUnit)
set .owner = GetOwningPlayer(udg_bossUnit)
//
call launch()
endmethod
static method fireTick takes nothing returns nothing
call fire(true)
call fire(false)
call ReleaseTimer(GetExpiredTimer())
endmethod
static method startFire takes nothing returns nothing
call fire(true)
call fire(false)
//call TimerStart(NewTimer(), 0.2, false, function thistype.fireTick)
endmethod
endstruct
function MCLLightningUpdate takes nothing returns nothing
local real bossX = GetUnitX(udg_bossUnit)
local real bossY = GetUnitY(udg_bossUnit)
//call BJDebugMsg("Update Start")
if UnitAlive(udg_boss_MC_crystal_l) then
call DestroyLightning(mcl_l_beam_to_boss)
set mcl_l_beam_to_boss = AddLightningEx("GRSB", false, mcl_transmitter_l_x, mcl_transmitter_l_y, GetZ(mcl_transmitter_l_x, mcl_transmitter_l_y) + 220., bossX, bossY, GetZ(bossX, bossY) + 72.)
//call BJDebugMsg("L Update")
endif
if UnitAlive(udg_boss_MC_crystal_r) then
call DestroyLightning(mcl_r_beam_to_boss)
set mcl_r_beam_to_boss = AddLightningEx("GRSB", false, mcl_transmitter_r_x, mcl_transmitter_r_y, GetZ(mcl_transmitter_r_x, mcl_transmitter_r_y) + 220., bossX, bossY, GetZ(bossX, bossY) + 72.)
//call BJDebugMsg("R Update")
endif
endfunction
function MCLFinalLightning takes nothing returns nothing
local real bossX = GetUnitX(udg_bossUnit)
local real bossY = GetUnitY(udg_bossUnit)
local real x = GetRectCenterX(gg_rct_FinalBoss_Door)
local real y = GetRectCenterY(gg_rct_FinalBoss_Door)
local lightning finalLightning = AddLightningEx("GRSB", false, bossX, bossY, GetZ(bossX, bossY) + 64., x, y, GetZ(x, y) + 64.)
call DestroyLightningLater(finalLightning, 0.5)
set finalLightning = null
endfunction
function MCLSetupLightning takes nothing returns nothing
local real bossX = GetUnitX(udg_bossUnit)
local real bossY = GetUnitY(udg_bossUnit)
set mcl_l_beam_static = AddLightningEx("GRSB", false, mcl_crystal_l_x, mcl_crystal_l_y, GetZ(mcl_crystal_l_x, mcl_crystal_l_y) + 40., mcl_transmitter_l_x, mcl_transmitter_l_y, GetZ(mcl_transmitter_l_x, mcl_transmitter_l_y) + 220.)
set mcl_r_beam_static = AddLightningEx("GRSB", false, mcl_crystal_r_x, mcl_crystal_r_y, GetZ(mcl_crystal_r_x, mcl_crystal_r_y) + 40., mcl_transmitter_r_x, mcl_transmitter_r_y, GetZ(mcl_transmitter_r_x, mcl_transmitter_r_y) + 220.)
set mcl_l_beam_to_boss = AddLightningEx("GRSB", false, mcl_transmitter_l_x, mcl_transmitter_l_y, GetZ(mcl_transmitter_l_x, mcl_transmitter_l_y) + 220., bossX, bossY, GetZ(bossX, bossY) + 72.)
set mcl_r_beam_to_boss = AddLightningEx("GRSB", false, mcl_transmitter_r_x, mcl_transmitter_r_y, GetZ(mcl_transmitter_r_x, mcl_transmitter_r_y) + 220., bossX, bossY, GetZ(bossX, bossY) + 72.)
endfunction
function MCLConveyorLightning_Create takes nothing returns nothing
local real y = GetRectCenterY(gg_rct_FinalBoss_ConveyorZap1_1)
local real minX = GetRectMinX(gg_rct_FinalBoss_ConveyorZap1_1) + 10.
local real maxX = GetRectMaxX(gg_rct_FinalBoss_ConveyorZap1_1) - 10.
local real z = GetZ(minX, y) + 60.
set mcl_conveyor_s = AddLightningEx("BLNL", true, minX, y, z, maxX, y, z)
set y = GetRectCenterY(gg_rct_FinalBoss_ConveyorZap2_1)
set mcl_conveyor_n = AddLightningEx("BLNL", true, minX, y, z, maxX, y, z)
call StartSound(gg_snd_MCL_Lightning1)
endfunction
function MCLConveyorLightning_End takes nothing returns nothing
call ReleaseTimer(mcl_conveyor_t)
if mcl_conveyor_counter < 30 then
call DestroyLightning(mcl_conveyor_s)
call DestroyLightning(mcl_conveyor_n)
endif
endfunction
function MCLConveyorDamage takes nothing returns nothing
local group g = CreateGroup()
call GroupEnumUnitsInRect(g, gg_rct_FinalBoss_ConveyorZap1_1, null)
call damageGroup(g, udg_bossUnit, MCL_CONVEYOR_BEAM_DMG_BASE, DAMAGE_TYPE_LIGHTNING)
set g = CreateGroup()
call GroupEnumUnitsInRect(g, gg_rct_FinalBoss_ConveyorZap2_1, null)
call damageGroup(g, udg_bossUnit, MCL_CONVEYOR_BEAM_DMG_BASE, DAMAGE_TYPE_LIGHTNING)
set g = null
endfunction
function MCLConveyorLightning_Tick takes nothing returns nothing
set mcl_conveyor_counter = mcl_conveyor_counter + 1
if mcl_conveyor_counter < 30 then
//Deal dmg
call MCLConveyorDamage()
elseif mcl_conveyor_counter == 30 then
//Stop
call DestroyLightning(mcl_conveyor_s)
call DestroyLightning(mcl_conveyor_n)
elseif mcl_conveyor_counter < 39 then
//Nothing Pause
else
//"Restart"
call MCLConveyorLightning_Create()
set mcl_conveyor_counter = 0
endif
endfunction
function MCLConveyorLightning_Start takes nothing returns nothing
set mcl_conveyor_t = NewTimer()
set mcl_conveyor_counter = 999
call TimerStart(mcl_conveyor_t, 0.05, true, function MCLConveyorLightning_Tick)
call SetSoundPosition(gg_snd_MCL_Lightning1, GetRectCenterX(gg_rct_FinalBoss_ConveyorZap1_1), GetRectCenterY(gg_rct_FinalBoss_ConveyorZap1_1), 512.)
endfunction
function MCLSpawnSawBotBirthed takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u = udg_UDexUnits[GetTimerData(t)]
call ResetUnitAnimation(u)
call PauseUnit(u, false)
call ReleaseTimer(t)
call AttackMoveTowardsRandomHero(u)
set t = null
set u = null
endfunction
function MCLSpawnSawBot takes real x, real y, real angle returns nothing
local unit u = CreateUnit(GetOwningPlayer(udg_bossUnit), 'u002', x, y, angle)
call PauseUnit(u, true)
call SetUnitAnimation(u, "birth")
call TimerStart(NewTimerEx(GetUnitUserData(u)), 1.07, false, function MCLSpawnSawBotBirthed)
set u = null
endfunction
function MCLSpawnSawBotNearBoss takes nothing returns nothing
local real bx = GetUnitX(udg_bossUnit)
local real by = GetUnitY(udg_bossUnit)
local real angle = GetRandomReal(0., 2. * bj_PI)
local real distance = 160.
local real tx = bx + Cos(angle) * distance
local real ty = by + Sin(angle) * distance
loop
exitwhen IsWalkableWithCollisionSize(tx, ty, 16.)
set angle = GetRandomReal(0., 2. * bj_PI)
set distance = distance + 5.
set tx = bx + Cos(angle) * distance
set ty = by + Sin(angle) * distance
endloop
call MCLSpawnSawBot(tx, ty, angle)
endfunction
function MCLHeroDeath takes nothing returns nothing
call DestroyLightning(mcl_l_beam_static)
call DestroyLightning(mcl_r_beam_static)
call DestroyLightning(mcl_l_beam_to_boss)
call DestroyLightning(mcl_r_beam_to_boss)
call MCLConveyorLightning_End()
endfunction
globals
constant integer DEST_ICE_WALL = 'B01T'
endglobals
function HasIceWallCheck takes nothing returns nothing
//call BJDebugMsg("EnumDest=" + I2S(GetDestructableTypeId(GetEnumDestructable())) + ", IW: " + I2S(DEST_ICE_WALL))
if GetDestructableTypeId(GetEnumDestructable()) == DEST_ICE_WALL then
set udg_t_bool1 = true
endif
endfunction
function CreateIceWall takes real x, real y returns nothing
//call BJDebugMsg("Create called!")
call CreateDestructable(DEST_ICE_WALL, x, y, GetRandomDirectionDeg(), GetRandomReal(0.42, 0.52), GetRandomInt(0, 5))
//call BJDebugMsg("Created!")
endfunction
function HasIceWall takes rect where returns boolean
set udg_t_bool1 = false
//call BJDebugMsg("Has check")
call EnumDestructablesInRect(where, null, function HasIceWallCheck)
//call BJDebugMsg("Has checked")
return udg_t_bool1
endfunction
function CreateIceWallUnlessExists takes rect where returns nothing
if not HasIceWall(where) then
call CreateIceWall(GetRectCenterX(where), GetRectCenterY(where))
endif
endfunction
function CreateIceWallUnlessExistsCoinflip takes rect where returns nothing
if GetRandomInt(0, 1) == 0 then
call CreateIceWallUnlessExists(where)
endif
endfunction
function CreateIceWallUEAtLeastOne takes rect r1, rect r2 returns nothing
local integer random = GetRandomInt(1, 3)
if BlzBitAnd(random, 1) > 0 then
call CreateIceWallUnlessExists(r1)
endif
if BlzBitAnd(random, 2) > 0 then
call CreateIceWallUnlessExists(r2)
endif
endfunction
function CreateIceWallUEAtMostOne takes rect r1, rect r2 returns nothing
local integer random = GetRandomInt(0, 2)
if random == 1 then
call CreateIceWallUnlessExists(r1)
endif
if random == 2 then
call CreateIceWallUnlessExists(r2)
endif
endfunction
globals
constant integer MECHA_DH_UID = 'h000'
constant integer ZOMBIE_UID = 'n001'
constant integer ZOMBIE_DOG_UID = 'n003'
constant integer SPIDER_UID = 'n006'
constant integer SPIDERLING_UID = 'n005'
constant integer BROODMOTHER_UID = 'n004'
constant integer MECHA_CL_UID = 'u001'
constant integer SAW_BOT_UID = 'u002'
Table enemyKillsT // tracks kills per unit-type
Table coreKillsToNextDropT // Kills left to next drop
Table coreDropGrowT // max drops per unit-type
Table coreDropKillsRequiredT // Minimum kills per dropped core
Table unitMovementSpeed // Setup unit-type to movement-speed. Movement speed is added as bonus, so I can easier slow unit with triggers
Table unitMaxHp // Setup unit-type to max-hp.
Table destructableEvents
endglobals
function initMovementSpeed takes nothing returns nothing
set unitMovementSpeed = Table.create()
set unitMovementSpeed[MECHA_DH_UID] = 285
set unitMovementSpeed[ZOMBIE_UID] = 175
set unitMovementSpeed[ZOMBIE_DOG_UID] = 280
set unitMovementSpeed[SPIDER_UID] = 280
set unitMovementSpeed[SPIDERLING_UID] = 330
set unitMovementSpeed[BROODMOTHER_UID] = 250
set unitMovementSpeed[MECHA_CL_UID] = 270
set unitMovementSpeed[SAW_BOT_UID] = 260
endfunction
function initMaxHp takes nothing returns nothing
local integer players = CountPlayersInForceBJ(udg_human_players)
set unitMaxHp = Table.create()
set unitMaxHp[MECHA_DH_UID] = 100
set unitMaxHp[ZOMBIE_UID] = 32 + 20 * players
set unitMaxHp[ZOMBIE_DOG_UID] = 25 + 15 * players
set unitMaxHp[SPIDER_UID] = 48 + 20 * players
set unitMaxHp[SPIDERLING_UID] = 28 + 10 * players
set unitMaxHp[BROODMOTHER_UID] = 750 * players + 75 * players * players
set unitMaxHp[MECHA_CL_UID] = 900 * players + 100 * players * players
set unitMaxHp[SAW_BOT_UID] = 65 + 20 * players
endfunction
function jassDelayedInit takes nothing returns nothing
call initMaxHp()
endfunction
function jassInit takes nothing returns nothing
call hideBaseGui()
call initMovementSpeed()
set destructableEvents = Table.create()
//Setup tables for core-drops from enemies
//enemyKills tracks kills per unit-type
set enemyKillsT = Table.create()
set coreKillsToNextDropT = Table.create()
set coreDropGrowT = Table.create()
set coreDropKillsRequiredT = Table.create()
set coreDropGrowT[ZOMBIE_UID] = 3
set coreDropKillsRequiredT[ZOMBIE_UID] = 5
set coreDropGrowT[ZOMBIE_DOG_UID] = 2
set coreDropKillsRequiredT[ZOMBIE_DOG_UID] = 5
set coreDropGrowT[SPIDER_UID] = 2
set coreDropKillsRequiredT[SPIDER_UID] = 4
set coreDropGrowT[SPIDERLING_UID] = 3
set coreDropKillsRequiredT[SPIDERLING_UID] = 7
set coreKillsToNextDropT[ZOMBIE_UID] = coreDropKillsRequiredT[ZOMBIE_UID]
set coreKillsToNextDropT[ZOMBIE_DOG_UID] = coreDropKillsRequiredT[ZOMBIE_DOG_UID]
set coreKillsToNextDropT[SPIDER_UID] = coreDropKillsRequiredT[SPIDER_UID]
set coreKillsToNextDropT[SPIDERLING_UID] = coreDropKillsRequiredT[SPIDERLING_UID]
endfunction
//set coreDropT[udg_t_unittype1] = coreDropT[udg_t_unittype1] + 1
//set udg_t_int1 = coreDropKillsRequiredT[udg_t_unittype1]
//set udg_t_int2 = coreMaxDropT[udg_t_unittype1]
//if enemyKillsT[udg_t_unittype1] * (coreDropT[udg_t_unittype1] + 1) >= coreDropKillsRequiredT[udg_t_unittype1] * (coreDropT[udg_t_unittype1] + 1) and coreDropT[udg_t_unittype1] <= coreMaxDropT[udg_t_unittype1] then
//
//call BJDebugMsg("Kills: " + I2S(enemyKillsT[udg_t_unittype1]) + ", coresDropped=" + I2S(coreDropT[udg_t_unittype1]) + ", req=" + I2S(coreDropKillsRequiredT[udg_t_unittype1]) + ", max=" + I2S(coreMaxDropT[udg_t_unittype1]))
//set destructableEvents[GetHandleId(udg_t_destructible1)] = function MCLOnBossVentDeath
//set coreDropKillsRequiredT[udg_t_unittype1] = coreDropKillsRequiredT[udg_t_unittype1] + coreDropGrowT[udg_t_unittype1]
//set coreKillsToNextDropT[udg_t_unittype1] = coreKillsToNextDropT[udg_t_unittype1] - 1