ID:178736
 
Hi, everybody.

Perhaps this is a question for the Design Philosophy board, but I still consider myself a newbie, so I'm asking it here.

Is it possible/can anybody think of a way to make certain verbs only accessible to certain people? More specifically, I'm programming a text-based MUD and setting up some verbs for special moves. Example, Heal1, Heal2, Heal3, Lightning1, Lighting2, whatever. I mean, the specials would be a lot more interesting than that. ;) But the point is that I could potentially have hundreds of them, or at least over 100, and obviously I don't want to give Level 1 characters access to all the spells right away, so I'd like to make all special moves inaccessible until the character is able to learn Heal2, for example, at which point it will appear on the Command list.

I wrote something somewhat similar once in C++ for this single player RPG I was making. The battle system was turn based rather than real time and of course, the interface was vastly different, but what I did there was I gave each character(mob) an array containing a list of reference numbers to specials they owned. The solution, however, was so inelegant that I won't even go into it. It happened to work well because all of the characters' specials were predetermined by the game designer (to wit, me), so I didn't chew up too much processing time looking them up.

One possible solution is to create an object, say "low-quality phase pistol", and then to associate the Special verb, say "Death Ray" with that object and set src = usr.contents so that you can only use the verb if you have this particular object in your inventory. This would work, though it seems a bit cumbersome (though now that I think about it, not nearly as bad as my knee-jerk reaction) and somewhat like a slight waste of resources (since you'd be duplicating code every once in a while, for example, if you wanted to have upgradable items that provided all the specials you already had associated with the item, and added another special--you'd have to repeat all the verb code from the lesser quality object--which would be calling procs, but still). Can anybody suggest an alternate way of handling this? Maybe something that doesn't necessarily require the possession of objects cluttering up your inventory.

I'm probably rambling. If so, I apologise. I get like this sometimes when I start coding. :)

-Justin
One way you can do this is with a datum type spell.

spell
parent_type = /obj
var/damage = ""
FireBlast
damage = "1d10"
DeathRay
damage = "12d2"
proc/Cast(mob/Target)
var/d = roll(damage)
Target << "You take [d] damage from [usr]'s [src]!"
Target.health -= d
mob
var/health = 100
verb/CastSpell()
var/mob/M = input("") as mob in oview()
var/spell/S = input("") as spell in src.contents
S.Cast(M)
src << "You cast [S] on [M]!"
In response to Lord of Water
Lord of Water,

I toyed with the notion of making special moves objects, and there are certain things I do actually like about that solution, to be sure. It's elegant, for one thing. I'm not 100% sure I'm not going to go with it, but there are a few associated pitfalls.

Not all Special Moves are going to be direct damage attacks. In the case of a heal spell, you could get around this pretty easily by simply negating the result of the dice roll, though it would still leave you with that pesky "You did -20 pts damage" message, even though I can think of a couple of relatively painless ways to make the damage report statement say what you want it to say. But you also have to consider that there are buff and debuff specials as well. You know, specials that will temporarily raise your strength, lower an enemy's quickness, etc. Then there are stamina restore specials, specials that will scan a mob's stats, specials that will possibly teleport you, and so on and so forth. Still, not impossible to handle with one umbrella SpellCast verb. You could, for example, associate some veriable, say "spelltype", with each spell object, where, say 1 is for heal spells, 2 for stamina restore spells, and so on. Then when you wrote your Cast procedure, you could run a switch statement or a bunch of else ifs where, if (src.spelltype == 1) [enter code for heal spells], and else if (src.spelltype == 2) [enter code for stamina restore spells]. It would work, to be sure. Perhaps well. I'm just not sure if it's my favourite solution, because you have to have an object in your inventory for every special you know how to do, which can get messy if you have, say, 30 special moves.

I do have a question, though, which I'd test out myself, but I'm not at home and don't have Byond installed here nor do I have my code. I'm still getting used to the syntax and I tend to write everything the way I'd write it in C++, so bear with me.

When you do

input("") as spell in src.contents

could you instead check a different list? Say, if you created a variable for each mob called special_list[] and you initialised it like special_list = list(""), and everytime you "learned" something new, you could just add the spell object to the list, would it then be possible to do

input("") as spell in src.special_list

? If so, it makes the object approach infinitely more appealing.

By the way...anybody know if there's any easy way to alphabetize the contents of a list? Or can you suggest a handy method whereby the user can reorganize the list? I've got an idea as to the latter, though I'm not really sure it's feasible.

-Justin
If you contact me on pager sometime, I'll send you my parser and show you how to use it. Building actions that work with the parser is more complex (since you have to add additional error-checking stuff that was already included with verbs), but once you get the hang of it, it gets easier. Making things available to certain players with the parser is much easier than with verbs.
In response to Lord of Water
Somebody tell me if what I'm doing is impossible.

I went ahead and created a subset of the object class called "special".

special
parent_type = /obj

Then I went ahead and created an object under there called Heal, which I did correctly, because I can populate the room with Heal spells, put them in my inventory, whatever. But what I really want to do is hide these special objects someplace else (read: not in the inventory). I was hoping I could put them in a list associated with my mobs, called specials_owned[]. Then I was going to follow LoW's, so I wrote this:

Special()
usr.specials_owned = list(/special/Heal)
var/mob/M = input("") as mob in view()
var/special/S = input("") in usr.specials_owned
if(S == null)
return //This was just debug
if(S.CheckSP())
S.ExecuteSpecial(M)

Unfortunately, even though I thought I put that object in a list, I either screwed up, or it isn't possible to do what I'm trying to do. If I chance usr.specials_owned to usr.contents, the verb will run fine, but I can't seem to get it to read the objects from the list. It's looking to me as though you can only have objects in actual physical space, so to speak (either in a room or on your person). Any help would be appreciated. Just let me know if I'm being unclear.
In response to TheWatcherUatu
I started with a simple answer, but my mind got away from me. Here's a spell system that includes three spell types: heal type, harm type, and bonus type. Have fun reading it!
mob
var
obj/spellbook/spellbook
mana = 500
health = 100
level = 1
damage = 7
attack_timer = 0
speed = 12 in 1 to 20
New()
spellbook = new /obj/spellbook (src.contents)
..()
while(1)
sleep(10)
if(mana < 490) mana += 10
else mana = 500
if(mana < 100) mana += 10
mana = round(mana,10)
if(health + 1 < 100) health += 1
else health = 100
Stat()
statpanel("Inventory",src.contents)
verb/Attack(mob/M as mob in oview(1))
if(attack_timer) return
view() << "([src]) Attacks: [M]"
var/minus = rand(round(damage/2,1),damage)
view() << "[minus] damage!"
attack_timer = 1
sleep(21-src.speed)
attack_timer = 0
client
Topic(href)
if(href == "Harm")
for(var/spell/Harm/S in mob.spellbook.spells)
var/mob/M = input("") in oview()
S.use(M)
if(href == "Heal Minor Wounds")
for(var/spell/Heal/S in mob.spellbook.spells)
var/mob/M = input("") in view()
S.use(M)
if(href == "Blazing Hands")
for(var/spell/Blaze/S in mob.spellbook.spells)
var/mob/M = input("") in view()
S.use(M)
spell
parent_type = /obj
var/cost = 0
verb/Add()
set src in usr.contents
if(usr.spellbook.addSpell(src)) del src
proc/use()
if(usr.mana < src.cost)
usr << "You've not enough mana to use [src]."
return 0
usr.mana -= src.cost
Harm
cost = 210
use(var/mob/M)
..()
var/damage = roll("[usr.level]d3")
M.health -= damage
usr << "You cast Harm on [M] for [damage] damage!"
M << "[usr] casts Harm on you for [damage] damage!"
Blaze
name = "Blazing Hands"
cost = 120
use(var/mob/M)
..()
if(M != usr) walk_to(usr,M,1)
BACK
if(get_dist(usr,M) > 1)
sleep(1)
goto BACK
view() << "([usr]) Blazing Hands: [M]."
var/bonus = roll("1d10")
M.damage += bonus
sleep(60)
M << "You feel the burn wearing off..."
M.damage -= bonus/3
sleep(140)
M << "The burn is gone."
M.damage -= 2*bonus/3
Heal
name = "Heal Minor Wounds"
cost = 40
use(var/mob/M)
..()
if(M != usr) walk_to(usr,M,1)
BACK
if(get_dist(usr,M) > 1)
sleep(1)
goto BACK
view() << "([usr]) Heal Minor Wounds: [M]"
usr.health += 20
obj/spellbook
name = "Spell Book"
var/list/spells = list()
proc
addSpell(var/spell/G)
var/spell/S = new G.type (null)
for(var/spell/M in spells)
if(M.type == S.type) return 0
spells.Add(S)
return 1
delSpell(var/spell/S)
if(S in spells)
spells.Remove(S)
return 1
return 0
Click()
var/browse_html = {"
<html>
<head>
<style>
a:link
{
color:red;
text-decoration:none;
}
a:hover
{
color:white;
text-decoration:none;
}
a:visited:link
{
color:red;
text-decoration:none;
}
a:visited:hover
{
color:white;
text-decoration:none;
}
</style>
<body bgcolor=BLACK text=WHITE>
<center>h3>Spells</h3>
<hr color=white size=1>
<small>Your Mana: !stat(mana)</small>
<table border=1 bordersize=1 cellpadding = 1 cellspacing = 0>
<tr><td>--</td><td>Spell Name</td><td>Spell Cost</td></tr>
!stat(replace)
</table>
<hr color=white size=1>
</body>
</html>
"}

var/number = 1
var/replace = ""
for(var/spell/S in spells)
replace += "<tr><td>[number]</td><td>\[ <a href=?[S.name]> ]</td><td>\[ [S.cost] ]</td></tr>"
number += 1
browse_html = treplace(browse_html,"!stat(mana)","[usr.mana]")
browse_html = treplace(browse_html,"!stat(replace)",replace)
usr << browse(browse_html)