At last count, the ideas list (it’s a thing) has ninety-two vague notes on it, comprising actual problems, desired features, huge future projects, bugfixes and stuff to make immortal lives easier (you know, a command to do that one thing). The topic that I’ve picked out is pet renaming!
Reasons to pick renaming:
- It’s got nothing to do with lag and I am fuelled by your anguish.
- We’re currently running janky mobol to allow pet renaming (or at least, maybe just horse renaming?) and as a point of principle janky mobol should be replaced by janky code.
- It’s the evening for me and I have limited time so this should be pretty easy, given I can copy/paste a lot of it
- Ely wants it done, and see above point about glue.
- I ate carbs today.
- I don’t feel especially well and so non-complex projects are the order of the day.
- The children are also ill and therefore there will be multiple disruptions – therefore non-complex yadda yadda.
- A new command that does this one thing and nothing else.
- A broader command with subcommands that handles this and a future suite of associated commands – eg pet rename blah, pet info, pet diagnose etc
- Add a subcommand to an existing command
Here’s what I’m going with: name <target> <name>, eg, name dog Binky.
Here are our conditions for allowing the name to go ahead:
- Victim must be following you, and you must be their master. Which might, to be fair, be the same thing, and I will crib from the order command for this. Big fan of using stuff that, crucially, already works.
- Victim must be an NPC
- Victim must also NOT be a humanoid. I could limit this to animals and horses and both kinds of birds but I think !HUMANOID is probably as much as we want to do here. Not there are a huge amount of pet snakes around.
- While I think of it, we must specifically exclude rats and ravens from this, with the same error message as the ‘target not found’ so players, who are both cheeky and sneaky bastards, cannot use this to locate shadoweyes easily.
- No-one involved is fighting.
- The player’s position must be <= RESTING. I think it’s reasonable to rename your dog while sitting down.
If I think of other sensible conditions en route, I’m sure I’ll mention them. Fingers crossed. And with that utterly minimal level of planning completed, onwards we go!
First, I enter ‘name’ into the command table, adding the usual options, which are something along the line of minimum level, minimum position, what actual function it calls, and a couple of other bits and bobs. It’s added at the bottom of the table as the penultimate. The last line now needs renumbering, and the constant for number commands incrementing.
If you’re interested, WoTMUD has 433 distinct commands – depending on which version of the MUD you’re using.
I go to the Spawn and insist it goes to sleep. It insists on back scratches. Spawn is bloody weird.
It’s sort of a lie that we have 433 distinct commands – one of the Imm commands has at 25 subcommands. I digress.
In the same file, we add a function prototype for our new command, right underneath the massive list of the same. A function prototype is basically a way to tell our compiler that a function exists and it should go look for it. This stops the compiler crapping itself if our function does not appear ahead of the main function, which is probably won’t. I wrote an example of that for you, and it was a painful waste of time.
With that, we’ve achieved a state where we could actually type ‘name’ into the game and it would go looking for the relevant function. What happens if the function doesn’t exist? I have no idea, and I don’t especially relish the idea of finding out. Given we’ve prototyped it, it might be a compiler error – alternatively, it might be fine until we actually called it. I feel like this is the kind of thing Aureus might know, but to be fair he went to school before he started his lucrative career as a romance fraudster.
Time to write our actual function! We’re going to place it in the general ‘stuff that mobs do’ file, which seems appropriate (later I changed my mind).
CircleMUDs, of which we are one, have a setup where commands use a kind of template (sort of). I write ACMD, and then a name in brackets, and that automatically gives me access to a number of arguments sent over automatically from the command table – this is a #define, which is a little like a mud alias.
I get to write:
ACMD(do_name)
Instead of:
void do_name(pointer to character data, string containing argument, int containing command ID, int subcommand ID)
…which I think we can all agree is, if not completely user-friendly, then at least not as awful as it could be. I presume the abbreviation is ACMD because it’s A COMMAND, but who knows.
When we use ACMD like this, we always get:
- A pointer to the character (PC, mob, whatever) that entered the command
- A string containing the argument, which in this case is everything they typed after the command itself. If I enter ‘kill Vampa he’s a goddamn idiot’, then my argument string contains ‘vampa he’s a goddamn idiot’. The mud trims leading spaces and casts to lowercase for ease of processing.
- Command ID – which is just the number it appears in the command table.
- Subcommand ID – not used that much, but basically where the same function is called by multiple commands – eg, narrate, chat etc – this lets the function know what kind of thing is using it, which is occasionally helpful.
What we don’t get, and what we definitely need, is a pointer to our victim, and we’ll be needing to extract that from the argument string.
I have no idea how to do that off the top of my head, but of course this happens all the time in the game, so I’m going to head over to ‘charge’ and steal the code from there - but first we need to work on some sanity checks – which is to say, reasons why we can’t do this.
Flash likes functions to exit as fast as possible, so our first round of checks is on the person who used the command – this way we don’t do unnecessary string processing.
We now exit if:
- Player is an NPC
- Player is fighting
- Player’s position is below resting
- Player has no followers (and therefore cannot be the master of something in the same room)
We use a simple “send this string to character” function to send a fail message to the NPC – Imms occasionally switch into mobs to debug mobol, and it’s useful to know it’s a mob thing. Derp moment – we don’t need to check for position below resting because we’ve done that in the command table. We only need to check for fighting.
With that done, and the pre-victim sanity checks in place, we can turn to some argument processing – remember, we have a pointer to the person doing the command but we have nothing pointing to the pet we want to do it to.
We need to take account of players writing stupid stuff – there’s every chance our argument line could be ‘dog Binky the massive arsehole’, and we only want to assign Binky. It actually wouldn’t break anything to have the rest, but we probably don’t want people to do it anyway.
Luckily, I don’t have to actually do the string handling myself – there’s a built in function to strip out the first two commands from a string, so we declare two character buffers, arg1 and arg2, make them as big as they could be (mild waste of memory maybe but they’re destroyed when the function ends), and pass the argument variable, arg1 and arg2 into the function. Following this, the values of those buffers are:
argument: “dog Binky the massive arsehole”
arg1: “dog”
arg2: “Binky” (or possibly binky, I did say earlier this got lowercased)
Where’s the rest? Who cares. Not my problem, don’t have to deal with it.
Next, we steal the code from charge that takes the string ‘dog’ and finds the first visible thing in the room that corresponds to it, assuming dot notation (2.dog etc). If successful, a pointer is returned, and we’re calling that pointer ‘pet’, to make this all nice and clear.
Now we have:
- A pointer to our main protagonist
- A pointer to our victim, ‘pet’
- A character buffer, arg2, which contains the word ‘Binky’
- If pet is a humanoid, return snarky message about asking them their name
- Do they have a master, and is that master our player?
- Are they fighting?
While we think of it, add NPC check to pet to stop people attempting to rename themselves. Come on, people.
Time for a test compile. I’m sure it’ll be one hundred percent fine and have no compilation errors at all. Absolutely.
Compiler Errors:
- Left in an old character buffer name that needed switching to arg2. Actually this happened like five times, turns out writing a blog is super distracting.
- The name finder that stops you naming your pet after Vampa was not declared in scope. So I declared it in scope.
- Deleted some asterixes thus converting pointers into arrays. Don’t ask because I will be unable to provide a coherent answer.
- TOO MANY ARGUMENTS IN NEW COMMAND AAAAAAAAAAAA okay so I deleted one comma before a right parenthesis and that fixed that.
Code: Select all
Rufferto, faithful dog of the sage starts following you.
* HP:Healthy SP:Bursting MV:Fresh > name dog Binky the massive arsehole
* HP:Healthy SP:Bursting MV:Fresh > l dog
A small sign on a chain around its neck says 'My name is binky'.
Rufferto, faithful dog of the sage is in excellent condition.
Code: Select all
if (islower(*arg2)) *arg2 = toupper(*arg2);
Code: Select all
Rufferto, faithful dog of the sage starts following you.
* HP:Healthy SP:Bursting MV:Fresh > name dog Binky the massive arsehole
* HP:Healthy SP:Bursting MV:Fresh > l dog
A small sign on a chain around its neck says 'My name is Binky'.
Rufferto, faithful dog of the sage is in excellent condition.
Oh god I’m tired.
Fin.
(Time Elapsed: About three hours, all told.)