Fireblast
By kenny!
v3
By kenny!
v3
Another one of my first spells using vJass, i have worked on this skill for a bit longer then my last, so i hope everyone likes it and puts it to good use.
There is a description for implementation in the map, labeled "Implementation" just above the Fireblast trigger.
Import Difficulty: Easy-Moderate.
vJass?: Yes.
Lagless?: Yes.
Leakless?: Yes.
MUI?: Yes.
Requires:
- GTrigger by Jesus4Lyf.
- Dummy.mdx model by Vexorian.
- Ground and Water sliding effects by unknown.
- NewGen.
Description:
Basically, this skill creates a blast or wave of fire that surges forward for XXX distance. If the blast comes in contact with a unit it will drag the unit to its destination. If that unit is enemy it will be dealt damage.
Screenshots:
Tooltip:
Launches a deadly fireball at a target location, picking up any unit in its path and dragging it until the fireball reaches its destination.
Level 1 - Deals 75 damage, maximum range of 700.
Level 2 - Deals 150 damage, maximum range of 900.
Level 3 - Deals 225 damage, maximum range of 1100.
Script:
JASS:
//------------------------------------------------------------------------------------\\
// Fireblast [v3] \\
// By kenny! \\
// Constructed using vJASS \\
// Requires NewGen WE & GTrigger \\
//------------------------------------------------------------------------------------\\
scope Fireblast
globals
private constant integer ABIL_ID = 039;A000039; // Raw code for the spell.
private constant integer PROJ_ID = 039;n000039; // Raw code of the dummy unit needed.
private constant integer CROW_FORM = 039;Amrf039; // Raw code of the ability, Crow Form.
private constant real INTERVAL = 0.04 // Interval for periodic timer. 0.04 is recommended.
private constant real HEIGHT = 25.00 // Height of the dummy "missile" unit.
private constant real SCALE = 1.25 // Scale size for the same unit.
private constant real COLLISION = 90.00 // Radius in which units will be picked up.
private constant real TREE_RADIUS = 150.00 // Radius for destroying trees.
private constant real DISTANCE = 40.00 // Distance infront of the dummy unit at which pathing will be checked.
private constant boolean PLAY_START = true // Whether or not to play the start effect.
private constant string START_SFX = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" // The start effect.
private constant boolean PLAY_END = true // Whether or not to play the end effect.
private constant string END_SFX = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl" // The end effect.
private constant string PROJ_SFX = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl" // What the missile looks like.
private constant string PROJ_POINT = "origin" // Only attachment point given to the dummy.mdx model.
private constant string DIRT_SFX = "Dust.mdx" // Special effect for sliding on land. Import this if needed.
private constant string WATER_SFX = "SlideWater.mdx" // Special effect for sliding on water. Import this is needed.
private constant string SLIDE_POINT = "origin" // Attachment point for the above effects.
private constant attacktype A_TYPE = ATTACK_TYPE_CHAOS // Attacktype of damage dealt.
private constant damagetype D_TYPE = DAMAGE_TYPE_UNIVERSAL // Damagetype of damage dealt.
private constant weapontype W_TYPE = WEAPON_TYPE_WHOKNOWS // Weapontype of damage dealt.
private constant boolean DESTROY_TREES = true // Whether or not to destroy trees.
private constant boolean CHECK_PATHING = true // Whether or not to check pathing.
endglobals
//---------------------------------------------------------------------\\
private function Max_dist takes integer lvl returns real
return 500.00 + (200.00 * lvl) // Maximum distance of the spell per level.
endfunction
private function Damage_dealt takes integer lvl returns real
return 75.00 * lvl // Damage dealt by the spell per level.
endfunction
private function Movement_speed takes integer lvl returns real
return 500.00 + (100.00 * lvl) // Movement speed of the missile per level
endfunction
// Below: Filters out which units you want to be effected by the spell.
private function Enemy_filter takes unit filter, unit caster returns boolean
return filter != caster and GetWidgetLife(filter) > 0.405 and IsUnitType(filter,UNIT_TYPE_STRUCTURE) == false and IsUnitType(filter,UNIT_TYPE_MECHANICAL) == false and GetUnitFlyHeight(filter) <= 50.00
endfunction
//---------------------------------------------------------------------\\
// \\
// DO NOT TOUCH PAST THIS POINT UNLESS YOU KNOW WHAT YOUR ARE DOING! \\
// \\
//---------------------------------------------------------------------\\
private struct Data
unit cast = null
unit proj = null
unit targ = null
effect psfx = null
effect tsfx = null
real dist = 0.00
real move = 0.00
real cos = 0.00
real sin = 0.00
integer lvl = 0
integer mod = 0
static Data array D
static item array H
static integer D_total = 0
static integer H_total = 0
static real Game_maxX = 0.00
static real Game_minX = 0.00
static real Game_maxY = 0.00
static real Game_minY = 0.00
static timer Move_timer = null
static group Enum_group = null
static unit Tree_dummy = null
static item Path_item = null
static rect Path_rect = null
static boolexpr Tree_filt = null
static boolexpr Enemy_filt = null
static constant real MAX_RANGE = 10.00
static method safex takes real x returns real
if x < Data.Game_minX then
return Data.Game_minX
elseif x > Data.Game_maxX then
return Data.Game_maxX
endif
return x
endmethod
static method safey takes real y returns real
if y < Data.Game_minY then
return Data.Game_minY
elseif y > Data.Game_maxY then
return Data.Game_maxY
endif
return y
endmethod
static method filt takes nothing returns boolean
return true
endmethod
static method hide takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Data.H[Data.H_total] = GetEnumItem()
call SetItemVisible(Data.H[Data.H_total],false)
set Data.H_total = Data.H_total + 1
endif
endmethod
static method pathability takes real x1, real y1 returns boolean
local real x2 = 0.00
local real y2 = 0.00
call SetRect(Data.Path_rect,0.00,0.00,128.00,128.00)
call MoveRectTo(Data.Path_rect,x1,y1)
call EnumItemsInRect(Data.Path_rect,null,function Data.hide)
call SetItemPosition(Data.Path_item,x1,y1)
set x2 = GetItemX(Data.Path_item)
set y2 = GetItemY(Data.Path_item)
call SetItemVisible(Data.Path_item,false)
loop
exitwhen Data.H_total <= 0
set Data.H_total = Data.H_total - 1
call SetItemVisible(Data.H[Data.H_total],true)
set Data.H[Data.H_total] = null
endloop
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < Data.MAX_RANGE * Data.MAX_RANGE
endmethod
static method destroyenumtrees takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endmethod
static method tree_filter takes nothing returns boolean
local destructable dest = GetFilterDestructable()
local boolean result = false
if GetDestructableLife(dest) > 0.405 then
call ShowUnit(Data.Tree_dummy,true)
call SetUnitX(Data.Tree_dummy,GetWidgetX(dest))
call SetUnitY(Data.Tree_dummy,GetWidgetY(dest))
set result = IssueTargetOrder(Data.Tree_dummy,"harvest",dest)
call IssueImmediateOrder(Data.Tree_dummy,"stop")
call ShowUnit(Data.Tree_dummy,false)
set dest = null
return result
endif
set dest = null
return result
endmethod
method terrain takes nothing returns integer
local real x = GetUnitX(.targ)
local real y = GetUnitY(.targ)
if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
return 1
elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
return 2
endif
return 0
endmethod
method onDestroy takes nothing returns nothing
call DestroyEffect(.psfx)
if .tsfx != null then
call DestroyEffect(.tsfx)
endif
if .targ != null then
call SetUnitPathing(.targ,true)
endif
if PLAY_END then
call DestroyEffect(AddSpecialEffect(PROJ_SFX,GetUnitX(.proj),GetUnitY(.proj)))
endif
call RemoveUnit(.proj)
set .cast = null
set .proj = null
set .targ = null
set .psfx = null
set .tsfx = null
endmethod
method periodic takes nothing returns nothing
local real x = GetUnitX(.proj)
local real y = GetUnitY(.proj)
local real m = .mod
local unit u = null
if DESTROY_TREES then
call SetRect(Data.Path_rect,x - TREE_RADIUS,y - TREE_RADIUS,x + TREE_RADIUS,y + TREE_RADIUS)
call EnumDestructablesInRect(Data.Path_rect,Data.Tree_filt,function Data.destroyenumtrees)
endif
set x = Data.safex(x + .move * .cos)
set y = Data.safey(y + .move * .sin)
call SetUnitX(.proj,x)
call SetUnitY(.proj,y)
if .targ == null then
call GroupEnumUnitsInRange(Data.Enum_group,x,y,COLLISION,Data.Enemy_filt)
if FirstOfGroup(Data.Enum_group) != null then
loop
set u = FirstOfGroup(Data.Enum_group)
exitwhen u == null or .targ != null
call GroupRemoveUnit(Data.Enum_group,u)
if Enemy_filter(u,.cast) then
set .targ = u
endif
endloop
if .targ != null then
call SetUnitPathing(.targ,false)
if IsUnitEnemy(.targ,GetOwningPlayer(.cast)) then
call UnitDamageTarget(.cast,.targ,Damage_dealt(.lvl),false,false,A_TYPE,D_TYPE,W_TYPE)
endif
endif
endif
else
if GetWidgetLife(.targ) < 0.406 then
call DestroyEffect(.tsfx)
call SetUnitPathing(.targ,true)
set .targ = null
else
call SetUnitPosition(.targ,x,y)
call SetUnitPathing(.targ,false)
set .mod = this.terrain()
if .tsfx == null then
if .mod == 1 then
set .tsfx = AddSpecialEffectTarget(DIRT_SFX,.targ,SLIDE_POINT)
elseif .mod == 2 then
set .tsfx = AddSpecialEffectTarget(WATER_SFX,.targ,SLIDE_POINT)
endif
else
if .mod == 1 and m == 2 then
call DestroyEffect(.tsfx)
set .tsfx = AddSpecialEffectTarget(DIRT_SFX,.targ,SLIDE_POINT)
elseif .mod == 2 and m == 1 then
call DestroyEffect(.tsfx)
set .tsfx = AddSpecialEffectTarget(WATER_SFX,.targ,SLIDE_POINT)
endif
endif
endif
endif
set .dist = .dist + .move
set u = null
endmethod
static method update takes nothing returns nothing
local integer i = 1
local boolean b = false
loop
exitwhen i > .D_total
if CHECK_PATHING then
if Data.pathability(GetUnitX(.D<i>.proj) + DISTANCE * .D<i>.cos,GetUnitY(.D<i>.proj) + DISTANCE * .D<i>.sin) == false then
set b = true
endif
endif
if Data.D<i>.dist >= Max_dist(Data.D<i>.lvl) or b then
call Data.D<i>.destroy()
set Data.D<i> = Data.D[Data.D_total]
set Data.D_total = Data.D_total - 1
set i = i - 1
set b = false
else
call Data.D<i>.periodic()
endif
set i = i + 1
endloop
if Data.D_total <= 0 then
call PauseTimer(Data.Move_timer)
set Data.D_total = 0
endif
endmethod
static method actions takes nothing returns boolean
local Data d = Data.create()
local location loc = GetSpellTargetLoc()
local real locx = GetLocationX(loc)
local real locy = GetLocationY(loc)
local real castx = 0.00
local real casty = 0.00
local real angle = 0.00
set d.cast = GetTriggerUnit()
set castx = GetUnitX(d.cast)
set casty = GetUnitY(d.cast)
set angle = Atan2((locy - casty),(locx - castx))
set d.cos = Cos(angle)
set d.sin = Sin(angle)
set d.lvl = GetUnitAbilityLevel(d.cast,ABIL_ID)
set d.move = Movement_speed(d.lvl) * INTERVAL
set d.proj = CreateUnit(GetOwningPlayer(d.cast),PROJ_ID,castx,casty,(angle * bj_RADTODEG))
set d.psfx = AddSpecialEffectTarget(PROJ_SFX,d.proj,PROJ_POINT)
call SetUnitScale(d.proj,SCALE,SCALE,SCALE)
call UnitAddAbility(d.proj,CROW_FORM)
call UnitRemoveAbility(d.proj,CROW_FORM)
call SetUnitFlyHeight(d.proj,HEIGHT,0.00)
if PLAY_START then
call DestroyEffect(AddSpecialEffect(START_SFX,castx,casty))
endif
set Data.D_total = Data.D_total + 1
set Data.D[Data.D_total] = d
if Data.D_total == 1 then
call TimerStart(Data.Move_timer,INTERVAL,true,function Data.update)
endif
call RemoveLocation(loc)
set loc = null
return false
endmethod
static method onInit takes nothing returns nothing
set Data.Move_timer = CreateTimer()
set Data.Enum_group = CreateGroup()
set Data.Path_rect = Rect(0.00,0.00,1.00,1.00)
set Data.Tree_filt = Filter(function Data.tree_filter)
set Data.Enemy_filt = Filter(function Data.filt)
set Data.Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
set Data.Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
set Data.Game_minX = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
set Data.Game_minY = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
call GT_AddStartsEffectAction(function Data.actions,ABIL_ID)
set Data.Tree_dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),PROJ_ID,0.00,0.00,0.00)
call SetUnitPathing(Data.Tree_dummy,false)
call ShowUnit(Data.Tree_dummy,false)
set Data.Path_item = CreateItem(039;ciri039;,0.00,0.00)
call SetItemVisible(Data.Path_item,false)
endmethod
endstruct
endscope
</i></i></i></i></i></i></i></i></i>
Thanks to Tinki3 for the cool Test map and to everyone who helped me with the skill.
Hope everyone likes the spell and is able to give me some constructive feedback.
kenny!
CHANGELOG:
- 11th April, 08 - Updated code a bit, added more local reals as suggested by ~GaLs~. Also neatened up the notes in the code a bit.
- 17th April, 08 - Updated code some more, now properly checks for map boundaries, therefore reducing risk of fatal errors.
- 2nd May, 09 - Version 3 released. Updates include:
- Complete re-write of the script, from scratch. It is now a lot more efficient, and easier to modify, read and understand.
- Got rid of dynamic trigger usage. Switched to global groups and use of methods and stuff.
- Standardisation of script, again for readability and stuff.
- Most modifications to the spell can now be done in the trigger itself.
- No longer requires all those libraries. It only requires GTrigger (by Jesus4Lyf) now.
- However it now requires Vexorians dummy.mdx model, and two special effect models for sliding purposes. All of which can be found in the new map.