• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

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 45
Joined
Feb 27, 2007
Messages
5,578
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
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Some function is lacking when importing this to another map.
Incorrect. I copied everything over and it saved just fine. You probably forgot to enable JASSHelper in your map because those are disabled by default on all new maps (Trigger Editor > JassHelper > Enable JASSHelper & Enable vJASS)... or your map is currently set to Lua mode and can't use this JASS resource directly. (But you can transpile it.)
 
Level 6
Joined
Apr 15, 2016
Messages
118
Actually it's an error regarding redeclared functions in a Script that you wrote right here:

In the map's Custom Script section you will find these functions:
JASS:
function distance takes real x1, real x2, real y1, real y2 returns real
    return SquareRoot((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
endfunction

//determine wheather a point is inside the configured map bounds
function IsOutsideMap takes real x, real y returns boolean
    return x < udg_minX or y < udg_minY or x > udg_maxX or y > udg_maxY
endfunction

//If MeatHLockOn != null and MeatHIgnore == true, this function is called to filter the locked unit
function FilterLockedUnit takes nothing returns boolean
    return ( GetFilterUnit() == udg_MeatHLockOn[udg_MeatHRef] )
endfunction
You can either put them in the same place in your map or you can put it all inside a library in any normal JASS trigger:
JASS:
library MeatHookUtils
function distance takes real x1, real x2, real y1, real y2 returns real
    return SquareRoot((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
endfunction

//determine wheather a point is inside the configured map bounds
function IsOutsideMap takes real x, real y returns boolean
    return x < udg_minX or y < udg_minY or x > udg_maxX or y > udg_maxY
endfunction

//If MeatHLockOn != null and MeatHIgnore == true, this function is called to filter the locked unit
function FilterLockedUnit takes nothing returns boolean
    return ( GetFilterUnit() == udg_MeatHLockOn[udg_MeatHRef] )
endfunction
endlibrary
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
I'm still confused because this resource doesn't have anything in its Custom Script section and I got no redeclaration errors when I imported and saved all of the code from this resource.

When you have an issue like this please quote the actual error it says and show the line it points to. Without that information it's just a wild guess what is getting redeclared. The most logical thing I can think is that you already have one of these supporting libraries imported into your map for some other resource/reason, so that's why it's telling you that. It could also be that something in the script isn't properly encapuslated and is using a generic function name like Init without the private or public keyword.
 
Level 6
Joined
Apr 15, 2016
Messages
118
I'm still confused because this resource doesn't have anything in its Custom Script section and I got no redeclaration errors when I imported and saved all of the code from this resource.

When you have an issue like this please quote the actual error it says and show the line it points to. Without that information it's just a wild guess what is getting redeclared. The most logical thing I can think is that you already have one of these supporting libraries imported into your map for some other resource/reason, so that's why it's telling you that. It could also be that something in the script isn't properly encapuslated and is using a generic function name like Init without the private or public keyword.

The Post I quoted you is from Meat Hook System, where many users were having the same trouble regarding Function Distance and FilterLockUnit. The solution you gave there worked fine for the issue, but it now causes problem with one of Breathe Fires library. I believe it's a generic function name issue, since the Jass Troubleshooter tells "redeclared" function.

des.png

das.png
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
I see, thank you for the screenshot. The issue is not directly with this resource, but with the Meat Hook system because it used the exceptionally generic unencapsulated function name "distance". This resource requires the library Missile which in some places uses "distance" as a variable name, causing a namespace conflict.

I replied in the other thread explaining a full fix for the MeatHook System.
 
Top