Flare
Stops copies me!
- Reaction score
- 662
Posted in a new thread because after asking Andrewgosu about it, he suggested I resubmit it completely
After about 3-4 days of lazy coding, I finally finished reconstructing my Black Arrow's JASS version (since, by my standards now, it was quite fail ) - so, without further ado, I present the new and improved
Why this version rules compared to the other one:
Importing guidelines
Code:
Screenshots (taken from GUI version, only difference in this version is the lack of custom modelfile)
Updates:
If you are interested in seeing the GUI version (don't be ), it can be found here
After about 3-4 days of lazy coding, I finally finished reconstructing my Black Arrow's JASS version (since, by my standards now, it was quite fail ) - so, without further ado, I present the new and improved
Black Arrow
The Ranger fires a dark arrow towards the target point. If an enemy is hit by the arrow, it will take damage and the arrow will store some damage. If an ally is hit, it will be healed and will store some healing. When the arrow reaches the target point, it explodes, healing allies for the accumulated healing and damaging enemies for the accumulated damageWhy this version rules compared to the other one:
- System-independant (for minimal importing requirements)
- No more dynamic triggers
- Much more efficient
- More configurable properties
- Abuse of colon syntax to make amusing code smilies
Importing guidelines
- Either copy the Black Arrow (new) trigger in the demo map into your map, or create a new trigger (name doesn't matter), convert to custom text, delete what is in that trigger, and copy the spell code into it
- Copy the ability Black Arrow
- Copy the unit Black Arrow
- Modify the constants DUMMYID and SPELLID so that they match the rawcodes of the ability and dummy (press Ctrl-D in Object Editor to view rawcodes)
- Modify any other constants/config functions as necessary
- Add the ability to a unit, and enjoy
Code:
JASS:
//------------------------------------------------------------------------------------\\
// Black Arrow \\
// by Flare \\
// Constructed using vJASS \\
//------------------------------------------------------------------------------------\\
scope BlackArrowNew initializer InitFunc
globals
//Raw code and SFX related globals
//------------------------------------------------------------------------------------\\
//Raw code of the base ability
private constant integer SPELLID = 039;A000039;
//Raw code of the dummy unit
private constant integer DUMMYID = 039;h000039;
//The SFX model used when an enemy is hit
private constant string DAMAGEFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
//The SFX model used when an ally is hit
private constant string HEALFX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
//The SFX model used on the end effect
private constant string ENDFX = "Abilities\\Spells\\Other\\HowlOfTerror\\HowlCaster.mdl"
//Floating text related globals
//------------------------------------------------------------------------------------\\
//Heal colour string
private constant string HEALSTRING = "|c0025D600"
//Damage colour string
private constant string DAMAGESTRING = "|c00710072"
//Globals that -SHOULD- be constant at all times (such as max distance, end damage/heal bonus per hit
//------------------------------------------------------------------------------------\\
//Should the ability heal the caster?
private constant boolean PROJHEALCASTER = false
private constant boolean ENDHEALCASTER = true
//Bonus to end heal/damage amount per unit hit
private constant real ENDBONUS = 10
//Distance travelled per second
private constant real SPEED = 800
//Timer interval
private constant real TIMERINTERVAL = 0.03
//Range in which unit is elligible to be hit by the arrow
private constant real DETECTRANGE = 100
//Damage related globals
//------------------------------------------------------------------------------------\\
//Boolean to determine whether impact damage/heal is constant or not
private constant boolean CONSTANTDAMAGE = false
//Constant values for impact damage/heal if above boolean is true
private constant real IMPACTDAMAGE = 25
private constant real IMPACTHEAL = 25
//Attack, damage, and weapon types for damage dealt
private constant attacktype AT = ATTACK_TYPE_MAGIC
private constant damagetype DT = DAMAGE_TYPE_NORMAL
private constant weapontype WT = null
endglobals
//This function is used to determine your own customised unit-type filter
//NOTE: SHOULD ONLY BE USED IF YOU KNOW WHAT YOU'RE DOING
//If, say, you want to exclude mechanical, structure, ethereal, etc. units
//Can be used for pretty much any filtering you want
//Ally/enemy filter are already covered, along with living filter, within the heart of the spell's code
private function GenericTypeFilter takes unit u returns boolean
return IsUnitType (GetFilterUnit (), UNIT_TYPE_STRUCTURE) == false and IsUnitType (GetFilterUnit (), UNIT_TYPE_MECHANICAL) == false
endfunction
//Varying damage
private function VariableDamage takes unit u returns real
local real base = 10
local real m1 = GetHeroInt (u, true)
local real m2 = 0.25
return base + m1 * m2
endfunction
//Varying heal
private function VariableHeal takes unit u returns real
local real base = 10
local real m1 = GetHeroInt (u, true)
local real m2 = 0.25
return base + m2 * m2
endfunction
//Varying end radius
private function VariableEndRadius takes unit u returns real
local real base = 150
local real m1 = GetUnitAbilityLevel (u, SPELLID)
local real m2 = 50
return base + m1 * m2
endfunction
//END OF CONFIGURATION\\
private keyword Data
globals
private timer T = CreateTimer ()
private integer N = 0
private Data array D
private Data g
private group Enum = CreateGroup ()
private boolexpr DamageFilter
private boolexpr TrueFilter
private boolexpr EndFilter
private real MaxX
private real MaxY
private real MinX
private real MinY
private constant real UNITS_PER_SEC = SPEED * TIMERINTERVAL
endglobals
private function SetUnitXY takes unit u, real x, real y returns nothing
if x > MaxX then
set x = MaxX
elseif x < MinX then
set x = MinX
endif
if y > MaxY then
set y = MaxY
elseif y < MinY then
set y = MinY
endif
call SetUnitX (u, x)
call SetUnitY (u, y)
endfunction
private struct Data
unit caster
unit dummy
real offsetcos
real offsetsin
real dmg
real heal
group prevhit
real dist
real healcount
real damagecount
method onDestroy takes nothing returns nothing
call GroupClear (.prevhit)
endmethod
endstruct
private function QuickTextTag takes string text, real x, real y, boolean dmgtext returns nothing
local texttag tt = CreateTextTag ()
call SetTextTagPos (tt, x, y, 0)
call SetTextTagPermanent (tt, false)
if dmgtext then
call SetTextTagText (tt, DAMAGESTRING + text + "|r", 0.023)
call SetTextTagVelocity (tt, Cos (bj_PI * 0.5) * 0.0355, Sin (bj_PI * 0.5) * 0.0355)
else
call SetTextTagText (tt, HEALSTRING + text + "|r", 0.023)
call SetTextTagVelocity (tt, Cos (bj_PI * 1.5) * 0.0355, Sin (bj_PI * 1.5) * 0.0355)
endif
call SetTextTagLifespan (tt, 3.)
call SetTextTagFadepoint (tt, 2.)
set tt = null
endfunction
private function DamageFunc takes nothing returns boolean
local unit u = GetFilterUnit ()
if IsUnitType (u, UNIT_TYPE_DEAD) == false and IsUnitInGroup (u, g.prevhit) == false and GenericTypeFilter (u) then
if IsUnitEnemy (u, GetOwningPlayer (g.caster)) then
set g.damagecount = g.damagecount + ENDBONUS
call UnitDamageTarget (g.caster, u, g.dmg, false, true, AT, DT, WT)
call GroupAddUnit (g.prevhit, u)
call DestroyEffect (AddSpecialEffect (DAMAGEFX, GetUnitX (u), GetUnitY (u)))
else
if u == g.caster and PROJHEALCASTER then
set g.healcount = g.healcount + ENDBONUS
call SetWidgetLife (u, GetWidgetLife (u) + g.heal)
call GroupAddUnit (g.prevhit, u)
call DestroyEffect (AddSpecialEffect (HEALFX, GetUnitX (u), GetUnitY (u)))
elseif u != g.caster then
set g.healcount = g.healcount + ENDBONUS
call SetWidgetLife (u, GetWidgetLife (u) + g.heal)
call GroupAddUnit (g.prevhit, u)
call DestroyEffect (AddSpecialEffect (HEALFX, GetUnitX (u), GetUnitY (u)))
endif
endif
endif
set u = null
return false
endfunction
private function EndFunc takes nothing returns boolean
local unit u = GetFilterUnit ()
if IsUnitType (u, UNIT_TYPE_DEAD) == false and GenericTypeFilter (u) then
if IsUnitEnemy (u, GetOwningPlayer (g.caster)) then
call UnitDamageTarget (g.caster, u, g.damagecount, false, true, AT, DT, WT)
else
if u == g.caster and ENDHEALCASTER then
set g.healcount = g.healcount + ENDBONUS
call SetWidgetLife (u, GetWidgetLife (u) + g.healcount)
elseif u != g.caster then
set g.healcount = g.healcount + ENDBONUS
call SetWidgetLife (u, GetWidgetLife (u) + g.healcount)
endif
endif
endif
set u = null
return false
endfunction
private function TimerCallback takes nothing returns nothing
local integer i = N
local Data a
local real x
local real y
loop
exitwhen i == 0
set a = i<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin :D" loading="lazy" data-shortname=":D" />
set x = GetUnitX (a.dummy)
set y = GetUnitY (a.dummy)
set x = x + a.offsetcos
set y = y + a.offsetsin
call SetUnitXY (a.dummy, x, y)
set g = a
call GroupEnumUnitsInRange (Enum, x, y, DETECTRANGE, DamageFilter)
set a.dist = a.dist - UNITS_PER_SEC
if a.dist <= 0 then
call KillUnit (a.dummy)
call GroupEnumUnitsInRange (Enum, x, y, VariableEndRadius (a.caster), EndFilter)
call DestroyEffect (AddSpecialEffect (ENDFX, x, y))
call QuickTextTag (I2S (R2I (a.healcount)) + "!", x, y, false)
call QuickTextTag (I2S (R2I (a.damagecount)) + "!", x, y, true)
call a.destroy ()
set i<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin :D" loading="lazy" data-shortname=":D" /> = N<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin :D" loading="lazy" data-shortname=":D" />
set N = N - 1
if N == 0 then
call PauseTimer (T)
endif
endif
set i = i - 1
endloop
endfunction
private function CondFunc takes nothing returns boolean
return GetSpellAbilityId () == SPELLID
endfunction
private function ActionFunc takes nothing returns nothing
local Data a = Data.create ()
local location l = GetSpellTargetLoc ()
local real tx = GetLocationX (l)
local real ty = GetLocationY (l)
local real cx
local real cy
local real dx
local real dy
local real angle
call RemoveLocation (l)
set l = null
set a.caster = GetTriggerUnit ()
set a.healcount = 0
set a.damagecount = 0
set cx = GetUnitX (a.caster)
set cy = GetUnitY (a.caster)
set dx = tx - cx
set dy = ty - cy
set a.dist = SquareRoot (dx*dx + dy*dy)
set a.dmg = VariableDamage (a.caster)
set a.heal = VariableHeal (a.caster)
set angle = Atan2 (dy, dx)
set a.dummy = CreateUnit (GetOwningPlayer (a.caster), DUMMYID, cx, cy, angle * bj_RADTODEG)
set a.offsetcos = Cos (angle) * (TIMERINTERVAL * SPEED)
set a.offsetsin = Sin (angle) * (TIMERINTERVAL * SPEED)
if a.prevhit == null then
set a.prevhit = CreateGroup ()
endif
if N == 0 then
call TimerStart (T, TIMERINTERVAL, true, function TimerCallback)
endif
set N = N + 1
set N<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite8" alt=":D" title="Big Grin :D" loading="lazy" data-shortname=":D" /> = a
endfunction
private function ReturnTrue takes nothing returns boolean
return true
endfunction
private function InitFunc takes nothing returns nothing
local trigger t = CreateTrigger ()
local integer i = 0
set MaxX = GetRectMaxX (bj_mapInitialPlayableArea)
set MaxY = GetRectMaxY (bj_mapInitialPlayableArea)
set MinX = GetRectMinX (bj_mapInitialPlayableArea)
set MinY = GetRectMinY (bj_mapInitialPlayableArea)
set DamageFilter = Condition (function DamageFunc)
set TrueFilter = Condition (function ReturnTrue)
set EndFilter = Condition (function EndFunc)
loop
exitwhen i == 16
call TriggerRegisterPlayerUnitEvent (t, Player (i), EVENT_PLAYER_UNIT_SPELL_EFFECT, TrueFilter)
set i = i + 1
endloop
call TriggerAddCondition (t, Condition (function CondFunc))
call TriggerAddAction (t, function ActionFunc)
endfunction
endscope
Screenshots (taken from GUI version, only difference in this version is the lack of custom modelfile)
Updates:
Code:
v1 - Initial release
v2 - Paused the timer when no instances running
v3 - Got rid of most of the floating text configuration (other than colour strings)
If you are interested in seeing the GUI version (don't be ), it can be found here