B/acvt: Activities Template. @Purpose: To run the necessary rulebooks to carry out an activity. @------------------------------------------------------------------------------- @p The Activities Stack. Activities are more like nested function calls than independent processes; they finish in reverse order of starting, and are placed on a stack. This needs only very limited size in practice: 20 might seem a bit low, but making it much higher simply means that oddball bugs in the user's code -- where activities recursively cause themselves ad infinitum -- will be caught less efficiently. @c Constant MAX_NESTED_ACTIVITIES = 20; Global activities_sp = 0; Array activities_stack --> MAX_NESTED_ACTIVITIES; Array activity_parameters_stack --> MAX_NESTED_ACTIVITIES; @p Rule Debugging Inhibition. The output from RULES or RULES ALL becomes totally illegible if it is applied even to the activities printing names of objects, so this is inhibited when any such activity is running. |FixInhibitFlag| is called each time the stack changes and ensures that |inhibit_flag| has exactly this meaning. @c Global inhibit_flag = 0; Global saved_debug_rules = 0; [ FixInhibitFlag n act inhibit_rule_debugging; for (n=0:nn; if (act == PRINTING_THE_NAME_ACT or PRINTING_THE_PLURAL_NAME_ACT or PRINTING_ROOM_DESC_DETAILS_ACT or LISTING_CONTENTS_ACT or GROUPING_TOGETHER_ACT) inhibit_rule_debugging = true; } if ((inhibit_flag == false) && (inhibit_rule_debugging)) { saved_debug_rules = debug_rules; debug_rules = 0; } if ((inhibit_flag) && (inhibit_rule_debugging == false)) { debug_rules = saved_debug_rules; } inhibit_flag = inhibit_rule_debugging; ]; @p Testing Activities. The following tests whether a given activity |A| is currently running whose parameter-object matches description |desc|, where as usual the description is represented by a routine testing membership, and where zero |desc| means that any parameter is valid. Alternatively, we can require a specific parameter value of |val|. @c [ TestActivity A desc val i; for (i=0:ii == A) { if (desc) { if ((desc)(activity_parameters_stack-->i)) rtrue; } else if (val) { if (val == activity_parameters_stack-->i) rtrue; } else rtrue; } rfalse; ]; @p Emptiness. An activity is defined by its three rulebooks: it is empty if they are all empty. @c [ ActivityEmpty A x; x = Activity_before_rulebooks-->A; if (((rulebooks_array-->x)-->0) ~= NULL) rfalse; x = Activity_for_rulebooks-->A; if (((rulebooks_array-->x)-->0) ~= NULL) rfalse; x = Activity_after_rulebooks-->A; if (((rulebooks_array-->x)-->0) ~= NULL) rfalse; rtrue; ]; [ RulebookEmpty rb; if (((rulebooks_array-->rb)-->0) ~= NULL) rfalse; rtrue; ]; @p Process Activity Rulebook. This is really much like processing any rulebook, except that |self| is temporarily set to the parameter, and is preserved by the process. @c [ ProcessActivityRulebook rulebook parameter rv; @push self; if (parameter) self = parameter; rv = ProcessRulebook(rulebook, parameter, true); @pull self; if (rv) rtrue; rfalse; ]; @p Carrying Out Activities. This is a three-stage process; most activities are run by calling the following simple routine, but some are run by calling the three subroutines independently. @c [ CarryOutActivity A o rv; BeginActivity(A, o); rv = ForActivity(A, o); EndActivity(A, o); return rv; ]; @p Begin. Note that when an activity based on the conjectural ``future action'' is being run -- in a few parser-related cases, that is -- the identity of this action is put temporarily into |action|, and the current value saved while this takes place. That allows rules in the activity rulebooks to have preambles based on the current action, and yet be tested against what is not yet the current action. @c [ BeginActivity A o x; if (activities_sp == MAX_NESTED_ACTIVITIES) return RunTimeProblem(RTP_TOOMANYACTS); activity_parameters_stack-->activities_sp = o; activities_stack-->(activities_sp++) = A; FixInhibitFlag(); MStack_CreateAVVars(A); if (Activity_atb_rulebooks->A) { x = action; action = action_to_be; } o = ProcessActivityRulebook(Activity_before_rulebooks-->A, o); if (Activity_atb_rulebooks->A) action = x; return o; ]; @p For. @c [ ForActivity A o x; if (Activity_atb_rulebooks->A) { x = action; action = action_to_be; } o = ProcessActivityRulebook(Activity_for_rulebooks-->A, o); if (Activity_atb_rulebooks->A) action = x; return o; ]; @p End. @c [ EndActivity A o rv x; if ((activities_sp > 0) && (activities_stack-->(activities_sp-1) == A)) { if (Activity_atb_rulebooks->A) { x = action; action = action_to_be; } rv = ProcessActivityRulebook(Activity_after_rulebooks-->A, o); if (Activity_atb_rulebooks->A) action = x; activities_sp--; FixInhibitFlag(); MStack_DestroyAVVars(A); return rv; } return RunTimeProblem(RTP_CANTABANDON); ]; @p Abandon. For (very) rare cases where an activity must be abandoned midway; such an activity must be being run by calling the three stages individually, and |EndActivity| must not have been called yet. @c [ AbandonActivity A o; if ((activities_sp > 0) && (activities_stack-->(activities_sp-1) == A)) { activities_sp--; FixInhibitFlag(); MStack_DestroyAVVars(A); return; } return RunTimeProblem(RTP_CANTEND); ];