Newbie Question: Implementing Player as an Outside Coordinator

Hi! So a problem I have sometimes with interactive fiction is players are often given a specific character to play for (and the reader might be disconnected from that character), or player characters are too blank in an attempt to be immersive. (This might also be a problem for only me specifically, as I’m neurodivergent and struggle with identifying with a lot of book characters, but I ask you to hear me out on this.)

I have some experience in TADS 3 (and a LOT of general coding experience), but I want some feedback on some initial ideas I had for addressing this minor problem, and ask if anyone knows of a better way to handle these ideas, before I drown myself in doing something the hardest-possible way (which my wife will attest that I often do).

All help and insight is appreciated!

The idea I want to implement in TADS 3 (adv3Lite library) is that the player is coordinating someone through a cellphone (without ever being in the world themselves), and so all commands entered into the story are messages being sent to this character through the phone. Effectively, you as the player have a goal, but you need to get another fully-realized character to achieve that goal for you, and so the character’s reactions to the situation can be both dynamic and described in detail, without the risk of ever feeling jarring to an immersed reader.

As another fun plot moment, I would like for the cellphone to eventually be handed off to another character, and the player begins messaging commands to them instead.

My initial ideas are as follows:

First idea is that the character you’re messaging is simply the player character described in third person. That would make it so you don’t have to constantly type in “Mida, take apple” or “Mida, push box north”, instead of the simpler “take apple” or “push box north”.

I know there’s also a way to change player characters, and I could just abort any action to give the phone to someone else until the story requires it.

The main obstacle with this is I want to do something like the classic text adventure called Lost Pig, where the action responses are all re-written to sound like something the main character would say, like “Grunk no see that”, etc. For example, if you enter in the command to jump, the response would describe Mida (the first main character) reading your message, chuckling, and replying back that the action sounds silly, and that she declines to jump. I would need to figure out how to change these action responses based on who the player character is, though, and I’m not sure how to do that (I’ve been reading a lot of the books that came with TADS 3 and adv3Lite).

Second idea is to make the cellphone item actually be the player character, and somehow reroute all commands to whatever NPC is currently carrying the phone in their inventory, so that the reader does not need to specifically mention the cellphone-holder by name every time they submit an action. I feel like this might be a more natural way of creating per-NPC responses for actions that cannot be taken, but I don’t have enough experience to be certain. I also am not sure how to reroute all commands this way. I’m assuming this might be a case of creating an exhaustive reroute list to a cellphone-holder object variable of the player, so that things like the undo command are not forwarded to the carrying NPC.

If anybody can shed any light on this, or provide any feedback, I would be extremely grateful!

2 Likes

There was a great game in this year’s IFComp, Closure, that was all about texting. It did it really well, although it sounds like you want to take the idea further. I recommend you play it… it may give you some insight into how someone else has dealt with some of these issues. I think that was an Inform game, though, so not sure at all how you could do this in TADS.

3 Likes

I just finished playing some of it lol. That’s some genius design. I’m glad it did well; the author should be proud of that. It was really cool because I was checking it out on my phone, so it was just like I was texting somebody!

I don’t think I want to lean into the visual style like Closure did. Without revealing too much, the basic idea for my story is you’re more of a computer construct in a network, so you’re not really seeing a messaging app, but instead directly wiretapping the network to message the main character, so I was considering styling some of the responses and narrative parts like a Linux terminal, but styling is something I want to worry about later. I’m more concerned about the potentially-daunting task of re-writing all action responses or re-routing all actions submitted in the game.

At this point, I’m more planning out the coding part of it, but I feel like there might be a smart way to go about this, specifically for handling actions per-character without constantly addressing the character. I’m just concerned that the code outline I have in mind might be the dumb way to implement it, so I’m checking in here just in case someone knows an easy way of mass-editing action responses per character, or mass-rerouting actions submitted by the player. I’ll go in and manually implement every action in the library, if that’s necessary. My main goal is just to make the characters feel more dynamic and alive if you type in an invalid command.

1 Like

I think that’s a great call. I’ve coded in adv3 and adv3Lite, and find the latter more nimble and easier to build from.

As Amanda said, check out Sarah Willson’s Closure. Also, the classic Infocom game Suspended requires the player to manipulate six robots, each with a specific skill, while confined inside a stasis tube.

adv3Lite (and adv3) have ways of issuing commands to NPCs built into the library. Check out this overview of programming NPCs in adv3Lite and section 14 of the “Learning adv3Lite” documentation.

In most TADS games, you supply a me object which acts as an agent for the player’s commands. This makes coding action handlers pretty straightforward, since there’s only one Actor which can perform the actions. The challenge you’ll have is some of your action handlers will have to check which Actor is performing the task and react appropriately.

In particular, you’ll probably be using adv3Lite’s ActorState class heavily, which organizes what commands the Actor reacts to at the moment, and what their reaction may be. You can use the same Actor and change their current ActorState depending on the situation, e.g., when they’re in the sauna, how they react when a specific Actor is in the room, etc. ActorState also makes it easy to define default reaction(s) for things the Actor can’t or won’t do at that moment.

adv3Lite can handle switching the current Actor. The gPlayerChar macro always points to the current “main” Actor.

Instead of making the phone the actual main character, I suggest building Actors for each character and have the act of handing the phone around switch which one of them is the current gPlayerChar.

The phone might be the main character in a storytelling sense, but I sense that’s more a narrative effect than a technical one.

UNDO is magic. As I understand things, it rewinds the game state of the entire VM, which prevents situations like you’re describing. I don’t think you’ll need to do anything special.

2 Likes

EDIT: So I did a lot of reading on NPCs, commands, ActorState objects, etc, and did some tests. Implementing an ActorState on an Actor did not change how it handles commands when that Actor is the gPlayerCharacter.

From what else I’m reading, it looks like I might be implementing a series of Doer objects, or I might be overriding the action response message list, and redefining them all with a method that sources the response from whichever character the player is currently set as. Would this be a bad idea, or this how it’s done?

The final product I’m aiming for would be something like:

> Yell
Mida’s response takes a moment to arrive, but it reads: “Um…no thanks? I don’t feel like yelling in the library.”

While the default behavior in most text adventures would be like:

> Yell
You scream as loud as you can.

And then if your gPlayerCharacter is, say Stefan, it might look like:

> Yell
There’s a delay, and Stefan’s message arrives: “Okay, I did that. It felt good, but I’m banned from the library now. Any other fun ideas, lol?”

EDIT 2:
Okay, so after a few crashes and a lot of experimentation, as well as the insights of jnelson, I think I’ve figured out how to do this. The player actor code is as follows to demonstrate responses for yelling:

Mida: Actor {
    'Mida;;mida'
    isHer = true
    person = 3

    wantsToYell = true // This can be changed to nil in other contexts

    actorAction() {
        if (gAction.ofKind(SystemAction))
            return; // Do not mess around with system actions!!
     
        if (gActionIs(Yell)) {
            if (wantsToYell == true) {
                "A message from Mida: <q>Uh...sure? Gimme a sec to hype myself up first, tho.</q><br>";
                return; // Action continues as normal.
            }
            else {
                "A message from Mida: <q>Uh...Nah. I'm not doing that right now.</q>";
                exit; // Action is cancelled.
            }
        }
    }

    afterAction() {
        if (gActionIs(Yell) && wantsToYell == true) // Follow-up message after action.
            "Another message follows shortly after: <br><q>That felt good, lol!</q>";
            return;
    }
}

Also worth pointing out that implementing a beforeAction() method for this character will (according to the docs) only occur if the action passes checks. However, if Mida has personal reasons to reject an action, even if she is logically able to execute it, then actorAction() seems to be called before any checks are made, as though she won’t even consider it.

Thank you everyone for your help and also the adventure recommendations!

I’m considering this one solved, and if anyone has any final corrections or input, I’ll be happy to read them and note them down!

ORIGINAL POST:

I had no idea that the IF community was so active that I would be getting responses this quickly; this is fantastic!

I will make sure to check out Suspended as well!

Okay, so to check for my understanding:

When an Actor object is assigned as the gPlayerChar, all commands/actions submitted by the player are sent to that actor to be evaluated (as though we were sending a command to an NPC), and I can use an ActorState class to handle the responses and executions of those actions (again, in a manner similar to how an NPC takes actions and commands)?

So, at that point, it’s just a matter of writing custom evaluations for all commands, grouping them by ActorState (so that when something would change how the player character reacts to a command, it’s handled correctly), and then ensuring that all ActorState objects (for every player character object) has enough overlap to handle the actions taken during the game. Am I following you correctly…?

And if I do understand this correctly, is it also correct that when you have a basic boilerplate gPlayerChar object of the Actor class (in a new project), then all of the ways the actions are handled for the player character are simply defined somewhere else in the normal library, and my ActorState implementations will just override those entirely?

Because I read somewhere in one of the manuals that I can modify an array of response messages for actions taken in the game, so I’m wondering if modifying those just modifies strings returned by the standard Actor. For example, a default Actor might have code that basically goes (using pseudo-code):

Actor {
    evaluateAction(action) {
        if (self == gPlayerChar) {
            sendMessage(referenceToMessageDefinedInMessagesList(action))
        }
        else {
            sendMessage(referenceToDefaultNPCMessageDefinedInList(action))
        }
    }
}

So instead of modifying the list of messages in the library (which is returned by standard action handlers), I am just modifying the class (and handlers) of the player character object to ignore that list, and instead use messages I’ve written in the ActorState object itself, yes?

Hopefully I don’t sound too convoluted; just making sure I’m understanding your explanation correctly, based on what I’ve read from the manuals…!

Also, I need to learn how to quote users on these forums. That’s handy…

And I thank you both again for your insights!

I really like the core idea for your game. There’s a lot you can do by mixing up the player/PC/agent relation.

Two games that did this really well:

Just highlight the part of the post you’d like to quote with the mouse. A handy “Quote”-option should appear, ready for you to click. The quoted sentence should then appear in your own post.

Lots of luck and perseverance to you.

I could have been clearer. The ActorState class is for other Actors responding to specific conversational-style commands. (You had mentioned one aspect of your game was the current main character telling the other character(s) what to do, but from your example I now see I misinterpreted that.) For example, it’s used to handle >TELL ALICE ABOUT WATERFALL or >GIVE BOB THE PEN.

If you’re happy with the approach you’re taking, I’ll let you keep going. You’re off to a good start.