I have stumbled onto two ways of performing side effects as part of movement in PunyInform.
The most obvious alternative is to assign a routine to the direction in question:
Object Library "The Library"
with
description "You are in a library.",
s_to [;
print "You somersault out the window.^^";
return Parking_Lot;
];
but it’s also possible to hook into this functionality with a before routine:
Object Library "The Library"
with
description "You are in a library.",
s_to Parking_Lot,
before [;
Go: if (selected_direction == s_to)
print "You somersault out the window.^^";
];
Although this leads to longer code, it makes it more clear that what’s south of the library cannot depend on conditions, but is always the parking lot.
Which is preferable, and what are the nuances that go into that decision?
This question (and those that follow) may seem silly or stupid, but the reason I ask is I want to write my games in the way that makes them as easy as possible to maintain (troubleshoot, expand, modify) later, and being inexperienced at this craft, I honestly don’t know the long-term maintainability effects of these various ways of doing things.
I suppose a third related question: what if we want to make directions possible when they were previously blocked? We could assign them directly
Object Library "The Library"
with
description "You are in a library.",
s_to nothing;
Object -> window "window"
with
name 'window',
before [;
Attack:
Library.s_to = Parking_Lot;
"The window shatters into tiny pieces, revealing
an opening to the south.";
];
but we could also conditionally allow the direction in the parent object:
Object Library "The Library"
with
description "You are in a library.",
s_to [; if (window has general) return Parking_Lot; ];
Object -> window "window"
with
name 'window',
before [;
Attack:
give self general;
"The window shatters into tiny pieces.";
];
The benefit of this approach is that it’s always clear what’s intended to be south of the library. The author cannot accidentally assign something else. But I suppose we could make this even more clear, and instead catch the player if they try to leave too early:
Object Library "The Library"
with
description "You are in a library.",
s_to Parking_Lot,
before [;
Go: if (selected_direction == s_to && window hasnt general)
"There's a huge window in the way!";
];
Object -> window "window"
with
name 'window',
before [;
Attack:
give self general;
"The window shatters into tiny pieces.";
];
Although now the code seems rather messy for what used to be a simple assignment.
i think it depends on your priorities. if readability is important, then using the BEFORE rule is probably best. but i’m guessing simply assigning a routine to a direction is probably fewer clock cycles and if game size/speed is a priority then that would be the way to go.
typically, if i’m using punyinform i’m concerned with game speed and size above everything else and readability kind of goes out the window. if you look at the punyinform library itself i think you’ll see that as well - it’s very dense, highly-optimized code.
These are all down to personal preference. There is no best way.
I’d rather use the implicit condition of using s_to rather than explicitly writing if (selected_direction == s_to). I also prefer to write
out_to [; <<Go s_obj>>; ];
(That’s the classic I6 library, rather than Puny, but you get the idea.)
It might make a difference if you have a separate Go check rule, like the classic “tied to a chair” or “stuck in quicksand”. But these cases are rare.
we could also conditionally allow the direction in the parent object:
I always prefer to check a condition rather than reassigning properties. Separates the question of “what changes this flag” from “what does this flag affect”.
Reassigning property values is generally okay in PunyInform for exit properties, such as s_to, out_to etc. I pretty much never do it myself, since I think it makes it too hard to follow the function and the intention of the code. (Yes, the library code is often hard to read, but that doesn’t mean your game code has to be)
It’s a different story with what we call the reactive properties in PunyInform: react_before, react_after, each_turn, add_to_scope, and possibly parse_name. Finding out which objects provide each of these routines is quite expensive, so the library tries to get away with not checking it all the time. This means that if you don’t have a react_before routine for an object, and you assign it one at some point in the game, it may not be considered immediately, maybe not ever, even. For these properties, it’s really better to always have a routine assigned, even if you just need it for part of the game.
This isn’t really a concern in PunyInform, but I would prefer before for printing the text, redirecting the Go action for aliased directions, and a routine that checks a flag instead of reassigning the property for connections that change in play.
Why? Because then you can check which room is in a given direction without actually going there, for things like an EXITS command or NPC movement or pathfinding. Or an automap, if you’re really ambitious and have a lot of RAM to spare.