HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Building a better spellbook.

04-16-2006, 05:12 AM#1
weaaddar
Admittedly, I'm not very big in the war3 community anymore, but this has always been an interesting problem for me in general.

The war3 ui is very limited, you only have 6 items, and you also only have about 6 abilities you can have active at any time.

Now 6 items is a pretty easy limit to overcome mainly because A) Items aren't that important. A good equipment system should make items less important, the only items with a good equip system you should worry about are consumables. If you need 6 consumables present your characters are probably underpowered in the map, and are compensated for with items. B) Items can stack, and have inate delayed natured. Who cares that you picked up 5 different pots of healing, its much more convient to simply have them all stack together. Simmilarly, if you pick up uber potion of mana, having it in a backpack isn't horribly disadvantegous. You rarely want to use potion you pick up that instant, so having to do a few extra clicks won't break the bank, and you can always move them to your main inventory screen. (if your using my or somebody elses bag system) C) The item pane is probably the best manipulated widget in war3 via jass. You have so much control, you can print items, detect drag and drop, clicks and all sorts of nonsense. a simply interface with items is generally considered not too complex, (certainly better then an interface with dialogues or through the spellbook).

Abilities do not have such nicety. A) Spells are generally very important. A spell may make or break a character. An aura makes an army more powerful, resurrection makes you nigh unkillable, and death coil makes your hero a killing machine. B) Spells can't stack, and do not always have a passive nature, and some would break the game if they could. Two bashes doesn't make your guy bash or often, and some abilities you want to have hotkeyed at all time, you don't want to have to dig through a menu while getting beat on to cast fireball. C) Abilities suck cock in jass. These are the worst things ever. They make trackables look fun.

So, it becomes quiet clear that the best way to work with abilities was to make your spellbook completely through items. But while items in themselves aren't terribly important (I use them as a menu), it sucks to lose items for abilities. So using a spell item should move it on to your units pane. The problem comes in limited space. You still are limited to how many spells you can have out. The best option is to let them queue out on thier own. Put one too many ability in, and the first one you put in should automagically remove itself to make room. [first-in,first-out structure]. But, there's more you should also be able to remove spells from your pane. (I.e. you just learned totally-worthless-spell and want to try it out, after learning its useless you want to remove it and replace it with better-spell-that got removed from your hotkey area, this should be possible.) Thankfully you have more then one interaction with items. You could make click it again simply remove it. (I had it be drag to cancel for whatever retarded reason)

Of course, people could use this to cheat. Avatar offensively long cooldown could be overcome by you simply removing avatar and then readding it. The "easy" way to solve this was to use timers and have a timer start as soon as soon as the spell was cast. By easy, I mean this was really hard. This sucked a lot. Theres no way for you to tell if a spell is in cooldown.

Limiting of spelltypes. It would be a good idea to not let a unit stack many passives. Nor to let a unit have 5 auras out. Holy light and death coil together at once doesn't seem kosher. I mean it would be great for him, but it would probably be gross abuse of your intent. So we would have to type spells. This does have a unique advantage of allowing our queue system to be much more deterministic. Trying to add a aura will remove any existant aura for instance. If you add holy light, it will remove all dark spell types. This system is probably a pain to implement.

Now the negatives are many in building it, but there are many advantages.
1) you get virtually unlimited abilities.
2) you can quickly build an interface to add more abilities.
3) Will be pretty flexible, and would allow things like items that add ability levels, or give spells.

Now the problem is that I'm the only person who implemented such a system that I know of, and I really hated it, more over I've also overhauled every other system I've made except for it. There is a reason for this I'm quiet sure.
04-16-2006, 08:45 AM#2
Captain Griffen
...hang on a moment, what advantages does this have over just using multiple spellbook abilities not on the items...? Because if you do that, you can then easily dynamically add/remove them, level them up or down, etc. Also, do spells cast by items trigger the same events? If they don't, then the event responses are going to be very limited in your system.

Quote:
B) Spells can't stack, and do not always have a passive nature, and some would break the game if they could. Two bashes doesn't make your guy bash or often, and some abilities you want to have hotkeyed at all time, you don't want to have to dig through a menu while getting beat on to cast fireball.

Spells don't stack...? I put 6 abilities, all based on two abilities, onto a hero, and they worked fine, including the autocast - using the power of spell books. And I still had room for an aura. Though buffs spells don't stack, that can't be got around by using items, and I'm not sure how bashes in spell books stack.

Oh, and two hotkeys is very quick to press, you know. 1-2, done.
04-16-2006, 04:31 PM#3
weaaddar
The item only act as a menu. You click on an item it adds it to your ability pane. Spellbooks are clumsy. Having to remember that fireball is in the my spellbook for fire attacks->direct attacks is annoying. Having a menu that could be sorted by the player is simply better.
http://eden.rutgers.edu/~weaaddar/bag3a.w3x
Heres the old version. Its not quite my vision but its the closet thing to it anyones ever made.
04-16-2006, 05:08 PM#4
Naakaloh
Welcome back, weaaddar, for as long as you plan to stay.

While it's not the best solution in all cases, I think that the design of the map can settle a lot of these issues. For example, if you make the spellbook active only to allow changing spells when in town (much like Guild Wars, if you've ever played it), it would prevent cheating as long as the ability has no use in town. And of course the limiting of spell types would be a pain to implement as a system, but assuming your map is custom made, the spells could be balanced so that even having of many auras doesn't have much point or might be the entire point of the hero. I think your current system does have quite a bit of potential, but it seems like it hasn't been implemented appropriately in any map. At least this is the way I see it, I don't see any reason it wouldn't be feasible, but perhaps I just don't know what kind of map or implementation you have in mind.

Captain Griffen, spellbooks, as weaaddar said, are clumsy... sure it's just two hotkeys for one spell, but casting additional spells in succession requires two more keystrokes for each additional spell, that not only increases the time required (In the worst case, if in a multiplayer game with a delay, that delay is doubled.) but also increases the chance of making a mistake.
04-16-2006, 05:34 PM#5
weaaddar
I've never played guild wars, but I was thinking of somethign along the line of everquest/World of Warcraft.

You can hotkey a few spells. Its really woeful for me to look at the old code only because there was so much planned for the system but because warcraft 3 hadn't evolved enough, it was simply impossible to get any of that functionality. I wanted to have levelling spells before we even had SetUnitAbilityLevel. Its also an incongrous mess of hack after hack though.
04-18-2006, 08:23 PM#6
Anitarf
For a moment there, I thought you were talking about something else, something that seems like a cool idea as well: entirely switching the spells and the items. The main 4x3 command card could be used for the inventory, spellbooks, and all other structured, slow to access data. The 2x3 items card would be used for hotkeyed abilities/items.

The problem with this and any other similar customization is, it's very map-specific. You can't make a be-all end-all interface system, because each map has different ammounts of abilities/items/systems... Once you reach this level of complexity, you really can't make very generic solutions, unless you build the functionality up layer by layer ina modular fashion, from the most fundemental support functions like drag&drop detection to specific interface alteration templates, but this can be rather inefficient.
04-20-2006, 09:12 PM#7
weaaddar
It isn't really. The problem is modularaity of systems like mine is really impossible without true abstraction. We need either lambda calculus or true object oriented systems (which we'd use just like lambda calculus). The problem lies in triggers. They are procedural and we can't have it simply call certain functions without it knowing what those functions are. This is where the problem lies. If I could pass functions and all the data they need for it to run, it'd be much much easier. But we don't have that. Composing is the only way to do such a system. Its not bad though. Composition really just requires somebody to slog through making all these systems and figuring out how to time interupts around them. (i've already done most of this). it then unfortuantly requires you to build data structures by hand of all widgets that are affected by these systems (or through a script), because jass doesn't let us grab all data from data objects in the slk files/w3o files.
04-20-2006, 09:50 PM#8
PipeDream
Bah, it doesn't take much at all if you're willing to lose all type safety. Write all functions that need to be passed around such that their arguments go on integer registers udg_reg[i]. Have a stack in game cache that you push down and up when you respectively enter and exit a function. This lends itself to automated optimization by only pushing the regs that are overwritten and not pushing for tail calls and so forth.

It might be feasible to write a language very similar to jass that allows passing functions with arguments, and compiles it into jass that looks like the above. Treat jass like machine language, which it practically is. You don't /really/ get arguments on x86.
04-21-2006, 03:03 PM#9
weaaddar
You clearly don't look at much of my code. My memory managers are written like you'd write a memory management on ASM.

That said writing completely with registers and without type safety is relative insanity. Even in a high end language its such a horrible pain to write like that. You'd still need to manage everything by hand. Even with 8192 registers I'd never want to do it. Globals are too annoying to be my sole argument type. Jass also offers no optomization for tail calls anyway and so my nerdy joy gotten from CPS transforming my code would be for naught.
04-21-2006, 10:09 PM#10
PipeDream
Granted it's a joke, but you can write tail call optimized code in JASS. for example:
Collapse JASS:
function factorial takes nothing returns nothing
    call DestroyTimer(GetExpiredTimer())
    if(udg_i <= 1) then
        call BJDebugMsg(I2S(udg_accum))
    else
        set udg_accum = udg_accum * udg_i
        set udg_i = udg_i - 1
        call TimerStart(CreateTimer(),0,false,code factorial)
    endif
endfunction

The optimization is just for getting O(1) memory use anyway, what do we need that for? the point is being able to write sexy code. calling functions doesn't take up nasty game sync memory.

You only write with registers when you need to pass arguments.
04-21-2006, 11:48 PM#11
weaaddar
We don't know how jass handles function calling,you also shouldn't use timers for anything thats recursive! Timer of 0 has a delay. You should use triggers or execfunc. you also aren't continuation passing.
Collapse JASS:
function identity takes nothing returns nothing
         call BJDebugMsg(udg_res)
endfunction

function anonC takes nothing returns nothing
         set udg_i=udg_Mem[c+1]
         set udg_c=udg_Mem[c+2]
         set udg_res=i*udg_res
         call ExecFunc(udg_FuncArray[udg_Mem[udg_c]])
endfunction

function factorialC takes nothing returns nothing
         if(udg_i<=0)then
           set udg_res=1
           call ExecFunc(udg_FuncArray[udg_Mem[udg_c]])
         endif
         set udg_sc=udg_c
         set udg_c=udg_free
         set udg_Mem[c]=2
         set udg_Mem[c+1]=udg_i
         set udg_Mem[c+2]=udg_sc
         set free=free+3
         set udg_i=udg_i-1
         call ExecFunc(udg_FuncArray[1])
endfunction


And you'd call it by doing some nonsense like this.
Collapse JASS:
...
set udg_FuncArray[0]="identity"
set udg_FuncArray[1]="factorialC"
set udg_FuncArray[2]="anonC"
set udg_c=0
set udg_i=5
set udg_res=0
set udg_Mem[c]=udg_FuncArray[0]
set udg_free=udg_free+1
call ExecFunc(udg_FuncArray[1])  
....
jesus christ is this fucking ugly. I would never want to program like this. It feels just like asm, most of the battle lies in memory managment. This would leak like a bitch because I didn't bother building a memory managment system. But if you were clever you'd instead of just simply incrementing free make free a function (one you'd consider native enough so you can call) and have it delocate memory address that are considered redeemable. (ie. the ministacks I build between each function call)
In case you were wondering here is the corresponding scheme code without using annonymous continuations.
Collapse JASS:
(define memlist '())
(define (identity x)
  (display x))
(define (anon-c res)
  (let ((i (car memlist))
        (c (cadr memlist)))
        (set! memlist (cddr memlist))
    (c (* i res))))
(define (factorial-c i c)
  (if (<= i 0) (c 1)
      (begin
      (set! memlist (append (list i c) memlist))
      (factorial-c (- i 1) anon-c))))
The fact that this is lambda calc allows me to call c as a function instead of having to save arguments and arg.
04-22-2006, 12:54 AM#12
PipeDream
The point to that example was just getting O(1) memory use. I agree that it is a retarded way of doing things. ExecuteFunc interrupts the calling thread while a timer with 0 delay will wait, as far as I know. We don't even really need tail call optimization, we've got plenty of call stack space in JASS.
Your example, that function passing by hand in JASS is stupid, is convincing. But it does demonstrate that it's possible, and if someone really wanted it, he could write a compiler.
04-22-2006, 05:43 AM#13
weaaddar
the reason I use exececuteFunc is because i know that all my calls are tail form. Who cares if it interupts a thread thats only action on its to do list is to commit suicide?

Yeah its pretty simple for basic algebraic things to build such a compiler using scheme. But i'd then have to be able to give it a list of natives and even still the intracacy of thread interactions (like timers) would still require a clever bit of work.
04-22-2006, 06:14 AM#14
PipeDream
Since ExecuteFunc interrupts, you can use it in any case. Timer is the one that you need to be in tailform for, because if you don't return right after you're screwed. I just mean to point out that executefunc space fills because it interrupts, which isn't a big deal.
05-05-2006, 06:23 PM#15
SuperDuperGuy
The way I pass functions is the use of triggers and global variables. I made a complicated random map generator that functions are passed to the cursor to decide if a legal path is being generated (Like pass a function that tells the list object which kind of room to take out of the list of unvisited rooms) then I can call my random walk function with different types of mazes generated. All that needs to be done is have globals for all arguments and return variables. Set the globals before the function is called, then when the function is called, make locals to store all the arguments. This makes sure your arguments are never overridden.

I have a rouge like map, where items can be equipped as abilities (find a spell book of holy light, equip it, and it would show up on your command card) I haven't solved the cooldown problem yet though. Right now, all spells are put into different classes, so holy light is a divine spell, mirror image is an illusion spell, fan of knifes is a rouge ability. Every character has dummy hero abilities that are leveled up, like the cleric has divine hero ability that allows him to equip divine spells. The level of the ability determines what the max level of holy light he is able to equip. My interface is really simple, I just use an "Action" ability that is used to loot structures (find spell books in bookcases), open doors, pull levers, and when the Action ability targets an item, it is equpped. I nearly decided to have 4 of every ability to make players able to decide what slot and hotkey the spell would have, (Z, X, C, V) But I have no idea what strain that would have on the system. Plus I use pretty much every spell, so i would have to triple the amout of abilities already there, plus all of my dummy abilities. Not sure if i would use up all the ability codes. Otherwise i'll have to just make sure there are no conflicts in the classes, or between multiple classes a character has. This solves the holy light/death coil deal, no character has both necromancer and divine hero abilites.

Plus if I wanted to make all leveling up done on a dummy hero 'ability book', equiping would be eaiser, and it would free up the level up slot.

The town idea seems good, I'll have to think about whether it would work in my map (combat is allowed in towns) players could abuse the cooldowns in town as well. Maybe I should make it so when a spell is being learned, the player is more vonerable. Like critical hits more likely, and evasion taken away. then if the spell learning time is less than the cooldown, the player has to risk being more vonerable.