Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Game
struct Game
public static boolean singlePlayer=false
public static unit lastCreatedHero
public static real array startX[4]
public static real array startY[4]
private static method onInit takes nothing returns nothing
local integer index=0
call FogEnable(false)
call FogMaskEnable(false)
loop
exitwhen index>3
set startX[index]=GetStartLocationX(index)
set startY[index]=GetStartLocationY(index)
set index=index+1
endloop
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope createOriannas initializer i
private function c takes nothing returns boolean
local integer val1=GetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_ATTACK_ID)
local integer val2=GetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_DISONNANCE_ID)
local integer val3=GetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_PROTECT_ID)
local integer val4=GetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_SHOCKWAVE_ID)
call SetHeroLevel(Game.lastCreatedHero,GetHeroLevel(Game.lastCreatedHero)+1,true)
call SetUnitState(Game.lastCreatedHero,UNIT_STATE_MANA,GetUnitState(Game.lastCreatedHero,UNIT_STATE_MAX_MANA))
call SetUnitState(Game.lastCreatedHero,UNIT_STATE_LIFE,GetUnitState(Game.lastCreatedHero,UNIT_STATE_MAX_LIFE))
if GetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_ATTACK_DIS_ID)==0 then
call UnitRemoveAbility(Game.lastCreatedHero,Orianna.COMMAND_ATTACK_ID)
call UnitRemoveAbility(Game.lastCreatedHero,Orianna.COMMAND_DISONNANCE_ID)
call UnitRemoveAbility(Game.lastCreatedHero,Orianna.COMMAND_PROTECT_ID)
call UnitRemoveAbility(Game.lastCreatedHero,Orianna.COMMAND_SHOCKWAVE_ID)
if val1>0 then
call UnitAddAbility(Game.lastCreatedHero,Orianna.COMMAND_ATTACK_ID)
call SetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_ATTACK_ID,val1)
endif
if val2>0 then
call UnitAddAbility(Game.lastCreatedHero,Orianna.COMMAND_DISONNANCE_ID)
call SetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_DISONNANCE_ID,val2)
endif
if val3>0 then
call UnitAddAbility(Game.lastCreatedHero,Orianna.COMMAND_PROTECT_ID)
call SetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_PROTECT_ID,val3)
endif
if val4>0 then
call UnitAddAbility(Game.lastCreatedHero,Orianna.COMMAND_SHOCKWAVE_ID)
call SetUnitAbilityLevel(Game.lastCreatedHero,Orianna.COMMAND_SHOCKWAVE_ID,val4)
endif
endif
return false
endfunction
private function i takes nothing returns nothing
local integer index=0
local integer count=0
local trigger t
loop
exitwhen index>3
if GetPlayerSlotState(Player(index))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(index))==MAP_CONTROL_USER then
set count=count+1
set Game.lastCreatedHero=CreateUnit(Player(index),Orianna.ID,Game.startX[index],Game.startY[index],270.)
endif
set index=index+1
endloop
if count==1 then
set Game.singlePlayer=true
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cff999999Singleplayer mode|r enabled. Type '-testx' where x is 1-3 for a premade test case, or -test0 to create some test units.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"Press |cffffcc00Esc|r to level up your Lady of Clockwork.")
set t=CreateTrigger()
call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
call TriggerAddCondition(t,Condition(function c))
set t=null
endif
endfunction
endscope
//TESH.scrollpos=20
//TESH.alwaysfold=0
scope revive initializer i
private struct ridiculous
unit u
endstruct
globals
private HandleTable tab
private trigger t
private constant real RESPAWN_TIME=5.
endglobals
private function after takes nothing returns nothing
local timer time=GetExpiredTimer()
local ridiculous r=tab[time]
local integer id=GetPlayerId(GetOwningPlayer(r.u))
if GetPlayerSlotState(Player(id))==PLAYER_SLOT_STATE_PLAYING then
call ReviveHero(r.u,GetStartLocationX(id),GetStartLocationY(id),true)
else
call DisplayTextToPlayer(GetLocalPlayer(),0.,0.,"|cffffcc00"+GetPlayerName(Player(id))+"|r has disconnected.")
endif
call tab.flush(time)
call r.destroy()
call DestroyTimer(time)
set time=null
endfunction
private function c takes nothing returns boolean
local timer time
local ridiculous r
local unit tU=GetTriggerUnit()
local unit kU
local player killer
local player tP
if GetUnitTypeId(tU)==Orianna.ID then
set kU=GetKillingUnit()
set killer=GetOwningPlayer(kU)
set tP=GetOwningPlayer(tU)
if not Game.singlePlayer then
if GetLocalPlayer()==tP then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cffffcc00"+GetPlayerName(killer)+"|r has killed you! You will respawn in |cffffcc005|r seconds.")
elseif GetLocalPlayer()==killer then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"You have slain |cffffcc00"+GetPlayerName(tP)+"|r!")
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,30.,"|cff999999"+GetPlayerName(killer)+"|r has slain |cff999999"+GetPlayerName(tP)+"|r!")
endif
if GetUnitTypeId(kU)==Orianna.ID then
call SetHeroLevel(kU,GetHeroLevel(kU)+1,true)
endif
set kU=null
set time=CreateTimer()
set r=ridiculous.create()
set r.u=tU
set tab[time]=r
call TimerStart(time,RESPAWN_TIME,false,function after)
else
call DestroyTrigger(t)
set t=null
call tab.destroy()
endif
endif
set tU=null
return false
endfunction
private function i takes nothing returns nothing
set t=CreateTrigger()
set tab=HandleTable.create()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t,Condition(function c))
endfunction
endscope
//TESH.scrollpos=17
//TESH.alwaysfold=0
//Creates a bunch of units to kill!
scope test0 initializer i
private function c takes nothing returns boolean
local integer index=0
if Game.singlePlayer then
//Solitary unit to the east
call CreateUnit(Player(1),'hfoo',-3555.,2303.,0.)
//Soliary hero to the south
call CreateUnit(Player(1),'Hpal',-5577.,1670.,0.)
//Nearby allied hero and unit
call CreateUnit(Player(2),'Hpal',-5770.,3946.,0.)
call CreateUnit(Player(2),'hfoo',-5950.,3895.,0.)
//Nearby hero and unit under player control
call CreateUnit(Player(0),'Hpal',-5956.,3359.,0.)
call CreateUnit(Player(0),'hfoo',-5880.,3233.,0.)
//Group of various units to the southeast
call CreateUnit(Player(4),'hrif',-4228.,1607.,0.)
call CreateUnit(Player(4),'hkni',-4190.,1607.,0.)
call CreateUnit(Player(4),'hsor',-4228.,1570.,0.)
call CreateUnit(Player(4),'hpri',-4190.,1570.,0.)
call CreateUnit(Player(4),'ogru',-4228.,1540.,0.)
call CreateUnit(Player(4),'otau',-4160.,1570.,0.)
//Multiple Oriannas in southwest region
call CreateUnit(Player(0),Orianna.ID,-4294.,-2686.,0.)
call CreateUnit(Player(0),Orianna.ID,-4294.,-2686.,0.)
call CreateUnit(Player(0),Orianna.ID,-4294.,-2686.,0.)
call CreateUnit(Player(0),Orianna.ID,-4294.,-2686.,0.)
call CreateUnit(Player(0),Orianna.ID,-4294.,-2686.,0.)
call CreateUnit(Player(0),Orianna.ID,-4294.,-2686.,0.)
call CreateUnit(Player(1),Orianna.ID,-1639.,-1790.,0.)
call CreateUnit(Player(1),Orianna.ID,-1639.,-1790.,0.)
call CreateUnit(Player(1),Orianna.ID,-1639.,-1790.,0.)
call CreateUnit(Player(1),Orianna.ID,-1639.,-1790.,0.)
call CreateUnit(Player(1),Orianna.ID,-1639.,-1790.,0.)
//Orianna with multiple enemy and allied heroes in southeast region
call CreateUnit(Player(0),Orianna.ID,1395.,-3132.,0.)
call CreateUnit(Player(0),'Hmkg',1395.,-3132.,0.)
call CreateUnit(Player(0),'Obla',1395.,-3132.,0.)
call CreateUnit(Player(0),'Ulic',1395.,-3132.,0.)
call CreateUnit(Player(0),'Ewar',1395.,-3132.,0.)
call CreateUnit(Player(0),'Edem',1395.,-3132.,0.)
call CreateUnit(Player(1),'Hmkg',1395.,-2132.,0.)
call CreateUnit(Player(2),'Obla',1395.,-2132.,0.)
call CreateUnit(Player(3),'Ulic',1395.,-2132.,0.)
call CreateUnit(Player(4),'Ewar',1395.,-2132.,0.)
call CreateUnit(Player(5),'Edem',1395.,-2132.,0.)
endif
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t,Player(0),"-test0",true)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=36
//TESH.alwaysfold=0
//Blink Engage Test
//Orianna Command: Protect's a Warden, Warden blinks to the center of a group of units,
//Oranna uses RW combo and Warden finishes the rest with Fan of Knives
scope test1 initializer i
globals
private constant integer WARDEN_ID='Ewar'
private constant real NEW_X=-785.
private constant real NEW_Y=-585.
private constant real CLOCK_PERIOD=1./30.
private boolean ready=true
private unit ori
private unit warden
private timer time=CreateTimer()
private integer kf
endglobals
private function p takes nothing returns nothing
set kf=kf+1
if kf==0.5/CLOCK_PERIOD then
call IssueTargetOrder(ori,"acolyteharvest",warden)
elseif kf==30 then
call IssuePointOrder(warden,"blink",NEW_X,NEW_Y-256.)
elseif kf==45 then
call IssueImmediateOrder(ori,"ambush")
elseif kf==60 then
call IssueImmediateOrder(warden,"fanofknives")
call IssueImmediateOrder(ori,"acidbomb")
elseif kf==150 then
call RemoveUnit(warden)
call RemoveUnit(ori)
call PauseTimer(time)
set ready=true
endif
endfunction
private function c takes nothing returns boolean
local integer index=0
if Game.singlePlayer and ready then
set ready=false
call PanCameraToTimed(NEW_X,NEW_Y,0.)
set ori=CreateUnit(Player(1),Orianna.ID,NEW_X,NEW_Y+490.,0.)
set warden=CreateUnit(Player(1),WARDEN_ID,NEW_X+64.,NEW_Y+490.,270.)
call UnitRemoveAbility(ori,'Aatk')
call UnitRemoveAbility(warden,'Aatk')
loop
exitwhen index>4
call CreateUnit(Player(2),'hfoo',NEW_X+100.*Cos(index*2.*bj_PI/5.),NEW_Y-256.+100.*Sin(index*2.*bj_PI/5.),0.)
set index=index+1
endloop
call SetHeroLevel(ori,6,false)
call SetHeroLevel(warden,2,false)
call SelectHeroSkill(ori,Orianna.COMMAND_SHOCKWAVE_ID)
call SelectHeroSkill(ori,Orianna.COMMAND_PROTECT_ID)
call SelectHeroSkill(ori,Orianna.COMMAND_DISONNANCE_ID)
call SelectHeroSkill(warden,'AEbl')
call SelectHeroSkill(warden,'AEfk')
set kf=0
call TimerStart(time,CLOCK_PERIOD,true,function p)
endif
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t,Player(0),"-test1",true)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Big Fling Test
//10 Oriannas Command: Attack to a location near a unit, and they all
//Command: Shockwave at the same time, sending it flying
scope test2 initializer i
globals
private constant integer DH_ID='Edem'
private constant real NEW_X=-785.
private constant real NEW_Y=-585.
private constant real CLOCK_PERIOD=1./30.
private boolean ready=true
private unit array oris[10]
private unit dh
private timer time=CreateTimer()
private integer kf
endglobals
private function p takes nothing returns nothing
local integer index=0
set kf=kf+1
if kf==15 then
loop
exitwhen index>9
call IssuePointOrder(oris[index],"absorb",NEW_X-128.,NEW_Y-256.)
set index=index+1
endloop
elseif kf==45 then
loop
exitwhen index>9
call IssueImmediateOrder(oris[index],"ambush")
set index=index+1
endloop
elseif kf==150 then
call RemoveUnit(dh)
loop
exitwhen index>9
call RemoveUnit(oris[index])
set index=index+1
endloop
call PauseTimer(time)
set ready=true
endif
endfunction
private function c takes nothing returns boolean
local integer index=0
if Game.singlePlayer and ready then
set ready=false
call PanCameraToTimed(NEW_X,NEW_Y,0.)
loop
exitwhen index>9
if index<5 then
set oris[index]=CreateUnit(Player(1),Orianna.ID,NEW_X-100.*index,NEW_Y+490.,0.)
else
set oris[index]=CreateUnit(Player(1),Orianna.ID,NEW_X-100.*(index-5),NEW_Y+390.,0.)
endif
call UnitRemoveAbility(oris[index],'Aatk')
call SetHeroLevel(oris[index],6,false)
call SelectHeroSkill(oris[index],Orianna.COMMAND_SHOCKWAVE_ID)
call SelectHeroSkill(oris[index],Orianna.COMMAND_ATTACK_ID)
set index=index+1
endloop
set dh=CreateUnit(Player(2),DH_ID,NEW_X-256.,NEW_Y-256.,270.)
call UnitRemoveAbility(dh,'Aatk')
set kf=0
call TimerStart(time,CLOCK_PERIOD,true,function p)
endif
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t,Player(0),"-test2",true)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Circle Test
//A circle of Oriannas throw their balls to each other and
//shockwave simultaneously
scope test3 initializer i
globals
private constant real NEW_X=-785.
private constant real NEW_Y=-585.
private constant real CLOCK_PERIOD=1./30.
private boolean ready=true
private unit array oris[12]
private timer time=CreateTimer()
private integer kf
endglobals
private function p takes nothing returns nothing
local integer index=0
set kf=kf+1
if kf==15 then
loop
exitwhen index>11
call IssuePointOrder(oris[index],"absorb",NEW_X+436.*Cos(2.*bj_PI*(index+1)/12.),NEW_Y+436.*Sin(2.*bj_PI*(index+1)/12.))
set index=index+1
endloop
elseif kf==45 then
loop
exitwhen index>11
call IssueImmediateOrder(oris[index],"ambush")
set index=index+1
endloop
elseif kf==150 then
loop
exitwhen index>11
call RemoveUnit(oris[index])
set index=index+1
endloop
call PauseTimer(time)
set ready=true
endif
endfunction
private function c takes nothing returns boolean
local integer index=0
if Game.singlePlayer and ready then
set ready=false
call PanCameraToTimed(NEW_X,NEW_Y,0.)
loop
exitwhen index>11
set oris[index]=CreateUnit(Player(ModuloInteger(index,4)),Orianna.ID,NEW_X+500.*Cos(2.*bj_PI*index/12.),NEW_Y+500.*Sin(2.*bj_PI*index/12.),0)
call UnitRemoveAbility(oris[index],'Aatk')
call SetHeroLevel(oris[index],6,false)
call SelectHeroSkill(oris[index],Orianna.COMMAND_SHOCKWAVE_ID)
call SelectHeroSkill(oris[index],Orianna.COMMAND_ATTACK_ID)
set index=index+1
endloop
set kf=0
call TimerStart(time,CLOCK_PERIOD,true,function p)
endif
return false
endfunction
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t,Player(0),"-test3",true)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.1
//* ------------
//*
//* 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 = InitHashtable()
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
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=156
//TESH.alwaysfold=0
//* API:
//* boolean ADD_ALL_UNITS: If enabled, a trigger turns on which automatically
//* registers all units in the map.
//* integer BUCKET_SIZE: How many units to add to each 'bucket' - a larger
//* bucket will have their trigger refresh less frequently but will be
//* more computationally expensive. A good starting value is about 20.
//* real PER_CLEANUP_TIMEOUT: How many seconds to wait in between each
//* scan for empty buckets. This value should be lower if units die often
//* in your map. A good starting value is about 60.
//* static method addHandler: Registers a callback function to the generic
//* unit damage event. Example: call StructuredDD.addHandler(function h)
//* static method add: Adds a unit to a bucket. If ADD_ALL_UNITS is enabled,
//* this method need not be used.
library StructuredDD
globals
//<< BEGIN SETTINGS SECTION
//* Set this to true if you want all units in your map to be
//* automatically added to StructuredDD. Otherwise you will have to
//* manually add them with StructuredDD.add(u).
private constant boolean ADD_ALL_UNITS=true
//* This is the amount of units that exist in each trigger bucket.
//* This number should be something between 5 and 30. A good starting
//* value will be an estimate of your map's average count of units,
//* divided by 10. When in doubt, just use 20.
private constant integer BUCKET_SIZE=20
//* This is how often StructuredDD will search for empty buckets. If
//* your map has units being created and dying often, a lower value
//* is better. Anything between 10 and 180 is good. When in doubt,
//* just use 60.
private constant real PER_CLEANUP_TIMEOUT=60.
//>> END SETTINGS SECTION
endglobals
//* Our bucket struct which contains a trigger and its associated contents.
private struct bucket
integer bucketIndex=0
trigger trig=CreateTrigger()
unit array members[BUCKET_SIZE]
endstruct
//* Our wrapper struct. We never intend to actually instanciate "a
//* StructuredDD", we just use this for a pretty, java-like API :3
struct StructuredDD extends array
private static boolexpr array conditions
private static bucket array bucketDB
private static integer conditionsIndex=-1
private static integer dbIndex=-1
private static integer maxDBIndex=-1
//* This method gets a readily available bucket for a unit to be added.
//* If the "current" bucket is full, it returns a new one, otherwise
//* it just returns the current bucket.
private static method getBucket takes nothing returns integer
local integer index=0
local integer returner=-1
local bucket tempDat
if thistype.dbIndex!=-1 and thistype.bucketDB[thistype.dbIndex].bucketIndex<BUCKET_SIZE then
return thistype.dbIndex
else
set thistype.maxDBIndex=thistype.maxDBIndex+1
set thistype.dbIndex=thistype.maxDBIndex
set tempDat=bucket.create()
set thistype.bucketDB[.maxDBIndex]=tempDat
loop
exitwhen index>thistype.conditionsIndex
call TriggerAddCondition(tempDat.trig,thistype.conditions[index])
set index=index+1
endloop
return thistype.dbIndex
endif
return -1
endmethod
//* This method is for adding a handler to the system. Whenever a
//* handler is added, damage detection will immediately trigger that
//* handler. There is no way to deallocate a handler, so don't try to
//* do this dynamically (!) Support for handler deallocation is
//* feasible (please contact me)
public static method addHandler takes code func returns nothing
local bucket tempDat
local integer index=0
set thistype.conditionsIndex=thistype.conditionsIndex+1
set thistype.conditions[thistype.conditionsIndex]=Condition(func)
loop
exitwhen index>thistype.maxDBIndex
set tempDat=thistype.bucketDB[index]
call TriggerAddCondition(tempDat.trig,thistype.conditions[thistype.conditionsIndex])
set index=index+1
endloop
endmethod
//* This method adds a unit to the damage detection system. If
//* ADD_ALL_UNITS is enabled, this method need not be used.
public static method add takes unit member returns nothing
local bucket tempDat
local integer whichBucket=thistype.getBucket()
set tempDat=thistype.bucketDB[whichBucket]
set tempDat.bucketIndex=tempDat.bucketIndex+1
set tempDat.members[tempDat.bucketIndex]=member
call TriggerRegisterUnitEvent(tempDat.trig,member,EVENT_UNIT_DAMAGED)
endmethod
//* This is just an auxillary function for ADD_ALL_UNITS' implementation
static if ADD_ALL_UNITS then
private static method autoAddC takes nothing returns boolean
call thistype.add(GetTriggerUnit())
return false
endmethod
endif
//* This method is used to check if a given bucket is empty (and thus
//* can be deallocated) - this is an auxillary reoutine for the
//* periodic cleanup system.
private static method bucketIsEmpty takes integer which returns boolean
local bucket tempDat=thistype.bucketDB[which]
local integer index=0
loop
exitwhen index==BUCKET_SIZE
//GetUnitTypeId(unit)==0 means that the unit has been removed.
if GetUnitTypeId(tempDat.members[index])!=0 then
return false
endif
set index=index+1
endloop
return true
endmethod
//* This method cleans up any empty buckets periodically by checking
//* if it has been fully allocated and then checking if all its
//* members no longer exist.
private static method perCleanup takes nothing returns nothing
local integer index=0
loop
exitwhen index>thistype.maxDBIndex
if index!=thistype.dbIndex and thistype.bucketIsEmpty(index) then
call DestroyTrigger(thistype.bucketDB[index].trig)
call thistype.bucketDB[index].destroy()
set thistype.bucketDB[index]=thistype.bucketDB[thistype.maxDBIndex]
set thistype.maxDBIndex=thistype.maxDBIndex-1
if thistype.maxDBIndex==thistype.dbIndex then
set thistype.dbIndex=index
endif
set index=index-1
endif
set index=index+1
endloop
endmethod
//* This is a initialization function necessary for the setup of
//* StructuredDD.
private static method onInit takes nothing returns nothing
local group grp
local region reg
local trigger autoAddUnits
local timer perCleanup
local unit FoG
static if ADD_ALL_UNITS then
//Add starting units
set grp=CreateGroup()
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call thistype.add(FoG)
call GroupRemoveUnit(grp,FoG)
endloop
//Add entering units
set autoAddUnits=CreateTrigger()
set reg=CreateRegion()
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(autoAddUnits,reg,null)
call TriggerAddCondition(autoAddUnits,Condition(function thistype.autoAddC))
set autoAddUnits=null
set reg=null
endif
//enable periodic cleanup:
set perCleanup=CreateTimer()
call TimerStart(perCleanup,PER_CLEANUP_TIMEOUT,true,function thistype.perCleanup)
set perCleanup=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* API
//* DamageType.NULLED Value returned when DamageType cannot conclusively
//* recognize damage type
//* DamageType.ATTACK Value returned when DamageType recognizes the
//* damage type as a physical attack
//* DamageType.SPELL Value returned when DamageType recognizes the
//* damage type as a spell
//* DamageType.CODE Value returned when DamageType recognizes the
//* damage type as having been dealt via dealCodeDamage()
//* DamageType.get() Requests what type of damage was dealt in the
//* scope of a StructuredDD handler
//* DamageType.dealCodeDamage() Deals an exact amount of damage to a unit. Arguments
//* are of the form (unit,unit,real) where the
//* first unit deals real damage to the second
//* unit.
library DamageType requires StructuredDD, Table
//* This struct contains data to deal damage to a target that would otherwise
//* die when hit with double damage in correction of spell damage recognition.
//* This is to counter the effect of a unit instantly healing some damage due
//* to negative spell damage, without using any unnecessary triggers.
private struct delayDat
unit target
unit source
real damage
endstruct
//* This is a struct shim which won't be instanciated; it is only useful for
//* making the pretty API like DamageType.doSomething()
struct DamageType
///< BEGIN CUSTOMIZE SECTION
//* One limitation of the spell damage reduction ability is that if a unit
//* is meant to receive bonus damage due to the damage table, such that the
//* result is greater than 100% damage, the unit would incorrectly take
//* damage capped at 100%. Using standard melee armor tables, this can only
//* occur when a damaged unit is ethereal; a simple ethereal shim no more
//* than 20 lines can be created, but for maps with custom damage tables,
//* this can be a useful option. If USE_BONUS_CALCULATOR is true, units that
//* would take greater than 100% spell damage will properly exhibit such
//* properly. (Disable this feature to improve performance)
private static constant boolean USE_BONUS_CALCULATOR=true
//* Make sure this matches the rawcode/id of your version of the DamageTypeCheck
//* ability (you must copy/paste it from the editor to your map)
private static constant integer DAMAGE_TYPE_CHECK_ID='A000'
///> END CUSTOMIZE SECTION
public static constant integer NULLED=-1
public static constant integer ATTACK= 0
public static constant integer SPELL = 1
public static constant integer CODE = 2
//ConvertAttackType(7) is a hidden attack type with some unique properties
//that make it very useful for us. For more information see goo.gl/9k8tn
private static constant attacktype ATTACK_TYPE_UNIVERSAL=ConvertAttackType(7)
//How long to delay before dealing the second half of the "correction" damage.
//Significant tests have shown that a delay of 0. will have no issues.
private static constant real DELAY_AMOUNT=0.
private static integer lastDamageType=thistype.NULLED
private static HandleTable delayed
//* Use this to get damage type in a damage event handler.
public static method get takes nothing returns integer
local real sourceDamage=GetEventDamage()
if thistype.lastDamageType==thistype.CODE then
return thistype.CODE
elseif sourceDamage>0. then
return thistype.ATTACK
elseif sourceDamage<0. then
return thistype.SPELL
endif
return thistype.NULLED
endmethod
//* Use this to damage units by trigger in your map without causing an infinite
//* loop.
public static method dealCodeDamage takes unit who, unit target, real damage returns nothing
local integer prevType=thistype.lastDamageType
local real hp=GetWidgetLife(target)-.405
local real d=damage
set thistype.lastDamageType=thistype.CODE
if hp>d then
call SetWidgetLife(target,hp-d+.405)
call UnitDamageTarget(who,target,0.,true,false,thistype.ATTACK_TYPE_UNIVERSAL,DAMAGE_TYPE_UNIVERSAL,null)
else
call UnitDamageTarget(who,target,1000000.+d,true,false,thistype.ATTACK_TYPE_UNIVERSAL,DAMAGE_TYPE_UNIVERSAL,null)
//Also deal magic damage for the special (unmodifiable) case of ethereal
//units.
call UnitDamageTarget(who,target,1000000.+d,true,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_UNIVERSAL,null)
endif
set thistype.lastDamageType=prevType
endmethod
//* Auxillary function for adding the dummy ability to all units.
private static method c takes nothing returns boolean
local unit tU=GetTriggerUnit()
call UnitAddAbility(tU,thistype.DAMAGE_TYPE_CHECK_ID)
call UnitMakeAbilityPermanent(tU,true,thistype.DAMAGE_TYPE_CHECK_ID)
set tU=null
return false
endmethod
//* This is the function that occurs 0 seconds after damaging a unit, for the
//* case that damaging it by twice as much at the same time would kill it
//* unexpectedly.
private static method after takes nothing returns nothing
local timer time=GetExpiredTimer()
local delayDat tempDat=thistype.delayed[time]
call thistype.dealCodeDamage(tempDat.source,tempDat.target,tempDat.damage)
call thistype.delayed.flush(time)
call tempDat.destroy()
call DestroyTimer(time)
set time=null
endmethod
private static method getUnitBonusSpellResistance takes unit u returns real
local integer prevType=thistype.lastDamageType
local real life=GetWidgetLife(u)
local real scale=GetUnitState(u,UNIT_STATE_MAX_LIFE)
call SetWidgetLife(u,scale)
set thistype.lastDamageType=thistype.CODE
call UnitDamageTarget(u,u,-scale/2.,false,false,null,DAMAGE_TYPE_UNIVERSAL,null)
set scale=2.*(scale-GetWidgetLife(u))/scale
call SetWidgetLife(u,life)
set thistype.lastDamageType=prevType
return scale
endmethod
//* This is the method that will invert any negative spell damage. It MUST be
//* StructuredDD.conditions[0] to function properly; thus, any library using a
//* StructuredDD handler should 'requires DamageType' (!)
private static method handler takes nothing returns nothing
local timer time
local delayDat tempDat
local real attemptedDamage=-GetEventDamage()
local unit tU
local real sampledLife
local real scale
if thistype.get()==thistype.SPELL then
set tU=GetTriggerUnit()
static if thistype.USE_BONUS_CALCULATOR then
set scale=thistype.getUnitBonusSpellResistance(tU)
if scale>1. then
set attemptedDamage=attemptedDamage*(scale+1.)/2.
endif
endif
set sampledLife=GetWidgetLife(tU)-.405
if sampledLife>=attemptedDamage and sampledLife<=2.*attemptedDamage then
call SetWidgetLife(tU,sampledLife-attemptedDamage)
set time=CreateTimer()
set tempDat=delayDat.create()
set tempDat.target=tU
set tempDat.source=GetEventDamageSource()
set tempDat.damage=attemptedDamage
set thistype.delayed[time]=tempDat
call TimerStart(time,DELAY_AMOUNT,false,function thistype.after)
set time=null
else
call thistype.dealCodeDamage(GetEventDamageSource(),tU,2.*attemptedDamage)
endif
set tU=null
endif
endmethod
//* Initialization method to enable the system.
private static method onInit takes nothing returns nothing
local group grp=CreateGroup()
local region reg=CreateRegion()
local trigger addBracer=CreateTrigger()
local unit FoG
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(addBracer,reg,null)
call TriggerAddCondition(addBracer,Condition(function thistype.c))
call StructuredDD.addHandler(function thistype.handler)
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
call UnitAddAbility(FoG,thistype.DAMAGE_TYPE_CHECK_ID)
call UnitMakeAbilityPermanent(FoG,true,thistype.DAMAGE_TYPE_CHECK_ID)
call GroupRemoveUnit(grp,FoG)
endloop
set thistype.delayed=HandleTable.create()
call DestroyGroup(grp)
set grp=null
set reg=null
set addBracer=null
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//* *API*
//* real TIMER_PERIOD: This value refers to how often Shield should search
//* for instances that are expired in regards to their
//* duration. A smaller value means that shields are less
//* likely to block damage when their duration has passed,
//* while a larger value is less computationally expensive.
//* A good value lies on [1./60.,1./2.].
//* static method add: This is the one and only important method for
//* instanciating a Shield. The arguments are of the
//* form (unit u, real a, real t, string f, string p)
//* which imparts a shield of size 'a' for time 't' on
//* 'u' using effect with model 'f' on attachment
//* point 'p'.
library Shield requires DamageType
//* We must declare the UnitAlive native because StructuredDD no longer
//* declares it for us :) UnitAlive is proven to have less computational
//* cost compared to IsUnitType(u,UNIT_TYPE_DEAD)
native UnitAlive takes unit u returns boolean
//* This struct is just a shim for a pretty API. (You don't instanciate
//* Shield objects yourself)
struct Shield
//< BEGIN CUSTOMIZABLE SECTION
//* How often to check for shield instances with expired durations. A
//* smaller value will reduce the likelihood of a unit blocking damage
//* with a shield that would otherwise be expired, at the cost of
//* performance. A good value is between 1./30. and 1./2.
private static constant real TIMER_PERIOD=1./10.
//> END CUSTOMIZABLE SECTION
private static thistype array allShields
private static integer shieldIndex=-1
private static timer time=CreateTimer()
private static HandleTable tab
private boolean useDuration=true
private boolean finiteShield=true
private unit u
private real shieldLeft
private real falseHP
private real timeLeft
private effect fx
private thistype prevShield=0
private thistype nextShield=0
//* This method is used for handling when a unit with a shield
//* receives damage.
private static method handler takes nothing returns nothing
local thistype tempDat
local thistype destroyingDat
local unit tU=GetTriggerUnit()
local real damageLeft=GetEventDamage()
local boolean noShieldsLeft=false
if DamageType.get()!=DamageType.SPELL and UnitAlive(tU) and tab.exists(tU) then
set tempDat=thistype.tab[tU]
//we need to loop until either all the damage is mitigated
//or there's no shield left on this unit.
loop
exitwhen damageLeft<=0. or noShieldsLeft
if tempDat.shieldLeft>damageLeft then
if tempDat.finiteShield then
set tempDat.shieldLeft=tempDat.shieldLeft-damageLeft
endif
call SetWidgetLife(tempDat.u,GetWidgetLife(tempDat.u)+damageLeft)
set damageLeft=0.
else
set damageLeft=damageLeft-tempDat.shieldLeft
call SetWidgetLife(tempDat.u,GetWidgetLife(tempDat.u)+tempDat.shieldLeft)
set tempDat.shieldLeft=0.
if tempDat.falseHP>damageLeft then
set tempDat.falseHP=tempDat.falseHP-damageLeft
set damageLeft=0.
else
set damageLeft=damageLeft-tempDat.falseHP
set tempDat.falseHP=0.
set destroyingDat=tempDat
if destroyingDat.nextShield!=0 then
set tempDat=destroyingDat.nextShield
set tempDat.prevShield=0
set thistype.tab[tempDat.u]=tempDat
else
set noShieldsLeft=true
endif
set destroyingDat.nextShield=0
endif
endif
endloop
endif
set tU=null
endmethod
//* This method runs every TIMER_PERIOD seconds as long as the stack
//* is not empty, and looks for members that need to be destroyed.
private static method timeout takes nothing returns nothing
local integer index=0
local thistype tempDat
local thistype otherDat
loop
exitwhen index>thistype.shieldIndex
set tempDat=thistype.allShields[index]
set tempDat.timeLeft=tempDat.timeLeft-TIMER_PERIOD
if (tempDat.timeLeft<=0. and tempDat.useDuration) or (tempDat.shieldLeft+tempDat.falseHP)<=0. or UnitAlive(tempDat.u)==false then
//deallocate it, depending on the values of prevShield
//and nextShield
if tempDat.prevShield==0 and tempDat.nextShield==0 and thistype.tab[tempDat.u]==tempDat then
//this was the only shield left on the unit so:
call thistype.tab.flush(tempDat.u)
elseif tempDat.prevShield==0 and tempDat.nextShield!=0 then
//the shield was the first in a list so we have to
//make next into the new first!
set otherDat=tempDat.nextShield
set otherDat.prevShield=0
set thistype.tab[tempDat.u]=otherDat
elseif tempDat.prevShield!=0 and tempDat.nextShield==0 then
//the shield was the end of the list so let's do the
//proper changes to the previous member.
set otherDat=tempDat.prevShield
set otherDat.nextShield=0
elseif tempDat.prevShield!=0 and tempDat.nextShield!=0 then
//this shield was somewhere in the middle of the list
//so let's make the prev and next link to each other
set otherDat=tempDat.prevShield
set otherDat.nextShield=tempDat.nextShield
set otherDat=tempDat.nextShield
set otherDat.prevShield=tempDat.prevShield
endif
//let's reset the unit's hp if he has extra
if tempDat.falseHP>0. then
call SetWidgetLife(tempDat.u,GetWidgetLife(tempDat.u)-tempDat.falseHP)
endif
call DestroyEffect(tempDat.fx)
call tempDat.destroy()
set thistype.allShields[index]=thistype.allShields[thistype.shieldIndex]
set thistype.shieldIndex=thistype.shieldIndex-1
set index=index-1
if thistype.shieldIndex==-1 then
call PauseTimer(thistype.time)
endif
endif
set index=index+1
endloop
endmethod
//* This method adds a shield to a unit. You can use
//* Shield.DEFAULT_EFFECT in the fx argument.
public static method add takes unit u, real amount, real time, string fx, string fxpoint returns nothing
local thistype tempDat=thistype.create()
local thistype linkedDat
local real initialLife=GetWidgetLife(u)
local real maxLife=GetUnitState(u,UNIT_STATE_MAX_LIFE)
local real diff=maxLife-initialLife
set tempDat.u=u
set tempDat.timeLeft=time
set tempDat.fx=AddSpecialEffectTarget(fx,u,fxpoint)
if time==-1. then
set tempDat.useDuration=false
endif
if amount==-1. then
set amount=100000.
set tempDat.finiteShield=false
endif
//figure out how to partition the damage
if diff>=amount then
//the unit has plenty of missing HP, so:
set tempDat.shieldLeft=0
set tempDat.falseHP=amount
call SetWidgetLife(u,initialLife+amount)
else
//the unit has less hp missing than the shield has value, so:
call SetWidgetLife(u,maxLife)
set tempDat.falseHP=diff
set tempDat.shieldLeft=amount-diff
endif
//Now let's figure out if the unit already has a shield - if he
//does, we add this shield to the front:
if thistype.tab.exists(u) then
set linkedDat=thistype.tab[u]
set tempDat.nextShield=linkedDat
set linkedDat.prevShield=tempDat
endif
set thistype.tab[u]=tempDat
//now let's add this shield to the stack so it can be properly
//deallocated when its time runs out:
set thistype.shieldIndex=thistype.shieldIndex+1
set thistype.allShields[thistype.shieldIndex]=tempDat
//if allShields was previously empty we have to jumpstart the timer:
if thistype.shieldIndex==0 then
call TimerStart(thistype.time,thistype.TIMER_PERIOD,true,function thistype.timeout)
endif
endmethod
//* Auxillary method used to initialize the HandleTable and
//* StructuredDD handler.
private static method onInit takes nothing returns nothing
set thistype.tab=HandleTable.create()
call StructuredDD.addHandler(function thistype.handler)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Thanks to anitarf and Vexorian @ wc3c.net for this library, it makes things easier.
library IsTerrainWalkable initializer Init
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE=10.
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X=0.
public real Y=0.
private rect r
private item check
private item array hidden
private integer hiddenMax=0
endglobals
private function Init takes nothing returns nothing
set check=CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r=Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r,x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
// try to move the check item and get it's coordinates
call SetItemPosition(check,x,y)//this unhides the item...
set X=GetItemX(check)
set Y=GetItemY(check)
call SetItemVisible(check,false)//...so we must hide it again
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y)<MAX_RANGE*MAX_RANGE
endfunction
endlibrary
// This is the main Orianna module which contains most of the code
// that handles Orianna's abilities. We depend on DamageType because
// Shield depends on DamageType, and is necessary to implement
// Command: Protect.
// Note: This library contains four (4) customizable sections
library Orianna requires DamageType
// InitShim is a small adapter used to prevent a specific
// instantiation issue.
private module InitShim
//The only implemented method necessary for the adapter
private static method onInit takes nothing returns nothing
call i()
endmethod
endmodule
// Throw handler is a private module used to throw units with
// a specific pattern which we desire to behave like Command:
// Shockwave in League of Legends. Hence it is only used by
// Command: Shockwave
private struct ThrowHandler
private unit u
private real dX
private real dY
private real dZ
private static thistype array DB
private static integer dbIndex=-1
private static timer time=CreateTimer()
//*********************************************************
// This section to be modified by the user
//*********************************************************
// The clock period determines the frame rate of the
// throwing algorithm. A value between 1./20. and 1./60. is
// recommended.
private static constant real CLOCK_PERIOD =1./30.
// The rate in units per second at which thrown units
// accelerate towards the ground.
private static constant real GRAVITY =200.
// Below this height, units are considered back on the
// ground. Choose something below 10. or you'll have visual
// abnormalities.
private static constant real GROUND_HEIGHT_MAX=5.
//*********************************************************
// End customizable section
//*********************************************************
// ThrowHandler.p is the periodic function ThrowHandler
// uses to handle multiple units simultaneously.
private static method p takes nothing returns nothing
local thistype t
local integer i=0
loop
exitwhen i>dbIndex
set t=DB[i]
call SetUnitFlyHeight(t.u,GetUnitFlyHeight(t.u)+t.dZ,0.)
set t.dZ=t.dZ-GRAVITY*CLOCK_PERIOD
call SetUnitX(t.u,GetUnitX(t.u)+t.dX)
call SetUnitY(t.u,GetUnitY(t.u)+t.dY)
if GetUnitFlyHeight(t.u)<GROUND_HEIGHT_MAX and t.dZ<0. then
call t.destroy()
set DB[i]=DB[dbIndex]
set dbIndex=dbIndex-1
set i=i-1
if dbIndex==-1 then
call PauseTimer(time)
endif
endif
set i=i+1
endloop
endmethod
// This is the public method used for imparting a throw vector on
// a unit. This is the only public interface with the struct.
public static method add takes unit u, real dx, real dy, real dz returns nothing
local thistype tempDat=thistype.create()
set tempDat.u=u
set tempDat.dX=dx*CLOCK_PERIOD
set tempDat.dY=dy*CLOCK_PERIOD
set tempDat.dZ=dz*CLOCK_PERIOD
set dbIndex=dbIndex+1
set DB[dbIndex]=tempDat
if UnitAddAbility(u,'Arav') then
call UnitRemoveAbility(u,'Arav')
endif
if dbIndex==0 then
call TimerStart(time,CLOCK_PERIOD,true,function thistype.p)
endif
endmethod
endstruct
// HasteHandler is a private struct used to increase the speed of
// allied units in a specific fashion implemented by Command:
// Distortion.
private struct HasteHandler
//**********************************************************
// Customizable Section
//**********************************************************
// The full haste duration units are imparted after leaving
// the distortion field in seconds.
private static constant real HASTE_DURATION=2.
// The clock period allows the user to control how accurate
// the slowing effect is. Recommend a value between 1./2. and
// 1./60.
private static constant real CLOCK_PERIOD=1./5.
// The rawcode ID of the dummy caster unit
private static constant integer DUMMY_CASTER_ID='h004'
// The rawcode ID of the haste ability
private static constant integer HASTE_ABILITY_ID='A007'
// The raw string order ID of the haste ability
private static constant string HASTE_STRING="bloodlust"
//**********************************************************
// End Customizable Section
//**********************************************************
private static thistype array DB
private static integer dbIndex=-1
private static HandleTable fromU
private static group grp=CreateGroup()
private static timer clock=CreateTimer()
private static unit caster
private unit who
private integer level=0
private real timeLeft=HASTE_DURATION
// The periodic method used to control the hasting of all
// units simultaneously
private static method periodic takes nothing returns nothing
local thistype tempDat
local integer index=0
loop
exitwhen index>dbIndex
set tempDat=DB[index]
set tempDat.timeLeft=tempDat.timeLeft-CLOCK_PERIOD
call SetUnitAbilityLevel(thistype.caster,thistype.HASTE_ABILITY_ID,R2I(tempDat.level*tempDat.timeLeft/thistype.HASTE_DURATION)+1)
call IssueTargetOrder(thistype.caster,HASTE_STRING,tempDat.who)
if tempDat.timeLeft<=0. then
call fromU.flush(tempDat.who)
call tempDat.destroy()
set DB[index]=DB[dbIndex]
set thistype.dbIndex=thistype.dbIndex-1
if dbIndex==-1 then
call PauseTimer(thistype.clock)
endif
endif
set index=index+1
endloop
endmethod
// This is the public method used to *set* a unit's haste
// value. This is important because we need to be able to
// over-write a unit's existing haste value if it steps
// back onto the distortion ring.
public static method setUnit takes unit u, integer z returns nothing
local thistype tempDat
if fromU.exists(u) then
set tempDat=fromU[u]
set tempDat.level=z
set tempDat.timeLeft=HASTE_DURATION
else
set tempDat=thistype.create()
set tempDat.who=u
set tempDat.level=z
set fromU[u]=tempDat
set dbIndex=dbIndex+1
set DB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(clock,CLOCK_PERIOD,true,function thistype.periodic)
endif
endif
call SetUnitAbilityLevel(thistype.caster,HASTE_ABILITY_ID,z)
call IssueTargetOrder(thistype.caster,HASTE_STRING,tempDat.who)
endmethod
// The initialization function called in the InitShim which
// creates a static dummy
private static method i takes nothing returns nothing
set thistype.fromU=HandleTable.create()
set thistype.caster=CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),DUMMY_CASTER_ID,0.,0.,0.)
call UnitAddAbility(thistype.caster,HASTE_ABILITY_ID)
endmethod
//calls i() on initialization
implement InitShim
endstruct
// SlowHandler is a private interface for handling the specific
// slow procedure used in Command: Distortion
private struct SlowHandler
//***********************************************************
// Customizable Section
//***********************************************************
// The duration of slows after leaving the distortion field
private static constant real SLOW_DURATION=2.
// The clock period for handling the slow timings. I suggest
// a value between 1./2. and 1./30.
private static constant real CLOCK_PERIOD=1./5.
// The unit ID of the dummy caster
private static constant integer DUMMY_CASTER_ID='h004'
// The ability ID of slow
private static constant integer SLOW_ABILITY_ID='A006'
// The order ID for the slow ability
private static constant string SLOW_ORDER="slow"
//***********************************************************
// End Customizable Section
//***********************************************************
private static thistype array DB
private static integer dbIndex=-1
private static HandleTable fromU
private static group grp=CreateGroup()
private static timer clock=CreateTimer()
private static unit caster
private unit who
private integer level=0
private real timeLeft=SLOW_DURATION
// Periodic function used to handle all the slow effects
// simultaneously.
private static method periodic takes nothing returns nothing
local thistype tempDat
local integer index=0
loop
exitwhen index>dbIndex
set tempDat=DB[index]
set tempDat.timeLeft=tempDat.timeLeft-CLOCK_PERIOD
call SetUnitAbilityLevel(thistype.caster,thistype.SLOW_ABILITY_ID,R2I(tempDat.level*tempDat.timeLeft/thistype.SLOW_DURATION)+1)
call IssueTargetOrder(thistype.caster,SLOW_ORDER,tempDat.who)
if tempDat.timeLeft<=0. then
call fromU.flush(tempDat.who)
call tempDat.destroy()
set DB[index]=DB[dbIndex]
set thistype.dbIndex=thistype.dbIndex-1
if dbIndex==-1 then
call PauseTimer(thistype.clock)
endif
endif
set index=index+1
endloop
endmethod
// The only public interface available for the SlowHandler.
// Allows us to set the slow level of a unit arbitrarily.
public static method setUnit takes unit u, integer z returns nothing
local thistype tempDat
if fromU.exists(u) then
set tempDat=fromU[u]
set tempDat.level=z
set tempDat.timeLeft=SLOW_DURATION
else
set tempDat=thistype.create()
set tempDat.who=u
set tempDat.level=z
set fromU[u]=tempDat
set dbIndex=dbIndex+1
set DB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(clock,CLOCK_PERIOD,true,function thistype.periodic)
endif
endif
call SetUnitAbilityLevel(thistype.caster,SLOW_ABILITY_ID,z)
call IssueTargetOrder(thistype.caster,"slow",tempDat.who)
endmethod
// Initialization function called by InitShim
private static method i takes nothing returns nothing
set thistype.fromU=HandleTable.create()
set thistype.caster=CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),DUMMY_CASTER_ID,0.,0.,0.)
call UnitAddAbility(thistype.caster,SLOW_ABILITY_ID)
endmethod
implement InitShim
endstruct
// Essentially a sound in stuct form - necessary due
// to the implementation specifics
private struct soundShim
sound s
endstruct
// Struct which overrides the library title. Here is
// the real contents of Orianna.
struct Orianna
private static group grp=CreateGroup()
private static integer dbIndex=-1
private static timer clock=CreateTimer()
private static thistype array DB
private static HandleTable fromOri
private static HandleTable preloadTable
private static boolean threadCheck = true
//**************************************************************
// Customizable Section
//**************************************************************
// The unit rawcode for the dummy which points to an Orianna
// instance's ball
private static constant integer ARROW_ID='h001'
// Unit rawcode for a ball
private static constant integer BALL_ID='h000'
// Unit rawcode for the Command: Disonnance indicator
private static constant integer DISONNANCE_INDICATOR_ID='h003'
// Unit rawcode for the small stand below an immobile ball
private static constant integer STAND_INDICATOR_ID='h002'
// Unit rawcode for the circle that appears while Command:
// Shockwave is charging
private static constant integer SHOCKWAVE_INDICATOR_ID='h005'
// Unit rawcode for the dummy electric balls that appear
// while casting Command: Shockwave
private static constant integer SHOCKWAVE_ORB_ID='h006'
// Opacity value for the ball (Out of 255)
private static constant integer BALL_OPACITY=200
// Opacity value for the disonnance indicator (Out of 255)
private static constant integer DISONNANCE_INDICATOR_OPACITY=155
// Disonnance indicator Red concentration (Out of 255)
private static constant integer DISONNANCE_INDICATOR_RED=55
// Disonnance indicator Green concentration (Out of 255)
private static constant integer DISONNANCE_INDICATOR_GREEN=165
// Disonnance indicator Blue concentration (Out of 255)
private static constant integer DISONNANCE_INDICATOR_BLUE=55
// How far away should Orianna's ball pointer be from here
// in game units
private static constant real ARROW_HOVER_DISTANCE=100.
// Model scale of the ball pointer
private static constant real ARROW_SCALE=.5
// Fly height of the ball pointer
private static constant real ARROW_FLY_HEIGHT=50.
// Distance to the ball at which the ball pointer is hidden
private static constant real ARROW_MIN_RANGE=150.
// Maximum distance at which the arrow is green (close)
private static constant real ARROW_GREEN_RANGE=650.
// Maximum distance at which the arrow is yellow (far)
private static constant real ARROW_YELLOW_RANGE=925.
// The radius around the ball which damages enemies while moving
private static constant real BALL_ATTACK_DAMAGE_RADIUS=100.
// The sound pitch of the ball's begin move sound
private static constant real BALL_MOVE_START_SOUND_PITCH=1.25
// The sound pitch of the ball's hit sound
private static constant real BALL_MOVE_HIT_SOUND_PITCH=.5
// The sound pitch of the ball's end move sound
private static constant real BALL_MOVE_END_SOUND_PITCH=.75
// The flying height a ball rests at while on a unit
private static constant real BALL_OVERHEAD_HEIGHT=150.
// The flyight height a ball rests at while on the ground
private static constant real BALL_GROUND_HEIGHT=50.
// The animation scale (speed) used by a ball by default
private static constant real BALL_TIME_SCALE=1./10.
// The animation scale (speed) used by a ball on the ground
private static constant real BALL_TIME_SCALE_GROUND=1./5.
// The maximum range a ball can be away from Orianna while
// on the ground
private static constant real BALL_LEASH_RANGE_GROUND=1125.
// The maximum range a ball can be away from Orianna while
// attached to another unit
private static constant real BALL_LEASH_RANGE_ONUNIT=1225.
// How close must Orianna get to the ball to pick it up
private static constant real BALL_PICKUP_RANGE=64.
// How fast should the ball move while under the effects of
// Command: Attack or Command: Shockwave
private static constant real BALL_VELOCITY=1200.
// How accurate should the timing clock be for Orianna?
// Recommend value between 1./27. and 1./80.
private static constant real CLOCK_PERIOD=1./90.
// How long is too long between attacks to retain Orianna's
// innate?
private static constant real CLOCKWORK_WINDUP_TIMEOUT=4.
// How much flat damage should Command: Attack deal?
private static constant real COMMAND_ATTACK_FLAT_DAMAGE=30.
// How much damage per level should Command: Attack deal?
private static constant real COMMAND_ATTACK_DAMAGE_PER_LEVEL=30.
// What multiplier should be used for Command: Attack
private static constant real COMMAND_ATTACK_INTELLIGENCE_MULTIPLIER=.5
// What is the minimum damage multiplier a unit can take from
// Command: Attack?
private static constant real COMMAND_ATTACK_MINIMUM_DAMAGE_MULTIPLIER=.4
// How much should Command: Attack's damage be reduced on
// subsequent hit?
private static constant real COMMAND_ATTACK_DAMAGE_REDUCTION=.1
// Modle scale of the disonnance indicator
private static constant real DISONNANCE_INDICATOR_SCALE=2.5
// Fly height of the active disonnance indicator
private static constant real DISONNANCE_INDICATOR_HEIGHT=25.
// Radius of the active disonnance effect
private static constant real DISONNANCE_RADIUS=190.
// Flat damage of Command: Disonnance
private static constant real DISONNANCE_FLAT_DAMAGE=25.
// Additional damage per level of Command: Disonnance
private static constant real DISONNANCE_DAMAGE_PER_LEVEL=45.
// Multiplier for intelligence based damage on Command:
// Disonnance
private static constant real DISONNANCE_INTELLIGENCE_MULTIPLIER=.7
// Duration of Command: Disonnance's Active Effect
private static constant real DISONNANCE_ACTIVE_DURATION=3.
// The duration at the end of Disonnance's effect during which
// the Disonnance indicator should fade away
private static constant real DISONNANCE_INDICATOR_FADE_PERIOD=.25
// Fly height off the ball when attached to an allied unit
private static constant real PROTECT_ATTACH_RANGE=100.
// Flying height given to units who are "hidden" in the top
// left of the map
private static constant real DUMMY_HIDDEN_HEIGHT=2000.
// Model scale of "hidden" units
private static constant real DUMMY_HIDDEN_SCALE=1./100.
// X value of the map's top left corner (should be inside the
// map boundary)
private static constant real MAP_TOP_LEFT_X=-7680.
// Y value
private static constant real MAP_TOP_LEFT_Y=5630.
// model scale of the dummy indicator for a ball on the ground
private static constant real STAND_INDICATOR_SCALE=.4
// Duration in seconds of the shield imparted by Command:
// Protect
private static constant real SHIELD_DURATION=4.
// Flat shield value of Command: Protect
private static constant real SHIELD_FLAT_AMOUNT=40.
// Flat damage value of Command: Protect
private static constant real SHIELD_FLAT_DAMAGE=30.
// Damage per level of Command: Protect
private static constant real SHIELD_DAMAGE_PER_LEVEL=30.
// Additional intelligence based damage multiplier for
// Command: Protect
private static constant real SHIELD_BONUS_DAMAGE_MULTIPLIER=.3
// Shield value per level of Command: Protect
private static constant real SHIELD_AMOUNT_PER_LEVEL=40.
// Bonus Shield per intelligence of Command: Protect
private static constant real SHIELD_BONUS_MULTIPLIER=.4
// Model scale of the Command: Shockwave indicator
private static constant real SHOCK_INDIC_SCALE=8.
// Radius of Command: Shockwave's effect
private static constant real SHOCK_RADIUS=300.
// Flying height of the Shockwave Indicator
private static constant real SHOCK_INDIC_HEIGHT=-100.
// Duration of Command: Shockwave's windup animation
private static constant real SHOCK_WINDUP_DURATION=.5
// Rotational velocity of shockwave orbs during the
// windup animation
private static constant real SHOCK_WINDUP_ORB_SPEED=3.*bj_PI
// The phase difference between the shockwave orbs
private static constant real SHOCK_ORB_PHASE_DIFFERENCE=2.*bj_PI/3.
// Model scale of shockwave orbs
private static constant real SHOCK_ORB_SCALE=1.25
// XY velocity at which units are thrown under the
// effect of Command: Shockwave
private static constant real THROW_VELOCITY=500.
// Multiplier for the initial vertical velocity
private static constant real THROW_VERT_MULTIPLIER=3.
// Spawned effect when an ability hits a unit
private static constant string ABILITY_HIT_EFFECT="Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl"
// Model file for the dummy that rests on the ball while it
// remains on the ground
private static constant string BALL_GROUND_EFFECT="buildings\\other\\CircleOfPower\\CircleOfPower.mdl"
// Sound played when the ball begins moving
private static constant string BALL_MOVE_START_SOUND="Abilities\\Spells\\Orc\\Ensnare\\EnsnareMissile.wav"
// Sound played when the ball finishes moving
private static constant string BALL_MOVE_END_SOUND="Abilities\\Weapons\\ShadowHunterMissile\\HeroShadowhunterMissileHit1.wav"
// Model displayed when disonnance occurs
private static constant string DISONNANCE_EFFECT="Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
// Model displayed when disonnacne hits a unit
private static constant string DISONNANCE_HIT_EFFECT="Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodFootman.mdl"
// Attachment point for the effect that occurs when a unit
// is hit by Command: Dissonance
private static constant string DISONNANCE_EFFECT_TARGET="chest"
// Model used when Command: Protect shields a unit
private static constant string SHIELD_FX="Abilities\\Spells\\Human\\ManaShield\\ManaShieldCaster.mdl"
// Orianna Raw ID
public static constant integer ID='E000'
// Volume passed for max volume (sounds)
public static constant integer SOUND_MAX_VOLUME=127
// Fade rate passed for max fade rate (sounds)
public static constant integer SOUND_MAX_FADERATE=12700
// Ball state values (change this if you want to avoid
// using identical values within other interfaces... I guess)
public static constant integer BALL_STATE_DEAD='d'
public static constant integer BALL_STATE_HOME='h'
public static constant integer BALL_STATE_ATTACK='a'
public static constant integer BALL_STATE_GROUND='g'
public static constant integer BALL_STATE_TARGET='t'
public static constant integer BALL_STATE_ONUNIT='o'
public static constant integer BALL_STATE_SHOCKWAVE='s'
// Ability raw ID for command: attack
public static constant integer COMMAND_ATTACK_ID='A002'
// Ability raw ID for the PASSIVE (disabled) Command: attack
public static constant integer COMMAND_ATTACK_DIS_ID='A004'
// Ability IDs for the active and passive Command: Disonnance
public static constant integer COMMAND_DISONNANCE_ID='A005'
public static constant integer COMMAND_DISSONANCE_DIS_ID='A008'
// Ability IDs for the active and passive Command: Protect
public static constant integer COMMAND_PROTECT_ID='A009'
public static constant integer COMMAND_PROTECT_DIS_ID='A00A'
// Ability ID for Command: Protect's passive effect
public static constant integer PROTECT_PASSIVE_ID='A00D'
// Ability IDs for the active and passiev Command: Shockwave
public static constant integer COMMAND_SHOCKWAVE_ID='A00B'
public static constant integer COMMAND_SHOCKWAVE_DIS_ID='A00C'
// Method ID passed. This probably won't have much use unless
// you decide to implement a second, different version of
// Orianna's innate (why?)
public static constant integer METHOD_INCREMENT_AND_COUNT='a'
//**************************************************************
// End Customizable Section
//**************************************************************
private group alreadyHit
private unit ori
private unit ball
private unit arrow
private unit shockwaveIndicator
private unit shockwaveOrb1
private unit shockwaveOrb2
private unit shockwaveOrb3
private unit standIndicator
private unit disonnanceIndicator
private unit target
private unit pTarget
private unit protectBonusUnit=null
private integer attackCount=0
private integer state=0
private integer steps=0
private integer attackRevertLevel=0
private integer dissonanceRevertLevel=0
private integer protectRevertLevel=0
private integer shockwaveRevertLevel=0
private real counterTimeout=0.
private real targetX=0.
private real targetY=0.
private real damage=0.
private real disonnanceTimeLeft=0.
private real delX=0.
private real delY=0.
private real delZ=0.
private real minDamage=0.
private real shieldAmount=0.
private player owner
// Method used to passify (disable) Orianna's abilities - occurs
// when the ball already has some action.
private method passifyAbilities takes nothing returns nothing
set this.attackRevertLevel=GetUnitAbilityLevel(ori,COMMAND_ATTACK_ID)
set this.dissonanceRevertLevel=GetUnitAbilityLevel(ori,COMMAND_DISONNANCE_ID)
set this.protectRevertLevel=GetUnitAbilityLevel(ori,COMMAND_PROTECT_ID)
set this.shockwaveRevertLevel=GetUnitAbilityLevel(ori,COMMAND_SHOCKWAVE_ID)
if GetUnitAbilityLevel(this.ori,COMMAND_ATTACK_ID)>0 then
call SetUnitAbilityLevel(this.ori,COMMAND_ATTACK_ID,5)
call IncUnitAbilityLevel(this.ori,COMMAND_ATTACK_ID)
call UnitAddAbility(this.ori,COMMAND_ATTACK_DIS_ID)
endif
if GetUnitAbilityLevel(this.ori,COMMAND_DISONNANCE_ID)>0 then
call SetUnitAbilityLevel(this.ori,COMMAND_DISONNANCE_ID,5)
call IncUnitAbilityLevel(this.ori,COMMAND_DISONNANCE_ID)
call UnitAddAbility(this.ori,COMMAND_DISSONANCE_DIS_ID)
endif
if GetUnitAbilityLevel(this.ori,COMMAND_PROTECT_ID)>0 then
call SetUnitAbilityLevel(this.ori,COMMAND_PROTECT_ID,5)
call IncUnitAbilityLevel(this.ori,COMMAND_PROTECT_ID)
call UnitAddAbility(this.ori,COMMAND_PROTECT_DIS_ID)
endif
if GetUnitAbilityLevel(this.ori,COMMAND_SHOCKWAVE_ID)>0 then
call SetUnitAbilityLevel(this.ori,COMMAND_SHOCKWAVE_ID,3)
call IncUnitAbilityLevel(this.ori,COMMAND_SHOCKWAVE_ID)
call UnitAddAbility(this.ori,COMMAND_SHOCKWAVE_DIS_ID)
endif
endmethod
// Method used to re-activate Orianna's active abilities
private method actifyAbilities takes nothing returns nothing
call UnitRemoveAbility(this.ori,COMMAND_ATTACK_DIS_ID)
call SetUnitAbilityLevel(this.ori,COMMAND_ATTACK_ID,this.attackRevertLevel)
call UnitRemoveAbility(this.ori,COMMAND_DISSONANCE_DIS_ID)
call SetUnitAbilityLevel(this.ori,COMMAND_DISONNANCE_ID,this.dissonanceRevertLevel)
call UnitRemoveAbility(this.ori,COMMAND_PROTECT_DIS_ID)
call SetUnitAbilityLevel(this.ori,COMMAND_PROTECT_ID,this.protectRevertLevel)
call UnitRemoveAbility(this.ori,COMMAND_SHOCKWAVE_DIS_ID)
call SetUnitAbilityLevel(this.ori,COMMAND_SHOCKWAVE_ID,this.shockwaveRevertLevel)
endmethod
// Public method called when a client wants to Command: Attack a location
public static method attack takes unit ori, real x, real y returns nothing
local thistype tempDat=fromOri[ori]
local real dist
local real bx=GetUnitX(tempDat.ball)
local real by=GetUnitY(tempDat.ball)
local real theta=Atan2(y-by,x-bx)
local sound snd=CreateSound(BALL_MOVE_START_SOUND,false,true,true,SOUND_MAX_FADERATE,SOUND_MAX_FADERATE,"")
call SetSoundVolume(snd,SOUND_MAX_VOLUME)
call SetSoundPosition(snd,bx,by,BALL_GROUND_HEIGHT)
call SetSoundPitch(snd,BALL_MOVE_START_SOUND_PITCH)
call StartSound(snd)
call KillSoundWhenDone(snd)
call GroupClear(tempDat.alreadyHit)
if tempDat.state==BALL_STATE_GROUND then
call SetUnitX(tempDat.standIndicator,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.standIndicator,MAP_TOP_LEFT_Y)
call SetUnitScale(tempDat.standIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
endif
set tempDat.state=BALL_STATE_ATTACK
set tempDat.targetX=x
set tempDat.targetY=y
set dist=SquareRoot((x-bx)*(x-bx)+(y-by)*(y-by))
set tempDat.steps=R2I(dist/(BALL_VELOCITY*CLOCK_PERIOD))
set tempDat.delX=BALL_VELOCITY*CLOCK_PERIOD*Cos(theta)
set tempDat.delY=BALL_VELOCITY*CLOCK_PERIOD*Sin(theta)
set tempDat.delZ=(BALL_GROUND_HEIGHT-GetUnitFlyHeight(tempDat.ball))/tempDat.steps
set tempDat.damage=COMMAND_ATTACK_FLAT_DAMAGE + COMMAND_ATTACK_DAMAGE_PER_LEVEL*tempDat.attackRevertLevel + COMMAND_ATTACK_INTELLIGENCE_MULTIPLIER*GetHeroInt(ori,true)
set tempDat.minDamage=tempDat.damage*COMMAND_ATTACK_MINIMUM_DAMAGE_MULTIPLIER
if tempDat.protectBonusUnit!=null then
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=null
endif
call tempDat.passifyAbilities()
set snd=null
endmethod
// Public method used when an Orianna should cast Command: Disonnance
public static method disonnance takes unit ori returns nothing
local thistype tempDat=fromOri[ori]
local real cX=GetUnitX(tempDat.ball)
local real cY=GetUnitY(tempDat.ball)
local real damage=DISONNANCE_FLAT_DAMAGE+DISONNANCE_DAMAGE_PER_LEVEL*GetUnitAbilityLevel(tempDat.ori,COMMAND_DISONNANCE_ID)+GetHeroInt(tempDat.ori,true)*DISONNANCE_INTELLIGENCE_MULTIPLIER
local unit FoG
set tempDat.disonnanceTimeLeft=DISONNANCE_ACTIVE_DURATION
call SetUnitX(tempDat.disonnanceIndicator,cX)
call SetUnitY(tempDat.disonnanceIndicator,cY)
call SetUnitScale(tempDat.disonnanceIndicator,DISONNANCE_INDICATOR_SCALE,DISONNANCE_INDICATOR_SCALE,DISONNANCE_INDICATOR_SCALE)
call DestroyEffect(AddSpecialEffect(DISONNANCE_EFFECT,cX,cY))
call GroupEnumUnitsInRange(thistype.grp,cX,cY,DISONNANCE_RADIUS,null)
loop
set FoG=FirstOfGroup(thistype.grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,tempDat.owner) and UnitAlive(FoG) then
call DamageType.dealCodeDamage(tempDat.ori,FoG,damage)
call DestroyEffect(AddSpecialEffectTarget(DISONNANCE_HIT_EFFECT,FoG,DISONNANCE_EFFECT_TARGET))
endif
call GroupRemoveUnit(thistype.grp,FoG)
endloop
endmethod
// Public method used when an orianna should Command: Protect an allied unit
public static method protect takes unit ori, unit target returns nothing
local thistype tempDat=fromOri[ori]
local real int=GetHeroInt(ori,true)
set tempDat.pTarget=target
call SetUnitTimeScale(tempDat.ball,BALL_TIME_SCALE)
call SetUnitFlyHeight(tempDat.ball,BALL_OVERHEAD_HEIGHT,0.)
call SetUnitX(tempDat.standIndicator,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.standIndicator,MAP_TOP_LEFT_Y)
call SetUnitScale(tempDat.standIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call GroupClear(tempDat.alreadyHit)
set tempDat.state=BALL_STATE_TARGET
set tempDat.shieldAmount=SHIELD_FLAT_AMOUNT+SHIELD_AMOUNT_PER_LEVEL*tempDat.protectRevertLevel+SHIELD_BONUS_MULTIPLIER*int
set tempDat.damage=SHIELD_FLAT_DAMAGE+SHIELD_DAMAGE_PER_LEVEL*tempDat.protectRevertLevel+SHIELD_BONUS_DAMAGE_MULTIPLIER*int
if tempDat.protectBonusUnit!=null then
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=null
endif
call tempDat.passifyAbilities()
endmethod
// Public method used when an Orianna should Command: Shockwave
public static method shockwave takes unit ori returns nothing
local thistype tempDat=fromOri[ori]
local real bX=GetUnitX(tempDat.ball)
local real bY=GetUnitY(tempDat.ball)
call SetUnitTimeScale(tempDat.ball,BALL_TIME_SCALE)
call SetUnitFlyHeight(tempDat.ball,BALL_GROUND_HEIGHT,0.)
set tempDat.steps=0
set tempDat.state=BALL_STATE_SHOCKWAVE
call SetUnitX(tempDat.shockwaveIndicator,bX)
call SetUnitY(tempDat.shockwaveIndicator,bY)
call SetUnitScale(tempDat.shockwaveIndicator,SHOCK_INDIC_SCALE,SHOCK_INDIC_SCALE,SHOCK_INDIC_SCALE)
call SetUnitFlyHeight(tempDat.shockwaveIndicator,SHOCK_INDIC_HEIGHT,0.)
call SetUnitX(tempDat.shockwaveOrb1,bX+SHOCK_RADIUS*Cos(0.*SHOCK_ORB_PHASE_DIFFERENCE))
call SetUnitY(tempDat.shockwaveOrb1,bY+SHOCK_RADIUS*Sin(0.*SHOCK_ORB_PHASE_DIFFERENCE))
call SetUnitScale(tempDat.shockwaveOrb1,SHOCK_ORB_SCALE,SHOCK_ORB_SCALE,SHOCK_ORB_SCALE)
call SetUnitX(tempDat.shockwaveOrb2,bX+SHOCK_RADIUS*Cos(1.*SHOCK_ORB_PHASE_DIFFERENCE))
call SetUnitY(tempDat.shockwaveOrb2,bY+SHOCK_RADIUS*Sin(1.*SHOCK_ORB_PHASE_DIFFERENCE))
call SetUnitScale(tempDat.shockwaveOrb2,SHOCK_ORB_SCALE,SHOCK_ORB_SCALE,SHOCK_ORB_SCALE)
call SetUnitX(tempDat.shockwaveOrb3,bX+SHOCK_RADIUS*Cos(2.*SHOCK_ORB_PHASE_DIFFERENCE))
call SetUnitY(tempDat.shockwaveOrb3,bY+SHOCK_RADIUS*Sin(2.*SHOCK_ORB_PHASE_DIFFERENCE))
call SetUnitScale(tempDat.shockwaveOrb3,SHOCK_ORB_SCALE,SHOCK_ORB_SCALE,SHOCK_ORB_SCALE)
if tempDat.protectBonusUnit!=null then
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=null
endif
endmethod
// Method used to get and increment the hitcount for an Orianna;
// used by her innate
public static method hitCount takes unit ori, unit targ, integer meth returns integer
local thistype tempDat
if meth==METHOD_INCREMENT_AND_COUNT then
set tempDat=thistype.fromOri[ori]
if tempDat.target==targ then
set tempDat.attackCount=tempDat.attackCount+1
else
set tempDat.target=targ
set tempDat.attackCount=1
endif
set tempDat.counterTimeout=CLOCKWORK_WINDUP_TIMEOUT
return tempDat.attackCount
endif
return 0
endmethod
// Method used for preloading sounds after a timer
private static method preloadSoundAfter takes nothing returns nothing
local timer time=GetExpiredTimer()
local soundShim shim=thistype.preloadTable[time]
call StartSound(shim.s)
call KillSoundWhenDone(shim.s)
call shim.destroy()
set time=null
endmethod
// Method used to preload sounds
private static method preloadSound takes string s returns nothing
local timer time=CreateTimer()
local sound snd=CreateSound(s,false,false,true,SOUND_MAX_FADERATE,SOUND_MAX_FADERATE,"")
local soundShim shim=soundShim.create()
set shim.s=snd
set thistype.preloadTable[time]=shim
call SetSoundVolume(snd,1)
call TimerStart(time,0.,false,function thistype.preloadSoundAfter)
set time=null
set snd=null
endmethod
// Periodic method that handles all the dynamic movement and timings
// of Orianna's basic effects... take a deep breath.
private static method periodic takes nothing returns nothing
local integer index=0
local thistype tempDat
local real ballX
local real ballY
local real dx
local real dy
local real pX
local real pY
local real pAng
local real sid
local real phase
local real uX
local real uY
local real dist2
local real theta
local sound snd
local unit FoG
static if DEBUG_MODE then
if not threadCheck then
call BJDebugMsg("Warning in Orianna.periodic: Thread Crash check failed!")
endif
set threadCheck = false
endif
loop
exitwhen index>dbIndex
set tempDat=DB[index]
set ballX=GetUnitX(tempDat.ball)
set ballY=GetUnitY(tempDat.ball)
set dx=ballX-GetUnitX(tempDat.ori)
set dy=ballY-GetUnitY(tempDat.ori)
set dist2=dx*dx+dy*dy
// Some logic for Clockwork Windup
if tempDat.counterTimeout>0. then
set tempDat.counterTimeout=tempDat.counterTimeout-CLOCK_PERIOD
if tempDat.counterTimeout<=0. then
set tempDat.target=null
endif
endif
// Some logic for disonnance's effect
if tempDat.disonnanceTimeLeft>0. then
set tempDat.disonnanceTimeLeft=tempDat.disonnanceTimeLeft-CLOCK_PERIOD
// 36 is between an arbitrary value and a constant. It's just a
// tenth of a circle.
call SetUnitFacingTimed(tempDat.disonnanceIndicator,GetUnitFacing(tempDat.disonnanceIndicator)-36.,2.)
if tempDat.disonnanceTimeLeft<DISONNANCE_INDICATOR_FADE_PERIOD then
call SetUnitVertexColor(tempDat.disonnanceIndicator,DISONNANCE_INDICATOR_RED,DISONNANCE_INDICATOR_GREEN,DISONNANCE_INDICATOR_BLUE,R2I(DISONNANCE_INDICATOR_OPACITY*tempDat.disonnanceTimeLeft/DISONNANCE_INDICATOR_FADE_PERIOD))
endif
call GroupEnumUnitsInRange(thistype.grp,GetUnitX(tempDat.disonnanceIndicator),GetUnitY(tempDat.disonnanceIndicator),thistype.DISONNANCE_RADIUS,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,tempDat.owner) then
call SlowHandler.setUnit(FoG,GetUnitAbilityLevel(tempDat.ori,thistype.COMMAND_DISONNANCE_ID)+3)
else
call HasteHandler.setUnit(FoG,GetUnitAbilityLevel(tempDat.ori,thistype.COMMAND_DISONNANCE_ID)+3)
endif
call GroupRemoveUnit(grp,FoG)
endloop
if tempDat.disonnanceTimeLeft<=0. then
set tempDat.disonnanceTimeLeft=0.
call SetUnitVertexColor(tempDat.disonnanceIndicator,DISONNANCE_INDICATOR_RED,DISONNANCE_INDICATOR_GREEN,DISONNANCE_INDICATOR_BLUE,DISONNANCE_INDICATOR_OPACITY)
call SetUnitX(tempDat.disonnanceIndicator,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.disonnanceIndicator,MAP_TOP_LEFT_Y)
call SetUnitScale(tempDat.disonnanceIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
endif
endif
// Some logic for when a ball is attached to a unit
if tempDat.state==BALL_STATE_ONUNIT then
call SetUnitX(tempDat.ball,GetUnitX(tempDat.pTarget))
call SetUnitY(tempDat.ball,GetUnitY(tempDat.pTarget))
if dist2>BALL_LEASH_RANGE_ONUNIT*BALL_LEASH_RANGE_ONUNIT then
set tempDat.state=BALL_STATE_HOME
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=tempDat.ori
call UnitAddAbility(tempDat.ori,PROTECT_PASSIVE_ID)
endif
endif
// Some logic while the the ball has been issued Command:
// Attack
if tempDat.state==BALL_STATE_ATTACK then
set uX=GetUnitX(tempDat.ball)+tempDat.delX
set uY=GetUnitY(tempDat.ball)+tempDat.delY
call SetUnitX(tempDat.ball,uX)
call SetUnitY(tempDat.ball,uY)
call SetUnitFlyHeight(tempDat.ball,GetUnitFlyHeight(tempDat.ball)+tempDat.delZ,0.)
set tempDat.steps=tempDat.steps-1
call GroupEnumUnitsInRange(grp,uX,uY,BALL_ATTACK_DAMAGE_RADIUS,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,tempDat.owner) and (not IsUnitInGroup(FoG,tempDat.alreadyHit)) then
call DamageType.dealCodeDamage(tempDat.ori,FoG,tempDat.damage)
call DestroyEffect(AddSpecialEffectTarget(ABILITY_HIT_EFFECT,FoG,"chest"))
set tempDat.damage=tempDat.damage*(1.-COMMAND_ATTACK_DAMAGE_REDUCTION)
call GroupAddUnit(tempDat.alreadyHit,FoG)
if tempDat.damage<tempDat.minDamage then
set tempDat.damage=tempDat.minDamage
endif
endif
call GroupRemoveUnit(grp,FoG)
endloop
if tempDat.steps<1 then
call SetUnitX(tempDat.ball,tempDat.targetX)
call SetUnitY(tempDat.ball,tempDat.targetY)
call SetUnitFlyHeight(tempDat.ball,BALL_GROUND_HEIGHT,0.)
call SetUnitTimeScale(tempDat.ball,BALL_TIME_SCALE_GROUND)
call SetUnitX(tempDat.standIndicator,tempDat.targetX)
call SetUnitY(tempDat.standIndicator,tempDat.targetY)
call SetUnitScale(tempDat.standIndicator,STAND_INDICATOR_SCALE,STAND_INDICATOR_SCALE,STAND_INDICATOR_SCALE)
set snd=CreateSound(BALL_MOVE_END_SOUND,false,true,true,12700,12700,"")
call tempDat.actifyAbilities()
call SetSoundVolume(snd,127)
call SetSoundPitch(snd,BALL_MOVE_END_SOUND_PITCH)
call SetSoundPosition(snd,tempDat.targetX,tempDat.targetY,BALL_GROUND_HEIGHT)
call StartSound(snd)
call KillSoundWhenDone(snd)
set tempDat.state=BALL_STATE_GROUND
endif
// Some logic while the ball has been issued Command:
// Shockwave
elseif tempDat.state==BALL_STATE_SHOCKWAVE then
set tempDat.steps=tempDat.steps+1
set sid=SHOCK_RADIUS*(-1.*CLOCK_PERIOD*tempDat.steps/SHOCK_WINDUP_DURATION+1)
set phase=tempDat.steps*CLOCK_PERIOD*SHOCK_WINDUP_ORB_SPEED
if GetUnitAbilityLevel(tempDat.ori,COMMAND_SHOCKWAVE_DIS_ID)==0 then
call tempDat.passifyAbilities()
endif
call SetUnitX(tempDat.shockwaveOrb1,ballX+sid*Cos(2*bj_PI+phase))
call SetUnitY(tempDat.shockwaveOrb1,ballY+sid*Sin(2*bj_PI+phase))
call SetUnitX(tempDat.shockwaveOrb2,ballX+sid*Cos(2.*bj_PI/3.+phase))
call SetUnitY(tempDat.shockwaveOrb2,ballY+sid*Sin(2.*bj_PI/3.+phase))
call SetUnitX(tempDat.shockwaveOrb3,ballX+sid*Cos(4.*bj_PI/3.+phase))
call SetUnitY(tempDat.shockwaveOrb3,ballY+sid*Sin(4.*bj_PI/3.+phase))
if tempDat.steps>SHOCK_WINDUP_DURATION/CLOCK_PERIOD then
set tempDat.state=BALL_STATE_GROUND
call GroupEnumUnitsInRange(grp,GetUnitX(tempDat.shockwaveIndicator),GetUnitY(tempDat.shockwaveIndicator),250.,null)
call tempDat.actifyAbilities()
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
set pY=75.+75.*GetUnitAbilityLevel(tempDat.ori,COMMAND_SHOCKWAVE_ID)+0.7*GetHeroInt(tempDat.ori,true)
if IsUnitEnemy(FoG,tempDat.owner) and UnitAlive(FoG) then
set pX=Atan2(GetUnitY(tempDat.ball)-GetUnitY(FoG),GetUnitX(tempDat.ball)-GetUnitX(FoG))
call DamageType.dealCodeDamage(tempDat.ori,FoG,pY)
call ThrowHandler.add(FoG,THROW_VELOCITY*Cos(pX),THROW_VELOCITY*Sin(pX),THROW_VELOCITY*THROW_VERT_MULTIPLIER)
endif
call GroupRemoveUnit(grp,FoG)
endloop
call SetUnitX(tempDat.shockwaveOrb1,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.shockwaveOrb1,MAP_TOP_LEFT_Y)
call SetUnitX(tempDat.shockwaveOrb2,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.shockwaveOrb2,MAP_TOP_LEFT_Y)
call SetUnitX(tempDat.shockwaveOrb3,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.shockwaveOrb3,MAP_TOP_LEFT_Y)
call SetUnitX(tempDat.shockwaveIndicator,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.shockwaveIndicator,MAP_TOP_LEFT_Y)
endif
// Some logic while the ball has been issued Command:
// Protect
elseif tempDat.state==BALL_STATE_TARGET then
set pX=GetUnitX(tempDat.pTarget)-ballX
set pY=GetUnitY(tempDat.pTarget)-ballY
set pAng=Atan2(pY,pX)
set uX=ballX+thistype.BALL_VELOCITY*thistype.CLOCK_PERIOD*Cos(pAng)
set uY=ballY+thistype.BALL_VELOCITY*thistype.CLOCK_PERIOD*Sin(pAng)
call SetUnitX(tempDat.ball,uX)
call SetUnitY(tempDat.ball,uY)
call GroupEnumUnitsInRange(grp,uX,uY,BALL_ATTACK_DAMAGE_RADIUS,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if IsUnitEnemy(FoG,tempDat.owner) and (not IsUnitInGroup(FoG,tempDat.alreadyHit)) then
call DamageType.dealCodeDamage(tempDat.ori,FoG,tempDat.damage)
call DestroyEffect(AddSpecialEffectTarget(ABILITY_HIT_EFFECT,FoG,"chest"))
call GroupAddUnit(tempDat.alreadyHit,FoG)
endif
call GroupRemoveUnit(grp,FoG)
endloop
if (pX*pX+pY*pY)<PROTECT_ATTACH_RANGE*PROTECT_ATTACH_RANGE then
set tempDat.state=BALL_STATE_ONUNIT
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=tempDat.pTarget
call UnitAddAbility(tempDat.pTarget,PROTECT_PASSIVE_ID)
call Shield.add(tempDat.pTarget,tempDat.shieldAmount,SHIELD_DURATION,SHIELD_FX,"origin")
call tempDat.actifyAbilities()
endif
// Other stuff for when Orianna is alive
elseif UnitAlive(tempDat.ori) then
if tempDat.state==BALL_STATE_DEAD then
set tempDat.state=BALL_STATE_HOME
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=tempDat.ori
call UnitAddAbility(tempDat.ori,PROTECT_PASSIVE_ID)
call SetUnitFlyHeight(tempDat.ball,BALL_OVERHEAD_HEIGHT,0.)
call SetUnitVertexColor(tempDat.ball,255,255,255,255)
call SetUnitOwner(tempDat.ball,tempDat.owner,false)
call SetUnitPosition(tempDat.ball,GetUnitX(tempDat.ori),GetUnitY(tempDat.ori))
elseif tempDat.state==BALL_STATE_GROUND then
if dist2>BALL_LEASH_RANGE_GROUND*BALL_LEASH_RANGE_GROUND or dist2<BALL_PICKUP_RANGE*BALL_PICKUP_RANGE then
call SetUnitTimeScale(tempDat.ball,BALL_TIME_SCALE)
call SetUnitFlyHeight(tempDat.ball,BALL_OVERHEAD_HEIGHT,0.)
call SetUnitX(tempDat.standIndicator,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.standIndicator,MAP_TOP_LEFT_Y)
call SetUnitScale(tempDat.standIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
set tempDat.state=BALL_STATE_HOME
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=tempDat.ori
call UnitAddAbility(tempDat.ori,PROTECT_PASSIVE_ID)
endif
elseif tempDat.state==BALL_STATE_HOME then
call SetUnitX(tempDat.ball,GetUnitX(tempDat.ori))
call SetUnitY(tempDat.ball,GetUnitY(tempDat.ori))
endif
// ... and when she's dead
elseif not UnitAlive(tempDat.ori) then
call SetUnitX(tempDat.ball,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.ball,MAP_TOP_LEFT_Y)
call SetUnitFlyHeight(tempDat.ball,DUMMY_HIDDEN_HEIGHT,0.)
call SetUnitVertexColor(tempDat.ball,255,255,255,0)
call SetUnitOwner(tempDat.ball,Player(PLAYER_NEUTRAL_PASSIVE),false)
if tempDat.state==BALL_STATE_GROUND then
call SetUnitX(tempDat.standIndicator,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.standIndicator,MAP_TOP_LEFT_Y)
call SetUnitScale(tempDat.standIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
endif
set tempDat.state=BALL_STATE_DEAD
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=null
endif
// When to hide the indicator
if tempDat.state==BALL_STATE_HOME or tempDat.state==BALL_STATE_DEAD or dist2<ARROW_MIN_RANGE*ARROW_MIN_RANGE then
call SetUnitX(tempDat.arrow,MAP_TOP_LEFT_X)
call SetUnitY(tempDat.arrow,MAP_TOP_LEFT_Y)
call SetUnitFlyHeight(tempDat.arrow,DUMMY_HIDDEN_HEIGHT,0.)
call SetUnitScale(tempDat.arrow,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
// When to move the indicator
else
set theta=Atan2(dy,dx)
call SetUnitX(tempDat.arrow,GetUnitX(tempDat.ori)+ARROW_HOVER_DISTANCE*Cos(theta))
call SetUnitY(tempDat.arrow,GetUnitY(tempDat.ori)+ARROW_HOVER_DISTANCE*Sin(theta))
if GetLocalPlayer()==tempDat.owner then
call SetUnitFlyHeight(tempDat.arrow,ARROW_FLY_HEIGHT,0.)
call SetUnitScale(tempDat.arrow,ARROW_SCALE,ARROW_SCALE,ARROW_SCALE)
endif
if dist2<ARROW_GREEN_RANGE*ARROW_GREEN_RANGE then
call SetUnitColor(tempDat.arrow,PLAYER_COLOR_GREEN)
elseif dist2<ARROW_YELLOW_RANGE*ARROW_YELLOW_RANGE then
call SetUnitColor(tempDat.arrow,PLAYER_COLOR_YELLOW)
else
call SetUnitColor(tempDat.arrow,PLAYER_COLOR_RED)
endif
endif
//Last check if Orianna exists so we can properly destroy her otherwise
if GetUnitTypeId(tempDat.ori)==0 then
call DestroyGroup(tempDat.alreadyHit)
call RemoveUnit(tempDat.ball)
call RemoveUnit(tempDat.arrow)
call RemoveUnit(tempDat.shockwaveIndicator)
call RemoveUnit(tempDat.shockwaveOrb1)
call RemoveUnit(tempDat.shockwaveOrb2)
call RemoveUnit(tempDat.shockwaveOrb3)
call RemoveUnit(tempDat.standIndicator)
call RemoveUnit(tempDat.disonnanceIndicator)
if tempDat.protectBonusUnit!=null then
call UnitRemoveAbility(tempDat.protectBonusUnit,PROTECT_PASSIVE_ID)
set tempDat.protectBonusUnit=null
endif
call tempDat.destroy()
set DB[index]=DB[dbIndex]
set dbIndex=dbIndex-1
set index=index-1
endif
set index=index+1
endloop
set snd=null
set threadCheck = true
endmethod
// Initialization function for an Orianna instance
private static method initialize takes unit u returns nothing
local thistype tempDat=thistype.create()
set tempDat.ori=u
set tempDat.owner=GetOwningPlayer(u)
set tempDat.ball=CreateUnit(tempDat.owner,BALL_ID,0.,0.,270.)
set tempDat.arrow=CreateUnit(tempDat.owner,ARROW_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
set tempDat.standIndicator=CreateUnit(tempDat.owner,STAND_INDICATOR_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
set tempDat.disonnanceIndicator=CreateUnit(tempDat.owner,DISONNANCE_INDICATOR_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
set tempDat.shockwaveIndicator=CreateUnit(tempDat.owner,SHOCKWAVE_INDICATOR_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
set tempDat.shockwaveOrb1=CreateUnit(tempDat.owner,SHOCKWAVE_ORB_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
set tempDat.shockwaveOrb2=CreateUnit(tempDat.owner,SHOCKWAVE_ORB_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
set tempDat.shockwaveOrb3=CreateUnit(tempDat.owner,SHOCKWAVE_ORB_ID,MAP_TOP_LEFT_X,MAP_TOP_LEFT_Y,270.)
call SetUnitScale(tempDat.shockwaveIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call SetUnitScale(tempDat.shockwaveOrb1,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call SetUnitScale(tempDat.shockwaveOrb2,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call SetUnitScale(tempDat.shockwaveOrb3,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call SetUnitScale(tempDat.disonnanceIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call SetUnitVertexColor(tempDat.disonnanceIndicator,DISONNANCE_INDICATOR_RED,DISONNANCE_INDICATOR_GREEN,DISONNANCE_INDICATOR_BLUE,DISONNANCE_INDICATOR_OPACITY)
call SetUnitScale(tempDat.standIndicator,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
call SetUnitColor(tempDat.arrow,PLAYER_COLOR_GREEN)
call SetUnitScale(tempDat.arrow,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE,DUMMY_HIDDEN_SCALE)
set thistype.fromOri[u]=tempDat
set tempDat.target=null
set tempDat.alreadyHit=CreateGroup()
call UnitAddAbility(tempDat.ball,'Arav')
call UnitRemoveAbility(tempDat.ball,'Arav')
call UnitAddAbility(tempDat.disonnanceIndicator,'Arav')
call UnitRemoveAbility(tempDat.disonnanceIndicator,'Arav')
call UnitAddAbility(tempDat.shockwaveIndicator,'Arav')
call UnitRemoveAbility(tempDat.shockwaveIndicator,'Arav')
call SetUnitFlyHeight(tempDat.disonnanceIndicator,DISONNANCE_INDICATOR_HEIGHT,0.)
call SetUnitX(tempDat.ball,GetUnitX(u))
call SetUnitY(tempDat.ball,GetUnitY(u))
call SetUnitTimeScale(tempDat.ball,BALL_TIME_SCALE)
call SetUnitFlyHeight(tempDat.ball,BALL_OVERHEAD_HEIGHT,0.)
call SetUnitVertexColor(tempDat.ball,255,255,255,BALL_OPACITY)
set dbIndex=dbIndex+1
set DB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(clock,CLOCK_PERIOD,true,function thistype.periodic)
endif
set tempDat.state=BALL_STATE_HOME
endmethod
// Auxillary function used for getting new Orianna
// instances
private static method c takes nothing returns boolean
local unit tU=GetTriggerUnit()
if GetUnitTypeId(tU)==ID then
call thistype.initialize(tU)
endif
set tU=null
return false
endmethod
// Initialization method called by the InitShim
private static method i takes nothing returns nothing
local unit FoG
local region reg=CreateRegion()
local trigger t=CreateTrigger()
set thistype.fromOri=HandleTable.create()
set thistype.preloadTable=HandleTable.create()
call thistype.preloadSound(BALL_MOVE_START_SOUND)
call thistype.preloadSound(BALL_MOVE_END_SOUND)
call RegionAddRect(reg,bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(t,reg,null)
call TriggerAddCondition(t,Condition(function thistype.c))
set reg=null
set t=null
call GroupEnumUnitsInRect(grp,bj_mapInitialPlayableArea,null)
loop
set FoG=FirstOfGroup(grp)
exitwhen FoG==null
if GetUnitTypeId(FoG)==thistype.ID then
call thistype.initialize(FoG)
endif
call GroupRemoveUnit(grp,FoG)
endloop
endmethod
implement InitShim
endstruct
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
// System for handling Orianna's Innate. Note: Contains
// one (1) customizable section
scope clockworkWindup initializer i
globals
//*********************************************************
// Customizable Section
//*********************************************************
// How many levels to wait before incrementing the damage
// bonus
private constant integer LEVEL_SKIP_REQUIREMENT=3
// Flat magic damage added to basic attacks
private constant real FLAT_BONUS=10.
// Incremental magic damage added to basic attacks
private constant real INCR_BONUS=8.
// Intelligence based magic damage added to basic attacks
private constant real INT_BONUS=.15
// Multiplier on second hit for bonus magic damage
private constant real HIT_TWO_BONUS=.2
// Bonus magic damage on third and consecutive hits
private constant real HIT_THREE_BONUS=.4
// Model displayed on attack
private constant string HIT_ANIMATION="Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
// Model displayed on subsequent attacks
private constant string HIT_ANIMATION_2="Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodSorceress.mdl"
//*********************************************************
// End Customizable Section
//*********************************************************
endglobals
// The handler
private function handler takes nothing returns nothing
local integer count
local real damage
local real damage2=0.
local unit dS=GetEventDamageSource()
local unit tU=GetTriggerUnit()
if DamageType.get()==DamageType.ATTACK and GetUnitTypeId(dS)==Orianna.ID then
set damage=FLAT_BONUS + INCR_BONUS*R2I((GetHeroLevel(dS)-1)/LEVEL_SKIP_REQUIREMENT) + INT_BONUS*GetHeroInt(dS,true)
set count=Orianna.hitCount(dS,tU,Orianna.METHOD_INCREMENT_AND_COUNT)
if count==2 then
set damage2=(GetEventDamage()+damage)*HIT_TWO_BONUS
elseif count>2 then
set damage2=(GetEventDamage()+damage)*HIT_THREE_BONUS
endif
call DamageType.dealCodeDamage(dS,tU,damage+damage2)
call DestroyEffect(AddSpecialEffectTarget(HIT_ANIMATION,tU,"chest"))
if count>1 then
call DestroyEffect(AddSpecialEffectTarget(HIT_ANIMATION_2,tU,"chest"))
endif
endif
set tU=null
set dS=null
endfunction
// Initialization function which adds a handler to
// recognize Orianna's basic attacks
private function i takes nothing returns nothing
call StructuredDD.addHandler(function handler)
endfunction
endscope
// Interface for Command: Attack. Essentially just an adapter
// for the primary Orianna interface
scope commandAttack initializer i
private struct CommandAttack
unit u
real x
real y
endstruct
globals
private HandleTable tab
private constant real DELAY = 0.
endglobals
private function after takes nothing returns nothing
local timer c = GetExpiredTimer()
local CommandAttack ca = tab[c]
call DestroyTimer(c)
call Orianna.attack(ca.u,ca.x,ca.y)
call ca.destroy()
set c = null
endfunction
// Registered function on cast
private function c takes nothing returns boolean
local CommandAttack ca
local timer clock
if GetSpellAbilityId()==Orianna.COMMAND_ATTACK_ID then
set ca = CommandAttack.create()
set ca.u = GetTriggerUnit()
set ca.x = GetSpellTargetX()
set ca.y = GetSpellTargetY()
set clock = CreateTimer()
set tab[clock] = ca
call TimerStart(clock,DELAY,false,function after)
set clock = null
endif
return false
endfunction
// Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
set tab = HandleTable.create()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Interface for Command: Disonnance; much like the
// commandAttack one
scope commandDissonance initializer i
//Registered Function on cast
private function c takes nothing returns boolean
if GetSpellAbilityId()==Orianna.COMMAND_DISONNANCE_ID then
call Orianna.disonnance(GetTriggerUnit())
endif
return false
endfunction
// Initialization Function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
// Interface for Command: Attack. Essentially just an adapter
// for the primary Orianna interface
scope commandProtect initializer i
private struct CommandProtect
unit u
unit t
endstruct
globals
private HandleTable tab
private constant real DELAY = 0.
endglobals
private function after takes nothing returns nothing
local timer c = GetExpiredTimer()
local CommandProtect ct = tab[c]
call DestroyTimer(c)
call Orianna.protect(ct.u,ct.t)
call ct.destroy()
set c = null
endfunction
// Registered function on cast
private function c takes nothing returns boolean
local CommandProtect ct
local timer clock
if GetSpellAbilityId()==Orianna.COMMAND_PROTECT_ID then
set ct = CommandProtect.create()
set ct.u = GetTriggerUnit()
set ct.t = GetSpellTargetUnit()
set clock = CreateTimer()
set tab[clock] = ct
call TimerStart(clock,DELAY,false,function after)
set clock = null
endif
return false
endfunction
// Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
set tab = HandleTable.create()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
// Interface for Command: Shockwave
scope commandShockwave initializer i
// Function registered on cast
private function c takes nothing returns boolean
if GetSpellAbilityId()==Orianna.COMMAND_SHOCKWAVE_ID then
call Orianna.shockwave(GetTriggerUnit())
endif
return false
endfunction
// Initialization function
private function i takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function c))
set t=null
endfunction
endscope