6E72 changes behavior of "the current action"--intentional?

In 5Z71, the “current action” stored action was not set until after a command was parsed. This meant that you could refer to the previous turn’s command in the “after reading a command” activity of the current turn. This was handy if you had a two-stage command, since you could use the “current action” to find out which command precipitated the second stage, e.g.:

[code]Carry out being impertinent:
say “Ralph turns to you and asks who the hell you think you are.”;
change the command prompt to “Who the hell do you think you are?”;
now question mode is true.

After reading a command when question mode is true:
now the current answer is the player’s command;
follow the question rules.

A question rule when the current action is being impertinent:
if the current answer is “”:
say “Your silence only enrages Ralph. He asks again:[line break]”[/code]

In 6E72, however, the current action is set to “waiting” (i.e., the waiting action) before “after reading a command”. Does anyone know (or care to speculate) whether this is 1) an intentional change, and 2) why it might have been done?

Thanks,
Erik

A bit more on this:

First, if you want to test this behavior yourself, compile the following in 5Z71 and 6Exx:

[code]After reading a command:
say “Current action (after reading command): [current action].”

Every turn:
say “Current action (end of turn): [current action].”

Test me with “look / jump / wave / look”[/code]

Again, I can’t see any utility in reverting to “waiting” every turn, so I’m guessing that this is not intentional and is the by-product of some other change. Evidently “wait” is the fallthrough result for “the current action,” which is a decide phrase rather than a variable, so I’m guessing that the change in question occurs in this routine. Unfortunately, I’m not familiar enough with the code to really see where this could creep in. Here is the code for the decide phrase from each of the two builds:

5Z71:

[code]To decide which stored action is the current action:
(- STORED_ACTION_TY_Current({-pointer-to-new:STORED_ACTION_TY}) -)

[ STORED_ACTION_TY_Current stora at text_of_command;
if ((stora==0) || (BlkType(stora) ~= STORED_ACTION_TY)) return 0;
BlkValueWrite(stora, 0, action);
BlkValueWrite(stora, 1, noun);
BlkValueWrite(stora, 2, second);
BlkValueWrite(stora, 3, actor);
if (act_requester) BlkValueWrite(stora, 4, true); else BlkValueWrite(stora, 4, false);
at = FindAction(-1);
if ((at) && ((ActionData–>(at+AD_NOUN_KOV) == UNDERSTANDING_TY) ||
(ActionData–>(at+AD_SECOND_KOV) == UNDERSTANDING_TY))) {
text_of_command = BlkValueRead(stora, 5);
if (text_of_command == 0) {
text_of_command = INDEXED_TEXT_TY_Support(CREATE_KOVS);
BlkValueWrite(stora, 5, text_of_command);
}
INDEXED_TEXT_TY_Support(CAST_KOVS, players_command, SNIPPET_TY, text_of_command);
} else BlkValueWrite(stora, 5, 0);
return stora;
];[/code]

6E72:

[code]To decide which stored action is the current action:
(- STORED_ACTION_TY_Current({-pointer-to-new:stored action}) -)

[ STORED_ACTION_TY_Current stora at text_of_command;
if ((stora==0) || (BlkType(stora) ~= STORED_ACTION_TY)) return 0;
BlkValueWrite(stora, 0, action);
at = FindAction(-1);
if (ActionData–>(at+AD_NOUN_KOV) == OBJECT_TY) BlkValueWrite(stora, 1, noun);
else BlkValueWrite(stora, 1, parsed_number);
if (ActionData–>(at+AD_SECOND_KOV) == OBJECT_TY) BlkValueWrite(stora, 2, second);
else BlkValueWrite(stora, 2, parsed_number);
BlkValueWrite(stora, 3, actor);
if (act_requester) BlkValueWrite(stora, 4, true); else BlkValueWrite(stora, 4, false);
if ((at) && ((ActionData–>(at+AD_NOUN_KOV) == UNDERSTANDING_TY) ||
(ActionData–>(at+AD_SECOND_KOV) == UNDERSTANDING_TY))) {
text_of_command = BlkValueRead(stora, 5);
if (text_of_command == 0) {
text_of_command = INDEXED_TEXT_TY_Support(CREATE_KOVS);
BlkValueWrite(stora, 5, text_of_command);
}
INDEXED_TEXT_TY_Support(CAST_KOVS, players_command, SNIPPET_TY, text_of_command);
} else BlkValueWrite(stora, 5, 0);
return stora;
];
[/code]

Does the answer pop out at anyone?

Thanks,
Erik

I don’t see anything in that routine that would explain the difference. Perhaps “action” itself is being reset.

Thanks for the confirmation. I think you’re probably right. I’ll try to hunt down where that happens before I report this.

–Erik

Well, I’ve found where the action is set, and it looks like it is fully intentional–action is quite explicity set to ##Wait at the beginning of every turn. in Main(). I can’t imagine why we’d want to put a totally bogus action into the “current action” variable at the beginning of every turn (it would make a whole lot more sense to me to make it null, but that isn’t possible in I7).

Anyway, here’s the new Main() as of 6E59:

[ Main; #ifdef TARGET_ZCODE; max_z_object = #largest_object - 255; #endif; ProcessRulebook(STARTUP_RB); #ifdef DEBUG; InternalTestCases(); #endif; while (true) { while (deadflag == false) { EarlyInTurnSequence = true; action = ##Wait; meta = false; noun = nothing; second = nothing; actor = player; FollowRulebook(TURN_SEQUENCE_RB); } if (FollowRulebook(SHUTDOWN_RB) == false) return; } ];

I guess the path of least resistance for my WIP is to just hack Main() and get that reassignment out of there.

–Erik

I think you’ve come at the answer from both sides: it is deliberately putting a null value in there, and the best choice for a “null action-name” (given I7’s policy towards null values) is Waiting.

Leaving the previous turn’s value in there is confusing for most users – it is much more likely to be an apparently-random starting value than a desired reference to the previous turn. It’s also not the best way to deal with the previous turn, because it gets reset by meta actions. Better to store the value in a global, and explicitly reset it “every turn” (which is bypassed for meta actions).

(This is not to say you shouldn’t hack Main – that will do what you want. But consider cases such as meta actions or failed actions.)

Actually, when I say “null” would be better, I mean a true null. “Waiting” is not a null value, even if Inform wants to try to use it as one. (It can serve as a null, but only when the player hasn’t typed WAIT; since we can’t ever be sure of that, the value is useless for testing. The same is true of the use of “Figure of Cover” as the “null” for figure names. You can’t really use it as a null, because someone may want to draw the cover art, and so you have to hack something else up in order to get real null behavior.)

I realize that from the standpoint of I7’s current design–default values instead of nulls–this is the orthodox thing to do. It’s just that the design is not really useful to anyone but the designer.

Maybe, but at least using the previous turn is useful–the starting value is never useful (again, because you can’t be sure it didn’t originate with the player).

Meta-actions aren’t a problem for my WIP–entering a meta-command cancels the two-stage action, so it’s entirely appropriate that the current action variable is reset (the current action is being used to interpret specialized parsing–maybe the player is entering a number, or a real number, or arbitrary text, and we have to deal with that input in some way). That said, if I were starting this anew, I probably would have just stored the action in a global–but since I’m just trying to convert this thing to work with 6E72 and shove it out the door, I really don’t feel like manually editing nearly half of the actions in my WIP to get them working again :slight_smile:

I also feel a real deep-seated need to denounce the lack of null values in I7 whenever I can. Maybe one day some impressionable soul with agree with me and we can start a movement… :wink:

–Erik

A sometimes-meaningful value can lead to worse code than a consistent null value. I have no sympathy there. :slight_smile:

I do have sympathy with the null-value problem. There’s an entry on the suggestion forum (“Provide a special “nothing” constant”) which covers this, which you can throw your weight on. (I see you’ve commented there, but not voted.)

The particular wrinkle for I7 is that type-polymorphic values break many assumptions of the compiler. So you can’t just go and decide that “nothing” is a null value for all types. (It would be impossible to compile “say nothing” or “let localvar be nothing”.) It will have to be a bunch of type-specific nothings: “nothing as an action-name”, “nothing as a room”, etc.

Even were that not adopted, it would make sense for the library to define a nothing-doing to use in this particular case (instead of waiting). I see some headaches if the compiler defines an extra first-class value for user-defined types, and some different headaches if the compiler defines a typed synonym for 0 (for user-defined reference types). But the library defines a big pile of action-names, and one more won’t hurt anything.

I continue to think that it’s just as logical for the underlying variables to remain as the old values (the last turn) until actively reset by new values, but I have no problem ceding the point–I am a definite newbie in this weird world of code…

In any case, for my WIP I have discovered that in fact there is a way for me to change all of my actions to use a stored action variable with a single search and replace, so I’ve done that. (For code maintenance reasons–if Main changes again in the next build of Inform, I won’t have to change my hacked Main too.)

Thanks for pointing out that I hadn’t voted on my own request! I’ve reneged on a vote for something else (sorry, friend) to vote this one up.

Yes, that would make sense. (It’s how I dealt with the Figure of Cover Art problem I mentioned, by defining a separate null-figure and using that to test nullness.)

–Erik

I think I found the reason for this change:

Just FYI