W!†A_cRaft
Ultra Cool Member
- Reaction score
- 28
A LITTLE BIT OF HISTORY:
I have been searching for a similar system everywhere for quite some time, maybe I just FAIL at searching but I could't find one, there were many systems
that handle channeling abilities and even have casting bars (which i personally don't like since they look ugly) but still haven't seen a single one that covers the CASTING of spells with a delayed effect, but making it so that it can be
stopped like any channeling spell can (stun, move, death etc).
Basically because it cannot be done in a non-Jass version (or at least i don't know how to do it) since the editor cannot get the "Target" of the ability on the even "Unit finishes casting an ability", since the ability is already finished and it's effect should now be triggered but the target is lost so you have to use global variables to store everything, and that makes it a non-MUI, and well we want to avoid that.
==========================================================================================
What to expect?
Expect castable spells that are affected by the things mentioned below.
What do I mean when I say castable spells?
Spells that have a certain casting time, but not a classic casting time that the world editor allows us to set. Rather a more complex casting.
World editor allows us to make a "casting" that delays the execution of the ability in both cooldown and mana, and if stunned during the "casting" they can be recast again with no lose whatsoever, these spells however cannot.
==========================================================================================
They can be:
interrupted - can be interrupted by moving, getting stunned, dying, etc.
line of sight avoided - can avoided if the caster loses the target out of his sight during the casting of the ability, either by really fast movement, or jump/blink triggers behind the caster, or even sideways. Yes, caster is slowly following the target's movement by his facing.
invisibility avoided - If the target turns invisible the spell is interrupted.
out of range avoided - If the target moves away from the ability range, the spell is interrupted. It is also possible to configure how much out of range is allowed to still make the cast.
delayed by direct damage - if you are either attacked or damaged directly (by a spell or physical ability) your casting progress is reduced, in other words it will take longer for a spell to go into the effect. Requires pre-configuring for every spell.
casting progress hastened and slowed by buffs - Allows the buffs that increase/decrease the speed at which abilities are being cast, for example bloodlust that increases casting speed, or slow that decreases it.
========================================
KEEP IN MIND
========================================
The whole system is unit target based, if you want a point targeted spell you can either modify the code to be point oriented, or use only some part of the trigger because the "point target" cannot run away (so out of range is needless), cannot go behind your back (line of sight is needless), cannot go invisible (so visibility is needless), so all the these things should be canceled in that specific trigger, which leaves us with only hasting/slowing of cast progress (which is not target oriented) and spellcast delaying which is again not target oriented.
In other words deleting the parts of the code and making the spell point castable will make the code work.
==========================================================================================
ABOUT THE CODE
The whole system was made by me assisted greatly by my brother.
Code itself is a little bit of complex, I would recommend that you know at least the basic of any programming language if you want to make it work.
Whole code however is in a single trigger, except the delayed attack, but they work on their own so it is still MUI.
Also I would like to say that this is the first thing we've done in JASS so you shouldn't expect it to be really great, however there is no similar system currently anywhere so I thought we could share it with the community of TheHelper.net
Used Vexorian's Handle Variables Functions which i edited in order for them to be compatible with 1.24 (god i hate that patch)
Special thanks to
Exide - for helping me be able to start my WC3 again...
Ivach - for help around Order strings
Rainther - for pointing out what my wc3 problem is, and for helping me understand how do Game - Text Messages work in Jass (testing would be impossible without these)
Komaqtion - for effort (he tried understanding my angle problem, I apparently didnt describe it well enough)
Here is the code for trigger #1
One thing you should know before going further, as explained below, the system works by giving you a base trigger code for your spell that you should put in your trigger,
since you cannot have 2 triggers of the same name you will be forced to change certain parts of the code (function names to be PRECISE) in order for it to work on different spells, beacuse there can only be 1 function with a same name.
I have solved my problem by adding a tag such as _001, _002, _003 to every function in the trigger
Also dont forget to change the fields of the 2 lowest functions in order to match your trigger:
the
change the name here.(check the
I have been searching for a similar system everywhere for quite some time, maybe I just FAIL at searching but I could't find one, there were many systems
that handle channeling abilities and even have casting bars (which i personally don't like since they look ugly) but still haven't seen a single one that covers the CASTING of spells with a delayed effect, but making it so that it can be
stopped like any channeling spell can (stun, move, death etc).
Basically because it cannot be done in a non-Jass version (or at least i don't know how to do it) since the editor cannot get the "Target" of the ability on the even "Unit finishes casting an ability", since the ability is already finished and it's effect should now be triggered but the target is lost so you have to use global variables to store everything, and that makes it a non-MUI, and well we want to avoid that.
==========================================================================================
What to expect?
Expect castable spells that are affected by the things mentioned below.
What do I mean when I say castable spells?
Spells that have a certain casting time, but not a classic casting time that the world editor allows us to set. Rather a more complex casting.
World editor allows us to make a "casting" that delays the execution of the ability in both cooldown and mana, and if stunned during the "casting" they can be recast again with no lose whatsoever, these spells however cannot.
==========================================================================================
They can be:
interrupted - can be interrupted by moving, getting stunned, dying, etc.
line of sight avoided - can avoided if the caster loses the target out of his sight during the casting of the ability, either by really fast movement, or jump/blink triggers behind the caster, or even sideways. Yes, caster is slowly following the target's movement by his facing.
invisibility avoided - If the target turns invisible the spell is interrupted.
out of range avoided - If the target moves away from the ability range, the spell is interrupted. It is also possible to configure how much out of range is allowed to still make the cast.
delayed by direct damage - if you are either attacked or damaged directly (by a spell or physical ability) your casting progress is reduced, in other words it will take longer for a spell to go into the effect. Requires pre-configuring for every spell.
casting progress hastened and slowed by buffs - Allows the buffs that increase/decrease the speed at which abilities are being cast, for example bloodlust that increases casting speed, or slow that decreases it.
========================================
KEEP IN MIND
========================================
The whole system is unit target based, if you want a point targeted spell you can either modify the code to be point oriented, or use only some part of the trigger because the "point target" cannot run away (so out of range is needless), cannot go behind your back (line of sight is needless), cannot go invisible (so visibility is needless), so all the these things should be canceled in that specific trigger, which leaves us with only hasting/slowing of cast progress (which is not target oriented) and spellcast delaying which is again not target oriented.
In other words deleting the parts of the code and making the spell point castable will make the code work.
==========================================================================================
ABOUT THE CODE
The whole system was made by me assisted greatly by my brother.
Code itself is a little bit of complex, I would recommend that you know at least the basic of any programming language if you want to make it work.
Whole code however is in a single trigger, except the delayed attack, but they work on their own so it is still MUI.
Also I would like to say that this is the first thing we've done in JASS so you shouldn't expect it to be really great, however there is no similar system currently anywhere so I thought we could share it with the community of TheHelper.net
Used Vexorian's Handle Variables Functions which i edited in order for them to be compatible with 1.24 (god i hate that patch)
Special thanks to
Exide - for helping me be able to start my WC3 again...
Ivach - for help around Order strings
Rainther - for pointing out what my wc3 problem is, and for helping me understand how do Game - Text Messages work in Jass (testing would be impossible without these)
Komaqtion - for effort (he tried understanding my angle problem, I apparently didnt describe it well enough)
Here is the code for trigger #1
One thing you should know before going further, as explained below, the system works by giving you a base trigger code for your spell that you should put in your trigger,
since you cannot have 2 triggers of the same name you will be forced to change certain parts of the code (function names to be PRECISE) in order for it to work on different spells, beacuse there can only be 1 function with a same name.
I have solved my problem by adding a tag such as _001, _002, _003 to every function in the trigger
Also dont forget to change the fields of the 2 lowest functions in order to match your trigger:
the
change the name here.(check the
Code:
)
and
[jass]function InitTrig_Casting_Spell takes nothing returns nothing
set gg_trg_Casting_Spell = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Casting_Spell, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_Casting_Spell, Condition( function Trig_spell_Conditions ) )
call TriggerAddAction( gg_trg_Casting_Spell, function Casting )
endfunction[/jass]
I dont know does this happen on its own when pasting the code, when copy-pasting the trigger these changes are done automatically
[code]
in this part you must change "Casting_Spell" to your trigger name
and Trig-spell_Conditions to the name you set in the previous part, your condition function
JASS:
//* The map itself contains a detailed description of every single function
//* every constant and everything that you should/could change.
//* -----------------------------------------------------------------------
//* =================================================
//* >>>>>>KEEP IN MIND<<<<<<
//* =================================================
//* For every castable spell you make, you must make
//* a new trigger that is an exact copy of this one
//* (casting spell) and than in the field desinged
//* for it make your actual spell, think of this as a
//* background mechanic for your spell, there is a
//* place where you should make your spell and all of
//* it's effect, described lower
//*
//* ALSO, do not forget, you spell should be based
//* on channel, and have follow throught time set to
//* 99999 to make it infinite, also it must not prevent
//* other abilities from beign used. if you make a NON
//* unti target spell make sure to change the trigger,
//* in order to not get errors.
//* =================================================
//* =================================================
//*
//* How to implement:
//* 1. Copy-Paste the code from the map header (spell system.w3x) to your map header
//* 2. Create an empty trigger named Casting Spell, select it, and do Edit - > Convert to Custom Text
//* 3. Copy the Code from below into the trigger
//* 4. Create another empty trigger named Attack Delay, select it, and do Edit - > Convert to Custom Text
//* 5. Copy the code from the next
<div class="bbCodeBlock bbCodeBlock--screenLimited bbCodeBlock--code">
<div class="bbCodeBlock-title">
Code:
</div>
<div class="bbCodeBlock-content" dir="ltr">
<pre class="bbCodeCode" dir="ltr" data-xf-init="code-block" data-lang=""><code> bracket in this thread.
//* 6. Enjoy your castable spells
//*
//*
//*
//*
//*==========================
//* Changeable Data Functions:
//*==========================
//*
//* function sightrange takes nothing returns real
//*range of an angle which the Line of Sight will use (recommended: 60)
//*
//* function rawcode takes nothing returns integer
//*Replace Bblo with the Rawcode of your buff, in my case it is suppose to increase casting speed by 100%
//*
//* function castrateset takes nothing returns real
//*Rate at which the casting progress builds up, rate is 1 for every 0.1 seconds, this is used in order to allow multiplication of the casting speed
//*
//* function extendedrange takes nothing returns real
//*Extended range after which the casting will be interrupted, modifi in order to determin to what extreme will the casting be allowed
//*
//* function castduration takes nothing returns real
//*Casting duration, or in other words how many tics will be needed in order for spel lto be completed, default rate is 1 per 0.1 second cast
//*
//* function castinterrupting takes nothing returns real
//*When a target casting an ability is hit by this spell his casting progress will be reduced by 20 points, or 2 seconds (@rate 1 per 0.1 sec)
//*
//* function spellid takes nothing returns integer
//*Replace the A000 with the Rawcode of your spell.
//*
//* function orderchecker takes nothing returns string
//*Used to determin what Spell Order Id is the game using, replace the "curse" with the Spell Id order your spell uses
//*
//===========================================================================
//#######################################
//---------------------------------------
//>>>>>>>>>>>> TRIGGER CODE<<<<<<<<<<<<<<
//---------------------------------------
//#######################################
function sightrange takes nothing returns real
return 60.00
endfunction
function rawcode takes nothing returns integer
return 039;Bblo039;
endfunction
function spellid takes nothing returns integer
return 039;A000039;
endfunction
function orderchecker takes nothing returns string
return "curse"
endfunction
function castrateset takes nothing returns real
return 1.00
endfunction
function extendedrange takes nothing returns real
return 900.00
endfunction
function castduration takes nothing returns real
return 25.00
endfunction
function castinterrupting takes nothing returns real
return 20.00
endfunction
function CheckOrderCast takes unit u returns boolean
if ( not ( GetUnitCurrentOrder(u) == String2OrderIdBJ(orderchecker()) ) ) then
return false
endif
return true
endfunction
function LoS takes unit u, real angbp, real ang returns nothing
local real a
local real b
set a = ang + sightrange()
set b = ang - sightrange()
if(GetBooleanAnd((ang>=sightrange()),(ang<=(360-sightrange()))))then
if(GetBooleanAnd((angbp>=b),(angbp<=a)))then
else
call IssueImmediateOrder(u, "stop")
endif
else
if(a > 360)then
set a = a - 360
if(GetBooleanOr(GetBooleanAnd((angbp<=a),(angbp>=0)),GetBooleanAnd((angbp>=b),(angbp<=360))))then
else
call IssueImmediateOrder(u, "stop")
endif
else
endif
if(b < 0) then
set b = b + 360
if(GetBooleanOr(GetBooleanAnd((angbp>=b),(angbp<=360)),GetBooleanAnd((angbp>=0),(angbp<=a))))then
else
call IssueImmediateOrder(u, "stop")
endif
else
endif
endif
endfunction
function BuffCheck takes unit u returns boolean
if ( not ( UnitHasBuffBJ(u, rawcode) == true ) ) then
return false
endif
return true
endfunction
function visibility takes unit u, unit t returns boolean
if ( not ( IsUnitInvisible(t, GetOwningPlayer(u)) == true ) ) then
return false
endif
return true
endfunction
function CastingCheck takes nothing returns nothing
local timer cast = GetExpiredTimer()
local unit u = H2U(GetHandleHandle(cast, "u"))
local unit t = H2U(GetHandleHandle(cast, "t"))
local real r = GetHandleReal(u, "r")
local real tr = GetHandleReal(t, "r")
local effect fx
local real castrate
local real distance = DistanceBetweenPoints(GetUnitLoc(u),GetUnitLoc(t))
local real angle = GetUnitFacing(u)
local real angbp = AngleBetweenPoints(GetUnitLoc(u),GetUnitLoc(t))
set castrate = castrateset()
if (angbp < 0) then
set angbp = angbp + 360
else
endif
call SetUnitFacingToFaceUnitTimed(u,t, 0.19)
if ( r < 0 ) then
set r = 0
else
endif
call LoS(u, angbp, angle)
if (distance >= extendedrange())then
call IssueImmediateOrderBJ(u, "stop")
else
endif
if (visibility(u,t))then
call IssueImmediateOrderBJ(u, "stop")
else
endif
//------------------------------------------------------------------------------------------------------------------------
//For every specific buff that interferes with casting speed you must make a seperate if/then/else check
//---------- if (BuffCheck(u))then
//---------- set castrate = castrate * 2
//---------- else
//---------- endif
//------------------------------------------------------------------------------------------------------------------------
if ( CheckOrderCast(u) ) then
if (BuffCheck(u))then
set castrate = castrate * 2
else
endif
set r = r + castrate
call SetHandleReal(u, "r", r)
if ( r >= castduration()) then
set fx = AddSpecialEffectTargetUnitBJ( "overhead", t, "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl" )
//-------------------------------------------------------------------
//You can here add the next few lines of code in order to make this spell reduce the cast of spell casted by it's target.
//-------------------------------------------------------------------
if (tr == 0) then
else
set tr = tr - castinterrupting()
call SetHandleReal(t,"r", tr)
endif
//=======================================================
// THIS IS WHERE YOUR SPELLCODE GOES //
//=======================================================
// keep in mind that in this spellsystem I only passed
// the target and the caster throught the timer/cache
// if you (and i know you do) need any other data, such
// as spell level, damage, mana, hp, or anything else
// you must modify the passing function below
//========================================================
call IssueImmediateOrderBJ(u, "stop")
call DestroyEffect(fx)
call PauseTimer(cast)
call FlushHandleLocals(cast)
call FlushHandleLocals(u)
call DestroyTimer(cast)
set u = null
else
endif
else
call PauseTimer(cast)
call FlushHandleLocals(cast)
call FlushHandleLocals(u)
call DestroyTimer(cast)
set u = null
endif
endfunction
function Casting takes nothing returns nothing
local unit u = GetSpellAbilityUnit()
local unit t = GetSpellTargetUnit()
local timer cast = CreateTimer()
//==========================================//
// PASSING FUNCTION, or rather par of it //
// pass everything you need for your spell //
// in the similar way the I passed the units//
//==========================================//
call SetHandleHandle(cast, "u", u)
call SetHandleHandle(cast, "t", t)
call SetUnitFacingToFaceUnitTimed(u,t, 0.1)
call TimerStart(cast, 0.1, true, function CastingCheck)
endfunction
//---------------------------------------------------------------------------
function Trig_spell_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == spellid() ) ) then
return false
endif
return true
endfunction
//===========================================================================
function InitTrig_codepasting takes nothing returns nothing
set gg_trg_codepasting = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_codepasting, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_codepasting, Condition( function Trig_spell_Conditions ) )
call TriggerAddAction( gg_trg_codepasting, function Casting )
endfunction
[/JASS][/spoiler]
[B][SIZE="4"]Here is the code for trigger #2:[/SIZE][/B]
[spoiler][JASS]function AttackDelay takes nothing returns nothing
local unit u = GetAttackedUnitBJ()
local real r = GetHandleReal(u, "r")
if (not(r == 0)) then //checks if the target is CURRENTLY casting a spell
set r = r - 5 //reduces the casting progress by 0.5 seconds (rate is 1 for every 0.1 sec)
call SetHandleReal(u, "r", r) //stores the modified value of the progress real
else
endif
endfunction
//Keep in mind that you can also make the abilities reduce the casting progress in the same way
//===========================================================================
function InitTrig_Attack_delay takes nothing returns nothing
set gg_trg_Attack_delay = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Attack_delay, EVENT_PLAYER_UNIT_ATTACKED )
call TriggerAddAction( gg_trg_Attack_delay, function AttackDelay )
endfunction
[/JASS][/spoiler]
[B][SIZE="4"]Map Header[/SIZE][/B]
Vexorian039;s Variables Handles, modified by me in order to be compatible with 1.24 patch
[spoiler][JASS]//**************************************************************************************************
//*
//* Handle Variables Functions by Vexorian (http://jass.sourceforge.net/doc/)
//*
//* -------------------------------
//* Enables usage of game cache for
//* transphering local variables
//* beetveen functions.
//* -------------------------------
//*
//* Requires:
//* ¯¯¯¯¯¯¯¯¯
//* A global variable with name "cache" and type "GameCache" as an exact match
//* note that the "LocalVars" function must be placed before anything in this
//* map header expect this kind of comments
//**************************************************************************************************
//
//* --------------------------------
//*
//* Edited by W!tA_cRaft & Noemis in order to be compatiable with 1.24 patch
//*
//* Thanks to the community of TheHelper.net for help on how to edit the code
//*
//* --------------------------------
function LocalVars takes nothing returns gamecache
if udg_cache==null then
set udg_cache=InitGameCache("cache")
endif
return udg_cache
endfunction
function H2I takes handle h returns integer
return GetHandleId(h)
endfunction
function H2U takes handle h returns unit
return h
call DoNothing()
return null
endfunction
function SetHandleInt takes handle subject, string name, integer value returns nothing
if value==0 then
call FlushStoredInteger(LocalVars(),I2S(GetHandleId(subject)),name)
else
call StoreInteger(LocalVars(), I2S(GetHandleId(subject)), name, value)
endif
endfunction
function SetHandleReal takes handle subject, string name, real value returns nothing
if value==0 then
call FlushStoredReal(LocalVars(), I2S(GetHandleId(subject)), name)
else
call StoreReal(LocalVars(), I2S(GetHandleId(subject)), name, value)
endif
endfunction
function SetHandleHandle takes handle subject, string name, handle value returns nothing
call SetHandleInt( subject, name, GetHandleId(value) )
endfunction
function GetHandleInt takes handle subject, string name returns integer
return GetStoredInteger(LocalVars(), I2S(GetHandleId(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
return GetStoredReal(LocalVars(), I2S(GetHandleId(subject)), name)
endfunction
function GetHandleHandle takes handle subject, string name returns handle
return GetStoredInteger(LocalVars(), I2S(GetHandleId(subject)), name)
call DoNothing()
return null
endfunction
function FlushHandleLocals takes handle subject returns nothing
call FlushStoredMission(LocalVars(), I2S(GetHandleId(subject)) )
endfunction[/JASS][/spoiler]
If people want me to I can modify the map so that it covers the AOE spells as well (but it is just going to come down to deleting few functions and that is it)
==============
[I][B]Changelog[/B][/I]
==============
[B]Map Corrections made and map reuploaded 19th August, 2009 @ 11:37 AM (GMT+1)[/B]
[B]Fixed a minor bug with buff not being detected thus not hasting casting, reuploaded 19th August, 2009 @6:38 PM (GMT+1)[/B]
[B]Optimized the code a little bit and fixed a bug that prevents you next if it cancled the casting, reuploaded 20th August, 2009 @11:25 AM (GMT+1)[/B]</code></pre>
</div>
</div>