Name | Type | is_array | initial_value |
allocatedAttacks | integer | No | |
allocCounter | integer | No | |
amount | real | No | |
arenaTiimerWindow | timerdialog | No | |
arenaTimer | timer | No | |
ATTACK_TYPE_UNIVERSAL | attacktype | No | |
B_BLAZE_CONFIG_ABILITY | abilcode | No | |
B_BLAZE_CONFIG_BUFF | buffcode | No | |
B_BLAZE_CONFIG_DURATION | real | No | |
B_BLAZE_CONFIG_LIFE_FACTOR | real | No | |
B_BLAZE_CONFIG_MAX_DAMAGE | real | No | |
B_BLAZE_CONFIG_SPFX | string | No | |
B_BLAZE_CURRENT_INDEX | integer | No | |
B_BLAZE_DAMAGE | real | Yes | |
B_BLAZE_DAMAGE_SOURCE | unit | Yes | |
B_BLAZE_DAMAGED_UNIT | unit | Yes | |
B_BLAZE_DURATION | real | Yes | |
B_BLAZE_EXAMPLE_LOOP | integervar | No | |
B_BLAZE_INDEX_CONTAINER | integer | Yes | |
B_BLAZE_INDEX_LISTENER | integer | No | |
B_BLAZE_INDEX_SIZE | integer | No | |
B_BLAZE_RECYCLE_CONTAINER | integer | Yes | |
B_BLAZE_RECYCLE_SIZE | integer | No | |
B_BLAZE_SPFX | effect | Yes | |
B_CON_ABILITY | abilcode | No | |
B_CON_CONFIG_AOE | real | Yes | |
B_CON_CONFIG_DAMAGE | real | Yes | |
B_CON_DAMAGE | real | No | |
B_CON_LVL_OF_ABILITY | integer | No | |
B_CON_PICKED_UNIT | unit | No | |
B_CON_PICKED_UNITS | group | No | |
B_CON_SPFX1 | string | No | |
B_CON_SPFX2 | string | No | |
B_CON_TARGET_LOC | location | No | |
B_CON_TARGET_UNIT | unit | No | |
B_CON_TRIGGER_UNIT | unit | No | |
B_POF_ABILITY | abilcode | No | |
B_POF_AOE | real | Yes | |
B_POF_BONUS_DAMAGE | real | Yes | |
B_POF_CONFIG_AOE | real | Yes | |
B_POF_CONFIG_DAMAGE | real | Yes | |
B_POF_CONFIG_DAMAGE_FACTOR | real | Yes | |
B_POF_CONFIG_DELAY | real | Yes | |
B_POF_CURRENT_INDEX | integer | No | |
B_POF_DAMAGE | real | Yes | |
B_POF_DAMAGE_FACTOR | real | Yes | |
B_POF_DELAY | real | Yes | |
B_POF_DUMMY_UNIT | unit | Yes | |
B_POF_EXAMPLE_LOOP | integervar | No | |
B_POF_INDEX_CONTAINER | integer | Yes | |
B_POF_INDEX_LISTENER | integer | No | |
B_POF_INDEX_SIZE | integer | No | |
B_POF_LVL_OFABILITY | integer | No | |
B_POF_PICKED_UNIT | unit | No | |
B_POF_PICKED_UNITS | group | No | |
B_POF_RECYCLE_CONTAINER | integer | Yes | |
B_POF_RECYCLE_SIZE | integer | No | |
B_POF_SPFX | string | No | |
B_POF_TARGET_POINT | location | Yes | |
B_POF_TRIGGER_UNIT | unit | Yes | |
B_PYR_ABILITY | abilcode | No | |
B_PYR_CONFIG_AOE | real | Yes | |
B_PYR_CONFIG_BOUNCES | integer | Yes | |
B_PYR_CONFIG_CURRENT_DISTANCE | real | No | |
B_PYR_CONFIG_DAMAGE | real | Yes | |
B_PYR_CONFIG_MIN_DISTANCE | real | No | |
B_PYR_CONFIG_SPEED | real | Yes | |
B_PYR_CONFIG_UNIT_TYPE | unitcode | No | |
B_PYR_CURRENT_INDEX | integer | No | |
B_PYR_EXAMPLE_LOOP | integervar | No | |
B_PYR_G_AOE | real | Yes | |
B_PYR_G_BOUNCES | integer | Yes | |
B_PYR_G_DAMAGE | real | Yes | |
B_PYR_G_PICKED_UNITS | group | No | |
B_PYR_G_PROJECTILE | unit | Yes | |
B_PYR_G_SPEED | real | Yes | |
B_PYR_G_TARGET_UNIT | unit | Yes | |
B_PYR_G_TEMP_ANGLE | real | No | |
B_PYR_G_TEMPLOC1 | location | No | |
B_PYR_G_TEMPLOC2 | location | No | |
B_PYR_G_TRIGGER_UNIT | unit | Yes | |
B_PYR_INDEX_CONTAINER | integer | Yes | |
B_PYR_INDEX_LISTENER | integer | No | |
B_PYR_INDEX_SIZE | integer | No | |
B_PYR_LVLOFABILITY | integer | No | |
B_PYR_RECYCLE_CONTAINER | integer | Yes | |
B_PYR_RECYCLE_SIZE | integer | No | |
B_PYR_SPFX | string | No | |
B_SEAR_ABILITY | abilcode | No | |
B_SEAR_ANGLE | real | Yes | |
B_SEAR_AOE | real | Yes | |
B_SEAR_CONFIG_BASE_AOE | real | Yes | |
B_SEAR_CONFIG_BASE_DAMAGE | real | Yes | |
B_SEAR_CONFIG_BASE_RANGE | real | Yes | |
B_SEAR_CONFIG_BASE_SPEED | real | Yes | |
B_SEAR_CURRENT_INDEX | integer | No | |
B_SEAR_DAMAGE | real | Yes | |
B_SEAR_EXAMPLE_LOOP | integer | No | |
B_SEAR_HASHTABLE | hashtable | No | |
B_SEAR_HIT_A_UNIT | boolean | Yes | |
B_SEAR_INDEX_CONTAINER | integer | Yes | |
B_SEAR_INDEX_LISTENER | integer | No | |
B_SEAR_INDEX_SIZE | integer | No | |
B_SEAR_LEVEL_OF_ABILITY | integer | Yes | |
B_SEAR_MISSILE_TYPE | unitcode | No | |
B_SEAR_PICKED_UNIT | unit | No | |
B_SEAR_PICKED_UNITS | group | No | |
B_SEAR_PROJECTILE | unit | Yes | |
B_SEAR_RANGE | real | Yes | |
B_SEAR_RECYCLE_CONTAINER | integer | Yes | |
B_SEAR_RECYCLE_SIZE | integer | No | |
B_SEAR_SPEED | real | Yes | |
B_SEAR_SPFX | string | No | |
B_SEAR_TEMPLOC1 | location | No | |
B_SEAR_TEMPLOC2 | location | No | |
B_SEAR_TRIGGER_UNIT | unit | Yes | |
Beast_Attack_Ability_Level | integer | No | |
Beast_Attack_Atribute | integer | No | |
Beast_Attack_Base_Offset | real | No | |
Beast_Attack_Base_Radius | real | No | |
Beast_Attack_Bonus_Offset | real | No | |
Beast_Attack_Bonus_Radius | real | No | |
Beast_Attack_Caster | unit | No | |
Beast_Attack_Damage_Group | group | No | |
Beast_Attack_Damage_Location | location | No | |
Beast_Attack_Damage_Multiplier | real | No | |
Beast_Attack_Destroy_Trees | boolean | No | |
Beast_Attack_Destroy_Trees_SE | string | No | |
Beast_Attack_Destruct_Kill_AoE | real | No | |
Beast_Attack_Direction | real | No | |
Beast_Attack_KB_Angle | real | No | |
Beast_Attack_KB_Base_Distance | real | No | |
Beast_Attack_KB_Bonus_Distance | real | No | |
Beast_Attack_KB_Group | group | No | |
Beast_Attack_KB_LocI | location | No | |
Beast_Attack_KB_LocII | location | No | |
Beast_Attack_KB_LocIII | location | No | |
Beast_Attack_KB_Minim_Distance | real | No | |
Beast_Attack_KB_Special_Effect | string | No | |
Beast_Attack_KB_Speed | real | No | |
Beast_Attack_KB_Total_Distance | real | No | |
Beast_Attack_Knock_Back_on | boolean | No | |
Beast_Attack_Location | location | No | |
Beast_Attack_LocationII | location | No | |
Beast_Attack_Random_Distance | boolean | No | |
Beast_Attack_Table | hashtable | No | |
Beast_Attack_Total_Damage | real | No | |
Beast_Attack_Total_Offset | real | No | |
Beast_Attack_Total_Radius | real | No | |
BRACERS_SPELL_DAMAGE_REDUCTION | real | No | |
Caster | unit | No | |
chaospherecastinggroup | group | No | |
chaospherechance | real | Yes | |
chaosphereemptyugroup | group | No | |
chaospherehashtable | hashtable | No | |
chaospherenumbereffects | integer | No | |
chaospherepickedunit | handle | No | |
chaospheretargetgroup | group | No | |
chaospheretotalchance | real | No | |
CI_Dummies | group | No | |
CI_Frozen | group | No | |
CI_Group | group | No | |
CI_Hash | hashtable | No | |
CI_Reals | real | Yes | |
CI_SFX_Group | group | No | |
CI_Terrain_Types | terraintype | Yes | |
CODE | integer | No | |
DAMAGE_TYPE_DETECTOR | integer | No | |
damageEvent | trigger | No | |
damageEventTrigger | real | No | |
damageHandler | trigger | No | |
damageType | integer | No | |
DoNotCopy_Angles | real | Yes | |
DoNotCopy_Loop | integer | No | |
DoNotCopy_Positions | location | Yes | |
DoNotCopy_Revive_Time | integer | No | |
DoNotCopy_Types | unitcode | Yes | |
DR_Dummy_Group | group | No | |
DR_Group | group | No | |
DR_Hash | hashtable | No | |
DR_Reals | real | Yes | |
Dummy_Itachi | group | No | |
DW_Effects | group | No | |
DW_Group | group | No | |
DW_Hash | hashtable | No | |
DW_Reals | real | Yes | |
ETHEREAL_DAMAGE_FACTOR | real | No | |
Eye_Fade | real | No | |
Eye_Group | group | No | |
FH_Charge_Group | group | No | |
FH_Group | group | No | |
FH_Hash | hashtable | No | |
FH_Knockback_Group | group | No | |
FH_Loop_Group_1 | group | No | |
FH_Loop_Group_2 | group | No | |
FH_Reals | real | Yes | |
FH_Unit_Type | unitcode | No | |
h | hashtable | No | |
HK_Ability | abilcode | No | |
HK_Ability_Stun | abilcode | No | |
HK_Attribute_Agiity | integer | No | |
HK_Attribute_Force | integer | No | |
HK_Caster | unit | No | |
HK_Chance | integer | Yes | |
HK_Damage_Critical | real | Yes | |
HK_Damage_Max | real | Yes | |
HK_Damage_Min | real | Yes | |
HK_Damage_Total | real | No | |
HK_Dummy | unit | No | |
HK_Heal_Max | real | Yes | |
HK_Heal_Total | real | No | |
HK_Level | integer | No | |
HK_Loc | location | No | |
HK_Model_Name | string | Yes | |
HK_Model_Target | string | Yes | |
HK_Target | unit | No | |
HK_Text_Angle | real | No | |
HK_Text_Color | string | Yes | |
HK_Text_Display | string | Yes | |
HK_Text_FadingAge | real | No | |
HK_Text_LifeSpan | real | No | |
HK_Text_On | boolean | No | |
HK_Text_Player | force | No | |
HK_Text_Size | real | No | |
HK_Type_Attack | attacktype | No | |
HK_Type_Damage | damagetype | No | |
HK_Type_Dummy | unitcode | No | |
IsUnitPreplaced | boolean | Yes | |
JustCastSpell | group | No | |
LB_Hashtable | hashtable | No | |
LB_Integer | integer | No | |
LB_ItemArray | item | Yes | |
Leap_Angle | real | No | |
Leap_Anim | real | No | |
Leap_Anim_Integer | integer | No | |
Leap_Anim_Reached | real | Yes | |
Leap_Attack_Type | attacktype | No | |
Leap_Boolean | boolean | Yes | |
Leap_Caster | unit | Yes | |
Leap_Caster_Loc | location | No | |
Leap_Damage | real | Yes | |
Leap_Damage_AoE | real | Yes | |
Leap_Damage_Group | group | No | |
Leap_Damage_Type | damagetype | No | |
Leap_Effect | string | Yes | |
Leap_Height | real | No | |
Leap_Height_Reached | real | Yes | |
Leap_Index | integer | Yes | |
Leap_Index_Max | integer | No | |
Leap_Index_Reached | integer | No | |
Leap_Index_Temp | integer | No | |
Leap_Loc | location | No | |
Leap_Loop | integer | No | |
Leap_Pos | location | No | |
Leap_Range | real | Yes | |
Leap_Range_Base | real | No | |
Leap_Range_Factor | real | No | |
Leap_Range_Half | real | Yes | |
Leap_Range_Reached | real | Yes | |
Leap_Sfx | effect | Yes | |
Leap_Speed | real | No | |
Loop_check | integer | Yes | |
Loop_check_Copy | integer | Yes | |
loopA | integer | No | |
Map_Point | location | No | |
Map_Point2 | location | No | |
Map_Point3 | location | No | |
Map_Point4 | location | No | |
MI_Group | group | No | |
MI_Hash | hashtable | No | |
MI_Lightning | string | No | |
MI_Reals | real | Yes | |
MI_Unit_Type | unitcode | No | |
NW_Dummies | group | No | |
NW_Hash | hashtable | No | |
NW_Lightning | string | No | |
NW_Loop | group | No | |
NW_Reals | real | Yes | |
PHYSICAL | integer | No | |
Point | location | No | |
positionangle | real | No | |
pureAmount | real | No | |
Random | integer | No | |
Random_Copy | integer | No | |
RandomUnits | unitcode | Yes | |
RespawningUnits | group | No | |
RespawningUnits2 | group | No | |
RespawningUnits2_Copy | group | No | |
RespawningUnits_Copy | group | No | |
runAllocatedAttacks | trigger | No | |
SA_A_ActiveAbility | abilcode | No | |
SA_A_PassiveAbility | abilcode | No | |
SA_A_Unlearn | abilcode | Yes | |
SA_B_UseStat | boolean | No | |
SA_BF_Buff | buffcode | No | |
SA_I_Level | integer | No | |
SA_I_TempInt | integer | No | |
SA_I_Total | integer | No | |
SA_P_AtkPos | location | No | |
SA_PL_Player | player | No | |
SA_R_AoE | real | Yes | |
SA_R_Heal | real | Yes | |
SA_R_HealAmt | real | No | |
SA_S_Attach | string | No | |
SA_S_SFX | string | No | |
SA_U_AtkUnit | unit | No | |
SA_U_Picked | unit | No | |
SA_U_TempUnit | unit | No | |
SA_UG_UnitGroup | group | No | |
SET_MAX_LIFE | integer | No | |
SFX | effect | Yes | |
source | unit | No | |
SPELL | integer | No | |
SPELL_DAMAGE_REDUCTION_ITEM | integer | No | |
SPELL_RESISTANCE_AUTO_DETECT | boolean | No | |
Target | unit | No | |
target | unit | No | |
Target_Position | location | No | |
Target_Sum | location | No | |
Teleporter | unit | No | |
Temp_Group_1 | group | No | |
Temp_Group_2 | group | No | |
Temp_Integer_1 | integer | No | |
Temp_Integer_2 | integer | No | |
Temp_Integer_3 | integer | No | |
Temp_Integer_4 | integer | No | |
Temp_Integer_5 | integer | No | |
Temp_Interger_10 | integer | No | |
Temp_Interger_6 | integer | No | |
Temp_Interger_7 | integer | No | |
Temp_Interger_8 | integer | No | |
Temp_Interger_9 | integer | No | |
Temp_Lightning_1 | lightning | No | |
Temp_Loc_1 | location | No | |
Temp_Loc_2 | location | No | |
Temp_Loc_3 | location | No | |
Temp_Loc_4 | location | No | |
Temp_Loc_5 | location | No | |
Temp_Real_1 | real | No | |
Temp_Real_2 | real | No | |
Temp_Real_3 | real | No | |
Temp_Real_4 | real | No | |
Temp_Real_5 | real | No | |
Temp_Real_6 | real | No | |
Temp_String_1 | string | No | |
Temp_Unit_1 | unit | No | |
Temp_Unit_2 | unit | No | |
Temp_Unit_3 | unit | No | |
tempinteger | integer | No | |
temppoint | location | No | |
temppoint2 | location | No | |
temppoint3 | location | No | |
tempreal | real | No | |
tempreal2 | real | No | |
tempugroup | group | No | |
tempugroup2 | group | No | |
tempunit2 | unit | No | |
Terrain_Hash | hashtable | No | |
totalAllocs | integer | No | |
TRIGGER_CLEANUP_PERIOD | real | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UNIT_MIN_LIFE | real | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
WTFUCKERS | force | No | |
WTFUCKERS_UNIT_PICKED | unit | No | |
WTFUCKERS_UNITS_OWNED | group | No | |
XCount | integer | Yes | |
XCount_Copy | integer | Yes | |
XEffect | string | No | |
XEffect2 | string | No | |
XEffect2_Copy | string | No | |
XEffect_Copy | string | No | |
Xitachi | unit | Yes | |
Xitachi2 | unit | Yes | |
Xitachi2_Copy | unit | Yes | |
Xitachi3 | unit | Yes | |
Xitachi3_Copy | unit | Yes | |
Xitachi4 | unit | Yes | |
Xitachi4_Copy | unit | Yes | |
Xitachi5 | unit | Yes | |
Xitachi5_Copy | unit | Yes | |
Xitachi_Copy | unit | Yes | |
Xpoint2 | location | Yes | |
XPpoint | location | Yes | |
XPpoint_Copy | location | Yes | |
XReal | real | Yes | |
Xreal | real | Yes | |
XReal_Copy | real | Yes | |
Xreal_Copy | real | Yes | |
XRegion | rect | No | |
XRegion_Copy | rect | No | |
Xtarget | unit | Yes | |
Xtarget_Copy | unit | Yes | |
XTgroup | group | Yes | |
XTgroup_Copy | group | Yes |
//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
//============================================================================
// 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
library PluginSpellEffect requires RegisterPlayerUnitEvent
/* ------------------- SpellEffectPlugin v1.1 by Chopinski ------------------ */
// Simple plugin for the SpellEffectEvent library by Bribe to cache some usefull
// values.
// Credits to Bribe and Magtheridon96
/* ----------------------------------- END ---------------------------------- */
private module Event
static location location = Location(0, 0)
readonly static unit unit
readonly static unit target
readonly static player player
readonly static integer ability
readonly static integer level
readonly static integer handle
readonly static integer index
readonly static real x
readonly static real y
readonly static real z
readonly static real unitX
readonly static real unitY
readonly static real unitZ
readonly static real targetX
readonly static real targetY
readonly static real targetZ
private static method GetUnitZ takes unit u returns real
call MoveLocation(location, GetUnitX(u), GetUnitY(u))
return GetUnitFlyHeight(u) + GetLocationZ(location)
endmethod
private static method GetSpellTargetZ takes nothing returns real
call MoveLocation(location, x, y)
return GetLocationZ(location)
endmethod
private static method onCast takes nothing returns nothing
set unit = GetTriggerUnit()
set target = GetSpellTargetUnit()
set ability = GetSpellAbilityId()
set level = GetUnitAbilityLevel(unit, ability)
set player = GetOwningPlayer(unit)
set handle = GetHandleId(unit)
set index = GetUnitUserData(unit)
set x = GetSpellTargetX()
set y = GetSpellTargetY()
set z = GetSpellTargetZ()
set unitX = GetUnitX(unit)
set unitY = GetUnitY(unit)
set unitZ = GetUnitZ(unit)
if target != null then
set targetX = GetUnitX(target)
set targetY = GetUnitY(target)
set targetZ = GetUnitZ(target)
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
struct Spell extends array
implement Event
endstruct
endlibrary
//TESH.scrollpos=224
//TESH.alwaysfold=0
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
//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 MouseUtils
/*
-------------------
MouseUtils
- MyPad
1.0.2.2
-------------------
----------------------------------------------------------------------------
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.
----------------------------------------------------------------------------
*/
// Arbitrary constants
globals
constant integer EVENT_MOUSE_UP = 1024
constant integer EVENT_MOUSE_DOWN = 2048
constant integer EVENT_MOUSE_MOVE = 3072
private constant boolean IMPL_LOCK = false
endglobals
private module Init
private static method onInit takes nothing returns nothing
call thistype.init()
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
endif
private static trigger array evTrigger
private static integer array mouseButtonStack
static if IMPL_LOCK and not IS_INSTANT then
private integer mouseEventCount
private timer mouseEventReductor
endif
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 init 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()
set evTrigger[EVENT_MOUSE_UP] = CreateTrigger()
set evTrigger[EVENT_MOUSE_DOWN] = CreateTrigger()
set evTrigger[EVENT_MOUSE_MOVE] = 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
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
//TESH.scrollpos=23
//TESH.alwaysfold=0
library TimedHandles uses optional TimerUtils
/**************************************************************
*
* v1.0.5 by TriggerHappy
* ----------------------
*
* Use this to destroy a handle after X amount seconds.
*
* It's useful for things like effects where you may
* want it to be temporary, but not have to worry
* about the cleaning memory leak. By default it supports
* effects, lightning, weathereffect, items, ubersplats, and units.
*
* If you want to add your own handle types copy a textmacro line
* at the bottom and add whichever handle you want along with it's destructor.
*
* Example: //! runtextmacro TIMEDHANDLES("handle", "DestroyHandle")
*
* Installation
----------------------
* 1. Copy this script and over to your map inside a blank trigger.
* 2. If you want more efficiency copy TimerUtils over as well.
*
* API
* ----------------------
* call DestroyEffectTimed(AddSpecialEffect("effect.mdx", 0, 0), 5)
* call DestroyLightningTimed(AddLightning("CLPB", true, 0, 0, 100, 100), 5)
*
* Credits to Vexorian for TimerUtils and his help on the script.
*
**************************************************************/
globals
// If you don't want a timer to be ran each instance
// set this to true.
private constant boolean SINGLE_TIMER = true
// If you chose a single timer then this will be the speed
// at which the timer will update
private constant real UPDATE_PERIOD = 0.05
endglobals
// here you may add or remove handle types
//! runtextmacro TIMEDHANDLES("effect", "DestroyEffect")
//! runtextmacro TIMEDHANDLES("lightning", "DestroyLightning")
//! runtextmacro TIMEDHANDLES("weathereffect", "RemoveWeatherEffect")
//! runtextmacro TIMEDHANDLES("item", "RemoveItem")
//! runtextmacro TIMEDHANDLES("unit", "RemoveUnit")
//! runtextmacro TIMEDHANDLES("ubersplat", "DestroyUbersplat")
// Do not edit below this line
//! textmacro TIMEDHANDLES takes HANDLE,DESTROY
struct $HANDLE$Timed
$HANDLE$ $HANDLE$_var
static integer index = -1
static thistype array instance
static real REAL=UPDATE_PERIOD
static if SINGLE_TIMER then
static timer timer = CreateTimer()
real duration
real elapsed = 0
else static if not LIBRARY.TimerUtils then
static hashtable table = InitHashtable()
endif
method destroy takes nothing returns nothing
call $DESTROY$(this.$HANDLE$_var)
set this.$HANDLE$_var = null
static if SINGLE_TIMER then
set this.elapsed = 0
endif
call this.deallocate()
endmethod
private static method remove takes nothing returns nothing
static if SINGLE_TIMER then
local integer i = 0
local thistype this
loop
exitwhen i > thistype.index
set this = instance[i]
set this.elapsed = this.elapsed + UPDATE_PERIOD
if (this.elapsed >= this.duration) then
set instance[i] = instance[index]
set i = i - 1
set index = index - 1
call this.destroy()
if (index == -1) then
call PauseTimer(thistype.timer)
endif
endif
set i = i + 1
endloop
else
local timer t = GetExpiredTimer()
static if LIBRARY.TimerUtils then
local $HANDLE$Timed this = GetTimerData(t)
call ReleaseTimer(t)
call this.destroy()
else
local $HANDLE$Timed this = LoadInteger(table, 0, GetHandleId(t))
call DestroyTimer(t)
set t = null
call this.destroy()
endif
endif
endmethod
static method create takes $HANDLE$ h, real timeout returns $HANDLE$Timed
local $HANDLE$Timed this = $HANDLE$Timed.allocate()
static if SINGLE_TIMER then
set index = index + 1
set instance[index] = this
if (index == 0) then
call TimerStart(thistype.timer, UPDATE_PERIOD, true, function thistype.remove)
endif
set this.duration = timeout
else
static if LIBRARY.TimerUtils then
call TimerStart(NewTimerEx(this), timeout, false, function $HANDLE$timed.remove)
else
local timer t = CreateTimer()
call SaveInteger(thistype.table, 0, GetHandleId(t), this)
call TimerStart(t, timeout, false, function $HANDLE$Timed.remove)
set t = null
endif
endif
set this.$HANDLE$_var = h
return this
endmethod
endstruct
function $DESTROY$Timed takes $HANDLE$ h, real duration returns $HANDLE$Timed
return $HANDLE$Timed.create(h, duration)
endfunction
//! endtextmacro
endlibrary
library Utilities requires TimerUtils, FloatingTextArc
/* ------------------------ Utilities functions v1.0 ------------------------ */
// How to Import:
// 1 - Copy this library into your map
// 2 - Copy the Stun, Silence, Slow and Fear abilities and match them below and the Slow Buff
// 3 - Copy the TimerUtils and DummyRecycler libraries into your map and follow their import instructions
/* ------------------------------ By Chopinski ------------------------------ */
globals
// The raw code of the ability used to stun an unit
public constant integer STUN = 'A05Q'
// The raw code of the ability used to silence an unit
public constant integer SILENCE = 'A085'
// The raw code of the ability used to slow an unit
public constant integer SLOW = 'A086'
// The rawcode of the bear form ability for the fear system
public constant integer FEAR = 'A084'
// The dummy caster unit id
public constant integer DUMMY = 'cast'
// location z
private location LOCZ = Location(0,0)
// One hashtable to rule them all
private hashtable table = InitHashtable()
// Closest Unit
private unit ClosestUnit
endglobals
// Only one declaration per map required
native UnitAlive takes unit id returns boolean
// Anlge between 2D points
function AngleBetweenCoordinates takes real x, real y, real x2, real y2 returns real
return Atan2(y2 - y, x2 - x)
endfunction
// Similar to AddSpecialEffect but scales the effect and considers z and return it
function AddSpecialEffectEx takes string model, real x, real y, real z, real scale returns effect
set bj_lastCreatedEffect = AddSpecialEffect(model, x, y)
if z > 0 then
call BlzSetSpecialEffectZ(bj_lastCreatedEffect, z)
endif
call BlzSetSpecialEffectScale(bj_lastCreatedEffect, scale)
return bj_lastCreatedEffect
endfunction
// Returns a group of enemy units of the specified player within the specified AOE of x and y
function GetEnemyUnitsInRange takes player enemyOf, real x, real y, real aoe, boolean structures, boolean magicImmune returns group
local group g = CreateGroup()
local group h = CreateGroup()
local unit w
call GroupEnumUnitsInRange(h, x, y, aoe, null)
if structures and magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w)) then
call GroupAddUnit(g, w)
endif
call GroupRemoveUnit(h, w)
endloop
elseif structures and not magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE)) then
call GroupAddUnit(g, w)
endif
call GroupRemoveUnit(h, w)
endloop
elseif magicImmune and not structures then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE)) then
call GroupAddUnit(g, w)
endif
call GroupRemoveUnit(h, w)
endloop
else
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE)) then
call GroupAddUnit(g, w)
endif
call GroupRemoveUnit(h, w)
endloop
endif
call DestroyGroup(h)
set h = null
return g
endfunction
// Returns the closest unit in a unit group with center at x and y
function GetClosestUnitGroup takes real x, real y, group h returns unit
local real md = 100000
local real d
local unit u
local unit v
local real dx
local real dy
local group g = CreateGroup()
loop
set v = FirstOfGroup(h)
exitwhen v == null
if (UnitAlive(v)) then
call GroupAddUnit(g, v)
endif
call GroupRemoveUnit(h, v)
endloop
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupAddUnit(h, u)
call GroupRemoveUnit(g, u)
set dx = GetUnitX(u) - x
set dy = GetUnitY(u) - y
if (dx * dx + dy * dy) / 100000 < md then
set ClosestUnit = u
set md = (dx * dx + dy * dy) / 100000
endif
endloop
call DestroyGroup(g)
set g = null
return ClosestUnit
endfunction
/* -------------------------------------------------------------------------- */
/* Knockback and Reset Ability Cooldown */
/* -------------------------------------------------------------------------- */
private struct Utils
static thistype array data
static integer didx = -1
static timer clock = CreateTimer()
timer timer
unit source
unit target
integer ability
real distance
real duration
real angle
real ticks
real x
real y
effect effect
method remove takes integer i returns integer
call DestroyEffect(effect)
call BlzPauseUnitEx(target, false)
set data[i] = data[didx]
set didx = didx - 1
set source = null
set target = null
set effect = null
if didx == -1 then
call PauseTimer(clock)
endif
call deallocate()
return i - 1
endmethod
method destroy takes nothing returns nothing
set source = null
set timer = null
call deallocate()
endmethod
private static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
local real offset
loop
exitwhen i > didx
set this = data[i]
if ticks <= duration then
set offset = distance*0.03125/duration
set x = GetUnitX(target) + offset*Cos(angle)
set y = GetUnitY(target) + offset*Sin(angle)
call SetUnitX(target, x)
call SetUnitY(target, y)
else
set i = remove(i)
endif
set ticks = ticks + 0.03125
set i = i + 1
endloop
endmethod
private static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call BlzEndUnitAbilityCooldown(source, ability)
call ReleaseTimer(timer)
call destroy()
endmethod
/* ------------------------- Reset Ability Cooldown ------------------------- */
static method ResetAbilityCooldown takes unit u, integer id returns nothing
local thistype this = thistype.allocate()
set timer = NewTimerEx(this)
set source = u
set ability = id
call TimerStart(timer, 0.01, false, function thistype.onExpire)
endmethod
/* -------------------------------- Knockbak -------------------------------- */
static method Knockback takes unit source, unit target, real distance, real duration, string fx, string attachPoint returns nothing
local thistype this = thistype.allocate()
set .source = source
set .target = target
set .distance = distance
set .duration = duration
set .ticks = 0
set .angle = AngleBetweenCoordinates(GetUnitX(source), GetUnitY(source), GetUnitX(target), GetUnitY(target))
set .effect = AddSpecialEffectTarget(fx, target, attachPoint)
set didx = didx + 1
set data[didx] = this
call BlzPauseUnitEx(target, true)
if didx == 0 then
call TimerStart(clock, 0.03125, true, function thistype.onPeriod)
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Timed Ability */
/* -------------------------------------------------------------------------- */
private struct TimedAbility
static timer t = CreateTimer()
//Dynamic Indexing
static integer didx = -1
static thistype array data
unit u
integer id
real tick
method remove takes integer i returns integer
call UnitRemoveAbility(.u, .id)
call RemoveSavedInteger(table, GetHandleId(.u), .id)
set data[i] = data[didx]
set didx = didx - 1
set .u = null
call .deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if .tick <= 0 then
set i = remove(i)
endif
set .tick = .tick - 1
set i = i + 1
endloop
endmethod
static method Add takes unit u, integer id, real duration, boolean hide returns nothing
local integer i = LoadInteger(table, GetHandleId(u), id)
//--------------------------------------------
local thistype this
//--------------------------------------------
if i != 0 then
set this = i
if GetUnitAbilityLevel(u, id) == 0 then
call UnitAddAbility(u, id)
call UnitMakeAbilityPermanent(u, true, id)
call BlzUnitHideAbility(u, id, hide)
endif
else
set this = thistype.allocate()
set .u = u
set .id = id
set didx = didx + 1
set data[didx] = this
call SaveInteger(table, GetHandleId(u), id, this)
call UnitAddAbility(u, id)
call UnitMakeAbilityPermanent(u, true, id)
call BlzUnitHideAbility(u, id, hide)
if didx == 0 then
call TimerStart(t, 0.1, true, function thistype.onPeriod)
endif
endif
if .tick <= duration/0.1 then
set .tick = duration/0.1
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Fear System: Credits to Malhorne */
/* -------------------------------------------------------------------------- */
private struct Fear
// System period
private static constant real PERIOD = 1./5.
// Number of periods until the unit changes direction
private static constant integer DIRECTION_CHANGE = 5
// SMax angle when changing directions
private static constant real MAX_CHANGE = 200.
//Dynamic Indexing
readonly static integer didx = -1
readonly static thistype array data
private static integer array n
private static timer t = CreateTimer()
//------------------------------------------------------
unit target
effect effect
integer idx
integer ticks
integer change
private static method disable takes unit target returns nothing
local boolean hide
call UnitAddAbility(target, 'Abun')
call UnitAddAbility(target, 'Aloc')
call UnitRemoveAbility(target, 'Aloc')
call UnitAddAbility(target, FEAR)
call IssueImmediateOrder(target, "bearform")
call UnitRemoveAbility(target, FEAR)
if GetLocalPlayer() != GetOwningPlayer(target) then
set hide = not IsUnitHidden(target)
call ShowUnit(target, false)
call ShowUnit(target, hide)
endif
endmethod
private static method enable takes unit target returns nothing
local boolean show = not IsUnitHidden(target)
call ShowUnit(target, false)
call UnitRemoveAbility(target, 'Abun')
call ShowUnit(target, show)
endmethod
static method feared takes unit target returns boolean
return n[GetUnitUserData(target)] != 0
endmethod
method remove takes integer i returns integer
call IssueImmediateOrder(target, "stop")
call DestroyEffect(effect)
call enable(target)
set n[idx] = 0
set target = null
set effect = null
set data[i] = data[didx]
set didx = didx - 1
call deallocate()
if didx == -1 then
call PauseTimer(t)
endif
return i - 1
endmethod
private static method onPeriod takes nothing returns nothing
local integer i = 0
local real x
local real y
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks > 0 and UnitAlive(target) then
set change = change + 1
if change >= DIRECTION_CHANGE then
set change = 0
set x = GetUnitX(target)
set y = GetUnitY(target)
call IssuePointOrder(target, "move", GetRandomReal(x - MAX_CHANGE, x + MAX_CHANGE), GetRandomReal(y - MAX_CHANGE, x + MAX_CHANGE))
endif
else
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method apply takes unit target, real duration, string fxpath, string attachment returns nothing
local real x = GetUnitX(target)
local real y = GetUnitY(target)
local integer idx = GetUnitUserData(target)
local thistype this
if n[idx] != 0 then
set this = n[idx]
else
set this = thistype.allocate()
set .idx = idx
set .target = target
set .change = 0
set didx = didx + 1
set data[didx] = this
set n[idx] = this
call disable(target)
if fxpath != "" and attachment != "" then
set .effect = AddSpecialEffectTarget(fxpath, target, attachment)
endif
if didx == 0 then
call TimerStart(t, PERIOD, true, function thistype.onPeriod)
endif
endif
call IssuePointOrder(target, "move", GetRandomReal(x - MAX_CHANGE, x + MAX_CHANGE), GetRandomReal(y - MAX_CHANGE, x + MAX_CHANGE))
set .ticks = R2I(duration/PERIOD)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Effect Spam */
/* -------------------------------------------------------------------------- */
struct EffectSpam
timer t
unit u //target unit
integer i //spam count
string s //model
string p //attachment point
real x
real y
real z
real g //scale
private static method onPeriod takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if i > 0 then
if u == null then
call DestroyEffect(AddSpecialEffectEx(s, x, y, z, g))
else
call DestroyEffect(AddSpecialEffectTarget(s, u, p))
endif
else
call ReleaseTimer(t)
set t = null
set u = null
call deallocate()
endif
set i = i - 1
endmethod
static method spam takes unit target, string model, string attach, real x, real y, real z, real scale, real interval, integer count returns nothing
local thistype this = thistype.allocate()
set .t = NewTimerEx(this)
set .u = target
set .i = count
set .s = model
set .x = x
set .y = y
set .z = z
set .g = scale
set .p = attach
call TimerStart(.t, interval, true, function thistype.onPeriod)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Chain Lightning */
/* -------------------------------------------------------------------------- */
struct ChainLightning
timer timer
unit unit
unit prev
unit next
group group
player player
real damage
real range
real duration
integer bounces
attacktype attacktype
damagetype damagetype
string lightning
string effect
string attach
private method destroy takes nothing returns nothing
call DestroyGroup(group)
call ReleaseTimer(timer)
set prev = null
set next = null
set unit = null
set group = null
set player = null
set attacktype = null
set damagetype = null
call deallocate()
endmethod
private static method onPeriod takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit aux
loop
exitwhen bounces == 0
set group = GetEnemyUnitsInRange(player, GetUnitX(prev), GetUnitY(prev), range, false, false)
call GroupRemoveUnit(group, prev)
if BlzGroupGetSize(group) == 0 then
call destroy()
else
if next == null or not UnitAlive(next) then
set next = GetClosestUnitGroup(GetUnitX(prev), GetUnitY(prev), group)
else
if BlzGroupGetSize(group) > 1 then
call GroupRemoveUnit(group, next)
set next = GetClosestUnitGroup(GetUnitX(prev), GetUnitY(prev), group)
endif
endif
call DestroyLightningTimed(AddLightningEx(lightning, true, GetUnitX(prev), GetUnitY(prev), BlzGetUnitZ(prev) + 60.0, GetUnitX(next), GetUnitY(next), BlzGetUnitZ(next) + 60.0), duration)
call DestroyEffect(AddSpecialEffectTarget(effect, next, attach))
call UnitDamageTarget(unit, next, damage, false, false, attacktype, damagetype, null)
call DestroyGroup(group)
set aux = prev
set prev = next
set next = aux
endif
set bounces = bounces - 1
endloop
set aux = null
endmethod
static method create takes unit source, unit target, real dmg, real aoe, real dur, real interval, integer bounceCount, attacktype attackType, damagetype damageType, string lightningType, string sfx, string attachPoint returns thistype
local group g
local thistype this
set g = GetEnemyUnitsInRange(GetOwningPlayer(source), GetUnitX(target), GetUnitY(target), aoe, false, false)
call GroupRemoveUnit(g, target)
if BlzGroupGetSize(g) == 0 then
call DestroyLightningTimed(AddLightningEx(lightningType, true, GetUnitX(source), GetUnitY(source), BlzGetUnitZ(source) + 60.0, GetUnitX(target), GetUnitY(target), BlzGetUnitZ(target) + 60.0), dur)
call DestroyEffect(AddSpecialEffectTarget(sfx, target, attachPoint))
call UnitDamageTarget(source, target, dmg, false, false, attackType, damageType, null)
else
set this = thistype.allocate()
set timer = NewTimerEx(this)
set prev = target
set next = null
set unit = source
set player = GetOwningPlayer(source)
set damage = dmg
set range = aoe
set duration = dur
set bounces = bounceCount
set attacktype = attackType
set damagetype = damageType
set lightning = lightningType
set effect = sfx
set attach = attachPoint
call UnitDamageTarget(source, target, damage, false, false, attacktype, damagetype, null)
call TimerStart(timer, interval, true, function thistype.onPeriod)
endif
call DestroyGroup(g)
set g = null
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Knockup */
/* -------------------------------------------------------------------------- */
struct Knockup
timer timer
unit unit
real rate
private static method onPeriod takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call SetUnitFlyHeight(unit, GetUnitDefaultFlyHeight(unit), rate)
call BlzPauseUnitEx(unit, false)
call ReleaseTimer(timer)
set timer = null
set unit = null
call deallocate()
endmethod
static method create takes unit whichUnit, real airTime, real maxHeight returns thistype
local thistype this = thistype.allocate()
set timer = NewTimerEx(this)
set unit = whichUnit
set rate = maxHeight/airTime
call UnitAddAbility(whichUnit, 'Amrf')
call UnitRemoveAbility(whichUnit, 'Amrf')
call BlzPauseUnitEx(whichUnit, true)
call SetUnitFlyHeight(whichUnit, (GetUnitDefaultFlyHeight(whichUnit) + maxHeight), rate)
call TimerStart(timer, airTime/2, false, function thistype.onPeriod)
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Dummy Pool */
/* -------------------------------------------------------------------------- */
struct DummyPool
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
debug call BJDebugMsg("[DummyPool] Error: Trying to recycle a non dummy unit")
else
call GroupAddUnit(group, dummy)
call SetUnitX(dummy, WorldBounds.maxX)
call SetUnitY(dummy, WorldBounds.maxY)
call SetUnitOwner(dummy, player, false)
call ShowUnit(dummy, false)
call BlzPauseUnitEx(dummy, true)
endif
endmethod
static method retrieve takes player owner, real x, real y, real z, real face returns unit
if BlzGroupGetSize(group) > 0 then
set bj_lastCreatedUnit = FirstOfGroup(group)
call BlzPauseUnitEx(bj_lastCreatedUnit, false)
call ShowUnit(bj_lastCreatedUnit, true)
call GroupRemoveUnit(group, bj_lastCreatedUnit)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
call BlzSetUnitFacingEx(bj_lastCreatedUnit, face*bj_RADTODEG)
call SetUnitOwner(bj_lastCreatedUnit, owner, false)
else
set bj_lastCreatedUnit = CreateUnit(owner, DUMMY, x, y, face*bj_RADTODEG)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
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 == 20
set u = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
call BlzPauseUnitEx(u, false)
call GroupAddUnit(group, u)
set i = i + 1
endloop
set u = null
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Effect Link */
/* -------------------------------------------------------------------------- */
struct EffectLink
static timer timer = CreateTimer()
//Dynamic Indexing for buff and timed
static integer didx = -1
static thistype array data
//Dynamic Indexing for items
static integer ditem = -1
static thistype array items
unit unit
effect effect
item item
integer buff
method remove takes integer i, boolean isItem returns integer
call DestroyEffect(effect)
if isItem then
set items[i] = items[ditem]
set ditem = ditem - 1
else
set data[i] = data[didx]
set didx = didx - 1
if didx == -1 then
call PauseTimer(timer)
endif
endif
set unit = null
set item = null
set effect = null
call deallocate()
return i - 1
endmethod
static method onDrop takes nothing returns nothing
local item j = GetManipulatedItem()
local integer i = 0
local thistype this
loop
exitwhen i > ditem
set this = items[i]
if item == j then
set i = remove(i, true)
endif
set i = i + 1
endloop
set j = null
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if GetUnitAbilityLevel(unit, buff) == 0 then
set i = remove(i, false)
endif
set i = i + 1
endloop
endmethod
static method BuffLink takes unit target, integer id, string model, string attach returns nothing
local thistype this = thistype.allocate()
set unit = target
set buff = id
set effect = AddSpecialEffectTarget(model, target, attach)
set didx = didx + 1
set data[didx] = this
if didx == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
static method ItemLink takes unit target, item i, string model, string attach returns nothing
local thistype this = thistype.allocate()
set item = i
set effect = AddSpecialEffectTarget(model, target, attach)
set ditem = ditem + 1
set items[ditem] = this
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onDrop)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Disarm */
/* -------------------------------------------------------------------------- */
struct Disarm
static constant integer ability = 'Abun'
static constant real period = 0.03125000
static timer timer = CreateTimer()
static integer didx = -1
static thistype array data
static thistype array n
readonly static integer array count
unit unit
integer index
integer ticks
static method disarmed takes unit target returns boolean
return GetUnitAbilityLevel(target, ability) > 0
endmethod
private method remove takes integer i returns integer
call apply(unit, false)
set n[index] = 0
set unit = null
set data[i] = data[didx]
set didx = didx - 1
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
private static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks <= 0 then
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method timed takes unit target, real duration returns nothing
local integer i = GetUnitUserData(target)
local thistype this
if n[i] != 0 then
set this = n[i]
else
set this = thistype.allocate()
set unit = target
set index = i
set didx = didx + 1
set data[didx] = this
set n[i] = this
call apply(target, true)
if didx == 0 then
call TimerStart(timer, period, true, function thistype.onPeriod)
endif
endif
set ticks = R2I(duration/period)
endmethod
static method apply takes unit target, boolean flag returns nothing
local integer i = GetUnitUserData(target)
if flag then
set count[i] = count[i] + 1
if count[i] > 0 then
call UnitAddAbility(target, ability)
endif
else
set count[i] = count[i] - 1
if count[i] <= 0 then
call UnitRemoveAbility(target, ability)
endif
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Start Ability Cooldown */
/* -------------------------------------------------------------------------- */
struct AbilityCooldown
timer timer
unit unit
integer ability
real newCd
private static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call BlzStartUnitAbilityCooldown(unit, ability, newCd)
call ReleaseTimer(timer)
call deallocate()
set timer = null
set unit = null
endmethod
static method start takes unit source, integer abilCode, real cooldown returns nothing
local thistype this = thistype.allocate()
set timer = NewTimerEx(this)
set unit = source
set ability = abilCode
set newCd = cooldown
call TimerStart(timer, 0.01, false, function thistype.onExpire)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Remove Destructable Timed */
/* -------------------------------------------------------------------------- */
struct TimedDestructable
private static constant real period = 0.03125000
private static timer timer = CreateTimer()
private static integer id = -1
private static thistype array array
destructable destructable
real duration
private method remove takes integer i returns integer
call RemoveDestructable(destructable)
set destructable = null
set array[i] = array[id]
set id = id - 1
if id == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
private static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > id
set this = array[i]
if duration <= 0 then
set i = remove(i)
endif
set duration = duration - period
set i = i + 1
endloop
endmethod
static method create takes destructable dest, real timeout returns thistype
local thistype this = thistype.allocate()
set destructable = dest
set duration = timeout
set id = id + 1
set array[id] = this
if id == 0 then
call TimerStart(timer, period, true, function thistype.onPeriod)
endif
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
// Removes a destructable after a period of time
function RemoveDestructableTimed takes destructable dest, real timeout returns nothing
call TimedDestructable.create(dest, timeout)
endfunction
// Returns true if a unit is disarmed
function IsUnitDisarmed takes unit target returns boolean
return Disarm.disarmed(target)
endfunction
// Disarms an unit for a duration
function DisarmUnitTimed takes unit target, real duration returns nothing
call Disarm.timed(target, duration)
endfunction
// Disarms an unit if flag is true
function DisarmUnit takes unit target, boolean flag returns nothing
call Disarm.apply(target, flag)
endfunction
// Link an effect to a unit buff or ability
function LinkEffectToBuff takes unit target, integer buffId, string model, string attach returns nothing
call EffectLink.BuffLink(target, buffId, model, attach)
endfunction
// Link an effect to an unit item.
function LinkEffectToItem takes unit target, item i, string model, string attach returns nothing
call EffectLink.ItemLink(target, i, model, attach)
endfunction
// Pretty obvious.
function R2I2S takes real r returns string
return I2S(R2I(r))
endfunction
// Spams the specified effect model at a location with the given interval for the number of times count
function SpamEffect takes string model, real x, real y, real z, real scale, real interval, integer count returns nothing
call EffectSpam.spam(null, model, "", x, y, z, scale, interval, count)
endfunction
// Spams the specified effect model attached to a unit for the given interval for the number of times count
function SpamEffectUnit takes unit target, string model, string attach, real interval, integer count returns nothing
call EffectSpam.spam(target, model, attach, 0, 0, 0, 0, interval, count)
endfunction
// Applys Fear to the specified unit for the given duration
function UnitApplyFear takes unit whichUnit, real duration, string targetEffect, string attchPoint returns nothing
call Fear.apply(whichUnit, duration, targetEffect, attchPoint)
endfunction
// Returns true if the specified unit is currently under the effect of fear effect
function IsUnitFeared takes unit whichUnit returns boolean
return Fear.feared(whichUnit)
endfunction
// Add the specified ability to the specified unit for the given duration. Use hide to show or not the ability button.
function UnitAddAbilityTimed takes unit whichUnit, integer abilityId, real duration, boolean hide returns nothing
call TimedAbility.Add(whichUnit, abilityId, duration, hide)
endfunction
// Resets the specified unit ability cooldown
function ResetUnitAbilityCooldown takes unit whichUnit, integer abilCode returns nothing
call Utils.ResetAbilityCooldown(whichUnit, abilCode)
endfunction
// Knockback the specified target unit taken in consideration the angle between the target and the specified source over a distance and duration
function KnockbackUnit takes unit source, unit target, real distance, real duration, string fx, string attachPoint returns nothing
call Utils.Knockback(source, target, distance, duration, fx, attachPoint)
endfunction
// Returns the distance between 2 coordinates in Warcraft III units
function DistanceBetweenCoordinates takes real x1, real y1, real x2, real y2 returns real
local real dx = (x2 - x1)
local real dy = (y2 - y1)
return SquareRoot(dx*dx + dy*dy)
endfunction
// Makes the specified source damage an area respecting some basic unit filters
function UnitDamageArea takes unit source, real x, real y, real aoe, real damage, attacktype atkType, damagetype dmgType, boolean structures, boolean magicImmune, boolean allies returns nothing
local group h = CreateGroup()
local player enemyOf = GetOwningPlayer(source)
local unit w
call GroupEnumUnitsInRange(h, x, y, aoe, null)
if allies then
if structures and magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and w != source) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif structures and not magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif magicImmune and not structures then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and w != source) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
else
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
endif
else
if structures and magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif structures and not magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif magicImmune and not structures then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
else
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
endif
endif
call DestroyGroup(h)
set h = null
set enemyOf = null
endfunction
// Makes the specified source damage a group. Creates a special effect if specified
function UnitDamageGroup takes unit u, group g, real damage, attacktype atk_type, damagetype dmg_type, string sfx, string atch_point, boolean destroy returns group
local unit v
local integer i = 0
local integer t = BlzGroupGetSize(g)
loop
exitwhen i == t
set v = BlzGroupUnitAt(g, i)
call UnitDamageTarget(u, v, damage, true, false, atk_type, dmg_type, null)
if sfx != "" and atch_point != "" then
call DestroyEffect(AddSpecialEffectTarget(sfx, v, atch_point))
endif
set i = i + 1
endloop
if destroy then
call DestroyGroup(g)
endif
return g
endfunction
// Returns a random range given a max value
function 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
endfunction
// Returns a random value in the x/y coordinates depending on the value of boolean x
function GetRandomCoordInRange takes real center, real radius, boolean x returns real
local real theta = 2*bj_PI*GetRandomReal(0, 1)
local real r
if x then
set r = center + radius*Cos(theta)
else
set r = center + radius*Sin(theta)
endif
return r
endfunction
// Clones the items in the source unit inventory to the target unit
function CloneItems takes unit source, unit target returns nothing
local item i
local item j
local integer id
local integer charges
local integer idx = 0
loop
set i = UnitItemInSlot(source, idx)
if i != null then
set charges = GetItemCharges(i)
set id = GetItemTypeId(i)
set j = CreateItem(id, GetUnitX(target), GetUnitY(target))
call UnitAddItem(target, j)
if charges > 0 then
call SetItemCharges(j, charges)
endif
endif
set idx = idx + 1
exitwhen idx >= bj_MAX_INVENTORY
endloop
set i = null
set j = null
endfunction
// Add the mount for he unit mana pool
function AddUnitMana takes unit whichUnit, real amount returns nothing
call SetUnitState(whichUnit, UNIT_STATE_MANA, (GetUnitState(whichUnit, UNIT_STATE_MANA) + amount))
endfunction
// Add the specified amounts to a hero str/agi/int base amount
function UnitAddStat takes unit whichUnit, integer strength, integer agility, integer intelligence returns nothing
if strength != 0 then
call SetHeroStr(whichUnit, GetHeroStr(whichUnit, false) + strength, true)
endif
if agility != 0 then
call SetHeroAgi(whichUnit, GetHeroAgi(whichUnit, false) + agility, true)
endif
if intelligence != 0 then
call SetHeroInt(whichUnit, GetHeroInt(whichUnit, false) + intelligence, true)
endif
endfunction
// Returns the closest unit from the x and y coordinates in the map
function GetClosestUnit takes real x, real y, boolexpr e returns unit
local real md = 100000
local real d
local group g = CreateGroup()
local unit u
local real dx
local real dy
call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, e)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g, u)
set dx = GetUnitX(u) - x
set dy = GetUnitY(u) - y
if (dx * dx + dy * dy) / 100000 < md then
set ClosestUnit = u
set md = (dx * dx + dy * dy) / 100000
endif
endloop
call DestroyGroup(g)
call DestroyBoolExpr(e)
set g = null
return ClosestUnit
endfunction
// Creates a chain lightning with the specified ligihtning effect with the amount of bounces
function CreateChainLightning takes unit source, unit target, real damage, real aoe, real duration, real interval, integer bounceCount, attacktype attackType, damagetype damageType, string lightningType, string sfx, string attachPoint returns nothing
call ChainLightning.create(source, target, damage, aoe, duration, interval, bounceCount, attackType, damageType, lightningType, sfx, attachPoint)
endfunction
// Add the specified amount to the specified player gold amount
function AddPlayerGold takes player whichPlayer, integer amount returns nothing
call SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_GOLD) + amount)
endfunction
// Unit Knockup
function KnockupUnit takes unit whichUnit, real airTime, real maxHeight returns nothing
call Knockup.create(whichUnit, airTime, maxHeight)
endfunction
// Creates a text tag in an unit position for a duration
function CreateTextOnUnit takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
local texttag tx = CreateTextTag()
call SetTextTagText(tx, text, 0.015)
call SetTextTagPosUnit(tx, whichUnit, 0)
call SetTextTagColor(tx, red, green, blue, alpha)
call SetTextTagLifespan(tx, duration)
call SetTextTagVelocity(tx, 0.0, 0.0355)
call SetTextTagPermanent(tx, false)
set tx = null
endfunction
// Add health regeneration to the unit base value
function UnitAddHealthRegen takes unit whichUnit, real regen returns nothing
call BlzSetUnitRealField(whichUnit, UNIT_RF_HIT_POINTS_REGENERATION_RATE, BlzGetUnitRealField(whichUnit, UNIT_RF_HIT_POINTS_REGENERATION_RATE) + regen)
endfunction
// Retrieves a dummy from the pool. Facing angle in radians
function DummyRetrieve takes player owner, real x, real y, real z, real face returns unit
return DummyPool.retrieve(owner, x, y, z, face)
endfunction
// Recycles a dummy unit type, putting it back into the pool.
function DummyRecycle takes unit dummy returns nothing
call DummyPool.recycle(dummy)
endfunction
// Recycles a dummy with a delay.
function DummyRecycleTimed takes unit dummy, real delay returns nothing
call DummyPool.recycleTimed(dummy, delay)
endfunction
// Casts an ability in the target unit. Must have no casting time
function CastAbilityTarget takes unit target, integer id, string order returns nothing
local unit dummy = DummyRetrieve(GetOwningPlayer(target), GetUnitX(target), GetUnitY(target), GetUnitFlyHeight(target), 0)
call UnitAddAbility(dummy, id)
call IssueTargetOrder(dummy, order, target)
call DummyRecycleTimed(dummy, 0.01)
set dummy = null
endfunction
// Casts an ability in the target unit and set the ability to the desired level. Must have no casting time
function CastAbilityTargetLevel takes unit target, integer id, string order, integer level returns nothing
local unit dummy = DummyRetrieve(GetOwningPlayer(target), GetUnitX(target), GetUnitY(target), GetUnitFlyHeight(target), 0)
call UnitAddAbility(dummy, id)
call SetUnitAbilityLevel(dummy, id, level)
call IssueTargetOrder(dummy, order, target)
call DummyRecycleTimed(dummy, 0.01)
set dummy = null
endfunction
// Silences the specified unit for the given duration
function SilenceUnit takes unit whichUnit, real duration returns nothing
local unit dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(whichUnit), GetUnitY(whichUnit), GetUnitFlyHeight(whichUnit), 0)
local ability abil
call UnitAddAbility(dummy, SILENCE)
set abil = BlzGetUnitAbility(dummy, SILENCE)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, 0, duration)
call IncUnitAbilityLevel(dummy, SILENCE)
call DecUnitAbilityLevel(dummy, SILENCE)
call IssueTargetOrder(dummy, "drunkenhaze", whichUnit)
call DummyRecycle(dummy)
set dummy = null
set abil = null
endfunction
// Silences a group of units for the given duration
function SilenceGroup takes group whichGroup, real duration returns nothing
local integer i = 0
local integer size = BlzGroupGetSize(whichGroup)
local unit u
local unit dummy
local ability abil
if size > 0 then
set u = BlzGroupUnitAt(whichGroup, i)
set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), 0)
call UnitAddAbility(dummy, SILENCE)
set abil = BlzGetUnitAbility(dummy, SILENCE)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, 0, duration)
call IncUnitAbilityLevel(dummy, SILENCE)
call DecUnitAbilityLevel(dummy, SILENCE)
loop
exitwhen i == size
set u = BlzGroupUnitAt(whichGroup, i)
if UnitAlive(u) then
call IssueTargetOrder(dummy, "drunkenhaze", u)
endif
set i = i + 1
endloop
call DummyRecycle(dummy)
endif
set u = null
set dummy = null
set abil = null
endfunction
// Silences all units within the specified AOE with the center at x and y for the given duration
function SilenceArea takes real x, real y, real aoe, real duration returns nothing
local group g = CreateGroup()
call GroupEnumUnitsInRange(g, x, y, aoe, null)
call SilenceGroup(g, duration)
call DestroyGroup(g)
set g = null
endfunction
// Stuns the specified unit for the given duration
function StunUnit takes unit whichUnit, real duration returns nothing
local unit dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(whichUnit), GetUnitY(whichUnit), GetUnitFlyHeight(whichUnit), 0)
local ability abil
call UnitAddAbility(dummy, STUN)
set abil = BlzGetUnitAbility(dummy, STUN)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, 0, duration)
call IncUnitAbilityLevel(dummy, STUN)
call DecUnitAbilityLevel(dummy, STUN)
call IssueTargetOrder(dummy, "thunderbolt", whichUnit)
call DummyRecycle(dummy)
set dummy = null
set abil = null
endfunction
// Stuns a group of units for the given duration
function StunGroup takes group whichGroup, real duration returns nothing
local integer i = 0
local integer size = BlzGroupGetSize(whichGroup)
local unit u
local unit dummy
local ability abil
if size > 0 then
set u = BlzGroupUnitAt(whichGroup, i)
set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), 0)
call UnitAddAbility(dummy, STUN)
set abil = BlzGetUnitAbility(dummy, STUN)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, 0, duration)
call IncUnitAbilityLevel(dummy, STUN)
call DecUnitAbilityLevel(dummy, STUN)
loop
exitwhen i == size
set u = BlzGroupUnitAt(whichGroup, i)
if UnitAlive(u) then
call IssueTargetOrder(dummy, "thunderbolt", u)
endif
set i = i + 1
endloop
call DummyRecycle(dummy)
endif
set u = null
set dummy = null
set abil = null
endfunction
// Stuns all units within the specified AOE with the center at x and y for the given duration
function StunArea takes real x, real y, real aoe, real duration returns nothing
local group g = CreateGroup()
call GroupEnumUnitsInRange(g, x, y, aoe, null)
call StunGroup(g, duration)
call DestroyGroup(g)
set g = null
endfunction
// Slows the specified unit for the given duration
function SlowUnit takes unit whichUnit, real amount, real duration returns nothing
local unit dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(whichUnit), GetUnitY(whichUnit), GetUnitFlyHeight(whichUnit), 0)
local ability abil
call UnitAddAbility(dummy, SLOW)
set abil = BlzGetUnitAbility(dummy, SLOW)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_MOVEMENT_SPEED_REDUCTION_PERCENT_CRI1, 0, amount)
call IncUnitAbilityLevel(dummy, SLOW)
call DecUnitAbilityLevel(dummy, SLOW)
call IssueTargetOrder(dummy, "cripple", whichUnit)
call DummyRecycle(dummy)
set dummy = null
set abil = null
endfunction
// Slows a group of units for the given duration
function SlowGroup takes group whichGroup, real amount, real duration returns nothing
local integer i = 0
local integer size = BlzGroupGetSize(whichGroup)
local unit u
local unit dummy
local ability abil
if size > 0 then
set u = BlzGroupUnitAt(whichGroup, i)
set dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u), 0)
call UnitAddAbility(dummy, SLOW)
set abil = BlzGetUnitAbility(dummy, SLOW)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_NORMAL, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_DURATION_HERO, 0, duration)
call BlzSetAbilityRealLevelField(abil, ABILITY_RLF_MOVEMENT_SPEED_FACTOR_SLO1, 0, amount)
call IncUnitAbilityLevel(dummy, SLOW)
call DecUnitAbilityLevel(dummy, SLOW)
loop
exitwhen i == size
set u = BlzGroupUnitAt(whichGroup, i)
if UnitAlive(u) then
call IssueTargetOrder(dummy, "slow", u)
endif
set i = i + 1
endloop
call DummyRecycle(dummy)
endif
set u = null
set dummy = null
set abil = null
endfunction
// Slows all units within the specified AOE with the center at x and y for the given duration
function SlowArea takes real x, real y, real aoe, real amount, real duration returns nothing
local group g = CreateGroup()
call GroupEnumUnitsInRange(g, x, y, aoe, null)
call SlowGroup(g, amount, duration)
call DestroyGroup(g)
set g = null
endfunction
// Returns a random unit within a group
function GroupPickRandomUnitEx takes group g returns unit
if BlzGroupGetSize(g) > 0 then
return BlzGroupUnitAt(g, GetRandomInt(0, BlzGroupGetSize(g) - 1))
else
return null
endif
endfunction
// Returns true if a unit is within a cone given a facing and fov angle in degrees (Less precise)
function IsUnitInConeEx takes unit u, real x, real y, real face, real fov returns boolean
return Acos(Cos((Atan2(GetUnitY(u) - y, GetUnitX(u) - x)) - face*bj_DEGTORAD)) < fov*bj_DEGTORAD/2
endfunction
// Returns true if a unit is within a cone given a facing, fov angle and a range in degrees (takes collision into consideration). Credits to AGD.
function IsUnitInCone takes unit u, real x, real y, real range, real face, real fov returns boolean
if IsUnitInRangeXY(u, x, y, range) then
set x = GetUnitX(u) - x
set y = GetUnitY(u) - y
set range = x*x + y*y
if range > 0. then
set face = face*bj_DEGTORAD - Atan2(y, x)
set fov = fov*bj_DEGTORAD/2 + Asin(BlzGetUnitCollisionSize(u)/SquareRoot(range))
return RAbsBJ(face) <= fov or RAbsBJ(face - 2.00*bj_PI) <= fov
endif
return true
endif
return false
endfunction
// Makes the source unit damage enemy unit in a cone given a direction, foy and range
function UnitDamageCone takes unit source, real x, real y, real face, real fov, real aoe, real damage, attacktype atkType, damagetype dmgType, boolean structures, boolean magicImmune, boolean allies returns nothing
local group h = CreateGroup()
local player enemyOf = GetOwningPlayer(source)
local unit w
call GroupEnumUnitsInRange(h, x, y, aoe, null)
if allies then
if structures and magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif structures and not magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif magicImmune and not structures then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
else
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and w != source and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
endif
else
if structures and magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif structures and not magicImmune then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
elseif magicImmune and not structures then
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
else
loop
set w = FirstOfGroup(h)
exitwhen w == null
if (IsUnitEnemy(w, enemyOf) and UnitAlive(w) and not IsUnitType(w, UNIT_TYPE_STRUCTURE) and not IsUnitType(w, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitInCone(w, x, y, aoe, face, fov)) then
call UnitDamageTarget(source, w, damage, true, false, atkType, dmgType, null)
endif
call GroupRemoveUnit(h, w)
endloop
endif
endif
call DestroyGroup(h)
set h = null
set enemyOf = null
endfunction
// Heals all allied units of specified player in an area
function HealArea takes player alliesOf, real x, real y, real aoe, real amount, string fxpath, string attchPoint returns nothing
local group g = CreateGroup()
local unit v
call GroupEnumUnitsInRange(g, x, y, aoe, null)
loop
set v = FirstOfGroup(g)
exitwhen v == null
if IsUnitAlly(v, alliesOf) and UnitAlive(v) and not IsUnitType(v, UNIT_TYPE_STRUCTURE) then
call SetWidgetLife(v, GetWidgetLife(v) + amount)
call ArcingTextTag.create(("|cff32cd32" + "+" + R2I2S(amount)), v)
if fxpath != "" then
if attchPoint != "" then
call DestroyEffect(AddSpecialEffectTarget(fxpath, v, attchPoint))
else
call DestroyEffect(AddSpecialEffect(fxpath, GetUnitX(v), GetUnitY(v)))
endif
endif
endif
call GroupRemoveUnit(g, v)
endloop
call DestroyGroup(g)
set g = null
endfunction
// Returns an ability real level field as a string. Usefull for toolltip manipulation.
function AbilityRealField takes unit u, integer abilityId, abilityreallevelfield field, integer level, integer multiplier, boolean asInteger returns string
if asInteger then
return R2I2S(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, level)*multiplier)
else
return R2SW(BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, level)*multiplier,1,1)
endif
endfunction
// Similar to GetUnitX and GetUnitY but for Z axis
function GetUnitZ takes unit u returns real
call MoveLocation(LOCZ, GetUnitX(u), GetUnitY(u))
return GetUnitFlyHeight(u) + GetLocationZ(LOCZ)
endfunction
// Similar to SetUnitX and SetUnitY but for Z axis
function SetUnitZ takes unit u, real z returns nothing
call SetUnitFlyHeight(u, z - (GetUnitZ(u) + GetUnitFlyHeight(u)), 0)
endfunction
// Returns the terrain Z value (Desync safe)
function GetLocZ takes real x, real y returns real
call MoveLocation(LOCZ, x, y)
return GetLocationZ(LOCZ)
endfunction
// Fix for camera pan desync. credits do Daffa
function SmartCameraPanBJModified takes player whichPlayer, location loc, real duration returns nothing
local real tx = GetLocationX(loc)
local real ty = GetLocationY(loc)
local real dx = tx - GetCameraTargetPositionX()
local real dy = ty - GetCameraTargetPositionY()
local real dist = SquareRoot(dx * dx + dy * dy)
if (GetLocalPlayer() == whichPlayer) then
if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
call PanCameraToTimed(tx, ty, duration)
// Far away = snap camera immediately to point
elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
call PanCameraToTimed(tx, ty, duration)
// Moderately close = pan camera over duration
else
// User is close, don't move camera
endif
endif
endfunction
// Fix for camera pan desync. credits do Daffa
function SmartCameraPanBJModifiedXY takes player whichPlayer, real x, real y, real duration returns nothing
local real dx = x - GetCameraTargetPositionX()
local real dy = y - GetCameraTargetPositionY()
local real dist = SquareRoot(dx * dx + dy * dy)
if (GetLocalPlayer() == whichPlayer) then
if (dist >= bj_SMARTPAN_TRESHOLD_SNAP) then
call PanCameraToTimed(x, y, duration)
// Far away = snap camera immediately to point
elseif (dist >= bj_SMARTPAN_TRESHOLD_PAN) then
call PanCameraToTimed(x, y, duration)
// Moderately close = pan camera over duration
else
// User is close, don't move camera
endif
endif
endfunction
// Start the cooldown for the source unit unit the new value
function StartUnitAbilityCooldown takes unit source, integer abilCode, real cooldown returns nothing
call AbilityCooldown.start(source, abilCode, cooldown)
endfunction
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Arcing Text Tag v1.0.0.3 by Maker
library FloatingTextArc
globals
private constant real SIZE_MIN = 0.014 // Minimum size of text
private constant real SIZE_BONUS = 0.000 // Text size increase
private constant real TIME_LIFE = 1.0 // How long the text lasts
private constant real TIME_FADE = 0.3 // When does the text start to fade
private constant real Z_OFFSET = 50 // Height above unit
private constant real Z_OFFSET_BON = 50 // How much extra height the text gains
private constant real VELOCITY = 2 // How fast the text move in x/y plane
private constant real ANGLE = bj_PI/2 // Movement angle of the text. Does not apply if
// ANGLE_RND is true
private constant boolean ANGLE_RND = true // Is the angle random or fixed
private timer TMR = CreateTimer()
endglobals
struct ArcingTextTag extends array
private texttag tt
private real as // angle, sin component
private real ac // angle, cos component
private real ah // arc height
private real t // time
private real x // origin x
private real y // origin y
private string s // text
private static integer array next
private static integer array prev
private static integer array rn
private static integer ic = 0 // Instance count
private static method update takes nothing returns nothing
local thistype this=next[0]
local real p
loop
set p = Sin(bj_PI*.t)
set .t = .t - 0.03125
set .x = .x + .ac
set .y = .y + .as
call SetTextTagPos(.tt, .x, .y, Z_OFFSET + Z_OFFSET_BON * p)
call SetTextTagText(.tt, .s, SIZE_MIN + SIZE_BONUS * p)
if .t <= 0 or .s == "" then
set .tt = null
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
set rn[this] = rn[0]
set rn[0] = this
if next[0]==0 then
call PauseTimer(TMR)
endif
endif
set this = next[this]
exitwhen this == 0
endloop
endmethod
public static method create takes string s, unit u returns thistype
local thistype this = rn[0]
static if ANGLE_RND then
local real a = GetRandomReal(0, 2*bj_PI)
else
local real a = ANGLE
endif
if this == 0 then
set ic = ic + 1
set this = ic
else
set rn[0] = rn[this]
endif
set next[this] = 0
set prev[this] = prev[0]
set next[prev[0]] = this
set prev[0] = this
set .s = s
set .x = GetUnitX(u)
set .y = GetUnitY(u)
set .t = TIME_LIFE
set .as = Sin(a)*VELOCITY
set .ac = Cos(a)*VELOCITY
set .ah = 0.
if IsUnitVisible(u, GetLocalPlayer()) then
set .tt = CreateTextTag()
call SetTextTagPermanent(.tt, false)
call SetTextTagLifespan(.tt, TIME_LIFE)
call SetTextTagFadepoint(.tt, TIME_FADE)
call SetTextTagText(.tt, s, SIZE_MIN)
call SetTextTagPos(.tt, .x, .y, Z_OFFSET)
endif
if prev[this] == 0 then
call TimerStart(TMR, 0.03125, true, function thistype.update)
endif
return this
endmethod
endstruct
endlibrary
library MissileEffect requires WorldBounds
/* -------------------- Missile Effect v1.8 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 ---------------------------------- */
// Alloc ~~ By Sevion ~~ Version 1.09
private 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 == JASS_MAX_ARRAY_SIZE) 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
// Simple linked list to cycle through
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
// Effect attachments helper
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
// The Main Missile effect
struct MissileEffect
real size
real yaw
real pitch
real roll
string path
effect effect
Effect attachments
method scale takes effect sfx, real scale returns nothing
set size = scale
call BlzSetSpecialEffectScale(sfx, scale)
endmethod
// Must be set in this order
method orient takes real yaw, real pitch, real roll returns nothing
local Effect node = attachments.next
// Main missile orientation
set .yaw = yaw
set .pitch = pitch
set .roll = roll
call BlzSetSpecialEffectOrientation(effect, yaw, pitch, roll)
// Attachments orientation
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
/* -------------------------- Contructor/Destructor ------------------------- */
method destroy takes nothing returns nothing
local Effect node = attachments.next
loop
exitwhen node == attachments
call DestroyEffect(node.effect)
set node.effect = null
call node.deallocate()
set node = node.next
endloop
call DestroyEffect(effect)
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 attachments = Effect.create()
call BlzSetSpecialEffectZ(effect, z)
return this
endmethod
endstruct
endlibrary
library Missiles requires MissileEffect
/* ----------------------- Missiles v1.8 by Chopinski ----------------------- */
// Thanks and Full 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 and MissileEffect librarys into your map
// How to Use:
// This system works almost identicaly to the Missile library by BPower but
// with a more user friendly interface in my opinion. Also this system
// allows you to create Arc/Curved Homing missiles like Dirac's system.
// Differently than both BPower and Dirac systems, you are not required
// to use the ImplementStruct at the end of your struct. To make your struct
// behave like a Missile simply make it extends Missiles and you are done.
// After that you will have access to the events in the MissileEvents Interface.
// Simply declare the event you want for your strcut to have and the system
// takes care of the rest. You can access the members and functions using
// the "this" or "." syntax which is a plus. to terminate a missile, simply
// return true or call terminate() within the method. Example:
// struct MySpell extends Missiles
// method onPeriod takes nothing returns boolean
// // will display the missile current position
// call ClearTextMessages()
// call BJDebugMsg(I2S(.x))
// call BJDebugMsg(I2S(.y))
// call BJDebugMsg(I2S(.z))
// return false
// endmethod
// endstruct
// function Spell takes nothing returns nothing
// // the create method takes the initial and the final coordinates
// // if the target member is set, the missile home
// local MySpell spell = MySpell.create(fromX, fromY, fromZ, toX, toY, toZ)
// set spell.source = GetTriggerUnit()
// set spell.target = GetSpellTargetUnit() -> when a target is specified the missile will be homing
// set spell.speed = 1000 -> warcraft 3 speed unit
// set spell.duration = 1.5 -> will set the speed to match the time passed
// set spell.model = "Some Model.dmx"
// set spell.scale = 1.3
// set spell.arc = 30 (degrees converted to radians internally)
// set spell.curve = GetRandomReal(-40, 40) (degrees converted to radians internally
// call spell.launch()
// endfunction
// Available members and methods:
// Coordinates impact -> the impact coordiantes (x, y, z, anngle, slope, alpha, distance)
// Coordinates origin -> same, but for origin
// //-------------------------------------------------------
// readonly real x -> current x position of the missile
// readonly real y -> current y position of the missile
// readonly real z -> current z position of the missile
// readonly real prevX -> last x position of the missile
// readonly real prevY -> last y position of the missile
// readonly real prevZ -> last z position of the missile
// readonly real height -> the arc of the missile (change it using the .arc operator)
// readonly real turn -> the turn rate of the missile
// readonly real open -> the curvature of the missile (change it using the .curve operator)
// readonly real veloc -> the missile speed (change it using the .speed or .duration operator)
// readonly real travel -> distance travelled
// readonly boolean launched -> true if the missile was already launched
// readonly boolean allocated -> true if the missile can still perform its movement operations
// //-------------------------------------------------------
// unit source -> the source unit (optional)
// unit target -> the target unit (optional and if set to a valid unit the missile will be homing)
// player owner -> the owner of the missile (optional)
// boolean collideZ -> set to true if you want the missile to consider z in collisions
// real collision -> the collision size of the missile (optional. The onHit and onDestructable events only works if this is greater than 0)
// real damage -> stores the damage you want the missile to deal (optional)
// real acceleration -> if different than 0 then the missile will change its speed with time (optional)
// integer data -> just in case you want to pass some information to the missile to retrieve it later (optional)
// (call)
// method deflect takes real x, real y returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x and y.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method deflectZ takes real x, real y, real z returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x, y and z.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method bounce takes nothing returns nothing
// - Bounces the missile from it's current Z position.
// - Useful when assigning targets to missiles that were
// - already active, it fixes the rough Z position change.
// (call)
// method flush takes widget w returns nothing
// - call this method to allow the missile to be able to hit
// - a unit or destructable again
// (call)
// method flushAll nothing returns nothing
// - flushes the hit table for the missile
// (call)
// method hitted takes widget w returns boolean
// - returns true if the missile has hitted the widget
// (call)
// method attach takes string model, real dx, real dy, real dz, real scale returns effect
// - attach another effect to the main missile.
// - dx, dy and dz are offsets from the main misisle position
// (call)
// method detach takes effect attachment returns nothing
// - detaches the effect from the missile, destrying it
// (optional)
// method onHit takes unit hit returns boolean
// - Runs every time the missile collides with a unit.
// - If returns true the missile is destroyed.
// (optional)
// method onDestructable takes destructable dest returns boolean
// - Runs every time the missile collides with a destructable.
// - If returns true the missile is destroyed.
// (optional)
// method onItem takes item i returns boolean
// - Runs every time the missile collides with an item.
// - If returns true the missile is destroyed.
// (optional)
// method onMissile takes Missiles missile returns boolean
// - Runs every time the missile collides with another missile.
// - By default, missiles collide only once
// - If returns true the missile is destroyed.
// - Please, be aware that this method can be very performance
// - intensive, so careful!
// (optional)
// method onPeriod takes nothing returns boolean
// - Runs every period.
// - If returns true the missile is destroyed.
// (optional)
// method onFinish takes nothing returns boolean
// - Runs whenever the missile finishes it's course.
// - If returns true the missile is destroyed.
// (optional)
// method onTerrain takes nothing returns boolean
// - Runs whenever the missile collides with terrain height
// - greater then the missile current z
// - If returns true the missile is destroyed.
// (optional)
// method onRemove takes nothing returns nothing
// - Runs whenever the missile is deallocated.
// (optional)
// method onBoundaries takes nothing returns boolean
// - Runs whenever the missile is trying to move
// - out of map bounds.
// /* ----------------------------------- 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.
// set this to true if you want the system to orient
// the missile roll. Roll can look really fishy for
// some users and it was never possible to be set
// until recent patches, so use it if you want.
private constant boolean ROLL = false
// Needed, dont touch. Seriously, dont touch!
private location LOC = Location(0., 0.)
private rect RECT = Rect(0., 0., 0., 0.)
private hashtable table = InitHashtable()
endglobals
// The available Events the user can implement in their structs
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onBoundaries 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
//Credits to Dirac for AdvLoc and BPower for fixing an error in it
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
/* ------------------------------ Missile Sytem ----------------------------- */
struct Missiles extends MissileEvents
private static timer t = CreateTimer()
private static group hitGroup = CreateGroup()
private static integer didx = -1
private static integer last = 0
private static real dilation
private static thistype temp = 0
private static thistype array missiles
//-------------------------------------------------------
private real cA // Current Angle
private real height // Arcs
private real open // Curves
private real toZ // Necessary to fix a bug for homming missiles
//-------------------------------------------------------
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 turn
readonly real veloc
readonly real travel
readonly boolean launched
readonly boolean allocated
//-------------------------------------------------------
unit source
unit target
player owner
boolean collideZ
real collision
real damage
real acceleration
integer data
//-------------------------------------------------------
/* -------------------------- 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)
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
set veloc = newspeed*PERIOD
endmethod
method operator speed takes nothing returns real
return veloc/PERIOD
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
endmethod
/* ---------------------------- Bound and Deflect --------------------------- */
method bounce takes nothing returns nothing
local real locZ = GetLocZ(x, y)
// This is here just to avoid an infinite loop
// with a deflect being called within an onTerrain
// event
if z < locZ then
set z = locZ
call impact.move(impact.x, impact.y, origin.z - GetLocZ(impact.x, impact.y))
endif
call origin.move(x, y, origin.z - GetLocZ(origin.x, origin.y))
set travel = 0
endmethod
method deflect takes real tx, real ty returns nothing
if target != null then
set target = null
endif
call impact.move(tx, ty, impact.z - GetLocZ(impact.x, impact.y))
call bounce()
endmethod
method deflectZ takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
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 ----------------------- */
// dx, dy, and dz are offsets from the main missile coordinates
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
/* ---------------------- Destructable collision method --------------------- */
private static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
local real dz
local real tz
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
if collideZ then
set dz = GetLocZ(GetWidgetX(d), GetWidgetY(d)) - GetLocZ(x, y)
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
set d = null
endmethod
/* -------------------------- Item collision method ------------------------- */
private 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)) - GetLocZ(x, y)
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
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set collideZ = false
set source = null
set target = null
set owner = 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 data = 0
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
if allocated then
set allocated = false
// onRemove event
if .onRemove.exists then
call .onRemove()
endif
call FlushChildHashtable(table, this)
endif
endmethod
/* -------------------------- Destroys the missile -------------------------- */
method remove takes integer i returns integer
call terminate()
call origin.destroy()
call impact.destroy()
call effect.destroy()
call reset()
set missiles[i] = missiles[didx]
set didx = didx - 1
//Compensation for time dilation
if didx + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (didx + 1)/SWEET_SPOT
else
set dilation = 1
endif
if didx == -1 then
call PauseTimer(t)
endif
call deallocate()
return i - 1
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set didx = didx + 1
set missiles[didx] = this
//Compensation for time dilation
if didx + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (didx + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if didx == 0 then
call TimerStart(t, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* ---------------------------- Missiles movement --------------------------- */
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 real locZ
local Missiles missile
local Coordinates o
local thistype this
// SWEET_SPOOT used to enable or disable
// relativistic processing
if SWEET_SPOT > 0 then
set i = last
else
set i = 0
endif
loop
exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > didx)
set this = missiles[i]
set temp = this
if allocated then
set o = origin
set h = height
set c = open
set d = o.distance
set locZ = GetLocZ(x, y)
//onPeriod Event
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
// onHit Event
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(hitGroup, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(hitGroup)
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) - locZ
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(hitGroup, u)
endloop
endif
endif
// onMissile Event
if .onMissile.exists then
if allocated and collision > 0 then
set k = 0
loop
exitwhen k > didx
set missile = missiles[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
// onDestructable Event
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(RECT, x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(RECT, null, function thistype.onDest)
endif
endif
// onItem Event
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
// 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 - prevX
set dy = impact.y - prevY
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 pitch = o.alpha
set x = prevX + vel*Cos(yaw)
set y = prevY + vel*Sin(yaw)
set s = travel + vel
set prevX = x
set prevY = y
set prevZ = z
set veloc = veloc + acceleration
set travel = s
// arc calculation
if h != 0 or o.slope != 0 then
set z = 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
// onTerrain event
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
if s >= d - 0.0001 then
// onFinish event
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
else
// deflected onFinish
if travel > 0 then
call terminate()
endif
endif
else
call terminate()
endif
else
static if ROLL then
call effect.orient(yaw, -pitch, Atan2(c, h))
else
call effect.orient(yaw, -pitch, 0)
endif
endif
if not effect.move(x, y, z) then
// onBoundaries event
if .onBoundaries.exists then
if allocated and .onBoundaries() then
call terminate()
endif
endif
endif
else
set i = remove(i)
set j = j - 1
endif
set i = i + 1
set j = j + 1
if i > didx and SWEET_SPOT > 0 then
set i = 0
endif
endloop
set last = i
set u = null
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, z)
set .allocated = true
set .x = x
set .y = y
set .z = z
set .prevX = x
set .prevY = y
set .prevZ = z
set .toZ = toZ
call Coordinates.link(origin, impact)
set .cA = origin.angle
return this
endmethod
endstruct
endlibrary
library DamageInterface requires optional Table
/* --------------------- DamageInterface v1.9 by Chopinski --------------------- */
// Allows for easy registration of specific damage type events like on attack
// damage or on spell damage, etc...
// How To Use?
// It works very similar to the RegisterPlayerUnitEvent and SpellEffectEvent
// library's, it's intended to turn this?
// private function OnDamage takes nothing returns nothing
// local boolean isSpell = BlzGetEventAttackType() == ATTACK_TYPE_NORMAL
// if isSpell then
// //do stuff
// endif
// endfunction
// private function Init takes nothing returns nothing
// local trigger t = CreateTrigger()
// call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DAMAGED)
// call TriggerAddCondition(t, Condition(function OnDamage))
// endfunction
// Into this:
// private function OnDamage takes nothing returns nothing
// //Do Stuff
// endfunction
// private function Init takes nothing returns nothing
// call RegisterSpellDamageEvent(function OnDamage)
// endfunction
// Added on v1.1 - Included functionality to allow registration of damage on before
// damage mitigation
// to register before damage mitigation simply do:
// private function OnDamage takes nothing returns nothing
// //Do Stuff
// endfunction
// private function Init takes nothing returns nothing
// call RegisterSpellDamagingEvent(function OnDamage)
// endfunction
// call RegisterSpellDamageEvent(function OnDamage)
// -> uses EVENT_PLAYER_UNIT_DAMAGED
// call RegisterSpellDamagingEvent(function OnDamage)
// -> uses EVENT_PLAYER_UNIT_DAMAGING
/* ----------------------------------- END ---------------------------------- */
globals
// This constant is used to define if the system will cache
// extra information from a Damage Event, like the unit
// Custom value (UnitUserData), a unit Handle Id, and more
// Additionaly you can see the CacheResponse method below
// to have an idea and comment the members you want cached or not
private constant boolean CACHE_EXTRA = true
endglobals
struct DamageI extends array
//Hashtables for after and before events
static if LIBRARY_Table then
private static HashTable afterTable
private static HashTable beforeTable
else
private static hashtable afterTable = InitHashtable()
private static hashtable beforeTable = InitHashtable()
endif
// Core evaluation triggers
static trigger Damaged = CreateTrigger()
static trigger Damaging = CreateTrigger()
/* --------------- Members -------------- */
readonly static damagetype damagetype
readonly static attacktype attacktype
readonly static unit source
readonly static unit target
readonly static player sourcePlayer
readonly static player targetPlayer
readonly static boolean isEnemy
readonly static boolean isAlly
readonly static boolean isMelee
readonly static boolean isRanged
readonly static boolean isAttack
readonly static boolean isSpell
readonly static boolean sourceIsHero
readonly static boolean targetIsHero
readonly static boolean structure
readonly static boolean magicImmune
readonly static real sourceX
readonly static real sourceY
readonly static real targetX
readonly static real targetY
readonly static integer sIdx
readonly static integer tIdx
readonly static integer sId
readonly static integer tId
// Setting the members of the struct
private static method cache takes unit src, unit tgt, damagetype dmg_type, attacktype atk_type returns nothing
set damagetype = dmg_type
set attacktype = atk_type
set source = src
set target = tgt
set isAttack = damagetype == DAMAGE_TYPE_NORMAL
set isSpell = attacktype == ATTACK_TYPE_NORMAL
// You can comment the members you dont want to be cached
// or set CACHE_EXTRA = false to not save them at all
if CACHE_EXTRA then
set sourcePlayer = GetOwningPlayer(source)
set targetPlayer = GetOwningPlayer(target)
set isEnemy = IsUnitEnemy(target, sourcePlayer)
set isAlly = IsUnitAlly(target, sourcePlayer)
set isMelee = IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER)
set isRanged = IsUnitType(source, UNIT_TYPE_RANGED_ATTACKER)
set sourceIsHero = IsUnitType(source, UNIT_TYPE_HERO)
set targetIsHero = IsUnitType(target, UNIT_TYPE_HERO)
set structure = IsUnitType(target, UNIT_TYPE_STRUCTURE)
set magicImmune = IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
set sourceX = GetUnitX(source)
set sourceY = GetUnitY(source)
set targetX = GetUnitX(target)
set targetY = GetUnitY(target)
set sIdx = GetUnitUserData(source)
set tIdx = GetUnitUserData(target)
set sId = GetHandleId(source)
set tId = GetHandleId(target)
endif
endmethod
private static method onDamaging takes nothing returns nothing
local integer i
local integer j
call cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())
if damagetype != DAMAGE_TYPE_UNKNOWN then
set i = GetHandleId(attacktype)
set j = GetHandleId(damagetype)
static if LIBRARY_Table then
if beforeTable[i].trigger[j] != null then
call TriggerEvaluate(beforeTable[i].trigger[j])
endif
if beforeTable[i].trigger[0] != null then
call TriggerEvaluate(beforeTable[i].trigger[0])
endif
if beforeTable[0].trigger[j] != null then
call TriggerEvaluate(beforeTable[0].trigger[j])
endif
else
if HaveSavedHandle(beforeTable, i, j) then
call TriggerEvaluate(LoadTriggerHandle(beforeTable, i, j))
endif
if HaveSavedHandle(beforeTable, i, 0) then
call TriggerEvaluate(LoadTriggerHandle(beforeTable, i, 0))
endif
if HaveSavedHandle(beforeTable, 0, j) then
call TriggerEvaluate(LoadTriggerHandle(beforeTable, 0, j))
endif
endif
endif
endmethod
private static method onDamage takes nothing returns nothing
local integer i
local integer j
call cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())
if damagetype != DAMAGE_TYPE_UNKNOWN then
set i = GetHandleId(attacktype)
set j = GetHandleId(damagetype)
static if LIBRARY_Table then
if afterTable[i].trigger[j] != null then
call TriggerEvaluate(afterTable[i].trigger[j])
endif
if afterTable[i].trigger[0] != null then
call TriggerEvaluate(afterTable[i].trigger[0])
endif
if afterTable[0].trigger[j] != null then
static if LIBRARY_Evasion then
if isAttack then
if not Evasion.evade then
call TriggerEvaluate(afterTable[0].trigger[j])
endif
else
call TriggerEvaluate(afterTable[0].trigger[j])
endif
else
call TriggerEvaluate(afterTable[0].trigger[j])
endif
endif
else
if HaveSavedHandle(afterTable, i, j) then
call TriggerEvaluate(LoadTriggerHandle(afterTable, i, j))
endif
if HaveSavedHandle(afterTable, i, 0) then
call TriggerEvaluate(LoadTriggerHandle(afterTable, i, 0))
endif
if HaveSavedHandle(afterTable, 0, j) then
static if LIBRARY_Evasion then
if isAttack then
if not Evasion.evade then
call TriggerEvaluate(LoadTriggerHandle(afterTable, 0, j))
endif
else
call TriggerEvaluate(LoadTriggerHandle(afterTable, 0, j))
endif
else
call TriggerEvaluate(LoadTriggerHandle(afterTable, 0, j))
endif
endif
endif
endif
endmethod
static method register takes attacktype attack, damagetype damage, code c, boolean after returns nothing
local integer i = GetHandleId(attack)
local integer j = GetHandleId(damage)
if after then
static if LIBRARY_Table then
if afterTable[i].trigger[j] == null then
set afterTable[i].trigger[j] = CreateTrigger()
endif
call TriggerAddCondition(afterTable[i].trigger[j], Filter(c))
else
if not HaveSavedHandle(afterTable, i, j) then
call SaveTriggerHandle(afterTable, i, j, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(afterTable, i, j), Filter(c))
endif
else
static if LIBRARY_Table then
if beforeTable[i].trigger[j] == null then
set beforeTable[i].trigger[j] = CreateTrigger()
endif
call TriggerAddCondition(beforeTable[i].trigger[j], Filter(c))
else
if not HaveSavedHandle(beforeTable, i, j) then
call SaveTriggerHandle(beforeTable, i, j, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(beforeTable, i, j), Filter(c))
endif
endif
endmethod
static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set afterTable = HashTable.create()
set beforeTable = HashTable.create()
endif
call TriggerRegisterAnyUnitEventBJ(Damaged, EVENT_PLAYER_UNIT_DAMAGED)
call TriggerAddCondition(Damaged, Condition(function thistype.onDamage))
call TriggerRegisterAnyUnitEventBJ(Damaging, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(Damaging, Condition(function thistype.onDamaging))
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
//After damage mitigation registration
function RegisterAttackDamageEvent takes code c returns nothing
call DamageI.register(null, DAMAGE_TYPE_NORMAL, c, true)
endfunction
function RegisterSpellDamageEvent takes code c returns nothing
call DamageI.register(ATTACK_TYPE_NORMAL, null, c, true)
endfunction
function RegisterDamageEvent takes attacktype attack, damagetype damage, code c returns nothing
call DamageI.register(attack, damage, c, true)
endfunction
function RegisterAnyDamageEvent takes code c returns nothing
call TriggerAddCondition(DamageI.Damaged, Filter(c))
endfunction
//Before damage mitigation registration
function RegisterAttackDamagingEvent takes code c returns nothing
call DamageI.register(null, DAMAGE_TYPE_NORMAL, c, false)
endfunction
function RegisterSpellDamagingEvent takes code c returns nothing
call DamageI.register(ATTACK_TYPE_NORMAL, null, c, false)
endfunction
function RegisterDamagingEvent takes attacktype attack, damagetype damage, code c returns nothing
call DamageI.register(attack, damage, c, false)
endfunction
function RegisterAnyDamagingEvent takes code c returns nothing
call TriggerAddCondition(DamageI.Damaging, Filter(c))
endfunction
endlibrary
library Evasion requires DamageInterface
/* ------------------------ Evasion v1.9 by Chopinski ----------------------- */
// Evasion implements an easy way to register and detect a custom evasion event.
// It works by monitoring custom evasion and missing values given to units,
// and nulling damage when the odds given occurs.
// It will only detect custom evasion, so all evasion or miss values given to a
// unit must be done so using the public API provided by this system.
// *Evasion requires DamageInterface. Do not use TriggerSleepAction() with Evasion.
// The API:
// function RegisterEvasionEvent(function YourFunction)
// -> YourFunction will run when a unit evades an attack.
// function GetMissingUnit takes nothing returns unit
// -> Returns the unit missing the attack
// function GetEvadingUnit takes nothing returns unit
// -> Returns the unit evading the attack
// function GetEvadedDamage takes nothing returns real
// -> Returns the amount of evaded damage
// function GetUnitEvasionChance takes unit u returns real
// -> Returns this system amount of evasion chance given to a unit
// function GetUnitMissChance takes unit u returns real
// -> Returns this system amount of miss chance given to a unit
// function SetUnitEvasionChance takes unit u, real chance returns nothing
// -> Sets unit evasion chance to specified amount
// function SetUnitMissChance takes unit u, real chance returns nothing
// -> Sets unit miss chance to specified amount
// function UnitAddEvasionChance takes unit u, real chance returns nothing
// -> Add to a unit Evasion chance the specified amount
// function UnitAddMissChance takes unit u, real chance returns nothing
// -> Add to a unit Miss chance the specified amount
// function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
// -> Will make a unit never miss attacks no matter the evasion chance of the attacked unit
// function DoUnitNeverMiss takes unit u returns boolean
// -> Returns true if the unit will never miss an attack
/* ----------------------------------- END ---------------------------------- */
//region System
struct Evasion
static unit source
static unit target
static real damage
static real array evasion
static real array miss
static integer array neverMiss
readonly static boolean evade = false
readonly static trigger trigger = CreateTrigger()
static constant real TEXT_SIZE = 0.016
static method getEvasionChance takes unit u returns real
return evasion[GetUnitUserData(u)]
endmethod
static method getMissChance takes unit u returns real
return miss[GetUnitUserData(u)]
endmethod
static method setEvasionChance takes unit u, real value returns nothing
set evasion[GetUnitUserData(u)] = value
endmethod
static method setMissChance takes unit u, real value returns nothing
set miss[GetUnitUserData(u)] = value
endmethod
static method text takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
local texttag tx = CreateTextTag()
call SetTextTagText(tx, text, TEXT_SIZE)
call SetTextTagPosUnit(tx, whichUnit, 0)
call SetTextTagColor(tx, red, green, blue, alpha)
call SetTextTagLifespan(tx, duration)
call SetTextTagVelocity(tx, 0.0, 0.0355)
call SetTextTagPermanent(tx, false)
set tx = null
endmethod
static method onDamage takes nothing returns nothing
local real amount = GetEventDamage()
set evade = false
if amount > 0 and not (neverMiss[DamageI.sIdx] > 0) then
set evade = GetRandomReal(0, 100) <= evasion[DamageI.tIdx] or GetRandomReal(0, 100) <= miss[DamageI.sIdx]
if evade then
set source = DamageI.source
set target = DamageI.target
set damage = amount
call TriggerEvaluate(trigger)
call BlzSetEventDamage(0)
call BlzSetEventWeaponType(WEAPON_TYPE_WHOKNOWS)
call text(source, "miss", 1.5, 255, 0, 0, 255)
set source = null
set target = null
set damage = 0.0
endif
endif
endmethod
static method register takes code c returns nothing
call TriggerAddCondition(trigger, Filter(c))
endmethod
static method onInit takes nothing returns nothing
call RegisterAttackDamagingEvent(function thistype.onDamage)
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function RegisterEvasionEvent takes code c returns nothing
call Evasion.register(c)
endfunction
function GetMissingUnit takes nothing returns unit
return Evasion.source
endfunction
function GetEvadingUnit takes nothing returns unit
return Evasion.target
endfunction
function GetEvadedDamage takes nothing returns real
return Evasion.damage
endfunction
function GetUnitEvasionChance takes unit u returns real
return Evasion.getEvasionChance(u)
endfunction
function GetUnitMissChance takes unit u returns real
return Evasion.getMissChance(u)
endfunction
function SetUnitEvasionChance takes unit u, real chance returns nothing
call Evasion.setEvasionChance(u, chance)
endfunction
function SetUnitMissChance takes unit u, real chance returns nothing
call Evasion.setMissChance(u, chance)
endfunction
function UnitAddEvasionChance takes unit u, real chance returns nothing
call Evasion.setEvasionChance(u, Evasion.getEvasionChance(u) + chance)
endfunction
function UnitAddMissChance takes unit u, real chance returns nothing
call Evasion.setMissChance(u, Evasion.getMissChance(u) + chance)
endfunction
function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
if flag then
set Evasion.neverMiss[GetUnitUserData(u)] = Evasion.neverMiss[GetUnitUserData(u)] + 1
else
set Evasion.neverMiss[GetUnitUserData(u)] = Evasion.neverMiss[GetUnitUserData(u)] - 1
endif
endfunction
function DoUnitNeverMiss takes unit u returns boolean
return Evasion.neverMiss[GetUnitUserData(u)] > 0
endfunction
endlibrary
library CriticalStrike requires DamageInterface optional Evasion
/* ------------------------ CriticalStrike v1.9 by Chopinski ----------------------- */
// CriticalStrike implements an easy way to register and detect a custom critical event.
// allows the manipulation of a unit critical strike chance and multiplier, as well as
// manipulating the critical damage dealt.
// It works by monitoring custom critical strike chance and multiplier values given to units.
// It will only detect custom critical strikes, so all critical chance given to a
// unit must be done so using the public API provided by this system.
// *CriticalStrike requires DamageInterface. Do not use TriggerSleepAction() with Evasion.
// It also requires optional Evasion so that this library is written after the Evasion
// library, so both custom events will not fire at the same time.
// The API:
// function RegisterCriticalStrikeEvent(function YourFunction)
// -> YourFunction will run when a unit hits a critical strike.
// function GetCriticalSource takes nothing returns unit
// -> Returns the unit hitting a critical strike.
// function GetCriticalTarget takes nothing returns unit
// -> Returns the unit being hit by a critical strike.
// function GetCriticalDamage takes nothing returns real
// -> Returns the critical strike damage amount.
// function GetUnitCriticalChance takes unit u returns real
// -> Returns the chance to hit a critical strike to specified unit.
// function GetUnitCriticalMultiplier takes unit u returns real
// -> Returns the chance to hit a critical strike to specified unit.
// function SetUnitCriticalChance takes unit u, real value returns nothing
// -> Set's the unit chance to hit a critical strike to specified value.
// -> 15.0 = 15%
// function SetUnitCriticalMultiplier takes unit u, real value returns nothing
// -> Set's the unit multiplier of damage when hitting a critical to value
// -> 1.0 = increases the multiplier by 1. all units have a multiplier of 1.0
// by default, so by adding 1.0, for example, the critical damage will be
// 2x the normal damage
// function SetCriticalEventDamage takes real newValue returns nothing
// -> Modify the critical damage dealt to the specified value.
// function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
// -> Adds the specified values of chance and multiplier to a unit
/* ----------------------------------- END ---------------------------------- */
//region System
struct CriticalStrike
static constant real TEXT_SIZE = 0.016
readonly static trigger trigger = CreateTrigger()
static unit source
static unit target
static real damage
static real array chance
static real array multiplier
static method getChance takes unit u returns real
return chance[GetUnitUserData(u)]
endmethod
static method getMultiplier takes unit u returns real
return multiplier[GetUnitUserData(u)]
endmethod
static method setChance takes unit u, real value returns nothing
set chance[GetUnitUserData(u)] = value
endmethod
static method setMultiplier takes unit u, real value returns nothing
set multiplier[GetUnitUserData(u)] = value
endmethod
static method add takes unit u, real chance, real multuplier returns nothing
call setChance(u, getChance(u) + chance)
call setMultiplier(u, getMultiplier(u) + multuplier)
endmethod
static method text takes unit whichUnit, string text, real duration, integer red, integer green, integer blue, integer alpha returns nothing
local texttag tx = CreateTextTag()
call SetTextTagText(tx, text, TEXT_SIZE)
call SetTextTagPosUnit(tx, whichUnit, 0)
call SetTextTagColor(tx, red, green, blue, alpha)
call SetTextTagLifespan(tx, duration)
call SetTextTagVelocity(tx, 0.0, 0.0355)
call SetTextTagPermanent(tx, false)
set tx = null
endmethod
static method onDamage takes nothing returns nothing
local real amount = GetEventDamage()
if amount > 0 and GetRandomReal(0, 100) <= chance[DamageI.sIdx] and DamageI.isEnemy and not DamageI.structure and multiplier[DamageI.sIdx] > 0 then
set source = DamageI.source
set target = DamageI.target
set damage = amount*(1 + multiplier[DamageI.sIdx])
call TriggerEvaluate(trigger)
call BlzSetEventDamage(damage) // in case of damage modification
if damage > 0 then
call text(target, (I2S(R2I(damage)) + "!"), 1.5, 255, 0, 0, 255)
endif
set source = null
set target = null
set damage = 0.0
endif
endmethod
static method register takes code c returns nothing
call TriggerAddCondition(trigger, Filter(c))
endmethod
static method onInit takes nothing returns nothing
call RegisterAttackDamageEvent(function thistype.onDamage)
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function RegisterCriticalStrikeEvent takes code c returns nothing
call CriticalStrike.register(c)
endfunction
function GetCriticalSource takes nothing returns unit
return CriticalStrike.source
endfunction
function GetCriticalTarget takes nothing returns unit
return CriticalStrike.target
endfunction
function GetCriticalDamage takes nothing returns real
return CriticalStrike.damage
endfunction
function GetUnitCriticalChance takes unit u returns real
return CriticalStrike.getChance(u)
endfunction
function GetUnitCriticalMultiplier takes unit u returns real
return CriticalStrike.getMultiplier(u)
endfunction
function SetUnitCriticalChance takes unit u, real value returns nothing
call CriticalStrike.setChance(u, value)
endfunction
function SetUnitCriticalMultiplier takes unit u, real value returns nothing
call CriticalStrike.setMultiplier(u, value)
endfunction
function SetCriticalEventDamage takes real newValue returns nothing
set CriticalStrike.damage = newValue
endfunction
function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
call CriticalStrike.add(u, chance, multiplier)
endfunction
endlibrary
library SpellPower requires DamageInterface
/* ------------------------ SpellPower v1.9 by Chopinski ----------------------- */
// SpellPower intends to simulate a system similiar to Ability Power from LoL or
// Spell Amplification from Dota 2.
// Whenever a units deals Spell damage, that dealt damage will be amplified by a flat
// and percentile amount that represents a unit spell power bonus.
// The formula for amplification is:
// final damage = (initial damage + flat bonus) * (1 + percent bonus)
// for percent bonus: 0.1 = 10% bonus
// *SpellPower requires DamageInterface. Do not use TriggerSleepAction() within triggers.
// The API:
// function GetUnitSpellPowerFlat takes unit u returns real
// -> Returns the Flat bonus of spell power of a unit
// function GetUnitSpellPowerPercent takes unit u returns real
// -> Returns the Percent bonus of spell power of a unit
// function SetUnitSpellPowerFlat takes unit u, real value returns nothing
// -> Set's the Flat amount of Spell Power of a unit
// function SetUnitSpellPowerPercent takes unit u, real value returns nothing
// -> Set's the Flat amount of Spell Power of a unit
// function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
// -> Add to the Flat amount of Spell Power of a unit
// function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
// -> Add to the Percent amount of Spell Power of a unit
// function GetSpellDamage takes real amount, unit u returns real
// -> Returns the amount of spell damage that would be dealt given an initial damage
/* ----------------------------------- END ---------------------------------- */
//region System
struct SpellPower
static real array flat
static real array percent
static method getFlat takes unit u returns real
return flat[GetUnitUserData(u)]
endmethod
static method getPercent takes unit u returns real
return percent[GetUnitUserData(u)]
endmethod
static method setFlat takes unit u, real value returns nothing
set flat[GetUnitUserData(u)] = value
endmethod
static method setPercent takes unit u, real value returns nothing
set percent[GetUnitUserData(u)] = value
endmethod
static method onDamage takes nothing returns nothing
local real damage = GetEventDamage()
if damage > 0 then
call BlzSetEventDamage((damage + flat[DamageI.sIdx])*(1 + percent[DamageI.sIdx]))
endif
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellDamageEvent(function thistype.onDamage)
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function GetUnitSpellPowerFlat takes unit u returns real
return SpellPower.getFlat(u)
endfunction
function GetUnitSpellPowerPercent takes unit u returns real
return SpellPower.getPercent(u)
endfunction
function SetUnitSpellPowerFlat takes unit u, real value returns nothing
call SpellPower.setFlat(u, value)
endfunction
function SetUnitSpellPowerPercent takes unit u, real value returns nothing
call SpellPower.setPercent(u, value)
endfunction
function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
call SpellPower.setFlat(u, SpellPower.getFlat(u) + amount)
endfunction
function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
call SpellPower.setPercent(u, SpellPower.getPercent(u) + amount)
endfunction
function GetSpellDamage takes real amount, unit u returns real
return ((amount + SpellPower.getFlat(u))*(1 + SpellPower.getPercent(u)))
endfunction
endlibrary
library LifeSteal requires DamageInterface
/* ------------------------ LifeSteal v1.9 by Chopinski ----------------------- */
// LifeSteal intends to simulate the Life Steal system in warcraft, and allow you
// to easily change the life steal amount of any unit.
// Whenever a unit deals Physical damage, and it has a value of life steal given by
// this system, it will heal based of this value and the damage amount.
// The formula for life steal is:
// heal = damage * life steal
// fror life steal: 0.1 = 10%
// *LifeSteal requires DamageInterface. Do not use TriggerSleepAction() within triggers.
// The API:
// function SetUnitLifeSteal takes unit u, real amount returns nothing
// -> Set the Life Steal amount for a unit
// function GetUnitLifeSteal takes unit u returns real
// -> Returns the Life Steal amount of a unit
// function UnitAddLifeSteal takes unit u, real amount returns nothing
// -> Add to the Life Steal amount of a unit the given amount
/* ----------------------------------- END ---------------------------------- */
//region System
struct LifeSteal
static string effect = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
static real array amount
static method Get takes unit u returns real
return amount[GetUnitUserData(u)]
endmethod
static method Set takes unit u, real value returns nothing
set amount[GetUnitUserData(u)] = value
endmethod
static method onDamage takes nothing returns nothing
local real damage = GetEventDamage()
if damage > 0 and amount[DamageI.sIdx] > 0 and not DamageI.structure then
call SetWidgetLife(DamageI.source, (GetWidgetLife(DamageI.source) + (damage * amount[DamageI.sIdx])))
call DestroyEffect(AddSpecialEffectTarget(effect, DamageI.source, "origin"))
endif
endmethod
static method onInit takes nothing returns nothing
call RegisterAttackDamageEvent(function thistype.onDamage)
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function SetUnitLifeSteal takes unit u, real amount returns nothing
call LifeSteal.Set(u, amount)
endfunction
function GetUnitLifeSteal takes unit u returns real
return LifeSteal.Get(u)
endfunction
function UnitAddLifeSteal takes unit u, real amount returns nothing
call LifeSteal.Set(u, LifeSteal.Get(u) + amount)
endfunction
endlibrary
library SpellVamp requires DamageInterface
/* ------------------------ SpellVamp v1.9 by Chopinski ----------------------- */
// SpellVamp intends to introduce to warcraft a healing based on Spell damage, like
// in LoL or Dota 2.
// Whenever a unit deals Spell damage, and it has a value of spell vamp given by
// this system, it will heal based of this value and the damage amount.
// The formula for spell vamp is:
// heal = damage * lspell vamp
// fror spell vamp: 0.1 = 10%
// *SpellVamp requires DamageInterface. Do not use TriggerSleepAction() within triggers.
// The API:
// function SetUnitSpellVamp takes unit u, real amount returns nothing
// -> Set the Spell Vamp amount for a unit
// function GetUnitSpellVamp takes unit u returns real
// -> Returns the Spell Vamp amount of a unit
// function UnitAddSpellVamp takes unit u, real amount returns nothing
// -> Add to the Spell Vamp amount of a unit the given amount
/* ----------------------------------- END ---------------------------------- */
//region System
struct SpellVamp
static real array amount
static method Get takes unit u returns real
return amount[GetUnitUserData(u)]
endmethod
static method Set takes unit u, real value returns nothing
set amount[GetUnitUserData(u)] = value
endmethod
static method onDamage takes nothing returns nothing
local real damage = GetEventDamage()
if damage > 0 and amount[DamageI.sIdx] > 0 and not DamageI.structure then
call SetWidgetLife(DamageI.source, (GetWidgetLife(DamageI.source) + (damage * amount[DamageI.sIdx])))
endif
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellDamageEvent(function thistype.onDamage)
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function SetUnitSpellVamp takes unit u, real amount returns nothing
call SpellVamp.Set(u, amount)
endfunction
function GetUnitSpellVamp takes unit u returns real
return SpellVamp.Get(u)
endfunction
function UnitAddSpellVamp takes unit u, real amount returns nothing
call SpellVamp.Set(u, SpellVamp.Get(u) + amount)
endfunction
endlibrary
library DamageInterfaceUtils requires Evasion, CriticalStrike, LifeSteal, SpellPower, SpellVamp
/* ------- Utility Library for all the Damage Interface Custom Events ------- */
/* ---------------------------- v1.9 by Chopinski --------------------------- */
// JASS API:
// Evasion System:
// function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
// -> Add to a unit Evasion chance the specified amount for a given period
// function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
// -> Add to a unit Miss chance the specified amount for a given period
// Critical Strike System:
// function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
// -> Adds the specified values of chance and multiplier to a unit for a given period
// function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
// -> Adds the specified values of critical chance to a unit for a given period
// function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
// -> Adds the specified values of critical multiplier to a unit for a given period
// Spell Power System:
// function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
// -> Add to the Flat amount of Spell Power of a unit for a given period
// function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
// -> Add to the Percent amount of Spell Power of a unit for a given period
// function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
// -> Given an ability field, will return a string that represents the damage that would be dealt
// taking into consideration the spell power bonusses of a unit.
// function AbilitySpellDamageEx takes real amount, unit u returns string
// -> Similar to GetSpellDamage will return the damage that would be dealt but as a string
// Life Steal System:
// function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
// -> Add to the Life Steal amount of a unit the given amount for a given period
// Spell Vamp System:
// function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
// -> Add to the Spell Vamp amount of a unit the given amount for a given period
/* ----------------------------------- END ---------------------------------- */
/* -------------------------------------------------------------------------- */
/* Evasion utils */
/* -------------------------------------------------------------------------- */
//region Evasion
private struct EvasionUtils extends Evasion
static thistype array data
static integer didx = -1
static timer timer = CreateTimer()
unit unit
real amount
real ticks
boolean type
method remove takes integer i returns integer
if type then
call setEvasionChance(unit, getEvasionChance(unit) - amount)
else
call setMissChance(unit, getMissChance(unit) - amount)
endif
set data[i] = data[didx]
set didx = didx - 1
set unit = null
set ticks = 0
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks <= 0 then
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real amount, real duration, boolean evasion returns nothing
local thistype this = thistype.allocate()
set .unit = u
set .amount = amount
set .ticks = duration/0.03125000
set .type = evasion
set didx = didx + 1
set data[didx] = this
if type then
call setEvasionChance(u, getEvasionChance(u) + amount)
else
call setMissChance(u, getMissChance(u) + amount)
endif
if didx == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Critical Strike Utils */
/* -------------------------------------------------------------------------- */
//region Critical Strike
private struct CriticalUtils extends CriticalStrike
static thistype array data
static integer didx = -1
static timer timer = CreateTimer()
unit unit
real crit
real multi
real ticks
integer type
method remove takes integer i returns integer
if type == 0 then
call add(unit, -crit, -multi)
elseif type == 1 then
call setChance(unit, getChance(unit) - crit)
else
call setMultiplier(unit, getMultiplier(unit) - multi)
endif
set data[i] = data[didx]
set didx = didx - 1
set unit = null
set ticks = 0
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks <= 0 then
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real chance, real multiplier, real duration, integer types returns nothing
local thistype this = thistype.allocate()
set .unit = u
set .crit = chance
set .multi = multiplier
set .ticks = duration/0.03125000
set .type = types
set didx = didx + 1
set data[didx] = this
if types == 0 then
call add(u, crit, multi)
elseif types == 1 then
call setChance(u, getChance(u) + crit)
else
call setMultiplier(u, getMultiplier(u) + multi)
endif
if didx == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Spell Power Utils */
/* -------------------------------------------------------------------------- */
//region Spell Power
private struct SpellPowerUtils extends SpellPower
static thistype array data
static integer didx = -1
static timer timer = CreateTimer()
unit unit
real amount
real ticks
boolean isFlat
method remove takes integer i returns integer
if isFlat then
call setFlat(unit, getFlat(unit) - amount)
else
call setPercent(unit, getPercent(unit) - amount)
endif
set data[i] = data[didx]
set didx = didx - 1
set unit = null
set ticks = 0
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks <= 0 then
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real amount, real duration, boolean isFlat returns nothing
local thistype this = thistype.allocate()
set .unit = u
set .amount = amount
set .ticks = duration/0.03125000
set .isFlat = isFlat
set didx = didx + 1
set data[didx] = this
if isFlat then
call setFlat(u, getFlat(u) + amount)
else
call setPercent(u, getPercent(u) + amount)
endif
if didx == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Life Steal Utils */
/* -------------------------------------------------------------------------- */
//region Life Steal
private struct LifeStealUtils extends LifeSteal
static thistype array data
static integer didx = -1
static timer timer = CreateTimer()
unit unit
real lifeSteal
real ticks
method remove takes integer i returns integer
call Set(unit, Get(unit) - lifeSteal)
set data[i] = data[didx]
set didx = didx - 1
set unit = null
set ticks = 0
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks <= 0 then
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real amount, real duration returns nothing
local thistype this = thistype.allocate()
set .unit = u
set .lifeSteal = amount
set .ticks = duration/0.03125000
set didx = didx + 1
set data[didx] = this
call Set(u, Get(u) + lifeSteal)
if didx == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Spell Vamp Utils */
/* -------------------------------------------------------------------------- */
//region Spell Vamp
private struct SpellVampUtils extends SpellVamp
static thistype array data
static integer didx = -1
static timer timer = CreateTimer()
unit unit
real spellVamp
real ticks
method remove takes integer i returns integer
call Set(unit, Get(unit) - spellVamp)
set data[i] = data[didx]
set didx = didx - 1
set unit = null
set ticks = 0
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i > didx
set this = data[i]
if ticks <= 0 then
set i = remove(i)
endif
set ticks = ticks - 1
set i = i + 1
endloop
endmethod
static method addTimed takes unit u, real amount, real duration returns nothing
local thistype this = thistype.allocate()
set .unit = u
set .spellVamp = amount
set .ticks = duration/0.03125000
set didx = didx + 1
set data[didx] = this
call Set(u, Get(u) + spellVamp)
if didx == 0 then
call TimerStart(timer, 0.03125000, true, function thistype.onPeriod)
endif
endmethod
endstruct
//endregion
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
call EvasionUtils.addTimed(u, amount, duration, true)
endfunction
function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
call EvasionUtils.addTimed(u, amount, duration, false)
endfunction
function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
call CriticalUtils.addTimed(u, chance, multiplier, duration, 0)
endfunction
function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
call CriticalUtils.addTimed(u, chance, 0, duration, 1)
endfunction
function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
call CriticalUtils.addTimed(u, 0, multiplier, duration, 2)
endfunction
function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
call SpellPowerUtils.addTimed(u, amount, duration, true)
endfunction
function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
call SpellPowerUtils.addTimed(u, amount, duration, false)
endfunction
function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
return I2S(R2I((BlzGetAbilityRealLevelField(BlzGetUnitAbility(u, abilityId), field, GetUnitAbilityLevel(u, abilityId) - 1) + SpellPower.getFlat(u)) * (1 + SpellPower.getPercent(u))))
endfunction
function AbilitySpellDamageEx takes real amount, unit u returns string
return I2S(R2I((amount + SpellPower.getFlat(u)) * (1 + SpellPower.getPercent(u))))
endfunction
function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
call LifeStealUtils.addTimed(u, amount, duration)
endfunction
function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
call SpellVampUtils.addTimed(u, amount, duration)
endfunction
endlibrary
library CooldownReduction requires RegisterPlayerUnitEvent, Table
/* ------------------ Cooldown Reduction v1.6 by Chopinski ------------------ */
//! novjass
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 and Unit Indexer by Bribe.
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
JASS API
/* --------------------------------- Getters -------------------------------- */
function GetUnitCooldownReduction takes unit u returns real
-> Returns the amount of cdr a unit has (this bonuses stacks multiplicatively)
-> 0.1 = 10%
function GetUnitCooldownReductionFlat takes unit u returns real
-> Returns the amount of cdr flat a unit has (this bonuses stacks additively)
-> 0.1 = 10%
function GetUnitCooldownOffset takes unit u returns real
-> Returns the amount of cdr offset a unit has (this bonuses stacks additively)
-> 3.0 = 3 seconds is taken from default cooldown before cdr calculation
/* --------------------------------- Setters -------------------------------- */
function SetUnitCooldownReduction takes unit u, real value returns nothing
-> Set the amount of cdr for a unit (stacks multiplicatively)
function SetUnitCooldownReductionFlat takes unit u, real value returns nothing
-> Set the amount of cdr flat for a unit (stacks additively)
function SetUnitCooldownOffset takes unit u, real value returns nothing
-> Set the amount of cdr offset for a unit (stacks additively)
function CalculateAbilityCooldown takes unit u, integer id, integer lvl, real cd returns nothing
-> use this function to pass a custom cooldown as parameter taht will
-> advantage of this system
/* -------------------------------- Aritmetic ------------------------------- */
function UnitAddCooldownReduction takes unit u, real value returns nothing
-> Add to the amount of cdr of a unit. Accepts positive and negative values
-> Adding a negative with the intent of removing a positive bonus will not work
-> for that use/read UnitRemoveCooldownReduction().
function UnitAddCooldownReductionFlat takes unit u, real value returns nothing
-> Add to the amount of cdr flat of a unit. Accepts positive and negative values
function UnitAddCooldownOffset takes unit u, real value returns nothing
-> Add to the amount of cdr offset of a unit. Accepts positive and negative values
function UnitRemoveCooldownReduction takes unit u, real value returns nothing
-> Use this function to remove a default cdr bonus from a unit. Additions of default cdr are paired,
-> meaning that if you added 0.5 and are trying to remove 0.4, it will not work.
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
globals
// Hashtable used by the system to save stuff
public hashtable Ability_Table = InitHashtable()
endglobals
// This struct represents a unit list of abilities, and is used
// to register and change a unit ability cooldown whenever that
// unit cooldown reductions value are manipulated
private struct Abilities
unit unit
integer count
Table table
/* ------------------------------- Destructor ------------------------------- */
method destroy takes nothing returns nothing
call FlushChildHashtable(Ability_Table, this)
call FlushChildHashtable(Ability_Table, GetHandleId(unit))
call table.destroy()
call deallocate()
set count = 0
set unit = null
endmethod
// the abilities default cooldowns
method default takes ability abil, integer level returns real
return LoadReal(Ability_Table, GetHandleId(abil), level)
endmethod
// register the default cooldowns for a new unit ability being inserted
method register takes ability abil returns nothing
local integer i = 0
local integer levels = BlzGetAbilityIntegerField(abil, ABILITY_IF_LEVELS)
loop
exitwhen i >= levels
call SaveReal(Ability_Table, GetHandleId(abil), (i + 1), BlzGetAbilityRealLevelField(abil, ABILITY_RLF_COOLDOWN, i))
set i = i + 1
endloop
endmethod
// update all abilities registered for the unit
method update takes integer bonus_count, real cdr, real flat, real offset returns nothing
local integer i = 0
local integer j = 0
local integer id
local integer lvl
local ability abi
local real cd
loop
exitwhen i > count - 1
set id = table[i]
set abi = BlzGetUnitAbility(unit, id)
set lvl = BlzGetAbilityIntegerField(abi, ABILITY_IF_LEVELS)
loop
exitwhen j >= lvl
if bonus_count > 0 then
set cd = ((default(abi, j+1) - offset) * cdr * (1 - flat))
else
set cd = ((default(abi, j+1) - offset) * (1 - flat))
endif
call BlzSetAbilityRealLevelField(abi, ABILITY_RLF_COOLDOWN, j, cd)
call IncUnitAbilityLevel(unit, id)
call DecUnitAbilityLevel(unit, id)
set j = j + 1
endloop
set i = i + 1
endloop
set abi = null
endmethod
// returns true if the ability is already registered for the unit
method has takes integer id returns boolean
return LoadBoolean(Ability_Table, this, id)
endmethod
// Insert a new ability into the unit ability list
method insert takes integer id returns nothing
set table[count] = id
set count = count + 1
call register(BlzGetUnitAbility(unit, id))
call SaveBoolean(Ability_Table, this, id, true)
endmethod
// Calculates the resultant cooldown for a custom value
// usefull for cases where the ability cooldown is calculated
// on its cast and you want the new cooldown to take advantage
// of the system
method calculate takes integer id, integer lvl, real newCD, integer bonus_count, real cdr, real flat, real offset returns nothing
local real cd
local ability abi = BlzGetUnitAbility(unit, id)
if bonus_count > 0 then
set cd = ((newCD - offset) * cdr * (1 - flat))
else
set cd = ((newCD - offset) * (1 - flat))
endif
call BlzSetAbilityRealLevelField(abi, ABILITY_RLF_COOLDOWN, lvl, cd)
call IncUnitAbilityLevel(unit, id)
call DecUnitAbilityLevel(unit, id)
endmethod
/* ------------------------------- Construtor ------------------------------- */
static method create takes unit u returns thistype
local thistype this = thistype.allocate()
set count = 0
set unit = u
set table = Table.create()
return this
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Cooldown Reduction */
/* -------------------------------------------------------------------------- */
struct CDR
readonly static real array CooldownReduction // stacks multiplicatively
readonly static real array CooldownReductionFlat // stacks additively
readonly static real array CooldownOffset // stacks additively
private static integer array BonusCount // the number of normal cdr a unit has
private static integer array n // each unit registered has it's own instance
// Retrieve or create the Abilities list from a unit
private static method instance takes unit u returns Abilities
local integer idx = GetUnitUserData(u)
if n[idx] == 0 then
set n[idx] = Abilities.create(u)
endif
return n[idx]
endmethod
// Update the unit abilities cooldowns
private static method update takes unit u returns nothing
local Abilities list = instance(u)
local integer idx = GetUnitUserData(u)
call list.update(BonusCount[idx], CooldownReduction[idx], CooldownReductionFlat[idx], CooldownOffset[idx])
endmethod
// Getter and Setter
// 0 -> default type, 1 -> flat, 2 -> offset
static method Get takes unit u, integer types returns real
if types == 0 then
return CooldownReduction[GetUnitUserData(u)]
elseif types == 1 then
return CooldownReductionFlat[GetUnitUserData(u)]
else
return CooldownOffset[GetUnitUserData(u)]
endif
endmethod
static method Set takes unit u, real value, integer types returns nothing
if types == 0 then
set CooldownReduction[GetUnitUserData(u)] = value
elseif types == 1 then
set CooldownReductionFlat[GetUnitUserData(u)] = value
else
set CooldownOffset[GetUnitUserData(u)] = value
endif
call update(u)
endmethod
//Calculates an unit cooldown reduction for the default type (stacks multiplicatively)
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 > BonusCount[idx]
set aux = LoadReal(Ability_Table, 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
// Added in version 1.1: Changed the default formula to stack multiplicatively instead of additively.
// Add inserts in the unit list of bonuses and Remove, well, removes it.
static method add takes unit u, real amount returns nothing
local integer idx = GetUnitUserData(u)
local integer id = GetHandleId(u)
//-----------------------------------------------------
if amount != 0 then
call SaveReal(Ability_Table, id, BonusCount[idx], amount)
set CooldownReduction[idx] = calculate(u)
set BonusCount[idx] = BonusCount[idx] + 1
call update(u)
endif
endmethod
//Removes are paired with additions. If trying to remove a value that was not
//added first, than it should fail.
//Since its a multiplication, the order of operations do not alter the result,
//so to save performance, when removing a bonus, the last bonus registered is
// moved to the position of the bonus that was just removed and the loop breks.
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 > BonusCount[idx] - 1
set aux = LoadReal(Ability_Table, id, i)
if aux == amount then
call RemoveSavedReal(Ability_Table, id, i)
if i != BonusCount[idx] - 1 then
set aux = LoadReal(Ability_Table, id, BonusCount[idx] - 1)
call SaveReal(Ability_Table, id, i, aux)
call RemoveSavedReal(Ability_Table, id, BonusCount[idx] - 1)
endif
set BonusCount[idx] = BonusCount[idx] - 1
set CooldownReduction[idx] = calculate(u)
set i = BonusCount[idx] + 1
call update(u)
else
set i = i + 1
endif
endloop
endmethod
// Sets the cooldown of a specific unit ability in a specific level given a custom cooldown
static method CalculateCooldown takes unit u, integer id, integer lvl, real newCD returns nothing
local integer idx = GetUnitUserData(u)
local Abilities list = instance(u)
call list.calculate(id, lvl - 1, newCD, BonusCount[idx], CooldownReduction[idx], CooldownReductionFlat[idx], CooldownOffset[idx])
endmethod
private static method onCast takes nothing returns nothing
local unit src = GetTriggerUnit()
local integer abi = GetSpellAbilityId()
local integer idx = GetUnitUserData(src)
local Abilities list
// this condition is here to stop the system spamming registration
// on every ability cast. The registration will only happen when a
// cooldown reduction amount is manipulated to a unit
if n[idx] != 0 then
set list = n[idx]
if not list.has(abi) then
call list.insert(abi)
call update(src)
endif
endif
set src = null
endmethod
// clean up on deindex event
private static method onDeIndex takes nothing returns nothing
local Abilities list = n[udg_UDex]
call list.destroy()
set n[udg_UDex] = 0
set CooldownReduction[udg_UDex] = 0
set CooldownReductionFlat[udg_UDex] = 0
set CooldownOffset[udg_UDex] = 0
set BonusCount[udg_UDex] = 0
endmethod
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)
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
/* --------------------------------- Getters -------------------------------- */
function GetUnitCooldownReduction takes unit u returns real
return 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
/* --------------------------------- Setters -------------------------------- */
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
/* -------------------------------- Aritmetic ------------------------------- */
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
/* -------------------------------- Utilities ------------------------------- */
function CalculateAbilityCooldown takes unit u, integer id, integer lvl, real cd returns nothing
call CDR.CalculateCooldown(u, id, lvl, cd)
endfunction
endlibrary
library CooldownReductionUtils requires CooldownReduction
/* --------------- Cooldown Reduction Utils v1.6 by Chopinski --------------- */
//! novjass
Intro
Utility Library that include a few extra functions to deal with
Cooldown Reduction
JASS API
/* ------------------------------- Timed Bonus ------------------------------ */
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
/* ----------------------------- String Previews ---------------------------- */
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
/* ------------------------------ Maybe Usefull ----------------------------- */
function GetAbilityTable takes nothing returns hashtable
-> Returns the hashtable that holds the units default cooldown reduction values.
-> Use with caution! you might break stuff
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
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
/* -------------------------------------------------------------------------- */
/* Public JASS API */
/* -------------------------------------------------------------------------- */
/* ------------------------------- Timed Bonus ------------------------------ */
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
/* ----------------------------- String Previews ---------------------------- */
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
/* ------------------------------ Maybe Usefull ----------------------------- */
function GetAbilityTable takes nothing returns hashtable
return CooldownReduction_Ability_Table
endfunction
endlibrary
library NewBonus requires DamageInterface, Evasion, CriticalStrike, SpellPower, LifeSteal, SpellVamp, CooldownReduction
/* ----------------------- NewBonus Expanded v2.0 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.31+.
How it works?
By using the new Object API recently introduced to warcraft, we can now
modify an ability field, so when giving an unit a bonus, we add a specific
ability to that unit, if it do not have it already, and change that ability
bonus value to the desired value. Retrieving the amount is also trivial, by
just reading that field value.
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 integer
-> Returns the specified bonus amount for the unit
-> Example: set amount = GetUnitBonus(GetTriggerUnit(), BONUS_AGILITY)
function SetUnitBonus takes unit u, integer bonus_type, integer amount returns integer
-> 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, integer amount returns integer
-> Add the specified amount for the specified bonus tyte for unit
-> Example: call AddUnitBonus(GetTriggerUnit(), BONUS_DAMAGE, 100)
function GetUnitBonusReal takes unit u, integer bonus_type returns real
-> Returns the specified bonus amount for the unit
-> Example: set amount = GetUnitBonusReal(GetTriggerUnit(), BONUS_HEALTH_REGEN)
function SetUnitBonusReal takes unit u, integer bonus_type, real amount returns nothing
-> Set the specified bonus type to amount for the unit
-> Example: call SetUnitBonusReal(GetTriggerUnit(), BONUS_ATTACK_SPEED, 0.15)
function RemoveUnitBonusReal takes unit u, real bonus_type returns nothing
-> Removes the Specified bonus type from unit
-> Example: call RemoveUnitBonusReal(GetTriggerUnit(), BONUS_MANA_REGEN)
function AddUnitBonusReal takes unit u, integer bonus_type, real amount returns real
-> Add the specified amount for the specified bonus tyte for unit
-> Example: call AddUnitBonusReal(GetTriggerUnit(), BONUS_LIFE_STEAL, 0.33)
Added in version 1.5:
Upper and Lower limit to max bonus amount set to 2147483647 and -2147483648
to avoid over/under flowing the integer fields. AddUnitBonus()
now returns the amount of bonus that was actually setted. In case of no over/under flow,
it returns the amount passed, in case of over/under flow it returns the amount that
was allowed to be setted. It is still up to the user to call the functions without
passing integers that already over/under flown.
Added in version 1.7
- Fixed a bug when Adding Health/Mana bonus not keeping the unit Health/Mana Percentage
- Added the funcitons: They manipuilate real bonuses values correctly.
- GetUnitBonusReal()
- SetUnitBonusReal()
- RemoveUnitBonusReal()
- AddUnitBonusReal()
- Refactored the LinkBonusToItem() to use the On Drop event instead of a periodic timer to check the link
- Included an Expanded version of NewBonus and NewBonusUtils that take advangtage of the Damage Inteface System and Cooldown Reduction System
- Renamed 4 bonus types for better readability.
- Removed the "Ex" from the end of the API functions
Credits to Earth Fury for the original Bonus idea
//! endnovjass
/* ----------------------------------- END ---------------------------------- */
globals
//The bonus types
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_HEALTH_REGEN = 10
constant integer BONUS_MANA_REGEN = 11
constant integer BONUS_ATTACK_SPEED = 12
constant integer BONUS_MAGIC_RESISTANCE = 13
//expanded bonus types not linked to abilities
constant integer BONUS_EVASION_CHANCE = 14
constant integer BONUS_MISS_CHANCE = 15
constant integer BONUS_CRITICAL_CHANCE = 16
constant integer BONUS_CRITICAL_DAMAGE = 17
constant integer BONUS_SPELL_POWER_FLAT = 18
constant integer BONUS_SPELL_POWER_PERCENT = 19
constant integer BONUS_LIFE_STEAL = 20
constant integer BONUS_SPELL_VAMP = 21
constant integer BONUS_COOLDOWN_REDUCTION = 22
constant integer BONUS_COOLDOWN_REDUCTION_FLAT = 23
constant integer BONUS_COOLDOWN_OFFSET = 24
//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 = 'A07V'
private constant integer ARMOR_ABILITY = 'A07T'
private constant integer STATS_ABILITY = 'A083'
private constant integer HEALTH_ABILITY = 'A07W'
private constant integer MANA_ABILITY = 'A07Z'
private constant integer HEALTHREGEN_ABILITY = 'A07X'
private constant integer MANAREGEN_ABILITY = 'A080'
private constant integer ATTACKSPEED_ABILITY = 'A07U'
private constant integer MOVEMENTSPEED_ABILITY = 'A081'
private constant integer SIGHT_RANGE_ABILITY = 'A082'
private constant integer MAGIC_RESISTANCE_ABILITY = 'A07Y'
//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
endglobals
struct NewBonus
//SetUnitAbilityBonusI() and SetUnitAbilityBonusR are necessary to manage abilities that have integer fields and real fields
//but the return is normalized to reals
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
//Increasing and Decreasing is necessary since the abilities do not get updated just by changing
//it's fields values. In the future, if Blizzard decides to patch it, it could be removed.
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 GetUnitBonus 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)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method SetUnitBonus 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 - GetUnitBonus(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 - GetUnitBonus(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 - GetUnitBonus(u, bonus_type)))
return SetUnitAbilityBonusI(u, SIGHT_RANGE_ABILITY, SIGHT_RANGE_FIELD, amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1
endmethod
static method AddUnitBonus takes unit u, integer bonus_type, integer amount returns integer
local integer current_amount = GetUnitBonus(u, bonus_type)
// Added in version 1.5 to avoid overflow/underflow of the field value
if amount > 0 and current_amount > 2147483647 - amount then //Overflow
set amount = 2147483647 - current_amount
elseif amount < 0 and current_amount < -2147483648 - amount then //Underflow
set amount = -2147483648 - current_amount
endif
call SetUnitBonus(u, bonus_type, (current_amount + amount))
return amount
endmethod
// Funtions that handle the real bonus types
static method GetUnitBonusR 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_EVASION_CHANCE then
return GetUnitEvasionChance(u)
elseif bonus_type == BONUS_MISS_CHANCE then
return GetUnitMissChance(u)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
return GetUnitCriticalChance(u)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
return GetUnitCriticalMultiplier(u)
elseif bonus_type == BONUS_SPELL_POWER_FLAT then
return GetUnitSpellPowerFlat(u)
elseif bonus_type == BONUS_SPELL_POWER_PERCENT then
return GetUnitSpellPowerPercent(u)
elseif bonus_type == BONUS_LIFE_STEAL then
return GetUnitLifeSteal(u)
elseif bonus_type == BONUS_SPELL_VAMP then
return GetUnitSpellVamp(u)
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)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
return -1.0
endmethod
static method SetUnitBonusR takes unit u, integer bonus_type, real amount returns nothing
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_EVASION_CHANCE then
call SetUnitEvasionChance(u, amount)
elseif bonus_type == BONUS_MISS_CHANCE then
call SetUnitMissChance(u, amount)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
call SetUnitCriticalChance(u, amount)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
call SetUnitCriticalMultiplier(u, amount)
elseif bonus_type == BONUS_SPELL_POWER_FLAT then
call SetUnitSpellPowerFlat(u, amount)
elseif bonus_type == BONUS_SPELL_POWER_PERCENT then
call SetUnitSpellPowerPercent(u, amount)
elseif bonus_type == BONUS_LIFE_STEAL then
call SetUnitLifeSteal(u, amount)
elseif bonus_type == BONUS_SPELL_VAMP then
call SetUnitSpellVamp(u, 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)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
endmethod
static method AddUnitBonusR takes unit u, integer bonus_type, real amount returns nothing
if bonus_type == BONUS_HEALTH_REGEN then
call SetUnitBonusR(u, bonus_type, GetUnitBonusR(u, bonus_type) + amount)
elseif bonus_type == BONUS_MANA_REGEN then
call SetUnitBonusR(u, bonus_type, GetUnitBonusR(u, bonus_type) + amount)
elseif bonus_type == BONUS_ATTACK_SPEED then
call SetUnitBonusR(u, bonus_type, GetUnitBonusR(u, bonus_type) + amount)
elseif bonus_type == BONUS_MAGIC_RESISTANCE then
call SetUnitBonusR(u, bonus_type, GetUnitBonusR(u, bonus_type) + amount)
elseif bonus_type == BONUS_EVASION_CHANCE then
call UnitAddEvasionChance(u, amount)
elseif bonus_type == BONUS_MISS_CHANCE then
call UnitAddMissChance(u, amount)
elseif bonus_type == BONUS_CRITICAL_CHANCE then
call UnitAddCriticalStrike(u, amount, 0)
elseif bonus_type == BONUS_CRITICAL_DAMAGE then
call UnitAddCriticalStrike(u, 0, amount)
elseif bonus_type == BONUS_SPELL_POWER_FLAT then
call UnitAddSpellPowerFlat(u, amount)
elseif bonus_type == BONUS_SPELL_POWER_PERCENT then
call UnitAddSpellPowerPercent(u, amount)
elseif bonus_type == BONUS_LIFE_STEAL then
call UnitAddLifeSteal(u, amount)
elseif bonus_type == BONUS_SPELL_VAMP then
call UnitAddSpellVamp(u, amount)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION then
call UnitAddCooldownReduction(u, amount)
elseif bonus_type == BONUS_COOLDOWN_REDUCTION_FLAT then
call UnitAddCooldownReductionFlat(u, amount)
elseif bonus_type == BONUS_COOLDOWN_OFFSET then
call UnitAddCooldownOffset(u, amount)
else
call DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "Invalid Bonus Type")
endif
endmethod
endstruct
/* -------------------------------------------------------------------------- */
/* JASS Public API */
/* -------------------------------------------------------------------------- */
function GetUnitBonus takes unit u, integer bonus_type returns integer
return NewBonus.GetUnitBonus(u, bonus_type)
endfunction
function SetUnitBonus takes unit u, integer bonus_type, integer amount returns integer
return NewBonus.SetUnitBonus(u, bonus_type, amount)
endfunction
function RemoveUnitBonus takes unit u, integer bonus_type returns nothing
call NewBonus.SetUnitBonus(u, bonus_type, 0)
endfunction
function AddUnitBonus takes unit u, integer bonus_type, integer amount returns integer
return NewBonus.AddUnitBonus(u, bonus_type, amount)
endfunction
// Extra functions for manipulating the real bonus types
function GetUnitBonusReal takes unit u, integer bonus_type returns real
return NewBonus.GetUnitBonusR(u, bonus_type)
endfunction
function SetUnitBonusReal takes unit u, integer bonus_type, real amount returns nothing
call NewBonus.SetUnitBonusR(u, bonus_type, amount)
endfunction
function RemoveUnitBonusReal takes unit u, integer bonus_type returns nothing
call NewBonus.SetUnitBonusR(u, bonus_type, 0)
endfunction
function AddUnitBonusReal takes unit u, integer bonus_type, real amount returns real
call NewBonus.AddUnitBonusR(u, bonus_type, amount)
return amount
endfunction
endlibrary
library NewBonusUtils requires NewBonus, RegisterPlayerUnitEvent
/* ----------------------- NewBonusUtils Expanded v2.0 by Chopinski ----------------------- */
//! novjass
Required Library: RegisterPlayerUnitEvent -> https://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 ---------------------------------- */
private struct NewBonusUtils extends NewBonus
static timer t = CreateTimer()
//Dynamic Indexing for buff and timed
static integer didx = -1
static thistype array data
//Dynamic Indexing for items
static integer ditem = -1
static thistype array items
unit u
item i
real ticks
integer bonus_type
integer buffId // Works for abilities too.
integer int_amount
real real_amount
boolean link //true -> Timed bonus / false -> Linked to a buff
method destroyInstance takes integer i, boolean isItem returns integer
if this.bonus_type < 10 then
call AddUnitBonus(this.u, this.bonus_type, -this.int_amount)
else
if this.bonus_type == BONUS_COOLDOWN_REDUCTION then
call UnitRemoveCooldownReduction(this.u, this.real_amount)
else
call AddUnitBonusReal(this.u, this.bonus_type, -this.real_amount)
endif
endif
if isItem then
set items[i] = items[ditem]
set ditem = ditem - 1
else
set data[i] = data[didx]
set didx = didx - 1
if didx == -1 then
call PauseTimer(t)
endif
endif
set this.u = null
set this.i = null
call this.deallocate()
return i - 1
endmethod
static method OnDrop takes nothing returns nothing
local item itm = GetManipulatedItem()
local integer i = 0
local thistype this
loop
exitwhen i > ditem
set this = items[i]
if this.i == itm then
set i = this.destroyInstance(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 > didx
set this = data[i]
if this.link then // Timed link
set this.ticks = this.ticks - 1
if this.ticks <= 0 then
set i = this.destroyInstance(i, false)
endif
else // Buff Link
if GetUnitAbilityLevel(this.u, this.buffId) == 0 then
set i = this.destroyInstance(i, false)
endif
endif
set i = i + 1
endloop
endmethod
static method TimeLink takes unit u, integer bonus_type, real amount, real duration, boolean link returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.bonus_type = bonus_type
set this.ticks = duration/0.03125000
set this.link = link
set didx = didx + 1
set data[didx] = this
if bonus_type < 10 then
set this.int_amount = AddUnitBonus(u, bonus_type, R2I(amount))
else
set this.real_amount = AddUnitBonusReal(u, bonus_type, amount)
endif
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
endif
endmethod
static method BuffLink takes unit u, integer bonus_type, real amount, integer buffId, boolean link returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.bonus_type = bonus_type
set this.buffId = buffId
set this.link = link
set didx = didx + 1
set data[didx] = this
if bonus_type < 10 then
set this.int_amount = AddUnitBonus(u, bonus_type, R2I(amount))
else
set this.real_amount = AddUnitBonusReal(u, bonus_type, amount)
endif
if didx == 0 then
call TimerStart(t, 0.03125000, true, function thistype.OnPeriod)
endif
endmethod
static method ItemLink takes unit u, integer bonus_type, real amount, item i returns nothing
local thistype this = thistype.allocate()
set this.u = u
set this.i = i
set this.bonus_type = bonus_type
set ditem = ditem + 1
set items[ditem] = this
if bonus_type < 10 then
set this.int_amount = AddUnitBonus(u, bonus_type, R2I(amount))
else
set this.real_amount = AddUnitBonusReal(u, bonus_type, amount)
endif
endmethod
static method CopyBonuses takes unit source, unit target returns nothing
local integer i = 1
loop
exitwhen i > 24
if i < 10 then
if GetUnitBonus(source, i) != 0 then
call AddUnitBonus(target, i, GetUnitBonus(source, i))
endif
else
if GetUnitBonusReal(source, i) != 0 then
call AddUnitBonusReal(target, i, GetUnitBonusReal(source, i))
endif
endif
set i = i + 1
endloop
endmethod
static method MirrorBonuses takes unit source, unit target returns nothing
local integer i = 1
loop
exitwhen i > 24
if i < 10 then
call SetUnitBonus(target, i, GetUnitBonus(source, i))
else
call SetUnitBonusReal(target, i, GetUnitBonusReal(source, i))
endif
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 Public API */
/* -------------------------------------------------------------------------- */
function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
call NewBonusUtils.TimeLink(u, bonus_type, amount, duration, true)
endfunction
function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
call NewBonusUtils.BuffLink(u, bonus_type, amount, buffId, false)
endfunction
function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
call NewBonusUtils.ItemLink(u, bonus_type, amount, i)
endfunction
function UnitCopyBonuses takes unit source, unit target returns nothing
call NewBonusUtils.CopyBonuses(source, target)
endfunction
function UnitMirrorBonuses takes unit source, unit target returns nothing
call NewBonusUtils.MirrorBonuses(source, target)
endfunction
endlibrary
library AdaptedWarrior requires Evasion, NewBonusUtils
/* -------------------- Adapted Warrior v1.0 by Chopinski ------------------- */
// Credits:
// FrIkY - Icon
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Adapted Warrior ability
private constant integer ADAPTED_WARRIOR = 'A089'
endglobals
// The Attack Speed bonus amount
private function GetAttackSpeedBonus takes integer level returns real
return 0.95 + 0.*level
endfunction
// The Attack Speed bonus duration
private function GetAttackSpeedDuration takes integer level returns real
return 10. + 0.*level
endfunction
// The Movement Speed bonus amount
private function GetMovementSpeedBonus takes integer level returns integer
return 50 + 0*level
endfunction
// The Movement Speed bonus duration
private function GetMovementSpeedDuration takes integer level returns real
return 10. + 0.*level
endfunction
/* --------------------------------- System --------------------------------- */
private struct AdaptedWarrior extends array
private static method onEvade takes nothing returns nothing
local unit source = GetMissingUnit()
local unit target = GetEvadingUnit()
local integer level = GetUnitAbilityLevel(target, ADAPTED_WARRIOR)
local boolean enemy = IsUnitEnemy(target, GetOwningPlayer(source))
if level > 0 and enemy then
call AddUnitBonusTimed(target, BONUS_ATTACK_SPEED, GetAttackSpeedBonus(level), GetAttackSpeedDuration(level))
call AddUnitBonusTimed(target, BONUS_MOVEMENT_SPEED, GetMovementSpeedBonus(level), GetMovementSpeedDuration(level))
endif
set source = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterEvasionEvent(function thistype.onEvade)
endmethod
endstruct
endlibrary
library Evade requires RegisterPlayerUnitEvent ,SpellEffectEvent, PluginSpellEffect, NewBonusUtils
/* ------------------------- Evade v1.1 by Chopinski ------------------------ */
// Credits:
// Blizzard - Icon
// Bribe - SpellEffectEvent
// Magtheridon96 - RegisterPlayerUnitEvent
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Evasion ability
private constant integer EVASION = 'A08B'
// The raw code of the Evasion buff
private constant integer BUFF = 'B00K'
endglobals
// The Evasion bonus per level
private function GetPassiveBonus takes integer level returns real
if level == 1 then
return 10.
else
return 5.
endif
endfunction
// The Evasion bonus on cast
private function GetActiveBonus takes integer level returns real
return 100.
endfunction
/* --------------------------------- System --------------------------------- */
private struct Evasion extends array
private static method onCast takes nothing returns nothing
call LinkBonusToBuff(Spell.unit, BONUS_EVASION_CHANCE, GetActiveBonus(Spell.level), BUFF)
endmethod
private static method onLevelUp takes nothing returns nothing
local unit source = GetTriggerUnit()
local integer skill = GetLearnedSkill()
local integer level = GetUnitAbilityLevel(source, skill)
if skill == EVASION then
call AddUnitBonusReal(source, BONUS_EVASION_CHANCE, GetPassiveBonus(level))
endif
set source = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(EVASION, function thistype.onCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function thistype.onLevelUp)
endmethod
endstruct
endlibrary
library Immolation requires RegisterPlayerUnitEvent, NewBonusUtils, TimerUtils
/* ---------------------- Immolation v1.0 by Chopinski ---------------------- */
// Credits:
// Blizzard - Icon
// Magtheridon96 - RegisterPlayerUnitEvent
// Vexorian - TimerUtils
// Mythic - Immolation Effect (Edited by me)
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Immolation ability
private constant integer IMMOLATION = 'A08C'
// The raw code of the Immolation buff
private constant integer BUFF = 'B00L'
// The immolation damage period
private constant real PERIOD = 1.
// The immolation Damage model
private constant string MODEL = "Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl"
// The immolation Damage model
private constant string ATTACH_POINT = "head"
endglobals
// The immolation AoE
private function GetAoE takes unit source, integer level returns real
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, IMMOLATION), ABILITY_RLF_AREA_OF_EFFECT, level - 1)
endfunction
// The Immolation damage
private function GetDamage takes integer level returns real
return 20.*level
endfunction
// The Immolation armor debuff duration
private function GetDuration takes integer level returns real
return 5. + 0.*level
endfunction
private function DamageFilter takes player owner, unit target returns boolean
return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
/* --------------------------------- System --------------------------------- */
private struct Immolation
static thistype array n
timer timer
unit unit
group group
player player
integer i
private static method onPeriod takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer level
local unit u
if GetUnitAbilityLevel(unit, BUFF) > 0 then
set level = GetUnitAbilityLevel(unit, IMMOLATION)
call GroupEnumUnitsInRange(group, GetUnitX(unit), GetUnitY(unit), GetAoE(unit, level), null)
loop
set u = FirstOfGroup(group)
exitwhen u == null
if DamageFilter(player, u) then
if UnitDamageTarget(unit, u, GetDamage(level), false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) then
call DestroyEffect(AddSpecialEffectTarget(MODEL, u, ATTACH_POINT))
call AddUnitBonusTimed(u, BONUS_ARMOR, -1, GetDuration(level))
endif
endif
call GroupRemoveUnit(group, u)
endloop
else
call ReleaseTimer(timer)
call DestroyGroup(group)
call deallocate()
set n[i] = 0
set timer = null
set unit = null
set player = null
set group = null
endif
set u = null
endmethod
private static method onOrder takes nothing returns nothing
local unit source = GetOrderedUnit()
local integer index = GetUnitUserData(source)
local thistype this
if n[index] == 0 and GetIssuedOrderId() == 852177 then
set this = thistype.allocate()
set i = index
set timer = NewTimerEx(this)
set group = CreateGroup()
set player = GetOwningPlayer(source)
set unit = source
set n[i] = this
call LinkEffectToBuff(source, BUFF, "Ember Green.mdl", "chest")
call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
endif
set source = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onOrder)
endmethod
endstruct
endlibrary
library AdaptiveStrike requires RegisterPlayerUnitEvent, NewBonus
/* -------------------- Adaptive Strike v1.0 by Chopinski ------------------- */
// Credits:
// Blizzard - Icon
// Magtheridon96 - RegisterPlayerUnitEvent
// Mythic - Culling Slash and Cleave Effects
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Adaptive Strike ability
private constant integer ADAPTIVE_STRIKE = 'A08A'
// The Adaptive Strike Slash model
private constant string SLASH = "Culling Slash.mdl"
// The Adaptive Strike Cleave model
private constant string CLEAVE = "Culling Cleave.mdl"
endglobals
// The Adaptive Strike proc chance
private function GetChance takes integer level returns boolean
return GetRandomInt(level, 4) <= level
endfunction
// The Adaptive Strike damage
private function GetDamage takes unit source, integer level returns real
return 50*level + GetUnitBonus(source, BONUS_DAMAGE)*0.5
endfunction
// The Adaptive Strike AoE
private function GetAoE takes boolean slash, integer level returns real
if slash then
return 250. + 0.*level
else
return 250. + 0.*level
endif
endfunction
/* --------------------------------- System --------------------------------- */
private struct AdaptiveStrike extends array
static integer array state
static method onDamage takes nothing returns nothing
local integer level
if state[DamageI.sIdx] != 0 then
set level = GetUnitAbilityLevel(DamageI.source, ADAPTIVE_STRIKE)
if state[DamageI.sIdx] == 1 then
call UnitDamageCone(DamageI.source, DamageI.sourceX, DamageI.sourceY, GetUnitFacing(DamageI.source), 150, GetAoE(false, level), GetDamage(DamageI.source, level), ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, false, true, false)
else
call UnitDamageArea(DamageI.source, DamageI.sourceX, DamageI.sourceY, GetAoE(true, level), GetDamage(DamageI.source, level), ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, false, true, false)
endif
set state[DamageI.sIdx] = 0
endif
endmethod
static method onAttack takes nothing returns nothing
local unit source = GetAttacker()
local integer level = GetUnitAbilityLevel(source, ADAPTIVE_STRIKE)
local integer i
if level > 0 and IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER) and GetChance(level) then
set i = GetUnitUserData(source)
set state[i] = GetRandomInt(1, 2)
if state[i] == 1 then
call SetUnitAnimationByIndex(source, 4)
call QueueUnitAnimation(source, "Stand Ready")
call DestroyEffect(AddSpecialEffectTarget(CLEAVE, source, "origin"))
else
call SetUnitAnimationByIndex(source, 5)
call QueueUnitAnimation(source, "Stand Ready")
call DestroyEffect(AddSpecialEffectTarget(SLASH, source, "origin"))
endif
endif
set source = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.onAttack)
call RegisterAttackDamageEvent(function thistype.onDamage)
endmethod
endstruct
endlibrary
library SpellShield requires DamageInterface, SpellEffectEvent, PluginSpellEffect, Utilities, NewBonusUtils, optional Metamorphosis
/* --------------------- Spell Shield v1.1 by Chopinski --------------------- */
// Credits:
// Darkfang - Icon
// Bribe - SpellEffectEvent
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Spell Shield ability
private constant integer SPELL_SHIELD = 'A08F'
// The raw code of the Spell Block ability
private constant integer SPELL_BLOCK = 'A088'
// The raw code of the Spell Shield buff
private constant integer BUFF = 'B00M'
// The Spell Shield model
private constant string MODEL = "SpellShield.mdl"
// Spell Shield block effect period
private constant real PERIOD = 0.03125
endglobals
// The Adaptive Strike damage
private function GetConversion takes integer level returns real
return 0.2 + 0.1*level
endfunction
// The Spell Shield Duration
private function GetDuration takes unit source, integer level returns real
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(source, SPELL_SHIELD), ABILITY_RLF_DURATION_HERO, level - 1)
endfunction
/* --------------------------------- System --------------------------------- */
private struct SpellShield
static thistype array data
static integer didx = -1
static timer timer = CreateTimer()
unit source
unit target
effect effect
integer count
real angle
real x
real y
method remove takes integer i returns integer
call DestroyEffect(effect)
set data[i] = data[didx]
set didx = didx - 1
set source = null
set target = null
set effect = null
if didx == -1 then
call PauseTimer(timer)
endif
call deallocate()
return i - 1
endmethod
static method onPeriod takes nothing returns nothing
local integer i = 0
local real height
local thistype this
loop
exitwhen i > didx
set this = data[i]
if count <= 0 then
set i = remove(i)
else
set x = GetUnitX(target)
set y = GetUnitY(target)
set angle = AngleBetweenCoordinates(x, y, GetUnitX(source), GetUnitY(source))
static if LIBRARY_Metamorphosis then
if GetUnitAbilityLevel(target, Metamorphosis_BUFF) > 0 then
set height = GetUnitZ(target) + 400
else
set height = GetUnitZ(target) + 100
endif
else
set height = GetUnitZ(target) + 100
endif
call BlzSetSpecialEffectPosition(effect, x + 60*Cos(angle), y + 60*Sin(angle), height)
call BlzSetSpecialEffectYaw(effect, angle)
endif
set count = count - 1
set i = i + 1
endloop
endmethod
static method onDamage takes nothing returns nothing
local real damage = GetEventDamage()
local real height
local thistype this
if damage > 0 and GetUnitAbilityLevel(DamageI.target, BUFF) > 0 then
set this = thistype.allocate()
set source = DamageI.source
set target = DamageI.target
set angle = AngleBetweenCoordinates(DamageI.targetX, DamageI.targetY, DamageI.sourceX, DamageI.sourceY)
set x = DamageI.targetX + 60*Cos(angle)
set y = DamageI.targetY + 60*Sin(angle)
set count = 16
set effect = AddSpecialEffect(MODEL, x, y)
set didx = didx + 1
set data[didx] = this
set damage = damage*(1 - GetConversion(GetUnitAbilityLevel(DamageI.target, SPELL_SHIELD)))
static if LIBRARY_Metamorphosis then
if GetUnitAbilityLevel(DamageI.target, Metamorphosis_BUFF) > 0 then
set height = GetUnitZ(DamageI.target) + 400
else
set height = GetUnitZ(DamageI.target) + 100
endif
else
set height = GetUnitZ(DamageI.target) + 100
endif
call BlzSetSpecialEffectZ(effect, height)
call BlzSetSpecialEffectScale(effect, 1.5)
call BlzSetSpecialEffectYaw(effect, angle)
call BlzSetSpecialEffectColor(effect, 0, 0, 255)
call BlzSetEventDamage(damage)
call LinkBonusToBuff(DamageI.target, BONUS_DAMAGE, damage, BUFF)
if didx == 0 then
call TimerStart(timer, PERIOD, true, function thistype.onPeriod)
endif
endif
endmethod
static method onCast takes nothing returns nothing
call UnitRemoveBuffs(Spell.unit, false, true)
call UnitAddAbilityTimed(Spell.unit, SPELL_BLOCK, GetDuration(Spell.unit, Spell.level), true)
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_SHIELD, function thistype.onCast)
call RegisterSpellDamageEvent(function thistype.onDamage)
endmethod
endstruct
endlibrary
library Metamorphosis requires DamageInterface, SpellEffectEvent, PluginSpellEffect, Utilities, NewBonusUtils
/* --------------------- Metamorphosis v1.1 by Chopinski -------------------- */
// Credits:
// BLazeKraze - Icon
// Bribe - SpellEffectEvent
// Mythic - Damnation Black model (edited by me)
// Henry - Dark Illidan model from Warcraft Underground
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Metamorphosis ability
private constant integer ABILITY = 'A08E'
// The raw code of the Metamorphosis buff
public constant integer BUFF = 'BEme'
// The Metamorphosis lift off model
private constant string MODEL = "Damnation Black.mdl"
// The fear model
private constant string FEAR_MODEL = "Fear.mdl"
// The the fear attachment point
private constant string ATTACH_FEAR = "overhead"
endglobals
// The Metamorphosis AoE for Fear effect
private function GetAoE takes integer level returns real
return 400. + 0.*level
endfunction
// The Metamorphosis Fear Duration
private function GetDuration takes unit source, integer level returns real
if IsUnitType(source, UNIT_TYPE_HERO) then
return 2. + 0.*level
else
return 5. + 0.*level
endif
endfunction
// The Metamorphosis Armor debuff Duration
private function GetArmorDuration takes unit source, integer level returns real
return 5. + 0.*level
endfunction
// The Metamorphosis Health Bonus
private function GetBonusHealth takes unit source, integer level returns integer
if IsUnitType(source, UNIT_TYPE_HERO) then
return 100*level
else
return 50*level
endif
endfunction
// The Metamorphosis Damage Bonus
private function GetBonusDamage takes unit source, integer level returns integer
if IsUnitType(source, UNIT_TYPE_HERO) then
return 10*level
else
return 5*level
endif
endfunction
// Fear Filter
private function FearFilter takes player owner, unit target returns boolean
return UnitAlive(target) and IsUnitEnemy(target, owner) and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
/* --------------------------------- System --------------------------------- */
private struct Metamorphosis
timer timer
unit unit
group group
player player
integer level
static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer health = 0
local integer damage = 0
local unit u
call DestroyEffect(AddSpecialEffectEx(MODEL, GetUnitX(unit), GetUnitY(unit), GetUnitZ(unit), 2))
call GroupEnumUnitsInRange(group, GetUnitX(unit), GetUnitY(unit), GetAoE(level), null)
loop
set u = FirstOfGroup(group)
exitwhen u == null
if FearFilter(player, u) then
set health = health + GetBonusHealth(u, level)
set damage = damage + GetBonusDamage(u, level)
call UnitApplyFear(u, GetDuration(u, level), FEAR_MODEL, ATTACH_FEAR)
endif
call GroupRemoveUnit(group, u)
endloop
call LinkBonusToBuff(unit, BONUS_HEALTH, health, BUFF)
call LinkBonusToBuff(unit, BONUS_DAMAGE, damage, BUFF)
call DestroyGroup(group)
call ReleaseTimer(timer)
call deallocate()
set timer = null
set unit = null
set group = null
set player = null
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set timer = NewTimerEx(this)
set unit = Spell.unit
set group = CreateGroup()
set player = GetOwningPlayer(unit)
set level = Spell.level
call TimerStart(timer, 0.5, false, function thistype.onExpire)
endmethod
static method onDamage takes nothing returns nothing
if GetUnitAbilityLevel(DamageI.source, BUFF) > 0 then
if DamageI.isEnemy and not DamageI.magicImmune then
call AddUnitBonusTimed(DamageI.target, BONUS_ARMOR, -1, GetArmorDuration(DamageI.target, GetUnitAbilityLevel(DamageI.source, ABILITY)))
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(ABILITY, function thistype.onCast)
call RegisterAttackDamageEvent(function thistype.onDamage)
endmethod
endstruct
endlibrary
library ManaBurn requires DamageInterface, RegisterPlayerUnitEvent, Utilities
/* ----------------------- Mana Burn v1.0 by Chopinski ---------------------- */
// Credits:
// Magtheridon96 - RegisterPlayerUnitEvent
// BETABABY - Icon
// Mythic - Mana Burn Special Effect
/* ----------------------------------- END ---------------------------------- */
/* ------------------------------ CONFIGURABLES ----------------------------- */
globals
// The raw code of the Mana Burn Ability
public constant integer ABILITY = 'A08D'
// The raw code of the Illidan unit in the editor
private constant integer ILLIDAN_ID = 'H00L'
// The raw code of the Dark Illidan unit in the editor
private constant integer DARK_ILLIDAN_ID = 'H00N'
// The GAIN_AT_LEVEL is greater than 0
// illidan will gain Mana Burn at this level
private constant integer GAIN_AT_LEVEL = 30
// The Mana Burn model
private constant string MODEL = "Abilities\\Spells\\Human\\Feedback\\SpellBreakerAttack.mdl"
// The Mana Burn bonus model
private constant string BONUS_MODEL = "ManaBurn.mdl"
// The Mana Burn attachment point
private constant string ATTACH_POINT = "origin"
endglobals
// The amount of mana burned in each attack
private function GetManaBurned takes integer level returns real
return 3000. + 0.*level
endfunction
/* --------------------------------- System --------------------------------- */
private struct ManaBurn extends array
static method onDamage takes nothing returns nothing
local integer level = GetUnitAbilityLevel(DamageI.source, ABILITY)
local real mana
local real burn
if DamageI.isEnemy and not DamageI.magicImmune and level > 0 and BlzGetUnitMaxMana(DamageI.target) > 0 then
set mana = GetUnitState(DamageI.target, UNIT_STATE_MANA)
set burn = GetManaBurned(level)
if burn > mana then
set burn = mana
endif
call AddUnitMana(DamageI.target, -burn)
if GetUnitManaPercent(DamageI.target) < 4000 then
call DestroyEffect(AddSpecialEffectTarget(BONUS_MODEL, DamageI.target, ATTACH_POINT))
call UnitDamageTarget(DamageI.source, DamageI.target, burn, false, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, null)
else
call DestroyEffect(AddSpecialEffectTarget(MODEL, DamageI.target, ATTACH_POINT))
call UnitDamageTarget(DamageI.source, DamageI.target, burn, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
endif
endif
endmethod
static method onLevelUp takes nothing returns nothing
local unit u = GetTriggerUnit()
if GAIN_AT_LEVEL > 0 then
if (GetUnitTypeId(u) == ILLIDAN_ID or GetUnitTypeId(u) == DARK_ILLIDAN_ID) and GetHeroLevel(u) == GAIN_AT_LEVEL then
call UnitAddAbility(u, ABILITY)
call UnitMakeAbilityPermanent(u, true, ABILITY)
endif
endif
set u = null
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_LEVEL, function thistype.onLevelUp)
call RegisterAttackDamageEvent(function thistype.onDamage)
endmethod
endstruct
endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
library Logarithm
globals
private constant integer ITERATIONS=20
endglobals
function Log takes real x returns real
local real min=-88.0
local real max= 88.0
local real mid
local integer i=ITERATIONS
loop
set mid=(min+max)/2
exitwhen(i<=0)
set i=i-1
if (Pow(bj_E,mid)>=x) then
set max=mid
else
set min=mid
endif
endloop
return mid
endfunction
function Logarithm takes real base, real x returns real
local real min=-88.0
local real max= 88.0
local real mid
local integer i=ITERATIONS
loop
set mid=(min+max)/2
exitwhen(i<=0)
set i=i-1
if (Pow(base,mid)>=x) then
set max=mid
else
set min=mid
endif
endloop
return mid
endfunction
endlibrary
//TESH.scrollpos=37
//TESH.alwaysfold=0
library ArmorUtils requires Logarithm, optional IntuitiveDamageSystem
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This is used to get the exact armor of a given unit. It deals damage to the
//* unit in order to determine armor, meaning that damage detection systems will
//* detect it. If your map has the Intuitive Damage Detection System (IDDS) in
//* it, it will use that system's internal ignored damage type for the check.
//* Using that removes the need to disable/enable any damage detection triggers
//* to avoid infinite loops.
//*
//* The attacktype constant, ATTACK_TYPE_USED, is by default set to use
//* ATTACK_TYPE_CHAOS. Chaos is used because it deals 100% to all types, so if
//* you change it in your map, make sure you update the constant to some type
//* with 100% damage to all armor types.
//*
//* This system can also be used as a means to detect if a unit is invulnerable
//* or not. If GetUnitArmor returns ARMOR_INVULNERABLE, then the unit is
//* invulnerable. The value for it is entirely random and will never be the
//* actual armor value for a unit, so there should be no conflicts.
//*
//* Damage reduction in WC3 due to negative armor is capped at -71%, which has
//* an equivalent armor value of -20. If you have a unit with less than -20
//* armor, this system will always return exactly -20.
//*
//* Default WC3 gameplay constants have the 'Armor Damage Reduction Multiplier'
//* set to 0.06. If you change this constant for your map, be sure to adjust the
//* ARMOR_REDUCTION_MULTIPLIER constant in this script to match.
//*
//* Example Usage:
//* local real armor = GetUnitArmor(MyUnit)
//*
//* Two other functions are included in this library. They are used for
//* calculating a unit's base damage knowing armor-factoring damage and a unit's
//* armor-factoring damage knowing its base damage. These functions do not
//* consider armor type in their calculations. If you want armor type damage
//* adjusting, it is recommended to trigger it.
//*
//* Example Usage:
//* local real basedmg = GetFullDamage(MyUnit, Armor)
//* local real armodmg = GetReducedDamage(MyUnit, Armor)
//*
//* An objectmerger call is included in this script to automatically generate
//* the bonus life ability if necessary. If you do not modify the 'AIlz' ability
//* in your map, you can replace the LIFE_BONUS_SPELL_ID constant with it and
//* not use the objectmerger call. The
//*
globals
//Values that should be changed for your map
private constant real ARMOR_REDUCTION_MULTIPLIER = 0.06
private constant integer LIFE_BONUS_SPELL_ID = 'A07R'
private constant attacktype ATTACK_TYPE_USED = ATTACK_TYPE_CHAOS
//Values that do not need to be changed
constant real ARMOR_INVULNERABLE = 917451.519
private constant real DAMAGE_TEST = 16.
private constant real DAMAGE_LIFE = 30.
private constant real NATLOG_094 =-0.061875
endglobals
////! external ObjectMerger w3a AIlz lif& anam "GetUnitArmorLifeBonus" ansf "" Ilif 1 30 aite 0
function GetUnitArmor takes unit u returns real
local real life = GetWidgetLife(u)
local real test = life
local real redc = 0.
local boolean enab = false
local trigger trig = GetTriggeringTrigger()
if u != null and life >= 0.405 then
if GetUnitState(u, UNIT_STATE_MAX_LIFE) <= DAMAGE_TEST then
//Add max life to keep it alive
call UnitAddAbility(u, LIFE_BONUS_SPELL_ID)
endif
if life <= DAMAGE_LIFE then
//If under the threshold, heal it for the moment
call SetWidgetLife(u, DAMAGE_LIFE)
set test = DAMAGE_LIFE
endif
static if LIBRARY_IntuitiveDamageSystem then
//Use the IGNORED type in the IDDS if present so it is 100% ignored
call UnitDamageTargetEx(u, u, DAMAGE_TEST, ATTACK_TYPE_USED, DAMAGE_TYPE_IGNORED, true)
else
if trig != null and IsTriggerEnabled(trig) then
//Disable the trigger to prevent it registering with damage detection systems
call DisableTrigger(trig)
set enab = true
endif
call UnitDamageTarget(u, u, DAMAGE_TEST, true, false, ATTACK_TYPE_USED, DAMAGE_TYPE_NORMAL, null)
if enab then
//Re-enable the trigger
call EnableTrigger(trig)
endif
endif
set redc = (DAMAGE_TEST-test+GetWidgetLife(u))/DAMAGE_TEST
//Remove the max life ability
call UnitRemoveAbility(u, LIFE_BONUS_SPELL_ID)
call SetWidgetLife(u, life)
set trig = null
if redc >= 1. then
//Invulnerable
return ARMOR_INVULNERABLE
elseif redc < 0. then
//Negative Armor
return -Log(redc+1.)/NATLOG_094
else
//Positive Armor
return redc/(ARMOR_REDUCTION_MULTIPLIER*(1.-redc))
endif
endif
set trig = null
return 0.
endfunction
function GetReducedDamage takes real baseDamage, real armor returns real
if armor >= 0. then
return baseDamage*(1.-((armor*ARMOR_REDUCTION_MULTIPLIER)/(1.+ARMOR_REDUCTION_MULTIPLIER*armor)))
else
return baseDamage*(2.-Pow(0.94,-armor))
endif
endfunction
function GetFullDamage takes real damage, real armor returns real
if armor >= 0. then
return damage/(1.-((armor*ARMOR_REDUCTION_MULTIPLIER)/(1.+ARMOR_REDUCTION_MULTIPLIER*armor)))
else
return damage/(2.-Pow(0.94,-armor))
endif
endfunction
endlibrary
//TESH.scrollpos=51
//TESH.alwaysfold=0
////////////////////////////////////////////////////////////////////
// Enhanced Critical Strike by 1)ark_NiTe //
// //
// i = Level of Enhanced Critical Strike //
// r = Extra Damage Dealt (Real Damage * Level of Ability) //
// //
////////////////////////////////////////////////////////////////////
//==================================================================================================================================
// Start of constants. These will need to be changed by you. They provide the user with easier implementing//changing of the spell's rawcodes and effects.
//==================================================================================================================================
constant function Crit_Strike_ID takes nothing returns integer
return 'A07S' // Enhanced Critical Strike ability rawcode.
endfunction
constant function Crit_Struck_ID takes nothing returns integer
return 'A07Q' // Critically Struck (Dummy) ability rawcode.
endfunction
constant function Dummy_Unit_ID takes nothing returns integer
return 'h00K' // Dummy Unit rawcode.
endfunction
//==================================================================================================================================
// End of constants. Do not touch anything below this unless you are familiar with JASS.
//==================================================================================================================================
function Enhanced_Critical_Strike_Conds takes nothing returns boolean
return GetOwningPlayer(GetTriggerUnit()) != GetOwningPlayer(GetEventDamageSource()) and GetUnitAbilityLevel(GetEventDamageSource(), 'A07S') >=1 and IsUnitInGroup(GetEventDamageSource(), udg_JustCastSpell) == false and GetRandomInt(1, 100) <= 15
endfunction
function Trig_Enhanced_Critical_Strike takes nothing returns nothing
// Declares Local Variables
local unit e
local unit d = GetEventDamageSource()
local unit t = GetTriggerUnit()
local force f = CreateForce()
local force g = CreateForce()
local texttag tag
local integer i = GetUnitAbilityLevel(d, Crit_Strike_ID())
local real dmg = ( GetEventDamage() * i )
local real a
local real fd
local real r
local trigger trig = GetTriggeringTrigger()
call DisableTrigger(trig)
set a = GetUnitArmor(t)
set fd = GetFullDamage(dmg, a)
set r = GetReducedDamage(fd, a)
call UnitDamageTarget(d, t, r, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
call EnableTrigger(trig)
// Creates Floating Text showing the Damage Dealt (dmg+r)
set tag = CreateTextTagUnitBJ( ( "|cffFF0000" + ( I2S(R2I(( ( r / i ) + r ))) + "|r" ) ), d, 0, 10, 100, 100, 100, 0 )
if (IsPlayerInForce(GetLocalPlayer(), bj_FORCE_ALL_PLAYERS))then
call SetTextTagVisibility(tag, false)
endif
call ForceEnumAllies(f, GetOwningPlayer(d), null)
call ForceEnumAllies(g, GetOwningPlayer(t), null)
if (IsPlayerInForce(GetLocalPlayer(), f)) or (IsPlayerInForce(GetLocalPlayer(), g)) then
call SetTextTagVisibility(tag, true)
endif
call SetTextTagVelocity(tag, 0, 0.04)
call SetTextTagPermanent(tag, false)
call SetTextTagFadepoint(tag, 2.00)
call SetTextTagLifespan(tag, 3.00)
if GetWidgetLife(t) > 0 then
set e = CreateUnit(GetOwningPlayer(d), Dummy_Unit_ID(), GetUnitX(t), GetUnitY(t), 0)
call UnitApplyTimedLife(e, 'BTLF', 1.00)
call UnitAddAbility(e, Crit_Struck_ID())
call IssueTargetOrder( e, "drunkenhaze", t )
else
// Does not create dummy unit because unit has died
endif
set tag = null
set trig = null
set f = null
set g = null
set e = null
set d = null
set t = null
endfunction
//===========================================================================
function InitTrig_Enhanced_Critical_Strike takes nothing returns nothing
set gg_trg_Enhanced_Critical_Strike = CreateTrigger( )
call TriggerAddCondition( gg_trg_Enhanced_Critical_Strike, Condition( function Enhanced_Critical_Strike_Conds ) )
call TriggerAddAction( gg_trg_Enhanced_Critical_Strike, function Trig_Enhanced_Critical_Strike )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Cast_Any_Spell_Conditions takes nothing returns boolean
return GetUnitTypeId(GetTriggerUnit()) != Dummy_Unit_ID() and GetOwningPlayer(GetTriggerUnit()) != Player(PLAYER_NEUTRAL_AGGRESSIVE)
endfunction
function Trig_Cast_Any_Spell_Actions takes nothing returns nothing
// This trigger helps to avoid damage detection triggers from executing
// automatically when a unit has any type of negative spell cast on them.
local unit t = GetTriggerUnit()
call GroupAddUnit( udg_JustCastSpell, t )
call TriggerSleepAction( 0.15 )
call GroupRemoveUnit( udg_JustCastSpell, t )
set t = null
endfunction
//===========================================================================
function InitTrig_Cast_Any_Spell takes nothing returns nothing
set gg_trg_Cast_Any_Spell = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Cast_Any_Spell, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_Cast_Any_Spell, Condition( function Trig_Cast_Any_Spell_Conditions ) )
call TriggerAddAction( gg_trg_Cast_Any_Spell, function Trig_Cast_Any_Spell_Actions )
endfunction
// CONFIGURATION
// Constants
// Main spell raw code
constant function LB_GetSpellID takes nothing returns integer
return 'A04E'
endfunction
// Stun raw code
constant function LB_GetStunID takes nothing returns integer
return 'A04D'
endfunction
// Spell book raw code
constant function LB_GetSpellBookID takes nothing returns integer
return 'A04F'
endfunction
// Caster dummy raw code
constant function LB_DummyCasterID takes nothing returns integer
return 'h00C'
endfunction
// Target effect
constant function LB_Sfx takes nothing returns string
return "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
endfunction
// Charge effect
constant function LB_ChargeSfx takes nothing returns string
return "Abilities\\Spells\\Orc\\Shockwave\\ShockwaveMissile.mdl"
endfunction
// Caster effect
constant function LB_Sfx1 takes nothing returns string
return "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
endfunction
// Attachment point
constant function LB_AttachPoint takes nothing returns string
return "origin"
endfunction
// Movement interval
constant function LB_Interval takes nothing returns real
return 0.03125
endfunction
// Booleans
// Should Smart Charge be enabled?
// True = enable Smart Charge, the Hero will choose another nearby valid unit to charge at
// if he is unable to reach the original target.
// False = disable Smart Charge
constant function LB_SmartCharge takes nothing returns boolean
return true
endfunction
// Filter valid units for Smart Charge
function LB_FilterUnit takes unit caster, unit u returns boolean
return IsUnitEnemy(u, GetOwningPlayer(caster)) and GetWidgetLife(u) > 0.405 and GetUnitAbilityLevel(u, 'Avul') == 0 and not IsUnitType (u, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType (u, UNIT_TYPE_STRUCTURE)
endfunction
// AoE check for Smart Charge
constant function LB_AoE takes nothing returns real
return 600.
endfunction
// Get Abilities Level
function LB_SpellLvl takes unit caster returns integer
return GetUnitAbilityLevel (caster, LB_GetSpellID()) // Get the spell level of the caster
endfunction
// Level dependable values
// Main target damage
function LB_DamageEnemy takes integer level returns real
return 0.
endfunction
// Self damage
function LB_DamageSelf takes integer level returns real
return 0.
endfunction
// Charge Speed
function LB_Speed takes integer level returns real
return (1000. + level*100.)/(1/LB_Interval())
endfunction
// Attack - Damage - Weapon type
// Attack type
function LB_AttackType takes nothing returns attacktype
return ATTACK_TYPE_HERO
endfunction
// Damage type
function LB_DamageType takes nothing returns damagetype
return DAMAGE_TYPE_MAGIC
endfunction
// Weapon type
function LB_WeaponType takes nothing returns weapontype
return WEAPON_TYPE_WHOKNOWS
endfunction
// END CONFIGURATION
// PRELOAD
function LB_Preload takes nothing returns nothing
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), LB_DummyCasterID(), 0, 0, 0.00)
local integer i = 0
call UnitAddAbility(u, LB_GetSpellID())
call UnitAddAbility(u, LB_GetStunID())
call UnitAddAbility(u, LB_GetSpellBookID())
loop
call SetPlayerAbilityAvailable(Player(i), LB_GetSpellBookID(), false)
set i = i + 1
exitwhen i > 15
endloop
call RemoveUnit (u)
set u = null
endfunction
// END PRELOAD
// PATHING CHECK
function LB_HideItems takes nothing returns nothing
if IsItemVisible (GetEnumItem()) then
set udg_LB_ItemArray[udg_LB_Integer] = GetEnumItem()
call SetItemVisible (udg_LB_ItemArray[udg_LB_Integer], false)
set udg_LB_Integer = udg_LB_Integer + 1
endif
endfunction
function LB_IsPathable takes real x, real y, item i returns boolean
local real x1
local real y1
call MoveRectTo (LoadRectHandle (udg_LB_Hashtable, -3, -1), x, y)
call EnumItemsInRect (LoadRectHandle (udg_LB_Hashtable, -3, -1), null, function LB_HideItems)
call SetItemPosition (i, x, y)
set x1 = GetItemX(i) - x
set y1 = GetItemY(i) - y
call SetItemVisible(i, false)
loop
exitwhen udg_LB_Integer <= 0
set udg_LB_Integer = udg_LB_Integer - 1
call SetItemVisible (udg_LB_ItemArray[udg_LB_Integer], true)
set udg_LB_ItemArray[udg_LB_Integer] = null
endloop
return x1*x1 + y1*y1 < 256 and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
// END PATHING CHECK
// SPELL FUNCTIONS
// Here we create a list of units that can be picked from
function LB_MakeList takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = LoadInteger(udg_LB_Hashtable, -4, 0)
local integer ID = LoadInteger(udg_LB_Hashtable, -4, -3)
local boolean b = LB_FilterUnit(LoadUnitHandle(udg_LB_Hashtable, -4, -2), u) and u != LoadUnitHandle(udg_LB_Hashtable, -4, -1) and not IsUnitInGroup(u, LoadGroupHandle(udg_LB_Hashtable, ID, 8))
if b then
set i = i + 1
call SaveUnitHandle(udg_LB_Hashtable, -4, i, u)
endif
call SaveInteger(udg_LB_Hashtable, -4, 0, i)
set u = null
return false
endfunction
// The loop where we index, deindex, and recycle everything
function LB_TimerLoop takes nothing returns nothing
local integer ID
local integer listmax = LoadInteger(udg_LB_Hashtable, -1, 0 )
local unit caster
local unit target
local unit dummy
local integer i = 0
local real x1
local real y1
local real x2
local real y2
local real x3
local real y3
local real x4
local real y4
local real angle
local integer level
local group g
local integer i1
local boolean b
local group g1
// Now we start looping through all the spell instances
loop
exitwhen i >= listmax
// First we load the saved values of a spell instance
set i = i + 1
set ID = LoadInteger (udg_LB_Hashtable, -1, i)
set caster = LoadUnitHandle (udg_LB_Hashtable, ID, 1)
set target = LoadUnitHandle (udg_LB_Hashtable, ID, 2)
set dummy = LoadUnitHandle (udg_LB_Hashtable, ID, 4)
set level = LB_SpellLvl (caster)
set x1 = GetUnitX (caster)
set y1 = GetUnitY (caster)
set x2 = GetUnitX (target)
set y2 = GetUnitY (target)
set g1 = LoadGroupHandle (udg_LB_Hashtable, ID, 8)
if (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) < 22500 then
call UnitRemoveAbility(caster, LB_GetSpellBookID())
call SetUnitTurnSpeed(caster, GetUnitDefaultTurnSpeed(caster))
call DestroyEffect(AddSpecialEffectTarget(LB_Sfx1(), caster, LB_AttachPoint()))
call DestroyEffect(AddSpecialEffectTarget(LB_Sfx(), target, LB_AttachPoint()))
call DestroyEffect(LoadEffectHandle(udg_LB_Hashtable, ID, 3))
call SetUnitState(caster, UNIT_STATE_LIFE, GetUnitState(caster, UNIT_STATE_LIFE)*(1 - (LB_DamageSelf(level)/100)))
call UnitDamageTarget(caster, target, GetUnitState(target, UNIT_STATE_LIFE)*(LB_DamageEnemy(level)/100), false, false, LB_AttackType(), LB_DamageType(), LB_WeaponType())
call IssueTargetOrder(caster, "attack", target)
call UnitAddAbility(dummy, LB_GetStunID())
call SetUnitX(dummy, x2)
call SetUnitY(dummy, y2)
call IssueTargetOrder(dummy, "thunderbolt", target)
call KillUnit(dummy)
call DestroyGroup (g1)
if listmax != i then
call SaveInteger (udg_LB_Hashtable, LoadInteger (udg_LB_Hashtable, -1, listmax), 0, i)
call SaveInteger (udg_LB_Hashtable, -1, i, LoadInteger (udg_LB_Hashtable, -1, listmax))
call RemoveSavedInteger (udg_LB_Hashtable, -1, listmax)
set i = i - 1
endif
set listmax = listmax - 1
call SaveInteger (udg_LB_Hashtable, -1, 0, listmax)
if LoadInteger (udg_LB_Hashtable, 0, 0) + 1 == LoadInteger (udg_LB_Hashtable, 0, -1) then
call FlushChildHashtable (udg_LB_Hashtable, 0)
call PauseTimer (LoadTimerHandle (udg_LB_Hashtable, -2, 0))
else
call SaveInteger (udg_LB_Hashtable, 0, 0, LoadInteger (udg_LB_Hashtable, 0, 0) + 1)
call SaveInteger (udg_LB_Hashtable, 0, LoadInteger (udg_LB_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable (udg_LB_Hashtable, ID)
else
set b = LoadBoolean (udg_LB_Hashtable, ID, 7)
if b then
call UnitRemoveAbility(caster, LB_GetSpellBookID())
call SetUnitTurnSpeed(caster, GetUnitDefaultTurnSpeed(caster))
call KillUnit(dummy)
call DestroyEffect(LoadEffectHandle(udg_LB_Hashtable, ID, 3))
call DestroyGroup (g1)
if listmax != i then
call SaveInteger (udg_LB_Hashtable, LoadInteger (udg_LB_Hashtable, -1, listmax), 0, i)
call SaveInteger (udg_LB_Hashtable, -1, i, LoadInteger (udg_LB_Hashtable, -1, listmax))
call RemoveSavedInteger (udg_LB_Hashtable, -1, listmax)
set i = i - 1
endif
set listmax = listmax - 1
call SaveInteger (udg_LB_Hashtable, -1, 0, listmax)
if LoadInteger (udg_LB_Hashtable, 0, 0) + 1 == LoadInteger (udg_LB_Hashtable, 0, -1) then
call FlushChildHashtable (udg_LB_Hashtable, 0)
call PauseTimer (LoadTimerHandle (udg_LB_Hashtable, -2, 0))
else
call SaveInteger (udg_LB_Hashtable, 0, 0, LoadInteger(udg_LB_Hashtable, 0, 0) + 1)
call SaveInteger (udg_LB_Hashtable, 0, LoadInteger(udg_LB_Hashtable, 0, 0), ID)
endif
call FlushChildHashtable (udg_LB_Hashtable, ID)
else
if IsUnitType(caster, UNIT_TYPE_DEAD) then
call SaveBoolean (udg_LB_Hashtable, ID, 7, true)
else
set angle = Atan2(y2-y1, x2-x1)
set x3 = LoadReal (udg_LB_Hashtable, ID, 5)
set y3 = LoadReal (udg_LB_Hashtable, ID, 6)
set x4 = x1 + (LB_Speed(level))*Cos(angle)
set y4 = y1 + (LB_Speed(level))*Sin(angle)
call SaveReal (udg_LB_Hashtable, ID, 5, x2)
call SaveReal (udg_LB_Hashtable, ID, 6, y2)
if IsUnitType(target, UNIT_TYPE_DEAD) or (x3-x2)*(x3-x2) + (y3-y2)*(y3-y2) > 1960000 or not LB_IsPathable(x4, y4, LoadItemHandle (udg_LB_Hashtable, -3, 0)) then
if LB_SmartCharge() then
call GroupAddUnit (g1, target)
set g = CreateGroup()
call SaveUnitHandle(udg_LB_Hashtable, -4, -1, target)
call SaveUnitHandle(udg_LB_Hashtable, -4, -2, caster)
call SaveInteger(udg_LB_Hashtable, -4, -3, ID)
call GroupEnumUnitsInRange(g, x1, y1, LB_AoE(), Condition(function LB_MakeList))
call DestroyGroup(g)
set g = null
set i1 = LoadInteger(udg_LB_Hashtable, -4, 0)
if i1 == 0 then
call SaveBoolean (udg_LB_Hashtable, ID, 7, true)
else
call SaveUnitHandle(udg_LB_Hashtable, ID, 2, LoadUnitHandle(udg_LB_Hashtable, -4, GetRandomInt(1, i1)))
call FlushChildHashtable(udg_LB_Hashtable, -4)
endif
else
call SaveBoolean (udg_LB_Hashtable, ID, 7, true)
endif
else
// Move the caster
call SetUnitX (caster, x4)
call SetUnitY (caster, y4)
call SetUnitFacing (caster, angle*bj_RADTODEG)
endif
endif
endif
endif
set dummy = null
set caster = null
set target = null
set g1 = null
endloop
endfunction
// Initiate function
function Trig_LB_Conditions takes nothing returns boolean
local unit caster
local unit target
local integer ID
local integer listmax
if GetSpellAbilityId() == LB_GetSpellID() then
set caster = GetTriggerUnit()
call UnitAddAbility(caster, LB_GetSpellBookID())
call SetUnitTurnSpeed (caster, 0)
set target = GetSpellTargetUnit()
set ID = LoadInteger (udg_LB_Hashtable, 0, 0)
// Now we start indexing the main projectiles
if ID>0 then
call SaveInteger (udg_LB_Hashtable, 0, 0, ID - 1)
set ID = LoadInteger (udg_LB_Hashtable, 0, ID)
else
set ID = LoadInteger (udg_LB_Hashtable, 0, -1) + 1
call SaveInteger (udg_LB_Hashtable,0 , -1, ID)
if ID == 1 then
call TimerStart (LoadTimerHandle (udg_LB_Hashtable, -2, 0), LB_Interval(), true, function LB_TimerLoop)
endif
endif
// Save the values related to this spell instance
call SaveUnitHandle (udg_LB_Hashtable, ID, 1, caster)
call SaveUnitHandle (udg_LB_Hashtable, ID, 2, target)
call SaveEffectHandle (udg_LB_Hashtable, ID, 3, AddSpecialEffectTarget(LB_ChargeSfx(), caster, LB_AttachPoint()))
call SaveUnitHandle (udg_LB_Hashtable, ID, 4, CreateUnit(GetOwningPlayer(caster), LB_DummyCasterID(), GetUnitX(caster), GetUnitY(caster), 0.00))
call SaveReal (udg_LB_Hashtable, ID, 5, GetUnitX(target))
call SaveReal (udg_LB_Hashtable, ID, 6, GetUnitY(target))
call SaveGroupHandle (udg_LB_Hashtable, ID, 8, CreateGroup())
set listmax = LoadInteger (udg_LB_Hashtable, -1, 0) + 1
call SaveInteger(udg_LB_Hashtable, -1, 0, listmax )
call SaveInteger (udg_LB_Hashtable, -1, listmax, ID)
call SaveInteger (udg_LB_Hashtable, ID, 0, listmax)
set caster = null
set target = null
endif
return false
endfunction
//===========================================================================
function InitTrig_Life_Break takes nothing returns nothing
local trigger t = CreateTrigger()
set udg_LB_Hashtable = InitHashtable()
call LB_Preload()
call SaveTimerHandle (udg_LB_Hashtable, -2, 0, CreateTimer())
call SaveItemHandle (udg_LB_Hashtable, -3, 0, CreateItem('afac', 0, 0))
call SaveRectHandle (udg_LB_Hashtable, -3, -1, Rect (0, 0, 128, 128))
call SetItemVisible (LoadItemHandle (udg_LB_Hashtable, -3, 0), false)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition( function Trig_LB_Conditions ) )
set t = null
endfunction