Cohadar
master of fugue
- Reaction score
- 209
Exloding Runes
(vJASSified)
By moyack und cohadar. 2009.
(vJASSified)
By moyack und cohadar. 2009.
A long time ago, I(moyack) did by request a spell for the project Tides of Darkness. Now I decided to vJASSify it and this is the result.
This spell resembles the one in WC2, used by the ogre magi units.
Requirements:
Spell Description:
It is an exploding thingy.
How to install:
- Open the test map.
- Copy the trigger and the ability used in this spell into you map.
- Copy ABC library to your map.
- Copy all rune units to your map and make sure their ID's are in sequential order like in demo map.
- Voila!! spell installed.
JASS:
//***************************************************************************************************************
//* *
//* Runes Spell (vJASSified). *
//* By Moyack and Cohadar *
//* V.3.1 *
//* *
//***************************************************************************************************************
//*
library Runes initializer init uses ABC
//***************************************************************************************************************
//* The constant data where you can modify the ability properties
//*
globals
// Returns the spell ID. Spell based on Channel
private constant integer SpellID = 039;A000039;
// These effects are loaded from Art-Special field on spell
private string Effect_Ground
private string Effect_Explosion
endglobals
//===========================================================================
// Rune dummy unit id's must be in sequential order, for example:
// 'o0R1', 'o0R2', 'o0R3', 'o0R4', 'o0R5', 'o0R6'
// Check object editor data for details
//===========================================================================
private constant function GetRuneId takes integer variation returns integer
return 039;o0R0039; + variation
endfunction
private constant function DetectionRange takes integer lvl returns real
return 120. + 50. * (lvl - 1) // Returns the range where the runes will detect units
endfunction
private constant function ExplosionRange takes real detection_range returns real
return 1.2 * detection_range // explosion range is a bit bigger than detection range
endfunction
private constant function Radius takes integer lvl returns real
return 2 * DetectionRange(lvl) // Returns the radius where the runes will be placed
endfunction
private constant function Amount takes integer lvl returns integer
return 4 + 1 * (lvl - 1) // Returns the number of runes at the perimeter
endfunction
private constant function Damage takes integer lvl returns real
return 350. + 50. * (lvl - 1) // Returns the damage dealt to units near to the runes
endfunction
private constant function Duration takes integer lvl returns real
return 40. + 5. * (lvl - 1) // Returns the runes timed life
endfunction
private constant function Scale takes integer lvl returns real
return 1.2 + 0.3 * (lvl - 1) // Returns the rune scale
endfunction
//***************************************************************************************************************
//* end constant data...
//*
globals
private group G = CreateGroup()
endglobals
//===========================================================================
private function DisplayDamage takes real x, real y, real dmg returns nothing
local texttag tt = CreateTextTag()
local string text = I2S(R2I(dmg)) + "!"
call SetTextTagText(tt, text, 0.024)
call SetTextTagPos(tt, x, y, 0.0)
call SetTextTagColor(tt, 255, 0, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
//set text = null
set tt = null
endfunction
//===========================================================================
struct RuneData
unit caster
unit rune
trigger proximity_trig
triggeraction action // used for cleaning up later
real damage
real detection_range
real x
real y
static RuneData THIS // for passing data to ExplosionEnum
//===========================================================================
private static method ExplosionEnum takes nothing returns nothing
if GetWidgetLife(RuneData.THIS.caster) > 0.405 then
call UnitDamageTarget(RuneData.THIS.caster, GetEnumUnit(), RuneData.THIS.damage, false, false, ATTACK_TYPE_SIEGE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_ROCK_HEAVY_BASH)
else
call UnitDamageTarget(RuneData.THIS.rune, GetEnumUnit(), RuneData.THIS.damage, false, false, ATTACK_TYPE_SIEGE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_ROCK_HEAVY_BASH)
endif
endmethod
//===========================================================================
// When ground unit walks over rune it is time for action
//===========================================================================
private static method RuneUnitFilter takes nothing returns boolean
if GetWidgetLife(GetFilterUnit()) > 0.405 then
if IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) == false then
return true
endif
endif
return false
endmethod
//===========================================================================
private static method RuneActions takes nothing returns nothing
local RuneData data = GetTriggerStructA(GetTriggeringTrigger()) // ABC
set RuneData.THIS = data
call GroupEnumUnitsInRange(G, data.x, data.y, 2*ExplosionRange(data.detection_range), Condition(function RuneData.RuneUnitFilter))
call ForGroup(G, function RuneData.ExplosionEnum)
call GroupClear(G)
call DisplayDamage(data.x, data.y, data.damage)
call data.destroy()
endmethod
//===========================================================================
static method create takes unit caster, real x, real y , integer level, boolean central returns RuneData
local RuneData data = RuneData.allocate()
local real scale = Scale(level)
set data.x = x
set data.y = y
set data.caster = caster
set data.proximity_trig = CreateTrigger()
set data.detection_range = DetectionRange(level)
set data.action = TriggerAddAction(data.proximity_trig, function RuneData.RuneActions)
call SetTriggerStructA(data.proximity_trig, data) // ABC trigger->data
// runes 1-2 are central runes
// runes 3-6 are perimiter runes
// Central runes have bigger model and deal double damage
if central then
set data.rune = CreateUnit(GetOwningPlayer(caster), GetRuneId(GetRandomInt(1,2)), x, y, 0)
set data.damage = Damage(level)*2
else
set data.rune = CreateUnit(GetOwningPlayer(caster), GetRuneId(GetRandomInt(3,6)), x, y, 0)
set data.damage = Damage(level)
endif
call SetUnitScale(data.rune, scale, scale, scale)
call UnitAddAbility(data.rune, 039;Agho039;) // ghost (invisibility)
call UnitAddAbility(data.rune, 039;Avul039;) // invulnerable
call UnitApplyTimedLife(data.rune, 039;BTLF039;, Duration(level))
call TriggerRegisterUnitInRange(data.proximity_trig, data.rune, data.detection_range, Filter( function RuneData.RuneUnitFilter ))
return data
endmethod
//===========================================================================
method onDestroy takes nothing returns nothing
call DestroyEffect(AddSpecialEffect(Effect_Ground, .x, .y))
call DestroyEffect(AddSpecialEffect(Effect_Explosion, .x, .y))
call DisableTrigger(this.proximity_trig)
call TriggerRemoveAction(this.proximity_trig, this.action)
call ClearTriggerStructA(this.proximity_trig) // ABC
call DestroyTrigger(this.proximity_trig)
call KillUnit(this.rune)
call ShowUnit(this.rune, false)
endmethod
endstruct
//***************************************************************************************************************
//* *
//* Runes Casting Functions *
//* *
//***************************************************************************************************************
//===========================================================================
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == SpellID
endfunction
//===========================================================================
private function Actions takes nothing returns nothing
local location targetLoc = GetSpellTargetLoc()
local real lx = GetLocationX(targetLoc)
local real ly = GetLocationY(targetLoc)
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SpellID)
local real fc = GetUnitFacing(GetTriggerUnit()) * bj_DEGTORAD
local real a = Amount(level)
local real R = Radius(level)
local real angle = 2 * bj_PI / a
local integer count = 0
local real x
local real y
call RemoveLocation(targetLoc)
set targetLoc = null
call RuneData.create(GetTriggerUnit(), lx, ly , level, true)
loop
exitwhen count == a
set x = lx + R * Cos(fc + count * angle)
set y = ly + R * Sin(fc + count * angle)
call RuneData.create(GetTriggerUnit(), x, y , level, false)
set count = count + 1
endloop
endfunction
//===========================================================================
private function GetFX takes integer id returns string
return GetAbilityEffectById(SpellID, EFFECT_TYPE_SPECIAL, id)
endfunction
//===========================================================================
private function init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( trig, Condition( function Conditions ) )
call TriggerAddAction( trig, function Actions )
set Effect_Ground = GetFX(0)
set Effect_Explosion = GetFX(1)
call Preload(Effect_Ground)
call Preload(Effect_Explosion)
endfunction
endlibrary
Changelog:
- 2.0: Initial Release
- 2.1: Improved the killing credits and performance a little bit more.
- 2.2: Simplified the code a little bit in the Grouping units, and if the caster is alive in that moment, the killing credit goes to him. Added to the resurrection some eyecandy
- 3.0: (Cohadar) Added new rune models. Central rune now deals double damage. Runes are now invisible and can be detected by true sight. No more perma timers but spell now requires ABC.
- 3.1: Added timed life to runes.
As to avoid confusion:
Moyack ran out of time so I(Cohadar) offered him to complete this spell according to my suggestions and resubmit it.
He agreed.
He agreed.