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!
I'm gonna ask, how do I create further vJASS spells? Without actually using math, I mean basic ones, I know how to make AoE spells which I learned from watermelon. I admire him because of helping me solve my problems. Can someone tell me how other spells are made? I can't make an instant spell like novas. Novas needs math for calculating the radians and such, I'm only a 12 year old boy and in the 1st year of high school (10 years of basic education). But can somebody give me a sample code of a vJASS spell similar to instant spells like Starfall, and a single targeted spell that hits the enemy many times (100 hits then when 100 hits is achieve it stops and it will charge for the final blow), please get it document and I'm going to learn it from your documents and the code itself. Obviously I'm going to ask questions about how they are made like before.
Thanks,
for the people who did help me I'm going to give a +rep even though it only helped me a bit or only tried.
But can somebody give me a sample code of a vJASS spell similar to instant spells like Starfall, and a single targeted spell that hits the enemy many times (100 hits then when 100 hits is achieve it stops and it will charge for the final blow)
Well I myself am not too good at math. Most that I use is learned from middleschool. For the difficult math I've asked around and grabbed systems that did it for me.
Your two biggest friends will be this:
AngleBetweenPoints/Atan2. This one lets you find out the angle between two things by their location or by getting their X coordinate and Y coordinate. AngleBetween takes locations in order of A(The Source) and B(The Target), Atan2 takes X and Y but in order of B and A.
JASS:
AngleBetweenPoints(GetUnitLoc(A), GetUnitLoc(B))
vs
Atan2(GetUnitY(B) - GetUnitY(A), GetUnitX(B) - GetUnitX(A))
They also return different numbers. Atan2 returns Radians, which can be converted to Degrees with bj_DEGTORAD. AngleBetween returns Degrees, which can be converted to Radians with bj_RADTODEG. Different functions and systems require Radians or Degrees. Don't worry about knowing Radians, since you can always convert. Like if you want a 90 degree angle in radians just do 90*bj_DEGTORAD.
What's important is to know which function takes what. Now that's the basis of learning. Understand where something is pointing in 360 degrees, which at this point you should in your education.
Great! You now know how to point things at where they need to go. Now how do you get them there? PolarProjection.
Polar Projection takes where the object starts, how far it will move, and at what angle. It takes angle in degrees. So lets say you want to move a Unit, A, 90 units at 45 degrees of where it's standing, which'd be at a nice even slant. PolarProjection(GetUnitLocation(A), 90, 45). That will return the point that the unit is moving to. Set it to a variable then SetUnitPositionLocation(A, TheVariable).
But lets say we want to move something towards a target point picked by a spell. Well, just find the Angle with AngleBetweenPoints(GetUnitLocation(A), GetSpellTargetLocation()). Then set that to a variable and plug it into PolarProjection. Easy as pie.
Polar Projection can also be determined in X and Y. Just remember angle must be converted to radians for this:
x = HowFar*Cos(angle*bj_DEGTORAD)
y = HowFar*Sin(angle*bj_DEGTORAD)
That gives the X and Y that Polar Projection would return as a location.
Knowing this, you can move units and special effects where you want them. Just determine the units moved by the grid on the WorldEdit minimap.
I used this to make frogs appear randomly in the vicinity of the caster which explode on death.
JASS:
local real ang = GetRandomReal(0,360)*bj_DEGTORAD
set x = GetUnitX(A)+GetRandomReal(50,400)*Cos(ang)
set y = GetUnitY(A)+GetRandomReal(50,400)*Sin(ang)
and I used this to move my hero in a rush towards his targeted point.
JASS:
local real angle = Atan2(GetSpellTargetY() - GetUnitY(A), GetSpellTargetX() - GetUnitX(A))
local real x = GetUnitX(A) + 20 * Cos(angle)
local real y = GetUnitY(A) + 20 * Sin(angle)
It's in a timer that goes off every 0.034 seconds, the recommended amount for moving graphics or units.
Now the final operation that will help you is finding the distance between two points. DistanceBetweenPoints() It takes two locations and gives you the distance between those two points. Great to know how close you are to a targeted point. Also great to know how long it'd take to get to the point a certain speed, but i've forgotten that formula.
In JASS:
JASS:
SquareRoot((GetUnitX(A)-GetSpellTargetX()) * (GetUnitX(A)-GetSpellTargetX()) + (GetUnitY(A)-GetSpellTargetY()) * (GetUnitY(A)-GetSpellTargetY()))
or
local real dx = GetUnitX(A) - GetSpellTargetX()
local real dy = GetUnitY(A) - GetSpellTargetY()
local real dist = SquareRoot(dx*dx+dy*dy)
Knowing how these three functions operate and what they can do will help you tremendously on making your own special effects, slide systems, movement systems, etc.
For your nova, well you'd want to send something out from your hero or cause an AoE damage around your hero. Best example: Increase your angle by how many things you want to send out/explosions you want to cause.
Lets say angle1 would be 30, 2 would be 60, etc. so 360/30 = 12. Create a loop that takes i*30 and exitwhen i < 12.
First of all TitanHex, thank you for your response, but I am still confused about it, I'm going to try that when I have time. Watermelon, sorry, I thought it was an instant spell but, sorry about that lol. You can do what ever you want, just as long as its an Single-Targeted spell.
Example of a healing over time spell (Everything is done in the struct):
JASS:
scope HealOverTime
globals
private constant integer SPELL_ID = 'A000' // Raw id of the ability
private constant integer BUFF_ID = 'Brej' // Raw id of the buff
private constant real TIMER_LOOP = 1. // How often the timer will loop. Affects how often the unit will be healed
endglobals
// How much the unit will be healed per second.
private function Heal takes integer lvl returns real
return 75. + 50*lvl // Note the . behind 75 allows us to avoid using I2R
endfunction
// How long should the buff last on the target unit. This is also there so that the multiple healing effects stack.
private constant function Duration takes integer lvl returns real
return 5. + 5*lvl
endfunction
// This struct deals with the entire spell.
private struct Data
// The struct members:
unit target // The target unit of the spell.
boolean hasBuff = false // Used to check when the target unit gets the buff.
real count = 0 // Counts how long the buff has been on the unit.
real heal // Amount healed per second.
real dur // How long the buff should last
timer tim
// This method runs periodically to heal the target or destroy the struct once the spell is finished
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer()) // This gets the struct attached to the timer.
// Checks to see when .target has the buff. If it does, set .hasBuff to true.
if not .hasBuff and GetUnitAbilityLevel(.target,BUFF_ID) > 0 then
set .hasBuff = true
endif
// Heals the unit as long as the buff is still there and .hasBuff is true. Also increases .count
if .hasBuff and GetUnitAbilityLevel(.target,BUFF_ID) > 0 then
call SetWidgetLife(.target,GetWidgetLife(.target)+.heal)
set .count = .count + TIMER_LOOP
endif
// Stops the spell when either the buff is gone or the duration has been exceeded.
if .hasBuff and (.count > .dur or GetUnitAbilityLevel(.target,BUFF_ID) == 0) then
call ReleaseTimer(.tim) // Releases the timer or else it will keep running.
call .destroy() // Destroy the struct because we don't need it anymore.
endif
endmethod
// This basically starts our spell
static method create takes unit target, integer lvl returns thistype // thistype is basically the same as writing Data, which is the struct's name.
local thistype this = thistype.allocate()
set .target = target
set .heal = Heal(lvl)*TIMER_LOOP // Multiply by TIMER_LOOP since the unit will be healed every TIMER_LOOP
set .dur = Duration(lvl)
set .tim = NewTimer() // We're using TimerUtils for recycling timers and attaching data
call SetTimerData(.tim,this)
call TimerStart(.tim,TIMER_LOOP,true,function thistype.onLoop)
return this
endmethod
// This is actually a trigger-condition thread. We're just doing everything in here to reduce the amount of threads needed.
static method spellActions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.create(GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID))
endif
return false // Always return false since we never need to use a trigger-actions thread.
endmethod
// This special method runs on map initialization. It must be named onInit.
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.spellActions))
endmethod
endstruct
endscope
This should be a pretty basic example of how effects over time should be done. If you want a more in-depth explanation you could look at this.
I just wanted to add a little gem to spellmaker tips:
When doing the special effects of spells using triggers, GetAbilityEffectById() is truly useful. It allows you to set the data in the Art fields to the special effect you want. In terms of configuration, it's really quiet helpful.
As for structs, I use them but I'm not entirely familar with them. Everything they do can be done in regular JASS. The secret is in the compiler, which makes things a lot easier. It turns JASS into an OOP language.
Basically all those variables at the top of the struct are states of the struct. They can be changed by the methods of the struct depending on the data flowing through them. The struct seems to be an object, which holds different functions it can do (methods) and several variables which can be changed. It can be converted into an integer and have it's data shared.
Methods are just functions, that's all. Method = function. But they can do things functions cannot. My suggestion is to learn how to use returns and takes, so functions make a lot more sense to you (If they don't already).
Structs are useful, and so are scopes. Especially for spell-makers. Functions are very helpful for systems makers. You should learn these. They're not that hard to use.
well.. when he sayd 100 hits, then charge for the final blow,
i think he meant like, 100 quick hits, fimiliar to neji - 128 palms, from naruto (anime).
he does 128 strikes really fast, and at the end he does a fatal blow knocking you back..
im pretty sure thats what he meant.. and its quite simple, making him repeat the same old attack animation at loops with timers, or w/e, unless you want it mui.
if you want it mpi, its quite simple aswell, making the timers as array's for each player.
then repeat the attack animation and deal damage each blow.
if you want me to tell you every step how to do it, i'll do that..
im pretty sure thats what he meant.. and its quite simple, making him repeat the same old attack animation at loops with timers, or w/e, unless you want it mui.
if you want it mpi, its quite simple aswell, making the timers as array's for each player.
then repeat the attack animation and deal damage each blow.
if you want me to tell you every step how to do it, i'll do that..
well.. when he sayd 100 hits, then charge for the final blow,
i think he meant like, 100 quick hits, fimiliar to neji - 128 palms, from naruto (anime).
he does 128 strikes really fast, and at the end he does a fatal blow knocking you back..
im pretty sure thats what he meant.. and its quite simple, making him repeat the same old attack animation at loops with timers, or w/e, unless you want it mui.
if you want it mpi, its quite simple aswell, making the timers as array's for each player.
then repeat the attack animation and deal damage each blow.
if you want me to tell you every step how to do it, i'll do that..
Yes but its just an example, the kind person can make a spell on what he prefers.
TitanHex said:
I just wanted to add a little gem to spellmaker tips:
When doing the special effects of spells using triggers, GetAbilityEffectById() is truly useful. It allows you to set the data in the Art fields to the special effect you want. In terms of configuration, it's really quiet helpful.
As for structs, I use them but I'm not entirely familar with them. Everything they do can be done in regular JASS. The secret is in the compiler, which makes things a lot easier. It turns JASS into an OOP language.
Basically all those variables at the top of the struct are states of the struct. They can be changed by the methods of the struct depending on the data flowing through them. The struct seems to be an object, which holds different functions it can do (methods) and several variables which can be changed. It can be converted into an integer and have it's data shared.
Methods are just functions, that's all. Method = function. But they can do things functions cannot. My suggestion is to learn how to use returns and takes, so functions make a lot more sense to you (If they don't already).
Structs are useful, and so are scopes. Especially for spell-makers. Functions are very helpful for systems makers. You should learn these. They're not that hard to use.
Example of a healing over time spell (Everything is done in the struct):
JASS:
scope HealOverTime
globals
private constant integer SPELL_ID = 'A000' // Raw id of the ability
private constant integer BUFF_ID = 'Brej' // Raw id of the buff
private constant real TIMER_LOOP = 1. // How often the timer will loop. Affects how often the unit will be healed
endglobals
// How much the unit will be healed per second.
private function Heal takes integer lvl returns real
return 75. + 50*lvl // Note the . behind 75 allows us to avoid using I2R
endfunction
// How long should the buff last on the target unit. This is also there so that the multiple healing effects stack.
private constant function Duration takes integer lvl returns real
return 5. + 5*lvl
endfunction
// This struct deals with the entire spell.
private struct Data
// The struct members:
unit target // The target unit of the spell.
boolean hasBuff = false // Used to check when the target unit gets the buff.
real count = 0 // Counts how long the buff has been on the unit.
real heal // Amount healed per second.
real dur // How long the buff should last
timer tim
// This method runs periodically to heal the target or destroy the struct once the spell is finished
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer()) // This gets the struct attached to the timer.
// Checks to see when .target has the buff. If it does, set .hasBuff to true.
if not .hasBuff and GetUnitAbilityLevel(.target,BUFF_ID) > 0 then
set .hasBuff = true
endif
// Heals the unit as long as the buff is still there and .hasBuff is true. Also increases .count
if .hasBuff and GetUnitAbilityLevel(.target,BUFF_ID) > 0 then
call SetWidgetLife(.target,GetWidgetLife(.target)+.heal)
set .count = .count + TIMER_LOOP
endif
// Stops the spell when either the buff is gone or the duration has been exceeded.
if .hasBuff and (.count > .dur or GetUnitAbilityLevel(.target,BUFF_ID) == 0) then
call ReleaseTimer(.tim) // Releases the timer or else it will keep running.
call .destroy() // Destroy the struct because we don't need it anymore.
endif
endmethod
// This basically starts our spell
static method create takes unit target, integer lvl returns thistype // thistype is basically the same as writing Data, which is the struct's name.
local thistype this = thistype.allocate()
set .target = target
set .heal = Heal(lvl)*TIMER_LOOP // Multiply by TIMER_LOOP since the unit will be healed every TIMER_LOOP
set .dur = Duration(lvl)
set .tim = NewTimer() // We're using TimerUtils for recycling timers and attaching data
call SetTimerData(.tim,this)
call TimerStart(.tim,TIMER_LOOP,true,function thistype.onLoop)
return this
endmethod
// This is actually a trigger-condition thread. We're just doing everything in here to reduce the amount of threads needed.
static method spellActions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.create(GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID))
endif
return false // Always return false since we never need to use a trigger-actions thread.
endmethod
// This special method runs on map initialization. It must be named onInit.
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.spellActions))
endmethod
endstruct
endscope
This should be a pretty basic example of how effects over time should be done. If you want a more in-depth explanation you could look at this.
Here is an example that comes up with no errors on my compiler. It's a spell that heals allies when the Duration reaches the end.
JASS:
scope Bloom initializer onInit
globals
private constant integer SPELLID = 'bloo'
private constant integer BUFFID = 'Bloo'
private constant real TICK = 1
private unit TEMP
endglobals
private struct Data
unit c
real dur
static method GroupEm takes nothing returns boolean
local integer lvl = GetUnitAbilityLevel(TEMP, SPELLID)
local unit t= GetFilterUnit()
if IsUnitAlly(t, GetOwningPlayer(TEMP)) and not(IsUnitType(t, UNIT_TYPE_DEAD) or IsUnitType(t, UNIT_TYPE_STRUCTURE)) then
call SetWidgetLife(t, GetWidgetLife(t)+30+50*lvl)
endif
set t= null
return false
endmethod
static method Timer takes nothing returns nothing
local timer tim = GetExpiredTimer()
local thistype this = GetTimerData(tim)
local group g= NewGroup()
set .dur = .dur+TICK
if .dur >= 5 then
set TEMP = .c
call GroupEnumUnitsInRange(g, GetUnitX(.c),GetUnitY(.c), 400, Filter(function thistype.GroupEm))
call ReleaseTimer(tim)
call .destroy()
endif
if GetUnitAbilityLevel(.c, BUFFID) < 1 then
call ReleaseTimer(tim)
call .destroy()
endif
call ReleaseGroup(g)
endmethod
static method create takes unit c returns thistype
local timer tim = NewTimer()
local thistype this = thistype.allocate()
set .c = c
set .dur = 0
call SetTimerData(tim,this)
call TimerStart(tim, TICK, true, function thistype.Timer)
return this
endmethod
static method Conditions takes nothing returns nothing
if GetSpellAbilityId() == SPELLID then
call thistype.create(GetTriggerUnit())
endif
endmethod
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddAction( t, function thistype.Conditions )
call XE_PreloadAbility(SPELLID)
set t = null
endmethod
endstruct
endscope
Just a note: These require TimerUtils to use.
To do MUI, you'll want to use a lot of local variables and avoid global variables. TimerUtils and Hashtables help with that greatly.
It's important to know that if you use a global variable, you must use it IMMEDIATELY to keep MUI. The thread cannot have a wait in it and the unit must not have to take further action before the Global is used.
Also my explanation was primarily about structs. Hopefully I didn't confuse you into thinking I was talking about scopes Scopes and libraries are actually very easy to use, but necessary for vJASS.
Private vs Public is important to learn too. I assume those are familiar to you.
You can either update JassHelper manually (head over to wc3c.net and download the most recent version from there, then replace jasshelper.exe, clijasshelper.exe and jasshelper.conf in the jasshelper folder of your JNGP with the most recent versions), or download this version of JNGP which already has JassHelper updated to the most recent version (among a multitude of other changes).
Example of a healing over time spell (Everything is done in the struct):
You see, this is why i hate using scopes for spells (i have never needed to use scopes in the last 2 to 3 years, i always used libraries instead). You have no clear indication as to which 3rd party libraries are being used.
That spell requires TimerUtils, and that post fails to link to it.
Theres also no indication that the spells needs to be based off an ability that places a buff (BUFF_ID) on the target unit (like Acid Bomb).
TimerUtils attaches the data of a struct (All those variables declared at the top) and then transfers it to the Timer so that when it expires, you can call up all that data and use it.
Sorry, I should have mentioned several things before I posted that code. I did mention that I was using TimerUtils in the code, but I should have at least posted a link to it.
To expand on what TitanHex said:
You attach structs to a timer using SetTimerData
This function takes a timer and an integer as its parameters.
It's interesting to note that structs can be treated like integers which allows their data to be easily passed.
When you retrieve data back from a timer, you'll want to turn that integer back to the struct so you can access the data, which is why you do this:
JASS:
local thistype this = GetTimerData(GetExpiredTimer())
instead of this
JASS:
local integer i = GetTimerData(GetExpiredTimer()) // This is pretty much useless to you like this.
Note that you can only use those functions on timers given by TimerUtils through NewTimer.
When done with a timer, use ReleaseTimer to recycle it.
This is done before the struct is destroyed or else you wouldn't be able to refer to the struct member.
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.