Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AOEDamageEvent | real | No | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CheckDeathInList | boolean | Yes | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
ClearDamageEvent | trigger | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageModifierEvent | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypeReduced | integer | No | |
DeathEvent | real | No | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
EnhancedDamageTarget | unit | No | |
heal_amount | real | No | |
heal_check | boolean | No | |
HEAL_CHECK_INTERVAL | real | No | |
heal_count | integer | No | |
heal_diff | real | No | |
heal_exitwhen | integer | No | |
heal_indexRef | integervar | Yes | |
heal_indices | integervar | Yes | |
heal_inSys | boolean | Yes | |
heal_integer | integervar | No | |
heal_lastLife | real | Yes | |
heal_life | real | No | |
heal_regen | real | Yes | |
heal_source | unit | No | |
heal_target | unit | No | |
HEAL_THRESHOLD | real | No | |
heal_timer | timer | No | |
HealEvent | real | No | |
HideDamageFrom | boolean | Yes | |
IsDamageSpell | boolean | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
KillerOfUnit | unit | Yes | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
NextHealAmount | real | No | |
NextHealSource | unit | No | |
NextHealTarget | unit | No | |
regen_buildup | real | Yes | |
REGEN_EVENT_INTERVAL | real | No | |
REGEN_STRENGTH_VALUE | real | No | |
REGEN_THRESHOLD | real | No | |
regen_timeleft | real | Yes | |
SpellDamageAbility | abilcode | No | |
SummonerOfUnit | unit | Yes | |
TempInteger | integer | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes | |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
WorldMaxX | real | No | |
WorldMaxY | real | No |
library OrderIds //v1.0 hiveworkshop.com/threads/orders-repo.290882/
globals
constant integer ORDER_OFFSET=851970 // follow rally point
constant integer ORDER_wandillusion=852274
constant integer ORDER_absorb=852529
constant integer ORDER_acidbomb=852662
constant integer ORDER_acolyteharvest=852185
constant integer ORDER_AImove=851988
constant integer ORDER_ambush=852131
constant integer ORDER_ancestralspirit=852490
constant integer ORDER_ancestralspirittarget=852491
constant integer ORDER_animatedead=852217
constant integer ORDER_antimagicshell=852186
constant integer ORDER_attack=851983
constant integer ORDER_attackground=851984
constant integer ORDER_attackonce=851985
constant integer ORDER_attributemodskill=852576
constant integer ORDER_auraunholy=852215
constant integer ORDER_auravampiric=852216
constant integer ORDER_autodispel=852132
constant integer ORDER_autodispeloff=852134
constant integer ORDER_autodispelon=852133
constant integer ORDER_autoentangle=852505
constant integer ORDER_autoentangleinstant=852506
constant integer ORDER_autoharvestgold=852021
constant integer ORDER_autoharvestlumber=852022
constant integer ORDER_avatar=852086
constant integer ORDER_avengerform=852531
constant integer ORDER_awaken=852466
constant integer ORDER_banish=852486
constant integer ORDER_barkskin=852135
constant integer ORDER_barkskinoff=852137
constant integer ORDER_barkskinon=852136
constant integer ORDER_battleroar=852599
constant integer ORDER_battlestations=852099
constant integer ORDER_bearform=852138
constant integer ORDER_berserk=852100
constant integer ORDER_blackarrow=852577
constant integer ORDER_blackarrowoff=852579
constant integer ORDER_blackarrowon=852578
constant integer ORDER_blight=852187
constant integer ORDER_blink=852525
constant integer ORDER_blizzard=852089
constant integer ORDER_bloodlust=852101
constant integer ORDER_bloodlustoff=852103
constant integer ORDER_bloodluston=852102
constant integer ORDER_board=852043
constant integer ORDER_breathoffire=852580
constant integer ORDER_breathoffrost=852560
constant integer ORDER_build=851994
constant integer ORDER_burrow=852533
constant integer ORDER_cannibalize=852188
constant integer ORDER_carrionscarabs=852551
constant integer ORDER_carrionscarabsinstant=852554
constant integer ORDER_carrionscarabsoff=852553
constant integer ORDER_carrionscarabson=852552
constant integer ORDER_carrionswarm=852218
constant integer ORDER_chainlightning=852119
constant integer ORDER_channel=852600
constant integer ORDER_charm=852581
constant integer ORDER_chemicalrage=852663
constant integer ORDER_cloudoffog=852473
constant integer ORDER_clusterrockets=852652
constant integer ORDER_coldarrows=852244
constant integer ORDER_coldarrowstarg=852243
constant integer ORDER_controlmagic=852474
constant integer ORDER_corporealform=852493
constant integer ORDER_corrosivebreath=852140
constant integer ORDER_coupleinstant=852508
constant integer ORDER_coupletarget=852507
constant integer ORDER_creepanimatedead=852246
constant integer ORDER_creepdevour=852247
constant integer ORDER_creepheal=852248
constant integer ORDER_creephealoff=852250
constant integer ORDER_creephealon=852249
constant integer ORDER_creepthunderbolt=852252
constant integer ORDER_creepthunderclap=852253
constant integer ORDER_cripple=852189
constant integer ORDER_curse=852190
constant integer ORDER_curseoff=852192
constant integer ORDER_curseon=852191
constant integer ORDER_cyclone=852144
constant integer ORDER_darkconversion=852228
constant integer ORDER_darkportal=852229
constant integer ORDER_darkritual=852219
constant integer ORDER_darksummoning=852220
constant integer ORDER_deathanddecay=852221
constant integer ORDER_deathcoil=852222
constant integer ORDER_deathpact=852223
constant integer ORDER_decouple=852509
constant integer ORDER_defend=852055
constant integer ORDER_detectaoe=852015
constant integer ORDER_detonate=852145
constant integer ORDER_devour=852104
constant integer ORDER_devourmagic=852536
constant integer ORDER_disassociate=852240
constant integer ORDER_disenchant=852495
constant integer ORDER_dismount=852470
constant integer ORDER_dispel=852057
constant integer ORDER_divineshield=852090
constant integer ORDER_doom=852583
constant integer ORDER_drain=852487
constant integer ORDER_dreadlordinferno=852224
constant integer ORDER_dropitem=852001
constant integer ORDER_drunkenhaze=852585
constant integer ORDER_earthquake=852121
constant integer ORDER_eattree=852146
constant integer ORDER_elementalfury=852586
constant integer ORDER_ensnare=852106
constant integer ORDER_ensnareoff=852108
constant integer ORDER_ensnareon=852107
constant integer ORDER_entangle=852147
constant integer ORDER_entangleinstant=852148
constant integer ORDER_entanglingroots=852171
constant integer ORDER_etherealform=852496
constant integer ORDER_evileye=852105
constant integer ORDER_faeriefire=852149
constant integer ORDER_faeriefireoff=852151
constant integer ORDER_faeriefireon=852150
constant integer ORDER_fanofknives=852526
constant integer ORDER_farsight=852122
constant integer ORDER_fingerofdeath=852230
constant integer ORDER_firebolt=852231
constant integer ORDER_flamestrike=852488
constant integer ORDER_flamingarrows=852174
constant integer ORDER_flamingarrowstarg=852173
constant integer ORDER_flamingattack=852540
constant integer ORDER_flamingattacktarg=852539
constant integer ORDER_flare=852060
constant integer ORDER_forceboard=852044
constant integer ORDER_forceofnature=852176
constant integer ORDER_forkedlightning=852586
constant integer ORDER_freezingbreath=852195
constant integer ORDER_frenzy=852561
constant integer ORDER_frenzyoff=852563
constant integer ORDER_frenzyon=852562
constant integer ORDER_frostarmor=852225
constant integer ORDER_frostarmoroff=852459
constant integer ORDER_frostarmoron=852458
constant integer ORDER_frostnova=852226
constant integer ORDER_getitem=851981
constant integer ORDER_gold2lumber=852233
constant integer ORDER_grabtree=852511
constant integer ORDER_harvest=852018
constant integer ORDER_heal=852063
constant integer ORDER_healingspray=852664
constant integer ORDER_healingward=852109
constant integer ORDER_healingwave=852501
constant integer ORDER_healoff=852065
constant integer ORDER_healon=852064
constant integer ORDER_hex=852502
constant integer ORDER_holdposition=851993
constant integer ORDER_holybolt=852092
constant integer ORDER_howlofterror=852588
constant integer ORDER_humanbuild=851995
constant integer ORDER_immolation=852177
constant integer ORDER_impale=852555
constant integer ORDER_incineratearrow=852670
constant integer ORDER_incineratearrowoff=852672
constant integer ORDER_incineratearrowon=852671
constant integer ORDER_inferno=852232
constant integer ORDER_innerfire=852066
constant integer ORDER_innerfireoff=852068
constant integer ORDER_innerfireon=852067
constant integer ORDER_instant=852200
constant integer ORDER_invisibility=852069
constant integer ORDER_lavamonster=852667
constant integer ORDER_lightningshield=852110
constant integer ORDER_load=852046
constant integer ORDER_loadarcher = 852142
constant integer ORDER_loadcorpse=852050
constant integer ORDER_loadcorpseinstant=852053
constant integer ORDER_locustswarm=852556
constant integer ORDER_lumber2gold=852234
constant integer ORDER_magicdefense=852478
constant integer ORDER_magicleash=852480
constant integer ORDER_magicundefense=852479
constant integer ORDER_manaburn=852179
constant integer ORDER_manaflareoff=852513
constant integer ORDER_manaflareon=852512
constant integer ORDER_manashieldoff=852590
constant integer ORDER_manashieldon=852589
constant integer ORDER_massteleport=852093
constant integer ORDER_mechanicalcritter=852564
constant integer ORDER_metamorphosis=852180
constant integer ORDER_militia=852072
constant integer ORDER_militiaconvert=852071
constant integer ORDER_militiaoff=852073
constant integer ORDER_militiaunconvert=852651
constant integer ORDER_mindrot=852565
constant integer ORDER_mirrorimage=852123
constant integer ORDER_monsoon=852591
constant integer ORDER_mount=852469
constant integer ORDER_mounthippogryph=852143
constant integer ORDER_move=851986
constant integer ORDER_nagabuild=852467
constant integer ORDER_neutraldetectaoe=852023
constant integer ORDER_neutralinteract=852566
constant integer ORDER_neutralspell=852630
constant integer ORDER_nightelfbuild=851997
constant integer ORDER_orcbuild=851996
constant integer ORDER_parasite=852601
constant integer ORDER_parasiteoff=852603
constant integer ORDER_parasiteon=852602
constant integer ORDER_patrol=851990
constant integer ORDER_phaseshift=852514
constant integer ORDER_phaseshiftinstant=852517
constant integer ORDER_phaseshiftoff=852516
constant integer ORDER_phaseshifton=852515
constant integer ORDER_phoenixfire=852481
constant integer ORDER_phoenixmorph=852482
constant integer ORDER_poisonarrows=852255
constant integer ORDER_poisonarrowstarg=852254
constant integer ORDER_polymorph=852074
constant integer ORDER_possession=852196
constant integer ORDER_preservation=852568
constant integer ORDER_purge=852111
constant integer ORDER_rainofchaos=852237
constant integer ORDER_rainoffire=852238
constant integer ORDER_raisedead=852197
constant integer ORDER_raisedeadoff=852199
constant integer ORDER_raisedeadon=852198
constant integer ORDER_ravenform=852155
constant integer ORDER_recharge=852157
constant integer ORDER_rechargeoff=852159
constant integer ORDER_rechargeon=852158
constant integer ORDER_rejuvination=852160
constant integer ORDER_renew=852161
constant integer ORDER_renewoff=852163
constant integer ORDER_renewon=852162
constant integer ORDER_repair=852024
constant integer ORDER_repairoff=852026
constant integer ORDER_repairon=852025
constant integer ORDER_replenish=852542
constant integer ORDER_replenishlife=852545
constant integer ORDER_replenishlifeoff=852547
constant integer ORDER_replenishlifeon=852546
constant integer ORDER_replenishmana=852548
constant integer ORDER_replenishmanaoff=852550
constant integer ORDER_replenishmanaon=852549
constant integer ORDER_replenishoff=852544
constant integer ORDER_replenishon=852543
constant integer ORDER_request_hero=852239
constant integer ORDER_requestsacrifice=852201
constant integer ORDER_restoration=852202
constant integer ORDER_restorationoff=852204
constant integer ORDER_restorationon=852203
constant integer ORDER_resumebuild=851999
constant integer ORDER_resumeharvesting=852017
constant integer ORDER_resurrection=852094
constant integer ORDER_returnresources=852020
constant integer ORDER_revenge=852241
constant integer ORDER_revive=852039
constant integer ORDER_roar=852164
constant integer ORDER_robogoblin=852656
constant integer ORDER_root=852165
constant integer ORDER_sacrifice=852205
constant integer ORDER_sanctuary=852569
constant integer ORDER_scout=852181
constant integer ORDER_selfdestruct=852040
constant integer ORDER_selfdestructoff=852042
constant integer ORDER_selfdestructon=852041
constant integer ORDER_sentinel=852182
constant integer ORDER_setrally=851980
constant integer ORDER_shadowsight=852570
constant integer ORDER_shadowstrike=852527
constant integer ORDER_shockwave=852125
constant integer ORDER_silence=852592
constant integer ORDER_sleep=852227
constant integer ORDER_slow=852075
constant integer ORDER_slowoff=852077
constant integer ORDER_slowon=852076
constant integer ORDER_smart=851971
constant integer ORDER_soulburn=852668
constant integer ORDER_soulpreservation=852242
constant integer ORDER_spellshield=852571
constant integer ORDER_spellshieldaoe=852572
constant integer ORDER_spellsteal=852483
constant integer ORDER_spellstealoff=852485
constant integer ORDER_spellstealon=852484
constant integer ORDER_spies=852235
constant integer ORDER_spiritlink=852499
constant integer ORDER_spiritofvengeance=852528
constant integer ORDER_spirittroll=852573
constant integer ORDER_spiritwolf=852126
constant integer ORDER_stampede=852593
constant integer ORDER_standdown=852113
constant integer ORDER_starfall=852183
constant integer ORDER_stasistrap=852114
constant integer ORDER_steal=852574
constant integer ORDER_stomp=852127
constant integer ORDER_stoneform=852206
constant integer ORDER_stop=851972
constant integer ORDER_submerge=852604
constant integer ORDER_summonfactory=852658
constant integer ORDER_summongrizzly=852594
constant integer ORDER_summonphoenix=852489
constant integer ORDER_summonquillbeast=852595
constant integer ORDER_summonwareagle=852596
constant integer ORDER_tankdroppilot=852079
constant integer ORDER_tankloadpilot=852080
constant integer ORDER_tankpilot=852081
constant integer ORDER_taunt=852520
constant integer ORDER_thunderbolt=852095
constant integer ORDER_thunderclap=852096
constant integer ORDER_tornado=852597
constant integer ORDER_townbelloff=852083
constant integer ORDER_townbellon=852082
constant integer ORDER_tranquility=852184
constant integer ORDER_transmute=852665
constant integer ORDER_unavatar=852087
constant integer ORDER_unavengerform=852532
constant integer ORDER_unbearform=852139
constant integer ORDER_unburrow=852534
constant integer ORDER_uncoldarrows=852245
constant integer ORDER_uncorporealform=852494
constant integer ORDER_undeadbuild=851998
constant integer ORDER_undefend=852056
constant integer ORDER_undivineshield=852091
constant integer ORDER_unetherealform=852497
constant integer ORDER_unflamingarrows=852175
constant integer ORDER_unflamingattack=852541
constant integer ORDER_unholyfrenzy=852209
constant integer ORDER_unimmolation=852178
constant integer ORDER_unload=852047
constant integer ORDER_unloadall=852048
constant integer ORDER_unloadallcorpses=852054
constant integer ORDER_unloadallinstant=852049
constant integer ORDER_unpoisonarrows=852256
constant integer ORDER_unravenform=852156
constant integer ORDER_unrobogoblin=852657
constant integer ORDER_unroot=852166
constant integer ORDER_unstableconcoction=852500
constant integer ORDER_unstoneform=852207
constant integer ORDER_unsubmerge=852605
constant integer ORDER_unsummon=852210
constant integer ORDER_unwindwalk=852130
constant integer ORDER_vengeance=852521
constant integer ORDER_vengeanceinstant=852524
constant integer ORDER_vengeanceoff=852523
constant integer ORDER_vengeanceon=852522
constant integer ORDER_volcano=852669
constant integer ORDER_voodoo=852503
constant integer ORDER_ward=852504
constant integer ORDER_waterelemental=852097
constant integer ORDER_wateryminion=852598
constant integer ORDER_web=852211
constant integer ORDER_weboff=852213
constant integer ORDER_webon=852212
constant integer ORDER_whirlwind=852128
constant integer ORDER_windwalk=852129
constant integer ORDER_wispharvest=852214
constant integer ORDER_buildtiny=852619
constant integer ORDER_scrollofspeed=852285
constant integer ORDER_scrollofregeneration=852609
constant integer ORDER_scrollofhealandmana=852282
constant integer ORDER_cancel=851976
constant integer ORDER_moveslot1=852002
constant integer ORDER_moveslot2=852003
constant integer ORDER_moveslot3=852004
constant integer ORDER_moveslot4=852005
constant integer ORDER_moveslot5=852006
constant integer ORDER_moveslot6=852007
constant integer ORDER_useslot1=852008
constant integer ORDER_useslot2=852009
constant integer ORDER_useslot3=852010
constant integer ORDER_useslot4=852011
constant integer ORDER_useslot5=852012
constant integer ORDER_useslot6=852013
constant integer ORDER_skillmenu=852000
constant integer ORDER_stunned=851973
constant integer ORDER_instant1=851991 //patrol sub-order
constant integer ORDER_instant2=851987 //?
constant integer ORDER_instant3=851975 //?
constant integer ORDER_instant4=852019 //?
endglobals
endlibrary
//TESH.scrollpos=0
//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
library MultidimensionalArray /* v1.2b
*/uses /*
*/Table /* http://www.hiveworkshop.com/threads/snippet-new-table.188084/
[Resource Link] - http://www.hiveworkshop.com/threads/snippet-multidimensional-array.289785/
*///! novjass
/* This snippet allows you to have array storage. Unlike default arrays, you can use an index beyond 8190
since this snippet uses Tables which uses hashtable. Therefore, saving data with a really large index
such as using GetHandleId(handle) as the index would not be a problem.
But you may ask, why would we want to use this when there is already the Table library which can support
any type that we want to store? First, this has a feature which supports multidimensions that allows you
to have up to 5-dimensional array. Table already allows you to create multidimensional storage if you do
proper nesting but this library removes the need for users to do it on their own and this also helps set
a standard instead of having redundant scripts that work for the same puspose. Secondly, unlike Table,
this implements a type specific storage i.e., you can create an array storage that is only exclusive for
a specific type but of course, this also provides a generic storage like Table but with a nicer API.
Furthermore, this includes some safety precautions such as compile time safety which throws an error if
you're using an incorrect number of dimensions, as well as preventing the use of an Array instance which
isn't allocated in DEBUG_MODE. lastly, this gives users a nice and intuitive syntax which resembles that
of the original vanilla Jass arrays (call KillUnit(u[1][3])) without having a need for an ugly keyword
at the end (ex: call KillUnit(u[1].unit[3])). */
|=========|
| Credits |
|=========|
/* AGD : Author
Bribe : For the Table library, and for the algorithm of making n-dimensional storage by nesting Tables */
|-----|
| API |
|-----|
Creating an Array:
/* Creates a new array for a specific type */
local Unit1D u = Array.create()
local Unit3D u3 = Unit3D.create()
local Unit5D u5 = Timer4D.create() //You could actually use any of the dimensional array creator
local Array4D a4 = Array.create()
Storing inside an Array:
/* Stores data inside an array */
set u[GetHandleId(timer)] = GetTriggerUnit()
set u3[0x2000]['AAAA'] = GetTriggerUnit() //Syntax error: number of indexes does not match with the number of dimensions
set u5[1][2][3][4][5] = GetTriggerUnit()
set a4[1][2][3][4].unit = GetTriggerUnit()
Retrieving from an Array:
/* Retrieves data from an array */
call KillUnit(u[1234567])
call KillUnit(u3['A']['B']['C'])
call KillUnit(u5[1][2][3][4]) //Syntax error: number of indexes does not match with the number of dimensions
call KillUnit(a4[1][2][3][4].unit)
Checking storage vacancy:
/* Checks if there is data stored inside an array index */
return u.has(index) //Similar to Table(u).unit.has(index)
return u3[1][2].has(3)
return u5[1].has(2) //Checks if the fourth dimension has index 2
return a4[1][2][3].hasHandle(4)
Removing an Array index:
/* Destroys the table instance of an index and clears all its child nodes if there are any */
call u.remove(1)
call u3[1].remove(2)
call u5[1][2][3][4][5].remove(6) //Syntax error: cannot use remove() on a node which has no children
call a4[1][2][3].removeHandle(4)
Flushing an Array Index:
/* Flushes all child nodes attached to the specific index */
call u.flush() //Flushes all data inside the array, analogous to flushing a parent hashtable
call u3[1][2][3].flush() //Syntax error: cannot clear a node which has no children, use u3[1][2].remove(3) instead
call u5[1][2].flush() //Flushes all child nodes attached to the index "2" of the second dimension
call a4[1][2].flush()
Destroying an Array:
/* Destroys an array instance, flushing all data inside it */
call u.destroy()
call u3.destroy()
call u5[1].destroy() //If destroy() is called upon a node which is not a root node, it will work like clear() instead
call a4.destroy()
//! endnovjass
static if DEBUG_MODE then
private struct S extends array
static key allocated
endstruct
private function Debug takes string msg returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|CFFFFCC00[MultidimensionalArray] |R" + msg)
endfunction
endif
private function AllocateIndex takes integer this, integer index returns integer
debug if not Table(S.allocated).boolean[this] then
debug return 0
debug endif
debug set Table(S.allocated).boolean[HashTable(this)[index]] = true
return HashTable(this)[index]
endfunction
/*============= For a uniform allocator syntax =) ==============*/
struct Array extends array
static method create takes nothing returns thistype
static if DEBUG_MODE then
local Table t = Table.create()
set Table(S.allocated).boolean[t] = true
return t
else
return Table.create()
endif
endmethod
endstruct
/*==============================================================*/
/*====================== Struct methods ========================*/
private module Methods
static method create takes nothing returns thistype
return Array.create()
endmethod
static if not thistype.remove.exists then
method remove takes integer index returns nothing
call HashTable(this).remove(index)
endmethod
endif
static if not thistype.has.exists then
method has takes integer index returns boolean
return HashTable(this).has(index)
endmethod
endif
method flush takes nothing returns nothing
call Table(this).flush()
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
debug set Table(S.allocated).boolean[this] = false
endmethod
endmodule
/*==============================================================*/
/*================= Generic Type Array Storage =================*/
private struct Type extends array
static key index
method operator agent= takes agent value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFF0000[Operator agent= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).agent[Table(this)[index]] = value
endmethod
//! textmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS takes TYPE
method operator $TYPE$ takes nothing returns $TYPE$
return Table(this).$TYPE$[Table(this)[index]]
endmethod
method operator $TYPE$= takes $TYPE$ value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFF0000[Operator $TYPE$= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).$TYPE$[Table(this)[index]] = value
endmethod
//! endtextmacro
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("integer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("real")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("string")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("boolean")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("player")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("widget")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("destructable")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("item")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("unit")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("ability")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("timer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("trigger")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("triggercondition")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("triggeraction")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("event")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("force")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("group")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("location")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("rect")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("boolexpr")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("sound")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("effect")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("unitpool")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("itempool")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("quest")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("questitem")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("defeatcondition")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("timerdialog")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("leaderboard")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("multiboard")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("multiboarditem")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("trackable")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("dialog")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("button")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("texttag")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("lightning")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("image")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("ubersplat")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("region")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("fogstate")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("fogmodifier")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("hashtable")
endstruct
struct Array1D extends array
//! textmacro GENERIC_DIMENSIONAL_ARRAY_METHODS takes NAME, TYPE
method has$NAME$ takes integer index returns boolean
return Table(this).$TYPE$.has(index)
endmethod
method remove$NAME$ takes integer index returns nothing
call Table(this).$TYPE$.remove(index)
endmethod
//! endtextmacro
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Integer", "integer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Real", "real")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("String", "string")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Boolean", "boolean")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Handle", "handle")
method has takes integer index returns boolean
return .hasInteger(index) /*
*/or .hasReal(index) /*
*/or .hasString(index) /*
*/or .hasBoolean(index) /*
*/or .hasHandle(index)
endmethod
method remove takes integer index returns nothing
call .removeInteger(index)
call .removeReal(index)
call .removeString(index)
call .removeBoolean(index)
call .removeHandle(index)
endmethod
implement Methods
method operator [] takes integer index returns Type
debug if not Table(S.allocated).boolean[this] then
debug return 0
debug endif
set Table(this)[Type.index] = index
return this
endmethod
endstruct
//! textmacro NEW_DIMENSIONAL_ARRAY_STRUCT takes DIM, RETURNED
struct Array$DIM$D extends array
implement Methods
method operator [] takes integer index returns Array$RETURNED$D
return AllocateIndex(this, index)
endmethod
endstruct
//! endtextmacro
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("2", "1")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("3", "2")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("4", "3")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("5", "4")
// If you want to increase the maximum number of available
// dimensions, just run the textmacros above once again like:
// runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("LAST_MAX_DIM + 1", "LAST_MAX_DIM")
/*==============================================================*/
/*================ Type Specific Array Storage =================*/
//! textmacro NEW_DIMENSIONAL_ARRAY takes NAME, TYPE
struct $NAME$1D extends array
method remove takes integer index returns nothing
call Table(this).$TYPE$.remove(index)
endmethod
method has takes integer index returns boolean
return Table(this).$TYPE$.has(index)
endmethod
implement Methods
method operator [] takes integer index returns $TYPE$
return Table(this).$TYPE$[index]
endmethod
method operator []= takes integer index, $TYPE$ value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFFCC00[ArrayType: $NAME$]|R |CFFFF0000[Operator []= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).$TYPE$[index] = value
endmethod
endstruct
struct $NAME$2D extends array
implement Methods
method operator [] takes integer index returns $NAME$1D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$3D extends array
implement Methods
method operator [] takes integer index returns $NAME$2D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$4D extends array
implement Methods
method operator [] takes integer index returns $NAME$3D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$5D extends array
implement Methods
method operator [] takes integer index returns $NAME$4D
return AllocateIndex(this, index)
endmethod
endstruct
//! endtextmacro
// If you want to increase the maximum number of available
// dimensions, just copy the last struct above and increase the
// number of dimension in the struct name and the returned struct
// of the operator [] by 1.
/*==============================================================*/
/*======== Implement textmacros for every storage type =========*/
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Integer", "integer")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Real", "real")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Str", "string")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Boolean", "boolean")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Player", "player")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Widget", "widget")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Item", "item")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Unit", "unit")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Ability", "ability")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Timer", "timer")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Force", "force")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Group", "group")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Location", "location")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Rect", "rect")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Sound", "sound")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Effect", "effect")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Quest", "quest")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Button", "button")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Image", "image")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Region", "region")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Hashtable", "hashtable")
/*==============================================================*/
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Missiles requires WorldBounds, optional DummyRecycler
/* ----------------------- Missiles v1.5 by Chopinski ----------------------- */
// Thanks and Full Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits to Vexorian for the dummy model.
// How to Import:
// 1 -First copy the Missile dummy unit into your map and then import the dummy.mdx
// model, setting the missile dummy model path to imported dummy.mdx model.
// Dummy model: https://www.hiveworkshop.com/threads/vexorians-dummy-model.149230/
// WorldBounds: https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
// DummyRecycler: https://www.hiveworkshop.com/threads/dummy-recycler-v1-25.277659/ (Highly Recommended)
// 2 - Copy this library into your map and set the
// DUMMY_RAW_CODE to the raw code of the missile dummy (ctrl + d) and you
// are done
// 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 unit dummy -> the dummy unit
// 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
// (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 raw code of the dummy unit. Match it with the
// Object Editor id
private constant integer DUMMY_RAW_CODE = 'dum0'
// 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.
// uint size used in z collision
private constant real BODY_SIZE = 64.
// How long takes for the missile to be removed.
// This is necessary so the death animation of the
// effect can play through
private constant real RECYCLE_TIME = 2.
// 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 methods 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
function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
//Little snippet to remove the dummy unit after a delay
//I'm not using MissileRecycler because of a leaking problem i discover
//This will only be used if you dont have DummyRecycler
private struct Timed
timer t
unit u
static method onPeriod takes nothing returns nothing
local thistype this = LoadInteger(table, GetHandleId(GetExpiredTimer()), 0)
call FlushChildHashtable(table, GetHandleId(.t))
call PauseTimer(.t)
call DestroyTimer(.t)
call RemoveUnit(.u)
set .u = null
set .t = null
call .deallocate()
endmethod
static method Recycle takes unit u, real timeout returns nothing
local thistype this = thistype.allocate()
set .u = u
set .t = CreateTimer()
call SaveInteger(table, GetHandleId(.t), 0, this)
call TimerStart(.t, timeout, false, function thistype.onPeriod)
endmethod
endstruct
//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)*bj_RADTODEG
// 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 thistype array missiles
private static integer didx = -1
private static integer last = 0
private static real dilation
private static timer t = CreateTimer()
private static group hitGroup = CreateGroup()
private static thistype temp = 0
//-------------------------------------------------------
private real cA // current angle
private effect sfx // effect
private string fxpath // model
private real size // scale
private real height // Arcs
private real open // Curves
private real toZ // Necessary to fix a bug for homming missiles
//-------------------------------------------------------
Coordinates impact
Coordinates origin
//-------------------------------------------------------
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 unit dummy
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 path returns nothing
call DestroyEffect(sfx)
set fxpath = path
set sfx = AddSpecialEffectTarget(path, dummy, "origin")
endmethod
method operator model takes nothing returns string
return fxpath
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)
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)
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real v returns nothing
call SetUnitScale(dummy, v, v, v)
set size = v
endmethod
method operator scale takes nothing returns real
return 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
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
/* ------------------------- Destructable hit 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 sfx = null
set dummy = null
set fxpath = ""
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 size = 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 DestroyEffect(sfx)
call origin.destroy()
call impact.destroy()
static if LIBRARY_DummyRecycler then
call DummyAddRecycleTimer(dummy, RECYCLE_TIME)
else
call Timed.Recycle(dummy, RECYCLE_TIME)
endif
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 locZ
local real yaw
local real pitch
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 = BODY_SIZE
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 x = prevX + vel*Cos(cA)
set y = prevY + vel*Sin(cA)
set s = travel + vel
set prevX = x
set prevY = y
set prevZ = z
set travel = s
set veloc = veloc + acceleration
set pitch = origin.alpha
set yaw = cA*bj_RADTODEG
// 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))*bj_RADTODEG
call SetUnitFlyHeight(dummy, z - GetLocZ(x, y), 0)
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = cA + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = (cA + Atan(-((4*c)*(2*s - d))/(d*d)))*bj_RADTODEG
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 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
endif
// missile orientation and positioning
call SetUnitAnimationByIndex(dummy, R2I(pitch + 90.5))
call SetUnitFacing(dummy, yaw)
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
else
// 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()
local real angle = Atan2(toY - y, toX - x)*bj_RADTODEG
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .allocated = true
set .x = x
set .y = y
set .z = z
set .prevX = x
set .prevY = y
set .prevZ = z
set .toZ = toZ
static if LIBRARY_DummyRecycler then
set .dummy = GetRecycledDummy(x, y, z - GetLocZ(x, y), angle)
else
set .dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_RAW_CODE, x, y, angle)
call SetUnitFlyHeight(.dummy, z - GetLocZ(x, y), 0)
endif
call Coordinates.link(origin, impact)
set .cA = origin.angle
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyRecycler /*
// DummyRecycler v1.24
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment units or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and it will always leave a permanent tiny bit of memory (0.04 KB).
// Furthermore, if a Damage Detection System (DDS) or a Unit Indexer is
// imported to your map, using this system will even result to better
// performance because DDS and Unit Indexer process new created units.
//
//
*/ requires /*
nothing
*/ optional Table/*
if not found, this system will use a hashtable. Hashtables are limited to
255 per map.
*/ optional WorldBounds /*
if not found, this system will initialize its own Map Boundaries.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */
globals
//The rawcode of the Dummy Unit
private constant integer DUMMY_ID = 'dum0'
//The owner of the Dummy Unit
private constant player OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
private constant integer ANGLES_COUNT = 10
//The number of Dummy units per ANGLES_COUNT. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
private constant integer STORED_UNIT_COUNT = 3
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
private constant integer MAX_DUMMY_COUNT = 100
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum STORED_UNIT_COUNT
private constant integer BORROW_REQUEST = 5
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle.
private constant real ANGLE_TOLERANCE = 10.0
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
private constant boolean HIDE_ON_MAP_CORNER = true
endglobals
//Every time a new dummy unit is retrieved, it will apply this resets
//If it is redundant/you dont need it, remove it.
//! textmacro DUMMY_UNIT_RESET
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
//! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
globals
private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
private real array angle
private integer array count
private integer array countHead
private integer array countNext
private integer array countPrev
private integer array next
private integer array prev
private unit array dummy
private integer upper
private integer lower
private integer lastInstance
private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
endglobals
static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
private module BoundsInit
readonly static real x
readonly static real y
private static method onInit takes nothing returns nothing
local rect map = GetWorldBounds()
set thistype.x = GetRectMaxX(map)
set thistype.y = GetRectMaxY(map)
call RemoveRect(map)
set map = null
endmethod
endmodule
private struct Bounds extends array
implement BoundsInit
endstruct
endif
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable hash = InitHashtable()
endif
private static method onInit takes nothing returns nothing
local real add = 360.0/ANGLES_COUNT
local real a = 0
local integer this = ANGLES_COUNT
local integer head = 0
local integer cHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
set upper = STORED_UNIT_COUNT
set lower = STORED_UNIT_COUNT
static if LIBRARY_Table then
set tb = Table.create()
endif
//Initialize countHeads
loop
exitwhen i < 0
set countNext[cHead] = cHead
set countPrev[cHead] = cHead
set countHead[i] = cHead
set cHead = cHead - 1
set i = i - 1
endloop
set cHead = countHead[STORED_UNIT_COUNT] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen a >= 360
//Initialize head
set next[head] = head
set prev[head] = head
set count[head] = STORED_UNIT_COUNT
set angle[head] = a
//Insert head in the Count List
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
set i = 0
loop
exitwhen i >= STORED_UNIT_COUNT
//Queued Linked List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
endif
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
endif
call PauseUnit(dummy[this], true)
static if LIBRARY_Table then
set tb[GetHandleId(dummy[this])] = this
else
call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
endif
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set a = a + add
endloop
set lastInstance = this
endmethod
endmodule
private struct S extends array
implement M
endstruct
private function GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*ANGLES_COUNT/360.0))
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = GetHead(R2I(facing + FACING_OFFSET))
local integer this = next[head]
local integer cHead
//If there are Dummy Units in the Queue List already facing close to the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
//Remove from the Queue List
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//For double free protection
set next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = dummy[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
//! runtextmacro DUMMY_UNIT_RESET()
//Update Count and Bounds
set count[head] = count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
set count[head] = count[head] + 1
set this = next[countNext[countHead[upper]]]
call SetUnitFacing(dummy[this], angle[head])
//Remove
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//Add to the Current List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
set head = countNext[countHead[upper]]
set count[head] = count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[upper]
if countNext[cHead] == cHead then
set upper = upper - 1
endif
if count[head] < lower then
set lower = count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if dummyCount < MAX_DUMMY_COUNT then
set this = lastInstance
//For double free protection
set next[this] = -1
set dummy[this] = bj_lastCreatedUnit
static if LIBRARY_Table then
set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
else
call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
endif
set lastInstance = lastInstance + 1
endif
set dummyCount = dummyCount + 1
endif
return bj_lastCreatedUnit
endfunction
function RecycleDummy takes unit u returns nothing
static if LIBRARY_Table then
local integer this = S.tb[GetHandleId(u)]
else
local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
endif
local integer head
local integer cHead
//If the unit is a legit Dummy Unit
if this > 0 and next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = countNext[countHead[lower]]
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
//Update Status
call SetUnitFacing(u, angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, OWNER, false)
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
call SetUnitX(u, WorldBounds.maxX)
call SetUnitY(u, WorldBounds.maxY)
else
call SetUnitX(u, Bounds.x)
call SetUnitY(u, Bounds.y)
endif
else
call SetUnitScale(u, 0, 0, 0)
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set count[head] = count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[lower]
if countNext[cHead] == cHead then
set lower = lower + 1
endif
if count[head] > upper then
set upper = count[head]
endif
elseif this == 0 then
call RemoveUnit(u)
debug elseif next[this] != -1 then
debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
endif
endfunction
private function Expires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
static if LIBRARY_Table then
call RecycleDummy(S.tb.unit[id])
call S.tb.unit.remove(id)
else
call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
call FlushChildHashtable(S.hash, id)
endif
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
static if LIBRARY_Table then
set S.tb.unit[GetHandleId(t)] = u
else
call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
endif
call TimerStart(t, time, false, function Expires)
set t = null
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS()
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RemoveAbilityTimed uses ListT optional DummyRecycler
//! novjass
// API
call RemoveAbilityTimed.create(whichUnit, whichAbility, whichTimeout, recycle)
whichAbility will be removed from whichUnit after whichTimeout. If recycle is true, the unit
will be recycled if you have DummyRecycler. Otherwise it will be killed.
//! endnovjass
private module RemoveAbilityTimedModule
private static constant real TIMEOUT = .1
private static constant integer DUMMY_ID = 'dum0'
private static IntegerList List
private static timer Clock = CreateTimer()
private unit u
private integer abil
private boolean recycle
private real time
private method destroy takes nothing returns nothing
set this.u = null
set this.abil = 0
set this.recycle = false
set this.time = 0.
call this.deallocate()
endmethod
private static method update takes nothing returns nothing
local IntegerListItem node = List.first
local IntegerListItem nodeNext
local thistype this
loop
exitwhen node == 0
set nodeNext = node.next
set this = node.data
set this.time = this.time - TIMEOUT
if this.time <= 0. then
call UnitRemoveAbility(this.u, this.abil)
static if LIBRARY_DummyRecycler then
if this.recycle then
if GetUnitTypeId(this.u) == DUMMY_ID then
call RecycleDummy(this.u)
else
call UnitApplyTimedLife(this.u, 'BTLF', .01)
endif
endif
else
if this.recycle then
call UnitApplyTimedLife(this.u, 'BTLF', .01)
endif
endif
call List.erase(node)
call this.destroy()
endif
set node = nodeNext
endloop
if List.empty() then
call PauseTimer(Clock)
endif
endmethod
static method create takes unit u, integer abil, real time, boolean recycle returns thistype
local thistype this = allocate()
set this.u = u
set this.abil = abil
set this.recycle = recycle
set this.time = time
if List.empty() then
call TimerStart(Clock, TIMEOUT, true, function thistype.update)
endif
call List.push(this)
return this
endmethod
private static method onInit takes nothing returns nothing
set List = IntegerList.create()
endmethod
endmodule
struct RemoveAbilityTimed
implement RemoveAbilityTimedModule
endstruct
endlibrary
//TESH.scrollpos=0
//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 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
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* List<T> v2.1.2.0
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
*
*****************************************************************************/
library ListT requires Table, Alloc
// Run here any global list types you want to be defined.
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
endstruct
//! endtextmacro
//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// No default ctor and dctor due to limited array size
method operator data takes nothing returns $TYPE$
return Table(this).$TYPE$[0] // hashtable[ node, 0 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set Table(this).$TYPE$[0] = value
endmethod
method operator next takes nothing returns thistype
return Table(this)[1] // hashtable[ node, 1 ] = next
endmethod
method operator next= takes thistype value returns nothing
set Table(this)[1] = value
endmethod
method operator prev takes nothing returns thistype
return Table(this)[-1] // hashtable[ node, -1 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set Table(this)[-1] = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set Table(node)[2] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return Table(node)[2]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = Table.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call Table(node).destroy() // also removes ownership
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::pop failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = first
set first = first.next
if first == 0 then
set last = 0
else
set first.prev = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::shift failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
static method operator [] takes thistype other returns thistype
local thistype instance = create()
local $NAME$Item node = other.first
loop
exitwhen node == 0
call instance.push(node.data)
set node = node.next
endloop
return instance
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = first
loop
exitwhen node == 0 or node.data == value
set node = node.next
endloop
return node
endmethod
method erase takes $NAME$Item node returns boolean
if getNodeOwner(node) == this then // match ownership
if node == first then
call shift()
elseif node == last then
call pop()
else
set node.prev.next = node.next
set node.next.prev = node.prev
call deleteNode(node)
set count = count - 1
endif
return true
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::erase failed for instance "+I2S(this)+". Attempted to remove invalid node "+I2S(node)+".")
endif
return false
endmethod
method remove takes $NAME$Item node returns boolean
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"Method $NAME$::remove is obsolete, use $NAME$::erase instead.")
return erase(node)
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item node = find(value)
if node != 0 then
call remove(node)
endif
return this
endmethod
endstruct
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Alloc /* v1.3.1.1
*************************************************************************************
*
* */ uses /*
*
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
* */ optional MemoryAnalysis /*
*
*************************************************************************************
*
* Minimizes code generation and global variables while maintaining
* excellent performance.
*
* local thistype this = recycler[0]
*
* if (recycler[this] == 0) then
* set recycler[0] = this + 1
* else
* set recycler[0] = recycler[this]
* endif
*
************************************************************************************
*
* module Alloc
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* The Following Require Error Message To Be In The Map
* --------------------------------------------------------
*
* debug readonly boolean allocated
*
* The Following Require Memory Analysis To Be In The Map
* --------------------------------------------------------
*
* debug readonly integer monitorCount
* - the amount of global memory being monitored by this
* debug readonly integer monitorString
* - gets a string representation of all global memory being monitored by this
* debug readonly integer address
* - global memory address for debugging
* - used with monitor and stopMonitor
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
* debug method monitor takes string label, integer address returns nothing
* - monitor a global memory address with a label
* - used to identify memory leaks
* - should be memory that ought to be destroyed by the time this is destroyed
* debug method stopMonitor takes integer address returns nothing
* - stops monitoring global memory
* debug method stopMonitorValue takes handle monitoredHandle returns nothing
* - stops monitoring handle values
*
* The Following Are Used To Monitor Handle Values
*
* debug method monitor_widget takes string label, widget handleToTrack returns nothing
* debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
* debug method monitor_item takes string label, item handleToTrack returns nothing
* debug method monitor_unit takes string label, unit handleToTrack returns nothing
* debug method monitor_timer takes string label, timer handleToTrack returns nothing
* debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
* debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
* debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
* debug method monitor_force takes string label, force handleToTrack returns nothing
* debug method monitor_group takes string label, group handleToTrack returns nothing
* debug method monitor_location takes string label, location handleToTrack returns nothing
* debug method monitor_rect takes string label, rect handleToTrack returns nothing
* debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
* debug method monitor_effect takes string label, effect handleToTrack returns nothing
* debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
* debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
* debug method monitor_quest takes string label, quest handleToTrack returns nothing
* debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
* debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
* debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
* debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
* debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
* debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
* debug method monitor_button takes string label, button handleToTrack returns nothing
* debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
* debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
* debug method monitor_image takes string label, image handleToTrack returns nothing
* debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
* debug method monitor_region takes string label, region handleToTrack returns nothing
* debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
* debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
*
*
* Thanks to Ruke for the algorithm
************************************************************************************/
module Alloc
/*
* stack
*/
private static integer array recycler
static if LIBRARY_MemoryAnalysis then
debug private MemoryMonitor globalAddress
debug method operator address takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress
debug endmethod
endif
/*
* allocation
*/
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
endif
if (recycler[this] == 0) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
static if LIBRARY_ErrorMessage then
debug set recycler[this] = -1
endif
static if LIBRARY_MemoryAnalysis then
debug set globalAddress = MemoryMonitor.allocate("thistype")
endif
return this
endmethod
method deallocate takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
endif
static if LIBRARY_MemoryAnalysis then
debug call globalAddress.deallocate()
debug set globalAddress = 0
endif
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static if LIBRARY_MemoryAnalysis then
debug method monitor takes string label, integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor(label, address)
debug endmethod
debug method stopMonitor takes integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitor(address)
debug endmethod
debug method stopMonitorValue takes handle monitoredHandle returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitorValue(monitoredHandle)
debug endmethod
debug method operator monitorCount takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorCount
debug endmethod
debug method operator monitorString takes nothing returns string
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorString
debug endmethod
debug method monitor_widget takes string label, widget handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_widget(label, handleToTrack)
debug endmethod
debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_destructable(label, handleToTrack)
debug endmethod
debug method monitor_item takes string label, item handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_item(label, handleToTrack)
debug endmethod
debug method monitor_unit takes string label, unit handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unit(label, handleToTrack)
debug endmethod
debug method monitor_timer takes string label, timer handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timer(label, handleToTrack)
debug endmethod
debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_trigger(label, handleToTrack)
debug endmethod
debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggercondition(label, handleToTrack)
debug endmethod
debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggeraction(label, handleToTrack)
debug endmethod
debug method monitor_force takes string label, force handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_force(label, handleToTrack)
debug endmethod
debug method monitor_group takes string label, group handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_group(label, handleToTrack)
debug endmethod
debug method monitor_location takes string label, location handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_location(label, handleToTrack)
debug endmethod
debug method monitor_rect takes string label, rect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_rect(label, handleToTrack)
debug endmethod
debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_boolexpr(label, handleToTrack)
debug endmethod
debug method monitor_effect takes string label, effect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_effect(label, handleToTrack)
debug endmethod
debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unitpool(label, handleToTrack)
debug endmethod
debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_itempool(label, handleToTrack)
debug endmethod
debug method monitor_quest takes string label, quest handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_quest(label, handleToTrack)
debug endmethod
debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
debug endmethod
debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timerdialog(label, handleToTrack)
debug endmethod
debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_leaderboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
debug endmethod
debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_dialog(label, handleToTrack)
debug endmethod
debug method monitor_button takes string label, button handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_button(label, handleToTrack)
debug endmethod
debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_texttag(label, handleToTrack)
debug endmethod
debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_lightning(label, handleToTrack)
debug endmethod
debug method monitor_image takes string label, image handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_image(label, handleToTrack)
debug endmethod
debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_ubersplat(label, handleToTrack)
debug endmethod
debug method monitor_region takes string label, region handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_region(label, handleToTrack)
debug endmethod
debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
debug endmethod
debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_hashtable(label, handleToTrack)
debug endmethod
static if DEBUG_MODE then
//! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
static method calculateMemoryUsage takes nothing returns integer
return calculateAllocatedMemory__recycler()
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
return allocatedMemoryString__recycler()
endmethod
endif
endif
/*
* analysis
*/
static if LIBRARY_ErrorMessage then
debug method operator allocated takes nothing returns boolean
debug return recycler[this] == -1
debug endmethod
endif
/*
* initialization
*/
private static method onInit takes nothing returns nothing
set recycler[0] = 1
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.2
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerEvent v1.0.2.2
* by Bannar
*
* Register version of TriggerRegisterPlayerEvent.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerEventTrigger takes playerevent whichEvent returns trigger
* Retrieves trigger handle for playerevent whichEvent.
*
* function GetPlayerEventTrigger takes player whichPlayer, playerevent whichEvent returns trigger
* Retrieves trigger handle for playerevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerEvent takes playerevent whichEvent, code func returns nothing
* Registers generic playerevent whichEvent adding code func as callback.
*
* function RegisterPlayerEvent takes player whichPlayer, playerevent whichEvent, code func returns nothing
* Registers playerevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerEvent requires RegisterNativeEvent
function GetAnyPlayerEventTrigger takes playerevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerEventTrigger takes player whichPlayer, playerevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerEvent takes playerevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerEvent(t, Player(index), whichEvent)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerEvent takes player whichPlayer, playerevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// OrderEvent by Bribe, special thanks to Nestharus and Azlier, version 3.0.1.1
//
// API
// ---
// RegisterOrderEvent(integer orderId, code eventFunc)
// RegisterAnyOrderEvent(code eventFunc) //Runs for point/target/instant for any order
//
// Requires
// --------
// RegisterPlayerUnitEvent: http://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
// Table: http://www.hiveworkshop.com/forums/showthread.php?t=188084
//
library OrderEvent requires RegisterPlayerUnitEvent, Table
globals
private Table t = 0
endglobals
//============================================================================
function RegisterAnyOrderEvent takes code c returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, c)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, c)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, c)
endfunction
//============================================================================
private function OnOrder takes nothing returns nothing
call TriggerEvaluate(t.trigger[GetIssuedOrderId()])
endfunction
//============================================================================
function RegisterOrderEvent takes integer orderId, code c returns nothing
local trigger trig
if integer(t) == 0 then
set t = Table.create()
call RegisterAnyOrderEvent(function OnOrder)
endif
set trig = t.trigger[orderId]
if trig == null then
set trig = CreateTrigger()
set t.trigger[orderId] = trig
endif
call TriggerAddCondition(trig, Filter(c))
set trig = null
endfunction
endlibrary
library BlizzardMessage /* v1.0.0.4
*************************************************************************************
*
* For creating Blizzard-like messages with a variety of sounds.
*
*************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
*
* The original SimError
*
*
* Maker
* -----------------------
*
* Sound label help
*
************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a timed Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* real dur
* - The duration of the message.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* SETTINGS
* -----------------------
*
*/
globals
constant real MSG_DURATION = 2.00 //Message Duration
/*************************************************************************************
*
* Sound Labels
*
*************************************************************************************/
constant integer ALLY_HERO_DIES_HUMAN = 0
constant integer ALLY_HERO_DIES_NAGA = 1
constant integer ALLY_HERO_DIES_NIGHTELF = 2
constant integer ALLY_HERO_DIES_ORC = 3
constant integer ALLY_HERO_DIES_UNDEAD = 4
constant integer ALLY_TOWN_UNDER_ATTACK_HUMAN = 5
constant integer ALLY_TOWN_UNDER_ATTACK_NAGA = 6
constant integer ALLY_TOWN_UNDER_ATTACK_NIGHTELF = 7
constant integer ALLY_TOWN_UNDER_ATTACK_ORC = 8
constant integer ALLY_TOWN_UNDER_ATTACK_UNDEAD = 9
constant integer ALLY_UNDER_ATTACK_HUMAN = 10
constant integer ALLY_UNDER_ATTACK_NAGA = 11
constant integer ALLY_UNDER_ATTACK_NIGHTELF = 12
constant integer ALLY_UNDER_ATTACK_ORC = 13
constant integer ALLY_UNDER_ATTACK_UNDEAD = 14
constant integer ARRANGED_TEAM_INVITATION = 15
constant integer AUTO_CAST_BUTTON_CLICK = 16
constant integer CANT_PLACE_HUMAN = 17
constant integer CANT_PLACE_NAGA = 18
constant integer CANT_PLACE_NIGHTELF = 19
constant integer CANT_PLACE_ORC = 20
constant integer CANT_PLACE_UNDEAD = 21
constant integer CANT_ROOT_NIGHTELF = 22
constant integer CHATROOM_TIMER_TICK = 23
constant integer CLAN_INVITATION = 24
constant integer CONSTRUCTING_BUILDING_DEFAULT = 25
constant integer CONSTRUCTING_BUILDING_NAGA = 26
constant integer CONSTRUCTING_BUILDING_NIGHTELF = 27
constant integer CONSTRUCTING_BUILDING_ORC = 28
constant integer CONSTRUCTING_BUILDING_UNDEAD = 29
constant integer CREEP_AGGRO = 30
constant integer ERROR_MESSAGE = 31
constant integer GAME_FOUND = 32
constant integer GLUE_SCREEN_CLICK = 33
constant integer GOLD_MINE_COLLAPSE_HUMAN = 34
constant integer GOLD_MINE_COLLAPSE_NAGA = 35
constant integer GOLD_MINE_COLLAPSE_NIGHTELF = 36
constant integer GOLD_MINE_COLLAPSE_ORC = 37
constant integer GOLD_MINE_COLLAPSE_UNDEAD = 38
constant integer GOLD_MINE_LOW_GENERIC = 39
constant integer GOLD_MINE_LOW_HUMAN = 40
constant integer GOLD_MINE_LOW_NAGA = 41
constant integer GOLD_MINE_LOW_NIGHTELF = 42
constant integer GOLD_MINE_LOW_ORC = 43
constant integer GOLD_MINE_LOW_UNDEAD = 44
constant integer GOOD_JOB = 45
constant integer HERO_DIES_GENERIC = 46
constant integer HERO_DIES_HUMAN = 47
constant integer HERO_DIES_NAGA = 48
constant integer HERO_DIES_NIGHTELF = 49
constant integer HERO_DIES_ORC = 50
constant integer HERO_DIES_UNDEAD = 51
constant integer HINT = 52
constant integer IN_GAME_CHAT_WHAT = 53
constant integer INTERFACE_CLICK = 54
constant integer INTERFACE_ERROR = 55
constant integer INVENTORY_FULL_HUMAN = 56
constant integer INVENTORY_FULL_NAGA = 57
constant integer INVENTORY_FULL_NIGHTELF = 58
constant integer INVENTORY_FULL_ORC = 59
constant integer INVENTORY_FULL_UNDEAD = 60
constant integer ITEM_DROP = 61
constant integer ITEM_GET = 62
constant integer ITEM_REWARD = 63
constant integer JOB_DONE_SOUND_HUMAN = 64
constant integer JOB_DONE_SOUND_NAGA = 65
constant integer JOB_DONE_SOUND_NIGHTELF = 66
constant integer JOB_DONE_SOUND_ORC = 67
constant integer JOB_DONE_SOUND_UNDEAD = 68
constant integer MAP_PING = 69
constant integer MENU_BUTTON_CLICK = 70
constant integer NEW_TOURNAMENT = 71
constant integer NO_FOOD_HUMAN = 72
constant integer NO_FOOD_NAGA = 73
constant integer NO_FOOD_NIGHTELF = 74
constant integer NO_FOOD_ORC = 75
constant integer NO_FOOD_UNDEAD = 76
constant integer NO_GOLD_GENERIC = 77
constant integer NO_GOLD_HUMAN = 78
constant integer NO_GOLD_NAGA = 79
constant integer NO_GOLD_NIGHTELF = 80
constant integer NO_GOLD_ORC = 81
constant integer NO_GOLD_UNDEAD = 82
constant integer NO_LUMBER_HUMAN = 83
constant integer NO_LUMBER_NAGA = 84
constant integer NO_LUMBER_NIGHTELF = 85
constant integer NO_LUMBER_ORC = 86
constant integer NO_LUMBER_UNDEAD = 87
constant integer NO_MANA_GENERIC = 88
constant integer NO_MANA_HUMAN = 89
constant integer NO_MANA_NAGA = 90
constant integer NO_MANA_NIGHTELF = 91
constant integer NO_MANA_ORC = 92
constant integer NO_MANA_UNDEAD = 93
constant integer OFF_BLIGHT_UNDEAD = 94
constant integer PAUSE_GAME = 95
constant integer PLACE_BUILDING_DEFAULT = 96
constant integer QUEST_COMPLETED = 97
constant integer QUEST_FAILED = 98
constant integer QUEST_LOG_MODIFIED = 99
constant integer QUEST_NEW = 100
constant integer QUEST_UPDATE = 101
constant integer RALLY_POINT_PLACE = 102
constant integer RESCUE = 103
constant integer RESEARCH_COMPLETE_GENERIC = 104
constant integer RESEARCH_COMPLETE_HUMAN = 105
constant integer RESEARCH_COMPLETE_NAGA = 106
constant integer RESEARCH_COMPLETE_NIGHTELF = 107
constant integer RESEARCH_COMPLETE_ORC = 108
constant integer RESEARCH_COMPLETE_UNDEAD = 109
constant integer SCORE_SCREEN_TAB_CLICK = 110
constant integer SECRET_FOUND = 111
constant integer SUB_GROUP_SELECTION_CHANGE = 112
constant integer TOWN_ATTACK_GENERIC = 113
constant integer TOWN_ATTACK_HUMAN = 114
constant integer TOWN_ATTACK_NAGA = 115
constant integer TOWN_ATTACK_NIGHTELF = 116
constant integer TOWN_ATTACK_ORC = 117
constant integer TOWN_ATTACK_UNDEAD = 118
constant integer UNDER_ATTACK_HUMAN = 119
constant integer UNDER_ATTACK_NAGA = 120
constant integer UNDER_ATTACK_NIGHTELF = 121
constant integer UNDER_ATTACK_ORC = 122
constant integer UNDER_ATTACK_UNDEAD = 123
constant integer UPGRADE_COMPLETE_GENERIC = 124
constant integer UPGRADE_COMPLETE_HUMAN = 125
constant integer UPGRADE_COMPLETE_NAGA = 126
constant integer UPGRADE_COMPLETE_NIGHTELF = 127
constant integer UPGRADE_COMPLETE_ORC = 128
constant integer UPGRADE_COMPLETE_UNDEAD = 129
constant integer UPKEEP_LEVEL = 130
constant integer WARNING = 131
constant integer WAYPOINT = 132
/* private */ string array SOUND_LABEL
endglobals
/*
************************************************************************************
*/
function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, MSG_DURATION, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, dur, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
private module BMInit
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct InitStruct extends array
private static method init takes nothing returns nothing
set SOUND_LABEL[0] = "AllyHeroDiesHuman"
set SOUND_LABEL[1] = "AllyHeroDiesNaga"
set SOUND_LABEL[2] = "AllyHeroDiesNightElf"
set SOUND_LABEL[3] = "AllyHeroDiesOrc"
set SOUND_LABEL[4] = "AllyHeroDiesUndead"
set SOUND_LABEL[5] = "AllyTownUnderAttackHuman"
set SOUND_LABEL[6] = "AllyTownUnderAttackNaga"
set SOUND_LABEL[7] = "AllyTownUnderAttackNightElf"
set SOUND_LABEL[8] = "AllyTownUnderAttackOrc"
set SOUND_LABEL[9] = "AllyTownUnderAttackUndead"
set SOUND_LABEL[10] = "AllyUnderAttackHuman"
set SOUND_LABEL[11] = "AllyUnderAttackNaga"
set SOUND_LABEL[12] = "AllyUnderAttackNightElf"
set SOUND_LABEL[13] = "AllyUnderAttackOrc"
set SOUND_LABEL[14] = "AllyUnderAttackUndead"
set SOUND_LABEL[15] = "ArrangedTeamInvitation"
set SOUND_LABEL[16] = "AutoCastButtonClick"
set SOUND_LABEL[17] = "CantPlaceHuman"
set SOUND_LABEL[18] = "CantPlaceNaga"
set SOUND_LABEL[19] = "CantPlaceNightElf"
set SOUND_LABEL[20] = "CantPlaceOrc"
set SOUND_LABEL[21] = "CantPlaceUndead"
set SOUND_LABEL[22] = "CantRootNightElf"
set SOUND_LABEL[23] = "ChatroomTimerTick"
set SOUND_LABEL[24] = "ClanInvitation"
set SOUND_LABEL[25] = "ConstructingBuildingDefault"
set SOUND_LABEL[26] = "ConstructingBuildingNaga"
set SOUND_LABEL[27] = "ConstructingBuildingNightElf"
set SOUND_LABEL[28] = "ConstructingBuildingOrc"
set SOUND_LABEL[29] = "ConstructingBuildingUndead"
set SOUND_LABEL[30] = "CreepAggro"
set SOUND_LABEL[31] = "ErrorMessage"
set SOUND_LABEL[32] = "GameFound"
set SOUND_LABEL[33] = "GlueScreenClick"
set SOUND_LABEL[34] = "GoldMineCollapseHuman"
set SOUND_LABEL[35] = "GoldMineCollapseNaga"
set SOUND_LABEL[36] = "GoldMineCollapseNightElf"
set SOUND_LABEL[37] = "GoldMineCollapseOrc"
set SOUND_LABEL[38] = "GoldMineCollapseUndead"
set SOUND_LABEL[39] = "GoldMineLowGeneric"
set SOUND_LABEL[40] = "GoldMineLowHuman"
set SOUND_LABEL[41] = "GoldMineLowNaga"
set SOUND_LABEL[42] = "GoldMineLowNightElf"
set SOUND_LABEL[43] = "GoldMineLowOrc"
set SOUND_LABEL[44] = "GoldMineLowUndead"
set SOUND_LABEL[45] = "GoodJob"
set SOUND_LABEL[46] = "HeroDiesGeneric"
set SOUND_LABEL[47] = "HeroDiesHuman"
set SOUND_LABEL[48] = "HeroDiesNaga"
set SOUND_LABEL[49] = "HeroDiesNightElf"
set SOUND_LABEL[50] = "HeroDiesOrc"
set SOUND_LABEL[51] = "HeroDiesUndead"
set SOUND_LABEL[52] = "Hint"
set SOUND_LABEL[53] = "InGameChatWhat"
set SOUND_LABEL[54] = "InterfaceClick"
set SOUND_LABEL[55] = "InterfaceError"
set SOUND_LABEL[56] = "InventoryFullHuman"
set SOUND_LABEL[57] = "InventoryFullNaga"
set SOUND_LABEL[58] = "InventoryFullNightElf"
set SOUND_LABEL[59] = "InventoryFullOrc"
set SOUND_LABEL[60] = "InventoryFullUndead"
set SOUND_LABEL[61] = "ItemDrop"
set SOUND_LABEL[62] = "ItemGet"
set SOUND_LABEL[63] = "ItemReward"
set SOUND_LABEL[64] = "JobDoneSoundHuman"
set SOUND_LABEL[65] = "JobDoneSoundNaga"
set SOUND_LABEL[66] = "JobDoneSoundNightElf"
set SOUND_LABEL[67] = "JobDoneSoundOrc"
set SOUND_LABEL[68] = "JobDoneSoundUndead"
set SOUND_LABEL[69] = "MapPing"
set SOUND_LABEL[70] = "MenuButtonClick"
set SOUND_LABEL[71] = "NewTournament"
set SOUND_LABEL[72] = "NoFoodHuman"
set SOUND_LABEL[73] = "NoFoodNaga"
set SOUND_LABEL[74] = "NoFoodNightElf"
set SOUND_LABEL[75] = "NoFoodOrc"
set SOUND_LABEL[76] = "NoFoodUndead"
set SOUND_LABEL[77] = "NoGoldGeneric"
set SOUND_LABEL[78] = "NoGoldHuman"
set SOUND_LABEL[79] = "NoGoldNaga"
set SOUND_LABEL[80] = "NoGoldNightElf"
set SOUND_LABEL[81] = "NoGoldOrc"
set SOUND_LABEL[82] = "NoGoldUndead"
set SOUND_LABEL[83] = "NoLumberHuman"
set SOUND_LABEL[84] = "NoLumberNaga"
set SOUND_LABEL[85] = "NoLumberNightElf"
set SOUND_LABEL[86] = "NoLumberOrc"
set SOUND_LABEL[87] = "NoLumberUndead"
set SOUND_LABEL[88] = "NoManaGeneric"
set SOUND_LABEL[89] = "NoManaHuman"
set SOUND_LABEL[90] = "NoManaNaga"
set SOUND_LABEL[91] = "NoManaNightElf"
set SOUND_LABEL[92] = "NoManaOrc"
set SOUND_LABEL[93] = "NoManaUndead"
set SOUND_LABEL[94] = "OffBlightUndead"
set SOUND_LABEL[95] = "PauseGame"
set SOUND_LABEL[96] = "PlaceBuildingDefault"
set SOUND_LABEL[97] = "QuestCompleted"
set SOUND_LABEL[98] = "QuestFailed"
set SOUND_LABEL[99] = "QuestLogModified"
set SOUND_LABEL[100] = "QuestNew"
set SOUND_LABEL[101] = "QuestUpdate"
set SOUND_LABEL[102] = "RallyPointPlace"
set SOUND_LABEL[103] = "Rescue"
set SOUND_LABEL[104] = "ResearchCompleteGeneric"
set SOUND_LABEL[105] = "ResearchCompleteHuman"
set SOUND_LABEL[106] = "ResearchCompleteNaga"
set SOUND_LABEL[107] = "ResearchCompleteNightElf"
set SOUND_LABEL[108] = "ResearchCompleteOrc"
set SOUND_LABEL[109] = "ResearchCompleteUndead"
set SOUND_LABEL[110] = "ScoreScreenTabClick"
set SOUND_LABEL[111] = "SecretFound"
set SOUND_LABEL[112] = "SubGroupSelectionChange"
set SOUND_LABEL[113] = "TownAttackGeneric"
set SOUND_LABEL[114] = "TownAttackHuman"
set SOUND_LABEL[115] = "TownAttackNaga"
set SOUND_LABEL[116] = "TownAttackNightElf"
set SOUND_LABEL[117] = "TownAttackOrc"
set SOUND_LABEL[118] = "TownAttackUndead"
set SOUND_LABEL[119] = "UnderAttackHuman"
set SOUND_LABEL[120] = "UnderAttackNaga"
set SOUND_LABEL[121] = "UnderAttackNightElf"
set SOUND_LABEL[122] = "UnderAttackOrc"
set SOUND_LABEL[123] = "UnderAttackUndead"
set SOUND_LABEL[124] = "UpgradeCompleteGeneric"
set SOUND_LABEL[125] = "UpgradeCompleteHuman"
set SOUND_LABEL[126] = "UpgradeCompleteNaga"
set SOUND_LABEL[127] = "UpgradeCompleteNightElf"
set SOUND_LABEL[128] = "UpgradeCompleteOrc"
set SOUND_LABEL[129] = "UpgradeCompleteUndead"
set SOUND_LABEL[130] = "UpkeepLevel"
set SOUND_LABEL[131] = "Warning"
set SOUND_LABEL[132] = "WayPoint"
endmethod
implement BMInit
endstruct
endlibrary
//TESH.scrollpos=0
//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 = false
private constant boolean USE_FLEXIBLE_OFFSET = true
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 ClearItems requires optional WorldBounds /* by Tirlititi */
/*
This library fixes the leak and the lag caused by unremoved items,
including powerups and manually-destroyed items.
The dead items are periodically removed. You can adjust the period by changing
the constant CLEANING_PERIOD. Note that items' death animations need some time
to play so adjust the DEATH_TIME delay accordingly.
If you don't know exactly what you are doing, you shouldn't change the life
of a dead item; the items are no longer usable after their death but
you can still change their life. If you set their life to more than 0.405,
they won't be properly cleaned. You should also remove items manually
if you kill them when they are carried by a unit.
*/
globals
// Interval between item-cleanups.
private constant real CLEANING_PERIOD = 15
// Time for the item's death animation, optimized for tomes and runes.
private constant real DEATH_TIME = 1.5
endglobals
globals
private keyword S
private integer N = 0
private code s_code
private boolexpr s_bool
private timer s_timer = CreateTimer()
private item array I
endglobals
private function DeleteItems takes nothing returns nothing
loop
set N = N - 1
call SetWidgetLife(I[N], 1)
call RemoveItem(I[N])
set I[N] = null
exitwhen (N == 0)
endloop
call TimerStart(s_timer, CLEANING_PERIOD - DEATH_TIME, true, s_code)
endfunction
private function CleanItems takes nothing returns boolean
if (GetWidgetLife(GetFilterItem()) < 0.405) then
set I[N] = GetFilterItem()
set N = N + 1
endif
return false
endfunction
private function SweepItems takes nothing returns nothing
static if (LIBRARY_WorldBounds) then
call EnumItemsInRect(WorldBounds.world, s_bool, null)
else
call EnumItemsInRect(S.world, s_bool, null)
endif
if (N > 0) then
call TimerStart(s_timer, DEATH_TIME, false, function DeleteItems)
endif
endfunction
private struct S extends array
static if (not LIBRARY_WorldBounds) then
static rect world
endif
static method onInit takes nothing returns nothing
static if (not LIBRARY_WorldBounds) then
set world = GetWorldBounds()
endif
set s_code = function SweepItems
set s_bool = Filter(function CleanItems)
call TimerStart(s_timer, CLEANING_PERIOD, true, s_code)
endmethod
endstruct
endlibrary
//TESH.scrollpos=30
//TESH.alwaysfold=0
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* GetClosestWidget v3.0.1.3
* by Bannar aka Spinnaker
*
* Allows finding closest widget with ease.
*
******************************************************************************
*
* Configurables:
*
* Choose which modules should or should not be implemented.
*
* constant boolean UNITS_MODULE
* constant boolean GROUP_MODULE
* constant boolean ITEMS_MODULE
* constant boolean DESTS_MODULE
*
* Define start and final distances for search iterations within generic GetClosest functions.
* If final value is reached, enumeration is performed on whole map.
*
* constant real START_DISTANCE
* constant real FINAL_DISTANCE
*
******************************************************************************
*
* Functions:
*
* Units:
* | function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* | returns unit closest to coords(x, y)
* |
* | function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* | returns unit closest to coords(x, y) within range radius
* |
* | function GetClosestUnitInGroup takes real x, real y, group g returns unit
* | returns unit closest to coords(x, y) within group g
*
*
* Group:
* | function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group dest, boolexpr filter returns nothing
* | adds to group dest up to N units, closest to coords(x, y) within range radius
* |
* | function GetClosestNUnitsInGroup takes real x, real y, integer n, group source, group dest returns nothing
* | adds to group dest up to N units, closest to coords(x, y) within group source
*
*
* Items:
* | function GetClosestItem takes real x, real y, boolexpr filter returns item
* | returns item closest to coords(x, y)
* |
* | function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
* | returns item closest to coords(x, y) within range radius
*
*
* Destructables:
* | function GetClosestDestructable takes real x, real y, boolexpr filter returns destructable
* | returns destructable closest to coords(x, y)
* |
* | function GetClosestDestructableInRange takes real x, real y, real radius, boolexpr filter returns destructable
* | returns destructable closest to coords(x, y) within range radius
*
*
*****************************************************************************/
library GetClosestWidget
globals
private constant boolean UNITS_MODULE = true
private constant boolean GROUP_MODULE = true
private constant boolean ITEMS_MODULE = true
private constant boolean DESTS_MODULE = true
private constant real START_DISTANCE = 800
private constant real FINAL_DISTANCE = 3200
endglobals
globals
private real distance
private real coordX
private real coordY
endglobals
private keyword GroupModule
private function calcDistance takes real x, real y returns real
local real dx = x - coordX
local real dy = y - coordY
return ( (dx*dx + dy*dy) / 10000 )
endfunction
private struct ClosestWidget extends array
static if UNITS_MODULE then
static unit unit
static group group = CreateGroup()
endif
static if GROUP_MODULE then
static if not UNITS_MODULE then
static group group = CreateGroup()
endif
static integer count = 0
static unit array sorted
static real array vector
implement GroupModule
endif
static if ITEMS_MODULE then
static item item
static rect area = Rect(0, 0, 0, 0)
endif
static if DESTS_MODULE then
static destructable destructable
static if not ITEMS_MODULE then
static rect area = Rect(0, 0, 0, 0)
endif
endif
endstruct
private function Defaults takes real x, real y returns nothing
static if UNITS_MODULE then
set ClosestWidget.unit = null
endif
static if ITEMS_MODULE then
set ClosestWidget.item = null
endif
static if DESTS_MODULE then
set ClosestWidget.destructable = null
endif
set distance = 100000
set coordX = x
set coordY = y
endfunction
static if UNITS_MODULE then
//! runtextmacro DEFINE_GCW_UNIT_MODULE()
endif
static if GROUP_MODULE then
//! runtextmacro DEFINE_GCW_GROUP_MODULE()
endif
static if ITEMS_MODULE then
//! runtextmacro DEFINE_GCW_MODULE("Item", "item")
endif
static if DESTS_MODULE then
//! runtextmacro DEFINE_GCW_MODULE("Destructable", "destructable")
endif
//! textmacro DEFINE_GCW_UNIT_MODULE
private function doEnumUnits takes unit u returns nothing
local real dist = calcDistance(GetUnitX(u), GetUnitY(u))
if ( dist < distance ) then
set ClosestWidget.unit = u
set distance = dist
endif
endfunction
private function enumUnits takes nothing returns nothing
call doEnumUnits(GetEnumUnit())
endfunction
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r = START_DISTANCE
local unit u
call Defaults(x, y)
loop
if ( r > FINAL_DISTANCE ) then
call GroupEnumUnitsInRect(ClosestWidget.group, GetWorldBounds(), filter)
exitwhen true
else
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, r, filter)
exitwhen FirstOfGroup(ClosestWidget.group) != null
endif
set r = 2*r
endloop
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call doEnumUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
return ClosestWidget.unit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
local unit u
call Defaults(x, y)
if ( radius >= 0 ) then
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, radius, filter)
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call doEnumUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
endif
return ClosestWidget.unit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call Defaults(x, y)
call ForGroup(g, function enumUnits)
return ClosestWidget.unit
endfunction
//! endtextmacro
//! textmacro DEFINE_GCW_GROUP_MODULE
private module GroupModule
static method doSaveUnits takes unit u returns nothing
set count = count + 1
set sorted[count] = u
set vector[count] = calcDistance(GetUnitX(u), GetUnitY(u))
endmethod
static method saveUnits takes nothing returns nothing
call doSaveUnits(GetEnumUnit())
endmethod
static method sortUnits takes integer lo, integer hi returns nothing
local integer i = lo
local integer j = hi
local real pivot = vector[(lo+hi)/2]
loop
loop
exitwhen vector[i] >= pivot
set i = i + 1
endloop
loop
exitwhen vector[j] <= pivot
set j = j - 1
endloop
exitwhen i > j
set vector[0] = vector[i]
set vector[i] = vector[j]
set vector[j] = vector[0]
set sorted[0] = sorted[i]
set sorted[i] = sorted[j]
set sorted[j] = sorted[0]
set i = i + 1
set j = j - 1
endloop
if ( lo < j ) then
call sortUnits(lo, j)
endif
if ( hi > i ) then
call sortUnits(i, hi)
endif
endmethod
static method fillGroup takes integer n, group dest returns nothing
loop
exitwhen count <= 0 or sorted[count] == null
if ( count <= n ) then
call GroupAddUnit(dest, sorted[count])
endif
set sorted[count] = null
set count = count - 1
endloop
endmethod
endmodule
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group dest, boolexpr filter returns nothing
local unit u
call Defaults(x, y)
if ( radius >= 0 )then
call GroupEnumUnitsInRange(ClosestWidget.group, x, y, radius, filter)
loop
set u = FirstOfGroup(ClosestWidget.group)
exitwhen u == null
call ClosestWidget.doSaveUnits(u)
call GroupRemoveUnit(ClosestWidget.group, u)
endloop
call ClosestWidget.sortUnits(1, ClosestWidget.count)
call ClosestWidget.fillGroup(n, dest)
endif
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group source, group dest returns nothing
local integer i = 0
call Defaults(x, y)
call ForGroup(source, function ClosestWidget.saveUnits)
call ClosestWidget.sortUnits(1, ClosestWidget.count)
call ClosestWidget.fillGroup(n, dest)
endfunction
//! endtextmacro
//! textmacro DEFINE_GCW_MODULE takes NAME, TYPE
private function enum$NAME$s takes nothing returns nothing
local $TYPE$ temp = GetEnum$NAME$()
local real dist = calcDistance(Get$NAME$X(temp), Get$NAME$Y(temp))
if ( dist < distance ) then
set ClosestWidget.$TYPE$ = temp
set distance = dist
endif
set temp = null
endfunction
function GetClosest$NAME$ takes real x, real y, boolexpr filter returns $TYPE$
local real r = START_DISTANCE
call Defaults(x, y)
loop
if ( r > FINAL_DISTANCE ) then
call Enum$NAME$sInRect(GetWorldBounds(), filter, function enum$NAME$s)
exitwhen true
else
call SetRect(ClosestWidget.area, x-r, y-r, x+r, y+r)
call Enum$NAME$sInRect(ClosestWidget.area, filter, function enum$NAME$s)
exitwhen ClosestWidget.$TYPE$ != null
endif
set r = 2*r
endloop
return ClosestWidget.$TYPE$
endfunction
function GetClosest$NAME$InRange takes real x, real y, real radius, boolexpr filter returns $TYPE$
call Defaults(x, y)
if ( radius > 0 ) then
call SetRect(ClosestWidget.area, x-radius, y-radius, x+radius, y+radius)
call Enum$NAME$sInRect(ClosestWidget.area, filter, function enum$NAME$s)
endif
return ClosestWidget.$TYPE$
endfunction
//! endtextmacro
endlibrary
//TESH.scrollpos=51
//TESH.alwaysfold=0
library IsDestructableTree uses optional UnitIndexer /* v1.3.1
*************************************************************************************
* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isdestructabletree-248054/
* by BPower
* Detect whether a destructable is a tree or not.
*
***************************************************************************
*
* Credits
*
* To PitzerMike
* -----------------------
*
* for IsDestructableTree
*
*************************************************************************************
*
* Functions
*
* function IsDestructableTree takes destructable d returns boolean
*
* function IsDestructableAlive takes destructable d returns boolean
*
* function IsDestructableDead takes destructable d returns boolean
*
* function IsTreeAlive takes destructable tree returns boolean
* - May only return true for trees.
*
* function KillTree takes destructable tree returns boolean
* - May only kill trees.
*
* function KillTreesAround takes real rectCenterX, real rectCenterY returns nothing
* Destroy trees in rectangle 256x256 on given coordinates
*/
globals
private constant integer HARVESTER_UNIT_ID = 'hpea'//* human peasant
private constant integer HARVEST_ABILITY = 'Ahrl'//* ghoul harvest
private constant integer HARVEST_ORDER_ID = 0xD0032//* harvest order ( 852018 )
private constant player NEUTRAL_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
unit IsDestructableTreeHarvester = null
rect IsDestructableTreeRect
endglobals
function IsDestructableTree takes destructable d returns boolean
//* 851973 is the order id for stunned, it will interrupt the preceding harvest order.
return (IssueTargetOrderById(IsDestructableTreeHarvester, HARVEST_ORDER_ID, d)) and (IssueImmediateOrderById(IsDestructableTreeHarvester, 851973))
endfunction
function IsDestructableDead takes destructable d returns boolean
return (GetWidgetLife(d) <= 0.405)
endfunction
function IsDestructableAlive takes destructable d returns boolean
return (GetWidgetLife(d) > .405)
endfunction
function IsTreeAlive takes destructable tree returns boolean
return IsDestructableAlive(tree) and IsDestructableTree(tree)
endfunction
function KillTree takes destructable tree returns boolean
if (IsTreeAlive(tree)) then
call KillDestructable(tree)
return true
endif
return false
endfunction
// added by Zibi:
// destroy trees in rectangle 256x256
function KillTreesRect_Enum takes nothing returns nothing
call KillTree(GetEnumDestructable())
endfunction
function KillTreesAround takes real rectCenterX, real rectCenterY returns nothing
call MoveRectTo(IsDestructableTreeRect, rectCenterX, rectCenterY)
call EnumDestructablesInRect(IsDestructableTreeRect, null, function KillTreesRect_Enum)
endfunction
//----------------------------------------
private function Init takes nothing returns nothing
static if LIBRARY_UnitIndexer then//* You may adapt this to your own indexer.
set UnitIndexer.enabled = false
endif
set IsDestructableTreeHarvester = CreateUnit(NEUTRAL_PLAYER, HARVESTER_UNIT_ID, 0, 0, 0)
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = true
endif
call UnitAddAbility(IsDestructableTreeHarvester, HARVEST_ABILITY)
call UnitAddAbility(IsDestructableTreeHarvester, 'Aloc')
call ShowUnit(IsDestructableTreeHarvester, false)
set IsDestructableTreeRect = Rect(0.0, 0.0, 256.0, 256.0)
endfunction
//* Seriously?
private module Inits
private static method onInit takes nothing returns nothing
call Init()
endmethod
endmodule
private struct I extends array
implement Inits
endstruct
endlibrary
//===========================================================================
function UnitEventDestroyGroup takes integer i returns nothing
if udg_CargoTransportGroup[i] != null then
call DestroyGroup(udg_CargoTransportGroup[i])
set udg_CargoTransportGroup[i] = null
endif
endfunction
function UnitEventCheckAfter takes nothing returns nothing
local integer i = 0
loop
set i = udg_CheckDeathList[i]
exitwhen i == 0
if udg_IsUnitNew[i] then
//The unit was just created.
set udg_IsUnitNew[i] = false
elseif udg_IsUnitTransforming[i] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UDex = i
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[i] = GetUnitTypeId(udg_UDexUnits[i]) //Set this afterward to give the user extra reference
set udg_IsUnitTransforming[i] = false
call UnitAddAbility(udg_UDexUnits[i], udg_DetectTransformAbility)
elseif udg_IsUnitAlive[i] then
//The unit has started reincarnating.
set udg_IsUnitReincarnating[i] = true
set udg_IsUnitAlive[i] = false
set udg_UDex = i
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
endif
set udg_CheckDeathInList[i] = false
endloop
//Empty the list
set udg_CheckDeathList[0] = 0
endfunction
function UnitEventCheckAfterProxy takes integer i returns nothing
if udg_CheckDeathList[0] == 0 then
call TimerStart(udg_CheckDeathTimer, 0.00, false, function UnitEventCheckAfter)
endif
if not udg_CheckDeathInList[i] then
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
set udg_CheckDeathInList[i] = true
endif
endfunction
function UnitEventOnUnload takes nothing returns nothing
local integer i = udg_UDex
call GroupRemoveUnit(udg_CargoTransportGroup[GetUnitUserData(udg_CargoTransportUnit[i])], udg_UDexUnits[i])
set udg_IsUnitBeingUnloaded[i] = true
set udg_CargoEvent = 0.00
set udg_CargoEvent = 2.00
set udg_CargoEvent = 0.00
set udg_IsUnitBeingUnloaded[i] = false
if not IsUnitLoaded(udg_UDexUnits[i]) or IsUnitType(udg_CargoTransportUnit[i], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_CargoTransportUnit[i]) == 0 then
set udg_CargoTransportUnit[i] = null
endif
endfunction
function UnitEventOnDeath takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_UDex != 0 then
set udg_KillerOfUnit[udg_UDex] = GetKillingUnit() //Added 29 May 2017 for GIMLI_2
set udg_IsUnitAlive[udg_UDex] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
set udg_KillerOfUnit[udg_UDex] = null
if udg_CargoTransportUnit[udg_UDex] != null then
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnOrder takes nothing returns boolean
local integer pdex = udg_UDex
local unit u = GetFilterUnit()
local integer i = GetUnitUserData(u)
if i > 0 then
set udg_UDex = i
if GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
if not udg_IsUnitRemoved[i] then
set udg_IsUnitRemoved[i] = true
set udg_IsUnitAlive[i] = false
set udg_SummonerOfUnit[i] = null
//For backwards-compatibility:
set udg_DeathEvent = 0.00
set udg_DeathEvent = 3.00
set udg_DeathEvent = 0.00
//Fire deindex event for UDex:
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 2.00
set udg_UnitIndexEvent = 0.00
set udg_UDexNext[udg_UDexPrev[i]] = udg_UDexNext[i]
set udg_UDexPrev[udg_UDexNext[i]] = udg_UDexPrev[i]
// Recycle the index for later use
set udg_UDexUnits[i] = null
set udg_UDexPrev[i] = udg_UDexLastRecycled
set udg_UDexLastRecycled = i
call UnitEventDestroyGroup(i)
endif
elseif not udg_IsUnitAlive[i] then
if not IsUnitType(u, UNIT_TYPE_DEAD) then
set udg_IsUnitAlive[i] = true
set udg_DeathEvent = 0.00
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
set udg_IsUnitReincarnating[i] = false
endif
elseif IsUnitType(u, UNIT_TYPE_DEAD) then
if udg_IsUnitNew[i] then
//This unit was created as a corpse.
set udg_IsUnitAlive[i] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
elseif udg_CargoTransportUnit[i] == null or not IsUnitType(u, UNIT_TYPE_HERO) then
//The unit may have just started reincarnating.
call UnitEventCheckAfterProxy(i)
endif
elseif GetUnitAbilityLevel(u, udg_DetectTransformAbility) == 0 and not udg_IsUnitTransforming[i] then
set udg_IsUnitTransforming[i] = true
call UnitEventCheckAfterProxy(i) //This block has been updated on 21 July 2017
endif
if udg_CargoTransportUnit[i] != null and not udg_IsUnitBeingUnloaded[i] and not IsUnitLoaded(u) or IsUnitType(u, UNIT_TYPE_DEAD) then
call UnitEventOnUnload()
endif
set udg_UDex = pdex
endif
set u = null
return false
endfunction
function UnitEventOnSummon takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_IsUnitNew[udg_UDex] then
set udg_SummonerOfUnit[udg_UDex] = GetSummoningUnit()
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 0.50
set udg_UnitIndexEvent = 0.00
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnLoad takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = GetUnitUserData(GetTriggerUnit())
local integer index
if i != 0 then
set udg_UDex = i
if udg_CargoTransportUnit[i] != null then
call UnitEventOnUnload()
endif
//Loaded corpses do not issue an order when unloaded, therefore must
//use the enter-region event method taken from Jesus4Lyf's Transport.
if not udg_IsUnitAlive[i] then
call SetUnitX(udg_UDexUnits[i], udg_WorldMaxX)
call SetUnitY(udg_UDexUnits[i], udg_WorldMaxY)
endif
set udg_CargoTransportUnit[i] = GetTransportUnit()
set index = GetUnitUserData(udg_CargoTransportUnit[i])
if udg_CargoTransportGroup[index] == null then
set udg_CargoTransportGroup[index] = CreateGroup()
endif
call GroupAddUnit(udg_CargoTransportGroup[index], udg_UDexUnits[i])
set udg_CargoEvent = 0.00
set udg_CargoEvent = 1.00
set udg_CargoEvent = 0.00
set udg_UDex = pdex
endif
return false
endfunction
function UnitEventEnter takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = udg_UDexLastRecycled
local unit u = GetFilterUnit()
if udg_UnitIndexerEnabled and GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
//Generate a unique integer index for this unit
if i == 0 then
set i = udg_UDexMax + 1
set udg_UDexMax = i
else
set udg_UDexLastRecycled = udg_UDexPrev[i]
endif
//Link index to unit, unit to index
set udg_UDexUnits[i] = u
call SetUnitUserData(u, i)
//For backwards-compatibility, add the unit to a linked list
set udg_UDexNext[i] = udg_UDexNext[0]
set udg_UDexPrev[udg_UDexNext[0]] = i
set udg_UDexNext[0] = i
set udg_UDexPrev[i] = 0
set udg_CheckDeathInList[i] = false
call UnitAddAbility(u, udg_DetectRemoveAbility)
call UnitMakeAbilityPermanent(u, true, udg_DetectRemoveAbility)
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_UnitTypeOf[i] = GetUnitTypeId(u)
set udg_IsUnitNew[i] = true
set udg_IsUnitAlive[i] = true
set udg_IsUnitRemoved[i] = false
set udg_IsUnitReincarnating[i] = false
set udg_IsUnitPreplaced[i] = udg_IsUnitPreplaced[0] //Added 29 May 2017 for Spellbound
call UnitEventCheckAfterProxy(i)
//Fire index event for UDex
set udg_UDex = i
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 1.00
set udg_UnitIndexEvent = 0.00
else
set udg_UDex = GetUnitUserData(u)
if udg_CargoTransportUnit[udg_UDex] != null and not IsUnitLoaded(u) then
//The unit was dead, but has re-entered the map.
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
set u = null
return false
endfunction
//===========================================================================
function UnitEventInit takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS //update to make it work with 1.29
local player p
local trigger t = CreateTrigger()
local trigger load = CreateTrigger()
local trigger death = CreateTrigger()
local trigger summon = CreateTrigger()
local rect r = GetWorldBounds()
local region re = CreateRegion()
local boolexpr enterB = Filter(function UnitEventEnter)
local boolexpr orderB = Filter(function UnitEventOnOrder)
set udg_WorldMaxX = GetRectMaxX(r)
set udg_WorldMaxY = GetRectMaxY(r)
call RegionAddRect(re, r)
call RemoveRect(r)
call UnitEventDestroyGroup(0)
call UnitEventDestroyGroup(1)
set udg_CheckDeathList[0] = 0
set udg_UnitIndexerEnabled = true
call TriggerRegisterEnterRegion(CreateTrigger(), re, enterB)
call TriggerAddCondition(load, Filter(function UnitEventOnLoad))
call TriggerAddCondition(death, Filter(function UnitEventOnDeath))
call TriggerAddCondition(summon, Filter(function UnitEventOnSummon))
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, udg_DetectRemoveAbility, false)
call SetPlayerAbilityAvailable(p, udg_DetectTransformAbility, false)
call TriggerRegisterPlayerUnitEvent(summon, p, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, orderB)
call TriggerRegisterPlayerUnitEvent(death, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(load, p, EVENT_PLAYER_UNIT_LOADED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
set summon = null
set death = null
set load = null
set re = null
set enterB = null
set orderB = null
set p = null
set r = null
set t = null
endfunction
function InitTrig_Unit_Event takes nothing returns nothing
endfunction
//TESH.scrollpos=51
//TESH.alwaysfold=0
library UnitIndexerGUI //requires GUI Unit Indexer
globals
private trigger index = null
private trigger deindex = null
private trigger init = null
private trigger tempTrig
endglobals
private function DoTheThings takes trigger t, code c, real r returns trigger
if t == null then
set t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, r)
endif
call TriggerAddCondition(t, Filter(c))
set tempTrig = t
set t = null
return tempTrig
endfunction
//call RegisterUnitIndexEvent(Filter(function Blah), EVENT_UNIT_INDEXED)
//->
//call OnUnitIndex(function Blah)
function OnUnitIndex takes code c returns nothing
set index = DoTheThings(index, c, 1.)
endfunction
//call RegisterUnitIndexEvent(Filter(function Blah), EVENT_UNIT_DEINDEXED)
//->
//call OnUnitDeindex(function Blah)
function OnUnitDeindex takes code c returns nothing
set deindex = DoTheThings(deindex, c, 2.)
endfunction
private function DestroyInit takes nothing returns boolean
call DestroyTrigger(init) //will still allow consecutive triggerconditions to run.
set init = null
return false
endfunction
//GUI Unit Indexer will initialize after any vJass stuff, so you need to do your
//unit-indexer-requiring stuff after this event instead of a module/struct/library
//initializer.
function OnUnitIndexerInitialized takes code c returns nothing
if init == null then
set init = CreateTrigger()
call TriggerAddCondition(init, Filter(function DestroyInit))
call TriggerRegisterVariableEvent(init, "udg_UnitIndexEvent", EQUAL, 3.00)
endif
call TriggerAddCondition(init, Filter(c))
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function GetUnitById takes integer id returns unit
return udg_UDexUnits[id]
endfunction
function GetIndexedUnit takes nothing returns unit
return udg_UDexUnits[udg_UDex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return udg_UDex
endfunction
function IsUnitIndexed takes unit u returns boolean
return udg_UDexUnits[GetUnitUserData(u)] == u
endfunction
endlibrary
//===========================================================================
// Damage Engine 3A.0.0.0 - a new type of Damage Engine for users who don't
// have access to the latest version of WarCraft 3, which incorporates new
// features to inherited from Damage Engine 5.7 by hooking TriggerRegisterVariableEvent.
// However, it requires having JassHelper installed.
//
// Stuff that doesn't work:
// - Pre-armor modification
// - Damage/Attack/Weapotype detection/modification
// - Armor/defense detection/modification
// - Melee/ranged detection
// - Filters for u.
// - Spirit link won't interact with custom damage.
// - Still needs workarounds for Anti-Magic Shell/Mana Shield/Life Drain/etc.
//
// Stuff that is changed from how it worked with 3.8:
// - Recursive damage now uses the Damage Engine 5 anti-recursion method. So
// all recursive damage will be postponed until the sequence has completed.
// - No more need for using ClearDamageEvent
// - No more need to disable the DamageEventTrigger in order to avoid things
// going recursively.
//
library DamageEngine
globals
private timer alarm = CreateTimer()
private boolean alarmSet = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean canKick = false
//These variables coincide with Blizzard's "limitop" type definitions so as to enable users (GUI in particular) with some nice performance perks.
public constant integer FILTER_ATTACK = 0 //LESS_THAN
public constant integer FILTER_OTHER = 2 //EQUAL
public constant integer FILTER_SPELL = 4 //GREATER_THAN
public constant integer FILTER_CODE = 5 //NOT_EQUAL
public constant integer FILTER_MAX = 6
private integer eventFilter = FILTER_OTHER
private constant integer LIMBO = 16 //When manually-enabled recursion is enabled via DamageEngine_recurion, the engine will never go deeper than LIMBO.
public boolean inception = false //When true, it allows your trigger to potentially go recursive up to LIMBO. However it must be set per-trigger throughout the game and not only once per trigger during map initialization.
private boolean dreaming = false
private integer sleepLevel = 0
private group proclusGlobal = CreateGroup() //track sources of recursion
private group fischerMorrow = CreateGroup() //track targets of recursion
private boolean kicking = false
private boolean eventsRun = false
private unit protectUnit = null
private real protectLife = 0.00
private boolean blocked = false
private keyword run
private keyword trigFrozen
private keyword levelsDeep
private keyword inceptionTrig
private keyword checkLife
private keyword lifeTrigger
endglobals
private function CheckAddUnitToEngine takes unit u returns boolean
if GetUnitAbilityLevel(u, 'Aloc') > 0 then
elseif not TriggerEvaluate(gg_trg_Damage_Engine_Config) then
//Add some more elseifs to rule out stuff you don't want to get registered, such as:
//elseif IsUnitType(u, UNIT_TYPE_STRUCTURE) then
else
return true
endif
return false
endfunction
struct DamageTrigger extends array
//The below variables are constant
readonly static thistype MOD = 1
readonly static thistype DAMAGE = 5
readonly static thistype ZERO = 6
readonly static thistype AFTER = 7
readonly static thistype AOE = 9
private static integer count = 9
static thistype lastRegistered = 0
private static thistype array trigIndexStack
static thistype eventIndex = 0
static boolean array filters
readonly string eventStr
readonly real weight
readonly boolean configured
boolean usingGUI
//The below variables are private
private thistype next
private trigger rootTrig
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer levelsDeep //How deep the user recursion currently is.
boolean inceptionTrig //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent trigger.
static method operator enabled= takes boolean b returns nothing
if b then
call EnableTrigger(udg_DamageEventTrigger)
else
call DisableTrigger(udg_DamageEventTrigger)
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(udg_DamageEventTrigger)
endmethod
static method setGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.damage
set udg_DamageEventType = Damage.index.userType
set udg_DamageEventOverride = Damage.index.override
if full then
set udg_DamageEventSource = Damage.index.sourceUnit
set udg_DamageEventTarget = Damage.index.targetUnit
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageSpell = Damage.index.isSpell
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
endif
endmethod
static method getVerboseStr takes string eventName returns string
if eventName == "Modifier" or eventName == "Mod" then
return "udg_DamageModifierEvent"
endif
return "udg_" + eventName + "DamageEvent"
endmethod
private static method getStrIndex takes string var, real lbs returns thistype
local integer root = R2I(lbs)
if var == "udg_DamageModifierEvent" then
set root= MOD
elseif var == "udg_DamageEvent" then
if root == 2 or root == 0 then
set root= ZERO
else
set root= DAMAGE //Above 0.00 but less than 2.00, generally would just be 1.00
endif
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_AOEDamageEvent" then
set root = AOE
else
set root = 0
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
endif
return root
endmethod
private method toggleAllFilters takes boolean flag returns nothing
set filters[this + FILTER_ATTACK] = flag
set filters[this + FILTER_OTHER] = flag
set filters[this + FILTER_SPELL] = flag
set filters[this + FILTER_CODE] = flag
endmethod
method operator filter= takes integer f returns nothing
set this = this*FILTER_MAX
if f == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if f == FILTER_ATTACK then
set filters[this + FILTER_ATTACK] = true
else
set filters[this + f] = true
endif
endif
endmethod
static method registerVerbose takes trigger whichTrig, string var, real lbs, boolean GUI, integer filt returns thistype
local thistype index= getStrIndex(var, lbs)
local thistype i = 0
local thistype id = 0
if index == 0 then
return 0
elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
set filters[lastRegistered*FILTER_MAX + filt] = true //allows GUI to register multiple different types of Damage filters to the same trigger
return 0
endif
if trigIndexStack[0] == 0 then
set count = count + 1 //List runs from index 10 and up
set id = count
else
set id = trigIndexStack[0]
set trigIndexStack[0] = trigIndexStack[id]
endif
set lastRegistered = id
set id.filter = filt
set id.rootTrig = whichTrig
set id.usingGUI = GUI
set id.weight = lbs
set id.eventStr = var
loop
set i = index.next
exitwhen i == 0 or lbs < i.weight
set index = i
endloop
set index.next = id
set id.next = i
//call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
return lastRegistered
endmethod
static method registerTrigger takes trigger t, string var, real lbs returns thistype
return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
endmethod
private static thistype prev = 0
static method getIndex takes trigger t, string eventName, real lbs returns thistype
local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
loop
set prev = index
set index = index.next
exitwhen index == 0 or index.rootTrig == t
endloop
return index
endmethod
static method unregister takes trigger t, string eventName, real lbs, boolean reset returns boolean
local thistype index = getIndex(t, eventName, lbs)
if index == 0 then
return false
endif
set prev.next = index.next
set trigIndexStack[index] = trigIndexStack[0]
set trigIndexStack[0] = index
if reset then
set index.configured = false
set index = index*FILTER_MAX
call index.toggleAllFilters(false)
endif
return true
endmethod
static method damageUnit takes unit u, real life returns nothing
call SetWidgetLife(u, RMaxBJ(life, 0.41))
if life <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(u, true)
endif
//Kill the unit
set DamageTrigger.enabled = false
call UnitDamageTarget(udg_DamageEventSource, u, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
set DamageTrigger.enabled = true
endif
endmethod
static method checkLife takes nothing returns boolean
if protectUnit != null then
if Damage.lifeTrigger != null then
call DestroyTrigger(Damage.lifeTrigger)
set Damage.lifeTrigger = null
endif
if GetUnitAbilityLevel(protectUnit, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(protectUnit, udg_DamageBlockingAbility)
call SetWidgetLife(protectUnit, protectLife)
elseif udg_IsDamageSpell or blocked then
call DamageTrigger.damageUnit(protectUnit, protectLife)
endif
if blocked then
set blocked = false
endif
set protectUnit = null
return true
endif
return false
endmethod
method run takes nothing returns nothing
local integer cat = this
local Damage d = Damage.index
if cat == MOD or not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set dreaming = true
//call BJDebugMsg("Start of event running")
loop
set this = this.next
exitwhen this == 0
if cat == MOD then
exitwhen d.override or udg_DamageEventOverride
exitwhen this.weight >= 4.00 and udg_DamageEventAmount <= 0.00
endif
set eventIndex = this
if not this.trigFrozen and filters[this*FILTER_MAX + eventFilter] and IsTriggerEnabled(this.rootTrig) then
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
if TriggerEvaluate(this.rootTrig) then
call TriggerExecute(this.rootTrig)
endif
if cat == MOD then
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
if this.usingGUI then
set d.damage = udg_DamageEventAmount
set d.userType = udg_DamageEventType
set d.override = udg_DamageEventOverride
elseif this.next == 0 or this.next.usingGUI then //Might offer a slight performance improvement
call setGUIFromStruct(false)
endif
endif
call checkLife()
endif
endloop
//call BJDebugMsg("End of event running")
set dreaming = false
endif
endmethod
static method finish takes nothing returns boolean
if checkLife() and not blocked and udg_DamageEventAmount != 0.00 then
call DamageTrigger.AFTER.run()
endif
return false
endmethod
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code c returns trigger
local integer i = 0
local boolexpr b = Filter(c)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
exitwhen true
endif
set i = i + 1
exitwhen b == autoFuncs[i]
endloop
return autoTriggers[i]
endmethod
endstruct
struct Damage extends array
readonly unit sourceUnit //stores udg_DamageEventSource
readonly unit targetUnit //stores udg_DamageEventTarget
real damage //stores udg_DamageEventAmount
readonly real prevAmt //stores udg_DamageEventPrevAmt
integer userType //stores udg_DamageEventType
readonly boolean isCode
readonly boolean isSpell //stores udg_IsDamageSpell
boolean override
readonly static unit aoeSource = null
readonly static Damage index = 0
private static Damage damageStack = 0
private static integer count = 0 //The number of currently-running queued or sequential damage instances
private Damage stackRef
private DamageTrigger recursiveTrig
static trigger lifeTrigger = null //private
static method operator source takes nothing returns unit
return udg_DamageEventSource
endmethod
static method operator target takes nothing returns unit
return udg_DamageEventTarget
endmethod
static method operator amount takes nothing returns real
return Damage.index.damage
endmethod
static method operator amount= takes real r returns nothing
set Damage.index.damage = r
endmethod
private static method onAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
call DamageTrigger.AOE.run()
endif
set udg_DamageEventAOE = 0
set udg_DamageEventLevel = 0
set udg_EnhancedDamageTarget = null
set aoeSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
static method finish takes nothing returns nothing
local Damage i = 0
local integer exit
if canKick then
set canKick = false
set kicking = true
call DamageTrigger.finish()
if damageStack != 0 then
loop
set exit = damageStack
set sleepLevel = sleepLevel + 1
loop
set eventFilter = FILTER_CODE
set Damage.index = i.stackRef
call DamageTrigger.setGUIFromStruct(true)
call DamageTrigger.MOD.run()
call DamageTrigger.DAMAGE.run()
if udg_DamageEventAmount != 0.00 then
call DamageTrigger.damageUnit(udg_DamageEventTarget, GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount)
call DamageTrigger.AFTER.run()
endif
set i = i + 1
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
loop
set i = i - 1
set i.stackRef.recursiveTrig.trigFrozen = false
set i.stackRef.recursiveTrig.levelsDeep = 0
exitwhen i == 0
endloop
set damageStack = 0
endif
set dreaming = false
set sleepLevel = 0
call GroupClear(proclusGlobal)
call GroupClear(fischerMorrow)
set kicking = false
//call BJDebugMsg("Cleared up the groups")
endif
endmethod
private static method wakeUp takes nothing returns nothing
set alarmSet = false
set dreaming = false
set DamageTrigger.enabled = true
call finish()
call onAOEEnd()
set Damage.count = 0
set Damage.index = 0
set udg_DamageEventTarget = null
set udg_DamageEventSource = null
endmethod
private static method createLifeTrigger takes unit u, limitop op, real amount returns nothing
if not blocked then
set lifeTrigger = CreateTrigger()
call TriggerAddCondition(lifeTrigger, Filter(function DamageTrigger.finish))
call TriggerRegisterUnitStateEvent(lifeTrigger, u, UNIT_STATE_LIFE, op, amount)
endif
set protectUnit = u
endmethod
private method mitigate takes real newAmount, boolean recursive returns nothing
local real prevLife
local real life
local unit u = targetUnit
local real prevAmount = prevAmt
set life = GetWidgetLife(u)
if not isSpell then
if newAmount != prevAmount then
set life = life + prevAmount - newAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set protectLife = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call createLifeTrigger(u, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set protectLife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > protectLife then
set life = RMaxBJ(protectLife - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + protectLife)/2.00
else
set life = life + prevAmount*0.50
endif
set protectLife = prevLife - (prevAmount - (prevAmount - newAmount))
call createLifeTrigger(u, GREATER_THAN, life)
endif
set u = null
endmethod
private method getSpellAmount takes real amt returns real
local integer i = 6
local real mult = 1.00
set isSpell = amt < 0.00
if isSpell then
set amt = -amt
if IsUnitType(target, UNIT_TYPE_ETHEREAL) and not IsUnitType(target, UNIT_TYPE_HERO) then
set mult = mult*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(target, 'Aegr') > 0 then
set mult = mult*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(target, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(target, i)) == udg_DmgEvBracers then
set mult = mult*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
return amt*mult
endif
return amt
endmethod
private method addRecursive takes nothing returns boolean
if this.damage != 0.00 then
set this.recursiveTrig = DamageTrigger.eventIndex
if not this.isCode then
set this.isCode = true
endif
set inception = inception or DamageTrigger.eventIndex.inceptionTrig
if kicking and IsUnitInGroup(this.sourceUnit, proclusGlobal) and IsUnitInGroup(this.targetUnit, fischerMorrow) then
if inception and not DamageTrigger.eventIndex.trigFrozen then
set DamageTrigger.eventIndex.inceptionTrig = true
if DamageTrigger.eventIndex.levelsDeep < sleepLevel then
set DamageTrigger.eventIndex.levelsDeep = DamageTrigger.eventIndex.levelsDeep + 1
if DamageTrigger.eventIndex.levelsDeep >= LIMBO then
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
else
set DamageTrigger.eventIndex.trigFrozen = true
endif
endif
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " levelsDeep: " + I2S(DamageTrigger.eventIndex.levelsDeep) + " sleepLevel: " + I2S(sleepLevel))
return true
endif
set inception = false
return false
endmethod
private static method onDamageResponse takes nothing returns boolean
local Damage d = Damage.count + 1
set Damage.count = d
set d.sourceUnit = GetEventDamageSource()
set d.targetUnit = GetTriggerUnit()
set d.damage = d.getSpellAmount(GetEventDamage())
set d.prevAmt = d.damage
set d.userType = udg_NextDamageType
set d.isCode = udg_NextDamageType != 0 or udg_NextDamageOverride or dreaming
set d.override = udg_NextDamageOverride
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
call finish() //in case the unit state event failed and the 0.00 second timer hasn't yet expired
if dreaming then
if d.addRecursive() then
set blocked = true
call d.mitigate(0.00, true)
else
set Damage.count = d - 1
endif
return false
endif
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
if alarmSet then
if d.sourceUnit != aoeSource then
call onAOEEnd()
set aoeSource = d.sourceUnit
elseif d.targetUnit == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1
elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
else
call TimerStart(alarm, 0.00, false, function Damage.wakeUp)
set alarmSet = true
set aoeSource = d.sourceUnit
set udg_EnhancedDamageTarget= d.targetUnit
endif
set Damage.index = d
call DamageTrigger.setGUIFromStruct(true)
call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
call GroupAddUnit(proclusGlobal, udg_DamageEventSource)
call GroupAddUnit(fischerMorrow, udg_DamageEventTarget)
if udg_DamageEventAmount == 0.00 then
call DamageTrigger.ZERO.run()
set canKick = true
call finish()
else
if d.isCode then
set eventFilter = FILTER_CODE
elseif udg_IsDamageSpell then
set eventFilter = FILTER_SPELL
else
set eventFilter = FILTER_ATTACK
endif
call DamageTrigger.MOD.run()
call DamageTrigger.DAMAGE.run()
//The damage amount is finalized.
call d.mitigate(udg_DamageEventAmount, false)
set canKick = true
endif
return false
endmethod
static method createDamageTrigger takes nothing returns nothing //private
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function thistype.onDamageResponse))
endmethod
static method setup takes nothing returns boolean //private
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if CheckAddUnitToEngine(u) then
set udg_UnitDamageRegistered[i] = true
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
call UnitAddAbility(u, udg_SpellDamageAbility)
call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
endif
set u = null
else
set udg_HideDamageFrom[i] = false
if udg_UnitDamageRegistered[i] then
set udg_UnitDamageRegistered[i] = false
set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
set udg_DamageEventsWasted = 0
//Rebuild the mass EVENT_UNIT_DAMAGED trigger:
call DestroyTrigger(udg_DamageEventTrigger)
call createDamageTrigger()
set i = udg_UDexNext[0]
loop
exitwhen i == 0
if udg_UnitDamageRegistered[i] then
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
endif
set i = udg_UDexNext[i]
endloop
endif
endif
endif
return false
endmethod
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
endstruct
public function RegisterFromHook takes trigger whichTrig, string var, limitop op, real value returns nothing
call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes trigger whichTrig, string eventName, real value, integer f returns DamageTrigger
return DamageTrigger.registerVerbose(whichTrig, DamageTrigger.getVerboseStr(eventName), value, false, f)
endfunction
function TriggerRegisterDamageEngine takes trigger whichTrig, string eventName, real value returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes code c, string eventName, real value, integer f returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[c], eventName, value, f)
endfunction
//Similar to TriggerRegisterDamageEvent, although takes code instead of trigger as the first argument.
function RegisterDamageEngine takes code c, string eventName, real value returns DamageTrigger
return RegisterDamageEngineEx(c, eventName, value, FILTER_OTHER)
endfunction
endlibrary
function InitTrig_Damage_Engine takes nothing returns nothing
local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
local integer i = bj_MAX_PLAYERS //Fixed in 3.8
//Create this trigger with UnitIndexEvents in order to add and remove units
//as they are created or removed.
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function Damage.setup))
set t = null
//Run the configuration actions to set all configurables:
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call Damage.createDamageTrigger()
//Disable SpellDamageAbility for every player.
loop
set i = i - 1
call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
exitwhen i == 0
endloop
//Preload abilities.
call UnitAddAbility(u, udg_DamageBlockingAbility)
call UnitAddAbility(u, udg_SpellDamageAbility)
call RemoveUnit(u)
set u = null
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyUtils uses DummyRecycler
globals
private unit DUMMY = null
endglobals
function DummyCaster takes real x, real y, real z, player p returns unit
set DUMMY = GetRecycledDummyAnyAngle(x, y, z)
call PauseUnit(DUMMY, false)
call SetUnitOwner(DUMMY, p, true)
return DUMMY
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private location L = Location(0, 0)
endglobals
//----------------------------------------------------------------------------------
//UNIT UTILITIES
//----------------------------------------------------------------------------------
function IsAlive takes unit u returns boolean
return GetWidgetLife(u) > 0.41 and not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0
endfunction
function HasAbility takes unit u, integer id returns boolean
return GetUnitAbilityLevel(u, id) > 0
endfunction
function HasBuff takes unit u, integer id returns boolean
return GetUnitAbilityLevel(u, id) > 0
endfunction
function IsResearched takes player p, integer id returns boolean
return GetPlayerTechCount(p, id, true) > 0
endfunction
function HasChance takes real chance returns boolean
return GetRandomInt(1, 100) <= chance
endfunction
function Percentage takes real perc returns real
return perc / 100
endfunction
function UnitHasMana takes unit u returns boolean
return GetUnitState(u, UNIT_STATE_MAX_MANA) > 0
endfunction
function GetUnitLife takes unit u returns real
return GetUnitState(u, UNIT_STATE_LIFE)
endfunction
function GetUnitMaxLife takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function GetUnitMissingLife takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetUnitState(u, UNIT_STATE_LIFE)
endfunction
function GetUnitPercentLife takes unit u returns real
return GetUnitLife(u) / GetUnitMaxLife(u) * 100
endfunction
function SetUnitLife takes unit u, real value returns nothing
call SetUnitState(u, UNIT_STATE_LIFE, value)
endfunction
function GetUnitMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MANA)
endfunction
function GetUnitMaxMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA)
endfunction
function GetUnitMissingMana takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_MANA) - GetUnitState(u, UNIT_STATE_MANA)
endfunction
function GetUnitPercentMana takes unit u returns real
return GetUnitMana(u) / GetUnitMaxMana(u) * 100
endfunction
function SetUnitMana takes unit u, real value returns nothing
call SetUnitState(u, UNIT_STATE_MANA, value)
endfunction
function UnitAddBook takes unit u, integer bookId returns nothing
local real mana
if UnitHasMana(u) then
set mana = GetUnitMana(u)
endif
call UnitAddAbility(u, bookId)
if UnitHasMana(u) then
call SetUnitMana(u, mana)
endif
endfunction
function GetUnitZ takes unit u returns real
call MoveLocation(L, GetUnitX(u), GetUnitY(u))
return GetLocationZ(L) + GetUnitFlyHeight(u)
endfunction
//----------------------------------------------------------------------------------
//EFFECTS
//----------------------------------------------------------------------------------
function CreateEffectOnUnit takes string fx, unit u, string attach returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx, u, attach))
endfunction
function CreateEffectOnLoc takes string fx, location loc returns nothing
call DestroyEffect(AddSpecialEffectLoc(fx, loc))
endfunction
//----------------------------------------------------------------------------------
//OPERATIONS
//----------------------------------------------------------------------------------
function Distance takes real aX, real aY, real bX, real bY returns real
local real dx = bX - aX
local real dy = bY - aY
return SquareRoot(dx * dx + dy * dy)
endfunction
//----------------------------------------------------------------------------------
//PLAYERS
//----------------------------------------------------------------------------------
function DetectComputerSlot takes player p returns boolean
return GetPlayerController(p) == MAP_CONTROL_COMPUTER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING
endfunction
function DetectHumanSlot takes player p returns boolean
return GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING
endfunction
//----------------------------------------------------------------------------------
//OTHERS
//----------------------------------------------------------------------------------
function IsDayTime takes nothing returns boolean
return GetFloatGameState(GAME_STATE_TIME_OF_DAY) >= 6. and GetFloatGameState(GAME_STATE_TIME_OF_DAY) < 18.
endfunction
function IsPointWalkable takes real x, real y, real tollerance returns boolean
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', x, y, 0)
local real dist = (GetUnitX(u) - x)*(GetUnitX(u) - x) + (GetUnitY(u) - y)*(GetUnitY(u) - y)
call RemoveUnit(u)
set u = null
return dist < tollerance*tollerance
endfunction
function NextWalkablePoint takes real x, real y returns location
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', x, y, 0)
local location loc = GetUnitLoc(u)
call RemoveUnit(u)
set u = null
return loc
endfunction
function UnitHasItemOfType takes unit u, integer id returns boolean
local boolean hasItem = false
local integer i = 0
local item indexItem
loop
exitwhen i >= bj_MAX_INVENTORY or hasItem
set indexItem = UnitItemInSlot(u, i)
if indexItem != null and GetItemTypeId(indexItem) == id then
set hasItem = true
endif
set indexItem = null
set i = i + 1
endloop
set indexItem = null
return hasItem
endfunction
function UnitCountItemOfType takes unit u, integer id returns integer
local integer i = 0
local integer count = 0
local item indexItem
loop
exitwhen i >= bj_MAX_INVENTORY
set indexItem = UnitItemInSlot(u, i)
if indexItem != null and GetItemTypeId(indexItem) == id then
set count = count + 1
endif
set indexItem = null
set i = i + 1
endloop
return count
endfunction
function IsAngleInRange takes real angleCenter, real angle, real whatDegreeRange returns boolean
local real halfRange = whatDegreeRange / 2.
local real min = .0
local real max = .0
call BJDebugMsg("angleCenter: "+R2S(angleCenter))
call BJDebugMsg("angle: "+R2S(angle))
if angleCenter < .0 then
set angleCenter = 360. + angleCenter
endif
if angle < .0 then
set angle = 360. + angle
endif
set min = angleCenter - halfRange
set max = angleCenter + halfRange
if min < .0 then
set min = 360. + min
return (angle>min) or (angle<max)
endif
if max > 360. then
set max = max - 360.
return (angle>min) or (angle<max)
endif
return (angle>min) and (angle<max)
endfunction
function IsUnitBehindUnit takes unit behind, unit inFront, real angleMargin returns boolean
local real angle = ModuloReal(Atan2(GetUnitY(behind) - GetUnitY(inFront), GetUnitX(behind) - GetUnitX(inFront)) * bj_RADTODEG, 360)
local real difference = GetUnitFacing(inFront) - angle
if difference < 0 then
set difference = -difference
endif
return difference > (180 - angleMargin) and difference < (180 + angleMargin)
endfunction
function IsUnitFacingUnitBehind takes unit behind, unit inFront, real angleMargin returns boolean
local real difference = GetUnitFacing(inFront) - GetUnitFacing(behind)
if difference < 0 then
set difference = -difference
endif
return difference < angleMargin and IsUnitBehindUnit(inFront, behind, angleMargin)
endfunction
function IsUnitFacingUnit takes unit theFacer, unit toFace, real angleMargin returns boolean
local real angle = ModuloReal(Atan2(GetUnitY(theFacer) - GetUnitY(toFace), GetUnitX(theFacer) - GetUnitX(toFace)) * bj_RADTODEG, 360)
local real difference = GetUnitFacing(theFacer) - angle
if difference < 0 then
set difference = -difference
endif
return difference > (180 - angleMargin) and difference < (180 + angleMargin)
endfunction
//----------------------------------------------------------------------------------
//UNIT TYPES
//----------------------------------------------------------------------------------
function IsHero takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_HERO)
endfunction
function IsMechanical takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MECHANICAL)
endfunction
function IsBuilding takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
function IsWorker takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_PEON)
endfunction
function IsMelee takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MELEE_ATTACKER)
endfunction
function IsDead takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
function IsFlying takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
function IsSummoned takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_SUMMONED)
endfunction
function IsMagicImmune takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
function AttacksFlying takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_ATTACKS_FLYING)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils requires SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant real MAX_COLLISION = 64.
private real tempX = 0.
private real tempY = 0.
private real tempRange = 0.
private unit tempUnit
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
function FilterInRange takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), tempX, tempY, tempRange)
endfunction
function AliveFilter takes nothing returns boolean
return IsAlive(GetFilterUnit()) and not IsBuilding(GetFilterUnit())
endfunction
//----------------------------------------------------------------------------------
//GROUP UTILITIES
//----------------------------------------------------------------------------------
function GroupEnumUnitsWithCollision takes group g, real x, real y, real radius returns group
set tempX = x
set tempY = y
set tempRange = radius
call GroupEnumUnitsInRange(g, x, y, radius+MAX_COLLISION, Filter(function FilterInRange))
return g
endfunction
function GroupEnumLivingUnitsOfPlayer takes group g, player p returns group
call GroupEnumUnitsOfPlayer(g, p, Filter(function AliveFilter))
return g
endfunction
function GetUnitsInRect takes group g, rect r returns group
call GroupEnumUnitsInRect(g, r, null)
return g
endfunction
function CopyGroup takes group g returns group
set bj_groupAddGroupDest = CreateGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
function GroupCountUnits takes group g returns integer
local integer count = 0
local group tempG = CopyGroup(g)
local unit u
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
set count = count + 1
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return count
endfunction
function GroupCountUnitsOfType takes group g, integer id, player p returns integer
local integer count = 0
local group tempG = CopyGroup(g)
local unit u
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
if GetUnitTypeId(u) == id and GetOwningPlayer(u) == p then
set count = count + 1
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return count
endfunction
function GroupRandomUnit takes group g returns unit
local integer t = 0
local integer curmax = 0
local group tempG = CopyGroup(g)
local unit u
set tempUnit = null
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
if IsAlive(u) then
set t = GetRandomInt(1,100)
if t > curmax then
set curmax = t
set tempUnit = u
endif
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return tempUnit
endfunction
function GroupLifePercent takes group g returns real
local real groupHp = 0
local real groupMaxHp = 0
local group tempG = CopyGroup(g)
local unit u
local real perc
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
if IsAlive(u) then
set groupHp = groupHp + GetUnitState(u, UNIT_STATE_LIFE)
set groupMaxHp = groupMaxHp + GetUnitState(u, UNIT_STATE_MAX_LIFE)
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
set perc = groupHp / groupMaxHp * 100.0
return perc
endfunction
function MostWoundedUnit takes group g returns unit
local group tempG = CopyGroup(g)
local unit u
local real uHp
set tempUnit = null
loop
set u = FirstOfGroup(tempG)
set uHp = GetUnitState(u, UNIT_STATE_LIFE)
exitwhen u == null
if tempUnit == null then
set tempUnit = u
endif
if IsAlive(u) and uHp < GetUnitState(tempUnit, UNIT_STATE_LIFE) then
set tempUnit = u
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return tempUnit
endfunction
function LessWoundedUnit takes group g returns unit
local group tempG = CopyGroup(g)
local unit u
local real uHp
set tempUnit = null
loop
set u = FirstOfGroup(tempG)
set uHp = GetUnitState(u, UNIT_STATE_LIFE)
exitwhen u == null
if tempUnit == null then
set tempUnit = u
endif
if IsAlive(u) and uHp > GetUnitState(tempUnit, UNIT_STATE_LIFE) then
set tempUnit = u
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return tempUnit
endfunction
endlibrary
scope GetOrderId
struct GetOrderId
private static method instantOrder takes nothing returns nothing
local integer id = GetIssuedOrderId()
if DetectHumanSlot(GetOwningPlayer(GetOrderedUnit())) then
call BJDebugMsg("Instant: " + I2S(id))
endif
endmethod
private static method pointOrder takes nothing returns nothing
local integer id = GetIssuedOrderId()
if DetectHumanSlot(GetOwningPlayer(GetOrderedUnit())) then
call BJDebugMsg("Point: " + I2S(id))
endif
endmethod
private static method targetOrder takes nothing returns nothing
local integer id = GetIssuedOrderId()
if DetectHumanSlot(GetOwningPlayer(GetOrderedUnit())) then
call BJDebugMsg("Target: " + I2S(id))
endif
endmethod
private static method unitOrder takes nothing returns nothing
local integer id = GetIssuedOrderId()
if DetectHumanSlot(GetOwningPlayer(GetOrderedUnit())) then
call BJDebugMsg("Unit: " + I2S(id))
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.instantOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.pointOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.targetOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function thistype.unitOrder)
endmethod
endstruct
endscope
library AIBehaviorController /* v1.41 by Riki
Special credits to Kingz for his Behaviour AI System, which inspired me and from which I
based to create this system.
Find it here: https://www.hiveworkshop.com/threads/behaviour-ai-system-v1-5.248639/
----------------------------
---NECESSARY REQUIREMENTS---
----------------------------
*/ requires /*
*/ MultidimensionalArray, /* by AGD: http://www.hiveworkshop.com/threads/snippet-multidimensional-array.289785/
*/ TimerUtils, /*
Others:
-Unit Indexer by Bribe: https://www.hiveworkshop.com/threads/gui-unit-indexer-1-4-0-0.197329/
-Damage Engine by Bribe: https://www.hiveworkshop.com/threads/damage-engine-5-7-1-2.201016/
-Table by Bribe: http://www.hiveworkshop.com/threads/snippet-new-table.188084/
---------------------------
---OPTIONAL REQUIREMENTS---
---------------------------
*/ optional /*
*/ UnitIndexerGUI, /* by Bribe: https://www.hiveworkshop.com/threads/snippet-gui-unit-indexer-vjass-plugin.268592/
*/ RegisterPlayerUnitEvent /* by Bannar: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
Others:
-RegisterNativeEvent by Bannar: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*/
//! novjass
================================== AI Behavior Controller ==================================
/* A system designed to make it easy for users to create AI by giving them the option to */
/* define custom behaviors for specific units or unit types as required. These behaviors */
/* include following a specified order under certain circumstances, using custom spells */
/* and abilities (including those based on Channel), etc. */
/* */
/* Sometimes it is frustrating to have to deal with an AI that does not cast certain */
/* spells because they are based on Channel or some other ability that the AI would not */
/* use under normal circunstances. */
/* */
/* The goal of this system in principle is to provide a tool that allows AI units to use */
/* any spell that the user wishes to create, and it has been extended to include even */
/* more options. */
/* */
/* Please note that this system is not designed to directly create custom AI, but to */
/* support AI creation. */
/*----------------------------------------------------------------------------------------*/
=================
- HOW TO IMPORT -
=================
/*----------------------------------------------------------------------------------------*/
/* For users who want to use the system in vJass, follow steps from 1 to 3. */
/* Those users who need the GUI version must follow all the steps listed. */
/* */
/* 1. Copy all the Necessary Requirements into your map. If you have newer versions of */
/* one of the listed requirements, you can skip it, but remember that you will need all */
/* of them. */
/* */
/* 2. Copy this library into your map. After this you will be ready to use this system. */
/* */
/* 3. I recommend copying the Optional Requirements as well, but you can skip this step */
/* if you want. */
/* */
/* 4. To use the GUI version, you must first follow all the steps above. After that, you */
/* need to copy all the content within the AIBC GUI category to your map. */
/*----------------------------------------------------------------------------------------*/
=======
- API -
=======
struct Controller
|- static method registerBehavior takes unit u, Behavior b, boolean isUserBehavior returns nothing
| // Adds a custom Behavior to a single unit.
| // If isUserBehavior is false, it will fire only for Computer owned units.
| // If isUserBehavior is true, it will fire only for Player owned units.
|
|- static method registerBehaviorType takes integer uTypeId, Behavior b, boolean isUserBehavior returns nothing
| // Adds a custom Behavior to an unit type.
| // If isUserBehavior is false, it will fire only for Computer owned units.
| // If isUserBehavior is true, it will fire only for Player owned units.
|
|- static method removeBehavior takes unit u, Behavior b returns nothing
| // Removes a custom Behavior from a single unit.
|
|- static method removeBehaviorType takes integer uTypeId, Behavior b returns nothing
| // Removes a custom Behavior from an unit type.
struct Behavior
|- method onPeriod takes unit u returns nothing
| // Fires on every interval. Use u to access the unit.
|
|- method onCast takes unit caster, unit target returns nothing
| // Fires when the unit casts an ability. Use caster to access the unit.
|
|- method onTargeted takes unit target, unit caster returns nothing
| // Fires when the unit is targeted with an ability. Use target to access the unit.
|
|- method onAttack takes unit attacker, unit target returns nothing
| // Fires when the unit attacks. Use attacker to access the unit.
|
|- method onAttacked takes unit target, unit attacker returns nothing
| // Fires when the unit is attacked. Use target to access the unit.
|
|- method onDamageDealt takes unit source, unit target returns nothing
| // Fires when the unit deals damage. Use source to access the unit.
|
|- method onDamageTaken takes unit target, unit source returns nothing
| // Fires when the unit receives damage. Use target to access the unit.
|
|- method onKill takes unit killer, unit victim returns nothing
| // Fires when the unit kills another. Use killer to access the unit.
|
|- method onAllyCasts takes unit u, unit ally, unit target returns nothing
|- method onAllyTargeted takes unit u, unit ally, unit caster returns nothing
|- method onAllyAttacks takes unit u, unit ally, unit target returns nothing
|- method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
|- method onAllyDmgDealt takes unit u, unit ally, unit target returns nothing
|- method onAllyDmgTaken takes unit u, unit ally, unit source returns nothing
|- method onAllyKills takes unit u, unit ally, unit victim returns nothing
|- method onAllyDeath takes unit u, unit ally, unit killer returns nothing
| // Events that will fire when a nearby friendly unit performs a certain action.
| // These events will only activate if OTHER_UNITS_EVENTS is set to true.
| // Use u to access the unit with custom behavior.
| // Use ally to access the allied unit that fires the event.
|
|- method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
|- method onEnemyTargeted takes unit u, unit enemy, unit caster returns nothing
|- method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
|- method onEnemyAttacked takes unit u, unit enemy, unit attacker returns nothing
|- method onEnemyDmgDealt takes unit u, unit enemy, unit target returns nothing
|- method onEnemyDmgTaken takes unit u, unit enemy, unit source returns nothing
|- method onEnemyKills takes unit u, unit enemy, unit victim returns nothing
|- method onEnemyDeath takes unit u, unit enemy, unit killer returns nothing
| // Events that will fire when a nearby enemy unit performs a certain action.
| // These events will only activate if OTHER_UNITS_EVENTS is set to true.
| // Use u to access the unit with custom behavior.
| // Use enemy to access the enemy unit that fires the event.
struct CustomOrder
|- method create takes unit u returns thistype
| // Registers the unit so it can use custom orders given by its Behavior.
|
|- method checkOrder takes string orderStr returns boolean
| // Checks if an orders is already registered.
|
|- method checkOrderById takes integer orderId returns boolean
| // Checks if an orders is already registered using integers for order id.
|
|- method isOrderInCooldown takes string orderStr returns boolean
| // Checks if an orders is in cooldown.
|
|- method isOrderInCooldownById takes integer orderId returns boolean
| // Checks if an orders is in cooldown using integers for order id.
|
|- method registerTargetOrder takes string orderStr, widget target, integer priority, real cooldown, integer manaCost returns nothing
|- method registerPointOrder takes string orderStr, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
|- method registerInstantOrder takes string orderStr, integer priority, real cooldown, integer manaCost returns nothing
| // Registers an order using strings.
|
|- method registerTargetOrderById takes integer orderId, widget target, integer priority, real cooldown, integer manaCost returns nothing
|- method registerPointOrderById takes integer orderId, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
|- method registerInstantOrderById takes integer orderId, integer priority, real cooldown, integer manaCost returns nothing
| // Registers an order using integers for order id.
//! endnovjass
native UnitAlive takes unit u returns boolean
/*------CONFIGURATION------*/
globals
private constant real PERIODIC_INTERVAL = .5 // Interval for Periodic Events
private constant real COOLDOWN_INTERVAL = .5 // Interval for Cooldown check
private constant integer MAX_ORDERS = 15 // Max number of orders for a unit per event
private constant integer LOWEST_PRIO = 1 // Minimum priority value for orders
private constant integer HIGHEST_PRIO = 100 // Maximum priority value for orders
private constant boolean OTHER_UNITS_EVENTS = true // Set to false if you don't want to fire onAlly / onEnemy events
private constant real UNIT_SEARCH_RANGE = 700. // Max range used to detect onAlly / onEnemy events
private constant real MIN_DMG = 1. // Minimum value required to fire a Damage event. Recommended value: 1.0
// Used for GroupEnumUnitsWithCollision function
private constant real MAX_COLLISION = 64. // Max collision size for units
private real tempX = 0. // Don't touch
private real tempY = 0. // Don't touch
private real tempRange = 0. // Don't touch
// Arrays, don't touch
Boolean2D OrderInCooldown
Boolean1D OrderExecuted
Integer1D RegOrders
Integer2D UnitBehavior
Integer2D UnitTypeBehavior
Boolean2D RequiredController
Boolean2D RequiredControllerType
Boolean1D HasCustomOrders
Integer1D TotalBehaviors
Integer1D TotalBehaviorsType
endglobals
/*------FUNCTIONS------*/
private function FilterInRange takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), tempX, tempY, tempRange)
endfunction
private function GroupEnumUnitsWithCollision takes group g, real x, real y, real radius returns group
set tempX = x
set tempY = y
set tempRange = radius
call GroupEnumUnitsInRange(g, x, y, radius+MAX_COLLISION, Filter(function FilterInRange))
return g
endfunction
private function BehaviorMapController takes mapcontrol mc returns boolean
if mc == MAP_CONTROL_USER then
return true
else
return false
endif
endfunction
/*------EVENTS------*/
interface Behavior
// Spell Cast Events
method onCast takes unit caster, unit target returns nothing defaults nothing
method onTargeted takes unit target, unit caster returns nothing defaults nothing
// Attack Events
method onAttack takes unit attacker, unit target returns nothing defaults nothing
method onAttacked takes unit target, unit attacker returns nothing defaults nothing
// Damage Events
method onDamageDealt takes unit source, unit target returns nothing defaults nothing
method onDamageTaken takes unit target, unit source returns nothing defaults nothing
// Kill Event
method onKill takes unit killer, unit victim returns nothing defaults nothing
// Periodic Event
method onPeriod takes unit u returns nothing defaults nothing
// OnAlly Events
method onAllyCasts takes unit u, unit ally, unit target returns nothing defaults nothing
method onAllyTargeted takes unit u, unit ally, unit caster returns nothing defaults nothing
method onAllyAttacks takes unit u, unit ally, unit target returns nothing defaults nothing
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing defaults nothing
method onAllyDmgDealt takes unit u, unit ally, unit target returns nothing defaults nothing
method onAllyDmgTaken takes unit u, unit ally, unit source returns nothing defaults nothing
method onAllyKills takes unit u, unit ally, unit victim returns nothing defaults nothing
method onAllyDeath takes unit u, unit ally, unit killer returns nothing defaults nothing
// OnEnemy Events
method onEnemyCasts takes unit u, unit enemy, unit target returns nothing defaults nothing
method onEnemyTargeted takes unit u, unit enemy, unit caster returns nothing defaults nothing
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing defaults nothing
method onEnemyAttacked takes unit u, unit enemy, unit attacker returns nothing defaults nothing
method onEnemyDmgDealt takes unit u, unit enemy, unit target returns nothing defaults nothing
method onEnemyDmgTaken takes unit u, unit enemy, unit source returns nothing defaults nothing
method onEnemyKills takes unit u, unit enemy, unit victim returns nothing defaults nothing
method onEnemyDeath takes unit u, unit enemy, unit killer returns nothing defaults nothing
endinterface
/*------MAIN SYSTEM------*/
struct Cooldown
private integer casterId
private integer order
private real duration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
endmethod
private static method check takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit caster
static if LIBRARY_UnitIndexerGUI then
set caster = GetUnitById(.casterId)
else
set caster = udg_UDexUnits[.casterId]
endif
if .duration > 0 and UnitAlive(caster) and OrderInCooldown[.casterId][.order] then
set .duration = .duration - COOLDOWN_INTERVAL
else
set OrderInCooldown[.casterId][.order] = false
call .destroy()
endif
set caster = null
endmethod
static method start takes integer unitId, integer orderId, real cooldown returns nothing
local thistype this = thistype.allocate()
set .casterId = unitId
set .order = orderId
set .duration = cooldown
set .loopTimer = NewTimerEx(this)
if not OrderInCooldown[.casterId][.order] then
set OrderInCooldown[.casterId][.order] = true
endif
call TimerStart(.loopTimer, COOLDOWN_INTERVAL, true, function thistype.check)
endmethod
endstruct
struct PreventNewOrders
private integer casterId
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
endmethod
private static method check takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set OrderExecuted[.casterId] = false
call .destroy()
endmethod
static method start takes integer unitId returns nothing
local thistype this = thistype.allocate()
set .casterId = unitId
set .loopTimer = NewTimerEx(this)
if not OrderExecuted[.casterId] then
set OrderExecuted[.casterId] = true
endif
call TimerStart(.loopTimer, 1., false, function thistype.check)
endmethod
endstruct
struct CustomOrder
private integer unitId
private integer totalOrders = 0
private integer array order[MAX_ORDERS]
private integer array priority[MAX_ORDERS]
private real array cooldown[MAX_ORDERS]
private integer array manaCost[MAX_ORDERS]
private widget array target[MAX_ORDERS]
private real array locX[MAX_ORDERS]
private real array locY[MAX_ORDERS]
private boolean array locBased[MAX_ORDERS]
private method onDestroy takes nothing returns nothing
local integer i = 0
set HasCustomOrders[.unitId] = false
loop
exitwhen i >= .totalOrders
set target[i] = null
set i = i + 1
endloop
endmethod
static method create takes unit u returns thistype
local thistype this
local integer id
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
if not HasCustomOrders[id] then
set HasCustomOrders[id] = true
set this = thistype.allocate()
set .unitId = id
set RegOrders[.unitId] = this
return RegOrders[.unitId]
else
return RegOrders[id]
endif
endmethod
method checkOrder takes string orderStr returns boolean
local integer i = 0
local boolean isRegistered = false
local integer orderId = OrderId(orderStr)
loop
exitwhen i >= .totalOrders or isRegistered
if orderId == .order[i] then
set isRegistered = true
endif
set i = i + 1
endloop
return isRegistered
endmethod
method checkOrderById takes integer orderId returns boolean
local integer i = 0
local boolean isRegistered = false
loop
exitwhen i >= .totalOrders or isRegistered
if orderId == .order[i] then
set isRegistered = true
endif
set i = i + 1
endloop
return isRegistered
endmethod
method isOrderInCooldown takes string orderStr returns boolean
local integer orderId = OrderId(orderStr)
return OrderInCooldown[.unitId][orderId]
endmethod
method isOrderInCooldownById takes integer orderId returns boolean
return OrderInCooldown[.unitId][orderId]
endmethod
method registerOrder takes integer orderId, widget target, real locX, real locY, boolean locBased, integer priority, real cooldown, integer manaCost returns nothing
set .order[.totalOrders] = orderId
if priority < LOWEST_PRIO then
set .priority[.totalOrders] = LOWEST_PRIO
elseif priority > HIGHEST_PRIO then
set .priority[.totalOrders] = HIGHEST_PRIO
else
set .priority[.totalOrders] = priority
endif
set .cooldown[.totalOrders] = cooldown
set .manaCost[.totalOrders] = manaCost
set .target[.totalOrders] = target
set .locX[.totalOrders] = locX
set .locY[.totalOrders] = locY
set .locBased[.totalOrders] = locBased
set .totalOrders = .totalOrders + 1
endmethod
// String Orders
method registerTargetOrder takes string orderStr, widget target, integer priority, real cooldown, integer manaCost returns nothing
local integer orderId = OrderId(orderStr)
call .registerOrder(orderId, target, 0, 0, false, priority, cooldown, manaCost)
endmethod
method registerPointOrder takes string orderStr, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
local integer orderId = OrderId(orderStr)
call .registerOrder(orderId, null, locX, locY, true, priority, cooldown, manaCost)
endmethod
method registerInstantOrder takes string orderStr, integer priority, real cooldown, integer manaCost returns nothing
local integer orderId = OrderId(orderStr)
call .registerOrder(orderId, null, 0, 0, false, priority, cooldown, manaCost)
endmethod
// Integer Orders
method registerTargetOrderById takes integer orderId, widget target, integer priority, real cooldown, integer manaCost returns nothing
call .registerOrder(orderId, target, 0, 0, false, priority, cooldown, manaCost)
endmethod
method registerPointOrderById takes integer orderId, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
call .registerOrder(orderId, null, locX, locY, true, priority, cooldown, manaCost)
endmethod
method registerInstantOrderById takes integer orderId, integer priority, real cooldown, integer manaCost returns nothing
call .registerOrder(orderId, null, 0, 0, false, priority, cooldown, manaCost)
endmethod
private method orderByPriority takes nothing returns nothing
local integer i = 0
local integer j
local integer tempOrder = 0
local integer tempPriority = 0
local real tempCooldown = 0
local integer tempMana = 0
local widget tempTarget
local real tempX = 0
local real tempY = 0
local boolean tempLocBased = false
local integer tempIndex
loop
exitwhen i > .totalOrders - 1
set j = i
set tempIndex = i
loop
exitwhen j > .totalOrders - 1
if .priority[tempIndex] < .priority[j + 1] then
set tempIndex = j + 1
endif
set j = j + 1
endloop
set tempOrder = .order[i]
set tempPriority = .priority[i]
set tempCooldown = .cooldown[i]
set tempMana = .manaCost[i]
set tempTarget = .target[i]
set tempX = .locX[i]
set tempY = .locY[i]
set tempLocBased = .locBased[i]
set .order[i] = .order[tempIndex]
set .priority[i] = .priority[tempIndex]
set .cooldown[i] = .cooldown[tempIndex]
set .manaCost[i] = .manaCost[tempIndex]
set .target[i] = .target[tempIndex]
set .locX[i] = .locX[tempIndex]
set .locY[i] = .locY[tempIndex]
set .locBased[i] = .locBased[tempIndex]
set .order[tempIndex] = tempOrder
set .priority[tempIndex] = tempPriority
set .cooldown[tempIndex] = tempCooldown
set .manaCost[tempIndex] = tempMana
set .target[tempIndex] = tempTarget
set .locX[tempIndex] = tempX
set .locY[tempIndex] = tempY
set .locBased[tempIndex] = tempLocBased
set i = i + 1
endloop
set tempTarget = null
endmethod
static method runOrders takes CustomOrder o returns nothing
local thistype this = o
local integer i = 0
local boolean hasUsedOrder = false
local unit caster
local real casterMana
local integer tempOrder
static if LIBRARY_UnitIndexerGUI then
set caster = GetUnitById(.unitId)
else
set caster = udg_UDexUnits[.unitId]
endif
set casterMana = GetUnitState(caster, UNIT_STATE_MANA)
if HasCustomOrders[.unitId] then
call .orderByPriority()
loop
exitwhen i >= .totalOrders or OrderExecuted[.unitId]
if not .isOrderInCooldownById(.order[i]) and casterMana >= .manaCost[i] then
if .target[i] != null then
if IssueTargetOrderById(caster, .order[i], .target[i]) then
set hasUsedOrder = true
endif
elseif .locBased[i] then
if IssuePointOrderById(caster, .order[i], .locX[i], .locY[i]) then
set hasUsedOrder = true
endif
elseif IssueImmediateOrderById(caster, .order[i]) then
set hasUsedOrder = true
endif
if hasUsedOrder then
call Cooldown.start(.unitId, .order[i], .cooldown[i])
call PreventNewOrders.start(.unitId)
endif
endif
set i = i + 1
endloop
endif
set caster = null
call .destroy()
endmethod
endstruct
private module Init
private static method onInit takes nothing returns nothing
call thistype.initArrays()
endmethod
endmodule
struct Controller extends array
private static timer loopTimer = CreateTimer()
//! textmacro LOAD_BEHAVIOR takes opName
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredControllerType[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredControllerType[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitTypeBehavior[id][i]
call b.$opName$(u, secU)
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredController[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredController[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitBehavior[id][i]
call b.$opName$(u, secU)
endif
set i = i + 1
endloop
if HasCustomOrders[id] then
call CustomOrder.runOrders(RegOrders[id])
endif
//! endtextmacro
//! textmacro LOAD_BEHAVIOR_OTHERS takes opName
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredControllerType[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredControllerType[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitTypeBehavior[id][i]
call b.$opName$(u, eventU, secU)
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredController[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredController[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitBehavior[id][i]
call b.$opName$(u, eventU, secU)
endif
set i = i + 1
endloop
if HasCustomOrders[id] then
call CustomOrder.runOrders(RegOrders[id])
endif
//! endtextmacro
public static method registerBehavior takes unit u, Behavior b, boolean isUserBehavior returns nothing
local integer i
local integer unitId
local mapcontrol mc
if isUserBehavior then
set mc = MAP_CONTROL_USER
else
set mc = MAP_CONTROL_COMPUTER
endif
static if LIBRARY_UnitIndexerGUI then
set unitId = GetUnitId(u)
else
set unitId = GetUnitUserData(u)
endif
set i = TotalBehaviors[unitId]
set UnitBehavior[unitId][i] = b
set RequiredController[unitId][i] = BehaviorMapController(mc)
set TotalBehaviors[unitId] = TotalBehaviors[unitId] + 1
set mc = null
endmethod
public static method registerBehaviorType takes integer uTypeId, Behavior b, boolean isUserBehavior returns nothing
local integer i = TotalBehaviorsType[uTypeId]
local mapcontrol mc
if isUserBehavior then
set mc = MAP_CONTROL_USER
else
set mc = MAP_CONTROL_COMPUTER
endif
set UnitTypeBehavior[uTypeId][i] = b
set RequiredControllerType[uTypeId][i] = BehaviorMapController(mc)
set TotalBehaviorsType[uTypeId] = TotalBehaviorsType[uTypeId] + 1
set mc = null
endmethod
public static method removeBehavior takes unit u, Behavior b returns nothing
local integer i = 0
local integer unitId
static if LIBRARY_UnitIndexerGUI then
set unitId = GetUnitId(u)
else
set unitId = GetUnitUserData(u)
endif
loop
exitwhen i == TotalBehaviors[unitId]
if UnitBehavior[unitId][i] == b then
set UnitBehavior[unitId][i] = 0
endif
set i = i + 1
endloop
endmethod
public static method removeBehaviorType takes integer uTypeId, Behavior b returns nothing
local integer i = 0
loop
exitwhen i == TotalBehaviorsType[uTypeId]
if UnitTypeBehavior[uTypeId][i] == b then
set UnitTypeBehavior[uTypeId][i] = 0
endif
set i = i + 1
endloop
endmethod
private static method registerCast takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
set u = GetSpellAbilityUnit()
set p = GetOwningPlayer(u)
set secU = GetSpellTargetUnit()
//! runtextmacro LOAD_BEHAVIOR("onCast")
set u = GetSpellTargetUnit()
set p = GetOwningPlayer(u)
set secU = GetSpellAbilityUnit()
//! runtextmacro LOAD_BEHAVIOR("onTargeted")
if OTHER_UNITS_EVENTS then
set eventU = GetSpellAbilityUnit()
set secU = GetSpellTargetUnit()
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyCasts")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyCasts")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = GetSpellTargetUnit()
set secU = GetSpellAbilityUnit()
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyTargeted")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyTargeted")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerAttack takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
set u = GetAttacker()
set p = GetOwningPlayer(u)
set secU = GetTriggerUnit()
//! runtextmacro LOAD_BEHAVIOR("onAttack")
set u = GetTriggerUnit()
set p = GetOwningPlayer(u)
set secU = GetAttacker()
//! runtextmacro LOAD_BEHAVIOR("onAttacked")
if OTHER_UNITS_EVENTS then
set eventU = GetAttacker()
set secU = GetTriggerUnit()
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyAttacks")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyAttacks")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = GetTriggerUnit()
set secU = GetAttacker()
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyAttacked")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyAttacked")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerDamage takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
if udg_DamageEventAmount >= MIN_DMG then
set u = udg_DamageEventSource
set p = GetOwningPlayer(u)
set secU = udg_DamageEventTarget
//! runtextmacro LOAD_BEHAVIOR("onDamageDealt")
set u = udg_DamageEventTarget
set p = GetOwningPlayer(u)
set secU = udg_DamageEventSource
//! runtextmacro LOAD_BEHAVIOR("onDamageTaken")
if OTHER_UNITS_EVENTS then
set eventU = udg_DamageEventSource
set secU = udg_DamageEventTarget
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyDmgDealt")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyDmgDealt")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = udg_DamageEventTarget
set secU = udg_DamageEventSource
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyDmgTaken")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyDmgTaken")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerKill takes nothing returns boolean
local unit u
local player p
local unit secU
local integer id
local Behavior b
local group g1 = CreateGroup()
local group g2 = CreateGroup()
local unit eventU
local integer i
set u = GetKillingUnit()
set p = GetOwningPlayer(u)
set secU = GetDyingUnit()
//! runtextmacro LOAD_BEHAVIOR("onKill")
if OTHER_UNITS_EVENTS then
set eventU = GetKillingUnit()
set secU = GetDyingUnit()
call GroupEnumUnitsWithCollision(g1, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g1)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyKills")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyKills")
endif
call GroupRemoveUnit(g1, u)
set u = null
set p = null
endloop
set eventU = GetDyingUnit()
set secU = GetKillingUnit()
call GroupEnumUnitsWithCollision(g2, GetUnitX(eventU), GetUnitY(eventU), UNIT_SEARCH_RANGE)
loop
set u = FirstOfGroup(g2)
exitwhen u == null
set p = GetOwningPlayer(u)
if IsUnitAlly(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onAllyDeath")
elseif IsUnitEnemy(eventU, p) and eventU != u then
//! runtextmacro LOAD_BEHAVIOR_OTHERS("onEnemyDeath")
endif
call GroupRemoveUnit(g2, u)
set u = null
set p = null
endloop
set eventU = null
endif
call DestroyGroup(g1)
call DestroyGroup(g2)
set g1 = null
set g2 = null
set u = null
set p = null
set secU = null
return false
endmethod
private static method registerPeriod takes nothing returns nothing
local group allUnits = CreateGroup()
local unit u
local player p
local integer id
local Behavior b
local integer i
call GroupEnumUnitsInRect(allUnits, bj_mapInitialPlayableArea, null)
loop
set u = FirstOfGroup(allUnits)
exitwhen u == null
set p = GetOwningPlayer(u)
set id = GetUnitTypeId(u)
set i = 0
loop
exitwhen i == TotalBehaviorsType[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredControllerType[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredControllerType[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitTypeBehavior[id][i]
call b.onPeriod(u)
endif
set i = i + 1
endloop
static if LIBRARY_UnitIndexerGUI then
set id = GetUnitId(u)
else
set id = GetUnitUserData(u)
endif
set i = 0
loop
exitwhen i == TotalBehaviors[id]
if (GetPlayerController(p) == MAP_CONTROL_USER and RequiredController[id][i] == BehaviorMapController(MAP_CONTROL_USER)) or /*
*/ (GetPlayerController(p) != MAP_CONTROL_USER and RequiredController[id][i] != BehaviorMapController(MAP_CONTROL_USER)) then
set b = UnitBehavior[id][i]
call b.onPeriod(u)
endif
set i = i + 1
endloop
if HasCustomOrders[id] then
call CustomOrder.runOrders(RegOrders[id])
endif
call GroupRemoveUnit(allUnits, u)
set u = null
set p = null
endloop
call DestroyGroup(allUnits)
set allUnits = null
endmethod
private static method initArrays takes nothing returns nothing
set OrderInCooldown = Boolean2D.create()
set OrderExecuted = Boolean1D.create()
set RegOrders = Integer1D.create()
set UnitBehavior = Integer2D.create()
set UnitTypeBehavior = Integer2D.create()
set RequiredController = Boolean2D.create()
set RequiredControllerType = Boolean2D.create()
set HasCustomOrders = Boolean1D.create()
set TotalBehaviors = Integer1D.create()
set TotalBehaviorsType = Integer1D.create()
endmethod
implement Init
private static method onInit takes nothing returns nothing
local trigger castEvent = CreateTrigger()
local trigger attackEvent = CreateTrigger()
local trigger damageEvent = CreateTrigger()
local trigger killEvent = CreateTrigger()
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.registerCast)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.registerAttack)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.registerKill)
call DestroyTrigger(castEvent)
call DestroyTrigger(attackEvent)
call DestroyTrigger(killEvent)
else
call TriggerRegisterAnyUnitEventBJ(castEvent, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(castEvent, function thistype.registerCast)
call TriggerRegisterAnyUnitEventBJ(attackEvent, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(attackEvent, function thistype.registerAttack)
call TriggerRegisterAnyUnitEventBJ(killEvent, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(killEvent, function thistype.registerKill)
endif
call TriggerRegisterVariableEvent(damageEvent, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddCondition(damageEvent, function thistype.registerDamage)
call TimerStart(thistype.loopTimer, PERIODIC_INTERVAL, true, function thistype.registerPeriod)
set castEvent = null
set attackEvent = null
set damageEvent = null
set killEvent = null
endmethod
endstruct
endlibrary
//==============================================================================
// Custom Race System by Archmage Owenalacaster (Note: with "CustomGetAllyKeyStructureCount" 1.30 fix)
//==============================================================================
//
// Purpose:
// - Creates the starting units for custom races and replaces the standard
// Melee Initialization trigger. Custom races are selected by race and
// handicap.
//
// Usage:
// - Register a new custom race with CustomRace.create(name, RACE, handicap)
// Handicaps: Valid handicap values are 1.0, 0.9, 0.8, 0.7, 0.6 and 0.5.
// - Register a new custom race for all handicaps of a single race with
// CustomRace.createAll(name, RACE)
// - Extend the registration of a race with c.register(RACE, handicap)
// - Set the townhall type with c.setTownHall(unitid)
// - Add a new worker type with c.addWorkerType(unitid, priority, qty)
// Priorities: c.NEAR_MINE spawns workers near the mine, where workers
// typically spawn.
// c.NEAR_HALL spawns workers near the town hall, where
// Ghouls spawn.
// - Add a random hero type with c.addHeroType(unitid)
// - Set the ai script used by computer players with c.setAIScript(stringpath)
// - Set a callback function with c.setCallback(CustomRaceCall.function)
// Callbacks: The callback is executed after all the starting units for a
// player are created, and its purpose is to provide enhanced
// initial behaviour for a race. A good example of this with the
// standard races would be the Undead Goldmine Haunting and
// Night Elves Goldmine Entangling.
// The callback function passes as arguments all the units
// generated in addition to the nearest goldmine detected.
// Please note that if a random hero is not created, the last
// argument will have a null value, so always do a check.
// - Get a player's custom race name string with GetPlayerCustomRaceName(player)
//
// Notes:
// - Supports a maximum of 24 custom races.
// - Maximum for worker and hero types are configurable.
//
// Requirements:
// - JassHelper version 0.9.E.0 or newer (older versions may still work).
//
// Installation:
// - Create a new trigger called CustomRaceSystem.
// - Convert it to custom text and replace all the code with this code.
//
// Special Thanks:
// - Alevice: He practically co-wrote the code.
// - cosmicat: His formula for circular unit formation.
// Co-developing the single-array registry.
//
//==============================================================================
library CustomRaceSystem initializer Init
//===========================================================================
// CONFIGURATION SECTION
//===========================================================================
globals
// Unit Type Constants
private constant integer MAX_WORKERTYPES = 4
private constant integer MAX_HEROTYPES = 4
endglobals
//===========================================================================
// END CONFIGURATION SECTION
//===========================================================================
function interface CustomRaceCall takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
private function r2S takes race r returns string
if r == RACE_HUMAN then
return "Human"
elseif r == RACE_ORC then
return "Orc"
elseif r == RACE_UNDEAD then
return "Undead"
elseif r == RACE_NIGHTELF then
return "Night Elf"
endif
return "Unknown"
endfunction
private function r2I takes race r returns integer
if r == RACE_HUMAN then
return 1
elseif r == RACE_ORC then
return 2
elseif r == RACE_UNDEAD then
return 3
elseif r == RACE_NIGHTELF then
return 4
endif
return 5
endfunction
globals
// Victory Defeat Variables
private string array KEY_STRUCTURE
private integer KEY_STRUCTURE_COUNT = 0
endglobals
//===========================================================================
// STRUCT DATA
//===========================================================================
private keyword createStartingUnits
struct CustomRace
string name
// Town Hall Variable
integer townhallType = 0
//string townhallName
// Town Hall name is not currently supported.
// Worker Variables
integer totalWorkerTypes = 0
integer array workerType[MAX_WORKERTYPES]
integer array workerPriority[MAX_WORKERTYPES]
integer array workerQty[MAX_WORKERTYPES]
// Random Hero Variables
integer totalHeroTypes = 0
integer array heroType[MAX_HEROTYPES]
// AI Script Directory String Variable
string aiscript = ""
// Callback Variable
private CustomRaceCall c
// Registry Variable
static integer array REGISTRY
// Spawn Priority Variables
static integer NEAR_MINE = 0
static integer NEAR_HALL = 1
static method get takes race r, real h returns CustomRace
return CustomRace(.REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))])
endmethod
method register takes race r, real h returns boolean
local CustomRace c = CustomRace.get(r,h)
if c != 0 then
debug call BJDebugMsg("|cffff0000Registration of "+.name+" failed due to conflict with "+c.name+" registered for "+r2S(r)+" race Handicap "+R2S(h))
return false
endif
set .REGISTRY[((r2I(r)-1)*6)+(10-R2I(h*10.))] = integer(this)
return true
endmethod
static method create takes string name, race r, real h returns CustomRace
local CustomRace c = CustomRace.allocate()
set c.name = name
if not c.register(r,h) then
call c.destroy()
return 0
endif
return c
endmethod
static method createAll takes string name, race r returns CustomRace
local CustomRace c = CustomRace.allocate()
set c.name = name
if not c.register(r,1.0) and not c.register(r,0.9) and not c.register(r,0.8) and not c.register(r,0.7) and not c.register(r,0.6) and not c.register(r,0.5) then
call c.destroy()
return 0
endif
return c
endmethod
method setTownHall takes integer hallid returns nothing
set .townhallType = hallid
set KEY_STRUCTURE[KEY_STRUCTURE_COUNT] = UnitId2String(hallid)
set KEY_STRUCTURE_COUNT = KEY_STRUCTURE_COUNT+1
endmethod
method addWorkerType takes integer workerid, integer priority, integer quantity returns nothing
set .workerType[.totalWorkerTypes] = workerid
set .workerPriority[.totalWorkerTypes] = priority
set .workerQty[.totalWorkerTypes] = quantity
set .totalWorkerTypes = .totalWorkerTypes+1
endmethod
method addHeroType takes integer heroid returns nothing
local integer i = 0
set .heroType[.totalHeroTypes] = heroid
set .totalHeroTypes = .totalHeroTypes+1
loop
call SetPlayerTechMaxAllowed(Player(i),heroid,1)
set i = i+1
exitwhen i == bj_MAX_PLAYERS
endloop
endmethod
private method getRandomHeroType takes nothing returns integer
local integer randomindex = GetRandomInt(0,.totalHeroTypes-1)
return .heroType[randomindex]
endmethod
method setAIScript takes string s returns nothing
set .aiscript = s
endmethod
method setCallback takes CustomRaceCall callb returns nothing
set .c = callb
endmethod
private method createRandomHero takes player p, location loc returns unit
local unit h = CreateUnitAtLoc(p, .getRandomHeroType(), loc, bj_UNIT_FACING)
if bj_meleeGrantHeroItems then
call MeleeGrantItemsToHero(h)
endif
return h
endmethod
method createStartingUnits takes player p returns nothing
local location startLoc = GetPlayerStartLocationLoc(p)
local location nearMineLoc = startLoc
local location nearTownLoc = startLoc
local location spawnLoc = startLoc
local location heroLoc = startLoc
local unit nearestMine = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
local unit myTownhall = null
local unit myRandHero = null
local group workerGroup = CreateGroup()
local integer workertypeindex = 0
local integer workerqty = 0
local integer spawnPriority = 0
if nearestMine != null then
set nearMineLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine),startLoc,320,0)
set nearTownLoc = MeleeGetProjectedLoc(startLoc,GetUnitLoc(nearestMine),288,0)
set heroLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine),startLoc,384,45)
endif
set myTownhall = CreateUnitAtLoc(p,.townhallType,startLoc,bj_UNIT_FACING)
loop
exitwhen workertypeindex == .totalWorkerTypes
set spawnPriority = .workerPriority[workertypeindex]
if (spawnPriority==.NEAR_HALL) then
set spawnLoc = nearTownLoc
elseif(spawnPriority==.NEAR_MINE) then
set spawnLoc = nearMineLoc
endif
loop
call GroupAddUnit(workerGroup, CreateUnitAtLoc(p,.workerType[workertypeindex],PolarProjectionBJ(spawnLoc,65,(I2R(workerqty)*(360.00 / I2R(.workerQty[workertypeindex]))) + 90),bj_UNIT_FACING))
set workerqty = workerqty + 1
exitwhen workerqty >= .workerQty[workertypeindex]
endloop
call RemoveLocation(spawnLoc)
set workerqty = 0
set workertypeindex = workertypeindex+1
endloop
if (IsMapFlagSet(MAP_RANDOM_HERO) and .totalHeroTypes>0 ) then
set myRandHero = .createRandomHero(p,heroLoc)
else
call SetPlayerState(p,PLAYER_STATE_RESOURCE_HERO_TOKENS,bj_MELEE_STARTING_HERO_TOKENS)
endif
if(.c!=0) then
call .c.evaluate(p,workerGroup,nearestMine,myTownhall,myRandHero)
else
call DestroyGroup(workerGroup)
endif
if nearMineLoc != startLoc then
call RemoveLocation(nearMineLoc)
call RemoveLocation(nearTownLoc)
call RemoveLocation(heroLoc)
endif
call RemoveLocation(startLoc)
set startLoc = null
set nearMineLoc = null
set nearTownLoc = null
set spawnLoc = null
set heroLoc = null
set nearestMine = null
set myTownhall = null
set myRandHero = null
set workerGroup = null
endmethod
endstruct
globals
private string array PLAYER_RACE
endglobals
function GetPlayerCustomRaceName takes player p returns string
return PLAYER_RACE[GetPlayerId(p)]
endfunction
//===========================================================================
// UNIT CREATION SECTION
//===========================================================================
private function CreateStartingUnitsForAllPlayers takes nothing returns nothing
local integer index = 0
local player indexPlayer
local race playerRace
local CustomRace c
loop
set indexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
set playerRace = GetPlayerRace(indexPlayer)
set c = CustomRace.get(playerRace,GetPlayerHandicap(indexPlayer)+0.01)
if (GetPlayerController(indexPlayer) == MAP_CONTROL_USER or (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER and c.aiscript != "" )) and c != 0 then
set PLAYER_RACE[index] = c.name
call c.createStartingUnits(indexPlayer)
elseif playerRace == RACE_HUMAN then
call MeleeStartingUnitsHuman(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseif playerRace == RACE_ORC then
call MeleeStartingUnitsOrc(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseif playerRace == RACE_NIGHTELF then
call MeleeStartingUnitsNightElf(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
elseif playerRace == RACE_UNDEAD then
call MeleeStartingUnitsUndead(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
else
call MeleeStartingUnitsUnknownRace(indexPlayer,GetStartLocationLoc(GetPlayerStartLocation(indexPlayer)),true,true,true)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
//===========================================================================
// CUSTOM MELEE AI SECTION
//===========================================================================
private function CustomMeleeStartingAI takes nothing returns nothing
local integer index = 0
local player indexPlayer
local race indexRace
local CustomRace c
loop
set indexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
set indexRace = GetPlayerRace(indexPlayer)
set c = CustomRace.get(indexRace,GetPlayerHandicap(indexPlayer)+0.01)
call SetPlayerHandicap(indexPlayer,1.0)
if (GetPlayerController(indexPlayer) == MAP_CONTROL_COMPUTER) then
// Run a race-specific melee AI script.
if c != 0 and c.aiscript != "" then
call StartMeleeAI(indexPlayer, c.aiscript)
elseif (indexRace == RACE_HUMAN) then
call PickMeleeAI(indexPlayer, "human.ai", null, null)
elseif (indexRace == RACE_ORC) then
call PickMeleeAI(indexPlayer, "orc.ai", null, null)
elseif (indexRace == RACE_UNDEAD) then
call PickMeleeAI(indexPlayer, "undead.ai", null, null)
call RecycleGuardPosition(bj_ghoul[index])
elseif (indexRace == RACE_NIGHTELF) then
call PickMeleeAI(indexPlayer, "elf.ai", null, null)
else
// Unrecognized race.
endif
call ShareEverythingWithTeamAI(indexPlayer)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
endfunction
//===========================================================================
// VICTORY DEFEAT SECTION
//===========================================================================
private function CustomGetAllyKeyStructureCount takes player whichPlayer returns integer
local integer i = 0
local integer keyStructs = 0
local integer playerIndex = 0
local player indexPlayer
loop
set indexPlayer = Player(playerIndex)
if (PlayersAreCoAllied(whichPlayer, indexPlayer)) then
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "townhall", true, true)
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "greathall", true, true)
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "necropolis", true, true)
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "treeoflife", true, true)
loop
set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, KEY_STRUCTURE[i], true, true)
set i = i+1
exitwhen i == KEY_STRUCTURE_COUNT
endloop
endif
set i = 0 //1.30 fix
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
return keyStructs
endfunction
private function CustomPlayerIsCrippled takes player whichPlayer returns boolean
local integer allyStructures = MeleeGetAllyStructureCount(whichPlayer)
local integer allyKeyStructures = CustomGetAllyKeyStructureCount(whichPlayer)
return (allyStructures > 0) and (allyKeyStructures <= 0)
endfunction
private function CustomCheckForCrippledPlayers takes nothing returns nothing
local integer playerIndex
local player indexPlayer
local boolean isNowCrippled
call MeleeCheckForLosersAndVictors()
if bj_finishSoonAllExposed then
return
endif
set playerIndex = 0
loop
set indexPlayer = Player(playerIndex)
set isNowCrippled = CustomPlayerIsCrippled(indexPlayer)
if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then
set bj_playerIsCrippled[playerIndex] = true
call TimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, function MeleeCrippledPlayerTimeout)
if (GetLocalPlayer() == indexPlayer) then
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_WARNING_HUMAN"))
endif
elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then
set bj_playerIsCrippled[playerIndex] = false
call PauseTimer(bj_crippledTimer[playerIndex])
if (GetLocalPlayer() == indexPlayer) then
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
if (MeleeGetAllyStructureCount(indexPlayer) > 0) then
if (bj_playerIsExposed[playerIndex]) then
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
else
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
endif
endif
endif
call MeleeExposePlayer(indexPlayer, false)
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
endfunction
private function CustomInitVictoryDefeat takes nothing returns nothing
local trigger checker = CreateTrigger()
local trigger trig
local integer index
local player indexPlayer
set bj_finishSoonTimerDialog = CreateTimerDialog(null)
call TriggerAddAction(checker, function CustomCheckForCrippledPlayers)
set trig = CreateTrigger()
call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_SOON)
call TriggerAddAction(trig, function MeleeTriggerTournamentFinishSoon)
set trig = CreateTrigger()
call TriggerRegisterGameEvent(trig, EVENT_GAME_TOURNAMENT_FINISH_NOW)
call TriggerAddAction(trig, function MeleeTriggerTournamentFinishNow)
set index = 0
loop
set indexPlayer = Player(index)
if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
set bj_meleeDefeated[index] = false
set bj_meleeVictoried[index] = false
set bj_playerIsCrippled[index] = false
set bj_playerIsExposed[index] = false
set bj_crippledTimer[index] = CreateTimer()
set bj_crippledTimerWindows[index] = CreateTimerDialog(bj_crippledTimer[index])
call TimerDialogSetTitle(bj_crippledTimerWindows[index], MeleeGetCrippledTimerMessage(indexPlayer))
call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null)
call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(checker, indexPlayer, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
call TriggerRegisterPlayerAllianceChange(checker, indexPlayer, ALLIANCE_PASSIVE)
call TriggerRegisterPlayerStateEvent(checker, indexPlayer, PLAYER_STATE_ALLIED_VICTORY, EQUAL, 1)
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_DEFEAT)
call TriggerAddAction(trig, function MeleeTriggerActionPlayerDefeated)
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
call TriggerAddAction(trig, function MeleeTriggerActionPlayerLeft)
else
set bj_meleeDefeated[index] = true
set bj_meleeVictoried[index] = false
if (IsPlayerObserver(indexPlayer)) then
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig, indexPlayer, EVENT_PLAYER_LEAVE)
call TriggerAddAction(trig, function MeleeTriggerActionPlayerLeft)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
call TimerStart(CreateTimer(), 2.0, false, function CustomCheckForCrippledPlayers)
endfunction
private function TimerAction takes nothing returns nothing
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, bj_MELEE_STARTING_TOD)
call MeleeStartingHeroLimit()
call MeleeGrantHeroItems()
call MeleeStartingResources()
call MeleeClearExcessUnits()
call CreateStartingUnitsForAllPlayers()
call CustomMeleeStartingAI()
call CustomInitVictoryDefeat()
call DestroyTimer(GetExpiredTimer())
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(),0,false,function TimerAction)
endfunction
endlibrary
library OriginalRacesSetup initializer Init requires CustomRaceSystem
private function WorkerHideToggle takes nothing returns nothing
call ShowUnit(GetEnumUnit(),IsUnitHidden(GetEnumUnit()))
endfunction
private function HauntGoldMine takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
call ForGroup(workers,function WorkerHideToggle)
call BlightGoldMineForPlayerBJ(goldmine,play)
call ForGroup(workers,function WorkerHideToggle)
call DestroyGroup(workers)
endfunction
private function EntangleGoldMine takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
call SetUnitPosition(townhall,GetUnitX(goldmine),GetUnitY(goldmine))
call IssueTargetOrder(townhall, "entangleinstant", goldmine)
call DestroyGroup(workers)
endfunction
private function Init takes nothing returns nothing
local CustomRace c = CustomRace.create("Human",RACE_HUMAN,1.0)
call c.setTownHall('htow') // Town Hall
call c.addWorkerType('hpea',c.NEAR_MINE,5) // Peasant
call c.addHeroType('Hpal') // Paladin
call c.addHeroType('Hamg') // Archmage
call c.addHeroType('Hmkg') // Mountain King
call c.addHeroType('Hblm') // Blood Mage
call c.setAIScript("human.ai")
set c = CustomRace.create("Orc",RACE_ORC,1.0)
call c.setTownHall('ogre') // Great Hall
call c.addWorkerType('opeo',c.NEAR_MINE,5) // Peon
call c.addHeroType('Obla') // Blademaster
call c.addHeroType('Ofar') // Far Seer
call c.addHeroType('Otch') // Tauren Chieftain
call c.addHeroType('Oshd') // Shadow Hunter
call c.setAIScript("orc.ai")
set c = CustomRace.create("Undead",RACE_UNDEAD,1.0)
call c.setTownHall('unpl') // Necropolis
call c.addWorkerType('uaco',c.NEAR_MINE,3) // Acolyte
call c.addWorkerType('ugho',c.NEAR_HALL,1) // Ghoul
call c.addHeroType('Udea') // Death Knight
call c.addHeroType('Ulic') // Lich
call c.addHeroType('Udre') // Dreadlord
call c.addHeroType('Ucrl') // Crypt Lord
call c.setCallback(CustomRaceCall.HauntGoldMine)
call c.setAIScript("undead.ai")
set c = CustomRace.create("Night Elf",RACE_NIGHTELF,1.0)
call c.setTownHall('etol') // Tree of Life
call c.addWorkerType('ewsp',c.NEAR_MINE,5) // Wisp
call c.addHeroType('Ekee') // Keeper of the Grove
call c.addHeroType('Emoo') // Priestess of the Moon
call c.addHeroType('Edem') // Demon Hunter
call c.addHeroType('Ewar') // Warden
call c.setCallback(CustomRaceCall.EntangleGoldMine)
call c.setAIScript("elf.ai")
endfunction
endlibrary
library AhnQirajSetup initializer Init requires CustomRaceSystem, EggSystem, MapSetup
globals
private constant real EGG_DISTANCE = 320.
private constant real EGG_1_DEGREES = 230.
private constant real EGG_2_DEGREES = 270.
private constant real EGG_3_DEGREES = 300.
private constant real EGG_RESPAWN_TIME = 16.
endglobals
private function QirajiConfig takes player play, group workers, unit goldmine, unit townhall, unit randhero returns nothing
local unit eggFirst
local unit eggSecond
local unit eggThird
local integer eggFirstId
local integer eggSecondId
local integer eggThirdId
local integer hallId
local Egg egg = Egg.create("Silithid Egg", play, EGG_ID)
call egg.setAttachedSettings(townhall, EGG_DISTANCE, EGG_1_DEGREES, 1)
call egg.setRespawnSettings(true, EGG_RESPAWN_TIME)
call egg.spawn()
set eggFirst = Egg.get(egg)
set eggFirstId = GetUnitId(eggFirst)
set egg = Egg.create("Silithid Egg", play, EGG_ID)
call egg.setAttachedSettings(townhall, EGG_DISTANCE, EGG_2_DEGREES, 2)
call egg.setRespawnSettings(true, EGG_RESPAWN_TIME)
call egg.spawn()
set eggSecond = Egg.get(egg)
set eggSecondId = GetUnitId(eggSecond)
set egg = Egg.create("Silithid Egg", play, EGG_ID)
call egg.setAttachedSettings(townhall, EGG_DISTANCE, EGG_3_DEGREES, 3)
call egg.setRespawnSettings(true, EGG_RESPAWN_TIME)
call egg.spawn()
set eggThird = Egg.get(egg)
set eggThirdId = GetUnitId(eggThird)
set hallId = GetUnitId(townhall)
set hiveId[eggFirstId] = hallId
set hiveId[eggSecondId] = hallId
set hiveId[eggThirdId] = hallId
set eggId[1][hallId] = eggFirstId
set eggId[2][hallId] = eggSecondId
set eggId[3][hallId] = eggThirdId
set eggFirst = null
set eggSecond = null
set eggThird = null
endfunction
private function Init takes nothing returns nothing
local CustomRace c = CustomRace.create("Ahn'Qiraj (Human)",RACE_HUMAN,0.9)
call c.setTownHall(NEST) // Silithid Nest
call c.addWorkerType(HATCHLING, c.NEAR_MINE, 3) // Hatchling
call c.addWorkerType(CRAWLER, c.NEAR_HALL, 1) // Crawler
call c.addHeroType(TYRANT) // Tyrant
call c.addHeroType(DEVOURER) // Devourer
call c.addHeroType(MINDSLAYER) // Mindslayer
call c.addHeroType(HIVE_LORD) // Hive Lord
call c.setCallback(CustomRaceCall.QirajiConfig)
call c.setAIScript("ahnQiraj.ai")
set c = CustomRace.create("Ahn'Qiraj (Orc)",RACE_ORC,0.9)
call c.setTownHall(NEST) // Silithid Nest
call c.addWorkerType(HATCHLING, c.NEAR_MINE, 3) // Hatchling
call c.addWorkerType(CRAWLER, c.NEAR_HALL, 1) // Crawler
call c.addHeroType(TYRANT) // Tyrant
call c.addHeroType(DEVOURER) // Devourer
call c.addHeroType(MINDSLAYER) // Mindslayer
call c.addHeroType(HIVE_LORD) // Hive Lord
call c.setCallback(CustomRaceCall.QirajiConfig)
call c.setAIScript("ahnQiraj.ai")
set c = CustomRace.create("Ahn'Qiraj (Undead)",RACE_UNDEAD,0.9)
call c.setTownHall(NEST) // Silithid Nest
call c.addWorkerType(HATCHLING, c.NEAR_MINE, 3) // Hatchling
call c.addWorkerType(CRAWLER, c.NEAR_HALL, 1) // Crawler
call c.addHeroType(TYRANT) // Tyrant
call c.addHeroType(DEVOURER) // Devourer
call c.addHeroType(MINDSLAYER) // Mindslayer
call c.addHeroType(HIVE_LORD) // Hive Lord
call c.setCallback(CustomRaceCall.QirajiConfig)
call c.setAIScript("ahnQiraj.ai")
set c = CustomRace.create("Ahn'Qiraj (Night Elf)",RACE_NIGHTELF,0.9)
call c.setTownHall(NEST) // Silithid Nest
call c.addWorkerType(HATCHLING, c.NEAR_MINE, 3) // Hatchling
call c.addWorkerType(CRAWLER, c.NEAR_HALL, 1) // Crawler
call c.addHeroType(TYRANT) // Tyrant
call c.addHeroType(DEVOURER) // Devourer
call c.addHeroType(MINDSLAYER) // Mindslayer
call c.addHeroType(HIVE_LORD) // Hive Lord
call c.setCallback(CustomRaceCall.QirajiConfig)
call c.setAIScript("ahnQiraj.ai")
endfunction
endlibrary
scope MainLoop
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant real OVERFLOW_MANA_PERC_REQ = 50.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function OverflowFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, OVERFLOW) and GetUnitPercentMana(u) >= OVERFLOW_MANA_PERC_REQ
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct GeneralLoop
private static method onLoop takes nothing returns nothing
local group allUnits = CreateGroup()
local integer lvl
local unit picked
local integer pickedId
local real pickedX
local real pickedY
local integer pickedType
local player pickedOwner
call GetUnitsInRect(allUnits, bj_mapInitialPlayableArea)
loop
set picked = FirstOfGroup(allUnits)
set pickedId = GetUnitId(picked)
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set pickedType = GetUnitTypeId(picked)
set pickedOwner = GetOwningPlayer(picked)
exitwhen picked == null
// Hatchlings Gather
if HasAbility(picked, HATCHLING_HARVEST_AI) and DetectHumanSlot(pickedOwner) then
call UnitRemoveAbility(picked, HATCHLING_HARVEST_AI)
call UnitAddAbility(picked, HATCHLING_HARVEST)
endif
// Overflow
if OverflowFilter(picked) then
call UnitAddBook(picked, BOOK_OVERFLOW)
else
call UnitRemoveAbility(picked, BOOK_OVERFLOW)
endif
// Hive Mind
if not HasBuff(picked, HIVE_MIND_BUFF) then
call UnitRemoveAbility(picked, BOOK_HIVE_MIND)
elseif IsAlive(picked) and HasBuff(picked, HIVE_MIND_BUFF) then
set lvl = GetUnitBuffLevel(pickedId, HIVE_MIND_BUFF)
call UnitAddBook(picked, BOOK_HIVE_MIND)
call SetUnitAbilityLevel(picked, HIVE_MIND_ATTSPEED, lvl)
endif
// Swarming Aura
if not HasBuff(picked, SWARMING_AURA_BUFF) then
call UnitRemoveAbility(picked, BOOK_SWARMING_AURA)
elseif IsAlive(picked) and HasBuff(picked, SWARMING_AURA_BUFF) then
call UnitAddBook(picked, BOOK_SWARMING_AURA)
endif
call GroupRemoveUnit(allUnits, picked)
set picked = null
set pickedOwner = null
endloop
call DestroyGroup(allUnits)
set allUnits = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), .2, true, function thistype.onLoop)
endmethod
endstruct
endscope
scope StopOrder
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant real CHECK = .02
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct StopOrder
private unit target
private player owner
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .target = null
set .owner = null
endmethod
private static method onCheck takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call IssueImmediateOrderById(.target, ORDER_stop)
call .destroy()
endmethod
static method run takes unit t, boolean usesMsg, string msg returns nothing
local thistype this = allocate()
set .target = t
set .owner = GetOwningPlayer(.target)
set .loopTimer = NewTimerEx(this)
if usesMsg then
call BlizzardMessage(msg, "|cffffcc00", 31, .owner)
endif
call IssueTargetOrderById(.target, ORDER_move, .target)
call TimerStart(.loopTimer, CHECK, false, function thistype.onCheck)
endmethod
endstruct
endscope
scope onDamage
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant real POX_DMG_MULTIPLIER = 20.
private constant real DUST_STORM_DMG_MULTIPLIER = 40.
private constant real ASPHYXIATE_MANA_COST = 10.
private constant real ASPHYXIATE_DMG_MULTIPLIER = 70.
private constant real SUNDER_DMG_MULTIPLIER = 30.
private constant real COUNTERSPELL_DMG_MULTIPLIER = 25.
private constant real COUNTERSPELL_DMG_MULTIPLIER_UP = 35.
private constant real COUNTERSPELL_MANA_PERC_REQ_UP = 50.
private constant real GLOOM_AURA_DMG_MULTIPLIER = 5. // per level
private constant real IMPERVIOUS_CARAPACE_DMG_MULTIPLIER_BASE = 15.
private constant real IMPERVIOUS_CARAPACE_DMG_MULTIPLIER_INC = 10.
private constant real IMPERVIOUS_CARAPACE_DEGREES = 75.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function SilithidPoxFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasBuff(target, SILITHID_POX_BUFF) and IsUnitEnemy(target, attackerOwner) and /*
*/ IsSilithid(attacker)
endfunction
private function DustStormFilter takes unit attacker, unit target returns boolean
return HasBuff(target, DUST_STORM_BUFF) and not IsMelee(attacker)
endfunction
private function AsphyxiateFilter takes unit attacker, unit target, player attackerOwner returns boolean
return GetUnitTypeId(attacker) == STRANGLER and HasAbility(attacker, ASPHYXIATE) and /*
*/ not IsBuilding(target) and not IsMagicImmune(target) and IsUnitEnemy(target, attackerOwner) and /*
*/ GetUnitMana(attacker) >= ASPHYXIATE_MANA_COST
endfunction
private function SunderFilter takes unit target returns boolean
return HasBuff(target, SUNDER_BUFF)
endfunction
private function CounterspellFilter takes unit target, player attackerOwner returns boolean
return HasAbility(target, COUNTERSPELL) and IsUnitEnemy(target, attackerOwner)
endfunction
private function GloomAuraFilter takes unit target returns boolean
return HasBuff(target, GLOOM_AURA_BUFF[1]) or HasBuff(target, GLOOM_AURA_BUFF[2]) or /*
*/ HasBuff(target, GLOOM_AURA_BUFF[3])
endfunction
private function ImperviousCarapaceFilter takes unit attacker, unit target returns boolean
return HasAbility(target, IMPERVIOUS_CARAPACE) and IsUnitFacingUnit(target, attacker, IMPERVIOUS_CARAPACE_DEGREES)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct onDamage
private static method Actions takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local integer attackedId = GetUnitId(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local player targetOwner = GetOwningPlayer(target)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local real lvl
// Damage calculations
if isSpell then
if dmgAmount >= 1 then
// First the modifiers that add or subtract, then those that multiply or divide
// ---ADD & SUBTRACT DAMAGE---
// Empty
// ---MULTIPLY & DIVIDE DAMAGE---
// Counterspell
if CounterspellFilter(target, attackerOwner) then
if GetUnitPercentMana(target) >= COUNTERSPELL_MANA_PERC_REQ_UP then
set dmgAmount = dmgAmount - dmgAmount * Percentage(COUNTERSPELL_DMG_MULTIPLIER_UP)
else
set dmgAmount = dmgAmount - dmgAmount * Percentage(COUNTERSPELL_DMG_MULTIPLIER)
endif
endif
// Gloom Aura
if GloomAuraFilter(target) then
if HasBuff(target, GLOOM_AURA_BUFF[3]) then
set lvl = 3
elseif HasBuff(target, GLOOM_AURA_BUFF[2]) then
set lvl = 2
elseif HasBuff(target, GLOOM_AURA_BUFF[1]) then
set lvl = 1
endif
set dmgAmount = dmgAmount + dmgAmount * Percentage(GLOOM_AURA_DMG_MULTIPLIER) * lvl
endif
// Impervious Carapace
if ImperviousCarapaceFilter(attacker, target) then
set lvl = GetUnitAbilityLevel(target, IMPERVIOUS_CARAPACE)
set dmgAmount = dmgAmount - dmgAmount * Percentage(IMPERVIOUS_CARAPACE_DMG_MULTIPLIER_BASE + IMPERVIOUS_CARAPACE_DMG_MULTIPLIER_INC * (lvl - 1))
endif
endif
else
if dmgAmount >= 1 then
// First the modifiers that add or subtract, then those that multiply or divide
// ---ADD & SUBTRACT DAMAGE---
// Empty
// ---MULTIPLY & DIVIDE DAMAGE---
// Silithid Pox
if SilithidPoxFilter(attacker, target, attackerOwner) then
set dmgAmount = dmgAmount + dmgAmount * Percentage(POX_DMG_MULTIPLIER)
endif
// Dust Storm
if DustStormFilter(attacker, target) then
set dmgAmount = dmgAmount - dmgAmount * Percentage(DUST_STORM_DMG_MULTIPLIER)
endif
// Asphyxiate
if AsphyxiateFilter(attacker, target, attackerOwner) and not asphyxiateDisabled[attackedId] then
set dmgAmount = dmgAmount - dmgAmount * Percentage(ASPHYXIATE_DMG_MULTIPLIER)
endif
// Sunder
if SunderFilter(target) then
set dmgAmount = dmgAmount - dmgAmount * Percentage(SUNDER_DMG_MULTIPLIER)
endif
// Impervious Carapace
if ImperviousCarapaceFilter(attacker, target) then
set lvl = GetUnitAbilityLevel(target, IMPERVIOUS_CARAPACE)
set dmgAmount = dmgAmount - dmgAmount * Percentage(IMPERVIOUS_CARAPACE_DMG_MULTIPLIER_BASE + IMPERVIOUS_CARAPACE_DMG_MULTIPLIER_INC * (lvl - 1))
endif
endif
endif
//Modifying the original damage
if dmgAmount >= 0 then
set udg_DamageEventAmount = dmgAmount
else
set udg_DamageEventAmount = 0
endif
set attacker = null
set attackerOwner = null
set target = null
set targetOwner = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageModifierEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.Actions)
set t = null
endmethod
endstruct
endscope
scope onDeath
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct onDeath
private static method Actions takes nothing returns nothing
local unit target = udg_DamageEventTarget
local real targetHP = GetUnitLife(target)
local real dmgAmount = udg_DamageEventAmount
//Checking for lethal damage and removing special effects and books
if dmgAmount >= targetHP then
if HasAbility(target, BOOK_SYMBIOTIC_LINK) then
call UnitRemoveAbility(target, BOOK_SYMBIOTIC_LINK)
endif
if HasAbility(target, BOOK_REVENGE) then
call UnitRemoveAbility(target, BOOK_REVENGE)
endif
if HasAbility(target, BOOK_OVERFLOW) then
call UnitRemoveAbility(target, BOOK_OVERFLOW)
endif
if HasAbility(target, BOOK_TORMENT) then
call UnitRemoveAbility(target, BOOK_TORMENT)
endif
if HasAbility(target, BOOK_EAT_CORPSE) then
call UnitRemoveAbility(target, BOOK_EAT_CORPSE)
endif
if HasAbility(target, BOOK_CONSUME) then
call UnitRemoveAbility(target, BOOK_CONSUME)
endif
if HasAbility(target, BOOK_HIVE_MIND) then
call UnitRemoveAbility(target, BOOK_HIVE_MIND)
endif
if HasAbility(target, BOOK_SWARMING_AURA) then
call UnitRemoveAbility(target, BOOK_SWARMING_AURA)
endif
endif
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.Actions)
set t = null
endmethod
endstruct
endscope
scope DelayedDamage
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct DelayedDamage
private unit attacker
private unit target
private real dmg
private attacktype atkT
private damagetype dmgT
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .attacker = null
set .target = null
set .atkT = null
set .dmgT = null
endmethod
private static method onCheck takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call UnitDamageTarget(.attacker, .target, .dmg, true, false, .atkT, .dmgT, null)
call .destroy()
endmethod
static method execute takes unit a, unit t, real d, attacktype aT, damagetype dT, real check returns nothing
local thistype this = allocate()
set .attacker = a
set .target = t
set .dmg = d
set .atkT = aT
set .dmgT = dT
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, check, false, function thistype.onCheck)
endmethod
endstruct
endscope
library EggSystem requires SpellUtils, MapSetup
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant real INTERVAL = .2
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Egg
private string name
private player owner
private integer unitType
private unit attachedBuilding
private real attachedDistance
private real attachedDegrees
private integer attachedPosition
private boolean canRespawn
private real respawnTime
private unit egg
private integer eggIdRef
private boolean isRespawning
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .owner = null
set .attachedBuilding = null
set .canRespawn = false
set .isRespawning = false
set .egg = null
endmethod
static method create takes string name, player p, integer unitId returns Egg
local Egg new = Egg.allocate()
set new.name = name
set new.owner = p
set new.unitType = unitId
return new
endmethod
static method get takes Egg new returns unit
return new.egg
endmethod
method setAttachedSettings takes unit building, real distance, real degrees, integer position returns nothing
set .attachedBuilding = building
set .attachedDistance = distance
set .attachedDegrees = degrees
set .attachedPosition = position
endmethod
method setRespawnSettings takes boolean canRespawn, real respawnTime returns nothing
set .canRespawn = canRespawn
set .respawnTime = respawnTime
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer buildingId = GetUnitId(.attachedBuilding)
local real x
local real y
local location tempLoc
if IsAlive(.attachedBuilding) then
if .isRespawning and .remainingDuration > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
elseif .isRespawning and .remainingDuration <= 0 then
set .isRespawning = false
set x = GetUnitX(.attachedBuilding) + .attachedDistance * Cos(.attachedDegrees * bj_DEGTORAD)
set y = GetUnitY(.attachedBuilding) + .attachedDistance * Sin(.attachedDegrees * bj_DEGTORAD)
set tempLoc = Location(x, y)
set .egg = CreateUnitAtLoc(.owner, .unitType, tempLoc, bj_UNIT_FACING)
set .eggIdRef = GetUnitId(.egg)
call SetUnitPathing(.egg, false)
call SetUnitPosition(.egg, x, y)
set hiveId[.eggIdRef] = buildingId
set eggId[.attachedPosition][buildingId] = .eggIdRef
endif
if not IsAlive(.egg) and .canRespawn and not .isRespawning then
set .isRespawning = true
set .remainingDuration = .respawnTime
set hiveId[.eggIdRef] = 0
set eggId[.attachedPosition][buildingId] = 0
elseif not IsAlive(.egg) and not .canRespawn then
call .destroy()
endif
else
call KillUnit(.egg)
call .destroy()
endif
call RemoveLocation(tempLoc)
set tempLoc = null
endmethod
method spawn takes nothing returns nothing
local real x
local real y
local location tempLoc
if IsAlive(.attachedBuilding) then
set x = GetUnitX(.attachedBuilding) + .attachedDistance * Cos(.attachedDegrees * bj_DEGTORAD)
set y = GetUnitY(.attachedBuilding) + .attachedDistance * Sin(.attachedDegrees * bj_DEGTORAD)
set tempLoc = Location(x, y)
set .loopTimer = NewTimerEx(this)
set .egg = CreateUnitAtLoc(.owner, .unitType, tempLoc, bj_UNIT_FACING)
set .eggIdRef = GetUnitId(.egg)
call SetUnitPathing(.egg, false)
call SetUnitPosition(.egg, x, y)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
else
call .destroy()
endif
call RemoveLocation(tempLoc)
set tempLoc = null
endmethod
endstruct
endlibrary
scope HatcheryEggs
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = SELECT_EGGS
private constant real EGG_DISTANCE = 320.
private constant real EGG_1_DEGREES = 230.
private constant real EGG_2_DEGREES = 270.
private constant real EGG_3_DEGREES = 300.
private constant real EGG_RESPAWN_TIME = 16.
private constant string ON_DEATH_FX = "war3mapImported\\BloodExplosion.mdx"
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function HiveHasEggs takes integer hiveId returns boolean
return eggId[1][hiveId] > 0 or eggId[2][hiveId] > 0 or eggId[3][hiveId] > 0
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct HatcheryEggs
private static method onCast takes nothing returns nothing
local unit building = GetSpellAbilityUnit()
local player buildingOwner = GetOwningPlayer(building)
local integer buildingId = GetUnitId(building)
local unit eggTemp
if HiveHasEggs(buildingId) then
call ClearSelectionForPlayer(buildingOwner)
if eggId[1][buildingId] > 0 then
set eggTemp = GetUnitById(eggId[1][buildingId])
call SelectUnitAddForPlayer(eggTemp, buildingOwner)
endif
if eggId[2][buildingId] > 0 then
set eggTemp = GetUnitById(eggId[2][buildingId])
call SelectUnitAddForPlayer(eggTemp, buildingOwner)
endif
if eggId[3][buildingId] > 0 then
set eggTemp = GetUnitById(eggId[3][buildingId])
call SelectUnitAddForPlayer(eggTemp, buildingOwner)
endif
endif
set building = null
set buildingOwner = null
set eggTemp = null
endmethod
private static method onBuild takes nothing returns nothing
local unit building = GetConstructedStructure()
local integer buildingType = GetUnitTypeId(building)
local integer buildingId = GetUnitId(building)
local player buildingOwner = GetOwningPlayer(building)
local unit eggFirst
local unit eggSecond
local unit eggThird
local integer eggFirstId
local integer eggSecondId
local integer eggThirdId
local Egg egg
if buildingType == NEST then
set egg = Egg.create("Silithid Egg", buildingOwner, EGG_ID)
call egg.setAttachedSettings(building, EGG_DISTANCE, EGG_1_DEGREES, 1)
call egg.setRespawnSettings(true, EGG_RESPAWN_TIME)
call egg.spawn()
set eggFirst = Egg.get(egg)
set eggFirstId = GetUnitId(eggFirst)
set egg = Egg.create("Silithid Egg", buildingOwner, EGG_ID)
call egg.setAttachedSettings(building, EGG_DISTANCE, EGG_2_DEGREES, 2)
call egg.setRespawnSettings(true, EGG_RESPAWN_TIME)
call egg.spawn()
set eggSecond = Egg.get(egg)
set eggSecondId = GetUnitId(eggSecond)
set egg = Egg.create("Silithid Egg", buildingOwner, EGG_ID)
call egg.setAttachedSettings(building, EGG_DISTANCE, EGG_3_DEGREES, 3)
call egg.setRespawnSettings(true, EGG_RESPAWN_TIME)
call egg.spawn()
set eggThird = Egg.get(egg)
set eggThirdId = GetUnitId(eggThird)
set hiveId[eggFirstId] = buildingId
set hiveId[eggSecondId] = buildingId
set hiveId[eggThirdId] = buildingId
set eggId[1][buildingId] = eggFirstId
set eggId[2][buildingId] = eggSecondId
set eggId[3][buildingId] = eggThirdId
endif
set building = null
set buildingOwner = null
set eggFirst = null
set eggSecond = null
set eggThird = null
endmethod
private static method onDeath takes nothing returns nothing
local unit dying = GetDyingUnit()
local real dyingX = GetUnitX(dying)
local real dyingY = GetUnitY(dying)
local location dyingLoc = Location(dyingX, dyingY)
local integer dyingType = GetUnitTypeId(dying)
if dyingType == NEST or dyingType == DEN or dyingType == HIVE then
call CreateEffectOnLoc(ON_DEATH_FX, dyingLoc)
call RemoveUnit(dying)
endif
call RemoveLocation(dyingLoc)
set dying = null
set dyingLoc = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.onBuild)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
endmethod
endstruct
endscope
scope EggsTraining
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = SELECT_EGGS
private constant real EGG_DISTANCE = 240.
private constant real EGG_1_DEGREES = 225.
private constant real EGG_2_DEGREES = 270.
private constant real EGG_3_DEGREES = 315.
private constant real EGG_RESPAWN_TIME = 16.
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct EggsTraining
private static method onTrain takes nothing returns nothing
local unit building = GetTriggerUnit()
local real buildingX = GetUnitX(building)
local real buildingY = GetUnitY(building)
local location buildingLoc = Location(buildingX, buildingY)
local integer buildingId = GetUnitId(building)
local integer buildingType = GetUnitTypeId(building)
local player buildingOwner = GetOwningPlayer(building)
local boolean checkSpawn = false
local integer spawnId
local unit rallyUnit
local destructable rallyDest
local location rallyPoint
local unit u
if buildingType == EGG_HATCHLING then
set spawnId = HATCHLING
set checkSpawn = true
elseif buildingType == EGG_CRAWLER then
set spawnId = CRAWLER
set checkSpawn = true
elseif buildingType == EGG_STALKER then
set spawnId = STALKER
set checkSpawn = true
elseif buildingType == EGG_SANDREAVER then
set spawnId = SANDREAVER
set checkSpawn = true
elseif buildingType == EGG_AMBUSHER then
set spawnId = AMBUSHER
set checkSpawn = true
elseif buildingType == EGG_LURKER then
set spawnId = LURKER
set checkSpawn = true
elseif buildingType == EGG_STINGER then
set spawnId = STINGER
set checkSpawn = true
endif
if checkSpawn then
set rallyUnit = GetUnitRallyUnit(building)
set rallyDest = GetUnitRallyDestructable(building)
set rallyPoint = GetUnitRallyPoint(building)
if rallyUnit == null and rallyDest == null and rallyPoint == null then
set rallyPoint = GetUnitLoc(building)
endif
set u = CreateUnitAtLoc(buildingOwner, spawnId, buildingLoc, bj_UNIT_FACING)
if rallyUnit != null then
call IssueTargetOrderById(u, ORDER_OFFSET, rallyUnit)
elseif rallyDest != null then
call IssueTargetOrderById(u, ORDER_OFFSET, rallyDest)
else
call IssuePointOrderById(u, ORDER_OFFSET, GetLocationX(rallyPoint), GetLocationY(rallyPoint))
endif
call KillUnit(building)
endif
call RemoveLocation(buildingLoc)
call RemoveLocation(rallyPoint)
set building = null
set buildingLoc = null
set buildingOwner = null
set rallyUnit = null
set rallyDest = null
set rallyPoint = null
set u = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_FINISH, function thistype.onTrain)
endmethod
endstruct
endscope
scope CrawlerLumber
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = CRAWLER_HARVEST
private constant integer RETURN_ID = CRAWLER_RETURN
private constant string ON_DEATH_FX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
private constant real MAX_DISTANCE = 800.
private constant real MAX_DISTANCE_AI = 700.
private constant real INACTIVITY_TIME = 10.
private constant real INTERVAL = .1
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
function FilterTree takes nothing returns boolean
return GetDestructableLife(GetFilterDestructable()) > 0 and IsTreeAlive(GetFilterDestructable())
endfunction
function InactiveNoTrees takes unit u returns boolean
return GetUnitCurrentOrder(u) == ORDER_stop or GetUnitCurrentOrder(u) == ORDER_holdposition or /*
*/ GetUnitCurrentOrder(u) <= 0
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct CrawlerLumber
private unit crawler
private unit waspFirst
private unit waspSec
private boolean isInactive
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
local player owner = GetOwningPlayer(.crawler)
local location waspFirstLoc = GetUnitLoc(.waspFirst)
local location waspSecLoc = GetUnitLoc(.waspSec)
call CreateEffectOnLoc(ON_DEATH_FX, waspFirstLoc)
call CreateEffectOnLoc(ON_DEATH_FX, waspSecLoc)
call RemoveUnit(.waspFirst)
call RemoveUnit(.waspSec)
if IsAlive(.crawler) and IsUnitPaused(.crawler) and DetectComputerSlot(owner) then
call PauseUnit(.crawler, false)
call StopOrder.run(.crawler, false, "")
endif
call RemoveLocation(waspFirstLoc)
call RemoveLocation(waspSecLoc)
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .crawler = null
set .waspFirst = null
set .waspSec = null
set owner = null
set waspFirstLoc = null
set waspSecLoc = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real workerX = GetUnitX(.crawler)
local real workerY = GetUnitY(.crawler)
local player workerOwner = GetOwningPlayer(.crawler)
local real wasp1X = GetUnitX(.waspFirst)
local real wasp1Y = GetUnitY(.waspFirst)
local real wasp2X = GetUnitX(.waspSec)
local real wasp2Y = GetUnitY(.waspSec)
local real distance1 = Distance(workerX, workerY, wasp1X, wasp1Y)
local real distance2 = Distance(workerX, workerY, wasp2X, wasp2Y)
local integer workerId = GetUnitId(.crawler)
local integer wasp1Id
local integer wasp2Id
if IsAlive(.crawler) then
set wasp1Id = GetUnitId(.waspFirst)
set wasp2Id = GetUnitId(.waspSec)
// Human Controller
if DetectHumanSlot(workerOwner) then
if .isInactive and .remainingDuration > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
if not inactivity[waspId[1][workerId]] and not inactivity[waspId[2][workerId]] then
set .isInactive = false
set .remainingDuration = .0
call UnitRemoveType(.crawler, UNIT_TYPE_PEON)
endif
elseif .isInactive and .remainingDuration <= 0 then
set hasWasps[workerId] = false
set inactivity[wasp1Id] = false
set inactivity[wasp2Id] = false
call .destroy()
endif
if (distance1 > MAX_DISTANCE or distance2 > MAX_DISTANCE or inactivity[wasp1Id] or inactivity[wasp2Id] or /*
*/ InactiveNoTrees(.waspFirst) or InactiveNoTrees(.waspSec)) and not .isInactive then
set .isInactive = true
set .remainingDuration = INACTIVITY_TIME
set inactivity[wasp1Id] = true
set inactivity[wasp2Id] = true
call UnitAddType(.crawler, UNIT_TYPE_PEON)
call IssueTargetOrderById(.waspFirst, ORDER_resumeharvesting, .crawler)
call IssueTargetOrderById(.waspSec, ORDER_resumeharvesting, .crawler)
endif
// AI Controller
elseif DetectComputerSlot(workerOwner) then
if .isInactive and .remainingDuration > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
if not inactivity[waspId[1][workerId]] and not inactivity[waspId[2][workerId]] then
set .isInactive = false
set .remainingDuration = .0
call PauseUnit(.crawler, true)
endif
elseif .isInactive and .remainingDuration <= 0 then
set hasWasps[workerId] = false
set inactivity[wasp1Id] = false
set inactivity[wasp2Id] = false
call .destroy()
endif
if (distance1 > MAX_DISTANCE_AI or distance2 > MAX_DISTANCE_AI or inactivity[wasp1Id] or inactivity[wasp2Id] or /*
*/ InactiveNoTrees(.waspFirst) or InactiveNoTrees(.waspSec)) and not .isInactive then
set .isInactive = true
set .remainingDuration = INACTIVITY_TIME
set inactivity[wasp1Id] = true
set inactivity[wasp2Id] = true
call IssueTargetOrderById(.waspFirst, ORDER_resumeharvesting, .crawler)
call IssueTargetOrderById(.waspSec, ORDER_resumeharvesting, .crawler)
call PauseUnit(.crawler, false)
call StopOrder.run(.crawler, false, "")
endif
endif
else
set hasWasps[workerId] = false
set inactivity[wasp1Id] = false
set inactivity[wasp2Id] = false
call .destroy()
endif
set workerOwner = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local destructable tree = GetSpellTargetDestructable()
local unit worker = GetSpellAbilityUnit()
local real workerX = GetUnitX(worker)
local real workerY = GetUnitY(worker)
local real workerFacing = GetUnitFacing(worker)
local integer workerId = GetUnitId(worker)
local player workerOwner = GetOwningPlayer(worker)
local real locX
local real locY
local unit wasp1
local unit wasp2
local integer waspTempId
if tree != null then
if not hasWasps[workerId] then
set locX = workerX + 120. * Cos(workerFacing * bj_DEGTORAD)
set locY = workerY + 120. * Sin(workerFacing * bj_DEGTORAD)
set wasp1 = CreateUnit(workerOwner, WASP, locX, locY, 270.)
set waspTempId = GetUnitId(wasp1)
call IssueTargetOrderById(wasp1, ORDER_harvest, tree)
set waspId[1][workerId] = waspTempId
set crawlerId[waspTempId] = workerId
set inactivity[waspTempId] = false
set locX = workerX - 120. * Sin(workerFacing * bj_DEGTORAD)
set locY = workerY - 120. * Cos(workerFacing * bj_DEGTORAD)
set wasp2 = CreateUnit(workerOwner, WASP, locX, locY, 270.)
set waspTempId = GetUnitId(wasp2)
call IssueTargetOrderById(wasp2, ORDER_harvest, tree)
set waspId[2][workerId] = waspTempId
set crawlerId[waspTempId] = workerId
set inactivity[waspTempId] = false
if DetectHumanSlot(workerOwner) then
call UnitRemoveType(worker, UNIT_TYPE_PEON)
elseif DetectComputerSlot(workerOwner) then
call PauseUnit(worker, true)
endif
set hasWasps[workerId] = true
set this = allocate()
set .crawler = worker
set .waspFirst = wasp1
set .waspSec = wasp2
set .isInactive = false
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
else
set wasp1 = GetUnitById(waspId[1][workerId])
set wasp2 = GetUnitById(waspId[2][workerId])
set inactivity[waspId[1][workerId]] = false
set inactivity[waspId[2][workerId]] = false
if DetectComputerSlot(workerOwner) then
call PauseUnit(worker, true)
endif
call IssueTargetOrderById(wasp1, ORDER_harvest, tree)
call IssueTargetOrderById(wasp2, ORDER_harvest, tree)
endif
endif
set tree = null
set worker = null
set workerOwner = null
set wasp1 = null
set wasp2 = null
endmethod
private static method onReturn takes nothing returns nothing
local unit worker = GetSpellAbilityUnit()
local integer workerId = GetUnitId(worker)
local unit wasp1
local unit wasp2
if hasWasps[workerId] then
set wasp1 = GetUnitById(waspId[1][workerId])
set wasp2 = GetUnitById(waspId[2][workerId])
call IssueTargetOrderById(wasp1, ORDER_resumeharvesting, worker)
call IssueTargetOrderById(wasp2, ORDER_resumeharvesting, worker)
endif
set worker = null
set wasp1 = null
set wasp2 = null
endmethod
private static method onOrder takes nothing returns nothing
local integer orderId = GetIssuedOrderId()
local unit worker = GetOrderedUnit()
local integer workerId = GetUnitId(worker)
local player workerOwner = GetOwningPlayer(worker)
local destructable tree
local unit crawler
if (orderId == ORDER_smart or orderId == ORDER_OFFSET) and HasAbility(worker, SPELL_ID) then
set tree = GetOrderTargetDestructable()
if tree != null and IsTreeAlive(tree) then
call IssueTargetOrderById(worker, ORDER_harvest, tree)
endif
elseif orderId == ORDER_harvest and HasAbility(worker, WASP_HARVEST) and inactivity[workerId] then
set crawler = GetUnitById(crawlerId[workerId])
call IssueTargetOrderById(worker, ORDER_move, crawler)
endif
set worker = null
set workerOwner = null
set tree = null
set crawler = null
endmethod
private static method onOrderFogOfWar takes nothing returns nothing
local integer orderId = GetIssuedOrderId()
local real pointX = GetOrderPointX()
local real pointY = GetOrderPointY()
local unit worker = GetOrderedUnit()
local destructable tree
local real treeX
local real treeY
local real pointsDistance
if (orderId == ORDER_smart or orderId == ORDER_OFFSET) and HasAbility(worker, SPELL_ID) then
set tree = GetClosestDestructable(pointX, pointY, Filter(function FilterTree))
set treeX = GetDestructableX(tree)
set treeY = GetDestructableY(tree)
set pointsDistance = Distance(pointX, pointY, treeX, treeY)
if tree != null and IsTreeAlive(tree) and pointsDistance < 5. then
call IssueTargetOrderById(worker, ORDER_harvest, tree)
endif
endif
set worker = null
set tree = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterSpellEffectEvent(RETURN_ID, function thistype.onReturn)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.onOrder)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.onOrderFogOfWar)
endmethod
endstruct
endscope
scope Hunger
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = HUNGER
private constant real LIFESTEAL_BASE = 25.
private constant real LIFESTEAL_INC = 5.
private constant real LIFESTEAL_AURA = 15. // Swarming Aura
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function HungerFilter takes unit attacker, unit target, player attackerOwner returns boolean
return IsAlive(attacker) and HasAbility(attacker, ABILITY_ID) and IsUnitEnemy(target, attackerOwner) and /*
*/ IsSilithid(attacker) and not IsBuilding(target)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Hunger
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local real attackerHP = GetUnitLife(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local integer lvl = GetUnitAbilityLevel(attacker, ABILITY_ID)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local real heal
if HungerFilter(attacker, target, attackerOwner) and dmgAmount >= 1. and not isSpell then
set heal = LIFESTEAL_BASE + LIFESTEAL_INC * (lvl - 1)
if HasBuff(attacker, SWARMING_AURA_BUFF) then
set heal = LIFESTEAL_BASE + LIFESTEAL_AURA + LIFESTEAL_INC * (lvl - 1)
endif
call SetUnitLife(attacker, attackerHP + dmgAmount * Percentage(heal))
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope StingBarrage
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = STING_BARRAGE
private constant integer UPGRADE_ID = STING_BARRAGE_UPG
private constant string MISSILE_MODEL = "Abilities\\Weapons\\QuillSprayMissile\\QuillSprayMissile.mdl"
private constant string ON_HIT_FX = "Abilities\\Weapons\\WardenMissile\\WardenMissile.mdl"
private constant real STING_DAMAGE = 6.
private constant real STING_DEGREES = 30.
private constant real STING_DISTANCE = 225.
private constant real STING_SPEED = 700.
private constant integer STING_MAX_STACKS = 4
private constant attacktype ATT_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function StingBarrageHitFilter takes unit hit, player attackerOwner returns boolean
return IsAlive(hit) and IsUnitEnemy(hit, attackerOwner) and not IsBuilding(hit) and not IsFlying(hit)
endfunction
private function StingBarrageFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasAbility(attacker, ABILITY_ID) and IsUnitEnemy(target, attackerOwner) and not IsBuilding(target) and /*
*/ IsResearched(attackerOwner, UPGRADE_ID)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct StingBarrage extends Missiles
method onHit takes unit hit returns boolean
local player owner = GetOwningPlayer(source)
if StingBarrageHitFilter(hit, owner) then
call UnitDamageTarget(dummy, hit, STING_DAMAGE, true, false, ATT_TYPE, DMG_TYPE, null)
call CreateEffectOnUnit(ON_HIT_FX, hit, "chest")
endif
set owner = null
return false
endmethod
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local integer attackerId = GetUnitId(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real angle = Atan2(targetY - attackerY, targetX - attackerX)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local integer i = 0
local real array toX
local real array toY
local thistype this
if StingBarrageFilter(attacker, target, attackerOwner) and dmgAmount >= 1. and not isSpell then
set stingsCount[attackerId] = stingsCount[attackerId] + 1
call SetUnitAbilityLevel(attacker, ABILITY_ID, stingsCount[attackerId] + 1)
if stingsCount[attackerId] >= STING_MAX_STACKS then
set stingsCount[attackerId] = 0
call SetUnitAbilityLevel(attacker, ABILITY_ID, 1)
set toX[0] = attackerX + STING_DISTANCE * Cos(angle)
set toY[0] = attackerY + STING_DISTANCE * Sin(angle)
set toX[1] = attackerX + STING_DISTANCE * Cos(angle + (STING_DEGREES * bj_DEGTORAD))
set toY[1] = attackerY + STING_DISTANCE * Sin(angle + (STING_DEGREES * bj_DEGTORAD))
set toX[2] = attackerX + STING_DISTANCE * Cos(angle - (STING_DEGREES * bj_DEGTORAD))
set toY[2] = attackerY + STING_DISTANCE * Sin(angle - (STING_DEGREES * bj_DEGTORAD))
loop
exitwhen i >= 3
set this = thistype.create(attackerX, attackerY, 20, toX[i], toY[i], 20)
set damage = STING_DAMAGE
set source = attacker
set model = MISSILE_MODEL
set speed = STING_SPEED
set scale = 1.
set collision = 10.
call launch()
set i = i + 1
endloop
endif
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope Neurotoxin
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = NEUROTOXIN
private constant integer UPGRADE_ID = NEUROTOXIN_UPG
private constant real CHANCE = 18.
private constant integer DUMMY_SPELL_ID = NEUROTOXIN_DUMMY
private constant integer DUMMY_SPELL_ORDER = ORDER_drunkenhaze
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function NeurotoxinFilter takes unit attacker, unit target, player attackerOwner returns boolean
return HasAbility(attacker, ABILITY_ID) and IsUnitEnemy(target, attackerOwner) and /*
*/ HasChance(CHANCE) and IsResearched(attackerOwner, UPGRADE_ID) and not IsBuilding(target) and /*
*/ not IsMechanical(target) and not IsMagicImmune(target)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Neurotoxin
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local unit dummy
if NeurotoxinFilter(attacker, target, attackerOwner) and dmgAmount >= 1. and not isSpell then
set dummy = DummyCaster(targetX, targetY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope SymbioticLink
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = SYMBIOTIC_LINK
private constant integer SPELL_ID_2 = SYMBIOTIC_LINK_FLY
private constant integer BOOK_ID = BOOK_SYMBIOTIC_LINK
private constant integer EFFECT_1_ID = SYMBIOTIC_LINK_MOVSPD
private constant integer BUFF_CASTER_ID = SYMBIOTIC_LINK_BUFF_C
private constant integer UNIT_TYPE = SWARMGUARD
private constant integer UNIT_TYPE_FLY = SWARMGUARD_FLY
private constant string CHAIN_MODEL = "SPLK"
private constant real HEAL = 8.
private constant real DISTANCE_MAX = 900.
private constant real DISTANCE_MAX_FLY = 1400.
private constant real DISTANCE_VANISH = 650.
private constant real DISTANCE_VANISH_FLY = 1050.
private constant real DURATION = 4.
private constant real INTERVAL = .1
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function OnCheckFilter takes unit caster, unit target returns boolean
return IsAlive(caster) and IsAlive(target) and GetUnitTypeId(caster) == UNIT_TYPE and /*
*/ Distance(GetUnitX(caster), GetUnitY(caster), GetUnitX(target), GetUnitY(target)) < DISTANCE_MAX
endfunction
private function OnCheckFlyFilter takes unit caster, unit target returns boolean
return IsAlive(caster) and IsAlive(target) and GetUnitTypeId(caster) == UNIT_TYPE_FLY and /*
*/ Distance(GetUnitX(caster), GetUnitY(caster), GetUnitX(target), GetUnitY(target)) < DISTANCE_MAX_FLY
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct SoulChain
private unit caster
private unit target
private lightning chain
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
call DestroyLightning(.chain)
set .loopTimer = null
set .caster = null
set .target = null
set .chain = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer casterId = GetUnitId(.caster)
local player casterOwner = GetOwningPlayer(.caster)
local real casterX = GetUnitX(.caster)
local real casterY = GetUnitY(.caster)
local real casterZ = GetUnitZ(.caster) + 50.
local real targetX = GetUnitX(.target)
local real targetY = GetUnitY(.target)
local real targetZ = GetUnitZ(.target) + 25.
local real targetHP = GetUnitLife(.target)
if .remainingDuration > 0 and (OnCheckFilter(.caster, .target) or OnCheckFlyFilter(.caster, .target)) then
set .remainingDuration = .remainingDuration - INTERVAL
call SetUnitLife(.target, targetHP + HEAL / 10)
call MoveLightningEx(.chain, true, casterX, casterY, casterZ, targetX, targetY, targetZ)
if Distance(casterX, casterY, targetX, targetY) >= DISTANCE_VANISH or /*
*/ Distance(casterX, casterY, targetX, targetY) >= DISTANCE_VANISH_FLY then
call SetLightningColor(.chain, .25, .25, 1., .6)
else
call SetLightningColor(.chain, .25, .25, 1., 1.)
endif
else
set linksCount[casterId] = linksCount[casterId] - 1
if IsAlive(.caster) and linksCount[casterId] <= 0 then
call UnitRemoveAbility(.caster, BOOK_ID)
call UnitRemoveAbility(.caster, BUFF_CASTER_ID)
endif
call .destroy()
endif
set casterOwner = null
endmethod
private static method onCast takes nothing returns nothing
local unit casterU = GetSpellAbilityUnit()
local real casterX = GetUnitX(casterU)
local real casterY = GetUnitY(casterU)
local real casterZ = GetUnitZ(casterU) + 50.
local integer casterId = GetUnitId(casterU)
local unit targetU = GetSpellTargetUnit()
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local real targetZ = GetUnitZ(targetU) + 25.
local thistype this
set this = allocate()
set .caster = casterU
set .target = targetU
set .chain = AddLightningEx(CHAIN_MODEL, true, casterX, casterY, casterZ, targetX, targetY, targetZ)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set linksCount[casterId] = linksCount[casterId] + 1
if linksCount[casterId] == 1 then
call UnitAddBook(.caster, BOOK_ID)
call UnitMakeAbilityPermanent(.caster, true, BOOK_ID)
call UnitMakeAbilityPermanent(.caster, true, EFFECT_1_ID)
endif
call SetLightningColor(.chain, .25, .25, 1., 1.)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set casterU = null
set targetU = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterSpellEffectEvent(SPELL_ID_2, function thistype.onCast)
endmethod
endstruct
endscope
scope Revenge
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = REVENGE
private constant integer BOOK_ID = BOOK_REVENGE
private constant real ACTIVATION_AMOUNT = 300.
private constant real HP_RESTORED = .1
private constant integer DUMMY_SPELL_ID = REVENGE_DUMMY
private constant integer DUMMY_SPELL_ORDER = ORDER_thunderbolt
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function RevengeAttackFilter takes unit attacker, unit target, player attackerOwner returns boolean
return IsAlive(attacker) and HasAbility(attacker, ABILITY_ID) and IsUnitEnemy(target, attackerOwner) and /*
*/ HasAbility(attacker, BOOK_ID) and not IsBuilding(target) and not IsFlying(target)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Revenge
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local integer attackerId = GetUnitId(attacker)
local real attackerHP = GetUnitLife(attacker)
local real attackerMaxHP = GetUnitMaxLife(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local integer targetId = GetUnitId(target)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local unit dummy
if HasAbility(target, ABILITY_ID) and revengeCount[targetId] < ACTIVATION_AMOUNT then
set revengeCount[targetId] = revengeCount[targetId] + dmgAmount
elseif HasAbility(target, ABILITY_ID) and revengeCount[targetId] >= ACTIVATION_AMOUNT then
set revengeCount[targetId] = 0
if not HasAbility(target, BOOK_ID) then
call UnitAddBook(target, BOOK_ID)
endif
endif
if RevengeAttackFilter(attacker, target, attackerOwner) and dmgAmount >= 1. and not isSpell then
call UnitRemoveAbility(attacker, BOOK_ID)
call SetUnitLife(attacker, attackerHP + attackerMaxHP * HP_RESTORED)
set dummy = DummyCaster(targetX, targetY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope SonicPulse
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = SONIC_PULSE
private constant real AOE = 900.
private constant integer DUMMY_SPELL_ID_1 = SONIC_PULSE_DUMMY_1
private constant integer DUMMY_SPELL_ORDER_1 = ORDER_thunderbolt
private constant integer DUMMY_SPELL_ID_2 = SONIC_PULSE_DUMMY_2
private constant integer DUMMY_SPELL_ORDER_2 = ORDER_farsight
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function SonicPulseFilter takes unit target, player casterOwner returns boolean
return IsUnitEnemy(target, casterOwner) and not IsBuilding(target) and /*
*/ (IsSummoned(target) or IsUnitIllusion(target))
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct SonicPulse
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local player casterOwner = GetOwningPlayer(caster)
local group enemies = CreateGroup()
local unit picked
local real pickedX
local real pickedY
local unit dummy
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_2)
call IssuePointOrderById(dummy, DUMMY_SPELL_ORDER_2, casterX, casterY)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_2, 1., true)
set dummy = null
call GroupEnumUnitsWithCollision(enemies, casterX, casterY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if SonicPulseFilter(picked, casterOwner) then
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set dummy = DummyCaster(pickedX, pickedY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_1)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_1, picked)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_1, 1., true)
set dummy = null
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call DestroyGroup(enemies)
set caster = null
set casterOwner = null
set enemies = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Asphyxiate
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = ASPHYXIATE
private constant integer ORDER_ACTIVATE_ID = ORDER_replenishmanaon
private constant integer ORDER_DEACTIVATE_ID = ORDER_replenishmanaoff
private constant integer ORDER_ID = ORDER_replenishmana
private constant real MANA_COST = 10.
private constant integer DUMMY_SPELL_ID_1 = ASPHYXIATE_DUMMY_1 // Normal attack lightning
private constant integer DUMMY_SPELL_ID_2 = ASPHYXIATE_DUMMY_2 // Enhanced attack lightning
private constant integer DUMMY_SPELL_ID_3 = ASPHYXIATE_DUMMY_3 // Debuff
private constant integer DUMMY_SPELL_ORDER_1 = ORDER_chainlightning
private constant integer DUMMY_SPELL_ORDER_2 = ORDER_cripple
// Sunder
private constant integer STRANGLER_UPG = STRANGLER_TRAIN_UPG
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function AsphyxiateCasterFilter takes unit caster returns boolean
return GetUnitTypeId(caster) == STRANGLER and HasAbility(caster, SPELL_ID)
endfunction
private function AsphyxiateTargetFilter takes unit target, player attackerOwner returns boolean
return IsUnitEnemy(target, attackerOwner) and not IsBuilding(target) and not IsMagicImmune(target)
endfunction
private function SunderFilter takes unit attacker, player attackerOwner returns boolean
return HasAbility(attacker, SUNDER) and GetPlayerTechCount(attackerOwner, STRANGLER_UPG, true) >= 2
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Asphyxiate
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local real attackerX = GetUnitX(attacker)
local real attackerY = GetUnitY(attacker)
local real attackerFacing = GetUnitFacing(attacker)
local integer attackerId = GetUnitId(attacker)
local real attackerHP = GetUnitLife(attacker)
local real attackerMana = GetUnitMana(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local real locX = attackerX + 50. * Cos(attackerFacing * bj_DEGTORAD)
local real locY = attackerY + 50. * Sin(attackerFacing * bj_DEGTORAD)
local unit dummy
if AsphyxiateCasterFilter(attacker) and dmgAmount >= 1. and not isSpell then
if asphyxiateDisabled[attackerId] or not AsphyxiateTargetFilter(target, attackerOwner) or /*
*/ attackerMana < MANA_COST then
set dummy = DummyCaster(locX, locY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_1)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_1, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_1, 2., true)
set dummy = null
else
call SetUnitMana(attacker, attackerMana - MANA_COST)
set dummy = DummyCaster(locX, locY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_2)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_1, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_2, 2., true)
set dummy = null
set dummy = DummyCaster(locX, locY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_3)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_2, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_3, 2., true)
set dummy = null
if SunderFilter(attacker, attackerOwner) then
call Sunder.onRun(attacker, target)
endif
endif
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
private static method onActivate takes nothing returns nothing
local unit caster = GetOrderedUnit()
local integer casterId = GetUnitId(caster)
if AsphyxiateCasterFilter(caster) then
set asphyxiateDisabled[casterId] = false
endif
set caster = null
endmethod
private static method onDeactivate takes nothing returns nothing
local unit caster = GetOrderedUnit()
local integer casterId = GetUnitId(caster)
if AsphyxiateCasterFilter(caster) then
set asphyxiateDisabled[casterId] = true
endif
set caster = null
endmethod
private static method onOrder takes nothing returns nothing
local unit caster = GetOrderedUnit()
if AsphyxiateCasterFilter(caster) then
call StopOrder.run(caster, false, "")
endif
set caster = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
call RegisterOrderEvent(ORDER_ACTIVATE_ID, function thistype.onActivate)
call RegisterOrderEvent(ORDER_DEACTIVATE_ID, function thistype.onDeactivate)
call RegisterOrderEvent(ORDER_ID, function thistype.onOrder)
endmethod
endstruct
endscope
scope NullifyingGrasp
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = NULLIFYING_GRASP
// Sunder
private constant integer STRANGLER_UPG = STRANGLER_TRAIN_UPG
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function SunderFilter takes unit caster, player casterOwner returns boolean
return HasAbility(caster, SUNDER) and GetPlayerTechCount(casterOwner, STRANGLER_UPG, true) >= 2
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct NullifyingGrasp
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
if SunderFilter(caster, casterOwner) then
call Sunder.onRun(caster, target)
endif
set caster = null
set casterOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Sunder
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant string SUNDER_CASTER_FX = "war3mapImported\\DarknessLeechCaster.mdx"
private constant string SUNDER_TARGET_FX = "war3mapImported\\DarknessLeechTarget.mdx"
private constant real SUNDER_HEAL = 10.
private constant integer DUMMY_SUNDER_ID = SUNDER_DUMMY
private constant integer DUMMY_SUNDER_ORDER = ORDER_rejuvination
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Sunder
static method onRun takes unit caster, unit target returns nothing
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local real casterHP = GetUnitLife(caster)
local player casterOwner = GetOwningPlayer(caster)
local unit dummy
call SetUnitLife(caster, casterHP + SUNDER_HEAL)
call CreateEffectOnUnit(SUNDER_CASTER_FX, caster, "origin")
call CreateEffectOnUnit(SUNDER_TARGET_FX, target, "origin")
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SUNDER_ID)
call IssueTargetOrderById(dummy, DUMMY_SUNDER_ORDER, caster)
call RemoveAbilityTimed.create(dummy, DUMMY_SUNDER_ID, 1., true)
set dummy = null
set casterOwner = null
endmethod
endstruct
endscope
scope NecroticPoison
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer BUFF_ID = NECROTIC_POISON_BUFF
private constant real HEAL_DECREASE_PERC = 30.
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct NecroticPoison
private static method onHeal takes nothing returns nothing
local unit healTarget = udg_heal_target
local real healTargetHP = GetUnitLife(healTarget)
local real healAmount
if HasBuff(healTarget, BUFF_ID) then
set healAmount = udg_heal_amount * Percentage(HEAL_DECREASE_PERC)
call SetUnitLife(healTarget, healTargetHP - healAmount)
endif
set healTarget = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_HealEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onHeal)
set t = null
endmethod
endstruct
endscope
scope Overflow
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = OVERFLOW
private constant integer BOOK_ID = BOOK_OVERFLOW
private constant real MANA_STEAL = 40.
private constant integer DUMMY_SPELL_ID = OVERFLOW_DUMMY
private constant integer DUMMY_SPELL_ORDER = ORDER_frostnova
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function OverflowFilter takes unit attacker, unit target, player attackerOwner returns boolean
return IsAlive(attacker) and HasAbility(attacker, ABILITY_ID) and IsUnitEnemy(target, attackerOwner) and /*
*/ not IsBuilding(target)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Overflow
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local real attackerMana = GetUnitMana(attacker)
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local unit dummy
if OverflowFilter(attacker, target, attackerOwner) and dmgAmount >= 1. and not isSpell then
call SetUnitMana(attacker, attackerMana + dmgAmount * Percentage(MANA_STEAL))
if HasAbility(attacker, BOOK_ID) then
set dummy = DummyCaster(targetX, targetY, 0., attackerOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope Counterspell
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = COUNTERSPELL
private constant string ON_TARGET_FX = "war3mapImported\\DarkShield.mdx"
private constant string MISSILE_MODEL = "war3mapImported\\Void Arrow.mdx"
private constant real DMG_AMOUNT = 30.
private constant real MANA_PERC_REQ = 50.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function CounterspellFilter takes unit caster, unit target, player targetOwner returns boolean
return IsAlive(caster) and HasAbility(target, ABILITY_ID) and IsUnitEnemy(caster, targetOwner) and /*
*/ not IsBuilding(caster) and not IsMagicImmune(caster) and GetUnitPercentMana(target) >= MANA_PERC_REQ
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct Counterspell extends Missiles
method onHit takes unit hit returns boolean
if hit == target then
call UnitDamageTarget(source, hit, damage, true, false, ATT_TYPE, DMG_TYPE, null)
endif
return false
endmethod
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local real casterZ = GetUnitFlyHeight(caster)
local unit targetU = GetSpellTargetUnit()
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local real targetZ = GetUnitFlyHeight(targetU)
local player targetOwner = GetOwningPlayer(targetU)
local thistype this
if CounterspellFilter(caster, targetU, targetOwner) then
call CreateEffectOnUnit(ON_TARGET_FX, targetU, "origin")
set this = thistype.create(targetX, targetY, targetZ, casterX, casterY, casterZ)
set damage = DMG_AMOUNT
set source = targetU
set target = caster
set model = MISSILE_MODEL
set speed = 1000.
set collision = 20.
call launch()
endif
set caster = null
set targetU = null
set targetOwner = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endstruct
endscope
scope SpectralBurst
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = SPECTRAL_BURST
private constant real AOE = 500.
private constant integer DUMMY_SPELL_ID_1 = SPECTRAL_BURST_DUMMY_1 // Lightning
private constant integer DUMMY_SPELL_ID_2 = SPECTRAL_BURST_DUMMY_2 // Buff
private constant integer DUMMY_SPELL_ORDER_1 = ORDER_chainlightning
private constant integer DUMMY_SPELL_ORDER_2 = ORDER_battleroar
// Soul Drain
private constant real SOUL_DRAIN_CHANCE_BASE = 15.
private constant real SOUL_DRAIN_CHANCE_INC = 5.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function SpectralBurstFilter takes unit target, unit picked, player casterOwner returns boolean
return IsAlive(picked) and IsUnitEnemy(picked, casterOwner) and picked != target and /*
*/ not IsBuilding(picked) and not IsMagicImmune(picked) and not IsMechanical(picked)
endfunction
private function SoulDrainFilter takes unit caster, integer lvl returns boolean
return HasAbility(caster, SOUL_DRAIN) and HasChance(SOUL_DRAIN_CHANCE_BASE + SOUL_DRAIN_CHANCE_INC * (lvl - 1))
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct SpectralBurst
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local integer touchLvl = GetUnitAbilityLevel(caster, SOUL_DRAIN)
local group enemies = CreateGroup()
local integer enemiesHit = 1
local unit picked
local unit dummy
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, AOE)
loop
set picked = GroupRandomUnit(enemies)
exitwhen picked == null or enemiesHit >= 3
if SpectralBurstFilter(target, picked, casterOwner) then
set enemiesHit = enemiesHit + 1
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_1)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID_1, lvl)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_1, picked)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_1, 2., true)
set dummy = null
if SoulDrainFilter(caster, touchLvl) then
call SoulDrain.onRun(caster, picked)
endif
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID_2)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID_2, enemiesHit)
call IssueImmediateOrderById(dummy, DUMMY_SPELL_ORDER_2)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID_2, 1., true)
set dummy = null
if SoulDrainFilter(caster, touchLvl) then
call SoulDrain.onRun(caster, target)
endif
call DestroyGroup(enemies)
set caster = null
set casterOwner = null
set target = null
set enemies = null
set picked = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Torment
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = TORMENT
private constant integer BOOK_ID = BOOK_TORMENT
private constant integer EFFECT_ID = TORMENT_BUFF_ABILITY
private constant integer BUFF_ID = TORMENT_BUFF
private constant integer ORDER_ID = ORDER_thunderbolt
private constant real DMG_PERC = 20. // per level
private constant real DURATION = 20.
private constant real INTERVAL = .5
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
// Soul Drain
private constant real SOUL_DRAIN_CHANCE_BASE = 15.
private constant real SOUL_DRAIN_CHANCE_INC = 5.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function TormentDamageFilter takes unit target, unit linkedUnit returns boolean
return GetUnitTypeId(target) == TYRANT and linkedUnit != null and IsAlive(linkedUnit) and /*
*/ HasAbility(linkedUnit, BOOK_ID)
endfunction
private function TormentOrderFilter takes unit caster, unit target returns boolean
return GetUnitTypeId(caster) == TYRANT and HasAbility(target, BOOK_ID)
endfunction
private function SoulDrainFilter takes unit caster, integer lvl returns boolean
return HasAbility(caster, SOUL_DRAIN) and HasChance(SOUL_DRAIN_CHANCE_BASE + SOUL_DRAIN_CHANCE_INC * (lvl - 1))
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Torment
private unit caster
private unit target
private integer lvl
private real remainingDuration
private timer loopTimer
private static method onDamage takes nothing returns nothing
local unit target = udg_DamageEventTarget
local integer targetId = GetUnitId(target)
local unit linkedUnit = GetUnitById(tormentLinked[targetId])
local integer linkedId = GetUnitId(linkedUnit)
local integer lvl = unitBuffLevel[linkedId][BUFF_ID]
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local real returnedDmg = dmgAmount * Percentage(DMG_PERC * (lvl))
if TormentDamageFilter(target, linkedUnit) and dmgAmount >= 1. then
call DelayedDamage.execute(target, linkedUnit, returnedDmg, ATT_TYPE, DMG_TYPE, .02)
endif
set target = null
set linkedUnit = null
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .target = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer casterId = GetUnitId(.caster)
local integer targetId = GetUnitId(.target)
if .remainingDuration > 0 and IsAlive(.caster) and IsAlive(.target) then
set .remainingDuration = .remainingDuration - INTERVAL
if not HasAbility(.target, BOOK_ID) then
call UnitAddAbility(.target, BOOK_ID)
call SetUnitAbilityLevel(.target, EFFECT_ID, .lvl)
set unitBuffLevel[targetId][BUFF_ID] = .lvl
endif
else
if IsAlive(.target) and HasAbility(.target, BOOK_ID) then
call UnitRemoveAbility(.target, BOOK_ID)
call UnitRemoveAbility(.target, BUFF_ID)
endif
set tormentLinked[casterId] = 0
call .destroy()
endif
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer casterId
local integer targetId
local integer touchLvl
set .caster = GetSpellAbilityUnit()
set .target = GetSpellTargetUnit()
set .lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set casterId = GetUnitId(.caster)
set targetId = GetUnitId(.target)
call UnitAddAbility(.target, BOOK_ID)
call SetUnitAbilityLevel(.target, EFFECT_ID, .lvl)
set unitBuffLevel[targetId][BUFF_ID] = .lvl
set tormentLinked[casterId] = targetId
set touchLvl = GetUnitAbilityLevel(.caster, SOUL_DRAIN)
if SoulDrainFilter(.caster, touchLvl) then
call SoulDrain.onRun(.caster, .target)
endif
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
private static method onOrder takes nothing returns nothing
local unit caster = GetOrderedUnit()
local unit target = GetOrderTargetUnit()
if TormentOrderFilter(caster, target) then
call StopOrder.run(caster, true, "This unit already has an active Torment.")
endif
set caster = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterOrderEvent(ORDER_ID, function thistype.onOrder)
endmethod
endstruct
endscope
scope SoulDrain
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant string ON_TARGET_FX = "Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayTarget.mdl"
private constant string MISSILE_MODEL = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
private constant integer RESIDUES_COUNT = 3
private constant real HEAL_PERC = 4.
private constant real CHANCE_BASE = 12.
private constant real CHANCE_INC = 4.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function SoulDrainFilter takes unit attacker, unit target, player attackerOwner, integer lvl returns boolean
return IsAlive(attacker) and (HasAbility(attacker, SOUL_DRAIN) or HasAbility(attacker, SOUL_DRAIN_DOPPLGANGR)) and /*
*/ IsUnitEnemy(target, attackerOwner) and not IsBuilding(target) and not IsMechanical(target) and /*
*/ HasChance(CHANCE_BASE + CHANCE_INC * (lvl - 1))
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct SoulDrain extends Missiles
method onHit takes unit hit returns boolean
local real targetHP = GetUnitLife(target)
local real targetMissingHP = GetUnitMissingLife(target)
local real healAmount = targetMissingHP * Percentage(HEAL_PERC)
if hit == target and IsAlive(target) then
call SetUnitLife(target, targetHP + healAmount)
endif
return false
endmethod
static method onRun takes unit caster, unit targetU returns nothing
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local real casterZ = GetUnitFlyHeight(caster)
local real casterHP = GetUnitLife(caster)
local player casterOwner = GetOwningPlayer(caster)
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local real targetZ = GetUnitFlyHeight(targetU)
local integer i = 0
local thistype this
call CreateEffectOnUnit(ON_TARGET_FX, targetU, "chest")
loop
set i = i + 1
exitwhen i > RESIDUES_COUNT
set this = thistype.create(targetX, targetY, targetZ, casterX, casterY, casterZ)
set source = targetU
set target = caster
set model = MISSILE_MODEL
set speed = GetRandomInt(35, 55) * 10
set collision = 5.
set arc = GetRandomReal(0, 30)
set curve = GetRandomReal(-30, 30)
call launch()
endloop
set casterOwner = null
endmethod
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local player attackerOwner = GetOwningPlayer(attacker)
local unit target = udg_DamageEventTarget
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local integer lvl
if HasAbility(attacker, SOUL_DRAIN) then
set lvl = GetUnitAbilityLevel(attacker, SOUL_DRAIN)
elseif HasAbility(attacker, SOUL_DRAIN_DOPPLGANGR) then
set lvl = GetUnitAbilityLevel(attacker, SOUL_DRAIN_DOPPLGANGR)
endif
if SoulDrainFilter(attacker, target, attackerOwner, lvl) and dmgAmount >= 1. and not isSpell then
call thistype.onRun(attacker, target)
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope TwinCrown
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant real HEAL_SHARED_PERC = 20.
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct TwinCrown
private static method onHeal takes nothing returns nothing
local unit healTarget = udg_heal_target
local integer healTargetId = GetUnitId(healTarget)
local real healTargetHP = GetUnitLife(healTarget)
local integer healTargetType = GetUnitTypeId(healTarget)
local real healAmount
local unit linked
local real linkedHP
if healTargetType == TYRANT then
set linked = GetUnitById(doppelgangerTC[healTargetId])
elseif healTargetType == DOPPELGANGER then
set linked = GetUnitById(tyrantTC[healTargetId])
endif
if IsAlive(linked) then
set linkedHP = GetUnitLife(linked)
set healAmount = udg_heal_amount * Percentage(HEAL_SHARED_PERC)
call SetUnitLife(linked, linkedHP + healAmount)
endif
set healTarget = null
set linked = null
endmethod
private static method onSummon takes nothing returns nothing
local unit caster = GetSummoningUnit()
local integer casterId = GetUnitId(caster)
local unit summoned = GetSummonedUnit()
local integer summonedId = GetUnitId(summoned)
local integer summonedType = GetUnitTypeId(summoned)
local integer lvl
if summonedType == DOPPELGANGER then
call UnitRemoveType(summoned, UNIT_TYPE_SUMMONED)
call SetUnitVertexColor(summoned, 135, 135, 175, 160)
if HasAbility(caster, SOUL_DRAIN) then
set lvl = GetUnitAbilityLevel(caster, SOUL_DRAIN)
call UnitAddAbility(summoned, SOUL_DRAIN_DOPPLGANGR)
call SetUnitAbilityLevel(summoned, SOUL_DRAIN_DOPPLGANGR, lvl)
endif
set doppelgangerTC[casterId] = summonedId
set tyrantTC[summonedId] = casterId
endif
set caster = null
set summoned = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_HealEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onHeal)
set t = null
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.onSummon)
endmethod
endstruct
endscope
scope PredatoryStrike
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = PREDATORY_STRIKE
private constant string MISSILE_MODEL = "Abilities\\Spells\\Undead\\Impale\\ImpaleMissTarget.mdl"
private constant string ON_TARGET_FX_1 = "Abilities\\Weapons\\snapMissile\\snapMissile.mdl"
private constant string ON_TARGET_FX_2 = "Abilities\\Weapons\\HydraliskImpact\\HydraliskImpact.mdl"
private constant real DMG_BASE = 80.
private constant real DMG_INC = 40.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct PredatoryStrike extends Missiles
private static method onRun takes unit caster, unit target returns nothing
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local real dmg = DMG_BASE + DMG_INC * (lvl - 1)
call CreateEffectOnUnit(ON_TARGET_FX_1, target, "chest")
call CreateEffectOnUnit(ON_TARGET_FX_2, target, "chest")
call UnitDamageTarget(caster, target, dmg, true, false, ATT_TYPE, DMG_TYPE, null)
endmethod
method onFinish takes nothing returns boolean
local real casterX = GetUnitX(source)
local real casterY = GetUnitY(source)
local real locX = GetUnitX(dummy)
local real locY = GetUnitY(dummy)
local real angle = Atan2(locY - casterY, locX - casterX)
local real toX = locX + 100 * Cos(angle)
local real toY = locY + 100 * Sin(angle)
call SetUnitX(source, toX)
call SetUnitY(source, toY)
if target != null then
call thistype.onRun(source, target)
endif
return true
endmethod
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local unit targetU = GetSpellTargetUnit()
local real locX
local real locY
local thistype this
if targetU != null then
set locX = GetUnitX(targetU)
set locY = GetUnitY(targetU)
else
set locX = GetSpellTargetX()
set locY = GetSpellTargetY()
endif
set this = thistype.create(casterX, casterY, 0, locX, locY, 0)
set source = caster
set model = MISSILE_MODEL
set speed = 1700
set scale = 1.5
set collision = 5.
if targetU != null then
set target = targetU
endif
call launch()
set caster = null
set targetU = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope LeechLife
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = LEECH_LIFE
private constant integer SPELL_ORDER = ORDER_magicleash
private constant integer BUFF_ID = LEECH_LIFE_BUFF
private constant string ON_TARGET_FX = "Objects\\Spawnmodels\\Human\\HumanBlood\\HeroBloodElfBlood.mdl"
private constant real DMG_BASE = 20.
private constant real DMG_INC = 10.
private constant real DURATION = 7.
private constant real INTERVAL = 1.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct LeechLife
private unit caster
private unit target
private integer lvl
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .target = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real casterHP = GetUnitLife(caster)
local player casterOwner = GetOwningPlayer(.caster)
local real targetX = GetUnitX(.target)
local real targetY = GetUnitY(.target)
local real dmg = DMG_BASE + DMG_INC * (.lvl - 1)
if .remainingDuration > 0 and IsAlive(.caster) and IsAlive(.target) and HasBuff(.target, BUFF_ID) and /*
*/ GetUnitCurrentOrder(.caster) == SPELL_ORDER then
set .remainingDuration = .remainingDuration - INTERVAL
call CreateEffectOnUnit(ON_TARGET_FX, .target, "chest")
call UnitDamageTarget(.caster, .target, dmg, true, false, ATT_TYPE, DMG_TYPE, null)
call SetUnitLife(.caster, casterHP + dmg)
else
call .destroy()
endif
set casterOwner = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .caster = GetSpellAbilityUnit()
set .target = GetSpellTargetUnit()
set .lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope EatCorpse
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = EAT_CORPSE
private constant integer BOOK_ID = BOOK_EAT_CORPSE
private constant integer EFFECT_ID = EAT_CORPSE_MOVSPEED
private constant integer BUFF_ID = EAT_CORPSE_BUFF
private constant string ON_CASTER_FX = "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl"
private constant real MANA_BASE = 50.
private constant real MANA_INC = 15.
private constant real DURATION = 10.
private constant real INTERVAL = .5
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct EatCorpse
private unit caster
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .remainingDuration > 0 and IsAlive(.caster) then
set .remainingDuration = .remainingDuration - INTERVAL
elseif .remainingDuration <= 0 and IsAlive(.caster) and HasAbility(.caster, BOOK_ID) then
call UnitRemoveAbility(.caster, BOOK_ID)
call UnitRemoveAbility(.caster, BUFF_ID)
call .destroy()
else
call .destroy()
endif
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer lvl
local real mana
set .caster = GetSpellAbilityUnit()
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
set mana = MANA_BASE + MANA_INC * (lvl - 1)
call CreateEffectOnUnit(ON_CASTER_FX, .caster, "chest")
call SetUnitMana(.caster, GetUnitMana(.caster) + mana)
call UnitAddAbility(.caster, BOOK_ID)
call SetUnitAbilityLevel(.caster, EFFECT_ID, lvl)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Consume
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = CONSUME
private constant integer BOOK_ID = BOOK_CONSUME
private constant real HEAL_PERC = 25.
private constant real DURATION = 40.
private constant real INTERVAL = .5
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Consume
private unit caster
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .remainingDuration > 0 and IsAlive(.caster) then
set .remainingDuration = .remainingDuration - INTERVAL
elseif .remainingDuration <= 0 and IsAlive(.caster) and HasAbility(.caster, BOOK_ID) then
call UnitRemoveAbility(.caster, BOOK_ID)
call .destroy()
else
call .destroy()
endif
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local real heal
set .caster = GetSpellAbilityUnit()
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
set heal = GetUnitMaxLife(.caster) * Percentage(HEAL_PERC)
call SetUnitLife(.caster, GetUnitLife(.caster) + heal)
call UnitAddAbility(.caster, BOOK_ID)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope GrimBolt
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = GRIM_BOLT
private constant string MISSILE_MODEL = "war3mapImported\\ShadowBoltMissile.mdx"
private constant real DMG_BASE = 85.
private constant real DMG_INC = 45.
private constant real AOE = 150.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
private constant integer DUMMY_SPELL_ID = GRIM_BOLT_DUMMY
private constant integer DUMMY_SPELL_ORDER = ORDER_silence
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function GrimBoltFilter takes unit picked, player casterOwner returns boolean
return IsAlive(picked) and IsUnitEnemy(picked, casterOwner) and not IsBuilding(picked) and /*
*/ not IsMagicImmune(picked) and not IsMechanical(picked)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct GrimBolt extends Missiles
private static method onRun takes unit caster, real locX, real locY returns nothing
local player casterOwner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local real dmg = DMG_BASE + DMG_INC * (lvl - 1)
local group enemies = CreateGroup()
local unit picked
local unit dummy
call GroupEnumUnitsWithCollision(enemies, locX, locY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if GrimBoltFilter(picked, casterOwner) then
call UnitDamageTarget(caster, picked, dmg, true, false, ATT_TYPE, DMG_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
set dummy = DummyCaster(locX, locY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssuePointOrderById(dummy, DUMMY_SPELL_ORDER, locX, locY)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
call DestroyGroup(enemies)
set casterOwner = null
set enemies = null
set picked = null
endmethod
method onFinish takes nothing returns boolean
local real locX = GetUnitX(dummy)
local real locY = GetUnitY(dummy)
call thistype.onRun(source, locX, locY)
return true
endmethod
method onHit takes unit hit returns boolean
local player casterOwner = GetOwningPlayer(source)
local real locX = GetUnitX(dummy)
local real locY = GetUnitY(dummy)
local boolean b = false
if GrimBoltFilter(hit, casterOwner) then
set b = true
call thistype.onRun(source, locX, locY)
endif
set casterOwner = null
if b then
return true
else
return false
endif
endmethod
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local real locX = GetSpellTargetX()
local real locY = GetSpellTargetY()
local thistype this
set this = thistype.create(casterX, casterY, 0, locX, locY, 0)
set source = caster
set model = MISSILE_MODEL
set speed = 600
set scale = 1.5
set collision = 20.
call launch()
set caster = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope Defile
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer ABILITY_ID = DEFILE
private constant integer BUFF_ID = DEFILE_BUFF
private constant string ON_TARGET_FX = "Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayDamage.mdl"
private constant real DMG_BASE = 12.
private constant real DMG_INC = 8.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function DefileFilter takes unit attacker, unit target, player attackerOwner returns boolean
return IsAlive(attacker) and HasAbility(attacker, ABILITY_ID) and HasBuff(attacker, BUFF_ID) and /*
*/ IsUnitEnemy(target, attackerOwner) and not IsBuilding(target) and not IsMagicImmune(target)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct Defile
private static method onDamage takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local player attackerOwner = GetOwningPlayer(attacker)
local integer lvl = GetUnitAbilityLevel(attacker, ABILITY_ID)
local unit target = udg_DamageEventTarget
local real dmgAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local real magicDmg
if DefileFilter(attacker, target, attackerOwner) and dmgAmount >= 1. and not isSpell then
set magicDmg = DMG_BASE + DMG_INC * (lvl - 1)
call CreateEffectOnUnit(ON_TARGET_FX, target, "chest")
call DelayedDamage.execute(attacker, target, magicDmg, ATT_TYPE, DMG_TYPE, .02)
endif
set attacker = null
set attackerOwner = null
set target = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(t, function thistype.onDamage)
set t = null
endmethod
endstruct
endscope
scope RitualMadness
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = RITUAL_MADNESS
private constant integer SUMMON_ID = AVATAR_CTHUN
private constant integer TENTACLE_ID = EYE_TENTACLE
private constant integer SPELL_ORDER = ORDER_channel
private constant string ON_DEATH_FX = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
private constant integer TENTACLES_COUNT = 4
private constant real TENTACLES_AOE = 310.
private constant real DURATION = 40.
private constant real INTERVAL = .5
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct RitualMadness
private unit caster
private unit summoned
private unit tentacleFirst
private unit tentacleSec
private unit tentacleThird
private unit tentacleFourth
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
local location loc = GetUnitLoc(.summoned)
call CreateEffectOnLoc(ON_DEATH_FX, loc)
call RemoveUnit(.summoned)
call RemoveUnit(.tentacleFirst)
call RemoveUnit(.tentacleSec)
call RemoveUnit(.tentacleThird)
call RemoveUnit(.tentacleFourth)
call RemoveLocation(loc)
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .summoned = null
set .tentacleFirst = null
set .tentacleSec = null
set .tentacleThird = null
set .tentacleFourth = null
set loc = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .remainingDuration > 0 and IsAlive(.caster) and GetUnitCurrentOrder(.caster) == SPELL_ORDER then
set .remainingDuration = .remainingDuration - INTERVAL
else
call .destroy()
endif
endmethod
private static method onCast takes nothing returns nothing
local player casterOwner
local real casterX
local real casterY
local real casterFacing
local real angle
local real x
local real y
local thistype this = allocate()
set .caster = GetSpellAbilityUnit()
set casterX = GetUnitX(.caster)
set casterY = GetUnitY(.caster)
set casterFacing = GetUnitFacing(.caster)
set casterOwner = GetOwningPlayer(.caster)
set .summoned = CreateUnit(casterOwner, SUMMON_ID, casterX, casterY, casterFacing)
set angle = 45.
set x = casterX + TENTACLES_AOE * Cos(angle * bj_DEGTORAD)
set y = casterY + TENTACLES_AOE * Sin(angle * bj_DEGTORAD)
set .tentacleFirst = CreateUnit(casterOwner, TENTACLE_ID, x, y, casterFacing)
set angle = angle + 90.
set x = casterX + TENTACLES_AOE * Cos(angle * bj_DEGTORAD)
set y = casterY + TENTACLES_AOE * Sin(angle * bj_DEGTORAD)
set .tentacleSec = CreateUnit(casterOwner, TENTACLE_ID, x, y, casterFacing)
set angle = angle + 90.
set x = casterX + TENTACLES_AOE * Cos(angle * bj_DEGTORAD)
set y = casterY + TENTACLES_AOE * Sin(angle * bj_DEGTORAD)
set .tentacleThird = CreateUnit(casterOwner, TENTACLE_ID, x, y, casterFacing)
set angle = angle + 90.
set x = casterX + TENTACLES_AOE * Cos(angle * bj_DEGTORAD)
set y = casterY + TENTACLES_AOE * Sin(angle * bj_DEGTORAD)
set .tentacleFourth = CreateUnit(casterOwner, TENTACLE_ID, x, y, casterFacing)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call SetUnitVertexColor(.summoned, 100, 100, 200, 110)
call SetUnitVertexColor(.tentacleFirst, 100, 100, 200, 110)
call SetUnitVertexColor(.tentacleSec, 100, 100, 200, 110)
call SetUnitVertexColor(.tentacleThird, 100, 100, 200, 110)
call SetUnitVertexColor(.tentacleFourth, 100, 100, 200, 110)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set casterOwner = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope ToxicSpray
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = TOXIC_SPRAY
private constant integer BUFF_ID = TOXIC_SPRAY_BUFF
private constant string MISSILE_MODEL = "Abilities\\Spells\\NightElf\\CorrosiveBreath\\CorrosiveBreathMissile.mdl"
private constant real DMG_PERC_BASE = 4.
private constant real DMG_PERC_INC = 2.
private constant attacktype ATT_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
private constant integer DUMMY_SPELL_ID = TOXIC_SPRAY_DUMMY
private constant integer DUMMY_SPELL_ORDER = ORDER_acidbomb
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct ToxicSpray extends Missiles
method onHit takes unit hit returns boolean
local player casterOwner = GetOwningPlayer(source)
local integer lvl = GetUnitAbilityLevel(source, SPELL_ID)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real targetHP = GetUnitMaxLife(target)
local real dmg = targetHP * Percentage(DMG_PERC_BASE + DMG_PERC_INC * (lvl - 1))
local unit dummy
if hit == target then
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, lvl)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER, hit)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
if HasBuff(hit, BUFF_ID) then
call UnitDamageTarget(source, hit, dmg, true, false, ATT_TYPE, DMG_TYPE, null)
endif
endif
set casterOwner = null
return false
endmethod
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local real casterZ = GetUnitFlyHeight(caster)
local unit targetU = GetSpellTargetUnit()
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local real targetZ = GetUnitFlyHeight(targetU)
local thistype this
set this = thistype.create(casterX, casterY, casterZ, targetX, targetY, targetZ)
set source = caster
set target = targetU
set model = MISSILE_MODEL
set speed = 800
set arc = 20
set scale = 1.
set collision = 5.
call launch()
set caster = null
set targetU = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
scope HiveMind
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
private constant integer SPELL_ID = HIVE_MIND
private constant integer BOOK_ID = BOOK_HIVE_MIND
private constant integer BUFF_ID = HIVE_MIND_BUFF
private constant integer EFFECT_ID = HIVE_MIND_ATTSPEED
private constant real AOE = 600.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function HiveMindFilter takes unit picked, player casterOwner returns boolean
return IsAlive(picked) and IsUnitAlly(picked, casterOwner)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
struct HiveMind
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local integer casterId = GetUnitId(caster)
local player casterOwner = GetOwningPlayer(caster)
local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local group allies = CreateGroup()
local unit picked
local integer pickedId
call GroupEnumUnitsWithCollision(allies, casterX, casterY, AOE)
loop
set picked = FirstOfGroup(allies)
exitwhen picked == null
if HiveMindFilter(picked, casterOwner) then
set pickedId = GetUnitId(picked)
set unitBuffLevel[pickedId][BUFF_ID] = lvl
call UnitAddBook(picked, BOOK_ID)
call SetUnitAbilityLevel(picked, EFFECT_ID, lvl)
endif
call GroupRemoveUnit(allies, picked)
set picked = null
endloop
call DestroyGroup(allies)
set caster = null
set casterOwner = null
set allies = null
endmethod
//----------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
library AmbusherAI requires AIBehaviorController, GroupUtils, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Dust Storm
private constant string ORDER = "tornado"
private constant real AOE = 300.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function DustStormFilter takes unit picked, player owner returns boolean
return IsAlive(picked) and not HasBuff(picked, DUST_STORM_BUFF) and IsUnitAlly(picked, owner) and /*
*/ GetPlayerTechCount(owner, AMBUSHER_TRAIN_UPG, true) >= 2
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct AmbusherAI extends Behavior
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
local real x = GetUnitX(ally)
local real y = GetUnitY(ally)
local player owner = GetOwningPlayer(u)
local group allies = CreateGroup()
local CustomOrder o = CustomOrder.create(u)
local integer qty = 0
local unit picked
if not IsMelee(attacker) and not IsBuilding(ally) then
call GroupEnumUnitsWithCollision(allies, x, y, AOE)
loop
set picked = FirstOfGroup(allies)
exitwhen picked == null
if DustStormFilter(picked, owner) then
if IsHero(picked) then
set qty = qty + 3
else
set qty = qty + 1
endif
endif
call GroupRemoveUnit(allies, picked)
set picked = null
endloop
if qty >= 3 then
call o.registerPointOrder(ORDER, x, y, 30, 15, 200)
endif
endif
call DestroyGroup(allies)
set owner = null
set allies = null
set picked = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = AmbusherAI.create()
call Controller.registerBehaviorType(AMBUSHER, b, false)
endmethod
endstruct
endlibrary
library StranglerAI requires AIBehaviorController, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Asphyxiate
private constant string ORDER_1 = "replenishmanaon"
private constant string ORDER_2 = "replenishmanaoff"
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct StranglerAI extends Behavior
method onAttack takes unit attacker, unit target returns nothing
local player owner = GetOwningPlayer(attacker)
local CustomOrder o = CustomOrder.create(attacker)
if IsUnitEnemy(target, owner) and not HasBuff(target, ASPHYXIATE_BUFF) then
call o.registerInstantOrder(ORDER_1, 30, 0, 10)
else
call o.registerInstantOrder(ORDER_2, 30, 0, 0)
endif
set owner = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = StranglerAI.create()
call Controller.registerBehaviorType(STRANGLER, b, false)
endmethod
endstruct
endlibrary
library AnubisathAI requires AIBehaviorController, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Sandfury
private constant string ORDER = "locustswarm"
endglobals
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct AnubisathAI extends Behavior
method onAttack takes unit attacker, unit target returns nothing
local player owner = GetOwningPlayer(attacker)
local CustomOrder o = CustomOrder.create(attacker)
if IsUnitEnemy(target, owner) and IsBuilding(target) then
call o.registerInstantOrder(ORDER, 30, 30, 0)
endif
set owner = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = AnubisathAI.create()
call Controller.registerBehaviorType(ANUBISATH, b, false)
endmethod
endstruct
endlibrary
library TyrantAI requires AIBehaviorController, GroupUtils, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Torment
private constant string ORDER = "thunderbolt"
private constant real AOE = 615.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function TormentFilter takes unit picked, player owner returns boolean
return IsAlive(picked) and not HasBuff(picked, TORMENT_BUFF) and not IsMechanical(picked) and /*
*/ not IsBuilding(picked) and not IsMagicImmune(picked) and IsUnitEnemy(picked, owner)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct TyrantAI extends Behavior
method onAttacked takes unit target, unit attacker returns nothing
local real x = GetUnitX(target)
local real y = GetUnitY(target)
local player owner = GetOwningPlayer(target)
local group enemies = CreateGroup()
local CustomOrder o = CustomOrder.create(target)
local integer qty = 0
local integer highestPriority = 0
local integer currentPriority = 0
local unit priorityTarget
local unit picked
if HasAbility(target, TORMENT) then
call GroupEnumUnitsWithCollision(enemies, x, y, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if TormentFilter(picked, owner) then
if IsHero(picked) or GetUnitLevel(picked) >= 7 then
set qty = qty + 5
set currentPriority = 5
elseif GetUnitLevel(picked) < 7 and GetUnitLevel(picked) >= 4 then
set qty = qty + 3
set currentPriority = 3
elseif GetUnitLevel(picked) < 4 then
set qty = qty + 2
set currentPriority = 2
endif
if currentPriority > highestPriority then
set highestPriority = currentPriority
set priorityTarget = picked
endif
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
if qty >= 7 and priorityTarget != null then
call o.registerTargetOrder(ORDER, priorityTarget, 30, 20, 65)
endif
endif
call DestroyGroup(enemies)
set owner = null
set enemies = null
set picked = null
set priorityTarget = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = TyrantAI.create()
call Controller.registerBehaviorType(TYRANT, b, false)
endmethod
endstruct
endlibrary
library DevourerAI requires AIBehaviorController, GroupUtils, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Consume
private constant string ORDER = "doom"
private constant real AOE = 270.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function ConsumeFilter takes unit picked, player owner returns boolean
return IsAlive(picked) and not IsMechanical(picked) and not IsBuilding(picked) and /*
*/ not IsHero(picked) and not IsFlying(picked) and GetUnitLevel(picked) <= 5 and /*
*/ IsUnitEnemy(picked, owner)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct DevourerAI extends Behavior
method onAttack takes unit attacker, unit target returns nothing
local real x = GetUnitX(attacker)
local real y = GetUnitY(attacker)
local player owner = GetOwningPlayer(attacker)
local group enemies = CreateGroup()
local CustomOrder o = CustomOrder.create(attacker)
local integer qty = 0
local integer highestPriority = 0
local integer currentPriority = 0
local unit priorityTarget
local unit picked
if HasAbility(attacker, CONSUME) then
call GroupEnumUnitsWithCollision(enemies, x, y, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if ConsumeFilter(picked, owner) then
if GetUnitLevel(picked) == 5 then
set qty = qty + 3
set currentPriority = 3
elseif GetUnitLevel(picked) < 5 and GetUnitLevel(picked) >= 3 then
set qty = qty + 2
set currentPriority = 2
elseif GetUnitLevel(picked) < 3 then
set qty = qty + 1
set currentPriority = 1
endif
if currentPriority > highestPriority then
set highestPriority = currentPriority
set priorityTarget = picked
endif
elseif (IsHero(picked) or GetUnitLevel(picked) > 5) and IsUnitEnemy(picked, owner) then
set qty = qty + 4
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
if qty >= 8 and priorityTarget != null then
call o.registerTargetOrder(ORDER, priorityTarget, 30, 110, 175)
endif
endif
call DestroyGroup(enemies)
set owner = null
set enemies = null
set picked = null
set priorityTarget = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = DevourerAI.create()
call Controller.registerBehaviorType(DEVOURER, b, false)
endmethod
endstruct
endlibrary
library MindslayerAI requires AIBehaviorController, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Grim Bolt
private constant string ORDER_1 = "inferno"
//Ritual of Madness
private constant string ORDER_2 = "channel"
private constant real AOE = 475.
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function GrimBoltFilter takes unit u, unit enemy, player owner returns boolean
return IsAlive(enemy) and not HasBuff(enemy, GRIM_BOLT_BUFF) and not IsMechanical(enemy) and /*
*/ not IsBuilding(enemy) and not IsMagicImmune(enemy) and IsUnitEnemy(enemy, owner) and /*
*/ HasAbility(u, GRIM_BOLT) and GetUnitCurrentOrder(u) != ORDER_channel
endfunction
private function RitualMadnessFilter takes unit enemy, player owner returns boolean
return IsUnitEnemy(enemy, owner) and not IsMagicImmune(enemy)
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct MindslayerAI extends Behavior
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
local real enemyX = GetUnitX(enemy)
local real enemyY = GetUnitY(enemy)
local player owner = GetOwningPlayer(u)
local CustomOrder o = CustomOrder.create(u)
if GrimBoltFilter(u, enemy, owner) then
call o.registerPointOrder(ORDER_1, enemyX, enemyY, 30, 11, 120)
endif
set owner = null
endmethod
method onAttack takes unit attacker, unit target returns nothing
local real x = GetUnitX(attacker)
local real y = GetUnitY(attacker)
local player owner = GetOwningPlayer(attacker)
local group enemies = CreateGroup()
local CustomOrder o = CustomOrder.create(attacker)
local integer qty = 0
local unit picked
if HasAbility(attacker, RITUAL_MADNESS) then
call GroupEnumUnitsWithCollision(enemies, x, y, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if RitualMadnessFilter(picked, owner) then
if IsHero(picked) then
set qty = qty + 3
elseif GetUnitLevel(picked) >= 4 then
set qty = qty + 2
else
set qty = qty + 1
endif
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
if qty >= 7 then
call o.registerInstantOrder(ORDER_2, 30, 140, 200)
endif
endif
call DestroyGroup(enemies)
set owner = null
set enemies = null
set picked = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = MindslayerAI.create()
call Controller.registerBehaviorType(MINDSLAYER, b, false)
endmethod
endstruct
endlibrary
library HiveLordAI requires AIBehaviorController, GroupUtils, SpellUtils
//----------------------------------------------------------------------------------
//GLOBALS
//----------------------------------------------------------------------------------
globals
//Toxic Spray
private constant string ORDER = "acidbomb"
endglobals
//----------------------------------------------------------------------------------
//FILTERS
//----------------------------------------------------------------------------------
private function ToxicSprayFilter takes unit u, unit enemy, player owner returns boolean
return IsAlive(enemy) and IsUnitEnemy(enemy, owner) and not IsMechanical(enemy) and /*
*/ not IsBuilding(enemy) and not IsMagicImmune(enemy) and HasAbility(u, TOXIC_SPRAY) and /*
*/ (HasBuff(enemy, TOXIC_SPRAY_BUFF) or (not HasBuff(enemy, TOXIC_SPRAY_BUFF) and HasChance(30) and /*
*/ GetUnitPercentLife(enemy) >= 30.))
endfunction
//----------------------------------------------------------------------------------
//FUNCTIONS
//----------------------------------------------------------------------------------
private struct HiveLordAI extends Behavior
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
local player owner = GetOwningPlayer(u)
local CustomOrder o = CustomOrder.create(u)
if ToxicSprayFilter(u, enemy, owner) then
call o.registerTargetOrder(ORDER, enemy, 30, 6, 65)
endif
set owner = null
endmethod
private static method onInit takes nothing returns nothing
local Behavior b = HiveLordAI.create()
call Controller.registerBehaviorType(HIVE_LORD, b, false)
endmethod
endstruct
endlibrary
library MapSetup initializer Init requires MultidimensionalArray
globals
// Buildings
constant integer NEST = 'q000'
constant integer DEN = 'q001'
constant integer HIVE = 'q002'
constant integer MAUSOLEUM = 'q003'
constant integer CATACOMBS = 'q004'
constant integer PYLON = 'q005'
constant integer MASTABA = 'q006'
constant integer ALTAR_SANDS = 'q007'
constant integer NEXUS = 'q008'
constant integer MENAGERIE = 'q009'
constant integer ROYAL_TOMBS = 'q00a'
constant integer RELIQUARY = 'q00b'
constant integer LOST_CHAMBERS = 'q00c'
// Units
constant integer HATCHLING = 'q00d'
constant integer CRAWLER = 'q00e'
constant integer WASP = 'q00f'
constant integer STALKER = 'q00g'
constant integer SANDREAVER = 'q00h'
constant integer SWARMGUARD = 'q00i'
constant integer SWARMGUARD_FLY = 'q00j'
constant integer GLADIATOR = 'q00k'
constant integer AMBUSHER = 'q00l'
constant integer STRANGLER = 'q00n'
constant integer LURKER = 'q00o'
constant integer ANUBISATH = 'q00p'
constant integer STINGER = 'q00r'
constant integer ERADICATOR = 'q00s'
constant integer DOPPELGANGER = 'q00u'
constant integer AVATAR_CTHUN = 'q00v'
constant integer EYE_TENTACLE = 'q00w'
// Eggs
constant integer EGG_ID = 'qegg'
constant integer EGG_HATCHLING = 'qeg0'
constant integer EGG_CRAWLER = 'qeg1'
constant integer EGG_STALKER = 'qeg2'
constant integer EGG_SANDREAVER = 'qeg3'
constant integer EGG_AMBUSHER = 'qeg4'
constant integer EGG_LURKER = 'qeg5'
constant integer EGG_STINGER = 'qeg6'
// Heroes
constant integer TYRANT = 'QH00'
constant integer DEVOURER = 'QH01'
constant integer MINDSLAYER = 'QH02'
constant integer HIVE_LORD = 'QH03'
// Upgrades
constant integer STING_BARRAGE_UPG = 'R005'
constant integer NEUROTOXIN_UPG = 'R006'
constant integer AMBUSHER_TRAIN_UPG = 'R00A'
constant integer STRANGLER_TRAIN_UPG = 'R00B'
// Abilities
constant integer HATCHLING_HARVEST = 'A000'
constant integer HATCHLING_HARVEST_AI = 'A02J'
constant integer CRAWLER_BASE_HARVEST = 'A001'
constant integer CRAWLER_HARVEST = 'A003'
constant integer CRAWLER_RETURN = 'A004'
constant integer WASP_HARVEST = 'A002'
constant integer SELECT_EGGS = 'A005'
constant integer HUNGER = 'A007'
constant integer STING_BARRAGE = 'A008'
constant integer NEUROTOXIN = 'A00D'
constant integer SYMBIOTIC_LINK = 'A00G'
constant integer SYMBIOTIC_LINK_FLY = 'A00I'
constant integer REVENGE = 'A00J'
constant integer SILITHID_POX = 'A00P'
constant integer SONIC_PULSE = 'A00Q'
constant integer ASPHYXIATE = 'A00U'
constant integer NULLIFYING_GRASP = 'A010'
constant integer SUNDER = 'A011'
constant integer OVERFLOW = 'A01C'
constant integer COUNTERSPELL = 'A01D'
constant integer SOUL_DRAIN_DOPPLGANGR = 'A01S'
// Hero Abilities
constant integer SPECTRAL_BURST = 'A01J'
constant integer TORMENT = 'A01M'
constant integer SOUL_DRAIN = 'A01N'
constant integer PREDATORY_STRIKE = 'A01U'
constant integer LEECH_LIFE = 'A01V'
constant integer EAT_CORPSE = 'A01W'
constant integer CONSUME = 'A01X'
constant integer GRIM_BOLT = 'A021'
constant integer DEFILE = 'A023'
constant integer RITUAL_MADNESS = 'A025'
constant integer TOXIC_SPRAY = 'A028'
constant integer HIVE_MIND = 'A029'
constant integer IMPERVIOUS_CARAPACE = 'A02C'
// Buffs
constant integer SYMBIOTIC_LINK_BUFF_C = 'B003'
constant integer SYMBIOTIC_LINK_BUFF_T = 'B002'
constant integer SILITHID_POX_BUFF = 'B004'
constant integer DUST_STORM_BUFF = 'B006'
constant integer ASPHYXIATE_BUFF = 'B005'
constant integer SUNDER_BUFF = 'B008'
constant integer NECROTIC_POISON_BUFF = 'B009'
constant integer TORMENT_BUFF = 'B00B'
constant integer LEECH_LIFE_BUFF = 'B00E'
constant integer EAT_CORPSE_BUFF = 'B00G'
constant integer GRIM_BOLT_BUFF = 'B00J'
constant integer DEFILE_BUFF = 'B00K'
constant integer TOXIC_SPRAY_BUFF = 'B00P'
constant integer HIVE_MIND_BUFF = 'B00Q'
constant integer SWARMING_AURA_BUFF = 'B00R'
// Dummy Abilities
constant integer NEUROTOXIN_DUMMY = 'A00E'
constant integer REVENGE_DUMMY = 'A00K'
constant integer SONIC_PULSE_DUMMY_1 = 'A00R' // Stun
constant integer SONIC_PULSE_DUMMY_2 = 'A00S' // Reveal
constant integer ASPHYXIATE_DUMMY_1 = 'A00X' // Normal attack
constant integer ASPHYXIATE_DUMMY_2 = 'A00Y' // Enhanced attack
constant integer ASPHYXIATE_DUMMY_3 = 'A00Z' // Debuff
constant integer SUNDER_DUMMY = 'A012'
constant integer OVERFLOW_DUMMY = 'A01A'
constant integer SPECTRAL_BURST_DUMMY_1 = 'A01K' // Lightning
constant integer SPECTRAL_BURST_DUMMY_2 = 'A01L' // Armor buff
constant integer GRIM_BOLT_DUMMY = 'A022'
constant integer TOXIC_SPRAY_DUMMY = 'A027'
// Books
constant integer BOOK_SYMBIOTIC_LINK = 'A00H'
constant integer BOOK_REVENGE = 'A00L'
constant integer BOOK_OVERFLOW = 'A01B'
constant integer BOOK_TORMENT = 'A01Q'
constant integer BOOK_EAT_CORPSE = 'A01Y'
constant integer BOOK_CONSUME = 'A01Z'
constant integer BOOK_HIVE_MIND = 'A02B'
constant integer BOOK_SWARMING_AURA = 'A02F'
// Effect Abilities
constant integer SYMBIOTIC_LINK_MOVSPD = 'S002'
constant integer TORMENT_BUFF_ABILITY = 'A01P'
constant integer EAT_CORPSE_MOVSPEED = 'S001'
constant integer HIVE_MIND_ATTSPEED = 'A02A'
// Arrays
integer array GLOOM_AURA_BUFF
// Buff Level Variable
Integer2D unitBuffLevel
// Lumber Gathering System
Integer1D crawlerId
Integer2D waspId
Boolean1D inactivity
Boolean1D hasWasps
// Egg System
Integer1D hiveId
Integer2D eggId
// Sting Barrage
Integer1D stingsCount
// Symbiotic Link
Integer1D linksCount
// Revenge
Real1D revengeCount
// Asphyxiate
Boolean1D asphyxiateDisabled
// Torment
Integer1D tormentLinked
// Twin Crown
Integer1D doppelgangerTC
Integer1D tyrantTC
endglobals
function GetUnitBuffLevel takes integer unitId, integer buffId returns integer
if unitBuffLevel[unitId][buffId] > 0 then
return unitBuffLevel[unitId][buffId]
else
return 1
endif
endfunction
function IsSilithid takes unit u returns boolean
return GetUnitTypeId(u) == HATCHLING or GetUnitTypeId(u) == CRAWLER or GetUnitTypeId(u) == STALKER or /*
*/ GetUnitTypeId(u) == SANDREAVER or GetUnitTypeId(u) == AMBUSHER or GetUnitTypeId(u) == LURKER or /*
*/ GetUnitTypeId(u) == STINGER
endfunction
private function Initialization takes nothing returns nothing
local integer i = 0
local player p
loop
exitwhen i == bj_MAX_PLAYERS
set p = Player(i)
// Hide Books
call SetPlayerAbilityAvailable(p, BOOK_SYMBIOTIC_LINK, false)
call SetPlayerAbilityAvailable(p, BOOK_REVENGE, false)
call SetPlayerAbilityAvailable(p, BOOK_OVERFLOW, false)
call SetPlayerAbilityAvailable(p, BOOK_TORMENT, false)
call SetPlayerAbilityAvailable(p, BOOK_EAT_CORPSE, false)
call SetPlayerAbilityAvailable(p, BOOK_CONSUME, false)
call SetPlayerAbilityAvailable(p, BOOK_HIVE_MIND, false)
call SetPlayerAbilityAvailable(p, BOOK_SWARMING_AURA, false)
if DetectHumanSlot(p) then
call SetPlayerAbilityAvailable(p, HATCHLING_HARVEST_AI, false)
endif
set i = i + 1
endloop
set GLOOM_AURA_BUFF[1] = 'B00M'
set GLOOM_AURA_BUFF[2] = 'B00N'
set GLOOM_AURA_BUFF[3] = 'B00O'
set unitBuffLevel = Integer2D.create()
set crawlerId = Integer1D.create()
set waspId = Integer2D.create()
set inactivity = Boolean1D.create()
set hasWasps = Boolean1D.create()
set hiveId = Integer1D.create()
set eggId = Integer2D.create()
set stingsCount = Integer1D.create()
set linksCount = Integer1D.create()
set revengeCount = Real1D.create()
set asphyxiateDisabled = Boolean1D.create()
set tormentLinked = Integer1D.create()
set doppelgangerTC = Integer1D.create()
set tyrantTC = Integer1D.create()
endfunction
private function Init takes nothing returns nothing
call Initialization()
endfunction
endlibrary