HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Couple (v)JASS questions...

08-28-2009, 04:17 AM#1
thelifelessone
(Sorry if this is the wrong forum! Still kinda new here (even though I've had the account forever...))

Anyway, I recently learned vJASS. I have a pretty good understanding of libraries, scopes, functions, takes, structs, etc.

But I can't seem to find out how to actually do anything...
I'm really interested in learning how to make spells and systems, but I can't seem to actually do any of that.

I've read plenty of tutorials, but I've had no success with them, and most of them require systems that were made by the person who made the tutorial, which seems pointless to me.

So how exactly do you make systems?
And how exactly do you make spells?

Thanks for any help.
08-28-2009, 05:38 AM#2
Pyrogasm
You just... write code. That's a pretty open-ended question, really.

Usually a system comes about as a result of one of these three scenarios:
  1. You want to do Y, but there is no easy way to do this, so a new system to help you easily do Y is needed.
  2. The way system X does Y is inefficient or could be improved upon, so you write a new system to do Y better.
  3. You don't like the way system X does Y, so you write Z to do Y in a different way; perhaps others like Z too, and it can become a viable alternative to X.

As for a spell, you just make whatever you want to make using whatever you need to use. Perhaps you mean to ask about what the basic format/structure for a spell is, in which case my answer is: however you want to structure it :P.

Take a look a the newer spells approved here and see how they're made; how the spell is separated into important parts and made modular.
08-28-2009, 06:41 AM#3
thelifelessone
Well, I think my main problem is something like I don't know which functions (natives) to use, or what variables to set, or when to use a struct, etc.
I'm kinda limited on what I'm able to download, so studying other peoples code isn't always an option for me.

I could try the whole "convert to custom code" on GUI spells, but I'd need a spell in the first place, and honestly, I'm very bad with spell ideas.
08-28-2009, 08:20 AM#4
Themerion
Okay... Let's first look at the structure of a very simple spell:

Collapse JASS:
//=======================================
// KILLING SLOW
// This script makes a normal sorceress slow spell killing.
//========================================

// Scopes allow for better control over code (public/private/globals).
scope MySpell

// Note how this function is private.
// No code outside this scope is supposed to be able to access it.
private function Actions takes nothing returns nothing

  // As said, a very simple spell.
  // Kills the target of the spell.
    call KillUnit( GetSpellTargetUnit() )
endfunction

// Note that this function is supposed to return a boolean.
private function Conditions takes nothing returns boolean

  // If the casted spell equals to slow, then
  // return 'Also' == 'Aslo' ( = true )
  // This let's the system know that the condition is true
  // and that we can run the trigger actions.
    return GetSpellAbilityId() == 'Aslo'

  // PS. 'Aslo' is an ability rawcode (Slow, in this case).
  // Go to the Object Editor and press Ctrl+D to see rawcodes.
endfunction

//===========================================================================
// A PUBLIC function named InitTrig (inside a scope)
// will always be run at map init.
// This is where we set up the trigger.
public function InitTrig takes nothing returns nothing
    local trigger trg = CreateTrigger()

  // If any unit begins the effect of any spell...
  // (triggers exactly when the spell is cast)
    call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_SPELL_EFFECT)

  // Check for what spell it was...
    call TriggerAddCondition(trg,Condition(function Conditions))

  // If the Condition was true, run the function Actions!
    call TriggerAddAction(trg, function Actions )

  // This might be unnecessary, but I like it ^^
    set trg=null
endfunction

endscope
Expand Without Comments:

---------------------------

Now, killing is a bit boring, so we'll just make it deal 200 damage after 5 seconds. For this end, we'll need TimerUtils.

Collapse JASS:
//=======================================
// DAMAGING SLOW
// This script makes a normal sorceress slow damage the target
// for 200 damage after 5 seconds.
//========================================

scope MySpell

// No other code is supposed to make instances of this struct.
// Hence, its private:
private struct Data

  // After 5 seconds have passed, we no longer have the original variables.
  // We need to know caster and target to be able to deal damage!
    unit target
    unit caster

  // PS. For hero spells, you could also store things that may vary over level, like:
  //  integer damage
endstruct

// Here's the function that will be run when a timer expires.
// Stuff like GetSpellTargetUnit and GetSpellAbilityUnit are forgotten.
// The only thing this function knows is GetExpiredTimer()
// Luckily we have attached a Data-instance to the timer.
private function End takes nothing returns nothing

  // Retrieve the Data instance associated with this timer.
    local Data d = Data( GetTimerData( GetExpiredTimer() ) )

  // Here it comes...!
  // d.caster shall damage d.target for 200 damage
  // The booleans are usually set that way (true,false)
  // Spell Damage = ATTACK_TYPE_NORMAL
  // Damage and weapon type are somewhat tricky.
  // In most cases, you'll be fine with normal and whoknows.
    call UnitDamageTarget(d.caster,d.target,200,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WNOKNOWS)

  // Clean up and destroy the struct instance:
    set d.target = null
    set d.caster = null
    call d.destroy()

  // Return the timer to TimerUtils
    call ReleaseTimer( GetExpiredTimer() )
endfunction

private function Actions takes nothing returns nothing

  // Obtain the timer from TimerUtils
  // We need a timer to wait 5 seconds
  // Normal Wait/TriggerSleepAction isn't good.
    local timer t = NewTimer()

  // Create a new instance of the struct:
    local Data d = Data.create()

  // The the instance's variables...
    set d.caster = GetSpellAbilityUnit()
    set d.target = GetSpellTargetUnit()

  // Associate your data instance with this timer:
    call SetTimerData(t,d)

  // Start your timer (t).
  // Let it expire in 5 seconds.
  // It should not be looping (false)
  // When it expires, the function End will be run.
    call TimerStart(t,5,false,function End)

  // Is this unnecessary too? Perhaps..
    set t=null
endfunction


private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'Aslo'
  // PS. 'Aslo' is an ability rawcode (Slow, in this case).
  // Go to the Object Editor and press Ctrl+D to see rawcodes.
endfunction

//===========================================================================
public function InitTrig takes nothing returns nothing
    local trigger trg = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trg,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trg,Condition(function Conditions))
    call TriggerAddAction(trg, function Actions )

  // This might be unnecessary, but I like it ^^
    set trg=null
endfunction

endscope
08-28-2009, 08:32 AM#5
Anachron
I have never used TimerUtils and I will never, why should I, if I have structs for it?
08-28-2009, 08:36 AM#6
ploks
Quote:
Originally Posted by Anachron
I have never used TimerUtils and I will never, why should I, if I have structs for it?

Because a standardised timer system is very useful?
08-28-2009, 08:48 AM#7
Themerion
Quote:
I have never used TimerUtils and I will never, why should I, if I have structs for it?

Because you need to associate your struct instances with timers in order to make the spell MUI!?

Sure you can use table, but TimerUtils are faster (and safer, since you don't destroy the timer, possibly causing a fatal error).

EDIT:
- Since I don't want to clog this thread with semi-o.t. stuff, I'll post my response to Anachrons post right here. Yeah, you can do it that way too. Not that I see a good reason for it, but you can (before you start to scream performance, think of when it will matter. In a map where two hundred (!) spells are cast simultaneously, will it matter? Probably not, but feel free to PROVE your solution is better (in another thread), and post your solution in the scripts section).
08-28-2009, 08:52 AM#8
Anachron
Quote:
Originally Posted by Themerion
Because you need to associate your struct instances with timers in order to make the spell MUI!?

Sure you can use table, but TimerUtils are faster (and safer, since you don't destroy the timer, possibly causing a fatal error).
No I don't, I use one timer for all instances.

And, those are even MUI!
A static array does all the job, so I know which instances are already running.

A timer would only be required if I wouldn't know which data I have, but in structs I have that data already saved.
08-28-2009, 10:16 AM#9
Pyrogasm
If you need a bunch of spell ideas, you're welcome to check out my Spell Request Thread. You could try making your own versions of the spells I've completed, or even use some of the non-finished request ideas on the "backburner".

Everything in that thread is open-source and you are free to download it, look at the code, compare it to yours, or do whatever you want.
08-28-2009, 11:37 AM#10
Viikuna-
Quote:
A static array does all the job, so I know which instances are already running.

That works well only for periodic timers. And its kinda stinky for bigger periods.

( Depending on how good accurary you need, ofc )
08-28-2009, 12:03 PM#11
Anachron
How else would you do that?
Creating an timer for all the instances? That would be a waste of
processor time, its faster to run through all instances instead.

And, yet you haven't proven that I need TimerUtils at all.
08-28-2009, 12:25 PM#12
Anitarf
You don't "need" it. It's still useful, if you can't see that then that's your loss.

Of course, when you post outright fabrications, I have to speak up.
Quote:
Creating an timer for all the instances? That would be a waste of
processor time, its faster to run through all instances instead.
False. If you're running through all instances periodically, you are wasting processing power unless the spell's design actually requires you to do stuff periodically. If you just want to wait a certain duration, though, then counting down periodically is a bad solution.


Edit: Themerion: We have had scope initializers for over a year now, there's no need to name the scope the same as the trigger it's in and call it's initializer InitTrig.
08-28-2009, 12:40 PM#13
Anachron
Quote:
Originally Posted by Anitarf
False. If you're running through all instances periodically, you are wasting processing power unless the spell's design actually requires you to do stuff periodically. If you just want to wait a certain duration, though, then counting down periodically is a bad solution.
But, most things do stuff periodicly?

I mean tell me a situation where the period time is not the same in the same spell.

TimerUtils is just good to attach data for something that is actually be diffrent from situation to situation, and even then, only for a few examples.

And ofcourse, my struct is everytime concepted to be runned at the same time for every instance, why not? I just need one timer then.
08-28-2009, 01:00 PM#14
Themerion
Quote:
Edit: Themerion: We have had scope initializers for over a year now, there's no need to name the scope the same as the trigger it's in and call it's initializer InitTrig.

O.o

Collapse JASS:
scope B initializer A

?
08-28-2009, 01:33 PM#15
Anachron
Quote:
Originally Posted by Themerion
O.o

Collapse JASS:
scope B initializer A

?
He meant

Collapse JASS:
public function InitTrig takes nothing returns nothing