//TESH.scrollpos=0
//TESH.alwaysfold=0
globals
/****************************************************
* MISC FOR TESTING, see Load Hero and Initialization
*****************************************************/
constant boolean ENABLE_TEST = false //false
constant boolean ENABLE_BLINK = true //ENABLE_TEST should be TRUE
constant boolean ENABLE_LIFE = false //ENABLE_TEST should be TRUE
constant integer HERO_LEVEL = 15
/****************************************************
* CONSTANTS
*****************************************************/
BaseSpawn den1
BoardEx mb
constant string DA_Version = "Dungeon Assault v2.8"
constant string RAISE_SFX = "Abilities\\Spells\\Undead\\RaiseSkeletonWarrior\\RaiseSkeleton.mdl"
constant string EXPLODE_SFX = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
constant string GEISH_SFX = "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl"
constant string DUST = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
constant string GATE_KEY_MSG = "A gate KEY is required."
constant real RESPAWN_TIME = 10 //180, only applies for Player(8), Undead Dwellers
constant real DEGTORAD = 0.017453333
constant real RADTODEG = 57.29564
constant real ENEMY_ADD_HP = 250 //250, normal, non-hero
constant real ENEMY_ADD_HP_MULTIPLIER = 3 //normal, non-hero
constant real BOSS_ADD_HP = 5000//5000, hard only, settings in Dialog code
constant integer HERO_MAX_LEVEL = 25
constant integer DUMMY_CAST = 'e002' //dummy caster caster for all
constant integer SECRET_ITEM = 'secr'
constant integer GATE_KEY = 'I000'
constant player MINIONS = Player(7) //Player 8 in GUI
constant player UNDEAD_DWELLER = Player(8) //player 9 in GUI
constant player ENEMY = Player(10) //player 11 in GUI, Valker's Minions
constant attacktype ATK = ATTACK_TYPE_CHAOS
constant damagetype DMG = DAMAGE_TYPE_DEATH
group SkelGrp = CreateGroup()
group enumG = CreateGroup() //used in all loopings alternate to bj_lastCreatedGroup
/****************************************************
* FOR MULTIBOARD
*****************************************************/
integer TotalSecretLocations = 0
integer TotalChests = 0
constant integer TOTAL_MINI_BOSS = 5
constant integer TOTAL_BOSS = 11
/****************************************************
* NON-CONSTANTS
*****************************************************/
integer SkeletonTrapCount = 0
integer HeroStopLevel = 5 //5, hero stops at every HERO_STOP_LEVEL_INTERVAL, from SimpleCodes library
boolean GargoyleEntrance = true //true, this is to check if all gargoyles are dead
integer array itemType
integer array secretLocations
/****************************************************
* ORDER IDs
*****************************************************/
constant integer OID_carrionswarm = 852218
constant integer OID_chainlightning = 852119
constant integer OID_cripple = 852189
constant integer OID_firebolt = 852231
constant integer OID_fanofknives = 852526
constant integer OID_flamestrike = 852488
constant integer OID_frostnova = 852226
constant integer OID_healingspray = 852664
constant integer OID_howlofterror = 852588
constant integer OID_mirrorimage = 852123
constant integer OID_rainoffire = 852238
constant integer OID_thunderbolt = 852095
constant integer OID_thunderclap = 852096
constant integer OID_whirlwind = 852128
/****************************************************
* SPECIAL EVENT ONLY
*****************************************************/
constant real SPECIAL_EVENT_DURATION = 600 //600, 10 minutes, duration of the SpecialEvent
constant integer SKELETON_LIMIT = 15
constant integer ITEM_SDROP = 30 //percentage of item drops
endglobals
//called from Load Hero trigger if ENABLE_TEST is true
function TestModeEnabled takes unit u returns nothing
if ENABLE_BLINK then
call UnitAddAbility(u, 'A02F')
endif
call SetPlayerStateBJ( Player(0), PLAYER_STATE_RESOURCE_GOLD, 10000 )
call SetPlayerStateBJ( Player(0), PLAYER_STATE_RESOURCE_LUMBER, 10000 )
call SetHeroLevel(u, HERO_LEVEL, false)
if ENABLE_LIFE then
call SetUnitMaxState(u, UNIT_STATE_MAX_LIFE, GetWidgetLife(u) + 10000)
endif
endfunction
//used only by udg_HERO
function AttackToPoint takes unit u returns nothing
call IssuePointOrder(u, "attack", GetUnitX(udg_HERO), GetUnitY(udg_HERO))
endfunction
//if HasKey(GetTriggerUnit()) then
function HasKey takes unit u returns boolean
local integer i = 0
local item it
loop
return GetItemTypeId(UnitItemInSlot(u, i))==GATE_KEY
set i = i+1
exitwhen i==6
endloop
return false
endfunction
//this function only calls when a boss unit dies
function CreateGateKey takes nothing returns nothing
set udg_GateKey = CreateItem(GATE_KEY, GetUnitX(udg_HERO), GetUnitY(udg_HERO))
call SetItemInvulnerable(udg_GateKey, true)
endfunction
function SetupThings takes nothing returns nothing
//===Global Location
set udg_TempLocation = Location(0, 0)
//===Special Event Items ONLY
set udg_RandomItem[1] = 'rhe1' //healing
set udg_RandomItem[2] = 'rman' //mana
set udg_RandomItem[3] = 'rsps' //shield
set udg_RandomItem[4] = 'rspd' //speed
set udg_RandomItem[5] = 'rres' //restoration
//===Items for Chest Open ONLY
//MINOR
set itemType[1] = 'phea' //portion fo health
set itemType[2] = 'pman' //portion of mana
set itemType[3] = 'sreg' //scroll of regeneration
set itemType[4] = 'wlsd' //wand of lightning
set itemType[5] = 'pinv' //invisibility
set itemType[6] = 'pnvl' //invulnerability
set itemType[7] = 'wcyc' //wand of the wind
//MAJOR
set itemType[8] = 'tdex' //tome of agility
set itemType[9] = 'tint' //tome of intelligence
set itemType[10] = 'tstr' //tome of strengh
set itemType[11] = 'rst1' //strength +3
set itemType[12] = 'rin1' //intelligence +3
set itemType[13] = 'rag1' //agility +3
set itemType[14] = 'pgma' //greater mana
set itemType[15] = 'pghe' //greater healing
//===Items for Secrets ONLY
set itemType[16] = 'rat6' //attack +6
set itemType[17] = 'belv' //agility +6
set itemType[18] = 'bgst' //strength +6
set itemType[19] = 'ciri' //intelligence +6
set itemType[20] = 'rde3' //armour +4
set itemType[21] = 'rma2' //rune of greater mana
set itemType[22] = 'rhe3' //rune of greater healing
set itemType[23] = 'rat9' //attack +9
set itemType[24] = 'tdx2' //tome of agility +2
set itemType[25] = 'tin2' //tome of intelligence +2
set itemType[26] = 'tst2' //tome of strenght +2
set itemType[27] = 'texp' //tome of experience
set itemType[28] = 'tpow' //agil, str, int +1
set itemType[29] = 'manh' //permanent health +50
set itemType[30] = 'mcou' //agil, str, int +4
//secret location items (question marks)
set secretLocations[0] = 123
endfunction
function ItemAbilityLimit takes unit u returns nothing
local IFS itemLimit
set itemLimit = itemLimit.create(u)
call itemLimit.addItemLimit('I005', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I00B', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I00C', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I007', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I003', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I00A', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I009', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I008', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I004', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I006', 1, "You may carry only 1 ability item per type.")
call itemLimit.addItemLimit('I002', 1, "You may carry only 1 ability item per type.")
endfunction
/*************************************************************
Skeleton Trap kills
**************************************************************/
function SkeletonTrapKills takes nothing returns nothing
if IsUnitInGroup(GetTriggerUnit(), SkelGrp) then
set SkeletonTrapCount = SkeletonTrapCount -1
endif
if SkeletonTrapCount==0 then
call DestroyTrigger(GetTriggeringTrigger())
call DestroyGroup(SkelGrp)
//call BJDebugMsg("Skeleton Trap cleared.")
endif
endfunction
function SkeletonTrapSetup takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(t, function SkeletonTrapKills)
set t = null
endfunction
/*************************************************************
RespawnSetup, this uses RespawnSyatem, respawns only banshees and ghosts
**************************************************************/
function RespawnSetup takes nothing returns nothing
local unit first
//local integer countTest = 0
call GroupEnumUnitsOfPlayer(enumG, UNDEAD_DWELLER, null)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
//set countTest = countTest + 1
//call BJDebugMsg("From Map Header Code")
//call BJDebugMsg(GetUnitName(first)+"=="+I2S(countTest))
if UnitAlive(first) and not IsUnitType(first, UNIT_TYPE_STRUCTURE) and not IsUnitType(first, UNIT_TYPE_HERO) /*
*/ and GetUnitTypeId(first)=='uban' or GetUnitTypeId(first)=='ngh1' then //banshee and ghost
call RespawnSystem.registerEx(first, RESPAWN_TIME, "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl", UNDEAD_DWELLER)
endif
call GroupRemoveUnit(enumG, first)
endloop
endfunction
Name | Type | is_array | initial_value |
AAngle | real | Yes | |
ACaster | unit | Yes | |
ACasterPoint | location | Yes | |
ACount | integer | No | |
ADamage | real | Yes | |
ADistance | real | Yes | |
AHas | boolean | Yes | |
AIndex | integer | No | |
AInteger | integervar | No | |
ALastRecycled | integer | No | |
AMax | integer | No | |
AMaxDistance | real | Yes | |
AMove | real | Yes | |
Angle | real | Yes | |
AoE | integer | Yes | |
ARecycledList | integer | Yes | |
ATarget | unit | Yes | |
BB__Acceleration | real | Yes | |
BB__Angle | real | Yes | |
BB__BoundMaxX | real | No | |
BB__BoundMaxY | real | No | |
BB__BoundMinX | real | No | |
BB__BoundMinY | real | No | |
BB__BrakePoint | real | Yes | |
BB__Caster | unit | Yes | |
BB__Count | integer | Yes | |
BB__Damage | real | Yes | |
BB__Dc | real | Yes | |
BB__Deacceleration | real | Yes | |
BB__Duration | real | Yes | |
BB__Dx | real | Yes | |
BB__Group | group | No | |
BB__Missile | unit | Yes | |
BB__Player | player | Yes | |
BB__Sfx | effect | Yes | |
BB__Speed | real | Yes | |
BB__SpeedMax | real | Yes | |
BB__SpeedMin | real | Yes | |
BB__State | boolean | Yes | |
BB__Target | unit | Yes | |
BB__TAU | real | No | |
BB__Timer | timer | No | |
BB__Total | integer | No | |
BB__TurnRateMax | real | Yes | |
BB__TurnRateMin | real | Yes | |
BE_angle | real | Yes | |
BE_array | integer | No | |
BE_array2 | integervar | No | |
BE_caster | unit | Yes | |
BE_distance | real | Yes | |
BE_dummy | unit | Yes | |
BE_group | group | Yes | |
BE_integer | integer | No | |
BE_MAX | integer | Yes | |
BE_move | location | Yes | |
BE_point1 | location | Yes | |
BE_speed | integer | Yes | |
BE_target | location | Yes | |
BelBaseHeight | real | No | |
BelBaseMoveOut | real | No | |
BelBool1 | boolean | No | |
BelBool2 | boolean | No | |
BelBool4 | boolean | No | |
BelCast | unit | No | |
BelConstant1 | real | No | |
BelConstant10 | real | No | |
BelConstant11 | real | No | |
BelConstant12 | real | No | |
BelConstant13 | real | No | |
BelConstant14 | real | No | |
BelConstant15 | real | No | |
BelConstant2 | real | No | |
BelConstant3 | real | No | |
BelConstant4 | real | No | |
BelConstant5 | real | No | |
BelConstant6 | real | No | |
BelConstant7 | real | No | |
BelConstant8 | real | No | |
BelConstant9 | real | No | |
BelDam | real | No | |
BelDummy | unit | No | |
BelGroup | group | No | |
BelHash | hashtable | No | |
BelLev | integer | No | |
BelLoop | integervar | No | |
BelReal | real | No | |
BelReal2 | real | No | |
BelRealA | real | No | |
BelRealB | real | No | |
BelRealC | real | No | |
BelRealD | real | No | |
BelRealE | real | No | |
BelSFXA | string | No | |
BelTrigger | integer | No | |
BoltsLanding | group | No | |
BoltsRising | group | No | |
BossAldil | unit | No | |
BossAldilCheck | boolean | No | |
BossAnubis | unit | No | |
BossDeath | unit | No | |
BossDeathCheck | boolean | No | |
BossDeathGroup | group | No | |
BossGrinMaulbrick | unit | No | |
BossLava | unit | No | |
BossMira | unit | No | |
BossMiraCheck | boolean | No | |
BossNaze | unit | No | |
BossNazeCheck | boolean | No | |
BossNazeGroup | group | No | |
BossSakagi | unit | No | |
BossScilla | unit | No | |
BossTentacles | unit | No | |
BossTest | unit | No | |
BossValker | unit | No | |
BossValkerCheck | boolean | No | |
bounces | integer | Yes | |
BP | location | No | |
BP2 | location | No | |
CAcceleration | real | Yes | |
CAccelerationRate | real | No | |
CameraAngle | real | No | |
CameraAngleOfAttack | real | No | |
CameraDistance | real | No | |
CameraDistanceOn | boolean | No | |
CAngle | real | Yes | |
CAnimSpeed | real | Yes | |
CArray | integer | No | |
Caster | unit | Yes | |
CCaster | unit | Yes | |
CCasterCheck | unit | No | |
CCasterPos | location | No | |
CCasterPosCheck | location | No | |
CDamage | real | Yes | |
CDistance | real | Yes | |
CEndOfCharge | boolean | Yes | |
ChaoticSphere_AbilityID | abilcode | No | |
ChaoticSphere_Missile1 | unitcode | No | |
ChaoticSphere_Missile2 | unitcode | No | |
CheckGateKey | boolean | No | true |
CheckMelithOwnership | boolean | No | |
CleanedItem | item | Yes | |
CLevel | integer | Yes | |
CLoop | integervar | No | |
CMovePoint | location | No | |
CPlayaGroup | force | Yes | |
CPlayaGroupCheck | force | No | |
CTarget | unit | Yes | |
CTargetCheck | unit | No | |
CTargetPos | location | No | |
CTargetPosCheck | location | No | |
Cur_D | real | Yes | |
CWait | real | Yes | |
damage | integer | Yes | |
damage1 | integer | Yes | |
DialogStat | dialog | No | |
Difficulty | integer | No | |
DSB_Ability_ID | abilcode | No | |
DSB_Ability_Level | integer | Yes | |
DSB_Angle | real | Yes | |
DSB_Animation_Speed | real | No | |
DSB_AoE | real | No | |
DSB_Caster | unit | Yes | |
DSB_Conditions | boolean | Yes | |
DSB_Current_Index | integervar | No | |
DSB_Damage | real | Yes | |
DSB_Damage_Group | group | No | |
DSB_Destroy_Tree | boolean | No | |
DSB_Destroyer | unit | No | |
DSB_Distance | real | No | |
DSB_Dummy | unit | Yes | |
DSB_Dummy2 | unit | Yes | |
DSB_Dummy2_Animation | string | No | |
DSB_Dummy2_BLUE | real | No | |
DSB_Dummy2_GREEN | real | No | |
DSB_Dummy2_Lifetime | real | No | |
DSB_Dummy2_RED | real | No | |
DSB_Dummy2_Transparency | real | No | |
DSB_Dummy_Type | unitcode | No | |
DSB_Dummy_Type2 | unitcode | No | |
DSB_Max_Index | integer | No | |
DSB_Owner | player | Yes | |
DSB_Shadow | boolean | No | |
DSB_Shdow | boolean | Yes | |
DSB_SoundEffect | sound | No | |
DSB_Special_Effect | string | No | |
DSB_Special_Effect_Location | string | No | |
DSB_Stage | integer | Yes | |
DSB_Start_Range | real | No | |
DSB_UnDamage_Group | group | Yes | |
Dummy | unit | Yes | |
Dummy2 | unit | Yes | |
EM_Angle | real | No | |
EM_CoreAoeBase | real | No | |
EM_CoreAoePerLevel | real | No | |
EM_CoreDistanceBase | real | No | |
EM_CoreDistancePerLevel | real | No | |
EM_Cores | group | No | |
EM_CoreUnit | unitcode | No | |
EM_DurationBase | real | No | |
EM_DurationPerLevel | real | No | |
EM_FlyHeight | real | No | |
EM_Hash | hashtable | No | |
EM_HealthDrainBase | real | No | |
EM_HealthDrainPerLevel | real | No | |
EM_I | integervar | No | |
EM_Lightning | string | No | |
EM_Lightning2 | string | No | |
EM_LightningCounter | integer | No | |
EM_LightningHighHeight | real | No | |
EM_LightningLowHeightCaster | real | No | |
EM_LightningLowHeightTarget | real | No | |
EM_ManaConversionBase | real | No | |
EM_ManaConversionPerLevel | real | No | |
EM_MaxTargetsBase | integer | No | |
EM_MaxTargetsPerLevel | integer | No | |
EM_SFX | string | No | |
EM_SidesBase | integer | No | |
EM_SidesPerLevel | integer | No | |
EM_Spell | abilcode | No | |
EM_SpinSpeed | real | No | |
EM_TempGroup | group | No | |
EM_TempLightning | lightning | No | |
EM_TempPoint | location | No | |
EM_TempPoint2 | location | No | |
EM_TempRealStore | real | No | |
EM_TempRealStore2 | real | No | |
EM_TempU | unit | No | |
EM_TempZ | real | No | |
EM_TempZ2 | real | No | |
EM_U | unit | No | |
EM_UHandle | integer | No | |
EnableHealingPoint | boolean | No | true |
FF_AllowCliffs | boolean | Yes | |
FF_AllowedToKeepMoving | boolean | Yes | |
FF_AllowWater | boolean | Yes | |
FF_Angle | real | Yes | |
FF_AoEDamages | real | Yes | |
FF_BuildingAoE | boolean | Yes | |
FF_Caster | unit | Yes | |
FF_DistanceCast | real | No | |
FF_DistanceReached | real | Yes | |
FF_Done | boolean | Yes | |
FF_DummyId | unitcode | No | |
FF_DummyUnit | unit | Yes | |
FF_ExplosionEffect | string | Yes | |
FF_FinalAoE | real | Yes | |
FF_FireBall1 | unit | Yes | |
FF_FireBall2 | unit | Yes | |
FF_FireBallAdditionnalDistance | real | Yes | |
FF_FireBallAoE | real | Yes | |
FF_FireBallArc | real | Yes | |
FF_FireBallArcSettings | real | Yes | |
FF_FireBallDistance | real | Yes | |
FF_FireBallDistanceInit | real | No | |
FF_FireBallId | unitcode | No | |
FF_FireBallSpeed | real | Yes | |
FF_FireBallTempAdd | real | Yes | |
FF_FullDistance | real | Yes | |
FF_GoThroughBuildings | boolean | Yes | |
FF_GoThroughTrees | boolean | Yes | |
FF_GroupFireBall1 | group | Yes | |
FF_GroupFireBall2 | group | Yes | |
FF_HitBuildings | boolean | Yes | |
FF_Integer1 | integer | No | |
FF_Integer2 | integer | No | |
FF_Integer3 | integer | No | |
FF_KillTrees | boolean | Yes | |
FF_KillTreesAoE | boolean | Yes | |
FF_Level | integer | No | |
FF_MinimalDistance | real | No | |
FF_RealTimer | real | Yes | |
FF_SingleDamages | real | Yes | |
FF_SingleUnitEffect | string | Yes | |
FF_SpellID | abilcode | No | |
FF_TempGroup | group | No | |
FF_TempPoint1 | location | No | |
FF_TempPoint2 | location | No | |
FF_TempPoint3 | location | No | |
first_integer | integer | No | |
GateKey | item | No | |
GeishCheck | boolean | No | |
GeishHeroType | unitcode | No | |
GeishUnit | unit | No | |
Group | group | No | |
GroupTorrent | group | No | |
Handle_Bel | integer | No | |
HASH | hashtable | No | |
Hashtable | hashtable | No | |
HashtableWater | hashtable | No | |
HERO | unit | No | |
HeroGroup | group | No | |
HeroName | string | No | |
HexingBlastSfx | effect | Yes | |
HexingBlastTempU | unit | No | |
Hints | string | Yes | |
HSphereAOE | real | No | |
HSphereDistance | real | No | |
HurlingWaveGroup | group | No | |
ID | integer | Yes | |
ID2 | integer | No | |
IDMax | integer | No | |
ImpactDamage | real | Yes | |
ImpactEffects | string | No | |
ImpactStun | abilcode | No | |
Integer | integer | No | |
Item | integer | No | |
ItemCheckRegular | boolean | No | true |
ItemCleanupFlag | boolean | No | |
ItemCleanupTimer | timer | No | |
ItemsFromEnemies | integer | Yes | |
ItemsToClean | integer | No | |
JD_Angle | real | Yes | |
JD_BouncesCur | integer | Yes | |
JD_BouncesMax | integer | Yes | |
JD_DamageBig | real | Yes | |
JD_DamageSmall | real | Yes | |
JD_Distances | real | Yes | |
JD_Group | group | No | |
JD_HighSettings | real | Yes | |
JD_IntegerAVar | integervar | No | |
JD_Integers | integer | Yes | |
JD_JumpHigh | real | Yes | |
JD_Owner | unit | Yes | |
JD_ReachedDistance | real | Yes | |
JD_RealTimer | real | Yes | |
JD_SmallFalse_BigTrue | boolean | Yes | |
JD_SmallPoint | location | No | |
JD_SpeedUnits | real | Yes | |
JD_TempGroup | group | No | |
JD_TempPoint | location | Yes | |
JD_TreesDestroy | boolean | Yes | |
JD_Unit | unit | Yes | |
JDA_Caster | unit | No | |
JDA_DestroyTrees_Dash | boolean | No | |
JDA_JumpHigh_Distance | real | No | |
JDA_SmallFalse_BigTrue | boolean | No | |
JDA_Speed | real | No | |
JDA_TargetPoint | location | No | |
JDA_Unit | unit | No | |
Level | integer | Yes | |
Loc | location | No | |
Loc1 | location | No | |
Loc2 | location | No | |
Loc3 | location | No | |
Loc4 | location | No | |
Loop | integervar | No | |
Max_D | real | Yes | |
Max_H | real | Yes | |
Max_Index | integer | No | |
MB_Chests | integer | No | |
MB_Secrets | integer | No | |
MD_Caster | unit | No | |
MD_Deg | real | No | |
MD_IntVar | integer | No | |
MD_Point | location | No | |
Melith | unit | No | |
MiniBossAnthis | unit | No | |
MiniBossFrosthand | unit | No | |
MiniBossKrakull | unit | No | |
MiniBossKrakullGrp | group | No | |
MiniBossTerrorblade | unit | No | |
MiniBossTheraphus | unit | No | |
Missile | unit | No | |
MissileGroup1 | group | No | |
MissileGroup2 | group | No | |
MUI_1 | integer | No | |
MUI_2 | integer | Yes | |
MUI_3 | integervar | No | |
NazePortalUnitCount | integer | No | 1 |
NEUTRAL_PLAYER4 | player | No | |
Player09_DungeonDweller | player | No | Player08 |
Player11_ValkersMinions | player | No | Player10 |
Point | location | No | |
Points | location | Yes | |
PortalNaze | unit | No | |
PortalValker | unit | No | |
Random | integer | No | |
RandomItem | integer | Yes | |
Real | real | Yes | |
SD_Caster | unit | No | |
SD_Effect | string | No | |
SD_Integer | integervar | No | |
SD_Offset | location | No | |
SD_Point | location | No | |
SD_Real | real | No | |
SE_Arc | real | No | |
SE_Caster | unit | No | |
SE_CasterLoc | location | No | |
SE_CreatorAngle | real | No | |
SE_CreatorKey | integer | No | |
SE_CurrentHeight | real | No | |
SE_Damage | real | Yes | |
SE_DamageGroup | group | No | |
SE_DestroyOrder | string | No | |
SE_Distance | real | No | |
SE_DummyLocationSys | location | No | |
SE_ExplosionDelay | real | No | |
SE_Fissure | unit | No | |
SE_FissureCreator | unit | No | |
SE_FissureKey | integer | No | |
SE_FissureMaxDistance | real | Yes | |
SE_FissureSpeed | real | Yes | |
SE_Fragment | unit | No | |
SE_FragmentAngle | real | No | |
SE_FragmentKey | integer | No | |
SE_FragmentNumber | integer | Yes | |
SE_FragmentOffset | location | No | |
SE_FragmentRadius | real | No | |
SE_FragmentSpeed | real | Yes | |
SE_GroupNumber | integer | No | |
SE_Hash | hashtable | No | |
SE_Height | real | No | |
SE_Integer | integervar | No | |
SE_Levels | integer | No | |
SE_LoopGroup | group | No | |
SE_Owner | player | No | |
SE_Radius | real | No | |
SE_SFX | string | Yes | |
SE_StunAbility | abilcode | No | |
SE_StunBuff | buffcode | No | |
SE_StunCurrent | real | No | |
SE_StunDuration | real | Yes | |
SE_StunGroup | group | No | |
SE_StunKey | integer | No | |
SE_Stunned | unit | No | |
SE_Stunner | unit | No | |
SE_StunOrder | string | No | |
SE_StunRadius | real | No | |
SE_StunTimer | real | No | |
SE_TargetLoc | location | No | |
SE_Travelled | real | No | |
SE_Tree | destructable | No | |
SE_TreeCutter | unit | No | |
SE_TreeRadiusDestroy | real | No | |
SE_U | unit | No | |
SE_UKey | integer | No | |
SE_ULoc | location | No | |
SF_BouncesNumber | integer | No | |
SF_DamageBigMissiles | real | No | |
SF_DamageSmallMissiles | real | No | |
SF_DestroyTrees | boolean | No | |
SF_NumberOfSmallMissiles | integer | No | |
SF_RangeBetweenBounces | real | No | |
SF_RangeOfSmallMissiles | real | No | |
SF_TempPoint | location | Yes | |
SfxGeish | effect | No | |
SinisterBlast_Caster | unit | No | |
SkeletalMage | unit | Yes | |
SkeletonCount | integer | No | |
SkeletonDen2Empty | boolean | No | |
SkeletonDenGroup | group | No | |
SkeletonSkullFloating | unit | Yes | |
Sloc | location | Yes | |
Speed | real | Yes | |
SphereCaster | unit | No | |
SphereDamage | group | No | |
SphereGroup | group | No | |
SphereInt | integervar | No | |
SphereTable | hashtable | No | |
SphereValue | integer | Yes | |
SReal | real | Yes | |
StalkerCount | integer | No | |
StalkerGroup | group | No | |
SU_AOE | real | No | |
SU_BaseDamageAdd | real | No | |
SU_BaseDamageMultiplier | real | No | |
SU_Caster | unit | Yes | |
SU_CasterAR | unit | Yes | |
SU_DamageAR | real | Yes | |
SU_DamageDelay | real | No | |
SU_DamageDelayAR | real | Yes | |
SU_DummySpellID | abilcode | No | |
SU_Dur | real | Yes | |
SU_DurationAR | real | Yes | |
SU_EnumG | group | No | |
SU_FilteredUnit | unit | No | |
SU_FlareSfx | string | No | |
SU_Index | integer | No | |
SU_IndexAR | integer | Yes | |
SU_IndexLoop | integervar | No | |
SU_IndexMAX | integer | No | |
SU_OwningPlayerAR | player | Yes | |
SU_SpellDuration | real | No | |
SU_SpellID | abilcode | No | |
SU_SunDummyID | unitcode | No | |
SU_SunUnit | unit | Yes | |
SU_SunUnitAR | unit | Yes | |
SU_Target | unit | No | |
SU_TempIndex | integer | No | |
SU_TempLoc | location | No | |
Target | unit | No | |
Temp | integer | No | |
TempAbility | abilcode | No | |
TempCaster | unit | No | |
TempDest | destructable | No | |
TempGeish | unit | No | |
TempGroupA | group | No | |
TempHandleA | integer | No | |
TempHandleB | integer | No | |
TempHandleC | integer | No | |
TempInteger | integer | No | |
TempIntegerA | integer | No | |
TempIntegerB | integer | No | |
TempLoc | location | No | |
TempLoc2 | location | No | |
TempLocation | location | No | |
TempPlayerA | player | No | |
TempPoint | location | No | |
TempPoint2 | location | No | |
TempPoint3 | location | No | |
TempPointA | location | No | |
TempPointB | location | No | |
TempPointC | location | No | |
TempRealA | real | No | |
TempRealB | real | No | |
TempSfx | effect | No | |
TempTargetUnit | unit | No | |
TempUnit | unit | No | |
TempUnitA | unit | No | |
TempUnitB | unit | No | |
TempUnitC | unit | No | |
TunnelLoc | location | No | |
TW_Angle | real | No | |
TW_Caster | unit | No | |
TW_Damage | real | No | |
TW_DamagedUnits | group | Yes | |
TW_Dummy | unit | No | |
TW_Group | group | No | |
TW_Handle | handle | No | |
TW_Hashtable | hashtable | No | |
TW_Int | integer | No | |
TW_MissileSpeed | real | No | |
TW_Point | location | Yes | |
TW_Return | boolean | No | |
TW_SFX | effect | No | |
TW_Spell_Distance | real | No | |
UltimaetDamage | real | No | |
UltimaetLifeGain | real | No | |
UltimaetManaGain | real | No | |
VampireBats | integer | No | |
WaveDamage | real | Yes | |
WaveRange | real | Yes | |
WaveSpeed | real | Yes | |
WaveThrow | real | Yes | |
WebTrap | effect | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
Boss units will be vulnerable when SetUnitEnemyAndAttack is called from CastingConditions
//========================
BUTTONS:
00 01 20 30
01 11 21 31
02 12 22 32
ITEMS USED:
Boots of speed
Cheeze
Ring of protection 2
Warsong Battledrums
Sobi mask
Boosts Intelligence by 1.
Keg of Ale
Soul gem
Druid Pouch
Clockwork Penguin
Serathil
//========================
GeishCheck = false, checks if Kellen is played or not
//TESH.scrollpos=119
//TESH.alwaysfold=0
[Simpletable=Dungeon Assault v2.7 created by mckill2009]
[Simpletable=Introduction][COLOR="Cyan"]
This Map is heavily inspired by [color=blue]Dungeon Siege[/color] and [color=blue]Castlevania: Symphony of the Night[/color] that’s why it has many boss fights and Dracula (in Dread Lord form) inside it.
[/COLOR][/simpletable]
[Simpletable=Gameplay and Features][COLOR="Orange"]
- Single Player RPG with custom made heroes and spells
- NPC AI follower for some heroes
- Choose difficulty
- Kill bosses to acquire keys to open certain gates
- Enemy respawns every 30 seconds
- Epic boss fights with custom spells
- Find secrets and acquire certain items
- Many hidden traps that will deal a huge amount of damage
- Custom made spells
- Buy additional custom powerful spells using souls
- Chest open system
[/COLOR][/simpletable]
[Simpletable=Video]
https://www.youtube.com/watch?v=21eax3Gr6JY&feature=youtu.be
[/Simpletable]
[Simpletable=Screenshots]
[/Simpletable]
[simpletable=Credits]
[hidden=Long Credit list]
[COLOR="Yellow"][SIZE="3"]Hero and Spells:[/SIZE][/COLOR]
Orist, the Hunter, by[self=http://www.hiveworkshop.com/forums/members/defskull/] defskull[/self] except Freezing Point
Faera, the Pyromistress, by [self=http://www.hiveworkshop.com/forums/members/ken-e/]Ken-E[/self]
Yma'dorie, the Water Lecher, by [self=http://www.hiveworkshop.com/forums/members/kyousuke+imadori/]Kyousuke Imadori[/self] and [COLOR="green"]CheckeredFlag[/COLOR]
Skull of Flames by Eccho and Paladon
Fiery Rage by Roflcoptor
Chaotic Sphere by ap0calypse
Split Earth by Almia
Fire Fury by Bluebay
Assault by Marsal
Double Spinning Blade by Rheiko
Energy Matrix by Tank-Commander
Electrifying Smash by The_Witcher
Belhammer by Archangel678
Charge-Slash by CoLd Bon3
Birds of energy by aaron
Freezing Field by Dark_Dragon
Frozen Orb by Eccho
Thunderwrath by Mainy
Hexing Blast by f0rsAk3n
Fierce Armor by moyack
Sinister Blast by G00dG4m3
BouncingGlaives by Geshishouhu
Lightning Speed Laceration by Maker
Ice Shards by Maker
Boomerang! by Deaod
Bone Blade by Dalvengyr
[COLOR="Yellow"][SIZE="3"]Systems:[/SIZE][/COLOR]
Magtheridon96, Bribe, Jesus4Lyf, Vexorian, Rising_Dusk, Nesthaurus, Spinnaker, Titlititi
[COLOR="Yellow"][SIZE="3"]Models:[/SIZE][/COLOR]
HeroDemonHunter by Afronight_76
Dwarf Axe Thrower by Amaruak
Chest04 by Chriz
Door 00 by Tranquil
DwarfDathbringer by Sellenisko
Dwarven warrior by Dwarven warrior
Healing Wind by JetFangInferno
Impaler by jigrael
Key by Mc !
MishaWallDoor by Misha
All-Purpose Gate/Archway\Portal by Vermillion Edict
Poison Mushroom by Dan van Ohllus
Porticulis Gate by Tranquil
The Grim Reaper by Kuhneghetz
Wooden Gate by Fingolfin
Dwarf Axe Thrower by Amaruak
Dwarven warrior by Dwarven warrior
VampireBat by Pyramidhe@d
Wood Elven Druidess by Wood Elven Druidess
Warlock's Dagger by debode
Timer Rune (FrostArmorTarget) by Matilda_Knights
DemonHunterDIII by CloudWolf; Sellenisko [Sellenisko]
Rockcliffs by Sunchips
Sea Amazon.blp By enjoy
Dark Soldier by Freddyk
[COLOR="Yellow"][SIZE="3"]Icons:[/SIZE][/COLOR]
BTNFirewall by Eraser|85
BTNColdGuard by bigapple90
BTNEntangle by -BerZeKeR-
BTNFIREBALL2 by bigapple90
BTNFrostBolt by -BerZeKeR-
BTNNatureHealingRay by NFWar
BTNPD3WizMeditation by Palaslayer
BTNPoisonMushroom by Dan van Ohllus
BTNRestoration by Coinblin
BTNSonicBlast by KelThuzad
BTNwaterelemental by inhuman89
BTNWaterLance by L_Lawliet
BTNEntangle By -BerZeKeR-
BTNHunter by Mr.Goblin
BTNRPGDwarf by Tenebrae
BTNWoodElfDruidess by NatDis
BTNDagger by NFWar
BTNWingedClock by Golden-Drake
BTNVanity Created by L_Lawliet
BTNDschinniSword by Palaslayer
BTNHuntressDefend by GreyArchon
BTNPowerGrip2 by Crowolf
BTNbladedancer by Afronight_76
BTNYellowBottle by Static
Darkfang and PeeKay
BTNPD3WizIncinerate by Palaslayer
BTNFlash By Palaslayer
BTNSeaAmazon By enjoy
BTNChaosWarrior by Sin'dorei300
BTNAngryPanda by BLazeKraze
[COLOR="Yellow"][SIZE="3"]For Suggestions and Bug found:[/SIZE][/COLOR]
Hell_Master, Mgeterno11, funAlways, asam3, Goatmilk, skyline18
[COLOR="Yellow"][SIZE="4"]NOTE:[/SIZE][/COLOR]
If I forgot your name in the Credit list, please inform me, thanks.
[/hidden][/simpletable]
[hidden=Changelogs]
v2.8
- ADDED some normal units have custom spells
- Items dropped are now significant for the Hero's survival
- Heroes now have starting healing and mana items
- FIXED Boss Death doesn't drop a key
- New set of items like healing and mana spawned
v2.7
- NEW hero: [COLOR="Yellow"]Delan Boldclaw, the Pandaren Champion[/COLOR]
- Fixed gate cannnot be opened after boss Naze fight
- Aldil spell is bugged, fixed
- Human form of Geish has been removed
v2.6
- NEW hero: [COLOR="Yellow"]Taranis, the Bounty Hunter[/COLOR]
- NEW 5 Mini Bosses with custom spells
- NEW extended locations
- NEW quests notes
- NEW Geish and Melith have custom abilities
- Camera locked angles and height
- Camera angle can be adjusted
- Bosses spells are more efficient
- Bosses are harder to defeat
- Improved hero spells
- Fixed a shitty bug that you cant open gate after death dies
- Fixed spawning infinite skeletons during death boss
- Fixed chest owner is player 1
- Improved tooltips
v2.5
- Gate keys bugs fixed
- 2 Chest boxes that cannot be clicked is removed
- Fixed, you cant go back to buy items after boss fights
- Fixed, skeleton dens bugs
- Added secret items/locations which I forgot to include in v2.4
- Added camera selection for lock or free to look
- Added respawns for enemy dungeon dwellers every 30 seconds
v2.4
- 2 New heros Jastra, the Dark Elf and Yma'dorie, the Water Lecher
- New Terrain
- Fixed when Faera cast her Scatter Ribbon gates explodes
- Boss fights improved
- Item drop system improved
- Skeleton bug den fixed
- Anubis and Death boss fight separated
- Hero abilities improved
v2.3
- New hero, Faera, the Pyromistress, made by [self=http://www.hiveworkshop.com/forums/members/ken-e/]Ken-E[/self]
- Knight removed from game
- Spirit Hold bug fixed
- Kellen's Fire Trail ability replaced by Spitting Fire
v2.2
- Bag has been removed
- Duration & armour of Orists has been increased
- Fixed keys that dont drop on some bosses, like Mira
- Fixed door that dont open when Lava Lord dies
- Fixed when you entered circle of power
v2.1a
- Fixed lightning cloud timer
v2.1
Gameplay & Triggers:
- New hero, Marflak, the warrior
- 90% of the spells & buying abilities is being converted to JASS/vJASS for better performance
- Sorceress Splash has increased damage and chance.
- Healing Wind changed from target to no target, non-automatic
- Added Libraries or systems for better performance such as ItemCleaup by Bribe, T32x by Jesus4Lyf
- Boss fights, spells and event is much more efficient and challenging
- Clock buying item replaced by Mass Sleep
- Fire Trail replaced by Spitting Fire
- Chest open event changed to left click action
- Multiboard improved
- Cinematics reduced
Terrain:
- Added 2nd floor for the library
v2.0
- NEW Hero selection
- NEW Camera system that can be rotated and zoomed out/in
- Cinematics has been changed
- Deflect for Hunter has been replaced by Freezing Point due to complains
- The Calling and Comet fall increased damage from 100 to 150
- Ice Devastation mana cost reduced from 110 to 90
- Icon Buttons is now in order
- Hero Description fixed
[/hidden][/simpletable]
//TESH.scrollpos=6
//TESH.alwaysfold=0
[Simpletable=Dungeon Assault v2.7 created by mckill2009]
[Simpletable=Introduction]
[COLOR="Cyan"]
This Map is heavily inspired by [color=blue]Dungeon Siege[/color] and [color=blue]Castlevania: Symphony of the Night[/color] that’s why it has many boss fights and Dracula (in Dread Lord form) inside it.
[/COLOR]
[/simpletable]
[Simpletable=Gameplay and Features]
[COLOR="Orange"]
- Single Player RPG with custom made heroes and spells
- NPC AI follower for some heroes
- Choose difficulty
- Kill bosses to acquire keys to open certain gates
- Enemy respawns every 30 seconds
- Epic boss fights with custom spells
- Find secrets and acquire certain items
- Many hidden traps that will deal a huge amount of damage
- Custom made spells
- Buy additional custom powerful spells using souls
- Chest open system
[/COLOR]
[/simpletable]
[Simpletable=Video]
https://www.youtube.com/watch?v=21eax3Gr6JY&feature=youtu.be
[/Simpletable]
[Simpletable=Screenshots]
[/Simpletable]
[simpletable=Credits]
[hidden=Long Credit list]
[COLOR="Yellow"][SIZE="3"]Hero and Spells:[/SIZE][/COLOR]
Orist, the Hunter, by[self=http://www.hiveworkshop.com/forums/members/defskull/] defskull[/self] except Freezing Point
Faera, the Pyromistress, by [self=http://www.hiveworkshop.com/forums/members/ken-e/]Ken-E[/self]
Yma'dorie, the Water Lecher, by [self=http://www.hiveworkshop.com/forums/members/kyousuke+imadori/]Kyousuke Imadori[/self] and [COLOR="green"]CheckeredFlag[/COLOR]
Skull of Flames by Eccho and Paladon
Fiery Rage by Roflcoptor
Chaotic Sphere by ap0calypse
Split Earth by Almia
Fire Fury by Bluebay
Assault by Marsal
Double Spinning Blade by Rheiko
Energy Matrix by Tank-Commander
Electrifying Smash by The_Witcher
Belhammer by Archangel678
Charge-Slash by CoLd Bon3
Birds of energy by aaron
Freezing Field by Dark_Dragon
Frozen Orb by Eccho
Thunderwrath by Mainy
Hexing Blast by f0rsAk3n
Fierce Armor by moyack
Sinister Blast by G00dG4m3
BouncingGlaives by Geshishouhu
Lightning Speed Laceration by Maker
Ice Shards by Maker
Boomerang! by Deaod
[COLOR="Yellow"][SIZE="3"]Systems:[/SIZE][/COLOR]
Magtheridon96, Bribe, Jesus4Lyf, Vexorian, Rising_Dusk, Nesthaurus, Spinnaker
[COLOR="Yellow"][SIZE="3"]Models:[/SIZE][/COLOR]
HeroDemonHunter by Afronight_76
Dwarf Axe Thrower by Amaruak
Chest04 by Chriz
Door 00 by Tranquil
DwarfDathbringer by Sellenisko
Dwarven warrior by Dwarven warrior
Healing Wind by JetFangInferno
Impaler by jigrael
Key by Mc !
MishaWallDoor by Misha
All-Purpose Gate/Archway\Portal by Vermillion Edict
Poison Mushroom by Dan van Ohllus
Porticulis Gate by Tranquil
The Grim Reaper by Kuhneghetz
Wooden Gate by Fingolfin
Dwarf Axe Thrower by Amaruak
Dwarven warrior by Dwarven warrior
VampireBat by Pyramidhe@d
Wood Elven Druidess by Wood Elven Druidess
Warlock's Dagger by debode
Timer Rune (FrostArmorTarget) by Matilda_Knights
DemonHunterDIII by CloudWolf; Sellenisko [Sellenisko]
Rockcliffs by Sunchips
Sea Amazon.blp By enjoy
Dark Soldier by Freddyk
[COLOR="Yellow"][SIZE="3"]Icons:[/SIZE][/COLOR]
BTNFirewall by Eraser|85
BTNColdGuard by bigapple90
BTNEntangle by -BerZeKeR-
BTNFIREBALL2 by bigapple90
BTNFrostBolt by -BerZeKeR-
BTNNatureHealingRay by NFWar
BTNPD3WizMeditation by Palaslayer
BTNPoisonMushroom by Dan van Ohllus
BTNRestoration by Coinblin
BTNSonicBlast by KelThuzad
BTNwaterelemental by inhuman89
BTNWaterLance by L_Lawliet
BTNEntangle By -BerZeKeR-
BTNHunter by Mr.Goblin
BTNRPGDwarf by Tenebrae
BTNWoodElfDruidess by NatDis
BTNDagger by NFWar
BTNWingedClock by Golden-Drake
BTNVanity Created by L_Lawliet
BTNDschinniSword by Palaslayer
BTNHuntressDefend by GreyArchon
BTNPowerGrip2 by Crowolf
BTNbladedancer by Afronight_76
BTNYellowBottle by Static
Darkfang and PeeKay
BTNPD3WizIncinerate by Palaslayer
BTNFlash By Palaslayer
BTNSeaAmazon By enjoy
BTNChaosWarrior by Sin'dorei300
BTNAngryPanda by BLazeKraze
[COLOR="Yellow"][SIZE="3"]For Suggestions and Bug found:[/SIZE][/COLOR]
Hell_Master, Mgeterno11, funAlways, asam3, Goatmilk, skyline18
[COLOR="Yellow"][SIZE="4"]NOTE:[/SIZE][/COLOR]
If I forgot your name in the Credit list, please inform me, thanks.
[/hidden]
[/simpletable]
[hidden=Changelogs]
v2.7
- NEW hero: [COLOR="Yellow"]Delan Boldclaw, the Pandaren Champion[/COLOR]
- Fixed gate cannnot be opened after boss Naze fight
- Aldil spell is bugged, fixed
- Human form of Geish has been removed
v2.6
- NEW hero: [COLOR="Yellow"]Taranis, the Bounty Hunter[/COLOR]
- NEW 5 Mini Bosses with custom spells
- NEW extended locations
- NEW quests notes
- NEW Geish and Melith have custom abilities
- Camera locked angles and height
- Camera angle can be adjusted
- Bosses spells are more efficient
- Bosses are harder to defeat
- Improved hero spells
- Fixed a shitty bug that you cant open gate after death dies
- Fixed spawning infinite skeletons during death boss
- Fixed chest owner is player 1
- Improved tooltips
v2.5
- Gate keys bugs fixed
- 2 Chest boxes that cannot be clicked is removed
- Fixed, you cant go back to buy items after boss fights
- Fixed, skeleton dens bugs
- Added secret items/locations which I forgot to include in v2.4
- Added camera selection for lock or free to look
- Added respawns for enemy dungeon dwellers every 30 seconds
v2.4
- 2 New heros Jastra, the Dark Elf and Yma'dorie, the Water Lecher
- New Terrain
- Fixed when Faera cast her Scatter Ribbon gates explodes
- Boss fights improved
- Item drop system improved
- Skeleton bug den fixed
- Anubis and Death boss fight separated
- Hero abilities improved
v2.3
- New hero, Faera, the Pyromistress, made by [self=http://www.hiveworkshop.com/forums/members/ken-e/]Ken-E[/self]
- Knight removed from game
- Spirit Hold bug fixed
- Kellen's Fire Trail ability replaced by Spitting Fire
v2.2
- Bag has been removed
- Duration & armour of Orists has been increased
- Fixed keys that dont drop on some bosses, like Mira
- Fixed door that dont open when Lava Lord dies
- Fixed when you entered circle of power
v2.1a
- Fixed lightning cloud timer
v2.1
Gameplay & Triggers:
- New hero, Marflak, the warrior
- 90% of the spells & buying abilities is being converted to JASS/vJASS for better performance
- Sorceress Splash has increased damage and chance.
- Healing Wind changed from target to no target, non-automatic
- Added Libraries or systems for better performance such as ItemCleaup by Bribe, T32x by Jesus4Lyf
- Boss fights, spells and event is much more efficient and challenging
- Clock buying item replaced by Mass Sleep
- Fire Trail replaced by Spitting Fire
- Chest open event changed to left click action
- Multiboard improved
- Cinematics reduced
Terrain:
- Added 2nd floor for the library
v2.0
- NEW Hero selection
- NEW Camera system that can be rotated and zoomed out/in
- Cinematics has been changed
- Deflect for Hunter has been replaced by Freezing Point due to complains
- The Calling and Comet fall increased damage from 100 to 150
- Ice Devastation mana cost reduced from 110 to 90
- Icon Buttons is now in order
- Hero Description fixed
[/hidden]
[/simpletable]
//TESH.scrollpos=0
//TESH.alwaysfold=0
|cffB93B8FCast Type:|r No Target, Summons Fireballs
|cff4E8975Mana Cost:|r 120
|cff00FFFFCooldown:|r 20 seconds
|cff660033Fireball Count:|r 6/8/10/12/14
|cffFFFFCCFirebolt Damage:|r 30/40/50/60/70
|cff33CC33Search For Enemy Range:|r 400/500/600/700/800
|cff0099CCFireball Cast Range:|r 400/550/700/850/900
|cff669900Fireball Cast Interval:|r Every 1 second
|cff669999Fireball Life:|r 10/15/20/25/30 seconds
PASSIVE
Gives a chance that the attacker of this unit can get a splash damage and stunning it for 2 seconds.
|cffB93B8FCast Type:|r Passive
|cff660033Chance:|r 15/20/25/30/35%
|cffFFFFCCSplash Damage:|r 40/70/100/130/160
//TESH.scrollpos=0
//TESH.alwaysfold=0
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TEST uses T32, SpellEffectEvent
globals
private constant integer SPELL_ID = 1234
endglobals
private struct NAME
private unit caster
private method periodic takes nothing returns nothing
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=30
//TESH.alwaysfold=0
library LightningStrike uses SpellEffectEvent, RandomUnit
globals
private constant integer SPELL_ID = 'ald1' //flame strike
private constant integer DUMMY_SPELL_ID = 'A01G'
private constant integer DUMMY_SPELL_ORDER_ID = 852587
private constant integer DUMMY_ID = 'h00K'
private constant string SFX = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
private unit tempUnit = null
endglobals
private function GetDummyCount takes integer level returns integer
return 2 * level
endfunction
private function FilterTargets takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(tempUnit))
endfunction
private struct LightningStrike
private static method onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit target
local unit dummy
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
local real randomAngle
local real randomOffset
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local real x1
local real y1
local integer i = 0
local integer dummyCount = GetDummyCount(level)
set tempUnit = u
loop
set i = i+1
set target = GetRandomUnitInArea(x, y, 600, Filter(function FilterTargets))
if target!=null then
set randomAngle = GetRandomReal(0,6.2)
set randomOffset = GetRandomReal(150, 300)
set x1 = GetUnitX(target) + randomOffset * Cos(randomAngle)
set y1 = GetUnitY(target) + randomOffset * Sin(randomAngle)
set dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, x1, y1, 0)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, level)
call DestroyEffect(AddSpecialEffect(SFX, x, y))
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_ID, target)
endif
exitwhen i==dummyCount
endloop
set target = null
set u = null
set dummy = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
//call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, function thistype.shadeDeath)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Snarl initializer init uses DamageEvent, DummyCaster
globals
private constant integer PASSIVE_SPELL_ID = 'A00K'
private constant integer DUMMY_SPELL_ID = 'A00L' //entangling roots
private unit dummy
endglobals
private function GetChance takes integer level returns integer
return 10 + level * 5
endfunction
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
private function SnarlActions takes nothing returns nothing
local integer level = GetUnitAbilityLevel(PDDS.target, PASSIVE_SPELL_ID)
if level > 0 then
if IsUnitEnemy(PDDS.source, GetOwningPlayer(PDDS.target)) then
if GetRandomInt(1, 100) < GetChance(level) then
call SetUnitOwner(dummy, GetOwningPlayer(PDDS.target), false)
call DC.setCastingFrom(dummy, GetUnitX(PDDS.source), GetUnitY(PDDS.source), DUMMY_SPELL_ID, level)
call IssueTargetOrderById(dummy, 852171, PDDS.source)
call UnitRemoveAbility(dummy, DUMMY_SPELL_ID)
endif
endif
endif
endfunction
private function init takes nothing returns nothing
call AddDamageHandler(function SnarlActions)
set dummy = DC.getDummy(DUMMY_SPELL_ID)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library NoEnemyUnits
globals
private group TempG = CreateGroup()
endglobals
function NoEnemyUnits takes unit u, real radius returns boolean
local unit first
call GroupEnumUnitsInRange(TempG, GetUnitX(u), GetUnitY(u), radius, null)
loop
set first = FirstOfGroup(TempG)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, GetOwningPlayer(u)) then
return false
endif
call GroupRemoveUnit(TempG, first)
endloop
return true
endfunction
endlibrary
//TESH.scrollpos=24
//TESH.alwaysfold=0
library Dialog initializer init uses DialogBox, SetUnitMaxState, PrintTextOnScreen
globals
private DialogBox s
private button normal
private button hard
endglobals
private function SetStateHP takes real regularUnits, real bosses returns nothing
local unit first
call GroupEnumUnitsInRect(enumG, bj_mapInitialPlayableArea, null)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
if UnitAlive(first) then
if IsUnitType(first, UNIT_TYPE_HERO) then
call SuspendHeroXP(first, true)
call SetUnitMaxState(first, UNIT_STATE_MAX_LIFE, GetWidgetLife(first) + bosses)
//call BJDebugMsg(GetHeroProperName(first))
else
call SetUnitMaxState(first, UNIT_STATE_MAX_LIFE, GetWidgetLife(first) + regularUnits)
endif
endif
call GroupRemoveUnit(enumG, first)
endloop
endfunction
private function difficulty takes nothing returns nothing
if s.clicked(normal) then
set udg_Difficulty = 0
call PrintHint("NORMAL ", "mode selected")
call SetStateHP(ENEMY_ADD_HP, 0)
else
set udg_Difficulty = 1
call PrintHint("HARD ", "mode selected")
call PrintSimpleText("Are you insane? I hope you know what you're doing. Good luck!")
call SetStateHP(ENEMY_ADD_HP * ENEMY_ADD_HP_MULTIPLIER, BOSS_ADD_HP)
endif
endfunction
private function run takes nothing returns nothing
set s = s.create("Game Mode", function difficulty)
set normal = s.addButton("Normal", 1)
set hard = s.addButton("Hard", 2)
call s.display(Player(0), true)
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endfunction
private function init takes nothing returns nothing
call TimerStart(CreateTimer(), 0, false, function run)
endfunction
endlibrary
//TESH.scrollpos=39
//TESH.alwaysfold=0
library Quests uses QuestUtils
//run via Load Hero trigger
globals
QuestUtils mainQ
QuestItem entrance
QuestItem val
//Bosses
QuestUtils boss
QuestItem bgrin //1st
QuestItem bscilla //2nd
QuestItem baldil //3rd
QuestItem bnaze //4th
QuestItem bmira //5th
QuestItem blava //6th
QuestItem bsakagi //7th
QuestItem bguard //8th
QuestItem banubis //9th
QuestItem bdeath //10th
QuestItem bvalker //11th last boss
//Mini bosses
private boolean mstart = true
QuestUtils mini
QuestItem msatyr
QuestItem mwraith
QuestItem mspider
QuestItem mskeleton
QuestItem mrevenant
//Optional quests, notes
QuestUtils credits
QuestUtils camera
QuestUtils npc
QuestUtils info
endglobals
function QuestSetup takes nothing returns nothing
set mainQ = mainQ.create(true)
set mainQ.questIcon = "ReplaceableTextures\\CommandButtons\\BTNSpy.blp"
set mainQ["Main Quest"] = "Venture into this underworld realm and destroy the evil in this place.
|cffffcc00- If you are playing as Mage. Geish must survive.|r"
set entrance = entrance.create(mainQ, "Find the entrance of the dungeon.")
//OPTIONALS:
//Credits:
set credits = credits.create(false)
set credits.questIcon = "ReplaceableTextures\\CommandButtons\\BTNScrollOfTownPortal.blp"
set credits["Credits"] = "
This map is made by |cffffcc00mckill2009|r which is heavily inspired by on Dungeon Siege and Castlevania games.
Full list of credits are written in map description of |cffffcc00hiveworkshop.com|r since it cannot be accommodated here."
//Camera:
set camera = camera.create(false)
set camera.questIcon = "ReplaceableTextures\\WorldEditUI\\Doodad-Cinematic.blp"
set camera["Camera"] = "
Type |cffffcc00-camr|r to rotate counter-clockwise 45 degrees.
Type |cffffcc00-cam reset|r to reset camera."
//NPC:
set npc = npc.create(false)
set npc.questIcon = "ReplaceableTextures\\WorldEditUI\\Editor-MultipleUnits.blp"
set npc["NPC's"] = "
|cffffcc00Mage: Kellen Wellsun|r
The only one who can have an escort wisp, an invulnerable spirit that cannot attack
but can heal Kellen and replenish his mana.
|cffffcc00Dwarf: Agor Stonebreaker|r
The only one who has 5 escort dwarfs from the beginning of the game."
set info = info.create(false)
set info.questIcon = "ReplaceableTextures\\CommandButtons\\BTNBansheeAdept.blp"
set info["Info"] = "a GATE KEY will usually drop in the XY of the Hero."
endfunction
function SetupQuestInit takes nothing returns nothing
set boss = boss.create(true)
set boss.questIcon = "ReplaceableTextures\\CommandButtons\\BTNCloakOfFlames.blp"
set boss["Boss Fights"] = "You must kill all bosses in this list in order to progress to the next level."
set mini = mini.create(true)
set mini.questIcon = "ReplaceableTextures\\CommandButtons\\BTNAncientOfWonders.blp"
set mini["Mini Boss Fights"] = "Kill all mini bosses on this list. Some mini-bosses are a must to kill to open a gate."
//Boss
set bgrin = bgrin.create(boss, "Slay the Undead Dwarf")
set bscilla = bscilla.create(boss, "Slay the Banshee Queen")
set baldil = baldil.create(boss, "Slay the Library Keeper")
set bnaze = bnaze.create(boss, "Slay the Fallen Priest")
set bmira = bmira.create(boss, "Slay the Assasin")
set blava = blava.create(boss, "Slay the Lava Lord")
set bsakagi = bsakagi.create(boss, "Slay the Dungeon Keeper")
set bguard = bguard.create(boss, "Slay the Valker's Pet")
set banubis = banubis.create(boss, "Slay the Underground Lord")
set bdeath = bdeath.create(boss, "Slay the Overseer")
set bvalker = bvalker.create(boss, "Slay the Valker and his minions")
//Mini Boss
set mspider = mspider.create(mini, "Slay the Spider Lord")
set msatyr = msatyr.create(mini, "Slay the Satyr Lord")
set mrevenant = mrevenant.create(mini, "Slay the Revenant Lord")
set mwraith = mwraith.create(mini, "Slay the Wraith")
set mskeleton = mskeleton.create(mini, "Slay the Skeleton champion")
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library SimpleCodes initializer init uses SimpleTextTag, SummonedEscort, RegisterPlayerUnitEvent, Table, UnitHasItemType
globals
private integer DwarfCount = 0
private real xO
private real yO
private integer Order
private Table dw
endglobals
/*
*
* Allows summoned enemies to attack the hero
*
*
*/
private function S_Summon takes nothing returns nothing
if IsUnitEnemy(udg_HERO, GetTriggerPlayer()) then
//if GetPlayerController(GetTriggerPlayer()) != MAP_CONTROL_USER then
call SE.summoned(udg_HERO, GetTriggerUnit())
endif
endfunction
/*
*
* Health and mana for hero if leveled up
*
*/
private function S_HealthAndManaAndLevel takes nothing returns nothing
if GetTriggerUnit()==udg_HERO then
//call SimTTUnit("Level Up!", GetTriggerUnit(), 0.015, 0.03)
call SetWidgetLife(GetTriggerUnit(), GetUnitState(GetTriggerUnit(), UNIT_STATE_MAX_LIFE))
call SetUnitState(GetTriggerUnit(), UNIT_STATE_MANA, GetUnitState(GetTriggerUnit(), UNIT_STATE_MAX_MANA))
if GetHeroLevel(GetTriggerUnit()) >= HERO_MAX_LEVEL then
call SuspendHeroXP(GetTriggerUnit(), true)
endif
endif
endfunction
/*
*
* Rewards
*
*/
public function SoulRelease takes unit u, integer souls returns nothing
call SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(Player(0), PLAYER_STATE_RESOURCE_LUMBER)+souls)
call DestroyEffect(AddSpecialEffectTarget("Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl", u, "origin"))
call SimTTUnit("You have freed "+I2S(souls)+" souls", u, 0, 0.025)
endfunction
/*
*
* Initializer
*
*/
private function init takes nothing returns nothing
set dw = Table.create()
//===Hero Full Health and Mana
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_LEVEL, function S_HealthAndManaAndLevel)
//===SummonedUnit Engage, for computer player
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function S_Summon)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library ChestOpen uses MultiBoard, SimpleCodes
globals
private constant integer CHEST_ID = 'h00P'
private constant integer CHANCE = 25
private constant real RANGE = 250
private integer Chest = 0
private group g = CreateGroup()
endglobals
private struct ChestOpen extends array
private static method click takes nothing returns nothing
local unit u
local unit first
local real x
local real y
local integer random
if GetUnitTypeId(GetTriggerUnit())==CHEST_ID then
set u = GetTriggerUnit() //this is the chest
if not IsUnitInGroup(u, g) then
set x = GetUnitX(u)
set y = GetUnitY(u)
//this ensures that the hero is near the chest
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, RANGE, null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitType(first, UNIT_TYPE_HERO) then
call SetUnitAnimation(u, "morph")
call GroupAddUnit(g, u)
set Chest = Chest + 1
call mb.setText(3, 1, I2S(Chest)+"/"+I2S(TotalChests ))
if GetRandomInt(0, 100) < CHANCE then
set random = GetRandomInt(0, 3)
if random==1 then
call CreateItem(itemType[GetRandomInt(0, 7)], x, y)
elseif random==3 then
call CreateItem(itemType[GetRandomInt(8, 15)], x, y)
else
call SimpleCodes_SoulRelease(u, GetRandomInt(15, 50))
endif
endif
exitwhen true //exits if chest is open
endif
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
set u = null
endif
endif
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_SELECTED, null)
call TriggerAddAction(t, function thistype.click)
set t= null
endmethod
endstruct
endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
scope Secrets initializer init
globals
private integer First = 0
private integer count = 0
endglobals
private function SecretItem takes nothing returns nothing
local item it
local integer random
local real x
local real y
if GetItemTypeId(GetManipulatedItem())==SECRET_ITEM then
set it = GetManipulatedItem()
set x = GetItemX(it)
set y = GetItemY(it)
set count = count + 1
call mb.setText(4, 1, I2S(count)+"/"+I2S(TotalSecretLocations))
if First==0 then
set First = 1
//running only for first time
call PrintHint("HINT: ", "This item you pick are secret locations. If you find all secrets, there is a big reward.")
call CreateItem('phea', x, y) //healing
elseif count==TotalSecretLocations then
//if found all secrets
call SimTTUnit("You've earned the big reward!", GetTriggerUnit(), 0, 0.025)
call SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(Player(0), PLAYER_STATE_RESOURCE_GOLD)+6000)
call SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(Player(0), PLAYER_STATE_RESOURCE_LUMBER)+3000)
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl", x, y))
call CreateItem('ckng', x, y)
else
//set mini rewards here...
set random = GetRandomInt(0, 3)
if random==3 then
set random = GetRandomInt(30, 70)
call SetPlayerState(Player(0), PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(Player(0), PLAYER_STATE_RESOURCE_LUMBER) + random)
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl", x, y))
call SimTTLoc("You have freed " + I2S(random) + " Souls!", x, y, 150, 0.025)
else
call CreateItem(itemType[GetRandomInt(16, 30)], x, y)
endif
endif
set it = null
endif
endfunction
private function init takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function SecretItem)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ItemsFromEnemies initializer init
private function DeathEvent takes nothing returns nothing
local unit u
local real x
local real y
if IsUnitEnemy(GetKillingUnit(), GetTriggerPlayer()) then
//call BJDebugMsg("okok1")
if udg_ItemCheckRegular and GetUnitAbilityLevel(GetTriggerUnit(), 'Aloc')==0 then
//call BJDebugMsg("okok2")
if GetRandomInt(0, 100) < 30 then
set u = GetTriggerUnit()
set x = GetUnitX(u)
set y = GetUnitY(u)
call CreateItem(udg_ItemsFromEnemies[GetRandomInt(0,20)], x, y)
if GetRandomInt(0, 7)==3 then
call SimpleCodes_SoulRelease(u, GetRandomInt(25, 65))
endif
set u = null
endif
endif
endif
endfunction
private function init takes nothing returns nothing
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_DEATH, function DeathEvent, UNDEAD_DWELLER)
//===Items From Enemies:
set udg_ItemsFromEnemies[0] = 'rhe2' //rune of healing
set udg_ItemsFromEnemies[1] = 'rman' //rune of mana
set udg_ItemsFromEnemies[2] = 'rhe1' //rune of lesser healing
set udg_ItemsFromEnemies[3] = 'rres' //rune of restoration
set udg_ItemsFromEnemies[4] = 'pams' //anti magic portion
set udg_ItemsFromEnemies[5] = 'phea' //portion of healing
set udg_ItemsFromEnemies[6] = 'pman' //portion of mana
set udg_ItemsFromEnemies[7] = 'pnvl' //portion of lesset invulnerability
set udg_ItemsFromEnemies[8] = 'sreg' //scroll of regeneration
set udg_ItemsFromEnemies[9] = 'tdex' //tome of agility
set udg_ItemsFromEnemies[10] = 'tint' //tome of intelligence
set udg_ItemsFromEnemies[11] = 'tstr' //tome of strength
set udg_ItemsFromEnemies[12] = 'rag1' //slippers of agility + 3
set udg_ItemsFromEnemies[13] = 'rin1' //mantle of intelligence + 3
set udg_ItemsFromEnemies[14] = 'rst1' //gauntlets of orge strength + 3
set udg_ItemsFromEnemies[15] = 'rat3' //claws of attack + 3
set udg_ItemsFromEnemies[16] = 'rej2' //lesser replenishment potion
set udg_ItemsFromEnemies[17] = 'wlsd' //wand of lightning shield
set udg_ItemsFromEnemies[18] = 'sman' //scroll of mana
set udg_ItemsFromEnemies[19] = 'shea' //scroll of healing
set udg_ItemsFromEnemies[20] = 'pinv' //potion of invisibility
endfunction
endscope
//TESH.scrollpos=76
//TESH.alwaysfold=0
library MultiBoard uses BoardEx, RandomColors, RegisterPlayerUnitEvent
//used also by ChestOpen, MiniBossDies
globals
public integer MiniBossKills = 0 //used by MiniBossDies trigger
private integer BossKills = 0
private integer Kills = 0
public boolean KILLS_NORMAL = true
endglobals
private function Setup takes nothing returns boolean
local integer i
set mb = mb.create(6, 2, true, false)
call mb.setTitle(DA_Version)
set i = 0
loop
call mb.setIndividualWidths(i, 0, 0.08)
call mb.setIndividualWidths(i, 1, 0.04)
call mb.setText(i, 1, "0")
set i = i + 1
exitwhen i==6
endloop
//First Column is TITLE
call mb.setText(0, 0, RandomColor("Kills"))
call mb.setText(1, 0, RandomColor("Mini Boss"))
call mb.setText(2, 0, RandomColor("Boss"))
call mb.setText(3, 0, RandomColor("Opened Chests"))
call mb.setText(4, 0, RandomColor("Secrets Found"))
call mb.setText(5, 0, RandomColor("Special Event"))
//Second Column is the Update
call mb.setText(1, 1, I2S(0)+"/"+I2S(TOTAL_MINI_BOSS))
call mb.setText(2, 1, I2S(0)+"/"+I2S(TOTAL_BOSS))
call mb.setText(3, 1, I2S(0)+"/"+I2S(TotalChests))
call mb.setText(4, 1, I2S(0)+"/"+I2S(TotalSecretLocations))
call mb.setText(5, 1, RandomColor("Not Yet"))
call mb.display(true, true)
return false
endfunction
/***************************************************************************
* Updates the kills, SpecialEvent is NOT included in this
****************************************************************************/
private function MB_UpdateKills takes nothing returns boolean
if IsUnitEnemy(GetKillingUnit(), GetTriggerPlayer()) and GetOwningPlayer(GetKillingUnit())==Player(0) then
if KILLS_NORMAL then //normal kills event
set Kills = Kills + 1
call mb.setText(0, 1, I2S(Kills))
endif
endif
return false
endfunction
/***************************************************************************
* BossDies, enables hero gain XP, for Player 10 only (Player 11 in GUI)
****************************************************************************/
private function MB_EventBossDeath takes nothing returns nothing
if IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) then
set BossKills = BossKills + 1
call mb.setText(2, 1, I2S(BossKills)+"/"+I2S(TOTAL_BOSS))
//call BJDebugMsg("HERO DEAD")
//Enables XP
call SuspendHeroXP(udg_HERO, false)
//creating tome of power everytime boss dies
set bj_lastCreatedItem = CreateItem('tkno', GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()))
endif
endfunction
private struct DAMB
private static method countItems takes nothing returns nothing
set TotalSecretLocations = TotalSecretLocations + 1
endmethod
private static method filterItems takes nothing returns boolean
return GetItemTypeId(GetFilterItem())==SECRET_ITEM
endmethod
private static method onInit takes nothing returns nothing
local unit chest
//Updates kills
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function MB_UpdateKills)
//BossDies, enables hero gain XP, for Player 10 only (Player 11 in GUI)
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_DEATH, function MB_EventBossDeath, ENEMY)
//Filters secret locations
call EnumItemsInRect(GetWorldBounds(), Filter(function thistype.filterItems), function thistype.countItems)
//Filters chests
call GroupEnumUnitsInRect(enumG, bj_mapInitialPlayableArea, null)
loop
set chest = FirstOfGroup(enumG)
exitwhen chest==null
if UnitAlive(chest) and GetUnitTypeId(chest)=='h00P' then
set TotalChests = TotalChests + 1
endif
call GroupRemoveUnit(enumG, chest)
endloop
//Setup for multiboard
call TimerStart(CreateTimer(), 0.1, false, function Setup)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SpecialEvent uses BossCastingSpells, EngageLite, MultiBoard
globals
private integer SpecialEvent = 0
private integer INTERVAL = 0
private timerdialog T_DIA
private integer array Skeletons
private rect array SRect
private location LOC = Location(0,0)
private group g = CreateGroup()
public boolean SKELETON_EVENT_ON = false
endglobals
//===INTERVAL
private function SkeletonsCreate takes nothing returns nothing
local integer random
local integer ran
local unit sk
set INTERVAL = INTERVAL + 1
if SPECIAL_EVENT_DURATION > INTERVAL then
set random = GetRandomInt(0, 100)
set ran = GetRandomInt(1,6)
if udg_SkeletonCount < SKELETON_LIMIT and SKELETON_EVENT_ON then
set udg_SkeletonCount = udg_SkeletonCount + 1
call MoveLocation(LOC, GetRandomReal(GetRectMinX(SRect[ran]), GetRectMaxX(SRect[ran])), GetRandomReal(GetRectMaxY(SRect[ran]), GetRectMaxY(SRect[ran])))
call DestroyEffect(AddSpecialEffectLoc(RAISE_SFX, LOC))
set sk = CreateUnitAtLoc(Player(10), Skeletons[GetRandomInt(1,3)], LOC, 0)
call MoveLocation(LOC, GetRandomReal(GetRectMinX(SRect[ran]), GetRectMaxX(SRect[ran])), GetRandomReal(GetRectMaxY(SRect[ran]), GetRectMaxY(SRect[ran])))
call GroupAddUnit(g, sk)
call EngageSingle(sk, udg_HERO)
if random < 60 then //creates level 5 warriors
set udg_SkeletonCount = udg_SkeletonCount + 1
call DestroyEffect(AddSpecialEffectLoc(RAISE_SFX, LOC))
set sk = CreateUnitAtLoc(Player(10), Skeletons[4], LOC, 0)
call GroupAddUnit(g, sk)
call EngageSingle(sk, udg_HERO)
elseif random < 25 then //creates level 8 warriors
set udg_SkeletonCount = udg_SkeletonCount + 1
call DestroyEffect(AddSpecialEffectLoc(RAISE_SFX, LOC))
set sk = CreateUnitAtLoc(Player(10), Skeletons[5], LOC, 0)
call GroupAddUnit(g, sk)
call EngageSingle(sk, udg_HERO)
endif
endif
set sk = null
else
//ends the special event
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endif
endfunction
//private function SkeletonFilter takes unit u returns boolean
// return GetUnitTypeId(u)=='nsko' /* Skeletal Orc, lv 3
//or GetUnitTypeId(u)=='nsog' /* Skeletal Orc Grunt, 70%, lv 5
//or GetUnitTypeId(u)=='nsoc' /* Skeletal Orc Champion, 30%, lv 8
//or GetUnitTypeId(u)=='nskf' /* Burning Archer, lv 3
//or GetUnitTypeId(u)=='nskg' // Giant Skeleton Warrior, lv 3
//endfunction
private function SEdeath takes nothing returns nothing
if SKELETON_EVENT_ON then
if IsUnitInGroup(GetTriggerUnit(), g) then
call GroupRemoveUnit(g, GetTriggerUnit())
//if SkeletonFilter(GetTriggerUnit()) then
set udg_SkeletonCount = udg_SkeletonCount - 1
//===Update multiboard for Special event
set SpecialEvent = SpecialEvent + 1
call mb.setText(5, 1, I2S(SpecialEvent))
//creating items
if GetRandomInt(1,100) <= ITEM_SDROP then
set bj_lastCreatedItem = CreateItem(udg_RandomItem[GetRandomInt(1,5)], GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()))
endif
endif
endif
endfunction
//===INTERVAL, see DURATION to see how long the waves
private function SkeletonTimerExpire takes nothing returns nothing
set SKELETON_EVENT_ON = false
set udg_ItemCheckRegular = true
set MultiBoard_KILLS_NORMAL = true
call BossSakagiFight(udg_BossSakagi) //Sakagi enters to the fight
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
call DestroyTimerDialog(T_DIA)
endfunction
//Executed after Sakagi Enters cinematic, runs only 1 time
function SkeletonTimer takes nothing returns nothing
local timer t = CreateTimer()
set SKELETON_EVENT_ON = true
set MultiBoard_KILLS_NORMAL = false
call TimerStart(t, SPECIAL_EVENT_DURATION, false, function SkeletonTimerExpire)
set T_DIA = CreateTimerDialog(t)
call TimerDialogDisplay(T_DIA, true)
call TimerDialogSetTitle(T_DIA, "Survive In")
//The spawning per seconds
call TimerStart(CreateTimer(), 1.0, true, function SkeletonsCreate)
set Skeletons[1] = 'nsko' //Skeletal Orc, lv 3
set Skeletons[2] = 'nskf' //Burning Archer, lv 3
set Skeletons[3] = 'nskg' //Giant Skeleton Warrior, lv 3
//===
set Skeletons[4] = 'nsog' //Skeletal Orc Grunt, 70%, lv 5
set Skeletons[5] = 'nsoc' //Skeletal Orc Champion, 30%, lv 8
set SRect[1] = gg_rct_zzz_Prison_1
set SRect[2] = gg_rct_zzz_Prison_2
set SRect[3] = gg_rct_zzz_Prison_3
set SRect[4] = gg_rct_zzz_Prison_4
set SRect[5] = gg_rct_zzz_Prison_5
set SRect[6] = gg_rct_zzz_Prison_6
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function SEdeath)
set t = null
endfunction
endlibrary
//TESH.scrollpos=104
//TESH.alwaysfold=0
library CastingConditions uses AutoCastSystem, EngageLite, RandomUnit, SimpleFunctions
globals
private group grp = CreateGroup()
private boolean castC = false
endglobals
private function CheckEnemy takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(CS.caster, GetOwningPlayer(GetFilterUnit())) /*
*/ and GetUnitAbilityLevel(GetFilterUnit(), 'Aloc')==0
endfunction
private function IsEnemy takes unit u returns boolean
return IsUnitEnemy(CS.caster, GetOwningPlayer(u))
endfunction
private function CheckAlly takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and not IsUnitEnemy(CS.caster, GetOwningPlayer(GetFilterUnit())) /*
*/ and GetWidgetLife(GetFilterUnit()) < GetUnitState(GetFilterUnit(), UNIT_STATE_MAX_LIFE) /*
*/ and GetUnitAbilityLevel(GetFilterUnit(), 'Brej')==0
endfunction
private function CheckAlly2 takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and not IsUnitEnemy(CS.caster, GetOwningPlayer(GetFilterUnit())) /*
*/ and GetWidgetLife(GetFilterUnit()) < GetUnitState(GetFilterUnit(), UNIT_STATE_MAX_LIFE) /*
*/ and GetUnitAbilityLevel(GetFilterUnit(), 'Brej')==0
endfunction
/***********************************************************
* ALLY CASTING
************************************************************/
//function CastReplenish350 takes nothing returns boolean
// set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 350, Filter(function CheckAlly2))
//return UnitAlive(CS.target)
//endfunction
function CastReplenishAllyMana takes nothing returns boolean
local unit first
call GroupEnumUnitsInRange(grp, GetUnitX(CS.caster), GetUnitY(CS.caster), 350, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and not IsUnitEnemy(CS.caster, GetOwningPlayer(first)) then
if GetUnitManaAmount(first) < GetUnitState(first, UNIT_STATE_MAX_MANA) then
set first = null
return true
endif
endif
call GroupRemoveUnit(grp, first)
endloop
return false
endfunction
function CastRejuvenation350 takes nothing returns boolean
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 350, Filter(function CheckAlly2))
return UnitAlive(CS.target)
endfunction
function CastHealToAlly takes nothing returns boolean
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 600, Filter(function CheckAlly))
return UnitAlive(CS.target)
endfunction
/***********************************************************
* Used only by VALKER
************************************************************/
function ValkerCastingHealingStrike takes nothing returns boolean
return (GetWidgetLife(CS.caster) < 6000) and GetRandomInt(1, 7)==2
endfunction
function ValkerCastingInfernals takes nothing returns boolean
return InfernalCount==0
endfunction
/***********************************************************
* ENEMY CASTING
************************************************************/
function CastChance takes nothing returns boolean
return GetRandomInt(1, 3)==1
endfunction
function CastChance2 takes nothing returns boolean
return GetRandomInt(1, 4)==2
endfunction
function CastChance3 takes nothing returns boolean
return GetRandomInt(1, 6)==3
endfunction
/***********************************************************
* NO TARGET
************************************************************/
function CastNegativeNoTarget takes nothing returns boolean
local unit first
call GroupEnumUnitsInRange(grp, GetUnitX(CS.caster), GetUnitY(CS.caster), 300, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and IsEnemy(first) then
set first = null
return true
endif
call GroupRemoveUnit(grp, first)
endloop
return false
endfunction
function CastNegativeNoTarget600 takes nothing returns boolean
local unit first
call GroupEnumUnitsInRange(grp, GetUnitX(CS.caster), GetUnitY(CS.caster), 600, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and IsEnemy(first) then
set first = null
return true
endif
call GroupRemoveUnit(grp, first)
endloop
return false
endfunction
/***********************************************************
* POINT TARGET - aoe
************************************************************/
function CastNegativePointTarget600Plus takes nothing returns boolean
local real angle
local real x1 = GetUnitX(CS.caster)
local real y1 = GetUnitY(CS.caster)
local real x2
local real y2
set castC = false
set CS.target = GetRandomUnitInArea(x1, y1, 600, Filter(function CheckEnemy))
if UnitAlive(CS.target) then
set x2 = GetUnitX(CS.target)
set y2 = GetUnitY(CS.target)
set angle = Atan2(y2 - y1, x2 - x1)
set CS.SpellTargetX = GetUnitX(CS.target) + 100 * Cos(angle)
set CS.SpellTargetY = GetUnitY(CS.target) + 100 * Sin(angle)
set castC = true
endif
return castC
endfunction
function CastNegativePointTarget600 takes nothing returns boolean
set castC = false
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 600, Filter(function CheckEnemy))
if UnitAlive(CS.target) then
set CS.SpellTargetX = GetUnitX(CS.target)
set CS.SpellTargetY = GetUnitY(CS.target)
set castC = true
endif
return castC
endfunction
function CastNegativePointTarget1200 takes nothing returns boolean
set castC = false
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 1200, Filter(function CheckEnemy))
if UnitAlive(CS.target) then
//call BJDebugMsg("casting")
set CS.SpellTargetX = GetUnitX(CS.target)
set CS.SpellTargetY = GetUnitY(CS.target)
set castC = true
endif
return castC
endfunction
//used by anubis
function CastNegativePointAngletEx takes nothing returns boolean
local real angle
local real xCaster = GetUnitX(CS.caster)
local real yCaster = GetUnitY(CS.caster)
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 500, Filter(function CheckEnemy))
if UnitAlive(CS.target) then
set angle = GetAngle(xCaster, yCaster, GetUnitX(CS.target), GetUnitY(CS.target))
set CS.SpellTargetX = GetUnitX(CS.caster) + 650 * Cos(angle)
set CS.SpellTargetY = GetUnitY(CS.caster) + 650 * Sin(angle)
endif
return UnitAlive(CS.target)
endfunction
/***********************************************************
* UNIT TARGET
************************************************************/
function CastNegativeUnitTarget takes nothing returns boolean
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 600, Filter(function CheckEnemy))
return UnitAlive(CS.target)
endfunction
function CastNegativeUnitTarget300 takes nothing returns boolean
local real distance = 0
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 900, Filter(function CheckEnemy))
if UnitAlive(CS.target) then
set distance = GetDistanceFromUnit(CS.caster, CS.target)
endif
return distance > 300
endfunction
function CastNegativeUnitTargetFar takes nothing returns boolean
local real distance = 0
set CS.target = GetRandomUnitInArea(GetUnitX(CS.caster), GetUnitY(CS.caster), 900, Filter(function CheckEnemy))
if UnitAlive(CS.target) then
set distance = GetDistanceFromUnit(CS.caster, CS.target)
endif
return distance > 600
endfunction
//Boss changes to Player 10 (11 in GUI)
function SetUnitEnemyAndAttack takes unit u returns nothing
call SetUnitOwner(u, ENEMY, true) //player 11 in GUI, Valker's Minions
call SetUnitInvulnerable(u, false)
call EngageSingle(u, udg_HERO)
set udg_EnableHealingPoint = false
endfunction
/***********************************************************
* FOR GEISH ONLY
************************************************************/
function GeishStart takes unit u returns nothing
local ACS ge
set ge = ge.register(u)
call ge.spellTypeNoTarget(function CastReplenishAllyMana, 852548) //replenish mana
call ge.spellTypeUnitTarget(function CastRejuvenation350, 852160) //rejuvenation
call ge.launch(1.0)
endfunction
endlibrary
//TESH.scrollpos=133
//TESH.alwaysfold=0
library BossCastingSpells uses CastingConditions //in Main Trigger
globals
private ACS grinmaulbrick //1st
private ACS scilla //2nd
private ACS aldil //3rd
private ACS naze //4th
private ACS mira //5th
private ACS lava //6th
private ACS sakagi //7th
private ACS guard //8th
private ACS anubis //9th
private ACS death //10th
private ACS valker //11th last boss
endglobals
/***********************************************************
* Grin Maulbrick - 1st Boss
************************************************************/
function BossGrinMaulbrickFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set grinmaulbrick = grinmaulbrick.register(u)
call grinmaulbrick.spellTypeUnitTarget(function CastNegativeUnitTarget, 852222) //death coil
call grinmaulbrick.spellTypeNoTarget(function CastNegativeNoTarget, 852096) //thunderclap
call grinmaulbrick.launch(0.5)
call IssueTargetOrderById(u, 852222, udg_HERO) //death coil
endfunction
/***********************************************************
* Scilla - 2nd Boss
************************************************************/
function BossScillaFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set scilla = scilla.register(u)
call scilla.spellTypeUnitTarget(function CastNegativeUnitTarget, 852487) //drain
call scilla.spellTypeUnitTarget(function CastNegativeUnitTarget, OID_firebolt)
call scilla.spellTypePointTarget(function CastNegativePointTarget600, 852592) //silence
call scilla.launch(0.5)
endfunction
/***********************************************************
* Aldil - 3rd Boss
************************************************************/
function BossAldilFight takes unit u returns nothing
local integer i = 0
loop
set i = i + 1
call SetUnitEnemyAndAttack(udg_SkeletalMage[i])
exitwhen i==4
endloop
call SetUnitEnemyAndAttack(u)
set aldil = aldil.register(u)
call aldil.spellTypePointTarget(function CastNegativePointTarget600, 852488) //flame strike
call aldil.spellTypeNoTarget(function CastNegativeNoTarget600, 852183) //starfall, freezing field
call aldil.launch(0.5)
call IssueImmediateOrderById(u, 852183) //freezing field
endfunction
/***********************************************************
* Naze - 4th Boss
************************************************************/
function BossNazeFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set naze = naze.register(u)
call naze.spellTypeUnitTarget(function CastNegativeUnitTarget, 852189) //cripple
call naze.spellTypeUnitTarget(function CastHealToAlly, 852160) //rejuvenation
call naze.spellTypePointTarget(function CastNegativePointTarget600, OID_carrionswarm)
call naze.spellTypeNoTarget(function CastChance, OID_howlofterror)
call naze.launch(0.5)
call IssuePointOrderById(u, OID_carrionswarm, GetUnitX(udg_HERO), GetUnitY(udg_HERO))
endfunction
/***********************************************************
* Mira - 5th Boss
************************************************************/
function BossMiraFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
call SetUnitEnemyAndAttack(udg_TempUnit)
set mira = mira.register(u)
call mira.spellTypeUnitTarget(function CastNegativeUnitTarget, 852577) //black arrow
call mira.spellTypeUnitTarget(function CastHealToAlly, OID_chainlightning)
call mira.spellTypeUnitTarget(function CastNegativeUnitTargetFar, OID_firebolt) //if more than 600
call mira.spellTypePointTarget(function CastNegativePointTarget600, OID_flamestrike) //if within 600
call mira.spellTypeNoTarget(function CastChance, OID_howlofterror)
call mira.launch(0.5)
endfunction
/***********************************************************
* Lava - 6th Boss
************************************************************/
function BossLavaFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set lava = lava.register(u)
call lava.spellTypePointTarget(function CastNegativePointTarget600, OID_carrionswarm)
call lava.spellTypeNoTarget(function CastNegativePointTarget600, OID_howlofterror)
call lava.launch(0.5)
endfunction
/***********************************************************
* Sakagi - 7th Boss
************************************************************/
function BossSakagiFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set sakagi = sakagi.register(u)
call sakagi.spellTypeNoTarget(function CastNegativeNoTarget, OID_mirrorimage)
call sakagi.spellTypeNoTarget(function CastNegativeNoTarget, OID_whirlwind)
call sakagi.spellTypeUnitTarget(function CastNegativeUnitTarget300, OID_firebolt)
call sakagi.launch(0.5)
endfunction
/***********************************************************
* Guard - 8th Boss
************************************************************/
function BossGuardFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set guard = guard.register(u)
call guard.spellTypePointTarget(function CastNegativePointTarget1200, OID_flamestrike) //if within 1200 aoe
call guard.spellTypePointTarget(function CastNegativePointTarget600, 852504) //ward, serpent ward
call guard.spellTypeUnitTarget(function CastNegativeUnitTarget, 852189) //cripple
call guard.spellTypeNoTarget(function CastChance2, OID_fanofknives) //fan of knives
call guard.launch(0.5)
endfunction
/***********************************************************
* Anubis - 9th Boss
************************************************************/
function BossAnubisFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set anubis = anubis.register(u)
call anubis.spellTypeNoTarget(function CastNegativeNoTarget, 852127) //war stomp
call anubis.spellTypePointTarget(function CastNegativePointTarget600, 852125) //shockwave
call anubis.spellTypePointTarget(function CastNegativePointAngletEx, OID_carrionswarm)
call anubis.spellTypePointTarget(function CastNegativePointTarget600, OID_rainoffire)
call anubis.launch(0.5)
endfunction
/***********************************************************
* Death - 10th Boss
************************************************************/
function BossDeathFight takes unit u returns nothing
call SetUnitEnemyAndAttack(u)
set death = death.register(u)
call death.spellTypeUnitTarget(function CastNegativeUnitTarget, 852149) //faeriefire
call death.spellTypePointTarget(function CastNegativePointTarget600, 852580) //breath of fire
call death.launch(0.5)
call IssueTargetOrderById(u, 852149, udg_HERO) //faeriefire
call GroupAddUnit(udg_BossDeathGroup, u)
endfunction
/***********************************************************
* Valker - 11th Boss LAST
************************************************************/
function BossValkerFight takes unit u returns nothing
local integer i = 0
loop
exitwhen i==4
set i = i+1
call SetUnitEnemyAndAttack(udg_SkeletonSkullFloating[i])
endloop
call SetUnitEnemyAndAttack(u)
set valker = death.register(u)
call valker.spellTypeUnitTarget(function CastNegativeUnitTarget, 852222) //death coil
call valker.spellTypeUnitTarget(function CastNegativeUnitTarget, 852119) //chainlightning
call valker.spellTypePointTarget(function CastNegativePointTarget600, OID_carrionswarm)
call valker.spellTypeNoTarget(function ValkerCastingHealingStrike, OID_howlofterror)
call valker.spellTypeNoTarget(function ValkerCastingInfernals, OID_fanofknives)
call valker.launch(0.5)
call IssueImmediateOrderById(u, 852526) //fan of knives
endfunction
endlibrary
//TESH.scrollpos=35
//TESH.alwaysfold=0
library MiniBossCastingSpells uses CastingConditions, Quests
globals
//not in order
private ACS satyr
private ACS wraith
private ACS spider
private ACS skeleton
private ACS revenant
endglobals
/***********************************************************
* SATYR HELLCASTER - Terrorblade
************************************************************/
function MiniBossTerrorbladeFight takes unit u returns nothing
//call MiniBossFightStart()
set satyr = satyr.register(u)
call satyr.spellTypeUnitTarget(function CastNegativeUnitTarget, OID_firebolt)
call satyr.spellTypePointTarget(function CastNegativePointTarget600, 852221) //death and decay
call satyr.launch(0.5)
endfunction
/***********************************************************
* BROOD MOTHER - Theraphus
************************************************************/
function MiniBossTheraphusFight takes unit u returns nothing
//call MiniBossFightStart()
set spider = spider.register(u)
call spider.spellTypePointTarget(function CastNegativePointTarget600, OID_flamestrike)
call spider.spellTypeNoTarget(function CastChance2, OID_fanofknives)
call spider.launch(0.5)
endfunction
/***********************************************************
* WRAITH - Anthis
************************************************************/
function MiniBossAnthisFight takes unit u returns nothing
//call MiniBossFightStart()
set wraith = wraith.register(u)
call wraith.spellTypePointTarget(function CastNegativePointTarget600, 852089) //blizzard
call wraith.spellTypeUnitTarget(function CastNegativeUnitTarget, 852226) //frost nova
call wraith.launch(0.5)
endfunction
/***********************************************************
* DEATH REVENANT - Frosthand
************************************************************/
function MiniBossFrosthandFight takes unit u returns nothing
//call MiniBossFightStart()
set revenant = revenant.register(u)
call revenant.spellTypePointTarget(function CastNegativePointTarget600, OID_flamestrike)
call revenant.spellTypeUnitTarget(function CastNegativeUnitTarget, OID_firebolt)
call revenant.launch(0.5)
endfunction
/***********************************************************
* SKELETON CHAMPION - Krakull
************************************************************/
function MiniBossKrakullFight takes unit u returns nothing
//call MiniBossFightStart()
set skeleton = skeleton.register(u)
call skeleton.spellTypeNoTarget(function CastNegativePointTarget600, OID_howlofterror)
call skeleton.spellTypeUnitTarget(function CastNegativeUnitTarget, OID_chainlightning)
call skeleton.launch(0.5)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//uses MiscSpells, DamageEvent
scope MiscCastingSpells initializer init
globals
private constant string BURNING_SFX = "Abilities\\Spells\\Human\\Slow\\SlowCaster.mdl"
private unit u
endglobals
private function BurningArcherAttacks takes nothing returns nothing
if GetUnitTypeId(PDDS.source)=='nskf' then
if GetRandomInt(1, 100) < 40 then
call DestroyEffect(AddSpecialEffectTarget(BURNING_SFX, PDDS.target, "origin"))
call UnitDamageTarget(PDDS.source, PDDS.target, GetRandomReal(5, 20), false, false, ATK, DMG, null)
endif
endif
endfunction
private function RegisterCaster takes nothing returns nothing
local unit first
call GroupEnumUnitsInRect(enumG, bj_mapInitialPlayableArea, null)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
if UnitAlive(first) and GetOwningPlayer(first)==UNDEAD_DWELLER then
if GetUnitTypeId(first)=='uban' or GetUnitTypeId(first)=='ngh1' then //banshee and ghost
call BanSheeGhostSpells(first)
elseif GetUnitTypeId(first)=='nskg' then //giant skeleton warrior
call GiantSkeletonChargedStrike(first)
endif
endif
call GroupRemoveUnit(enumG, first)
endloop
call DestroyTimer(GetExpiredTimer())
endfunction
private function Enters takes nothing returns boolean
set u = GetFilterUnit()
if GetUnitTypeId(u)=='uban' or GetUnitTypeId(u)=='ngh1' then //banshee and ghost
call BanSheeGhostSpells(u)
endif
//call PingMinimap(GetUnitX(GetFilterUnit()), GetUnitY(GetFilterUnit()), 0.1)
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local region rectRegion = CreateRegion()
call RegionAddRect(rectRegion, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(t, rectRegion, Condition(function Enters))
call TimerStart(CreateTimer(), 0.5, false, function RegisterCaster)
call AddDamageHandler(function BurningArcherAttacks)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library MiscSpells uses CastingConditions
globals
//not in order
private ACS boneblade
private ACS chargedS
endglobals
/***********************************************************
* Bone Blade
************************************************************/
function BanSheeGhostSpells takes unit u returns nothing
set boneblade = boneblade.register(u)
call boneblade.spellTypeUnitTarget(function CastNegativeUnitTarget, OID_chainlightning)
call boneblade.launch(0.5)
//call BJDebugMsg("BONE BLADE")
endfunction
function GiantSkeletonChargedStrike takes unit u returns nothing
set chargedS = chargedS.register(u)
call chargedS.spellTypePointTarget(function CastNegativePointTarget600Plus, OID_carrionswarm)
call chargedS.launch(0.5)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library FireTraps uses SimpleTextTag
globals
private constant string MODEL = "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
private real facing
private real array yy
private boolean FT = true
private integer test = 0
private integer count = 0
public boolean FireTrapKill = false //false, true When Lava Lord dies and LEVER is opened
endglobals
struct FireTraps extends array
private static method run takes nothing returns nothing
local MC m
//set test = test + 1
if FireTrapKill then
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
else
set m = m.create(Player(10), 1620, yy[GetRandomInt(0,8)], MODEL)
call m.setupCollision(80, 80, "")
call m.setupDamage(150, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE)
call m.launch(facing, 175, 50, 10)
//call BJDebugMsg("From FireTraps==" + I2S(test))
endif
//call BJDebugMsg("From FireTraps==" + I2S(test))
endmethod
static method FireTrapOn takes nothing returns nothing
if FT then
set FT = false
set facing = 180*DEGTORAD
set yy[0] = 3000
set yy[1] = 3400
set yy[2] = 3800
set yy[3] = 4160
set yy[4] = 4550
set yy[5] = 4950
set yy[6] = 5320
set yy[7] = 5700
set yy[8] = 6100
call TimerStart(CreateTimer(), 0.2, true, function thistype.run)
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=39
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 100
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=175
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=2
//TESH.alwaysfold=0
library IsDestructableTree uses optional UnitIndexer /* v1.3.1
*************************************************************************************
*
* 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.
*
*/
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)
private unit harvester = null
endglobals
function IsDestructableTree takes destructable d returns boolean
//* 851973 is the order id for stunned, it will interrupt the preceding harvest order.
return (IssueTargetOrderById(harvester, HARVEST_ORDER_ID, d)) and (IssueImmediateOrderById(harvester, 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
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 harvester = CreateUnit(NEUTRAL_PLAYER, HARVESTER_UNIT_ID, 0, 0, 0)
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = true
endif
call UnitAddAbility(harvester, HARVEST_ABILITY)
call UnitAddAbility(harvester, 'Aloc')
call ShowUnit(harvester, false)
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimedEffect /* v1.3
*************************************************************************************
*
* Allows one to create timed effects on 3d points or units.
*
*************************************************************************************
*
* API
*
* static method createOnPoint takes string mdl, real x, real y, real z, real duration returns thistype
* - starts a timed effect on a 3d point
*
* static method createOnUnit takes string mdl, unit u, string attach, real duration returns thistype
* - starts a timed effect on a unit
*
* method operator newDuration= takes real value returns nothing
* - sets an instance' new duration
*
* method destroy takes nothing returns nothing
* - destroys an instance
*
*************************************************************************************
*
* Credits
*
* PurgeAndFire for the OTip trick.
*
**************************************************************************************/
struct TimedEffect extends array
private static timer t = CreateTimer()
private static thistype instance = 0
private static integer count = 0
private static effect tempEffect
private static destructable platform
private thistype recycle
private thistype next
private thistype prev
private effect effects
private real time
method destroy takes nothing returns nothing
call DestroyEffect(effects)
set effects = null
set time = 0
set prev.next = next
set next.prev = prev
set recycle = thistype(0).recycle
set thistype(0).recycle = this
set count = count - 1
if 0 == count then
call PauseTimer(t)
endif
endmethod
method operator newDuration= takes real value returns nothing
set time = time + value
endmethod
private static method periodic takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen 0 == this
set time = time - 0.031250000
if 0 >= time then
call destroy()
endif
set this = next
endloop
endmethod
private static method allocate takes nothing returns thistype
local thistype this = thistype(0).recycle
if 0 == this then
set this = instance + 1
set instance = this
else
set thistype(0).recycle = recycle
endif
set next = 0
set prev = 0
set thistype(0).prev.next = this
set thistype(0).prev = this
set count = count + 1
if 1 == count then
call TimerStart(t, 0.031250000, true, function thistype.periodic)
endif
return this
endmethod
static method createOnPoint takes string mdl, real x, real y, real z, real duration returns thistype
local thistype this
if 0 < z then
set platform = CreateDestructableZ('OTip', x, y, z, 0, 1, 0)
endif
set tempEffect = AddSpecialEffect(mdl, x, y)
if null != platform then
call RemoveDestructable(platform)
endif
if duration >= 0.031250000 then
set this = allocate()
set time = duration
set effects = tempEffect
return this
endif
call DestroyEffect(tempEffect)
set tempEffect = null
return 0
endmethod
static method createOnUnit takes string mdl, unit u, string attach, real duration returns thistype
local thistype this
set tempEffect = AddSpecialEffectTarget(mdl, u, attach)
if duration >= 0.031250000 then
set this = allocate()
set effects = tempEffect
set time = duration
return this
endif
call DestroyEffect(tempEffect)
set tempEffect = null
return 0
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ClearItems requires optional WorldBounds
/*
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=-1
//TESH.alwaysfold=0
// API:
//
// >>CreateDelayedEffect(whichEffect, X, Y, Delay, Timeout)
// - whichEffect is of type string and contains the path of the
// effect-to-be-spawned
// - X and Y indicate where the effect should be spawned
// - Delay is of type real and indicates how long to wait before spawning the
// effect
// - Timeout is of type real and indicates how long to wait before destroying
// the effect after it has been created
//
// >>CreateDelayedEffectZ(whichEffect, X, Y, Z, Delay, Timeout)
// - whichEffect: see above
// - X, Y and Z indicate where to spawn the effect
// - Delay: see above
// - Timeout: see above
//
// >>CreateDelayedEffectTarget(whichEffect, Target, AttachmentPoint, Delay, Timeout)
// - whichEffect: see above
// - Target is of type widget and indicates on which widget the effect should
// be spawned
// - AttachmentPoint is of type string and holds the attachment point where
// the effect should be spawned on target widget
// - Delay: see above
// - Timeout: see above
//
// CREDITS:
// - Vexorian (JassHelper; TimerUtils)
// - Anitarf (Suggestions)
// - KaTTaNa (AddSpecialEffectZ function @ wc3jass.com)
// - PitzerMike (JassNewGenPack)
// - Pipedream (Grimoire)
// - SFilip (TESH)
library DelFX uses TimerUtils
private struct DELFX
private effect fx
private string path
private boolean target
private widget tar
private string attpt
private real x
private real y
private real z
private timer t
private real timeout
private method onDestroy takes nothing returns nothing
if .fx!=null then
call DestroyEffect(.fx)
set .fx=null
endif
set .tar=null
call ReleaseTimer(.t)
endmethod
private static method Release takes nothing returns nothing
call DELFX.destroy(GetTimerData(GetExpiredTimer()))
endmethod
private static method Callback takes nothing returns nothing
local DELFX s=GetTimerData(GetExpiredTimer())
local destructable d
debug if s.fx==null then
if s.target then
set s.fx=AddSpecialEffectTarget(s.path, s.tar, s.attpt)
elseif s.z==0 then
set s.fx=AddSpecialEffect(s.path, s.x, s.y)
else
set d=CreateDestructableZ('OTip', s.x, s.y, s.z, 0,1.,0)
set s.fx=AddSpecialEffect(s.path, s.x, s.y)
call RemoveDestructable(d)
set d=null
endif
call TimerStart(s.t, s.timeout, false, function DELFX.Release)
debug else
debug call BJDebugMsg("DELFX["+I2S(s)+"].Callback: Effect already spawned!")
debug endif
endmethod
static method Create takes string path, boolean target, widget tar, string attpt, real x, real y, real z, real delay, real timeout returns DELFX
local DELFX s=DELFX.allocate()
set s.t=NewTimer()
call SetTimerData(s.t, s)
set s.path=path
set s.target=target
set s.tar=tar
set s.attpt=attpt
set s.x=x
set s.y=y
set s.z=z
set s.timeout=timeout
call TimerStart(s.t, delay, false, function DELFX.Callback)
return s
endmethod
endstruct
// The functions below have been explained above.
function CreateDelayedEffect takes string path, real x, real y, real delay, real timeout returns nothing
call DELFX.Create(path, false, null, "", x, y, 0, delay, timeout)
endfunction
function CreateDelayedEffectZ takes string path, real x, real y, real z, real delay, real timeout returns nothing
call DELFX.Create(path, false, null, "", x, y, z, delay, timeout)
endfunction
function CreateDelayedEffectTarget takes string path, widget target, string attachmentpoint, real delay, real timeout returns nothing
call DELFX.Create(path, true, target, attachmentpoint, 0, 0, 0, delay, timeout)
endfunction
endlibrary
//TESH.scrollpos=225
//TESH.alwaysfold=0
library Missile /* v2.0.2
*************************************************************************************
*
* 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, 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.
* */ optional WorldBounds /* githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
* */ optional BoundSentinel /* wc3c.net/showthread.php?t=102576
*
* c). Handle recycling ( Speed 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). Misc ( Absolutely not needed, just listed to avoid an onIndex event )
* */ 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. 1./32. is recommended.
* • Too short timeout values may cause performance issues.
* • Too large timeout values 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 missiles 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 = 'e002'
/**
* 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 used.
* • 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 used 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 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 any widget collision is 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 is up to you to figure out a fictional item height.
*/
function GetItemHeight takes item i returns real
return 20.
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 snippet.
* 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.
// 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 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.
// • Runs after onDestructableFilter ( if a filter is declared )
static method onItem takes Missile missile, item hit returns boolean
// • Runs for items in collision range of a missile.
// • Runs after onItemFilter ( if a filter is declared )
static method onDestructableFilter takes nothing returns boolean
// • Runs before onDestructable as filter function.
// • Runs for destructables in collision range + Missile_MAX_COLLISION_RANGE.
// • Get the filter destructable via "GetFilterDestructable()"
// • Designed only for improving code read-ability.
static method onDestructableFilter takes nothing returns boolean
// • Runs before onItem as filter function.
// • Runs for items in collision range + Missile_MAX_COLLISION_RANGE.
// • Get the filter item via "GetFilterItem()"
// • Designed only for improving code read-ability.
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
// 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 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 missileStack
private boolexpr array expression
private triggercondition array condition
private integer array remove
// Arrays for widgets filter functions in structs.
private boolexpr array destFilter
private boolexpr array itemFilter
// Rectangle filter functions in Missile.
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 dummy for DELAYED_MISSILE_DEATH_ANIMATION_TIME,
// until the are recylced. ( Placed in a static if )
//
//! runtextmacro optional WRITE_MISSILE_RECYCLE_BIN("WRITE_DELAYED_MISSILE_RECYCLING", "DELAYED_MISSILE_DEATH_ANIMATION_TIME")
// The code of this macro 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(), insertEnd(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 xPrivate / yPrivate for cool mathematics.
private real xPrivate
private real yPrivate
private real xPrev
private real yPrev
// 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
// 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.
// Members which are monitored: ( DEBUG_MODE )
// ============================
//
static if not DEBUG_MODE then
real collision // Collision size in plane x / y.
real collisionZ // Collision size in z - axis.
else
// Check for collision < 0.
debug private real collisionDebug
method operator collision= takes real value returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError((value < 0), "Missile", "collision=", "collision", this, "Found collision below zero [" + R2S(value) + "]!")
endif
debug set collisionDebug = value
endmethod
method operator collision takes nothing returns real
debug return collisionDebug
endmethod
// Check for collisionZ < 0.
debug private real collisionZDebug
method operator collisionZ= takes real value returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError((value < 0), "Missile", "collisionZ=", "collisionZ", this, "found collisionZ below zero [" + R2S(value) + "]!")
endif
debug set collisionZDebug = value
endmethod
method operator collisionZ takes nothing returns real
debug return collisionZDebug
endmethod
endif
// Operator overloading:
// =====================
//
// Effect handle on a missile dummy. For multiple effect attaching, access "dummy" in your struct.
private effect sfx
private string path
method operator model= takes string modelFile returns nothing
if (sfx != null) then
call DestroyEffect(sfx)
endif
//
set path = modelFile
set sfx = AddSpecialEffectTarget(modelFile, dummy, ORIGIN)
endmethod
method operator model takes nothing returns string
return path
endmethod
real open
// Enables curved movement for your missile. PI/2 should be avoided.
method operator curve= takes real value returns nothing
set open = Tan(value)*origin.distance
endmethod
method operator curve takes nothing returns real
if (origin.distance == 0.) then
return 0.// It's a limit of sequence and crashes the thread. Intuitively chosen 0 as return value.
endif
return Atan(open/origin.distance)
endmethod
real height
// Enables arcing movement for your missile. PI/2 should be avoided.
method operator arc= takes real value returns nothing
set height = Tan(value)*origin.distance/4.
endmethod
method operator arc takes nothing returns real
if (origin.distance == 0.) then
return 0.// It's a limit of sequence and crashes the thread. Intuitively chosen 0 as return value.
endif
return Atan(4.*height/origin.distance)
endmethod
private real scaleValue
method operator scale= takes real value returns nothing
call SetUnitScale(dummy, value, 1., 1.)
set scaleValue = value
endmethod
method operator scale takes nothing returns real
return scaleValue
endmethod
// Methods:
// ========
//
method bounce takes nothing returns nothing
call origin.move(xPrivate, yPrivate, z)
set distance = 0.
endmethod
method deflect takes real tx, real ty returns nothing
local real a = 2.*Atan2(ty - yPrivate, tx - xPrivate) + bj_PI - angle
call impact.move(xPrivate + (origin.distance - distance)*Cos(a), yPrivate + (origin.distance - distance)*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
// Please do not pass in 0.
method flightTime2Speed takes real duration returns nothing
set speed = RMaxBJ(0.00000001, (origin.distance - distance)*Missile_TIMER_TIMEOUT/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
set allocated = false
call remove()// Throws an error for invalid nodes.
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
endmethod
// Runs in createEx.
//! textmacro MISSILE_RESET_ALL_MEMBERS
set path = null
set speed = 0.
set acceleration = 0.
set distance = 0.
set height = 0.
set turn = 0.
set open = 0.
set collision = 0.
set collisionZ = 0.
set collisionType = 0
set scaleValue = 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)
set allocated = true
//
//! 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 xPrivate = originX
set yPrivate = originY
set x = originX
set y = originY
set z = originZ
set angle = origin.angle
set dummy = missileDummy
set stackSize = 0
call SetUnitFlyHeight(missileDummy, originZ, 0.)
//
static if LIBRARY_ErrorMessage then
debug call ThrowError((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 Dummy.
//! 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 Dummy.
//! runtextmacro MISSILE_GET_DUMMY_FROM_LIBRARY()
endmethod
// Called from each active struct every Missile_TIMER_TIMEOUT seconds.
// In this method performance matters.
// • Locals used for faster multiple lookups.
// • Short variable names.
method move takes nothing returns nothing
local MissilePosition loc
local real a
local real d
local real s
local real v
local unit u
local real h
local real tx
local real ty
local real dex
loop
exitwhen (0 == this)
set loc = origin
set h = height
// Missile target.
set u = target
if (u != null) then
if (0 != GetUnitTypeId(u)) then
set tx = GetUnitX(u)
set ty = GetUnitY(u)
set a = Atan2(ty - yPrivate, tx - xPrivate)
//
// Uses a fictional hit box of GetUnitBodySize()*HIT_BOX for the z offset.
call origin.move(x, y, z)
call impact.move(tx, ty, GetUnitFlyHeight(u) + GetUnitBodySize(u)*Missile.HIT_BOX)
set distance = loc.distance - SquareRoot((tx - xPrivate)*(tx - xPrivate) + (ty - yPrivate)*(ty - yPrivate))
else
set a = loc.angle
set target = null
endif
else
set a = loc.angle
endif
set u = dummy
// Missile turn.
if (0 != turn) and (Cos(angle - a) < Cos(turn)) then
if (Sin(a - angle) >= 0) then
set angle = angle + turn
else
set angle = angle - turn
endif
else
set angle = a
endif
// Update position members.
set v = speed
set d = loc.distance
//call BJDebugMsg(R2S(d))
if (distance + v > d) then
set s = d + 0.000001
else
set s = distance + v
endif
set distance = s
//
set tx = xPrivate + v*Cos(angle)
set ty = yPrivate + v*Sin(angle)
set speed = v + acceleration
set xPrivate = tx
set yPrivate = ty
// Detect the collision type.
if (v < collision*Missile_COLLISION_ACCURACY_FACTOR) then
set collisionType = Missile_COLLISION_TYPE_CIRCLE
else
set collisionType = Missile_COLLISION_TYPE_RECTANGLE
endif
// Missile curve.
if (open != 0) then
set a = 4*open*s*(d - s)/(d*d)
set tx = tx + a*Cos(angle + 1.57)
set ty = ty + a*Sin(angle + 1.57)
call SetUnitFacing(u, (angle + Atan(-((4*open)*(2*s - d))/(d*d)))*bj_RADTODEG)
else
call SetUnitFacing(u, angle*bj_RADTODEG)
endif
// Missile arc.
call MoveLocation(LOC, tx, ty)
set terrainZ = GetLocationZ(LOC)
if (h != 0) or (loc.slope != 0) then
set z = loc.z - terrainZ + loc.slope*s
set dex = loc.alpha
if (h != 0) then
set z = z + (4*h*s*(d - s)/(d*d))
set dex = dex - Atan(((4*h)*(2*s - d))/(d*d))*bj_RADTODEG
endif
if (GetUnitTypeId(u) == Missile_DUMMY_UNIT_ID) then
call SetUnitAnimationByIndex(u, R2I(dex + 90.5))
endif
else
set z = loc.z - terrainZ
endif
// Missile position.
static if not LIBRARY_BoundSentinel and LIBRARY_WorldBounds then
if (tx > WorldBounds.maxX) or (tx < WorldBounds.minX) or (ty > WorldBounds.maxY) or (ty < WorldBounds.minY) then
// set wantDestroy = true
else
call SetUnitX(u, tx)
call SetUnitY(u, ty)
endif
else
call SetUnitX(u, tx)
call SetUnitY(u, ty)
endif
call SetUnitFlyHeight(u, z, 0.)
// Update missile x / y ( readonly )
set xPrev = x
set yPrev = y
set x = tx
set y = ty
// Check for destination reached.
if (s >= d) then
set recycle = true
endif
set this = next
endloop
set u = null
endmethod
// Widget collision API:
// =====================
//
// Runs automatically on widget collision.
method hitWidget takes widget w returns nothing
call SaveWidgetHandle(HASH, this, GetHandleId(w), w)
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
call RemoveSavedHandle(HASH, this, GetHandleId(w))
endmethod
// Flushes a missile's memory for collision. ( All widgets can be hit again )
method flushHitWidgets takes nothing returns nothing
call FlushChildHashtable(HASH, this)
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)
local integer dex
if (id == 0) then
return
endif
if HaveSavedInteger(HASH, this, id) then
set dex = LoadInteger(HASH, this, id)
else
set dex = stackSize
set stackSize = stackSize + 1
call SaveInteger(HASH, this, id, dex)
call SaveInteger(HASH, this, dex, id)
endif
call SaveReal(HASH, this, id, seconds)// Set time.
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)
set dex = dex - 1
else
call SaveReal(HASH, this, id, time)
endif
set dex = dex + 1
endloop
endmethod
// Widget collision code:
// ======================
//
// Transfer data to action functions.
private static Missile temp = 0
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 checkWidgetRectangle takes widget w, real height returns boolean
local real wx = GetWidgetX(w)
local real wy = GetWidgetY(w)
local real wz = Missile_GetLocZ(wx, wy) - terrainZ
local real dx = x - xPrev
local real dy = y - yPrev
local real s = (dx*(wx - xPrev) + dy*(wy - yPrev))/(dx*dx + dy*dy)
static if Missile_USE_COLLISION_Z_FILTER then
local real cz = collisionZ
else
local real cz = collision
endif //
if (s < 0) then
set s = 0.
elseif (s > 1) then
set s = 1.
endif
set dx = (xPrev + s*dx) - wx
set dy = (yPrev + s*dy) - wy
set s = dx*dx + dy*dy
//call BJDebugMsg(R2S(s))
if (s < bj_enumDestructableRadius) and (wz + height >= z - cz) and (wz <= z + cz) then
set bj_enumDestructableRadius = s
return true
endif
return false
endmethod
//
// 2.) Circular collision detection for all other missiles.
//
// Runs for destructables and items in a circle.
// Z - axis collision is precise.
private method checkWidgetCircle takes widget w, real height returns boolean
local real wx = GetWidgetX(w)
local real wy = GetWidgetY(w)
local real wz = Missile_GetLocZ(wx, wy) - terrainZ
local real d = (x - wx)*(x - wx) + (y - wy)*(y - wy)
static if Missile_USE_COLLISION_Z_FILTER then
local real cz = collisionZ
else
local real cz = collision
endif //
if (d < bj_enumDestructableRadius) and (wz + height >= z - cz) and (wz <= z + cz) then
set bj_enumDestructableRadius = d
return true
endif
return false
endmethod
//
// 3.) Action functions inside the widget enumeration thread.
//
// Runs for every enumerated destructable.
// • Searches the closest available destructables
// • Distance formula based on the Pythagorean theorem.
//
private static method enumDests takes nothing returns nothing
local destructable d = GetEnumDestructable()
if HaveSavedHandle(HASH, temp, GetHandleId(d)) then
set d = null
return
endif
if (circle) then
if temp.checkWidgetCircle(d, GetDestructableHeight(d)) then
set bj_destRandomCurrentPick = d
endif
// Runs rectangle collision check.
elseif temp.checkWidgetRectangle(d, GetDestructableHeight(d)) then
set bj_destRandomCurrentPick = d
endif
set d = null
endmethod
//
// Runs for every enumerated item.
// • Searches the closest available item.
// • Distance formula based on the Pythagorean theorem.
//
private static method enumItems takes nothing returns nothing
local item i = GetEnumItem()
if HaveSavedHandle(HASH, temp, GetHandleId(i)) then
set i = null
return
endif
if (circle) then
if temp.checkWidgetCircle(i, GetItemHeight(i)) then
set bj_itemRandomCurrentPick = i
endif
// Runs rectangle collision check.
elseif temp.checkWidgetRectangle(i, GetItemHeight(i)) then
set bj_itemRandomCurrentPick = i
endif
set i = null
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.
//
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
// Missile knows this unit already.
if HaveSavedBoolean(HASH, this, GetHandleId(u)) then
set u = null
return false
endif
//
set dx = x - xPrev
set dy = y - yPrev
set s = (dx*(GetUnitX(u) - xPrev) + dy*(GetUnitY(u)- yPrev))/(dx*dx + dy*dy)
if(s < 0) then
set s = 0.
elseif(s > 1) then
set s = 1.
endif
set is = IsUnitInRangeXY(u, xPrev + s*dx, yPrev + s*dy, collision)
set u = null
return is
endmethod
//
// 5.) Proper rect preparation.
//
// For rectangle.
private method prepareRectRectangle takes nothing returns nothing
local real x1 = xPrev
local real y1 = yPrev
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
else
if( 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
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 boolexpr expr returns nothing
set circle = (collisionType == Missile_COLLISION_TYPE_CIRCLE)
if (circle) then
call prepareRectCircle()
else
call prepareRectRectangle()
endif
//
set thistype.temp = this
set bj_destRandomCurrentPick = null
set bj_enumDestructableRadius = collision*collision + 1.
call EnumDestructablesInRect(RECT, expr, function thistype.enumDests)
endmethod
//
// Prepares item enumeration, then runs enumItems.
method checkItemCollision takes boolexpr expr returns nothing
set circle = (collisionType == Missile_COLLISION_TYPE_CIRCLE)
if (circle) then
call prepareRectCircle()
else
call prepareRectRectangle()
endif
//
set thistype.temp = this
set bj_itemRandomCurrentPick = null
set bj_enumDestructableRadius = collision*collision + 1.
call EnumItemsInRect(RECT, expr, function thistype.enumItems)
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", "typeid", 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)
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
return this
endmethod
method insertEnd takes thistype node returns thistype
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 0, "MissileStructure", "enqueue", "thistype", this, "Attempted To Enqueue On To Null List.")
debug call ThrowError(not isCollection, "MissileStructure", "enqueue", "thistype", this, "Attempted To Enqueue 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._prev = 0
else
set _last._next = node
set node._prev = _last
set _last = node
endif
set node._next = 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
endmethod
endmodule
// Boolean expressions per struct:
// ===============================
//
// Filter function for item enumeration. ( Runs only if a filter is declared in the struct )
private function MissileCreateItemFilter takes integer structId, code func returns nothing
set itemFilter[structId] = Filter(func)
endfunction
// Filter function for destructable enumeration. ( Runs only if a filter is declared in the struct )
private function MissileCreateDestructableFilter takes integer structId, code func returns nothing
set destFilter[structId] = Filter(func)
endfunction
// 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 missileStack[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 i == 0
if (instances[i] == 0) and (condition[i] != null) then
call TriggerRemoveCondition(CORE, condition[i])
set condition[i] = null
set active = active - 1
endif
set i = remove[i]
endloop
if (active == 0) then
call PauseTimer(TMR)
else
call TriggerEvaluate(CORE)
endif
endfunction
// Conditionally starts the timer.
private function StartPeriodic takes integer structId returns nothing
if (instances[structId] == 0) then
if (active == 0) then
call TimerStart(TMR, Missile_TIMER_TIMEOUT, true, function Fire)
endif
if (condition[structId] == null) then
set condition[structId] = TriggerAddCondition(CORE, expression[structId])
set active = active + 1
endif
endif
set instances[structId] = instances[structId] + 1
endfunction
// Conditionally stops the timer.
private function StopPeriodic takes integer structId returns nothing
set instances[structId] = instances[structId] - 1
if (instances[structId] == 0) then
set remove[structId] = remove[0]
set remove[0] = structId
endif
endfunction
// Module:
// =======
//
module MissileStruct
// Runs check during compile time.
static if DEBUG_MODE then
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
// Called from missileIterate. "P" to avoid code collision.
private static method missileTerminateP takes Missile node returns nothing
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)
endmethod
// Runs every Missile_TIMER_TIMEOUT for this struct.
private static method missileIterateP takes nothing returns boolean
local Missile this = missileStack[thistype.typeid].first
local Missile node
local real collideZ
local boolean b
local unit u
call this.move()// Moves all missiles of this collection.
loop
exitwhen (0 == this)
set node = this.next// The linked list should not lose the next node.
if (this.wantDestroy) then
static if thistype.onFinish.exists then
if thistype.onFinish(this) then
call thistype.missileTerminateP(this)
endif
else
call thistype.missileTerminateP(this)
endif
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(destFilter[thistype.typeid])
if (bj_destRandomCurrentPick != null) then
call SaveDestructableHandle(HASH, this, GetHandleId(bj_destRandomCurrentPick), bj_destRandomCurrentPick)
if thistype.onDestructable(this, bj_destRandomCurrentPick) then
call missileTerminateP(this)
endif
endif
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(itemFilter[thistype.typeid])
if (bj_itemRandomCurrentPick != null) then
call SaveItemHandle(HASH, this, GetHandleId(bj_itemRandomCurrentPick), bj_itemRandomCurrentPick)
if thistype.onItem(this, bj_itemRandomCurrentPick) then
call missileTerminateP(this)
endif
endif
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)
else
set this.recycle = false
endif
else
call thistype.missileTerminateP(this)
endif
endif
// Runs on terrian collision.
static if thistype.onTerrain.exists then
if (this.allocated) and (this.z < 0) 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
return false
endmethod
// Places the launcher in your struct.
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 missileStack[thistype.typeid].insertEnd(missile)
call StartPeriodic(thistype.typeid)
endmethod
// Runs onInit. Creates the collection, trigger condition and widget filters.
private static method onInit takes nothing returns nothing
call MissileCreateCollection(thistype.typeid)
call MissileCreateExpression(thistype.typeid, function thistype.missileIterateP)
static if thistype.onDestructableFilter.exists and thistype.onDestructable.exists then
call MissileCreateDestructableFilter(thistype.typeid, function thistype.onDestructableFilter)
endif
static if thistype.onItemFilter.exists and thistype.onItem.exists then
call MissileCreateItemFilter(thistype.typeid, function thistype.onItemFilter)
endif
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 slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns boolean
// Set a.
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.distance = SquareRoot((b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y))
if (a.distance != 0) then
set a.slope = (b.z - a.z)/a.distance
else// For distance == 0 the thread crashes!
set a.x = a.x + .0001
return false// Runs the thread again.
endif
set a.alpha = Atan(a.slope)*bj_RADTODEG
// Set b.
set b.angle = a.angle + bj_PI
set b.distance = a.distance
set b.slope = -a.slope
set b.alpha = -a.alpha
return true
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
loop
exitwhen math(a, b)
endloop
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
loop
exitwhen math(this, ref)
endloop
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=20
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e002'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=55
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.6
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
private constant real RECYCLE_DELAY = 4.0
//recycling, in order to show the effect correctly, must wait some time before
//removing the unit.
private timer recycler
private timer NOW
endglobals
private struct recyclebin extends array
unit u
real schedule
static recyclebin end=0
static recyclebin begin=0
static method Recycle takes nothing returns nothing
call RemoveUnit(.begin.u) //this unit is private, systems shouldn't mess with it.
set .begin.u=null
set .begin=recyclebin(integer(.begin)+1)
if(.begin==.end) then
set .begin=0
set .end=0
else
call TimerStart(recycler, .begin.schedule-TimerGetElapsed(NOW), false, function recyclebin.Recycle)
endif
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
set NOW=CreateTimer()
call TimerStart(NOW,43200,true,null)
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
private method onDestroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
if (recyclebin.end==MAX_INSTANCES) then
call TimerStart(recycler,0,false,function recyclebin.Recycle)
call ExplodeUnitBJ(this.dummy)
else
set recyclebin.end.u=this.dummy
set recyclebin.end.schedule=TimerGetElapsed(NOW)+RECYCLE_DELAY
set recyclebin.end= recyclebin( integer(recyclebin.end)+1)
if( recyclebin.end==1) then
call TimerStart(recycler, RECYCLE_DELAY, false, function recyclebin.Recycle)
endif
call SetUnitOwner(this.dummy,Player(15),false)
endif
set this.dummy=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library DestructableLib initializer Initialization
//* ============================================================================ *
//* Made by PitzerMike *
//* *
//* I made this to detect if a destructable is a tree or not. It works not only *
//* for the standard trees but also for custom destructables created with the *
//* object editor. It uses a footie as a dummy with the goul's harvest ability. *
//* The dummy ids can be changed though. I also added the IsDestructableDead *
//* function for completeness. *
//* ============================================================================ *
globals
private constant integer DUMMY_UNIT_ID = 'hfoo' // footman
private constant integer HARVEST_ID = 'Ahrl' // ghouls harvest
private constant player OWNING_PLAYER = Player(15)
private unit dummy = null
endglobals
function IsDestructableDead takes destructable dest returns boolean
return GetWidgetLife(dest) <= 0.405
endfunction
function IsDestructableTree takes destructable dest returns boolean
local boolean result = false
if (dest != null) then
call PauseUnit(dummy, false)
set result = IssueTargetOrder(dummy, "harvest", dest)
call PauseUnit(dummy, true) // stops order
endif
return result
endfunction
private function Initialization takes nothing returns nothing
set dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, 0.0, 0.0, 0.0)
call ShowUnit(dummy, false) // cannot enumerate
call UnitAddAbility(dummy, HARVEST_ID)
call UnitAddAbility(dummy, 'Aloc') // unselectable, invulnerable
call PauseUnit(dummy, true)
endfunction
endlibrary
//TESH.scrollpos=0
//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=40
//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
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//* This implementation is particularly useful because the boolexpr you pass to
//* the function will not be evaluated unless the unit is in collisional range
//* of the origin.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
//If you set this constant to false, the BOOLEXPR_TRUE and BOOLEXPR_FALSE
//boolean expressions will not be initialized in your map. Since they are
//no longer important, this is not a problem, but they are defined by
//default to maintain backwards compatibility.
private constant boolean USE_TRUEFALSE_BOOLEXPRS = true
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for storage purposes
private hashtable ht = InitHashtable()
//* Temporary references used internally
private boolean Flag = false
private group Temp = null
private group Area = CreateGroup()
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Temp)
set Flag = false
endif
call GroupAddUnit(Temp, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Temp = g
call ForGroup(Temp, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
local integer id = GetHandleId(g)
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(ht, 1, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(ht, 1, bid))
call RemoveSavedHandle(ht, 1, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
private function RangeFilter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function RangeFilter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(ht, 1, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(ht, 1, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function RangeFilter), filter)
call SaveBooleanExprHandle(ht, 1, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local unit u = null
call GroupClear(whichGroup)
call GroupEnumUnitsInRange(Area, x, y, GetRadius(radius), null)
loop
set u = FirstOfGroup(Area)
exitwhen u == null
call GroupRemoveUnit(Area, u)
if IsUnitInRangeXY(u, x, y, radius) then
call GroupAddUnit(whichGroup, u)
endif
endloop
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
static if USE_TRUEFALSE_BOOLEXPRS then
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endif
endfunction
endlibrary
//TESH.scrollpos=210
//TESH.alwaysfold=0
/*****************************************************************************
*
* Get Closest Widget
* by Spinnaker v2.0.0.0
*
* Special thanks to Troll-Brain
*
******************************************************************************
*
* This snippet contains several functions which returns closest
* widget to given coordinates and passed filter.
*
******************************************************************************
*
* Requirements:
* Snippet optionaly requires IsDestructableTree,
* enabling user to choose if enumerated should be all destructables
* or only tree-type ones, therefore gives option to get closest tree.
*
******************************************************************************
*
* Important:
* Performance drop depends on amount of objects currently on the map
* Snippet functions are designed to reduce the search time as much as
* posible, although it's recommended to use non specific functions
* wisely, especialy if your map contains large numbers of widgets.
*
******************************************************************************
*
* Functions:
* function GetClosestItem takes real x, real y, boolexpr filter returns item
* - Gets single item, nearest passed coordinates
* function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
* - Returns single item, closest to coordinates in given range
*
* function GetClosestDestructable takes real x, real y, boolean treeOnly, boolexpr filter returns destructable
* - Gets single destructable, nearest passed coordinates
* function GetClosestDestructableInRange takes real x, real y, real radius, boolean treeOnly, boolexpr filter returns destructable
* - Retrieves single destructable, closest to coordinates in given range
*
* function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* - Returns closest unit matching specific filter
* function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* - Gets nearest unit in range matching specific filter
* function GetClosestUnitInGroup takes real x, real y, group g returns unit
* - Retrieves closest unit in given group
*
* function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
* - Returns up to N nearest units in range of passed coordinates
* function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
* - Retrieves up to N closest units in passed group
*
*****************************************************************************/
library GetClosestWidget uses optional IsDestructableTree
private keyword Init
globals
private unit array Q
private real array V
private integer C=0
endglobals
struct ClosestWidget extends array
readonly static destructable cDest=null
readonly static item cItem=null
readonly static real distance=0
readonly static real cX=0
readonly static real cY=0
readonly static unit cUnit=null
static boolean cTree=false
static rect initRect=null
static method resetData takes real x, real y returns nothing
set cDest=null
set distance=100000
set cItem=null
set cUnit=null
set cX=x
set cY=y
endmethod
static method enumItems takes nothing returns nothing
local item i=GetEnumItem()
local real dx=GetWidgetX(i)-cX
local real dy=GetWidgetY(i)-cY
set dx = (dx*dx+dy*dy)/10000.
if dx<distance then
set cItem=i
set distance=dx
endif
set i =null
endmethod
static method enumDestructables takes nothing returns nothing
local destructable d=GetEnumDestructable()
local real dx
local real dy
static if LIBRARY_IsDestructableTree then
if cTree and not IsDestructableTree(d) then
return
endif
endif
set dx=GetWidgetX(d)-cX
set dy=GetWidgetY(d)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cDest=d
set distance=dx
endif
set d=null
endmethod
static method enumUnits takes nothing returns nothing
local unit u=GetEnumUnit()
local real dx=GetUnitX(u)-cX
local real dy=GetUnitY(u)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cUnit=u
set distance=dx
endif
set u=null
endmethod
static method sortUnits takes integer l, integer r returns nothing
local integer i=l
local integer j=r
local real v=V[(l+r)/2]
loop
loop
exitwhen V[i]>=v
set i=i+1
endloop
loop
exitwhen V[j]<=v
set j=j-1
endloop
if i<=j then
set V[0]=V[i]
set V[i]=V[j]
set V[j]=V[0]
set Q[0]=Q[i]
set Q[i]=Q[j]
set Q[j]=Q[0]
set i=i+1
set j=j-1
endif
exitwhen i>j
endloop
if l<j then
call sortUnits(l,j)
endif
if r>i then
call sortUnits(i,r)
endif
endmethod
static method saveGroup takes nothing returns nothing
local real dx
local real dy
set C=C+1
set Q[C]=GetEnumUnit()
set dx=GetUnitX(Q[C])-cX
set dy=GetUnitY(Q[C])-cY
set V[C]=(dx*dx+dy*dy)/10000.
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
set thistype.initRect=Rect(0,0,0,0)
endmethod
endmodule
function GetClosestItem takes real x, real y, boolexpr filter returns item
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call EnumItemsInRect(bj_mapInitialPlayableArea, filter, function ClosestWidget.enumItems)
exitwhen true
else
call SetRect(ClosestWidget.initRect,x-r,y-r,x+r,y+r)
call EnumItemsInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumItems)
exitwhen ClosestWidget.cItem!=null
endif
set r=2*r
endloop
return ClosestWidget.cItem
endfunction
function GetClosestItemInRange takes real x, real y, real radius, boolexpr filter returns item
call ClosestWidget.resetData(x,y)
if radius>=0 then
call SetRect(ClosestWidget.initRect,x-radius,y-radius,x+radius,y+radius)
call EnumItemsInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumItems)
endif
return ClosestWidget.cItem
endfunction
function GetClosestDestructable takes real x, real y, boolean treeOnly, boolexpr filter returns destructable
local real r=800.
call ClosestWidget.resetData(x,y)
set ClosestWidget.cTree=treeOnly
loop
if r>3200. then
call EnumDestructablesInRect(bj_mapInitialPlayableArea, filter, function ClosestWidget.enumDestructables)
exitwhen true
else
call SetRect(ClosestWidget.initRect,x-r,y-r,x+r,y+r)
call EnumDestructablesInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumDestructables)
exitwhen ClosestWidget.cDest!=null
endif
set r=2*r
endloop
return ClosestWidget.cDest
endfunction
function GetClosestDestructableInRange takes real x, real y, real radius, boolean treeOnly, boolexpr filter returns destructable
call ClosestWidget.resetData(x,y)
if radius>=0 then
set ClosestWidget.cTree=treeOnly
call SetRect(ClosestWidget.initRect,x-radius,y-radius,x+radius,y+radius)
call EnumDestructablesInRect(ClosestWidget.initRect, filter, function ClosestWidget.enumDestructables)
endif
return ClosestWidget.cDest
endfunction
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, filter)
exitwhen true
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, r, filter)
exitwhen FirstOfGroup(bj_lastCreatedGroup)!=null
endif
set r=2*r
endloop
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
endif
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call ClosestWidget.resetData(x,y)
call ForGroup(g, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(g, Q[-n+q])
set n =n-1
endloop
endif
set C=0
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
call ForGroup(sourceGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(destGroup, Q[-n+q])
set n=n-1
endloop
set C=0
endfunction
endlibrary
//TESH.scrollpos=65
//TESH.alwaysfold=0
/**************************************
*
* IsUnitChanneling
* v1.2.0.2
* By Magtheridon96
*
* - Tells whether a unit is channeling or not.
*
* Optional Requirements:
* ----------------------
*
* - RegisterPlayerUnitEvent by Magtheridon96
* - hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
* - UnitIndexer by Nestharus
* - hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
* - Table by Bribe
* - hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* Note:
* -----
*
* - All the requirements are completely optional.
* You can have none if you want. If all are present, the system
* will utilize UnitIndexer and RegisterPlayerUnitEvent.
* - This will not work with Table versions older than 3.1
*
* API:
* ----
*
* - function IsUnitChanneling takes unit whichUnit returns boolean
* - Tells whether a unit is channeling or not.
*
**************************************/
library IsUnitChanneling requires optional UnitIndexer, optional Table, optional RegisterPlayerUnitEvent
private struct OnChannel extends array
static if LIBRARY_UnitIndexer then
static boolean array channeling
elseif LIBRARY_Table then
private static key tb
static Table channeling = tb
else
static hashtable channeling = InitHashtable()
endif
private static method onStart takes nothing returns boolean
static if LIBRARY_UnitIndexer then
set channeling[GetUnitUserData(GetTriggerUnit())] = true
elseif LIBRARY_Table then
set channeling.boolean[GetHandleId(GetTriggerUnit())] = true
else
call SaveBoolean(channeling, GetHandleId(GetTriggerUnit()), 0, true)
endif
return false
endmethod
private static method onEnd takes nothing returns boolean
static if LIBRARY_UnitIndexer then
set channeling[GetUnitUserData(GetTriggerUnit())] = false
elseif LIBRARY_Table then
call channeling.boolean.remove(GetHandleId(GetTriggerUnit()))
else
call RemoveSavedBoolean(channeling, GetHandleId(GetTriggerUnit()), 0)
endif
return false
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onStart)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onEnd)
else
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
call TriggerAddCondition(t, Condition(function thistype.onStart))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function thistype.onEnd))
set t = null
endif
endmethod
endstruct
function IsUnitChanneling takes unit u returns boolean
static if LIBRARY_UnitIndexer then
return OnChannel.channeling[GetUnitUserData(u)]
elseif LIBRARY_Table then
return OnChannel.channeling.boolean[GetHandleId(u)]
else
return LoadBoolean(OnChannel.channeling, GetHandleId(u), 0)
endif
endfunction
endlibrary
//TESH.scrollpos=48
//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=30
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.0
* 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 = 260 + 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
//============================================================================
// 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
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.0.1
One map, one hashtable. Welcome to NewTable 3.1
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
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")
//! 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 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)
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb)
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, 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
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library SimError initializer init
//**************************************************************************************************
//*
//* SimError by Vexorian
//*
//* Mimic an interface error message
//* call SimError(ForPlayer, msg)
//* ForPlayer : The player to show the error
//* msg : The error
//*
//* To implement this function, copy this trigger and paste it in your map.
//* Unless of course you are actually reading the library from wc3c's scripts section, then just
//* paste the contents into some custom text trigger in your map.
//*
//**************************************************************************************************
//==================================================================================================
globals
private sound error
endglobals
//====================================================================================================
function SimError takes player ForPlayer, string msg returns nothing
set msg="\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00"+msg+"|r"
if (GetLocalPlayer() == ForPlayer) then
call ClearTextMessages()
call DisplayTimedTextToPlayer( ForPlayer, 0.52, 0.96, 2.00, msg )
call StartSound( error )
endif
endfunction
private function init takes nothing returns nothing
set error=CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
//call StartSound( error ) //apparently the bug in which you play a sound for the first time
//and it doesn't work is not there anymore in patch 1.22
endfunction
endlibrary
//TESH.scrollpos=60
//TESH.alwaysfold=0
//////////////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ SetUnitMaxState
//@=======================================================================================
//@ Credits:
//@---------------------------------------------------------------------------------------
//@ Written by:
//@ Earth-Fury
//@ Based on the work of:
//@ Blade.dk
//@
//@ If you use this system, please credit all of the people mentioned above in your map.
//@=======================================================================================
//@ SetUnitMaxState Readme
//@---------------------------------------------------------------------------------------
//@
//@ SetUnitMaxState() is a function origionally written by Blade.dk. It takes advantage of
//@ a bug which was introduced in one of the patches: Bonus life and mana abilitys will
//@ only ever add the bonus ammount for level 1. However, when removed, they will remove
//@ the ammount they should have added at their current level. This allows you to change a
//@ units maximum life and mana, without adding a perminent ability to the unit.
//@
//@---------------------------------------------------------------------------------------
//@ Adding SetUnitMaxState to your map:
//@
//@ Simply copy this library in to a trigger which has been converted to custom text.
//@ After that, you must copy over the abilitys. This is made easy by the ObjectMerger in
//@ JASS NewGen. Distributed with this system are //! external calls to the ObjectMerger.
//@ Simply copy both of them in to your map, save your map, close and reopen your map in
//@ the editor, and remove the external calls. (Or otherwise disable them. Removing the !
//@ after the // works.)
//@
//@---------------------------------------------------------------------------------------
//@ Using SetUnitMaxState:
//@
//@ nothing SetUnitMaxState(unit <target>, unitstate <state>, real <new value>)
//@
//@ This function changes <target>'s unitstate <state> to be eqal to <new value>. Note
//@ that the only valid unitstates this function will use are UNIT_STATE_MAX_MAN and
//@ UNIT_STATE_MAX_LIFE. Use SetUnitState() to change other unitstates.
//@
//@ nothing AddUnitMaxState(unit <target>, unitstate <state>, real <add value>)
//@
//@ This function adds <add value> to <target>'s <state> unitstate. <add value> can be
//@ less than 0, making this function reduce the specified unitstate. This function will
//@ only work with the unitstates UNIT_STATE_MAX_LIFE and UNIT_STATE_MAX_MANA.
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//////////////////////////////////////////////////////////////////////////////////////////
library SetUnitMaxState initializer Initialize
globals
//========================================================================================
// Configuration
//========================================================================================
// The rawcode of the life ability:
private constant integer MAX_STATE_LIFE_ABILITY = 'Zx01'
// The rawcode of the mana ability:
private constant integer MAX_STATE_MANA_ABILITY = 'Zx00'
// The maximum power of two the abilitys use:
private constant integer MAX_STATE_MAX_POWER = 8
endglobals
//========================================================================================
// System Code
//----------------------------------------------------------------------------------------
// Do not edit below this line unless you wish to change the way the system works.
//========================================================================================
globals
private integer array PowersOf2
endglobals
function SetUnitMaxState takes unit u, unitstate state, real newValue returns nothing
local integer stateAbility
local integer newVal = R2I(newValue)
local integer i = MAX_STATE_MAX_POWER
local integer offset
if state == UNIT_STATE_MAX_LIFE then
set stateAbility = MAX_STATE_LIFE_ABILITY
elseif state == UNIT_STATE_MAX_MANA then
set stateAbility = MAX_STATE_MANA_ABILITY
else
debug call BJDebugMsg("SetUnitMaxState Error: Invalid unitstate")
return
endif
set newVal = newVal - R2I(GetUnitState(u, state))
if newVal > 0 then
set offset = MAX_STATE_MAX_POWER + 3
elseif newVal < 0 then
set offset = 2
set newVal = -newVal
else
return
endif
loop
exitwhen newVal == 0 or i < 0
if newVal >= PowersOf2[i] then
call UnitAddAbility(u, stateAbility)
call SetUnitAbilityLevel(u, stateAbility, offset + i)
call UnitRemoveAbility(u, stateAbility)
set newVal = newVal - PowersOf2[i]
else
set i = i - 1
endif
endloop
endfunction
function AddUnitMaxState takes unit u, unitstate state, real addValue returns nothing
call SetUnitMaxState(u, state, GetUnitState(u, state) + addValue)
endfunction
private function Initialize takes nothing returns nothing
local integer i = 1
set PowersOf2[0] = 1
loop
set PowersOf2[i] = PowersOf2[i - 1] * 2
set i = i + 1
exitwhen i == MAX_STATE_MAX_POWER + 3
endloop
endfunction
endlibrary
//TESH.scrollpos=92
//TESH.alwaysfold=0
library DamageEvent/* v 1.2.0.0
**********************************************************************************
*
* Physical Damage Detection Engine
* --------------------------------
* By looking_for_help aka eey
*
* This system is able to detect, modify and deal damage. It can differentiate
* between physical and spell damage, as well as block, reduce or increase the
* applied damage to a desired value. You can also allocate damage events from
* running damage events.
*
**********************************************************************************
*
* Implementation
* --------------
* 1. Copy this trigger to your map. With the AddDamageHandler function you can
* add as many handlers as you like (compare the OnDamage scope).
* 2. Copy the two custom abilities to your map and make sure they have the
* correct ID in the globals variable block.
* 3. Go to the locust swarm ability and invert its damage return portion
* from (default) 0.75 to -0.75.
* 4. Remove the spell damage reduction ability from the spell damage reduction
* items you use (runed bracers). You can configure the resistance of this
* item in the globals block, modifying BRACERS_SPELL_DAMAGE_REDUCTION.
*
**********************************************************************************
*
* Important Notes
* ---------------
* 1. Life Drain does not work with this system, so you should use a triggered
* version of this spell if you want to use it.
* 2. Same for Finger of Death, if you want to use this spell bug free with this
* system, you should use a triggered version of it.
* 3. If you use damage modifiers by setting the damage amount variable, you have
* to use GetUnitLife, GetUnitMaxLife and SetUnitLife instead of GetWidgetLife,
* GetUnitState for UNIT_STATE_MAX_LIFE and SetWidgetLife in your whole map to
* ensure there occure no bugs.
* 4. The boolean USE_SPELL_RESISTANCE_AUTO_DETECT is only neccessary set to true
* if you want to use a customized damage table with spell damage resistance
* above 100%. If this is not the case, it should be set to false, as the
* system is faster then and still works correct.
* 5. As already mentioned you can't use the spell reduction ability when using this
* system (runed bracers and elunes grace). If you want to use them, you can
* trigger them by using the damage modifiers. Runed bracers is already considered
* in this system, so you don't have to code it.
*
**********************************************************************************
*
* System API
* ----------
* unit PDDS.target
* - In this global unit variable, the damaged unit is saved. Don't write any-
* thing into this variable, use it as readonly.
*
* unit PDDS.source
* - In this global unit variable, the damage source is saved. Don't write any-
* thing into this variable, use it as readonly.
*
* real PDDS.amount
* - In this global real variable, the amount of damage is saved. This amount
* can be modified by simply setting it to the desired amount that should be
* applied to the target. Set the amount to 0.0 to block the damage completly.
* Negative values will result in heal.
*
* integer PDDS.damageType
* - In this global integer variable, the damage type of the current damage is
* saved. Use it to differentiate between physical, spell and code damage. The
* variable can takes the values PHYSICAL == 0, SPELL == 1 and CODE == 2. Don't
* write anything into this variable, use it as readonly.
*
* function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount,
* boolean attack, boolean ranged, attacktype localAttackType,
* damagetype localDamageType, weapontype localWeaponType
* returns boolean
* - Use this function to allocate attacks from a running damage event. You can use
* it in exactly the same way as the standard native UnitDamageTarget. The function
* is designed recursive, so you can allocate as many attacks as you want. Do not
* use this function outside of a damage event.
*
* function GetUnitLife takes unit u returns real
* - Use this function instead of the GetWidgetLife native. It ensures that you
* get the correct health value, even when damage modifiers are applied. If
* you don't use damage modifiers at all, you don't need this function.
*
* function GetUnitMaxLife takes unit u returns real
* - Use this function instead of the GetUnitState(u, UNIT_STATE_MAX_LIFE) native.
* It will return the correct value, even when damage modifiers are applied. If
* you don't use damage modifiers at all, you don't need this function.
*
* function SetUnitLife takes unit u, real newLife returns nothing
* - Use this function instead of the SetWidgetLife(u, newLife) native if you use
* damage modifiers in your map. Same rules as for the GetUnitMaxLife and the
* GetUnitMaxLife functions.
*
* function AddDamageHandler takes code damageHandler returns nothing
* - Allows you to add a damage handler function to the system. Compare the
* OnDamage scope for an example usage.
*
* function RemoveDamageHandler takes code damageHandler returns nothing
* - Allows you to remove a damage handler function from the system dynamic-
* ally. If you added the same handler function multiple times to the sys-
* tem, this function will remove all of these equal functions.
*
*********************************************************************************/
globals
/*************************************************************************
* Customizable globals
*************************************************************************/
private constant integer DAMAGE_TYPE_DETECTOR = 0 //'pdd1'
private constant integer SET_MAX_LIFE = 0//'pdd2'
private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'
private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false
private constant real ETHEREAL_DAMAGE_FACTOR = 1.66
private constant real BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
private constant real TRIGGER_CLEANUP_PERIOD = 60.0
/*************************************************************************
* End of Customizable globals
*************************************************************************/
constant integer PHYSICAL = 0
constant integer SPELL = 1
constant integer CODE = 2
constant real UNIT_MIN_LIFE = 0.406
private constant attacktype ATTACK_TYPE_UNIVERSAL = ConvertAttackType(7)
private hashtable h
private real pureAmount
private trigger damageEvent
private trigger damageHandler
private trigger runAllocatedAttacks
private integer allocatedAttacks
private integer totalAllocs
private integer allocCounter
private real damageEventTrigger
endglobals
struct PDDS extends array
static unit source
static unit target
static real amount
static integer damageType
endstruct
/******************************************************************************
* User functions
******************************************************************************/
function AddDamageHandler takes code func returns nothing
local integer id = GetHandleId(Condition(func))
local integer index = 0
// Loop to manage equal damage handlers
loop
exitwhen ( LoadTriggerConditionHandle(h, id, index) == null )
set index = index + 1
endloop
// Store the desired damage handler function
call SaveTriggerConditionHandle(h, id, index, TriggerAddCondition(damageHandler, Filter(func)))
endfunction
function RemoveDamageHandler takes code func returns nothing
local integer id = GetHandleId(Condition(func))
local integer index = 0
// Loop through all equal damage handlers
loop
exitwhen ( LoadTriggerConditionHandle(h, id, index) == null )
call TriggerRemoveCondition(damageHandler, LoadTriggerConditionHandle(h, id, index))
set index = index + 1
endloop
// Clean things up
call FlushChildHashtable(h, id)
endfunction
function GetUnitLife takes unit u returns real
local boolean duringModification
local integer uId = GetHandleId(u)
local real health
local real storedHealth = LoadReal(h, uId, 2)
local real storedDamage = LoadReal(h, uId, 1)
// Check if the unit is being rescued from damage
set duringModification = GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0
if duringModification then
call UnitRemoveAbility(u, SET_MAX_LIFE)
endif
// Get the correct health value of the unit
if storedHealth != 0.0 then
set health = storedHealth - storedDamage
else
set health = GetWidgetLife(u) - storedDamage
endif
// Restore the rescue ability and return
if duringModification then
call UnitAddAbility(u, SET_MAX_LIFE)
endif
return health
endfunction
function GetUnitMaxLife takes unit u returns real
local real maxHealth
// Check if the unit is being rescued from damage
if GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(u, SET_MAX_LIFE)
set maxHealth = GetUnitState(u, UNIT_STATE_MAX_LIFE)
call UnitAddAbility(u, SET_MAX_LIFE)
return maxHealth
endif
// If the unit isn't being rescued, use the standard native
return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
local integer targetId
local integer oldTimerId
local real oldHealth
// Check if the unit is being rescued from damage
if GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, newLife)
call UnitAddAbility(u, SET_MAX_LIFE)
// Get the unit specific timer information
set targetId = GetHandleId(u)
set oldHealth = LoadReal(h, targetId, 0)
// Update the unit specific timer information
if oldHealth != 0.0 then
set oldTimerId = LoadInteger(h, targetId, 3)
call SaveReal(h, targetId, 2, newLife)
call SaveReal(h, targetId, 0, newLife)
call SaveReal(h, oldTimerId, 4, newLife)
endif
return
endif
// If the unit isn't being rescued, use the standard native
call SetWidgetLife(u, newLife)
endfunction
function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, /*
*/boolean ranged, attacktype localAttackType, damagetype localDamageType, /*
*/weapontype localWeaponType returns boolean
// Avoid infinite loop due to recursion
if PDDS.damageType == CODE then
return false
endif
// Avoid allocating attacks on units that are about to be killed
if ( localTarget == PDDS.target and GetUnitLife(localTarget) - PDDS.amount < UNIT_MIN_LIFE ) then
return false
endif
// Store all damage parameters determined by the user
set allocatedAttacks = allocatedAttacks + 1
call SaveUnitHandle(h, allocatedAttacks, 0, localSource)
call SaveUnitHandle(h, allocatedAttacks, 1, localTarget)
call SaveReal(h, allocatedAttacks, 2, localAmount)
call SaveBoolean(h, allocatedAttacks, 3, attack)
call SaveBoolean(h, allocatedAttacks, 4, ranged)
call SaveInteger(h, allocatedAttacks, 5, GetHandleId(localAttackType))
call SaveInteger(h, allocatedAttacks, 6, GetHandleId(localDamageType))
call SaveInteger(h, allocatedAttacks, 7, GetHandleId(localWeaponType))
// Return true if the damage was allocated
return true
endfunction
/******************************************************************************
* Sub functions
******************************************************************************/
private function DealFixDamage takes unit source, unit target, real pureAmount returns nothing
local real MAX_DAMAGE = 1000000.0
local real beforeHitpoints
local real newHitpoints
// Ensure the amount is positive
if pureAmount < 0 then
set pureAmount = -pureAmount
endif
// Save the targets hitpoints
set beforeHitpoints = GetWidgetLife(target)
set newHitpoints = beforeHitpoints - pureAmount
// Apply the desired, fixed amount
if newHitpoints >= UNIT_MIN_LIFE then
call SetUnitState(target, UNIT_STATE_LIFE, newHitpoints)
else
if ( IsUnitType(target, UNIT_TYPE_ETHEREAL) == false ) then
call SetWidgetLife(target, 1.0)
call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_UNIVERSAL, DAMAGE_TYPE_UNIVERSAL, null)
else
call UnitRemoveAbility(target, DAMAGE_TYPE_DETECTOR)
call SetWidgetLife(target, 1.0)
call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
endif
endif
endfunction
private function GetUnitSpellResistance takes unit u returns real
local real originalHP
local real beforeHP
local real afterHP
local real resistance
local real DUMMY_DAMAGE = 100
local real DUMMY_FACTOR = 0.01
// Deal spell damage in order to get the units resistance
call UnitRemoveAbility(u, DAMAGE_TYPE_DETECTOR)
set originalHP = GetWidgetLife(u)
call UnitAddAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, 20000.0)
set beforeHP = GetWidgetLife(u)
call DisableTrigger(damageEvent)
call UnitDamageTarget(PDDS.source, u, DUMMY_DAMAGE, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(damageEvent)
set afterHP = GetWidgetLife(u)
call UnitRemoveAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, originalHP)
call UnitAddAbility(u, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(u, true, DAMAGE_TYPE_DETECTOR)
// Calculate this resistance
set resistance = DUMMY_FACTOR*(beforeHP - afterHP)
// If the resistance was greater than 100%, return it
if resistance > 1.0 then
return resistance
else
return 1.0
endif
endfunction
private function UnitHasItemOfType takes unit u, integer itemId returns integer
local integer index = 0
local item indexItem
// Check if the target has a spell damage reducing item
loop
set indexItem = UnitItemInSlot(u, index)
if ( indexItem != null ) and ( GetItemTypeId(indexItem) == itemId ) then
return index + 1
endif
set index = index + 1
exitwhen index >= bj_MAX_INVENTORY
endloop
return 0
endfunction
/******************************************************************************
* Damage Engine
******************************************************************************/
private function AfterDamage takes nothing returns nothing
local timer time = GetExpiredTimer()
local integer id = GetHandleId(time)
local unit localSource = LoadUnitHandle(h, id, 0)
local unit localTarget = LoadUnitHandle(h, id, 1)
local real amount = LoadReal(h, id, 3)
local real originalHealth = LoadReal(h, id, 4)
// If the damage was modified, restore units health
if originalHealth != 0.0 then
call UnitRemoveAbility(localTarget, SET_MAX_LIFE)
call SetWidgetLife(localTarget, originalHealth)
endif
// Apply the desired amount of damage
if amount > 0.0 then
call DisableTrigger(damageEvent)
call DealFixDamage(localSource, localTarget, amount)
call EnableTrigger(damageEvent)
else
call SetWidgetLife(localTarget, originalHealth - amount)
endif
// Clean things up
call FlushChildHashtable(h, id)
call FlushChildHashtable(h, GetHandleId(localTarget))
call DestroyTimer(time)
set time = null
set localSource = null
set localTarget = null
endfunction
private function DamageEngine takes nothing returns nothing
local timer time
local integer id
local real rawAmount
local real originalHealth
local integer targetId
local integer oldTimerId
local real oldHealth
local real oldOriginalHealth
local real oldAmount
// Set damage variables
set rawAmount = GetEventDamage()
if rawAmount == 0.0 then
return
endif
set PDDS.source = GetEventDamageSource()
set PDDS.target = GetTriggerUnit()
// Determine the damage type
if rawAmount > 0.0 then
if PDDS.damageType != CODE then
set PDDS.damageType = PHYSICAL
endif
set PDDS.amount = rawAmount
else
if PDDS.damageType != CODE then
set PDDS.damageType = SPELL
endif
set PDDS.amount = -rawAmount
endif
// Register spell reduction above 100%
static if USE_SPELL_RESISTANCE_AUTO_DETECT then
set PDDS.amount = GetUnitSpellResistance(PDDS.target)*PDDS.amount
else
if ( IsUnitType(PDDS.target, UNIT_TYPE_ETHEREAL) and IsUnitEnemy(PDDS.target, GetOwningPlayer(PDDS.source)) and rawAmount < 0.0 ) then
set PDDS.amount = ETHEREAL_DAMAGE_FACTOR*PDDS.amount
endif
endif
// Register spell damage reducing items like runed bracers
if ( IsUnitType(PDDS.target, UNIT_TYPE_HERO) and UnitHasItemOfType(PDDS.target, SPELL_DAMAGE_REDUCTION_ITEM) > 0 ) and rawAmount < 0.0 then
set PDDS.amount = (1 - BRACERS_SPELL_DAMAGE_REDUCTION)*PDDS.amount
endif
// Save health and damage variables
if PDDS.damageType != CODE then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
endif
set pureAmount = PDDS.amount
set originalHealth = GetWidgetLife(PDDS.target)
set oldTimerId = 0
// Call damage handlers
set damageEventTrigger = 1.0
set damageEventTrigger = 0.0
// If the damage was modified, apply the rescue ability
if PDDS.amount != pureAmount then
call UnitAddAbility(PDDS.target, SET_MAX_LIFE)
call SetWidgetLife(PDDS.target, GetWidgetLife(PDDS.target) + pureAmount)
endif
// Check if a previous timer didn't yet expire
set targetId = GetHandleId(PDDS.target)
set oldHealth = LoadReal(h, targetId, 0)
// If this is the case, update the timer information
if oldHealth != 0.0 then
set oldTimerId = LoadInteger(h, targetId, 3)
set oldOriginalHealth = LoadReal(h, targetId, 2)
set oldAmount = LoadReal(h, targetId, 1)
set originalHealth = oldOriginalHealth - oldAmount
call SaveReal(h, oldTimerId, 4, oldOriginalHealth)
endif
// Call after damage event if damage was spell, modified, code or parallel
if ( rawAmount < 0.0 or pureAmount != PDDS.amount or oldTimerId != 0 or allocatedAttacks > 0 ) then
set time = CreateTimer()
set id = GetHandleId(time)
// Save handles for after damage event
call SaveUnitHandle(h, id, 0, PDDS.source)
call SaveUnitHandle(h, id, 1, PDDS.target)
call SaveReal(h, id, 2, pureAmount)
call SaveReal(h, id, 3, PDDS.amount)
call SaveReal(h, id, 4, originalHealth)
// Save this extra to manage parallel damage instances
call SaveReal(h, targetId, 0, GetWidgetLife(PDDS.target))
call SaveReal(h, targetId, 1, PDDS.amount)
call SaveReal(h, targetId, 2, originalHealth)
call SaveInteger(h, targetId, 3, id)
// Avoid healing of negative spell damage
if rawAmount < 0.0 then
call DisableTrigger(damageEvent)
call DealFixDamage(PDDS.source, PDDS.target, -rawAmount)
if ( originalHealth - PDDS.amount < UNIT_MIN_LIFE ) then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
call DealFixDamage(PDDS.source, PDDS.target, PDDS.amount)
endif
call EnableTrigger(damageEvent)
endif
// Guarantee unit exploding
if originalHealth - PDDS.amount < UNIT_MIN_LIFE then
if rawAmount > 0.0 then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
call SetWidgetLife(PDDS.target, UNIT_MIN_LIFE)
endif
endif
// Start the after damage event
call TimerStart(time, 0.0, false, function AfterDamage)
endif
// Handle allocated attacks from UnitDamageTargetEx
if totalAllocs == 0 then
set totalAllocs = allocatedAttacks
endif
if allocatedAttacks > 0 then
set allocatedAttacks = allocatedAttacks - 1
set allocCounter = allocCounter + 1
call TriggerEvaluate(runAllocatedAttacks)
endif
// Reset all required variables
set totalAllocs = 0
set allocCounter = -1
set PDDS.damageType = -1
endfunction
/******************************************************************************
* Initialization
******************************************************************************/
private function RestoreTriggers takes nothing returns nothing
local unit enumUnit = GetEnumUnit()
// Re-register units that are alive
if GetWidgetLife(enumUnit) >= UNIT_MIN_LIFE then
call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
endif
set enumUnit = null
endfunction
private function ClearMemory_Actions takes nothing returns nothing
local group g = CreateGroup()
local code c = function DamageEngine
// Reset the damage event
call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
call ResetTrigger(damageEvent)
call DestroyTrigger(damageEvent)
set damageEvent = null
// Rebuild it then
set damageEvent = CreateTrigger()
call TriggerAddCondition(damageEvent, Filter(c))
call ForGroup(g, function RestoreTriggers)
// Clean things up
call DestroyGroup(g)
set g = null
set c = null
endfunction
private function MapInit takes nothing returns nothing
local unit enumUnit = GetEnumUnit()
// Register units on map initialization
call UnitAddAbility(enumUnit, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(enumUnit, true, DAMAGE_TYPE_DETECTOR)
call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
set enumUnit = null
endfunction
private function UnitEntersMap takes nothing returns nothing
local unit triggerUnit = GetTriggerUnit()
// Register units that enter the map
if ( GetUnitAbilityLevel(triggerUnit, DAMAGE_TYPE_DETECTOR) < 1 ) then
call UnitAddAbility(triggerUnit, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(triggerUnit, true, DAMAGE_TYPE_DETECTOR)
call TriggerRegisterUnitEvent(damageEvent, triggerUnit, EVENT_UNIT_DAMAGED)
endif
set triggerUnit = null
endfunction
private function RunAllocatedAttacks takes nothing returns nothing
local integer localAllocAttacks = allocatedAttacks + 1
// Calculate the correct sequence of allocated attacks
set localAllocAttacks = localAllocAttacks - totalAllocs + 1 + 2*allocCounter
// Deal code damage if the unit isn't exploding
set PDDS.damageType = CODE
if GetUnitLife(LoadUnitHandle(h, localAllocAttacks, 1)) >= UNIT_MIN_LIFE then
call UnitDamageTarget(LoadUnitHandle(h, localAllocAttacks, 0), LoadUnitHandle(h, localAllocAttacks, 1), /*
*/LoadReal(h, localAllocAttacks, 2), LoadBoolean(h, localAllocAttacks, 3), /*
*/LoadBoolean(h, localAllocAttacks, 4), ConvertAttackType(LoadInteger(h, /*
*/localAllocAttacks, 5)), ConvertDamageType(LoadInteger(h, localAllocAttacks, 6)), /*
*/ConvertWeaponType(LoadInteger(h, localAllocAttacks, 7)))
else
call FlushChildHashtable(h, localAllocAttacks - 1)
endif
// Clean things up
call FlushChildHashtable(h, localAllocAttacks)
endfunction
private module Inits
private static method onInit takes nothing returns nothing
local group g = CreateGroup()
local region r = CreateRegion()
local trigger UnitEnters = CreateTrigger()
local trigger ClearMemory = CreateTrigger()
local code cDamageEngine = function DamageEngine
local code cUnitEnters = function UnitEntersMap
local code cClearMemory = function ClearMemory_Actions
local code cRunAllocatedAttacks = function RunAllocatedAttacks
// Initialize variables
set h = InitHashtable()
set damageEvent = CreateTrigger()
set damageHandler = CreateTrigger()
set PDDS.damageType = -1
set allocatedAttacks = 0
set runAllocatedAttacks = CreateTrigger()
set totalAllocs = 0
set allocCounter = -1
// Register units on map initialization
call TriggerRegisterVariableEvent(damageHandler, SCOPE_PRIVATE + "damageEventTrigger", EQUAL, 1.0)
call TriggerAddCondition(damageEvent, Filter(cDamageEngine))
call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
call ForGroup(g, function MapInit)
// Register units that enter the map
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(UnitEnters, r, null)
call TriggerAddCondition(UnitEnters, Filter(cUnitEnters))
// Register trigger for allocated attacks
call TriggerAddCondition(runAllocatedAttacks, Filter(cRunAllocatedAttacks))
// Clear memory leaks
call TriggerRegisterTimerEvent(ClearMemory, TRIGGER_CLEANUP_PERIOD, true)
call TriggerAddCondition(ClearMemory, Filter(cClearMemory))
// Clean things up
call DestroyGroup(g)
set UnitEnters = null
set ClearMemory = null
set cDamageEngine = null
set cUnitEnters = null
set cClearMemory = null
set cRunAllocatedAttacks = null
set g = null
set r = null
endmethod
endmodule
private struct Init extends array
implement Inits
endstruct
endlibrary
//TESH.scrollpos=145
//TESH.alwaysfold=0
library SimpleFunctions initializer init
globals
private location LOC = Location(0,0)
private group TempG = CreateGroup()
private unit dummy = null
endglobals
function TimerDestroy takes timer t returns nothing
call PauseTimer(t)
call DestroyTimer(t)
endfunction
function DisableAttack takes unit u, boolean b returns nothing
if b then
call UnitAddAbility(u, 'Abun')
else
call UnitRemoveAbility(u, 'Abun')
endif
endfunction
function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
function GetParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)//Standard parabola
endfunction
function GetUnitZ takes unit u, real height returns real
call MoveLocation(LOC, GetUnitX(u), GetUnitY(u))
return GetLocationZ(LOC) + height
endfunction
function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
function GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
function GetDistanceEx takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
endfunction
function GetDistanceFromUnit takes unit u, unit u2 returns real
return GetDistance(GetUnitX(u), GetUnitY(u), GetUnitX(u2), GetUnitY(u2))
endfunction
function SetUnitXY takes unit u, real xPoint, real yPoint, real speed, real angle returns nothing
call SetUnitX(u, xPoint + speed * Cos(angle))
call SetUnitY(u, yPoint + speed * Sin(angle))
endfunction
function GetAngleUnitFacing takes unit u returns real
return GetUnitFacing(u) * bj_DEGTORAD
endfunction
function MakeUnitFly takes unit u returns nothing
call UnitAddAbility(u, 'Arav')
call UnitRemoveAbility(u, 'Arav')
endfunction
function UnitAddLocust takes unit u returns nothing
call UnitAddAbility(u, 'Aloc')
endfunction
function UnitRemoveLocust takes unit u returns nothing
call ShowUnit(u, false)
call UnitRemoveAbility(u, 'Aloc')
call ShowUnit(u, true)
endfunction
function SpecialEffectXY takes string sfx, real x, real y returns nothing
call DestroyEffect(AddSpecialEffect(sfx, x, y))
endfunction
function SpecialEffectTarget takes unit u, string sfx, string attachment returns nothing
call DestroyEffect(AddSpecialEffectTarget(sfx, u, attachment))
endfunction
function IsUnitOrganic takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and UnitAlive(u)
endfunction
function LifeTaken takes unit u returns boolean
return (GetWidgetLife(u)) < (GetUnitState(u, UNIT_STATE_MAX_LIFE))
endfunction
function DamageEnemyUnits takes unit source, unit target, real d, attacktype atk, damagetype dmg, boolean air returns nothing
if IsUnitEnemy(source, GetOwningPlayer(target)) and UnitAlive(target) then
if air and IsUnitType(target, UNIT_TYPE_FLYING) then
call UnitDamageTarget(source, target, d, false, false, atk, dmg, null)
else
call UnitDamageTarget(source, target, d, false, false, atk, dmg, null)
endif
endif
endfunction
function DamageTarget takes unit source, unit target, real d, attacktype atk, damagetype dmg returns nothing
call UnitDamageTarget(source, target, d, false, false, atk, dmg, null)
endfunction
function AngularHeightDrop takes real height, real distance, real speed returns real
return (distance/height) * speed
endfunction
function NoEnemyUnits takes unit u, real radius returns boolean
local unit first
call GroupEnumUnitsInRange(TempG, GetUnitX(u), GetUnitY(u), radius, null)
loop
set first = FirstOfGroup(TempG)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, GetOwningPlayer(u)) or IsUnitType(first, UNIT_TYPE_STRUCTURE) then
return false
endif
call GroupRemoveUnit(TempG, first)
endloop
return true
endfunction
function CreateUnitAtRect takes integer unitID, rect r, player owner, real facing, boolean center returns unit
local unit u
local integer x
local integer y
if center then
set u = CreateUnit(owner, unitID, GetRectCenterX(r), GetRectCenterY(r), facing)
else
set x = GetRandomInt(R2I(GetRectMinX(r)), R2I(GetRectMaxX(r)))
set y = GetRandomInt(R2I(GetRectMinY(r)), R2I(GetRectMaxY(r)))
set u = CreateUnit(owner, unitID, I2R(x), I2R(y), facing)
endif
return u
endfunction
function GetUnitManaAmount takes unit u returns real
return GetUnitState(u, UNIT_STATE_MANA)
endfunction
/*************************************************
/ TEXTMACROS
**************************************************/
//! textmacro FOG_ADD takes X, Y, A
call GroupEnumUnitsInRange(TempG, $X$, $Y$, $A$, null)
loop
set first = FirstOfGroup(TempG)
exitwhen first==null
if UnitAlive(first) then
//! endtextmacro
//! textmacro FOG_REMOVE
endif
call GroupRemoveUnit(TempG, first)
endloop
//! endtextmacro
//! textmacro FOG_ADD_RECT
call GroupEnumUnitsInRect(TempG, bj_mapInitialPlayableArea, null)
loop
set first = FirstOfGroup(TempG)
exitwhen first==null
if UnitAlive(first) then
//! endtextmacro
//! textmacro FOG_REMOVE_RECT
endif
call GroupRemoveUnit(TempG, first)
endloop
//! endtextmacro
private function init takes nothing returns nothing
set dummy = CreateUnit(Player(15), 'ewsp', 0, 0, 0)
call UnitAddAbility(dummy, 'Aloc')
call ShowUnit(dummy, false)
call PauseUnit(dummy, true)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library PrintTextOnScreen
function PrintHint takes string title, string msg returns nothing
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "|cffffcc00" + title +"|r" + msg)
call StartSound(bj_questHintSound)
endfunction
function PrintSimpleText takes string msg returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 3.0, msg)
endfunction
function PrintSimpleTimedText takes string title, string msg, real duration returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, duration, title + msg)
endfunction
function PrintCinematicTimedText takes unit u, string msg, real duration returns nothing
if IsUnitType(u, UNIT_TYPE_HERO) then
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, duration, "|cffffcc00" + GetHeroProperName(u) + ":|r " + msg)
else
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, duration, "|cffffcc00" + GetUnitName(u) + ":|r " + msg)
endif
endfunction
endlibrary
//TESH.scrollpos=29
//TESH.alwaysfold=0
/******************************************************
DummyInstantCaster v1.1
by mckill2009
*******************************************************
NOTE:
This ONLY supports instant cast, NOT CHANNELING
*******************************************************/
library DummyInstantCaster
globals
private constant integer DUMMY_ID = 'e002'
endglobals
struct DC
readonly static unit dummy
private static method remove takes nothing returns boolean
if GetTriggerUnit()==dummy then
call SetUnitOwner(dummy, Player(15), false)
call UnitRemoveAbility(dummy, GetSpellAbilityId())
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function thistype.remove))
set dummy = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call SetUnitInvulnerable(dummy, true)
call UnitAddAbility(dummy, 'Aloc')
call UnitAddAbility(dummy, 'Arav')
call UnitRemoveAbility(dummy, 'Amov')
call ShowUnit(dummy, false)
set t = null
endmethod
/***************************************************
* API
****************************************************/
static method setDummyFrom takes player owner, real x, real y, integer spellID, integer level returns nothing
call SetUnitOwner(dummy, owner, false)
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call UnitAddAbility(dummy, spellID)
call SetUnitAbilityLevel(dummy, spellID, level)
endmethod
static method cast takes integer spellID, integer orderID returns nothing
call IssueImmediateOrderById(DC.dummy, orderID)
//call UnitRemoveAbility(DC.dummy, spellID)
endmethod
static method castToUnit takes unit target, integer spellID, integer orderID returns nothing
call IssueTargetOrderById(DC.dummy, orderID, target)
//call UnitRemoveAbility(DC.dummy, spellID)
endmethod
static method castToPoint takes real x, real y, integer spellID, integer orderID returns nothing
call IssuePointOrderById(DC.dummy, orderID, x, y)
//call UnitRemoveAbility(DC.dummy, spellID)
endmethod
endstruct
endlibrary
//TESH.scrollpos=33
//TESH.alwaysfold=0
library MUIDummyCasters /* v5.0 */ initializer Init /*
************************************************************************************
by mckill2009
************************************************************************************
Creates only 1 dummy for instant spells like firebolt, carrion swarm, banish, etc
Supports channel spells by creating another dummy to channel the spell like flamestrike, starfall, blizzard, etc
You may set the point where the dummy is created and cast to a target or location where you can see the projectile is traveling
************************************************************************************
*/ uses /*
*/ Table /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
************************************************************************************
Installation
- Copy ALL that is in the "MUIDummyCasters" folder to your map.
- Copy the Table library to your map.
- Replace the rawID of the DUMMY_CASTER if needed, you may view it via pressing CTRL+D in the object editor.
************************************************************************************
SYSTEM API:
static method operator owner= takes player pl returns nothing
static method operator targetUnit= takes unit target returns nothing
static method operator []= takes integer abilityID, integer orderID returns nothing
static method operator abilityLevel= takes integer level returns nothing
static method operator xDummyLoc= takes real xDummy returns nothing
static method operator yDummyLoc= takes real yDummy returns nothing
static method operator xTargetLoc= takes real xTarget returns nothing
static method operator yTargetLoc= takes real yTarget returns nothing
static method cast takes integer castType returns nothing
API CONSTANTS:
static constant integer targetNone = 0
static constant integer targetObject = 1
static constant integer targetPoint = 2
************************************************************************************/
globals
/*
* Change the DUMMY_CASTER raw code if needed
*/
private constant integer DUMMY_CASTER = 'e002'
/*
* Constant globals, never touch these!
*/
private Table c
private constant player OWNER = Player(15)
private integer index = 0
private integer maxIndex = 0
private integer ability_id = 0
private integer order_id = 0
private integer cur_level = 0
private real xLoc = 0
private real yLoc = 0
private real xTar = 0
private real yTar = 0
private unit tar = null
private player owning_player = null
private unit array dummyCaster
/*
* TEST is for testing duh!, set to true to display how many
* dummies are created, set to false to disable it
*/
private constant boolean TEST = true
endglobals
private function ResetVariables takes nothing returns nothing
set ability_id = 0
set order_id = 0
set cur_level = 0
set xLoc = 0
set yLoc = 0
set xTar = 0
set yTar = 0
set tar = null
set owning_player = null
endfunction
private function RefreshDummyUnit takes unit d returns nothing
set dummyCaster[index] = d
set index = index - 1
call SetUnitOwner(d, OWNER, false)
call UnitRemoveAbility(d, c[GetHandleId(d)])
call ResetVariables()
endfunction
private function GetDummyUnit takes nothing returns unit
set index = index + 1
if dummyCaster[index]==null then
set dummyCaster[index] = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0)
call UnitRemoveAbility(dummyCaster[index],'Amov')
static if TEST then
call BJDebugMsg(I2S(index) + " Dummies created (MUIDummyCasters)")
endif
endif
return dummyCaster[index]
endfunction
private function DummyCasting takes integer castType returns nothing
local unit dum = GetDummyUnit()
call UnitAddAbility(dum, ability_id)
call SetUnitAbilityLevel(dum, ability_id, cur_level)
call SetUnitOwner(dum, owning_player, false)
call SetUnitX(dum, xLoc)
call SetUnitY(dum, yLoc)
//saving the ability id before casting is a must
set c[GetHandleId(dum)] = ability_id
if castType==0 then //no target
call IssueImmediateOrderById(dum, order_id)
elseif castType==1 then //point target
call IssuePointOrderById(dum, order_id, xTar, yTar)
elseif castType==2 then //unit target
call IssueTargetOrderById(dum, order_id, tar)
endif
set dum = null
endfunction
private function CastEnd takes nothing returns boolean
/*
* Checks the caster if he belongs in this system instead of
* checking the unit type (which bugs)
*/
if c.has(GetHandleId(GetTriggerUnit())) then
call RefreshDummyUnit(GetTriggerUnit())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
/*
* Preloading only to avoid first time lag
*/
local unit u = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function CastEnd))
set c = Table.create()
call KillUnit(u)
call RemoveUnit(u)
set t = null
set u = null
endfunction
/********************************************************
SYSTEM API:
*********************************************************/
struct MDC
static constant integer targetNone = 0
static constant integer targetObject = 1
static constant integer targetPoint = 2
static method operator owner= takes player pl returns nothing
set owning_player = pl
endmethod
static method operator targetUnit= takes unit target returns nothing
set tar = target
endmethod
static method operator []= takes integer abilityID, integer orderID returns nothing
set ability_id = abilityID
set order_id = orderID
endmethod
static method operator abilityLevel= takes integer level returns nothing
set cur_level = level
endmethod
static method operator xDummyLoc= takes real xDummy returns nothing
set xLoc = xDummy
endmethod
static method operator yDummyLoc= takes real yDummy returns nothing
set yLoc = yDummy
endmethod
static method operator xTargetLoc= takes real xTarget returns nothing
set xTar = xTarget
endmethod
static method operator yTargetLoc= takes real yTarget returns nothing
set yTar = yTarget
endmethod
static method cast takes integer castType returns nothing
if castType==0 then
call DummyCasting(targetNone) //cast none
elseif castType==1 then
call DummyCasting(targetPoint) //cast point
elseif castType==2 then
call DummyCasting(targetObject) //cast target
else
debug call BJDebugMsg("MUIDummyCasters ERROR: Unable to identify cast type.")
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=9
//TESH.alwaysfold=0
library MUIDummyCasters /* v4.4a */ initializer Init /*
*************************************************************************************
*
* by mckill2009
*
*************************************************************************************
*
* Creates only 1 dummy for instant spells like firebolt, carrion swarm, banish, etc
* Supports channel spells by creating another dummy to channel the spell like flamestrike, starfall, blizzard, etc
* You may control the point where the dummy is created and cast to a target or location where you can see the projectile is traveling
*
*************************************************************************************
*
* Credits
*
* Bribe
* -----------------------
* For info about the 'Amov' ability remove
*
* Nesthaurus
* -----------------------
* For his Comment header
*
*************************************************************************************
*
* */ uses /*
*
* */ Table /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
************************************************************************************
*
* Installation
* - Copy ALL that is in the "MUIDummyCasters" folder to your map.
* - Copy the Table library to your map.
* - Replace the rawID of the DUMMY_CASTER if needed, you may view it via pressing CTRL+D in the object editor.
*
************************************************************************************
*
* GLOBALS
*
*/
globals
/*
* Change the DUMMY_CASTER raw code if needed
*/
private constant integer DUMMY_CASTER = 'e002'
/*
* Constant globals, never touch these!
*/
private Table c
private constant player OWNER = Player(15)
private group DummyTake = CreateGroup()
private unit Dummy = null
/*
* TEST is for testing duh!, set to true to display how many
* dummies are created, set to false to disable it
*/
private constant boolean TEST = false
private integer DummyCount = 0
endglobals
/*
************************************************************************************
*
* Functions
*
* function MDCCast takes player owningPlayer, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
* function MDCCastToPoint takes player owningPlayer, real xDummy, real yDummy, real xTarget, real yTarget, integer abilityID, integer orderID, integer level returns nothing
* function MDCCastToTarget takes player owningPlayer, unit target, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
*
* Function Parameter Description
* player owningPlayer
* - the owner of the casting unit
*
* unit target (unit)
* - the spelltarget, used only by MDCCastToTarget
*
* real xDummy and yDummy
* - the point/coordinate where the dummy will be placed
*
* real xTarget and yTarget
* - the point/coordinate where the dummy cast the spell
* - used only by MDCCastToPoint
*
* integer abilityID
* - the raw code of of the ability being cast by the dummt caster
*
* integer orderID
* - the orderID of the abilityID
*
* integer level
* - the level of the abilityID
*
*
*/
//! textmacro ISSUE takes order
local unit dum = GetDummyUnit()
call UnitAddAbility(dum, abilityID)
call SetUnitAbilityLevel(dum, abilityID, level)
call SetUnitOwner(dum, owningPlayer, false)
call SetUnitX(dum, xDummy)
call SetUnitY(dum, yDummy)
call Issue$order$
set c[GetHandleId(dum)] = abilityID
/*
* This is just a fail safe, in case the target is invulnerable, invisible or cannot be casted
*/
if GetUnitCurrentOrder(dum)==0 then
call RefreshDummyUnit(dum)
endif
set dum = null
//! endtextmacro
private function RefreshDummyUnit takes unit d returns nothing
call SetUnitOwner(d, OWNER, false)
call UnitRemoveAbility(d, c[GetHandleId(d)])
call GroupAddUnit(DummyTake, d)
endfunction
private function GetDummyUnit takes nothing returns unit
if FirstOfGroup(DummyTake)==null then
set Dummy = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0)
call UnitRemoveAbility(Dummy,'Amov')
static if TEST then
set DummyCount = DummyCount + 1
call BJDebugMsg("Dummies Created = "+I2S(DummyCount))
endif
else
/*
* if group is not empty then it will pick the first dummy in the group
* I've used this above indexing to be.....different XD...
*/
set Dummy = FirstOfGroup(DummyTake)
call GroupRemoveUnit(DummyTake, Dummy)
endif
return Dummy
endfunction
private function CastEnd takes nothing returns boolean
/*
* Checks the caster if he belongs in this system instead of
* checking the unit type (which bugs)
*/
if c.has(GetHandleId(GetTriggerUnit())) then
call RefreshDummyUnit(GetTriggerUnit())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
/*
* Preloading only to avoid first time lag
*/
local unit u = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function CastEnd))
set c = Table.create()
call KillUnit(u)
call RemoveUnit(u)
set t = null
set u = null
endfunction
/*
* This the main core system, 3 API you may use
*/
function MDCCast takes player owningPlayer, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
//! runtextmacro ISSUE("ImmediateOrderById(dum, orderID)")
endfunction
function MDCCastToPoint takes player owningPlayer, real xDummy, real yDummy, real xTarget, real yTarget, integer abilityID, integer orderID, integer level returns nothing
//! runtextmacro ISSUE("PointOrderById(dum, orderID, xTarget, yTarget)")
endfunction
function MDCCastToTarget takes player owningPlayer, unit target, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
//! runtextmacro ISSUE("TargetOrderById(dum, orderID, target)")
endfunction
endlibrary
//TESH.scrollpos=153
//TESH.alwaysfold=0
/*********************************************************************
BaseSpawn v1.0
by mckill2009
**********************************************************************
INTRODUCTION:
Allows you to spawn random units from a base, if the spawned unit dies, it will respawn another unit randomly
as long as the base still stands.
**********************************************************************
REQUIRES:
- Table by Bribe
- T32 by Jesus4Lyf
- GroupUtils by Rising_Dusk
**********************************************************************
API:
static method register takes unit base returns thistype
method addUnit takes unit spawnableUnit, string sfx, real delay returns nothing
method releaseGroup takes boolean b returns nothing
method isGroupEmpty takes nothing returns boolean
method stopSpawning takes nothing returns nothing
**********************************************************************/
library BaseSpawn uses Table, T32, GroupUtils
globals
private Table chkBase
private Table uAdd
private TableArray sp
private integer count
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
private function ErrorMessage takes string msg returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10.0, msg)
endfunction
private function CheckUnitsInGroup takes nothing returns nothing
if UnitAlive(GetEnumUnit()) then
set count = count + 1
endif
endfunction
private struct SpawnDelay
private unit base
private integer baseID
private integer deadUnitID
private integer unitTypeID
private real xSpawn
private real ySpawn
private real facing
private real delay
private real delayEx
private string sfx
private player pl
private BaseSpawn data
private method destroy takes nothing returns nothing
call uAdd.remove(.deadUnitID)
set .base = null
set .pl = null
call .stopPeriodic()
call .deallocate() //this is NOT the base
endmethod
private method periodic takes nothing returns nothing
local unit spawnUnit
local integer newID
if UnitAlive(.base) and chkBase.has(.baseID) then
set .delay = .delay - 0.03125
if .delay < 0 then
//spawn here
set spawnUnit = CreateUnit(.pl, .unitTypeID, .xSpawn, .ySpawn, .facing)
set newID = GetHandleId(spawnUnit)
call DestroyEffect(AddSpecialEffectTarget(.sfx, spawnUnit, "origin"))
set uAdd[newID] = .data
set sp[1].unit[newID] = .base
set sp[2].real[newID] = .xSpawn
set sp[3].real[newID] = .ySpawn
set sp[4].real[newID] = .facing
set sp[5].real[newID] = .delayEx
set sp[6].string[newID] = .sfx
set sp[7][newID] = GetUnitTypeId(spawnUnit)
set sp[8].player[newID] = GetOwningPlayer(spawnUnit)
call GroupAddUnit(.data.grp, spawnUnit)
set spawnUnit = null
call .destroy()
endif
else
call .destroy()
endif
endmethod
implement T32x
static method run takes BaseSpawn dat, unit deadUnit returns nothing
local thistype this
local unit b = sp[1].unit[GetHandleId(deadUnit)] //this is the base
if UnitAlive(b) and chkBase.has(GetHandleId(b)) then
set this = allocate()
set .data = dat //the index 'THIS' in BaseSpawn
set .base = b
set .baseID = GetHandleId(b)
set .deadUnitID = GetHandleId(deadUnit)
set .xSpawn = sp[2].real[.deadUnitID]
set .ySpawn = sp[3].real[.deadUnitID]
set .facing = sp[4].real[.deadUnitID]
set .delay = sp[5].real[.deadUnitID]
set .sfx = sp[6].string[.deadUnitID]
set .unitTypeID = sp[7][.deadUnitID]
set .pl = sp[8].player[.deadUnitID]
call GroupRemoveUnit(.data.grp, deadUnit)
set .delayEx = .delay
call .startPeriodic()
endif
set b = null
endmethod
endstruct
struct BaseSpawn
private unit base
private integer baseID
private boolean relGrp
public group grp
private static BaseSpawn data
private method destroy takes nothing returns nothing
call ReleaseGroup(this.grp)
set .base = null
set .grp = null
call .deallocate()
endmethod
private static method removeID takes nothing returns nothing
call uAdd.remove(GetHandleId(GetEnumUnit()))
endmethod
private static method death takes nothing returns boolean
local integer ID = GetHandleId(GetTriggerUnit())
if uAdd.has(ID) then //the killed and latter spawned unit
call SpawnDelay.run(uAdd[ID], GetTriggerUnit())
elseif chkBase.has(ID) then //the base
set data = chkBase[ID]
call ForGroup(data.grp, function thistype.removeID)
if data.relGrp then
call data.destroy()
endif
call chkBase.remove(ID)
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(function thistype.death))
set chkBase = Table.create()
set uAdd = Table.create()
set sp = TableArray[0x2000]
set t = null
endmethod
/***********************************************************
* API
************************************************************/
static method register takes unit base returns thistype
local thistype this
local integer bID = GetHandleId(base)
if chkBase.has(bID) then
call ErrorMessage("thistype.register ERROR: "+ GetUnitName(base) +" is registered twice!")
return -1
else
set this = allocate()
set .base = base
set .baseID = bID
set .grp = NewGroup()
set .relGrp = false
call GroupAddUnit(.grp, .base)
set chkBase[bID] = this
endif
return this
endmethod
method addUnit takes unit spawnableUnit, string sfx, real delay returns nothing
local integer spawnID = GetHandleId(spawnableUnit)
if chkBase.has(GetHandleId(spawnableUnit)) then
call ErrorMessage("thistype.addUnit ERROR: "+ GetUnitName(spawnableUnit) +" is a base!")
return
endif
if uAdd.has(spawnID) then
call ErrorMessage("thistype.register ERROR: "+ GetUnitName(spawnableUnit) +" is registered twice!")
else
set uAdd[spawnID] = this
set sp[1].unit[spawnID] = .base
set sp[2].real[spawnID] = GetUnitX(spawnableUnit)
set sp[3].real[spawnID] = GetUnitY(spawnableUnit)
set sp[4].real[spawnID] = GetUnitFacing(spawnableUnit)
set sp[5].real[spawnID] = delay
set sp[6].string[spawnID] = sfx
set sp[7][spawnID] = GetUnitTypeId(spawnableUnit)
set sp[8].player[spawnID] = GetOwningPlayer(spawnableUnit)
call GroupAddUnit(.grp, spawnableUnit)
endif
endmethod
method releaseGroup takes boolean b returns nothing
set .relGrp = b
endmethod
method isGroupEmpty takes nothing returns boolean
set count = 0
call ForGroup(this.grp, function CheckUnitsInGroup)
//call BJDebugMsg(I2S(count))
if count==0 then
if not UnitAlive(.base) then
call .destroy()
return true
endif
endif
return false
endmethod
method stopSpawning takes nothing returns nothing
if chkBase.has(.baseID) then
call ForGroup(this.grp, function thistype.removeID)
call chkBase.remove(.baseID)
call this.destroy()
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=83
//TESH.alwaysfold=0
/*********************************************************************
AutoCastSystem v1.1
by mckill2009
**********************************************************************
INSTALLATION:
Copy ALL the required libraries and the AutoCastSystem trigger to your trigger editor
OR
Copy ALL the triggers from the AutoCastSystem folder
**********************************************************************
Struct API: See the tutorial/demo on how this works
static method register takes unit caster returns thistype
method spellTypeUnitTarget takes code func, integer spellOrderID returns nothing
method spellTypePointTarget takes code func, integer spellOrderID returns nothing
method spellTypeNoTarget takes code func, integer spellOrderID returns nothing
method removeSpellOrderUnit takes integer spellOrderID returns nothing
method removeSpellOrderPoint takes integer spellOrderID returns nothing
method removeSpellOrderNone takes integer spellOrderID returns nothing
method launch takes real interval returns nothing
method removeWhenDead takes boolean b returns nothing
method remove takes nothing returns nothing
Struct globals
CS.caster
CS.target
CS.SpellTargetX
CS.SpellTargetY
**********************************************************************
CREDITS:
TimerUtils by Vexorian http://www.wc3c.net/showthread.php?t=101322
Table by Bribe http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
IsUnitChanneling by Magtheridon96 http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isunitchanneling-211254/
**********************************************************************/
library AutoCastSystem uses TimerUtils, Table, IsUnitChanneling
globals
/***********************************************
* NON-CONFIGURABLES
************************************************/
private TableArray trig
private TableArray oID
private Table chk
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
struct CS extends array
static unit caster
static unit target
static real SpellTargetX
static real SpellTargetY
private static method onInit takes nothing returns nothing
set trig = TableArray[0x2000]
set oID = TableArray[0x2000]
set chk = Table.create()
endmethod
endstruct
struct ACS
private unit caster
private integer indexUnitMax
private integer indexPointMax
private integer indexNoneMax
private integer spellType
private boolean isOn
private boolean isOnEx
private static integer pointIndex = 10000
private static integer noneIndex = 20000
private method destroy takes nothing returns nothing
call chk.remove(GetHandleId(.caster))
set .caster = null
call .deallocate()
endmethod
static method period takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local integer randomizer
local integer i
if .isOn then
if UnitAlive(.caster) then
set CS.caster = .caster
if not IsUnitChanneling(.caster) then
set randomizer = GetRandomInt(1, 3)
if randomizer==1 then //Unit target
set i = GetRandomInt(1, .indexUnitMax)
if TriggerEvaluate(trig[this].trigger[i]) then
call IssueTargetOrderById(.caster, oID[this][i], CS.target)
endif
elseif randomizer==2 then //Point target
set i = GetRandomInt(pointIndex, .indexPointMax)
if TriggerEvaluate(trig[this].trigger[i]) then
call IssuePointOrderById(.caster, oID[this][i], CS.SpellTargetX, CS.SpellTargetY)
endif
elseif randomizer==3 then //No target
set i = GetRandomInt(noneIndex, .indexNoneMax)
if TriggerEvaluate(trig[this].trigger[i]) then
call IssueImmediateOrderById(.caster, oID[this][i])
endif
endif
endif
elseif .isOnEx then
call ReleaseTimer(t)
call .destroy()
endif
else
call ReleaseTimer(t)
call .destroy()
endif
set t = null
endmethod
static method register takes unit caster returns thistype
local thistype this
if chk.has(GetHandleId(caster)) then
if IsUnitType(caster, UNIT_TYPE_HERO) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "thistype.register ERROR: "+GetHeroProperName(caster)+" is already registered!")
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "thistype.register ERROR: "+GetUnitName(caster)+" is already registered!")
endif
return 0
else
set this = allocate()
set .caster = caster
set .isOn = true
set .isOnEx = false
set .indexUnitMax = 0
set .indexPointMax = pointIndex
set .indexNoneMax = noneIndex
set chk[GetHandleId(caster)] = this
endif
return this
endmethod
method spellTypeUnitTarget takes code func, integer spellOrderID returns nothing
set .indexUnitMax = .indexUnitMax + 1
set oID[this][.indexUnitMax] = spellOrderID
set trig[this].trigger[.indexUnitMax] = CreateTrigger()
call TriggerAddCondition(trig[this].trigger[.indexUnitMax], Condition(func))
endmethod
method spellTypePointTarget takes code func, integer spellOrderID returns nothing
set .indexPointMax = .indexPointMax + 1
set oID[this][.indexPointMax] = spellOrderID
set trig[this].trigger[.indexPointMax] = CreateTrigger()
call TriggerAddCondition(trig[this].trigger[.indexPointMax], Condition(func))
endmethod
method spellTypeNoTarget takes code func, integer spellOrderID returns nothing
set .indexNoneMax = .indexNoneMax + 1
set oID[this][.indexNoneMax] = spellOrderID
set trig[this].trigger[.indexNoneMax] = CreateTrigger()
call TriggerAddCondition(trig[this].trigger[.indexNoneMax], Condition(func))
endmethod
method removeSpellOrderUnit takes integer spellOrderID returns nothing
local integer i = 0
loop
set i = i+1
if oID[this][i]==spellOrderID then
set oID[this][i] = oID[this][.indexUnitMax]
set .indexUnitMax = .indexUnitMax - 1
exitwhen true
endif
exitwhen i==.indexUnitMax
endloop
endmethod
method removeSpellOrderPoint takes integer spellOrderID returns nothing
local integer i = pointIndex
loop
set i = i+1
if oID[this][i]==spellOrderID then
set oID[this][i] = oID[this][.indexPointMax]
set .indexPointMax = .indexPointMax - 1
exitwhen true
endif
exitwhen i==.indexPointMax
endloop
endmethod
method removeSpellOrderNone takes integer spellOrderID returns nothing
local integer i = noneIndex
loop
set i = i+1
if oID[this][i]==spellOrderID then
set oID[this][i] = oID[this][.indexNoneMax]
set .indexNoneMax = .indexNoneMax - 1
exitwhen true
endif
exitwhen i==.indexNoneMax
endloop
endmethod
method launch takes real interval returns nothing
call TimerStart(NewTimerEx(this), interval, true, function thistype.period)
endmethod
method removeWhenDead takes boolean b returns nothing
set .isOnEx = b
endmethod
method remove takes nothing returns nothing
set .isOn = false
endmethod
endstruct
endlibrary
//TESH.scrollpos=54
//TESH.alwaysfold=0
//! zinc
library RespawnSystem requires Table, TimerUtils, optional RegisterPlayerUnitEvent /* v1.2
*************************************************************************************
*
* by mckill2009
* - My first ever system written in Zinc.
*
*************************************************************************************
*
* Respawns the same unit(s) at the same point and facing angle with customable effects and delay.
*
*************************************************************************************
*
* Required Libraries:
* Table by Bribe (www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084)
* TimerUtils by Vexorian (www.wc3c.net/showthread.php?t=101322)
*
* Optional Library:
* RegisterPlayerUnitEvent by Magtheridon96 (http://www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/)
*
*************************************************************************************
*
* Installation:
* Just copy all the required libraries and this script to your map and follow the
* API callings or refer to the DEMO trigger.
*
*************************************************************************************
*
* API:
* public static method register(unit u)
* - call RespawnSystem.register(UNIT)
* - Sets the unit to respawn in simple manner
*
* public static method registerEx(unit u, real delay, string sfx, player pl)
* - call RespawnSystem.registerEx(UNIT, custom delay, custom effect, owner player)
* - Respawns with configurables
*
* public static method remove(unit u)
* - call RespawnSystem.remove(UNIT)
* - Unit will not be respawned again
*
*************************************************************************************/
{
constant real DEFAULT_DELAY = 5;
constant string DEFAULT_SFX = "";
Table chk;
public struct RespawnSystem
{
unit u;
real x;
real y;
real f;
real del;
string sfx;
player pl;
integer id;
static method respawnNow()
{
timer t = GetExpiredTimer();
thistype this = GetTimerData(t);
thistype new = thistype.allocate();
unit nonHero;
if (IsUnitType(this.u, UNIT_TYPE_HERO)) {
ReviveHero(this.u, this.x, this.y, true);
chk[GetHandleId(this.u)] = new;
} else {
nonHero = CreateUnit(this.pl, this.id, this.x, this.y, this.f);
if (this.sfx != "")
{DestroyEffect(AddSpecialEffectTarget(this.sfx, nonHero, "origin"));}
chk[GetHandleId(nonHero)] = new;
nonHero = null;
}
new.x = this.x;
new.y = this.y;
new.f = this.f;
new.del = this.del;
new.sfx = this.sfx;
new.pl = this.pl;
this.pl = null;
this.u = null;
ReleaseTimer(t);
chk.remove(id);
this.deallocate();
t = null;
}
static method death()->boolean
{
thistype this;
unit u = GetTriggerUnit();
if (chk.has(GetHandleId(u))) {
this = chk[GetHandleId(u)];
this.id = GetUnitTypeId(u);
this.u = u;
TimerStart(NewTimerEx(this), this.del, false, function thistype.respawnNow);
}
u = null;
return false;
}
static method onInit()
{
static if (LIBRARY_RegisterPlayerUnitEvent) {
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.death);
} else {
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH);
TriggerAddCondition(t,function thistype.death);
t = null;
}
chk = Table.create();
}
/*****************************************************************
* API:
******************************************************************/
public static method register(unit u)
{
thistype this = thistype.allocate();
chk[GetHandleId(u)] = this;
this.id = GetUnitTypeId(u);
this.x = GetUnitX(u);
this.y = GetUnitY(u);
this.f = GetUnitFacing(u);
this.del = DEFAULT_DELAY;
this.sfx = DEFAULT_SFX;
this.pl = GetOwningPlayer(u);
}
public static method registerEx(unit u, real delay, string sfx, player pl)
{
thistype this = thistype.allocate();
chk[GetHandleId(u)] = this;
this.x = GetUnitX(u);
this.y = GetUnitY(u);
this.f = GetUnitFacing(u);
this.del = delay;
this.sfx = sfx;
this.pl = GetOwningPlayer(u);
if (pl==null) {
this.pl = GetOwningPlayer(u);
}
}
public static method remove(unit u)
{chk.remove(GetHandleId(u));}
}
}
//! endzinc
//TESH.scrollpos=7
//TESH.alwaysfold=0
/**************************************************************************
EngageLite v1.0
by mckill2009
***************************************************************************
API:
function EngageSingle takes unit attacker, unit target returns nothing
function EngageGroupForm takes unit attacker, unit target, real range returns nothing
function EngageGroupForPlayer takes player owner, unit target, real x, real y, real range returns nothing
***************************************************************************/
library EngageLite uses IsUnitChanneling
globals
private constant real INTERVAL = 0.1 //recommended
//NON-CONFIGURABLES
private constant integer ATTACK = 851983 //dont adjust
private timer t = CreateTimer()
private group g = CreateGroup()
private group attackingGroup = CreateGroup()
private integer instance = 0
private integer array instanceAR
endglobals
/**********************************************
* CONFIGURABLES
***********************************************/
private function FilterEngagingUnits takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
/**********************************************
* NON-CONFIGURABLES
***********************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
private struct EngageLite
private unit attacker //attacking unit
private unit target
private real interval
//private real lockTime
private static method engaging takes nothing returns nothing
local thistype this
local unit first
local integer i = 0
local real xTar
local real yTar
loop
set i = i + 1
set this = instanceAR[i]
if UnitAlive(.attacker) then
if UnitAlive(.target) then
set .interval = .interval + INTERVAL
set xTar = GetUnitX(.target)
set yTar = GetUnitY(.target)
if .interval > 3. then
set .interval = 0
if not IsUnitChanneling(.attacker) then
call IssuePointOrderById(.attacker, ATTACK, xTar, yTar)
endif
endif
else
//search for new target enemy
call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(.attacker, GetOwningPlayer(first)) and FilterTargets(first) then
set .target = first
exitwhen true
endif
call GroupRemoveUnit(g, first)
endloop
endif
else
call GroupRemoveUnit(attackingGroup, .attacker)
set .attacker = null
set .target = null
call .deallocate()
set instanceAR[i] = instanceAR[instance]
set instanceAR[instance] = this
set instance = instance - 1
set i = i-1
if instance==0 then
call PauseTimer(t)
endif
endif
exitwhen i==instance
endloop
endmethod
static method register takes unit attacker, unit target returns nothing
local thistype this = allocate()
set .attacker = attacker
set .target = target
set .interval = 0
if instance==0 then
call TimerStart(t, INTERVAL, true, function thistype.engaging)
endif
set instance = instance + 1
set instanceAR[instance] = this
endmethod
endstruct
/**********************************
/ API
***********************************/
function EngageSingle takes unit attacker, unit target returns nothing
if not IsUnitInGroup(attacker, attackingGroup) then
call GroupAddUnit(attackingGroup, attacker)
call RemoveGuardPosition(attacker)
call IssuePointOrderById(attacker, ATTACK, GetUnitX(target), GetUnitY(target))
call EngageLite.register(attacker, target)
endif
endfunction
function EngageGroupForm takes unit attacker, unit target, real range returns nothing
local unit first
local player pl = GetOwningPlayer(attacker)
call GroupEnumUnitsInRange(g, GetUnitX(attacker), GetUnitY(attacker), range, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and FilterEngagingUnits(first) and GetOwningPlayer(first)==pl then
call EngageSingle(first, target)
endif
call GroupRemoveUnit(g, first)
endloop
set pl = null
endfunction
function EngageGroupForPlayer takes player owner, unit target, real x, real y, real range returns nothing
local unit first
call GroupEnumUnitsInRange(g, x, y, range, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and FilterEngagingUnits(first) and GetOwningPlayer(first)==owner then
call EngageSingle(first, target)
endif
call GroupRemoveUnit(g, first)
endloop
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
/*
===SummonedEscort v1.4
===By Mckill2009
Allows your summoned units to follow and guards the summoner, if summoner dies, the summoned unit
searches for a new master or returns to it's original location.
INSTALLATION:
Copy and paste the trigger "SummonedEscort" and "GetClosestWidget" to your map and do:
call SE.summoned(GetSummoningUnit(),GetTriggerUnit())
If AUTO is true, then it will do automatically for you.
API:
static method summoned takes unit summoningUnit, unit summonedUnit returns nothing
CREDITS:
- GetClosestWidget by Spinnaker [http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217/]
*/
library SummonedEscort uses GetClosestWidget
globals
//===CONFIGURABLES:
private constant boolean AUTO = false //auto registers ALL summoned units in map
private constant boolean ALLY_IN_RANGE = true //searches for ally hero if main hero is dead
private constant boolean FOLLOW_ONLY_HEROES = true
private constant real OFFSET = 200
private constant real CLOSEST_ALLY = 600 //ALLY_IN_RANGE must be true
private constant real CLOSEST_ENEMY = 400 //targets closest enemy in range of master
endglobals
struct SE
private unit master
private unit sum
private real xUnit
private real yUnit
private static integer DATA
private static constant integer ATTACK = 851983
private static timer t = CreateTimer()
private static integer instance = 0
private static integer array insAR
private static unit TempUnit = null
private static method UnitAlive takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_DEAD) and u!=null
endmethod
private static method closestEnemy takes nothing returns boolean
local thistype this = DATA
set TempUnit = GetFilterUnit()
return UnitAlive(TempUnit) and IsUnitEnemy(TempUnit, GetOwningPlayer(.sum))
endmethod
private static method closestAlly takes nothing returns boolean
local thistype this = DATA
set TempUnit = GetFilterUnit()
if UnitAlive(TempUnit) and GetOwningPlayer(TempUnit)==GetOwningPlayer(.sum) and TempUnit!=.sum /*
*/ and not IsUnitType(TempUnit,UNIT_TYPE_STRUCTURE) and GetUnitMoveSpeed(TempUnit)>0 then
static if FOLLOW_ONLY_HEROES then
return IsUnitType(TempUnit, UNIT_TYPE_HERO)
endif
return true
endif
return false
endmethod
private method destroy takes nothing returns nothing
set .master = null
set .sum = null
call .deallocate()
endmethod
private static method looper takes nothing returns nothing
local thistype this
local unit target
local integer orderSum
local integer index = 0
local real angle
local real xMaster
local real yMaster
local real xSummoned
local real ySummoned
loop
set index = index+1
set this = insAR[index]
if UnitAlive(.sum) then
set angle = GetRandomReal(0,6.28)
set orderSum = GetUnitCurrentOrder(.sum)
if UnitAlive(.master) then
if orderSum==0 then
set xMaster = GetUnitX(.master)+OFFSET*Cos(angle)
set yMaster = GetUnitY(.master)+OFFSET*Sin(angle)
call IssuePointOrderById(.sum,ATTACK,xMaster,yMaster)
set DATA = this
set target = GetClosestUnitInRange(xMaster,yMaster,CLOSEST_ENEMY,Filter(function thistype.closestEnemy))
if target!=null then
if IsUnitType(target,UNIT_TYPE_SLEEPING) then
call IssueTargetOrderById(.sum,ATTACK,target)
else
call IssuePointOrderById(.sum,ATTACK,GetUnitX(target),GetUnitY(target))
endif
set target = null
endif
endif
else
set DATA = this
set xSummoned = GetUnitX(.sum)
set ySummoned = GetUnitY(.sum)
static if ALLY_IN_RANGE then
set .master = GetClosestUnitInRange(xSummoned,ySummoned,CLOSEST_ALLY,Filter(function thistype.closestAlly))
else
set .master = GetClosestUnit(xSummoned,ySummoned,Filter(function thistype.closestAlly))
endif
if .master==null and orderSum==0 then
call IssuePointOrderById(.sum,ATTACK,.xUnit+OFFSET*Cos(angle),.yUnit+OFFSET*Sin(angle))
endif
endif
else
call .destroy()
set insAR[index] = insAR[instance]
set insAR[instance] = this
set index = index - 1
set instance = instance - 1
if instance==0 then
call PauseTimer(t)
endif
endif
exitwhen index==instance
endloop
endmethod
private static method create takes unit summoningUnit, unit summonedUnit returns thistype
local thistype this
if instance==8190 then
call BJDebugMsg("summoned ERROR: Too many instances!")
else
set this = allocate()
set .master = summoningUnit
set .sum = summonedUnit
set .xUnit = GetUnitX(summonedUnit)
set .yUnit = GetUnitY(summonedUnit)
if instance==0 then
call TimerStart(t,1.0,true,function thistype.looper)
endif
set instance = instance + 1
set insAR[instance] = this
call RemoveGuardPosition(summonedUnit)
endif
return this
endmethod
private static method fire takes nothing returns boolean
call thistype.create(GetSummoningUnit(),GetTriggerUnit())
return false
endmethod
private static method onInit takes nothing returns nothing
static if AUTO then
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SUMMON)
call TriggerAddCondition(t,function thistype.fire)
set t = null
endif
endmethod
//API:======================================
static method summoned takes unit summoningUnit, unit summonedUnit returns nothing
call thistype.create(summoningUnit,summonedUnit)
endmethod
endstruct
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
/*
===UnSelectableUnit v1.3
===by mckill2009
Simple to understand, makes any unit unselectable...
REQUIRES:
- JassNewGenPack by vexorian
API:
function MakeUnitSelectable takes unit u returns nothing
function MakeUnitUnSelectable takes unit u, real duration returns nothing
- put 0 for to the duration for permanent
KNOWN ISSUES:
- Unit can still be attacked, ordered and castable by spells
*/
library UnSelectableUnit
globals
private hashtable ht = InitHashtable()
endglobals
private struct US
unit u
boolean permanent
private static integer index = 0
private static integer array indexAR
private static timer t = CreateTimer()
private static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local integer id
loop
set i = i+1
set this = indexAR[i]
set id = GetHandleId(.u)
if LoadInteger(ht, id, 1)==2 or IsUnitType(.u, UNIT_TYPE_DEAD) then //reset
call FlushChildHashtable(ht, id)
set .u = null
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set index = index - 1
set i = i-1
if index==0 then
call PauseTimer(t)
endif
elseif LoadInteger(ht, id, 1)==1 then //permanent
call SelectUnit(.u, false)
else
if LoadReal(ht, id, 2) > 0 and not IsUnitType(.u, UNIT_TYPE_DEAD) then
call SaveReal(ht, id, 2, LoadReal(ht, id, 2)-0.03125)
call SelectUnit(.u, false)
else
call SaveInteger(ht, id, 1, 2)
endif
endif
exitwhen i==index
endloop
endmethod
static method startTimer takes unit u, real d, integer id returns nothing
local thistype this
if LoadBoolean(ht, id, 0) then
if d==0 then
debug call DisplayTimedTextToPlayer(GetOwningPlayer(u), 0, 0, 10, "[UnSelectableUnit][MakeUnitUnSelectable] ERROR: Zero duration is not possible!")
else
call SaveReal(ht, id, 2, LoadReal(ht, id, 2)+d)
endif
else
set this = allocate()
set .u = u
if d==0 then
call SaveInteger(ht, id, 1, 1) //making permanent
endif
call SaveReal(ht, id, 2, d)
call SaveBoolean(ht, id, 0, true)
if index==0 then
call TimerStart(t, 0.03125, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
endif
endmethod
endstruct
//API:
function MakeUnitSelectable takes unit u returns nothing
call SaveInteger(ht, GetHandleId(u), 1, 2) //resetting
endfunction
function MakeUnitUnSelectable takes unit u, real duration returns nothing
call US.startTimer(u, duration, GetHandleId(u))
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
library QuestUtils /* v1.5
****************************************************************************
by mckill2009
A simple quest wrapper for Quest creation
****************************************************************************
API:
struct QuestUtils extends array
static method create takes boolean required returns thistype
method operator []= takes string title, string description returns nothing
method operator questIcon= takes string iconPath returns nothing
method operator completed= takes boolean b returns nothing
method operator failed= takes boolean b returns nothing
method operator enable= takes boolean b returns nothing
method operator discover= takes boolean b returns nothing
method operator completed takes nothing returns boolean
method operator discovered takes nothing returns boolean
method operator enabled takes nothing returns boolean
method operator required takes nothing returns boolean
method destroy takes nothing returns nothing
struct QuestItem extends array
static method create takes QuestUtils index, string text returns thistype
method operator text= takes string text returns nothing
method operator completed= takes boolean b returns nothing
method operator completed takes nothing returns boolean
method destroy takes nothing returns nothing
****************************************************************************/
globals
private constant integer QUEST_LIMIT = 100
endglobals
struct QuestUtils extends array
quest q
private static thistype instance = 0
static method create takes boolean required returns thistype
local thistype this
if instance==QUEST_LIMIT then
debug call BJDebugMsg("QuestUtils ERROR: Maximum quest is: "+I2S(QUEST_LIMIT))
else
set this = instance + 1
set instance = this
set .q = CreateQuest()
call QuestSetRequired(.q, required)
endif
return this
endmethod
method operator []= takes string title, string description returns nothing
call QuestSetTitle(.q, title)
call QuestSetDescription(.q, description)
call FlashQuestDialogButton()
endmethod
method operator questIcon= takes string iconPath returns nothing
call QuestSetIconPath(.q, iconPath)
endmethod
method operator completed= takes boolean b returns nothing
call QuestSetCompleted(.q, b)
call FlashQuestDialogButton()
endmethod
method operator failed= takes boolean b returns nothing
call QuestSetFailed(.q, b)
call FlashQuestDialogButton()
endmethod
method operator enable= takes boolean b returns nothing
call QuestSetEnabled(.q, b)
if b then
call FlashQuestDialogButton()
endif
endmethod
method operator discover= takes boolean b returns nothing
call QuestSetDiscovered(.q, b)
if b then
call FlashQuestDialogButton()
endif
endmethod
method operator completed takes nothing returns boolean
return IsQuestCompleted(.q)
endmethod
method operator discovered takes nothing returns boolean
return IsQuestDiscovered(.q)
endmethod
method operator enabled takes nothing returns boolean
return IsQuestEnabled(.q)
endmethod
method operator required takes nothing returns boolean
return IsQuestRequired(.q)
endmethod
method destroy takes nothing returns nothing
call DestroyQuest(.q)
set .q = instance.q
set instance = instance - 1
endmethod
endstruct
struct QuestItem extends array
questitem qi
private static thistype instance = 0
static method create takes QuestUtils index, string text returns thistype
local thistype this = instance + 1
set instance = this
set .qi = QuestCreateItem(index.q)
call QuestItemSetDescription(.qi, text)
call FlashQuestDialogButton()
return this
endmethod
method operator text= takes string text returns nothing
call QuestItemSetDescription(.qi, text)
call FlashQuestDialogButton()
endmethod
method operator completed= takes boolean b returns nothing
call QuestItemSetCompleted(.qi, b)
call FlashQuestDialogButton()
endmethod
method operator completed takes nothing returns boolean
return IsQuestItemCompleted(.qi)
endmethod
method destroy takes nothing returns nothing
set .qi = instance.qi
set instance = instance - 1
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
===DialogBox v1.0
===By Mckill2009
This is just a simple Dialog System, no need to have vJass knowledge, just follow the
instructions from the demo.
API:
static method create takes string messageText, code whichFunction returns thistype
method operator dialogTitle= takes string messageText returns nothing
method addButton takes string buttonText, integer hotkey returns button
method addQuitButton takes boolean showScores, string buttonText, integer hotkey returns nothing
method playerClicks takes player whichPlayer returns boolean
method display takes player forPlayer, boolean b returns nothing
method clicked takes button whichButton returns boolean
method clear takes nothing returns nothing
method destroy takes nothing returns nothing
*/
library DialogBox
globals
private constant integer LIMIT = 100
endglobals
struct DialogBox
trigger t
dialog dia
static method create takes string messageText, code whichFunction returns thistype
local thistype this = allocate()
if this==LIMIT then
debug call BJDebugMsg("DialogBox ERROR: "+I2S(LIMIT)+" limit exceeded!")
return -1
else
set .dia = DialogCreate()
set .t = CreateTrigger()
call TriggerRegisterDialogEvent(.t, .dia)
call TriggerAddCondition(.t, Filter(whichFunction))
call DialogSetMessage(.dia, messageText)
endif
return this
endmethod
method operator title= takes string messageText returns nothing
call DialogSetMessage(.dia, messageText)
endmethod
method addButton takes string buttonText, integer hotkey returns button
return DialogAddButton(.dia, buttonText, hotkey)
endmethod
method addQuitButton takes boolean showScores, string buttonText, integer hotkey returns nothing
call DialogAddQuitButton(.dia, showScores, buttonText, hotkey)
endmethod
method playerClicks takes player whichPlayer returns boolean
return GetTriggerPlayer()==whichPlayer
endmethod
method display takes player forPlayer, boolean b returns nothing
call DialogDisplay(forPlayer, .dia, b)
endmethod
method clicked takes button whichButton returns boolean
return GetClickedButton()==whichButton
endmethod
method clear takes nothing returns nothing
call DialogClear(.dia)
endmethod
method destroy takes nothing returns nothing
local integer i
if .dia==null then
debug call BJDebugMsg("BoardEx ERROR: Attempt to destroy an invalid instance.")
else
set i = 0
loop
call DialogDisplay(Player(i), .dia, false)
set i = i+1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call DialogClear(.dia)
call DialogDestroy(.dia)
call DestroyTrigger(.t)
set .dia = null
set .t = null
call .deallocate()
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=67
//TESH.alwaysfold=0
/*
=====BoardEx v1.5
=====Made by: Mckill2009
This is just a Multiboard System, simple to use even for GUIer's
INSTALLATION:
- Copy and Paste the "BoardEx" trigger to your map.
- Read the instructions. DONE!
API:
static method create takes integer rowQty, integer columnQty, boolean showtexts, boolean showicons returns thistype
method setTitle takes string s returns nothing
method setAllWidth takes real width returns nothing
method setIndividualWidths takes integer row, integer column, real width returns nothing
method showValues takes integer row, integer column, boolean showtext, boolean showicon returns nothing
method display takes boolean minimize, boolean show returns nothing
method setIcon takes integer row, integer column, string iconpath returns nothing
method setText takes integer row, integer column, string text returns nothing
method destroy takes nothing returns nothing
*/
library BoardEx
struct BoardEx extends array
private multiboard MB
private static constant integer boardLimit = 8191
private static multiboarditem boardItem = null
private static thistype countBoard = 0
//===This is the first thing you need to do, setting up the multiboard
//===Take note that this allocates a new index
static method create takes integer rowQty, integer columnQty, boolean showtexts, boolean showicons returns thistype
local thistype this
if countBoard==boardLimit then
debug call BJDebugMsg("boardSetUp ERROR: Maximum exceeded: "+I2S(boardLimit))
else
set countBoard = countBoard + 1
set this = countBoard
set .MB = CreateMultiboard()
call .display(false,true)
call MultiboardSetColumnCount(.MB, columnQty)
call MultiboardSetRowCount(.MB, rowQty)
call MultiboardSetItemsStyle(.MB, showtexts, showicons)
endif
return this
endmethod
method setTitle takes string s returns nothing
call MultiboardSetTitleText(.MB, s)
endmethod
//===Sets all width of the multiboard in uniform order, real value should be 0.1 or less
method setAllWidth takes real width returns nothing
call MultiboardSetItemsWidth(.MB, width)
endmethod
//===Sets individual widths, real value should be 0.1 or less
method setIndividualWidths takes integer row, integer column, real width returns nothing
set boardItem = MultiboardGetItem(.MB, row, column)
call MultiboardSetItemWidth(boardItem, width)
call MultiboardReleaseItem(boardItem)
endmethod
//===Show/Hide values such as text or icons
method showValues takes integer row, integer column, boolean showtext, boolean showicon returns nothing
set boardItem = MultiboardGetItem(.MB, row, column)
call MultiboardSetItemStyle(boardItem, showtext, showicon)
call MultiboardReleaseItem(boardItem)
endmethod
//===Maximize/Minimize, Show/Hide multiboard
method display takes boolean minimize, boolean show returns nothing
call MultiboardDisplay(.MB, show)
call MultiboardMinimize(.MB, minimize)
endmethod
//===Change the icon, showicon from the showValues must be true
method setIcon takes integer row, integer column, string iconpath returns nothing
set boardItem = MultiboardGetItem(.MB, row, column)
call MultiboardSetItemIcon(boardItem, iconpath)
call MultiboardReleaseItem(boardItem)
endmethod
//===This is the MAIN multiboard value and text change
method setText takes integer row, integer column, string text returns nothing
set boardItem = MultiboardGetItem(.MB, row, column)
call MultiboardSetItemValue(boardItem, text)
call MultiboardReleaseItem(boardItem)
endmethod
//===destroys the 'this' (indexed) multiboard
method destroy takes nothing returns nothing
call MultiboardClear(.MB)
call DestroyMultiboard(.MB)
set .MB = countBoard.MB
set countBoard = countBoard - 1
endmethod
endstruct
endlibrary
//TESH.scrollpos=105
//TESH.alwaysfold=0
/*
===Spawn System v1.2
===by: Mckill2009
REQUIRES:
- Jass New Gen Pack by Vexorian
- TimerUtils by Vexorian
INSTALLATION:
- Copy the TimerUtils and SpawnSystem to your map
API:
static method create takes player owner, integer unitId, integer maxSpawnUnit returns thistype
- owner = the owner of the spawned unit
- unitId = raw code of the spawned unit, press CTRL+D from object editor
- maxCount = how many units will be created
method spawnInRect takes rect where, boolean center returns nothing
- completely optional, as you may use the coordinate of your map
- gg_rct_where = generated global, is the region created via region palette
- center = if you want to create in center or random point in rect
method spawnInCoordinates takes real x, real y returns nothing
- sets the XY coordinate of the spawned unit (s)
method setEffect takes string sfx returns nothing
- sets the effect when spawned
method setTime takes real spawnDuration, real spawnInterval, real spawnedUnitLife returns nothing
- duration = how long the spawning lasts
- interval = how fast the spawning takes place
- life = the timer of the spawned unit, set to 0 if no life timer
method stop takes nothing returns nothing
- stops the spawnings prematurely
method start takes nothing returns nothing
- starts the spawn event >>> call this.start()
globals
public unit spawned
- you may use this in trigger through unit enters playable map area
- sample: if GetTriggerUnit()==SpawnSystem_spawned then
CREDITS: For their suggestions
- watermelon_1234
- Magtheridon96
*/
library SpawnSystem uses TimerUtils
globals
public unit spawned = null
//===CONFIGURABLES:
private constant integer FIXED_MAX_COUNT = 500 //sets the max unit created
endglobals
struct SpawnSystem
player owner
integer unitId
integer maxCount
real duration
real interval
real life
real xLoc
real yLoc
string sfx
rect r
private boolean center
private static method onSpawnThem takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local unit dummy
if .duration > 0 and .maxCount > 0 then
set .duration = .duration - .interval
set .maxCount = .maxCount - 1
if .r==null then
set dummy = CreateUnit(.owner, .unitId, .xLoc, .yLoc, 0)
else
if .center then
set dummy = CreateUnit(.owner, .unitId, GetRectCenterX(.r), GetRectCenterY(.r), 0)
else
set dummy = CreateUnit(.owner, .unitId, GetRandomReal(GetRectMinX(.r), GetRectMaxX(.r)), GetRandomReal(GetRectMinY(.r), GetRectMaxY(.r)), 0)
endif
endif
if IsUnitType(dummy, UNIT_TYPE_FLYING) then
call DestroyEffect(AddSpecialEffectTarget(.sfx, dummy,"origin"))
else
call DestroyEffect(AddSpecialEffect(.sfx, GetUnitX(dummy), GetUnitY(dummy)))
endif
call UnitApplyTimedLife(dummy, 'BTLF', .life)
set spawned = dummy
set dummy = null
else
set .owner = null
set .r = null
call ReleaseTimer(t)
call .destroy()
endif
set t = null
endmethod
/***********************************************************************
* API:
************************************************************************/
static method create takes player owner, integer unitId, integer maxSpawnUnit returns thistype
local thistype this = thistype.allocate()
set .owner = owner
set .unitId = unitId
set .sfx = ""
set .r = null
if maxCount > FIXED_MAX_COUNT then
set .maxCount = FIXED_MAX_COUNT
else
set .maxCount = maxSpawnUnit
endif
return this
endmethod
method spawnInRect takes rect where, boolean center returns nothing
set .r = where
set .center = center
endmethod
method spawnInCoordinates takes real x, real y returns nothing
set .xLoc = x
set .yLoc = y
endmethod
method setEffect takes string sfx returns nothing
set .sfx = sfx
endmethod
method setTime takes real spawnDuration, real spawnInterval, real spawnedUnitLife returns nothing
set .duration = spawnDuration
set .interval = spawnInterval
set .life = spawnedUnitLife
endmethod
method stop takes nothing returns nothing
set .duration = 0
endmethod
method start takes nothing returns nothing
call TimerStart(NewTimerEx(this), .interval, true, function thistype.onSpawnThem)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Thrown uses Table, T32
globals
private Table i
endglobals
struct Thrown
unit u
integer uID
real baseH
real hUp
real hDown
real s
private static method onInit takes nothing returns nothing
set i = Table.create()
endmethod
private method reset takes nothing returns nothing
call SetUnitFlyHeight(.u, GetUnitDefaultFlyHeight(.u), 0)
call SetUnitPropWindow(.u, 1)
call i.remove(.uID)
set .u = null
call .stopPeriodic()
call .destroy()
endmethod
private method periodic takes nothing returns nothing
if not IsUnitType(.u, UNIT_TYPE_DEAD) then
if .baseH > .hUp then
set .hUp = .hUp + .s
call SetUnitFlyHeight(.u, .hUp, 0) //go up
elseif .hDown > 0 then
set .hDown = .hDown - .s
call SetUnitFlyHeight(.u, .hDown, 0) //go down
else
call .reset()
endif
else
call .reset()
endif
endmethod
implement T32x
static method run takes unit u, real height, real speed returns nothing
local thistype this
local integer id = GetHandleId(u)
if not i.has(id) then
set this = create()
set .u = u
set .hUp = 0
set .hDown = height
set .s = speed
set .baseH = height
set .uID = id
if UnitAddAbility(u, 'Arav') and UnitRemoveAbility(u, 'Arav') then
endif
call SetUnitPropWindow(u, 0)
set i[id] = .uID
call .startPeriodic()
endif
endmethod
static method isUnitThrown takes unit u returns boolean
return i.has(GetHandleId(u))
endmethod
endstruct
endlibrary
//TESH.scrollpos=44
//TESH.alwaysfold=0
/*************************************************************************
ItemTable v1.0
by mckill2009
Allows to make Item Sets and drops item by chance on unit death event
**************************************************************************/
library ItemTable uses Table
globals
private Table data
private TableArray ItemGroup
endglobals
private module DI
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition( t, function thistype.death)
set data = Table.create()
set ItemGroup = TableArray[0x2000]
set t = null
endmethod
endmodule
struct ItemTable
private integer itemTypeCount
private integer chance
private static method dropItem takes unit u returns nothing
local integer id = GetHandleId(u)
local thistype this = data[id]
local integer randomItem = GetRandomInt(1, 100)
if randomItem <= this.chance then
call CreateItem(ItemGroup[this][GetRandomInt(1, this.itemTypeCount)], GetUnitX(u), GetUnitY(u))
endif
call data.remove(id)
endmethod
static method death takes nothing returns boolean
if data.has(GetHandleId(GetTriggerUnit())) then
call dropItem(GetTriggerUnit())
endif
return false
endmethod
implement DI
/*********************************************************
* SYSTEM API
***********************************************************/
static method create takes integer chance returns thistype
local thistype this = allocate()
set this.chance = chance
return this
endmethod
method addUnit takes unit u returns nothing
set data[GetHandleId(u)] = this
endmethod
method addItemType takes integer itemType returns nothing
set this.itemTypeCount = this.itemTypeCount + 1
set ItemGroup[this][this.itemTypeCount] = itemType
endmethod
method setDropChance takes integer chance returns nothing
set this.chance = chance
endmethod
method destroy takes nothing returns nothing
call this.deallocate()
endmethod
endstruct
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
/*****************************
* Simple Random pick unit
* by mckill2009
******************************/
library RandomUnit
globals
private unit TempU = null
private group TempG = CreateGroup()
private integer count = 0
endglobals
private function EnumUnits takes nothing returns nothing
set count = count+1
endfunction
function GetRandomUnitInArea takes real x, real y, real aoe, boolexpr b returns unit
local integer ran
call GroupEnumUnitsInRange(TempG, x, y, aoe, b)
set count = 0
call ForGroup(TempG, function EnumUnits)
set ran = GetRandomInt(1, count)
set count = 0
loop
set TempU = FirstOfGroup(TempG)
set count = count + 1
exitwhen TempU==null or count==ran
call GroupRemoveUnit(TempG, TempU)
endloop
call GroupClear(TempG)
return TempU
endfunction
endlibrary
//TESH.scrollpos=58
//TESH.alwaysfold=0
library MissileCollision /* v1.3
*************************************************************************************
*
* by mckill2009
*
*************************************************************************************
*
* An extremely simple missile projectile system that collides and damages on enemy units
* or blows on impact when desired distance is reached.
* The missile is recycle able so that it will not create different dummies everytime.
*
*************************************************************************************
*
* */ uses /*
*
* */ T32 /* http://www.thehelper.net/forums/showthread.php/132538-Timer32
*
************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
* For his DUMMY.mdx model
*
* Jesus4Lyf
* -----------------------
* For his Timer32 library
*
*
************************************************************************************
*
* Installation:
* - Import the DUMMY.mdx model to your map
* - Copy the DummySfx unit from the object editor to your map and change the model to DUMMY.mdx
* - Copy ALL the Required Libraries and MissileCollicion folder to your map (except the DEMO)
* - Change the DUMMY_ID of the Dummy library to your imported DummySfx
*
************************************************************************************
*
* API:
* struct MC
* static method create takes player owningPlayer, real xWhere, real yWhere, string missileModel returns thistype
* - owningPlayer is the owner of the missile
* - xWhere and yWhere is the coordinate where the missile's origin or launched
*
* method setupCollision takes real aoe, real collision, string collisionEffect returns nothing
* - aoe is the area of effect damage when missile explodes
* - collision, missile will explode and deals damage if it collides with the first enemy unit in range
* - collisionEffect, the effect or eye candy of the explosion
*
* method setupDamage takes real damage, attacktype attackType, damagetype damageType returns nothing
* - damage, the damage when missile explodes
* - attackType and damageType is self explanatory
*
* method launch takes real facingInRadiants, real distance, real height, real speed returns nothing
* - facingInRadiants is the angle where the missile goes
* - distance is how far the missile traver
* - height and speed is the height of the missile and speed travel of the missile respectively
*
*
************************************************************************************
*/
globals
private constant integer DUMMY_ID = 'e002'
private group g = CreateGroup()
private group mGroup = CreateGroup()
private integer test = 0
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and u!=null
endfunction
/************************************************************************
Delay struct in only used to delay the dummy to be destroyed
so that the effects will be visible from their origin attachment
*************************************************************************/
struct MC
private unit missile
private real aoeDamage
private real collision
private real distance
private real damage
private real height
private real speed
private real cos
private real sin
private real del
private string collisionEffect
private effect missileModel
private player pl
private attacktype atk
private damagetype dmg
private boolean check
private boolean delay
private method damageThem takes real x, real y returns nothing
local unit first
call GroupEnumUnitsInRange(g, x, y, .aoeDamage, null)
call DestroyEffect(AddSpecialEffectTarget(.collisionEffect, .missile, "origin"))
call DestroyEffect(.missileModel)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) then
if .atk==null or .dmg==null then
call UnitDamageTarget(.missile, first, .damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
else
call UnitDamageTarget(.missile, first, .damage, false, false, .atk, .dmg, null)
endif
endif
call GroupRemoveUnit(g, first)
endloop
endmethod
private method periodic takes nothing returns nothing
local unit first
local real xMissile = GetUnitX(.missile)
local real yMissile = GetUnitY(.missile)
if .distance > 0 and .check then
set .distance = .distance - .speed
call SetUnitX(.missile, xMissile + .cos)
call SetUnitY(.missile, yMissile + .sin)
//detects first collision target, stopping the missile
call GroupEnumUnitsInRange(g, xMissile, yMissile, .collision, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) then
set .check = false
//call BJDebugMsg("MISSILE COLLIDE")
endif
call GroupRemoveUnit(g, first)
endloop
else
//means delay the missile dummy removal
if .delay then
set .del = .del - T32_PERIOD
if .del < 0 then
call SetUnitOwner(.missile, Player(15), false)
call GroupAddUnit(mGroup, .missile)
set .missile = null
set .missileModel = null
call .stopPeriodic()
call .deallocate()
endif
else
call .damageThem(xMissile, yMissile)
set .delay = true
endif
endif
endmethod
implement T32x
/************************************************************************
API
*************************************************************************/
static method create takes player owningPlayer, real xWhere, real yWhere, string missileModel returns thistype
local thistype this = allocate()
set .missile = FirstOfGroup(mGroup)
if UnitAlive(.missile) then
call GroupRemoveUnit(mGroup, .missile)
call SetUnitX(.missile, xWhere)
call SetUnitY(.missile, yWhere)
else
set .missile = CreateUnit(owningPlayer, DUMMY_ID, xWhere, yWhere, 0)
//set test = test + 1
//call BJDebugMsg(I2S(test))
endif
set .missileModel = AddSpecialEffectTarget(missileModel, .missile, "origin")
set .pl = owningPlayer
set .collisionEffect = ""
set .check = true
set .delay = false
set .del = 3.0
if UnitAddAbility(.missile, 'Aloc') and UnitAddAbility(.missile, 'Arav') and UnitAddAbility(.missile, 'Amov')then
endif
return this
endmethod
method setupCollision takes real aoe, real collision, string collisionEffect returns nothing
set .aoeDamage = aoe
set .collision = collision
set .collisionEffect = collisionEffect
endmethod
method setupDamage takes real damage, attacktype attackType, damagetype damageType returns nothing
set .damage = damage
set .atk = attackType
set .dmg = damageType
endmethod
method launch takes real facingInRadiants, real distance, real height, real speed returns nothing
set .distance = distance
set .height = height
set .speed = speed
set .cos = speed * Cos(facingInRadiants)
set .sin = speed * Sin(facingInRadiants)
call SetUnitFlyHeight(.missile, height, 0)
call SetUnitFacing(.missile, facingInRadiants*bj_RADTODEG)
call .startPeriodic()
endmethod
endstruct
endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
/*******************************************************
TimedSfx v1.0
by mckill2009
********************************************************
Just a simple Timed Effect remover
********************************************************/
library TimedSfx
struct TimedSfx
private effect sfx
private real duration
private static integer ind = 0
private static integer array indAR
private static timer t = CreateTimer()
private static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i + 1
set this = indAR[i]
set .duration = .duration - 0.03125
if 0 > .duration then
call DestroyEffect(.sfx)
set .sfx = null
call .deallocate()
set indAR[i] = indAR[ind]
set ind = ind - 1
set i = i - 1
if ind==0 then
call PauseTimer(t)
endif
endif
exitwhen i==ind
endloop
endmethod
static method start takes effect sfx, real duration returns nothing
local thistype this = allocate()
set .sfx = sfx
set .duration = duration
if ind==0 then
call TimerStart(t, 0.03125, true, function thistype.periodic)
endif
set ind = ind + 1
set indAR[ind] = this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*********************************************************
Jumper v1.0
by mckill2009
**********************************************************
Simple Jumping system that uses standard parabola
**********************************************************/
library Jumper uses T32, TerrainPathability
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
endfunction
private function GetParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)//Standard parabola
endfunction
struct Jumper
private unit u
private real a
private real d
private real mD
private real h
private real s
private string at
private string sf
private string sfEx
private boolean on
private method periodic takes nothing returns nothing
local real xTer
local real yTer
if UnitAlive(.u) and .on then
set xTer = GetUnitX(.u)+50*Cos(.a)
set yTer = GetUnitY(.u)+50*Sin(.a)
if .d > 0 and IsTerrainWalkable(xTer, yTer) then
set .d = .d - .s
call SetUnitX(.u, GetUnitX(.u) + .s * Cos(.a))
call SetUnitY(.u, GetUnitY(.u) + .s * Sin(.a))
call SetUnitFlyHeight(.u, GetParabolaZ(.h, .mD, .d), 0)
else
set .on = false
endif
else
if sf=="" then
call DestroyEffect(AddSpecialEffectTarget(.sfEx, .u, .at))
else
call DestroyEffect(AddSpecialEffect(.sf, GetUnitX(.u), GetUnitY(.u)))
endif
call SetUnitFlyHeight(.u, GetUnitDefaultFlyHeight(.u), 0)
call UnitRemoveAbility(.u, 'Avul')
call SetUnitPropWindow(.u, 1)
call UnitRemoveAbility(.u, 'Abun')
call SetUnitPathing(.u, true)
set .u = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
/************************************************************
* API
*************************************************************/
static method register takes unit jumper, real xWhere, real yWhere, real speed returns thistype
local thistype this = allocate()
local real x = GetUnitX(jumper)
local real y = GetUnitY(jumper)
set .u = jumper
set .a = GetAngle(x, y, xWhere, yWhere)
set .d = GetDistance(x, y, xWhere, yWhere)
set .mD = .d
set .on = true
set .sf = ""
set .sfEx = ""
set .at = ""
set .s = speed
call SetUnitPropWindow(.u, 0)
call UnitAddAbility(.u, 'Abun')
call UnitAddAbility(.u, 'Avul')
call SetUnitPathing(.u, false)
if UnitAddAbility(.u, 'Arav') then
call UnitAddAbility(.u, 'Arav')
endif
return this
endmethod
method addEffectOnImpact takes string sfEx, string attachment returns nothing
set .sfEx = sfEx
set .at = attachment
endmethod
method setDistance takes real d returns nothing
set .d = d
set .mD = d
endmethod
method setHeight takes real h returns nothing
set .h = h
endmethod
method start takes nothing returns nothing
call .startPeriodic()
endmethod
endstruct
endlibrary
//TESH.scrollpos=6
//TESH.alwaysfold=0
library OrderToLoc
globals
private real xO
private real yO
private integer Order
private group npc = CreateGroup()
//private group leader = CreateGroup()
endglobals
private function MoveTo takes nothing returns nothing
call IssuePointOrderById(GetEnumUnit(), Order, xO, yO)
endfunction
private function MoveGroup takes nothing returns nothing
set xO = GetOrderPointX()
set yO = GetOrderPointY()
set Order = GetIssuedOrderId()
call ForGroup(npc, function MoveTo)
endfunction
function RegisterOrderTrigger takes unit hero returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterUnitEvent(t, hero, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerAddAction(t, function MoveGroup)
set t = null
endfunction
function AddOrderedUnit takes unit u returns nothing
call GroupAddUnit(npc, u)
call IssuePointOrderById(u, 851986, -11000, 7200)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*******************************************************
UnitHasItemType v1.0
by: mckill2009
********************************************************/
library UnitHasItemType
globals
private item it = null
endglobals
function UnitHasItemType takes unit u, integer itemId returns boolean
local integer i = 0
local boolean b
loop
set b = GetItemTypeId(UnitItemInSlot(u, i))==itemId
set i = i+1
exitwhen i==6 or b
endloop
return b
endfunction
function UnitHasNItemType takes unit u, integer itemId returns integer
local integer i = 0
local integer index = 0
loop
if GetItemTypeId(UnitItemInSlot(u, i))==itemId then
set index = index + 1
endif
set i = i+1
exitwhen i==6
endloop
return index
endfunction
function RemoveItemType takes unit u, integer itemId returns nothing
local integer i = 0
local integer index = 0
loop
set it = UnitItemInSlot(u, i)
if GetItemTypeId(it)==itemId then
call RemoveItem(it)
exitwhen true
endif
set i = i+1
exitwhen i==6
endloop
endfunction
endlibrary
//TESH.scrollpos=33
//TESH.alwaysfold=0
/*****************************************************************
ItemFilterSystem v1.0
by mckill2009
******************************************************************
Limits the number of item type for a certain unit/hero
******************************************************************
REQUIRED LIBRARIES:
- Table by Bribe
******************************************************************
SYSTEM API:
static method create takes unit u returns thistype
static method getInstance takes unit u returns thistype
method addItemLimit takes integer itemid, integer limit, string errorDisplay returns nothing
method removeItemLimit takes integer itemid returns nothing
method setErrorDisplay takes integer itemid, string errorDisplay returns nothing
method destroy takes nothing returns nothing
******************************************************************/
library ItemFilter uses Table
globals
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private Table xs
private TableArray ct
private item it = null
endglobals
private function UnitHasItemType takes unit u, integer itemId, integer qty returns boolean
local integer i = 0
local integer index = 0
loop
set it = UnitItemInSlot(u, i)
if GetItemTypeId(it)==itemId then
set index = index + 1
if index > (qty) then
return true
endif
endif
set i = i+1
exitwhen i==6
endloop
return false
endfunction
struct IFS
private unit hero
private integer limitIndex
private static method pick takes nothing returns boolean
local thistype this
local unit u = GetTriggerUnit()
local item itm
local integer i
local integer itemid
local integer id = GetHandleId(u)
local real xItem
local real yItem
if xs.has(id) then //id is the one who picks the item
set itm = GetManipulatedItem()
set itemid = GetItemTypeId(itm)
set xItem = GetItemX(itm)
set yItem = GetItemY(itm)
set this = xs[id] //access the struct instance
set i = 0
loop
set i = i + 1
if ct[this][i]==itemid then
if ct[this][itemid]==0 then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, ct[this].string[itemid])
call UnitDropItemPoint(GetTriggerUnit(), itm, xItem, yItem)
exitwhen true
else
if UnitHasItemType(GetTriggerUnit(), ct[this][i], ct[this][itemid]) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, ct[this].string[itemid])
call UnitDropItemPoint(GetTriggerUnit(), itm, xItem, yItem)
exitwhen true
endif
endif
endif
exitwhen i==.limitIndex
endloop
endif
set u = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerAddCondition(t, function thistype.pick)
set t = null
set xs = Table.create()
set ct = TableArray[0x2000]
endmethod
/**************************************
* API
***************************************/
static method create takes unit u returns thistype
local thistype this
local integer uID = GetHandleId(u)
if xs.has(uID) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "thistype.register ERROR: " + GetUnitName(u) + " cannot be registered twice!")
return -1
else
set this = allocate()
set .hero = u
set .limitIndex = 0
set xs[uID] = this
endif
return this
endmethod
static method getInstance takes unit u returns thistype
local integer uID = GetHandleId(u)
if not xs.has(uID) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "thistype.getInstance ERROR: " + GetUnitName(u) + " is not regietered!")
return -1
endif
return xs[uID]
endmethod
method addItemLimit takes integer itemid, integer limit, string errorDisplay returns nothing
local integer i = - 1
loop
set i = i + 1
if ct[this][i]==itemid then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "thistype.addObtainOnce ERROR: may only add ONE item type per instance.")
return
endif
exitwhen i==.limitIndex
endloop
set .limitIndex = .limitIndex + 1 //index started at 1
set ct[this][.limitIndex] = itemid
set ct[this][itemid] = limit
set ct[this].string[itemid] = errorDisplay
endmethod
method removeItemLimit takes integer itemid returns nothing
local integer i = 0
set ct[this][itemid] = -1
set ct[this].string[itemid] = ""
loop
set i = i + 1
if ct[this][i]==itemid then
set ct[this][i] = ct[this][.limitIndex]
set .limitIndex = .limitIndex - 1 //reducing the index
exitwhen true
endif
exitwhen i==.limitIndex
endloop
endmethod
method setErrorDisplay takes integer itemid, string errorDisplay returns nothing
set ct[this].string[itemid] = errorDisplay
endmethod
method destroy takes nothing returns nothing
local integer i = 0
local integer limit = .limitIndex
loop
set i = i + 1
call this.removeItemLimit(ct[this][i])
exitwhen i==limit
endloop
call xs.remove(GetHandleId(.hero))
call this.deallocate()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//PreloadUtils
//Created by Mckill2009
library PreloadUtils
globals
private unit DUM
endglobals
function PreloadSpell takes integer abilityid returns nothing
call UnitAddAbility(DUM, abilityid)
call UnitRemoveAbility(DUM, abilityid)
endfunction
function PreloadUnit takes integer unitID returns nothing
local unit u = CreateUnit(Player(15), unitID,0,0,0)
call UnitApplyTimedLife(u, 'BTLF', 1.0)
call ShowUnit(u, false)
set u = null
endfunction
private module SetPreload
static method onInit takes nothing returns nothing
set DUM = CreateUnit(Player(15),'hpea',0,0,0)
call ShowUnit(DUM, false)
call UnitAddAbility(DUM, 'Aloc')
endmethod
endmodule
private struct onInit extends array
implement SetPreload
endstruct
endlibrary
//TESH.scrollpos=69
//TESH.alwaysfold=0
//System Name: HashT v1.0 (One Hashtable for vJASS codes)
//Made By: Mckill2009
//Inspired by Bribe's New Table
//How do you like to create ONE hashtable for your entire vJASS codes or map?
//well, this system is just right and enough for you
//this is also GUI friendly
//As long as it has a unique childID and not flushed, it should'nt conflict things even if it has the same parentID,
//much more if it has different parentID
library HashT
globals
private constant hashtable HT = InitHashtable()
endglobals
//! textmacro ONE_HASH takes FuncName, Native, Prefix
function Set$FuncName$ takes integer parent, integer child, $Prefix$ value returns nothing
call Save$Native$(HT, parent, child, value)
endfunction
function Get$FuncName$ takes integer parent, integer child returns $Prefix$
return Load$Native$(HT, parent, child)
endfunction
//! endtextmacro
//! runtextmacro ONE_HASH("Integer", "Integer", "integer")
//! runtextmacro ONE_HASH("Real", "Real", "real")
//! runtextmacro ONE_HASH("Str", "Str", "string")
//! runtextmacro ONE_HASH("UnitHandle", "UnitHandle", "unit")
//! runtextmacro ONE_HASH("Boolean", "Boolean", "boolean")
//! runtextmacro ONE_HASH("AbilityHandle", "AbilityHandle", "ability")
//! runtextmacro ONE_HASH("ButtonHandle", "ButtonHandle", "button")
//! runtextmacro ONE_HASH("GroupHandle", "GroupHandle", "group")
//! runtextmacro ONE_HASH("ImageHandle", "ImageHandle", "image")
//! runtextmacro ONE_HASH("ItemHandle", "ItemHandle", "item")
//! runtextmacro ONE_HASH("ItemPoolHandle", "ItemPoolHandle", "itempool")
//! runtextmacro ONE_HASH("LeaderboardHandle", "LeaderboardHandle", "leaderboard")
//! runtextmacro ONE_HASH("LightningHandle", "LightningHandle", "lightning")
//! runtextmacro ONE_HASH("LocationHandle", "LocationHandle", "location")
//! runtextmacro ONE_HASH("MultiboardHandle", "MultiboardHandle", "multiboard")
//! runtextmacro ONE_HASH("PlayerHandle", "PlayerHandle", "player")
//! runtextmacro ONE_HASH("QuestHandle", "QuestHandle", "quest")
//! runtextmacro ONE_HASH("RectHandle", "RectHandle", "rect")
//! runtextmacro ONE_HASH("SoundHandle", "SoundHandle", "sound")
//! runtextmacro ONE_HASH("RegionHandle", "RegionHandle", "region")
//! runtextmacro ONE_HASH("TextTagHandle", "TextTagHandle", "texttag")
//! runtextmacro ONE_HASH("TimerDialogHandle", "TimerDialogHandle", "timerdialog")
//! runtextmacro ONE_HASH("TimerHandle", "TimerHandle", "timer")
//! runtextmacro ONE_HASH("TrackableHandle", "TrackableHandle", "trackable")
//! runtextmacro ONE_HASH("WidgetHandle", "WidgetHandle", "widget")
//! runtextmacro ONE_HASH("EffectHandle", "EffectHandle", "effect")
//=====RemoveSaved and HaveSaved:
//! textmacro SR_HASH takes TypeName
function Has$TypeName$ takes integer parent, integer child returns boolean
return HaveSaved$TypeName$(HT, parent, child)
endfunction
function Remove$TypeName$ takes integer parent, integer child returns nothing
call RemoveSaved$TypeName$(HT, parent, child)
endfunction
//! endtextmacro
//! runtextmacro SR_HASH("Integer")
//! runtextmacro SR_HASH("Real")
//! runtextmacro SR_HASH("String")
//! runtextmacro SR_HASH("Boolean")
//! runtextmacro SR_HASH("Handle")
function Flush takes integer parent returns nothing
call FlushChildHashtable(HT, parent)
endfunction
function RemoveTimer takes timer t, integer ID returns nothing
call Flush(ID)
call PauseTimer(t)
call DestroyTimer(t)
endfunction
function RemoveFromGroupAndEnd takes integer index, group g, unit u, timer t returns integer
set index = index - 1
call GroupRemoveUnit(g, u)
if index==0 then
call PauseTimer(t)
endif
return index
endfunction
function AddToGroupAndStart takes integer index, group g, unit u, timer t, real interval, boolean b, code func returns integer
if not IsUnitInGroup(u, g) then
call GroupAddUnit(g, u)
if index==0 then
call TimerStart(t, interval, b, func)
endif
set index = index + 1
endif
return index
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library KnockBack /* v1.0
*
* by mckill2009
*
*************************************************************************************
*
* */uses/*
* */ TerrainPathability /* by Rising_Dusk (http://www.wc3c.net/showthread.php?t=103862)
* */ Table /* by Bribe (http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084)
*
************************************************************************************/
struct KnockBack extends array
private static timer t = CreateTimer()
private static group g = CreateGroup()
private static integer count = 0
private static TableArray c
private static method onInit takes nothing returns nothing
set c = TableArray[0x2000]
endmethod
private static method periodic takes nothing returns nothing
local unit u = GetEnumUnit()
local integer id = GetHandleId(u)
local real angle = c[0].real[id]
local real distance = c[1].real[id]
local real speed = c[2].real[id]
local real x = GetUnitX(u)
local real y = GetUnitY(u)
if not IsUnitType(u, UNIT_TYPE_DEAD) and distance > 0 then
set c[1].real[id] = c[1].real[id] - speed
if IsTerrainWalkable(x+speed*Cos(angle), y+speed*Sin(angle)) then
call SetUnitX(u, x + speed * Cos(angle))
call SetUnitY(u, y + speed * Sin(angle))
endif
else
call DestroyEffect(AddSpecialEffectTarget(c[3].string[id], u, "origin"))
call GroupRemoveUnit(g, u)
set count = count - 1
if count==0 then
call PauseTimer(t)
endif
endif
set u = null
endmethod
private static method groupLoop takes nothing returns nothing
call ForGroup(g, function thistype.periodic)
endmethod
static method run takes unit target, real angle, real distance, real speed, string sfx returns nothing
local integer id = GetHandleId(target)
set c[0].real[id] = angle
set c[1].real[id] = distance
set c[2].real[id] = speed
set c[3].string[id] = sfx
if count==0 then
call TimerStart(t, 0.03125, true, function thistype.groupLoop)
endif
if not IsUnitInGroup(target, g) then
call GroupAddUnit(g, target)
set count = count + 1
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SimpleTextTag
function SimTTLoc takes string s, real x, real y, real position, real size returns nothing
local texttag tag = CreateTextTag()
call SetTextTagPos(tag, x, y, position) //150 OK
call SetTextTagText(tag, s, size) //0.025 OK
call SetTextTagPermanent(tag, false)
call SetTextTagVelocity(tag, 0.03, 0.03)
call SetTextTagLifespan(tag, 3)
call SetTextTagFadepoint(tag, 0.01)
set tag = null
endfunction
function SimTTUnit takes string s, unit u, real possize, real size returns nothing
local texttag tag = CreateTextTag()
call SetTextTagPosUnit(tag, u, possize) //0 OK
call SetTextTagText(tag, s, size) //0.025 OK
call SetTextTagPermanent(tag, false)
call SetTextTagVelocity(tag, 0.03, 0.03)
call SetTextTagLifespan(tag, 3)
call SetTextTagFadepoint(tag, 0.01)
set tag = null
endfunction
endlibrary
//TESH.scrollpos=3
//TESH.alwaysfold=0
library RandomColors
globals
private string array Color
private integer MaxColor = 10
endglobals
private function ColorSetup takes nothing returns nothing
set Color[0] = "|cffff0000" //red
set Color[1] = "|cff0000ff" //blue
set Color[2] = "|cff00f5ff" //Teal
set Color[3] = "|cff551A8B" //Purple
set Color[4] = "|cffffff00" //Yellow
set Color[5] = "|cffEE9A00" //Orange
set Color[6] = "|cff00CD00" //Green
set Color[7] = "|cffFF69B4" //Pink
set Color[8] = "|cffC0C0C0" //Gray
set Color[9] = "|cffB0E2FF" //Light Blue
set Color[10] = "|cff006400" //Dark Green
//set Color[12] = "|cff8B4513" //???
//set Color[13] = "|cff8B4513" //???
//set Color[14] = "|cff8B4513" //???
//set Color[15] = "|cff8B4513" //???
//and so on until the INDEX reaches 50
endfunction
function RandomColor takes string s returns string
return Color[GetRandomInt(0, MaxColor)] + s +"|r"
endfunction
private struct CS
static method onInit takes nothing returns nothing
call ColorSetup()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
set udg_EnableHealingPoint = false //if a boss fight is ON from CastingConditions
//healing point is ON after reaper TALK
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ShadeDrain uses SpellEffectEvent, IsUnitChanneling, TimerUtils
globals
private constant integer SPELL_ID = 'sci1' //firebolt
private constant integer SHADE_SPELL_ID = 'A01F'
private constant integer DRAIN_ORDER_ID = 852487
private constant integer SHADE_ID = 'ushd'
private constant string SFX = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
endglobals
private function GetShadeCount takes integer level returns integer
return 2 * level
endfunction
private struct ShadeDrain
private unit shade
private real delay
private static method isChanneling takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if not IsUnitChanneling(.shade) then
call UnitApplyTimedLife(.shade, 'BTLF', 0.1)
set .shade = null
call ReleaseTimer(GetExpiredTimer())
call .deallocate()
endif
endmethod
private static method start takes unit u returns nothing
local thistype this = allocate()
set .shade = u
call TimerStart(NewTimerEx(this), 0.5, true, function thistype.isChanneling)
endmethod
private static method onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local unit shade
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
local real randomAngle
local real randomOffset
local real x
local real y
local integer i = 0
local integer shadeCount = GetShadeCount(level)
loop
set i = i+1
set randomAngle = GetRandomReal(0,6.2)
set randomOffset = GetRandomReal(150, 300)
set x = GetUnitX(target) + randomOffset * Cos(randomAngle)
set y = GetUnitY(target) + randomOffset * Sin(randomAngle)
set shade = CreateUnit(GetTriggerPlayer(), SHADE_ID, x, y, 0)
call UnitAddAbility(shade, SHADE_SPELL_ID)
call SetUnitAbilityLevel(shade, SHADE_SPELL_ID, level)
call DestroyEffect(AddSpecialEffect(SFX, x, y))
call IssueTargetOrderById(shade, DRAIN_ORDER_ID, target)
call thistype.start(shade)
exitwhen i==shadeCount
endloop
set target = null
set u = null
set shade = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
//call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, function thistype.shadeDeath)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LightningStrike uses SpellEffectEvent, RandomUnit
globals
private constant integer SPELL_ID = 'ald1' //flame strike
private constant integer DUMMY_SPELL_ID = 'A01G'
private constant integer DUMMY_SPELL_ORDER_ID = 852587 //forked lightning
private constant integer DUMMY_ID = 'h00K'
private constant string SFX = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
private unit tempUnit = null
endglobals
private function GetDummyCount takes integer level returns integer
return 2 * level
endfunction
private function FilterTargets takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(tempUnit))
endfunction
private struct LightningStrike
private static method onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit target
local unit dummy
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
local real randomAngle
local real randomOffset
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
local real x1
local real y1
local integer i = 0
local integer dummyCount = GetDummyCount(level)
set tempUnit = u
loop
set i = i+1
set target = GetRandomUnitInArea(x, y, 600, Filter(function FilterTargets))
if target!=null then
set randomAngle = GetRandomReal(0,6.2)
set randomOffset = GetRandomReal(150, 300)
set x1 = GetUnitX(target) + randomOffset * Cos(randomAngle)
set y1 = GetUnitY(target) + randomOffset * Sin(randomAngle)
set dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, x1, y1, 0)
call UnitApplyTimedLife(dummy, 'BTLF', 1.0)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, level)
call DestroyEffect(AddSpecialEffect(SFX, x, y))
call IssueTargetOrderById(dummy, DUMMY_SPELL_ORDER_ID, target)
endif
exitwhen i==dummyCount
endloop
set target = null
set u = null
set dummy = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
//call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, function thistype.shadeDeath)
endmethod
endstruct
endlibrary
//TESH.scrollpos=512
//TESH.alwaysfold=0
// *****************************************************************************
// * Spell: Frezzing Field
// * -> By: Dark Dragon
// * -> Date: March 4 2009
// *
// *
// * --- Installation ---
// *
// * - 1. Get latest version of Jass NewGen and JassHelper from wc3c.net
// * - 2. If you don't have dummy.mdx model in your map extract it from here
// * - 3. Copy dummy unit from Object Editor (F6) and paste it in your map
// * - 4. Copy abilities: Freezing Field Damage, Freezing Field and/or Freezing Field Upgraded
// * - 5. Copy FF Upgrade ability if you want to use upgraded and add that ability to item you want
// * - 6. Copy this trigger and paste it in your map
// * - 7. Start reading below until it says "don't edit if you don't know jass"
// * (you must edit rawcodes to match your map) go to object editor and enable View -> Display Values As Raw Data
// * - 8. Edit rawcodes here and spell to style, as you wish and save your map
// * - 9. Enjoy!
// *****************************************************************************
// =====================================================================
// Frezzing Field library
//
library FrizzingField initializer Init_FreezingField
// **********************************************************
// --- Editable Spell Globals
// **********************************************************
globals
// This is the rawcode of the spell frezzing field
// Default: 'A000'
private constant integer FF_RAWCODE = 'A03B'
// This is the rawcode of the spell frezzing field upgraded
// Default: 'A004'
private constant integer FFU_RAWCODE = 'A03D'
// This is the rawcode of dummy unit
// Default: 'e000'
private constant integer DUM_RAWCODE = 'e00K'
// This is the dummy ability frost nova
// Default: 'A001'
private constant integer DFN_RAWCODE = 'A03C'
// This is the path to graphic effect
// Default: "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
private constant string GEFF_MODEL = "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
// This is the casters channel effect model
// Default: "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
private constant string CEFF_MODEL = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
// This is the path to sound file which will be played when spell is cast
// Default: "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget1.wav"
private constant string GLACIER_SOUND = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget1.wav"
// This tells should graphic effects be created
// Default: true
private constant boolean ALLOW_GEFFECTS = true
// This is the graphic effect speed
// Default: 500.
private constant real GEFFECT_SPEED = 500.
// Shards min pass distance
// Default: 175.
private constant real SHARD_MIN_DISTANCE = 175.
// Shards max pass distance
// Default: 525.
private constant real SHARD_MAX_DISTANCE = 700.
// Shards min pass height
// Default: 200.
private constant real SHARD_MIN_HEIGHT = 200.
// Shards max pass height
// Default: 375.
private constant real SHARD_MAX_HEIGHT = 375.
// This is shards min count per cast
// Default: 8
private constant integer SHARD_MIN_COUNT = 10
// This is shards max count per cast
// Default: 10
private constant integer SHARD_MAX_COUNT = 16
// Shards min duration
// Default: 0.9
private constant real SHARD_MIN_TIMEOUT = 0.9
// Shards max duration
// Default: 1.8
private constant real SHARD_MAX_TIMEOUT = 1.8
// Do not touch this variables here
//{
private integer Levels = 0
private real array ExplosionDelay[8191]
private real array ExplosionArea[8191]
private real array ExplosionDur[8191]
//}
endglobals
// ===================================================
// *** Edit Frezzing Field Level Data ***
// ===================================================
private function Setup_FreezingField takes nothing returns nothing
// This is the max level of spell (add + one level if you use upgrader)
// So if Levels = 3, you must defined 4 levels like below
// Default: 3
set Levels = 5
// Max spell area of effect
// Default: 400, 435, 475, 500
set ExplosionArea[1] = 600.
set ExplosionArea[2] = 600.
set ExplosionArea[3] = 600.
set ExplosionArea[4] = 600.
set ExplosionArea[5] = 600.
// How offten explosions are created
// Default: 0.15, 0.13, 0.11, 0.10
set ExplosionDelay[1] = 0.15
set ExplosionDelay[2] = 0.13
set ExplosionDelay[3] = 0.11
set ExplosionDelay[4] = 0.10
set ExplosionDelay[5] = 0.08
// How long spell will last
// Default: 3, 3, 3, 3
set ExplosionDur[1] = 3.
set ExplosionDur[2] = 3.
set ExplosionDur[3] = 3.
set ExplosionDur[4] = 3.
set ExplosionDur[5] = 3.
endfunction
// ===== Do not edit below if you don't know jass =====
// --- Keywords ---
private keyword ArrayId
private keyword GetSpellLevel
private keyword GetAverageValue
// *******************************
// Spell uneditable globals
globals
private constant real TIMER_MOVE_DELAY = 0.025
private constant real PI_HALF = bj_PI / 2.
private constant real TWO_PI = 2. * bj_PI
private filterfunc this_filter = null
private boolean array IsCasting[8191]
endglobals
// ===================================================
// --- Freezing Field main struct ---
// ===================================================
private struct ffield
// **************************
// --- ffield members ---
private unit u
private real x
private real y
private integer lvl
private integer counter
private real dur
private player p
// ****************************
// --- Static variables ---
private static constant real MIN_DIST = 75.
// *** Constructor ***
public static method create takes nothing returns ffield
local ffield this = ffield.allocate()
set this.u = GetTriggerUnit()
set this.x = GetUnitX(this.u)
set this.y = GetUnitY(this.u)
set this.p = GetOwningPlayer(this.u)
set this.lvl = GetSpellLevel.evaluate(this.u)
set this.dur = ExplosionDur[this.lvl]
set this.counter = 1
set IsCasting[ArrayId.evaluate(this.u)] = true
return (this)
endmethod
// Will generate next counter
private method NextCounter takes nothing returns nothing
set this.counter = this.counter + GetRandomInt(1, 2)
set this.counter = (this.counter - (this.counter / 4) * 4) + 1
endmethod
// Will return angle for current explosion
private method Angle takes nothing returns real
local real min = (this.counter-1)*PI_HALF
local real max = min + PI_HALF
call this.NextCounter()
return GetRandomReal(min, max)
endmethod
// Returns distance for each explosion
private method Distance takes nothing returns real
return GetRandomReal(this.MIN_DIST, ExplosionArea[this.lvl])
endmethod
// Causes a single explosion
public method Explode takes nothing returns nothing
local unit u = null
local real ang = this.Angle()
//local real dist = this.Distance()
local real dist = GetRandomReal(75, I2R(GetRandomInt(100, 600)))
set u = CreateUnit(this.p, DUM_RAWCODE, this.x + dist * Cos(ang), this.y + dist * Sin(ang), bj_UNIT_FACING)
call UnitAddAbility(u, DFN_RAWCODE)
call SetUnitAbilityLevel(u, DFN_RAWCODE, this.lvl)
call IssueTargetOrder(u, "frostnova", u)
call UnitAddAbility(u, 'Aloc')
call UnitApplyTimedLife(u, 'BTLF', 0.5)
set u = null
//call SetRandomSeed(GetRandomInt(0, 1000000))
endmethod
// Adds caster effect to caster
public method AddEffect takes string modelFile returns nothing
call DestroyEffect(AddSpecialEffectTarget(modelFile, this.u, "origin"))
endmethod
// Timeout for explosion
public method operator timeout takes nothing returns real
return ExplosionDelay[this.lvl]
endmethod
// Checks is this ffield valid
public method operator valid takes nothing returns boolean
//debug call BJDebugMsg("in valid lvl = "+I2S(this.lvl))
return (this.lvl != -1)
endmethod
// Our end spell check
public method operator done takes nothing returns boolean
set this.dur = this.dur - this.timeout
return (this.dur <= 0. or not IsCasting[ArrayId.evaluate(this.u)])
endmethod
// How should this object be destroyed ~Destructor
public method onDestroy takes nothing returns nothing
debug set IsCasting[ArrayId.evaluate(this.u)] = false
set this.u = null
set this.p = null
endmethod
endstruct
// ===================================================
// --- Triggerx new non-leaking trigger type ---
// ===================================================
private struct triggerx
// Triggerx members
private trigger t
private triggeraction ta
private integer value
private timer tm
// Static vars
private static triggerx array ThisTriggx[8191]
// *** Constructor ***
public static method create takes code cond, code act, integer val returns triggerx
local triggerx this = triggerx.allocate()
set this.t = CreateTrigger()
set this.tm = null
set this.value = val
if (cond != null) then
call TriggerAddCondition(this.t, Condition(cond))
endif
if (act != null) then
set this.ta = TriggerAddAction(this.t, act)
endif
// Pointer at our triggerx
set this.ThisTriggx[ArrayId.evaluate(this.t)] = (this)
return (this)
endmethod
// Set custom value
public method SetValue takes integer val returns nothing
set this.value = val
endmethod
// Get custom value
public method GetValue takes nothing returns integer
return (this.value)
endmethod
// Will execute this triggerx
public method Execute takes nothing returns nothing
call TriggerExecute(this.t)
endmethod
// Returns triggering triggerx
public static method TriggeringTriggerx takes trigger trig returns triggerx
return (triggerx.ThisTriggx[ArrayId.evaluate(trig)])
endmethod
// Registers give timer to triggerx
public method RegisterTimerEvent takes real timeout returns nothing
set this.tm = CreateTimer()
call TimerStart(this.tm, timeout, true, null)
call TriggerRegisterTimerExpireEvent(this.t, this.tm)
endmethod
// This will add new event (when units stop spell cast)
public method RegisterEndSpellCast takes unit u returns nothing
call TriggerRegisterUnitEvent(this.t, u, EVENT_UNIT_SPELL_ENDCAST)
endmethod
// Returns current execution of triggerx
public method operator exec takes nothing returns integer
return GetTriggerExecCount(this.t)
endmethod
// *** ~Destructor ***
public method onDestroy takes nothing returns nothing
if (this.tm != null) then
call PauseTimer(this.tm)
call DestroyTimer(this.tm)
set this.tm = null
endif
set this.ThisTriggx[ArrayId.evaluate(this.t)] = 0
call TriggerRemoveAction(this.t, this.ta)
call DestroyTrigger(this.t)
set this.t = null
set this.ta = null
endmethod
endstruct
// ===================================================
// --- This is our ice shard struct ---
// ===================================================
// *** Functions for shard ***
// Calculates sign of number
private constant function Sign takes real x returns real
if (x >= 0.) then
return 1.
endif
return -1.
endfunction
// Will change unit's z axes
private function SetUnitZ takes unit whichUnit, real newz returns nothing
call SetUnitFlyHeight(whichUnit, newz, 0.)
endfunction
private struct shard
// * Shard struct members
private unit u
private effect e
private real x
private real y
private real z
private real dx
private real dy
private real xy_vel
private real z_vel
private real max_dist
private real dist
// Shard static members
private static constant player NEUTRAL = Player(0xF)
private static constant string ATTACH = "origin"
// * Constructor
public static method create takes real x, real y, real facing, real dist, real height, real time returns shard
local shard this = shard.allocate()
set this.u = CreateUnit(this.NEUTRAL, DUM_RAWCODE, x, y, facing)
set this.e = AddSpecialEffectTarget(GEFF_MODEL, this.u, this.ATTACH)
set this.x = x
set this.y = y
set this.z = 0.
set this.dx = Cos(facing * bj_DEGTORAD)
set this.dy = Sin(facing * bj_DEGTORAD)
set this.xy_vel = (2.*dist) / time
set this.z_vel = (2.*height) / time
set this.max_dist = dist
set this.dist = 0.
// Make dummy unselectable and flyable
call UnitAddAbility(this.u, 'Amrf')
call UnitAddAbility(this.u, 'Aloc')
return (this)
endmethod
// *** Our offset move method ***
private method OffsetMove takes nothing returns nothing
set this.x = this.x + this.xy_vel*TIMER_MOVE_DELAY * this.dx
set this.y = this.y + this.xy_vel*TIMER_MOVE_DELAY * this.dy
set this.z = this.z + this.z_vel*TIMER_MOVE_DELAY * Sign(-this.dist+this.max_dist/2.)
set this.dist = this.dist + this.xy_vel*TIMER_MOVE_DELAY
call SetUnitX(this.u, this.x)
call SetUnitY(this.u, this.y)
call SetUnitZ(this.u, this.z)
endmethod
// Checks for condition
public method OffsetMoveEx takes nothing returns nothing
if (this.dist < this.max_dist) then
call this.OffsetMove()
endif
endmethod
// Checks did shard complete its movement
public method operator done takes nothing returns boolean
return (this.dist >= this.max_dist)
endmethod
// ~Destructor
public method onDestroy takes nothing returns nothing
call DestroyEffect(this.e)
call RemoveUnit(this.u)
set this.e = null
set this.u = null
endmethod
endstruct
// ===================================================
// --- The cluster is the shards group ---
// ===================================================
private struct cluster
// *Cluster members
private shard array sh[SHARD_MAX_COUNT]
private boolean fin
private integer count
private real dur
private real cx
private real cy
// Cluster static constants
private static constant real a = 1./(125.*(SHARD_MIN_DISTANCE-SHARD_MAX_DISTANCE))
private static constant real b = -SHARD_MAX_DISTANCE/(125.*(SHARD_MIN_DISTANCE-SHARD_MAX_DISTANCE))
// *Constructor
public static method create takes integer count, real dur, real cx, real cy returns cluster
local cluster this = cluster.allocate()
local real passd
local real offsetd
local real x
local real y
local real rad
local real offsetrad
local real h
local real t
set this.count = count
set this.fin = false
set this.dur = dur
set this.cx = cx
set this.cy = cy
set offsetrad = (TWO_PI*0.5)/count
set count = 0
loop
exitwhen (count >= this.count)
set rad = count*(TWO_PI/this.count)
set rad = GetRandomReal(rad-offsetrad, rad+offsetrad)
set passd = GetRandomReal(SHARD_MIN_DISTANCE, SHARD_MAX_DISTANCE)
set offsetd = (passd * this.a) + this.b
set x = cx + offsetd * Cos(rad)
set y = cy + offsetd * Sin(rad)
set h = GetRandomReal(SHARD_MIN_HEIGHT, SHARD_MAX_HEIGHT)
set t = GetRandomReal(SHARD_MIN_TIMEOUT, SHARD_MAX_TIMEOUT)
set this.sh[count] = shard.create(x, y, rad*bj_RADTODEG, passd, h, t)
call SetRandomSeed(GetRandomInt(0, 1000000))
set count = count + 1
endloop
return (this)
endmethod
// This moves all shards in cluster
public method OffsetMove takes nothing returns nothing
local integer i = 0
local boolean b = true
loop
exitwhen (i >= this.count)
call this.sh[i].OffsetMoveEx()
set b = b and this.sh[i].done
set i = i + 1
endloop
set this.dur = this.dur - TIMER_MOVE_DELAY
set this.fin = b
endmethod
// Reloads (recreates the) cluster -> returns did he failed to load new cluster
public method Reload takes nothing returns boolean
local triggerx tx
local cluster cl
if (this.dur >= GetAverageValue.evaluate(SHARD_MIN_TIMEOUT, SHARD_MAX_TIMEOUT)) then
set tx = triggerx.TriggeringTriggerx(GetTriggeringTrigger())
set cl = cluster.create(GetRandomInt(SHARD_MIN_COUNT, SHARD_MAX_COUNT), this.dur, this.cx, this.cy)
call tx.SetValue(integer(cl))
call this.destroy()
return false
endif
call this.destroy()
return true
endmethod
// Did all shards reach ground?
public method operator complete takes nothing returns boolean
return (this.fin)
endmethod
// ~Destructor
public method onDestroy takes nothing returns nothing
local integer i = 0
loop
exitwhen (i >= this.count)
call this.sh[i].destroy()
set this.sh[i] = 0
set i = i + 1
endloop
set this.count = 0
endmethod
endstruct
// ======================================================
// *** Frezzing Field Custom Defined Functions ***
// ======================================================
// Returns Frezzing Field spell level
private function GetSpellLevel takes unit whichUnit returns integer
local integer id = GetSpellAbilityId()
local integer lvl = GetUnitAbilityLevel(whichUnit, id)
//debug call BJDebugMsg(I2S(lvl))
if (lvl > Levels) then
return (-1)
endif
if (id == FF_RAWCODE) then
return (lvl)
elseif (id == FFU_RAWCODE) then
return (lvl + 1)
endif
return (0)
endfunction
// Average value of min, max
private constant function GetAverageValue takes real min, real max returns real
return min + (max-min) / 2.
endfunction
// Calculates modulo integer of a mod b
private constant function Mod takes integer a, integer b returns integer
return a - (a/b) * b
endfunction
// Gets local object id
private function ArrayId takes handle h returns integer
local integer id = GetHandleId(h)
return (id - (id / 8191) * 8191)
endfunction
// Creates triggerx
private function CreateTriggerx takes code cond, code act, integer val returns triggerx
return triggerx.create(cond, act, val)
endfunction
// Destroys triggerx
private function DestroyTriggerx takes triggerx whichTrigger returns nothing
call triggerx.destroy(whichTrigger)
endfunction
// Returns triggering triggerx
private function GetTriggeringTriggerx takes nothing returns triggerx
return triggerx.TriggeringTriggerx(GetTriggeringTrigger())
endfunction
// Will register timer event
private function TriggerxRegisterTimerEvent takes triggerx tx, real timeout returns nothing
call tx.RegisterTimerEvent(timeout)
endfunction
// Triggers when unit stops casting a spell
private function TriggerxRegisterEndSpellCast takes triggerx tx, unit whichUnit returns nothing
call tx.RegisterEndSpellCast(whichUnit)
endfunction
// Executes any triggerx
private function TriggerxExecute takes triggerx tx returns nothing
call tx.Execute()
endfunction
// Will play custom sound file
private function PlayGlacierExplode takes integer volume, real x, real y returns nothing
local sound s = CreateSound(GLACIER_SOUND, false, true, true, 10, 10, "DefaultEAXON")
call SetSoundParamsFromLabel( s, "FrostNova" )
call SetSoundDuration( s, 2904 )
call SetSoundPitch( s, 0.5 )
call SetSoundVolume(s, volume)
call SetSoundPosition(s, x, y, 0.)
call StartSound(s)
call KillSoundWhenDone(s)
set s = null
endfunction
// ===========================================================================
// ------ ------
// *** Spell's Main Code ***
// ------ ------
// ===========================================================================
// ------- Frezzing Field Condition -------
private constant function Freezing_Field_Cond takes nothing returns boolean
return GetSpellAbilityId() == FF_RAWCODE or GetSpellAbilityId() == FFU_RAWCODE
endfunction
// *** This is our loop function (field explosion) ***
private function FieldExplode takes nothing returns nothing
local triggerx tx = GetTriggeringTriggerx()
local ffield f = ffield( tx.GetValue() )
// Is spell done?
if (f.done) then
//debug call BJDebugMsg("|cffff0000spell done casting|r")
call DestroyTriggerx(tx)
call f.destroy()
return
endif
if (Mod(tx.exec-1, R2I(1./f.timeout)) == 0) then
call f.AddEffect(CEFF_MODEL)
endif
// Cause single blow
call f.Explode()
endfunction
// Moves all shards (timed)
private function EnumShards takes nothing returns nothing
local triggerx tx = GetTriggeringTriggerx()
local cluster cl = cluster( tx.GetValue() )
call cl.OffsetMove()
if (cl.complete) then
if (cl.Reload()) then
call DestroyTriggerx(tx)
endif
endif
endfunction
// This is executed when caster stops casting the spell
private function FreezingFieldStop takes nothing returns nothing
//debug call BJDebugMsg("End freezing field spell cast")
set IsCasting[ArrayId(GetTriggerUnit())] = false
call DestroyTriggerx( GetTriggeringTriggerx() )
endfunction
// *** Frezzing Field Action ***
private function Freezing_Field_Act takes nothing returns nothing
// ** Locals **
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
local ffield f = ffield.create()
local triggerx tx
local cluster cl
// Vaildate our ffield
if (not f.valid) then
//call BJDebugMsg("|cffff0000Unable to cast Frezzing Field: Level of spell greather then defined|r")
call f.destroy()
call DestroyTrigger( GetTriggeringTrigger() )
return
endif
// Load our cluster here
if (ALLOW_GEFFECTS) then
set cl = cluster.create( GetRandomInt(SHARD_MIN_COUNT, SHARD_MAX_COUNT), ExplosionDur[GetSpellLevel(GetTriggerUnit())], x, y )
endif
// Its valid, now create our trigger
set tx = CreateTriggerx(null, function FieldExplode, integer(f))
call TriggerxRegisterTimerEvent(tx, f.timeout)
// Now load end cast triggerx
set tx = CreateTriggerx(function Freezing_Field_Cond, function FreezingFieldStop, 0)
call TriggerxRegisterEndSpellCast(tx, GetTriggerUnit())
if (ALLOW_GEFFECTS) then
// Now load shard loop trigger
set tx = CreateTriggerx(null, function EnumShards, integer(cl))
call TriggerxRegisterTimerEvent(tx, TIMER_MOVE_DELAY)
endif
// Sound file play
call PlayGlacierExplode(127, x, y)
endfunction
// ===================================================================================================
private constant function DummyBool takes nothing returns boolean
return (true)
endfunction
private function PreloadSounds takes nothing returns nothing
call TriggerSleepAction(3.)
call PlayGlacierExplode(1, 0., 0.)
call DestroyTriggerx(GetTriggeringTriggerx())
endfunction
private function Init_FreezingField takes nothing returns nothing
// --- Load locals ---
local trigger t = CreateTrigger()
local integer i = 0
// *** Dummy Filter ***
set this_filter = Filter(function DummyBool)
// --- Setup spell user style ---
call Setup_FreezingField()
// Load trigger event, condition and action
loop
exitwhen (i >= bj_MAX_PLAYERS)
call TriggerRegisterPlayerUnitEvent( t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, this_filter )
set i = i + 1
endloop
call TriggerAddCondition( t, Condition( function Freezing_Field_Cond ) )
call TriggerAddAction( t, function Freezing_Field_Act )
// --- Preloader ---
call Preload(GEFF_MODEL)
call Preload(CEFF_MODEL)
call Preload("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl")
call Preload("Abilities\\Spells\\Other\\FrostDamage\\FrostDamage.mdl")
call TriggerxExecute(CreateTriggerx(null, function PreloadSounds, 0))
set t = null
endfunction
endlibrary
//TESH.scrollpos=62
//TESH.alwaysfold=0
library Gateway uses SpellEffectEvent, T32, EngageLite
globals
private constant integer SPELL_ID = 'naz1' //howl of terror
//private constant integer DUMMY_ID = 'h00F' //portal
private constant string SFX = "Abilities\\Spells\\Demon\\DarkConversion\\ZombifyTarget.mdl"
private integer array unitID
endglobals
private function GetDummyCount takes integer level returns integer
return 2 * level
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct GatewayCast
private unit caster
private unit portal
private integer count
private real animationTime
private real duration
private real xCreate
private real yCreate
private player pl
private method destroy takes nothing returns nothing
set .caster = null
set .portal = null
set .pl = null
call .stopPeriodic()
call .deallocate()
endmethod
private method periodic takes nothing returns nothing
local unit u
local integer random
local integer i
if UnitAlive(.caster) then
set .duration = .duration + T32_PERIOD
if .duration > .animationTime then
set i = 0
loop
exitwhen i==.count
set i = i+1
set random = GetRandomInt(0,4)
set u = CreateUnit(.pl, unitID[random], .xCreate, .yCreate, 0)
call DestroyEffect(AddSpecialEffect(SFX, .xCreate, .yCreate))
set udg_NazePortalUnitCount = udg_NazePortalUnitCount + 1
call GroupAddUnit(udg_BossNazeGroup, u)
call EngageSingle(u, udg_HERO)
call SetUnitAnimation(.portal, "death")
set u = null
endloop
call .destroy()
endif
else
call KillUnit(.portal)
call .destroy()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
set .caster = u
set .count = GetDummyCount(level)
set .duration = 0
set .animationTime = 10
set .pl = GetTriggerPlayer()
set .portal = udg_PortalNaze
//set .portal = CreateUnit(.pl, udg_TempPortalUnitID[random], .xCreate, .xCreate, 0)
set .xCreate = GetUnitX(.portal)
set .yCreate = GetUnitY(.portal)
call SetUnitAnimation(.portal, "birth")
set u = null
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set unitID[0] = 'nhhr'
set unitID[1] = 'ners'
set unitID[2] = 'nvdg'
set unitID[3] = 'nfgb'
set unitID[4] = 'nfel'
endmethod
endstruct
endlibrary
//TESH.scrollpos=19
//TESH.alwaysfold=0
library SwiftPoison uses SpellEffectEvent, T32
globals
private constant integer SPELL_ID = 'mir1' //how of terror
private constant integer DUMMY_SPELL_ID = 'A01H'
private constant integer DUMMY_ID = 'e001'
private constant string SFX = "Abilities\\Spells\\Undead\\AntiMagicShell\\AntiMagicShell.mdl"
endglobals
private struct SwiftPoison
unit m
unit dum
real dur
effect sfx
private method periodic takes nothing returns nothing
if .dur > 0 and UnitAlive(.m) then
set .dur = .dur - T32_PERIOD
call SetUnitX(.dum, GetUnitX(.m))
call SetUnitY(.dum, GetUnitY(.m))
else
call KillUnit(.dum)
call PauseUnit(.m, false)
call DestroyEffect(.sfx)
set .dum = null
set .m = null
set .sfx = null
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .m = GetTriggerUnit()
set .dum = CreateUnit(GetTriggerPlayer(), DUMMY_ID, GetUnitX(.m), GetUnitY(.m), 0)
call UnitAddAbility(.dum, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(.dum, DUMMY_SPELL_ID, GetUnitAbilityLevel(.m, SPELL_ID))
set .dur = 10
set .sfx = AddSpecialEffectTarget(SFX, .m, "chest")
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library FireWaves uses SpellEffectEvent, T32, IsUnitChanneling
globals
private constant integer SPELL_ID = 'lav1' //channel
private constant integer DUMMY_SPELL_ID = 'A01I' //carrion swarm
private constant integer DUMMY_ID = 'e002'
private constant integer ORDER_ID = 852218 //carrion swarm OID
private constant real INTERVAL = 0.1
private constant real ROTATE = 0.5 //in radians
private unit dummy
endglobals
private struct FW
private unit caster
private real angle
private real duration
private real xU
private real yU
private real delay
private integer level
private player pl
private method periodic takes nothing returns nothing
local real x
local real y
if duration > 0 and IsUnitChanneling(.caster) then
set .duration = .duration - T32_PERIOD
set .delay = .delay + T32_PERIOD
if .delay > INTERVAL then
set .delay = 0
set .angle = .angle + ROTATE
set x = .xU + 75 * Cos(.angle)
set y = .yU + 75 * Sin(.angle)
call SetUnitX(dummy, .xU)
call SetUnitY(dummy, .yU)
call SetUnitOwner(dummy, .pl, false)
call SetUnitFlyHeight(dummy, GetUnitFlyHeight(.caster)+50, 0)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, .level)
call IssuePointOrderById(dummy, ORDER_ID, x, y)
call UnitRemoveAbility(dummy, DUMMY_SPELL_ID)
endif
else
set .caster = null
set .pl = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
set .caster = u
set .angle = 0
set .delay = 0
set .duration = 20 //depends on the object editor's duration
set .level = GetUnitAbilityLevel(u, SPELL_ID)
set .pl = GetTriggerPlayer()
set .xU = GetUnitX(u)
set .yU = GetUnitY(u)
call .startPeriodic()
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set dummy = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call UnitRemoveAbility(dummy, 'Amov')
call UnitRemoveAbility(dummy, DUMMY_SPELL_ID)
endmethod
endstruct
endlibrary
//TESH.scrollpos=134
//TESH.alwaysfold=0
/*********************************************************
TailSurge v1.0
by mckill2009
**********************************************************
REQUIRES:
- Timer32 by Jesus4lyf
- SpellEffectEvent by Bribe
- TerrainPathability by Rising_Dusk
**********************************************************/
library TailSurge uses T32, SpellEffectEvent, TerrainPathability
globals
private constant integer SPELL_ID = 'A02V' //Based on Carrion swarm
private constant integer DUMMY_SPELL_ID = 'A01I' //Based on Carrion swarm
private constant integer ORDER_ID = 852218 //Based on Carrion swarm
private constant integer DUMMY_ID = 'e002'
/*********************************************************
* CONFIGURABLES for the main spell
**********************************************************/
private constant real AOE = 135 //damage aoe
private constant string THRUST_SFX = "Abilities\\Spells\\Orc\\Shockwave\\ShockwaveMissile.mdl"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_NORMAL
/*********************************************************
* CONFIGURABLES for the tail surge spell
**********************************************************/
private constant boolean ENABLE_BACK_SURGE = true
private constant integer BACK_WAVE_COUNT = 6
private constant real THRUST_SPEED = 20.
private constant real WAVE_SPREAD = 20. //degrees
/*********************************************************
* NON-CONFIGURABLES
**********************************************************/
private group grp = CreateGroup()
private unit dummy
endglobals
/*********************************************************
* CONFIGURABLE FUNCTIONS
**********************************************************/
private function GetDamage takes integer i returns real
return 25 + i * 25.
endfunction
private function FilterMore takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
/*********************************************************
* NON-CONFIGURABLE BELOW THIS BLOCK
**********************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct TailSurge
unit caster
integer level
real angle
real distance
real damage
real x
real y
effect sfx
private method destroyEx takes real x, real y returns nothing
local integer i = 0
local real count
local real angleEx
local real gap
local real spread
local real x1
local real y1
if ENABLE_BACK_SURGE then
set spread = WAVE_SPREAD*BACK_WAVE_COUNT
set count = BACK_WAVE_COUNT
set angleEx = ((.angle * bj_RADTODEG) - (spread/2)) * bj_DEGTORAD
set gap = WAVE_SPREAD * bj_DEGTORAD
call SetUnitOwner(dummy, GetOwningPlayer(.caster), false)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitPosition(dummy, x, y)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, .level)
loop
set i = i+1
set angleEx = angleEx + gap
set x1 = x - 100 * Cos(angleEx)
set y1 = y - 100 * Sin(angleEx)
call IssuePointOrderById(dummy, ORDER_ID, x1, y1)
exitwhen i==BACK_WAVE_COUNT
endloop
call UnitRemoveAbility(dummy, DUMMY_SPELL_ID)
endif
call DestroyEffect(.sfx)
call SetUnitInvulnerable(.caster, false)
call SetUnitPathing(.caster, true)
set .caster = null
set .sfx = null
call .stopPeriodic()
call .deallocate()
endmethod
private method periodic takes nothing returns nothing
local unit first
local real x
local real y
local real x1 = GetUnitX(.caster)
local real y1 = GetUnitY(.caster)
if .distance > 0 then
set .distance = .distance - THRUST_SPEED
set x = x1+THRUST_SPEED*Cos(.angle)
set y = y1+THRUST_SPEED*Sin(.angle)
if IsTerrainWalkable(x, y) then
call SetUnitX(.caster, x)
call SetUnitY(.caster, y)
call GroupEnumUnitsInRange(grp, x1, y1, AOE, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(.caster, GetOwningPlayer(first)) and FilterMore(first) then
call UnitDamageTarget(.caster, first, .damage, false, false, ATK, DMG, null)
endif
call GroupRemoveUnit(grp, first)
endloop
else
call .destroyEx(x1, y1)
endif
else
call .destroyEx(x1, y1)
endif
endmethod
implement T32x
static method onCast takes nothing returns nothing
local thistype this = allocate()
local real x1 = GetSpellTargetX()
local real y1 = GetSpellTargetY()
local real dx
local real dy
set .caster = GetTriggerUnit()
set .level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .x = GetUnitX(.caster)
set .y = GetUnitY(.caster)
set dx = x1-.x
set dy = y1-.y
set .angle = Atan2(y1-.y, x1-.x)
set .distance = SquareRoot(dx*dx+dy*dy)
set .damage = GetDamage(.level)*T32_PERIOD
set .sfx = AddSpecialEffectTarget(THRUST_SFX, .caster, "origin")
call SetUnitInvulnerable(.caster, true)
call SetUnitPathing(.caster, false)
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
if ENABLE_BACK_SURGE then
set dummy = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(dummy, 'Aloc')
call UnitAddAbility(dummy, 'Arav')
call UnitRemoveAbility(dummy, 'Amov')
call UnitRemoveAbility(dummy, DUMMY_SPELL_ID)
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=59
//TESH.alwaysfold=0
library Fireballs uses SpellEffectEvent, T32 optional EngageLite
globals
private constant integer SPELL_ID = 'dea1' //faeriefire
private constant integer MISSILE_ID = 'e007'
private constant integer SKELETAL_ID = 'n009'
private constant real MAX_HEIGHT = 200
private constant real FIREBALL_MAX_HEIGHT = 300
private constant real HEIGHT_SPEED = 5
private constant real MISSILE_CREATION_INTERVAL = 0.5
private constant string START_SFX = "Abilities\\Spells\\Human\\DivineShield\\DivineShieldTarget.mdl"
private constant string EXPLODE_SFX = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private constant string CREATION_SFX = "Abilities\\Spells\\Undead\\RaiseSkeletonWarrior\\RaiseSkeleton.mdl"
private constant boolean ENABLE_CREATION = true
endglobals
private function GetDummyCount takes integer level returns integer
return 1 //3 * level
endfunction
private function GetChance takes integer level returns integer
return 20
endfunction
private function GetDuration takes integer level returns real
return 10.
endfunction
private function GetDamage takes integer level returns real
return 150. * level
endfunction
private struct Missile
unit m
unit caster
unit target
real damage
real a
real d
real h
integer chance
integer count
static real speed = 10
private method periodic takes nothing returns nothing
local unit first
local unit en
local real xM = GetUnitX(.m)
local real yM = GetUnitY(.m)
local integer i
if .d > 0 then
set .d = .d - speed
set .h = .h - ((speed/.d)*.h)
call SetUnitFlyHeight(.m, .h, 0)
call SetUnitX(.m, xM + speed * Cos(.a))
call SetUnitY(.m, yM + speed * Sin(.a))
else
call GroupEnumUnitsInRange(enumG, xM, yM, 250, null)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, GetOwningPlayer(.m)) then
call UnitDamageTarget(.m, first, .damage, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DEATH, null)
endif
call GroupRemoveUnit(enumG, first)
endloop
call DestroyEffect(AddSpecialEffect(EXPLODE_SFX, xM, yM))
if ENABLE_CREATION then
if GetRandomInt(1, 100) < .chance then
set i = 0
loop
set i = i+1
set en = CreateUnit(GetOwningPlayer(.m), SKELETAL_ID, xM, yM, 0)
call GroupAddUnit(udg_BossDeathGroup, en)
call DestroyEffect(AddSpecialEffect(CREATION_SFX, GetUnitX(en), GetUnitY(en)))
static if LIBRARY_EngageLite then
call EngageSingle(en, .target)
endif
set en = null
exitwhen i==.count
endloop
endif
endif
call UnitApplyTimedLife(.m, 'BTLF', 0.1)
set .m = null
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
static method run takes unit caster, unit target, real damage, integer chance, integer count returns nothing
local thistype this = create()
local real x = GetUnitX(caster)
local real y = GetUnitY(caster)
local real x1 = GetUnitX(target)
local real y1 = GetUnitY(target)
set .m = CreateUnit(GetOwningPlayer(caster), MISSILE_ID, x, y, 0)
call SetUnitFlyHeight(.m, h + 20, 0)
set .target = target
set .caster = caster
set .damage = damage
set .chance = chance
set .count = count
set .a = Atan2(y1 - y, x1 - x)
set .d = SquareRoot((x - x1) * (x - x1)+(y - y1)*(y - y1))
set .h = FIREBALL_MAX_HEIGHT
call UnitAddAbility(.m, 'Arav')
call .startPeriodic()
endmethod
endstruct
private struct FireballsFly
private unit caster
private unit target
private real damage
private real highHeight
private real lowHeight
private real duration
private real interval
private integer chance
private integer count
private effect sfx
private method periodic takes nothing returns nothing
if MAX_HEIGHT > .lowHeight then
set .lowHeight = .lowHeight + HEIGHT_SPEED
call SetUnitFlyHeight(.caster, .lowHeight, 0)
else
if .duration > 0 then
set .duration = .duration - T32_PERIOD
set .interval = .interval + T32_PERIOD
if .interval > MISSILE_CREATION_INTERVAL then
set .interval = 0
//run codes here
call Missile.run(.caster, .target, .damage, .chance, .count)
endif
else
if .highHeight > 0 then
set .highHeight = .highHeight - HEIGHT_SPEED
call SetUnitFlyHeight(.caster, .highHeight, 0)
else
call SetUnitFlyHeight(.caster, 0, 0)
call PauseUnit(.caster, false)
call SetUnitInvulnerable(.caster, false)
call SetUnitAnimation(.caster, "stand")
call DestroyEffect(.sfx)
set .sfx = null
set .caster = null
set .target = null
call .stopPeriodic()
call .deallocate()
endif
endif
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer dummyCount
local integer level
set .caster = GetTriggerUnit()
set .target = GetSpellTargetUnit()
set .interval = 0
set .lowHeight = 0
set .highHeight = MAX_HEIGHT
set level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .duration = GetDuration(level)
set .damage = GetDamage(level)
set .chance = GetChance(level)
set .count = GetDummyCount(level)
set .sfx = AddSpecialEffectTarget(START_SFX, .caster, "chest")
call PauseUnit(.caster, true)
call UnitAddAbility(.caster, 'Arav')
call UnitRemoveAbility(.caster, 'Arav')
call SetUnitInvulnerable(.caster, true)
call SetUnitAnimation(.caster, "channel")
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=24
//TESH.alwaysfold=0
library HealingStrike uses SpellEffectEvent, T32, DummyInstantCaster, SimpleFunctions
globals
private constant integer SPELL_ID = 'A030'
private constant integer DUMMY_SPELL_ID = 'val1' //healing strike, chainlightning
private constant string SFX_TELEPORT = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl"
private integer array utype
endglobals
private struct HealingStrike
unit valker
real interval
integer i
static unit dummy = null
private method periodic takes nothing returns nothing
if UnitAlive(.valker) then
set .interval = .interval + 0.03125
if .interval > 1 then
set .interval = 0
set .i = .i + 1
if .i==1 then
//opening the portal and teleporting valker
call SetUnitAnimation(udg_PortalValker, "birth")
call SetUnitPosition(.valker, 10106, -460)
call PauseUnit(.valker, true)
call SetUnitFacing(.valker, 90)
call DestroyEffect(AddSpecialEffect(SFX_TELEPORT, 10106, -460))
elseif .i==10 then
//creating dummy to heal valker
set dummy = CreateUnit(Player(15), utype[GetRandomInt(0,11)], 10086, 767, 270)
call DestroyEffect(AddSpecialEffect(SFX_TELEPORT, GetUnitX(dummy), GetUnitY(dummy)))
elseif .i==11 then
//casting healing strike to the dummy, healing valker
call DC.setDummyFrom(Player(11), GetUnitX(.valker), GetUnitY(.valker), DUMMY_SPELL_ID, 1)
call DC.castToUnit(dummy, DUMMY_SPELL_ID, 852119)
call SetUnitInvulnerable(.valker, false)
call SetWidgetLife(.valker, GetWidgetLife(.valker) + (GetWidgetLife(dummy)/2))
elseif .i==12 then
//closing the portal
call SetUnitAnimation(udg_PortalValker, "death")
elseif .i==20 then
call PauseUnit(.valker, false)
call SetUnitPropWindow(.valker, 1)
call DisableAttack(.valker, false)
set .valker = null
call .destroy()
endif
endif
else
set .valker = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
static method onCast takes nothing returns nothing
local thistype this = allocate()
set .valker = GetTriggerUnit()
set .interval = 0
set .i = 0
call DestroyEffect(AddSpecialEffect(SFX_TELEPORT, GetUnitX(.valker), GetUnitY(.valker)))
call SetUnitPropWindow(.valker, 0)
call SetUnitInvulnerable(.valker, true)
call DisableAttack(.valker, true)
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set utype[0] = 'H004'
set utype[1] = 'O000'
set utype[2] = 'U001'
set utype[3] = 'U000'
set utype[4] = 'U003'
set utype[5] = 'N005'
set utype[6] = 'N001'
set utype[7] = 'N004'
set utype[8] = 'N007'
set utype[9] = 'H004'
set utype[10] = 'u008'
set utype[11] = 'n009'
endmethod
endstruct
endlibrary
//TESH.scrollpos=30
//TESH.alwaysfold=0
library Infernals uses DummyInstantCaster, SpellEffectEvent, T32, Table
globals
private constant integer SPELL_ID = 'A020' //fan of knives, infernals
private constant integer DUMMY_ID = 'e002'
private constant integer DUMMY_SPELL_ID = 'A003' //infernals
private constant integer DUMMY_SPELL_ORDER_ID = 852232 //inferno
private constant integer CREATION_INTERVAL = 1
private Table c
integer InfernalCount = 0
endglobals
private function GetInfernalMaxCount takes integer level returns integer
return 2 * level
endfunction
private struct Infernals
player owner
integer infernalMaxCount
integer infernalCount
integer level
real delay
private static method summonInfernals takes nothing returns nothing
local thistype this
if GetSummoningUnit()==DC.dummy and GetUnitTypeId(GetSummonedUnit())=='ninf' then
set c[GetHandleId(GetSummonedUnit())] = c[GetHandleId(GetSummoningUnit())]
endif
endmethod
private static method deathInfernals takes nothing returns nothing
local thistype this
local integer id = GetHandleId(GetTriggerUnit())
if c.has(id) then
set this = c[id]
set .infernalCount = .infernalCount - 1
if .infernalCount==0 then
set InfernalCount = 0
endif
endif
endmethod
private method periodic takes nothing returns nothing
local real x
local real y
set .delay = .delay + T32_PERIOD
if .delay > CREATION_INTERVAL then
set .delay = 0
if .infernalMaxCount > infernalCount then
set .infernalCount = infernalCount + 1
set InfernalCount = InfernalCount + 1
set x = GetUnitX(udg_HERO)
set y = GetUnitY(udg_HERO)
set c[GetHandleId(DC.dummy)] = this
call DC.setDummyFrom(.owner, x, y, DUMMY_SPELL_ID, .level)
call DC.castToPoint(x, y, DUMMY_SPELL_ID, DUMMY_SPELL_ORDER_ID)
else
set .owner = null
call .stopPeriodic()
call .deallocate()
endif
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .infernalMaxCount = GetInfernalMaxCount(.level)
set .infernalCount = 0
set .owner = GetTriggerPlayer()
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.summonInfernals) //valker's dummy
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.deathInfernals)
set c = Table.create()
endmethod
endstruct
endlibrary
//TESH.scrollpos=81
//TESH.alwaysfold=0
/**************************************************************
EggHatch v1.0
by mckill2009
***************************************************************/
library EggHatch uses SpellEffectEvent optional EngageLite
globals
/*******************************************************
* CONFIGURABLE GLOBALS
********************************************************/
private constant integer SPELL_ID = 'A031'
private constant integer DUMMY_ID = 'e002'
private constant integer MAX_UNIT = 2
private constant string MODEL_SFX1 = "Doodads\\Dungeon\\Terrain\\EggSack\\EggSack0.mdl"
private constant string MODEL_SFX2 = "Doodads\\Dungeon\\Terrain\\EggSack\\EggSack1.mdl"
private constant string EXPLODE_SFX = "Objects\\Spawnmodels\\Demon\\DemonLargeDeathExplode\\DemonLargeDeathExplode.mdl"
private constant real HATCH_DELAY = 7
private constant real EGG_SIZE = 0.7
private constant real HATCHLING_SCALE = 0.7
private integer array unitType
endglobals
private function CountHatched takes integer level returns integer
return 2 * level + 3
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct EggHatch
private unit caster
private unit egg
private player owner
private effect eggModel
private integer count
private real hatch
private real x
private real y
private static real interval = 1.0
private static timer t = CreateTimer()
private static integer ind = 0
private static integer array indAR
private static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local integer c
local unit u
local real angle
local real offset
loop
set i = i+1
set this = indAR[i]
if .hatch > 0 then
set .hatch = .hatch - interval
else
set c = 0
loop
set c = c + 1
set u = CreateUnit(.owner, unitType[GetRandomInt(1,2)], .x, .y, 0)
call SetUnitScale(u, HATCHLING_SCALE, HATCHLING_SCALE, HATCHLING_SCALE)
call SetUnitX(u, .x)
call SetUnitY(u, .y)
set angle = GetRandomReal(0, 6)
set offset = I2R(GetRandomInt(100, 300))
call IssuePointOrderById(u, 851983, .x + offset * Cos(angle), .y + offset * Sin(angle))
static if LIBRARY_EngageLite then
call EngageSingle(u, udg_HERO)
endif
exitwhen c==.count
endloop
set u = null
call DestroyEffect(AddSpecialEffect(EXPLODE_SFX, .x, .y))
call DestroyEffect(.eggModel)
call KillUnit(.egg)
set .eggModel = null
set .caster = null
set .egg = null
set .owner = null
call .destroy()
set indAR[i] = indAR[ind]
set ind = ind - 1
set i = i - 1
if ind==0 then
call PauseTimer(t)
endif
endif
exitwhen i==ind
endloop
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .caster = GetTriggerUnit()
set .owner = GetTriggerPlayer()
set .x = GetUnitX(.caster)-100 * Cos(GetUnitFacing(.caster)*bj_DEGTORAD)
set .y = GetUnitY(.caster)-100 * Sin(GetUnitFacing(.caster)*bj_DEGTORAD)
set .hatch = HATCH_DELAY
set .count = CountHatched(level)
set .egg = CreateUnit(.owner, DUMMY_ID, .x, .y, 0)
call SetUnitScale(.egg, EGG_SIZE, EGG_SIZE, EGG_SIZE)
call SetUnitFlyHeight(.egg, 0, 0)
if GetRandomInt(1,2)==1 then
set .eggModel = AddSpecialEffectTarget(MODEL_SFX1, .egg, "origin")
else
set .eggModel = AddSpecialEffectTarget(MODEL_SFX2, .egg, "origin")
endif
if ind==0 then
call TimerStart(t, interval, true, function thistype.periodic)
endif
set ind = ind + 1
set indAR[ind] = this
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set unitType[1] = 'nspg'
set unitType[2] = 'nspb'
endmethod
endstruct
endlibrary
//TESH.scrollpos=63
//TESH.alwaysfold=0
/**************************************************************
Spit v1.0
by mckill2009
***************************************************************/
library Spit uses T32, SpellEffectEvent, TimedSfx
globals
/*******************************************************
* CONFIGURABLE GLOBALS
********************************************************/
private constant integer SPELL_ID = 'A032'
private constant integer DUMMY_SPELL_ID = 'A033'
private constant integer BUFF_IF = 'Bslo' //to disable stacking
private constant integer ORDER_ID = 852075
private constant integer DUMMY_ID = 'e00H'
private constant string SFX = "Abilities\\Spells\\NightElf\\CorrosiveBreath\\ChimaeraAcidTargetArt.mdl"
private constant real SPEED = 10
private constant real RANGE = 300
private group g = CreateGroup()
endglobals
private function FilterOutMoreUnits takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL) or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
endfunction
private struct Spit
private unit web
private player owner
private real angle
private real distance
private real x
private real y
private integer level
private method periodic takes nothing returns nothing
local unit target
local real xWeb = GetUnitX(.web)
local real yWeb = GetUnitY(.web)
if .distance > 0 then
set .distance = .distance - SPEED
call SetUnitX(.web, xWeb + SPEED * Cos(.angle))
call SetUnitY(.web, yWeb + SPEED * Sin(.angle))
else
call UnitApplyTimedLife(.web, 'BTLF', 0.1)
call TimedSfx.start(AddSpecialEffect(SFX, xWeb, yWeb), 1.0)
call UnitAddAbility(.web, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(.web, DUMMY_SPELL_ID, .level)
call GroupEnumUnitsInRange(g, xWeb, yWeb, RANGE, null)
loop
set target = FirstOfGroup(g)
exitwhen target==null
if UnitAlive(target) and IsUnitEnemy(target, .owner) and FilterOutMoreUnits(target) then
call IssueTargetOrderById(.web, ORDER_ID, target)
endif
call GroupRemoveUnit(g, target)
endloop
set .web = null
set .owner = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .x = GetUnitX(GetTriggerUnit())
set .y = GetUnitY(GetTriggerUnit())
set .angle = GetAngle(.x, .y, GetSpellTargetX(), GetSpellTargetY())
set .distance = GetDistance(.x, .y, GetSpellTargetX(), GetSpellTargetY())
set .owner = GetTriggerPlayer()
set .web = CreateUnit(.owner, DUMMY_ID, .x, .y, (.angle*bj_RADTODEG)+180)
call UnitRemoveAbility(.web, 'Amov')
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=75
//TESH.alwaysfold=0
/*********************************************************
SmartMines v1.0
by mckill2009
**********************************************************
**********************************************************/
library SmartMines uses T32, SpellEffectEvent, Jumper, RandomUnit
globals
private constant integer SPELL_ID = 'A035'
private constant integer DUMMY_ID = 'e00I'
private constant integer MAX_OFFSET = 400
private constant integer HEIGHT = 250
private constant real SEARCH_TARGET = 400
private constant real ESCAPE_RANGE = 300
private constant real TARGET_SPEED = 7
private constant string DEATH_SFX = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
private constant boolean POINT_TARGET = true
endglobals
/*******************************************************
* CONFIGURABLE FUNCTIONS
********************************************************/
private function CountMine takes integer level returns integer
return 2 * level + 3
endfunction
private function GetAoeDamage takes integer level returns real
return 200.
endfunction
private function GetMineLifeDuration takes integer level returns real
return 10.
endfunction
private function GetDamage takes integer level returns real
return 10. * level + 20
endfunction
private function FilterOutMoreUnits takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
/*******************************************************
* NON-CONFIGURABLE FUNCTIONS
********************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
endfunction
private function GetParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)//Standard parabola
endfunction
struct SmartMines
unit mine
unit target
player owner
real aoeDam
real angle
real maxDist
real distX
real damage
real life
boolean lock
boolean searching
private static player ownerEX
private static group g = CreateGroup()
private static method filters takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), ownerEX) and FilterOutMoreUnits(GetFilterUnit())
endmethod
private method damageThem takes real x, real y returns nothing
local unit first
call GroupEnumUnitsInRange(g, x, y, .aoeDam, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .owner) and FilterOutMoreUnits(first) then
call UnitDamageTarget(.mine, first, .damage, false, false, ATK, DMG, null)
endif
call GroupRemoveUnit(g, first)
endloop
call DestroyEffect(AddSpecialEffect(DEATH_SFX, x, y))
call KillUnit(.mine)
set .mine = null
set .target = null
set .owner = null
call .stopPeriodic()
call .deallocate()
endmethod
private method periodic takes nothing returns nothing
local real x = GetUnitX(.mine)
local real y = GetUnitY(.mine)
local real xTar
local real yTar
local unit first
if UnitAlive(.mine) and .life > 0 then
if .searching then
if UnitAlive(.target) then
set xTar = GetUnitX(.target)
set yTar = GetUnitY(.target)
if .lock then
set .distX = .distX - TARGET_SPEED
call SetUnitX(.mine, x + TARGET_SPEED * Cos(.angle))
call SetUnitY(.mine, y + TARGET_SPEED * Sin(.angle))
call SetUnitFlyHeight(.mine, GetParabolaZ(HEIGHT, .maxDist, .distX), 0)
if GetUnitFlyHeight(.mine) <= 1 then
if GetDistance(x, y, xTar, yTar) > ESCAPE_RANGE then
set .target = null
else
//if target is in range of mine, it will damage all filtered enemies
call .damageThem(x, y)
endif
endif
else
set .angle = GetAngle(x, y, xTar, yTar)
set .maxDist = GetDistance(x, y, xTar, yTar)
set .distX = .maxDist
set .lock = true
endif
else
call SetUnitFlyHeight(.mine, 0, 0)
set .lock = false
set ownerEX = .owner
set .target = GetRandomUnitInArea(x, y, SEARCH_TARGET, Filter(function thistype.filters))
set .life = .life - T32_PERIOD
endif
else
if GetUnitFlyHeight(.mine) <= 1 then
set .searching = true
endif
endif
else
call .damageThem(x, y)
endif
endmethod
implement T32x
private static method start takes unit mine, integer level returns nothing
local thistype this = allocate()
set .mine = mine
set .target = null
set .searching = false
set .lock = false
set .owner = GetOwningPlayer(mine)
set .damage = GetDamage(level)
set .aoeDam = GetAoeDamage(level)
set .life = GetMineLifeDuration(level)
set .angle = 0
set .distX = 0
set .maxDist = 0
call .startPeriodic()
endmethod
private static method onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
local integer count = CountMine(level)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real x1
local real y1
local real angle
local integer offset
local integer i = 0
local unit m
local Jumper lo
if POINT_TARGET then
set x = GetSpellTargetX()
set y = GetSpellTargetY()
else
set x = GetUnitX(u)
set y = GetUnitY(u)
endif
loop
set i = i + 1
set angle = GetRandomReal(0,6)
set offset = GetRandomInt(100, MAX_OFFSET)
set x1 = x + offset * Cos(angle)
set y1 = y + offset * Sin(angle)
set m = CreateUnit(GetTriggerPlayer(), DUMMY_ID, x, y, 0)
set lo = lo.register(m, x1, y1, TARGET_SPEED)
call lo.setHeight(HEIGHT)
call lo.start()
call SmartMines.start(m, level)
exitwhen i==count
endloop
set m = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=105
//TESH.alwaysfold=0
scope Omnislash // requires TimerUtils, GT
globals
private constant integer SPELL_ID = 'sak1' // The ID of the ability
private constant real AREA = 450. // Range of the ennumeration
private constant real LOOP = .4 // Period
private constant string SFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl" // Principal SFX
private constant string SFX2 = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl" // SFX created in each slash
private constant string SFXATTACH = "weapon"
private constant string SFX2ATTACH = "chest"
private constant string ATTACKSTG = "attack slam" // You can change it to "attack slam" if you want
private constant boolean INVULNERABLE = true // Dooes Yurnero becomes invulnerable while omnislashing?
private constant attacktype ATTACKTYPE = ATTACK_TYPE_HERO
private constant damagetype DAMAGETYPE = DAMAGE_TYPE_NORMAL
private group Enum_Group = CreateGroup() // Group for the enum, is always empty
private constant integer MAX_ENUM_UNITS = 30 // Max size of the array...
endglobals
private function GetDamage takes integer level returns real
return 25. * level + 25
endfunction
private struct OmniStruct
private effect sfx // Phoenix effect
private player p // Owning player of casting unit
private unit c // Casting unit
private unit t // Target unit
private integer lvl
private integer cnt// Number of slashes
private timer tm
private real ang // Angle used to define a random position around the target
private real cX
private real cY
private real tX
private real tY
private real dmg // Damage, it's a random number between 150 and 250 in each slash
private integer num=2 // Number of actual slashes
private static unit array targets[MAX_ENUM_UNITS] // Used for group the targets and select a random one from there
private static integer index=0
method onDestroy takes nothing returns nothing
local integer i=0
call DestroyEffect(.sfx)
call SetUnitVertexColor(.c,255,255,255,255)
call SetUnitInvulnerable(.c,false)
call SetUnitPathing(.c,true)
call ReleaseTimer(.tm)
loop
exitwhen i>30
set .targets[i]=null // Set all the targets to null
set i=i+1
endloop
set .sfx=null
set .p=null
set .c=null
set .t=null
set .tm=null
endmethod
method Slash takes nothing returns nothing
set .ang=GetRandomReal(1.,360.)
set .tX=GetUnitX(.t)+50.*Cos(.ang*bj_DEGTORAD)
set .tY=GetUnitY(.t)+50.*Sin(.ang*bj_DEGTORAD)
call SetUnitFacing(.c,Atan2(.tY-.cY,.tX-.cX)*bj_RADTODEG)
//set .dmg=GetRandomReal(150.,250.)
call SetUnitX(.c,.tX)
call SetUnitY(.c,.tY)
call SetUnitAnimation(.c,ATTACKSTG)
call DestroyEffect(AddSpecialEffectTarget(SFX2,.c,SFX2ATTACH))
call UnitDamageTarget(.c,.t,.dmg,true,true,ATTACKTYPE,DAMAGETYPE,null)
call IssueTargetOrder(.c,ATTACKSTG,.t)
endmethod
static method Filter takes nothing returns boolean
local thistype this=GetTimerData(GetExpiredTimer())
set .t=GetFilterUnit()
if IsUnitEnemy(.t,.p) and GetWidgetLife(.t)>.405 and not IsUnitType(.t,UNIT_TYPE_STRUCTURE) and IsUnitVisible(.t,.p) then
set .targets[.index]=.t // If the conditions are true, then add the filter unit to the array
set .index=.index+1
endif
return false // No unit enters the group
endmethod
static method onLoop takes nothing returns nothing
local thistype this=GetTimerData(GetExpiredTimer())
local integer i=0
if .num>.cnt then
call .destroy()
else
set .num=.num+1
call GroupEnumUnitsInRange(Enum_Group,.tX,.tY,AREA,Condition(function thistype.Filter))
set .t=.targets[GetRandomInt(0,.index-1)]
if .t!=null then
call .Slash()
set .index=0
loop
exitwhen i>MAX_ENUM_UNITS
set .targets[i]=null // Cleaning the array
set i=i+1
endloop
else
call .destroy()
endif
endif
endmethod
static method create takes nothing returns thistype
local thistype this=.allocate()
set .tm=NewTimer()
set .c=GetTriggerUnit()
set .cX=GetUnitX(.c)
set .cY=GetUnitY(.c)
set .t=GetSpellTargetUnit()
set .p=GetOwningPlayer(.c)
set .lvl=GetUnitAbilityLevel(.c,SPELL_ID)
set .dmg = GetDamage(.lvl)
// Number of slashs
set .cnt=3
if .lvl==2 then
set .cnt=5
elseif .lvl==3 then
set .cnt=8
endif
// End
call SetUnitVertexColor(.c,255,255,255,125)
call SetUnitPathing(.c,false)
call SetUnitInvulnerable(.c,INVULNERABLE)
call .Slash()
set .sfx=AddSpecialEffectTarget(SFX,.c,SFXATTACH)
call SetTimerData(.tm,this)
call TimerStart(.tm,LOOP,true,function thistype.onLoop)
return this
endmethod
endstruct
private function Execute takes nothing returns nothing
call OmniStruct.create()
endfunction
public function InitTrig takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function Execute)
endfunction
endscope
//TESH.scrollpos=110
//TESH.alwaysfold=0
library FrozenOrb initializer Init requires xebasic
//------------------------------------------------------<>
// 'FROZEN ORB'
//
// Submitted by Eccho 2010-02-05 (1.0)
// 2010-02-15 (1.01)
// Changelog can be found at the official submission post
// at
// http://www.hiveworkshop.com/forums/spells-569/frozen-orb-v1-0-a-158026/
//
// Give credits if used!!
//------------------------------------------------------<>
//------------------------------------------------------<>
// 'Introduction'
//
// The reason why I did this spell is because I have not
// seen yet any frozen orb spell which is properly
// functional, or that I think does not look diablo 2'ish
// enough (Vexorian made a frozen orb spell, I know, but
// it isn't working at all anymore. This spell was indeed
// an interpretation of the sorceress' spell Frozen Orb
// in Diablo 2.
//
// A small note before I continue, due to some
// complications with vJass I did not manage certain
// things I had in mind in the beginning. The code may
// indeed (the callback especially) be written in
// another way, and I tried that, and failed.
// Suggestions are welcome.
//
// A second note is that, frozen orb by default demands
// some attention in the memory. Casting this multiply
// times in a row without a cooldown, is going to have
// some consequences. This is nothing I can change.
// Adapt the XE_ANIMATION_PERIOD if needed.
//
// Third note is, you will not be able to control the
// amount of bolts spawned while the orb is alive. These
// are only depending on the animation period. This is
// also how Diablo 2 handles Frozen Orb. Each bolt is
// released each frame until the orb vanishes.
//
// 'Implementation instructions'
//
// First of all, you will need this library and the
// xebasic library. Copy these to your map.
//
// Second, you need the Universal Dummy unit found in the
// unit editor. Don't forget to import the dummy.mdx and
// set the dummy to use it as a model.
//
// Important: In order to apply the slow
// buff to the affected unit, make sure the universal
// dummy has:
// A cooldown time > 0
// Damage base != 0 (default -1)
// Damage die > 0 (default 1)
// A fast projectile speed
// A decent range
// Targets allowed - At least 'ground'
// Weapon type - missile
// Attack 1 enabled
// (if the unit does not have a movement speed, add it)
// And the usual stuff
//
// Next copy the ability which will cast the spell, and
// the ability which will apply the buff, and modify the
// rawcode id's in the constants below. Don't forget to
// make sure the dummy unit id in the xebasic library is
// set right as well.
//
// 'Credits'
//
// Vexorian - JassHelper, xebasic
// PitzerMike & MindWorX - JNGP
// Blizzard - Once again for some tooltip inspiration.
//
// Give proper credits if used!!
//------------------------------------------------------<>
//------------------------------------------------------<>
// 'Native including'
//
// If you have this in your code somewhere else,
// make sure to not double define it.
//------------------------------------------------------<>
//native UnitAlive takes unit id returns boolean
//------------------------------------------------------<>
// 'Configuration section'
//
// Change the spell to fit your needs.
//------------------------------------------------------<>
globals
private constant integer ABILITY_ID = 'A038' //The ability triggering the spell
private constant integer FROST_SLOW_ID = 'A037' //The ability containing the frost attack. It the duration isn't altered of the already existing one in wc3, use that id instead.
private constant integer FROST_SLOW_BUFF = 'Bfro' //The buff which is used in the slow attack ability
//Art of the main orb unit
private constant string ORB_ART = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
private constant integer ORB_SPEED = 800 //Max travel speed
private constant integer ORB_RANGE = 900 //Max travel distance
private constant integer ORB_RADIUS = 32 //The radius the orb has. It is required as a polar projection offset when the orb explodes in the end
private constant integer ORB_HEIGHT = 48 //The z-height from the ground the orb will travel
private constant real ORB_SCALE = 1.0 //The size/scaling of the orb (I believe the current model have some issues here, but it works with other models)
//Art of the released bolts. The bolts released when the orb explodes uses this art too.
private constant string MISSILE_ART = "Abilities\\Weapons\\LichMissile\\LichMissile.mdl"
private constant integer MISSILE_SPEED = 450 //...
private constant integer MISSILE_RANGE = 400 //...
private constant integer MISSILE_RADIUS = 32 //Bolt radius. It is used to check the collision of which enemies the bolts will hit and damage.
private constant integer MISSILE_HEIGHT = 48 //...
private constant real MISSILE_SCALE = 0.5 //...
private constant real MISSILE_RAD_OFFSET = 3*bj_PI/5 //Each bolt is released with a certain angle offset (in radians), and is defined by this constant. Each bolt is added this constant + the previous angle.
private constant integer MISSILE_AMOUNT_MAX = 100 //Keep this constant at a secure amount. The spell could go very wrong if this value is less than the maximum amount of bolts released. 100 is used by default.
private constant integer ORB_EXPLODE_AMOUNT = 12 //Amount of bolts released when the orb explodes
private constant integer ORB_EXPLODE_SPEED = 500 //Special bolt travel speed
private constant integer ORB_EXPLODE_RANGE = 400 //Special bolt travel distance
//If a unit shatters, this effect will be created at it's position
private constant string IMPACT_SHATTER_ART = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
private constant integer IMPACT_SHATTER_PER_BASE = 8 //Percental base chance to shatter a unit which dies from the spell
private constant integer IMPACT_SHATTER_PER_INC = 4 //Percental increament chance per level to shatter a unit which dies from the spell (example, if base is 8 and incr is 4, each level adds 4 chance)
private constant integer IMPACT_DAMAGE_BASE = 20 //Base damage
private constant integer IMPACT_DAMAGE_INC = 10 //Increamental damage (works in the same way as the shatter base/incr fields)
//Self-explanatory
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
//------------------------------------------------------<>
// 'Damage filter'
//
// Add more options as desired.
// Some info: It is used as a condition not a boolexpr,
// thus, not going to give any issue with IsUnitType.
//------------------------------------------------------<>
private function TargetFilter takes unit enemy, player caster, real x, real y returns boolean
return UnitAlive(enemy) and IsUnitEnemy(enemy, caster) and IsUnitType(enemy, UNIT_TYPE_GROUND) and IsUnitInRangeXY(enemy, x, y, MISSILE_RADIUS)
endfunction
//------------------------------------------------------<>
// 'Shatter filter'
//
// Add more options as desired.
// It determines the units which may shatter
// The filter does not need to contain the same context
// as the damage filter. It is only potentially ran after
// the target filter have became true.
//------------------------------------------------------<>
private function ShatterFilter takes unit enemy returns boolean
return not IsUnitType(enemy, UNIT_TYPE_MECHANICAL) and not IsUnitType(enemy, UNIT_TYPE_HERO) and not IsUnitType(enemy, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
//------------------------------------------------------<>
// 'Other constants'
//
// These should not be altered by default, but if you
// know what you are doing, or see a way to use other
// constants instead, feel free.
//------------------------------------------------------<>
globals
private constant real ORB_TMAX = 1.0*ORB_RANGE/ORB_SPEED
private constant real MISSILE_TMAX = 1.0*MISSILE_RANGE/MISSILE_SPEED
private constant real EXPLODE_TMAX = 1.0*ORB_EXPLODE_RANGE/ORB_EXPLODE_SPEED
private constant real RAD_BETWEEN_EXPL = bj_PI*2/ORB_EXPLODE_AMOUNT
private constant group ENUM_GROUP = CreateGroup()
private constant timer ANIM_TIMER = CreateTimer()
endglobals
//------------------------------------------------------<>
// 'Spell code'
//
// If you even think up of an optimize fully working
// version, tell me about it.
//------------------------------------------------------<>
private struct missile
unit obj
effect art
real sx
real sy
real xvel
real yvel
static method create takes string art, player p, real x, real y, real z, real rad, integer speed, real scale returns thistype
local thistype this = thistype.allocate()
set this.obj = CreateUnit(p, XE_DUMMY_UNITID, x, y, rad*bj_RADTODEG)
call UnitAddAbility(this.obj, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(this.obj, XE_HEIGHT_ENABLER)
call SetUnitFlyHeight(this.obj, z, 0)
call SetUnitScale(this.obj, scale, scale, scale)
call SetUnitAnimationByIndex(this.obj, 90)
call UnitRemoveAbility(this.obj, 'Aatk')
set this.art = AddSpecialEffectTarget(art, this.obj, "origin")
set this.sx = x
set this.sy = y
set this.xvel = speed*Cos(rad)
set this.yvel = speed*Sin(rad)
return this
endmethod
method clear takes nothing returns nothing
call DestroyEffect(this.art)
call KillUnit(this.obj)
set this.art = null
set this.obj = null
endmethod
endstruct
private struct spell
missile orb
real otick
missile array mis[MISSILE_AMOUNT_MAX]
real array mtick[MISSILE_AMOUNT_MAX]
integer mcount
integer mtot
missile array xpl[ORB_EXPLODE_AMOUNT]
real xtick //All exploding missiles have the same tickoffset
player owner
integer damage
integer chance
static thistype array tta
static integer tot = 0
static method callback takes nothing returns nothing
local thistype this
local missile m
local integer i = 0
local integer j
local integer k
local real x
local real y
local unit u
local unit v
loop
exitwhen (i >= thistype.tot)
set this = thistype.tta[i]
//Bolts goes here
set j = 0
loop
exitwhen (j >= this.mtot)
if (this.mis[j].obj != null) then
set this.mtick[j] = this.mtick[j]+XE_ANIMATION_PERIOD
set x = this.mis[j].sx+this.mis[j].xvel*this.mtick[j]
set y = this.mis[j].sy+this.mis[j].yvel*this.mtick[j]
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, XE_MAX_COLLISION_SIZE+MISSILE_RADIUS, null)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen (u == null)
call GroupRemoveUnit(ENUM_GROUP, u)
exitwhen (TargetFilter(u, this.owner, x, y))
endloop
//A nice BJ, wrapped anyway
if (this.mtick[j] <= MISSILE_TMAX and RectContainsCoords(bj_mapInitialPlayableArea, x, y) and u == null) then
call SetUnitX(this.mis[j].obj, x)
call SetUnitY(this.mis[j].obj, y)
else
if (u != null) then
call UnitDamageTarget(this.mis[j].obj, u, this.damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
if (UnitAlive(u)) then
if (GetUnitAbilityLevel(u, FROST_SLOW_BUFF) == 0) then
set v = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), XE_DUMMY_UNITID, x, y, 0)
call UnitAddAbility(v, FROST_SLOW_ID)
call UnitApplyTimedLife(v, 'BTLF', 1.0)
call IssueTargetOrder(v, "attackonce", u)
endif
elseif (GetRandomInt(0, 100) <= this.chance and ShatterFilter(u)) then
call DestroyEffect(AddSpecialEffect(IMPACT_SHATTER_ART, x, y))
call RemoveUnit(u)
endif
endif
//A note - instances are not destroyed until the end of the spell, when all instances are properly cleared.
call this.mis[j].clear()
set this.mcount = this.mcount-1
endif
endif
set j = j+1
endloop
//Orb goes here
if (this.orb.obj != null) then
set this.otick = this.otick+XE_ANIMATION_PERIOD
set x = this.orb.sx+this.orb.xvel*this.otick
set y = this.orb.sy+this.orb.yvel*this.otick
if (this.otick < ORB_TMAX and RectContainsCoords(bj_mapInitialPlayableArea, x, y)) then
call SetUnitX(this.orb.obj, x)
call SetUnitY(this.orb.obj, y)
set this.mis[this.mtot] = missile.create(MISSILE_ART, this.owner, x, y, MISSILE_HEIGHT, this.mtot*MISSILE_RAD_OFFSET, MISSILE_SPEED, MISSILE_SCALE)
set this.mtick[this.mtot] = 0
set this.mcount = this.mcount+1
set this.mtot = this.mtot+1
else
//Clears the orb
call this.orb.clear()
//Proceeds with creating special bolts, aka bolts released when the orb vanishes.
set j = 0
loop
exitwhen (j >= ORB_EXPLODE_AMOUNT)
set this.xpl[j] = missile.create(MISSILE_ART, this.owner, x+ORB_RADIUS*Cos(j*RAD_BETWEEN_EXPL), y+ORB_RADIUS*Sin(j*RAD_BETWEEN_EXPL), MISSILE_HEIGHT, j*RAD_BETWEEN_EXPL+bj_PI*0.25, ORB_EXPLODE_SPEED, MISSILE_SCALE)
set j = j+1
endloop
set this.xtick = 0
endif
else
//Special bolt stuff goes here
set this.xtick = this.xtick+XE_ANIMATION_PERIOD
set j = 0
loop
exitwhen (j >= ORB_EXPLODE_AMOUNT)
if (this.xpl[j].obj != null) then
set x = this.xpl[j].sx+this.xpl[j].xvel*this.xtick
set y = this.xpl[j].sy+this.xpl[j].yvel*this.xtick
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, XE_MAX_COLLISION_SIZE+MISSILE_RADIUS, null)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen (u == null)
call GroupRemoveUnit(ENUM_GROUP, u)
exitwhen (TargetFilter(u, this.owner, x, y))
endloop
if (this.xtick < EXPLODE_TMAX and RectContainsCoords(bj_mapInitialPlayableArea, x, y) and u == null) then
call SetUnitX(this.xpl[j].obj, x)
call SetUnitY(this.xpl[j].obj, y)
else
if (u != null) then
call UnitDamageTarget(this.xpl[j].obj, u, this.damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
if (UnitAlive(u)) then
if (GetUnitAbilityLevel(u, FROST_SLOW_BUFF) == 0) then
set v = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), XE_DUMMY_UNITID, x, y, 0)
call UnitAddAbility(v, FROST_SLOW_ID)
call UnitApplyTimedLife(v, 'BTLF', 1.0)
call IssueTargetOrder(v, "attackonce", u)
endif
elseif (GetRandomInt(0, 100) <= this.chance and ShatterFilter(u)) then
call DestroyEffect(AddSpecialEffect(IMPACT_SHATTER_ART, x, y))
call RemoveUnit(u)
endif
endif
//Clears a special bolt
call this.xpl[j].clear()
endif
endif
set j = j+1
endloop
endif
//Completely destroys the spell and all instances
if (this.mcount == 0 and this.orb.obj == null and this.xtick >= ORB_TMAX) then
set j = 0
loop
exitwhen (j >= this.mcount)
call this.mis[j].destroy()
set j = j+1
endloop
set j = 0
loop
exitwhen (j >= ORB_EXPLODE_AMOUNT)
call this.xpl[j].destroy()
set j = j+1
endloop
call this.orb.destroy()
call this.destroy()
set thistype.tot = thistype.tot-1
set thistype.tta[i] = thistype.tta[thistype.tot]
if (thistype.tot == 0) then
call PauseTimer(ANIM_TIMER)
endif
else
set i = i+1
endif
endloop
set v = null
endmethod
static method create takes unit su, real tx, real ty returns thistype
local thistype this = thistype.allocate()
local real x = GetUnitX(su)
local real y = GetUnitY(su)
local integer level = GetUnitAbilityLevel(su, ABILITY_ID)
set this.owner = GetOwningPlayer(su)
set this.orb = missile.create(ORB_ART, this.owner, x, y, ORB_HEIGHT, Atan2(ty-y, tx-x), ORB_SPEED, ORB_SCALE)
set this.otick = 0
set this.mcount = 0
set this.mtot = 0
set this.damage = IMPACT_DAMAGE_BASE+IMPACT_DAMAGE_INC*(level-1)
set this.chance = IMPACT_SHATTER_PER_BASE+IMPACT_SHATTER_PER_INC*(level-1)
set thistype.tta[thistype.tot] = this
if (thistype.tot == 0) then
call TimerStart(ANIM_TIMER, XE_ANIMATION_PERIOD, true, function thistype.callback)
endif
set thistype.tot = thistype.tot+1
return this
endmethod
endstruct
private function Evaluate takes nothing returns boolean
if (GetSpellAbilityId() == ABILITY_ID) then
call spell.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Filter(function Evaluate))
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ElectrifyingSmash initializer Init
// Electrifying Smash by The_Witcher
//The Mage lifts his enemy high into the sky to burn him with heavy lightnings.
//Then he smashes him to the ground, damaging all enemies in AOE.
// Setup Part
globals
// The Rawcode of the ability (Press Ctrl + D in object editor to show/hide rawcodes)
private constant integer ABILITY_ID = 'A034'
// The timer Interval (increase if laggs ingame)
private constant real INTERVAL = 0.03125
// The amount the speed changes each interval (a amount nearer to 0 will make the unit raise higher before falling)
private constant real GRAVITY = -9.81 * INTERVAL
// The attacktype with which the units in the AOE are damaged
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damagetype with which the units in the AOE are damaged
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
// The weapontype with which the units in the AOE are damaged
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// The effect created when the target crushes down on the ground
private constant string CRUSH_EFFECT = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
// The effect created when the target reached the sky
private constant string LIGHTNING_EFFECT = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"
// The Effect created when a lightning effect hits
private constant string DAMAGE_EFFECT = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBoltImpact.mdl"
// The Effect created when the AOE damage is dealt
private constant string AOE_DAMAGE_EFFECT = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
// The attachement point for all the effects
private constant string EFFECT_ATTACH = "chest"
endglobals
// Returns the damage each unit in AOE takes
private function GetDamage takes integer level returns real
return I2R(level) * 100
endfunction
// Returns the radius of the AOE
private function GetDamageRange takes integer level returns real
return I2R(level) * 50 + 150
endfunction
// Returns the maximal distance the unit gets shaked relative to the units original position
private function GetShakeRange takes integer level returns real
return I2R(level) * 7
endfunction
//-----------Don't modify anything below this line---------
private struct data
unit u
unit d
real z = 0
real x
real y
real Vz
integer t = 0
integer t2 = 0
integer phase = 1
timer tim = CreateTimer()
integer lvl
endstruct
globals
private hashtable h = InitHashtable()
private group g = CreateGroup()
private data temp
endglobals
private function FriendsOnly takes nothing returns boolean
return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(temp.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction
private function DamageGroup takes nothing returns nothing
call UnitDamageTarget(temp.d, GetEnumUnit(), GetDamage(temp.lvl), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call DestroyEffect(AddSpecialEffectTarget(AOE_DAMAGE_EFFECT, GetEnumUnit(), EFFECT_ATTACH))
endfunction
private function execute takes nothing returns nothing
local data dat = LoadInteger(h,GetHandleId(GetExpiredTimer()),0)
local real zz = dat.Vz
if dat.phase == 1 then
set dat.Vz = dat.Vz + GRAVITY
if dat.Vz <= 0 and zz > 0 then
set dat.phase = 2
set dat.t = R2I(0.1/INTERVAL) - 1
endif
set dat.z = dat.z + dat.Vz
call SetUnitFlyHeight(dat.u,dat.z,0)
elseif dat.phase == 2 then
set dat.t = dat.t + 1
if ModuloInteger(dat.t,2) == 0 then
call SetUnitX(dat.u,GetRandomReal(dat.x-GetShakeRange(dat.lvl), dat.x+GetShakeRange(dat.lvl)))
call SetUnitY(dat.u,GetRandomReal(dat.y-GetShakeRange(dat.lvl), dat.y+GetShakeRange(dat.lvl)))
endif
if dat.t == R2I(0.2/INTERVAL) then
if dat.t2 == 6 then
set dat.phase = 3
call SetUnitPosition(dat.u,dat.x,dat.y)
set dat.Vz = -20
endif
call DestroyEffect(AddSpecialEffectTarget(LIGHTNING_EFFECT, dat.u, EFFECT_ATTACH))
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, dat.u, EFFECT_ATTACH))
set dat.t = 0
set dat.t2 = dat.t2 + 1
endif
elseif dat.phase == 3 then
set dat.Vz = dat.Vz + GRAVITY
if dat.z <= 0 then
set dat.phase = 4
call DestroyEffect(AddSpecialEffect(CRUSH_EFFECT, dat.x, dat.y))
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, dat.u, EFFECT_ATTACH))
set temp = dat
call GroupEnumUnitsInRange(g,dat.x,dat.y,GetDamageRange(dat.lvl),Condition(function FriendsOnly))
call UnitRemoveAbility(dat.u,'Avul')
call ForGroup(g,function DamageGroup)
call GroupClear(g)
endif
set dat.z = dat.z + dat.Vz
call SetUnitFlyHeight(dat.u,dat.z,0)
else
call PauseUnit(dat.u,false)
call PauseTimer(dat.tim)
call DestroyTimer(dat.tim)
call FlushChildHashtable(h,GetHandleId(dat.tim))
call dat.destroy()
endif
endfunction
private function cast takes nothing returns boolean
local data dat
if GetSpellAbilityId() == ABILITY_ID then
set dat = data.create()
set dat.u = GetSpellTargetUnit()
set dat.d = GetTriggerUnit()
call UnitAddAbility(dat.u,'Amrf')
call UnitRemoveAbility(dat.u,'Amrf')
call PauseUnit(dat.u, true)
call UnitAddAbility(dat.u,'Avul')
set dat.Vz = 8
set dat.x = GetUnitX(dat.u)
set dat.y = GetUnitY(dat.u)
set dat.lvl = GetUnitAbilityLevel(dat.d, ABILITY_ID)
call SaveInteger(h,GetHandleId(dat.tim),0,dat)
call TimerStart(dat.tim,INTERVAL,true,function execute)
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t,Condition(function cast))
call Preload(CRUSH_EFFECT)
call Preload(LIGHTNING_EFFECT)
call Preload(DAMAGE_EFFECT)
call Preload(AOE_DAMAGE_EFFECT)
endfunction
endscope
//TESH.scrollpos=216
//TESH.alwaysfold=0
scope BouncingGlaives initializer init
/*************************************************************************************
* BouncingGlaves v1.0.5
* Discription: the caster hurls several glaives from his position. When the glaive hits an obstacle
* it will bounce to other direction. By default, each glaive can bounce five times if it
* keeps hitting obstacles. However, if an enemey is hit by the glaive, the glaive will
* damage the enemy before its vanishment.
*
*************************************************************************************
*
* Requires IsTerrainWalkable
*
*************************************************************************************
*
* Credits
*
* To Anitarf and Maker for makeing the IsTerrainWalkable library
* To Malhorne for making the vJass spell tutorial
* To PurgeandFire for answering all my questions
* To Vexorian for making vJass
*
************************************************************************************/
globals
//Configurations===============================================================
//The dummy ID, please do match this ID to your dummy ID.
private constant integer DUM_ID = 'e00D'
//The ability ID, please match this ID to your ability ID.
private constant integer ABID = 'A03O'
//The maximum number of glaives that can be hurled by the Hero. Do not set this more than 50.
private constant integer MAX_NUM_OF_GLAIVE = 30
//The number of glaive that will be hurled by the Hero at level one.
private constant integer NUM_OF_GLAIVE = 3
//How many more glaives when you level up (for each level).
private constant integer LEVEL_UP = 3
//The bouncing times of each glaive.
private constant integer BOUNCE = 5
//Whether the bouncing angle is random or not. If false, do remember to set the ANGLE bellow.
private constant boolean RANDOM_ANGLE = true
//The bouncing angle. Only works when the above RANDOM_ANGLE is false.
private constant real ANGLE = 180
//How fast the glaive moves.
private constant real SPEED = 25
//How far away the glaives will appear from the Hero.
private constant real DISTANCE = 300
//The attack type of the damage done to the enemy.
private constant attacktype AT = ATTACK_TYPE_CHAOS
//The damage type of the damage done to the enemy.
private constant damagetype DT = DAMAGE_TYPE_DEMOLITION
//The detection AOE of the glaive. If the distance between the enemy and the glaive exceeds this range, the glaive will not damage it.
private constant real RANGE = 128
//The effect when the glaive hits an enemy.
private constant string FX = "Abilities\\Spells\\Undead\\FrostArmor\\FrostArmorDamage.mdl"
//The attach point of the effect
private constant string ATTACH = "origin"
endglobals
//The damage amount upon hiting an enemy.
private constant function GetDamage takes integer level returns real
return 50. //35. + (10*level)
endfunction
//How many glaives for each level
private constant function GetNumberofGlaives takes integer level returns integer
return 6 //NUM_OF_GLAIVE + (level -1 ) * LEVEL_UP
endfunction
//Filter out which units should be damaged.
private function FilterUnits takes unit u, player p returns boolean
return UnitAlive(u) and IsUnitEnemy(u, p)
endfunction
//Configuration Ends====================================================================
globals
private integer deindex = -1
private timer t = CreateTimer()
private group g = CreateGroup()
endglobals
//native UnitAlive takes unit id returns boolean
//Uses this struct to store data.
private struct Data
integer array bounce[50] //bouncing times of a glaive
unit array dum[50] //glaive dmummy
real array angle[50] //bouncing angle
real dmg //damage when hits an enemy
real X //x coordinate of the spell casting unit
real Y //y coordinate of the spell casting unit
integer num //the number of glaives
player p
method destroy takes nothing returns nothing
if deindex == -1 then
call PauseTimer(t)
endif
set this.p = null
call this.deallocate()
endmethod
endstruct
globals
private Data array data
endglobals
private function Loop takes nothing returns nothing
local integer i = 0
local integer ii = 0
local integer iii = 0
local Data this
local real x
local real y
local boolean bb = false
local unit u
//Loop through all the instances
loop
exitwhen i > deindex
set this = data[i]
loop
exitwhen ii > this.num
//Calculate the new coordinates for the glaive
set x = GetUnitX(this.dum[ii]) + SPEED * Cos(this.angle[ii] * bj_DEGTORAD)
set y = GetUnitY(this.dum[ii]) + SPEED * Sin(this.angle[ii] * bj_DEGTORAD)
//Moves the glaive if it hasn't reach it maximum bouncing limite and the terrain is walkable
if this.bounce[ii] > 0 and IsTerrainWalkable(x,y) then
call SetUnitX(this.dum[ii],x)
call SetUnitY(this.dum[ii],y)
//Check if there are enemys near the glaive
call GroupEnumUnitsInRange(g,x,y,RANGE,null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g,u)
//If the glaive hits an enemy, does damage and remove the glaive
if FilterUnits(u,this.p) then
call UnitDamageTarget(this.dum[ii],u,this.dmg,true,false,AT,DT,null)
call DestroyEffect(AddSpecialEffectTarget(FX,u,ATTACH))
set this.bounce[ii] = 0
set u = null
//Only needs the loop to operate once, so ends it here.
exitwhen true
endif
endloop
else
//If the glaive hits an obstacle, it bounces.
static if RANDOM_ANGLE then
set this.angle[ii] = this.angle[ii] - GetRandomReal(90,180)
else
set this.angle[ii] = this.angle[ii] - ANGLE
endif
//Setting new locations for the bouncing glaive
set x = GetUnitX(this.dum[ii]) + SPEED * Cos(this.angle[ii] * bj_DEGTORAD)
set y = GetUnitY(this.dum[ii]) + SPEED * Sin(this.angle[ii] * bj_DEGTORAD)
call SetUnitX(this.dum[ii],x)
call SetUnitY(this.dum[ii],y)
//Reduce its remaining bouncing times
set this.bounce[ii] = this.bounce[ii] - 1
//If the glaive has reached its maximum bouncing times, it will be removed.
if this.bounce[ii] <= 0 then
call RemoveUnit(this.dum[ii])
set this.dum[ii] = null
//Checks if all glaives are removed
loop
exitwhen iii > this.num
if UnitAlive(this.dum[iii]) then
set bb = true
endif
set iii = iii + 1
endloop
set iii = 0
//If all glaives are removed then deallocates the instance
if not bb then
set data[i] = data[deindex]
set i = i - 1
set deindex = deindex - 1
//Ends the loop
set ii = this.num + 10
call this.destroy()
set bb = false
endif
endif
endif
set ii = ii + 1
endloop
set ii = 0
set i = i + 1
endloop
endfunction
private function onCast takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer i = 0
local real angle = 0
local real newX
local real newY
local integer n
local integer lvl = GetUnitAbilityLevel(u,ABID)
local Data this = Data.create()
//Stores data in the struct
set this.p = GetTriggerPlayer()
set this.X = GetUnitX(u)
set this.Y = GetUnitY(u)
set this.num = GetNumberofGlaives(lvl) - 1
set this.dmg = GetDamage(lvl)
set n = GetNumberofGlaives(lvl)
if this.num >= MAX_NUM_OF_GLAIVE then
set this.num = MAX_NUM_OF_GLAIVE - 1
set n = MAX_NUM_OF_GLAIVE
endif
//Creates glaives=======================================
loop
exitwhen i > this.num
set newX = this.X + DISTANCE * Cos(angle * bj_DEGTORAD)
set newY = this.Y + DISTANCE * Sin(angle * bj_DEGTORAD)
set this.dum[i] = CreateUnit(this.p,DUM_ID,newX,newY,angle)
set this.bounce[i] = BOUNCE + 1
set this.angle[i] = angle
set angle = angle + 360/n
set i = i + 1
endloop
//=======================================================
set deindex = deindex + 1
set data[deindex] = this
if deindex == 0 then
call TimerStart(t,0.0312500,true,function Loop)
endif
set u = null
return false
endfunction
private function run takes nothing returns boolean
if GetSpellAbilityId() == ABID then
call onCast()
endif
return false
endfunction
//===========================================================================
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition(function run))
set t = null //I don't think it needs to be nulled here, but nulling it won't hurt.
endfunction
endscope
//TESH.scrollpos=129
//TESH.alwaysfold=0
// **************************************************************************** //
// //
// Bone Blade //
// v1.6 //
// by: Dalvengyr //
// //
// Description //
// Launches a bone blade at targeted enemy unit. The bone blade will //
// attack the target several times. Each successful landed attack deals //
// pure damage. //
// //
// Requirements //
// - //
// //
// How to import //
// 1. Import all models and icons at Import Manager to your map //
// 2. Copy both dummy unit and spell to your map //
// 3. Copy this folder to your map //
// 4. Delete the variable creator trigger //
// //
// Credits //
// 1. BTNBonespear by Peekay //
// 2. BoneSpear.mdx by Peekay //
// 3. dummy.mdx by Vexorian //
// //
// Link //
// hiveworkshop.com/forums/spells-569/bone-blade-v1-0-a-255874/ //
// //
// **************************************************************************** //
// **************************************************************************** //
// //
// CONFIGURATION //
// //
// 1. Dummy unit's raw code at Object Editor //
constant function BB__DummyID takes nothing returns integer
return 'e002'
endfunction
// 2. Main spell raw code at Object Editor //
constant function BB__SpellID takes nothing returns integer
return 'A045'
endfunction
// 3. Accuracy of the spell. Just leave it //
constant function BB__Accuracy takes nothing returns real
return 0.0312500
endfunction
// 4. File path of the missile //
constant function BB__MissileSfx takes nothing returns string
return "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
endfunction
// 5. Attached sfx upon hit //
constant function BB__HitSfx takes nothing returns string
return "Abilities\\Weapons\\HydraliskImpact\\HydraliskImpact.mdl"
endfunction
// 6. Attachment point for hit sfx //
constant function BB__HitSfxPt takes nothing returns string
return "origin"
endfunction
// 7. Normal height Z for missiles //
constant function BB__MissileZ takes nothing returns real
return 40.0
endfunction
// 8. Higher toleration makes the missile become less precise //
// Toleration is additional angle to tolerate the missiles when they are aiming //
// for their target. So they can start dashing earlier even before they are //
// successfully facing at the target //
constant function BB__Toleration takes nothing returns real
return 0.0
endfunction
// 9. Hit radius of each missile. Maximum radius for missiles to hit/attack //
// their targets //
constant function BB__HitRadius takes nothing returns real
return 100.0
endfunction
// 10. Turn rate of missiles when they are dashing at target (at high speed) //
constant function BB__MissileTurnRateMin takes integer level returns real
return 1.0
endfunction
// 11. Turn rate of missiles when they are turning around (at low speed) //
// Logically, this one should have higher value than the other one (minimum) //
constant function BB__MissileTurnRateMax takes integer level returns real
return 4.0
endfunction
// 12. Speed of missiles when they are turning (aiming) for the target //
constant function BB__MissileSpeedMin takes integer level returns real
return 10.0
endfunction
// 13. Max speed of missiles when they are dashing at their targets //
constant function BB__MissileSpeedMax takes integer level returns real
return 50.0
endfunction
// 14. Acceleration rate after missiles are turning (aiming) //
constant function BB__MissileAcceleration takes integer level returns real
return 2.5
endfunction
// 15. Deacceleration (braking) rate after missiles hit/miss their target //
constant function BB__MissileDeacceleration takes integer level returns real
return 1.5
endfunction
// 16. Distance extension when missile starts to brake //
constant function BB__MissileBrakePoint takes integer level returns real
return 200.0
endfunction
// 17. Dealt damage on hit //
constant function BB__DealtDamage takes integer level returns real
return 30.0
endfunction
// 18. Lifespan duration for each missile //
constant function BB__Duration takes integer level returns real
return 10.0
endfunction
// 19. Maximum number of attacks before missiles are expired //
constant function BB__AttackCount takes integer level returns integer
return 1 + level
endfunction
// 20. Damage type of dealt damage //
constant function BB__DamageType takes nothing returns damagetype
return DAMAGE_TYPE_NORMAL
endfunction
// 21. Attack type of dealt damage //
constant function BB__AttackType takes nothing returns attacktype
return ATTACK_TYPE_CHAOS
endfunction
// 22. Weapon type of dealt damage //
constant function BB__WeaponType takes nothing returns weapontype
return WEAPON_TYPE_WHOKNOWS
endfunction
// 23. Decay time for missiles when death //
constant function BB__DecayTime takes nothing returns real
return 5.0
endfunction
// 24. Additional effect attached to the target on hit //
function BB__AdditionalEffect takes unit target returns nothing
// Put here //
call DestroyEffect(AddSpecialEffectTarget("Units\\NightElf\\Wisp\\WispExplode.mdl", target, "overhead"))
endfunction
// //
// END OF CONFIGURATION //
// //
// **************************************************************************** //
// Furthermore, edit them by your own risk!
// Function to get distance between given coordinates
function BB__GetDistance takes real x, real y, real xt, real yt returns real
return SquareRoot((xt - x) * (xt - x) + (yt - y) * (yt - y))
endfunction
// Function to return facing angle between coordinates (in radian)
function BB__GetAngle takes real x, real y, real xt, real yt returns real
return Atan2(yt - y, xt - x)
endfunction
// Function to check whether a unit is alive or not
function IsUnitAlive takes unit id returns boolean
return not IsUnitType(id, UNIT_TYPE_DEAD) and GetUnitTypeId(id) != 0
endfunction
function BB__Deindex takes integer i returns nothing
set udg_BB__Caster[i] = udg_BB__Caster[udg_BB__Total]
set udg_BB__Player[i] = udg_BB__Player[udg_BB__Total]
set udg_BB__Target[i] = udg_BB__Target[udg_BB__Total]
set udg_BB__Angle[i] = udg_BB__Angle[udg_BB__Total]
set udg_BB__Missile[i] = udg_BB__Missile[udg_BB__Total]
set udg_BB__Sfx[i] = udg_BB__Sfx[udg_BB__Total]
set udg_BB__Dx[i] = udg_BB__Dx[udg_BB__Total]
set udg_BB__Dc[i] = udg_BB__Dc[udg_BB__Total]
set udg_BB__State[i] = udg_BB__State[udg_BB__Total]
set udg_BB__SpeedMin[i] = udg_BB__SpeedMin[udg_BB__Total]
set udg_BB__SpeedMax[i] = udg_BB__SpeedMax[udg_BB__Total]
set udg_BB__Acceleration[i] = udg_BB__Acceleration[udg_BB__Total]
set udg_BB__Deacceleration[i] = udg_BB__Deacceleration[udg_BB__Total]
set udg_BB__BrakePoint[i] = udg_BB__BrakePoint[udg_BB__Total]
set udg_BB__TurnRateMax[i] = udg_BB__TurnRateMax[udg_BB__Total]
set udg_BB__TurnRateMin[i] = udg_BB__TurnRateMin[udg_BB__Total]
set udg_BB__Speed[i] = udg_BB__Speed[udg_BB__Total]
set udg_BB__Damage[i] = udg_BB__Damage[udg_BB__Total]
set udg_BB__Count[i] = udg_BB__Count[udg_BB__Total]
set udg_BB__Duration[i] = udg_BB__Duration[udg_BB__Total]
endfunction
function BB__onPeriodic takes nothing returns nothing
local integer i = 1
local real a
local real x
local real y
local real x2
local real y2
local unit f
loop
exitwhen i > udg_BB__Total
set x = GetUnitX(udg_BB__Missile[i])
set y = GetUnitY(udg_BB__Missile[i])
set x2 = GetUnitX(udg_BB__Target[i])
set y2 = GetUnitY(udg_BB__Target[i])
set a = BB__GetAngle(x, y, x2, y2)
set udg_BB__Duration[i] = udg_BB__Duration[i] - BB__Accuracy()
if udg_BB__State[i] then
if udg_BB__Count[i] > 0 then
// Detect the target as long as missile is still in dashing state
call GroupEnumUnitsInRange(udg_BB__Group, x, y, BB__HitRadius(), null)
loop
set f = FirstOfGroup(udg_BB__Group)
exitwhen f == null
if f == udg_BB__Target[i] and IsUnitAlive(udg_BB__Target[i]) then
set udg_BB__Count[i] = udg_BB__Count[i] - 1
call UnitDamageTarget(udg_BB__Caster[i], udg_BB__Target[i], udg_BB__Damage[i], false, false, BB__AttackType(), BB__DamageType(), BB__WeaponType())
call DestroyEffect(AddSpecialEffectTarget(BB__HitSfx(), udg_BB__Target[i], BB__HitSfxPt()))
call BB__AdditionalEffect(udg_BB__Target[i])
if udg_BB__Count[i] > 0 then
set udg_BB__State[i] = false
endif
endif
call GroupRemoveUnit(udg_BB__Group, f)
endloop
else
set udg_BB__Duration[i] = 0.0
endif
if udg_BB__Dc[i] < udg_BB__Dx[i] then
set udg_BB__Speed[i] = udg_BB__Speed[i] + udg_BB__Acceleration[i]
set udg_BB__Dc[i] = udg_BB__Dc[i] + udg_BB__Speed[i]
if udg_BB__Speed[i] > udg_BB__SpeedMax[i] then
set udg_BB__Speed[i] = udg_BB__SpeedMax[i]
endif
// Adjust missile's facing
if udg_BB__TurnRateMin[i] > 0 and Cos(udg_BB__Angle[i]-a) < Cos(udg_BB__TurnRateMin[i]) then
if Sin(a-udg_BB__Angle[i]) >= 0 then
set udg_BB__Angle[i] = udg_BB__Angle[i] + udg_BB__TurnRateMin[i]
else
set udg_BB__Angle[i] = udg_BB__Angle[i] - udg_BB__TurnRateMin[i]
endif
else
set udg_BB__Angle[i] = a
endif
else
set udg_BB__State[i] = false
endif
else
if udg_BB__Speed[i] > udg_BB__SpeedMin[i] then
set udg_BB__Speed[i] = udg_BB__Speed[i] - udg_BB__Deacceleration[i]
if udg_BB__Speed[i] < udg_BB__SpeedMin[i] then
set udg_BB__Speed[i] = udg_BB__SpeedMin[i]
endif
else
if udg_BB__TurnRateMax[i] > 0 and Cos(udg_BB__Angle[i]-a) < Cos(udg_BB__TurnRateMax[i]) then
if Sin(a-udg_BB__Angle[i]) >= 0 then
set udg_BB__Angle[i] = udg_BB__Angle[i] + udg_BB__TurnRateMax[i]
else
set udg_BB__Angle[i] = udg_BB__Angle[i] - udg_BB__TurnRateMax[i]
endif
else
set udg_BB__Angle[i] = a
set udg_BB__Dc[i] = 0.0
set udg_BB__Dx[i] = BB__GetDistance(x, y, x2, y2) + udg_BB__BrakePoint[i]
set udg_BB__State[i] = true
endif
endif
endif
set x = x + udg_BB__Speed[i] * Cos(udg_BB__Angle[i])
set y = y + udg_BB__Speed[i] * Sin(udg_BB__Angle[i])
call SetUnitFacing(udg_BB__Missile[i], udg_BB__Angle[i] * bj_RADTODEG)
if x < udg_BB__BoundMaxX and x > udg_BB__BoundMinX and y < udg_BB__BoundMaxY and y > udg_BB__BoundMinY then
call SetUnitX(udg_BB__Missile[i], x)
call SetUnitY(udg_BB__Missile[i], y)
else
// Kill the missile if not in map boudaries
set udg_BB__Duration[i] = 0.0
endif
if udg_BB__Duration[i] <= 0.0 then
call DestroyEffect(udg_BB__Sfx[i])
call UnitApplyTimedLife(udg_BB__Missile[i], 'BTLF', BB__DecayTime())
if i != udg_BB__Total then
call BB__Deindex(i)
endif
set udg_BB__Total = udg_BB__Total - 1
if udg_BB__Total == 0 then
call PauseTimer(udg_BB__Timer)
else
set i = i - 1
endif
endif
set i = i + 1
endloop
endfunction
function BB__onCast takes nothing returns boolean
local real x
local real y
local integer l
if GetSpellAbilityId() == BB__SpellID() then
set udg_BB__Total = udg_BB__Total + 1
set udg_BB__Caster[udg_BB__Total] = GetTriggerUnit()
set udg_BB__Player[udg_BB__Total] = GetTriggerPlayer()
set x = GetUnitX(udg_BB__Caster[udg_BB__Total])
set y = GetUnitY(udg_BB__Caster[udg_BB__Total])
set l = GetUnitAbilityLevel(udg_BB__Caster[udg_BB__Total], BB__SpellID())
set udg_BB__Target[udg_BB__Total] = GetSpellTargetUnit()
set udg_BB__Angle[udg_BB__Total] = GetUnitFacing(udg_BB__Caster[udg_BB__Total]) * bj_DEGTORAD
set udg_BB__Missile[udg_BB__Total] = CreateUnit(udg_BB__Player[udg_BB__Total], BB__DummyID(), x, y, udg_BB__Angle[udg_BB__Total] * bj_RADTODEG)
set udg_BB__Sfx[udg_BB__Total] = AddSpecialEffectTarget(BB__MissileSfx(), udg_BB__Missile[udg_BB__Total], "origin")
set udg_BB__Dx[udg_BB__Total] = 0.0
set udg_BB__Dc[udg_BB__Total] = 0.0
set udg_BB__State[udg_BB__Total] = false
set udg_BB__SpeedMin[udg_BB__Total] = BB__MissileSpeedMin(l)
set udg_BB__SpeedMax[udg_BB__Total] = BB__MissileSpeedMax(l)
set udg_BB__Acceleration[udg_BB__Total] = BB__MissileAcceleration(l)
set udg_BB__Deacceleration[udg_BB__Total] = BB__MissileDeacceleration(l)
set udg_BB__BrakePoint[udg_BB__Total] = BB__MissileBrakePoint(l)
set udg_BB__TurnRateMax[udg_BB__Total] = BB__MissileTurnRateMax(l) * bj_DEGTORAD
set udg_BB__TurnRateMin[udg_BB__Total] = BB__MissileTurnRateMin(l) * bj_DEGTORAD
set udg_BB__Speed[udg_BB__Total] = udg_BB__SpeedMin[udg_BB__Total]
set udg_BB__Damage[udg_BB__Total] = BB__DealtDamage(l)
set udg_BB__Count[udg_BB__Total] = BB__AttackCount(l)
set udg_BB__Duration[udg_BB__Total] = BB__Duration(l)
if UnitAddAbility(udg_BB__Missile[udg_BB__Total], 'Amrf') and UnitRemoveAbility(udg_BB__Missile[udg_BB__Total], 'Amrf') then
endif
call SetUnitFlyHeight(udg_BB__Missile[udg_BB__Total], BB__MissileZ(), 0.0)
if udg_BB__Total == 1 then
call TimerStart(udg_BB__Timer, BB__Accuracy(), true, function BB__onPeriodic)
endif
endif
return false
endfunction
function InitTrig_BoneBlade takes nothing returns nothing
set gg_trg_BoneBlade = CreateTrigger()
set udg_BB__Timer = CreateTimer()
set udg_BB__Group = CreateGroup()
set udg_BB__TAU = 2.0 * bj_PI
// Set the map bound for later use
set udg_BB__BoundMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
set udg_BB__BoundMinX = GetRectMinX(bj_mapInitialPlayableArea)
set udg_BB__BoundMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
set udg_BB__BoundMinY = GetRectMinY(bj_mapInitialPlayableArea)
call TriggerRegisterAnyUnitEventBJ(gg_trg_BoneBlade, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_BoneBlade, Condition(function BB__onCast))
endfunction
//TESH.scrollpos=45
//TESH.alwaysfold=0
/**********************************************
ChargeStrike v1.0
by mckill2009
***********************************************/
library ChargeStrike uses T32, SpellEffectEvent, TerrainPathability, TimedEffect, GroupUtils
globals
private constant integer SPELL_ID = 'A046'
//70, recommended
private constant real MOVE_SPEED = 70
//250, recommended
private constant real DAMAGE_AOE = 150
private constant string DAM_SFX_ATTACHMENT = "chest"
private constant string DAMAGE_SFX = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
private attacktype ATK = ATTACK_TYPE_HERO
private damagetype DMG = DAMAGE_TYPE_NORMAL
/**********************************************
* THIS IS ONLY FOR THE TRAIL
***********************************************/
private constant string TRAIL_SFX = "Environment\\UndeadBuildingFire\\UndeadLargeBuildingFire1.mdl"
//0.5, recommended
private constant real EFFECT_DURATION = 0.3
//animation of the trail
private constant string ANIMATION = "attack"
//attachments of the trail
private constant string SFX_ATTACHMENT = "origin"
endglobals
/**********************************************
* CONFIGURABLES
***********************************************/
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function GetDamage takes integer level returns real
return 35. //25. * level + 25
endfunction
/**********************************************
* NON-CONFIGURABLES
***********************************************/
private function UnitAlive takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u)==0 or u==null)
endfunction
private struct Cast
unit caster
real damage
real angle
real angleDeg
real dist
real distX
player pl
group g
private static group grp = CreateGroup()
private method hitThem takes nothing returns nothing
local TimedEffect eff
local unit first
call GroupEnumUnitsInRange(grp, GetUnitX(.caster), GetUnitY(.caster), DAMAGE_AOE, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) and not IsUnitInGroup(first, .g) and FilterTargets(first) then
call GroupAddUnit(.g, first)
call UnitDamageTarget(.caster, first, .damage, false, false, ATK, DMG, null)
set eff = eff.createOnUnit(DAMAGE_SFX, first, DAM_SFX_ATTACHMENT, EFFECT_DURATION)
endif
call GroupRemoveUnit(grp, first)
endloop
endmethod
private method periodic takes nothing returns nothing
local real x
local real y
local TimedEffect eff
if .dist > .distX then
set .distX = .distX + MOVE_SPEED
set x = GetUnitX(.caster) + MOVE_SPEED * Cos(.angle)
set y = GetUnitY(.caster) + MOVE_SPEED * Sin(.angle)
if IsTerrainWalkable(x, y) then
call SetUnitX(.caster, x)
call SetUnitY(.caster, y)
call .hitThem()
set eff = eff.createOnPoint(TRAIL_SFX, x, y, 1., EFFECT_DURATION)
else
set .distX = .dist
endif
else
set .caster = null
set .pl = null
call ReleaseGroup(.g)
set .g = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .pl = GetTriggerPlayer()
set .caster = GetTriggerUnit()
set .angle = Atan2(GetSpellTargetY()-y, GetSpellTargetX()-x)
set .angleDeg = .angle * bj_RADTODEG
set .dist = SquareRoot((GetSpellTargetX()-x)*(GetSpellTargetX()-x) + (GetSpellTargetY()-y)*(GetSpellTargetY()-y))
set .distX = 0
set .g = NewGroup()
set .damage = GetDamage(level)
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MassSleep initializer init
private function FilterMore takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_MECHANICAL) and not IsUnitType(u,UNIT_TYPE_SLEEPING)
endfunction
private function SleepCond takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit first
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, 700., null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, GetTriggerPlayer()) and FilterMore(first) then
call MDCCastToTarget(GetTriggerPlayer(), first, x, y, 'A012', 852227, 1)
endif
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
set u = null
endfunction
private function init takes nothing returns nothing
call RegisterSpellEffectEvent('A01J', function SleepCond)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Image uses SpellEffectEvent, Table, DamageEvent
//DummyInstantCaster DO NOT WORK
globals
private constant integer ITEM_ID = 'I003'
private constant integer DUMMY_SPELL_ID = 'A01X'
private constant integer CASTER_ID = 'e002'
private constant integer ILLUSION_COUNT = 5
private Table c
endglobals
private function FilterAttackedUnit takes unit u returns boolean
return not (IsUnitIllusion(u) or IsUnitType(u, UNIT_TYPE_HERO))
endfunction
private function UnitHaveItem takes unit u, integer itemId returns boolean
local item indexItem
local integer slot = 0
local integer index = 0
loop
set indexItem = UnitItemInSlot(u, slot)
if (indexItem != null) and (GetItemTypeId(indexItem) == itemId) then
return true
endif
set slot = slot + 1
exitwhen slot >= bj_MAX_INVENTORY
endloop
return false
endfunction
private struct Image extends array
private static integer hID = 0
static method run takes unit u, unit attacked returns nothing
local unit dummy
set hID = GetHandleId(u) //the hero
if ILLUSION_COUNT > (c[hID]) then
set dummy = CreateUnit(GetOwningPlayer(u), CASTER_ID, GetUnitX(u), GetUnitY(u), 0)
call UnitApplyTimedLife(dummy, 'BTLF', 1)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrderById(dummy, 852274, attacked)
set c[hID] = c[hID] + 1
//saving the hero's ID to the dummy
set c[GetHandleId(dummy)] = hID
//call BJDebugMsg("CAST "+I2S(hID))
//call BJDebugMsg("CAST "+I2S(c[hID]))
set dummy = null
endif
endmethod
//source is the hero wearing the item
static method onDamage takes nothing returns nothing
if GetRandomInt(1, 100) <= 30 then
if IsUnitEnemy(PDDS.source, GetOwningPlayer(PDDS.target)) then
if UnitHaveItem(PDDS.source, ITEM_ID) and FilterAttackedUnit(PDDS.target) then
call Image.run(PDDS.source, PDDS.target)
endif
endif
endif
endmethod
static method deathEvent takes nothing returns nothing
if IsUnitIllusion(GetTriggerUnit()) and c.has(GetHandleId(GetTriggerUnit())) then
//the hero ID
set hID = c[GetHandleId(GetTriggerUnit())]
set c[hID] = c[hID] - 1
if c[hID]==0 then
set c[hID] = 0
endif
call c.remove(GetHandleId(GetTriggerUnit()))
endif
endmethod
//transfering data from summoner to summoned
static method summon takes nothing returns nothing
if IsUnitIllusion(GetTriggerUnit()) then
set c[GetHandleId(GetTriggerUnit())] = c[GetHandleId(GetSummoningUnit())]
call c.remove(GetHandleId(GetSummoningUnit()))
//call BJDebugMsg("SUMMON "+I2S(c[GetHandleId(GetTriggerUnit())]))
endif
endmethod
static method onInit takes nothing returns nothing
call AddDamageHandler(function thistype.onDamage)
//call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.attackCond)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.summon)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.deathEvent)
set c = Table.create()
endmethod
endstruct
endlibrary
//TESH.scrollpos=28
//TESH.alwaysfold=0
/*
===DamageShield v1.1
===By Mckill2009
*/
scope DamageShield
globals
private constant integer SPELL_ID = 'A01W' //Raw code of the Hero spell
private constant integer DUMMY_ID = 'h00T' //Raw code of the Breath of Fire
private constant string SFX = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
private constant string ATT_SFX = "Abilities\\Spells\\Human\\InnerFire\\InnerFireTarget.mdl"
endglobals
struct DamageShield
unit caster
unit target
effect sfx
real duration
real heal
real life
private static Table d
private method getDuration takes integer i returns real
return 2 + i * 5.
endmethod
//75,100,125,150,175
private method getRestoreLife takes integer i returns real
return 25. * i + 50
endmethod
private method periodic takes nothing returns nothing
if UnitAlive(.target) and .duration > 0 then
call SetWidgetLife(.target, .life)
set .duration = .duration - 0.03125
else
call DestroyEffect(AddSpecialEffectTarget(SFX,.target,"origin"))
call DestroyEffect(.sfx)
call SetWidgetLife(.target, .life + .heal)
call d.remove(GetHandleId(.target))
set .caster = null
set .sfx = null
set .target = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method cast takes nothing returns nothing
local thistype this
local unit u = GetSpellTargetUnit()
local integer level
local integer targetID = GetHandleId(u)
if d.has(targetID) then
call IssueImmediateOrder(GetTriggerUnit(),"stop")
call SimError(GetTriggerPlayer(),"This unit cannot be targeted yet.")
else
set this = allocate()
set .caster = GetTriggerUnit()
set .target = u
set .sfx = AddSpecialEffectTarget(ATT_SFX, u, "overhead")
set level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .duration = 15. //getDuration(level)
set .life = GetWidgetLife(u)
set .heal = 250. //.life * getRestoreLife(level)
set d[targetID] = this
set d.real[targetID] = GetWidgetLife(u) //getRestoreLife(level)
call .startPeriodic()
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
set t = null
set d = Table.create()
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope FireWall
globals
private constant integer SPELL_ID = 'A026'
private constant integer FIRE_ID = 'h005'
private constant integer FIRE_QTY = 15
private constant real AOE = 100
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
private constant string SFX = "Abilities\\Spells\\NightElf\\Immolation\\ImmolationDamage.mdl"
private constant string ATTACHMENT = "overhead"
endglobals
private function GetDamage takes integer level returns real
return 15 + level * 15.
endfunction
private function GetDuration takes integer level returns real
return 15. //5 + level * 5.
endfunction
struct FireWall
unit fire
real damage
real sfxD
method periodic takes nothing returns nothing
local unit first
if UnitAlive(.fire) then
set .sfxD = .sfxD + T32_PERIOD
if .sfxD > 1.0 then
set .sfxD = 0
call GroupEnumUnitsInRange(bj_lastCreatedGroup,GetUnitX(.fire),GetUnitY(.fire),AOE,null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first,GetOwningPlayer(.fire)) and not IsUnitType(first,UNIT_TYPE_FLYING) then
call UnitDamageTarget(.fire, first, .damage, false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(SFX, first, ATTACHMENT))
endif
call GroupRemoveUnit(bj_lastCreatedGroup,first)
endloop
endif
else
call .stopPeriodic()
set .fire = null
call .deallocate()
endif
endmethod
implement T32x
static method fw takes unit f, real dam returns nothing
local thistype this = allocate()
set .fire = f
set .damage = dam
set .sfxD = 0
call .startPeriodic()
endmethod
endstruct
struct FireWallStart
unit caster
real angle
real maxA
real damage
real duration
real offset
real xSpell
real ySpell
real delay
static real angleInterval = 0.3
method periodic takes nothing returns nothing
local unit fire
local real x
local real y
if .maxA > .angle then
set .delay = .delay + T32_PERIOD
if .delay > 0.1 then
set .delay = 0
set x = .xSpell+.offset*Cos(.angle)
set y = .ySpell+.offset*Sin(.angle)
set fire = CreateUnit(GetOwningPlayer(.caster),FIRE_ID,x,y,0)
call FireWall.fw(fire, .damage)
call UnitApplyTimedLife(fire,'BTLF',.duration)
set .angle = .angle + angleInterval
set fire = null
endif
else
set .caster = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
static method cast takes nothing returns nothing
local thistype this = allocate()
local integer level
set .caster = GetTriggerUnit()
set level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .angle = GetUnitFacing(.caster)*bj_DEGTORAD
set .maxA = .angle + 6.2831
set .damage = GetDamage(level)
set .duration = GetDuration(level)
set .delay = 0
set .offset = FIRE_QTY*14
set .xSpell = GetSpellTargetX()
set .ySpell = GetSpellTargetY()
call .startPeriodic()
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Grip //uses T32x
globals
private constant integer SPELL_ID = 'A00G'
private constant integer DUMMY_ID = 'h00G'
private constant real AOE = 300.
endglobals
private function FilterUnit takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_STRUCTURE) and not IsUnitType(u,UNIT_TYPE_MECHANICAL) and not IsUnitType(u,UNIT_TYPE_DEAD)
endfunction
private function GetDuration takes integer i returns real
return 15. // + i * 15.
endfunction
private struct Grip
unit caster
unit dummy
real xSpell
real ySpell
real duration
static real aoe
private method periodic takes nothing returns nothing
local unit first
local real angle
local real xDummy
local real yDummy
local real xF
local real yF
local real xd
local real yd
if .duration > 0 then
set .duration = .duration - T32_PERIOD
set xDummy = GetUnitX(.dummy)
set yDummy = GetUnitY(.dummy)
call GroupEnumUnitsInRange(bj_lastCreatedGroup, xDummy, yDummy, AOE, null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if IsUnitEnemy(.dummy,GetOwningPlayer(first)) and FilterUnit(first) then
set xF = GetUnitX(first)
set yF = GetUnitY(first)
set xd = xF - xDummy
set yd = yF - yDummy
set angle = Atan2(yF-yDummy,xF-xDummy)
if xd*xd+yd*yd > aoe then
call SetUnitX(first,xF-50*Cos(angle))
call SetUnitY(first,yF-50*Sin(angle))
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup,first)
endloop
else
call KillUnit(.dummy)
set .dummy = null
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set .caster = GetTriggerUnit()
set .xSpell = GetSpellTargetX()
set .ySpell = GetSpellTargetY()
set .dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, .xSpell, .ySpell,0)
set .duration = GetDuration(GetUnitAbilityLevel(.caster, SPELL_ID))
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set aoe = (AOE-50.0)*(AOE-50.0)
endmethod
endstruct
endscope
//TESH.scrollpos=37
//TESH.alwaysfold=0
scope SpiritHold initializer init //uses HashT, SimpleTextTag
globals
private constant integer SPELL_ID = 'A00Q'
private constant integer ITEM_ID = 'I006'
private constant integer MAX_SOUL = 20
private constant integer MIN_SOUL = 2
private constant string STR1 = "You have captured "
private constant string STR2 = " souls!"
private constant string STR3 = "You can't use this yet!"
private constant string SLASH = "/"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_NORMAL
private constant string SFX1 = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
private constant string SFX2 = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
private constant boolean ITEM_ABILITY = true
endglobals
private function GetPercentageDamage takes integer i returns real
return 0.05 + i * 0.05
endfunction
private function SpiritGather takes unit killingunit, unit killed returns nothing
local integer i = GetHandleId(killingunit)
local integer soul = GetInteger(i, 1)+1
local real damage = GetReal(i, 2) + (GetUnitState(killed, UNIT_STATE_MAX_LIFE))
call SetInteger(i, 1, soul)
call SetReal(i, 2, damage)
set soul = GetInteger(i, 1)
call SimTTUnit(STR1 + I2S(soul) + SLASH + I2S(MAX_SOUL) + STR2 , killingunit, 0, 0.025)
endfunction
private function SpiritDamage takes unit caster, unit target returns nothing
local integer i = GetHandleId(caster)
local integer level = GetUnitAbilityLevel(caster, SPELL_ID)
local real damage = GetReal(i, 2) * GetPercentageDamage(level)
local integer d = R2I(damage)
call UnitDamageTarget(caster, target, damage, false, false, ATK, DMG, null)
call SimTTUnit(I2S(d), target, 5, 0.025)
call DestroyEffect(AddSpecialEffectTarget(SFX1, target, "chest"))
call DestroyEffect(AddSpecialEffectTarget(SFX2, target, "chest"))
call RemoveInteger(i, 1)
call RemoveReal(i, 2)
endfunction
//===Conditions:
private function SpiritHoldDeath takes nothing returns boolean
//If target dies, it will store the spirit
if GetUnitAbilityLevel(GetKillingUnit(), SPELL_ID) > 0 and GetInteger(GetHandleId(GetKillingUnit()), 1) < (MAX_SOUL) then
call SpiritGather(GetKillingUnit(), GetTriggerUnit())
endif
return false
endfunction
private function SpiritCast takes nothing returns boolean
//Releases the spirits to deal damage
if GetSpellAbilityId()==SPELL_ID then
if GetInteger(GetHandleId(GetTriggerUnit()), 1) >= MIN_SOUL then
call SpiritDamage(GetTriggerUnit(), GetSpellTargetUnit())
else
call SimTTUnit(STR3 , GetTriggerUnit(), 0, 0.025)
endif
endif
return false
endfunction
private function SpiritHoldItemDrop takes nothing returns boolean
if GetItemTypeId(GetManipulatedItem())==ITEM_ID then
call RemoveInteger(GetHandleId(GetTriggerUnit()), 1)
call RemoveReal(GetHandleId(GetTriggerUnit()), 2)
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function SpiritCast))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(function SpiritHoldDeath))
static if ITEM_ABILITY then
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DROP_ITEM)
call TriggerAddCondition(t, Condition(function SpiritHoldItemDrop))
endif
set t = null
endfunction
endscope
//TESH.scrollpos=40
//TESH.alwaysfold=0
/*******************************************************************
LightningAura v1.0
by mckill2009
********************************************************************
Required Libraries
- DamageEvent by looking_for_help
- Table by Bribe
- SpellEffectEvent by Bribe
- BoundSentinel by Vexorian
********************************************************************/
library LightningAura uses DamageEvent, SpellEffectEvent, Table, BoundSentinel
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A01C'
private constant integer DUMMY_SPELL_ID = 'A03P'
private constant integer ORDER_ID = 852119
private constant integer DUMMY_ID = 'e002'
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant real LIGHTNING_HEIGHT = 1500.
private constant real OFFSET_FROM_TARGET = 300.
private constant string BUFF_SFX = "Abilities\\Spells\\Orc\\LightningShield\\LightningShieldTarget.mdl"
private constant string BUFF_ATTACHMENT = "origin"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private unit dummy
private group grp = CreateGroup()
private Table ck
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDamage takes integer level returns real
return 75. //25. * level + 25 //50/75/100/125/150
endfunction
private function GetDuration takes integer level returns real
return 20. //5. * level + 10 //15/20/25/30/35
endfunction
private function GetAoe takes integer level returns real
return 400. //150. * level + 200
endfunction
private function GetChance takes integer level returns integer
return 40
endfunction
private function FilterBuffed takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function FilterDamageSource takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct LightningAura
unit target
effect sfx
real duration
real damage
static timer t = CreateTimer()
static integer index = 0
static integer array indexAR
static real interval = 1.0
private static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = indexAR[i]
if UnitAlive(.target) and .duration > 0 then
set .duration = .duration - interval
else
call ck.remove(GetHandleId(.target))
call DestroyEffect(.sfx)
set .sfx = null
set .target = null
call .deallocate()
set indexAR[i] = indexAR[index]
set index = index - 1
set i = i - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
private static method start takes unit u, integer level returns nothing
local thistype this
local integer id = GetHandleId(u)
if not ck.has(id) then
set this = allocate()
set .target = u
set .sfx = AddSpecialEffectTarget(BUFF_SFX, u, BUFF_ATTACHMENT)
set .duration = GetDuration(level)
set ck[id] = level
if index==0 then
call TimerStart(t, interval, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
endif
endmethod
private static method onCast takes nothing returns nothing
local unit first
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
call GroupEnumUnitsInRange(grp, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), GetAoe(level), null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and not IsUnitEnemy(first, GetTriggerPlayer()) and FilterBuffed(first) then
call thistype.start(first, level)
endif
call GroupRemoveUnit(grp, first)
endloop
endmethod
private static method onDamage takes nothing returns nothing
local integer targetID = GetHandleId(PDDS.target)
local real angle
local real xSource
local real ySource
local real xLightning
local real yLightning
if ck.has(targetID) then
if GetRandomInt(0, 100) < GetChance(ck[targetID]) then
if IsUnitEnemy(PDDS.source, GetOwningPlayer(PDDS.target)) and FilterDamageSource(PDDS.source) then
set angle = GetRandomReal(0, 6)
set xSource = GetUnitX(PDDS.source)
set ySource = GetUnitY(PDDS.source)
set xLightning = xSource + OFFSET_FROM_TARGET * Cos(angle)
set yLightning = ySource + OFFSET_FROM_TARGET * Sin(angle)
call SetUnitX(dummy, xLightning)
call SetUnitY(dummy, yLightning)
call IssueTargetOrderById(dummy, ORDER_ID, PDDS.source)
call UnitDamageTarget(PDDS.target, PDDS.source, GetDamage(ck[targetID]), false, false, ATK, DMG, null)
endif
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call AddDamageHandler(function thistype.onDamage)
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set ck = Table.create()
set dummy = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(dummy, 'Aloc')
call UnitAddAbility(dummy, 'Arav')
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call UnitRemoveAbility(dummy, 'Amov')
call SetUnitFlyHeight(dummy, LIGHTNING_HEIGHT, 0)
endmethod
endstruct
endlibrary
//TESH.scrollpos=57
//TESH.alwaysfold=0
//==================================================================================//
// //
// LIGHTNING SPEED LACERATION by Maker v1.00 //
// //
// The caster strikes the target several times at lightning-like speed //
// causing severe blunt force trauma and stun. //
// //
// Requires JNGP and TimerUtils //
// //
// How to import: //
// 1. Copy the ability //
// 2. Copy the dummy ability //
// 3. Create a copy of your unit and make it a dummy unit. //
// You can take a look at the paladin dummy in this map to see //
// which data field you need to edit. //
// 4. Set ABILID, DUMABILID and DUMMYID to correct raw codes. //
// You can view raw codes in object editor by clicking Ctrl + D. //
// 5. Edit animation indexes in SetAnim function. //
// You can use the Animation Test trigger in Test folder to //
// check animation indexes. //
// //
//==================================================================================//
scope LSL
globals
// Ability raw code
private constant integer ABILID = 'A03Q'
// Dummy stun ability raw code
private constant integer DUMABILID = 'A03R'
// Dummy unit raw code
private constant integer DUMMYID = 'H001'
private constant attacktype ATT = ATTACK_TYPE_NORMAL
private constant damagetype DAM = DAMAGE_TYPE_NORMAL
// Affects the sound when damage is applied
private constant weapontype WEA = WEAPON_TYPE_METAL_HEAVY_BASH
// Delay between starting the hit animation and applying damage
private constant real DMGDELAY = 0.25
// Delay between applying damage and removing dummies
private constant real DEATHDELAY = 0.50
// Delay of dummy creation between creating copies
private constant real CREATEDELAY = 0.15
// How fast the animation plays
private constant real TIMESCALE = 1.50
// Dummy unit transparency. 0 = invisible, 255 = fully visible
private constant integer ALPHA = 127
// Attached to the the attackers
private constant string WPNEFF = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"
// Attached on the target
private constant string HITEFF = "Abilities\\Weapons\\GyroCopter\\GyroCopterImpact.mdl"
// Attach point of WPNEFF
private constant string HITATT = "weapon"
// Attach point of HITEFF
private constant string DMGATT = "chest"
private integer ANIMCOUNT
private integer array ANIMS
endglobals
// Configure the attack animation indexes an how many of the exist
private function SetAnims takes nothing returns nothing
set ANIMS[1] = 4
set ANIMS[2] = 5
set ANIMCOUNT = 2
endfunction
// How many times the ability hits
private constant function GetHits takes integer lvl returns integer
return 3 //2 + lvl
endfunction
// Damage per hit
private constant function GetDamage takes integer lvl returns real
return 30. * lvl //15. + lvl * 15.
endfunction
// Handles hit instances
private struct Hit
unit hit
unit caster
unit target
real dmg
real dmgdelay
real dur
timer tim
effect eff
boolean b
static method remove takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if .b then
call RemoveUnit(.hit)
else
call SetUnitTimeScale(.hit, TIMESCALE)
endif
call DestroyEffect(.eff)
call ReleaseTimer(t)
call .destroy()
set t = null
endmethod
static method damage takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call UnitDamageTarget(.caster, .target, .dmg, true, false, ATT, DAM, WEA)
call DestroyEffect(AddSpecialEffectTarget(HITEFF, .target, DMGATT))
call TimerStart(.tim, DEATHDELAY, false, function thistype.remove)
set t = null
endmethod
static method onHit takes boolean b, integer ind, unit caster, unit tar, real dmg, real x, real y returns nothing
local thistype this = thistype.allocate()
if b then
set .hit = CreateUnit(Player(15), DUMMYID, x, y, GetUnitFacing(caster))
call UnitApplyTimedLife(.hit, 1, 2)
call SetUnitColor(.hit, GetPlayerColor(GetOwningPlayer(caster)))
call SetUnitVertexColor(.hit, 255, 255, 255, ALPHA)
call SetUnitAnimationByIndex(.hit, ind)
else
set .hit = caster
endif
set .b = b
set .caster = caster
set .target = tar
set .dmg = dmg
set .tim = NewTimer()
call SetUnitTimeScale(.hit, TIMESCALE)
set .eff = AddSpecialEffectTarget(WPNEFF, .hit, HITATT)
call SetTimerData(.tim, this)
call TimerStart(.tim, DMGDELAY, false, function thistype.damage)
endmethod
endstruct
private struct LSL
real x
real y
real dmg
integer lvl
integer loops
integer maxLoops
unit caster
unit target
integer anim
timer tim
static method periodic takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if not(IsUnitType(.caster, UNIT_TYPE_DEAD) or GetUnitTypeId(.caster) == 0) /*
*/ and not(IsUnitType(.target, UNIT_TYPE_DEAD) or GetUnitTypeId(.target) == 0) /*
*/ and .loops != maxLoops /*
*/ and .x == GetUnitX(.caster) /*
*/ and .y == GetUnitY(.caster) then
if .loops == 0 then
set bj_lastCreatedUnit = CreateUnit(Player(15), DUMMYID, .x, .y, GetUnitFacing(.caster))
call UnitApplyTimedLife(bj_lastCreatedUnit, 1, 1)
call SetUnitAbilityLevel(bj_lastCreatedUnit, DUMABILID, .lvl)
call IssueTargetOrder(bj_lastCreatedUnit, "thunderbolt", .target)
call ShowUnit(bj_lastCreatedUnit, false)
call Hit.onHit(false, .anim, .caster, .target, .dmg, .x, .y)
else
call Hit.onHit(true, .anim, .caster, .target, .dmg, .x, .y)
endif
set .loops = .loops + 1
else
call ReleaseTimer(t)
call .destroy()
endif
set t = null
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set .lvl = GetUnitAbilityLevel(GetTriggerUnit(), ABILID)
set .tim = NewTimer()
set .loops = 0
set .maxLoops = GetHits(.lvl)
set .dmg = GetDamage(.lvl)
set .caster = GetTriggerUnit()
set .target = GetSpellTargetUnit()
set .x = GetUnitX(.caster)
set .y = GetUnitY(.caster)
set .anim = ANIMS[GetRandomInt(1, ANIMCOUNT)]
call SetTimerData(.tim, this)
call SetUnitAnimationByIndex(.caster, .anim)
call TimerStart(.tim, CREATEDELAY, true, function thistype.periodic)
endmethod
static method condition takes nothing returns boolean
if GetSpellAbilityId() == ABILID then
call thistype.onCast()
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call SetAnims()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.condition))
set t = null
endmethod
endstruct
endscope
//TESH.scrollpos=6
//TESH.alwaysfold=0
scope IceShards initializer init
globals
private constant integer ABILID = 'A03U'
private constant integer SHARDID = 'e00F'
private constant integer CASTERID = 'e002'
private constant integer BASESHARDS = 0
private constant integer BONUSSHARDS = 6
private constant real AOE = 55.
private constant real BASEDAMAGE = 0.
private constant real BONUSDAMAGE = 30.
private constant real BASEBURNDAMAGE = 0.
private constant real BONUSBURNDAMAGE = 0.
private constant real BURNTIME = 0.
private constant real BASESPEED = 15.
private constant real BONUSSPEED = 3.
private constant real BASEPASS = 50.
private constant real BONUSPASS = 15.
private constant real PROJHEIGHT = 65.
private constant real SPAWNOFFSET = 40.
private constant real OFFSETANGLE = 15 * bj_PI / 180
private constant real NEXTANGLE = 8 * bj_PI / 180
private constant real NEXTANGLEREDUCT = 2 * bj_PI / 180
private constant real NEXTANGLEMIN = 3 * bj_PI / 180
private constant real TIMEOUT = 0.03
private constant attacktype ATK_T = ATTACK_TYPE_CHAOS
private constant damagetype DMG_T = DAMAGE_TYPE_NORMAL
//private constant string HITEFFECT = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
private constant string HITEFFECT = "Abilities\\Weapons\\DragonHawkMissile\\DragonHawkMissile.mdl"
private player plr
private unit un
private real dam
private integer projs = 0
private group grp = CreateGroup()
private group tgrp = CreateGroup()
private group shardGrp = CreateGroup()
private timer shardTmr = CreateTimer()
private hashtable ht = InitHashtable()
endglobals
private constant function HashCaster takes nothing returns integer
return 0
endfunction
private constant function HashDamage takes nothing returns integer
return 1
endfunction
private constant function HashAngle takes nothing returns integer
return 2
endfunction
private constant function HashPass takes nothing returns integer
return 3
endfunction
private constant function HashSpeed takes nothing returns integer
return 4
endfunction
private constant function HashGroup takes nothing returns integer
return 5
endfunction
private constant function HashBurnDmg takes nothing returns integer
return 6
endfunction
private function DamageFilt takes nothing returns boolean
local unit u = GetFilterUnit()
local integer id = GetHandleId(u)
if not(IsUnitInGroup(u, tgrp)) and /*
*/ IsUnitEnemy(u, plr) and /*
*/ GetWidgetLife(u) > 0.405 and /*
*/ GetUnitFlyHeight(u) < PROJHEIGHT then
call GroupAddUnit(tgrp, u)
call DestroyEffect(AddSpecialEffectTarget(HITEFFECT, u, "chest"))
call UnitDamageTarget(un, u, dam, false, true, ATK_T, DMG_T, null)
set bj_lastCreatedUnit = CreateUnit(Player(15), CASTERID, GetUnitX(u), GetUnitY(u), 0)
call UnitAddAbility(bj_lastCreatedUnit, 'A03W')
call IssueTargetOrder(bj_lastCreatedUnit, "slow", u)
call SetUnitExploded(bj_lastCreatedUnit, true)
call UnitApplyTimedLife(bj_lastCreatedUnit, 1, 1)
endif
set u = null
return false
endfunction
private function ShardLoop takes nothing returns nothing
local unit u = GetEnumUnit()
local integer id = GetHandleId(u)
local real spd = LoadReal(ht, id, HashSpeed())
local real ang = LoadReal(ht, id, HashAngle())
local real x
local real y
if GetWidgetLife(u) > 0.405 then
set x = GetUnitX(u) + spd * Cos(ang)
set y = GetUnitY(u) + spd * Sin(ang)
call SetUnitX(u, x)
call SetUnitY(u, y)
set dam = LoadReal(ht, id, HashDamage())
set un = LoadUnitHandle(ht, id, HashCaster())
set plr = GetOwningPlayer(un)
set tgrp = LoadGroupHandle(ht, id, HashGroup())
call GroupEnumUnitsInRange(grp, x, y, AOE, function DamageFilt)
else
call DestroyGroup(LoadGroupHandle(ht, id, HashGroup()))
call GroupRemoveUnit(shardGrp, u)
call FlushChildHashtable(ht, id)
set projs = projs - 1
if projs == 0 then
call PauseTimer(shardTmr)
endif
endif
set u = null
endfunction
private function TimerExp takes nothing returns nothing
call ForGroup(shardGrp, function ShardLoop)
endfunction
private function onCast takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer id = 0
local integer side = 0
local integer shards = 0
local integer loops = 0
local integer level = GetUnitAbilityLevel(u, ABILID)
local real f = GetUnitFacing(u) * bj_DEGTORAD
local real x = GetUnitX(u) + SPAWNOFFSET * Cos(f)
local real y = GetUnitY(u) + SPAWNOFFSET * Sin(f)
local real dmg = 0
local real ang = 0
local real pass = 0
local real speed = 0
local real nxtang = 0
local real burndmg = 0
if GetSpellAbilityId() == ABILID then
if projs == 0 then
call TimerStart(shardTmr, TIMEOUT, true, function TimerExp)
endif
set dmg = BASEDAMAGE + BONUSDAMAGE * level
set burndmg = BASEBURNDAMAGE + BONUSBURNDAMAGE * level
set shards = BASESHARDS + BONUSSHARDS * level
set pass = BASEPASS + BONUSPASS * level
set speed = BASESPEED + BONUSSPEED * level
set nxtang = NEXTANGLE - (level-1)*NEXTANGLEREDUCT
if nxtang < NEXTANGLEMIN then
set nxtang = NEXTANGLEMIN
endif
if GetRandomInt(1,2) == 1 then
set side = 1
else
set side = -1
endif
loop
set ang = side * ang + f + GetRandomReal(0, OFFSETANGLE)
set bj_lastCreatedUnit = CreateUnit(Player(15), SHARDID, x, y, ang * bj_RADTODEG)
call SetUnitExploded(bj_lastCreatedUnit, true)
call UnitApplyTimedLife(bj_lastCreatedUnit, 1, 1)
call SetUnitFlyHeight(bj_lastCreatedUnit, PROJHEIGHT, 0)
call GroupAddUnit(shardGrp, bj_lastCreatedUnit)
set id = GetHandleId(bj_lastCreatedUnit)
call SaveReal(ht, id, HashAngle(), ang)
call SaveReal(ht, id, HashPass(), pass)
call SaveReal(ht, id, HashDamage(), dmg)
call SaveReal(ht, id, HashSpeed(), speed)
call SaveReal(ht, id, HashBurnDmg(), burndmg)
call SaveUnitHandle(ht, id, HashCaster(), u)
call SaveGroupHandle(ht, id, HashGroup(), CreateGroup())
set projs = projs + 1
exitwhen loops == shards
set side = side * -1
set loops = loops + 1
if ModuloInteger(loops, 2) == 1 then
set ang = nxtang * (1 + loops/2)
else
set ang = nxtang * (loops/2)
endif
endloop
endif
set u = null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t, function onCast )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set t = null
set bj_lastCreatedUnit = CreateUnit(Player(15), SHARDID, 0, 0, 0)
call UnitAddAbility(bj_lastCreatedUnit, 'A03W')
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
call ShowUnit(bj_lastCreatedUnit, false)
endfunction
endscope
//TESH.scrollpos=40
//TESH.alwaysfold=0
/***************************************************************************
Locust Raid v1.3
by: mckill2009
****************************************************************************
REQUIRES:
- Jass New Gen Pack (JNGP) by Vexorian
- T32 by Jesus4lyf
- SpellEffectEvent by Bribe
OPTIONAL REWUIREMENTS:
- SummonedEscort by mckill2009
*****************************************************************************/
library LocustRaid uses T32, SpellEffectEvent optional SummonedEscort
globals
private constant integer SPELL_ID = 'jas1' //The Main spell
private constant integer BOTTLE_ID = 'lr02'
private constant integer SUMMONED_UNIT_ID = 'lr01'
private constant real AOE = 300 //enemy filter for passive and active
private constant real HEIGHT = 300 //bottle height
private constant real SPEED = 15 //bottle speed
//===USED ONLY BY PASSIVE SPELL
private constant integer PASSIVE_SPELL_ID = 'jas3' //Passive spell, optional
private constant attacktype ATK = ATTACK_TYPE_MAGIC //used only by passive
private constant damagetype DMG = DAMAGE_TYPE_POISON //used only by passive
private constant string SFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl" //used only by passive
//===NON-CONFIGURABLE
private unit TempUnit
private group G = CreateGroup()
endglobals
/**********************************************************
* CONFIGURABLES:
***********************************************************/
private function GetPassiveDamage takes integer level returns real
return 30. * level + 60 //used by passive only
endfunction
private function SummonedCount takes integer level returns integer
return level * 3 + 2
endfunction
private function SummonedTimer takes integer level returns real
return 15. //level * 10.
endfunction
private function EnemyFilter takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) /*
*/ and not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
/**********************************************************
* NON-CONFIGURABLES:
***********************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function UnitInRange takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(TempUnit, GetOwningPlayer(GetFilterUnit()))
endfunction
private struct Cast
unit caster
unit bottle
real xUnit
real yUnit
real xLoc
real yLoc
real damage
real distance
real distX
real angle
real summonedLife
real cos
real sin
integer summonedCount
integer level
player pl
/*******************************************************************
* This is the PASSIVE SPELL, activates only when bottle breaks
********************************************************************/
private method passiveAbilDamage takes nothing returns nothing
local unit tempUnit
local real passiveDamage
local integer passiveLevel = GetUnitAbilityLevel(.caster, PASSIVE_SPELL_ID)
if passiveLevel > 0 then
if UnitAlive(.caster) then
set passiveDamage = GetPassiveDamage(passiveLevel)
call GroupEnumUnitsInRange(G, .xLoc, .yLoc, AOE, null)
loop
set tempUnit = FirstOfGroup(G)
exitwhen tempUnit==null
if UnitAlive(tempUnit) and EnemyFilter(tempUnit) and IsUnitEnemy(tempUnit, .pl) then
call DestroyEffect(AddSpecialEffectTarget(SFX, tempUnit, "origin"))
call UnitDamageTarget(.bottle, tempUnit, passiveDamage, false, false, ATK, DMG, null)
endif
call GroupRemoveUnit(G, tempUnit)
endloop
endif
endif
endmethod
private method periodic takes nothing returns nothing
local unit tempUnit
local integer count
local real ht
if .distance > .distX then
set .distX = .distX + SPEED
call SetUnitX(.bottle, .xUnit+.distX * .cos)
call SetUnitY(.bottle, .yUnit+.distX * .sin)
set ht = (4 * HEIGHT / .distance) * (.distance - .distX) * (.distX / .distance)
call SetUnitFlyHeight(.bottle, ht, 0)
else
call .passiveAbilDamage()
call UnitApplyTimedLife(.bottle, 'BTLF', 0.1)
set count = 0
loop
exitwhen count==.summonedCount
set count = count + 1
set tempUnit = CreateUnit(.pl, SUMMONED_UNIT_ID, .xLoc, .yLoc, 0)
static if LIBRARY_SummonedEscort then
call SE.summoned(.caster, tempUnit)
endif
call UnitApplyTimedLife(tempUnit, 'BTLF', .summonedLife)
endloop
set tempUnit = null
set .caster = null
set .bottle = null
set .pl = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method cast takes nothing returns nothing
local thistype this = allocate()
local real dx
local real dy
set .caster = GetTriggerUnit()
set .level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .xUnit = GetUnitX(.caster)
set .yUnit = GetUnitY(.caster)
set .xLoc = GetSpellTargetX()
set .yLoc = GetSpellTargetY()
set dx = .xLoc - .xUnit
set dy = .yLoc - .yUnit
set .distance = SquareRoot(dx * dx + dy * dy)
set .distX = 0
set .summonedCount = SummonedCount(.level)
set .summonedLife = SummonedTimer(.level)
set .angle = Atan2(.yLoc-.yUnit, .xLoc-.xUnit)
set .pl = GetTriggerPlayer()
set .bottle = CreateUnit(.pl, BOTTLE_ID , .xUnit, .yUnit, 0)
set .cos = Cos(.angle)
set .sin = Sin(.angle)
call SetUnitFlyHeight(.bottle, GetUnitFlyHeight(.caster)+100, 0)
if UnitAddAbility(.bottle, 'Arav') and UnitRemoveAbility(.bottle, 'Arav') then
endif
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=135
//TESH.alwaysfold=0
/*******************************************************************
Twin Wolves v1.3
by mckill2009
********************************************************************
REQUIRES:
- Jass New Gen Pack (JNGP) by Vexorian
- RegisterPlayerUnitEvent by Magtheridon96
- GetClosestWidget by Spinnaker
- Table by Bribe
- SimError by Vexorian
- SummonedEscort by mckill2009
- UnselectableUnit by mckill2009
********************************************************************/
library TwinWolves uses RegisterPlayerUnitEvent, Table, SimError, SummonedEscort, GetClosestWidget, UnSelectableUnit
globals
private constant integer SPELL_ID = 'jas2'
private constant integer WOLF_ID = 'tw01'
private constant integer FOR_ALLY_SPELL_ID = 'twh1' //rejuvenation
private constant integer FOR_ENEMY_SPELL_ID = 'twd1' //chainlightning
private constant integer FOR_ALLY_OID = 852160 //this should match the FOR_ALLY_SPELL_ID
private constant integer FOR_ENEMY_OID = 852119 //this should match the FOR_ENEMY_SPELL_ID
private constant real AOE = 600.
private constant string SFX_APPEAR = "Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdl"
private constant real PERIOD = 1.0 //how fast the wolves will cast spells, affected by cooldowns
private constant real WOLF_LIFE = 60.
//===NON-CONFIGURABLE
private Table c
private unit FilterUnit
private unit TempUnit
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function UnitAllyInRange takes nothing returns boolean
set FilterUnit = GetFilterUnit()
return UnitAlive(FilterUnit) and not IsUnitEnemy(TempUnit, GetOwningPlayer(FilterUnit)) /*
*/ and not IsUnitType(FilterUnit, UNIT_TYPE_STRUCTURE) and not IsUnitType(FilterUnit, UNIT_TYPE_MECHANICAL) /*
*/ and not IsUnitType(FilterUnit, UNIT_TYPE_MAGIC_IMMUNE) and GetWidgetLife(FilterUnit) < GetUnitState(FilterUnit, UNIT_STATE_MAX_LIFE)
endfunction
private function UnitEnemyInRange takes nothing returns boolean
set FilterUnit = GetFilterUnit()
return UnitAlive(FilterUnit) and IsUnitEnemy(TempUnit, GetOwningPlayer(FilterUnit)) /*
*/ and not IsUnitType(FilterUnit, UNIT_TYPE_STRUCTURE) and not IsUnitType(FilterUnit, UNIT_TYPE_MECHANICAL) /*
*/ and not IsUnitType(FilterUnit, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
struct TW
unit caster
unit healer
unit damager
boolean isOn
private static integer index = 0
private static integer array indexAR
private static timer t = CreateTimer()
private static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
local unit first
loop
set i = i+1
set this = indexAR[i]
if UnitAlive(.caster) and .isOn then
if UnitAlive(.damager) or UnitAlive(.healer) then
set TempUnit = .caster
if UnitAlive(.damager) then
set first = GetClosestUnitInRange(GetUnitX(.damager), GetUnitY(.damager), AOE, Filter(function UnitEnemyInRange))
if first!=null then
call IssueTargetOrderById(.damager, FOR_ENEMY_OID, first)
endif
endif
if UnitAlive(.healer) then
set first = GetClosestUnitInRange(GetUnitX(.healer), GetUnitY(.healer), AOE, Filter(function UnitAllyInRange))
if first!=null then
call IssueTargetOrderById(.healer, FOR_ALLY_OID, first)
endif
endif
else
set .isOn = false
endif
else
call c.remove(GetHandleId(.caster))
set .caster = null
set .damager = null
set .healer = null
call .deallocate()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set index = index - 1
set i = i-1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
set first = null
endmethod
private static method cast takes nothing returns nothing
local unit u
local integer level
local real facing
local real x
local real y
local thistype this
if GetSpellAbilityId()==SPELL_ID then
set u = GetTriggerUnit()
if c.has(GetHandleId(u)) then
call IssueImmediateOrder(u, "stop")
call SimError(GetTriggerPlayer(), "you cant use this spell yet")
else
set this = allocate()
set x = GetUnitX(u)
set y = GetUnitY(u)
set facing = GetUnitFacing(u)*0.017453333
set .caster = u
set level = GetUnitAbilityLevel(u, SPELL_ID)
//===Healer
set .healer = CreateUnit(GetTriggerPlayer(), WOLF_ID, x+150*Cos(facing+1.5708), y+150*Sin(facing+1.5708), GetUnitFacing(u))
call DestroyEffect(AddSpecialEffectTarget(SFX_APPEAR, .healer, "origin"))
call UnitAddAbility(.healer, FOR_ALLY_SPELL_ID)
call SetUnitAbilityLevel(.healer, FOR_ALLY_SPELL_ID, level)
call MakeUnitUnSelectable(.healer, 0)
call SE.summoned(.caster, .healer)
//===Damager
set .damager = CreateUnit(GetTriggerPlayer(), WOLF_ID, x+150*Cos(facing-1.5708), y+150*Sin(facing-1.5708), GetUnitFacing(u))
call DestroyEffect(AddSpecialEffectTarget(SFX_APPEAR, .damager, "origin"))
call UnitAddAbility(.damager, FOR_ENEMY_SPELL_ID)
call SetUnitAbilityLevel(.damager, FOR_ENEMY_SPELL_ID, level)
call MakeUnitUnSelectable(.damager, 0)
call SE.summoned(.caster, .damager)
call UnitApplyTimedLife(.damager, 'BTLF', WOLF_LIFE)
call UnitApplyTimedLife(.healer, 'BTLF', WOLF_LIFE)
set .isOn = true
set c[GetHandleId(u)] = this
if index==0 then
call TimerStart(t, PERIOD, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
endif
set u = null
endif
endmethod
private static method onInit takes nothing returns nothing
local unit u = CreateUnit(Player(15), WOLF_ID, 0,0,0)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.cast)
set c = Table.create()
//Preloading
call UnitAddAbility(u, FOR_ENEMY_SPELL_ID)
call UnitAddAbility(u, FOR_ALLY_SPELL_ID)
call ShowUnit(u, false)
call RemoveUnit(u)
set u = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=73
//TESH.alwaysfold=0
/************************************************************
ForbiddenWrath v1.3
by mckill2009
*************************************************************
REQUIRES:
- Jass New Gen Pack (JNGP) by Vexorian
- SpellEffectEvent by Bribe
*************************************************************/
library ForbiddenWrath uses SpellEffectEvent
globals
private constant integer SPELL_ID = 'jas4'
private constant integer AVATAR_1 = 'nrwm' //red dragon
private constant integer AVATAR_2 = 'nsll' //salamander lord
private constant integer AVATAR_3 = 'ntrd' //dragon turtle
private constant real AVATAR_LIFE = 60.
private constant string CASTER_SFX_RETAIN = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
private constant string SFX_AVATAR_APPEAR = "Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdl"
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct FW
unit u
unit a1
unit a2
unit a3
private static real interval = 1.0
private static integer array indexAR
private static integer index = 0
private method destroy takes nothing returns nothing
call SetUnitPropWindow(.u, 1)
call UnitRemoveAbility(.u, 'Abun')
call UnitRemoveAbility(.u, 'Avul')
call SuspendHeroXP(.u, false)
call ShowUnit(.u, true)
call DestroyEffect(AddSpecialEffectTarget(CASTER_SFX_RETAIN, .u, "origin"))
set .u = null
set .a1 = null
set .a2 = null
set .a3 = null
call .deallocate()
endmethod
static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = indexAR[i]
if not (UnitAlive(.a1) or UnitAlive(.a2) or UnitAlive(.a3)) then
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set index = index - 1
set i = i-1
if index==0 then
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endif
endif
exitwhen i==index
endloop
endmethod
static method onCast takes nothing returns nothing
local thistype this = allocate()
local real facing = GetUnitFacing(GetTriggerUnit())
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
set .u = GetTriggerUnit()
set .a1 = CreateUnit(GetTriggerPlayer(), AVATAR_1, x, y, facing)
set .a2 = CreateUnit(GetTriggerPlayer(), AVATAR_2, x, y, facing)
set .a3 = CreateUnit(GetTriggerPlayer(), AVATAR_3, x, y, facing)
call DestroyEffect(AddSpecialEffectTarget(SFX_AVATAR_APPEAR, .a1, "origin"))
call DestroyEffect(AddSpecialEffectTarget(SFX_AVATAR_APPEAR, .a2, "origin"))
call DestroyEffect(AddSpecialEffectTarget(SFX_AVATAR_APPEAR, .a3, "origin"))
call UnitApplyTimedLife(.a1, 'BTLF', AVATAR_LIFE)
call UnitApplyTimedLife(.a2, 'BTLF', AVATAR_LIFE)
call UnitApplyTimedLife(.a3, 'BTLF', AVATAR_LIFE)
call SetUnitUseFood(.a1, false)
call SetUnitUseFood(.a2, false)
call SetUnitUseFood(.a3, false)
call SetUnitPropWindow(.u, 0)
call UnitAddAbility(.u, 'Abun')
call UnitAddAbility(.u, 'Avul')
call SuspendHeroXP(.u, true)
call ShowUnit(.u, false)
if index==0 then
call TimerStart(CreateTimer(), interval, true, function thistype.periodic)
endif
set index = index + 1
set indexAR[index] = this
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope IceDevastation initializer init
private function onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local unit portal = CreateUnit(GetTriggerPlayer(), 'h007', GetUnitX(target), GetUnitY(target), GetUnitFacing(u))
call SetUnitX(target, GetUnitX(u)+150*Cos(GetUnitFacing(portal)*bj_DEGTORAD))
call SetUnitY(target, GetUnitY(u)+150*Sin(GetUnitFacing(portal)*bj_DEGTORAD))
call MDCCastToTarget(GetTriggerPlayer(), target, GetUnitX(portal), GetUnitY(portal), 'A01L', OID_thunderbolt, GetUnitAbilityLevel(u, 'A00C'))
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl", target, "origin"))
call UnitApplyTimedLife(portal, 'BTLF', 0.1)
set portal = null
set target = null
set u = null
endfunction
private function init takes nothing returns nothing
call PreloadSpell('A01L')
call RegisterSpellEffectEvent('A00C', function onCast)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function FreezingPointActions takes nothing returns nothing
call MDCCastToTarget(GetTriggerPlayer(), GetTriggerUnit(), GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), 'A00R', OrderId("frostarmor"), GetUnitAbilityLevel(GetTriggerUnit(), 'A017'))
endfunction
function InitTrig_FrostArmor takes nothing returns nothing
call PreloadSpell('A00R')
call RegisterSpellEffectEvent('A017', function FreezingPointActions)
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library FrostStrike initializer init uses DamageEvent, DummyInstantCaster
globals
private constant integer PASSIVE_SPELL_ID = 'A00D'
private constant integer DUMMY_SPELL_ID = 'A00E' //frost nova
endglobals
private function GetChance takes integer level returns integer
return 10 + level * 5
endfunction
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function FrostStrikeAction takes nothing returns nothing
local integer level = GetUnitAbilityLevel(PDDS.source, PASSIVE_SPELL_ID)
if level > 0 then
if PDDS.damageType==PHYSICAL and IsUnitEnemy(PDDS.source, GetOwningPlayer(PDDS.target)) then
if GetRandomInt(1, 100) < GetChance(level) and FilterTargets(PDDS.target) then
call DC.setDummyFrom(GetOwningPlayer(PDDS.source), GetUnitX(PDDS.target), GetUnitY(PDDS.target), DUMMY_SPELL_ID, level)
call DC.castToUnit(PDDS.target, DUMMY_SPELL_ID, OID_frostnova)
endif
endif
endif
endfunction
private function init takes nothing returns nothing
call AddDamageHandler(function FrostStrikeAction)
endfunction
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
library Felsburg uses RegisterPlayerUnitEvent, DamageEvent
globals
private constant integer SPELL_ID = 'A00O' //passive
private constant integer DUMMY_SPELL_ID = 'A00P' //waterelemental
private constant integer ORDER_ID = 852097
private Table c
private unit DUMMY
private unit tempHero
endglobals
private function FilterAttackedUnit takes unit u returns boolean
return not (IsUnitIllusion(u) or IsUnitType(u, UNIT_TYPE_HERO))
endfunction
private function GetChance takes integer level returns integer
return 10 + level * 5
endfunction
private function WaterElementalCount takes integer level returns integer
return 5 //2 + level * 3
endfunction
private struct Felsburg extends array
private static integer hID = 0
static method FelsburgSpellDamageEvent takes nothing returns nothing
local integer level = GetUnitAbilityLevel(PDDS.target, SPELL_ID)
local integer heroID
if level > 0 then
if IsUnitEnemy(PDDS.target, GetOwningPlayer(PDDS.source)) then
set heroID = GetHandleId(PDDS.target)
if GetRandomInt(1, 100) < GetChance(level) and WaterElementalCount(level) > (c[heroID]) then
//if GetRandomInt(1, 100) < 90 and WaterElementalCount(level) > (c[heroID]) then
call SetUnitOwner(DUMMY, GetOwningPlayer(PDDS.target), false)
call SetUnitX(DUMMY, GetUnitX(PDDS.source))
call SetUnitY(DUMMY, GetUnitY(PDDS.source))
call UnitAddAbility(DUMMY, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(DUMMY, DUMMY_SPELL_ID, level)
call IssueImmediateOrderById(DUMMY, ORDER_ID)
call UnitRemoveAbility(DUMMY, DUMMY_SPELL_ID)
set c.unit[GetHandleId(DUMMY)] = PDDS.target
set c[heroID] = c[heroID] + 1
endif
endif
endif
endmethod
static method deathEvent takes nothing returns nothing
local integer summonedUnit = GetHandleId(GetTriggerUnit())
if c.has(summonedUnit) then
//the hero
set hID = GetHandleId(c.unit[summonedUnit])
set c[hID] = c[hID] - 1
call c.remove(summonedUnit)
endif
endmethod
static method summon takes nothing returns nothing
local integer summoningUnitID = GetHandleId(GetSummoningUnit())
if c.has(summoningUnitID) then
//transfering the unit info from summoning unit to summoned unit
set c.unit[GetHandleId(GetTriggerUnit())] = c.unit[summoningUnitID]
//call BJDebugMsg(GetUnitName(c.unit[GetHandleId(GetTriggerUnit())]))
set c[GetHandleId(GetTriggerUnit())] = 0
endif
endmethod
static method onInit takes nothing returns nothing
call AddDamageHandler(function thistype.FelsburgSpellDamageEvent)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.deathEvent)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.summon)
set c = Table.create()
set DUMMY = CreateUnit(Player(15), DUMMY_CAST, 0, 0, 0)
call UnitRemoveAbility(DUMMY, 'Amov')
call UnitAddAbility(DUMMY, DUMMY_SPELL_ID)
call UnitRemoveAbility(DUMMY, DUMMY_SPELL_ID)
set c[GetHandleId(DUMMY)] = 0
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope TheCalling //uses T32x
private function GetDuration takes integer i returns real
return 10 + i * 10.
endfunction
private struct TC
unit caster
unit dummy
real duration
private method periodic takes nothing returns nothing
set .duration = .duration - T32_PERIOD
if not IsUnitChanneling(.caster) or 0 > .duration then
call KillUnit(.dummy)
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
private static method create takes unit caster returns thistype
local thistype this = thistype.allocate()
set .duration = GetDuration(GetUnitAbilityLevel(caster, 'A01S'))
set .caster = caster
set .dummy = CreateUnit(GetOwningPlayer(caster), DUMMY_CAST, GetUnitX(caster), GetUnitY(caster), 0)
call UnitAddAbility(.dummy, 'A00S')
call IssuePointOrder(.dummy, "blizzard", GetUnitX(caster), GetUnitY(caster))
call .startPeriodic()
return this
endmethod
private static method cond takes nothing returns boolean
if GetSpellAbilityId()=='A01S' then
call TC.create(GetTriggerUnit())
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition( function thistype.cond))
call PreloadSpell('A00S') //blizzard
set t = null
endmethod
endstruct
endscope
//TESH.scrollpos=3
//TESH.alwaysfold=0
scope LifeSteal initializer init
private function LifeStealSfx takes nothing returns string
return "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
endfunction
private function LifeStealGetDamage takes integer i returns real
return i * 100.
endfunction
private function onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, 'A00V')
local real damage = LifeStealGetDamage(level)
local real lifeState = GetWidgetLife(GetSpellTargetUnit())
local real heal
call UnitDamageTarget(u, GetSpellTargetUnit(), damage, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
set heal = lifeState - GetWidgetLife(GetSpellTargetUnit())
//call BJDebugMsg(R2S(heal))
if GetWidgetLife(u) < GetUnitState(u, UNIT_STATE_MAX_LIFE) then
call SetWidgetLife(u, GetWidgetLife(u) + heal)
call DestroyEffect(AddSpecialEffectTarget(LifeStealSfx(), u, "origin"))
endif
set u = null
endfunction
private function init takes nothing returns nothing
call RegisterSpellEffectEvent('A00V', function onCast)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Fever initializer init uses SpellEffectEvent, DummyInstantCaster, PreloadUtils
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit first
local real x = GetUnitX(GetSpellTargetUnit())
local real y = GetUnitY(GetSpellTargetUnit())
local integer level = GetUnitAbilityLevel(u, 'A018')
call GroupEnumUnitsInRange(enumG, x, y, 800, null)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, GetTriggerPlayer()) then
//call MDCCastToTarget(GetTriggerPlayer(), first, x, y, 'A008', 852527 /*shadowstrike*/, level)
call DC.setDummyFrom(GetTriggerPlayer(), x, y, 'A008', GetUnitAbilityLevel(u, 'A018'))
call DC.castToUnit(first, 'A008', 852527)
endif
call GroupRemoveUnit(enumG, first)
endloop
set u = null
endfunction
private function init takes nothing returns nothing
call PreloadSpell('A008') //casto to enemy
call RegisterSpellEffectEvent('A018', function onCast) //cast to ally
endfunction
endlibrary
//TESH.scrollpos=24
//TESH.alwaysfold=0
/*******************************************************************
Mind Control v1.0
by mckill2009
*******************************************************************
REQUIRES:
- SpellEffectEvent by Bribe
- SimError by Vexorian
********************************************************************/
library MindControl uses SpellEffectEvent, SimError
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A01V'
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant integer MAX_LEVEL = 5
private constant string START_SFX = "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
private constant string FIXED_SFX = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlTarget.mdl"
private constant string FINISH_SFX = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private group grp = CreateGroup()
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDuration takes integer level returns real
return 10. * level + 10
endfunction
private function FilterEnemies takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function FilterRandom takes nothing returns boolean
return UnitAlive(GetFilterUnit())
endfunction
private struct MC
unit caster
unit target
real duration
player pl
effect sfx
private static timer t = CreateTimer()
private static integer ind = 0
private static integer array indAR
private static method periodic takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = indAR[i]
if UnitAlive(.target) and .duration > 0 then
set .duration = .duration - 1
else
call DestroyEffect(AddSpecialEffectTarget(FINISH_SFX, .target, "origin"))
call DestroyEffect(.sfx)
call SetUnitOwner(.target, .pl, true)
call GroupRemoveUnit(grp, .target)
set .target = null
set .pl = null
call .deallocate()
set indAR[i] = indAR[ind]
set indAR[ind] = this
set ind = ind - 1
set i = i - 1
if ind==0 then
call PauseTimer(t)
endif
endif
exitwhen i==ind
endloop
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit u = GetSpellTargetUnit()
local integer level
if IsUnitInGroup(u, grp) then
call IssueImmediateOrder(GetTriggerUnit(), "stop")
call SimError(GetLocalPlayer(), "Unable to target unit that has been controlled already.")
else
if GetUnitLevel(u) > MAX_LEVEL then
call IssueImmediateOrder(GetTriggerUnit(), "stop")
call SimError(GetLocalPlayer(), "Unable to control units above level " + I2S(MAX_LEVEL))
else
set this = allocate()
set .target = GetSpellTargetUnit()
set .duration = GetDuration(GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID))
set .pl = GetOwningPlayer(.target)
set .sfx = AddSpecialEffectTarget(FIXED_SFX, .target, "overhead")
call DestroyEffect(AddSpecialEffectTarget(START_SFX, .target, "origin"))
call SetUnitOwner(.target, GetTriggerPlayer(), true)
call GroupAddUnit(grp, .target)
if .ind==0 then
call TimerStart(t, 1.0, true, function thistype.periodic)
endif
set ind = ind + 1
set indAR[ind] = this
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
/***************************************************************
Doppelganger (passive) v1.1
by mckill2009
****************************************************************
REQUIRES:
- Table by Bribe
- RegisterPlayerUnitEvent by Magtheridon96
- DamageEvent by looking_for_help
****************************************************************/
library Doppelganger initializer init uses Table, RegisterPlayerUnitEvent, DamageEvent
globals
private constant integer SPELL_ID = 'A013' //passive
private constant integer MAX_LEVEL = 5
private constant string SFX = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
private constant string DOPP_SFX = "Abilities\\Spells\\Human\\Banish\\BanishTarget.mdl"
private TableArray d
private group grp = CreateGroup()
private group doppG = CreateGroup()
endglobals
/***************************************************************
CONFIGURABLE FUNCTIONS
****************************************************************/
private function FilterOutDamageSource takes unit attacker returns boolean
return not (IsUnitType(attacker, UNIT_TYPE_STRUCTURE) or IsUnitType(attacker, UNIT_TYPE_FLYING) /*
*/ or IsUnitType(attacker, UNIT_TYPE_HERO) or IsUnitType(attacker, UNIT_TYPE_MECHANICAL) /*
*/ or IsUnitType(attacker, UNIT_TYPE_MAGIC_IMMUNE))
endfunction
private function GetChance takes integer level returns integer
return 5 * level + 10 //15, 20, 25, 30, 35
endfunction
private function GetDuration takes integer level returns real
return level * 10. //10, 20, 30, 40, 50
endfunction
/***************************************************************
NON-CONFIGURABLE FUNCTIONS
****************************************************************/
private function UnitNotInGroup takes unit u returns boolean
return not IsUnitInGroup(u, grp) and not IsUnitInGroup(u, doppG)
endfunction
private function FilterOutDamageSourceU takes unit attacker returns boolean
return not IsUnitIllusion(attacker) and FilterOutDamageSource(attacker)
endfunction
private function CreateDoppelganger takes unit attacked, unit source, integer level returns nothing
local unit dummy = CreateUnit(GetOwningPlayer(attacked), GetUnitTypeId(source), GetUnitX(source), GetUnitY(source), GetUnitFacing(attacked))
call UnitApplyTimedLife(dummy, 'BTLF', GetDuration(level))
call DestroyEffect(AddSpecialEffectTarget(SFX, dummy, "origin"))
call GroupAddUnit(doppG, dummy)
set d[1].unit[GetHandleId(dummy)] = source
set d[2].effect[GetHandleId(dummy)] = AddSpecialEffectTarget(DOPP_SFX, dummy, "origin")
call SetUnitVertexColor(dummy, 140, 200, 50, 100)
call SetUnitUseFood(dummy, false)
set dummy = null
endfunction
//PDDS.target is the one who have the SPELL_ID
private function OnDamage takes nothing returns nothing
local integer level
if IsUnitEnemy(PDDS.source, GetOwningPlayer(PDDS.target)) then
set level = GetUnitAbilityLevel(PDDS.target, SPELL_ID)
if level > 0 then
if GetRandomInt(1, 100) < GetChance(level) then
if UnitNotInGroup(PDDS.source) and FilterOutDamageSourceU(PDDS.source) and (GetUnitLevel(PDDS.source) <= MAX_LEVEL) then
call GroupAddUnit(grp, PDDS.source)
call CreateDoppelganger(PDDS.target, PDDS.source, level)
endif
endif
endif
endif
endfunction
private function DoppelgangerDeath takes nothing returns nothing
local integer id
if IsUnitInGroup(GetTriggerUnit(), grp) then
call GroupRemoveUnit(grp, GetTriggerUnit())
elseif IsUnitInGroup(GetTriggerUnit(), doppG) then
set id = GetHandleId(GetTriggerUnit())
call GroupRemoveUnit(doppG, GetTriggerUnit())
call GroupRemoveUnit(grp, d[1].unit[id]) //this is the source
call DestroyEffect(d[2].effect[id])
set d[1].unit[id] = null
set d[2].effect[id] = null
endif
endfunction
private function init takes nothing returns nothing
call AddDamageHandler(function OnDamage)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function DoppelgangerDeath)
set d = TableArray[0x2000]
endfunction
endlibrary
//TESH.scrollpos=78
//TESH.alwaysfold=0
library Karma uses T32, RandomUnit
globals
private constant integer SUMMONED_UNIT_ID = 'ohwd'
//=================CONFIGURATIONS==================//
private constant string SFX = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl"
private constant real AOE = 800
private integer array SUMMONED_SPELL_ID
endglobals
private function SetupSkills takes nothing returns nothing
set SUMMONED_SPELL_ID[0] = 'A00Z' //chainlightning
set SUMMONED_SPELL_ID[1] = 'A010' //polymorph
set SUMMONED_SPELL_ID[2] = 'A011' //slow
endfunction
private function PreLoadAll takes nothing returns nothing
local integer i = 0
local unit dummy = CreateUnit(Player(15), 'hpea', 0,0,0)
call UnitApplyTimedLife(dummy, 'BTLF', 1.0)
call Preload(SFX)
loop
call UnitAddAbility(dummy, SUMMONED_SPELL_ID[i])
set i = i + 1
exitwhen i==3
endloop
call ShowUnit(dummy, false)
set dummy = null
endfunction
private struct KAR
unit caster
unit karma
static thistype DATA
static method first takes nothing returns boolean
local thistype this = DATA
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(.karma, GetOwningPlayer(GetFilterUnit()))
endmethod
method periodic takes nothing returns nothing
local unit u
local integer random
if UnitAlive(.karma) then
set DATA = this
set u = GetRandomUnitInArea(GetUnitX(.karma), GetUnitY(.karma), AOE, null)
if u!=null then
set random = GetRandomInt(1, 3)
if random==1 then
call IssueTargetOrder(.karma, "chainlightning", u)
elseif random==2 then
call IssueTargetOrder(.karma, "slow", u)
elseif random==3 then
call IssueTargetOrder(.karma, "polymorph", u)
endif
endif
else
call DestroyEffect(AddSpecialEffect(SFX, GetUnitX(.karma), GetUnitY(.karma)))
call .destroy()
call .stopPeriodic()
endif
endmethod
implement T32x
private static method create takes unit caster, unit karma returns thistype
local thistype this = thistype.allocate()
local integer level = GetUnitLevel(karma)
local integer i = 0
set .caster = caster
set .karma = karma
call UnitAddAbility(karma, 'Aloc')
loop
call UnitAddAbility(.karma, SUMMONED_SPELL_ID[i])
call SetUnitAbilityLevel(.karma, SUMMONED_SPELL_ID[i], level)
set i = i + 1
exitwhen i==3
endloop
call .startPeriodic()
return this
endmethod
private static method cond takes nothing returns boolean
//trigger unit is the unit being summoned
if GetUnitTypeId(GetTriggerUnit()) == SUMMONED_UNIT_ID then
call thistype.create(GetSummoningUnit(), GetTriggerUnit())
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SUMMON)
call TriggerAddCondition(t, Condition(function thistype.cond))
call SetupSkills()
call PreLoadAll()
set t = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library FlameWard uses T32
globals
private constant integer SPELL_ID = 'A016'
private constant integer BUFF_ID = 'BNpi' //immolation
private constant integer FLAME_ID = 'fwrd' //flame is A02J
private constant string SFX = "Environment\\NightElfBuildingFire\\ElfLargeBuildingFire1.mdl"
private group grp = CreateGroup()
endglobals
private function GetDamage takes integer level returns real
return 2. * level
endfunction
private struct FlameWard
unit u
player pl
real damage
real x
real y
effect sfx
private method periodic takes nothing returns nothing
local unit first
if UnitAlive(.u) then
call GroupEnumUnitsInRange(grp, .x, .y, 550, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) and GetUnitAbilityLevel(first, BUFF_ID) > 0 then
call UnitDamageTarget(.u, first, .damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, null)
endif
call GroupRemoveUnit(grp, first)
endloop
else
call DestroyEffect(.sfx)
set .u = null
set .sfx = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method cond takes nothing returns nothing
local thistype this
local integer level
if GetUnitTypeId(GetTriggerUnit())==FLAME_ID then
set this = allocate()
set level = GetUnitAbilityLevel(GetSummoningUnit(), SPELL_ID)
set .u = GetTriggerUnit()
set .x = GetUnitX(.u)
set .y = GetUnitY(.u)
set .pl = GetTriggerPlayer()
set .sfx = AddSpecialEffect(SFX, .x, .y)
set .damage = GetDamage(level)*T32_PERIOD
call .startPeriodic()
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.cond)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WalkingBomb uses SpellEffectEvent
globals
private constant integer SPELL_ID = 'A02S'
private constant integer BUFF_ID = 'B00I'
private constant string SFX_TARGET = "Abilities\\Spells\\Other\\Incinerate\\IncinerateBuff.mdl"
private constant string SFX_EXPLODE = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
private constant attacktype ATT = ATTACK_TYPE_MAGIC
private constant damagetype DAM = DAMAGE_TYPE_FIRE
private constant real INTERVAL = 0.5
private constant real AOE = 400.
private group tempG = CreateGroup()
private integer ind = 0
private integer array indAR
private timer t = CreateTimer()
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
private function GetDamage takes integer i returns real
return 50. * i + 50
endfunction
private function GetDuration takes integer i returns real
return 3.
endfunction
private struct WalkingBomb
unit caster
unit target
player pl
real damage
real bigDamage
real duration
effect sfx
boolean isOn
private method destroy takes nothing returns nothing
call DestroyEffect(.sfx)
set .pl = null
set .caster = null
set .target = null
set .sfx = null
call .deallocate()
endmethod
private method damageThem takes nothing returns nothing
local unit first
call GroupEnumUnitsInRange(tempG, GetUnitX(.target), GetUnitY(.target), AOE, null)
loop
set first = FirstOfGroup(tempG)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) then
if UnitAlive(.target) then
call UnitDamageTarget(.caster, first, .damage, false, false, ATT, DAM, null)
else
call UnitDamageTarget(.caster, first, .bigDamage, false, false, ATT, DAM, null)
endif
call DestroyEffect(AddSpecialEffectTarget(SFX_EXPLODE, first, "chest"))
endif
call GroupRemoveUnit(tempG, first)
endloop
set .isOn = false
endmethod
private static method looper takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = indAR[i]
if .isOn then
if UnitAlive(.target) then
set .duration = .duration - INTERVAL
if .duration < 0 then
call .damageThem()
endif
else
call .damageThem()
endif
else
call .destroy()
set indAR[i] = indAR[ind]
set indAR[ind] = this
set i = i - 1
set ind = ind - 1
if ind==0 then
call PauseTimer(t)
endif
endif
exitwhen i==ind
endloop
endmethod
private static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .caster = GetTriggerUnit()
set .target = GetSpellTargetUnit()
set .sfx = AddSpecialEffectTarget(SFX_TARGET, .target, "chest")
set .damage = GetDamage(level)
set .bigDamage = .damage*2
set .duration = GetDuration(level)
set .pl = GetTriggerPlayer()
set .isOn = true
if ind==0 then
call TimerStart(t, INTERVAL, true, function thistype.looper)
endif
set ind = ind + 1
set indAR[ind] = this
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//By: Roflcoptor
scope FieryRage initializer init
globals
//The Spell ID. Click ctrl+d in object editor to check if your's is the right.
private constant integer SpellID = 'A02C'
//The ID of the dummy unit, which has to be imported.
private constant integer DummyID = 'e002'
//Just neccesary, if you modified existing skills.
private constant integer CrowFormID = 'Amrf'
//The SFX attached to the first dummies, which are flying in a hilf circle from the caster to the target.
private constant string Dummy1attach1 = "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl"
//See above
private constant string Dummy1attach2 = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
//The SFX attached to the second dummies, which are flying at changing heights, away from the detonation center.
private constant string Dummy2attach1 = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl"
//See above
private constant string Dummy2attach2 = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
//The SFX attached to the third dummies, which are generated out of the second ones.
private constant string Dummy3attach = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
//The SFX shown, when the first dummies arrive.
private constant string SFX1 = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
//The SFX shown, when the second dummies arrive.
private constant string SFX2 = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
//See above
private constant string SFX3 = "Abilities\\Spells\\Other\\Volcano\\VolcanoDeath.mdl"
//The SFX which is shown on every unit (with a 10% chance) every TimerInt, when they are near a projectile.
private constant string SpamSFX = "Environment\\SmallBuildingFire\\SmallBuildingFire2.mdl"
//The size of the first SFX, whereas 1. would be the normal size.
private constant real SFX1size = 1.5
//Quite self-explanatory, isn't it?
private constant real TimerInt = .03
//Damage Setup
private constant boolean Attack = false
//Damage Setup
private constant boolean Ranged = false
//Damage Setup
private constant attacktype AttackType = ATTACK_TYPE_CHAOS
//Damage Setup
private constant damagetype DamageType = DAMAGE_TYPE_NORMAL
//Damage Setup
private constant weapontype WeaponType = WEAPON_TYPE_WHOKNOWS
//The steps, the first dummies take to reach their target, so less steps mean more speed.
private constant integer Steps1 = 40
//The steps, the second dummies take to reach their target, so less steps mean more speed.
private constant integer Steps2 = 20
//The steps, the third dummies take to reach their target, so less steps mean more speed.
private constant integer Steps3 = 16
endglobals
//The number of the first dummies. This should be an uneven number, so you have one missile flying in the middle.
//Else it will look awkward.
private function Dummys1 takes integer lev returns integer
return 5
endfunction
//The number of the second dummies.
private function Dummys2 takes integer lev returns integer
return 10
endfunction
//The number of the first dummies. It should be divideable by Dummys2, since every Dummy3 has its origin in a Dummy2.
private function Dummys3 takes integer lev returns integer
return Dummys2(0)*3
endfunction
//The damage done, when the first dummies reach their target.
private function Damage1 takes real dist, integer lev returns real
return 350. //lev*100.
endfunction
//The damage done, when the second dummies reach their target.
private function Damage2 takes real dist, integer lev returns real
return 150. //lev*25.
endfunction
//The damage done, when the unit is near a projectile.
private function DamagePerSec takes real dist, integer lev returns real
return 20. //lev*10.*TimerInt
endfunction
//The Aoe of Damage1.
private function AoE1 takes real dist, integer lev returns real
return dist/2.
endfunction
//The Aoe of Damage2.
private function AoE2 takes real dist, integer lev returns real
return dist/3.
endfunction
//The maximum flying height of the second dummies.
private function Dummy2maxHeight takes real dist, integer lev returns real
return AoE2(dist, 0)
endfunction
//The flying height of the third dummies. I chose a random number to imitate an explosion (scatters flying around).
private function Dummy3maxHeight takes real dist, integer lev returns real
return GetRandomReal(0., 300.)
endfunction
//The AoE of Damage3.
private function DamagePerSecAoe takes real dist, integer lev returns real
return 220.
endfunction
//Ignore the following 5 lines.
globals
private player p
private boolean heightcheck
private real heighttolerance = 20.
endglobals
//Setup continues
//The Filter for units.
private function UnitFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), p) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) and GetWidgetLife(GetFilterUnit())>.405
endfunction
//End of Setup
globals
private boolexpr b
private group g = CreateGroup()
endglobals
private struct data
unit cast
unit array dum [50]
real dist
real curdist
real aoe1
real aoe2
real aoe3
real dmg1
real dmg2
real dmg3
real cx
real cy
real tx
real ty
real ang
real array dumang [50]
real formula
real maxheight2
real array maxheight3 [50]
integer step
integer dummys1
integer dummys2
integer dummys3
effect e0
effect array e1 [50]
effect array e2 [50]
static data array ar
static timer tim = CreateTimer()
static integer total = 0
static real dam
static unit cas
static real h
static method groupdamage takes nothing returns nothing
local unit u = GetEnumUnit()
if heightcheck then
if GetUnitFlyHeight(u)>data.h-heighttolerance and GetUnitFlyHeight(u)<data.h+heighttolerance then
call UnitDamageTarget(data.cas, u, data.dam, Attack, Ranged, AttackType, DamageType, WeaponType)
if GetRandomInt(1, 10) == 10 then
call DestroyEffect(AddSpecialEffect(SpamSFX, GetUnitX(u), GetUnitY(u)))
endif
call GroupRemoveUnit(g, u)
endif
else
call UnitDamageTarget(data.cas, u, data.dam, Attack, Ranged, AttackType, DamageType, WeaponType)
if GetRandomInt(1, 10) == 10 then
call DestroyEffect(AddSpecialEffect(SpamSFX, GetUnitX(u), GetUnitY(u)))
endif
call GroupRemoveUnit(g, u)
endif
set u = null
endmethod
static method periodic takes nothing returns nothing
local data d
local integer i = 0
local integer ii = 0
local integer iii = 0
local real x = .0
local real y = .0
local unit u
loop
exitwhen i >= d.total
set d = d.ar[i]
if d.step == 0 then
loop
exitwhen ii >= d.dummys1
set ii = ii + 1
set d.dum[ii] = CreateUnit(Player(15), DummyID, d.cx, d.cy, d.ang)
call PauseUnit(d.dum[ii], true)
set d.e1[ii] = AddSpecialEffectTarget(Dummy1attach1 , d.dum[ii], "chest")
set d.e2[ii] = AddSpecialEffectTarget(Dummy1attach2 , d.dum[ii], "origin")
call UnitAddAbility(d.dum[ii], CrowFormID)
endloop
set d.formula = bj_PI/2.
set ii = 0
set d.step = 1
endif
if d.step == 1 then
set d.formula = d.formula + bj_PI/(Steps1*2.)
loop
exitwhen ii >= d.dummys1
set ii = ii + 1
set d.curdist = d.curdist + d.dist/(Steps1*5.)
set x = d.cx+d.curdist*Cos(d.ang+Sin(d.formula)*(bj_PI/2.)*(I2R(ii-(d.dummys1/2+1))/2.))
set y = d.cy+d.curdist*Sin(d.ang+Sin(d.formula)*(bj_PI/2.)*(I2R(ii-(d.dummys1/2+1))/2.))
call SetUnitPosition(d.dum[ii], x, y)
set p = GetOwningPlayer(d.cast)
call GroupEnumUnitsInRange(g, x, y, d.aoe3, b)
set d.dam = d.dmg3
set d.cas = d.cast
set heightcheck = false
call ForGroup(g, function data.groupdamage)
if d.formula >= bj_PI then
set d.step = 2
call DestroyEffect(d.e1[ii])
call DestroyEffect(d.e2[ii])
call RemoveUnit(d.dum[ii])
endif
endloop
set ii = 0
endif
if d.step == 2 then
set p = GetOwningPlayer(d.cast)
call GroupEnumUnitsInRange(g, d.tx, d.ty, d.aoe1, b)
set d.dam = d.dmg1
set d.cas = d.cast
set heightcheck = false
call ForGroup(g, function data.groupdamage)
set u = CreateUnit(Player(15), DummyID, d.tx, d.ty, .0)
call SetUnitScale(u, SFX1size, SFX1size, SFX1size)
set d.e0 = AddSpecialEffectTarget(SFX1, u, "chest")
call UnitApplyTimedLife(u, 'BTLF', 1.)
call PauseUnit(u, true)
set u = null
loop
exitwhen ii >= d.dummys2
set ii = ii + 1
set d.dum[ii] = CreateUnit(Player(15), DummyID, d.tx, d.ty, d.ang)
call PauseUnit(d.dum[ii], true)
set d.e1[ii] = AddSpecialEffectTarget(Dummy2attach1 , d.dum[ii], "chest")
set d.e2[ii] = AddSpecialEffectTarget(Dummy2attach2 , d.dum[ii], "origin")
call UnitAddAbility(d.dum[ii], CrowFormID)
set d.dumang[ii] = bj_PI*2./ii
endloop
set ii = 0
set d.step = 3
set d.formula = 0.
set d.curdist = 0.
endif
if d.step == 3 then
set d.formula = d.formula + bj_PI/Steps2
set d.curdist = d.curdist + d.aoe2/Steps2
loop
exitwhen ii >= d.dummys2
set ii = ii + 1
set x = d.tx+d.curdist*Cos(bj_PI*2./d.dummys2*ii)
set y = d.ty+d.curdist*Sin(bj_PI*2./d.dummys2*ii)
call SetUnitPosition(d.dum[ii], x, y)
call SetUnitFlyHeight(d.dum[ii], d.maxheight2*Sin(d.formula), 0.)
set p = GetOwningPlayer(d.cast)
call GroupEnumUnitsInRange(g, x, y, d.aoe3, b)
set d.dam = d.dmg3
set d.cas = d.cast
set d.h = GetUnitFlyHeight(d.dum[ii])
set heightcheck = true
call ForGroup(g, function data.groupdamage)
endloop
set ii = 0
if d.formula >= bj_PI then
set d.step = 4
endif
endif
if d.step == 4 then
call DestroyEffect(d.e0)
loop
exitwhen ii == d.dummys2
set ii = ii + 1
set x = GetUnitX(d.dum[ii])
set y = GetUnitY(d.dum[ii])
set p = GetOwningPlayer(d.cast)
call GroupEnumUnitsInRange(g, x, y, d.aoe3, b)
set d.dam = d.dmg2
set d.cas = d.cast
set heightcheck = false
call ForGroup(g, function data.groupdamage)
call DestroyEffect(AddSpecialEffect(SFX2, x, y))
call DestroyEffect(AddSpecialEffect(SFX3, x, y))
call DestroyEffect(d.e1[ii])
call DestroyEffect(d.e2[ii])
call RemoveUnit(d.dum[ii])
endloop
set ii = 0
set d.step = 5
endif
if d.step == 5 then
loop
exitwhen ii == d.dummys2
set ii = ii + 1
set x = d.tx+d.aoe2*Cos(bj_PI*2./d.dummys2*ii)
set y = d.ty+d.aoe2*Sin(bj_PI*2./d.dummys2*ii)
loop
exitwhen iii == d.dummys3/d.dummys2
set iii=iii+1
set d.dum[iii] = CreateUnit(Player(15), DummyID, x, y, (ii*(bj_PI*2.)/3.))
call PauseUnit(d.dum[ii], true)
set d.e1[iii] = AddSpecialEffectTarget(Dummy3attach , d.dum[iii], "chest")
call UnitAddAbility(d.dum[iii], CrowFormID)
set d.maxheight3[iii] = Dummy3maxHeight(d.dist, GetUnitAbilityLevel(d.cast, SpellID))
endloop
endloop
set d.formula = 0.
set ii = 0
set iii = 0
set d.step = 6
endif
if d.step == 6 then
set d.formula = d.formula+1./Steps3
loop
exitwhen ii == d.dummys3
set ii = ii + 1
set x = GetUnitX(d.dum[ii])+d.aoe3/20.*Cos(ii*(bj_PI*2.)/3.)
set y = GetUnitY(d.dum[ii])+d.aoe3/20.*Sin(ii*(bj_PI*2.)/3.)
call SetUnitPosition(d.dum[ii], x, y)
call SetUnitFlyHeight(d.dum[ii], d.maxheight3*d.formula, 0.)
set p = GetOwningPlayer(d.cast)
set d.cas = d.cast
set d.dam = d.dmg3
set heightcheck = true
set d.h = GetUnitFlyHeight(d.dum[ii])
call GroupEnumUnitsInRange(g, x, y, d.aoe3, b)
call ForGroup(g, function data.groupdamage)
endloop
set ii = 0
if d.formula >= 1. then
loop
exitwhen ii == d.dummys3
set ii = ii + 1
call DestroyEffect(d.e1[ii])
call RemoveUnit(d.dum[ii])
endloop
set ii = 0
set d.formula = 0
set d.curdist = 0
set d.total = d.total - 1
set d.ar[i] = d.ar[d.total]
call d.destroy()
set i = i - 1
endif
endif
set i = i + 1
endloop
if d.total == 0 then
call PauseTimer(d.tim)
endif
endmethod
static method actions takes nothing returns nothing
local data d = data.allocate()
local integer lev = GetUnitAbilityLevel(GetTriggerUnit(), SpellID)
set d.tx = GetSpellTargetX()
set d.ty = GetSpellTargetY()
set d.cast = GetTriggerUnit()
set d.cx = GetUnitX(d.cast)
set d.cy = GetUnitY(d.cast)
set d.ang = Atan2(d.ty-d.cy, d.tx-d.cx)
set d.dist = SquareRoot(Pow(d.tx-d.cx, 2.)+Pow(d.ty-d.cy,2.))
set d.aoe1 = AoE1(d.dist, lev)
set d.aoe2 = AoE2(d.dist, lev)
set d.aoe3 = DamagePerSecAoe(d.dist, lev)
set d.dmg1 = Damage1(d.dist, lev)
set d.dmg2 = Damage2(d.dist, lev)
set d.dmg3 = DamagePerSec(d.dist, lev)*TimerInt
set d.maxheight2 = Dummy2maxHeight(d.dist, lev)
set d.step = 0
set d.dummys1 = Dummys1(lev)
set d.dummys2 = Dummys2(lev)
set d.dummys3 = Dummys3(lev)
if d.total == 0 then
call TimerStart(d.tim, TimerInt, true, function data.periodic)
endif
set d.ar[d.total] = d
set d.total = d.total + 1
endmethod
endstruct
private function condition takes nothing returns boolean
return GetSpellAbilityId()==SpellID
endfunction
private function noleakfilter takes nothing returns boolean
return true
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local filterfunc ff = Filter(function noleakfilter)
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, ff)
set i = i + 1
endloop
set i = 0
call TriggerAddAction(t, function data.actions)
call TriggerAddCondition(t, Condition(function condition))
set b = Condition(function UnitFilter)
call DestroyFilter(ff)
set ff = null
endfunction
endscope
//TESH.scrollpos=24
//TESH.alwaysfold=0
/****************************************************************
GiantFireball v1.0
by mckill2009
*****************************************************************
REQUIRED LIBRARIES:
- SpellEffectEvent by Bribe
- BoundSentinel by Vexorian
*****************************************************************/
library GiantFireball uses SpellEffectEvent, BoundSentinel
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A02D'
private constant integer FIREBALL_ID = 'h009'
/*************************************************
* CONFIGURABLE GLOBALS
**************************************************/
private constant real SPEED = 5
private constant real AOE_DAMAGE = 150
private constant real CREATION_OFFSET = 150
private constant string SFX = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl"
private constant string ATTACHMENT = "overhead"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_FIRE
/*************************************************
* NON-CONFIGURABLE GLOBALS
**************************************************/
private group grp = CreateGroup()
endglobals
/*************************************************
* CONFIGURABLE FUNCTIONS
**************************************************/
private function GetDamage takes integer level returns real
return 100 + level * 50.
endfunction
private function GetDistance takes integer level returns real
return 600. //+ level * 300. //
endfunction
/*************************************************
* NON-CONFIGURABLE FUNCTIONS
**************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct GiantFireball extends array
unit caster
unit fireball
player owner
real angle
real distance
real distx
real xFireball
real yFireball
real damage
//used to index this struct
static integer ins = 0
static integer array insAR
//used to index instances
static integer index = 0
static integer array indexAR
static timer t = CreateTimer()
//non-constant
static thistype DATA
static real periodic = 0.03125
static method looper takes nothing returns nothing
local thistype this
local integer i = 0
local unit first
loop
set i = i+1
set this = indexAR[i]
if .distance > .distx then
set .distx = .distx + SPEED
call SetUnitX(.fireball, .xFireball + .distx * Cos(.angle))
call SetUnitY(.fireball, .yFireball + .distx * Sin(.angle))
call GroupEnumUnitsInRange(grp, GetUnitX(.fireball), GetUnitY(.fireball), AOE_DAMAGE, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .owner) then
call UnitDamageTarget(.fireball, first, .damage, false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(SFX, first, ATTACHMENT))
endif
call GroupRemoveUnit(grp, first)
endloop
else
call KillUnit(.fireball)
set .owner = null
set .caster = null
set .fireball = null
set insAR[this] = insAR[0]
set insAR[0] = this
set indexAR[i] = indexAR[index]
set index = index - 1
set i = i - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
static method onCast takes nothing returns nothing
local thistype this = insAR[0]
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
if this==0 then
set ins = ins + 1
set this = ins
else
set insAR[0] = insAR[this]
endif
set .damage = GetDamage(level) * periodic
set .owner = GetTriggerPlayer()
set .xFireball = GetUnitX(u)
set .yFireball = GetUnitY(u)
set .angle = Atan2(GetSpellTargetY() - .yFireball, GetSpellTargetX() - .xFireball)
set .distance = GetDistance(level) + CREATION_OFFSET
set .fireball = CreateUnit(.owner, FIREBALL_ID, .xFireball + CREATION_OFFSET * Cos(.angle), .yFireball + CREATION_OFFSET * Sin(.angle), 0)
set .distx = CREATION_OFFSET
if index==0 then
call TimerStart(t, periodic, true, function thistype.looper)
endif
set index = index + 1
set indexAR[index] = this
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=43
//TESH.alwaysfold=0
library SpittingFire uses T32, SpellEffectEvent, RandomUnit
globals
private constant integer SPELL_ID = 'A00F'
private constant integer DUMMY_SPELL_ID = 'A02E' //firebolt
private constant integer FIRE_ID = 'h008'
private constant integer ORDER = 852231 //firebolt
//===CONFIGURABLES:
private constant boolean ENABLE_RANGE = true //sets if fire will spit another fire or not
private constant attacktype ATK = ATTACK_TYPE_MAGIC
private constant damagetype DMG = DAMAGE_TYPE_FIRE
private constant real AOE_DAMAGE = 128
private constant real FIRE_RANGE = 450
endglobals
//===CONFIGURABLES:
private function FilterOutMoreUnits takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL) /*
*/ or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))
endfunction
private function GetDuration takes integer i returns real
return 10. //10.
endfunction
private function GetDamage takes integer i returns real
return 10 + i * 10.
endfunction
//===END OF CONFIGURABLES
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct SF
unit caster
unit fire
unit target
real angle
real duration
real damage
private static player PL
private static method fil takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), PL) and FilterOutMoreUnits(GetFilterUnit())
endmethod
private method periodic takes nothing returns nothing
local unit first
local real x
local real y
if .duration > 0 and UnitAlive(.caster) then
set .duration = .duration - T32_PERIOD
set .angle = .angle + 0.1
call SetUnitX(.fire, GetUnitX(.caster)+150*Cos(.angle))
call SetUnitY(.fire, GetUnitY(.caster)+150*Sin(.angle))
set x = GetUnitX(.fire)
set y = GetUnitY(.fire)
call GroupEnumUnitsInRange(enumG, x, y, AOE_DAMAGE, null)
loop
set first=FirstOfGroup(enumG)
exitwhen first == null
if UnitAlive(first) and IsUnitEnemy(first, GetOwningPlayer(.fire)) then
call UnitDamageTarget(.fire, first, .damage, false, false, ATK, DMG, null)
endif
call GroupRemoveUnit(enumG, first)
endloop
if ENABLE_RANGE then
set PL = GetOwningPlayer(.caster)
set first = GetRandomUnitInArea(x, y, FIRE_RANGE, Filter(function thistype.fil))
if UnitAlive(first) then
call IssueTargetOrderById(.fire, ORDER, first)
endif
endif
else
call KillUnit(.fire)
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method create takes nothing returns thistype
local thistype this = thistype.allocate()
local unit caster = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(caster, SPELL_ID)
set .caster = caster
set .duration = GetDuration(level)
set .damage = GetDamage(level)*T32_PERIOD
set .angle = 0
set .fire = CreateUnit(GetTriggerPlayer(), FIRE_ID, 0, 0, 0)
call UnitAddAbility(.fire, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(.fire, DUMMY_SPELL_ID, level)
set .target = null
call .startPeriodic()
set caster = null
return this
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.create)
call PreloadUnit(FIRE_ID)
call PreloadSpell(DUMMY_SPELL_ID)
endmethod
endstruct
endlibrary
//TESH.scrollpos=19
//TESH.alwaysfold=0
/*******************************************************************
Blazing Rocks v1.0 (channel version)
by mckill2009
********************************************************************
REQUIRED LIBRARIES:
- T32 by Jesus4lyf
- SpellEffectEvent by Bribe
- IsUnitChanneling by Magtheridon96
********************************************************************/
library BlazingRocksChannel uses SpellEffectEvent, T32, IsUnitChanneling
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A03V'
private constant integer DUMMY_ID = 'e00A'
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant real CREATION_DELAY = 0.5
private constant real AOE = 400
private constant real DAMAGE_AOE = 200
private constant real START_HEIGHT = 1000
private constant real SCALE = 1.5
private constant real DAMAGE_MULTIPLIER = 1
private constant integer MIN_DROP_SPEED = 10
private constant integer MAX_DROP_SPEED = 40
private constant string DUMMY_MODEL = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
private constant string FIRE = "Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl"
private constant string EXPLODE = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private group damG = CreateGroup()
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDuration takes integer i returns real
return 15. //5 * i + 15.
endfunction
private function GetDamage takes integer level returns real
return 150. //15. * level
endfunction
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS:
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function AngularHeightDrop takes real height, real distance, real speed returns real
return (distance/height) * speed
endfunction
private struct BlazingRocks
unit rock
player owner
effect model
real angle
real dam
real dist
real distX
real dropSpeed
real spellX
real spellY
real z
private method periodic takes nothing returns nothing
local unit first
local real x = GetUnitX(.rock)
local real y = GetUnitY(.rock)
if .z > 0 then
set .z = .z - .dropSpeed
call SetUnitFlyHeight(.rock, .z, 0)
call SetUnitX(.rock, x + .distX * Cos(.angle))
call SetUnitY(.rock, y + .distX * Sin(.angle))
else
call DestroyEffect(AddSpecialEffect(EXPLODE, x, y))
call GroupEnumUnitsInRange(damG, x, y, DAMAGE_AOE, null)
loop
set first = FirstOfGroup(damG)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .owner) and FilterTargets(first) then
call UnitDamageTarget(.rock, first, .dam, false, false, ATK, DMG, null)
endif
call GroupRemoveUnit(damG, first)
endloop
call DestroyEffect(.model)
call KillUnit(.rock)
set .rock = null
set .model = null
set .owner = null
call .deallocate()
call .stopPeriodic()
endif
endmethod
implement T32x
static method start takes unit caster, integer level, real x, real y, real xSpell, real ySpell returns nothing
local thistype this = allocate()
set .owner = GetOwningPlayer(caster)
set .rock = CreateUnit(.owner, DUMMY_ID, x, y, 0)
set .spellX = xSpell
set .spellY = ySpell
set .angle = Atan2(.spellY-y, .spellX-x)
set .dist = SquareRoot((.spellX-x)*(.spellX-x)+(.spellY-y)*(.spellY-y))
set .dropSpeed = GetRandomReal(I2R(MIN_DROP_SPEED), I2R(MAX_DROP_SPEED))
set .distX = AngularHeightDrop(START_HEIGHT, .dist, .dropSpeed)
set .model = AddSpecialEffectTarget(DUMMY_MODEL, .rock, "origin")
set .z = START_HEIGHT
set .dam = GetDamage(level) + (.dropSpeed * DAMAGE_MULTIPLIER)
call SetUnitScale(.rock, SCALE, SCALE, SCALE)
call .startPeriodic()
endmethod
endstruct
private struct Cast
private unit caster
private integer level
private real delay
private real duration
private real xU
private real yU
private real xSpell
private real ySpell
private method periodic takes nothing returns nothing
local real x
local real y
local real x1
local real y1
local real angle
local real offset
if IsUnitChanneling(.caster) then
if .duration > 0 then
set .duration = .duration - T32_PERIOD
set .delay = .delay + T32_PERIOD
if .delay > CREATION_DELAY then
set .delay = 0
set angle = GetRandomReal(0, 6)
set offset = GetRandomReal(I2R(50), GetRandomReal(60, AOE))
set x = .xU + offset * Cos(angle)
set y = .yU + offset * Sin(angle)
set x1 = .xSpell + offset * Cos(angle)
set y1 = .ySpell + offset * Sin(angle)
call BlazingRocks.start(.caster, .level, x, y, x1, y1)
endif
else
call IssueImmediateOrderById(.caster, 851972)
endif
else
set .caster = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
set .caster = GetTriggerUnit()
set .level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .delay = 0
set .xU = GetUnitX(.caster)
set .yU = GetUnitY(.caster)
set .xSpell = GetSpellTargetX()
set .ySpell = GetSpellTargetY()
set .duration = GetDuration(.level)
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library HealingWind initializer init uses SpellEffectEvent, MUIDummyCasters
globals
private constant integer SPELL_ID = 'A02H'
private constant integer DUMMY_SPELL_ID = 'A015'
endglobals
private function onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call MDCCastToPoint(GetTriggerPlayer(), x, y, x, y, DUMMY_SPELL_ID, OID_healingspray, GetUnitAbilityLevel(u, 'A02H'))
set u = null
endfunction
private function init takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function onCast)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Splash initializer init uses DamageEvent, DummyInstantCaster
globals
private constant integer SPELL_ID = 'A00A' //splash passive
private constant integer DUMMY_SPELL_ID = 'A00B' //firebolt
endglobals
private function GetChance takes integer level returns integer
return 10 + level * 5
endfunction
private function SplashDamageEvent takes nothing returns nothing
local integer level = GetUnitAbilityLevel(PDDS.target, SPELL_ID) //the hero
local integer heroID
if level > 0 then
if IsUnitEnemy(PDDS.target, GetOwningPlayer(PDDS.source)) then
if GetRandomInt(1, 100) < GetChance(level) then
call DC.setDummyFrom(GetOwningPlayer(PDDS.target), GetUnitX(PDDS.target), GetUnitY(PDDS.target), DUMMY_SPELL_ID, level)
call DC.castToUnit(PDDS.source, DUMMY_SPELL_ID, OID_firebolt)
endif
endif
endif
endfunction
private function init takes nothing returns nothing
call AddDamageHandler(function SplashDamageEvent)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Hydrosphere uses T32, SpellEffectEvent, Thrown, Impact
globals
private constant integer SPELL_ID = 'A02N'
private constant real SPEED = 40
private constant string MODEL = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"
private constant string SFX = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
private attacktype ATT = ATTACK_TYPE_MAGIC
private damagetype DAM = DAMAGE_TYPE_COLD
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
endfunction
private function Count takes integer level returns integer
return 2 * level + 2 //4,6,8,10,12
endfunction
private function GetAoe takes integer level returns real
return 400.
endfunction
private function GetDamage takes integer level returns real
return 50. * level + 25 //75,125,175,225,275
endfunction
struct Hydrosphere
unit caster
unit target
unit missile
real angle
real aoe
real damage
real distance
integer count
integer level
group g
static thistype DATA
private static method filterThem takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(DATA.missile)) and /*
*/ not (Thrown.isUnitThrown(GetFilterUnit()) or IsUnitInGroup(GetFilterUnit(), DATA.g) or IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) or/*
*/ IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE))
endmethod
private method periodic takes nothing returns nothing
local unit first
if not UnitAlive(.target) or .target==null or .count==0 then
call KillUnit(.missile)
call DestroyGroup(.g)
set .caster = null
set .target = null
set .missile = null
set .g = null
call .stopPeriodic()
call .destroy()
else
if .distance > 0 then
set .distance = .distance - SPEED
call SetUnitX(.missile, GetUnitX(.missile)+SPEED*Cos(.angle))
call SetUnitY(.missile, GetUnitY(.missile)+SPEED*Sin(.angle))
call DestroyEffect(AddSpecialEffect(MODEL, GetUnitX(.missile), GetUnitY(.missile)))
else
call UnitDamageTarget(.caster, .target, .damage, false, false, ATT, DAM, null)
call GroupAddUnit(.g, .target)
set .count = .count - 1
call DestroyEffect(AddSpecialEffectTarget(SFX, .target, "origin"))
call Thrown.run(.target, 150, 20)
if .level > 0 then
call ImpactSpell(.missile, .target, .level)
endif
set DATA = this
//set .target = GetRandomUnit(GetUnitX(.missile), GetUnitY(.missile), .aoe, Filter(function thistype.filterThem))
set .target = GetClosestUnitInRange(GetUnitX(.missile), GetUnitY(.missile), .aoe, Filter(function thistype.filterThem))
if .target != null then
set .distance = GetDistance(GetUnitX(.target), GetUnitY(.target), GetUnitX(.missile), GetUnitY(.missile))
set .angle = GetAngle(GetUnitX(.missile), GetUnitY(.missile), GetUnitX(.target), GetUnitY(.target))
endif
endif
endif
endmethod
implement T32x
private static method cast takes nothing returns nothing
local thistype this
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
if not Thrown.isUnitThrown(GetSpellTargetUnit()) then
set this = create()
set .caster = u
set .target = GetSpellTargetUnit()
set .missile = CreateUnit(GetTriggerPlayer(), 'e002', GetUnitX(u), GetUnitY(u), 0)
set .distance = GetDistance(GetUnitX(.target), GetUnitY(.target), GetUnitX(.missile), GetUnitY(.missile))
set .angle = GetAngle(GetUnitX(.missile), GetUnitY(.missile), GetUnitX(.target), GetUnitY(.target))
set .count = Count(level)
set .aoe = GetAoe(level)
set .damage = GetDamage(level)
set .level = GetUnitAbilityLevel(u, 'A02P')
set .g = CreateGroup()
call .startPeriodic()
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=33
//TESH.alwaysfold=0
scope Bubble //uses T32
globals
private constant integer SPELL_ID = 'A02O'
private constant integer MAX_BUBBLE = 10
private constant string MODEL = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
private constant string EXPLODE_SFX = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
private constant string DAMAGE_SFX = "Objects\\Spawnmodels\\NightElf\\NEDeathSmall\\NEDeathSmall.mdl"
private attacktype ATT = ATTACK_TYPE_MAGIC
private damagetype DAM = DAMAGE_TYPE_COLD
private Table b
endglobals
struct Bubble
unit caster
unit bu
effect sfx
real aoe
real damage
real dur
integer id
integer level
private method damageThem takes nothing returns nothing
local unit first
call GroupEnumUnitsInRange(enumG, GetUnitX(.bu), GetUnitY(.bu), .aoe, null)
call DestroyEffect(AddSpecialEffect(EXPLODE_SFX, GetUnitX(.bu), GetUnitY(.bu)))
call DestroyEffect(.sfx)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitEnemy(.bu, GetOwningPlayer(first)) and not IsUnitType(first, UNIT_TYPE_FLYING) then
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, first, "chest"))
call UnitDamageTarget(.caster, first, .damage, false, false, ATT, DAM, null)
if .level > 0 then
call ImpactSpell(.bu, first, .level)
endif
endif
call GroupRemoveUnit(enumG, first)
endloop
call KillUnit(.bu)
endmethod
private method periodic takes nothing returns nothing
local unit first
set .dur = .dur - 0.03125
call GroupEnumUnitsInRange(enumG, GetUnitX(.bu), GetUnitY(.bu), .aoe, null)
loop
set first = FirstOfGroup(enumG)
exitwhen first==null
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitEnemy(.bu, GetOwningPlayer(first)) and not IsUnitType(first, UNIT_TYPE_FLYING) then
call .damageThem()
exitwhen true
endif
call GroupRemoveUnit(enumG, first)
endloop
if IsUnitType(.bu, UNIT_TYPE_DEAD) or 0 > .dur then
if 0 > .dur then
call KillUnit(.bu)
call DestroyEffect(.sfx)
endif
set b[.id] = b[.id] - 1
set .caster = null
set .bu = null
set .sfx = null
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
private static method cast takes nothing returns nothing
local thistype this
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
if (b[GetHandleId(u)])==10 then
call SimError(GetTriggerPlayer(), "Maximum of "+I2S(MAX_BUBBLE)+" allowed")
else
set this = create()
set .caster = u
set .bu = CreateUnit(GetTriggerPlayer(), DUMMY_CAST, GetUnitX(u), GetUnitY(u), 0)
call SetUnitScale(.bu, 2.0, 2.0, 0)
call SetUnitVertexColor(.bu, 100, 150, 150, 100)
call SetUnitFlyHeight(.bu, 0, 0)
set .dur = 150
set .damage = 50*level+25
set .aoe = 200
set .sfx = AddSpecialEffectTarget(MODEL, .bu, "origin")
set .id = GetHandleId(u)
set .level = GetUnitAbilityLevel(u, 'A02P')
set b[.id] = b[.id] + 1
call .startPeriodic()
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
set b = Table.create()
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Impact initializer init uses PreloadUtils, DummyInstantCaster
globals
private constant integer DUMMY_SPELL_ID = 'A02R' //storm bolt
endglobals
function ImpactSpell takes unit u, unit target, integer level returns nothing
if GetUnitAbilityLevel(target, 'BPSE')==0 and UnitAlive(target) then
call DC.setDummyFrom(GetOwningPlayer(u), GetUnitX(target), GetUnitY(target), DUMMY_SPELL_ID, level)
call DC.castToUnit(target, DUMMY_SPELL_ID, 852095)
endif
endfunction
private function init takes nothing returns nothing
call PreloadSpell('A02R')
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
library SealOfLife uses DamageEvent, Table, SpellEffectEvent, TimerUtils
globals
private Table c
private constant string LIFE_SFX = "Abilities\\Spells\\Items\\ResourceItems\\ResourceEffectTarget.mdl"
private constant string MANA_SFX = "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl"
private constant string ATTACHMENT_SFX = "Abilities\\Spells\\Items\\OrbDarkness\\OrbDarkness.mdl"
endglobals
struct SealOfLife
unit u
real dur
effect sfx
static method onDam takes nothing returns nothing
local real maxLife
local real minMana
local real maxMana
if c.has(GetHandleId(PDDS.source)) then
set maxLife = GetUnitState(PDDS.source, UNIT_STATE_MAX_LIFE)
set minMana = GetUnitState(PDDS.source, UNIT_STATE_MANA)
set maxMana = GetUnitState(PDDS.source, UNIT_STATE_MAX_MANA)
//call BJDebugMsg(R2S(maxLife*0.75))
if GetUnitLife(PDDS.source) < (maxLife*0.75) then
call DestroyEffect(AddSpecialEffectTarget(LIFE_SFX, PDDS.source, "origin"))
call SetUnitLife(PDDS.source, GetUnitLife(PDDS.source) + PDDS.amount)
endif
if minMana < (maxMana*0.5) then
call DestroyEffect(AddSpecialEffectTarget(MANA_SFX, PDDS.source, "origin"))
call SetUnitState(PDDS.source, UNIT_STATE_MANA, minMana + PDDS.amount)
endif
endif
endmethod
static method onExpire takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set .dur = .dur - 0.1
if not UnitAlive(.u) or 0 > .dur then
call DestroyEffect(.sfx)
call c.remove(GetHandleId(.u))
set .u = null
set .sfx = null
call ReleaseTimer(GetExpiredTimer())
endif
endmethod
static method cast takes nothing returns nothing
local thistype this
if not c.has(GetHandleId(GetTriggerUnit())) then
set this = allocate()
set .u = GetTriggerUnit()
set .dur = 45
set .sfx = AddSpecialEffectTarget(ATTACHMENT_SFX, .u, "hand, right")
set c[GetHandleId(.u)] = 0
call TimerStart(NewTimerEx(this), 0.1, true, function thistype.onExpire)
endif
endmethod
static method onInit takes nothing returns nothing
set c = Table.create()
call AddDamageHandler(function thistype.onDam)
call RegisterSpellEffectEvent('A02Q', function thistype.cast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RockFormation initializer init uses T32, SpellEffectEvent
globals
private unit dummy
endglobals
private struct Cast
private unit target
private player pl
private integer level
private integer strike
private real angle
private real xD
private real yD
private method destroy takes nothing returns nothing
set .target = null
set .pl = null
call .stopPeriodic()
call .deallocate()
endmethod
private method periodic takes nothing returns nothing
local real x
local real y
if UnitAlive(.target) then
if .strike > 0 then
set .strike = .strike - 1
set .angle = .angle + 0.75
set x = .xD + 100 * Cos(.angle)
set y = .yD + 100 * Sin(.angle)
call SetUnitOwner(dummy, .pl, false)
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call UnitAddAbility(dummy, 'A00N')
call SetUnitAbilityLevel(dummy, 'A00N', .level)
call IssueTargetOrderById(dummy, 852095, .target)
call UnitRemoveAbility(dummy, 'A00N')
else
call .destroy()
endif
else
call .destroy()
endif
endmethod
implement T32x
static method castRock takes unit caster, unit target, integer i returns nothing
local thistype this = allocate()
set .angle = -0.75
set .level = GetUnitAbilityLevel(caster, 'A00M')
set .target = target
set .strike = i
set .xD = GetUnitX(caster)
set .yD = GetUnitY(caster)
set .pl = GetOwningPlayer(caster)
call .startPeriodic()
endmethod
endstruct
private function onCast takes nothing returns nothing
call Cast.castRock(GetTriggerUnit(), GetSpellTargetUnit(), 3)
endfunction
private function init takes nothing returns nothing
set dummy = CreateUnit(Player(15), DUMMY_CAST, 0, 0, 0)
call UnitAddAbility(dummy, 'A00N')
call UnitRemoveAbility(dummy, 'A00N')
call UnitRemoveAbility(dummy, 'Amov')
call RegisterSpellEffectEvent('A00M', function onCast)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SnareBoom initializer init uses T32, SpellEffectEvent
globals
private constant integer SPELL_ID = 'A01A'
private constant integer ROCK_ID = 'h00I'
//=================CONFIGURATIONS==================//
private constant attacktype ATK = ATTACK_TYPE_NORMAL
private constant damagetype DMG = DAMAGE_TYPE_NORMAL
private constant real ROCK_HEIGHT = 1000.
private constant real FALL_SPEED = 25.
private string SFX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
endglobals
private function GetDamage takes integer i returns real
return 100 + i * 50.
endfunction
private struct RockFall
unit u
unit target
unit rock
real height
real damage
private method periodic takes nothing returns nothing
if .height > 10 then
set .height = .height - FALL_SPEED
call SetUnitFlyHeight(.rock, .height, 0)
else
call DestroyEffect(AddSpecialEffectTarget(SFX, .target, "origin"))
call UnitDamageTarget(.rock, .target, .damage, false, false, ATK, DMG, null)
call KillUnit(.rock)
set .u = null
set .target = null
set .rock = null
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
static method run takes unit u, unit target returns nothing
local thistype this = create()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
set .u = u
set .target = target
set .rock = CreateUnit(GetTriggerPlayer(), ROCK_ID, GetUnitX(target), GetUnitY(target), 0)
set .height = ROCK_HEIGHT
set .damage = GetDamage(level)
call SetUnitFlyHeight(.rock, ROCK_HEIGHT, 0)
call .startPeriodic()
endmethod
endstruct
private function onCast takes nothing returns nothing
call RockFall.run(GetTriggerUnit(), GetSpellTargetUnit())
endfunction
private function init takes nothing returns nothing
call PreloadSpell(SPELL_ID)
call RegisterSpellEffectEvent(SPELL_ID, function onCast)
endfunction
endlibrary
//TESH.scrollpos=2
//TESH.alwaysfold=0
library Snarl initializer init uses DamageEvent, DummyInstantCaster
globals
private constant integer PASSIVE_SPELL_ID = 'A00K'
private constant integer DUMMY_SPELL_ID = 'A00L' //entangling roots
endglobals
private function GetChance takes integer level returns integer
return 10 + level * 5
endfunction
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
private function SnarlActions takes nothing returns nothing
local integer level = GetUnitAbilityLevel(PDDS.target, PASSIVE_SPELL_ID)
if level > 0 then
if IsUnitEnemy(PDDS.source, GetOwningPlayer(PDDS.target)) then
if GetRandomInt(1, 100) < GetChance(level) then
call DC.setDummyFrom(GetOwningPlayer(PDDS.target), GetUnitX(PDDS.source), GetUnitY(PDDS.source), DUMMY_SPELL_ID, level)
call DC.castToUnit(PDDS.source, DUMMY_SPELL_ID, 852171)
endif
endif
endif
endfunction
private function init takes nothing returns nothing
call AddDamageHandler(function SnarlActions)
endfunction
endlibrary
//TESH.scrollpos=6
//TESH.alwaysfold=0
scope DeathForce initializer init
private function onCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, 'A014')
local real rad = 0.
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real x1
local real y1
local integer angle = 0
loop
exitwhen angle==8
set angle = angle + 1
set rad = rad + 0.7854
set x1 = x + 100 * Cos(rad)
set y1 = y + 100 * Sin(rad)
call MDCCastToPoint(GetTriggerPlayer(), x, y, x1, y1, 'A01U', 852580, level)
endloop
set u = null
endfunction
private function init takes nothing returns nothing
call PreloadSpell('A014')
call RegisterSpellEffectEvent('A014', function onCast)
endfunction
endscope
//TESH.scrollpos=30
//TESH.alwaysfold=0
scope Delude
globals
private constant integer SPELL_ID = 'A023'
private constant integer LIFE_BONUS_ID = 'A028'
private constant group GRP = CreateGroup()
private constant timer TIMER = CreateTimer()
private integer INDEX = 0
private unit TEMPUNIT
private real DAMAGE
//===CONFIGURABLES:
private constant real ATK_INTERVAL = 2.0
private constant real AOE = 500
endglobals
private function FilterUnit takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD)
endfunction
private function AttackThis takes nothing returns boolean
local unit u = GetFilterUnit()
if FilterUnit(u) and IsUnitEnemy(TEMPUNIT, GetOwningPlayer(u)) and not IsUnitType(u, UNIT_TYPE_SLEEPING) then
call IssueTargetOrder(u, "attack", TEMPUNIT)
endif
set u = null
return false
endfunction
private function Looper takes nothing returns nothing
local unit u = GetEnumUnit()
if FilterUnit(u) then
set TEMPUNIT = u
call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(u), GetUnitY(u), AOE, function AttackThis)
else
set INDEX = RemoveFromGroupAndEnd(INDEX, GRP, u, TIMER)
endif
set u = null
endfunction
private function GroupLooper takes nothing returns nothing
call ForGroup(GRP, function Looper)
endfunction
private function Actions takes unit caster, unit u returns nothing
call UnitAddAbility(u, LIFE_BONUS_ID)
set INDEX = AddToGroupAndStart(INDEX, GRP, u, TIMER, ATK_INTERVAL, true, function GroupLooper)
endfunction
private function SpellCond takes nothing returns boolean
if IsUnitIllusion(GetTriggerUnit()) and GetUnitAbilityLevel(GetSummoningUnit(), SPELL_ID) > 0 then
call Actions(GetSummoningUnit(), GetTriggerUnit())
endif
return false
endfunction
private struct DL
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SUMMON)
call TriggerAddCondition(t, Condition( function SpellCond))
set t = null
endmethod
endstruct
endscope
//TESH.scrollpos=3
//TESH.alwaysfold=0
/*******************************************************************
Spinning Axe v1.0
by mckill2009
********************************************************************
REQUIRED LIBRARIES:
- T32 by Jesus4lyf
- SpellEffectEvent
- RandomUnit
********************************************************************/
library SpinningAxe uses T32, SpellEffectEvent, RandomUnit
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A029'
private constant integer DUMMY_ID = 'e002'
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant real SCALE = 0.8
private constant real SPIN_SPEED = 0.2
private constant real OFFSET_SPEED = 2.0
private constant real SPEED = 15.0
private constant real MAX_OFFSET = 400.
private constant real SEARCH_ENEMY_RANGE = 1000.
private constant real DAMAGE_AOE = 100
private constant real DAMAGE_MULTIPLIER = 2
private constant string DUMMY_MODEL = "Abilities\\Weapons\\RexxarMissile\\RexxarMissile.mdl"
private constant string DAMAGE_SFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
private attacktype ATK = ATTACK_TYPE_MAGIC
private damagetype DMG = DAMAGE_TYPE_NORMAL
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private group damGrp = CreateGroup()
private player TempPlayer
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDamage takes integer level returns real
return 10. * level + 10
endfunction
private function MaxAxeCount takes integer level returns integer
return 2 + level
endfunction
private function FilterOutTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING) and not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
private function FilterOutLastTarget takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
endfunction
private struct SpinningAxe
private unit caster
private unit axe
private unit target
private player pl
private effect model
private real angle
private real damage
private real damageEx
private real offset
private integer movement
private static method filterFunc takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), TempPlayer) and FilterOutLastTarget(GetFilterUnit())
endmethod
private method destroy takes nothing returns nothing
call DestroyEffect(.model)
call KillUnit(.axe)
set .model = null
set .caster = null
set .target = null
set .axe = null
set .pl = null
call .stopPeriodic()
call .deallocate()
endmethod
private method spin takes real x, real y returns nothing
call SetUnitX(.axe, x + .offset * Cos(.angle))
call SetUnitY(.axe, y + .offset * Sin(.angle))
endmethod
private method periodic takes nothing returns nothing
local unit first
local real xAxe
local real yAxe
local real xCaster
local real yCaster
local real xTar
local real yTar
if UnitAlive(.caster) then
set xAxe = GetUnitX(.axe)
set yAxe = GetUnitY(.axe)
set xCaster = GetUnitX(.caster)
set yCaster = GetUnitY(.caster)
//Damage nearby units
call GroupEnumUnitsInRange(damGrp, xAxe, yAxe, DAMAGE_AOE, null)
loop
set first = FirstOfGroup(damGrp)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) and FilterOutTargets(first) then
call UnitDamageTarget(.axe, first, .damage, false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, first, "origin"))
endif
call GroupRemoveUnit(damGrp, first)
endloop
if .movement==0 then
call .spin(xCaster, yCaster)
if MAX_OFFSET > .offset then
set .angle = .angle + SPIN_SPEED
set .offset = .offset + OFFSET_SPEED
else
set .movement = 1
endif
elseif .movement==1 then
call .spin(xCaster, yCaster)
if .offset > 0 then
set .angle = .angle - SPIN_SPEED
set .offset = .offset - OFFSET_SPEED
else
set .movement = 2
endif
elseif .movement==2 then
if .target==null then
set TempPlayer = .pl
set .target = GetRandomUnitInArea(xAxe, yAxe, SEARCH_ENEMY_RANGE, Filter(function thistype.filterFunc))
if not UnitAlive(.target) then
call .destroy()
endif
else
set xTar = GetUnitX(.target)
set yTar = GetUnitY(.target)
set .angle = GetAngle(xAxe, yAxe, xTar, yTar)
if GetDistance(xAxe, yAxe, xTar, yTar) > 50 then
call SetUnitX(.axe, xAxe + SPEED * Cos(angle))
call SetUnitY(.axe, yAxe + SPEED * Sin(angle))
else
call UnitDamageTarget(.axe, .target, .damageEx, false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, .target, "origin"))
call .destroy()
endif
endif
endif
else
call .destroy()
endif
endmethod
implement T32x
private static method startSpinning takes unit c, real a, real x, real y, real damage, player pl returns nothing
local thistype this = allocate()
set .caster = c
set .pl = pl
set .axe = CreateUnit(.pl, DUMMY_ID, x, y, 0)
set .model = AddSpecialEffectTarget(DUMMY_MODEL, .axe, "chest")
set .angle = a
set .offset = 0
set .damage = damage
set .damageEx = (damage / T32_PERIOD) * DAMAGE_MULTIPLIER
set .movement = 0
set .target = null
call SetUnitScale(.axe, SCALE, SCALE, SCALE)
call .startPeriodic()
endmethod
private static method onCast takes nothing returns nothing
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
local integer count = MaxAxeCount(level)
local real damage = GetDamage(level) * T32_PERIOD
local real angle = 0
local real aGap = (360/count) * bj_DEGTORAD
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
loop
exitwhen count==0
set count = count - 1
set angle = angle + aGap
call SpinningAxe.startSpinning(GetTriggerUnit(), angle, x, y, damage, GetTriggerPlayer())
endloop
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Created by: mckill2009
library Tremble uses SpellEffectEvent, TimerUtils, IsUnitChanneling
globals
private constant integer SPELL_ID = 'A022'
private constant integer DUMMY_SPELL_ID = 'A025'
private constant integer DUMMY_ID = 'e002'
private constant integer BUFF_ID = 'BOeq' //earthquake buff
private constant real INTERVAL = 1.0
//private constant real INTERVAL_MULTIPLIER = 2
private constant real AOE = 400 //must match the AOE of spell in object editor
//private constant string SFX = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private constant attacktype ATK = ATTACK_TYPE_NORMAL
private constant damagetype DMG = DAMAGE_TYPE_NORMAL
private group grp = CreateGroup()
endglobals
private function GetDamagePercentage takes integer level returns real
return level * 0.1
endfunction
private function FilterOutMoreTargets takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_FLYING) or IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) or IsUnitType(u, UNIT_TYPE_MECHANICAL))
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct Tremble
private unit dummy
private player pl
private real damage
private real xD
private real yD
private static method periodic takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit first
if IsUnitChanneling(.dummy) then
call GroupEnumUnitsInRange(grp, .xD, .yD, AOE, null)
loop
set first = FirstOfGroup(grp)
exitwhen first==null
if UnitAlive(first) and GetUnitAbilityLevel(first, BUFF_ID) > 0 then
if IsUnitType(first, UNIT_TYPE_STRUCTURE) then
call UnitDamageTarget(.dummy, first, .damage, false, false, ATTACK_TYPE_SIEGE, DAMAGE_TYPE_DEMOLITION, null)
else
call UnitDamageTarget(.dummy, first, .damage, false, false, ATK, DMG, null)
endif
endif
call GroupRemoveUnit(grp, first)
endloop
else
call KillUnit(.dummy)
set .dummy = null
set .pl = null
call ReleaseTimer(GetExpiredTimer())
call .deallocate()
endif
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .pl = GetTriggerPlayer()
set .xD = GetUnitX(GetTriggerUnit())
set .yD = GetUnitY(GetTriggerUnit())
set .dummy = CreateUnit(.pl, DUMMY_ID, .xD, .yD, 0)
set .damage = (GetDamagePercentage(level) * GetHeroStr(GetTriggerUnit(), false)) * INTERVAL
call UnitAddAbility(.dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(.dummy, DUMMY_SPELL_ID, level)
call IssuePointOrder(.dummy, "earthquake", .xD, .yD)
call TimerStart(NewTimerEx(this), INTERVAL, true, function thistype.periodic)
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RecoveryStrike initializer init uses DamageEvent, DummyInstantCaster
globals
private constant integer PASSIVE_SPELL_ID = 'A02B'
private constant integer DUMMY_SPELL_ID = 'A006' //holy bolt
endglobals
private function GetChance takes integer level returns integer
return 10 + level * 5
endfunction
private function FilterTargets takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
private function OnDamage takes nothing returns nothing
local integer level = GetUnitAbilityLevel(PDDS.source, PASSIVE_SPELL_ID)
if level > 0 then
if GetRandomInt(1, 100) < GetChance(level) then
if GetWidgetLife(PDDS.source) < GetUnitState(PDDS.source, UNIT_STATE_MAX_LIFE) then
call DC.setDummyFrom(GetOwningPlayer(PDDS.source), GetUnitX(PDDS.source), GetUnitY(PDDS.source), DUMMY_SPELL_ID, level)
call DC.castToUnit(PDDS.source, DUMMY_SPELL_ID, 852092)
endif
endif
endif
endfunction
private function init takes nothing returns nothing
call AddDamageHandler(function OnDamage)
endfunction
endlibrary
//TESH.scrollpos=81
//TESH.alwaysfold=0
library ImpalingSlam uses T32, SpellEffectEvent
globals
private constant integer SPELL_ID = 'A03X'
private constant integer DUMMY_SPELL_ID = 'A02G' //impale
private constant integer DUMMY_ID = 'e002'
private constant integer ORDER_ID = 852555
private constant real SPEED = 75
private constant real AOE = 250
private constant real BIG_AOE = 500
private constant string SFX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
private constant attacktype ATK = ATTACK_TYPE_NORMAL
private constant damagetype DMG = DAMAGE_TYPE_NORMAL
private group damGrp = CreateGroup()
endglobals
private function GetDamage takes integer i returns real
return 400. //250 + i * 150.
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct DestructiveForce
unit caster
unit target
player pl
group grp
integer level
real damage
real distance
real distx
real angle
real angleEx
real xLoc
real yLoc
private method damageThem takes real aoe, real x, real y returns nothing
local unit first
call GroupEnumUnitsInRange(damGrp, x, y, aoe, null)
loop
set first = FirstOfGroup(damGrp)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .pl) and not IsUnitType(first, UNIT_TYPE_FLYING) and not IsUnitInGroup(first, .grp) then
call UnitDamageTarget(.caster, first, .damage, false, false, ATK, DMG, null)
call GroupAddUnit(.grp, first)
endif
call GroupRemoveUnit(damGrp, first)
endloop
endmethod
private method periodic takes nothing returns nothing
local unit dummy
local real x = .xLoc + .distx * Cos(.angle)
local real y = .yLoc + .distx * Sin(.angle)
if .distance > .distx then
set .distx = .distx + SPEED
call DestroyEffect(AddSpecialEffect(SFX, x,y))
call .damageThem(AOE, x, y)
else
set dummy = CreateUnit(.pl, DUMMY_ID, x, y, 0)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, .level)
call IssuePointOrderById(dummy, ORDER_ID, .xLoc, .yLoc)
//call IssuePointOrderById(dummy, ORDER_ID, x - 100 * Cos(.angle), y - 100 * Sin(.angle))
call DestroyGroup(.grp)
set dummy = null
set .grp = null
set .caster = null
set .target = null
set .pl = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real x1 = GetSpellTargetX()
local real y1 = GetSpellTargetY()
set .level = GetUnitAbilityLevel(u, SPELL_ID)
set .caster = u
set .target = target
set .pl = GetTriggerPlayer()
set .angle = Atan2(y1 - y, x1 - x)
set .distance = SquareRoot((x1-x) * (x1-x) + (y1-y) * (y1-y))
set .distx = 0
set .damage = GetDamage(.level)// * T32_PERIOD
set .xLoc = GetUnitX(u)
set .yLoc = GetUnitY(u)
set .grp = CreateGroup()
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
local unit dummy
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set dummy = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
set dummy = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=15
//TESH.alwaysfold=0
/*******************************************************************
RaidShock v1.0
by mckill2009
********************************************************************/
library RaidShock uses T32, SpellEffectEvent, TerrainPathability
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A03K'
private constant integer DUMMY_ID = 'e002'
private constant integer DUMMY_SPELL_ID = 'A03G' //storm bolt
private constant integer ORDER_ID = 852095 //storm bolt
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant real AOE = 200
private constant real SPEED = 10.
private constant real DUMMY_SCALE = 2.0
private constant string CASTER_ANIMATION = "attack slam"
private constant string MODEL_SFX = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl"
private constant string EXPLODE_SFX = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
private constant string START_SFX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
private attacktype ATK = ATTACK_TYPE_CHAOS
private damagetype DMG = DAMAGE_TYPE_DEATH
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private group dam = CreateGroup()
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDamage takes integer level returns real
return 15. * level //15/30/45/60/75
endfunction
private function FilterOutMoreUnits takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
private struct RaidShock
unit caster
unit missile
player owner
integer level
real angle
real distance
real damage
real xTar
real yTar
effect sfx
boolean atk
private method periodic takes nothing returns nothing
local real a
local real x = GetUnitX(.missile)
local real y = GetUnitY(.missile)
local real x1
local real y1
local unit first
if .distance > 0 then
set x1 = x + SPEED * Cos(.angle)
set y1 = y + SPEED * Sin(.angle)
if IsTerrainWalkable(x1, y1) then
set .distance = .distance - SPEED
call SetUnitX(.missile, x1)
call SetUnitY(.missile, y1)
else
set .distance = 0
set .atk = false
endif
else
call SetUnitX(.caster, x)
call SetUnitY(.caster, y)
call UnitRemoveAbility(.caster, 'Abun')
call SetUnitInvulnerable(.caster, false)
call SetUnitPropWindow(.caster, 1)
call ShowUnit(.caster, true)
call DestroyEffect(.sfx)
call UnitApplyTimedLife(.missile, 'BTLF', 1.0)
call SetUnitFacing(.caster, Atan2(.yTar-y, .xTar-x)*bj_RADTODEG)
if .atk then
call UnitAddAbility(.missile, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(.missile, DUMMY_SPELL_ID, .level)
call GroupEnumUnitsInRange(dam, x, y, AOE, null)
loop
set first = FirstOfGroup(dam)
exitwhen first==null
if UnitAlive(first) and IsUnitEnemy(first, .owner) and FilterOutMoreUnits(first) then
call IssueTargetOrderById(.missile, ORDER_ID, first)
call UnitDamageTarget(.caster, first, .damage, false, false, ATK, DMG, null)
endif
call GroupRemoveUnit(dam, first)
endloop
call SetUnitAnimation(.caster, CASTER_ANIMATION)
call DestroyEffect(AddSpecialEffect(EXPLODE_SFX, x, y))
endif
set .caster = null
set .missile = null
set .sfx = null
set .owner = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
call DestroyEffect(AddSpecialEffect(START_SFX, x, y))
set .atk = true
set .level = GetUnitAbilityLevel(u, SPELL_ID)
set .owner = GetTriggerPlayer()
set .damage = GetDamage(level)
set .xTar = GetUnitX(GetSpellTargetUnit())
set .yTar = GetUnitY(GetSpellTargetUnit())
set .angle = Atan2(.yTar-y, .xTar-x)
set .distance = SquareRoot((.xTar-x)*(.xTar-x)+(.yTar-y)*(.yTar-y))
set .caster = u
set .missile = CreateUnit(.owner, DUMMY_ID, x, y, .angle*bj_RADTODEG)
call SetUnitScale(.missile, DUMMY_SCALE, DUMMY_SCALE, DUMMY_SCALE)
call UnitRemoveAbility(.missile, 'Amov')
call SetUnitFlyHeight(.missile, 0, 0)
set .sfx = AddSpecialEffectTarget(MODEL_SFX, .missile, "origin")
call UnitAddAbility(u, 'Abun')
call SetUnitInvulnerable(u, true)
call SetUnitPropWindow(u, 0)
call ShowUnit(u, false)
call SetUnitPathing(u, false)
call .startPeriodic()
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library_once Boomerang initializer Init requires GroupUtils, xefx, DestructableLib, TerrainPathability
private keyword Boomerang // DO NOT TOUCH; configuration is below
// Credits:
// - Ciebron for the inspiration
// - -JonNny for reporting some bugs
// - Atideva for reporting a bug
// - Rising_Dusk for his GroupUtils library
// - Anitarf for his IsTerrainWalkable library
// - Vexorian for JassHelper and xe
// - PipeDream for Grimoire
// - PitzerMike for JassNewGenPack and DestructableLib
// - MindWorX for JassNewGenPack
// - SFilip for TESH
globals
private constant real TICK = 0.03125 //1./40 // granulation of boomerang movement
private constant integer AID = 'A00J' // the ability triggering this spell
private real array DAMAGE // damage dealt by the boomerang to units it hits
private constant real DAMAGE_BOUNDARY = 10. // once the damage is lower or equal to this, the boomerang stops flying
private constant string BOOMERANG_MODEL = "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl" // this is the model representing the boomerang
private constant real BOOMERANG_COLLSIZE = 96. // the AoE in which units are damaged by the boomerang
private constant real BOOMERANG_SIZE = 1.25 // the scaling of the boomerang
private constant real BOOMERANG_HEIGHT = 64. // the Z height of the boomerang
private constant real array BOOMERANG_SPEED // the speed at which the boomerang moves // due to limitations this is only an approximation
private constant real BOOMERANG_FOCUS = 2. // the higher this number the more focused is the path of the boomerang. Any value greater than 0 should work. Values below 2 might not look so good.
private constant boolean ALLOW_MULTIPLE_HITS = true // if this is true, the boomerang can hit units more than once on his path
private constant boolean USE_RIGHT_BOOMERANG = true // just avoid setting both to false, okay?
private constant boolean USE_LEFT_BOOMERANG = true
private constant boolean COLLIDE_WITH_GROUND = true // do boomerangs collide with unwalkable terrain?
private real array MIN_RANGE // minimum throwing distance for the boomerang
private constant boolean IGNORE_TREES = false // if true, the boomerang will just fly through the trees without doing anything
private constant boolean KILL_TREES = false // if true, trees are killed once the boomerang hits one, if this is false, the boomerang stops flying
private constant string HIT_FX = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" // when the boomerang hits a unit, this effect is spawned on the unit hit
private constant string HIT_FX_ATTPT = "chest" // the beforementioned effect will be attached to this point
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC // the attack type of the damage the boomerang deals
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC // the damage type of the damage the boomerang deals
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_METAL_MEDIUM_SLICE // sound when boomerang hits a unit
endglobals
private function Damage takes integer level returns real // PROXY
return 20. * level + 10
endfunction
private function Boomerang_Speed takes integer level returns real // PROXY
return 500.
endfunction
private function Min_Range takes integer level returns real // PROXY
return 200.
endfunction
private function ValidTarget takes unit target returns boolean
return not IsUnitType(target, UNIT_TYPE_STRUCTURE) and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) /*
*/ and not IsUnitType(target, UNIT_TYPE_MECHANICAL)
endfunction
// This is shit. Don't touch shit.
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
globals
private rect R
private Boomerang tmpBoomerang
private constant boolean ANGLE_DIRECTION_FORWARD = true
private constant boolean ANGLE_DIRECTION_REVERSE = false
endglobals
private struct Boomerang
unit caster
unit target
integer level
real angleCurrent
boolean angleDirection
real targetX
real targetY
xefx dummy
boolean active
real damage
group damagedUnits
boolean overTree
boolean markedForDestruction
static boolexpr DamageFilter
static boolexpr TreeFilter
static boolexpr LightTreeFilter
private integer index
private static thistype array Structs
private static timer T = CreateTimer()
private static integer Count = 0
private static method onInit takes nothing returns nothing
set DamageFilter = Filter(function thistype.DamageFilterFunc)
set TreeFilter = Filter(function thistype.TreeFilterFunc)
set LightTreeFilter = Filter(function thistype.LightTreeFilterFunc)
endmethod
method onDestroy takes nothing returns nothing
set caster = null
set target = null
call dummy.destroy()
call ReleaseGroup(damagedUnits)
// clean your struct here
set Count = Count - 1
set Structs[index] = Structs[Count]
set Structs[index].index = .index
if Count == 0 then
call PauseTimer(T)
endif
endmethod
static method UnitDistCheck takes nothing returns nothing
local unit u = GetEnumUnit()
local real dx = tmpBoomerang.dummy.x - GetUnitX(u)
local real dy = tmpBoomerang.dummy.y - GetUnitY(u)
if (dx*dx + dy*dy) > (BOOMERANG_COLLSIZE * BOOMERANG_COLLSIZE) then
call GroupRemoveUnit(tmpBoomerang.damagedUnits, u)
endif
set u = null
endmethod
static method DamageFilterFunc takes nothing returns boolean
local unit u = GetFilterUnit()
if tmpBoomerang.markedForDestruction then
return false
endif
// check if unit is a valid target for damage
if UnitAlive(u) and IsUnitEnemy(u, GetOwningPlayer(tmpBoomerang.caster)) and not IsUnitInGroup(u, tmpBoomerang.damagedUnits) and ValidTarget(u) then
// damage the unit; if the unit for some reason cant be damaged, dont continue
if UnitDamageTarget(tmpBoomerang.caster, u, tmpBoomerang.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
call GroupAddUnit(tmpBoomerang.damagedUnits, u)
endif
endif
set u = null
return false
endmethod
static method TreeFilterFunc takes nothing returns boolean
local destructable d = GetFilterDestructable()
local real x
local real y
local real dx
local real dy
if tmpBoomerang.markedForDestruction then
// short circuit logic after the damage boundary has been reached
return false
endif
// filter out dead and non-tree destructables
if (not IsDestructableDead(d)) and IsDestructableTree(d) then
set tmpBoomerang.overTree = true
set x = GetWidgetX(d)
set y = GetWidgetY(d)
set dx = tmpBoomerang.dummy.x - x
set dy = tmpBoomerang.dummy.y - y
// tree must be inside the collision radius
if (dx*dx + dy*dy) <= (BOOMERANG_COLLSIZE * BOOMERANG_COLLSIZE) then
static if KILL_TREES then
call KillDestructable(d)
//call tmpBoomerang.reduceDamage()
else
// if Trees may not be destroyed, destroy the boomerang instead
set tmpBoomerang.markedForDestruction = true
endif
endif
endif
set d = null
return false
endmethod
static method LightTreeFilterFunc takes nothing returns boolean
local destructable d = GetFilterDestructable()
if not IsDestructableDead(d) and IsDestructableTree(d) then
set tmpBoomerang.overTree = true
endif
set d = null
return false
endmethod
private static method Callback takes nothing returns nothing
local integer i = Count - 1
local thistype this
local real x
local real y
local real launchX
local real launchY
local real deltaX
local real deltaY
local real distance
local real angleBase
local real angleIncrement
local real offset
loop
exitwhen i < 0
set this = Structs[i]
set tmpBoomerang = this
//
// make the boomerang home, even if the caster moves
if this.target != null then
set this.targetX = GetUnitX(this.target)
set this.targetY = GetUnitY(this.target)
endif
set launchX = GetUnitX(this.caster)
set launchY = GetUnitY(this.caster)
set deltaX = this.targetX - launchX
set deltaY = this.targetY - launchY
set distance = SquareRoot(deltaX*deltaX + deltaY*deltaY)
set angleBase = Atan2(deltaY, deltaX) - ((bj_PI / BOOMERANG_FOCUS) / 2)
// functions for moving the boomerang:
// r(a)=distance*Sin(BOOMERANG_FOCUS*a) // a is the angle and goes from 90 to 0 // distance from center point
// x(a)=Cos(a)*r(a) // x and y coordinates in relation to the location it was cast.
// y(a)=Sin(a)*r(a) // note that i inlined some things to allow casting the boomerang in all directions from any point on the map
set offset = distance * Sin(BOOMERANG_FOCUS * this.angleCurrent)
set x = launchX + (Cos(angleBase + this.angleCurrent) * offset)
set y = launchY + (Sin(angleBase + this.angleCurrent) * offset)
set this.dummy.x = x
set this.dummy.y = y
static if ALLOW_MULTIPLE_HITS then
call ForGroup(this.damagedUnits, function thistype.UnitDistCheck)
endif
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, DamageFilter)
static if not IGNORE_TREES then
set this.overTree = false
call MoveRectTo(R, x, y)
call EnumDestructablesInRect(R, TreeFilter, null)
endif
static if COLLIDE_WITH_GROUND then
static if IGNORE_TREES then
set this.overTree = false
call MoveRectTo(R, x, y)
call EnumDestructablesInRect(R, LightTreeFilter, null)
endif
if not IsTerrainWalkable(x, y) and not this.overTree then
set this.markedForDestruction = true
endif
endif
set angleIncrement = TICK * ((bj_PI / BOOMERANG_FOCUS) / 2) * (Boomerang_Speed(this.level) / distance)
if this.angleDirection == ANGLE_DIRECTION_FORWARD then
set this.angleCurrent = this.angleCurrent + angleIncrement
else
set this.angleCurrent = this.angleCurrent - angleIncrement
endif
if this.angleCurrent <= 0 or this.angleCurrent >= (bj_PI / BOOMERANG_FOCUS) or IsUnitType(this.caster, UNIT_TYPE_DEAD) == true then
set this.markedForDestruction = true
endif
if this.markedForDestruction then
call this.destroy()
endif
set i = i - 1
endloop
endmethod
static method create takes unit caster, unit target, real targetX, real targetY, boolean direction returns thistype
local thistype this = allocate()
local real dx
local real dy
local real distance
local real angleBase
set this.caster = caster
if target == null or target == caster then
set this.target = null
set this.targetX = targetX
set this.targetY = targetY
else
set this.target = target
set this.targetX = GetUnitX(target)
set this.targetY = GetUnitY(target)
endif
set dx = this.targetX - GetUnitX(caster)
set dy = this.targetY - GetUnitY(caster)
set this.level = GetUnitAbilityLevel(this.caster, AID)
set distance = SquareRoot(dx*dx + dy*dy)
if distance == 0.0 then
set angleBase = (GetUnitFacing(this.caster) * bj_DEGTORAD)
else
set angleBase = Atan2(dy, dx)
endif
if distance < Min_Range(this.level) then
// enforce the minimum distance
set distance = Min_Range(this.level)
set this.targetX = GetUnitX(caster) + (distance * Cos(angleBase))
set this.targetY = GetUnitY(caster) + (distance * Sin(angleBase))
endif
if direction == ANGLE_DIRECTION_FORWARD then
set this.angleCurrent = 0.
else
set this.angleCurrent = (bj_PI / BOOMERANG_FOCUS)
endif
set this.angleDirection = direction
set this.damage = Damage(this.level)
set this.dummy = xefx.create(GetUnitX(caster), GetUnitY(caster), 0)
set this.dummy.fxpath = BOOMERANG_MODEL
set this.dummy.scale = BOOMERANG_SIZE
set this.dummy.z = BOOMERANG_HEIGHT
set this.damagedUnits = NewGroup()
set this.overTree = false
set this.markedForDestruction = false
// initialize the struct here
set Structs[Count] = this
set this.index = Count
if Count == 0 then
call TimerStart(T, TICK, true, function thistype.Callback)
endif
set Count = Count + 1
return this
endmethod
endstruct
private function SpellCond takes nothing returns boolean
return GetSpellAbilityId() == AID
endfunction
private function SpellAction takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local real targetX = GetSpellTargetX()
local real targetY = GetSpellTargetY()
static if USE_RIGHT_BOOMERANG then
call Boomerang.create(caster, target, targetX, targetY, ANGLE_DIRECTION_FORWARD)
endif
static if USE_LEFT_BOOMERANG then
call Boomerang.create(caster, target, targetX, targetY, ANGLE_DIRECTION_REVERSE)
endif
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function SpellCond))
call TriggerAddAction(t, function SpellAction)
//call SetUpSpellData()
set R = Rect(0, 0, 2 * BOOMERANG_COLLSIZE, 2 * BOOMERANG_COLLSIZE)
endfunction
endlibrary
//TESH.scrollpos=75
//TESH.alwaysfold=0
/**********************************************************************
FierceArmour v1.0
by mckill2009
originally from Torchlight (Fierce Armor) and re-created by moyack
***********************************************************************/
library FierceArmour uses T32, SpellEffectEvent
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A03M'
private constant integer ARMOUR_ID = 'A03L'
private constant integer DUMMY_ID = 'N006'
/*************************************************
* CONFIGURABLE GLOBALS
**************************************************/
private constant integer RED = 75
private constant integer BLUE = 200
private constant integer GREEN = 100
private constant integer ALPHA = 50
private constant string SPAWN_SFX = "Abilities\\Spells\\Human\\ReviveHuman\\ReviveHuman.mdl"
private constant string DEATH_SFX = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
/*************************************************
* NON-CONFIGURABLE GLOBALS
**************************************************/
endglobals
/*************************************************
* CONFIGURABLE FUNCTIONS
**************************************************/
private function GetDuration takes integer i returns real
return 60. //100 + i * 50.
endfunction
private function FilterOutMoreUnits takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
/*************************************************
* NON-CONFIGURABLE FUNCTIONS
**************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct GWA
unit caster
unit dummy
real duration
private method periodic takes nothing returns nothing
if .duration > 0 then
if UnitAlive(.caster) then
set .duration = .duration - T32_PERIOD
call SetUnitX(.dummy, GetUnitX(.caster))
call SetUnitY(.dummy, GetUnitY(.caster))
else
set .duration = 0
endif
else
call SetUnitOwner(.dummy, Player(15), false)
call UnitRemoveAbility(.caster, ARMOUR_ID)
call DestroyEffect(AddSpecialEffectTarget(DEATH_SFX, .caster, "origin"))
call KillUnit(.dummy)
call ShowUnit(.dummy, false)
set .caster = null
set .dummy = null
call .stopPeriodic()
call .deallocate()
endif
endmethod
implement T32x
static method onCast takes nothing returns nothing
local thistype this = allocate()
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
set .caster = GetTriggerUnit()
set .dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, GetUnitX(.caster), GetUnitY(.caster), GetUnitFacing(.caster))
set .duration = GetDuration(level)
call DestroyEffect(AddSpecialEffectTarget(SPAWN_SFX, .dummy, "origin"))
call UnitRemoveAbility(.dummy, 'Amov')
call SetHeroLevel(.dummy, GetHeroLevel(.caster), false)
call SetUnitVertexColor(dummy, RED, BLUE, GREEN, ALPHA)
call UnitAddAbility(.caster, ARMOUR_ID)
call SetUnitAbilityLevel(.caster, ARMOUR_ID, level)
call .startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
local unit d
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
//Preloading to avoid lag
set d = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(d, ARMOUR_ID)
call KillUnit(d)
call ShowUnit(d, false)
set d = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**********************************************
Glaive Hurl v1.0
by mckill2009
***********************************************/
library GlaiveOfDeath uses SpellEffectEvent, GetClosestWidget, T32, GroupUtils
globals
private constant integer SPELL_ID = 'A03Y'
private constant integer DUMMY_ID = 'e002'
private constant string DUMMY_MODEL = "Abilities\\Weapons\\GlaiveMissile\\GlaiveMissile.mdl"
private constant string HIT_SFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
private constant real DUMMY_SCALE = 0.3 //1 is original size
private constant real SPEED = 20
private constant boolean REPEAT_TARGET = false
private integer DATA
endglobals
/*****************************************************
CONFIGURABLE FUNCTIONS
******************************************************/
private function GetAoe takes real level returns real
return 400.
endfunction
private function GetDamage takes real level returns real
return 30 * level + 20 //50, 80, 110, 140, 170
endfunction
private function GetHitCount takes integer level returns integer
return 2 * level + 2 //4, 6, 8, 10, 12
endfunction
private function FilterTargets takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL))
endfunction
/*****************************************************
NON-CONFIGURABLE FUNCTIONS
******************************************************/
private function GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endfunction
private function GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and u!=null
endfunction
private struct GH
unit target
unit missile
player pl
integer hit
real aoe
real damage
real impact
group g
effect sfx
private static method filterTarget takes nothing returns boolean
local thistype this = DATA
return UnitAlive(GetFilterUnit()) and FilterTargets(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), .pl) /*
*/ and not (IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) or IsUnitInGroup(GetFilterUnit(), .g))
endmethod
private method periodic takes nothing returns nothing
local real angle
local real distance
local real x = GetUnitX(.missile)
local real y = GetUnitY(.missile)
local real xT
local real yT
if UnitAlive(.target) then
set xT = GetUnitX(.target)
set yT = GetUnitY(.target)
set angle = GetAngle(x, y, xT, yT)
set distance = GetDistance(x, y, xT, yT)
if distance > .impact then
call SetUnitX(.missile, x + SPEED * Cos(angle))
call SetUnitY(.missile, y + SPEED * Sin(angle))
else
call UnitDamageTarget(.missile, .target, .damage, false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(HIT_SFX, .target, "chest"))
set .target = null
endif
else
if .hit > 1 then
set .hit = .hit - 1
set DATA = this
set .target = GetClosestUnitInRange(x, y, .aoe, Condition(function thistype.filterTarget))
if UnitAlive(.target) then
call GroupAddUnit(.g, .target)
elseif REPEAT_TARGET then
call GroupClear(.g)
set .target = GetClosestUnitInRange(x, y, .aoe, Condition(function thistype.filterTarget))
call GroupAddUnit(.g, .target)
endif
else
call DestroyEffect(.sfx)
call KillUnit(.missile)
set .sfx = null
set .target = null
set .missile = null
set .pl = null
call ReleaseGroup(.g)
set .g = null
call .stopPeriodic()
call .deallocate()
endif
endif
endmethod
implement T32x
private static method run takes nothing returns nothing
local thistype this = allocate()
local unit caster = GetTriggerUnit()
local integer lv
set .target = GetSpellTargetUnit()
set .pl = GetTriggerPlayer()
set lv = GetUnitAbilityLevel(caster, SPELL_ID)
set .missile = CreateUnit(.pl, DUMMY_ID, GetUnitX(caster), GetUnitY(caster), GetUnitFacing(caster))
set .sfx = AddSpecialEffectTarget(DUMMY_MODEL, .missile, "origin")
call SetUnitScale(.missile, DUMMY_SCALE, DUMMY_SCALE, DUMMY_SCALE)
if UnitAddAbility(.missile, 'Arav') then
endif
call SetUnitFlyHeight(.missile, 100, 0)
set .hit = GetHitCount(lv)
set .damage = GetDamage(lv)
set .aoe = GetAoe(lv)
set .impact = 50*50
set .g = NewGroup()
call GroupAddUnit(.g, .target)
call .startPeriodic()
set caster = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.run)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*******************************************************************
Dancing Fire v1.0
by mckill2009
********************************************************************/
library DancingFire uses T32, SpellEffectEvent, IsUnitChanneling
globals
/**************************************************
* RAW CODES: must be changed according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A03Z'
private constant integer DUMMY_SPELL_ID = 'A040'
private constant integer DUMMY_ID = 'e002'
private constant integer ORDER_ID = 852580
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
//the faster for it to move, higher is faster
private constant real DELAY_INTERVAL = 0.1
//how wide is it to rotate, higher is wider
private constant real MOVE_INTERVAL = 0.1
endglobals
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and u!=null
endfunction
private struct BOF
unit caster
unit dummy
real move
real xCaster
real yCaster
real xSpell
real ySpell
real delay
boolean check
private method periodic takes nothing returns nothing
// local timer t = GetExpiredTimer()
// local thistype this = GetTimerData(t)
local real xCast
local real yCast
if UnitAlive(.caster) and IsUnitChanneling(.caster) then
set xCast = .xCaster + 100 * Cos(.move)
set yCast = .yCaster + 100 * Sin(.move)
if .check then
set .delay = .delay + DELAY_INTERVAL
set .move = .move + MOVE_INTERVAL
else
set .delay = .delay - DELAY_INTERVAL
set .move = .move - MOVE_INTERVAL
endif
//center 2
if (.delay > 1) and .check then
set .check = false
elseif (.delay) < (-1) then
set .check = true
endif
call IssuePointOrderById(.dummy, ORDER_ID, xCast, yCast)
else
call KillUnit(.dummy)
set .caster = null
set .dummy = null
call .stopPeriodic()
//call ReleaseTimer(t)
call .deallocate()
endif
//set t = null
endmethod
implement T32x
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
local real facing = GetUnitFacing(u)
local real xDum
local real yDum
set .caster = u
set .xCaster = GetUnitX(u)
set .yCaster = GetUnitY(u)
set xDum = .xCaster + 100 * Cos(facing)
set yDum = .yCaster + 100 * Sin(facing)
set .xSpell = .xCaster + 125 * Cos(facing)
set .ySpell = .yCaster + 125 * Sin(facing)
set .move = facing * bj_DEGTORAD
set .dummy = CreateUnit(GetTriggerPlayer(), DUMMY_ID, .xCaster, .yCaster, 0)
set .check = true
set .delay = 0
call UnitAddAbility(.dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(.dummy, DUMMY_SPELL_ID, GetUnitAbilityLevel(u, SPELL_ID))
call .startPeriodic() //TimerStart(NewTimerEx(this), interval, true, function thistype.periodic)
set u = null
endmethod
private static method onInit takes nothing returns nothing
local unit dum
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
set dum = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(dum, DUMMY_SPELL_ID)
call KillUnit(dum)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*******************************************************************
Intervention v1.0
by mckill2009
********************************************************************
REQUIRED LIBRARIES:
- IsUnitChanneling
- SpellEffectEvent
- TimerUtils
********************************************************************/
library Intervention uses IsUnitChanneling, SpellEffectEvent, TimerUtils
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A041'
private constant integer INVULNERABLE_ID = 'Avul'
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant string HEAL_SFX = "Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl"
private constant string INVULNERABLE_SFX = "Abilities\\Spells\\Human\\DivineShield\\DivineShieldTarget.mdl"
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDuration takes integer level returns real
return 2. * level * 2 //4, 6, 8, 10, 12
endfunction
private function GetHealAmount takes integer level returns real
return 100. * level //75, 150, 225, 300, 375
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS:
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0 and u!=null
endfunction
private struct Intervention
unit caster
real maxlife
real heal
real duration
effect insfx
private static method periodic takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if UnitAlive(.caster) and IsUnitChanneling(.caster) and .duration > 0 then
set .duration = .duration - 0.1
else
call IssueImmediateOrderById(.caster, 851972)
call UnitRemoveAbility(.caster, INVULNERABLE_ID)
call DestroyEffect(.insfx)
if GetWidgetLife(.caster) < .maxlife then
call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, .caster, "origin"))
call SetUnitState(.caster, UNIT_STATE_LIFE, GetWidgetLife(.caster) + .heal)
endif
set .caster = null
set .insfx = null
call ReleaseTimer(t)
call .deallocate()
endif
set t = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
local integer i = 0
set .caster = u
set .duration = GetDuration(level)
set .heal = GetHealAmount(level)
set .maxlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set .insfx = AddSpecialEffectTarget(INVULNERABLE_SFX, u, "origin")
call UnitAddAbility(.caster, INVULNERABLE_ID)
call TimerStart(NewTimerEx(this), 0.1, true, function thistype.periodic)
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
/*******************************************************************
Blood Brothers v1.0
by mckill2009
********************************************************************
REQUIRED LIBRARIES:
-
-
-
********************************************************************/
library BloodBrothers uses TimerUtils, SpellEffectEvent, Table, SimError
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A042' //passive, active
/**************************************************
* CONFIGURABLE GLOBALS:
***************************************************/
private constant integer MODEL_1 = 'npn1'
private constant integer MODEL_2 = 'npn2'
private constant integer MODEL_3 = 'npn3'
private constant real SPAWN_DELAY = 1
private constant string APPEAR_SFX = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl"
/**************************************************
* NON-CONFIGURABLE GLOBALS:
***************************************************/
private Table tb
private integer array spawnModel
private group g = CreateGroup()
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetAoe takes integer level returns real
return 500.
endfunction
private function GetChance takes integer level returns integer
return 10 * level //10, 20, 30 , 40, 50
endfunction
//spell duration
private function GetDuration takes integer level returns real
return 120. //20. * level + 10
endfunction
//spawned unit duration
private function GetLifeTimer takes integer level returns real
return 10. * level + 10 //20, 30, 40, 50, 60
endfunction
private function FilterOutTargets takes unit u returns boolean
return true //not (IsUnitType(u, UNIT_TYPE_MECHANICAL) or IsUnitType(u, UNIT_TYPE_STRUCTURE))
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct SpawnedTimer
unit sp
real dur
private static real period = 0.1
private static method periodic takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set .dur = .dur - period
if .dur < 0 then
if UnitAlive(.sp) then
call DestroyEffect(AddSpecialEffect(APPEAR_SFX, GetUnitX(.sp), GetUnitY(.sp)))
call ShowUnit(.sp, false)
call KillUnit(.sp)
endif
set tb.unit[GetHandleId(.sp)] = null
call tb.remove(GetHandleId(.sp))
set .sp = null
call ReleaseTimer(t)
call .deallocate()
endif
set t = null
endmethod
static method timed takes unit sp, real lifeS returns nothing
local thistype this = allocate()
local integer spID = GetHandleId(sp)
set tb[spID] = 1234
set .sp = sp
set .dur = lifeS
call TimerStart(NewTimerEx(this), period, true, function thistype.periodic)
endmethod
endstruct
private struct BloodBrothers
unit caster
unit sp
integer chance
real duration
real interval
real aoe
real lifeS
player pl
private static real period = 0.1
private static method periodic takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local unit first
local integer i
local integer hID
local integer spID
set hID = GetHandleId(.caster)
if UnitAlive(.caster) and .duration > 0 then
set .duration = .duration - period
set .interval = .interval + period
if (.interval) >= (SPAWN_DELAY) then
set interval = 0
set i = 0
call GroupEnumUnitsInRange(g, GetUnitX(.caster), GetUnitY(.caster), .aoe, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null or i > 0
if UnitAlive(first) and IsUnitEnemy(first, .pl) and FilterOutTargets(first) then
set i = i + 1
endif
call GroupRemoveUnit(g, first)
endloop
if i > 0 then
if not UnitAlive(tb.unit[hID]) then
if GetRandomInt(1, 100) <= .chance then
set i = GetRandomInt(1, 3)
set .sp = CreateUnit(.pl, spawnModel[i], GetUnitX(.caster), GetUnitY(.caster), 0)
set tb.unit[hID] = .sp
call DestroyEffect(AddSpecialEffectTarget(APPEAR_SFX, .sp, "origin"))
call SpawnedTimer.timed(.sp, .lifeS)
endif
endif
endif
endif
else
call tb.remove(hID)
set .caster = null
set .sp = null
set .pl = null
call ReleaseTimer(t)
call .deallocate()
endif
set t = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit u = GetTriggerUnit()
local integer level
local integer hID = GetHandleId(u)
if not tb.has(hID) then
set this = allocate()
set level = GetUnitAbilityLevel(u, SPELL_ID)
set .caster = u
set .duration = GetDuration(level)
set .aoe = GetAoe(level)
set .chance = GetChance(level)
set .lifeS = GetLifeTimer(level)
set .pl = GetTriggerPlayer()
set .interval = 0
set tb[hID] = level
set tb.unit[hID] = null
set .sp = null
call TimerStart(NewTimerEx(this), period, true, function thistype.periodic)
else
call IssueImmediateOrder(u, "stop")
call SimError(GetTriggerPlayer(), "you cant double cast this spell!")
endif
set u = null
endmethod
private static method deathEvent takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer uID = GetHandleId(u)
if tb[uID]==1234 then
call DestroyEffect(AddSpecialEffectTarget(APPEAR_SFX, u, "origin"))
call ShowUnit(u, false)
call KillUnit(u)
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.deathEvent)
set tb = Table.create()
set spawnModel[1] = MODEL_1
set spawnModel[2] = MODEL_2
set spawnModel[3] = MODEL_3
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*******************************************************************
Invincibility v1.0
by mckill2009
********************************************************************/
library Invincibility uses TimerUtils, SpellEffectEvent
globals
/**************************************************
* RAW CODES: must be matched according to the object editor
***************************************************/
private constant integer SPELL_ID = 'A043'
private constant integer BONUS_ID = 'A044'
private constant integer EVADE_ID = 'ACes' //evasion 100%
endglobals
/**************************************************
* CONFIGURABLE FUNCTIONS:
***************************************************/
private function GetDuration takes integer level returns real
return 45. //150. * level + 200
endfunction
/**************************************************
* NON-CONFIGURABLE FUNCTIONS: May NOT be editable below this line
***************************************************/
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u)!=0
endfunction
private struct Invincibility
unit caster
real dur
real mana
private static method periodic takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local real manaR = GetUnitState(.caster, UNIT_STATE_MANA)
set .dur = .dur - 0.1
if manaR < .mana then
set manaR = manaR + 1
call SetUnitState(.caster, UNIT_STATE_MANA, manaR)
endif
if not UnitAlive(.caster) or .dur < 0 then
//call BJDebugMsg("SPELL FINISHED")
if GetUnitAbilityLevel(.caster, BONUS_ID)>0 then
call UnitRemoveAbility(.caster, BONUS_ID)
call UnitRemoveAbility(.caster, EVADE_ID)
endif
set .caster = null
call ReleaseTimer(t)
call .deallocate()
endif
set t = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local unit u = GetTriggerUnit()
local integer level = GetUnitAbilityLevel(u, SPELL_ID)
set .caster = u
set .dur = GetDuration(level)
set .mana = GetUnitState(u, UNIT_STATE_MAX_MANA)
if GetUnitAbilityLevel(u, BONUS_ID)==0 then
call UnitAddAbility(u, BONUS_ID)
call UnitAddAbility(u, EVADE_ID)
endif
call TimerStart(NewTimerEx(this), 0.1, true, function thistype.periodic)
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary