One Framework or Two?

I’m working on a fairly large game that divides quite neatly into two parts. The two parts have the same characters, but not the same locations. (Well, four of the locations are the same, but now it’s night.)

I’m wondering whether to implement the second half (which I haven’t yet started, other than designing it) as a continuation of the first half, or whether to create an entirely new framework. This would involve, among other things, having two entirely different characters named Julia, two named Leo, and so on.

I’m using TADS 3, so creating new objects for the cast of characters in Part 2 is child’s play – I don’t need to worry about Inform’s issues with namespace clashes. What may prove to be less trivial is the question of tracking the characters’ knowledge, possessions, and so forth. It would be pretty easy to introduce bugs by forgetting to set up the second cast so that it duplicates the relevant features of the first cast. Likewise the rooms. Since there are four rooms that exist in both parts, I would need to make sure that anything dropped by the player in a Part 1 room got transferred silently to the “identical” Part 2 room when Part 2 starts.

If I don’t create an entirely new framework for Part 2, it seems to me I’ll have a lot MORE work to do. Some of the characters (again, this is a T3 thing – sorry) already have nine or ten ActorStates. If the same character has nine or ten more states in Part 2, the potential for the conversation system to get out of sync is huge.

Have other authors dealt with similar challenges? What did you do?

As you might know Jim, I don’t really know anything about Tads3 (or OOP for that matter), but you got me thinking. Since I sort of remembered that Tads has dynamic object creation, I wondered if it was possible to clone an object. It looks like it is (from tads.org/t3doc/doc/libref/ob … bject.html):

Now I don’t know how this handles any methods associated with the object and I don’t know what a constructor is, so this may be way off base. If this does what I think it does, instead of creating Julia #2 at the outset, you could wait until you need her and then clone her, insuring that at least the properties are duplicated with their current values. It looks like you’d still have to move the objects separately.

I’m sure someone else will come up with something better, but I thought I’d throw that out there. :slight_smile:

I guess it depends on how much of the world’s state would need to carry over from part 1 to part 2. If it’s just a couple of object lying in a room, I see no disadvantages of implementing the game’s parts separately. If you are using complex stuff like knowledge states for the actors which remain relevant in the second part, you might be better off reusing the actors.

Not a very helpful reply, perhaps, but this decision just depends very much on the details of your game. :slight_smile:

Is there a potential for the action to move back and forth between the two parts, or is the transition a one-shot where the player completes part 1 and then moves on to part 2 never to return?

I don’t know anything about TADS, but I would think that if its the former, using the “two separate frameworks” approach has to get a lot more complicated, because you would need to transfer the state of each character (and room) to his/its other-framework counterpart not just once, but every time the player moves from one “world” to the other.

Robert Rothman

To me, “part 1” and “part 2” doesn’t sound any more different than “intro” and “midgame”. That is, it has no special significance from a programming point of view. It’s just a screen in-between. If you need actors to use new states, drop the old states and assign new ones (to the same actor objects.) If you need to keep rooms around, keep them. You can assign different desc() methods to them in part 2. Etc, etc.

I hadn’t thought of moving the old ActorStates out and moving new ones in. That’s a good suggestion, and may make it more practical. There will also be different AskTopics and so forth, which could be handled with AltTopics, but that could easily get messy.

I can see that this thread will turn into a T3 how-to if we go much further. Maybe that’s the forum for it. But I’m still curious, as a general question, about the advantages and disadvantages of implementing what amounts to two separate games (and no, the player won’t be moving back and forth between them) within a single master game file.

My gut feeling is to use one framework and embrace the chaos. I’m guessing TADS lets you build your source code in separate files, so a clear system of file organization could potentially do as much as having separate frameworks. And test, test, test. Does TADS3 have an automated testing tool?

After a bit of refinement from Eric Eve (well, more than a bit, actually), we now have a working extension that allows the author to do I7-style test scripts. You can now enter test scripts into your code like this:

Test 'shell' [ 's', 'e', 'ne', 'take shell', 'sw' ] ;

…with which you can type ‘test shell’ in the game and run that series of commands automatically.

T3 has various other testing tools built in, including things like the ability to set breakpoints and watch expressions.

I think the central issue for me is that the NPCs can get themselves into so very many ActorStates. Almost every room an NPC is in demands a new ActorState, simply to use its specialDesc property (to print out the proper text when the player looks in that room). The potential to leave the NPC in the wrong ActorState is large. Plus, the way T3 works, conversation topics tend to want to be nested inside ActorStates. You don’t have to do it this way, but if Howard knows about the golden wombat in 12 of his possible states, but not in another 12, I’ll have to manually list the various states in Howard’s AskTopic for the golden wombat. If I later add another ActorState for Howard, I then need to remember to edit his AskTopic and TellTopic entries (and there could be dozens of them) so that the conversations will read naturally.

Creating two objects, howardOne and howardTwo, which appear to the player to be the same character, might conceivably simplify that process and make it more reliable.

Purely from a programming perspective, it sounds to me like you have too many variables to manage them with the single ActorState class that TADS3 gives you. Can ActorStates inherit from each other? Is multiple inheritance allowed?

From your description, it sounds like an actor can’t have multiple states at one time, but if you create a new kind of thing that represents just one aspect of a person’s condition (like what they are doing in each particular room), you might be able to take some of the burden off of the conversation-oriented state information. Independent variables shouldn’t be modeled by a single object - that makes for messy code.

You might consider handling the specialDesc at the Actor level rather delegating to ActorState. That could let you collapse the number of states to a more manageable level, if you have many more rooms than distinct ActorStates.

[rant][code]
stacks: Room ‘STACKS’
"You are surrounded by shelves of books. Stairs lead down. "
down = storage
;

storage: Room ‘STORAGE ROOM’
"Old books are heaped in piles on the floor. Stairs lead up. "
up = stacks
;

librarian: Person ‘librarian’ ‘LIBRARIAN’ @storage
"She’s a middle-aged librarian. "
globalParamName = ‘librarian’
isHer = true
specialDesc {
local desc = roomDescTable.subset({lst: lst[1].getOutermostRoom == getOutermostRoom
&& lst2});
if (desc && desc.length)
mainReport(desc[1][3]);
else
curState.specialDesc;

}
specialDescBeforeContents = true
roomDescTable = [
	[storage, {: true}, 'The librarian dusts off a book.'],
	[stacks, {: true}, 'You glimpse the librarian through the shelves.']
]

;

  • librarianFollowing: AccompanyingState
    stateDesc = "She’s standing beside you. "
    accompanyTravel(leadActor, conn) { return leadActor == gPlayerChar; }
    isInitState = true
    ;
    [/code][/rant]

The heart of this is the roomDescTable, which is a list of lists. The first element is the room object; the second element is an anonymous function that evaluates a logical condition; and the third is the description to print when the actor is in that room and the condition is true.

My thought is that the condition could check whether a particular scene was active. You could also use it to shuffle between descriptions within the same room.