| 08-28-2009, 04:17 AM | #1 |
(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 |
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:
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 |
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 |
Okay... Let's first look at the structure of a very simple spell: 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 Without Comments:scope MySpell private function Actions takes nothing returns nothing call KillUnit( GetSpellTargetUnit() ) endfunction private function Conditions takes nothing returns boolean return GetSpellAbilityId() == 'Aslo' 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 ) set trg=null endfunction endscope --------------------------- 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. 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 |
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 | |
Quote:
Because a standardised timer system is very useful? |
| 08-28-2009, 08:48 AM | #7 | |
Quote:
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 | |
Quote:
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 |
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 | |
Quote:
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 |
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 | |
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:
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 | |
Quote:
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 | |
Quote:
O.o JASS:scope B initializer A ? |
| 08-28-2009, 01:33 PM | #15 | |
Quote:
JASS:public function InitTrig takes nothing returns nothing |
