Verb abuse - attributes for the web editor
mrangel
24 Dec 2020, 17:09This is something I've done a few times, but not sure if I ever shared. So if there's anyone else using the Quest web editor (for example because you've not got a Windows PC to run the desktop one), I thought it might be worth making a little post.
You presumably know that you can add attributes to an object by using its initialisation script; but this can be a little awkward when you have to enable it for every object. So why not abuse the verb system?
For example, I want to give every monster in my RPG a message which it says when it's defeated.
So I give each monster a verb called "defeatmsg", and give it a text response. I now have a string attribute named defeatmsg
, which I can use however I want.
Or another example. I've got a simple shop system, and I want some items to run a script when they're purchased. There's already a script to run after the player takes an object, but maybe I want something to run after the player buys something. Maybe some weapons have a random chance of coming with ammo, and the type is only determined after you buy it. And I need an attribute for the price as well. So I create a verb "price", and a verb "buy". The price is going to be a string; that's not a problem.
But then… what if the player types "defeatmsg dragon" or "price sword"? Even worse, what if they type "buy shotgun" and its initialisation happens early?
Well, there's a simple way around that. In the start script, I could put:
destroy ("price")
destroy ("defeatmsg")
to delete the verbs. Then I'd just have normal attributes. However, this isn't ideal. Quest builds a dictionary of verbs during initialisation, and deleting verbs at this point could cause errors in some circumstances, or cause the verbs that no longer exist to appear in automatically-generated verb menus.
It's better to do this in the UI Initialisation script - but that is also run when a saved game is loaded, so you need to check if the game is currently running, but in Quest that isn't hard.
There's a couple of other little tweaks as well, which make it a little more intuitive during the editing process. I can just do this once, and than use my "Verbs" tab as if it were the desktop editor's "attributes" tab.
So now my UI Initialisation script looks like this:
// Only do this when initialising the game for the first time
firsttime {
// destroy the command objects for the verbs that aren't actually verbs
destroy ("price")
destroy ("defeatmsg")
// We can convert the "price" attribute to an int as we start, to save effort later
foreach (obj, AllObjects()) {
if (HasString (obj, "price")) {
if (IsInt (obj.price)) {
obj.price = ToInt (obj.price)
}
}
}
// I'm not going to destroy 'buy', because it's useful. I just want it to not behave like a standard verb
// ... this means that the "Buy" menu will show up on the verbs list of any object with a "price"
// attribute, even though that isn't the name of the verb
buy.property = "price"
// ... and we change the 'script' for the buy command, so it does what we want:
buy.script => {
if (Got (object)) {
msg ("You already have it!")
}
else if (not IsShop (game.pov.parent)) {
// assuming that function esists
msg ("This isn't a shop.")
}
else if (HasString (object, "price")) {
// If the 'price' is a non-numeric string, assume it's a message saying why you can't buy it
msg (object.price)
}
else if (not HasInt (object, "price")) {
msg (CapFirst (WriteVerb (object, "isn't")) + " for sale.")
}
else if (object.price >= game.pov.money) {
msg (CapFirst (WriteVerb (object, "cost")) + " " + DisplayMoney (object.price) + " and you only have " + DisplayMoney (game.pov.money) + ".")
}
else {
// No problems buying that!
AddToInventory (object)
if (HasString (object, "buy")) {
msg (object.buy)
}
else if (HasScript (object, "buy")) {
do (object, "buy")
}
else {
msg ("You buy the {object:" + object.name + "}.")
}
}
}
}
Basically, once the game starts verbs are treated like any other command. You can change their attributes in the UI Initialisation script to change the way they behave:
property
is the name of the attribute that the verb checks for, when deciding whether or not it will appear in an automatically generated verb menu- Note that you can set this to
null
if you don't want it on the generated verbs list, or set theproperty
attribute of any command if you do want it to appear
- Note that you can set this to
displayverb
is the name that will display on those verb menusscript
is the script that will run for the command. By default, for verbs, it checks if the object has a string or script in the atribute named inproperty
, and displays/runs it. But you can replace it with any script you want.pattern
is a regexp to match the command. For the buy command, it will be^(buy|purchase) (?<object>.+)$
, but you can change it at any time before the first command is run.
OK, this post is longer than I intended. But hopefully, I've shown that you can use the "Verbs" tab for things it was never intended for, in order to make editing using the web editor just a little easier.