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!
The Caster charges towards the target unit,dealing 100 + 2/3/4 X the caster's agility as damage.The charge speed is 500 units per second.Stops if the caster or the target unit dies.
Has a cast range of 10000
private constant string CHARGE_SFX = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl"//Refers to the sfx created when charging.
private constant string CHARGE_ATTACH = "chest"//Refers to the attachment of ^ SFX
private constant string DAMAGE_SFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"//Refers to the sfx when damaging
private constant string DAMAGE_ATTACH = "overhead"
private constant string SWORD_SFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"//Refers to the sfx of the sword when charging.
private constant string SWORD_ATTACH = "weapon"
//Animation
private constant integer ANIM_ID = 6 //Refers to the walking animation
private constant real ANIM_SPEED = 2
private constant real MIN_DIST = 100
endglobals
//Damage Function
private function GetDamage takes unit u , integer lvl returns real
return I2R((GetHeroAgi(u,true) * (lvl + 1))) + 100
endfunction
//Get Speed
private function GetSpeed takes integer lvl returns real
return 500 * TIMEOUT
endfunction
//Get Minimum Distance
private function GetMinDist takes nothing returns real
return MIN_DIST * MIN_DIST
endfunction
//Running Spell
struct RefractionCharge
private thistype next
private thistype prev
private unit caster
private unit target
private effect sword
private real speed
private real damage
private method destroy takes nothing returns nothing
/*
* Deallocate this instance.
*/
call this.deallocate()
/*
* We remove the instance from the linked list.
*/
set this.next.prev = this.prev
set this.prev.next = this.next
/*
* We decrease the count by 1.
* If the count is 0, we pause the timer.
*/
set count = count - 1
if count == 0 then
call PauseTimer(iterator)
endif
/*
* We null the data in the struct.
* This is completely optional. It doesn't really make a difference
* at all. (Unless you're casting the spell some hundreds of times.)
* If you have some real memory intense systems in your map,
* you might want to do this, especially if your struct has a lot of data.
*
* These are global variables, so they will be recycled eventually.
* It's all up to you, my friend.
*/
call DestroyEffect(this.sword)
set this.sword = null
call SetUnitAnimation(this.caster,"stand")
call SetUnitTimeScale(this.caster,1)
call PauseUnit(this.caster,false)
set this.caster = null
set this.target = null
endmethod
private static method periodic takes nothing returns nothing
/*
* Starting from the first instance, we loop
* over all the instance in the list until we hit
* a dead-end.
*/
local real x
local real y
local real x2
local real y2
local real dist
local real angle
local thistype this = thistype(0).next
loop
exitwhen this == 0
set x = GetUnitX(this.caster)
set y = GetUnitY(this.caster)
set x2 = GetUnitX(this.target)
set y2 = GetUnitY(this.target)
set angle = Atan2(y2 - y,x2 - x)
set dist = (x2-x) * (x2-x) + (y2-y)*(y2-y)
if not IsUnitType(this.caster,UNIT_TYPE_DEAD) and not IsUnitType(this.target,UNIT_TYPE_DEAD) then
if dist >= GetMinDist() then
set x = x + this.speed * Cos(angle)
set y = y + this.speed * Sin(angle)
//Moving Unit
call SetUnitX(this.caster,x)
call SetUnitY(this.caster,y)
call DestroyEffect(AddSpecialEffect(CHARGE_SFX,x,y))
call SetUnitAnimationByIndex(this.caster,ANIM_ID)
call SetUnitFacing(this.caster,angle * bj_RADTODEG)
else
call UnitDamageTarget(this.caster,this.target,this.damage,false,false,ATTACK_TYPE,DAMAGE_TYPE,null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX,this.target,DAMAGE_ATTACH))
call this.destroy()
endif
else
call this.destroy()
endif
set this = this.next
endloop
endmethod
private static method run takes nothing returns boolean
/*
* We allocate an instance.
*/
local thistype this = thistype.allocate()
local integer lvl
/*
* We add the instance to the linked list.
*/
set this.next = 0
set this.prev = thistype(0).prev
set thistype(0).prev.next = this
set thistype(0).prev = this
/*
* We increase the count by 1.
* If the count is 1, we start the timer to loop through
* the instances. This is because recasting the spell while
* an instance is already running shouldn't restart the timer.
*/
set count = count + 1
if count == 1 then
call TimerStart(iterator, TIMEOUT, true, function thistype.periodic)
endif
/*
* We set our struct data.
*/
set this.caster = GetTriggerUnit()
set this.target = GetSpellTargetUnit()
set this.sword = AddSpecialEffectTarget(SWORD_SFX,this.caster,SWORD_ATTACH)
set lvl = GetUnitAbilityLevel(this.caster,ABIL_CODE)
set this.damage = GetDamage(this.caster,lvl)
set this.speed = GetSpeed(lvl)
//Setting Animation Speed
call SetUnitTimeScale(this.caster,ANIM_SPEED)
call PauseUnit(this.caster,true)
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.run))
set t = null
endmethod
endstruct
endlibrary
[/code]
Credits
-Magtheridon96 for the model.
Changelogs:
-Added Library,a function and globals
-fixed indention,variable names and more.
-removed lvl from struct and changed to locals
Understanding
not jass Knowledge
and you're using too many BJs
call ResetUnitAnimation(this.caster)
This only resets the Rarity of the animation
This is the best AngleBetweenPoints set angle = bj_RADTODEG * Atan2(GetLocationY(locB) - GetLocationY(locA), GetLocationX(locB) - GetLocationX(locA))
instead of using these call SetUnitTimeScalePercent(this.caster,200)
You can use this bj call SetUnitTimeScale(this.caster, 200/ 100.)
(it's not bj)
it would also be better to have things like damage be changeable via functions rather than having them hardcoded inside the main code... speed should also be configurable too via functions in case the user wants them to scale with the ability level for example...
JASS:
//something like
function getDamage takes unit caster, integer level returns real
return GetHeroInt(caster,true)*(level+1.0)
endfunction
//put this at the upper portion so that your user won't need to delve deep into your code just to modify this
use coordinates rather than locations...
then maybe you'd like to use SpellEffectEvent too (optional)...
as for the angle, Atan2(TargetY-CasterY,TargetX-CasterX) returns radians so using that would be your best option...
as for distance, use the distance formula...
also the variables like attacktype, effect etc seem to be constants so please make them as static constant or static readonly variables so that they will act like single globals... because right now they are created for every struct instance which is a waste since they are constants...
JASS:
private static readonly attacktype //this will make only one of this variable for the whole struct
private readonly attacktype //this will make one of this variable per struct instance
//readonly I think means that you cannot modify it outside of the struct
//you can also use constant, that might actually be better
Also, the animation id might not be correct for different models...
/*
* We null the data in the struct.
* This is completely optional. It doesn't really make a difference
* at all. (Unless you're casting the spell some hundreds of times.)
* If you have some real memory intense systems in your map,
* you might want to do this, especially if your struct has a lot of data.
*
* These are global variables, so they will be recycled eventually.
* It's all up to you, my friend.
*/
set this.caster = null
set this.target = null
// Code.
Over here, you were supposed to null all the handles, not just the caster and the target.
A handle is anything that is not a:
- String
- Integer
- Real
- Boolean
- Code
Another thing: In vJASS, we prefer to use pure coordinates instead of locations.
Use GetUnitX/GetUnitY instead of GetUnitLoc.
Tip: Hold ctrl and click a function highlighted in red to see what it actually does. Then do that.
I see that you're using the locations only to get the angle. You don't need the locations for that because you can do this: bj_RADTODEG * Atan2(targetY - casterY, targetX - casterX)
^This returns the angle from the caster to the target in degrees.
As for distance: SquareRoot((targetX - casterX)*(targetX - casterX) + (targetY - casterY)*(targetY - casterY))
^This returns the distance between the caster and the target.
But, you can optimize this. In your case, since you're only using the distance for a comparison, you can omit the SquareRoot function call and instead compare the distance with 10000 instead of 100. (As if you squared both sides.)
edit Also, I specifically stated in that tutorial that these models should not be used in public resources if you're not going to wrap all the code inside a library or a scope.
You need proper documentation, explicit requirements and proper importing instructions with a version number/name/author tag and possibly a section for credits.
Just put all your code inside a library:
JASS:
library SpellNameHere requires Requirement1, Requirement2, etc...
// All the code.
endlibrary
And on top of the library, put documentation.
Check out some of my resources for some nice documentation models
If you want something that's absolutely brilliant in terms of configuration, check this one out. No, this is not an excuse for me to advertise my own resources, I'm just not aware of any well-documented spells and even if I was, on such short notice, I wouldn't remember them :V
edit
Most of the variables in the struct are being initialized when they don't need to be.
Why are you initializing the special effect string for each instance? It should be a configurable constant in a globals block or a constant function. (Preferably, a globals block).
The same applies to all the other values that are up for configuration.
I configured the ABIL_CODE but for some reason every time a peasant tries to build anything, it triggers refraction charge and destroys the building. Repair is not the same ability code. So I'm not sure what's wrong here.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.