//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MAPINIT initializer INIT
globals
unit hero
endglobals
private function D_Conditions takes nothing returns boolean
return IsUnitType(GetDyingUnit(),UNIT_TYPE_HERO)
endfunction
private function D_Actions takes nothing returns nothing
call ReviveHero(hero,GetStartLocationX(GetPlayerStartLocation(Player(0))),GetStartLocationY(GetPlayerStartLocation(Player(0))),true)
endfunction
private function Act takes nothing returns nothing
set hero=CreateUnit(Player(0),'Udea',GetStartLocationX(GetPlayerStartLocation(Player(0))),GetStartLocationY(GetPlayerStartLocation(Player(0))),90.0)
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12)
call SetTimeOfDayScale(0.0)
call FogEnable(false)
call FogMaskEnable(false)
set bj_lastCreatedItem = CreateItem('I000', GetUnitX(hero), GetUnitY(hero))
call UnitAddItem(hero, bj_lastCreatedItem)
call SetItemDroppable(bj_lastCreatedItem, false)
set bj_lastCreatedItem = CreateItem('I001', GetUnitX(hero), GetUnitY(hero))
call UnitAddItem(hero, bj_lastCreatedItem)
call SetItemDroppable(bj_lastCreatedItem, false)
set bj_lastCreatedItem = CreateItem('I002', GetUnitX(hero), GetUnitY(hero))
call UnitAddItem(hero, bj_lastCreatedItem)
call SetItemDroppable(bj_lastCreatedItem, false)
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, 2500.00, 0 )
call PlayThematicMusic("Sound\\Music\\mp3Music\\PH1.mp3")
call DisplayTimedTextToPlayer(Player(0),0,0,20.," |cff8b814c B L A C K P A R A D E|r")
call DisplayTimedTextToPlayer(Player(0),0,0,20.," |cffcdbe70 By: blanc_dummy aka scorpion182")
endfunction
private function item_ref takes nothing returns boolean
if (GetSpellAbilityId() == 'A001') then
call SetUnitLifePercentBJ( GetTriggerUnit(), 100 )
call SetUnitManaPercentBJ( GetTriggerUnit(), 100 )
call UnitRemoveBuffsBJ( bj_REMOVEBUFFS_ALL, GetTriggerUnit() )
elseif (GetSpellAbilityId() == 'A002') then
call UnitResetCooldown( GetTriggerUnit() )
elseif (GetSpellAbilityId() == 'A003') then
call SetHeroLevelBJ( GetTriggerUnit(), ( GetHeroLevel(GetTriggerUnit()) + 1 ), true )
endif
return false
endfunction
private function C_Conditions takes nothing returns boolean
return GetOwningPlayer(GetDyingUnit()) == Player(PLAYER_NEUTRAL_AGGRESSIVE)
endfunction
private function C_Actions takes nothing returns nothing
local integer id=GetUnitTypeId(GetTriggerUnit())
local real x=GetUnitX(GetTriggerUnit())
local real y=GetUnitY(GetTriggerUnit())
call TriggerSleepAction( GetRandomReal(4.00, 8.00) )
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),id,x,y,GetRandomReal(0, 359.00))
endfunction
//===========================================================================
private function INIT takes nothing returns nothing
local trigger t= CreateTrigger( )
call TriggerRegisterTimerEventSingle(t, 0.00 )
call TriggerAddAction(t, function Act)
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition(t, Condition( function D_Conditions ) )
call TriggerAddAction(t, function D_Actions )
set t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition( function item_ref ) )
set t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition(t, Condition( function C_Conditions ) )
call TriggerAddAction(t, function C_Actions )
set t=null
endfunction
endscope
//TESH.scrollpos=3
//TESH.alwaysfold=0
//==============================================================================
// PUI -- Perfect Unit Indexing by Cohadar -- v5.1
//==============================================================================
//
// PURPOUSE:
// * Extending UnitUserData()
// * This is basically perfect hashing algorithm for units
//
// HOW TO USE:
// * You have only one function at your disposal GetUnitIndex(unit)
// It will return a unique index for each unit in range 1..8190
//
// * What you will do with that index is all up to you
// Of course using global arrays is the most obvious choice
// Advanced jassers will think of a couple of more clever ones ofc.
//
// * There are also 2 textmacros available at the end of library code
// They can be used for easier attaching to units
// PUI for structs
// PUI_PROPERTY for unit, integer, real, boolean and string variables
//
// PROS:
// * You can use any number of systems that previously could not work together
// because they all required exclusive access of UnitUserData()
//
// * You can also use this to attach spell data structs to casters
//
// * There are no SetUnitIndex() or ClearUnitIndex() functions here
// Each unit gets assigned one index that cannot be changed
// That index will be automatically recycled when unit is removed from the game.
//
// CONS:
// * This system uses UnitUserData() itself
// That means that if you want to use PUI you must recode
// any other system that uses UnitUserData() to use GetUnitIndex() instead
//
// * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
// you must check if timer on that index already exists before you create a new one.
// This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
// to the newly created unit for which you intended to use timer for
//
// * All in all this is not a sys for newbies, it gives great power,
// but it requires knowledge and carefull handling
//
// DETAILS:
// * System is using unit array to keep track of all units with an index.
// Array is periodically checked for removed units,
// when removed unit is found, index is recycled.
// Indexes have "decay time" to prevent bugs
// caused by attaching to "Can't Raise, Does not decay" type units,
// or by using RemoveUnit() function
//
// SYSTEM COMMANDS: (debug mode only, red player only)
//
// * type -pui to display indexes of currently selected units
// * type -puistats to display some stats
//
// THANKS TO:
// * Vexorian - for his help with PUI textmacro
//
// HOW TO IMPORT:
// * Just create a trigger named PUI
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library PUI initializer Init
//==============================================================================
globals
//-----------------------------------------------
private constant real INDEX_DECAY_TIME = 5. // seconds
//-----------------------------------------------
private constant real PERIOD = 0.03125 // 32 fps
//-----------------------------------------------
private constant integer DECAY_TICKS = R2I(INDEX_DECAY_TIME/PERIOD)
//-----------------------------------------------
private integer array Indexz
private unit array Unitz
private integer array Decayz
private integer array Tickz
private integer maxindex = 0
private integer topindex = 0
private integer decayindex = 0
private integer checker = 0
private integer decayer = 0
private integer tick = 0
endglobals
//==============================================================================
private function PeriodicRecycler takes nothing returns boolean
local integer temp
set tick = tick + 1
if topindex > decayindex then
set checker = checker + 1
if checker > topindex then
set checker = decayindex + 1
endif
if (GetUnitUserData(Unitz[checker])==0) then
set decayindex = decayindex + 1
set Unitz[checker] = Unitz[decayindex]
// swap(checker, decayindex)
set temp = Indexz[checker]
set Indexz[checker] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Decayz[decayindex] = DECAY_TICKS
set Tickz[decayindex] = tick
endif
endif
if decayindex > 0 then
set decayer = decayer + 1
if decayer > decayindex then
set decayer = 1
endif
set Decayz[decayer] = Decayz[decayer] - (tick-Tickz[decayer])
if Decayz[decayer] > 0 then
set Tickz[decayer] = tick
else
// swap(decayer, decayindex)
set temp = Indexz[decayer]
set Indexz[decayer] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Unitz[decayindex] = Unitz[topindex]
// swap(decayindex, topindex)
set temp = Indexz[decayindex]
set Indexz[decayindex] = Indexz[topindex]
set Indexz[topindex] = temp
set decayindex = decayindex - 1
set topindex = topindex - 1
endif
endif
return false
endfunction
//==============================================================================
// Main and only function exported by this library
//==============================================================================
function GetUnitIndex takes unit whichUnit returns integer
local integer index
debug if whichUnit == null then
debug call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
debug return 0
debug endif
set index = GetUnitUserData(whichUnit)
if index == 0 then
set topindex = topindex + 1
if topindex > maxindex then
set maxindex = topindex
set Indexz[topindex] = topindex
endif
set index = Indexz[topindex]
set Unitz[topindex] = whichUnit
call SetUnitUserData(whichUnit, index)
set index = GetUnitUserData(whichUnit)
// this happens when requesting unit index for removed unit
debug if index == 0 then
debug call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
debug endif
//debug call BJDebugMsg("|c00FFCC00PUI: Index assigned #" + I2S(index))
endif
return index
endfunction
//==============================================================================
private function DisplayStats takes nothing returns nothing
call BJDebugMsg("=============================================")
call BJDebugMsg("Biggest index ever = " + I2S(maxindex))
call BJDebugMsg("Indexes in use = " + I2S(topindex-decayindex))
call BJDebugMsg("Decaying indexes = " + I2S(decayindex))
call BJDebugMsg("Released indexes = " + I2S(maxindex-topindex))
call BJDebugMsg("=============================================")
endfunction
//===========================================================================
private function DisplaySelectedEnum takes nothing returns nothing
call BJDebugMsg( "PUI(" + ( GetUnitName(GetEnumUnit()) + ( ") = " + I2S(GetUnitUserData(GetEnumUnit())) ) ) )
endfunction
//===========================================================================
private function DisplaySelected takes nothing returns nothing
local group g = CreateGroup()
call SyncSelections()
call GroupEnumUnitsSelected(g, Player(0), null)
call ForGroup(g, function DisplaySelectedEnum)
call DestroyGroup(g)
set g = null
endfunction
//==============================================================================
private function Init takes nothing returns nothing
local trigger trig
set trig = CreateTrigger()
call TriggerRegisterTimerEvent( trig, PERIOD, true )
call TriggerAddCondition( trig, Condition(function PeriodicRecycler) )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-pui", true )
debug call TriggerAddAction( trig, function DisplaySelected )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-puistats", true )
debug call TriggerAddAction( trig, function DisplayStats )
endfunction
endlibrary
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitIndex(whichUnit)
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
if .pui_unit[pui] != whichUnit then
// recycled handle detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//* function NewGroup takes nothing returns group
//* function ReleaseGroup takes group g returns boolean
//* function GroupRefresh takes group g returns nothing
//*
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//*
globals
//* Group for use with all instant enumerations
group ENUM_GROUP = CreateGroup()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Assorted constants
private constant integer MAX_HANDLE_COUNT = 408000
private constant integer MIN_HANDLE_ID = 0x100000
//* Arrays and counter for the group stack
private group array Groups
private integer array Status[MAX_HANDLE_COUNT]
private integer Count = 0
endglobals
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
set Status[GetHandleId(Groups[Count])-MIN_HANDLE_ID] = 1
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer stat = Status[GetHandleId(g)-MIN_HANDLE_ID]
local boolean b = true
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Null groups cannot be released")
set b = false
elseif stat == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Group not part of stack")
set b = false
elseif stat == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Groups cannot be multiply released")
set b = false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Max groups achieved, destroying group")
call DestroyGroup(g)
set b = false
else
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
set Status[GetHandleId(g)-MIN_HANDLE_ID] = 2
endif
return b
endfunction
endlibrary
//TESH.scrollpos=64
//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=0
//TESH.alwaysfold=0
//============================================================================================
//Black Parade v1.51 by blanc_dummy aka scorpion182
//2009
//
//Requires :
//- vJASS compiler
//- TimerUtils, by Vexorian
//- PUI by Cohadar
//- GroupUtils by Rising_Dusk
//
//
//============================================================================================
scope BLACKPARADE initializer INIT
//====Config Option===========================================================================
globals
private constant integer DUMMY_ID='e000'
private constant integer SPELL_ID='A000' //rawcode of Black Parade Ability
private constant real EXPIRATION_TIMER=45.0 //skeleton minion life time
private constant real ANIMATE_D_TIME=45.0 //animate dead unit life time
private constant integer BUFF_TIME_LIFE='Brai' //minion's buff time life
private constant integer ANIMATE_DEAD_BUFF='BUan' //buff for animate dead unit
private constant integer LEVEL=3 //how many level?
private constant string FX_IMPACT="Abilities\\Weapons\\MeatwagonMissile\\MeatwagonMissile.mdl" //effect on target when hit
private constant string FX_ANIMATE_D="Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" //animate dead effect
private constant string FX_MISSILE="Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl" //healer missile effect
private constant string FX_HEAL="Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" //effect on heal
private constant real AOE=800.0 //spell AOE
private constant real RADIUS=75. //wave radius
private constant real INTERVAL=0.03125 //soul interval
private constant integer MINION=2 //how many minions create per second
private constant attacktype AT=ATTACK_TYPE_MAGIC //attack type
private constant damagetype DT=DAMAGE_TYPE_UNIVERSAL //damage type
private constant weapontype WT=WEAPON_TYPE_WHOKNOWS //weapon type
private constant string ORDER_ID="stampede" //spell order id
private integer array SKELL_ID[LEVEL]
//for damage filter
private boolexpr b
private integer array ABIL_LVL
endglobals
private function SKELETON takes nothing returns nothing
set SKELL_ID[0]='h001' //skeleton lvl 1
set SKELL_ID[1]='h002' //skeleton lvl 2
set SKELL_ID[2]='h003' //skeleton lvl 3
endfunction
private constant function damage takes integer lvl returns real
return 50.0*lvl //deal 50/100/150 damage per hit
endfunction
private constant function heal takes integer lvl returns real
return 25.0+lvl*0.0 //recover hp and mp by 25 per soul
endfunction
//======end of config=========================================================================
//=====Don't touch anything below unless you konw what are you doing!=========================
globals
private integer array UnitData
endglobals
private function AttachIntegerToUnit takes unit u, integer i returns nothing
set UnitData[GetUnitIndex(u)] = i
endfunction
private function GetIntegerFromUnit takes unit u returns integer
return UnitData[GetUnitIndex(u)]
endfunction
//struct #1 for spell wave
private struct data
unit caster
timer t
real angle=0.
group fading
real dur=0.
static method create takes unit c, timer t returns data
local data d=data.allocate()
set d.caster=c
set d.t=t
set d.fading=NewGroup()
call AttachIntegerToUnit(d.caster,GetUnitIndex(d.caster))
set ABIL_LVL[GetIntegerFromUnit(d.caster)]=GetUnitAbilityLevel(d.caster,SPELL_ID)
return d
endmethod
private method onDestroy takes nothing returns nothing
local unit f
//kill minions
loop
set f=FirstOfGroup(.fading)
exitwhen f==null
call KillUnit(f)
call GroupRemoveUnit(.fading,f)
endloop
call ReleaseTimer(.t)
call ReleaseGroup(.fading)
set f=null
endmethod
endstruct
//spell filter damage
private function filter takes nothing returns boolean
return (IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)==false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//spell casting condition
private function Conditions takes nothing returns boolean
return(GetSpellAbilityId() == SPELL_ID)
endfunction
//Function CopyGroup made by Blade.dk;BJ variables are NOT evil as many people think, they are actually faster. Only some BJ functions are evil :D
private function CopyGroup takes group g returns group
set bj_groupAddGroupDest = NewGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
//create & move skeleton minions per interval
private function Create_Skel takes nothing returns nothing
local timer t=GetExpiredTimer()
local data d=data(GetTimerData(t))
local unit f
local unit targ
local unit fnew
local real x = GetUnitX(d.caster) + GetRandomReal(-1.0*AOE, AOE) * Cos((d.angle+90.) * bj_DEGTORAD)
local real y = GetUnitY(d.caster) + GetRandomReal(-1.0*AOE, AOE) * Sin((d.angle+90.) * bj_DEGTORAD)
local group g=NewGroup()
local group victim
local group temp
local integer id=GetIntegerFromUnit(d.caster)
if (GetUnitCurrentOrder(d.caster)==OrderId(ORDER_ID)) then
//create skeleton
if(d.dur>=1.0/MINION) then
set d.dur=0.
set f=CreateUnit(GetOwningPlayer(d.caster),SKELL_ID[0],x,y,d.angle)
call UnitAddAbility(f,'Aloc')
call UnitAddType(f, UNIT_TYPE_PEON)
call UnitApplyTimedLife(f,BUFF_TIME_LIFE,6.0)
call SetUnitColor(f,PLAYER_COLOR_PURPLE)
call SetUnitAnimationByIndex(f,6)
call GroupAddUnit(d.fading,f)
endif
set g=CopyGroup(d.fading)
//move skeleton
loop
set f=FirstOfGroup(g)
exitwhen f==null
if (GetUnitState(f,UNIT_STATE_LIFE)>0) then
set x = GetUnitX(f) + 12.0 * Cos(d.angle * bj_DEGTORAD)
set y = GetUnitY(f) + 12.0 * Sin(d.angle * bj_DEGTORAD)
call SetUnitPosition(f,x,y)
call SetUnitFacingTimed(f,d.angle,0)
//check & damage
set victim=NewGroup()
set temp=NewGroup()
call GroupEnumUnitsInRange(temp,GetUnitX(f),GetUnitY(f),RADIUS,b)
loop
set targ=FirstOfGroup(temp)
exitwhen targ==null
if (IsUnitEnemy(targ,GetOwningPlayer(d.caster))) then
call GroupAddUnit(victim,targ)
endif
call GroupRemoveUnit(temp,targ)
endloop
set targ=FirstOfGroup(victim)
//minion crashed enemy
if (targ!=null) then
call DestroyEffect(AddSpecialEffectTarget(FX_IMPACT,targ,"origin"))
call UnitDamageTarget(d.caster,targ,damage(ABIL_LVL[id]),true, false, AT, DT, WT) //damage the target
call GroupRemoveUnit(d.fading,f)
set fnew=CreateUnit(GetOwningPlayer(d.caster),SKELL_ID[ABIL_LVL[id]-1],GetUnitX(f),GetUnitY(f),GetUnitFacing(f))
call AttachIntegerToUnit(fnew,id)
call RemoveUnit(f)
call UnitApplyTimedLife(fnew,BUFF_TIME_LIFE,EXPIRATION_TIMER)
endif
call ReleaseGroup(temp)
call ReleaseGroup(victim)
else
call GroupRemoveUnit(d.fading,f)
call RemoveUnit(f)
endif
call GroupRemoveUnit(g,f)
endloop
set d.dur=d.dur+0.03
call TimerStart(t,0.03,false,function Create_Skel)
else
call d.destroy()
endif
call ReleaseGroup(g)
set fnew=null
set temp=null
set targ=null
set victim=null
set g=null
set f=null
set t=null
endfunction
//spell casting actions
private function Actions takes nothing returns nothing
local timer t=NewTimer()
local data d=data.create(GetSpellAbilityUnit(),t)
local location l=GetSpellTargetLoc()
local real angle
set angle=bj_RADTODEG * Atan2(GetLocationY(l) - GetUnitY(d.caster), GetLocationX(l) - GetUnitX(d.caster))
set d.angle=angle
call SetTimerData(t,integer(d))
call TimerStart(t,0.03,false,function Create_Skel)
call RemoveLocation(l)
set l=null
set t=null
endfunction
//skeleton kill enemy cond
private function SkellD_Conditions takes nothing returns boolean
local integer lvl=GetIntegerFromUnit(GetKillingUnit())
return( GetUnitTypeId(GetKillingUnit()) == SKELL_ID[ABIL_LVL[lvl]-1] )
endfunction
//check player's hero
private function Hero_Cond takes nothing returns boolean
return IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == true and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(),SPELL_ID)>0
endfunction
//struct #2 for recover HP & mana
private struct heals
unit hero
unit healer
timer t
effect fx
static method create takes unit h, unit healer, timer t returns heals
local heals d=heals.allocate()
set d.hero=h
set d.healer=healer
set d.t=t
set d.fx=AddSpecialEffectTarget(FX_MISSILE,d.healer,"origin")
call UnitAddAbility(d.healer,'Aloc')
call UnitAddAbility(d.healer,'Avul')
return d
endmethod
private method onDestroy takes nothing returns nothing
call KillUnit(.healer)
call DestroyEffect(.fx)
endmethod
endstruct
//move the souls
private function Healing takes nothing returns nothing
local timer t=GetExpiredTimer()
local heals h=heals(GetTimerData(t))
local real x1 = GetUnitX(h.healer)
local real y1 = GetUnitY(h.healer)
local real x2 = GetUnitX(h.hero)
local real y2 = GetUnitY(h.hero)
local real ang = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
local real newx = x1 + 15 * Cos(ang * bj_DEGTORAD)
local real newy = y1 + 15 * Sin(ang * bj_DEGTORAD)
local real dx=GetUnitX(h.hero) - GetUnitX(h.healer)
local real dy=GetUnitY(h.hero) - GetUnitY(h.healer)
local real dist=SquareRoot(dx * dx + dy * dy)
if GetUnitState(h.hero,UNIT_STATE_LIFE)>0 then
if (dist>80) then
//move souls
call SetUnitX(h.healer, newx)
call SetUnitY(h.healer, newy)
call SetUnitFacing(h.healer,ang)
call TimerStart(t,INTERVAL,false,function Healing)
else
//recover hp & mana
call SetUnitState(h.hero,UNIT_STATE_LIFE,GetUnitState(h.hero,UNIT_STATE_LIFE)+heal(GetUnitAbilityLevel(h.hero,SPELL_ID)))
call SetUnitState(h.hero,UNIT_STATE_MANA,GetUnitState(h.hero,UNIT_STATE_MANA)+heal(GetUnitAbilityLevel(h.hero,SPELL_ID)))
call DestroyEffect(AddSpecialEffectTarget(FX_HEAL,h.hero,"origin"))
call h.destroy()
endif
else
call h.destroy()
endif
set t=null
endfunction
//skeleton kill enemy actions
private function SkellD_Actions takes nothing returns nothing
local unit k=GetKillingUnit()
local unit f=GetDyingUnit()
local unit d
local unit healer
local group g
local group temp
local boolexpr bol
local timer t
local heals h
local integer id=GetIntegerFromUnit(k)
if (IsUnitEnemy(f,GetOwningPlayer(k)) and IsUnitType(f,UNIT_TYPE_HERO)==false and IsUnitType(f,UNIT_TYPE_STRUCTURE)==false) then
//animate death
set d=CreateUnit(GetOwningPlayer(k),GetUnitTypeId(f),GetUnitX(f),GetUnitY(f),GetUnitFacing(f))
call DestroyEffect(AddSpecialEffectTarget(FX_ANIMATE_D,d,"origin"))
call UnitApplyTimedLife(d,ANIMATE_DEAD_BUFF,ANIMATE_D_TIME)
set healer=CreateUnit(GetOwningPlayer(k),DUMMY_ID,GetUnitX(f),GetUnitY(f),bj_UNIT_FACING)
//filter group
set temp=NewGroup()
set g=NewGroup()
set bol=Condition(function Hero_Cond)
call GroupEnumUnitsOfPlayer(temp, GetOwningPlayer(d), bol)
loop
set d=FirstOfGroup(temp)
exitwhen d==null
//who the owner of the skeleton
if (GetIntegerFromUnit(d)==id) then
call GroupAddUnit(g,d)
endif
call GroupRemoveUnit(temp,d)
endloop
set d=FirstOfGroup(g)
if (d!=null) then
set t=NewTimer()
set h=heals.create(d,healer,t)
call SetTimerData(t,integer(h))
//move the souls
call TimerStart(t,INTERVAL,false,function Healing)
else
call RemoveUnit(healer)
endif
call DestroyBoolExpr(bol)
call ReleaseGroup(g)
call ReleaseGroup(temp)
endif
set temp=null
set k=null
set healer=null
set t=null
set bol=null
set g=null
set d=null
set f=null
endfunction
//init function scope, hoorayy!!
private function INIT takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition( function Conditions ) )
call TriggerAddAction(t, function Actions )
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition(t, Condition( function SkellD_Conditions ) )
call TriggerAddAction(t, function SkellD_Actions )
//init globals
set b=Condition(function filter)
call SKELETON()
set t=null
endfunction
endscope