//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* 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.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* 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.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
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==8191) 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
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=5
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
This is the JESP standard document, if a map contains this document it means that there are
spells that follow this standard.
Spells of this map that follow the standard:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- "FireBomber"
Advantages of the Standard
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- Implementing spells that follow the standard is relatively easier than implementing JASS
spells that don't follow the standard.
- Configuring/Balancing spells that follow the standard is relatively easier than
implementing JASS spells that don't follow the standard.
- Users may do the following procedure to make a new ability that uses the spell's script :
* Create a new Trigger with a name (case sensitive)
* Convert that trigger to custom text.
* Copy the spell's script to a text editor like Notepad or your OS equivalent.
* Replace the spell's Code name with the name you used on the trigger.
* Copy the new text to the new trigger
* Duplicate the Spell's original objects to have new ones for the new spell script.
You are now able to use that new version of the spell.
- In case two guys give the same name to 2 different spells, there are no conflict problems
because you can easily change the name of one of them
What is the JESP Standard?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The JESP standard was designed to make spell sharing much better. And to make sure JASS
enhanced spells follow a rule, to prevent chaos.
What does JESP Standard stands for?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
JASS
Enhanced
Spell
Pseudotemplate
Requirements for a spell to follow the JESP Standard
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- The spell is written in JASS
- The spell is 100% multi instanceable.
- The spell script is ready to support spells of any number of levels.
(default config header is not required to support all of them)
- The Spell has an specific code name.
- The Spell's trigger must have the spell's codename as name
- The Spell's InitTrig function must be named: InitTrig_<CodeName>
- The spell has a configuration header.
- It is mandatory that rawcodes of objects are configurable in the header.
- All the spell's specific code is inside the spell's "Trigger" (Trigger== that custom text
slot that world editor calls Trigger, the spell may use as many 'trigger' OBJECTS as needed)
- Every spell-specific single identifier or key works in such a way that reproducing the
spell's trigger but after performing a text-replace of codename with another name (and thus
renaming the cloned trigger to the new code name) it won't cause compile errors / conflicts
when playing the map.
- There is no code inside the spell's "Trigger" that is not specific to the spell.
- There are no requirements for GUI variables that are specific to the spell. If a system
used by the spell requires GUI variables the code for the system must be outside the "Trigger"
- Eyecandy and spell's balance have to be easy to configure
- The name of the author should be included in the spell's script.
- The reason to exist of this standard is spell sharing. This document should be included
within the map. And it should specify which spell follows the standard, in the top list.
//TESH.scrollpos=0
//TESH.alwaysfold=0
This file was specially made to help the common user using the spell. Here I explain how
the code is meant to be used.
Description:
- I always start reading a code from the Init function, and this is no exception. The Init
function creates the triggers and sets the globals. There are two triggers used. The first
one will run when the caster casts the spell. This trigger will call the function Condi-
tions. If the spell is of the correct type then this function creates the plane and calls
the MovePlane function periodically. This function is very important. This function will
make the plane walk, dive and drop bombs. This function is timed so we have a member from
the structure PlaneData "elapsedTime" that tells us the time that passed. With this we
can know what to do. Moving on to the important section of this function, we have the
bomb dropping. We only drop bombs if the plane is on the bombing height and if he his
100% visible (this can be changed). Every time you want to drop a bomb you type
"BombData.create(plane.bomber)". This will create a bomb object. Note that the bomb being
created must know the plane who dropped her, so we pass it as an argument. After creating
a bomb, we make a wild trip inside the methods of the structure bomb. Note that each bomb
is independent of the environment that surrounds her. When created a bomb will start fal-
ling according to some simple laws of physics. We make the bombs fall using the method
"method bombFall". A bomb falls according to the gravity variable and when its height is
low enough we kill it. When a bomb dies, we activate its effect, which you can change in
the SETUP section and we destroy the object bomb.
After dropping all bombs we make the plane go away, if elapsedTime(keeps track of current
time) >= .totalFlightDuration we hide the plane and kill it, else it means the plane was
killed during the bombing and so we add an effect to his death animation.
The second trigger is when a unit dies. This is important because we want to know
what to do in that case, we want to know if the dying unit is our plane. To know such a
thing I use a system called Table. I am not going to explain the implementation of the
system since it is quite complex. When a unit dies we call the "onDeath" Condition func-
tion. This function will tell us if the dying unit is our plane. If it is than we call
the "onDestroy" method of the PlaneData structure. Now because the totalflytime was not
reached it means our plane died before dropping all bombs, and so we add an effect to it.
You can do more stuff if a plane dies before completing the spell, to do so you could
use this piece of code:
method onDestroy takes nothing returns nothing
//since the bomber dies now we clean the table
call activeTable.flush(.bomber)
//the unit is not anymore in the active units group
call GroupRemoveUnit(Bombers, .bomber)
call ReleaseTimer(.mover)
if (.elapsedTime >= .totalFlightDuration) then
call ShowUnit(.bomber, false)
call KillUnit(.bomber)
else
call My_plane_death_function([parameters here])
endif
endmethod
Know that you "My_plane_death_function([parameters here])" must be above the PlaneData
structure or above the "onDestroy" method for this to work properly.
I hope this small report helps the user changing and using my spell so he can make better
spells himself. Leave comments if you have questions or PM me.
Also please know, this spell took me many years(yes years, not a joke) to reach the state
of perfection and simplicity. Please if you use this spell credit me, Flame_Phoenix or
my project Castle vs Castle Flame Edition. If you don't do this than it means you are
stealing and you are a thief with no respect for other people good will and work.
Hoping you all enjoy, Flame_Phoenix
//TESH.scrollpos=0
//TESH.alwaysfold=0
//===========================================================================
//A spell calls a gnomish plane from behind the caster. The plane will drop
//Incendiary bombs and burn the enemies. This plane can be controled by the
//user and it can die when bombing as well. It is also JESP standard.
//
//Requires TimerUtils and Table
//
//@author Flame_Phoenix
//
//@credits
//- Anitarf, the only guy who helped me when all others didn't
//- Bobo_The_Kodo, for suggestions on code and bug fixing
//- Here-b-Trollz, for suggestions on code and bug fixing
//- xxdingo93xx, my first loyal student, who gave me the motivation to end this spell
//- My first teacher of vJASS: Blue_Jeans
//- All other people I forgot or ignored
//
//@version 1.7
//
//Note: You can make the plane unselectable and invulnerable by using the WE
//abilities. Just add them to the unit. If you still want to make the plane
//vulnerable, but don't want people to change its flight course than just
//set the plane's movement speed to 0 in the object editor.
//===========================================================================
scope FireBomber initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
globals
//Settings for the Bomber
private constant integer AID = 'A000' //rawcode of the ability
private constant integer BOMBER_ID = 'h002' //rawcode of the bomber
private constant real FADE_TIME = 0.3 //the amount of time the plane will take to fade in
private constant real DIVE_TIME = 0.7 //the time the plane will take into diving in and out
private constant real PLANE_SPEED = 8 //the speed of the plane
private constant integer BOMBARD_HEIGHT = 400 //the bombard height
private constant real MOVE_TIME = 0.02 //the frequency of the timer
private constant real BOMB_INTERVAL = 1. //the interval that separates each bomb
private constant string BOOM = "Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl" //the effect that appears when the bomber dies before finishing the mission
private constant integer PLANE_RED = 255 //The Red RGB color of the Plane
private constant integer PLANE_GREEN = 255 //The Green RGB color of the Plane
private constant integer PLANE_BLUE = 255 //The Blue RGB color of the Plane
private constant integer FADE_MAX = 255 //The maximum Alpha the plane will have. This is ralated to the transparency and therefore to the fading.
//Settings for the Bombs
private constant integer BOMB_ID = 'h003' //the Id of the bomb
private constant integer DUM_ID = 'h001' //the dummy unit that will attack and cause fire
private constant string DUM_ORDER = "attackground" //it's order
private constant integer FIRE_ABILITY_ID = 'A001' //Burning Oil Ability
private constant integer FIRE_UNIT_ID = 'h005' //Fire effect unit
private constant real FIRE_TIME = 20.//duration of the fire effect
private constant real GRAVITY = 5.5 //the velocity at which the bomb will fall
endglobals
private constant function PlaneStartDistance takes integer level returns real
//the ditance that the plane will be from the caster when it is created
//you can make it depend on the level like in the next example
return 850. + (level * 0)
endfunction
private constant function BombsNumber takes integer level returns integer
//the number of bombs the plane will drop
return 5 + (level * 0)
endfunction
private function BombEffect takes unit aBomb returns nothing
//This is the effect of the bomb. It will be displayed when it dies.
//the function takes as a parameter the dying bomb
local unit bomb = aBomb
local real bombX = GetUnitX(bomb)
local real bombY = GetUnitY(bomb)
local player p = GetOwningPlayer(bomb)
local unit dummy
local unit fire
//if the bomb is falling inside the map we create the fire
if (GetRectMinX(bj_mapInitialPlayableArea) <= bombX) and (bombX <= GetRectMaxX(bj_mapInitialPlayableArea)) and (GetRectMinY(bj_mapInitialPlayableArea) <= bombY) and (bombY <= GetRectMaxY(bj_mapInitialPlayableArea)) then
//here the dummy launches burning oil to the ground
set dummy = CreateUnit(p, DUM_ID, bombX, bombY, 0.0)
call UnitAddAbility(dummy, FIRE_ABILITY_ID)
call IssuePointOrder(dummy, DUM_ORDER, bombX, bombY)
call UnitApplyTimedLife( dummy,'BTLF', 1.)
//here we use a dummy unit as an effect of the fire
set fire = CreateUnit(p, FIRE_UNIT_ID, bombX, bombY, 0.0)
call UnitApplyTimedLife( fire,'BTLF', FIRE_TIME)
call SetUnitPathing(fire, false)
endif
set bomb = null
set p = null
set dummy = null
set fire = null
endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
globals
private group Bombers //to store the bombers!
private HandleTable activeTable //your private Table's global variable
endglobals
//this keeps track of all information we need for the bomber plane
private struct PlaneData
unit caster //the caster of the spell
integer level //the level of the ability
unit bomber //this is the plane that will appear
real height //this is the innitial height of the plane. It starts with 600 (look object editor)
timer mover //this timer will move the plane
real elapsedTime //this specifies the current time of the spell
integer bombsDropped //number of bombs dropped
real totalFlightDuration //the total amount of time the flight will take
boolean isOnBombingHeight //if true than our plane is dropping bombs, else it is flying
static method create takes unit caster, real spellX, real spellY, real angle returns PlaneData
local PlaneData data = PlaneData.allocate()
//set variables about the caster
set data.caster = caster
set data.level = GetUnitAbilityLevel(data.caster, AID)
//variables about the bomber
set data.bomber = CreateUnit(GetOwningPlayer(data.caster), BOMBER_ID, spellX - PlaneStartDistance(data.level) * Cos(angle * bj_DEGTORAD), spellY - PlaneStartDistance(data.level) * Sin(angle * bj_DEGTORAD), angle)
set data.height = GetUnitFlyHeight(data.bomber)
set data.mover = NewTimer()
set data.bombsDropped = 0
set data.isOnBombingHeight = false
set data.elapsedTime = 0
//the total amount of fly time will take
set data.totalFlightDuration = (FADE_TIME + DIVE_TIME) + (2 * DIVE_TIME) + (2 * FADE_TIME) + (BombsNumber(data.level) * BOMB_INTERVAL)
//this makes the plane start invisible
call SetUnitVertexColor(data.bomber, PLANE_RED, PLANE_GREEN, PLANE_BLUE, R2I(255 * (data.elapsedTime / FADE_TIME)))
call SetUnitPathing(data.bomber, false)
//put the struct in the Table, we just use the bomber's
//handle adress as the key which tells us where in the
//Table the struct is stored
set activeTable[data.bomber] = data
call GroupAddUnit(Bombers, data.bomber)
return data
endmethod
method onDestroy takes nothing returns nothing
//since the bomber dies now we clean the table
call activeTable.flush(.bomber)
//the unit is not anymore in the active units group
call GroupRemoveUnit(Bombers, .bomber)
call ReleaseTimer(.mover)
if (.elapsedTime >= .totalFlightDuration) then
call ShowUnit(.bomber, false)
call KillUnit(.bomber)
else
call DestroyEffect(AddSpecialEffectTarget(BOOM, .bomber, "origin"))
endif
endmethod
endstruct
//this takes care of each bomb individualy. So if the bomber dies while bombing
//the released bombs will still fall and damage the enemies !
private struct BombData
unit bomb
real bombX
real bombY
timer t
static method bombFall takes nothing returns nothing
local BombData data = BombData(GetTimerData(GetExpiredTimer()))
//here we move the bomb !
set data.bombX = GetUnitX(data.bomb) + PLANE_SPEED * Cos(bj_DEGTORAD*GetUnitFacing(data.bomb))
set data.bombY = GetUnitY(data.bomb) + PLANE_SPEED * Sin(bj_DEGTORAD*GetUnitFacing(data.bomb))
call SetUnitPosition(data.bomb,data.bombX, data.bombY)
if (GetUnitFlyHeight(data.bomb) > GRAVITY) then
//if the bomb didn't reach ground, make it fall
call SetUnitFlyHeight(data.bomb, GetUnitFlyHeight(data.bomb) - GRAVITY, 0)
else
//if it reached the ground we kill it and fire the effect trigger
call data.destroy()
endif
endmethod
static method create takes unit plane returns BombData
local BombData data = BombData.allocate()
set data.bomb = CreateUnit(GetOwningPlayer(plane), BOMB_ID, GetUnitX(plane), GetUnitY(plane), GetUnitFacing(plane))
set data.bombX = GetUnitX(plane)
set data.bombY = GetUnitY(plane)
call SetUnitPathing(data.bomb, false)
call SetUnitFlyHeight(data.bomb, GetUnitFlyHeight(plane), 0)
set data.t = NewTimer()
call SetTimerData(data.t, integer(data))
call TimerStart(data.t, MOVE_TIME, true, function BombData.bombFall)
return data
endmethod
method onDestroy takes nothing returns nothing
call BombEffect(.bomb)
call ReleaseTimer(.t)
call KillUnit(.bomb)
endmethod
endstruct
//======================================================================================
private function onDeath takes nothing returns boolean
local PlaneData plane
local unit u = GetTriggerUnit()
//we make sure that the unit that dies is the bomber
if(IsUnitInGroup(u, Bombers)) then
//recover that plane (the struct) from the bomber
set plane = activeTable[u]
call plane.destroy()
endif
set u = null
return false
endfunction
//======================================================================================
private function MovePlane takes nothing returns nothing
local PlaneData plane = PlaneData(GetTimerData(GetExpiredTimer()))
local BombData bomb
set plane.elapsedTime = plane.elapsedTime + MOVE_TIME
//here we make the plane fade in or fade out, depending on the timer
if (plane.elapsedTime <= FADE_TIME) then
call SetUnitVertexColor(plane.bomber, PLANE_RED, PLANE_GREEN, PLANE_BLUE, R2I(255 * (plane.elapsedTime / FADE_TIME)))
elseif (plane.elapsedTime >= plane.totalFlightDuration - FADE_TIME) then
call SetUnitVertexColor(plane.bomber, PLANE_RED, PLANE_GREEN, PLANE_BLUE, R2I(255 * ((plane.totalFlightDuration - plane.elapsedTime) / FADE_TIME)))
endif
//here we make the plane dive in or dive out, depending on the timer
if (plane.elapsedTime >= FADE_TIME) and not(plane.isOnBombingHeight) then
set plane.isOnBombingHeight = true
call SetUnitFlyHeight(plane.bomber, BOMBARD_HEIGHT, (plane.height - BOMBARD_HEIGHT) / DIVE_TIME)
elseif (plane.elapsedTime >= plane.totalFlightDuration - DIVE_TIME - FADE_TIME) and (plane.isOnBombingHeight) then
set plane.isOnBombingHeight = false
call SetUnitFlyHeight(plane.bomber, plane.height, (plane.height - BOMBARD_HEIGHT) / DIVE_TIME)
endif
//if we already faded in and dived in, we drop the bombs !
if (plane.elapsedTime > (FADE_TIME + DIVE_TIME) + (plane.bombsDropped * BOMB_INTERVAL)) and (plane.bombsDropped < BombsNumber(plane.level)) then
set bomb = BombData.create(plane.bomber)
set plane.bombsDropped = plane.bombsDropped + 1
endif
//if we already did everything we wanted, we kill everything ! xD
if (plane.elapsedTime > plane.totalFlightDuration) then
call plane.destroy()
endif
call SetUnitPosition(plane.bomber, GetUnitX(plane.bomber) + PLANE_SPEED * Cos(bj_DEGTORAD*GetUnitFacing(plane.bomber)), GetUnitY(plane.bomber) + PLANE_SPEED * Sin(bj_DEGTORAD*GetUnitFacing(plane.bomber)))
endfunction
//======================================================================================
private function Conditions takes nothing returns boolean
local location spellLoc
local real angle
local PlaneData plane
if (GetSpellAbilityId() == AID) then
set spellLoc = GetSpellTargetLoc()
set angle = bj_RADTODEG * Atan2(GetLocationY(spellLoc) - GetUnitY(GetTriggerUnit()), GetLocationX(spellLoc) - GetUnitX(GetTriggerUnit()))
set plane = PlaneData.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc), angle)
call SetTimerData(plane.mover, integer(plane))
call TimerStart(plane.mover, MOVE_TIME, true, function MovePlane)
call RemoveLocation(spellLoc)
endif
set spellLoc = null
return false
endfunction
//======================================================================================
private function Init takes nothing returns nothing
local trigger FireBomberTrigger = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(FireBomberTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(FireBomberTrigger, Condition( function Conditions ) )
set FireBomberTrigger = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(FireBomberTrigger, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition(FireBomberTrigger, Condition(function onDeath))
set FireBomberTrigger = null
//SETTING OUR GLOBALS
set activeTable = HandleTable.create() //Create our spell's private Table for the bombers
set Bombers = CreateGroup()
//Preloading Effect
call Preload(BOOM)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Melee_Initialization_Copy_Actions takes nothing returns nothing
call MeleeStartingVisibility( )
call MeleeGrantHeroItems( )
call SetHeroLevelBJ( gg_unit_Hamg_0017, 4, true )
call CreateFogModifierRectBJ( true, Player(0), FOG_OF_WAR_VISIBLE, GetPlayableMapRect() )
endfunction
//===========================================================================
function InitTrig_Melee_Initialization takes nothing returns nothing
set gg_trg_Melee_Initialization = CreateTrigger( )
call TriggerAddAction( gg_trg_Melee_Initialization, function Trig_Melee_Initialization_Copy_Actions )
endfunction