Name | Type | is_array | initial_value |
//TESH.scrollpos=270
//TESH.alwaysfold=0
globals
//strange ones at bottom
constant integer ORDER_OFFSET=851970
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=852099
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_scrollofspeed=852285
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 //?
constant integer ORDER_instant2=851987 //?
constant integer ORDER_instant3=851975 //?
constant integer ORDER_instant4=852019 //?
endglobals
//TESH.scrollpos=2
//TESH.alwaysfold=0
library_once WorldBounds /* v2.0.0.0
************************************************************************************
*
* 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
*
************************************************************************************/
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=79
//TESH.alwaysfold=0
library Missile /* version 2.5.1
*************************************************************************************
*
* Creating custom projectiles in Warcraft III.
*
* Major goal:
* No unessary external requirements.
* Implements code optional.
*
* Philosophy:
* I want that feature --> Compiler writes that code into your map script.
* I don't want that --> Compiler ignores that code completly.
*
* Important:
* Take yourself 2 minutes time to setup Missile correctly.
* Otherwise I can't guarantee, that Missile works the way you want.
* Once the setup is done, you can check out some examples and Missile will be easy
* to use for everyone. I promise it.
*
* Do the setup at:
*
* 1.) Import instruction
* 2.) Global configuration
* 3.) Function configuration
*
* Credits to Dirac, emjlr3, AceHart, Bribe, Wietlol,
* Nestharus, Maghteridon96, Vexorian and Zwiebelchen.
*
*************************************************************************************
*
* */ requires /*
*
* - Missile requires nothing.
*
*************************************************************************************
*
* Optional requirements listed can reduce overall code generation,
* add safety mechanisms, decrease overhead and optimize handle management.
* For a better overview I put them into blocks.
*
* I recommend to use at least one per block in your map.
*
* a) For best debug results: ( Useful )
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*
* b) Fatal error protection: ( Case: unit out moves of world bounds )
* - WorldBounds is safer than BoundSentinel.
* - WorldBounds adds more overhead than BoundSentinel.
* - Using none of these two forces Missile to switch from SetUnitX/Y to the SetUnitPosition native.
* */ optional WorldBounds /* githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
* */ optional BoundSentinel /* wc3c.net/showthread.php?t=102576
*
* c) Handle recycling: ( Performace gain, memory management )
* - uses MissileRecylcer > Dummy > xedummy.
* */ optional MissileRecycler /* hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
* */ optional Dummy /* github.com/nestharus/JASS/blob/master/jass/Systems/Dummy/Dummy.w3x
* */ optional xedummy /* wc3c.net/showthread.php?t=101150
*
* d) Unit indexing: ( Avoid an onIndex event )
* - not required for Missile. Only if you use one already.
* */ optional UnitIndexer /* github.com/nestharus/JASS/tree/master/jass/Systems/Unit%20Indexer
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* • Copy Missile into to your map.
* • You need a dummy unit, using Vexorians "dummy.mdx".
* This unit must use the locust and crow form ability. ( Aloc & Amrf )
* ¯¯¯¯
*
* 2. Global configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Seven constants to setup!
*/
globals
/**
* Missiles are moved periodically. An interval of 1./32. is recommended.
* • Too short timeout intervals may cause performance issues.
* • Too large timeout intervals may look fishy.
*/
public constant real TIMER_TIMEOUT = 1./32.
/**
* Owner of all Missile dummies. Should be a neutral player in your map.
*/
public constant player NEUTRAL_PASSIVE = Player(15)
/**
* Raw code of the dummy unit. Object Editor ( F6 )
* • Must be correct, otherwise missile dummies can neither be recycled nor destroyed.
* • Units of other type ids will not be thrown into the recycler bin.
*/
public constant integer DUMMY_UNIT_ID = 'dumi'
/**
* The maximum collision size used in your map. If unsure use 197. ( Town hall collision )
* • Applies for all types of widgets.
* • A precise value can improve Missile's performance,
* since smaller values enumerate less widgtes per loop per missile.
*/
public constant real MAXIMUM_COLLISION_SIZE = 197.
/**
* Collision types for missiles. ( Documentation only )
* Missile decides internally each loop which type of collision is required.
* • Uses circular collision dectection for speed < collision. ( Good accuracy, best performance )
* • Uses rectangle collision for speed >= collision. ( Best accuracy, normal performance )
*/
public constant integer COLLISION_TYPE_CIRCLE = 0
public constant integer COLLISION_TYPE_RECTANGLE = 1
/**
* Determine when rectangle collision is required to detect nearby widgets.
* • The smaller the factor the earlier rectangle collision is used. ( by default 1. )
* • Formula: speed >= collision*Missile_COLLISION_ACCURACY_FACTOR
*/
public constant real COLLISION_ACCURACY_FACTOR = 1.
// Optional toogles:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Set booleans listed below to "true" and the compiler will write
// the feature into your map. Otherwise this code is completly ignored.
// • Yay, I want that --> "true"
// • Naah that's useless for me --> "false"
/**
* USE_COLLISION_Z_FILTER enables z axis checks for widget collision. ( Adds minimal overhead )
* Use it when you need:
* • Missiles flying over or under widgets.
* • Determine between flying and walking units.
*/
public constant boolean USE_COLLISION_Z_FILTER = true
/**
* WRITE_DELAYED_MISSILE_RECYCLING enables a delayed dummy recycling system. ( Very recommended )
* Use it if:
* • You use a dummy recycling library like MissileRecycler, Dummy or xedummy.
* • You want to properly display death animations of effects added to missiles.
*/
public constant boolean WRITE_DELAYED_MISSILE_RECYCLING = true
/**
* DELAYED_MISSILE_DEATH_ANIMATION_TIME is the delay in seconds
* Missile holds back a dummy, before recycling it.
* • The time value does not have to be precise.
* • Requires WRITE_DELAYED_MISSILE_RECYCLING = true
*/
private constant real DELAYED_MISSILE_DEATH_ANIMATION_TIME = 2.
/**
* USE_DESTRUCTABLE_FILTER and USE_ITEM_FILTER are redundant constants from previous Missile versions.
* They do nothing, but remain for backwards compatibilty.
* From Missile version 1.5 on all widget collisions are always enabled.
*/
public constant boolean USE_DESTRUCTABLE_FILTER = true
public constant boolean USE_ITEM_FILTER = true
endglobals
/*
* 3. Function configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Four functions to setup!
*/
/**
* GetUnitBodySize(unit) returns a fictional value for z - axis collision.
* You have two options:
* • One constant value shared over all units.
* • Dynamic values based on handle id, type id, unit user data, scaling or other parameters.
*/
function GetUnitBodySize takes unit whichUnit returns real
return 100.// Other example: return LoadReal(hash, GetHandleId(whichUnit), KEY_UNIT_BODY_SIZE)
endfunction
/**
* Same as GetUnitBodySize, but for destructables.
* Using occluder height is an idea of mine. Of course you can use your own values.
*/
function GetDestructableHeight takes destructable d returns real
return GetDestructableOccluderHeight(d)// Other example: return 100.
endfunction
/**
* Same as GetUnitBodySize, but for items.
* Again it's up to you to figure out a fictional item height.
*/
function GetItemHeight takes item i returns real
return 16.
endfunction
/**
* Unit indexers and missiles ( Only if you don't use a dummy recycling library )
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* It is most likely intended that projectiles don't run through a unit indexing process.
* ToogleUnitIndexer runs:
* • Directly before a dummy is created.
* • Directly after dummy unit creation.
*
* Please return the previous setup of your indexing tool ( enabled, disabled ),
* so Missile can properly reset it to the original state.
*/
private function ToogleUnitIndexer takes boolean enable returns boolean
local boolean prev = true//UnitIndexer.enabled
// set UnitIndexer.enabled = enable
return prev
endfunction
/**
* 4. API
* ¯¯¯¯¯¯
*/
//! novjass ( Disables the compiler until the next endnovjass )
// Custom type Missile for your projectile needs.
struct Missile extends array
// Constants:
// ==========
//
readonly static constant string ORIGIN = "origin"
// • Attach point name for fxs on dummies.
readonly static constant real HIT_BOX = (2./3.)
// • Fictional hit box for homing missiles.
// while 0 would be the toe and 1 the head of a unit.
// Available creators:
// ===================
//
static method createEx takes unit missileDummy, real impactX, real impactY, real impactZ returns Missile
// • Core creator method.
// • May launches any unit.
// • Units of type Missile_DUMMY_UNIT_ID get recycled in the end.
static method create takes real x, real y, real z, real angleInRadians, real distanceToTravel, real endZ returns Missile
// • Converts arguments to fit into createEx, then calls createEx.
static method createXYZ takes real x, real y, real z, real impactX, real impactY, real impactZ returns Missile
// • Converts arguments to fit into createEx, then calls createEx.
// Available destructors:
// ======================
//
return true
// • Core destructor.
// • Returning true in any of the interface methods of the MissileStruct module
// will destroy that instance instantly.
method destroy takes nothing returns nothing
// • Destroys the missile during the next timer callback.
method terminate takes nothing returns nothing
// • Destroys the missile instantly.
// Fields you can set and read directly:
// =====================================
//
unit source
unit target // For homing missiles.
real distance // Distance traveled.
player owner // Pseudo owner for faster onCollide evaluation. The proper dummy owner remains PLAYER_NEUTRAL_PASSIVE.
real speed // Vector lenght for missile movement in plane x / y. ( DOES NOT TAKE THE TIMER TIMEOUT IN ACCOUNT )
real acceleration
real damage
real turn // Set a turn rate for missiles.
integer data // For data transfer set and read data.
boolean recycle // Is automatically set to true, when a Missile reaches it's destination.
boolean wantDestroy // Set wantDestroy to true, to destroy a missile during the next timer callback.
// Neither collision nor collisionZ accept values below zero.
real collision // Collision size in plane x / y.
real collisionZ // Collision size in z - axis. ( deprecated )
// Fields you can only read:
// =========================
//
readonly boolean allocated
readonly unit dummy// The dummy unit of this missile.
// Position members for you needs.
readonly MissilePosition origin// Grants access to readonly members of MissilePosition,
readonly MissilePosition impact// which are "x", "y", "z", "angle", "distance", "slope" and the pitch angle "alpha".
// Furthermore method origin.move(x, y, z) and impact.move(x, y, z).
readonly real terrainZ
readonly real x
readonly real y
readonly real z
readonly real angle// Current angle in radians.
// Method operators for set and read:
// ==================================
//
method operator model= takes string modelFile returns nothing
method operator model takes nothing returns string
// • Adds an effect handle on a missile dummy to it's "origin".
// • You can read the file path.
// • For multiple effects access "this.dummy" in your struct.
method operator scale= takes real value returns nothing
method operator scale takes nothing returns real
// • Set and read the scaling of the dummy unit.
method operator curve= takes real value returns nothing
method operator curve takes nothing returns real
// • Enables curved movement for your missile. ( Pass in radians, NOT degrees )
// • Do not pass in PI/2.
method operator arc= takes real value returns nothing
method operator arc takes nothing returns real
// • Enables arcing movement for your missile. ( Pass in radians, NOT degrees )
// • Do not pass in PI/2.
// Methods for missile movement:
// =============================
//
method bounce takes nothing returns nothing
// • Moves the MissilePosition "origin" to the current missile coordinates.
// • Resets the distance traveled to 0.
method deflect takes real tx, real ty returns nothing
// • Deflect the missile towards tx, ty. Then calls bounce.
method deflectEx takes real tx, real ty, real tz returns nothing
// • Deflect the missile towards tx, ty, tz. Then calls bounce.
method flightTime2Speed takes real duration returns nothing
// • Converts a fly time to a vector lenght for member "speed".
// • Does not take acceleration into account. ( Disclaimer )
method setMovementSpeed takes real value returns nothing
// • Converts Warcraft III movement speed to a vector lenght for member "speed".
// Methods for missile collision: ( all are hashtable entries )
// ==============================
// By default a widget can only be hit once per missile.
//
method hitWidget takes widget w returns nothing
// • Saves a widget in the memory as hit by this instance.
method hasHitWidget takes widget w returns boolean
// • Returns true, if "w" has been hit by this instance.
method removeHitWidget takes widget w returns nothing
// • Removes a widget from this missile's memory for widget collision. ( Can hit "w" again )
method flushHitWidgets takes nothing returns nothing
// • Flushes a missile's memory for widget collision. ( All widgets can be hit again )
method enableHitAfter takes widget w, real seconds returns nothing
// • Automatically calls removeHitWidget(w) after "seconds" time. ( Can hit "w" again after given time )
// Module MissileStruct:
// =====================
//
module MissileLaunch // ( optional )
module MissileStruct
// • Enables the entire missile interface for that struct.
// Interface in structs: ( Must implement module MissileStruct )
// =====================
//
// • Write one, many or all of the static method below into your struct.
// • Missiles launched in this struct will run those methods, when their events fire.
//
// • All of those static methods must return a boolean.
// a) return true --> destroys the missile instance instantly.
// b) return false --> keep on flying.
// Available static method:
// ========================
//
static method onCollide takes Missile missile, unit hit returns boolean
// • Runs for units in collision range of a missile.
static method onDestructable takes Missile missile, destructable hit returns boolean
// • Runs for destructables in collision range of a missile.
static method onItem takes Missile missile, item hit returns boolean
// • Runs for items in collision range of a missile.
static method onTerrain takes Missile missile returns boolean
// • Runs when a missile collides with the terrain. ( Ground and cliffs )
static method onFinish takes Missile missile returns boolean
// • Runs only when a missile reaches it's destination.
// • However does not run, if a Missile is destroyed in another method.
static method onPeriod takes Missile missile returns boolean
// • Runs every Missile_TIMER_TIMEOUT seconds.
static method onRemove takes Missile missile returns boolean
// • Runs when a missile is destroyed.
// • Unlike onFinish, onRemove will runs ALWAYS when a missile is destroyed!!!
//
// For onRemove the returned boolean has a unique meaning:
// • Return true will recycle this missile delayed. ( Only if WRITE_DELAYED_MISSILE_RECYCLING = true )
// • Return false will recycle this missile right away.
static method launch takes Missile toLaunch returns nothing
// • Well ... Launches this Missile.
// • Missile "toLaunch" will behave as declared in the struct. ( static methods from above )
// Misc: ( From the global setup )
// =====
//
// Constants:
// ==========
//
public constant real TIMER_TIMEOUT
public constant player NEUTRAL_PASSIVE
public constant integer DUMMY_UNIT_ID
public constant real MAXIMUM_COLLISION_SIZE
public constant boolean USE_COLLISION_Z_FILTER
public constant boolean WRITE_DELAYED_MISSILE_RECYCLING
public constant boolean USE_DESTRUCTABLE_FILTER
public constant boolean USE_ITEM_FILTER
readonly static constant string ORIGIN
readonly static constant real HIT_BOX
// Functions:
// ==========
//
public function GetLocZ takes real x, real y returns real
function GetUnitBodySize takes unit whichUnit returns real
function GetDestructableHeight takes destructable d returns real
function GetItemHeight takes item i returns real
//========================================================================
// Missile system. Make changes carefully.
//========================================================================
//! endnovjass ( Enables the compiler )
// Hello and welcome to Missile.
// I wrote a guideline for every piece of code inside Missile, so you
// can easily understand how the it gets compiled and evaluated.
//
// Let's go!
globals
// Core constant handle variables of Missile.
private constant trigger CORE = CreateTrigger()
private constant trigger MOVE = CreateTrigger()
private constant timer TMR = CreateTimer()
private constant location LOC = Location(0., 0.)
private constant rect RECT = Rect(0., 0., 0., 0.)
private constant group GROUP = CreateGroup()
private constant hashtable HASH = InitHashtable()
// For starting and stopping the timer.
private integer active = 0
// Arrays for data structure.
private integer array instances
private Missile array missileList
private boolexpr array expression
private triggercondition array condition
private integer array remove
private boolean array destroying
private boolean array recycling
private integer array nextNode
private integer array prevNode
// Internal widget filter functions.
private boolexpr destFilter
private boolexpr itemFilter
private boolexpr unitFilter
endglobals
public function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
// For WRITE_DELAYED_MISSILE_RECYCLING = true Missile will hold back
// dummies for DELAYED_MISSILE_DEATH_ANIMATION_TIME before they are recylced. ( Code placed in a static if )
//
//! runtextmacro optional WRITE_MISSILE_RECYCLE_BIN("WRITE_DELAYED_MISSILE_RECYCLING", "DELAYED_MISSILE_DEATH_ANIMATION_TIME")
// The code of WRITE_MISSILE_POSITION_CODE boxes a missiles position and does the required trigonometry.
//
//! runtextmacro WRITE_MISSILE_POSITION_CODE()
// Missiles structure works like a linked list with the folling methods:
// allocateCollection(), allocateNode(), insertFront(node) and remove()
//
private keyword MissileStructure
struct Missile extends array
implement MissileStructure
// Constants:
// ==========
//
// Attach point name for effects created by model=.
readonly static constant string ORIGIN = "origin"
// Set a ficitional hit box in range of 0 to 1,
// while 0 is the toe and 1 the head of a unit.
readonly static constant real HIT_BOX = (2./3.)
// DEBUG_MODE only members:
// ========================
//
// Checks for double launching. Throws an error message.
debug boolean launched
// Private members:
// ================
//
// The position of a missile using curve= does not
// match the position used by Missile's trigonometry.
// Therefore we need this member two times.
// Readable x / y / z for your needs and posX / posY for cool mathematics.
private real posX
private real posY
private real dist// distance
// Readonly members:
// =================
//
// Prevents a double free case.
readonly boolean allocated
// The dummy unit.
readonly unit dummy
// Position members for your needs.
readonly MissilePosition origin// Grants access to readonly members of MissilePosition,
readonly MissilePosition impact// which are "x", "y", "z", "angle", "distance", "slope" and "alpha".
readonly real terrainZ
readonly real x
readonly real y
readonly real z
readonly real angle// Current angle
readonly real prevX
readonly real prevY
readonly real prevZ
// Collision detection type. ( Evaluated new in each loop )
readonly integer collisionType// Current collision type ( circular or rectangle )
// Members you can set:
// ====================
//
unit source
unit target // For homing missiles.
real distance // Distance traveled.
player owner // Pseudo owner for faster onCollide evaluation. The proper dummy owner is PLAYER_NEUTRAL_PASSIVE.
real speed // Vector lenght for missile movement in plane x / y.
real acceleration
real damage
integer data // For data transfer set and read data.
boolean recycle // Is set to true, when a Missile reaches it's destination.
real turn // Sets a turn rate for a missile.
real collision // Collision size in plane x / y.
// Setting collision z is deprecated since Missile v2.5.
method operator collisionZ= takes real value returns nothing
endmethod
method operator collisionZ takes nothing returns real
return collision
endmethod
// Operator overloading:
// =====================
//
// Special effect on the missile dummy. For multiple effect attaching, access unit "dummy" directly.
private effect sfx
private string path
method operator model= takes string file returns nothing
if sfx != null then
call DestroyEffect(sfx)
set sfx = null
endif
// null and ""
if StringLength(file) > 0 then
set sfx = AddSpecialEffectTarget(file, dummy, ORIGIN)
set path = file
else
set path = null
endif
endmethod
method operator model takes nothing returns string
return path
endmethod
real open
// Enables curved movement for your missile.
// Remember that the result of Tan(PI/2) is infinity.
method operator curve= takes real value returns nothing
set open = Tan(value)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)
endmethod
real height
// Enables arcing movement for your missile.
method operator arc= takes real value returns nothing
set height = Tan(value)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)
endmethod
private real scaling
method operator scale= takes real value returns nothing
call SetUnitScale(dummy, value, 0., 0.)
set scaling = value
endmethod
method operator scale takes nothing returns real
return scaling
endmethod
// Methods:
// ========
//
method bounce takes nothing returns nothing
call origin.move(x, y, z)
set dist = 0.
endmethod
method deflect takes real tx, real ty returns nothing
local real a = 2.*Atan2(ty - y, tx - x) + bj_PI - angle
call impact.move(x + (origin.distance - dist)*Cos(a), y + (origin.distance - dist)*Sin(a), impact.z)
call bounce()
endmethod
method deflectEx takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
endmethod
method flightTime2Speed takes real duration returns nothing
set speed = RMaxBJ(0.00000001, (origin.distance - dist)*Missile_TIMER_TIMEOUT/RMaxBJ(0.00000001, duration))
endmethod
method setMovementSpeed takes real value returns nothing
set speed = value*Missile_TIMER_TIMEOUT
endmethod
boolean wantDestroy// For "true" a missile is destroyed on the next timer callback. 100% safe.
method destroy takes nothing returns nothing
set wantDestroy = true
endmethod
// Instantly destroys a missile.
method terminate takes nothing returns nothing
if allocated then
call remove()
call impact.destroy()
call origin.destroy()
call DestroyEffect(sfx)
call FlushChildHashtable(HASH, this)
if GetUnitTypeId(dummy) == Missile_DUMMY_UNIT_ID then
// MissileRecycler > Dummy > xe.
static if LIBRARY_MissileRecycler then
call RecycleMissile(dummy)
elseif LIBRARY_Dummy and Dummy.create.exists then
call Dummy[dummy].destroy()
elseif LIBRARY_xedummy and xedummy.release.exists then
call xedummy.release(dummy)
else
call RemoveUnit(dummy)
endif
endif
set sfx = null
set source = null
set target = null
set dummy = null
set owner = null
endif
endmethod
// Runs in createEx.
//! textmacro MISSILE_RESET_ALL_MEMBERS
set path = null
set speed = 0.
set acceleration = 0.
set distance = 0.
set dist = 0
set dist = 0.
set height = 0.
set turn = 0.
set open = 0.
set collision = 0.
set collisionType = 0
set stackSize = 0
set scaling = 1.
set wantDestroy = false
set recycle = false
//! endtextmacro
// Launches a dummy of your choice.
static method createEx takes unit missileDummy, real impactX, real impactY, real impactZ returns thistype
local thistype this = thistype.allocateNode()
local real originX = GetUnitX(missileDummy)
local real originY = GetUnitY(missileDummy)
local real originZ = GetUnitFlyHeight(missileDummy)
//
//! runtextmacro MISSILE_RESET_ALL_MEMBERS()
//
set origin = MissilePosition.create(originX, originY, originZ)
set impact = MissilePosition.create(impactX, impactY, impactZ)
call MissilePosition.link(origin, impact)
set posX = originX
set posY = originY
set x = originX
set y = originY
set z = originZ
set angle = origin.angle
set dummy = missileDummy
call SetUnitFlyHeight(missileDummy, originZ, 0.)
call SaveUnitHandle(HASH, this, GetHandleId(missileDummy), missileDummy)
//
static if LIBRARY_ErrorMessage then
debug call ThrowWarning(GetUnitTypeId(missileDummy) == 0, "Missile", "createEx", "missileDummy", this, "Invalid missile dummy unit ( null )!")
endif
debug set launched = false
return this
endmethod
// Freaky static if ensures these libraries really don't exist.
static if not LIBRARY_MissileRecycler and not LIBRARY_Dummy and not Dummy.create.exists and not LIBRARY_xe_dummy and not xe_dummy.new.exists then
private static method newMissileUnit takes real x, real y, real z, real face returns unit
local boolean prev = ToogleUnitIndexer(false)
set bj_lastCreatedUnit = CreateUnit(Missile_NEUTRAL_PASSIVE, Missile_DUMMY_UNIT_ID , x, y, face)
call ToogleUnitIndexer(prev)
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call UnitAddAbility(bj_lastCreatedUnit, 'Amrf')
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0.)
call PauseUnit(bj_lastCreatedUnit, true)
return bj_lastCreatedUnit
endmethod
endif
// MissileRecylcer > Dummy > xe > Missile.
//! textmacro MISSILE_GET_DUMMY_FROM_LIBRARY
static if LIBRARY_MissileRecycler then
return createEx(GetRecycledMissile(x, y, z, angle*bj_RADTODEG), impactX, impactY, impactZ)
elseif LIBRARY_Dummy and Dummy.create.exists then
local Dummy dummy = Dummy.create(x, y, angle*bj_RADTODEG)
call SetUnitFlyHeight(dummy.unit, z, 0.)
return createEx(dummy.unit, impactX, impactY, impactZ)
elseif LIBRARY_xedummy and xedummy.new.exists then
set bj_lastCreatedUnit = xedummy.new(Missile_NEUTRAL_PASSIVE, x, y, angle*bj_RADTODEG)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0.)
return createEx(bj_lastCreatedUnit, impactX, impactY, impactZ)
else
return createEx(Missile.newMissileUnit(x, y, z, angle*bj_RADTODEG), impactX, impactY, impactZ)
endif
//! endtextmacro
// Wrapper to createEx.
static method create takes real x, real y, real z, real angle, real distance, real impactZ returns thistype
local real impactX = x + distance*Cos(angle)
local real impactY = y + distance*Sin(angle)
// Get the dummy unit.
//! runtextmacro MISSILE_GET_DUMMY_FROM_LIBRARY()
endmethod
// Wrapper to createEx.
static method createXYZ takes real x, real y, real z, real impactX, real impactY, real impactZ returns thistype
local real angle = Atan2(impactY - y, impactX - x)
// Get the dummy unit.
//! runtextmacro MISSILE_GET_DUMMY_FROM_LIBRARY()
endmethod
// Missile motion takes place every Missile_TIMER_TIMEOUT
// before accessing each active struct.
static Missile temp = 0
static method move takes nothing returns boolean
local integer loops = 0 // Current iteration.
local integer limit = 150 // Set iteration border per trigger evaluation to avoid hitting the operation limit.
local thistype this = thistype.temp
local MissilePosition p
local real a
local real d
local unit u
local real newX
local real newY
local real vel
local real point
local real pitch
loop
exitwhen 0 == this or loops == limit
set p = origin
// Save previous, respectively current missile position.
set prevX = x
set prevY = y
set prevZ = z
// Evaluate the collision type.
set vel = speed
set speed = vel + acceleration
if vel < collision*Missile_COLLISION_ACCURACY_FACTOR then
set collisionType = Missile_COLLISION_TYPE_CIRCLE
else
set collisionType = Missile_COLLISION_TYPE_RECTANGLE
endif
// Update missile guidance to its intended target.
set u = target
if u != null then
if 0 == GetUnitTypeId(u) then
set target = null
else
call origin.move(x, y, z)
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + GetUnitBodySize(u)*Missile.HIT_BOX)
set dist = 0
set height = 0
set curve = 0
endif
endif
set a = p.angle
// Update the missile facing angle depending on the turn ratio.
if 0. != turn and Cos(angle - a) < Cos(turn) then
if 0. > Sin(a - angle) then
set angle = angle - turn
else
set angle = angle + turn
endif
else
set angle = a
endif
// Update the missile position on the parabola.
set d = p.distance// origin - impact distance.
set recycle = dist + vel >= d
if recycle then// Maximum distance reached.
set point = d
set distance = distance + d - dist
else
set distance = distance + vel
set point = dist + vel
endif
set dist = point
set newX = posX + vel*Cos(angle)
set newY = posY + vel*Sin(angle)
set posX = newX
set posY = newY
// Update point(x/y) if a curving trajectory is defined.
set u = dummy
if 0. != open and target == null then
set vel = 4*open*point*(d - point)/p.square
set a = angle + bj_PI/2
set newX = newX + vel*Cos(a)
set newY = newY + vel*Sin(a)
set a = angle + Atan(-((4*open)*(2*point - d))/p.square)
else
set a = angle
endif
set x = newX
set y = newY
// Update pos z if an arc or height is set.
call MoveLocation(LOC, newX, newY)
set terrainZ = GetLocationZ(LOC)
set pitch = p.alpha
if 0. == height and 0. == pitch then
set z = p.z - terrainZ
else
set z = p.z - terrainZ + p.slope*point
if 0. != height and target == null then
set z = z + (4*height*point*(d - point)/p.square)
set pitch = pitch - Atan(((4*height)*(2*point - d))/p.square)*bj_RADTODEG
endif
endif
// Update the pitch angle of the dummy unit.
if GetUnitTypeId(u) == Missile_DUMMY_UNIT_ID then
call SetUnitAnimationByIndex(u, R2I(pitch + 90.5))
endif
// Move the missile dummy via native.
call SetUnitFlyHeight(u, z, 0.)
call SetUnitFacing(u, a*bj_RADTODEG)
// WorldBounds > BoundSentinel.
static if not LIBRARY_BoundSentinel and not LIBRARY_WorldBounds then
if RectContainsCoords(bj_mapInitialPlayableArea, newX, newY) then
call SetUnitX(u, newX)
call SetUnitY(u, newY)
endif
elseif LIBRARY_WorldBounds then
if newX < WorldBounds.maxX and newX > WorldBounds.minX and newY < WorldBounds.maxY and newY > WorldBounds.minY then
call SetUnitX(u, newX)
call SetUnitY(u, newY)
endif
else
call SetUnitX(u, newX)
call SetUnitY(u, newY)
endif
set loops = loops + 1
set this = nextNode[this]
endloop
set u = null
set thistype.temp = this
return this == 0
endmethod
// Widget collision API:
// =====================
//
// Runs automatically on widget collision.
method hitWidget takes widget w returns nothing
if w != null then
call SaveWidgetHandle(HASH, this, GetHandleId(w), w)
endif
endmethod
// All widget which have been hit return true.
method hasHitWidget takes widget w returns boolean
return HaveSavedHandle(HASH, this, GetHandleId(w))
endmethod
// Removes a widget from the missile's memory of hit widgets. ( This widget can be hit again )
method removeHitWidget takes widget w returns nothing
if w != null then
call RemoveSavedHandle(HASH, this, GetHandleId(w))
endif
endmethod
// Flushes a missile's memory for collision. ( All widgets can be hit again )
method flushHitWidgets takes nothing returns nothing
call FlushChildHashtable(HASH, this)
call hitWidget(dummy)
endmethod
// Tells missile to call removeHitWidget(w) after "seconds" time.
// Does not apply to widgets, which are already hit by this missile.
readonly integer stackSize
method enableHitAfter takes widget w, real seconds returns nothing
local integer id = GetHandleId(w)
if w != null then
if not HaveSavedInteger(HASH, this, id) then
call SaveInteger(HASH, this, id, stackSize)
call SaveInteger(HASH, this, stackSize, id)
set stackSize = stackSize + 1
endif
call SaveReal(HASH, this, id, seconds)// Set time.
endif
endmethod
method updateStack takes nothing returns nothing
local integer dex = 0
local integer id
local real time
loop
exitwhen dex == stackSize
set id = LoadInteger(HASH, this, dex)
set time = LoadReal(HASH, this, id) - Missile_TIMER_TIMEOUT
if time <= 0. or not HaveSavedHandle(HASH, this, id) then
set stackSize = stackSize - 1
set id = LoadInteger(HASH, this, stackSize)
call SaveInteger(HASH, this, dex, id)
call SaveInteger(HASH, this, id, dex)
// Enables hit.
call RemoveSavedHandle(HASH, this, id)
// Remove data from stack.
call RemoveSavedReal(HASH, this, id)
call RemoveSavedInteger(HASH, this, id)
call RemoveSavedInteger(HASH, this, stackSize)
else
call SaveReal(HASH, this, id, time)
set dex = dex + 1
endif
endloop
endmethod
// Widget collision code:
// ======================
//
private static boolean circle = true
//
// 1.) Rectangle collision for fast moving missiles with small collision radius.
//
// Runs for destructables and items in a rectangle.
// Checks if widget w is in collision range of a missile.
// Is not precise in z - axis collision.
private method isWidgetInRectangle takes widget w, real wz, real distance returns boolean
local real wx = GetWidgetX(w)
local real wy = GetWidgetY(w)
local real dz = Missile_GetLocZ(wx, wy) - terrainZ
local real dx = x - prevX
local real dy = y - prevY
local real s = (dx*(wx - prevX) + dy*(wy - prevY))/(dx*dx + dy*dy)
if s < 0. then
set s = 0.
elseif s > 1 then
set s = 1.
endif
set dx = (prevX + s*dx) - wx
set dy = (prevY + s*dy) - wy
return dx*dx + dy*dy <= distance*distance and dz + wz >= z - distance and dz <= z + distance
endmethod
//
// 2.) Circular collision detection for all other missiles.
//
// Returns true for widgets in a xyz collision range.
private method isWidgetInRange takes widget w, real wz, real distance returns boolean
local real wx = GetWidgetX(w)
local real wy = GetWidgetY(w)
local real dz = Missile_GetLocZ(wx, wy) - terrainZ
// collision in plane x and y, collision in z axis.
return IsUnitInRangeXY(dummy, wx, wy, distance) and dz + wz >= z - distance and dz <= z + distance
endmethod
//
// 3.) Action functions inside the widget enumeration thread.
//
// Runs for every enumerated destructable.
// • Directly filters out already hit destructables.
// • Distance formula based on the Pythagorean theorem.
//
private static method filterDests takes nothing returns boolean
local destructable d = GetFilterDestructable()
local boolean b = false
if not HaveSavedHandle(HASH, temp, GetHandleId(d)) then
if circle then
set b = temp.isWidgetInRange(d, GetDestructableHeight(d), temp.collision)
else
set b = temp.isWidgetInRectangle(d, GetDestructableHeight(d), temp.collision)
endif
endif
set d = null
return b
endmethod
//
// Runs for every enumerated item.
// • Directly filters out already hit items.
// • Distance formula based on the Pythagorean theorem.
// • Items have a fix collision size of 16.
//
private static method filterItems takes nothing returns boolean
local item i = GetFilterItem()
local boolean b = false
if not HaveSavedHandle(HASH, temp, GetHandleId(i)) then
if circle then // Item in missile collision size or item pathing in missile range.
set b = temp.isWidgetInRange(i, GetItemHeight(i), RMaxBJ(temp.collision, 16.))
else
set b = temp.isWidgetInRectangle(i, GetItemHeight(i), RMaxBJ(temp.collision, 16.))
endif
endif
set i = null
return b
endmethod
//
// 4.) Filter function for rectangle unit collision.
//
// Runs for every enumerated units.
// • Filters out units which are not in collision range in plane x / y.
// • Inlined and therefore a bit faster than item and destructable collision.
//
private static method filterUnits takes nothing returns boolean
local thistype this = thistype.temp
local unit u = GetFilterUnit()
local real dx
local real dy
local real s
local boolean is = false
if not HaveSavedHandle(HASH, this, GetHandleId(u)) then
set dx = x - prevX
set dy = y - prevY
set s = (dx*(GetUnitX(u) - prevX) + dy*(GetUnitY(u)- prevY))/(dx*dx + dy*dy)
if s < 0. then
set s = 0.
elseif s > 1. then
set s = 1.
endif
set is = IsUnitInRangeXY(u, prevX + s*dx, prevY + s*dy, collision)
endif
set u = null
return is
endmethod
//
// 5.) Proper rect preparation.
//
// For rectangle.
private method prepareRectRectangle takes nothing returns nothing
local real x1 = prevX
local real y1 = prevY
local real x2 = x
local real y2 = y
local real d = collision + Missile_MAXIMUM_COLLISION_SIZE
// What is min, what is max ...
if x1 < x2 then
if y1 < y2 then
call SetRect(RECT, x1 - d, y1 - d, x2 + d, y2 + d)
else
call SetRect(RECT, x1 - d, y2 - d, x2 + d, y1 + d)
endif
elseif y1 < y2 then
call SetRect(RECT, x2 - d, y1 - d, x1 + d, y2 + d)
else
call SetRect(RECT, x2 - d, y2 - d, x1 + d, y1 + d)
endif
endmethod
//
// For circular.
private method prepareRectCircle takes nothing returns nothing
local real d = collision + Missile_MAXIMUM_COLLISION_SIZE
call SetRect(RECT, x - d, y - d, x + d, y + d)
endmethod
//
// 5.) API for the MissileStruct iteration.
//
method groupEnumUnitsRectangle takes nothing returns nothing
call prepareRectRectangle()
set thistype.temp = this
call GroupEnumUnitsInRect(GROUP, RECT, unitFilter)
endmethod
//
// Prepares destructable enumeration, then runs enumDests.
method checkDestCollision takes code func returns nothing
set circle = collisionType == Missile_COLLISION_TYPE_CIRCLE
if circle then
call prepareRectCircle()
else
call prepareRectRectangle()
endif
//
set thistype.temp = this
call EnumDestructablesInRect(RECT, destFilter, func)
endmethod
//
// Prepares item enumeration, then runs enumItems.
method checkItemCollision takes code func returns nothing
set circle = collisionType == Missile_COLLISION_TYPE_CIRCLE
if circle then
call prepareRectCircle()
else
call prepareRectRectangle()
endif
//
set thistype.temp = this
call EnumItemsInRect(RECT, itemFilter, func)
endmethod
static if Missile_WRITE_DELAYED_MISSILE_RECYCLING then
method nullBefore takes nothing returns nothing
set dummy = null
endmethod
endif
// Does not check for 'Aloc' and 'Amrf' as they could be customized.
private static method onInit takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug local boolean prev = ToogleUnitIndexer(false)
debug local unit dummy = CreateUnit(Missile_NEUTRAL_PASSIVE, Missile_DUMMY_UNIT_ID, 0., 0., 0.)
debug call ToogleUnitIndexer(prev)
//
debug call ThrowError((GetUnitTypeId(dummy) != Missile_DUMMY_UNIT_ID), "Missile", "DEBUG_MISSILE", "type id", 0, "Global setup for public integer DUMMY_UNIT_ID is incorrect! This map currently can't use Missile!")
debug call ThrowError((Missile_MAXIMUM_COLLISION_SIZE < 0), "Missile", "DEBUG_MISSILE", "collision", 0, "Global setup for public real MAXIMUM_COLLISION_SIZE is incorrect, below zero! This map currently can't use Missile!")
debug call RemoveUnit(dummy)
debug set dummy = null
endif
//
set unitFilter = Filter(function thistype.filterUnits)
set destFilter = Filter(function thistype.filterDests)
set itemFilter = Filter(function thistype.filterItems)
call TriggerAddCondition(MOVE, Condition(function thistype.move))
endmethod
endstruct
// You made it to the end of Missile, but we are not finished.
// Do you remember about the data structure, the delayed recycler
// and of course our interface module "MissileStruct"
//
// This comes now!
// Debug code taken from List ( full credits to Nestharus )
private module MissileStructure
private static thistype collectionCount = 0
private static thistype nodeCount = 0
static if LIBRARY_ErrorMessage then
debug private boolean isNode
debug private boolean isCollection
endif
private thistype _list
method operator list takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "list", "thistype", this, "Attempted To Read Null Node.")
debug call ThrowError(not isNode, "MissileStructure", "list", "thistype", this, "Attempted To Read Invalid Node.")
endif
return _list
endmethod
private thistype _next
method operator next takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "next", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "MissileStructure", "next", "thistype", this, "Attempted To Read Invalid Node.")
endif
return _next
endmethod
private thistype _prev
method operator prev takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "prev", "thistype", this, "Attempted To Go Out Of Bounds.")
debug call ThrowError(not isNode, "MissileStructure", "prev", "thistype", this, "Attempted To Read Invalid Node.")
endif
return _prev
endmethod
private thistype _first
method operator first takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "first", "thistype", this, "Attempted To Read Null List.")
debug call ThrowError(not isCollection, "MissileStructure", "first", "thistype", this, "Attempted To Read Invalid List.")
endif
return _first
endmethod
private thistype _last
method operator last takes nothing returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "last", "thistype", this, "Attempted To Read Null List.")
debug call ThrowError(not isCollection, "MissileStructure", "last", "thistype", this, "Attempted To Read Invalid List.")
endif
return _last
endmethod
static method allocateCollection takes nothing returns thistype
local thistype this = thistype(0)._first
if (0 == this) then
static if LIBRARY_ErrorMessage then
debug call ThrowError(collectionCount == 8191, "MissileStructure", "allocateCollection", "thistype", 0, "Overflow.")
endif
set this = collectionCount + 1
set collectionCount = this
else
set thistype(0)._first = _first
endif
static if LIBRARY_ErrorMessage then
debug set isCollection = true
endif
set _first = 0
return this
endmethod
static method allocateNode takes nothing returns thistype
local thistype this = thistype(0)._next
if (0 == this) then
static if LIBRARY_ErrorMessage then
debug call ThrowError(nodeCount == 8191, "MissileStructure", "allocateNode", "thistype", 0, "Overflow.")
endif
set this = nodeCount + 1
set nodeCount = this
else
set thistype(0)._next = _next
endif
set allocated = true
return this
endmethod
method insertFront takes thistype node returns thistype
// Extra static unique list for missile motion.
set nextNode[node] = 0
set prevNode[node] = prevNode[0]
set nextNode[prevNode[0]] = node
set prevNode[0] = node
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "List", "push", "thistype", this, "Attempted To Push On To Null List.")
debug call ThrowError(not isCollection, "List", "push", "thistype", this, "Attempted To Push On To Invalid List.")
debug set node.isNode = true
endif
set node._list = this
if (_first == 0) then
set _first = node
set _last = node
set node._next = 0
else
set _first._prev = node
set node._next = _first
set _first = node
endif
set node._prev = 0
return node
endmethod
method remove takes nothing returns nothing
local thistype node = this
set this = node._list
static if LIBRARY_ErrorMessage then
debug call ThrowError(node == 0, "MissileStructure", "remove", "thistype", this, "Attempted To Remove Null Node.")
debug call ThrowError(not node.isNode, "MissileStructure", "remove", "thistype", this, "Attempted To Remove Invalid Node (" + I2S(node) + ").")
debug set node.isNode = false
endif
set node._list = 0
if (0 == node._prev) then
set _first = node._next
else
set node._prev._next = node._next
endif
if (0 == node._next) then
set _last = node._prev
else
set node._next._prev = node._prev
endif
set node._next = thistype(0)._next
set thistype(0)._next = node
set node.allocated = false
// Static unique list for missile motion.
set nextNode[prevNode[node]] = nextNode[node]
set prevNode[nextNode[node]] = prevNode[node]
endmethod
endmodule
// Boolean expressions per struct:
// ===============================
//
// Condition function for the core trigger evaluation. ( Runs for all struct using module MissileStruct )
private function MissileCreateExpression takes integer structId, code func returns nothing
set expression[structId] = Condition(func)
endfunction
// Creates a collection for a struct. ( Runs for all struct using module MissileStruct )
private function MissileCreateCollection takes integer structId returns nothing
set missileList[structId] = Missile.allocateCollection()
endfunction
// Core:
// =====
//
// Fires every Missile_TIMER_TIMEOUT.
private function Fire takes nothing returns nothing
local integer i = remove[0]
set remove[0] = 0
loop
exitwhen 0 == i
if recycling[i] then
call TriggerRemoveCondition(CORE, condition[i])
set condition[i] = null
set active = active - 1
endif
set destroying[i] = false
set recycling[i] = false
set i = remove[i]
endloop
if 0 == active then
call PauseTimer(TMR)
else
// Move all launched missiles.
set Missile.temp = nextNode[0]
set i = 0
loop
exitwhen TriggerEvaluate(MOVE)
exitwhen i == 60// Moved over 8910 missiles, something buggy happened.
set i = i + 1 // This case is impossible, hence never happens. But if I'm wrong, which also never happens
endloop // then the map 100% crashes. i == 66 will prevent the game crash and Missile will start to
// to debug itself over the next iterations.
// Access all structs using module MissileStruct.
static if DEBUG_MODE and LIBRARY_ErrorMesssage then
if not TriggerEvaluate(CORE) then
call ThrowWarning(true, "Missile", "Fire", "op limit", 0, /*
*/"You just ran into a op limit!
The op limit protection of Missile is insufficient for your map.
The code of the missile module can't run in one thread and must be splitted.
If unsure make a post in the official 'The Hive Workshop' forum thread of Missile!")
endif
else
call TriggerEvaluate(CORE)
endif
endif
endfunction
// Conditionally starts the timer.
private function StartPeriodic takes integer structId returns nothing
if 0 == instances[structId] or destroying[structId] then
if destroying[structId] then
set recycling[structId] = false
else
if 0 == active then
call TimerStart(TMR, Missile_TIMER_TIMEOUT, true, function Fire)
endif
set active = active + 1
set condition[structId] = TriggerAddCondition(CORE, expression[structId])
endif
endif
set instances[structId] = instances[structId] + 1
endfunction
// Conditionally stops the timer in the next callback.
private function StopPeriodic takes integer structId returns nothing
set instances[structId] = instances[structId] - 1
if 0 == instances[structId] and condition[structId] != null then
if not destroying[structId] and not recycling[structId] then
set destroying[structId] = true
set recycling[structId] = true
set remove[structId] = remove[0]
set remove[0] = structId
endif
endif
endfunction
// Modules:
// ========
//
// You want to place module MissileLaunch at the very top of your struct.
module MissileLaunch
static method launch takes Missile missile returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(missile.launched, "thistype", "launch", "missile.launched", missile, "This missile was already launched before!")
endif
debug set missile.launched = true
//
call missileList[thistype.typeid].insertFront(missile)
call StartPeriodic(thistype.typeid)
endmethod
endmodule
module MissileTerminate
// Called from missileIterate. "P" to avoid code collision.
static method missileTerminateP takes Missile node returns nothing
if node.allocated then
static if thistype.onRemove.exists then
static if Missile_WRITE_DELAYED_MISSILE_RECYCLING and RecycleBin.recycle.exists then
if thistype.onRemove(node) and GetUnitTypeId(node.dummy) == Missile_DUMMY_UNIT_ID then
call RecycleBin.recycle(node.dummy)
call node.nullBefore()
endif
else
call thistype.onRemove(node)
endif
endif
call node.terminate()
call StopPeriodic(thistype.typeid)
endif
endmethod
endmodule
// Allows you to inject missile in certain stages of the motion process.
module MissileAction
static if DEBUG_MODE then
// Runs check during compile time.
static if thistype.onMissile.exists then
Error Message from library Missile in struct thistype !
thistype.onMissile is a reserved name for Missile, once you implemented MissileStruct.
thistype.onMissile is currently not supported by library Missile.
Please delete or re-name that method.
endif
endif
static if thistype.onItem.exists then
private static method missileActionItem takes nothing returns nothing
local item i = GetEnumItem()
local Missile this = Missile.temp
if this.allocated then
call SaveItemHandle(HASH, this, GetHandleId(i), i)
if thistype.onItem(this, i) then
call missileTerminateP(this)
endif
endif
set i = null
endmethod
endif
static if thistype.onDestructable.exists then
private static method missileActionDest takes nothing returns nothing
local destructable d = GetEnumDestructable()
local Missile this = Missile.temp
if this.allocated then
call SaveDestructableHandle(HASH, this, GetHandleId(d), d)
if thistype.onDestructable(this, d) then
call missileTerminateP(this)
endif
endif
set d = null
endmethod
endif
// Runs every Missile_TIMER_TIMEOUT for this struct.
static method missileIterateP takes nothing returns boolean
local Missile this = missileList[thistype.typeid].first
local Missile node
local real collideZ
local boolean b
local unit u
loop
exitwhen 0 == this
set node = this.next// The linked list should not lose the next node.
if this.wantDestroy then
call thistype.missileTerminateP(this)
else
if this.stackSize > 0 then
call this.updateStack()
endif
// Runs unit collision.
static if thistype.onCollide.exists then
if this.allocated and 0. < this.collision then
set b = this.collisionType == Missile_COLLISION_TYPE_RECTANGLE
if b then
call this.groupEnumUnitsRectangle()
else
call GroupEnumUnitsInRange(GROUP, this.x, this.y, this.collision + Missile_MAXIMUM_COLLISION_SIZE, null)
endif
loop
set u = FirstOfGroup(GROUP)
exitwhen u == null
call GroupRemoveUnit(GROUP, u)
if not HaveSavedHandle(HASH, this, GetHandleId(u)) then
if IsUnitInRange(u, this.dummy, this.collision) or b then
// Eventually run z collision checks.
static if Missile_USE_COLLISION_Z_FILTER then
set collideZ = Missile_GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u) - this.terrainZ
if (collideZ + GetUnitBodySize(u) >= this.z - this.collisionZ) and (collideZ <= this.z + this.collisionZ) then
// Mark as hit.
call SaveUnitHandle(HASH, this, GetHandleId(u), u)
if thistype.onCollide(this, u) then
call thistype.missileTerminateP(this)
exitwhen true
endif
endif
else
// Runs unit collision without z collision checks.
call SaveUnitHandle(HASH, this, GetHandleId(u), u)
if thistype.onCollide(this, u) then
call thistype.missileTerminateP(this)
exitwhen true
endif
endif
endif
endif
endloop
endif
endif
// Runs destructable collision.
static if thistype.onDestructable.exists then
// Check if the missile is not terminated.
if this.allocated and 0. < this.collision then
call this.checkDestCollision(function thistype.missileActionDest)
endif
endif
// Runs item collision.
static if thistype.onItem.exists then
// Check if the missile is not terminated.
if this.allocated and 0. < this.collision then
call this.checkItemCollision(function thistype.missileActionItem)
endif
endif
// Runs when the destination is reached.
if this.recycle and this.allocated then
static if thistype.onFinish.exists then
if thistype.onFinish(this) then
call thistype.missileTerminateP(this)
endif
else
call thistype.missileTerminateP(this)
endif
endif
// Runs on terrian collision.
static if thistype.onTerrain.exists then
if this.allocated and 0. > this.z and thistype.onTerrain(this) then
call missileTerminateP(this)
endif
endif
// Runs every Missile_TIMER_TIMEOUT.
static if thistype.onPeriod.exists then
if this.allocated and thistype.onPeriod(this) then
call missileTerminateP(this)
endif
endif
endif
set this = node
endloop
set u = null
static if DEBUG_MODE and LIBRARY_ErrorMessage then
return true
else
return false
endif
endmethod
endmodule
module MissileStruct
implement MissileLaunch
implement MissileTerminate
implement MissileAction
private static method onInit takes nothing returns nothing
call MissileCreateCollection(thistype.typeid)
call MissileCreateExpression(thistype.typeid, function thistype.missileIterateP)
endmethod
endmodule
// Missile position:
// =================
//
// Simple trigonometry.
//! textmacro WRITE_MISSILE_POSITION_CODE
struct MissilePosition extends array
private static integer array recycler
private static integer alloc = 0
// Readonly members you can access.
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 - Missile_GetLocZ(b.x -.01, b.y) + Missile_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 + Missile_GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static method create takes real x, real y, real z returns MissilePosition
local thistype this = recycler[0]
if 0 == this then
set alloc = alloc + 1
set this = alloc
else
set recycler[0] = recycler[this]
endif
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
//! endtextmacro
// Delayed dummy recycling:
// ========================
//
// Ensures proper fx death animations.
//! textmacro WRITE_MISSILE_RECYCLE_BIN takes DO_THIS, AFTER_TIME
static if $DO_THIS$ then
private struct RecycleBin extends array
private static constant timer t = CreateTimer()
private static integer max = 0
private static unit array dummy
private static real array time
private static method onPeriodic takes nothing returns nothing
local integer dex = 0
loop
exitwhen dex == thistype.max
set thistype.time[dex] = thistype.time[dex] - 1
if 0 >= thistype.time[dex] then
static if LIBRARY_MissileRecycler then
call RecycleMissile(thistype.dummy[dex])
elseif Dummy.create.exists and LIBRARY_Dummy then
call Dummy[thistype.dummy[dex]].destroy()
elseif LIBRARY_xedummy and xedummy.release.exists then
call xedummy.release(thistype.dummy[dex])
else
call RemoveUnit(thistype.dummy[dex])
endif
set thistype.dummy[dex] = null
set thistype.max = thistype.max - 1
set thistype.dummy[dex] = thistype.dummy[thistype.max]
set thistype.time[dex] = thistype.time[thistype.max]
set thistype.dummy[thistype.max] = null
set dex = dex - 1
if 0 == thistype.max then
call PauseTimer(thistype.t)
endif
endif
set dex = dex + 1
endloop
endmethod
static method recycle takes unit toRecycle returns nothing
if 0 == thistype.max then
call TimerStart(thistype.t, 1., true, function thistype.onPeriodic)
endif
set thistype.dummy[max] = toRecycle
set thistype.time[max] = $AFTER_TIME$ + TimerGetRemaining(thistype.t)
set thistype.max = thistype.max + 1
endmethod
endstruct
endif
//! endtextmacro
// The end!
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 RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/* This is a detailed instruction how to import Frozen Orb*/
------------------------------------------------------------
1. Import the "Frozen Orb" script and "library Missile" into your map.
2. Missile uses a unit called "particle" and a imported modelfile called war3mapImported\dummy.mdx. Copy the unit and the modelfile into your map
Inside the Missile library you have to set "public constant DUMMY_UNIT_ID" to the correct raw of particle in your map.
3. Follow the instructions inside the Frozen Orb scope for proper and optimal configuration and spell usage.
4. Hint: Read Missile API carefully to make perfect use of projectile behaviour in your map. ( i.e. destructable collision, ... )
/*End of Instruction*/
A. How do I get raw codes ?
B. I want to apply something else than slow effect on hit, is that possible?
A: Go to the object editor, Press CTRL + D. The raw code is written before the unit,abilty, buff, name. e.g. for Particle it is 'h000'.
B: Ofc e.g. you want to apply purge. Find purge in the object editor, set cooldown to 0, mana costs to 0 , range to 99999. Look up the OrderId of purge: 852111
Find all order ids in this demo map in scope OrderIds. They were published by Nestharus and can also be found in his github account.
Set everything correctly in the Frozen Orb scope
private constant integer BUFF_ABILITY = 'enter purge raw code here'
private constant integer BUFF_ORDER_ID = enter purge OrderId here
//TESH.scrollpos=7
//TESH.alwaysfold=0
native UnitAlive takes unit id returns boolean
scope FrozenOrb initializer Init
//=========================================================================
// Name: Frozen Orb; ©Blizzard Entertainment, Diablo II
// Version: 2.1
// Author: BPower
// Requirements: Missile, SpellEffectEvent
// Credits: Bribe for SpellEffectEvent
//=========================================================================
// Description:
// A pulsating orb that shreds an area with Ice Bolts.
// Each Bolt deals damage and has chance to burst dying enemies,
// leaving no corpse behind.
//=========================================================================
// User settings:
// ==============
globals
private constant integer FROZEN_ORB_ABILITY = 'A001'
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
private constant real MISSILE_FLY_HEIGHT = 50.00// Shared over all missiles of FrozenOrb.
private constant string ON_BURST_SFX = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
endglobals
// Set the maximum fly distance for the main orb.
private constant function GetOrbMaxDistance takes integer level returns real
return 800. + level*0.
endfunction
// Set the maximum fly distance for the ice bolts.
private constant function GetIceBoltMaxDistance takes integer level returns real
return 400. + level*0.
endfunction
// Set the amount of ice bolts created when the main orb explodes.
private constant function GetIceBoltCountOnExplode takes integer level returns integer
return 12
endfunction
// You have full access granted to all members and methods from library Missile.
// However missile.data, missile.source and missile.owner are reserved.
private function CustomizeFrozenOrb takes Missile orb, integer level returns nothing
set orb.speed = 13.
set orb.model = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
set orb.scale = .75
endfunction
// Same as CustomizeFrozenOrb, but for ice bolts.
private function CustomizeIceBolt takes Missile bolt, integer level returns nothing
set bolt.speed = 20.
set bolt.model = "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"
set bolt.scale = .65
set bolt.damage = 10. + 20.*level
set bolt.collision = 48.
endfunction
// Filter which units are hit by ice bolts.
private function TargetFilter takes unit target, player owner returns boolean
return not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(target, UNIT_TYPE_STRUCTURE) and UnitAlive(target) and IsUnitEnemy(target, owner)
endfunction
// Set the chance to burst an enemy to ice. Real comparison in range of 0 - 100.
// Return smaller or equal to 0 if you don't need this feature.
private constant function GetBurstChance takes integer level returns real
return 5. + 5.*level
endfunction
// Filter out all units which shouldn't be removed from the game on burst. Heroes, .. etc
// Those units who pass the filter get removed from the game and are inevitably gone.
private function ExplodeFilter takes unit target returns boolean
return not IsUnitType(target, UNIT_TYPE_MECHANICAL) and not IsUnitType(target, UNIT_TYPE_HERO)
endfunction
// The following struct "Buff" creates the buff- and frostnova effect of Frozen Orb.
// Adjust the constants to your needs.
private struct Buff extends array
// Buff type settings.
private static constant integer CASTER_UNIT_ID = 'n000'
private static constant integer BUFF_ORDER_ID = 852226// OrderId("frostnova")
private static constant integer BUFF_ABILITY = 'A002'
private static unit caster = null
static method castTarget takes player owner, unit target returns nothing
call SetUnitOwner(caster, owner, false)
call IssueTargetOrderById(caster, BUFF_ORDER_ID, target)
call SetUnitOwner(caster, Missile_NEUTRAL_PASSIVE, false)
endmethod
private static method onInit takes nothing returns nothing
set caster = CreateUnit(Missile_NEUTRAL_PASSIVE, CASTER_UNIT_ID, 0., 0., 0.)
call SetUnitPosition(caster, 2147483647, 2147483647)
call UnitAddAbility(caster, BUFF_ABILITY)
call ShowUnit(caster, false)
endmethod
endstruct
//=====================================================================
// Frozen orb source code. Make changes carefully.
//=====================================================================
// You can extend struct IceBolt and / or struct FrozenOrb with
// available Missile API, such as onDestructable, onTerrain ...
private struct IceBolt extends array
static constant real OFFSET = 32.// That ice bolts don't emit
// directly from the center of the orb.
real chance
private static method onRemove takes Missile missile returns boolean
return true
endmethod
// Runs when a missile collides with a unit.
private static method onCollide takes Missile missile, unit hit returns boolean
local thistype this = thistype(missile)
if TargetFilter(hit, missile.owner) then
call Buff.castTarget(missile.owner, hit)
call UnitDamageTarget(missile.source, hit, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
if GetUnitTypeId(hit) != 0 and GetRandomReal(0., 100.) < chance and ExplodeFilter(hit) then
call DestroyEffect(AddSpecialEffect(ON_BURST_SFX, missile.x, missile.y))
call RemoveUnit(hit)
endif
return true// returning true will destroy the missile instance.
endif
return false// returning false will keep the missile flying.
endmethod
implement MissileStruct
endstruct
private function CreateIceBolt takes Missile orb, real angle, integer level returns nothing
local real x = orb.x + IceBolt.OFFSET*Cos(angle)
local real y = orb.y + IceBolt.OFFSET*Sin(angle)
local Missile missile = Missile.create(x, y, MISSILE_FLY_HEIGHT, angle, GetIceBoltMaxDistance(level), MISSILE_FLY_HEIGHT)
set IceBolt(missile).chance = GetBurstChance(level)
call CustomizeIceBolt(missile, level)
set missile.source = orb.source
set missile.owner = orb.owner
call IceBolt.launch(missile)
endfunction
private struct FrozenOrb extends array
static constant real SPACING_ON_PERIOD = bj_PI*3/5
static constant real SPACING_ON_REMOVE = bj_PI/4
integer level
// When the main orb gets deallocated.
static method onRemove takes Missile missile returns boolean
local thistype this = thistype(missile)
local integer index = 0
local integer count = GetIceBoltCountOnExplode(level)
local real space = 2*bj_PI/count
loop
exitwhen index >= count
call CreateIceBolt(missile, index*space + thistype.SPACING_ON_REMOVE, level)
set index = index + 1
endloop
return true
endmethod
// Every Missile_TIMER_TIMEOUT interval.
static method onPeriod takes Missile missile returns boolean
local thistype this = thistype(missile)
call CreateIceBolt(missile, thistype.SPACING_ON_PERIOD*missile.data, level)
set missile.data = missile.data + 1
return false
endmethod
implement MissileStruct
endstruct
// EVENT_PLAYER_UNIT_SPELL_EFFECT.
private function OnEffect takes nothing returns nothing
local unit source = GetTriggerUnit()
local real x = GetUnitX(source)
local real y = GetUnitY(source)
local real angle = Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
local integer level = GetUnitAbilityLevel(source, FROZEN_ORB_ABILITY)
local Missile missile = Missile.create(x, y, MISSILE_FLY_HEIGHT, angle, GetOrbMaxDistance(level), MISSILE_FLY_HEIGHT)
set FrozenOrb(missile).level = level
call CustomizeFrozenOrb(missile, level)
set missile.source = source
set missile.owner = GetTriggerPlayer()
call FrozenOrb.launch(missile)
set source = null
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectEvent(FROZEN_ORB_ABILITY, function OnEffect)
endfunction
endscope
//TESH.scrollpos=4
//TESH.alwaysfold=0
scope Show
globals
private constant integer MAX = 10
private unit u
private constant integer W = 200
endglobals
private module I
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerEvent(t, Player(0),EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t, Condition(function thistype.revive))
call init()
call PanCameraTo(0,0)
endmethod
endmodule
private struct X extends array
real x
real y
integer i
integer t
player p
private static method init takes nothing returns nothing
local integer i = 0
set u = CreateUnit(Player(0), 'Ulic', 0,0,0)
call SetHeroLevel(u,10,false)
call CreateFogModifierRectBJ( true, Player(0), FOG_OF_WAR_VISIBLE, WorldBounds.world)
loop
exitwhen i == MAX
call CreateUnit(Player(1),'hfoo',1000,1000,0)
set i = i + 1
endloop
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,20, "Press ESC to spawn footmans")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,20, "---------------------------")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,20, "-----------HINT------------")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,20, "Make use of Missile API to extend")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0,0,20, "Frozen Orb projectile behaviour!!")
endmethod
private static method revive takes nothing returns boolean
local integer i = 0
loop
exitwhen i == MAX
call CreateUnit(Player(1),'hfoo',1000,1000,0)
set i = i + 1
endloop
return false
endmethod
implement I
endstruct
endscope