//**********************************************************************************************
//* Fire Disc v1.2d By xD.Schurke - 17.04.2009
//* --------------------------------------------
//*
//* Requires:
//* * xDModules (are needed for struct stacking issues)
//* * JNGP (JassHelper v0.9.G.1)
//*
//* This is my first spell created with my struct stacking module. I wanted to test this new
//* feature and for that I created this spell.
//*
//* Fire Disc is a spell which damages all enemies it hits. Also it can cut through trees.
//*
//*
//* How to implement this spell?
//* * Copy this trigger and the xDModules library to your map
//* * Copy the spell to your map (object editor) but don't forget to adjust the Raw-Code
//* * Adjust the rawcodes to your map specific ones
//* * Now you are done
//*
//* Credits:
//* *PitzerMike for TreeFilter function
//* *Vexorian for vJass
//*
//*
//*
//* Changelog:
//* v1.2
//* * Fixed leak for TriggerRegisterPlayerUnitEvent
//* * Added GroupRecycler
//*
//* v1.2b
//* * Added more documentation
//*
//* v1.2c
//* * Forgot to destroy DummyFilter boolexpr in the init function :(
//*
//* v1.2d
//* * Decreased damage delt by the part missiles
//* * Increased range of part missile damage
//*
//**********************************************************************************************
library FireDisc initializer init requires xDModules,GroupRecycler
//Setup change the Variables you want to change, but better don't change the spell script
globals
private constant integer dummyID = 'n000'
private constant integer spellID = 'A000'
private constant integer flyID = 'Amrf'
private constant integer partMissiles = 8 //Number of disc parts (I choosed 8, because it looks realy good that way)
private constant integer alphaDec = 51 //255/5 = 51 the number for slowly hiding the missiles, should not look to instant (the end of spell)
private constant real interval = 0.03125
private constant real alphaDecStart = 900. //The distance when the hiding process should start
private constant real speed = 20. //The speed of the main missile => always has to be a multipler of maxDis and effectDis and also alphaDecStart
private constant real speedPart = 10. //The speed of the part missile (needed for angle calculation, creates a rotation)
private constant real maxDeg = 360.
private constant real maxDis = 1100. //The distance the disc can fly
private constant real effectDis = 860. //At this distance the effect for the last explosion spawns
private constant real partMisDist = 80. //The distance between the main missile and the part missiles
private constant real treeKillRange = 60. //Radius where Trees get killed
private constant real pDamage = 25. //Damage of the part missiles
private constant real mDamage = 65. //Damage of the main missile
private constant real mDamageRange = 250. //Radius of main missile Damage
private constant real pDamageRange = 50. //Radius of part missile Damage
//The following strings are the effect strings
private constant string mainEffect = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private constant string partEffect = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
private constant string partEffect2 = "Abilities\\Spells\\Other\\ImmolationRed\\ImmolationRedDamage.mdl"
private constant string explodeEffect = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl"
//Following Variables should not be changed
private group tmpG = CreateGroup()
private player tmpP
private boolexpr Cond = null
private boolexpr condTree = null
endglobals
//Setup end
//constantFunctionPart
private function partDamage takes integer lvl returns real
return lvl*pDamage
endfunction
private function mainDamage takes integer lvl returns real
return lvl*mDamage
endfunction
//endconstantFunctionPart
//Do not change anything below!
private function DummyFilter takes nothing returns boolean
return true
endfunction
private function GroupFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),tmpP) and (GetUnitState(GetFilterUnit(),UNIT_STATE_LIFE)>0) and IsUnitType(GetFilterUnit(),UNIT_TYPE_GROUND) and (GetUnitTypeId(GetFilterUnit())!=dummyID)
endfunction
private function TreeFilter takes nothing returns boolean
local destructable d = GetFilterDestructable()
local boolean i = IsDestructableInvulnerable(d)
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), dummyID, GetWidgetX(d), GetWidgetY(d), 0)
local boolean result = false
call UnitAddAbility(u, 'Ahrl')
if i then
call SetDestructableInvulnerable(d, false)
endif
set result = IssueTargetOrder(u, "harvest", d)
call RemoveUnit(u)
if i then
call SetDestructableInvulnerable(d, true)
endif
set u = null
set d = null
return result
endfunction
private function KillTree takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
//Following struct is used for the single parts of the disc, so each part of the disc works for its own
private struct discPart
integer alpha = 255 //Alpha-Channel
unit caster
unit missile
unit tarMissile //The mainMissile
real angle = 0. //Angle, needed for the speed of rotation
real x = 0. //x-point of the end
real y = 0. //y-point of the end
real dis = 0. //how far the missile got
group g //Is the unit already hit?
method actions takes nothing returns nothing
local real x = 0.
local real y = 0.
local rect r
local unit u
local integer i = 0
//Moving Part of the missile
set .angle = .angle + speedPart
set x = GetUnitX(.tarMissile) + partMisDist * Cos(.angle * bj_DEGTORAD)
set y = GetUnitY(.tarMissile) + partMisDist * Sin(.angle * bj_DEGTORAD)
call SetUnitFacing(.missile,.angle)
call SetUnitPosition(.missile,x,y)
//endmovingpart
//TreeFilter part
set r = Rect(x-treeKillRange,y-treeKillRange,x+treeKillRange,y+treeKillRange)
call EnumDestructablesInRect(r,condTree,function KillTree)
//end TreeFilter part
//Unit damaging part
set tmpP = GetOwningPlayer(.caster)
call GroupEnumUnitsInRange(tmpG,x,y,pDamageRange,Cond)
loop
set u = FirstOfGroup(tmpG)
exitwhen u == null
if IsUnitInGroup(u,.g) != true then
call UnitDamageTarget(.caster,u,partDamage(GetUnitAbilityLevel(.caster,spellID)),false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
call GroupAddUnit(.g,u)
endif
call GroupRemoveUnit(tmpG,u)
endloop
//end unit damaging part
//hiding part => alpha-channel manipulation
set .dis = .dis + speed
if .dis >= alphaDecStart then
set .alpha = .alpha - alphaDec
call SetUnitVertexColor(.missile,255,255,255,.alpha)
endif
//end hiding part
//destroying part of the struct + clear leaks
if .dis >= maxDis then
call RemoveUnit(.missile)
call ReleaseGroup(.g)
set .missile = null
set .g = null
set .caster = null
set .active = false
endif
call RemoveRect(r)
set u = null
set r = null
endmethod
implement structStack
endstruct
//Following struct is used for the mainPart of the disc (this missile in the middle, where all partMissiles will fly aroung
private struct discMain
integer alpha = 255 //Alpha-Channel
unit caster
unit missile
real x = 0. //X-Point of the end
real y = 0. //Y-Point of the end
real angle = 0. //Angle between caster and SpellTargetPoint
real dis = 0. //how far the missile got
method actions takes nothing returns nothing
//moving part of the missile
local real x = GetUnitX(.missile) + speed * Cos(.angle * bj_DEGTORAD)
local real y = GetUnitY(.missile) + speed * Sin(.angle * bj_DEGTORAD)
local unit u
call SetUnitPosition(.missile,x,y)
set .dis = .dis + speed
//endmovingpart
//part for the last effect => AOE damage and hiding part by manipulating the alpha-channels
if .dis == effectDis then
call DestroyEffect(AddSpecialEffect(explodeEffect,.x,.y))
elseif .dis >= alphaDecStart then
set .alpha = .alpha - alphaDec
call SetUnitVertexColor(.missile,255,255,255,.alpha)
endif
//end effect/hiding part
//destroying part of the struct + clear leaks
if .dis >= maxDis then
set tmpP = GetOwningPlayer(.caster)
call GroupEnumUnitsInRange(tmpG,x,y,mDamageRange,Cond)
loop
set u = FirstOfGroup(tmpG)
exitwhen u == null
call UnitDamageTarget(.caster,u,mainDamage(GetUnitAbilityLevel(.caster,spellID)),false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
call GroupRemoveUnit(tmpG,u)
endloop
call RemoveUnit(.missile)
set .missile = null
set .caster = null
set .active = false
endif
set u = null
endmethod
implement structStack
endstruct
private function cond takes nothing returns boolean
return GetSpellAbilityId() == spellID
endfunction
private function action takes nothing returns nothing
local discMain dM = discMain.create(interval)
local discPart dP
local location tmpLoc = GetSpellTargetLoc()
local integer i = 0
local real x = 0
local real y = 0
//initialize mainMissile
set dM.caster = GetTriggerUnit()
set dM.missile = CreateUnit(GetOwningPlayer(dM.caster),dummyID,GetUnitX(dM.caster),GetUnitY(dM.caster),0)
call UnitAddAbility(dM.missile,flyID)
call UnitRemoveAbility(dM.missile,flyID)
call SetUnitFlyHeight(dM.missile,100,0)
call AddSpecialEffectTarget(mainEffect,dM.missile,"origin")
set dM.angle = bj_RADTODEG * Atan2(GetLocationY(tmpLoc) - GetUnitY(dM.caster), GetLocationX(tmpLoc) - GetUnitX(dM.caster))
set dM.x = GetUnitX(dM.caster) + maxDis * Cos(dM.angle * bj_DEGTORAD)
set dM.y = GetUnitY(dM.caster) + maxDis * Sin(dM.angle * bj_DEGTORAD)
//mainMissile initialization finished
//initialize partMissile
loop
exitwhen i >= partMissiles
set dP = discPart.create(interval)
set dP.caster = dM.caster
set dP.tarMissile = dM.missile
set dP.angle = (maxDeg/partMissiles)*i
set x = GetUnitX(dP.tarMissile) + partMisDist * Cos(dP.angle * bj_DEGTORAD)
set y = GetUnitY(dP.tarMissile) + partMisDist * Sin(dP.angle * bj_DEGTORAD)
set dP.missile = CreateUnit(GetOwningPlayer(dP.caster),dummyID,x,y,0)
call UnitAddAbility(dP.missile,flyID)
call UnitRemoveAbility(dP.missile,flyID)
call SetUnitFlyHeight(dP.missile,100,0)
call AddSpecialEffectTarget(partEffect,dP.missile,"origin")
set dP.x = dM.x
set dP.y = dM.y
set dP.g = NewGroup()
set i = i + 1
endloop
//partMissile initialization finished
call RemoveLocation(tmpLoc)
set tmpLoc = null
endfunction
private function init takes nothing returns nothing
local trigger int = CreateTrigger()
local integer i = 0
local boolexpr boolTemp = Filter(function DummyFilter)
loop
call TriggerRegisterPlayerUnitEvent(int,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,boolTemp)
set i = i + 1
exitwhen i >= bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(int,Condition(function cond))
call TriggerAddAction(int,function action)
set int = null
//Setup the filters
set condTree = Filter(function TreeFilter)
set Cond = Filter(function GroupFilter)
//Preload
call Preload(mainEffect)
call Preload(partEffect)
call Preload(partEffect2)
call Preload(explodeEffect)
call DestroyBoolExpr(boolTemp)
set boolTemp = null
endfunction
endlibrary