- Joined
- Jul 12, 2009
- Messages
- 321
Nice catch. I guess the Tooltip - Normal stole an "e" from Tooltip - Learn. 

It should be, but if you ask me not MUI spells should be limited in judging, in other words, they can't gain max score in judging triggering!its not stated in the rules, so im guessing it doesnt have to be MUI?
/*=====================================================================*\
//====== SUICIDE SQUAD =====*\\
\\====== Version 1.0 ======//
\\====== by Kricz ======//
|| ||
|| ||
|| Requires: ||
\\ Table [Vexorian] //
\\ ListModule [grim001] //
\\ xebasic, xedamage & xefx [Vexorian] //
\\ ARGB [Vexorian] //
|| SoundUtils [Rising_Dusk] ||
|| GroupUtils [Rising_Dusk] ||
|| Z-Utils [Kricz] ||
// \\
// Optional: \\
|| AutoFly [Kricz] (requires AutoIndex) ||
|| AbilityPreload [grim001] ||
|| ||
|| Please give credits if you use this spell in your map. ||
|| ||
\*==============================================================?=*/
scope SuicideSquad initializer onInit
/*Main Setup*/
globals
//The Spell-Id of the SuicideSquad Ability
private constant integer SPELL_ID = 'A000'
//The Timer-Interval of timers for movements and checks
//Default Value: 0.03125
private constant real TIMER_INTERVAL = 0.03125
//If true, the system will use sounds using Rising_Dusk's SoundUtils Library.
//You can manually disable specific sounds, by setting their path to "".
private constant boolean USE_SOUNDS = true
endglobals
/*Sapper Setup*/
globals
//The Unit-Id of the Sapper
private constant integer SAPPER_UNIT_ID = 'sapp'
//The speed of the down-falling sappers
//Default Value: 225.00
private constant real SAPPER_DROP_SPEED = 225.00
//The radius, in where sappers search valid targets
//Default Value: 550.00
private constant real SAPPER_TARGET_SEARCH_AREA = 550.00
//If this is true, the sapper will walk to the position of the target unit once it dies
//and search for new targets while walking. If false, the sapper will stop walking and
//then search for new targets
//Default Value: false
private constant boolean SAPPER_STOP_WHEN_TARGET_DIES = false
//This is the distance a sapper has to be with its target to make suicide.
//Default Value: 100.00
private constant real SAPPER_SUICIDE_RANGE = 100.00
//This is the animation-id which is played indstead of the normal death-animation.
//Information: 3 is the "spell death" animation, which shows, that the sapper does suicide
//4 is the normal death animation. Ofcourse you could use any other number, if you are funny :D
//Default Value: 3
private constant integer SAPPER_SUICIDE_ANIMATION_ID = 3
//This is the effect that is created when a sapper is dropped by a zeppeling and falling down
//Use "" for no effect.
//Default Value: "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
private constant string SAPPER_DROP_EFFECT = "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
//This is the attachment point of the drop effect above.
//Default Value: "chest"
private constant string SAPPER_DROP_EFFECT_ATTACHPOINT = "chest"
//This is the effect that is created once a sapper reaches the ground and kindles himself
//Default Value: "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
private constant string SAPPER_BURN_EFFECT = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
//This is the attachment point of the burn effect above
//Default Value: "chest"
private constant string SAPPER_BURN_EFFECT_ATTACHPOINT = "chest"
//This is the effect that is displayed, when a sapper found a target
//Set it to "" if you want no effect.
//Default Value: "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
private constant string SAPPER_TARGET_FOUND_EFFECT = "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
//This is the attachment point of the target-found effect above
//Default Value: "overhead"
private constant string SAPPER_TARGET_FOUND_EFFECT_ATTACHPOINT = "overhead"
//The effect created by the explosion. Set it to "" for no effect
//Default Value: "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private constant string SAPPER_EXPLOSION_EFFECT = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
//This boolean decides, whether the movespeed of a sapper should be changed using
//the native SetUnitMoveSpeed or not. In some maps, changing the movespeed via trigger
//may cause some bugs with other systems/spells.
//If you dont want, that the movespeed is changed using SetUnitMoveSpeed, change it in the OE.
//Default Value: true
private constant boolean SAPPER_ENABLE_MOVESPEED_CHANGE = true
//This is the movespeed of sappers. It's only used, if the value above is set to true
//Default Value: 225.00
private constant real SAPPER_MOVEMENT_SPEED = 225.00
//If this boolean is set a true, a texttag is created above each sapper, showing when it will
//explode. Please note, that you can only have max. 100 Texttags active at the same time, so
//disable it, when you think you could reach more than 100.
//Default Value: true
private constant boolean SAPPER_SHOW_EXPLOSION_INDICATOR = true
//This is the color of the texttag. Read the ARGB Readme for mroe information about how to use it.
//Default Value: 0xFFFF0000 (red)
private ARGB SAPPER_EXPLOSION_TIME_INDICATOR_COLOR = 0xFFFF0000
//This is the size of the texttag.
//Default Value: 10.00
private constant real SAPPER_EXPLOSION_TIME_INDICATOR_SIZE = 10.00
//This is the height of the texttag.
//Default Value: 75.00
private constant real SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT = 75.00
//This is the attack type which xedamage uses once a sapper does suicide or explodes
//Default Value: ATTACK_TYPE_MAGIC
private constant attacktype SAPPER_EXPLOSION_ATTACK_TYPE = ATTACK_TYPE_MAGIC
//This is the damage type which xedamage uses once a sapper does suicide or explodes
//Default Value: DAMAGE_TYPE_FIRE
private constant damagetype SAPPER_EXPLOSION_DAMAGE_TYPE = DAMAGE_TYPE_FIRE
//This is the radius of the explosion.
//Default Value: 250.00
private constant real SAPPER_EXPLOSION_RADIUS = 250.00
//If this boolean is set to true, the explosion will also damage allied unit.
//Default Value: false
private constant boolean SAPPER_EXPLOSION_DAMAGE_ALLIED = false
//If this boolean is true, sappers will play a random sound,
//which you can add/remove/change in the SAPPER_INIT_SOUNDS textmacro below
//Default Value: true
private constant boolean SAPPER_ENABLE_FUNNY_SOUNDS = true
//This value is only important, if funny sounds of sappers are enabled.
//Because it may sounds stupid, when 5 sappers speak at the same time,
//there is this possibility to give each sapper only a chance to tell something
//which you can setup with this value.
//Default Value: 0.33 (33%)
private constant real SAPPER_FUNNY_SOUND_CHANCE = 0.30
endglobals
//in this macro, you can add funny sounds for sappers.
//the function you have to call is:
//call addSapperSound(string soundHandle, real duration)
//soundHandle is the path of the sound, you can get them i.e from the SoundEditor (F5)
//duration is the duration of the sound, you can also get this information in the SoundEditor
//Example: call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes1.wav", 1.463)
//! textmacro SAPPER_INIT_SOUNDS
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes1.wav", 1.463)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes5.wav", 1.434)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperWhat2.wav", 1.694)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYesAttack3.wav", 2.026)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYesAttack4.wav", 1.091)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed1.wav", 1.515)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed2.wav", 2.776)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed4.wav", 2.554)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed5.wav", 0.592)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed6.wav", 2.201)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed7.wav", 2.374)
//! endtextmacro
//This is the amount of sappers dropped each level.
//Default Value: 3 + lvl
private constant function SAPPER_AMOUNT takes integer lvl returns integer
return 3 + lvl
endfunction
//This is the time, a sapper has until it will explode
//Default Value: 6.00 + lvl
private constant function SAPPER_EXPLOSION_TIMER takes integer lvl returns real
return 6.00 + lvl
endfunction
//This is the damage a explosion deals.
//Default Value: 25.00 + 25.00 * lvl
private constant function SAPPER_EXPLOSION_DAMAGE takes integer lvl returns real
return 15.00 * lvl
endfunction
//This is the filter used to get valid targets for sappers.
//Default Value: not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(sapper)) and IsUnitEnemy(u, GetOwningPlayer(sapper))
private constant function SAPPER_TARGET_SEARCH_FILTER takes unit u, unit sapper returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(sapper)) and IsUnitEnemy(u, GetOwningPlayer(sapper))
endfunction
/*Zeppelin Setup*/
globals
//The model of the zeppelin that drops the sappers
//Default Value: "units\\creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
private constant string ZEPPELIN_MODEL = "units\\creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
//The height of the zeppelin. Please note, that the higher this value is, the longer a sapper requires
//to reach the ground. If you change this value, you may should change the drop speed of the sappers too.
//Default Value: 350.00
private constant real ZEPPELIN_HEIGHT = 350.00
//The model-size of the zeppelin.
//Default Value: 0.80
private constant real ZEPPELIN_SIZE = 0.80
//The speed the zeppelin is moving with. This does not effect the distance, when a sapper is dropped
//The formula for the distance for a sapper drop is:
//ZEPPELIN_MOVE_DISTANCE / (SAPPER_DROP_AMOUNT(lvl) + 1)
//Default Value: 450.00
private constant real ZEPPELIN_SPEED = 450.00
//This is the distance the zeppelin moves until is expires.
//Default Value: 1000.00
private constant real ZEPPELIN_MOVE_DISTANCE = 1000.00
//If this boolean is set to true, the zeppelin will display its death animation
//If false, it will be just removed without any animation
//Default Value: false
private constant boolean ZEPPELIN_DESTROY_ON_END = false
//Zeppelin Sounds
//This is the sound which is played when the spell is cast. Set this to "" if you want no sound.
//Default Value: "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelinWhat3.wav"
private constant string ZEPPELIN_SUMMON_SOUND = "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelinWhat3.wav"
//This is the duration of the sound above
//Default Value: 1.838
private constant real ZEPPELIN_SUMMON_SOUND_DUR = 1.838
//This sound is played whenever a zeppelin drops a sapper. Set it to "" if you want no sound.
//Default Value: "Abilities\\Spells\\Other\\LoadUnload\\Loading.wav"
private constant string ZEPPELIN_SAPPER_DROP_SOUND = "Abilities\\Spells\\Other\\LoadUnload\\Loading.wav"
//This is the duration of the sound above
//Default Value: 0.74
private constant real ZEPPELIN_SAPPER_DROP_SOUND_DUR = 0.74
endglobals
//=================================================================//
// Do not touch anything below unless you know what you are doing! //
//=================================================================//
private keyword Main
private keyword Zeppelin
private keyword Sapper
private struct Sapper
unit caster = null
unit target = null
unit sapper = null
real time = 0.00
real x = 0.00
real y = 0.00
real tx = 0.00
real ty = 0.00
real z = 0.00
real explosion = 0.00
texttag explosionIndicator = null
integer lvl = 0
effect dropEffect = null
effect burnEffect = null
boolean onGround = false
static trigger onDeath = CreateTrigger()
static timer ticker = CreateTimer()
static thistype temp = 0
static HandleTable table = 0
static delegate xedamage dmg = 0
static real dropSpeed = SAPPER_DROP_SPEED * TIMER_INTERVAL
static integer array sapperSound
static integer sapperSoundAmount = 0
implement List
static method addSapperSound takes string soundHandle, real duration returns nothing
set sapperSound[sapperSoundAmount] = DefineSound(soundHandle, R2I(duration * 1000), false, true)
set sapperSoundAmount = sapperSoundAmount + 1
endmethod
static method unitFilter takes nothing returns boolean
return SAPPER_TARGET_SEARCH_FILTER(GetFilterUnit(), temp.sapper)
endmethod
method onDestroy takes nothing returns nothing
call table.flush(sapper)
call listRemove()
if count == 0 then
call PauseTimer(ticker)
endif
static if SAPPER_SHOW_EXPLOSION_INDICATOR then
call DestroyTextTag(explosionIndicator)
endif
endmethod
static method onLoop takes nothing returns nothing
local thistype this = first
local real dx = 0.00
local real dy = 0.00
loop
exitwhen this == 0
if not onGround then
call SetUnitZ(sapper, GetUnitZ(sapper) - dropSpeed)
if GetUnitZ(sapper) <= 0.01 then
if dropEffect != null then
call DestroyEffect(dropEffect)
endif
if SAPPER_BURN_EFFECT != "" then
set burnEffect = AddSpecialEffectTarget(SAPPER_BURN_EFFECT, sapper, SAPPER_BURN_EFFECT_ATTACHPOINT)
endif
static if SAPPER_SHOW_EXPLOSION_INDICATOR then
set explosionIndicator = CreateTextTag()
call SetTextTagVisibility(explosionIndicator, true)
call SetTextTagPermanent(explosionIndicator, true)
call SetTextTagText(explosionIndicator, I2S(R2I(explosion + 0.5)), SAPPER_EXPLOSION_TIME_INDICATOR_SIZE * 0.0023)
call SetTextTagColor(explosionIndicator, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.red, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.green, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.blue, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.alpha)
call SetTextTagPos(explosionIndicator, x, y, GetUnitZ(sapper) + SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT)
endif
set onGround = true
endif
else
set x = GetUnitX(sapper)
set y = GetUnitY(sapper)
set explosion = explosion - TIMER_INTERVAL
static if SAPPER_SHOW_EXPLOSION_INDICATOR then
call SetTextTagPos(explosionIndicator, x, y, GetUnitZ(sapper) + SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT)
call SetTextTagText(explosionIndicator, I2S(R2I(explosion + 0.5)), SAPPER_EXPLOSION_TIME_INDICATOR_SIZE * 0.0023)
endif
if explosion <= 0 then
call SetUnitExploded(sapper, true)
call KillUnit(sapper)
call SetUnitAnimationByIndex(sapper, SAPPER_SUICIDE_ANIMATION_ID)
call destroy()
endif
if target == null then
set temp = this
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, SAPPER_TARGET_SEARCH_AREA, Condition(function thistype.unitFilter))
//These are valid BJ's which SHOULD be used...
if CountUnitsInGroup(ENUM_GROUP) > 0 then
set target = GroupPickRandomUnit(ENUM_GROUP)
set tx = GetUnitX(target)
set ty = GetUnitY(target)
call IssuePointOrder(sapper, "smart", tx, ty)
if SAPPER_TARGET_FOUND_EFFECT != "" then
call DestroyEffect(AddSpecialEffectTarget(SAPPER_TARGET_FOUND_EFFECT, sapper, SAPPER_TARGET_FOUND_EFFECT_ATTACHPOINT))
endif
static if SAPPER_ENABLE_FUNNY_SOUNDS then
if GetRandomReal(0.00, 1.00) <= SAPPER_FUNNY_SOUND_CHANCE then
call RunSoundOnUnit(sapperSound[GetRandomInt(0, sapperSoundAmount - 1)], sapper)
endif
endif
endif
else
set tx = GetUnitX(target)
set ty = GetUnitY(target)
if IsUnitType(target, UNIT_TYPE_DEAD) then
static if not SAPPER_STOP_WHEN_TARGET_DIES then
call IssuePointOrder(sapper, "smart", tx, ty)
else
call IssueImmediateOrder(sapper, "stop")
endif
set target = null
else
//This here is a problem... If the target moves, the sapper will stuck a little bit
//when i reorder smart or move each interval.
//So i made a check with the position of the target to only reorder the smart order
//once the target changed its position, dunno what else i could do...
if tx != GetUnitX(target) or ty != GetUnitY(target) then
set tx = GetUnitX(target)
set ty = GetUnitY(target)
call IssuePointOrder(sapper, "smart", tx, ty)
endif
set dx = x - tx
set dy = y - ty
if (dx * dx + dy * dy) <= SAPPER_SUICIDE_RANGE * SAPPER_SUICIDE_RANGE then
call SetUnitExploded(sapper, true)
call KillUnit(sapper)
call SetUnitAnimationByIndex(sapper, SAPPER_SUICIDE_ANIMATION_ID)
call destroy()
endif
endif
endif
endif
set this = next
endloop
endmethod
static method create takes Zeppelin data returns thistype
local thistype this = allocate()
set caster = data.caster
set y = data.y
set x = data.x
set z = data.z
set lvl = data.lvl
set explosion = SAPPER_EXPLOSION_TIMER(lvl)
set sapper = CreateUnit(GetOwningPlayer(caster), SAPPER_UNIT_ID, x, y, data.xyangle * bj_RADTODEG)
static if not LIBRARY_AutoFly then
call UnitAddAbility(sapper, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(sapper, XE_HEIGHT_ENABLER)
endif
if SAPPER_DROP_EFFECT != "" then
set dropEffect = AddSpecialEffectTarget(SAPPER_DROP_EFFECT, sapper, SAPPER_DROP_EFFECT_ATTACHPOINT)
endif
call SetUnitFlyHeight(sapper, GetTerrainZ(x, y) + z, 0.00)
call UnitAddAbility(sapper, 'Aloc')
call TriggerRegisterUnitEvent(onDeath, sapper, EVENT_UNIT_DEATH)
set table[sapper] = integer(this)
call listAdd()
if count == 1 then
call TimerStart(ticker, TIMER_INTERVAL, true, function thistype.onLoop)
endif
return this
endmethod
static method onUnitDeath takes nothing returns boolean
local thistype this = thistype(table[GetDyingUnit()])
if this != 0 then
call damageAOE(sapper, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
if SAPPER_EXPLOSION_EFFECT != "" then
call DestroyEffect(AddSpecialEffect(SAPPER_EXPLOSION_EFFECT, x, y))
endif
endif
return false
endmethod
static method onInit takes nothing returns nothing
set table = HandleTable.create()
set dmg = xedamage.create()
set dtype = SAPPER_EXPLOSION_DAMAGE_TYPE
set atype = SAPPER_EXPLOSION_ATTACK_TYPE
set damageAllies = SAPPER_EXPLOSION_DAMAGE_ALLIED
//! runtextmacro SAPPER_INIT_SOUNDS()
call TriggerAddCondition(onDeath, Condition(function thistype.onUnitDeath))
endmethod
endstruct
private struct Zeppelin
unit caster = null
real cos = 0.00
real sin = 0.00
real moved = 0.00
delegate xefx model = 0
sound summonSound = null
integer lvl = 0
integer sapperDropped = 0
integer sapperToDrop = 0
real sapperDropCounter = 0.00
static integer zeppelinSummonSound = 0
static integer zeppelinDropSound = 0
static timer ticker = CreateTimer()
static real speed = TIMER_INTERVAL * ZEPPELIN_SPEED
implement List
method onDestroy takes nothing returns nothing
static if not ZEPPELIN_DESTROY_ON_END then
call model.hiddenDestroy()
else
call model.destroy()
endif
call listRemove()
if count == 0 then
call PauseTimer(ticker)
endif
endmethod
static method onLoop takes nothing returns nothing
local thistype this = first
loop
exitwhen this == 0
set x = x + cos
set y = y + sin
static if USE_SOUNDS then
if summonSound != null then
call SetSoundPosition(summonSound, x, y, z)
endif
endif
set moved = moved + speed
set sapperDropCounter = sapperDropCounter + speed
if sapperDropCounter >= ZEPPELIN_MOVE_DISTANCE / (sapperToDrop + 1) then
set sapperDropCounter = 0.00
call Sapper.create(this)
static if USE_SOUNDS then
if zeppelinDropSound != 0 then
call RunSoundAtPoint(zeppelinDropSound, x, y, z)
endif
endif
endif
if moved >= ZEPPELIN_MOVE_DISTANCE then
call destroy()
endif
set this = next
endloop
endmethod
static method create takes Main data returns thistype
local thistype this = allocate()
set sin = ZEPPELIN_SPEED * TIMER_INTERVAL * Sin(data.angle)
set cos = ZEPPELIN_SPEED * TIMER_INTERVAL * Cos(data.angle)
set caster = data.caster
set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
set sapperToDrop = SAPPER_AMOUNT(lvl)
set model = xefx.create(data.cx, data.cy, data.angle)
set fxpath = ZEPPELIN_MODEL
set z = ZEPPELIN_HEIGHT
set scale = ZEPPELIN_SIZE
set teamcolor = GetPlayerColor(GetOwningPlayer(caster))
set owner = GetOwningPlayer(caster)
static if USE_SOUNDS then
if ZEPPELIN_SUMMON_SOUND != "" then
call RunSoundAtPoint(zeppelinSummonSound, x, y, z)
endif
endif
call listAdd()
if count == 1 then
call TimerStart(ticker, TIMER_INTERVAL, true, function thistype.onLoop)
endif
return this
endmethod
static method onInit takes nothing returns nothing
static if USE_SOUNDS then
if ZEPPELIN_SUMMON_SOUND != "" then
set zeppelinSummonSound = DefineSound(ZEPPELIN_SUMMON_SOUND, R2I(ZEPPELIN_SUMMON_SOUND_DUR * 1000), false, true)
endif
if ZEPPELIN_SAPPER_DROP_SOUND != "" then
set zeppelinDropSound = DefineSound(ZEPPELIN_SAPPER_DROP_SOUND, R2I(ZEPPELIN_SAPPER_DROP_SOUND_DUR * 1000), false, true)
endif
endif
endmethod
endstruct
private struct Main
unit caster = null
integer lvl = 0
real cx = 0.00
real cy = 0.00
real angle = 0.00
static method onCast takes nothing returns boolean
local thistype this = 0
if GetSpellAbilityId() != SPELL_ID then
return false
endif
set this = allocate()
set caster = GetSpellAbilityUnit()
set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set angle = Atan2(GetSpellTargetY() - cy, GetSpellTargetX() - cx)
call Zeppelin.create(this)
call destroy()
return false
endmethod
private static trigger cast = CreateTrigger()
static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(cast, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(cast, Condition(function thistype.onCast))
endmethod
endstruct
endscope
i dont think we should get point reduction for no MUI since its not in the criteria for "coding"
Nice work Weep. Simple spell, though. Maybe u should add timed life to these critter summoned by this, because sometimes, it can kill a map reason by having too many critter wandering... and maybe make the flower effect disappear when the spell effect end maybe? It'll give the impression that the flower is really that spell's effect and not just random doodad appeared if u know what i mean...
Well, maybe it is so. After all, that's in the site rules for spell resources...Nah...you don't get points deducted for that,you just get instantly disqualified.
Raw uber bloody spell, now only polishing and optimizing ^^
/*=====================================================================*\
//====== SUICIDE SQUAD =====*\\
\\====== Version 1.0b ======//
\\====== by Kricz ======//
|| ||
|| ||
|| Requires: ||
\\ Table [Vexorian] //
\\ ListModule [grim001] //
\\ xebasic, xedamage & xefx [Vexorian] //
\\ ARGB [Vexorian] //
|| SoundUtils [Rising_Dusk] ||
|| GroupUtils [Rising_Dusk] ||
|| Z-Utils [Kricz] ||
// \\
// Optional: \\
|| AutoFly [Kricz] (requires AutoIndex) ||
|| AbilityPreload [grim001] ||
|| ||
|| Please give credits if you use this spell in your map. ||
|| ||
\*==============================================================?=*/
scope SuicideSquad initializer onInit
/*Main Setup*/
globals
//The Spell-Id of the SuicideSquad Ability
private constant integer SPELL_ID = 'A000'
//The Timer-Interval of timers for movements and checks
//Default Value: 0.05
private constant real TIMER_INTERVAL = 0.05
//If true, the system will use sounds using Rising_Dusk's SoundUtils Library.
//You can manually disable specific sounds, by setting their path to "".
private constant boolean USE_SOUNDS = true
endglobals
/*Sapper Setup*/
globals
//The Unit-Id of the Sapper
private constant integer SAPPER_UNIT_ID = 'sapp'
//The speed of the down-falling sappers
//Default Value: 375.00
private constant real SAPPER_DROP_SPEED = 375.00
//The radius, in where sappers search valid targets
//Default Value: 700.00
private constant real SAPPER_TARGET_SEARCH_AREA = 700.00
//If this is true, the sapper will walk to the position of the target unit once it dies
//and search for new targets while walking. If false, the sapper will stop walking and
//then search for new targets
//Default Value: false
private constant boolean SAPPER_STOP_WHEN_TARGET_DIES = false
//This is the distance a sapper has to be with its target to make suicide.
//Default Value: 150.00
private constant real SAPPER_SUICIDE_RANGE = 150.00
//If true, the sappers are invulnerable while falling down and become normal once reaching the gorund
//Default Value: true
private constant boolean SAPPER_INVULNERABLE_WHILE_FALLING = true
//This is the animation-id which is played indstead of the normal death-animation.
//Information: 3 is the "spell death" animation, which shows, that the sapper does suicide
//4 is the normal death animation. Ofcourse you could use any other number, if you are funny :D
//Default Value: 3
private constant integer SAPPER_SUICIDE_ANIMATION_ID = 3
//This is the effect that is created when a sapper is dropped by a zeppeling and falling down
//Use "" for no effect.
//Default Value: "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
private constant string SAPPER_DROP_EFFECT = "Abilities\\Spells\\Other\\Tornado\\Tornado_Target.mdl"
//This is the attachment point of the drop effect above.
//Default Value: "chest"
private constant string SAPPER_DROP_EFFECT_ATTACHPOINT = "chest"
//This is the effect that is created once a sapper reaches the ground and kindles himself
//Default Value: "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
private constant string SAPPER_BURN_EFFECT = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
//This is the attachment point of the burn effect above
//Default Value: "chest"
private constant string SAPPER_BURN_EFFECT_ATTACHPOINT = "chest"
//This is the effect that is displayed, when a sapper found a target
//Set it to "" if you want no effect.
//Default Value: "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
private constant string SAPPER_TARGET_FOUND_EFFECT = "Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl"
//This is the attachment point of the target-found effect above
//Default Value: "overhead"
private constant string SAPPER_TARGET_FOUND_EFFECT_ATTACHPOINT = "overhead"
//The effect created by the explosion. Set it to "" for no effect
//Default Value: "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private constant string SAPPER_EXPLOSION_EFFECT = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
//This boolean decides, whether the movespeed of a sapper should be changed using
//the native SetUnitMoveSpeed or not. In some maps, changing the movespeed via trigger
//may cause some bugs with other systems/spells.
//If you dont want, that the movespeed is changed using SetUnitMoveSpeed, change it in the OE.
//Default Value: true
private constant boolean SAPPER_ENABLE_MOVESPEED_CHANGE = true
//This is the movespeed of sappers. It's only used, if the value above is set to true
//Default Value: 250.00
private constant real SAPPER_MOVEMENT_SPEED = 250.00
//If this boolean is set a true, a texttag is created above each sapper, showing when it will
//explode. Please note, that you can only have max. 100 Texttags active at the same time, so
//disable it, when you think you could reach more than 100.
//Default Value: true
private constant boolean SAPPER_SHOW_EXPLOSION_INDICATOR = true
//This is the color of the texttag. Read the ARGB Readme for mroe information about how to use it.
//Default Value: 0xFFFF0000 (red)
private ARGB SAPPER_EXPLOSION_TIME_INDICATOR_COLOR = 0xFFFF0000
//This is the size of the texttag.
//Default Value: 10.00
private constant real SAPPER_EXPLOSION_TIME_INDICATOR_SIZE = 10.00
//This is the height of the texttag.
//Default Value: 75.00
private constant real SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT = 75.00
//If this boolean is set to true, the damage will be done by the caster, not by the sapper
//that exploded. Result of that is, the each unit, that received damage through the explosion
//will attack the caster. If false, they will just do like nothing, cause they cannnot attack
//dead units (the sapper).
//Default Value: true
private constant boolean SAPPER_EXPLOSION_USE_CASTER_AS_SOURCE = true
//This is the attack type which xedamage uses once a sapper does suicide or explodes
//Default Value: ATTACK_TYPE_MAGIC
private constant attacktype SAPPER_EXPLOSION_ATTACK_TYPE = ATTACK_TYPE_MAGIC
//This is the damage type which xedamage uses once a sapper does suicide or explodes
//Default Value: DAMAGE_TYPE_FIRE
private constant damagetype SAPPER_EXPLOSION_DAMAGE_TYPE = DAMAGE_TYPE_FIRE
//This is the radius of the explosion.
//Default Value: 250.00
private constant real SAPPER_EXPLOSION_RADIUS = 250.00
//If true, the explosion will also deal damage to destructables in the explosion area
//Default Value: true
private constant boolean SAPPER_EXPLOSION_DAMAGE_DESTRUCTS = true
//If this boolean is set to true, the explosion will also damage allied unit.
//Default Value: false
private constant boolean SAPPER_EXPLOSION_DAMAGE_ALLIED = false
//If this boolean is true, sappers will play a random sound,
//which you can add/remove/change in the SAPPER_INIT_SOUNDS textmacro below
//Default Value: true
private constant boolean SAPPER_ENABLE_FUNNY_SOUNDS = true
//This value is only important, if funny sounds of sappers are enabled.
//Because it may sounds stupid, when 5 sappers speak at the same time,
//there is this possibility to give each sapper only a chance to tell something
//which you can setup with this value.
//Default Value: 0.33 (33%)
private constant real SAPPER_FUNNY_SOUND_CHANCE = 0.30
endglobals
//in this macro, you can add funny sounds for sappers.
//the function you have to call is:
//call addSapperSound(string soundHandle, real duration)
//soundHandle is the path of the sound, you can get them i.e from the SoundEditor (F5)
//duration is the duration of the sound, you can also get this information in the SoundEditor
//Example: call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes1.wav", 1.463)
//! textmacro SAPPER_INIT_SOUNDS
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes1.wav", 1.463)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYes5.wav", 1.434)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperWhat2.wav", 1.694)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYesAttack3.wav", 2.026)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperYesAttack4.wav", 1.091)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed1.wav", 1.515)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed2.wav", 2.776)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed4.wav", 2.554)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed5.wav", 0.592)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed6.wav", 2.201)
call addSapperSound("Units\\Creeps\\GoblinSapper\\GoblinSapperPissed7.wav", 2.374)
//! endtextmacro
//This is the amount of sappers dropped each level.
//Default Value: 3 + lvl
private constant function SAPPER_AMOUNT takes integer lvl returns integer
return 3 + lvl
endfunction
//This is the time, a sapper has until it will explode
//Default Value: 6.00 + lvl
private constant function SAPPER_EXPLOSION_TIMER takes integer lvl returns real
return 6.00 + lvl
endfunction
//This is the damage a explosion deals.
//Default Value: 25.00 + 25.00 * lvl
private constant function SAPPER_EXPLOSION_DAMAGE takes integer lvl returns real
return 15.00 * lvl
endfunction
//This is the filter used to get valid targets for sappers.
//Default Value: not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(sapper)) and IsUnitEnemy(u, GetOwningPlayer(sapper))
private constant function SAPPER_TARGET_SEARCH_FILTER takes unit u, unit sapper returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(sapper)) and IsUnitEnemy(u, GetOwningPlayer(sapper))
endfunction
/*Zeppelin Setup*/
globals
//The model of the zeppelin that drops the sappers
//Default Value: "units\\creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
private constant string ZEPPELIN_MODEL = "units\\creeps\\GoblinZeppelin\\GoblinZeppelin.mdl"
//The height of the zeppelin. Please note, that the higher this value is, the longer a sapper requires
//to reach the ground. If you change this value, you may should change the drop speed of the sappers too.
//Default Value: 500.00
private constant real ZEPPELIN_HEIGHT = 500.00
//The model-size of the zeppelin.
//Default Value: 0.80
private constant real ZEPPELIN_SIZE = 0.80
//The speed the zeppelin is moving with. This does not effect the distance, when a sapper is dropped
//The formula for the distance for a sapper drop is:
//ZEPPELIN_MOVE_DISTANCE / (SAPPER_DROP_AMOUNT(lvl) + 1)
//Default Value: 400.00
private constant real ZEPPELIN_SPEED = 400.00
//This is the distance the zeppelin moves until is expires.
//Default Value: 1000.00
private constant real ZEPPELIN_MOVE_DISTANCE = 1000.00
//If this boolean is set to true, the zeppelin will display its death animation
//If false, it will be just removed without any animation
//Default Value: false
private constant boolean ZEPPELIN_DESTROY_ON_END = false
//Zeppelin Sounds
//This is the sound which is played when the spell is cast. Set this to "" if you want no sound.
//Default Value: "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelinWhat3.wav"
private constant string ZEPPELIN_SUMMON_SOUND = "Units\\Creeps\\GoblinZeppelin\\GoblinZeppelinWhat3.wav"
//This is the duration of the sound above
//Default Value: 1.838
private constant real ZEPPELIN_SUMMON_SOUND_DUR = 1.838
//This sound is played whenever a zeppelin drops a sapper. Set it to "" if you want no sound.
//Default Value: "Abilities\\Spells\\Other\\LoadUnload\\Loading.wav"
private constant string ZEPPELIN_SAPPER_DROP_SOUND = "Abilities\\Spells\\Other\\LoadUnload\\Loading.wav"
//This is the duration of the sound above
//Default Value: 0.74
private constant real ZEPPELIN_SAPPER_DROP_SOUND_DUR = 0.74
endglobals
//=================================================================//
// Do not touch anything below unless you know what you are doing! //
//=================================================================//
private keyword Main
private keyword Zeppelin
private keyword Sapper
private struct Sapper
unit caster = null
unit target = null
unit sapper = null
real time = 0.00
real x = 0.00
real y = 0.00
real tx = 0.00
real ty = 0.00
real z = 0.00
real explosion = 0.00
texttag explosionIndicator = null
integer lvl = 0
effect dropEffect = null
effect burnEffect = null
player owner = null
boolean onGround = false
boolean oldTargetDied = false
static trigger onDeath = CreateTrigger()
static trigger onOrder = CreateTrigger()
static timer ticker = CreateTimer()
static thistype temp = 0
static HandleTable sapperTable = 0
static HandleTable targetTable = 0
static delegate xedamage dmg = 0
static boolean ignoreOrders = false
static real dropSpeed = SAPPER_DROP_SPEED * TIMER_INTERVAL
static integer array sapperSound
static integer sapperSoundAmount = 0
implement List
static method addSapperSound takes string soundHandle, real duration returns nothing
set sapperSound[sapperSoundAmount] = DefineSound(soundHandle, R2I(duration * 1000), false, true)
set sapperSoundAmount = sapperSoundAmount + 1
endmethod
static method unitFilter takes nothing returns boolean
return SAPPER_TARGET_SEARCH_FILTER(GetFilterUnit(), temp.sapper)
endmethod
method onDestroy takes nothing returns nothing
call sapperTable.flush(sapper)
call listRemove()
if count == 0 then
call PauseTimer(ticker)
endif
static if SAPPER_SHOW_EXPLOSION_INDICATOR then
call DestroyTextTag(explosionIndicator)
endif
endmethod
method searchForTarget takes nothing returns boolean
set temp = this
call GroupEnumUnitsInArea(ENUM_GROUP, x, y, SAPPER_TARGET_SEARCH_AREA, Condition(function thistype.unitFilter))
//These are valid BJ's which SHOULD be used...
if CountUnitsInGroup(ENUM_GROUP) > 0 then
set target = GroupPickRandomUnit(ENUM_GROUP)
//Using the table to save whether a unit is already registred to the trigger or not
if targetTable[target] == 0 then
call TriggerRegisterUnitEvent(onDeath, target, EVENT_UNIT_DEATH)
set targetTable[target] = 1
endif
set tx = GetUnitX(target)
set ty = GetUnitY(target)
call IssueTargetOrder(sapper, "attack", target)
if SAPPER_TARGET_FOUND_EFFECT != "" then
call DestroyEffect(AddSpecialEffectTarget(SAPPER_TARGET_FOUND_EFFECT, sapper, SAPPER_TARGET_FOUND_EFFECT_ATTACHPOINT))
endif
static if SAPPER_ENABLE_FUNNY_SOUNDS then
if GetRandomReal(0.00, 1.00) <= SAPPER_FUNNY_SOUND_CHANCE then
call RunSoundOnUnit(sapperSound[GetRandomInt(0, sapperSoundAmount - 1)], sapper)
endif
endif
else
set target = null
endif
return target != null
endmethod
method moveSapper takes nothing returns nothing
if target == null then
static if not SAPPER_STOP_WHEN_TARGET_DIES then
if oldTargetDied then
call IssuePointOrder(sapper, "smart", tx, ty)
else
call PauseUnit(sapper, true)
call IssueImmediateOrder(sapper, "stop")
call PauseUnit(sapper, false)
endif
else
call PauseUnit(sapper, true)
call IssueImmediateOrder(sapper, "stop")
call PauseUnit(sapper, false)
endif
else
if not IsUnitVisible(target, owner) then
if GetUnitCurrentOrder(sapper) == 851983 then
//if target becomes invisible or gets out of sight, the sapper will
//run to the position, where the target was last seen.
call IssuePointOrder(sapper, "smart", tx, ty)
endif
else
if IsUnitType(target, UNIT_TYPE_DEAD) then
if not searchForTarget() then
set oldTargetDied = true
static if not SAPPER_STOP_WHEN_TARGET_DIES then
call IssuePointOrder(sapper, "smart", tx, ty)
else
call PauseUnit(sapper, true)
call IssueImmediateOrder(sapper, "stop")
call PauseUnit(sapper, false)
endif
else
set oldTargetDied = false
endif
elseif GetUnitCurrentOrder(sapper) != 851983 then
call IssueTargetOrder(sapper, "attack", target)
endif
endif
endif
endmethod
static method onLoop takes nothing returns nothing
local thistype this = first
local real dx = 0.00
local real dy = 0.00
set ignoreOrders = true
loop
exitwhen this == 0
if not onGround then
call SetUnitZ(sapper, GetUnitZ(sapper) - dropSpeed)
if GetUnitZ(sapper) <= 0.01 then
static if SAPPER_INVULNERABLE_WHILE_FALLING then
call SetUnitInvulnerable(sapper, false)
endif
if dropEffect != null then
call DestroyEffect(dropEffect)
endif
if SAPPER_BURN_EFFECT != "" then
set burnEffect = AddSpecialEffectTarget(SAPPER_BURN_EFFECT, sapper, SAPPER_BURN_EFFECT_ATTACHPOINT)
endif
static if SAPPER_SHOW_EXPLOSION_INDICATOR then
set explosionIndicator = CreateTextTag()
call SetTextTagVisibility(explosionIndicator, true)
call SetTextTagPermanent(explosionIndicator, true)
call SetTextTagText(explosionIndicator, I2S(R2I(explosion + 0.5)), SAPPER_EXPLOSION_TIME_INDICATOR_SIZE * 0.0023)
call SetTextTagColor(explosionIndicator, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.red, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.green, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.blue, SAPPER_EXPLOSION_TIME_INDICATOR_COLOR.alpha)
call SetTextTagPos(explosionIndicator, x, y, GetUnitZ(sapper) + SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT)
endif
set onGround = true
endif
else
set x = GetUnitX(sapper)
set y = GetUnitY(sapper)
set explosion = explosion - TIMER_INTERVAL
static if SAPPER_SHOW_EXPLOSION_INDICATOR then
call SetTextTagPos(explosionIndicator, x, y, GetUnitZ(sapper) + SAPPER_EXPLOSION_TIME_INDICATOR_HEIGHT)
call SetTextTagText(explosionIndicator, I2S(R2I(explosion + 0.5)), SAPPER_EXPLOSION_TIME_INDICATOR_SIZE * 0.0023)
endif
if explosion <= 0 then
call SetUnitExploded(sapper, true)
call KillUnit(sapper)
call SetUnitAnimationByIndex(sapper, SAPPER_SUICIDE_ANIMATION_ID)
call destroy()
endif
if target == null then
call searchForTarget()
else
call moveSapper()
if IsUnitVisible(target, owner) then
set tx = GetUnitX(target)
set ty = GetUnitY(target)
set dx = x - tx
set dy = y - ty
if (dx * dx + dy * dy) <= SAPPER_SUICIDE_RANGE * SAPPER_SUICIDE_RANGE then
call SetUnitExploded(sapper, true)
call KillUnit(sapper)
call SetUnitAnimationByIndex(sapper, SAPPER_SUICIDE_ANIMATION_ID)
call destroy()
endif
endif
endif
endif
set this = next
endloop
set ignoreOrders = false
endmethod
static method create takes Zeppelin data returns thistype
local thistype this = allocate()
set caster = data.caster
set y = data.y
set x = data.x
set z = data.z
set lvl = data.lvl
set explosion = SAPPER_EXPLOSION_TIMER(lvl)
set sapper = CreateUnit(GetOwningPlayer(caster), SAPPER_UNIT_ID, x, y, data.xyangle * bj_RADTODEG)
set owner = GetOwningPlayer(sapper)
static if not LIBRARY_AutoFly then
call UnitAddAbility(sapper, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(sapper, XE_HEIGHT_ENABLER)
endif
static if SAPPER_INVULNERABLE_WHILE_FALLING then
call SetUnitInvulnerable(sapper, true)
endif
if SAPPER_DROP_EFFECT != "" then
set dropEffect = AddSpecialEffectTarget(SAPPER_DROP_EFFECT, sapper, SAPPER_DROP_EFFECT_ATTACHPOINT)
endif
call SetUnitZ(sapper, z)
call TriggerRegisterUnitEvent(onDeath, sapper, EVENT_UNIT_DEATH)
set sapperTable[sapper] = integer(this)
call listAdd()
if count == 1 then
call TimerStart(ticker, TIMER_INTERVAL, true, function thistype.onLoop)
endif
return this
endmethod
static method onUnitDeath takes nothing returns boolean
local unit killed = GetDyingUnit()
local thistype this = thistype(sapperTable[killed])
if this != 0 then
static if SAPPER_EXPLOSION_USE_CASTER_AS_SOURCE then
call damageAOE(caster, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
else
call damageAOE(sapper, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
endif
static if SAPPER_EXPLOSION_DAMAGE_DESTRUCTS then
call damageDestructablesAOE(caster, x, y, SAPPER_EXPLOSION_RADIUS, SAPPER_EXPLOSION_DAMAGE(lvl))
endif
if SAPPER_EXPLOSION_EFFECT != "" then
call DestroyEffect(AddSpecialEffect(SAPPER_EXPLOSION_EFFECT, x, y))
endif
else
//loop through everything and check targets. if the dying unit is a target of any sapper, find a new one
//or order it to move to the target's location if enabled
set this = first
loop
exitwhen this == 0
if killed == target then
set oldTargetDied = true
if not searchForTarget() then
static if not SAPPER_STOP_WHEN_TARGET_DIES then
set tx = GetUnitX(killed)
set ty = GetUnitY(killed)
call IssuePointOrder(sapper, "smart", tx, ty)
else
call PauseUnit(sapper, true)
call IssueImmediateOrder(sapper, "stop")
call PauseUnit(sapper, false)
endif
endif
endif
set this = next
endloop
endif
set killed = null
return false
endmethod
static method onUnitOrder takes nothing returns boolean
local thistype this = thistype(sapperTable[GetOrderedUnit()])
if this != 0 and not ignoreOrders then
set ignoreOrders = true
call moveSapper()
set ignoreOrders = false
endif
return false
endmethod
static method onInit takes nothing returns nothing
set sapperTable = HandleTable.create()
set targetTable = HandleTable.create()
set dmg = xedamage.create()
set dtype = SAPPER_EXPLOSION_DAMAGE_TYPE
set atype = SAPPER_EXPLOSION_ATTACK_TYPE
set damageAllies = SAPPER_EXPLOSION_DAMAGE_ALLIED
//! runtextmacro SAPPER_INIT_SOUNDS()
call TriggerAddCondition(onDeath, Condition(function thistype.onUnitDeath))
call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterAnyUnitEventBJ(onOrder, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
call TriggerAddCondition(onOrder, Condition(function thistype.onUnitOrder))
endmethod
endstruct
private struct Zeppelin
unit caster = null
real cos = 0.00
real sin = 0.00
real moved = 0.00
delegate xefx model = 0
sound summonSound = null
integer lvl = 0
integer sapperDropped = 0
integer sapperToDrop = 0
real sapperDropCounter = 0.00
static integer zeppelinSummonSound = 0
static integer zeppelinDropSound = 0
static timer ticker = CreateTimer()
static real speed = TIMER_INTERVAL * ZEPPELIN_SPEED
implement List
method onDestroy takes nothing returns nothing
static if not ZEPPELIN_DESTROY_ON_END then
call model.hiddenDestroy()
else
call model.destroy()
endif
call listRemove()
if count == 0 then
call PauseTimer(ticker)
endif
endmethod
static method onLoop takes nothing returns nothing
local thistype this = first
loop
exitwhen this == 0
set x = x + cos
set y = y + sin
static if USE_SOUNDS then
if summonSound != null then
call SetSoundPosition(summonSound, x, y, z)
endif
endif
set moved = moved + speed
set sapperDropCounter = sapperDropCounter + speed
if sapperDropCounter >= ZEPPELIN_MOVE_DISTANCE / (sapperToDrop + 1) then
set sapperDropCounter = 0.00
call Sapper.create(this)
static if USE_SOUNDS then
if zeppelinDropSound != 0 then
call RunSoundAtPoint(zeppelinDropSound, x, y, z)
endif
endif
endif
if moved >= ZEPPELIN_MOVE_DISTANCE then
call destroy()
endif
set this = next
endloop
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set sin = ZEPPELIN_SPEED * TIMER_INTERVAL * Sin(Main.angle)
set cos = ZEPPELIN_SPEED * TIMER_INTERVAL * Cos(Main.angle)
set caster = Main.caster
set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
set sapperToDrop = SAPPER_AMOUNT(lvl)
set model = xefx.create(Main.cx, Main.cy, Main.angle)
set fxpath = ZEPPELIN_MODEL
set z = ZEPPELIN_HEIGHT
set scale = ZEPPELIN_SIZE
set teamcolor = GetPlayerColor(GetOwningPlayer(caster))
set owner = GetOwningPlayer(caster)
static if USE_SOUNDS then
if ZEPPELIN_SUMMON_SOUND != "" then
call RunSoundAtPoint(zeppelinSummonSound, x, y, z)
endif
endif
call listAdd()
if count == 1 then
call TimerStart(ticker, TIMER_INTERVAL, true, function thistype.onLoop)
endif
return this
endmethod
static method onInit takes nothing returns nothing
static if USE_SOUNDS then
if ZEPPELIN_SUMMON_SOUND != "" then
set zeppelinSummonSound = DefineSound(ZEPPELIN_SUMMON_SOUND, R2I(ZEPPELIN_SUMMON_SOUND_DUR * 1000), false, true)
endif
if ZEPPELIN_SAPPER_DROP_SOUND != "" then
set zeppelinDropSound = DefineSound(ZEPPELIN_SAPPER_DROP_SOUND, R2I(ZEPPELIN_SAPPER_DROP_SOUND_DUR * 1000), false, true)
endif
endif
endmethod
endstruct
private struct Main extends array
static unit caster = null
static integer lvl = 0
static real cx = 0.00
static real cy = 0.00
static real angle = 0.00
static method onCast takes nothing returns boolean
if GetSpellAbilityId() != SPELL_ID then
return false
endif
set caster = GetSpellAbilityUnit()
set lvl = GetUnitAbilityLevel(caster, SPELL_ID)
set cx = GetUnitX(caster)
set cy = GetUnitY(caster)
set angle = Atan2(GetSpellTargetY() - cy, GetSpellTargetX() - cx)
call Zeppelin.create()
return false
endmethod
private static trigger cast = CreateTrigger()
static method onInit takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ(cast, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(cast, Condition(function thistype.onCast))
static if LIBRARY_AbilityPreload then
call AbilityPreload(SPELL_ID)
endif
endmethod
endstruct
endscope
library getFlowerID
globals
private integer rand
private constant integer HAPPINESSONE='e001'
private constant integer HAPPINESSTWO='e002'
private constant integer HAPPINESSTHR='e003'
private constant integer HAPPINESSFOU='e004'
endglobals
function getFlower takes nothing returns integer
set rand=GetRandomInt(0,3)
if rand==0 then
return HAPPINESSONE
elseif rand==1 then
return HAPPINESSTWO
elseif rand==2 then
return HAPPINESSTHR
elseif rand==3 then
return HAPPINESSFOU
endif
return 0
endfunction
endlibrary
//this is the only script I made that requires a third party library. Generally I integrate only the parts of libs I need into my systems, but IsTerrainWalkable is one of my favorites.
library launchUnit requires IsTerrainWalkable
private struct dat
unit u
real xV
real yV
real zPrime
endstruct
globals
private constant real GRAVITY=4*9.8*.03
private integer dbIndex=-1
private dat array datDB
private timer time=CreateTimer()
endglobals
private function p takes nothing returns nothing
local integer index=0
local effect fx
local dat tempDat
local real dir
loop
exitwhen index>dbIndex
set tempDat=datDB[index]
call SetUnitX(tempDat.u,GetUnitX(tempDat.u)+tempDat.xV)
call SetUnitY(tempDat.u,GetUnitY(tempDat.u)+tempDat.yV)
call SetUnitFlyHeight(tempDat.u,GetUnitFlyHeight(tempDat.u)+tempDat.zPrime,0)
set tempDat.zPrime=tempDat.zPrime-GRAVITY
if GetUnitFlyHeight(tempDat.u)<25 then
set fx=AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl",GetUnitX(tempDat.u),GetUnitY(tempDat.u))
call DestroyEffect(fx)
if IsTerrainWalkable(GetUnitX(tempDat.u),GetUnitY(tempDat.u))==false then
set dir=Atan(tempDat.yV/tempDat.xV)
call SetUnitX(tempDat.u,GetUnitX(tempDat.u)-128*Cos(dir))
call SetUnitY(tempDat.u,GetUnitY(tempDat.u)-128*Sin(dir))
set fx=AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl",GetUnitX(tempDat.u),GetUnitY(tempDat.u))
call DestroyEffect(fx)
endif
set datDB[index]=datDB[dbIndex]
set dbIndex=dbIndex-1
call UnitRemoveAbility(tempDat.u,'Arav')
call PauseUnit(tempDat.u,false)
if dbIndex<0 then
call PauseTimer(time)
endif
endif
set index=index+1
endloop
endfunction
function launch takes unit u, real dir, real zTheta, real power returns nothing
local dat tempDat=dat.create()
local real pow=power-(GetUnitState(u,UNIT_STATE_MAX_LIFE)*.01)
call UnitAddAbility(u,'Arav')
call SetUnitFlyHeight(u,100,0)
call PauseUnit(u,true)
set tempDat.u=u
set tempDat.xV=pow*Cos(dir)*Cos(zTheta)
set tempDat.yV=pow*Sin(dir)*Cos(zTheta)
set tempDat.zPrime=pow*Sin(zTheta)
set dbIndex=dbIndex+1
set datDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,.03,true,function p)
endif
endfunction
endlibrary
library HExplosion requires getFlowerID, launchUnit
private struct dat
real xvel
real yvel
real zvelPrime
unit u
endstruct
globals
private constant real PROJVEL=(1024-128)*.03
private constant real GRAVITY=4*9.8*.03
private group g=CreateGroup()
private player p
private integer dmg
private integer dbIndex=-1
private timer time=CreateTimer()
private dat array datDB
private real savedX
private real savedY
endglobals
private function per takes nothing returns nothing
local integer loopIndex=0
local dat tempDat
loop
exitwhen loopIndex>dbIndex
set tempDat=datDB[loopIndex]
call SetUnitX(tempDat.u,GetUnitX(tempDat.u)+tempDat.xvel)
call SetUnitY(tempDat.u,GetUnitY(tempDat.u)+tempDat.yvel)
call SetUnitFlyHeight(tempDat.u,GetUnitFlyHeight(tempDat.u)+tempDat.zvelPrime,0)
set tempDat.zvelPrime=tempDat.zvelPrime-GRAVITY
if GetUnitFlyHeight(tempDat.u)<50 then
call RemoveUnit(tempDat.u)
set datDB[loopIndex]=datDB[dbIndex]
set dbIndex=dbIndex-1
if dbIndex<0 then
call PauseTimer(time)
endif
endif
set loopIndex=loopIndex+1
endloop
endfunction
private function throwFlower takes real arc, real x, real y, real planeTheta returns nothing
local integer id=getFlower()
local unit u
local real newArc=arc*GetRandomReal(.95,1.05)
local real newPlane=planeTheta*GetRandomReal(.95,1.05)
local dat tempDat=dat.create()
set tempDat.u=CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),id,x,y,planeTheta)
call SetUnitVertexColor(tempDat.u,255,255,255,155)
call UnitAddAbility(tempDat.u,'Arav')
call UnitAddAbility(tempDat.u,'Aloc')
call SetUnitFlyHeight(tempDat.u,100,0)
call SetUnitPathing(tempDat.u,false)
call SetUnitX(tempDat.u,x)
call SetUnitY(tempDat.u,y)
set tempDat.xvel=PROJVEL*Cos(newPlane)*Cos(newArc)
set tempDat.yvel=PROJVEL*Sin(newPlane)*Cos(newArc)
set tempDat.zvelPrime=PROJVEL*Sin(newArc)
set dbIndex=dbIndex+1
set datDB[dbIndex]=tempDat
if dbIndex==0 then
call TimerStart(time,.03,true,function per)
endif
endfunction
private function f takes nothing returns boolean
local real ang
local real zT
local real dist
if IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)==false then
call UnitDamageTarget(GetFilterUnit(),GetFilterUnit(),dmg,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
set ang=Atan2(GetUnitY(GetFilterUnit())-savedY,GetUnitX(GetFilterUnit())-savedX)
set dist=SquareRoot((GetUnitX(GetFilterUnit())-savedX)*(GetUnitX(GetFilterUnit())-savedX)+(GetUnitY(GetFilterUnit())-savedY)*(GetUnitY(GetFilterUnit())-savedY))
set zT=Atan(100/dist)
call launch(GetFilterUnit(),ang,zT,PROJVEL)
endif
return false
endfunction
function happyExplosion takes real x, real y, integer hp, player deathPlayer returns nothing
local integer index=0
local integer arcIndex=1
set p=deathPlayer
set dmg=hp/10
set savedX=x
set savedY=y
call GroupEnumUnitsInRange(g,x,y,100+hp,Filter(function f))
call throwFlower(bj_PI/2,x,y,0)
loop
exitwhen arcIndex>7
loop
exitwhen index>15
call throwFlower(bj_PI/16*arcIndex,x,y,bj_PI/8*index)
set index=index+1
endloop
set index=0
set arcIndex=arcIndex+1
endloop
endfunction
endlibrary
scope castHappiness initializer i
private struct muiDat
unit caster
unit target
integer steps
endstruct
private struct projDat
unit target
unit projectile
integer steps
real xOff
real yOff
endstruct
globals
private constant integer HAPPINESSID='A001'
private constant real OFFSET=40.
private integer stackIndex=-1
private timer time=CreateTimer()
private muiDat array muiDB
private integer moveIndex=-1
private timer fastTime=CreateTimer()
private projDat array projDB
private sound snd=CreateSound("Abilities\\Weapons\\BansheeMissile\\BansheeMissileHit2.wav",false,true,true,0,0,"")
endglobals
private function fastP takes nothing returns nothing
local integer tempIndex=0
local integer circleIndex=0
local real rads
local real dist
local real xDis
local real yDis
local real cX
local real cY
local effect e
local projDat tempDat
loop
exitwhen tempIndex>moveIndex
set tempDat=projDB[tempIndex]
set rads=Atan2(GetUnitY(tempDat.target)+tempDat.yOff-GetUnitY(tempDat.projectile),GetUnitX(tempDat.target)+tempDat.xOff-GetUnitX(tempDat.projectile))
call SetUnitX(tempDat.projectile,GetUnitX(tempDat.projectile)+OFFSET*Cos(rads))
call SetUnitY(tempDat.projectile,GetUnitY(tempDat.projectile)+OFFSET*Sin(rads))
set xDis=GetUnitX(tempDat.target)-GetUnitX(tempDat.projectile)
set yDis=GetUnitY(tempDat.target)-GetUnitY(tempDat.projectile)
set dist=SquareRoot(xDis*xDis+yDis*yDis)
if dist<80 then
call RemoveUnit(tempDat.projectile)
call SetSoundPosition(snd,GetUnitX(tempDat.target),GetUnitY(tempDat.target),50)
call SetSoundVolume(snd,127)
call StartSound(snd)
call SetUnitMoveSpeed(tempDat.target,GetUnitMoveSpeed(tempDat.target)-4)
call SetUnitState(tempDat.target,UNIT_STATE_LIFE,GetUnitState(tempDat.target,UNIT_STATE_LIFE)+1)
call SetUnitTimeScale(tempDat.target,1-.02*tempDat.steps)
call SetUnitScale(tempDat.target,1+.025*tempDat.steps,1+.025*tempDat.steps,1+.025*tempDat.steps)
call SetUnitVertexColor(tempDat.target,255,255-tempDat.steps*5/2,255-tempDat.steps*5/2,255)
if tempDat.steps>50 and IsUnitType(tempDat.target,UNIT_TYPE_DEAD)==false then
set e=AddSpecialEffect("Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl",GetUnitX(tempDat.target),GetUnitY(tempDat.target))
call DestroyEffect(e)
set e=AddSpecialEffect("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl",GetUnitX(tempDat.target),GetUnitY(tempDat.target))
call DestroyEffect(e)
call happyExplosion(GetUnitX(tempDat.target),GetUnitY(tempDat.target),R2I(GetUnitState(tempDat.target,UNIT_STATE_MAX_LIFE)),GetOwningPlayer(tempDat.target))
call KillUnit(tempDat.target)
call SetUnitScale(tempDat.target,.01,.01,.01)
call UnitAddAbility(tempDat.target,'Arav')
call SetUnitFlyHeight(tempDat.target,2000,0)
endif
set projDB[tempIndex]=projDB[moveIndex]
set moveIndex=moveIndex-1
endif
set tempIndex=tempIndex+1
endloop
if moveIndex<0 then
call PauseTimer(fastTime)
endif
endfunction
private function p takes nothing returns nothing
local integer tempIndex=0
local integer rand
local integer createID
local muiDat tempDat
local real offs
local real theta
loop
exitwhen tempIndex>stackIndex
set tempDat=muiDB[tempIndex]
if IsUnitType(tempDat.caster,UNIT_TYPE_DEAD)==false and IsUnitType(tempDat.target,UNIT_TYPE_DEAD)==false and GetUnitCurrentOrder(tempDat.caster)==OrderId("channel") then
set createID=getFlower()
set moveIndex=moveIndex+1
set tempDat.steps=tempDat.steps+1
set projDB[moveIndex]=projDat.create()
set projDB[moveIndex].target=tempDat.target
set projDB[moveIndex].projectile=CreateUnit(GetOwningPlayer(tempDat.caster),createID,GetUnitX(tempDat.caster),GetUnitY(tempDat.caster),GetUnitFacing(tempDat.caster))
set projDB[moveIndex].steps=tempDat.steps
set theta=GetRandomReal(0,2*bj_PI)
set offs=GetRandomReal(5,75)
set projDB[moveIndex].xOff=offs*Cos(theta)
set projDB[moveIndex].yOff=offs*Sin(theta)
call UnitAddAbility(projDB[moveIndex].projectile,'Arav')
call SetUnitFlyHeight(projDB[moveIndex].projectile,50,0)
call SetUnitPathing(projDB[moveIndex].projectile,false)
call UnitAddAbility(projDB[moveIndex].projectile,'Aloc')
call SetUnitX(projDB[moveIndex].projectile,GetUnitX(tempDat.caster))
call SetUnitY(projDB[moveIndex].projectile,GetUnitY(tempDat.caster))
if moveIndex==0 then
call TimerStart(fastTime,.03,true,function fastP)
endif
else
set muiDB[tempIndex]=muiDB[stackIndex]
set stackIndex=stackIndex-1
if stackIndex<0 then
call PauseTimer(time)
endif
endif
set tempIndex=tempIndex+1
endloop
endfunction
private function c takes nothing returns boolean
local effect fx
if GetSpellAbilityId()==HAPPINESSID then
set stackIndex=stackIndex+1
set muiDB[stackIndex]=muiDat.create()
set muiDB[stackIndex].caster=GetTriggerUnit()
set muiDB[stackIndex].target=GetSpellTargetUnit()
call SetUnitScale(GetSpellTargetUnit(),1,1,1)
call SetUnitVertexColor(GetSpellTargetUnit(),255,255,255,255)
set fx=AddSpecialEffect("Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl",GetUnitX(GetSpellTargetUnit()),GetUnitY(GetSpellTargetUnit()))
call DestroyEffect(fx)
if stackIndex==0 then
call TimerStart(time,.1,true,function p)
endif
endif
return false
endfunction
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))
endfunction
endscope
I think I might not finish the spell... well the spell itself is finished but for some weird reason it lags when I cast it successively, but once the effect wears out, the game gets back to normal... and I don't have anymore time for it coz of lots of exams... ^_^
Well, its an instant spell based on channel, then when you cast it, units around you can get "frosted" (by means of using a custom Soul Burn spell)
OP said:Overall Aspect (5 points) - Making sure your spell is easy to read (tooltip, comments, logical variable names), as well as having a practical use in-game.
Hehe. Well, actually, I like GUI...I just wanted to impress Bribe.WOW
I've been seeing a loooooooot of vJASS in this thread o.o
You guys make me look like SHIT!
I'm coding my spell in GUI (ntn special)
WOW
I've been seeing a loooooooot of vJASS in this thread o.o
You guys make me look like SHIT!
I'm coding my spell in GUI (ntn special)
...
*idea*
I'll write in GUI then convert to JASS, remove BJs, split trigger into functions, add globals, make it a scope and there you have it.. a vJASS spell
![]()
/*
Super Frost Joke by Adiktuz
Description:
The caster cracks a joke which can freeze enemy units around him
Configurables:
-The jokes
-The chances of freezing for each joke
-The duration of the freeze
-The targets of the freeze
-The damage of the freeze
-The AoE of the freeze
How to import:
-Copy and paste this code into your trigger editor
-Copy the two abilities and the buff
-Create a dummy caster if you don't have one or copy the one on this map
-Change the rawcodes to fit the new rawcodes on your map
-Edit the configurables to your liking
-Add your jokes using [function RegisterJokes takes string joke, real chance]
*examples given on InitJokes trigger
IMPORTANT: On the gameplay constants, set the UNIT MIN MOVESPEED to 0.00
Credits:
Jesus4Lyf for Timer32
*/
library SuperFrostJoke initializer Init requires T32
globals
//The rawcode of the Frost joke spell
private constant integer FJ_SPELL_ID = 'A000'
//The rawcode of the frost joke buff placer (the soulburn-based ability)
private constant integer FJ_BUFFPLACER_ID = 'A001'
//The rawcode of the frost joke buff which the buffplacer uses
private constant integer FJ_BUFF_ID = 'B000'
//The rawcode of the dummy unit
private constant integer DUMMY_ID = 'h000'
//Checks if the FJ deals damage, if yes be sure to set the damage amount at the function below the globals
private constant boolean DEALS_DAMAGE = false
//Checks if the FJ deals damage after the duration, if yes be sure to set the damage amount at the function below the globals
private constant boolean DEALS_END_DAMAGE = true
/*
Checks if the duration of the buff will stack if a unit gets hit by SFJ while still
being affected by a previous one, if false, the new cast will overwrite the older one
*/
private constant boolean STACK_TIME = false
//The attack type of the primary damage
private constant attacktype ATPD = ATTACK_TYPE_NORMAL
//The damage type of the primary damage
private constant damagetype DTPD = DAMAGE_TYPE_NORMAL
//The attack type of the secondary damage
private constant attacktype ATSD = ATTACK_TYPE_NORMAL
//The damage type of the secondary damage
private constant damagetype DTSD = DAMAGE_TYPE_NORMAL
//DO NOT EDIT BELOW THIS LINE UP TO THE NEXT COMMENT
private string array FrostJokes //[8190]
private real array FrostChances //[8190]
private integer JokesTotal = 0
private group FJ_Group = CreateGroup()
private boolean array IsJokeShown
private hashtable SFJ_Hash = InitHashtable()
//END of DO NOT EDIT
endglobals
//Set the damage formula using this function
private function GetDamage takes integer level returns real
return 0.00
endfunction
//Set the end damage formula using this function
private function GetEndDamage takes integer level returns real
return 75.00*level
endfunction
//Set the buff duration using this function
private function GetDuration takes integer level returns real
return 5.00
endfunction
//Set the chance formula using this function
private function GetChance takes integer level, integer index returns real
return FrostChances[index]
endfunction
//Set The AoE formula using this function
private function GetAoE takes integer level returns real
return 250.00*level
endfunction
//The frost joke struct which handles most of the system's work
private struct FrostJokeStruct
unit target //the units hit by SFJ
unit caster //the caster of SFJ
integer abillevel //the ability level of SFJ for the caster
real timeleft //the timeleft for the duration of the buff
static thistype data
static unit TempUnit = null
static unit FiltUnit = null
static unit TempCaster = null
static real chance = 0.00
static integer joke
static integer level
static integer playern
static player controller
static integer id
/*
the periodic method for the buff duration, executed by T32
*/
private method periodic takes nothing returns nothing
set this.timeleft = this.timeleft - T32_PERIOD
if this.timeleft <= 0.00 then
call this.stopPeriodic()
call UnitRemoveAbility(this.target, FJ_BUFFPLACER_ID)
call UnitRemoveAbility(this.target, FJ_BUFF_ID)
call FlushChildHashtable(SFJ_Hash, GetHandleId(this.target))
if DEALS_END_DAMAGE then
call UnitDamageTarget(this.caster, this.target, GetEndDamage(this.abillevel), false, false, ATPD, DTPD, null)
endif
call this.destroy()
endif
endmethod
//implements the T32x module
implement T32x
/*
the method run when a unit gets hit by the SFJ
*/
static method create takes unit target, unit caster, integer level returns thistype
set thistype.id = GetHandleId(target)
set data = LoadInteger(SFJ_Hash, thistype.id, 0)
/*
if the unit is currently unaffected by a SFJ, a new instance is created
else we override the current one or increase the time depending
on the global STACK_TIME
*/
if data == 0 then
set data = thistype.allocate()
set data.target = target
set data.caster = caster
set data.abillevel = level
set data.timeleft = GetDuration(level)
set thistype.TempCaster = CreateUnit(GetOwningPlayer(caster), DUMMY_ID,GetUnitX(caster), GetUnitY(caster), 0.00)
call UnitApplyTimedLife(thistype.TempCaster, 'BTLF', .5)
call SetUnitExploded(thistype.TempCaster, true)
call UnitAddAbility(thistype.TempCaster, FJ_BUFFPLACER_ID)
call IssueTargetOrder(thistype.TempCaster, "soulburn", target)
call data.startPeriodic()
call SaveInteger(SFJ_Hash, thistype.id, 0, data)
else
set data.target = target
set data.caster = caster
set data.abillevel = level
if STACK_TIME then
set data.timeleft = data.timeleft + GetDuration(level)
else
set data.timeleft = GetDuration(level)
endif
endif
if DEALS_DAMAGE then
call UnitDamageTarget(caster, target, GetDamage(level), false, false, ATPD, DTPD, null)
endif
return data
endmethod
/*
this method checks if a unit will get affected by the SFJ or not
*/
static method FrostJokeGroupLoop takes nothing returns boolean
set thistype.FiltUnit = GetFilterUnit()
set thistype.controller = GetOwningPlayer(FiltUnit)
set thistype.playern = GetPlayerId(thistype.controller)
if not IsJokeShown[thistype.playern] then
set IsJokeShown[thistype.playern] = true
call DisplayTextToPlayer(thistype.controller, 0, 0, FrostJokes[thistype.joke])
endif
//checks if the unit is alive
if GetWidgetLife(FiltUnit) > .405 and /*
checks if the unit is an enemy
*/ IsPlayerEnemy(thistype.controller, GetOwningPlayer(thistype.TempUnit)) and /*
checks if the unit will be frozen
*/ GetRandomReal(0.00, 100.00) <= thistype.chance then
call FrostJokeStruct.create(FiltUnit, thistype.TempUnit ,thistype.level)
endif
return false
endmethod
/*
this method picks every unit around the caster of SFJ and passes them to the method above
*/
static method FrostJoke takes nothing returns boolean
local integer i = 0
set thistype.TempUnit = GetTriggerUnit()
set thistype.level = GetUnitAbilityLevel(thistype.TempUnit, FJ_SPELL_ID)
set thistype.joke = GetRandomInt(0, JokesTotal)
set thistype.chance = GetChance(thistype.level, thistype.joke)
if GetSpellAbilityId() == FJ_SPELL_ID then
call GroupEnumUnitsInRange(FJ_Group, GetUnitX(thistype.TempUnit), GetUnitY(thistype.TempUnit), GetAoE(thistype.level), Condition(function thistype.FrostJokeGroupLoop))
endif
loop
exitwhen i > 15
set IsJokeShown[i] = false
set i = i + 1
endloop
return false
endmethod
endstruct
//The init method of this library
private function Init takes nothing returns nothing
local integer i = 0
local trigger t = CreateTrigger()
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition(function FrostJokeStruct.FrostJoke))
set t = null
endfunction
//The function used to register jokes
function RegisterJokes takes string joke, real chance returns nothing
set FrostJokes[JokesTotal] = joke
set FrostChances[JokesTotal] = chance
set JokesTotal = JokesTotal + 1
endfunction
endlibrary