Stored actions and your former self

I’m using 6M62.

I discovered/noticed today that if you’ve got actions stored in tables (in your static source code) those actions are attributed explicitly to the ‘unidentified player’ at boot time.

For example - if you set up a game by saying ‘Jane is a woman’ and ‘The player is Jane’, Jane can’t execute any actions stored in tables (e.g. ‘taking inventory’) in the source. Trying them results in ‘your former self’ trying them.

To me, the preferred behaviour by Inform would be to attach the actions to whomever the player is determined to be at boot time by use of the ‘The player is so-and-so’ phrase. What do you think?

You can try this with these two examples. The first one shows the problem. The second one shows the problem being fixed by rewriting the action ‘when play begins’.

-Wade

[code]“Action thief” by Wade.

Test Room is a room.

John is a man in Test Room. John carries a sack.

The player is John.

Every turn:
choose row 1 in table of drama;
say “(trying ‘[action entry]’)”;
try action entry.

Table of drama
action
taking inventory

Test me with “wait”.[/code]

[code]“Action thief 2” by Wade Clarke

Test Room is a room.

John is a man in Test Room. John carries a sack.

The player is John.

When play begins:
choose row 1 in table of drama;
now action entry is taking inventory;
say “(By rewriting the action entry at boot time, we have reattributed it to the player-as-John.)”

Every turn:
choose row 1 in table of drama;
say “(trying ‘[action entry]’)[line break]”;
try action entry.

Table of drama
action (a stored action)
taking inventory

Test me with “wait”.[/code]

This is not entirely surprising. A stored action has to be specific about actor and nouns; it can’t refer to “the player”. So if you change player during play, this table won’t work. (You would have to use that extension that lets you modify stored actions.)

Of course you’re not changing the player during play.

I agree with this. Worth filing, although I don’t know if it’ll wind up counting as a bug or a feature request.

You should be able to store “John taking inventory” in the table I think?

Without all the complexity of being able to modify stored actions, a phrase to overwrite the actor (something like “to have (P - a person) try (A - an action)”) would probably be quite useful and perhaps not too hard to create.

I personally could store ‘John doing such-and-such’, but the rub is that this is an extension I’m making with pre-stored actions in the source. So if someone uses it to make a new game, and makes a new protagonist for that game, the shipped stored actions break.

Dannii, could you please elaborate on how your idea works in relation to my extension situation?

-Wade

Can you set up a table of rules rather than of stored actions? If you write

This is the whatever rule: try taking inventory.

…you’ll get the right (current-player) behavior.

(Or a rulebook of rules, rather than using a table at all. I like rulebooks. Rulebooks are cool.)

Wade, because stored actions were substantially changed in the new version I’ll have to wait for the Windows installer to be published before I can experiment sorry.

No worries Dannii. In the meantime, I’ve got things working again with some of Zarf’s ideas:

Thanks Zarf. I need to stick with the table, as it’s a grid of game features and hotkeys and corresponding bits and pieces. So I took your table-of-rules approach.

-Wade

Okay, this code will let you override the actor of an action. Let me know if it’s actually useful!

[code]Include (- Global substitute_actor; -) after “Definitions.i6t”.
Include (-
[ TryAction req by ac n s stora smeta tbits saved_command text_of_command;
if (stora) return STORED_ACTION_TY_New(ac, n, s, by, req, stora);
tbits = req & (16+32);
req = req & 1;
@push actor; @push act_requester; @push inp1; @push inp2;
@push parsed_number; smeta = meta;
actor = by; if (req) act_requester = player; else act_requester = 0;
if ( substitute_actor ) {
actor = substitute_actor;
substitute_actor = 0;
}

by = FindAction(ac);
if (by) {
	if (ActionData-->(by+AD_NOUN_KOV) == OBJECT_TY) inp1 = n;
	else { inp1 = 1; parsed_number = n; }
	if (ActionData-->(by+AD_SECOND_KOV) == OBJECT_TY) inp2 = s;
	else { inp2 = 1; parsed_number = s; }
	if (((ActionData-->(by+AD_NOUN_KOV) == UNDERSTANDING_TY) ||
		(ActionData-->(by+AD_SECOND_KOV) == UNDERSTANDING_TY)) && (tbits)) {
		saved_command = BlkValueCreate(TEXT_TY);
		BlkValueCast(saved_command, SNIPPET_TY, players_command);
		text_of_command = BlkValueCreate(TEXT_TY);
		BlkValueCopy(text_of_command, parsed_number);
		SetPlayersCommand(text_of_command);
		if (tbits == 16) {
			n = players_command; inp1 = 1; parsed_number = players_command;
		} else {
			s = players_command; inp2 = 1; parsed_number = players_command;
		}
		BlkValueFree(text_of_command);
		@push consult_from; @push consult_words;
		consult_from = 1; consult_words = parsed_number - 100;
	}
}

BeginAction(ac, n, s, 0, true);

if (saved_command) {
	@pull consult_words; @pull consult_from;
	SetPlayersCommand(saved_command);
	BlkValueFree(saved_command);
}

meta = smeta; @pull parsed_number;
@pull inp2; @pull inp1; @pull act_requester; @pull actor;
TrackActions(true, smeta);

];
-) instead of “Try Action” in “Actions.i6t”.

To have (P - a person) try (A - an action):
(- substitute_actor = {P}; {-try-action:A} -).[/code]

Thanks much Dannii.

I guess both ways suggested here of doing my thing are kind of hacky. One diverts actions through rules, the other makes actors try actions not assigned to them. However, seeing the hairy-looking I6 code for the latter, I’ll probably stick with the all-I7 diversion method because it means I can understand it and maintain it myself in the extension.

-Wade

Sure. But just FYI, the only I6 code change is this addition:

if ( substitute_actor ) { actor = substitute_actor; substitute_actor = 0; }

And maybe it will be useful to someone else in the future :slight_smile:

The table of rules thing would be slightly faster too, because it’s avoiding using stored actions as block values, which has lots of overhead. It would take more storage space though. But in Glulx probably neither difference will be noticeable to the player.

I’ve had pretty severe speed problems with running this thing online (haven’t retried that since switching to rules), but any ways I can get it faster are good.

EDIT - I just reviewed the speed online, and it appears to be up significantly to the point I no longer consider it an issue. It may be in issue in some heavy games, but the point is a decent example project now runs fine.

A lot has changed since I last reviewed the online speed. In order:

  1. We made the regular expression work more efficient
  2. I stopped running a certain keypress-constructing routine every turn, and now make the author call it if necessary
  3. Project moved to 6M62, which also brought Quixe 2.1.2
  4. We replaced stored actions with rules

Together this all seems to have done the trick. So thanks everyone (again)!

-Wade