• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Breath of Fire v1.4

  • Like
Reactions: IcemanBo
Breath%20of%20Fire.jpg



Spell CodeDescription
[jass=Breath of Fire]scope BreathOfFire//version 1.4 by kawas11
/******************************************************************************************************
*
* Requires: Missle, DelFX, TimerUtils, and IsUnitChanneling
* Optional: SpellEffectEvent, IsDestructableTree (if you want to check if destructables are trees)
*
*
*
* How to import:
* - Copy BoF trigger
* - Copy Libraries folder
* - Copy Universal Dummy
* - Copy Breath of Fire Ability
*
* Changelog:
* v1.0
* - First Release
* v1.1
* - Tooltip correction
* - Removed unused globals
* - Shorter code
* - Tree filter (IsDestructableTree)
* - onCast now returns boolean
* v1.2
* - Now using KillTree function from IsDestructableTree
* - Improved documentation
* - Structured trigger folders
* 1.3
* - Now based on channel ability
* - Removed unused tree_rect
* - Updated missle library
* - No longer requires CTL, now using TimerUtils
* - Fixed tooltip
* 1.4
* - method Destroy -> method destroy
* - Caster's animation speed correction
* - Fireballs will be now always removed in onRemove method independant of the user setting
*
*
*********************************************************************************************************/

/**************************************************************************************
* *
* CONFIGURATION *
* *
**************************************************************************************/

native UnitAlive takes unit id returns boolean

globals

//Breath of Fire ability rawcode
private constant integer SPELL_ID = 'A000'

//Cast time - time needed for animation
private constant real CAST_TIME = 1.00
//Caster animation speed in percent
private constant real CASTER_ANIMATION_SPEED = 60

//Total spell channel time (exclude CAST_TIME)
private constant real CHANNEL_TIME = 5.00

//Every X timer ticks (0.031250) spawn new fireball
private constant integer FIRE_CD = 3

//Attachment point for damage effect
private constant string DAMAGE_EFFECT_ATTACH = "chest"
//Damage effect
private constant string DAMAGE_EFFECT = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
//Fireballs remove effect
private constant string REMOVE_EFFECT = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
//Fireballs spawn effect
private constant string SPAWN_EFFECT = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
//Fireballs model
private constant string DUMMY_MODEL = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"
//Fireballs Scale in percent
private constant real DUMMY_SCALE = 120
//Fireballs height
private constant real DUMMY_HEIGHT = 70
//Fireballs move speed every 0.031250
private constant real DUMMY_SPEED = 25
//Fireballs collision size - used for units and destructables
private constant real DUMMY_COLISION = 75
//Distance between caster and fireballs spawn location
private constant real DUMMY_CREATE_DIST = 85

//Set minimum angle difference of caster's facing
private constant real MIN_ANGLE = 15
//Set maximum angle difference of caster's facing
private constant real MAX_ANGLE = 15

//Remove fireballs when collide with units and trees?
private constant boolean REMOVE = true
//Remove fireballs when collide with other destructables (not trees)?
private constant boolean REMOVE2 = true

//Destroy trees?
private constant boolean TREE = true

//Damage and attack type
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC

private constant real INTERVAL = 0.031250

endglobals

//Max distance traveled by fireballs
private constant function GetDistance takes integer level returns real
return 800.00 + 100.00 * level
endfunction

//Damage per fireball
private constant function GetDamage takes integer level returns real
return 5.00 + 5.00 * level
endfunction

//Which units should/shouldn't be damaged?
private function DamageFilter takes unit target, player owner returns boolean
return (not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)) and (not IsUnitType(target, UNIT_TYPE_STRUCTURE)) and (not IsUnitType(target, UNIT_TYPE_FLYING)) and (UnitAlive(target)) and (IsUnitEnemy(target, owner))
endfunction

/**************************************************************************************
* *
* CONFIGURATION END *
* *
**************************************************************************************/
private struct BoF

unit caster
player owner

integer cast
integer fireCD

real castTime
real distance
real targetX
real targetY
real channelTime
real damage

timer t

method destroy takes nothing returns nothing
call ReleaseTimer(t)

call SetUnitTimeScale(caster, 1)

set t = null
set caster = null
set owner = null

call deallocate()
endmethod

static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real casterX
local real casterY
local real createX
local real createY
local real angle
local Missile m

//Cast time
if castTime > 0 then
set castTime = castTime - INTERVAL

else

//No reason to set caster's animation sped multiple times
if cast == 1 then
call SetUnitTimeScale(caster, 0)
set cast = 0
endif

//Cooldown between creating new fireballs
if FIRE_CD == fireCD then
set casterX = GetUnitX(caster)
set casterY = GetUnitY(caster)

set angle = Atan2(targetY - casterY, targetX - casterX) + (GetRandomReal(-MIN_ANGLE, MAX_ANGLE) * bj_DEGTORAD)

set createX = casterX + DUMMY_CREATE_DIST * Cos(angle)
set createY = casterY + DUMMY_CREATE_DIST * Sin(angle)

//Spawn effect
call CreateDelayedEffectZ(SPAWN_EFFECT, createX, createY, DUMMY_HEIGHT, 0, 0)

//Fireball create and launch it
set m = Missile.create(createX, createY, DUMMY_HEIGHT, angle, distance, DUMMY_HEIGHT)
set m.source = caster
set m.owner = owner
set m.speed = DUMMY_SPEED
set m.damage = damage
set m.collision = DUMMY_COLISION
set m.scale = DUMMY_SCALE * 0.01
set m.model = DUMMY_MODEL
call thistype.launch(m)

//Reseting fireballs create cooldown
set fireCD = 0
endif
set fireCD = fireCD + 1
set channelTime = channelTime - INTERVAL
endif

if channelTime <= 0 or not IsUnitChanneling(caster) then
call destroy()
endif

endmethod

static method onCollide takes Missile missile, unit hit returns boolean
if DamageFilter(hit, missile.owner) then

call UnitDamageTarget(missile.source, hit, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, hit, DAMAGE_EFFECT_ATTACH))

endif

return REMOVE

endmethod

static method onRemove takes Missile missile returns boolean

call DestroyEffect(AddSpecialEffectTarget(REMOVE_EFFECT, missile.dummy, "origin"))
return true
endmethod

static method onDestructable takes Missile missile, destructable hit returns boolean
static if LIBRARY_IsDestructableTree and TREE then
call KillTree(hit)
endif

return REMOVE2
endmethod


implement MissileStruct

static method onCast takes nothing returns boolean
local BoF data = BoF.create()

local integer level

set data.t = NewTimerEx(data)

set data.caster = GetTriggerUnit()
set data.owner = GetTriggerPlayer()

set level = GetUnitAbilityLevel(data.caster, SPELL_ID)

set data.targetX = GetSpellTargetX()
set data.targetY = GetSpellTargetY()

set data.channelTime = CHANNEL_TIME
set data.castTime = CAST_TIME
set data.distance = GetDistance(level)
set data.damage = GetDamage(level)

set data.cast = 1
set data.fireCD = FIRE_CD

call SetUnitTimeScale(data.caster, CASTER_ANIMATION_SPEED * 0.01)

call TimerStart(data.t, INTERVAL, true, function BoF.onLoop)

return false
endmethod

static if not LIBRARY_SpellEffectEvent then
private static method condition takes nothing returns boolean
return (GetSpellAbilityId() == SPELL_ID) and thistype.onCast()
endmethod
endif

private static method onInit takes nothing returns nothing
static if LIBRARY_SpellEffectEvent then
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
else
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.condition))
set t = null
endif

//Preloading effects to prevent first use lag
call Preload(DUMMY_MODEL)
call Preload(SPAWN_EFFECT)
call Preload(REMOVE_EFFECT)
call Preload(DAMAGE_EFFECT)

endmethod

endstruct
endscope[/code]
Chen takes deep breath and release fireballs upon target area.
Level 1 - Each Fireball deals 10 Damage.
Level 2 - Each Fireball deals 15 Damage.
Level 3 - Each Fireball deals 20 Damage.
Cast time: 1 second
Channel time: 5 seconds

Requires:
Missle by BPower
TimerUtils by Vexorian
DelFX by Deaod
IsUnitChanneling By Magtheridon96
Optional:
SpellEffectEvent by Bribe
IsDestructableTree by BPower


v1.4
- method Destroy -> method destroy
- Caster's animation speed correction
- Fireballs will be now always removed in onRemove method independant of the user setting
*
v1.31
- Fixed bug where spell was ending too fast (removed else by accident)
v1.3
- Now based on channel ability
- Removed unused tree_rect
- Updated missle library
- No longer requires CTL, now using TimerUtils
- Fixed tooltip
v1.2
- Now using KillTree function from IsDestructableTree
- Improved documentation
- Structured trigger folders
v1.1
- Tooltip correction
- Remove unused globals
- Shorter code
- Tree filter (IsDestructableTree)
-onCast now returns boolean
v1.0
- First release


Keywords:
Breath, Fire, Chen, Channel
Contents

Breath of Fire v1.0 (Map)

Reviews
20:36, 6th Feb 2016 BPower: You made the changes I asked for. Approved. For me it's a bit confusing that you setting for some fields is in percent or in per 1/32 units. 18:00, 20th Jan 2016 BPower: Very promising spell. There are...

Moderator

M

Moderator

20:36, 6th Feb 2016
BPower: You made the changes I asked for. Approved.

For me it's a bit confusing that you setting for some fields is in percent
or in per 1/32 units.


18:00, 20th Jan 2016
BPower:

Very promising spell. There are some things I mentioned in the [self=http://www.hiveworkshop.com/forums/spells-569/breath-fire-v1-2-a-274265/#post2778742]comments[/self]

For now Need Fix.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
KillDestructable also works for all types of destructables. ( bridges, pathing blockers also ).
You should use a tree detection for public submitted spells.

The spell will not compile without SpellEffectEvent, because onCast returns currently nothing,
but method condition expect functions which return a boolean.

Overall good coding. A bit too many white spaces for my personal taste :/

I'm not a fan of the DelFX library, but of course it's ok to use it.
 
Level 5
Joined
Sep 16, 2008
Messages
47
Ok, co now onCast return boolean and when fireballs collide with destructable it will filter for trees but there is still options to kill fireballs when collide with other destructables.

I just realized that i can shorten code a bit and had unused globals like DUMMY_ID - lol.
 
Last edited:
The used IsDestructableTree system is by BPower, not PitzerMike. ;P
Also directky "function KillTree takes destructable tree returns boolean" can be used without any further checks on your side.

Could you structure the triggers folder a bit better :)? System itself is with all other libs, and then there is an other spell seperated.

For example, like speed, it should be noted that it's speed* 0.03125 actually, please. (like you do for Fire_CD)
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Uhhh didn't see it requires Missile :). I'm on it.

Review:

1.) Update to Missile v.2.0 it's much more powerful than v.1.4 which is in the demo map. :)

2.) It's a bit odd that the spell requires CTL and TimerUtils. It would be better to just use one.
Let me know if you need help with the data structure ( linked list and allocation ) in combination with TimerUtils.

3.) Use "Channel" as base ability. This offers you object editor fields like "follow through time".
Follow through time is the maximum channel time for the spell. Also you can set the animation string used for the spell.
Set the animation string to spell, slam. This brings you the effect you try to achieve using SetUnitAnimationByIndex.

4.) DelFX appears to be useless in my eyes, because you use a 0. delay. Instead destroy the effect on the missile dummy
and return true. Missile will keep the dummy at it's location for 2-3 seconds so the effect is displayed properly.

5.) I just saw after writing point 4, that you use DelFx also on missile creation. Try to destroy the fx directly on the missile dummy,
if that looks fishy then stay with DelFx.

6.) The tree rect should be removed.

It's fun to cast the spell. I like it.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
The setting for cast time seems not to fit perfectly to the panda channel animation.

JASS:
        static method onRemove takes Missile missile returns boolean
       
            call DestroyEffect(AddSpecialEffectTarget(REMOVE_EFFECT, missile.dummy, "origin"))           
            return REMOVE
        endmethod
This method should return true independant of the user setting.
onRemove boolean determines if Missile should keep the dummy a bit at this place
or recycle it right away.

method Destroy --> method destroy
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
2 things for moderators: this spell's code in the initial post is borked. I found an error with this spell's silly implementation of a global 'remove when hitting a unit or tree' flag. Right now the missile will end on any unit, not just units that match the flag. You need to replace this method:
JASS:
        static method onCollide takes Missile missile, unit hit returns boolean
            if DamageFilter(hit, missile.owner) then
      
                call UnitDamageTarget(missile.source, hit, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
                call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, hit, DAMAGE_EFFECT_ATTACH))
              
            endif
          
            return REMOVE
      
        endmethod
With this:
JASS:
        static method onCollide takes Missile missile, unit hit returns boolean
            if DamageFilter(hit, missile.owner) then
      
                call UnitDamageTarget(missile.source, hit, missile.damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
                call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, hit, DAMAGE_EFFECT_ATTACH))

                return REMOVE
            endif
          
            return false
      
        endmethod
 
Top