//=============================//
// F O R E S T B O R N //
// by Tommerbob v2.1b //
//=============================//
//
//Required: Jass NewGen, DestructableLib, TimerUtils (All included)
//
//Implementation:
// 1. Copy the "Spell" Category from the demo map. It includes everything you need.
// 2. Copy the custom buff in the Object Editor.
// 3. Copy the 4 custom abilities in the Object Editor.
// 4. In the Configeration Section below, make sure all 4 ability ID's and the buff ID match up with the rawcodes
// in the Object Editor. You can see the rawcodes by pressing CTRL+D.
// 5. Edit the Configeration Section below to your liking and enjoy!
//
//Note: If you want to use the leaves SFX, you need to import it into your map.
//
//Credits: Vexorian for TimerUtils, PitzerMike for DestructableLib, Leaves.mdx from DotA
//Thanks to many others for helping me with the code!
//
scope Forestborn initializer init
//
//CONFIGERATION SECTION
//
globals
private constant integer SPELL_ID = 'A000' //rawcode of the Forestborn spell
private constant integer BOOK_ID = 'A001' //rawcode of the Evasion spell book
private constant integer EVADE_ID = 'A002' //rawcode of the evasion ability
private constant integer MOVE_ID = 'A003' //rawcode of the move speed bonus ability
private constant integer BUFF_ID = 'B000' //rawcode of the Forestborn buff
private constant real INTERVAL = 0.5 //How often it checks for trees around caster.
private constant real AOE = 200. //Max distance from a tree the caster may be.
private constant real DURATION_BASE = 10. //Base duration of spell.
private constant real DURATION_LVL = 5. //Added duration for each level of spell.
private constant integer EVADE_BASE = 50 //Base chance for evasion. Set to 0 if you don't want the caster gaining evasion.
private constant integer EVADE_LVL = 0 //Amount added to base evasion for each level of spell.
private constant real HEAL_BASE = 8. //Base amount that the caster is healed for each second.
private constant real HEAL_LVL = 4. //Adds this amount to the base heal amount for each level of spell.
//If you don't want the caster healed, set both HEAL_BASE and HEAL_LVL to 0.
private constant integer MOVE_BASE = 25 //This is the move speed percent bonus the caster receives. Set to 0 if no bonus.
private constant integer MOVE_LVL = 0 //Amount added to base speed bonus for each level of spell.
private constant boolean CAN_MOVE = true //Setting this to false will require the caster to remain stationary.
private constant string SFX1 = "Leaves.mdx" //Bling bling
private constant string SFX2 = "Abilities\\Spells\\NightElf\\TargetArtLumber\\TargetArtLumber.mdl" //More bling bling!
private constant string SFX1_ATTACH = "chest" //Where the first SFX is attached to the caster
private constant string SFX2_ATTACH = "origin" //Where the second SFX is attached to the caster
private constant integer TRANSPARENCY = 30 //Caster becomes this transparent (even more bling bling!)
endglobals
//
//END CONFIGERATION SECTION. Do not edit past this point unless you know what you are doing.
//
private function Duration takes integer level returns real
return DURATION_BASE + DURATION_LVL * (level)
endfunction
private function Evasion takes integer level returns integer
return 1 + EVADE_BASE + EVADE_LVL * (level)
endfunction
private function Heal takes integer level returns real
return (HEAL_BASE + HEAL_LVL * (level)) * INTERVAL
endfunction
private function MoveBonus takes integer level returns integer
return 1 + MOVE_BASE + MOVE_LVL * (level)
endfunction
private struct Data
unit caster
integer level
integer evasion
integer movebonus
boolean tree
boolean hassfx
effect sfx1
effect sfx2
real heal
real move
real time
real duration
real oldx
real oldy
real newx
real newy
timer t
static method create takes unit u returns thistype
local thistype this = thistype.allocate()
set this.caster = u
set this.level = GetUnitAbilityLevel(this.caster, SPELL_ID)
set this.time = 0.
set this.duration = Duration(this.level)
set this.evasion = Evasion(this.level)
set this.heal = Heal(this.level)
set this.move = GetUnitMoveSpeed(this.caster)
set this.movebonus = MoveBonus(this.level)
set this.oldx = GetUnitX(this.caster)
set this.oldy = GetUnitY(this.caster)
set this.tree = false
set this.hassfx = false
set this.t = NewTimer()
return this
endmethod
method destroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
globals
private Data D
endglobals
private function TreeCheck takes nothing returns nothing
local destructable dest = GetEnumDestructable()
if IsDestructableTree(dest) and not IsDestructableDead(dest) then
set D.tree = true
endif
set dest = null
endfunction
private function NearTree takes nothing returns nothing
local Data data = GetTimerData(GetExpiredTimer())
local location l = GetUnitLoc(data.caster)
set D = data
set D.tree = false
call EnumDestructablesInCircleBJ(AOE, l, function TreeCheck) //Picks every destructable in range
if D.tree then
if CAN_MOVE then //If the caster should not move, updates his location.
set D.newx = GetUnitX(D.caster)
set D.newy = GetUnitY(D.caster)
if D.newx != D.oldx or D.newy != D.oldy then
set D.oldx = GetUnitX(D.caster)
set D.oldy = GetUnitY(D.caster)
call UnitRemoveAbility(D.caster, BOOK_ID) //Remove bonuses if not stationary
call UnitRemoveAbility(D.caster, BUFF_ID) //Remove buff
if D.hassfx then
call SetUnitVertexColor(D.caster, 255, 255, 255, 255) //Reset caster transparency
call DestroyEffect(D.sfx1) //Remove SFX
call DestroyEffect(D.sfx2)
set D.hassfx = false
endif
else
if not D.hassfx then
call SetUnitVertexColor(D.caster, 255, 255, 255, (100 - TRANSPARENCY)) //Set caster transparency
set D.sfx1 = AddSpecialEffectTarget(SFX1, D.caster, SFX1_ATTACH) //Add SFX
set D.sfx2 = AddSpecialEffectTarget(SFX2, D.caster, SFX2_ATTACH)
set D.hassfx = true
endif
call UnitAddAbility(D.caster, BOOK_ID)
call SetUnitAbilityLevel(D.caster, EVADE_ID, D.evasion) //Sets evasion if near a tree and is stationary.
if HEAL_BASE > 0. then //Heals caster if he is near a tree and is stationary.
call SetWidgetLife(D.caster, GetWidgetLife(D.caster) + D.heal)
endif
endif
else
if not D.hassfx then
call SetUnitVertexColor(D.caster, 255, 255, 255, (100 - TRANSPARENCY)) //Set caster transparency
set D.sfx1 = AddSpecialEffectTarget(SFX1, D.caster, SFX1_ATTACH) //Add SFX
set D.sfx2 = AddSpecialEffectTarget(SFX2, D.caster, SFX2_ATTACH)
set D.hassfx = true
endif
call UnitAddAbility(D.caster, BOOK_ID)
call SetUnitAbilityLevel(D.caster, MOVE_ID, D.movebonus) //Sets the move bonus if near a tree
call SetUnitAbilityLevel(D.caster, EVADE_ID, D.evasion) //Sets evasion if near a tree
if HEAL_BASE > 0. then //Heals caster if near a tree
call SetWidgetLife(D.caster, GetWidgetLife(D.caster) + D.heal)
endif
endif
else
if D.hassfx then
call SetUnitVertexColor(D.caster, 255, 255, 255, 255) //Reset unit transparency
call DestroyEffect(D.sfx1) //Remove SFX
call DestroyEffect(D.sfx2)
set D.hassfx = false
endif
call UnitRemoveAbility(D.caster, BOOK_ID) //Remove bonuses if not near a tree
call UnitRemoveAbility(D.caster, BUFF_ID) //Remove buff
endif
set D.time = D.time + INTERVAL
if D.time >= D.duration then
set D.tree = false
set D.hassfx = false
call DestroyEffect(D.sfx1) //Remove SFX
call DestroyEffect(D.sfx2)
call SetUnitVertexColor(D.caster, 255, 255, 255, 255) //Reset caster transparency
call UnitRemoveAbility(D.caster, BOOK_ID) //Removes evasion at end of duration
call UnitRemoveAbility(D.caster, BUFF_ID) //Remove buff
call D.destroy()
endif
call RemoveLocation(l)
set l = null
endfunction
private function Actions takes nothing returns boolean
local Data data
if GetSpellAbilityId() != SPELL_ID then
return false
endif
set data = Data.create(GetTriggerUnit())
call SetPlayerAbilityAvailable(GetOwningPlayer(GetTriggerUnit()), BOOK_ID, false) //Remove icons from hero UI
call SetTimerData(data.t, data)
call TimerStart(data.t, INTERVAL, true, function NearTree)
return false
endfunction
//======================================================
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local unit u
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(t, Condition(function Actions))
set u = CreateUnit(Player(15), 'Hpal', 0., 0., 0.) //Preload ability
call UnitAddAbility(u, BOOK_ID)
call SetUnitAbilityLevel(u, MOVE_ID, MOVE_BASE)
call SetUnitAbilityLevel(u, EVADE_ID, EVADE_BASE+1)
call RemoveUnit(u)
set u = null
set t = null
endfunction
endscope