//TESH.scrollpos=12
//TESH.alwaysfold=0
globals
BoardEx mb
StrikeForce arthas
StrikeForce mal
StrikeForceEx ZC
StrikeForceEx UD //Undead, UNDEAD_FORCE
QuestUtils save
EscortSystem uther
boolean MalganisTeleport = true
group TeleportGroup = CreateGroup()
unit TempU = null
player TempPlayer = null
integer CONV = 0 //MalGanis Score
integer KILLED = 0 //Arthas Score
integer SAVED = 0 //Uther/Jaina Score
integer SECRETS = 0
constant player ARTHAS_ONLY = Player(1) //Arthas Only
constant player ARTHAS_FORCE = Player(3) //Artha's Force
constant player JAINAS_FORCE = Player(2)
constant player ZOMBIE_FORCE = Player(8)
constant player UNDEAD_FORCE = Player(9)
constant player MALGANIS_PLAYER = Player(10)
constant player VILLAGER_FORCE = Player(11)
constant boolean ENABLE_TEST = false //should be false
constant boolean ENABLE_ARTHAS_AI = true
constant boolean ENABLE_MALGANIS_AI = true
constant boolean ENABLE_UNDEAD_AI = true
constant integer OID_darkconversion = 852228
constant integer DARK_CONV_MAL_SPELL_ID = 'ANdc' //MalGaniss spell, creates Lvl 1 zombie
constant integer DARK_CONV_BUFF_ID = 'BNdc'
constant integer SAVE_VILLAGER_SPELL_ID = 'A001'
constant integer SECRET_RUNE = 'secr'
constant integer TARGET_BUILDING = 6 //Player 7 targeted by Arthas and MalGanis
constant integer REQUIRES = 200 //required score, 200
constant integer MIN_UNITS = 9 //min villagers units
constant integer MAX_UNITS = 15 //max villagers units
constant integer MAX_SECRETS = 30
constant real XMalLast = 8500 //arthas also
constant real YMalLast = -6100
constant real YArtLast = -5750
constant real MALGANIS_REVIVE_DELAY = 60 //normal 60, hard 30, should be 60
constant real JAINA_REVIVE_DELAY = 60 //normal 60, hard 120, should be 60
constant real ARTHAS_REVIVES = 30 //normal 30, hard 60, should be 30
constant real MALGANIS_ENTERS_MAP = 90 //normal 180, hard 90, , should be 90
constant string TELEPORT_SFX = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl"
endglobals
function TestEnable takes nothing returns nothing
if ENABLE_TEST then
call FogEnable(false)
call FogMaskEnable(false)
call SetPlayerStateBJ( Player(0), PLAYER_STATE_RESOURCE_GOLD, 50000 )
call SetPlayerStateBJ( Player(0), PLAYER_STATE_RESOURCE_LUMBER, 50000 )
endif
endfunction
//=====MalGanis first enter
function MalGanisAttacksFirstBuilding takes nothing returns nothing
local unit first
if not udg_SkipCinematics then
call PauseUnit(udg_Arthas,true)
call SetUnitPosition(udg_Arthas,11061,4448)
call SetUnitFacing(udg_Arthas,180)
endif
//Malganis attacks building
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) and SamePlayer(first, udg_MalGanis) then
call IssueTargetOrder(first,"attack",udg_TempUnit)
endif
//! runtextmacro GROUP_ENUM_RECT_END()
endfunction
function MG_Filter takes player p returns boolean
return Player(0)==p or Player(1)==p or Player(9)==p
endfunction
function MalGanisFirstAppear takes nothing returns nothing
local integer i = 0
local integer c = udg_DifficultyS+2 //5 & 8
local unit first
if ENABLE_MALGANIS_AI then
call SetUnitPosition(udg_MalGanis,2815,300)
loop
set first = CreateUnit(Player(10),'ugho',2850,250,0)
set first = null
set i = i+1
exitwhen i==c
endloop
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) and MG_Filter(GetOwningPlayer(first)) and not IsUnitType(first,UNIT_TYPE_STRUCTURE) and not IsUnitType(first,UNIT_TYPE_PEON) then
call IssueImmediateOrder(first,"stop")
endif
//! runtextmacro GROUP_ENUM_RECT_END()
endif
endfunction
//====================
//=====Win code
function Win takes player p returns nothing
if p==Player(0) then
call CustomVictoryBJ(Player(0),true,true)
elseif p==ARTHAS_ONLY then
call CustomDefeatBJ(Player(0),"Defeat!")
elseif p==MALGANIS_PLAYER then
call CustomDefeatBJ(Player(0),"Defeat!")
endif
endfunction
//===All units in 1 place when ending
function UtherJaina takes nothing returns nothing
call SetUnitPosition(udg_Uther, 8500, -5500)
call SetUnitPosition(udg_Jaina, 8700, -5300)
endfunction
function SetAllUnitsInvulnerable takes nothing returns nothing
local unit first
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) then
call SetUnitInvulnerable(first,true)
endif
//! runtextmacro GROUP_ENUM_RECT_END()
endfunction
function SetUnitsXY takes integer cinematic returns nothing
local unit first
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) then
if not FilterBuildings(GetUnitTypeId(first)) then
call RemoveUnit(first)
endif
endif
//! runtextmacro GROUP_ENUM_RECT_END()
call DestroyTimerDialog(ArthasForce.tdia)
call DestroyTimerDialog(JainasForce.tdia)
call DestroyTimerDialog(MalGanisForce.tdia)
set udg_Arthas = CreateUnit(Player(0),'Hart',11000,-1500,0)
set udg_Jaina = CreateUnit(Player(0),'Hjai',11000,-1500,0)
set udg_MalGanis = CreateUnit(Player(0),'Umal',11000,-1500,0)
set udg_Uther = CreateUnit(Player(0),'Huth',11000,-1500,0)
call SetUnitInvulnerable(udg_Arthas, true)
call SetUnitInvulnerable(udg_Jaina, true)
call SetUnitInvulnerable(udg_MalGanis, true)
call SetUnitInvulnerable(udg_Uther, true)
if cinematic==1 then
//Cinematic for Arthas and MalGanis only
//or Arthas, MalGanis, Uther and Jaina if Player(0) wins
call SetUnitPosition(udg_Arthas, XMalLast, YArtLast)
call SetUnitPosition(udg_MalGanis, XMalLast, YMalLast)
elseif cinematic==2 then
//Cinematic for Malganis
endif
call SetUnitFacing(udg_Arthas, 270)
call SetUnitFacing(udg_MalGanis, 90)
call PauseUnit(udg_Arthas,true)
call PauseUnit(udg_MalGanis,true)
endfunction
//===MalGanis teleports to either Arthas or Uther, see MultiboardSetupAndScores
function MalTeleport takes unit teleportToWhom returns nothing
local unit first
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) and not IsUnitInGroup(first, TeleportGroup) and not IsUnitType(first, UNIT_TYPE_STRUCTURE) /*
*/ and MALGANIS_PLAYER==GetOwningPlayer(first) then
call SetUnitPosition(first,GetUnitX(teleportToWhom),GetUnitY(teleportToWhom))
//call BJDebugMsg(GetUnitName(teleportToWhom))
endif
//! runtextmacro GROUP_ENUM_RECT_END()
call DestroyEffect(AddSpecialEffect(TELEPORT_SFX, GetUnitX(teleportToWhom), GetUnitY(teleportToWhom)))
set MalganisTeleport = false
endfunction
Name | Type | is_array | initial_value |
Arthas | unit | No | |
ArthasBuildingEnemy | boolean | No | |
Barracks | unit | No | |
CheckLast | boolean | No | true |
Counter1 | integer | No | 4 |
CT | integervar | No | |
CT_Angle | real | Yes | |
CT_Cosmic_Distance | real | Yes | |
CT_CosmicDistanceINC | real | Yes | |
CT_DAMAGE | real | No | |
CT_Damage_Reduction | real | Yes | |
CT_DEBUG | group | No | |
CT_Group | group | Yes | |
CT_Hero | unit | Yes | |
CT_Integer_Debuger | integer | Yes | |
CT_KnockBack_Formula | real | Yes | |
CT_KnockBackRange | real | Yes | |
CT_KnockBackSPeed | real | Yes | |
CT_KnockOff | boolean | Yes | |
CT_LEVEL | integer | No | |
CT_Point | location | Yes | |
CT_real_ext | real | No | |
CT_Skip | integer | No | |
CT_Spam | integer | Yes | |
CT_Target | unit | Yes | |
CT_Times | integer | No | |
CT_TimeTwist_Half | integer | Yes | |
CT_Twist_Speed | real | Yes | |
CT_twist_time | integer | No | |
CT_Twist_Time | integer | Yes | |
CT_TwistOff | boolean | Yes | |
CT_X_Point | location | Yes | |
DancingFlame | unit | Yes | |
DancingFlameAngle | real | Yes | |
DancingFlameDistance | real | No | |
DancingFlameGroup | group | No | |
DancingFlameHashtable | hashtable | No | |
DancingFlameID | integer | No | |
DancingFlameSpins | integer | No | |
DancingFlameStep | real | No | |
DancingFlameStep2 | real | No | |
DifficultyS | integer | No | |
Hash | hashtable | No | |
HC_AbilityId | abilcode | No | |
HC_Caster | unit | No | |
HC_CasterPt | location | No | |
HC_CheckLight | boolean | No | |
HC_Confg_CrossBaseHeight | real | No | |
HC_Config_CasterHeight | real | No | |
HC_Config_CrossDamageAoE | real | No | |
HC_Config_CrossFar | real | No | |
HC_Config_CrossNear | real | No | |
HC_Config_CrossRotationSpeed | real | No | |
HC_Config_LightDamageAoE | real | No | |
HC_Config_LightSizeBase | real | No | |
HC_Config_LightSizeMax | real | No | |
HC_CrossDamage | real | No | |
HC_CrossU | unit | No | |
HC_CrossUType | unitcode | No | |
HC_DummyID | integer | No | |
HC_Duration | real | No | |
HC_ID | integer | No | |
HC_Index1 | integer | No | |
HC_InstanceIndex | integer | No | |
HC_Level | integer | No | |
HC_Light | unit | No | |
HC_LightDamage | real | No | |
HC_LightSfxUType | unitcode | No | |
HC_LightSize | real | No | |
HC_LightUType | unitcode | No | |
HC_LocP | location | No | |
HC_RotationC | real | No | |
HC_SfxID | abilcode | No | |
HC_UGrp | group | No | |
Jaina | unit | No | |
Mal_Crypt | unit | No | |
Mal_Sh | unit | No | |
MalGanis | unit | No | |
MalGanisBuildingEnemy | boolean | No | |
MB_Caster | unit | Yes | |
MB_Center | location | Yes | |
MB_Coutbuff | integer | Yes | |
MB_Damage | real | Yes | |
MB_Distance | real | Yes | |
MB_Eyecandy1 | real | Yes | |
MB_Formula | real | Yes | |
MB_Group | group | Yes | |
MB_HOW | integer | Yes | |
MB_Loop | integervar | No | |
MB_Point | location | Yes | |
MB_Speed | real | Yes | |
MB_Switch | integer | Yes | |
MBLevel | integer | Yes | |
OnorOff | group | No | |
Portal | unit | No | |
PortalZone | location | No | |
SkipCinematics | boolean | No | false |
TempInt | integervar | No | |
TempLoc | location | No | |
TempLoc2 | location | No | |
TempPlayer | player | No | |
TempUnit | unit | No | |
Times2 | integer | No | |
Uther | unit | No | |
WinArthas | boolean | No | |
WinArthasUtherCinematic | real | No | |
WinMalGanisCinematic | real | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
[hidden=Changelogs:]
v1.1
- Fixed Undead AI that does not attack in waves
- Fixed Jaina attack Artha's force
- Gold Mine sets to 30000
- Fixed tech that's not available for this chapter is removed
- Secret locations added, find it for your own advantage
v1.0
- Initial release
[/hidden]
//TESH.scrollpos=0
//TESH.alwaysfold=0
[B][SIZE="5"][COLOR="Yellow"]The Harvest v1.1[/COLOR][/SIZE][/B]
This is a fanmade parody/reboot of [B][I][U]Chapter Six: The Culling from Reign of Chaos[/U][/I][/B]. We know already
that Uther was removed by Arthas from duty, then Arthas purges the city by killing the villagers. This story is different,
because what if Uther decided to stay and helped the villagers to escape?
The story lets you play Uther. Malganis continues to convert the villagers into zombies while Arthas
will be played by an AI and kill villagers. Your goal is to let the villagers teleport to the portal created by Jaina.
On the other hand, Jaina will play as AI, assisting you in teleporting the villagers.
[B][SIZE="3"][COLOR="Yellow"]Features:[/COLOR][/SIZE][/B]
- Cinematics and voice overs from the original campaign.
- Custom spells for Arthas/Uther/Jaina/MalGanis.
- Kills/escape requirements increased to 200.
- Improved AI offensive and defensive strategy.
- Secret locations find it for your own advantage
[B][SIZE="3"][COLOR="yellow"]Screenshots: version 1.0 screenshots available[/COLOR][/SIZE][/B]
[hidden=]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62360.jpg[/IMG]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62361.jpg[/IMG]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62362.jpg[/IMG]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62363.jpg[/IMG]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62364.jpg[/IMG]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62365.jpg[/IMG]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4826-picture62366.jpg[/IMG]
[/hidden]
[B][SIZE="3"][COLOR="yellow"]Staff/Ideas/Credits:[/COLOR][/SIZE][/B]
[hidden=]
Mckill2009 - Project Leader/Coder/Cinematics
Wandering Soul - Dialog suggestions
[COLOR="yellow"][COLOR="yellow"]Terrain:[/COLOR][/COLOR]
- Blizzard Entertainment
[COLOR="yellow"][COLOR="yellow"]Systems:[/COLOR][/COLOR]
- Vexorian - TimerUtils, BoundSentinel, SimError, PowerupSentinel
- Bribe - Table, SpellEffectEvent
- Magtheridon96 - RegisterPlayerUnitEvent
- Spinnaker - GetClosestWidget
- Jesus4lyf - Timer32
- The_Reborn_Devil - TimedNoise
[COLOR="yellow"][COLOR="yellow"]Spells:[/COLOR][/COLOR]
- Sun Ray by Flame_Phoenix
- Chicken Burst by Garfield1337
- Dancing Flame by Jory-PL
- Massive Blast On by RMX
- Cosmic Twist by RMX
- Cursed Soul by Moyack
- DiabolicCountdown by watermelon_1234
[COLOR="Yellow"][COLOR="yellow"]Models:[/COLOR][/COLOR]
- Nebula by WILL THE ALMIGHTY
[COLOR="yellow"][COLOR="yellow"]Icons:[/COLOR][/COLOR]
- BTNPHolyBolt by Paladon
[/hidden]
[hidden=Changelogs:]
v1.1
- Fixed Undead AI that does not attack in waves
- Fixed Jaina attack Artha's force
- Gold Mine sets to 30000
- Fixed tech that's not available for this chapter is removed
- Secret locations added, find it for your own advantage
v1.0
- Initial release
[/hidden]
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
Button Placements:
0,0 1,0 2,0 3,0
0,1 1,1 2,1 3,1
0,2 1,2 2,2 3,2
*/
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Resources initializer init
private function resource takes nothing returns boolean
local integer i = 1
local integer gold
local integer lumber
loop
set gold = GetPlayerState(Player(i),PLAYER_STATE_RESOURCE_GOLD)
set lumber = GetPlayerState(Player(i),PLAYER_STATE_RESOURCE_LUMBER)
if gold < 1500 then
call SetPlayerState(Player(i),PLAYER_STATE_RESOURCE_GOLD,gold+498)
endif
if lumber < 1099 then
call SetPlayerState(Player(i),PLAYER_STATE_RESOURCE_LUMBER,lumber+498)
endif
set i = i+1
exitwhen i==11
endloop
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterTimerEvent(t, 2.0, true)
call TriggerAddCondition(t,function resource)
set t = null
endfunction
endscope
//TESH.scrollpos=105
//TESH.alwaysfold=0
//MalGanis Scores is in ZombieStrikeForce
scope MultiboardSetup initializer init
// uses BoardEx,ColorUtils,Functions,SpellEffectEvent,MuiDummyCasters//,Skirmish
globals
private string array MODE
private unit Villager = null
private unit TU = null
private integer RAN_MAX = 10
endglobals
//Uther and Jaina Scores
struct TeleportVillager
unit tar
real dur
effect sfx
static string sfxTel = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTo.mdl"
static string sfxDone = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportCaster.mdl"
static real d = 2.0
implement DTloop
if .tar!=null then
set .dur = .dur + 0.5
if .dur==d then
call DestroyEffect(AddSpecialEffect(sfxDone,GetUnitX(tar),GetUnitY(tar)))
elseif .dur==d+.5 then
call DestroyEffect(.sfx)
call SetUnitPosition(.tar,3000,8000)
call DestroyEffect(AddSpecialEffect(sfxDone,GetUnitX(tar),GetUnitY(tar)))
elseif .dur==d+1 then
set SAVED = SAVED + 1
call mb.setText(1, 1, I2S(SAVED))
call RemoveUnit(.tar)
if SAVED >= REQUIRES then
set udg_WinArthas = false
set udg_WinArthasUtherCinematic = 1
endif
set .tar = null
endif
implement DTend
static method TV takes unit u returns nothing
local thistype this = create(0.5)
set .tar = u
set .dur = 0
set .sfx = AddSpecialEffect(sfxTel,GetUnitX(u),GetUnitY(u))
call PauseUnit(u,true)
call SetUnitInvulnerable(u,true)
endmethod
endstruct
private function SaveVil takes nothing returns nothing
local unit tar = GetSpellTargetUnit()
local integer random
if FilterVillagers(GetUnitTypeId(tar)) then
call TeleportVillager.TV(tar)
if MalganisTeleport then
if UnitAlive(udg_MalGanis) and UnitAlive(udg_Uther) then
set random = GetRandomInt(0,RAN_MAX)
if random==3 then
call MalTeleport(udg_Uther)
endif
endif
endif
else
call IssueImmediateOrder(GetTriggerUnit(),"stop")
call SimError(GetTriggerPlayer(),"You can only target a villager")
endif
set tar = null
endfunction
//Arthas Scores
private function VillagerDies takes nothing returns nothing
local unit killingU
local player ownerP
local integer random
local unit first
local unit dying = GetTriggerUnit()
if FilterVillagers(GetUnitTypeId(dying)) then
set killingU = GetKillingUnit()
set ownerP = GetOwningPlayer(killingU)
if ownerP==Player(0) or ownerP==Player(1) or ownerP==Player(2) or ownerP==ARTHAS_FORCE then
set KILLED = KILLED + 1
call mb.setText(2, 1, I2S(KILLED))
if KILLED >= REQUIRES then
set udg_WinArthas = true
set udg_WinArthasUtherCinematic = 1
endif
if MalganisTeleport then
if UnitAlive(udg_MalGanis) and UnitAlive(udg_Arthas) then
set random = GetRandomInt(0,10)
if random==3 then
call MalTeleport(udg_Arthas)
endif
endif
endif
endif
set killingU = null
set ownerP = null
elseif dying==udg_Arthas or dying==udg_Uther then
set MalganisTeleport = true
endif
set dying = null
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function VillagerDies)
call RegisterSpellEffectEvent(SAVE_VILLAGER_SPELL_ID, function SaveVil) //unholy frenzy to save villagers
set MODE[3] = "(Normal Mode)"
set MODE[6] = "(Hard Mode)"
endfunction
//not private
function SetupMB takes nothing returns nothing
local integer max = 6 //4 normal
local real first = 0.07
local real second = 0.035
local integer i = 0
set mb = mb.create(max,3,true,false)
loop
call mb.setIndividualWidths(i,0,first)
call mb.setIndividualWidths(i,1,second-0.01)
call mb.setIndividualWidths(i,2,second+0.02)
set i = i + 1
exitwhen i==max
endloop
set i = 0
loop
call mb.setText(4, i, "===============")
set i = i + 1
exitwhen i==3
endloop
call mb.setTitle(FixedColor(48, "The Harvest - by Mckill2009 ")+FixedColor(36, MODE[udg_DifficultyS]))
call mb.setText(0, 0, FixedColor(45, "Requires"))
call mb.setText(1, 0, FixedColor(49, "Uther and Jaina"))
call mb.setText(2, 0, FixedColor(37, "Arthas's Force"))
call mb.setText(3, 0, FixedColor(0, "MalGanis's Force"))
call mb.setText(0, 1, FixedColor(48,I2S(REQUIRES)))
call mb.setText(1, 1, I2S(SAVED))
call mb.setText(2, 1, I2S(KILLED))
call mb.setText(3, 1, I2S(CONV))
call mb.setText(0, 2, FixedColor(45, "Villagers"))
call mb.setText(1, 2, FixedColor(49, "Saved"))
call mb.setText(2, 2, FixedColor(37, "Killed"))
call mb.setText(3, 2, FixedColor(0, "Converted"))
call mb.setText(5, 0, RandomColor("Secrets Found"))
call mb.setText(5, 1, I2S(SECRETS)+"/"+I2S(MAX_SECRETS))
call mb.display(false, true)
//setting up levels for enemy heroes
if udg_DifficultyS==6 then
set RAN_MAX = 4
call SetHeroLevel(udg_Arthas, 6, false)
call SetHeroLevel(udg_MalGanis, 10, false)
endif
set i = 0
loop
call HeroPickSkill(udg_Arthas)
set i = i + 1
exitwhen i==10
endloop
set i = 0
loop
call HeroPickSkill(udg_MalGanis)
set i = i + 1
exitwhen i==10
endloop
endfunction
endscope
//TESH.scrollpos=18
//TESH.alwaysfold=0
scope DialogSetup initializer init
globals
private DialogBox start
private button Yes
private button No
private button Normal
private button Hard
private boolean chk = true
endglobals
private function StartGame takes nothing returns nothing
set Yes = null
set No = null
set Normal = null
set Hard = null
call start.destroy()
call CameraSetupApplyForPlayer( true, gg_cam_Arthas_and_Jaina_1, Player(0), 0 )
call TriggerExecute(gg_trg_IntroOrigCin)
endfunction
private function LaunchDifficultySettings takes nothing returns nothing
if start.clicked(Yes) then
set udg_SkipCinematics = true
endif
if chk then
set chk = false
call start.clear()
set start.title = "Select Difficulty"
set Normal = start.addButton("Normal", 1)
set Hard = start.addButton("Hard", 2)
call start.display(Player(0),true)
endif
if start.clicked(Normal) then
set udg_DifficultyS = 3
call SetPlayerState(Player(0),PLAYER_STATE_RESOURCE_GOLD,600)
call SetPlayerState(Player(0),PLAYER_STATE_RESOURCE_LUMBER,300)
call StartGame()
elseif start.clicked(Hard) then
set udg_DifficultyS = 6
call SetPlayerState(Player(0),PLAYER_STATE_RESOURCE_GOLD,200)
call SetPlayerState(Player(0),PLAYER_STATE_RESOURCE_LUMBER,100)
call StartGame()
endif
endfunction
private function DialogStart takes nothing returns nothing
call EnableUserControl(true)
call DestroyTimer(GetExpiredTimer())
set start = start.create("Skip Cinematics ?", function LaunchDifficultySettings)
set Yes = start.addButton("Yes", 1)
set No = start.addButton("No", 2)
call start.display(Player(0),true)
endfunction
private function init takes nothing returns nothing
if not ENABLE_TEST then
call CinematicModeBJ( true, GetPlayersAll())
call CinematicFadeBJ( bj_CINEFADETYPE_FADEOUT, 0.00, "ReplaceableTextures\\CameraMasks\\White_mask.blp", 0, 0, 0, 0 )
call TimerStart(CreateTimer(),0,false,function DialogStart)
endif
endfunction
endscope
//TESH.scrollpos=22
//TESH.alwaysfold=0
scope BuildingDies
globals
private constant real WAIT_ATTACK = 40.
private integer array Utype //used for creating villagers after building dies
endglobals
//==========BUILDING DIES
private function BuildingDies takes nothing returns nothing
local unit first
local unit villager
local unit killingU
local integer i
local integer qty
local real x
local real y
local player killingPlayer
//Player 6 building destroyed
if FilterBuildings(GetUnitTypeId(GetTriggerUnit())) then
call StartSound(gg_snd_CityBuildingDeath1)
call ApplyNoise(20, 20, 2.5)
set killingU = GetKillingUnit()
set killingPlayer = GetOwningPlayer(killingU)
//create Villagers for Player 11
set i = 0
set x = GetUnitX(GetTriggerUnit())
set y = GetUnitY(GetTriggerUnit())
set qty = GetRandomInt(MIN_UNITS,MAX_UNITS)
loop
set i = i + 1
set villager = CreateUnit(Player(11),Utype[GetRandomInt(1,4)],x,y,0)
call IssuePointOrder(villager,"move",x+250*Cos(GetRandomReal(0,6)),y+250*Sin(GetRandomReal(0,6)))
set villager = null
exitwhen i==qty
endloop
if killingPlayer!=Player(0) then
call PlayWarningSound()
endif
if killingPlayer==ARTHAS_ONLY or killingPlayer==ARTHAS_FORCE then //Arthas destroys building
set udg_ArthasBuildingEnemy = true //see ArthasForce AI
call SetPlayerAlliance(ARTHAS_ONLY,Player(6),ALLIANCE_PASSIVE,true)
call SetPlayerAlliance(ARTHAS_FORCE,Player(6),ALLIANCE_PASSIVE,true)
elseif killingPlayer==MALGANIS_PLAYER then //Malganis destroys building
set udg_MalGanisBuildingEnemy = true //see MalGanisForce AI
call SetPlayerAlliance(MALGANIS_PLAYER,Player(6),ALLIANCE_PASSIVE,true)
endif
set killingU = null
set killingPlayer = null
endif
endfunction
function startBuildingDies takes integer difficulty returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function BuildingDies)
//Villager types
set Utype[1] = 'nC00'
set Utype[2] = 'nC01'
set Utype[3] = 'nC02'
set Utype[4] = 'nC04'
endfunction
endscope
//TESH.scrollpos=3
//TESH.alwaysfold=0
scope QuestAndInfo
globals
endglobals
function SetupQuest takes nothing returns nothing
local QuestUtils hint
local QuestItem hint1
local QuestItem hint2
local QuestItem hint3
local QuestItem hint4
set save = save.create(true)
set save["Main Quest"] = "Save the villagers by teleporting them to the portal."
set save.questIcon = "ReplaceableTextures\\CommandButtons\\BTNVillagerMan1.blp"
set hint = hint.create(false)
set hint["Hints"] = ""
set hint.questIcon = "ReplaceableTextures\\CommandButtons\\BTNBrilliance.blp"
set hint1 = hint1.create(hint, "Fortify your base as the attacks will get intense even on normal mode.")
set hint2 = hint2.create(hint, "Try to slay MalGanis as he's scoring faster than Arthas's force.")
set hint3 = hint3.create(hint, "Do not let Jaina die as she's a powerful escort.")
set hint4 = hint4.create(hint, "Use your abilities when a flock of enemies surrounds you.")
call PlayDiscoveredSound()
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope SecretsUpdate
globals
private unit uther
private unit rune
private string array sfx
private integer array itemid
private boolean checkfirst = true
endglobals
private function RuneRewards takes nothing returns nothing
local integer i = GetRandomInt(0,15)
local real x = GetUnitX(rune)
local real y = GetUnitY(rune)
call CreateItem(itemid[i], x, y)
endfunction
private function SecretsUpdate takes nothing returns boolean
set SECRETS = SECRETS + 1
call RuneRewards()
call DestroyEffect(AddSpecialEffect(sfx[GetRandomInt(0,5)], GetUnitX(rune), GetUnitY(rune)))
call KillUnit(rune)
call mb.setText(5, 1, I2S(SECRETS)+"/"+I2S(MAX_SECRETS))
if checkfirst then
set checkfirst = false
call DisplayTextToPlayer(Player(0), 0, 0, "Find runes like this to get random advantage.")
call StartSound(bj_questHintSound)
endif
if SECRETS==MAX_SECRETS then
set rune = null
call DestroyTrigger(GetTriggeringTrigger())
endif
return false
endfunction
private function RuneInRange takes nothing returns boolean
set rune = GetFilterUnit()
return GetUnitTypeId(rune)==SECRET_RUNE
endfunction
function RunSecrets takes nothing returns nothing
local trigger t = CreateTrigger()
set uther = udg_Uther
call TriggerRegisterUnitInRange(t, uther, 100, Filter(function RuneInRange))
call TriggerAddCondition(t, function SecretsUpdate)
set sfx[0] = "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl"
set sfx[1] = "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
set sfx[2] = "Abilities\\Spells\\Items\\AIam\\AIamTarget.mdl"
set sfx[3] = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
set sfx[4] = "Abilities\\Spells\\Human\\Polymorph\\PolyMorphDoneGround.mdl"
set sfx[5] = "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl"
set t = null
//setting items
set itemid[0] = 'tdx2' //tome of agility + 2
set itemid[1] = 'tin2' //tome of intelligence + 2
set itemid[2] = 'tst2' //tome of strength +2
set itemid[3] = 'tpow' //tome of knowledge
set itemid[4] = 'rhe3' //rune of greater healing
set itemid[5] = 'gold' //gold coins
set itemid[6] = 'lmbr' //lumber
set itemid[7] = 'rma2' //rune of greater mana
set itemid[8] = 'manh' //manual of health
set itemid[9] = 'ajen' //endurance
set itemid[10] = 'rat6' //claws of attack +6
set itemid[11] = 'rlif' //regeneration
set itemid[12] = 'pmna' //pendant of mana
set itemid[13] = 'prvt' //periant of vitality
set itemid[14] = 'clfm' //cloak of flames
set itemid[15] = 'hslv' //healing salve
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ArthasForce
//uses RegisterPlayerUnitEvent, TrainSystem, StrikeForce
globals
private integer groupSizeMax // = 2 //maximum units that will escort Arthas
private integer groupSizeMin = 0
private integer groupCount = 0
private group Grp = CreateGroup()
private integer AID
private unit Arthas
private unit U
private constant integer MaxArmySize = 16
endglobals
//Player(1) is Arthas ONLY
//Player(3) is Artha's Force
struct ArthasForce
static group aGrp = CreateGroup()
static timer tim = CreateTimer()
static timerdialog tdia = null
static real reviveDelay
static method filterFollower takes integer uID returns boolean
return uID=='hrif' or uID=='hfoo' or uID=='hkni' or uID=='hmpr' or uID=='hsor'
endmethod
static method filterStructure takes integer uID returns boolean
return uID=='hbar' or uID=='hars'
endmethod
static method constructFinish takes nothing returns nothing
local unit structure = GetTriggerUnit()
if GetUnitTypeId(structure)=='hars' then
call TrainSystem.register(structure, 4)
call TrainSystem.addTrain(structure, 'hsor')
call TrainSystem.addTrain(structure, 'hmpr')
elseif GetUnitTypeId(structure)=='hbar' then
call TrainSystem.register(structure, groupSizeMax+2)
call TrainSystem.addTrain(structure, 'hfoo')
call TrainSystem.addTrain(structure, 'hkni')
call TrainSystem.addTrain(structure, 'hkni')
call TrainSystem.addTrain(structure, 'hrif')
endif
set structure = null
endmethod
static method trainEnd takes nothing returns nothing
local unit trained = GetTrainedUnit()
if filterFollower(GetUnitTypeId(trained)) then
if groupSizeMax >= groupSizeMin then
//call BJDebugMsg("REGISTER STRIKE==="+I2S(groupSizeMin))
set groupSizeMin = groupSizeMin + 1
call arthas.addUnit(trained)
call GroupAddUnit(Grp,trained)
endif
endif
set trained = null
endmethod
//This timer checks if there's no more villagers in arthas's point of view
private static method checkVillagerEnemies takes nothing returns nothing
local unit target = GetClosestUnitInRange(GetUnitX(Arthas),GetUnitY(Arthas),1000,Filter(function PickVillager))
if udg_ArthasBuildingEnemy then
set udg_ArthasBuildingEnemy = false //see BuildingDies
call SetPlayerAlliance(ARTHAS_ONLY,Player(6),ALLIANCE_PASSIVE,false)
call SetPlayerAlliance(ARTHAS_FORCE,Player(6),ALLIANCE_PASSIVE,false)
endif
set target = null
endmethod
//Action for Player(1)
static method arthasRevives takes unit altar returns nothing
local integer count = 0
local unit first
if udg_CheckLast then
set groupCount = groupSizeMin
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_START("ARTHAS_FORCE")
if not IsUnitInGroup(first,Grp) and UnitAlive(first) then
if filterFollower(GetUnitTypeId(first)) then
if groupSizeMax > groupCount then
call arthas.addUnit(first)
call GroupAddUnit(Grp,first)
set groupCount = groupCount + 1
endif
endif
endif
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_END()
//increasing the groupSizeMax to escort Arthas
call arthas.setMaxAttackSize(groupSizeMax)
//destroying the dialog and pausing the timer
call TimerDialogDisplay(tdia,false)
call PauseTimer(tim)
call ReviveHero(Arthas, GetUnitX(altar), GetUnitY(altar), true)
endif
endmethod
static method arthasRevivesCondition takes nothing returns nothing
local unit first
//! runtextmacro GROUP_ENUM_RECT_START()
if CF.done(first) and GetOwningPlayer(first)==ARTHAS_FORCE then
call thistype.arthasRevives(first)
exitwhen true
endif
//! runtextmacro GROUP_ENUM_RECT_END()
endmethod
static method deathEvent takes nothing returns nothing
local unit u = GetTriggerUnit()
if GetOwningPlayer(u)==ARTHAS_FORCE and IsUnitInGroup(u,Grp) then
set groupSizeMin = groupSizeMin - 1
call GroupRemoveUnit(Grp,u)
elseif u==Arthas then //actions for Player(1) ONLY
if udg_CheckLast then
//set groupSizeMin = 0 //resetting the sizeMin
if (groupSizeMax) > (MaxArmySize) then
set groupSizeMax = groupSizeMax + 2 //increases the unit # who follows Arthas
endif
call TimerStart(tim,reviveDelay,true,function thistype.arthasRevivesCondition)
set tdia = CreateTimerDialog(tim)
call TimerDialogSetTitle(tdia, "Arthas Revives In")
call TimerDialogDisplay(tdia,true)
endif
endif
set u = null
endmethod
static method onInit takes nothing returns nothing
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.constructFinish, ARTHAS_FORCE)
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_TRAIN_FINISH, function thistype.trainEnd, ARTHAS_FORCE)
//used by 2 players
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.deathEvent)
endmethod
//this code runs after malganis enters
static method start takes integer difficulty returns nothing
local unit first
if ENABLE_ARTHAS_AI then
set Arthas = udg_Arthas
set AID = GetHandleId(Arthas)
call CF.makeAvailable(gg_unit_halt_0012)
call StartCampaignAI( ARTHAS_FORCE, "war3mapImported\\AIHuman.ai" )
call TimerStart(CreateTimer(), 1.0, true, function thistype.checkVillagerEnemies)
call TrainSystem.register(udg_Barracks, 7)
call TrainSystem.addTrain(udg_Barracks, 'hfoo')
call TrainSystem.addTrain(udg_Barracks, 'hkni')
call TrainSystem.addTrain(udg_Barracks, 'hkni')
//index==1, size depends
set groupSizeMax = difficulty + 1
//udg_DifficultyS = 3,6 are values
if difficulty==3 then
set reviveDelay = ARTHAS_REVIVES
set arthas = arthas.create(Arthas, false, groupSizeMax, 5)
else
set reviveDelay = ARTHAS_REVIVES*2
set arthas = arthas.create(Arthas, false, groupSizeMax, 5)
endif
call arthas.setHeroTargets(TARGET_BUILDING)
call arthas.setBase(10900., 5500.)
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_START("ARTHAS_FORCE")
if GetUnitTypeId(first)=='hfoo' then
call arthas.addUnit(first)
call GroupAddUnit(Grp,first)
set groupSizeMin = groupSizeMin + 1
endif
exitwhen groupSizeMin==groupSizeMax
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_END()
endif
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope JainasForce
struct JainasForce //Player 2 (Player 3 in GUI)
static unit jaina = null
static timer tim = CreateTimer()
static timerdialog tdia = null
static real delay
static method FV takes nothing returns boolean
set TempU = GetFilterUnit()
return FilterVillagers(GetUnitTypeId(TempU)) and GetUnitAbilityLevel(TempU, 'BNdc')==0 and GetUnitAbilityLevel(TempU, 'B000')==0
endmethod
static method jainaTeleportingVillagers takes nothing returns nothing
local unit vil = GetClosestUnitInRange(GetUnitX(jaina),GetUnitY(jaina),500,Filter(function thistype.FV))
local integer oid
if vil!=null then
set oid = GetUnitCurrentOrder(jaina)
if oid==0 or oid==851983 or oid==851986 or oid==851971 or oid==851972 then
call IssueTargetOrder(jaina,"unholyfrenzy",vil)
set vil = null
endif
endif
endmethod
static method jainaRevives takes unit altar returns nothing
if udg_CheckLast then
call TimerDialogDisplay(tdia,false)
call PauseTimer(tim)
call ReviveHero(jaina, GetUnitX(altar), GetUnitY(altar), true)
set uther.addUnit = jaina
endif
endmethod
static method jainaRevivesCondition takes nothing returns nothing
local unit first
//! runtextmacro GROUP_ENUM_RECT_START()
if CF.done(first) and SamePlayer(first, udg_Uther) then
call thistype.jainaRevives(first)
exitwhen true
endif
//! runtextmacro GROUP_ENUM_RECT_END()
endmethod
static method jainaDies takes nothing returns nothing
if GetTriggerUnit()==jaina and udg_CheckLast then
call TimerStart(tim,delay,true,function thistype.jainaRevivesCondition)
set tdia = CreateTimerDialog(tim)
call TimerDialogSetTitle(tdia, "Jaina Revives In")
call TimerDialogDisplay(tdia,true)
endif
endmethod
static method jainaCast takes integer difficulty returns nothing
set jaina = udg_Jaina
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_DEATH,function thistype.jainaDies,JAINAS_FORCE)
set uther = uther.create(udg_Uther, true)
set uther.lifeCheck = 40
set uther.retreatX = 2600
set uther.retreatY = 7460
set uther.addUnit = jaina
call CF.makeAvailable(gg_unit_halt_0158)
call TimerStart(CreateTimer(),1.0,true,function thistype.jainaTeleportingVillagers)
if difficulty==3 then
set delay = JAINA_REVIVE_DELAY //60
else
set delay = JAINA_REVIVE_DELAY*2 //120
endif
endmethod
endstruct
endscope
//TESH.scrollpos=7
//TESH.alwaysfold=0
scope UndeadForce
//uses StrikeCaptain
globals
private integer MaxUnit = 0
private real xC = -540
private real yC = -8500
private unit Captain = null
endglobals
private function FormCaptain takes nothing returns nothing
set Captain = CreateUnit(UNDEAD_FORCE, 'h003', xC, yC, 0)
set UD = UD.create(Captain, 5, true)
set UD.xReturn = xC
set UD.yReturn = yC
set UD.targets = 1
endfunction
//usep==crypt, utod==temple, uslh==slaughter
//starts undead AI, Player 9 (Player10 in GUI)
struct UndeadForce
static group uGrp = CreateGroup()
static integer countIncrease = 0
static integer count = 0
static integer countU = 0
static integer countEx = 0
//ugho==ghoul, ucry===cryptfiend, umtw==meat wagon, uabo==abomination
static method filterFollower takes integer uID returns boolean
return uID=='ugho' or uID=='ucry' or uID=='unec' or uID=='uabo' or uID=='umtw'
endmethod
static method construct takes nothing returns nothing
local unit building = GetTriggerUnit()
if GetUnitTypeId(building)=='usep' then //crypt
call TrainSystem.register(building, 3)
call TrainSystem.addTrain(building,'ugho') //ghoul
call TrainSystem.addTrain(building,'ucry') //crypt fiend
elseif GetUnitTypeId(building)=='utod' then //temple
call TrainSystem.register(building, 4)
call TrainSystem.addTrain(building,'unec') //necromancer
elseif GetUnitTypeId(building)=='uslh' then //slaughter
call TrainSystem.register(building, 3)
call TrainSystem.addTrain(building,'uabo') //abomination
call TrainSystem.addTrain(building,'uabo') //abomination
call TrainSystem.addTrain(building,'umtw') //meat wagon
endif
set building = null
endmethod
static method trainEnd takes nothing returns nothing
local unit trained = GetTrainedUnit()
if GetTriggerPlayer()==UNDEAD_FORCE and filterFollower(GetUnitTypeId(trained)) then
call GroupAddUnit(uGrp,trained)
if countU>countEx then
set countEx = countEx + 1
call UD.addUnit(trained, xC, yC)
endif
endif
set trained = null
endmethod
//increasing the undead force attacking player 1 everytime the captain dies
static method deathEvent takes nothing returns nothing
local unit first
if GetTriggerUnit()==Captain then
if countU < MaxUnit then
set countIncrease = countIncrease + 1
if countIncrease==2 then
set countIncrease = 0
set countU = countU + 2
endif
endif
call FormCaptain()
set countEx = 0
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_START("UNDEAD_FORCE")
if UnitAlive(first) and filterFollower(GetUnitTypeId(first)) and countU>countEx then
call UD.addUnit(first, xC, yC)
set countEx = countEx+1
endif
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_END()
endif
call GroupRemoveUnit(uGrp,GetTriggerUnit())
endmethod
static method start takes integer difficulty returns nothing
local integer initAtk = 0
local unit cap
local unit first
local unit ghoul
if ENABLE_UNDEAD_AI then
call SetPlayerState(UNDEAD_FORCE,PLAYER_STATE_RESOURCE_GOLD,1000000)
call SetPlayerState(UNDEAD_FORCE,PLAYER_STATE_RESOURCE_LUMBER,1000000)
//h003 is a dummy captain
//udg_DifficultyS = 3,6
if difficulty==3 then
set countU = 6 //normal
set MaxUnit = 15
call StartCampaignAI( UNDEAD_FORCE, "war3mapImported\\Undead_Normal.ai" )
else
set countU = 10 //hard
set MaxUnit = 23
call StartCampaignAI( UNDEAD_FORCE, "war3mapImported\\Undead_Hard.ai" )
endif
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.construct,UNDEAD_FORCE)
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_TRAIN_FINISH, function thistype.trainEnd,UNDEAD_FORCE)
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_DEATH, function thistype.deathEvent,UNDEAD_FORCE)
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_START("UNDEAD_FORCE")
if GetOwningPlayer(first)==UNDEAD_FORCE then
if GetUnitTypeId(first)=='usep' then //crypt
call TrainSystem.register(first, 3)
call TrainSystem.addTrain(first,'ugho') //ghoul
call TrainSystem.addTrain(first,'ucry') //crypt fiend
elseif GetUnitTypeId(first)=='utod' then //temple
call TrainSystem.register(first, 4)
call TrainSystem.addTrain(first,'unec') //necromancer
elseif GetUnitTypeId(first)=='uslh' then //slaughter
call TrainSystem.register(first, 3)
call TrainSystem.addTrain(first,'uabo') //abomination
call TrainSystem.addTrain(first,'uabo') //abomination
call TrainSystem.addTrain(first,'umtw') //meat wagon
endif
endif
//! runtextmacro GROUP_ENUM_PLAYER_UNIT_END()
//Initial attack
set countEx = countU
call FormCaptain()
loop
set initAtk = initAtk + 1
set ghoul = CreateUnit(UNDEAD_FORCE,'ugho',xC,yC,0)
call UD.addUnit(ghoul, xC, yC)
set ghoul = null
exitwhen initAtk==countU
endloop
endif
endmethod
endstruct
endscope
//TESH.scrollpos=7
//TESH.alwaysfold=0
/*
This will start when Uther or Jaina enters the village or after 1 minute
*/
scope MalGanisForce
globals
private constant real xR = 2850
private constant real yR = 250
private unit TU = null
endglobals
struct MalGanisForce //Player 10 (Player 11 in GUI)
static unit malganis
static timer tim = CreateTimer()
static timerdialog tdia = null
static real reviveDelay
static group G = CreateGroup()
static sound malganisReviveSound
static integer count = 0
static real teleport = 0
private static method FilterU takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_STRUCTURE) and u!=malganis
endmethod
private static method BuildingEnemy takes nothing returns nothing
call TempTimerEnd()
call SetPlayerAlliance(MALGANIS_PLAYER,Player(6),ALLIANCE_PASSIVE,false)
endmethod
private static method MalGanisConverting takes nothing returns nothing
local unit target = GetClosestUnitInRange(GetUnitX(malganis),GetUnitY(malganis),1000,Filter(function PickVillager))
if target!=null then
call IssueTargetOrderById(malganis,OID_darkconversion,target)
set target = null
else
if udg_MalGanisBuildingEnemy then
set udg_MalGanisBuildingEnemy = false //see BuildingDies
if udg_DifficultyS==3 then
call TempTimerStart(15, function thistype.BuildingEnemy)
else
call SetPlayerAlliance(MALGANIS_PLAYER,Player(6),ALLIANCE_PASSIVE,false)
endif
endif
endif
endmethod
//Malganis and his forces teleports to attack base when he's revived
static method teleportOn takes nothing returns nothing
local unit first
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) and FilterU(first) and SamePlayer(first, udg_MalGanis) then
call DestroyEffect(AddSpecialEffect(TELEPORT_SFX, GetUnitX(first), GetUnitY(first)))
call SetUnitPosition(first,2850,250)
call GroupRemoveUnit(TeleportGroup,first)
endif
//! runtextmacro GROUP_ENUM_RECT_END()
call SetUnitPosition(malganis,2850,250)
call DestroyEffect(AddSpecialEffect(TELEPORT_SFX, 2850, 250))
set MalganisTeleport = true
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endmethod
static method malRevives takes nothing returns nothing
if udg_CheckLast then
call TimerDialogDisplay(tdia,false)
call PauseTimer(tim)
call ReviveHero(malganis, -4255, -6800, true)
call StartSound(malganisReviveSound)
call TimerStart(CreateTimer(), 10, false, function thistype.teleportOn)
endif
endmethod
static method malDies takes nothing returns nothing
if udg_CheckLast then
if GetTriggerUnit()==malganis then
call TimerStart(tim,reviveDelay,true,function thistype.malRevives)
set tdia = CreateTimerDialog(tim)
call TimerDialogSetTitle(tdia, "MalGanis Revives In")
call TimerDialogDisplay(tdia,true)
endif
endif
endmethod
static method endTrain takes nothing returns nothing
call mal.addUnit(GetTrainedUnit())
call GroupAddUnit(TeleportGroup,GetTrainedUnit())
endmethod
//difficulty 3 or 6, this starts after "First Building Dies" trigger
static method start takes integer difficulty returns nothing
local unit first
if ENABLE_MALGANIS_AI then
set malganis = udg_MalGanis
set malganisReviveSound = gg_snd_H06MalGanis41
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_DEATH,function thistype.malDies,MALGANIS_PLAYER)
call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_TRAIN_FINISH,function thistype.endTrain,MALGANIS_PLAYER)
call TimerStart(CreateTimer(),1.0,true,function thistype.MalGanisConverting)
if difficulty==3 then
set reviveDelay = MALGANIS_REVIVE_DELAY //60
set mal = mal.create(malganis, false, 4, 0)
call TrainSystem.register(udg_Mal_Crypt, 6)
call TrainSystem.register(udg_Mal_Sh, 4)
elseif difficulty==6 then
set reviveDelay = MALGANIS_REVIVE_DELAY/2 //30
set mal = mal.create(malganis, false, 7, 0)
call TrainSystem.register(udg_Mal_Crypt, 10)
call TrainSystem.register(udg_Mal_Sh, 6)
endif
//udg_Mal_Crypt, udg_Mal_Sh
call TrainSystem.addTrain(udg_Mal_Crypt,'ugho')
call TrainSystem.addTrain(udg_Mal_Sh,'uabo')
call mal.setHeroTargets(TARGET_BUILDING)
call mal.setBase(xR, yR)
//! runtextmacro GROUP_ENUM_RECT_START()
if UnitAlive(first) and FilterU(first) and not IsUnitInGroup(first,G) and SamePlayer(first, udg_MalGanis) then
call GroupAddUnit(G,first)
call mal.addUnit(first)
endif
//! runtextmacro GROUP_ENUM_RECT_END()
endif
endmethod
endstruct
endscope
//TESH.scrollpos=109
//TESH.alwaysfold=0
scope ZombieStrikeForce initializer init
globals
private constant string DISSIPATE = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
private unit Zombie = null
//Normal Zombies
private group N_Zombies = CreateGroup()
private integer N_ZombieCount = 6 //used by zombie force (player 8)
private integer N_Count = 0
//Level 5 Zombies
private group SuperZombies = CreateGroup()
private integer SuperZombieCount = 0
private integer MaxZombiesL1 = 12
endglobals
//Converting villagers, MalGanis Scores
struct Conv
unit u
real dur
//StrikeForceEx
static method strikeForceEx takes nothing returns nothing
call ZC.addUnit(GetEnumUnit(), 6700, -820)
call GroupRemoveUnit(N_Zombies, GetEnumUnit())
endmethod
//EngageSystem
static method engageSystem takes nothing returns nothing
local unit u = GetEnumUnit()
local integer i = GetRandomInt(0,1)
call EngageSystem.SetEngageUnit(u, false)
if i==0 then
call EngageSystem.SetUnitLockTargets(u, 1, 40)
else
call EngageSystem.SetUnitLockTargets(u, 4, 40)
endif
call EngageSystem.SetReturnPoints(u, GetUnitX(u), GetUnitY(u))
call GroupRemoveUnit(SuperZombies, u)
set u = null
endmethod
private method destroy takes nothing returns nothing
local integer random
if UnitAlive(.u) then
set CONV = CONV + 1
call mb.setText(3, 1, I2S(CONV))
if CONV >= REQUIRES then
set udg_WinMalGanisCinematic = 1
endif
call SetUnitOwner(.u,ZOMBIE_FORCE,true) //converted to Undead Force
call SetUnitInvulnerable(.u,false)
call RemoveGuardPosition(.u)
if udg_DifficultyS==6 then
set random = GetRandomInt(0, 3)
if random==2 then
//transforms into a level 5 zombie
set .u = ReplaceUnitBJ(.u, 'nC03', bj_UNIT_STATE_METHOD_MAXIMUM)
endif
endif
//Picks between StrikeForceEx or EngageSystem
set random = GetRandomInt(0, 3)
if random==2 then
set random = GetRandomInt(0, 10)
call GroupAddUnit(SuperZombies, .u)
if random==3 then
call SetUnitOwner(.u, MALGANIS_PLAYER, true)
call mal.addUnit(.u)
call GroupRemoveUnit(SuperZombies, .u)
else
set SuperZombieCount = SuperZombieCount + 1
if SuperZombieCount==5 then
set SuperZombieCount = 0
call ForGroup(SuperZombies, function thistype.engageSystem)
endif
endif
else //StrikeForceEx
call GroupAddUnit(N_Zombies, .u)
set N_Count = N_Count + 1
if N_Count==N_ZombieCount then
set N_Count = 0
if MaxZombiesL1 > N_ZombieCount then
set N_ZombieCount = N_ZombieCount + 1
endif
call ForGroup(N_Zombies, function thistype.strikeForceEx)
endif
endif
endif
set .u = null
call .deallocate()
endmethod
implement DTloop
if .dur > 0 then
if .dur==3 then
call DestroyEffect(AddSpecialEffect(DISSIPATE, GetUnitX(.u), GetUnitY(.u)))
endif
set .dur = .dur - 1.0
implement DTend
static method MagGanisCast takes nothing returns nothing
local thistype this = create(1.0)
set .u = GetSpellTargetUnit()
call SetUnitInvulnerable(.u,true)
set .dur = 6
endmethod
endstruct
private function FilterSum takes unit u returns boolean
return GetUnitTypeId(u)=='nC03' or GetUnitTypeId(u)=='nzom'
endfunction
private function Sum takes nothing returns nothing
local unit u = GetTriggerUnit()
if not FilterSum(u) then
call SE.summoned(GetSummoningUnit(), u)
endif
set u = null
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
set Zombie = CreateUnit(ZOMBIE_FORCE, 'hpea', 6700, -820, 0)
call RegisterSpellEffectEvent(DARK_CONV_MAL_SPELL_ID, function Conv.MagGanisCast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function Sum)
//attacks Player 1
set ZC = ZC.create(Zombie, 5, false)
set ZC.xReturn = 6700
set ZC.yReturn = -820
set ZC.targets = 1
set t = null
endfunction
endscope
//TESH.scrollpos=238
//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=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=36
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//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=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=5
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.0.0.1
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, Table
globals
private Table tb
endglobals
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
if not tb.handle.has(abil) then
set tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(tb.trigger[abil], Filter(onCast))
endfunction
private module M
static method onCast takes nothing returns boolean
return TriggerEvaluate(tb.trigger[GetSpellAbilityId()])
endmethod
private static method onInit takes nothing returns nothing
set tb = Table.create()
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
private struct S extends array
implement M
endstruct
endlibrary
//TESH.scrollpos=11
//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=0
//TESH.alwaysfold=0
/*
Made by The_Reborn_Devil
*/
library TimedNoise
globals
private constant timer t = CreateTimer()
endglobals
private function Stop takes nothing returns nothing
call CameraSetTargetNoise(0, 0)
call CameraSetSourceNoise(0, 0)
endfunction
function ApplyNoise takes real m, real v, real d returns nothing
call PauseTimer(t)
call CameraSetTargetNoise(m, v)
call CameraSetSourceNoise(m, v)
call TimerStart(t, d, false, function Stop)
endfunction
endlibrary
//TESH.scrollpos=39
//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
//! zinc
/*
* PowerupSentinel
* ------------
* Placing this library in your map will automatically fix all rune/tome
* memory leaks in your map.
*
* Powerup items don't get removed automatically by the game, they instead
* just leave a small item in the map, this caused memory leaks but - worse -
* it also makes areas of your map where a lot of tomes have been used lag a lot.
*
*/
library PowerupSentinel
{
function onInit(){
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DROP_ITEM);
TriggerAddCondition(t, function()->boolean {
if (GetWidgetLife(GetManipulatedItem())==0) {
RemoveItem(GetManipulatedItem());
}
return false;
});
}
}
//! endzinc
//TESH.scrollpos=59
//TESH.alwaysfold=0
/**************************************
*
* IsUnitChanneling
* v2.1.0.0
* By Magtheridon96
*
* - Tells whether a unit is channeling or not.
*
* Requirements:
* -------------
*
* - RegisterPlayerUnitEvent by Magtheridon96
* - hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
* Optional:
* ---------
*
* - 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/
*
* API:
* ----
*
* - function IsUnitChanneling takes unit whichUnit returns boolean
* - Tells whether a unit is channeling or not.
* (This function is only available if you have UnitIndexer)
*
* - function IsUnitChannelingById takes integer unitIndex returns boolean
* - Tells whether a unti is channeling or not given the unit index.
*
**************************************/
library IsUnitChanneling requires optional UnitIndexer, optional Table, RegisterPlayerUnitEvent
private struct OnChannel extends array
static if LIBRARY_UnitIndexer then
static boolean array channeling
else
static if LIBRARY_Table then
static key k
static Table channeling = k
else
static hashtable hash = InitHashtable()
endif
endif
private static method onEvent takes nothing returns nothing
static if LIBRARY_UnitIndexer then
local integer id = GetUnitUserData(GetTriggerUnit())
set channeling[id] = not channeling[id]
else
static if LIBRARY_Table then
local integer id = GetHandleId(GetTriggerUnit())
set channeling.boolean[id] = not channeling.boolean[id]
else
local integer id = GetHandleId(GetTriggerUnit())
call SaveBoolean(hash, 0, id, not LoadBoolean(hash, 0, id))
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onEvent)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onEvent)
endmethod
endstruct
static if LIBRARY_UnitIndexer then
function IsUnitChannelingById takes integer id returns boolean
return OnChannel.channeling[id]
endfunction
endif
function IsUnitChanneling takes unit u returns boolean
static if LIBRARY_UnitIndexer then
return OnChannel.channeling[GetUnitUserData(u)]
else
static if LIBRARY_Table then
return OnChannel.channeling.boolean[GetHandleId(u)]
else
return LoadBoolean(OnChannel.hash, 0, GetHandleId(u))
endif
endif
endfunction
endlibrary
//TESH.scrollpos=23
//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 = 'h000'
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=0
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.7
// --------
// 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 operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
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
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set fxpath=null
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
if(level != 0) then
call UnitAddAbility(this.dummy, abilityid)
endif
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)
set this.z = z
set zangle = za
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
//I'd like to see this happen...
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
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=129
//TESH.alwaysfold=0
/*
=====Train System v1.3
=====By Mckill2009
This is a system in which your building will automatically train a unit, this is good for
AI's or computer players...
REQUIRED NATIVE ABOVE MAP SCRIPT:
native UnitAlive takes unit u returns boolean
API:
static method register takes unit source, integer limit returns nothing
- first thing to do
- registers your source unit that will automatically train units
- sets the initial limit to train units
static method addTrain takes unit source, integer trainedID returns nothing
- second thing to do
- source - the unit that will be training
- trainedID - the unittypeID of the unit being trained
- the more trainedID you register, the higher the probability it will be trained in the list
static method setLimit takes unit source, integer limit returns nothing
- sets/adds the limit of your units being trained
- the limit must be greater than the limit from initial setup (from register)
static method removeTrain takes unit source returns nothing
- removes the source from the list or stops the auto train
static method clearAll takes nothing returns nothing
- removes all units from the list
- destroys the ID's and starts a fresh one
CREDITS:
- Table by Bribe
- RegisterPlayerUnitEvent by Magtheridon96
OTHER CREDITS:
Magtheridon96 (for suggestions)
Troll-Brain (for tests)
*/
library TrainSystem uses Table, RegisterPlayerUnitEvent
struct TrainSystem
unit source
static constant integer maxInstance = 8190
static constant integer childID = 1000
static integer instance = 0
static integer array instanceAr
static timer t = CreateTimer()
static real interval = 1.0
static Table lim //limit of units trained by a particular structure
static Table chk //checks the counter of the trained unit
static Table tra //trained unit reduce counter
static TableArray random
private static method looper takes nothing returns nothing
local thistype this
local integer looper = 0
local integer i
local integer ID
local integer ran
local integer array ar
loop
set looper = looper + 1
set this = instanceAr[looper]
set ID = GetHandleId(.source)
if UnitAlive(.source) and chk.has(ID) then
if (lim[ID]) > (chk[ID]) and chk.boolean[ID] then
set i = 0
loop
set i = i + 1
set ar[i] = random[i][ID]
exitwhen i==random[childID][ID] //maxINDEX
endloop
set ran = GetRandomInt(1,random[childID][ID])
if IssueImmediateOrderById(.source, ar[ran]) then
set chk.boolean[ID] = false
endif
endif
else
call chk.remove(ID)
set .source = null
call .destroy()
set instanceAr[looper] = instanceAr[instance]
set instanceAr[instance] = this
set looper = looper - 1
set instance = instance - 1
if instance==0 then
call PauseTimer(t)
endif
endif
exitwhen looper==instance
endloop
endmethod
private static method deathEvent takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit source //the source that trains the dying unit
local integer ID = GetHandleId(u)
local integer sourceID
if u==tra.unit[ID] then
set source = chk.unit[ID]
set sourceID = GetHandleId(source)
set chk[sourceID] = chk[sourceID] - 1
set chk.boolean[sourceID] = true
call tra.remove(ID)
set source = null
endif
set u = null
endmethod
private static method trainCancel takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit trained = GetTrainedUnit()
local integer ID = GetHandleId(u)
if not chk.boolean[ID] then
set chk.boolean[ID] = true
set chk[ID] = chk[ID] - 1
endif
set u = null
set trained = null
endmethod
private static method trainEnd takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit trained = GetTrainedUnit()
local integer ID = GetHandleId(u)
local integer trID = GetHandleId(trained)
if chk.has(ID) then
set chk[ID] = chk[ID] + 1
set chk.boolean[ID] = true
set chk.unit[trID] = u
set tra.unit[trID] = trained
endif
set u = null
set trained = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_TRAIN_CANCEL, function thistype.trainCancel)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_TRAIN_FINISH, function thistype.trainEnd)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.deathEvent)
set lim = Table.create()
set chk = Table.create()
set tra = Table.create()
set random = TableArray[0x2000]
endmethod
//API's==============================
static method register takes unit source, integer limit returns nothing
local integer ID = GetHandleId(source)
local thistype this
if chk.has(ID) then
debug call BJDebugMsg("register ERROR: "+GetUnitName(source)+" is already registered!")
else
if instance==maxInstance then
debug call BJDebugMsg("register ERROR: Unable to allocate more than "+I2S(maxInstance)+" instance.")
call DestroyTimer(t)
else
set this = allocate()
set .source = source
set lim[ID] = limit
set chk[ID] = 0 //checks the limit
set chk.boolean[ID] = true //cheks if registered or not
if instance==0 then
set t = CreateTimer()
call TimerStart(t,interval,true,function thistype.looper)
endif
set instance = instance + 1
set instanceAr[instance] = this
endif
endif
endmethod
static method addTrain takes unit source, integer trainedID returns nothing
local integer ID = GetHandleId(source)
local integer index
if chk.has(ID) then
set random[childID][ID] = random[childID][ID]+1 //maxINDEX
set index = random[childID][ID]
set random[index][ID] = trainedID //so that all trainedID will be saved in a unique index
else
debug call BJDebugMsg("addTrain ERROR: Please register "+GetUnitName(source)+" first.")
endif
endmethod
static method setLimit takes unit source, integer limit returns nothing
local integer ID = GetHandleId(source)
if chk.has(ID) then
if (limit) > (lim[ID]) then
set lim[ID] = limit
else
debug call BJDebugMsg("setLimit ERROR: Limit must be greater than registered value!")
endif
else
debug call BJDebugMsg("addLimit ERROR: "+GetUnitName(source)+" is not registered!")
endif
endmethod
static method removeTrain takes unit source returns nothing
local integer ID = GetHandleId(source)
if chk.has(ID) then
call lim.remove(ID)
call chk.remove(ID)
set random[childID][ID] = 0
else
debug call BJDebugMsg("removeTrain ERROR: "+GetUnitName(source)+" is not registered!")
endif
endmethod
static method clear takes nothing returns nothing
call PauseTimer(t)
call DestroyTimer(t)
call random.destroy()
call lim.destroy()
call chk.destroy()
call tra.destroy()
set instance = 0
set lim = Table.create()
set chk = Table.create()
set tra = Table.create()
set random = TableArray[0x2000]
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
=====Strike Force v1.5
=====Made By Mckill2009
REQUIRED LIBRARIES and CREDITS:
- Table by Bribe
- GetClosestWidget by Spinnaker
- IsUnitChanneling by Magtheridon96
- RegisterPlayerUnitEvent by Magtheridon96
- IsUnitEngageable by Mckill2009
HOW TO INSTALL:
- Copy and Paste the above mentioned libraries and the StrikeForce trigger to your map
- Follow and read carefully all the instructions in the 'IsUnitEngageable' library
- Follow the Demo instructions
- Done!
FEATURES:
- Waits for lost units (optional) and depends on how much time it waits (check HERO_WAITS)
- Returns to base when no more targets around
- Attacks closest target when engaging on the main target (optional)
- The hero and its army will not engage if attack size is not complete (StrikeForce)
- The army will not engage before the attack delay (StrikeForceEx)
- If hero is dead, all units will regroup at base
- Retreat to base is optional
NOTE:
- It is recommended to set your AI to a computer player so that they can cast spells independently
- Never Run melee AI scripts nor Start a custom Melee/Capaign AI scripts coz that will conflict with your hero AI
- Read the IsUnitEngageable library very carefully, else this will not work
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API:
StrikeForce
static method create takes unit hero, boolean retreat, integer maxAttackSize, real attackDelay returns thistype
- Assigns a hero captain, he will lead the attack of the army
- Set the retreat to true so that if hero dies all units will return to base (if base is set)
- maxAttackSize is the size quantity of the group that's engaging
- attackDelay is the delay of the next attack
method setHeroTargets takes integer targetID returns nothing
- 16 Total Players (0-15), see global constants for refference
- If you want the AI to target only player 1 then >>> call structInstanceName.setHeroTargets(0)
- If you want the AI to target only heroes then >>> call structInstanceName.setHeroTargets(ATTACK_ENEMY_HEROES_ONLY)
method addUnit takes unit follower returns nothing
- Adds a unit to the group which increases the maxAttackSize
method setBase takes real x, real y returns nothing
- Sets the base which the AI will retreat if the hero dies or when retreat is true
method setMaxAttackSize takes integer maxatksize returns nothing
- Changes/Sets the maxAttackSize of the group
method setAttackDelay takes real attackDelay returns nothing
- Changes/Sets the delay of the next attack
method remove takes nothing returns nothing
- Removes all units in the group that's engaging
StrikeForceEx
static method create takes unit captain, real maxDelay, boolean killCaptainWhenGroupIsEnmpty returns thistype
- Assigns a dummy captain, he will lead the attack but he cant attack
- It is advised that the dummy walks and doesnt have a locust ability
- The dummy can be killed when group is empty
static method createEx takes player p, real xCaptain, real yCaptain, real maxDelay returns thistype
- Creates a dummy captain automatically at XY coordinate in the form of a peasant ('hpea')
- Take note that this captain will die automatically once the group is empty
method addUnit takes unit follower, real xReturn, real yReturn returns nothing
- Adds a unit to the group and sets the return XY when no more targets around
method operator xReturn= takes real xReturn returns nothing
method operator yReturn= takes real yReturn returns nothing
- Sets the XY of the units in the group when no more targets around
method operator delayReset= takes real delay returns nothing
- Change/Sets the delay of the next attack
method operator targets= takes integer targetID returns nothing
- 16 Total Players (0-15), see global constants for refference
- If you want the AI to target only player 2 then >>> set structInstanceName.targets = 2
- If you want the AI to target all enemies then >>> set structInstanceName.targets = ATTACK_ALL_ENEMIES
method operator off= takes boolean killCaptain returns nothing
- Removes all units from the attackgroup and kills the dummy (optional)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*/
library StrikeForce uses Table, GetClosestWidget, IsUnitChanneling, IsUnitEngageable
globals
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Used by StrikeForce and StrikeForceEx
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//ENABLE_HERO_WAITS or DUMMY_CAPTAIN_WAITS must be true
private constant real HERO_WAITS = 15
//Engages closest target, ENABLE_CLOSEST_TARGET must be true
private constant real CLOSEST_TARGET = 600
//Distance offset of following unit and captain
private constant real OFFSET_FROM_CAPTAIN = 250
//Distance of attacking unit and target, if less than indicated, it will not stop
private constant real IS_TARGET_FAR = 490000 //squareroot 700
//Unit will keep on retreating (or go back to captain) until less than indicated
private constant real RETURN_OFFSET = 360000 //squareroot 600
//Allows attacker to target closest unit in CLOSEST_TARGET range
private constant boolean ENABLE_CLOSEST_TARGET = false
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Used by StrikeForce only
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
private constant boolean ENABLE_HERO_WAITS = true
//Used for retreating
private constant real LIFE = 0.4 //40%
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//Used by StrikeForceEx only
//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
private constant boolean DUMMY_CAPTAIN_WAITS = true
endglobals
//=====NON-CONFIGURABLES: NEVER TOUCH THIS BLOCK!=====
globals
private constant real INTERVAL = 1.0 //recommended
private constant real MOVE_DELAY = 3.0 //recommended
private constant integer ATTACK = 851983
private constant integer MOVE = 851986
private constant integer SMART = 851971
private constant integer STOP = 851972
constant integer ATTACK_ALL_ENEMIES = 16
constant integer ATTACK_ENEMY_STRUCTURES_ONLY = 17
constant integer ATTACK_ENEMY_FLYIER_ONLY = 18
constant integer ATTACK_ENEMY_HEROES_ONLY = 19
private real WAIT_ADD = 0 //HERO_WAITS + 4
private unit TempUnit = null
private real xHero
private real yHero
private real xCaptain
private real yCaptain
private real xTarget
private real yTarget
endglobals
//===Simple group recycling
globals
private group array Grp
private integer indexGrp = 0
endglobals
private function NGroup takes nothing returns group
set indexGrp = indexGrp + 1
if Grp[indexGrp]==null then
set Grp[indexGrp] = CreateGroup()
endif
return Grp[indexGrp]
endfunction
private function RGroup takes group g returns nothing
set Grp[indexGrp] = g
call GroupClear(g)
set indexGrp = indexGrp - 1
endfunction
//==========
//! textmacro EngageS takes tID, heroU
private static method engageSpecificPlayerUnit takes nothing returns boolean
local thistype this = DATA
local integer i = 0
set TempUnit = GetFilterUnit()
if UnitAlive(TempUnit) and IsUnitEnemy($heroU$,GetOwningPlayer(TempUnit)) and IsUnitEngageable(TempUnit) then
if $tID$ < 16 then
loop
if $tID$==i then
return GetOwningPlayer(TempUnit)==Player(i)
endif
exitwhen i==$tID$
set i = i+1
endloop
elseif $tID$==16 then
return true
elseif $tID$==17 then
return IsUnitType(TempUnit,UNIT_TYPE_STRUCTURE)
elseif $tID$==18 then
return IsUnitType(TempUnit,UNIT_TYPE_FLYING)
elseif $tID$==19 then
return IsUnitType(TempUnit,UNIT_TYPE_HERO)
endif
endif
return false
endmethod
//! endtextmacro
//! textmacro Formation takes x, y
private static method groupFormation takes nothing returns nothing
call MoveToCaptain(GetEnumUnit(), $x$, $y$, OFFSET_FROM_CAPTAIN)
endmethod
//! endtextmacro
//! textmacro Targets takes targetID, tar, unitU
if $tar$ > -1 and $tar$ < 16 then
if GetOwningPlayer($unitU$)==Player($tar$) or not IsUnitEnemy($unitU$, Player($tar$)) then
debug call BJDebugMsg("ERROR: can't target this Player!")
else
set $targetID$ = $tar$
endif
elseif $tar$ > 15 and $tar$ < 20 then
set $targetID$ = $tar$
else
debug call BJDebugMsg("ERROR: Please input only from 0 to 19!")
endif
//! endtextmacro
private function AttakMoveOrders takes integer order returns boolean
return (order==ATTACK or order==MOVE)
endfunction
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
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 IsTargetFar takes unit u, unit u2 returns boolean
return (GetDistance(GetUnitX(u), GetUnitY(u), GetUnitX(u2), GetUnitY(u2))) > (IS_TARGET_FAR) //this is 700AOE
endfunction
private function AttackNow takes unit u, unit target, real x, real y returns nothing
if IsUnitType(u, UNIT_TYPE_SLEEPING) then
call IssueTargetOrderById(u, ATTACK, target)
else
call IssuePointOrderById(u, ATTACK, x, y)
endif
endfunction
private function EngageCloseTargets takes nothing returns boolean
return UnitAlive(GetFilterUnit()) and IsUnitEnemy(TempUnit,GetOwningPlayer(GetFilterUnit())) and IsUnitEngageable(GetFilterUnit())
endfunction
private function RandomAngle takes nothing returns real
return GetRandomReal(0,6.28)
endfunction
private function MoveToCaptain takes unit u, real x, real y, real offset returns nothing
call IssuePointOrderById(u, MOVE, x+offset*Cos(RandomAngle()), y+offset*Sin(RandomAngle()))
endfunction
//=====END OF NON-CONFIGURABLE BLOCK=====
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
struct StrikeForce
private unit hero
private unit target
private unit closeTarget
private real baseX
private real baseY
private real moveDelay
private real minAttackDelay //starts with 0, max is attackDelay
private real maxAttackDelay
private real heroWaits
private integer minAtkSize
private integer maxAtkSize
private integer handleID
private integer targetPlayerID
private boolean retreat
private boolean onSF
private group grp
private static Table chk //used only if hero is registered already
private static integer DATA
private static timer t
private static integer instance = 0
private static integer array insAR
//INITIALIZER: StrikeForce===================
private static method onInit takes nothing returns nothing
set chk = Table.create()
set WAIT_ADD = HERO_WAITS + 4
endmethod
//===========================================
//! runtextmacro Formation("xHero", "yHero")
//! runtextmacro EngageS(".targetPlayerID",".hero")
private method destroy takes nothing returns nothing
call RGroup(.grp)
set .closeTarget = null
set .hero = null
set .target = null
set .grp = null
call .deallocate()
endmethod
private method returnToBase takes unit u returns nothing
if (GetDistance(GetUnitX(u), GetUnitY(u), .baseX, .baseY)) > (RETURN_OFFSET) then
call IssuePointOrderById(u, MOVE, .baseX, .baseY)
endif
endmethod
private method retreatOn takes unit u returns nothing
if AttakMoveOrders(GetUnitCurrentOrder(u)) then
if GetWidgetLife(.hero) < ((GetUnitState(u, UNIT_STATE_MAX_LIFE)*LIFE)) then
call .returnToBase(u)
endif
endif
endmethod
private static method grpReturn takes nothing returns nothing
local thistype this = DATA
local unit u = GetEnumUnit()
if UnitAlive(u) then
call .returnToBase(u)
else
call GroupRemoveUnit(.grp,u)
endif
set u = null
endmethod
private static method grpAttack takes nothing returns nothing
local thistype this = DATA //DATA taken from looper
local unit u = GetEnumUnit()
local real angle
local real distHeroFollower
local real distTargetFollower
local real xFollower
local real yFollower
if UnitAlive(u) then
if not IsUnitChanneling(u) then
set xFollower = GetUnitX(u)
set yFollower = GetUnitY(u)
set distTargetFollower = GetDistance(xTarget, yTarget, xFollower, yFollower)
set distHeroFollower = GetDistance(xHero, yHero, xFollower, yFollower)
if .retreat then
call .retreatOn(u)
endif
if distHeroFollower > RETURN_OFFSET then
call MoveToCaptain(u, xHero, yHero, OFFSET_FROM_CAPTAIN)
elseif AttakMoveOrders(GetUnitCurrentOrder(.hero)) then
call AttackNow(u, .target, xTarget, yTarget)
endif
endif
else
call GroupRemoveUnit(.grp, u)
endif
set u = null
endmethod
//this runs every delay of 3.0
private method heroEngaging takes nothing returns nothing
set xTarget = GetUnitX(.target)
set yTarget = GetUnitY(.target)
//===Hero retreats, this should be true if hero wants to retreat
if .retreat then
call .retreatOn(.hero)
endif
if not IsUnitChanneling(.hero) then
static if ENABLE_HERO_WAITS then
if .heroWaits < HERO_WAITS then
call AttackNow(.hero, .target, xTarget, yTarget)
call ForGroup(.grp,function thistype.grpAttack)
endif
else
call AttackNow(.hero, .target, xTarget, yTarget)
call ForGroup(.grp,function thistype.grpAttack)
endif
endif
endmethod
private static method looper takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = insAR[i]
if .onSF then
if .minAtkSize >= .maxAtkSize then
set .minAttackDelay = .minAttackDelay + INTERVAL
if .minAttackDelay > .maxAttackDelay then
set DATA = this
if UnitAlive(.hero) then
set xHero = GetUnitX(.hero)
set yHero = GetUnitY(.hero)
if UnitAlive(.target) and IsUnitEngageable(.target) then
static if ENABLE_HERO_WAITS then
set .heroWaits = .heroWaits + INTERVAL
if .heroWaits==HERO_WAITS then
if IsTargetFar(.hero, .target) then
call IssueImmediateOrderById(.hero, STOP)
call ForGroup(.grp, function thistype.groupFormation)
endif
elseif .heroWaits==WAIT_ADD then
set .heroWaits = 0 //resets the engage
endif
endif
set .moveDelay = .moveDelay + INTERVAL
if .moveDelay > MOVE_DELAY then
set .moveDelay = 0
//movement of hero
call .heroEngaging()
endif
else
//if target is dead, hero will return to base
set .target = GetClosestUnit(xHero, yHero, Filter(function thistype.engageSpecificPlayerUnit))
set .minAttackDelay = 0 //resets the delay when engaging the next target
set .heroWaits = 0
if .target==null then
call .returnToBase(.hero)
call ForGroup(.grp, function thistype.grpReturn)
else
call ForGroup(.grp, function thistype.groupFormation)
call .heroEngaging()
endif
endif
else
//Hero is dead
call ForGroup(.grp,function thistype.grpReturn)
endif
endif
endif
else
call .destroy()
set insAR[i] = insAR[instance]
set insAR[instance] = this
set i = i - 1
set instance = instance - 1
if instance==0 then
call PauseTimer(t)
call DestroyTimer(t)
endif
endif
exitwhen i==instance
endloop
endmethod
//StrikeForce API:
static method create takes unit hero, boolean retreat, integer maxAttackSize, real attackDelay returns thistype
local thistype this
if chk.has(GetHandleId(hero)) then
debug call BJDebugMsg("StrikeGroup.create ERROR: "+GetUnitName(hero)+" is already registered!")
else
if IsUnitType(hero,UNIT_TYPE_HERO) then
set chk[GetHandleId(hero)] = 0
set this = allocate()
set .grp = NGroup()
set .hero = hero
set .handleID = GetHandleId(hero)
set .target = null
set .closeTarget = null
set .moveDelay = 0
set .minAttackDelay = 0
set .maxAttackDelay = attackDelay
set .minAtkSize = 0
set .maxAtkSize = maxAttackSize
set .retreat = retreat
set .onSF = true
set .heroWaits = 0
if instance==0 then
set t = CreateTimer()
call TimerStart(t,INTERVAL,true,function thistype.looper)
endif
set instance = instance + 1
set insAR[instance] = this
call RemoveGuardPosition(hero)
else
debug call BJDebugMsg("StrikeGroup.create ERROR: Please register a Hero Type unit!")
endif
endif
return this
endmethod
method setHeroTargets takes integer targetID returns nothing
//! runtextmacro Targets(".targetPlayerID","targetID",".hero")
endmethod
method addUnit takes unit follower returns nothing
call GroupAddUnit(.grp,follower)
call RemoveGuardPosition(follower)
call MoveToCaptain(follower, GetUnitX(.hero), GetUnitY(.hero), OFFSET_FROM_CAPTAIN)
set .minAtkSize = .minAtkSize + 1
endmethod
method setBase takes real x, real y returns nothing
set .baseX = x
set .baseY = y
endmethod
method setMaxAttackSize takes integer maxatksize returns nothing
set .maxAtkSize = maxatksize
endmethod
method setAttackDelay takes real attackDelay returns nothing
set .maxAttackDelay = attackDelay
endmethod
method remove takes nothing returns nothing
set .onSF = false
call chk.remove(GetHandleId(.hero))
endmethod
endstruct
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
struct StrikeForceEx
private unit captain
private unit target
private unit closestTarget
private real maxDelay
private real minDelay
private real xBack
private real yBack
private real moveWait //use 3.0
private real dummyWaits
private integer targetPlayerID
private group atkGroup
private boolean on
private boolean killCaptain
private static TableArray tba
private static integer DATA
private static integer countUnit = 0
private static real distanceFromTarget //main target
//instance count
private static integer index = 0
private static integer array indexAR
private static timer t
//INITIALIZER: StrikeForceEx=================
private static method onInit takes nothing returns nothing
set tba = TableArray[0x2000]
endmethod
//===========================================
//! runtextmacro Formation("xCaptain", "yCaptain")
//! runtextmacro EngageS(".targetPlayerID",".captain")
private method destroy takes nothing returns nothing
if .killCaptain then
call KillUnit(.captain)
endif
call RGroup(.atkGroup)
set .captain = null
set .target = null
set .closestTarget = null
set .atkGroup = null
call .deallocate()
endmethod
//this is only used if the group is about to be destroyed
private static method grpReturn takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = DATA
local real xRet = tba[0].real[GetHandleId(u)]
local real yRet = tba[1].real[GetHandleId(u)]
if GetDistance(xRet, yRet, GetUnitX(u), GetUnitY(u)) > RETURN_OFFSET then
call IssuePointOrderById(u, MOVE, xRet, yRet)
endif
set u = null
endmethod
private static method isGroupActive takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = DATA
if UnitAlive(u) then
set countUnit = countUnit + 1
else
call GroupRemoveUnit(.atkGroup,u)
endif
set u = null
endmethod
//this is the movements of the follower, not the captain
private static method grpEngaging takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = DATA
local real xFol
local real yFol
local real distFromCaptain
if UnitAlive(u) then
if not IsUnitChanneling(u) then
set xFol = GetUnitX(u)
set yFol = GetUnitY(u)
set distFromCaptain = GetDistance(xCaptain, yCaptain, xFol, yFol)
if ENABLE_CLOSEST_TARGET then
if UnitAlive(.closestTarget) and IsUnitEngageable(.closestTarget) then
call AttackNow(u, .closestTarget, GetUnitX(.closestTarget), GetUnitY(.closestTarget))
endif
endif
if distanceFromTarget < 600000 then //600
call AttackNow(u, .target, GetUnitX(.target), GetUnitY(.target))
endif
if distFromCaptain > 800000 then //800
call MoveToCaptain(u, xCaptain, yCaptain, OFFSET_FROM_CAPTAIN)
endif
endif
endif
set u = null
endmethod
//runs every 3.0 seconds
private method captainMovements takes nothing returns nothing
//captain moves to the target point
local real xTarget = GetUnitX(.target)
local real yTarget = GetUnitY(.target)
static if ENABLE_CLOSEST_TARGET then
if UnitAlive(.closestTarget) and IsUnitEngageable(.closestTarget) then
call IssuePointOrderById(.captain, MOVE, GetUnitX(.closestTarget), GetUnitY(.closestTarget))
else
set TempUnit = .captain
set .closestTarget = GetClosestUnitInRange(xCaptain, yCaptain, CLOSEST_TARGET, Filter(function EngageCloseTargets))
endif
endif
set distanceFromTarget = GetDistance(xCaptain, yCaptain, xTarget, yTarget)
static if DUMMY_CAPTAIN_WAITS then
if .dummyWaits < HERO_WAITS then
call IssuePointOrderById(.captain, MOVE, xTarget, yTarget)
call ForGroup(.atkGroup, function thistype.grpEngaging)
endif
else
call IssuePointOrderById(.captain, MOVE, xTarget, yTarget)
call ForGroup(.atkGroup, function thistype.grpEngaging)
endif
endmethod
private static method looper takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = indexAR[i]
if .on and UnitAlive(.captain) then
set .minDelay = .minDelay + INTERVAL
if .minDelay > .maxDelay then
set DATA = this
set countUnit = 0
call ForGroup(.atkGroup, function thistype.isGroupActive)
if countUnit > 0 then //checks if the group is empty or not
set xCaptain = GetUnitX(.captain)
set yCaptain = GetUnitY(.captain)
if UnitAlive(.target) and IsUnitEngageable(.target) then
static if DUMMY_CAPTAIN_WAITS then
set .dummyWaits = .dummyWaits + INTERVAL
if .dummyWaits==HERO_WAITS then
if IsTargetFar(.captain, .target) then
call IssueImmediateOrderById(.captain, STOP)
call ForGroup(.atkGroup, function thistype.groupFormation)
endif
elseif .dummyWaits==WAIT_ADD then
set .dummyWaits = 0
endif
endif
set .moveWait = .moveWait + INTERVAL
if .moveWait > MOVE_DELAY then
set .moveWait = 0
call .captainMovements()
endif
else
set .minDelay = 0
set .dummyWaits = 0
set .target = GetClosestUnit(xCaptain, yCaptain, Filter(function thistype.engageSpecificPlayerUnit))
if .target==null then
call IssuePointOrderById(.captain, MOVE, .xBack, .yBack)
call ForGroup(.atkGroup, function thistype.grpReturn)
else
call ForGroup(.atkGroup, function thistype.groupFormation)
call .captainMovements()
endif
endif
else
if .killCaptain then
set .on = false
else
call SetUnitPosition(.captain, .xBack, .yBack)
endif
endif
endif
else
//this order returns all units in the group to it's base
set DATA = this
call ForGroup(.atkGroup, function thistype.grpReturn)
//deallocating instance
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)
call DestroyTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
//StrikeForceEx API:
static method create takes unit captain, real maxDelay, boolean killCaptainWhenGroupIsEnmpty returns thistype
local thistype this
if IsUnitType(captain, UNIT_TYPE_HERO) then
debug call BJDebugMsg("StrikeForceEx (create) ERROR: "+GetUnitName(captain)+" must be a non-hero!")
else
set this = allocate()
set .captain = captain
set .target = null
set .closestTarget = null
set .minDelay = 0
set .maxDelay = maxDelay
set .atkGroup = NGroup() //CreateGroup()
set .on = true
set .killCaptain = killCaptainWhenGroupIsEnmpty
set .moveWait = 0
set .targetPlayerID = 0
set .dummyWaits = 0
if index==0 then
set t = CreateTimer()
call TimerStart(t,INTERVAL,true,function thistype.looper)
endif
set index = index + 1
set indexAR[index] = this
call RemoveGuardPosition(captain)
call SetUnitInvulnerable(captain,true)
call ShowUnit(captain,false)
endif
return this
endmethod
static method createEx takes player p, real xCaptain, real yCaptain, real maxDelay returns thistype
return create(CreateUnit(p, 'hpea', xCaptain, yCaptain, 0), maxDelay, true)
endmethod
method addUnit takes unit follower, real xReturn, real yReturn returns nothing
set tba[0].real[GetHandleId(follower)] = xReturn
set tba[1].real[GetHandleId(follower)] = yReturn
call GroupAddUnit(.atkGroup, follower)
call RemoveGuardPosition(follower)
call MoveToCaptain(follower, GetUnitX(.captain), GetUnitY(.captain), OFFSET_FROM_CAPTAIN)
endmethod
method operator xReturn= takes real xReturn returns nothing
set .xBack = xReturn
endmethod
method operator yReturn= takes real yReturn returns nothing
set .yBack = yReturn
endmethod
method operator delayReset= takes real delay returns nothing
set .minDelay = 0
set .maxDelay = delay
endmethod
method operator targets= takes integer targetID returns nothing
//! runtextmacro Targets(".targetPlayerID","targetID",".captain")
endmethod
method operator off= takes boolean killCaptain returns nothing
set .killCaptain = killCaptain
set .on = false
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
===EscortSystem v1.0
===by Mckill2009
This system allows your AI units to follow a UNIT/HERO and retreats
API:
static method create takes unit hero, boolean retreat returns thistype
method operator addUnit= takes unit follower returns nothing
method operator retreatX= takes real xCoordinate returns nothing
method operator retreatY= takes real yCoordinate returns nothing
method operator lifeCheck= takes real lifePercentage returns nothing
method remove takes nothing returns nothing
*/
library EscortSystem uses IsUnitChanneling
globals
private constant real FAR_OFFSET = 500 //500 AOE
private constant real RETURN = 250 //250 offset from hero
endglobals
//===NON-CONFIGURABLES
globals
private constant integer ATTACK = 851983
private constant integer MOVE = 851986
private real offsetFar
private real offsetFromHero
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_DEAD) and u!=null
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 GetDistanceFromUnit takes unit u, unit u2 returns real
return GetDistance(GetUnitX(u), GetUnitY(u), GetUnitX(u2), GetUnitY(u2))
endfunction
private function GetDistanceFromBase takes unit u, real x, real y returns real
return GetDistance(GetUnitX(u), GetUnitY(u), x, y)
endfunction
private function IsTargetFar takes unit u, unit u2 returns boolean
return GetDistanceFromUnit(u, u2) > offsetFromHero
endfunction
private function RandomAngle takes nothing returns real
return GetRandomReal(0, 6.28)
endfunction
private function LifeLow takes unit u, real life returns boolean
return GetWidgetLife(u) < ((GetUnitState(u, UNIT_STATE_MAX_LIFE)*life))
endfunction
private function MoveTo takes unit u, real x, real y, real offset returns nothing
call IssuePointOrderById(u, MOVE, x+offset*Cos(RandomAngle()), y+offset*Sin(RandomAngle()))
endfunction
//===END OF NON-CONFIGURABLES
struct EscortSystem
unit hero
real moveDelay
real xBase
real yBase
real life
boolean retreat
boolean isOn
group armyGrp
private static timer t = CreateTimer()
private static integer index = 0
private static integer array indexAR
private static integer DATA
private static method onInit takes nothing returns nothing
set offsetFar = FAR_OFFSET*FAR_OFFSET
set offsetFromHero = (FAR_OFFSET*FAR_OFFSET)/1.5
endmethod
private method destroy takes nothing returns nothing
call DestroyGroup(.armyGrp)
set .hero = null
set .armyGrp = null
call .deallocate()
endmethod
private method returnNow takes unit u returns nothing
if GetDistance(GetUnitX(u), GetUnitY(u), .xBase, .yBase) > offsetFar then
call MoveTo(u, .xBase, .yBase, RETURN)
endif
endmethod
private method returnToHero takes unit u returns nothing
if IsTargetFar(u, .hero) then
call MoveTo(u, GetUnitX(.hero), GetUnitY(.hero), RETURN)
endif
endmethod
//if retreat is true and hero is dead
private static method retreatToBase takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = DATA
if UnitAlive(u) then
if UnitAlive(.hero) then
if .retreat then
if LifeLow(u, .life) then
call .returnNow(u)
else
call .returnToHero(u)
endif
else
call .returnToHero(u)
endif
else
call .returnNow(u)
endif
else
call GroupRemoveUnit(.armyGrp, u)
endif
set u = null
endmethod
private static method followerMovements takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = DATA
local real distanceFromHero
if UnitAlive(u) then
if not IsUnitChanneling(u) then
if GetUnitCurrentOrder(.hero)==ATTACK then
set distanceFromHero = GetDistanceFromUnit(u, .hero)
if distanceFromHero > offsetFar then
call IssuePointOrderById(u, ATTACK, GetUnitX(.hero)+RETURN*Cos(RandomAngle()), GetUnitY(.hero)+RETURN*Sin(RandomAngle()))
endif
endif
endif
endif
set u = null
endmethod
private static method looper takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i + 1
set this = indexAR[i]
set DATA = this
if .isOn then
call ForGroup(.armyGrp, function thistype.retreatToBase)
if UnitAlive(.hero) then
set .moveDelay = .moveDelay + 1
if .moveDelay==2 then
set .moveDelay = 0
call ForGroup(.armyGrp, function thistype.followerMovements)
endif
endif
else
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)
call DestroyTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
//API:
static method create takes unit hero, boolean retreat returns thistype
local thistype this = allocate()
set .hero = hero
set .retreat = retreat
set .isOn = true //checks if the hero is holding a group or not
set .moveDelay = 0
set .armyGrp = CreateGroup()
if index==0 then
set t = CreateTimer()
call TimerStart(t, 1.0, true, function thistype.looper)
endif
set index = index + 1
set indexAR[index] = this
return this
endmethod
method operator addUnit= takes unit follower returns nothing
if .armyGrp!=null then
call GroupAddUnit(.armyGrp, follower)
call RemoveGuardPosition(follower)
call MoveTo(follower, GetUnitX(.hero), GetUnitY(.hero), RETURN)
else
debug call BJDebugMsg("addUnit ERROR: the group is null!")
endif
endmethod
method operator retreatX= takes real xCoordinate returns nothing
set .xBase = xCoordinate
endmethod
method operator retreatY= takes real yCoordinate returns nothing
set .yBase = yCoordinate
endmethod
method operator lifeCheck= takes real lifePercentage returns nothing
if .retreat then
set .life = lifePercentage * 0.01
else
debug call BJDebugMsg("lifeCheck ERROR: retreat must be true!")
endif
endmethod
method remove takes nothing returns nothing
set .isOn = false
endmethod
endstruct
endlibrary
//TESH.scrollpos=10
//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 = false
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=201
//TESH.alwaysfold=0
/*
===================EngageSystem v3.2===================
==================Made by: Mckill2009==================
FEATURES:
- This system will order a unit to engage and search for enemies
- Allows attack captain (hero leads the attack)
- Allows pick up item
- Allows target random enemy units include creeps and buildings
- Option to retreat away from attacker or to building when HP is low
- Allows target refresh when duration runs out
HOW TO USE:
- Make a new trigger and convert to custom text via EDIT >>> CONVERT CUSTOM TEXT
- Copy ALL that is written here (overwrite the existing texts in the trigger)
- Make a dummy ability of UNHOLY FRENZY that has no buff, mana cost and replace the SPELL_ID raw code
written below the THIRD global block if needed, see below the SPELL_ID for more info.
KNOWN ISSUES:
- This system does not support Buy and Selling of items for AI
REQUIRES:
- Jass New Gen Pack (JNGP) by Vexorian
- GetClosestWidget by Spinnaker
- IsUnitChanneling by Magtheridon96
- RegisterPlayerUnitEvent by Magtheridon96
CREDITS: For their contribution
- watermelon_1234
- Bribe
API:
static method SetEngageUnit takes unit u, boolean attackcaptain returns nothing
static method SetUnitPickItem takes unit u returns nothing
static method SetUnitRetreat takes unit u returns nothing
static method SetUnitLockTargets takes unit u, integer targets, real atkdur returns nothing
static method SetReturnPoints takes unit u, real xReturn, real yReturn returns nothing
static method SetHeroToProtect takes unit follower, unit hero returns nothing
static method RemoveEngagingUnit takes unit u returns nothing
*/
library EngageSystem uses GetClosestWidget, IsUnitChanneling
globals
//===CONFIGURABLES: All settings below are recommended
private constant real AOE_ITEM_PICK = 700 //range of the targeted item
private constant real AOE_NEAR_TARGET = 500 //targeting nearby enemies while other target is locked
private constant real INTERVAL = 3 //interval for attack
private constant real MAX_LIFE_PERCENTAGE = 0.4 //used for retreat (this is 40%)
private constant real OFFSET_FROM_HERO = 300 //used in hero guard/follow
private constant integer MAX_ITEM_PICKED = 4 //Warning: MAX is 6
private constant boolean ENABLE_PICK_UP_ITEM = true
private constant boolean ENABLE_RETREAT = true
private constant boolean ENABLE_NEAR_TARGET = true //enables to search/target nearby units
//If FOUNTAIN_OF_HEALTH doesnt exist, it will retreat to an allied bubilding
private constant boolean ENABLE_RETREAT_TO_BUILDING = false //ENABLE_RETREAT must be true
//SPELL_ID must target "Air, ground, enemy, friend, vulnerable, mechanical, structure, organic, neutral", NO MANA, NO COOLDOWN
//In other words, it MUST target ALL UNITS excluding invulnerable.
//SPELL_ID MUST be Unholy Frenzy ability, else it wont work
private constant integer SPELL_ID = 'eng0' //Unholy frenzy RAW CODE, replace this if needed
endglobals
//=====================================================
//===============NEVER TOUCH THIS BLOCK!===============
//=====================================================
globals
private constant hashtable HASH = InitHashtable()
private constant integer ATTACK = 851983
private constant integer MOVE = 851986
private constant integer STOP = 851972
private constant integer SMART = 851971
private constant integer FOUNTAIN_OF_HEALTH = 'nfoh'
private rect R
private unit DUMMY
endglobals
native UnitAlive takes unit u returns boolean
private function IsUnitEngageable takes unit u returns boolean
call SetUnitPosition(DUMMY, GetUnitX(u), GetUnitY(u))
call IssueTargetOrderById(DUMMY, 852209, u) //Unholy Frenzy orderID
if GetUnitCurrentOrder(DUMMY)==852209 then
return true
endif
call IssueImmediateOrderById(DUMMY, STOP)
return false
endfunction
private function FilterEngagingUnit takes unit u returns boolean
return GetUnitMoveSpeed(u) > 0 and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and /*
*/ not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_PEON) and /*
*/ GetPlayerController(GetOwningPlayer(u))==MAP_CONTROL_COMPUTER
endfunction
private function UnitEnemy takes unit u1, unit u2 returns boolean
return IsUnitEnemy(u1, GetOwningPlayer(u2))
endfunction
private function UnitHero takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_HERO)
endfunction
private function UnitStructure takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_STRUCTURE)
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
//=====================================================
//===========END OF NEVER TOUCH THIS BLOCK!============
//=====================================================
//========================================
//=============ENGAGE CODE:===============
//========================================
//! textmacro EScreatetimer
private static timer t = CreateTimer()
private static integer instance = 0
private static integer array instanceAr
//! endtextmacro
//! textmacro deallocate
call .destroy()
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
//! endtextmacro
//===NON-HERO UNITS ARE ALWAYS THE FOLLOWER
private struct Follower
unit follower
unit hero
unit mainhero
unit target
private static integer DATA
private static unit filU
//! runtextmacro EScreatetimer()
private method destroy takes nothing returns nothing
set .follower = null
set .hero = null
set .mainhero = null
set .target = null
call .deallocate()
endmethod
static method filterHero takes nothing returns boolean
local thistype this = DATA
set filU = GetFilterUnit()
return UnitAlive(filU) and not UnitEnemy(filU,.follower) and UnitHero(filU) and IsUnitEngageable(filU) and GetOwningPlayer(filU)==GetOwningPlayer(.follower)
endmethod
private static method engagingTarget takes nothing returns nothing
local thistype this
local real xAttacker
local real yAttacker
local real xHero
local real yHero
local real xOffset
local real yOffset
local real DistX
local real angle
local real facing
local integer uID
local integer orderIDU
local integer orderIDH
local integer i = 0
loop
set i = i + 1
set this = instanceAr[i]
set uID = GetHandleId(.follower)
if LoadBoolean(HASH,uID,1) and UnitAlive(.follower) then
set xAttacker = GetUnitX(.follower)
set yAttacker = GetUnitY(.follower)
if UnitAlive(.hero) then
set orderIDH = GetUnitCurrentOrder(.hero)
if not IsUnitChanneling(.hero) or orderIDH==ATTACK then
set xHero = GetUnitX(.hero)
set yHero = GetUnitY(.hero)
if UnitAlive(.target) then
set facing = GetUnitFacing(.hero)*bj_DEGTORAD
set angle = GetRandomReal(-3,3)
set xOffset = xHero+OFFSET_FROM_HERO*Cos(facing+angle)
set yOffset = yHero+OFFSET_FROM_HERO*Sin(facing+angle)
set DistX = GetDistance(xAttacker,yAttacker,xHero,yHero)
set orderIDU = GetUnitCurrentOrder(.follower)
if not IsUnitChanneling(.follower) then
if DistX < (600*600) then
if IsUnitType(.target,UNIT_TYPE_SLEEPING) then
call IssueTargetOrderById(.follower,ATTACK,.target)
else
call IssuePointOrderById(.follower,ATTACK,GetUnitX(.target),GetUnitY(.target))
endif
else
call IssuePointOrderById(.follower,ATTACK,xOffset,yOffset)
endif
endif
else
set .target = LoadUnitHandle(HASH,GetHandleId(.hero),7)
call IssuePointOrderById(.follower,ATTACK,xOffset,yOffset)
endif
endif
else
set .hero = .mainhero
if .hero==null then
set DATA = this
set .hero = GetClosestUnit(xAttacker,yAttacker,Filter(function thistype.filterHero))
call IssuePointOrderById(.follower, ATTACK, LoadReal(HASH, uID, 4), LoadReal(HASH, uID, 5))
endif
endif
else
call FlushChildHashtable(HASH,uID)
//! runtextmacro deallocate()
endif
exitwhen i==instance
endloop
endmethod
static method addFollower takes unit u returns nothing
local thistype this
if instance < 8190 then
set this = thistype.allocate()
set .follower = u
set .mainhero = LoadUnitHandle(HASH, GetHandleId(u), 8)
set .hero = null
set .target = null
if instance==0 then
call TimerStart(t, 2.5, true, function thistype.engagingTarget)
endif
set instance = instance + 1
set instanceAr[instance] = this
else
call DestroyTimer(t)
debug call BJDebugMsg("ERROR: Limit of 8190 reached!, cannot allocate anymore instance: DESTROYING TIMER. ")
endif
endmethod
endstruct
//===REGISTERS INDEPENDENT UNITS (means, this unit will NOT search and follows a hero)
//=== A HERO WILL ALWAYS BE REGISTERED AS INDEPENDENT UNITS
private struct IndependentEngager
private unit engager
private unit target
private real interval
private static integer DATA
private static unit filterU
//! runtextmacro EScreatetimer()
//16 Total Players, 17==ALL PLAYERS, 18==Structures, 19==Flying, 20==Heroes
private static method FilterEngage takes nothing returns boolean
local thistype this = DATA
local integer targetID
local integer i = 0
set filterU = GetFilterUnit()
if UnitAlive(filterU) and IsUnitEnemy(.engager,GetOwningPlayer(filterU)) and IsUnitEngageable(filterU) then
set targetID = LoadInteger(HASH,GetHandleId(.engager),2)
if targetID < 17 then
loop
set i = i+1
if targetID==i then
return GetOwningPlayer(filterU)==Player(i-1)
endif
exitwhen i==targetID
endloop
elseif targetID==17 then
return true
elseif targetID==18 then
return IsUnitType(filterU,UNIT_TYPE_STRUCTURE)
elseif targetID==19 then
return IsUnitType(filterU,UNIT_TYPE_FLYING)
elseif targetID==20 then
return IsUnitType(filterU,UNIT_TYPE_HERO)
endif
endif
return false
endmethod
private static method engagingTarget takes nothing returns nothing
local thistype this
local real xAttacker
local real yAttacker
local real xTarget
local real yTarget
local integer orderU
local integer uID
local integer i = 0
loop
set i = i + 1
set this = instanceAr[i]
set uID = GetHandleId(.engager)
if LoadBoolean(HASH, uID, 1) and UnitAlive(.engager) then
set xAttacker = GetUnitX(.engager)
set yAttacker = GetUnitY(.engager)
if .interval > 0 then
set .interval = .interval - INTERVAL
if UnitAlive(.target) and IsUnitEngageable(.target) then
set xTarget = GetUnitX(.target)
set yTarget = GetUnitY(.target)
set orderU = GetUnitCurrentOrder(.engager)
if not IsUnitChanneling(.engager) then
if IsUnitType(.target,UNIT_TYPE_SLEEPING) then
call IssueTargetOrderById(.engager, ATTACK, .target)
else
call IssuePointOrderById(.engager, ATTACK, xTarget, yTarget)
endif
endif
else
set DATA = this
set .target = GetClosestUnit(xAttacker,yAttacker,Filter(function thistype.FilterEngage))
call SaveUnitHandle(HASH,uID,7,.target)
if .target==null then
call IssuePointOrderById(.engager, ATTACK, LoadReal(HASH, uID, 4), LoadReal(HASH, uID, 5))
endif
endif
else
set DATA = this
set .interval = LoadReal(HASH, uID, 3) //resumes the timer
set .target = GetClosestUnit(xAttacker,yAttacker,Filter(function thistype.FilterEngage))
call SaveUnitHandle(HASH,uID,7,.target)
endif
else
if not UnitHero(.engager) then
set .engager = null
set .target = null
//! runtextmacro deallocate()
elseif not LoadBoolean(HASH, uID, 1) then
//Uses by heroes
set .engager = null
set .target = null
//! runtextmacro deallocate()
endif
endif
exitwhen i==instance
endloop
endmethod
static method addUnit takes unit e returns nothing
local thistype this
if instance < 8190 then
set this = allocate()
set .engager = e
set .target = null
set .interval = 0
if instance==0 then
call TimerStart(t, INTERVAL, true, function thistype.engagingTarget)
endif
set instance = instance + 1
set instanceAr[instance] = this
else
call DestroyTimer(t)
debug call BJDebugMsg("ERROR: Limit of 8190 reached!, cannot allocate anymore instance: DESTROYING TIMER. ")
endif
endmethod
endstruct
//========================================
//=========END OF ENGAGE CODE:============
//========================================
//===ITEM PICK:
private struct ItemPick
unit hero
private static thistype DATA
//! runtextmacro EScreatetimer()
private static method onPickItem takes nothing returns boolean
local thistype this = DATA
local integer orderID = GetUnitCurrentOrder(.hero)
if orderID==MOVE or orderID==ATTACK then
call IssueTargetOrderById(.hero, SMART, GetFilterItem())
endif
return false
endmethod
private static method enumerateItem takes nothing returns nothing
local thistype this
local real x
local real y
local integer index
local integer count
local integer orderID
local integer i = 0
loop
set i = i + 1
set this = instanceAr [i]
if LoadBoolean(HASH, GetHandleId(.hero), 1) then
if UnitAlive(.hero) then
set count = 0
set index = 0
loop
if (UnitItemInSlot(.hero, index) != null) then
set count = count + 1
endif
set index = index + 1
exitwhen index==6
endloop
if count < MAX_ITEM_PICKED then
set x = GetUnitX(.hero)
set y = GetUnitY(.hero)
set DATA = this
call MoveRectTo(R, x, y)
call EnumItemsInRect(R, null, function thistype.onPickItem)
endif
endif
else
set .hero = null
//! runtextmacro deallocate()
endif
exitwhen i==instance
endloop
endmethod
static method create takes unit hero returns thistype
local thistype this = thistype.allocate()
set .hero = hero
if instance==0 then
call TimerStart(t, 1.0, true, function thistype.enumerateItem)
endif
set instance = instance + 1
set instanceAr[instance] = this
return this
endmethod
endstruct
//===RETREAT:
private struct Retreat
unit hero
unit retunit
real minlife
//! runtextmacro EScreatetimer()
private static method searchtarget takes nothing returns nothing
local thistype this
local unit first
local real xUnit
local real yUnit
local real distX
local real distEx
local real xBase
local real yBase
local integer orderID
local integer uID
local integer i = 0
loop
set i = i + 1
set this = instanceAr [i]
if LoadBoolean(HASH, GetHandleId(.hero), 1) then
if UnitAlive(.hero) then
set orderID = GetUnitCurrentOrder(.hero)
if not IsUnitChanneling(.hero) and orderID==ATTACK or orderID==MOVE then
if GetWidgetLife(.hero) < .minlife then
set uID = GetHandleId(.hero)
set xUnit = GetUnitX(.hero)
set yUnit = GetUnitY(.hero)
static if ENABLE_RETREAT_TO_BUILDING then
if .retunit==null then
//searches a place to retreat
call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if UnitAlive(first) and not UnitEnemy(first, .hero) then
if GetUnitTypeId(first)==FOUNTAIN_OF_HEALTH then
set .retunit = first
exitwhen true
elseif UnitStructure(first) and not (GetOwningPlayer(first)==Player(PLAYER_NEUTRAL_PASSIVE)) then
set .retunit = first
exitwhen true
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
else
if .retunit != null then
set distEx = GetDistance(xUnit,yUnit,GetUnitX(.retunit),GetUnitY(.retunit))
//searching fountain of health
call IssuePointOrderById(.hero, MOVE, GetUnitX(.retunit), GetUnitY(.retunit))
else
set xBase = LoadReal(HASH, uID, 4)
set yBase = LoadReal(HASH, uID, 5)
set distX = GetDistance(xUnit,yUnit,xBase,yBase)
if distX > (1300*1300) then
//if no more allies, the AI will return to where is it assigned
call IssuePointOrderById(.hero,MOVE,xBase,yBase)
endif
endif
endif
else
set xBase = LoadReal(HASH, uID, 4)
set yBase = LoadReal(HASH, uID, 5)
set distX = GetDistance(xUnit,yUnit,xBase,yBase)
if distX > (1300*1300) then
//if no more allies, the AI will return to where is it assigned
call IssuePointOrderById(.hero,MOVE,xBase,yBase)
endif
endif
endif
endif
endif
else
set .hero = null
set .retunit = null
//! runtextmacro deallocate()
endif
exitwhen i==instance
endloop
endmethod
static method create takes unit u, real x, real y returns thistype
local thistype this = thistype.allocate()
set .hero = u
set .minlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)*MAX_LIFE_PERCENTAGE
set .retunit = null
if instance==0 then
call TimerStart(t, 1.0, true, function thistype.searchtarget)
endif
set instance = instance + 1
set instanceAr[instance] = this
return this
endmethod
endstruct
//===INITIALIZER:
private struct EngageInit
private static method onInit takes nothing returns nothing
local trigger t
static if ENABLE_PICK_UP_ITEM then
set R = Rect(-AOE_ITEM_PICK, -AOE_ITEM_PICK, AOE_ITEM_PICK, AOE_ITEM_PICK)
endif
set DUMMY = CreateUnit(Player(15), 'hmpr', 0,0,0)
call UnitAddAbility(DUMMY, SPELL_ID)
call UnitAddAbility(DUMMY, 'Aloc')
call ShowUnit(DUMMY, false)
set t = null
endmethod
endstruct
struct EngageSystem
//===SETUP EVERYTHING: SYSTEM API
//attackcaptain - sets the attack captain which will find and guard heroes or hero that will lead the attack group
static method SetEngageUnit takes unit u, boolean attackcaptain returns nothing
local integer unitID = GetHandleId(u)
if FilterEngagingUnit(u) then
//Removes the guard position so that AI's dont go back to their original location
if not LoadBoolean(HASH,unitID,1) then
call RemoveGuardPosition(u)
call SaveBoolean(HASH, unitID, 1, true)
if UnitHero(u) or not attackcaptain then
call SaveBoolean(HASH, unitID, 6, true)
call IndependentEngager.addUnit(u)
else
call Follower.addFollower(u)
endif
else
debug call BJDebugMsg("ERROR: SetEngageUnit, "+GetUnitName(u)+" has already been registered!")
endif
endif
endmethod
//Works only for computer controlled unit and heroes
static method SetUnitPickItem takes unit u returns nothing
static if ENABLE_PICK_UP_ITEM then
if FilterEngagingUnit(u) and UnitHero(u) then
call ItemPick.create(u)
endif
else
debug call BJDebugMsg("ERROR: ENABLE_PICK_UP_ITEM is false, set it to true!")
endif
endmethod
static method SetUnitRetreat takes unit u returns nothing
static if ENABLE_RETREAT then
if FilterEngagingUnit(u) then
call Retreat.create(u, GetUnitX(u), GetUnitY(u))
endif
else
debug call BJDebugMsg("ERROR: ENABLE_RETREAT is false, set it to true!")
endif
endmethod
/* Targets;
1 to 16 = Targets a specific ENEMY PLAYER
17 = Targets ALL ENEMY PLAYERS
18 = Targets only enemy STRUCTURES
19 = Targets only enemy FLYING UNITS
20 = Targets only enemy HEROES
*/
static method SetUnitLockTargets takes unit u, integer targets, real atkdur returns nothing
local integer unitID = GetHandleId(u)
if IsUnitType(u, UNIT_TYPE_HERO) or LoadBoolean(HASH,unitID,6) and FilterEngagingUnit(u) then
if targets < 17 and targets > 0 then
if GetOwningPlayer(u)==Player(targets-1) then
debug call BJDebugMsg("ERROR: Player "+I2S(targets)+" can't target his own units!")
else
call SaveInteger(HASH, unitID, 2, targets)
endif
elseif targets > 16 and targets < 21 then
call SaveInteger(HASH, unitID, 2, targets)
else
debug call BJDebugMsg("ERROR: Please input only from 1 to 20!")
endif
call SaveReal(HASH,unitID,3,atkdur)
endif
endmethod
//Sets the specific hero to protect, if main hero is dead, it will search for a new hero
static method SetHeroToProtect takes unit follower, unit hero returns nothing
if not LoadBoolean(HASH, GetHandleId(follower), 6) and FilterEngagingUnit(follower) then
call SaveUnitHandle(HASH, GetHandleId(follower), 8, hero)
endif
endmethod
static method SetReturnPoints takes unit u, real xReturn, real yReturn returns nothing
call SaveReal(HASH, GetHandleId(u), 4, xReturn)
call SaveReal(HASH, GetHandleId(u), 5, yReturn)
endmethod
static method RemoveEngagingUnit takes unit u returns nothing
call FlushChildHashtable(HASH,GetHandleId(u))
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
===IsUnitEngageable v1.1
===Created by: Mckill2009
HOW TO INSTALL:
- Copy this script to your trigger editor via custom text
- Copy the custom abilty from the object editor named 'IsUnitEngageable' to your map
- Make sure you change also the rawID below (SPELL_ID) if you change the rawID of your imported ability
- Done!
API:
function IsUnitEngageable takes unit u returns boolean
Undetects units that is:
- Invulnerable
- Burrowed
- Invisible
- Ethereal
- Have locust
*/
library IsUnitEngageable
globals
//Custom ability based on Unholy Frenzy
private constant integer SPELL_ID = 'eng0' //rawID
//Unit ID based on priest, never change this!
private constant integer DUMMY_ID = 'hmpr'
private unit DUMMY
endglobals
function IsUnitEngageable takes unit u returns boolean
call SetUnitPosition(DUMMY, GetUnitX(u), GetUnitY(u))
return IssueTargetOrderById(DUMMY, 852209, u) and not IsUnitType(u,UNIT_TYPE_ETHEREAL)
endfunction
struct onInit
static method onInit takes nothing returns nothing
set DUMMY = CreateUnit(Player(15), DUMMY_ID, 0,0,0)
call UnitAddAbility(DUMMY, SPELL_ID)
call UnitAddAbility(DUMMY, 'Aloc')
call ShowUnit(DUMMY, false)
endmethod
endstruct
endlibrary
//TESH.scrollpos=67
//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=76
//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=0
//TESH.alwaysfold=0
/*
===QuestUtils v1.4
===By Mckill2009
This is an alternative to GUI Quest creation, very easy to use if you follow the instructions
written in this sample map...
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
*/
library QuestUtils uses optional QuestSounds
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 instance = instance + 1
set this = instance
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)
endmethod
method operator failed= takes boolean b returns nothing
call QuestSetFailed(.q, b)
endmethod
method operator enable= takes boolean b returns nothing
call QuestSetEnabled(.q, b)
endmethod
method operator discover= takes boolean b returns nothing
call QuestSetDiscovered(.q, b)
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 = null
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
set instance = instance + 1
set this = instance
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 = null
set .qi = instance.qi
set instance = instance - 1
endmethod
endstruct
endlibrary
//TESH.scrollpos=9
//TESH.alwaysfold=0
/*
===QuestSounds v1.0
===By Mckill2009
Sounds should be in Mp3 format
*/
library QuestSounds
//sample: call PlayCompletedSound()
//! textmacro Sound takes Func, Snd
function Play$Func$Sound takes nothing returns nothing
call StartSound(bj_quest$Snd$Sound)
endfunction
//! endtextmacro
//! runtextmacro Sound("Completed","Completed")
//! runtextmacro Sound("Discovered","Discovered")
//! runtextmacro Sound("Failed","Failed")
//! runtextmacro Sound("Hint","Hint")
//! runtextmacro Sound("ItemAcquired","ItemAcquired")
//! runtextmacro Sound("Secret","Secret")
//! runtextmacro Sound("Updated","Updated")
//! runtextmacro Sound("Warning","Warning")
//sample: call PlaySound(gg_snd_SOUNDFILENAME, false)
//if killWheDone is true, it will destroy the sound and clears the leak but
//if you use the sound again, then you should set it to false
function PlayCustomSound takes sound soundHandle, boolean killWhenDone returns nothing
call StartSound(soundHandle)
if killWhenDone then
call KillSoundWhenDone(soundHandle)
endif
endfunction
endlibrary
//TESH.scrollpos=5
//TESH.alwaysfold=0
library CastSystem
globals
private constant integer DUMMY_ID = 'h000' //Dummy must be created in the object editor
endglobals
private function CastSetup takes unit u, integer abilityid, integer level returns nothing
call UnitApplyTimedLife(u, 'BTLF', 1.0)
call UnitAddAbility(u, abilityid)
call SetUnitAbilityLevel(u, abilityid, level)
endfunction
function CastToUnit takes player p, unit target, real x, real y, integer abilityid, integer orderid, integer level returns nothing
local unit dummy = CreateUnit(p, DUMMY_ID, x, y, 0)
call CastSetup(dummy, abilityid, level)
call IssueTargetOrderById(dummy, orderid, target)
set dummy = null
endfunction
function CastToPoint takes player p, real x, real y, real xLoc, real yLoc, integer abilityid, integer orderid, integer level returns nothing
local unit dummy = CreateUnit(p, DUMMY_ID, x, y, 0)
call CastSetup(dummy, abilityid, level)
call IssuePointOrderById(dummy, orderid, xLoc, yLoc)
set dummy = null
endfunction
function CastToNone takes player p, real x, real y, integer abilityid, integer orderid, integer level returns nothing
local unit dummy = CreateUnit(p, DUMMY_ID, x, y, 0)
call CastSetup(dummy, abilityid, level)
call IssueImmediateOrderById(dummy, orderid)
set dummy = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*
===Damage Detection v1.1
===By Mckill2009
Inpired by: GUI Friendly Damage Detection by Weep
HOW TO USE:
call TriggerRegisterVariableEvent(trigger, "DamageDetection_DD_Event", EQUAL, 0.)
*/
library DamageDetection uses Table
globals
private trigger trig = CreateTrigger()
public unit DD_DamagedUnit = null
public unit DD_DamageSource = null
public real DD_DamageTaken = 0
public real DD_Event = 0
endglobals
struct DamageDetection extends array
private static Table chk
private static method DamageRegister takes nothing returns boolean
local unit u = GetEventDamageSource()
if chk.has(GetHandleId(u)) then
set DD_DamagedUnit = GetTriggerUnit()
set DD_DamageSource = u
set DD_DamageTaken = GetEventDamage()
set DD_Event = 1.
set DD_Event = 0.
endif
set u = null
return false
endmethod
static method SetUnitDamaged takes unit u returns nothing
local trigger t
local integer ID = GetHandleId(u)
if not chk.has(ID) then
call TriggerRegisterUnitEvent(trig, u, EVENT_UNIT_DAMAGED)
set chk[ID] = 0
set t = null
endif
endmethod
private static method RemoveDamagedUnit takes nothing returns boolean
local integer ID = GetHandleId(GetTriggerUnit())
if not IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) and chk.has(ID) then
call chk.remove(ID)
endif
return false
endmethod
private static method FilterDamageUnitEvent takes nothing returns boolean
call SetUnitDamaged(GetFilterUnit())
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t
local region r
set chk = Table.create()
call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, function thistype.FilterDamageUnitEvent)
call TriggerAddCondition(trig, function thistype.DamageRegister)
set t= CreateTrigger()
set r = CreateRegion()
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(t, r, function thistype.FilterDamageUnitEvent)
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, function thistype.RemoveDamagedUnit)
set t = null
set r = null
endmethod
//API:
static method add takes unit damagesource returns nothing
local integer ID = GetHandleId(damagesource)
if not chk.has(ID) then
set chk[ID] = 0
else
debug call BJDebugMsg("add: Already added.")
endif
endmethod
static method remove takes unit damagesource returns nothing
local integer ID = GetHandleId(damagesource)
if chk.has(ID) then
call chk.remove(ID)
else
debug call BJDebugMsg("remove: Already removed.")
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=57
//TESH.alwaysfold=0
/*
ColorUtils
Created by Mckill2009
Applies 50 colors to your texts
Source site:
[url]www.computerhope.com/htmcolor.htm[/url] (credits to ALONE for providing this site)
*/
library ColorUtils initializer init
globals
private string array Color
private integer MaxColor = 50
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] = "|cff342D7E" //State Blue
set Color[13] = "|cff2B60DE" //Royal Blue
set Color[14] = "|cff3BB9FF" //Deep Sky Blue
set Color[15] = "|cff25587E" //Deep Sky Blue 4
set Color[16] = "|cff8D38C9" //Violet
set Color[17] = "|cffF52887" //Deep Pink
set Color[18] = "|cff437C17" //Chartreuse4
set Color[19] = "|cff800517" //Firebrick
set Color[20] = "|cff810541" //Maroon
set Color[21] = "|cffFF00FF" //Magenta
set Color[22] = "|cffB048B5" //Medium Orchid
set Color[23] = "|cff6A287E" //Medium Orchid 4
set Color[24] = "|cff8E35EF" //Purple
set Color[25] = "|cffB93B8F" //Plum
set Color[26] = "|cffC3FDB8" //Dark Sea Green1
set Color[27] = "|cffE3E4FA" //Lavender
set Color[28] = "|cffAF7817" //Dark Goldenrod
set Color[29] = "|cff00FFFF" //Cryan
set Color[30] = "|cff43C6DB" //Turquoise
set Color[31] = "|cff82CAFF" //Sky Blue
set Color[32] = "|cff4E8975" //Sea Green
set Color[33] = "|cff254117" //Dark Green
set Color[34] = "|cffA52A2A" //Brown
set Color[35] = "|cffC0C0C0" //Silver
set Color[36] = "|cff808000" //Olive
set Color[37] = "|cffFF00FF" //Fuchsia
set Color[38] = "|cff00FF00" //Lime
set Color[39] = "|cffF76541" //Coral
set Color[40] = "|cffFFF8C6" //Lemon Chiffon
set Color[41] = "|cffB38481" //Rosy Brown
set Color[42] = "|cff7D2252" //Hot Pink4
set Color[43] = "|cffF660AB" //Hot Pink
set Color[44] = "|cffC85A17" //Chocolate
set Color[45] = "|cffD4A017" //Gold
set Color[46] = "|cffE18B6B" //Dark Salmon
set Color[47] = "|cff8A4117" //Sienna
set Color[48] = "|cffADA96E" //Khaki
set Color[49] = "|cff8AFB17" //Chartreuse
set Color[50] = "|cffADDFFF" //Light Blue
//set Color[51] = "|cff" //
endfunction
function RandomColor takes string s returns string
return Color[GetRandomInt(0, MaxColor)] + s +"|r"
endfunction
function FixedColor takes integer colorindex, string s returns string
return Color[colorindex] + s +"|r"
endfunction
private function init takes nothing returns nothing
call ColorSetup()
endfunction
endlibrary
//TESH.scrollpos=68
//TESH.alwaysfold=0
/*
MuiDummyCasters v4.4
Created by: Mckill2009
REQUIRES:
- Table by Bribe
- RegisterPlayerUnitEvent by Magtheridon96
INTRODUCTION:
- This System uses the FirstOfGroup native to take the caster from a group, then orders it to cast
a certain spell, instead of indexing the dummy.
- Very simple to use.
INSTRUCTIONS:
- Copy ALL that is in the "MuiDummyCasters" folder into your map.
- Enable the "MuiDummyCastersSetup", save the map and exit.
- Open your map again. Disable OR delete the "MuiDummyCastersSetup".
- Copy the required libraries Table and RegisterPlayerUnitEvent to your map
- In the object editor, the "DummyCaster(LUA)" should be there.
- DONE!
- Note: The raw code "dumy" can be used as a dummy unit for your entire map.
WARNING: Bugs may occur
- Please DO NOT name ANY of your custom unit's raw code as 'xcvb', coz it will screw everything.
The LUA SCRIPT will overwrite unit objects in your map if you do that.
- Since the dummy cannot move, it's best that the hitPoint and hitTarget is reachable, else
this will bug.
- The ability casted MUST have 0 cooldown.
API:
call MuiDummyCasters.hitTarget(owningplayer, target, xSource, ySource, abilityID, orderID, level)
call MuiDummyCasters.hitPoint(owningplayer, xSource, ySource, xDist, yDist, abilityID, orderID, level)
call MuiDummyCasters.hitNone(owningplayer, xSource, ySource, abilityID, orderID, level)
PARAMETEER EXPLANATION:
- owningplayer (player) - the owner of the casting unit
- target (unit) - the spelltarget
- xSource and ySource (real) - the coordinate where the dummy will be located
- xDist and yDist (real) - the coordinate where the dummy will cast the spell
- abilityID (integer) - the raw code of of the ability being cast
- orderID (integer) - the orderID of the abilityID
- level (integer) - the level of the abilityID
CREDITS:
- Bribe (for info about the 'Amov' ability remove)
*/
library MuiDummyCasters uses Table, RegisterPlayerUnitEvent
globals
//The DUMMY_CASTER raw ID is the only one you may adjust, NEVER touch the rest!
private TableArray mdc
private constant player OWNER = Player(15)
private constant integer DUMMY_CASTER = 'xcvb'
private group DummyTake = CreateGroup()
private unit dummy = null
//===FOR TESTING ONLY ON HOW MANY DUMMIES ARE CREATED:
private constant boolean DEBUG_MODE = false
private integer index = 0
endglobals
//! textmacro ISSUE takes order
local unit dum = thistype.takeDummyUnit()
call SetUnitPosition(dum, xSource, ySource)
call UnitAddAbility(dum, abilityID)
call SetUnitAbilityLevel(dum, abilityID, level)
call SetUnitOwner(dum, owningplayer, false)
call Issue$order$
set mdc[0][GetHandleId(dum)] = abilityID
//This is just a fail safe, in case the target is invulnerable, invisible or cannot be casted
if GetUnitCurrentOrder(dummy)==0 then
call thistype.refreshDummyUnit(dum)
endif
set dum = null
//! endtextmacro
struct MuiDummyCasters extends array
private static method refreshDummyUnit takes unit d returns nothing
local integer ID = GetHandleId(d)
call SetUnitOwner(d, OWNER, false)
call UnitRemoveAbility(d, mdc[0][ID])
call GroupAddUnit(DummyTake, d)
endmethod
private static method takeDummyUnit takes nothing returns unit
if FirstOfGroup(DummyTake)== null then
debug static if DEBUG_MODE then
debug set index = index + 1
debug call BJDebugMsg("only "+I2S(index) + " dummies created")
debug endif
set dummy = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0)
call UnitRemoveAbility(dummy,'Amov')
else
//if group is not empty then it will pick the first dummy in the group
set dummy = FirstOfGroup(DummyTake)
call GroupRemoveUnit(DummyTake, dummy)
endif
return dummy
endmethod
private static method instantCastEnd takes nothing returns boolean
if GetUnitTypeId(GetTriggerUnit())==DUMMY_CASTER then
call thistype.refreshDummyUnit(GetTriggerUnit())
endif
return false
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.instantCastEnd)
set mdc = TableArray[0x2000]
endmethod
//==================================
// 3 API functions you can use.
//==================================
static method hitTarget takes player owningplayer, unit target, real xSource, real ySource, integer abilityID, integer orderID, integer level returns nothing
//! runtextmacro ISSUE("TargetOrderById(dum, orderID, target)")
endmethod
static method hitPoint takes player owningplayer, real xSource, real ySource, real xDist, real yDist,integer abilityID, integer orderID, integer level returns nothing
//! runtextmacro ISSUE("PointOrderById(dum, orderID, xDist, yDist)")
endmethod
static method hitNone takes player owningplayer, real xSource, real ySource, integer abilityID, integer orderID, integer level returns nothing
//! runtextmacro ISSUE("ImmediateOrderById(dum, orderID)")
endmethod
endstruct
endlibrary
//TESH.scrollpos=90
//TESH.alwaysfold=0
/*
===Dynamic Timer v1.2
===Created by Mckill2009
INPIRED BY:
- CTL by Nesthaurus
- Dynamic GUI Indexing by Hanky
API: Module
implement DTloop
- Not optional, this needs to be on TOP of implementations
- You may declare your locals here
- This is where your condition is generated/running, this by default is TRUE
- If it encounters FALSE on the first "if", statement, it will automatically deindexed
- Take note that you dont need to put an "endif" to your condition
Sample:
implement DTloop
local unit x = GetUnitX(.hero)
local unit y = GetUnitY(.hero)
if UnitAlive(.hero) then
YOUR ACTIONS HERE!
implement DTend
implement DTnulls
- Optional, used to null your local variables created via DTloop
implement DTend
- Not optional, this needs to be BELOW of DTloop or implementations
- This calls the "create(real variable)", this is where a timer interval is dynamic
API: Methods
static method create takes real interval returns thistype
- By default, timer runs at INTERVAL (0.03125)
- The "interval" simply delays the 0.03125
CREDITS:
- Deaod for suggesting standard methods
*/
library DynamicTimer
globals
//These should NOT be modified
private constant integer MAX_INDEX = 8190
private constant real INTERVAL = 0.03125
endglobals
module DTloop //not optional
readonly boolean bol
readonly real gap
readonly real in
static boolean chkindex = true
static integer index1 = 0 //
static integer array indexAr
static timer t
method chk takes nothing returns boolean
endmodule
module DTnulls //optional
return true
endif
endmodule
module DTend //not optional
implement DT_DO_NOT_IMPLEMENT_THIS
endif
endif
exitwhen looper==index1
endloop
endmethod
private static method create takes real interval returns thistype
local thistype this
if (index1 < MAX_INDEX) then
set this = allocate()
set .gap = 0
set .in = interval
set .bol = true
if index1==0 then
set t = CreateTimer()
call TimerStart(t,INTERVAL,true,function thistype.looper)
endif
set index1 = index1 + 1
set indexAr[index1] = this
elseif chkindex then
set chkindex = false
debug call BJDebugMsg("ERROR: Struct has "+ I2S(MAX_INDEX)+ " index, unable to allocate!: TIMER DESTROYED.")
call PauseTimer(t)
call DestroyTimer(t)
endif
return this
endmethod
endmodule
//NEVER IMPLEMENT THIS
module DT_DO_NOT_IMPLEMENT_THIS //XD
implement DTnulls
set .bol = false
return .bol
endmethod
static method looper takes nothing returns nothing
local thistype this
local integer looper = 0
loop
set looper = looper + 1
set this = indexAr[looper]
set .gap = .gap + INTERVAL
if .gap >= .in then
set .gap = 0
call chk()
if not .bol then
call .destroy()
set indexAr[looper] = indexAr[index1]
set indexAr[index1] = this
set looper = looper - 1
set index1 = index1 - 1
if index1==0 then
call PauseTimer(t)
call DestroyTimer(t)
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library HeroLearn
globals
private integer array skill
endglobals
struct HeroLearn
static method learnSkill takes unit hero, integer a1, integer a2, integer a3, integer a4, integer a5 returns nothing
local integer i = 0
set skill[1] = a1
set skill[2] = a2
set skill[3] = a3
set skill[4] = a4
set skill[5] = a5
call SelectHeroSkill(hero,skill[GetRandomInt(1,5)])
loop
set i = i + 1
call SelectHeroSkill(hero,skill[i])
exitwhen i==5
endloop
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 heightoffset, real size returns nothing
local texttag tag = CreateTextTag()
call SetTextTagPosUnit(tag, u, heightoffset) //heightoffset is 0 OK
call SetTextTagText(tag, s, size) //size is 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=57
//TESH.alwaysfold=0
/*
===RectEvent
===by Mckill2009
Use GetFilterUnit() for the unit that's entering and leaving
API:
static method enter takes rect r, code cond returns nothing
static method leave takes rect r, code cond returns nothing
static method removeEnter takes rect r returns nothing
static method removeLeave takes rect r returns nothing
static method destroyAll takes rect r returns nothing
*/
library RectEvent
struct RectEvent extends array
private static hashtable ht = InitHashtable()
private static integer enterID = 0
private static integer leaveID = 10
static method enter takes rect r, code cond returns nothing
local integer rID = GetHandleId(r)
local trigger t
local region reg
if HaveSavedHandle(ht,rID,enterID) then
debug call BJDebugMsg("RectEvent.enter ERROR: Trying allocate a double rect!")
else
set t = CreateTrigger()
set reg = CreateRegion()
call SaveTriggerHandle(ht,rID,enterID,t) //uses 0
call SaveRegionHandle(ht,rID,enterID+1,reg) //uses 1
call RegionAddRect(reg,r)
call TriggerRegisterEnterRegion(t,reg,Filter(cond))
set t = null
set reg = null
endif
endmethod
static method leave takes rect r, code cond returns nothing
local integer rID = GetHandleId(r)
local trigger t
local region reg
if HaveSavedHandle(ht,rID,leaveID) then
debug call BJDebugMsg("RectEvent.leave ERROR: Trying allocate a double rect!")
else
set t = CreateTrigger()
set reg = CreateRegion()
call SaveTriggerHandle(ht,rID,leaveID,t) //uses 10
call SaveRegionHandle(ht,rID,leaveID+1,reg) //uses 11
call RegionAddRect(reg,r)
call TriggerRegisterLeaveRegion(t,reg,Filter(cond))
set t = null
set reg = null
endif
endmethod
static method removeEnter takes rect r returns nothing
local integer rID = GetHandleId(r)
if HaveSavedHandle(ht,rID,enterID) then
call DestroyTrigger(LoadTriggerHandle(ht,rID,enterID))
call RegionClearRect(LoadRegionHandle(ht,rID,enterID+1),r)
call RemoveSavedHandle(ht,rID,enterID)
else
debug call BJDebugMsg("RectEvent.removeEnter ERROR: Trying to remove an invalid rect!")
endif
endmethod
static method removeLeave takes rect r returns nothing
local integer rID = GetHandleId(r)
if HaveSavedHandle(ht,rID,leaveID) then
call DestroyTrigger(LoadTriggerHandle(ht,rID,leaveID))
call RegionClearRect(LoadRegionHandle(ht,rID,leaveID+1),r)
call RemoveSavedHandle(ht,rID,leaveID)
else
debug call BJDebugMsg("RectEvent.removeLeave ERROR: Trying to remove an invalid rect!")
endif
endmethod
static method destroyAll takes rect r returns nothing
local integer rID = GetHandleId(r)
if HaveSavedHandle(ht,rID,enterID) or HaveSavedHandle(ht,rID,leaveID) then
call DestroyTrigger(LoadTriggerHandle(ht,rID,enterID))
call RegionClearRect(LoadRegionHandle(ht,rID,enterID+1),r)
call DestroyTrigger(LoadTriggerHandle(ht,rID,leaveID))
call RegionClearRect(LoadRegionHandle(ht,rID,leaveID+1),r)
call FlushChildHashtable(ht,rID)
else
debug call BJDebugMsg("RectEvent.destroyAll ERROR: Trying to destroy an invalid rect!")
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=7
//TESH.alwaysfold=0
/*
REQUIRED NATIVE ABOVE MAP SCRIPT:
native UnitAlive takes unit u returns boolean
*/
library ConstructionFinish uses RegisterPlayerUnitEvent, Table
globals
private Table chk
endglobals
struct CF extends array
static method done takes unit u returns boolean
return UnitAlive(u) and chk.has(GetHandleId(u))
endmethod
static method makeAvailable takes unit u returns nothing
set chk[GetHandleId(u)] = 0
endmethod
private static method onInit takes nothing returns nothing
set chk = Table.create()
endmethod
endstruct
endlibrary
//=============================================
scope FC
struct FC
static method finishConstruction takes nothing returns nothing
local unit u = GetTriggerUnit()
if GetUnitTypeId(u)=='halt' then
call CF.makeAvailable(u)
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.finishConstruction)
endmethod
endstruct
endscope
//TESH.scrollpos=3
//TESH.alwaysfold=0
/*
===SoundEx v1.0
===By Mckill2009
*/
library SoundEx
struct SoundEx
private sound snd
static method create takes sound soundHandle returns thistype
local thistype this = allocate()
set .snd = soundHandle
return this
endmethod
//call sample.destroy()
method destroy takes nothing returns nothing
call KillSoundWhenDone(.snd)
set .snd = null
call .deallocate()
endmethod
//call sample.onXY(gg_snd_CityBuildingDeath1)
method onXY takes real xPos, real yPos, real zPos returns nothing
call SetSoundPosition(.snd, xPos, yPos, zPos)
call StartSound(.snd)
endmethod
//call sample.onUnit(gg_snd_Disenchant)
method onUnit takes unit whichUnit returns nothing
call AttachSoundToUnit(.snd, whichUnit)
call StartSound(.snd)
endmethod
endstruct
endlibrary
//TESH.scrollpos=8
//TESH.alwaysfold=0
/*
===PreloadUtils
===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 init
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 init
endstruct
endlibrary
//TESH.scrollpos=24
//TESH.alwaysfold=0
library Functions
function FilterBuildings takes integer uID returns boolean
return uID=='ncb0' or uID=='ncb1' or uID=='ncb2' or uID=='ncb3' or uID=='ncb4' or uID=='ncb5' /*
*/ or uID=='ncb6' or uID=='ncb7' or uID=='ncb8' or uID=='ncb9' or uID=='ncba' or uID=='ncbb' /*
*/ or uID=='ncbc' or uID=='ncbd' or uID=='ncbe' or uID=='ncbf'
endfunction
function FilterVillagers takes integer uID returns boolean
return uID=='nC00' or uID=='nC01' or uID=='nC02' or uID=='nC04'
endfunction
function FilterOnlyZombies takes integer uID returns boolean
return uID=='nzom' or uID=='nC03'
endfunction
function PickVillager takes nothing returns boolean
set TempU = GetFilterUnit()
//BNdc==sleep buff, B000==saved buff
return FilterVillagers(GetUnitTypeId(TempU)) and GetUnitAbilityLevel(TempU, 'BNdc')==0 and GetUnitAbilityLevel(TempU, 'B000')==0 and UnitAlive(TempU)
endfunction
function LifeTimer takes unit u, real r returns nothing
call UnitApplyTimedLife(u,'BTLF',r)
endfunction
function Print takes string s returns nothing
call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,20,s)
endfunction
function PrintReal takes real r returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0,0,R2S(r))
endfunction
function PrintInteger takes integer i returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(i))
endfunction
function TempTimerStart takes real dur, code c returns nothing
call TimerStart(CreateTimer(),dur,false,c)
endfunction
function TempTimerEnd takes nothing returns nothing
call PauseTimer(GetExpiredTimer())
call DestroyTimer(GetExpiredTimer())
endfunction
function SamePlayer takes unit u, unit u2 returns boolean
return GetOwningPlayer(u)==GetOwningPlayer(u2)
endfunction
endlibrary
//TESH.scrollpos=15
//TESH.alwaysfold=0
//===GroupEnumUnitsInRange
//! textmacro FOG_ADD takes X, Y, A
call GroupEnumUnitsInRange(bj_lastCreatedGroup, $X$, $Y$, $A$, null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if UnitAlive(first) then
//! endtextmacro
//! textmacro FOG_REMOVE
endif
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
//! endtextmacro
//===GroupEnumUnitsInRect
//! textmacro GROUP_ENUM_RECT_START
call GroupEnumUnitsInRect(bj_lastCreatedGroup,bj_mapInitialPlayableArea, null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
//! endtextmacro
//! textmacro GROUP_ENUM_RECT_END
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
//! endtextmacro
//===GroupEnumPlayer
//! textmacro GROUP_ENUM_PLAYER_UNIT_START takes PL
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup,$PL$,null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
//! endtextmacro
//! textmacro GROUP_ENUM_PLAYER_UNIT_END
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
//! endtextmacro
//TESH.scrollpos=0
//TESH.alwaysfold=0
//System: Fundamentals
//Made by Mckill2009
//These are my simple functions/conversions, very easy to use
library Fundamentals
globals
private location LOC = Location(0,0)
endglobals
native UnitAlive takes unit u returns boolean
struct Fundamentals
static method GetParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)//Standard parabola
endmethod
static method GetUnitZ takes unit u, real height returns real
call MoveLocation(LOC, GetUnitX(u), GetUnitY(u))
return GetLocationZ(LOC) + height
endmethod
static method GetAngle takes real x1, real y1, real x2, real y2 returns real
return Atan2(y2-y1, x2-x1)
endmethod
static method GetDistance takes real x1, real y1, real x2, real y2 returns real
return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)
endmethod
static method 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))
endmethod
static method GetAngleUnitFacing takes unit u returns real
return GetUnitFacing(u) * bj_DEGTORAD
endmethod
static method MakeUnitFly takes unit u returns nothing
call UnitAddAbility(u, 'Arav')
call UnitRemoveAbility(u, 'Arav')
endmethod
static method UnitAddLocust takes unit u returns nothing
call UnitAddAbility(u, 'Aloc')
endmethod
static method UnitRemoveLocust takes unit u returns nothing
call ShowUnit(u, false)
call UnitRemoveAbility(u, 'Aloc')
call ShowUnit(u, true)
endmethod
static method SpecialEffectXY takes string sfx, real x, real y returns nothing
call DestroyEffect(AddSpecialEffect(sfx, x, y))
endmethod
static method SpecialEffectTarget takes unit u, string sfx, string attachment returns nothing
call DestroyEffect(AddSpecialEffectTarget(sfx, u, attachment))
endmethod
static method OrganicUnits 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)
endmethod
static method LifeTaken takes unit u returns boolean
return (GetWidgetLife(u)) < (GetUnitState(u, UNIT_STATE_MAX_LIFE))
endmethod
static method 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
endmethod
static method DamageTarget takes unit source, unit target, real d, attacktype atk, damagetype dmg returns nothing
call UnitDamageTarget(source, target, d, false, false, atk, dmg, null)
endmethod
endstruct
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
/*
This code will only run the Cinematic for MalGanis
*/
scope MalGanisEnter
struct MalGanisEnter
static boolean rct = true
static boolean tim = true
private static method timerExp takes nothing returns nothing
if tim then
set rct = false
call ExecuteFunc("StartMalCin") //starts MalGanisCinematic
call RectEvent.removeEnter(gg_rct_007_EnterVillage)
call RectEvent.removeEnter(gg_rct_008_EnterVillage)
endif
call DestroyTimer(GetExpiredTimer())
endmethod
private static method FilterEntering takes unit u returns boolean
return GetOwningPlayer(u)==Player(0) or GetOwningPlayer(u)==Player(2)
endmethod
private static method Enter takes nothing returns nothing
if FilterEntering(GetFilterUnit()) then
if rct then
set tim = false
call ExecuteFunc("StartMalCin") //starts MalGanisCinematic
call RectEvent.removeEnter(gg_rct_007_EnterVillage)
call RectEvent.removeEnter(gg_rct_008_EnterVillage)
endif
endif
endmethod
static method start takes integer difficulty returns nothing
call RectEvent.enter(gg_rct_007_EnterVillage,function thistype.Enter)
call RectEvent.enter(gg_rct_008_EnterVillage,function thistype.Enter)
if difficulty==3 then
call TimerStart(CreateTimer(),MALGANIS_ENTERS_MAP*2,false,function thistype.timerExp) //180
else
call TimerStart(CreateTimer(),MALGANIS_ENTERS_MAP,false,function thistype.timerExp) //90
endif
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library HeroPickSkill initializer init uses RegisterPlayerUnitEvent
function HeroPickSkill takes unit u returns nothing
if u==udg_Uther then
if GetHeroLevel(u)==15 then
call SuspendHeroXP(u,true)
endif
endif
if u==udg_Arthas then
//holy bolt, sun ray, devotion aura, Touch of Light, Dancing Flame
call HeroLearn.learnSkill(u,'AHhb','A00M','AHad','A00C','A00D')
elseif u==udg_MalGanis then
//cursed soul, carrion swarm, vampiric aura, diabolic countdown
call HeroLearn.learnSkill(u,'A00K','AUcs','AUav','A00L',1)
elseif u==udg_Jaina then
//blizzard, briliance aura, water elemental, massive blast, cosmic twist
call HeroLearn.learnSkill(u,'AHbz','AHab','AHwe','A00H','A00I')
endif
endfunction
private function learn takes nothing returns nothing
call HeroPickSkill(GetTriggerUnit())
endfunction
private function init takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_LEVEL,function learn)
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
/*
===Confinement v1.5
===Made by: Mckill2009
REQUIRES and CREDITS:
- T32 by Jesus4Lyf
- BoundSentinel by Vexorian
HOW TO USE:
- Make a new trigger and convert to custom text via EDIT >>> CONVERT CUSTOM TEXT
- Copy ALL that is written here (overwrite the existing texts in the trigger)
- Copy the Dummy unit and the custom ability OR make your own
- You MUST input or change the correct raw ID's (see below)
- To view raw ID, press CTRL+D in the object editor
*/
library Confinement uses T32, BoundSentinel
globals
//the SPELL_ID and ORDER_ID must match to each other because of channeling reasons
private constant integer SPELL_ID = 'A005' //blizzard
private constant integer FENCE1_ID = 'h00A'
private constant integer FENCE2_ID = 'h00B'
private constant integer AURA_ID = 'h009'
private constant integer ORDER_ID = 852089 //blizzard
private constant attacktype ATT = ATTACK_TYPE_PIERCE
private constant damagetype DAM = DAMAGE_TYPE_DEATH
private constant real PRISON_AOE = 400 //this is the prison range
private constant real AOE_DAM = 80 //this is the damage range of the fence
private constant real ROTATION_SPEED = 0.1 //rotation in radians, so this should be low
private real Pull
endglobals
private function UnitAlive takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and u!=null
endfunction
private function GetDuration takes integer i returns real
return 5 + i * 5.
endfunction
private function GetDamage takes integer i returns real
return 3 + i * 5.
endfunction
private struct Confinement
unit caster
unit aura
unit fence1
unit fence2
real x
real y
real hurt
real angle1
real angle2
real duration
boolean isChanneling
private method filterunits takes unit u returns boolean
return IsUnitEnemy(u, GetOwningPlayer(.aura)) and UnitAlive(u) and not /*
*/ IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_MECHANICAL)
endmethod
private method damageThem takes unit u returns nothing
local unit first
call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(u), GetUnitY(u), AOE_DAM, null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if .filterunits(first) then
call UnitDamageTarget(.aura, first, .hurt , false, false, ATT, DAM, null)
endif
call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
endmethod
method periodic takes nothing returns nothing
local real angle
local real dist
local real x1
local real y1
local real x2
local real y2
local unit u
if UnitAlive(.caster) and .isChanneling then
if .duration > 0 and GetUnitCurrentOrder(.caster)==ORDER_ID then
set .angle1 = .angle1 + ROTATION_SPEED
set .angle2 = .angle2 - ROTATION_SPEED
set .duration = .duration - T32_PERIOD
call SetUnitX(.fence1, .x + PRISON_AOE * Cos(.angle1))
call SetUnitY(.fence1, .y + PRISON_AOE * Sin(.angle1))
call SetUnitX(.fence2, .x + PRISON_AOE * Cos(.angle2))
call SetUnitY(.fence2, .y + PRISON_AOE * Sin(.angle2))
//===ConfineThem
call GroupEnumUnitsInRange(bj_lastCreatedGroup, .x, .y, PRISON_AOE, null)
loop
set u = FirstOfGroup(bj_lastCreatedGroup)
exitwhen u==null
if filterunits(u) then
set x1 = GetUnitX(u)
set y1 = GetUnitY(u)
set x2 = x1-.x
set y2 = y1-.y
set angle = Atan2(y-y1, x-x1)
set dist = (x2*x2) + (y2*y2)
if dist > Pull then
call SetUnitX(u, x1 + 30 * Cos(angle))
call SetUnitY(u, y1 + 30 * Sin(angle))
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup, u)
endloop
//===Fence1, DamageThem
call damageThem(.fence1)
//===Fence2, DamageThem
call damageThem(.fence2)
else
set .isChanneling = false
call IssueImmediateOrder(.caster, "stop")
endif
else
call KillUnit(.aura)
call KillUnit(.fence1)
call KillUnit(.fence2)
set .aura = null
set .fence1 = null
set .fence2 = null
call .stopPeriodic()
call .destroy()
endif
endmethod
implement T32x
static method cast takes nothing returns boolean
local thistype this
local integer level
local player p
if GetSpellAbilityId()==SPELL_ID then
set this = create()
set p = GetTriggerPlayer()
set .caster = GetTriggerUnit()
set level = GetUnitAbilityLevel(caster, SPELL_ID)
set .x = GetSpellTargetX()
set .y = GetSpellTargetY()
set .aura = CreateUnit(p, AURA_ID, .x, .y, 0)
set .fence1 = CreateUnit(p, FENCE1_ID, .x, .y, 0)
set .fence2 = CreateUnit(p, FENCE2_ID, .x, .y, 0)
set .duration = GetDuration(level)
set .hurt = GetDamage(level)
set .angle1 = 0
set .angle2 = 0
set .isChanneling = true
call .startPeriodic()
set p = null
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, function thistype.cast)
set Pull = (PRISON_AOE-50)*(PRISON_AOE-50)
set t = null
endmethod
endstruct
endlibrary
//TESH.scrollpos=59
//TESH.alwaysfold=0
/*
===DamageShield v1.1
===By Mckill2009
*/
scope DamageShield
globals
private constant integer SPELL_ID = 'A004' //Raw code of the Hero spell
private constant integer DUMMY_ID = 'h008' //Raw code of the Breath of Fire
private constant string SFX = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
endglobals
struct DamageShield
unit caster
unit target
unit dummy
real angle
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
set .angle = .angle + 0.1
call SetUnitX(.dummy,GetUnitX(.target)+90*Cos(.angle))
call SetUnitY(.dummy,GetUnitY(.target)+90*Sin(.angle))
else
call DestroyEffect(AddSpecialEffectTarget(SFX,.target,"origin"))
call SetWidgetLife(.target, .life + .heal)
call d.remove(GetHandleId(.target))
call KillUnit(.dummy)
call .stopPeriodic()
set .caster = null
set .dummy = null
set .target = null
call .destroy()
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 level = GetUnitAbilityLevel(.caster, SPELL_ID)
set .duration = getDuration(level)
set .life = GetWidgetLife(u)
set .heal = .life * getRestoreLife(level)
set .dummy = CreateUnit(GetTriggerPlayer(),DUMMY_ID,0,0,0)
set .angle = 0
set d[targetID] = 0
set d.real[targetID] = GetWidgetLife(u) //getRestoreLife(level)
call .startPeriodic()
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
set d = Table.create()
endmethod
endstruct
endscope
//TESH.scrollpos=66
//TESH.alwaysfold=0
/*
===Luck Aura v1.3
===By Mckill2009
INSTALLATION:
- Copy the custom dummy unit/buff/abilities to your map
- You MUST input or change the correct rawID's (see below)
- To view rawID, press CTRL+D in the object editor
- Copy ALL the required libraries and the LuckAura trigger to your map (SimError is optional)
- Change the CONFIGURABLES and AOE if needed
- DONE!
REQUIRED LIBRARIES:
- RegisterPlayerUnitEvent by Magtheridon96
- Table by Bribe
- SpellEffectEvent by Bribe
- *SimError (Optional)
FULL DESCRIPTION:
Once activated, it brings good luck to the caster and his allies, giving a chance to drop random items from kills depending on the level of the spell.
|cffffcc00Level 1|r - 15% of Level 1 items,
|cffffcc00Level 2|r - 20% of Level 1-2 items.
|cffffcc00Level 3|r - 25% of Level 1-3 items.
|cffffcc00Level 4|r - 30% of Level 1-4 items.
|cffffcc00Level 5|r - 35% of Level 1-5 items.
|cffffcc00Spell lasts 30 seconds.|r
*/
scope LuckAura
globals
private Table lu //NEVER TOUCH THIS!
private constant itemtype ITEMID = ITEM_TYPE_ANY
private constant real AOE = 600 //Buff Effect or range, make sure that this matches the AOE of DUMMY_SPELL_ID
private constant integer SPELL_ID = 'A003' //Hero ability rawID
private constant integer DUMMY_SPELL_ID = 'A002' //Buff Aura rawID
private constant integer DUMMY_ID = 'h000' //Dummy Unit rawID
private constant integer BUFF = 'B002' //Buff Aura rawID
endglobals
//===CONFIGURABLES:
private function GetDuration takes integer i returns real
return 30.
endfunction
private function GetChance takes integer i returns integer
return 10 + i * 5
endfunction
//===END OF CONFIGURABLES
private function EnemyDies takes unit u, unit k returns nothing
local unit first
local integer level
call GroupEnumUnitsInRange(bj_lastCreatedGroup,GetUnitX(k),GetUnitY(k),AOE,null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
exitwhen first==null
if UnitAlive(first) and lu.has(GetHandleId(first)) then
set level = lu[GetHandleId(first)]
if GetRandomInt(1,100) <= GetChance(level) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
if level==1 then
call CreateItem(ChooseRandomItemEx(ITEMID, 1), GetUnitX(u), GetUnitY(u))
else
call CreateItem(ChooseRandomItemEx(ITEMID, GetRandomInt(1,level+1)), GetUnitX(u), GetUnitY(u))
endif
endif
endif
call GroupRemoveUnit(bj_lastCreatedGroup,first)
endloop
endfunction
private function EnemyDiesCond takes nothing returns nothing
if (GetUnitAbilityLevel(GetKillingUnit(), BUFF) > 0) and IsUnitEnemy(GetKillingUnit(),GetTriggerPlayer()) then
call EnemyDies(GetTriggerUnit(), GetKillingUnit())
endif
endfunction
private struct Luck
unit caster
unit dummy
real duration
private static timer tim
private static integer index = 0
private static integer array indexAR
private static thistype DATA
private static method looper takes nothing returns nothing
local thistype this
local integer i = 0
loop
set i = i+1
set this = indexAR[i]
if .duration > 0 and UnitAlive(.caster) then
set .duration = .duration - 0.5
call SetUnitX(.dummy, GetUnitX(.caster))
call SetUnitY(.dummy, GetUnitY(.caster))
else
call KillUnit(.dummy)
call lu.remove(GetHandleId(.caster))
set .caster = null
set .dummy = null
call .destroy()
set indexAR[i] = indexAR[index]
set indexAR[index] = this
set i = i-1
set index = index - 1
if index==0 then
call PauseTimer(tim)
call DestroyTimer(tim)
endif
endif
exitwhen i==index
endloop
endmethod
private static method cast takes nothing returns nothing
local thistype this
local unit u = GetTriggerUnit()
local integer casterID = GetHandleId(u)
local integer level
local player p = GetTriggerPlayer()
if not lu.has(casterID) then
set this = allocate()
set level = GetUnitAbilityLevel(u, SPELL_ID)
set lu[casterID] = level
set .caster = GetTriggerUnit()
set .dummy = CreateUnit(p, DUMMY_ID, GetUnitX(u), GetUnitY(u), 0)
set .duration = GetDuration(level)
if index==0 then
set tim = CreateTimer()
call TimerStart(tim, 0.5, true, function thistype.looper)
endif
set index = index + 1
set indexAR[index] = this
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, level)
else
call IssueImmediateOrder(u, "stop")
static if LIBRARY_SimError then
call SimError(p, "Can't be used yet!")
endif
endif
set u = null
set p = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function EnemyDiesCond)
set lu = Table.create()
endmethod
endstruct
endscope
//TESH.scrollpos=19
//TESH.alwaysfold=0
scope HealingBolts
globals
private constant integer SPELL_ID = 'A008' //Passive ability
private constant integer DUMMY_SPELL_ID = 'A007' //Firebolt
private constant integer ORDER_ID = 852231 //Firebolt
private constant real AOE = 800
endglobals
struct HealingBolts extends array
private static method chance takes integer level returns integer
return 5*level+5
endmethod
private static method orderID takes integer oid returns boolean
return oid==0 or oid==851983 or oid==851986
endmethod
private static method action takes nothing returns boolean
local unit first
local unit u
local integer i
local integer level
if GetUnitAbilityLevel(GetAttacker(),SPELL_ID)>0 and IsUnitEnemy(GetAttacker(), GetTriggerPlayer()) then
set u = GetAttacker()
set level = GetUnitAbilityLevel(u,SPELL_ID)
if GetRandomInt(0,100) < chance(level) then
call GroupEnumUnitsInRange(bj_lastCreatedGroup,GetUnitX(u),GetUnitY(u),AOE,null)
loop
set first = FirstOfGroup(bj_lastCreatedGroup)
if UnitAlive(first) and IsUnitEnemy(first, GetTriggerPlayer()) and first!=u and orderID(GetUnitCurrentOrder(first)) then
call MuiDummyCasters.hitTarget(GetOwningPlayer(u), first, GetUnitX(u), GetUnitY(u), DUMMY_SPELL_ID, ORDER_ID, level)
endif
call GroupRemoveUnit(bj_lastCreatedGroup,first)
endloop
endif
endif
return false
endmethod
private static method onInit takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.action)
call PreloadSpell(DUMMY_SPELL_ID)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope HolyLight
globals
private constant integer SPELL_ID = 'AHhb' //Holy Light
private constant integer DAMAGE_SPELL_ID = 'A009' //Chain Lightning
private constant integer HEAL_SPELL_ID = 'A00A' //Healing Wave
private constant real AOE = 800
endglobals
struct HolyLight extends array
private static method filterAllies takes nothing returns boolean
set TempU = GetFilterUnit()
return not IsUnitType(TempU,UNIT_TYPE_STRUCTURE) and IsUnitAlly(TempU, TempPlayer)
endmethod
private static method filterEnemies takes nothing returns boolean
set TempU = GetFilterUnit()
return not IsUnitType(TempU,UNIT_TYPE_STRUCTURE) and IsUnitEnemy(TempU, TempPlayer)
endmethod
private static method cast takes nothing returns nothing
local unit first
local unit u = GetSpellTargetUnit()
local integer i
local integer level = GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID)
set TempPlayer = GetTriggerPlayer()
if IsUnitEnemy(u, TempPlayer) then
set first = GetClosestUnitInRange(GetUnitX(u),GetUnitY(u),AOE,Filter(function thistype.filterAllies))
call CastToUnit(TempPlayer, first, GetUnitX(u), GetUnitY(u), HEAL_SPELL_ID, 852501, level)
set first = null
else
set first = GetClosestUnitInRange(GetUnitX(u),GetUnitY(u),AOE,Filter(function thistype.filterEnemies))
call CastToUnit(TempPlayer, first, GetUnitX(u), GetUnitY(u), DAMAGE_SPELL_ID, 852119, level)
set first = null
endif
set u = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.cast)
call PreloadSpell(SPELL_ID)
call PreloadSpell(DAMAGE_SPELL_ID)
call PreloadSpell(HEAL_SPELL_ID)
endmethod
endstruct
endscope
//TESH.scrollpos=27
//TESH.alwaysfold=0
//===========================================================================
//The caster uses his light powers to summon waves of Light Rays around him,
//damaging enemy Undead units nearby, and healing allied units
//
//Requires TimerUtils
//
//@author Flame_Phoenix
//
//@credits
//- Blackroot (aka Modeler), the first creator, gave me the basic idea and concept
//- the-thingy, by telling me where to use bj_DEGTORAD and why
//- Alexander244, for helping me fixing a bug with parenthesis ... lol
//- Anitarf, for suggesting a slight improvement for the code
//- My first teacher of vJASS: Blue_Jeans
//- All other people I forgot or ignored
//
//@version 1.5
//===========================================================================
scope SunRay initializer Init
//needed for spell's core, don't change
private keyword tmpPlayer
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
globals
private constant integer AID = 'A00M' //raecode of ability
private constant real LIGHT_RADIUS = 100 //radius of each single light
private constant string LIGHT_EFFECT = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" //the effect of the light
private constant real LIGHT_INTERVAL = 0.2 //the interval that separates each light
private constant boolean CASTER_ANG = true //if true we consider the caster's facing angle, else we don't
endglobals
private constant function Damage takes integer level returns real
return level * 5. //the damage the enemy undead units will take
endfunction
private constant function Heal takes integer level returns real
return level * 10. //the heal your allied units will receive
endfunction
private constant function LightNumber takes integer level returns integer
//each Sun Ray of (4 + level) lights
return 4 + level
endfunction
private constant function RayNumber takes integer level returns integer
//the number of Sun Rays of the spell
return 5 + level
endfunction
private function AllyTargets takes nothing returns boolean
//what allied units will be affected by the spell
return IsUnitAlly(GetFilterUnit(), tmpPlayer) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
private function EnemyTargets takes nothing returns boolean
//what enemy units will be affected by the spell
return IsUnitEnemy(GetFilterUnit(), tmpPlayer) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (GetUnitRace(GetFilterUnit()) == RACE_UNDEAD) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
globals
private player tmpPlayer
private group g
private boolexpr enemies
private boolexpr allies
endglobals
private struct MyStruct
unit caster
real casterX
real casterY
integer level
timer t
real distInc
integer lightsCreated
static method create takes unit caster returns MyStruct
local MyStruct data = MyStruct.allocate()
//setting variables
set data.caster = caster
set data.casterX = GetUnitX(caster)
set data.casterY = GetUnitY(caster)
set data.level = GetUnitAbilityLevel(caster, AID)
set data.t = NewTimer() //this will create the lights =)
set data.distInc = LIGHT_RADIUS
set data.lightsCreated = 0
return data
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
//===========================================================================
private function CreateLights takes nothing returns nothing
local MyStruct data = MyStruct(GetTimerData(GetExpiredTimer()))
local unit f
local real x //the X position of the effect
local real y //the Y position of the effect
local real angle
local integer i //a counter for the loop
//we set this variable depending if we want to consider the
//caster's angle or not
if CASTER_ANG then
set angle = GetUnitFacing(data.caster)
else
set angle = 0
endif
//if we didn't pass the number of lights, than we create lights
//again, however, if we did, we end everything
if data.lightsCreated < LightNumber(data.level) then
//in this outer loop we create the Sun Rays
set i = 0
loop
exitwhen(i == RayNumber(data.level))
set x = data.casterX + data.distInc * Cos(angle)
set y = data.casterY + data.distInc * Sin(angle)
call DestroyEffect(AddSpecialEffect(LIGHT_EFFECT, x, y))
//in this first double inner loop we select the enemy units and damage them
set tmpPlayer = GetOwningPlayer(data.caster)
call GroupEnumUnitsInRange(g, x, y, LIGHT_RADIUS, enemies)
loop
set f = FirstOfGroup(g)
exitwhen(f == null)
call GroupRemoveUnit(g, f)
call UnitDamageTarget(data.caster, f, Damage(data.level), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
endloop
//in this second double inner loop we select the allied units and heal them
set tmpPlayer = GetOwningPlayer(data.caster)
call GroupEnumUnitsInRange(g, x, y, LIGHT_RADIUS, allies)
loop
set f = FirstOfGroup(g)
exitwhen(f == null)
call GroupRemoveUnit(g, f)
call SetWidgetLife(f, GetWidgetLife(f) + Heal(data.level))
endloop
set angle = angle + (360 / RayNumber(data.level)) * bj_DEGTORAD
set i = i + 1
endloop
set data.distInc = data.distInc + LIGHT_RADIUS
set data.lightsCreated = data.lightsCreated + 1
else
call data.destroy()
endif
endfunction
//===========================================================================
private function Conditions takes nothing returns boolean
local MyStruct data
if GetSpellAbilityId() == AID then
set data = MyStruct.create(GetTriggerUnit())
//now we start the timer !
call SetTimerData(data.t, integer(data))
call TimerStart(data.t, LIGHT_INTERVAL, true, function CreateLights)
endif
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger SunRayTrg = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( SunRayTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( SunRayTrg, Condition( function Conditions ) )
set SunRayTrg = null
//setting globals
set g = CreateGroup()
set enemies = Condition(function EnemyTargets)
set allies = Condition(function AllyTargets)
//preloading effect
call Preload(LIGHT_EFFECT)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
//===========================================================================
// Chicken Burst spell
// by Garfield1337
//===========================================================================
scope ChickenBurst initializer Init
globals
private constant integer CHICKEN_ID = 'h00C' //Raw code of chicken
private constant integer DUMMY_ID = 'h000' //Raw code of dummy
private constant integer EGG_ID = 'h00C' //Raw code of egg
private constant integer ABILITY_ID = 'A00C' //Raw code of ability
private constant real EGG_SPEED = 24.00 //Egg's flying speed
private constant real CHICKEN_SPEED = 4.00 //Chickens' flying speed
private constant real CHICKEN_HEIGHT = 600.0 //Chickens' max height when flying
private constant real CHICKEN_EXPLOSION_AOE = 160.0 //Radius of area which chickens damage
private constant real CHICKEN_RANGE_MIN = 150.0 //Minimum range chickens fly from egg
private constant real CHICKEN_RANGE_MAX = 350.0 //Maximum range chickens fly from egg
private constant string CHICKEN_EXPLOSION_SFX = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl" //Effect created upon chicken explosion
private constant boolean DAMAGE_ALLIED = false //Should the spell damage allied units?
private constant boolean DAMAGE_ENEMY = true //Should the spell damage enemy units?
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL //Explosion attack type
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Explosion damage type
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS //Explosion weapon type
private constant boolean DESTROY_DESTRUCTABLES = false //Should the explosion destroy destructables?
private constant boolean TREES_ONLY = false //Should the explosion only affect trees as destructables?
//If DESTROY_DESTRUCTABLES is false,DESTRUCTABLE_DAMAGE can be set to a number greater than 0.00
//to damage destructables instead of instantly killing them
//If DESTROY_DESTRUCTABLES is false and DESTRUCTABLE_DAMAGE is 0.00 the spell will not affect any destructables
private constant real DESTRUCTABLE_DAMAGE = 40.0
endglobals
private keyword INVALID_TARGETS //Don't change this line
private function InvalidTargets takes nothing returns nothing
//Which unit types shouldn't be affected by spell
set INVALID_TARGETS[0] = UNIT_TYPE_FLYING
set INVALID_TARGETS[1] = UNIT_TYPE_STRUCTURE
set INVALID_TARGETS[2] = UNIT_TYPE_MAGIC_IMMUNE
endfunction
private function Damage takes integer lvl returns real
//Damage per explosion per level
return 50.0 + 20.0 * lvl
endfunction
private function ChickenAmount takes integer lvl returns integer
//Number of chickens per level
return 5 + 5 * lvl
endfunction
private function ChickenDuration takes nothing returns real
//Time before each chicken explodes
return GetRandomReal(1.00,2.00)
endfunction
//End of configuration part
//===========================================================================
globals
private hashtable Hash = InitHashtable()
private group g = CreateGroup()
private location l = Location(0.00,0.00)
private unittype array INVALID_TARGETS
private group EGGS = CreateGroup()
private group CHICKENS = CreateGroup()
private rect r
private unit t
private real r1
private real r2
private real r3
private real r4
private integer i1
private integer i2
private integer i3
private unit u1
private unit u2
private real x1
private real y1
private real x2
private real y2
endglobals
private function Parabola takes real y0, real y1, real h, real d, real x returns real
local real A = (2*(y0+y1)-4*h)/(d*d)
local real B = (y1-y0-A*d*d)/d //Credits to moyack for the parabola function
return A*x*x + B*x + y0
endfunction
private function Cast takes nothing returns boolean
if GetSpellAbilityId() == ABILITY_ID then
set x1 = GetUnitX(GetTriggerUnit())
set y1 = GetUnitY(GetTriggerUnit())
//Creating egg
set u1 = CreateUnit(GetOwningPlayer(GetTriggerUnit()),EGG_ID,x1,y1,GetRandomReal(0.00,360.0))
set x2 = GetSpellTargetX()
set y2 = GetSpellTargetY()
set i1 = GetHandleId(u1)
//Moving the egg on caster's position since it is created beside
call SetUnitX(u1,x1)
call SetUnitY(u1,y1)
//Enabling the egg to fly by adding and removing crow form
call UnitAddAbility(u1,'Amrf')
call UnitRemoveAbility(u1,'Amrf')
//Adding a slight height to egg so it doesn't fly off the ground
call SetUnitFlyHeight(u1,30.0,0.00)
//Saving angle between caster and target
set r1 = Atan2(y2 - y1,x2 - x1)
call SaveReal(Hash,i1,0,r1)
//To prevent the egg from hitting cliff while in air before reaching it's target
//i check height of terrain on egg's way and find the peak (if any)
//then later i add the peak's height to max height of parabola in loop
set r2 = 0
set r3 = SquareRoot(Pow(x2 - x1,2) + Pow(y2 - y1,2))
set r4 = 0
loop
call MoveLocation(l,x1 + r2 * Cos(r1),y1 + r2 * Sin(r1))
if r2 == 0 then
//Saving caster's height for parabola
call SaveReal(Hash,i1,1,GetLocationZ(l) + 30.0)
elseif r2 >= r3 then
//Saving target's height for parabola
call SaveReal(Hash,i1,2,GetLocationZ(l))
exitwhen true
endif
if GetLocationZ(l) > r4 then
set r4 = GetLocationZ(l)
endif
set r2 = r2 + 32.0
endloop
//Saving peak's height
call SaveReal(Hash,i1,3,r4)
//Saving total distance
call SaveReal(Hash,i1,4,r3)
//Saving passed distance which is 0
call SaveReal(Hash,i1,5,0.00)
//Saving spell level
call SaveInteger(Hash,i1,6,GetUnitAbilityLevel(GetTriggerUnit(),ABILITY_ID))
//Saving a custom integer which will be used in loop
call SaveInteger(Hash,i1,7,0)
call GroupAddUnit(EGGS,u1)
endif
return false
endfunction
private function Loop1 takes nothing returns nothing
//Egg loop
set u1 = GetEnumUnit()
set i1 = GetHandleId(u1)
set i2 = LoadInteger(Hash,i1,7)
set i3 = 0
if i2 == 0 then //At first, the egg is just flying
//Egg movement
set x1 = GetUnitX(u1) + EGG_SPEED * Cos(LoadReal(Hash,i1,0))
set y1 = GetUnitY(u1) + EGG_SPEED * Sin(LoadReal(Hash,i1,0))
call SetUnitX(u1,x1)
call SetUnitY(u1,y1)
call SaveReal(Hash,i1,5,LoadReal(Hash,i1,5) + EGG_SPEED)
call MoveLocation(l,x1,y1)
//Setting egg's flying height with parabola
call SetUnitFlyHeight(u1,Parabola(LoadReal(Hash,i1,1),LoadReal(Hash,i1,2),LoadReal(Hash,i1,4) / 3 + LoadReal(Hash,i1,3),LoadReal(Hash,i1,4),LoadReal(Hash,i1,5)) - GetLocationZ(l),0.00)
if LoadReal(Hash,i1,5) >= LoadReal(Hash,i1,4) then
//Playing egg's birth animation to make it look as if it jumped off the ground
call SetUnitAnimation(u1,"birth")
//If the egg hits ground,the custom integer is set to 1 to prevent egg from moving anymore
call SaveInteger(Hash,i1,7,1)
endif
else
set x1 = GetUnitX(u1)
set y1 = GetUnitY(u1)
//The custom integer is now used as a counter to simulate wait function
call SaveInteger(Hash,i1,7,i2 + 1)
if i2 == 20 then //Upon reaching 20 the egg explodes...
set u2 = CreateUnit(GetOwningPlayer(u1),DUMMY_ID,x1,y1,GetRandomReal(0.00,360.0))
call SetUnitX(u2,x1)
call SetUnitY(u2,y1)
//Mine's death spell animation is used as a small explosion for egg
call SetUnitAnimation(u2,"death spell")
call UnitApplyTimedLife(u2,'BTLF',2.00)
elseif i2 == 25 then //...and after a short delay,chickens burst
loop
set i3 = i3 + 1
//Creating chickens until it hits wanted amount
exitwhen i3 > ChickenAmount(LoadInteger(Hash,i1,6))
//Defining the facing angle of each chicken to make them form a circle
set r1 = 6.28318 / ChickenAmount(LoadInteger(Hash,i1,6)) * (i3 - 1) + GetRandomReal(0.00,6.28318 / ChickenAmount(LoadInteger(Hash,i1,6)))
set u2 = CreateUnit(GetOwningPlayer(u1),CHICKEN_ID,x1,y1,r1 * bj_RADTODEG)
call SetUnitX(u2,x1)
call SetUnitY(u2,y1)
set i2 = GetHandleId(u2)
//Enabling the chickens to fly
call UnitAddAbility(u2,'Amrf')
call UnitRemoveAbility(u2,'Amrf')
//Defining the coordinates for each chicken to fall on
set x2 = x1 + GetRandomReal(CHICKEN_RANGE_MIN,CHICKEN_RANGE_MAX) * Cos(r1)
set y2 = y1 + GetRandomReal(CHICKEN_RANGE_MIN,CHICKEN_RANGE_MAX) * Sin(r1)
//Saving the egg's height for parabola
call MoveLocation(l,x1,y1)
call SaveReal(Hash,i2,0,GetLocationZ(l))
//Saving target coordinates' height
call MoveLocation(l,x2,y2)
call SaveReal(Hash,i2,1,GetLocationZ(l))
//Saving total distance
call SaveReal(Hash,i2,2,SquareRoot(Pow(x2 - x1,2) + Pow(y2 - y1,2)))
//Saving passed distance
call SaveReal(Hash,i2,3,0.00)
//Saving spell level, inherited from the egg
call SaveInteger(Hash,i2,4,LoadInteger(Hash,i1,6))
call GroupAddUnit(CHICKENS,u2)
endloop
//After chickens are created, the egg is destroyed
call KillUnit(u1)
call GroupRemoveUnit(EGGS,u1)
call FlushChildHashtable(Hash,i1)
endif
endif
endfunction
private function Loop2 takes nothing returns nothing
//Chicken loop
set u1 = GetEnumUnit()
set i1 = GetHandleId(u1)
//Chicken movement
set x1 = GetUnitX(u1) + CHICKEN_SPEED * Cos(GetUnitFacing(u1) * bj_DEGTORAD)
set y1 = GetUnitY(u1) + CHICKEN_SPEED * Sin(GetUnitFacing(u1) * bj_DEGTORAD)
call SetUnitX(u1,x1)
call SetUnitY(u1,y1)
call MoveLocation(l,x1,y1)
call SaveReal(Hash,i1,3,LoadReal(Hash,i1,3) + CHICKEN_SPEED)
//Setting chickens' flying height with parabola
call SetUnitFlyHeight(u1,Parabola(LoadReal(Hash,i1,0),LoadReal(Hash,i1,1),CHICKEN_HEIGHT + RMaxBJ(LoadReal(Hash,i1,0),LoadReal(Hash,i1,1)),LoadReal(Hash,i1,2),LoadReal(Hash,i1,3)) - GetLocationZ(l),0.00)
if LoadReal(Hash,i1,3) >= LoadReal(Hash,i1,2) then
//Upon hitting ground,each chicken is ordered to move to a random point
//The distance is defined by chickens' movement speed and time before they explode
set r1 = ChickenDuration()
set r2 = GetUnitMoveSpeed(u1) * r1 * 3
set x1 = GetUnitX(u1) + r2 * Cos(GetRandomReal(0.00,6.28318))
set y1 = GetUnitY(u1) + r2 * Sin(GetRandomReal(0.00,6.28318))
call IssuePointOrder(u1,"smart",x1,y1)
//Applying timed life to chickens to make them explode after the set time
call UnitApplyTimedLife(u1,'BTLF',r1)
call GroupRemoveUnit(CHICKENS,u1)
endif
endfunction
private function LoopInit takes nothing returns nothing
call ForGroup(EGGS,function Loop1)
call ForGroup(CHICKENS,function Loop2)
endfunction
private function ExplosionFilter takes nothing returns boolean
//Explosion's filter
local boolean b = true
set i1 = 0
set u1 = GetTriggerUnit()
set u2 = GetFilterUnit()
//Checking whether filter unit is enemy or ally
//whether the corresponding booleans are true or false
//and whether the filter unit is alive
if ((DAMAGE_ENEMY == true and IsUnitEnemy(u2,GetOwningPlayer(u1))) or (DAMAGE_ALLIED == true and IsUnitAlly(u2,GetOwningPlayer(u1)))) and IsUnitType(u2, UNIT_TYPE_DEAD) == false then
loop
//Looping through all invalid target types
//If the filter unit belongs to any,it's invalid
exitwhen INVALID_TARGETS[i1] == null
if IsUnitType(u2,INVALID_TARGETS[i1]) then
set b = false
endif
set i1 = i1 + 1
endloop
if b then
//If the filter unit passes all checks, it's damaged
call UnitDamageTarget(u1,u2,Damage(LoadInteger(Hash,GetHandleId(u1),4)),true,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
endif
endif
return false
endfunction
private function DestructableEnum takes nothing returns nothing
local destructable d = GetFilterDestructable()
//Checking destructable life and filtering out dead ones
if GetDestructableLife(d) > 0 then
set x1 = GetDestructableX(d)
set y1 = GetDestructableY(d)
//If only trees should be damaged or destroyed, a tree-check is applied
if TREES_ONLY then
call IssueTargetOrder(t,"harvest",d)
if not(GetUnitCurrentOrder(t) == OrderId("harvest")) then
//In case destructable is not a tree, the function ends
call IssueImmediateOrder(t,"stop")
return
endif
call IssueImmediateOrder(t,"stop")
endif
if DESTROY_DESTRUCTABLES then
//Destroying destructable if the configurable boolean is true...
call KillDestructable(d)
else
//...if not, it's damaged instead
if DESTRUCTABLE_DAMAGE < GetDestructableLife(d) then
call SetDestructableAnimation(d,"stand hit")
endif
call SetDestructableLife(d,GetDestructableLife(d) - DESTRUCTABLE_DAMAGE)
endif
endif
set d = null
endfunction
private function Death takes nothing returns boolean
set u1 = GetTriggerUnit()
set x1 = GetUnitX(u1)
set y1 = GetUnitY(u1)
if GetUnitTypeId(u1) == CHICKEN_ID then
//When a chicken dies, it creates the SFX and enumerates
//the surrounding units and destructables for damage
call DestroyEffect(AddSpecialEffect(CHICKEN_EXPLOSION_SFX,GetUnitX(u1),GetUnitY(u1)))
call GroupEnumUnitsInRange(g,x1,y1,CHICKEN_EXPLOSION_AOE,Filter(function ExplosionFilter))
//Checking if destructable annihilation or damage is ON
if DESTROY_DESTRUCTABLES or DESTRUCTABLE_DAMAGE != 0.00 then
call MoveRectTo(r,x1,y1)
call EnumDestructablesInRect(r,null,function DestructableEnum)
endif
call FlushChildHashtable(Hash,GetHandleId(u1))
elseif GetUnitTypeId(u1) == DUMMY_ID then
//When a dummy dies,it's instantly removed to hide the dying animation
call RemoveUnit(u1)
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t1 = CreateTrigger()
local trigger t2 = CreateTrigger()
call InvalidTargets()
set t = CreateUnit(Player(15),DUMMY_ID,0.00,0.00,0.00)
call ShowUnit(t,false)
set r = Rect(-CHICKEN_EXPLOSION_AOE,-CHICKEN_EXPLOSION_AOE,CHICKEN_EXPLOSION_AOE,CHICKEN_EXPLOSION_AOE)
set i1 = 0
loop
call TriggerRegisterPlayerUnitEvent(t1,Player(i1),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
call TriggerRegisterPlayerUnitEvent(t2,Player(i1),EVENT_PLAYER_UNIT_DEATH,null)
set i1 = i1 + 1
exitwhen i1 == 16
endloop
call TriggerAddCondition(t1,Condition(function Cast))
call TriggerAddCondition(t2,Condition(function Death))
call TimerStart(CreateTimer(),0.03,true,function LoopInit)
set t1 = null
set t2 = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* Cursed Soul Spell *
//* By Moyack *
//* Version 1.0 *
//* *
//******************************************************************************
//******************************************************************************
//* The constant functions where you can modify the ability properties
//*
constant function Cursed_Soul_SpellID takes nothing returns integer
// Returns the spell ID. Based on Raise Dead ability.
return 'A00K'
endfunction
constant function Cursed_Soul_LifeStolen takes integer level returns real
// Returns the percentge of life stolen to a random unit
return 0.15 + 0.1 * (level - 1)
endfunction
constant function Cursed_Soul_DamageRate takes nothing returns real
// Returns the percentge of life stolen to a random unit
return 5.0
endfunction
constant function Cursed_Soul_PossessDur takes integer level returns real
// Returns the possession duration for the target unit
return 20.0 + 10.0 * (level - 1)
endfunction
constant function Cursed_Soul_AOE takes nothing returns real
// Returns the AOE where the spell will try to find any enemy.
return 1000.0
endfunction
constant function Cursed_Soul_Radius takes nothing returns real
// Returns the distace which the soul will have from the caster.
return 300.0
endfunction
constant function Cursed_Soul_StartEffect takes nothing returns string
// Returns the spirit effect for the spell.
return "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
endfunction
constant function Cursed_Soul_SpiritEffect takes nothing returns string
// Returns the spirit effect for the spell.
return "Abilities\\Spells\\Human\\Banish\\BanishTarget.mdl"
endfunction
constant function Cursed_Soul_SpiritEffectDead takes nothing returns string
// Returns the spirit effect when the soul dies before reaching the target.
return "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
endfunction
constant function Cursed_Soul_SpiritEffectTarget takes nothing returns string
// Returns the spirit effect when the soul dies before reaching the target.
return "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
endfunction
constant function Cursed_Soul_SpiritEffectDamRate takes nothing returns string
// Returns the spirit effect when the soul dies before reaching the target.
return "Abilities\\Spells\\Undead\\Sleep\\SleepSpecialArt.mdl"
endfunction
constant function Cursed_Soul_SpiritEffectPossession takes nothing returns string
// Returns the spirit effect when the soul dies before reaching the target.
return "Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl"
endfunction
//******************************************************************************
//* The custom functions used in the trigger
//*
function Cursed_Soul_GetAngleBetweenUnits takes unit a, unit b returns real
return Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
endfunction
function Cursed_Soul_GetRandomEnemy takes unit c, group g, real range returns unit
local unit u
local boolean b1
local boolean b2
local boolean b3
local boolean b4
local boolean b5
loop
set u = FirstOfGroup(g)
set b1 = GetWidgetLife( u ) > 0.405
set b2 = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false
set b3 = IsUnitType(u, UNIT_TYPE_FLYING) == false
set b4 = IsUnitEnemy(u, GetOwningPlayer(c))
set b5 = IsUnitInRangeXY(u, GetUnitX(c), GetUnitY(c), range)
exitwhen u == null or (b1 and b2 and b3 and b4 and b5)
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
return u
endfunction
function Cursed_Soul_ControlSoul takes nothing returns nothing
local unit u = GetEnumUnit()
local real t = 0.0
local effect fx = AddSpecialEffectTarget(Cursed_Soul_SpiritEffect(), u, "origin")
call UnitAddAbility(u, 'Amrf')
call UnitRemoveAbility(u, 'Amrf')
loop
exitwhen GetWidgetLife(u) < 0.405 or u == null
call SetUnitVertexColor(u, 100, 100, 100, R2I(70.0 + 20.0 * Cos(t)))
call SetUnitFlyHeight(u, 40.0 + 40 * Cos(t), 100.0)
set t = t + 10.0
call TriggerSleepAction(0.0)
endloop
call DestroyEffect(fx)
set u = null
set fx = null
endfunction
function Cursed_Soul_StartControlSoul takes nothing returns nothing
call ExecuteFunc("Cursed_Soul_ControlSoul")
endfunction
function Cursed_Soul_CatchEnemy takes unit soul, unit caster, unit target returns nothing
local integer st = GetUnitTypeId(soul)
local player pt = GetOwningPlayer(target)
local player ps = GetOwningPlayer(soul)
local timer t = CreateTimer()
local real d = GetUnitState(soul, UNIT_STATE_MAX_LIFE) * Cursed_Soul_LifeStolen(GetUnitAbilityLevel(caster, Cursed_Soul_SpellID()))
loop
exitwhen IsUnitInRange(soul, target, 50.0) or GetWidgetLife(soul) < 0.405 or GetWidgetLife(target) < 0.405
call IssuePointOrder(soul, "move", GetUnitX(target), GetUnitY(target))
call TriggerSleepAction(0.0)
endloop
if GetWidgetLife(target) < 0.405 and GetWidgetLife(soul) >= 0.405 then
call DestroyEffect(AddSpecialEffect(Cursed_Soul_SpiritEffectDead(), GetUnitX(soul), GetUnitY(soul)))
call RemoveUnit(soul)
else//if GetWidgetLife(target) >= 0.405 and GetWidgetLife(soul) >= 0.405 then
call DestroyEffect(AddSpecialEffectTarget(Cursed_Soul_SpiritEffectTarget(), target, "origin"))
if GetUnitTypeId(target) == st then
call RemoveUnit(soul)
call DestroyEffect(AddSpecialEffectTarget(Cursed_Soul_SpiritEffectPossession(), target, "origin"))
call SetUnitOwner(target, ps, true)
call TimerStart(t, Cursed_Soul_PossessDur(GetUnitAbilityLevel(caster, Cursed_Soul_SpellID())), false, null)
loop
exitwhen TimerGetRemaining(t) <= 0 or GetWidgetLife(target) < 0.405
call DestroyEffect(AddSpecialEffectTarget(Cursed_Soul_SpiritEffectDamRate(), target, "origin"))
call UnitDamageTarget(caster, target, Cursed_Soul_DamageRate(), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
call TriggerSleepAction(1.0)
endloop
call SetUnitOwner(target, pt, true)
else
call UnitDamageTarget(soul, target, d, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
call RemoveUnit(soul)
endif
endif
call DestroyTimer(t)
set pt = null
set ps = null
set t = null
endfunction
function Cursed_Soul_WaitNearbyEnemy takes unit soul, unit caster returns nothing
local real t = Cursed_Soul_GetAngleBetweenUnits(soul, caster)
local location lc
local group g
local unit u
local real x
local real y
loop
set lc = GetUnitLoc(caster)
set g = GetUnitsInRangeOfLocAll(0.6 * Cursed_Soul_AOE(), lc)
call RemoveLocation(lc)
set u = Cursed_Soul_GetRandomEnemy(caster, g, 0.6 * Cursed_Soul_AOE())
exitwhen u != null
call DestroyGroup(g)
set t = t + bj_PI / 15.0
set x = GetUnitX(caster) + Cursed_Soul_Radius() * Cos(t)
set y = GetUnitY(caster) + Cursed_Soul_Radius() * Sin(t)
call IssuePointOrder(soul, "move", x, y)
call TriggerSleepAction(0.0)
endloop
call Cursed_Soul_CatchEnemy(soul, caster, u)
set lc = null
set g = null
set u = null
endfunction
//******************************************************************************
//* The main functions used in the trigger
//*
function Cursed_Soul_Conditions takes nothing returns boolean
return GetSpellAbilityId() == Cursed_Soul_SpellID()
endfunction
function Cursed_Soul_Actions takes nothing returns nothing
local unit c = GetSpellAbilityUnit()
local unit t = GetSpellTargetUnit()
local real x = GetUnitX(t)
local real y = GetUnitY(t)
local integer utt = GetUnitTypeId(t)
local real ft = GetUnitFacing(t)
local unit d = CreateUnit(GetOwningPlayer(c), utt, x, y, ft)
local group g = CreateGroup()
local location lc = GetUnitLoc(c)
call DestroyEffect(AddSpecialEffect(Cursed_Soul_StartEffect(), x, y))
call UnitAddAbility(d, 'Abun')
call UnitAddAbility(d, 'Aloc')
call GroupAddUnit(g, d)
call ForGroup(g, function Cursed_Soul_StartControlSoul)
call DestroyGroup(g)
set g = GetUnitsInRangeOfLocAll(Cursed_Soul_AOE(), lc)
call RemoveLocation(lc)
set t = Cursed_Soul_GetRandomEnemy(c, g, Cursed_Soul_AOE())
if t == null then
call Cursed_Soul_WaitNearbyEnemy(d, c)
else
call Cursed_Soul_CatchEnemy(d, c, t)
endif
set c = null
set t = null
set d = null
endfunction
//===========================================================================
function InitTrig_Cursed_Soul takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, Condition( function Cursed_Soul_Conditions ) )
call TriggerAddAction( t, function Cursed_Soul_Actions )
call Preload(Cursed_Soul_StartEffect())
call Preload(Cursed_Soul_SpiritEffect())
call Preload(Cursed_Soul_SpiritEffectDead())
call Preload(Cursed_Soul_SpiritEffectTarget())
call Preload(Cursed_Soul_SpiritEffectDamRate())
call Preload(Cursed_Soul_SpiritEffectPossession())
set t = null
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
//==========================================================================================
// Diabolic Countdown v1.00a by watermelon_1234
//******************************************************************************************
// Libraries required: (Libraries with * are optional)
// - TimerUtils
// - xe system (xebasic and xefx)
// * BoundSentinel
// * GroupUtils
//##########################################################################################
// Importing:
// 1. Copy the ability, Diabolic Countdown.
// 2. Copy this trigger.
// 3. Implement the required libraries.
// 4. Configure the spell.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notes:
// - If BoundSentinel is used, the ghost may not move correctly if it's near the map bounds.
//==========================================================================================
scope DiabolicCountdown
native UnitAlive takes unit id returns boolean // It's not neccessary, but you can remove this line if you already have UnitAlive implemented
// Provided as an easier way to use another method that detects if a unit is alive.
private function IsUnitAlive takes unit u returns boolean
return UnitAlive(u) // GetWidgetLife(u) > 0.405
endfunction
globals
private constant integer SPELL_ID = 'A00L' // Raw id of the Diabolic Countdown ability
private constant integer TICK_NUMBER = 12 // The number of ticks in the clock. Recommended to keep this at 12.
private constant integer DIRECTION = -1 // -1 for clockwise (default), 1 for counter-clockwise.
private constant real KILL_DAMAGE = 9999. // The damage an enemy unit will receive if it has less than WeakPercent of its max life.
private constant real TIMER_LOOP = 0.03 // Determines how often the timer will loop. If it's too high, the spell may not function correctly.
private constant real START_ANGLE = 1.570796 // Starting angle where the ghost emerges from. In radians. Default is 1/2*PI
private constant real GHOST_MOVE_SPEED = 500 // Determines the move speed when the ghost is emerging from/going back to the center
private constant real GHOST_ROTATE_SPEED = 2.094395 // Determines how many radians the ghost will be rotated around the center per second.
// SFX settings
private constant boolean DO_PRELOAD = true // Determines whether or not to preload the SFX
private constant real SFX_HEIGHT = 65. // Determines the height for all of the sfx used by this spell.
private constant string CENTER_SFX = "Abilities\\Spells\\Other\\Drain\\DrainCaster.mdl" // Path for the center of the clock
private constant string GHOST_SFX = "Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl" // Path for the ghost
private constant string HAND_PATH = "DRAL" // Path for the lightning which is used as the "hand" of the clock
private constant string TICK_SFX = "GreenCurseTarget.mdl" // Path for the sfx that's used as a "tick" of the clock
private constant integer TICK_ALPHA = 255 // Initial alpha the tick starts with
private constant real TICK_DUR = 1.15 // How long it will take for the "tick" to fade away after the spell is finished.
private constant real TICK_DELAY = 0.15 // This is a delay added to every tick after the first one before they fade away. Increaes with every other tick.
private constant string CHIME_SFX = "GreenTauntCaster.mdl" // Path for the chime sfx
private constant string DMG_SFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl" // Path for the sfx played when a unit is damaged by this spell
private constant string DMG_SFX_ATTACH = "origin" // Attachment point for the DMG_SFX
// Damage settings
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WPN_TYPE = null
endglobals
// Determines which units get affected by the spell
private function AffectedTargets takes unit targ, player owner returns boolean
return IsUnitAlive(targ) and IsUnitEnemy(targ,owner) and not IsUnitType(targ,UNIT_TYPE_MAGIC_IMMUNE) and not IsUnitType(targ,UNIT_TYPE_MECHANICAL)
endfunction
// The spell's target area
private constant function Area takes integer lvl returns real
return 350. + 50*lvl
endfunction
// Spell's damage
private constant function Damage takes integer lvl returns real
return 50. + 125*lvl
endfunction
// Determines the percent of max life when the unit will be killed regardless of spell damage
private constant function WeakPercent takes integer lvl returns real
return .3 + 0*lvl
endfunction
// Scaling for CHIME_SFX
private constant function ChimeScale takes integer lvl returns real
return 1.2 + .2*lvl
endfunction
//==========================================================================================
// END OF CONFIGURATION
//==========================================================================================
globals
private constant real TWO_PI = 6.283185 // Determines a full rotation
private constant location loc = Location(0,0) // Global location used for GetLocationZ
private boolexpr e // Used for group enumeration
private group g // Will be used if GroupUtils is not present
endglobals
// Destroys an xefx after making it completely transparent over TICK_DUR seconds. If there is a delay, runs a timer first that will run onLoop later.
private struct timedxefx
xefx sfx
real count = 0
timer tim
static method create takes xefx sfx, real delay returns thistype
local thistype this = thistype.allocate()
set .sfx = sfx
set .tim = NewTimer()
call SetTimerData(.tim,this)
if delay > 0 then
call TimerStart(.tim,delay,false,function thistype.delayed)
else
call TimerStart(.tim,TIMER_LOOP,true,function thistype.onLoop)
endif
return this
endmethod
static method delayed takes nothing returns nothing
call TimerStart(GetExpiredTimer(),TIMER_LOOP,true,function thistype.onLoop)
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .count + TIMER_LOOP < TICK_DUR then
set .sfx.alpha = .sfx.alpha - R2I(TICK_ALPHA*(TIMER_LOOP/TICK_DUR))
set .count = .count + TIMER_LOOP
else
call .sfx.destroy()
call ReleaseTimer(.tim)
call .destroy()
endif
endmethod
endstruct
// The actual code of the spell
private struct Data
unit cast // Unit who casted the spell
real x // x-coordinate of spell target
real y // y-coordinate of spell target
integer lvl // The level of the ability
integer tickNumber = 0 // Counts how many "ticks" were created so far
real area // Stores Area(.lvl) as it is used a lot
real angle = START_ANGLE // Angle used to determine how much to rotate the ghost by
real count = 0 // Used for various counting, like distance/angles
real totalCount = 0 // Used to count how much the ghost has rotated by
real mx // The offset of x where the ghost will move to/from
real my // The offset of y where the ghost will move to/from
lightning hand // Used to store the lightning for the "hand"
xefx center // The special effect at the center
xefx ghost // The ghost sfx
xefx array ticks[TICK_NUMBER] // Stores the ticks that were created
timer tim // Timer for this spell
static thistype temp
// Starts the spell.
static method create takes unit c, real x, real y returns thistype
local thistype this = thistype.allocate()
// Variable setting
set .cast = c
set .x = x
set .y = y
set .lvl = GetUnitAbilityLevel(.cast,SPELL_ID)
set .area = Area(.lvl)
set .mx = .x + .area*Cos(START_ANGLE)
set .my = .y + .area*Sin(START_ANGLE)
// Special effect creation
set .center = xefx.create(.x,.y,0)
set .center.fxpath = CENTER_SFX
set .center.z = SFX_HEIGHT
set .ghost = xefx.create(.x,.y,START_ANGLE)
set .ghost.fxpath = GHOST_SFX
set .ghost.z = SFX_HEIGHT
call MoveLocation(loc,.x,.y)
set .hand = AddLightningEx(HAND_PATH,true,.x,.y,SFX_HEIGHT+GetLocationZ(loc),.x,.y,SFX_HEIGHT+GetLocationZ(loc))
// Timer Stuff:
set .tim = NewTimer()
call SetTimerData(.tim,this)
call TimerStart(.tim,TIMER_LOOP,true,function thistype.ghostEmerge)
return this
endmethod
// Cleanup the spell when done.
method onDestroy takes nothing returns nothing
local integer i = 0
loop
call timedxefx.create(.ticks[i],TICK_DELAY*i)
set i = i + 1
exitwhen i >= .tickNumber
endloop
call .center.destroy()
call .ghost.destroy()
call DestroyLightning(.hand)
call ReleaseTimer(.tim)
endmethod
// Actions done to the units enumerated.
static method groupActions takes nothing returns boolean
local unit u = GetFilterUnit()
if AffectedTargets(u,GetOwningPlayer(temp.cast)) then
call DestroyEffect(AddSpecialEffectTarget(DMG_SFX,u,DMG_SFX_ATTACH))
if GetWidgetLife(u)/GetUnitState(u,UNIT_STATE_MAX_LIFE) <= WeakPercent(temp.lvl) then
call UnitDamageTarget(temp.cast,u,KILL_DAMAGE,false,true,ATK_TYPE,DMG_TYPE,WPN_TYPE)
else
call UnitDamageTarget(temp.cast,u,Damage(temp.lvl),false,true,ATK_TYPE,DMG_TYPE,WPN_TYPE)
endif
endif
set u = null
return false
endmethod
// Made as a method since updating the hand is used all the time.
method updateHand takes nothing returns nothing
local real h1
local real h2
call MoveLocation(loc,.x,.y)
set h1 = GetLocationZ(loc)
call MoveLocation(loc,.ghost.x,.ghost.y)
set h2 = GetLocationZ(loc)
call MoveLightningEx(.hand,true,.x,.y,SFX_HEIGHT+h1,.ghost.x,.ghost.y,SFX_HEIGHT+h2)
endmethod
// Deals with the ghost coming out of the center
static method ghostEmerge takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
if .count + GHOST_MOVE_SPEED*TIMER_LOOP < .area then
set .ghost.x = .ghost.x + GHOST_MOVE_SPEED*TIMER_LOOP*Cos(START_ANGLE)
set .ghost.y = .ghost.y + GHOST_MOVE_SPEED*TIMER_LOOP*Sin(START_ANGLE)
set .count = .count + GHOST_MOVE_SPEED*TIMER_LOOP
else
set .ghost.x = .mx
set .ghost.y = .my
set .count = 0
call TimerStart(.tim,TIMER_LOOP,true,function thistype.ghostRotate)
endif
call .updateHand()
endmethod
// Deals with rotating the ghost around the center
static method ghostRotate takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real gx = .ghost.x
local real gy = .ghost.y
if .totalCount + GHOST_ROTATE_SPEED*TIMER_LOOP < TWO_PI then
set .angle = .angle + DIRECTION*GHOST_ROTATE_SPEED*TIMER_LOOP
set .ghost.x = .x + .area*Cos(.angle)
set .ghost.y = .y + .area*Sin(.angle)
set .ghost.xyangle = Atan2(.ghost.y-gy,.ghost.x-gx)
set .totalCount = .totalCount + GHOST_ROTATE_SPEED*TIMER_LOOP
set .count = .count + GHOST_ROTATE_SPEED*TIMER_LOOP
if .count >= TWO_PI/TICK_NUMBER then // TWO_PI/TICK_NUMBER is basically the angle between each tick.
set .ticks[.tickNumber] = xefx.create(.x+.area*Cos(START_ANGLE+DIRECTION*TWO_PI/TICK_NUMBER*(.tickNumber+1)),.y+.area*Sin(START_ANGLE+DIRECTION*TWO_PI/TICK_NUMBER*(.tickNumber+1)),0)
set .ticks[.tickNumber].fxpath = TICK_SFX
set .ticks[.tickNumber].alpha = TICK_ALPHA
set .ticks[.tickNumber].z = SFX_HEIGHT
set .tickNumber = .tickNumber + 1
set .count = 0
endif
else
set .ghost.x = .mx
set .ghost.y = .my
set .ghost.xyangle = Atan2(.y-.my,.x-.mx)
// Create the last tick
set .ticks[.tickNumber] = xefx.create(.mx,.my,0)
set .ticks[.tickNumber].fxpath = TICK_SFX
set .ticks[.tickNumber].alpha = TICK_ALPHA
set .ticks[.tickNumber].z = SFX_HEIGHT
set .tickNumber = .tickNumber + 1
set .count = 0
call TimerStart(.tim,TIMER_LOOP,true,function thistype.ghostExit)
endif
call .updateHand()
endmethod
// Deals with the ghost going back to the center
static method ghostExit takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local xefx chime
if .count + GHOST_MOVE_SPEED*TIMER_LOOP < .area then
set .ghost.x = .ghost.x - GHOST_MOVE_SPEED*TIMER_LOOP*Cos(START_ANGLE)
set .ghost.y = .ghost.y - GHOST_MOVE_SPEED*TIMER_LOOP*Sin(START_ANGLE)
call .updateHand()
set .count = .count + GHOST_MOVE_SPEED*TIMER_LOOP
else
set .ghost.x = .x
set .ghost.y = .y
// Play the CHIME_SFX
set chime = xefx.create(.x,.y,0)
set chime.fxpath = CHIME_SFX
set chime.scale = ChimeScale(.lvl)
set chime.z = SFX_HEIGHT
call chime.destroy()
set temp = this
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInArea(ENUM_GROUP,.x,.y,.area,e)
else
call GroupEnumUnitsInRange(g,.x,.y,.area,e)
endif
call .destroy()
endif
endmethod
static method spellActions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY())
endif
return false
endmethod
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.spellActions))
set e = Filter(function thistype.groupActions)
static if not LIBRARY_GroupUtils then
set g = CreateGroup()
endif
static if DO_PRELOAD then
call Preload(CENTER_SFX)
call Preload(GHOST_SFX)
call Preload(TICK_SFX)
call Preload(CHIME_SFX)
call Preload(DMG_SFX)
call PreloadStart()
endif
endmethod
endstruct
endscope