A plugin for actions, by which animate characters change the world model.

§1. Support for actions is contained in the "actions" plugin, which occupies this entire chapter.

void ActionsPlugin::start(void) {
    ActionsNodes::nodes_and_annotations();

    PluginManager::plug(MAKE_SPECIAL_MEANINGS_PLUG, ActionsPlugin::make_special_meanings);
    PluginManager::plug(NEW_BASE_KIND_NOTIFY_PLUG, ActionsPlugin::new_base_kind_notify);
    PluginManager::plug(COMPILE_CONSTANT_PLUG, RTActions::actions_compile_constant);
    PluginManager::plug(OFFERED_PROPERTY_PLUG, ActionVariables::actions_offered_property);
    PluginManager::plug(OFFERED_SPECIFICATION_PLUG, ActionsPlugin::actions_offered_specification);
    PluginManager::plug(TYPECHECK_EQUALITY_PLUG, ActionsPlugin::actions_typecheck_equality);
    PluginManager::plug(PRODUCTION_LINE_PLUG, ActionsPlugin::production_line);

    Vocabulary::set_flags(Vocabulary::entry_for_text(L"doing"), ACTION_PARTICIPLE_MC);
    Vocabulary::set_flags(Vocabulary::entry_for_text(L"asking"), ACTION_PARTICIPLE_MC);
}

int ActionsPlugin::production_line(int stage, int debugging, stopwatch_timer *sequence_timer) {
    if (stage == INTER1_CSEQ) {
        BENCH(RTNamedActionPatterns::compile);
        BENCH(RTActions::ActionData);
        BENCH(RTActions::ActionCoding_array);
        BENCH(RTActions::ActionHappened);
        BENCH(RTActions::compile_action_routines);
    }
    return FALSE;
}

§2. It may be useful to distinguish three ideas right from the outset:

kind *K_action_name = NULL;
kind *K_stored_action = NULL;
kind *K_description_of_action = NULL;

§3. These are created by a Neptune file inside WorldModelKit, and are recognised by their Inter identifiers:

§4.

int ActionsPlugin::new_base_kind_notify(kind *new_base, text_stream *name, wording W) {
    if (Str::eq_wide_string(name, L"ACTION_NAME_TY")) {
        K_action_name = new_base; return TRUE;
    }
    if (Str::eq_wide_string(name, L"DESCRIPTION_OF_ACTION_TY")) {
        K_description_of_action = new_base; return TRUE;
    }
    if (Str::eq_wide_string(name, L"STORED_ACTION_TY")) {
        K_stored_action = new_base; return TRUE;
    }
    return FALSE;
}

§5. A stored action can always be compared to a gerund: for instance,

if the current action is taking something...

int ActionsPlugin::actions_typecheck_equality(kind *K1, kind *K2) {
    if ((Kinds::eq(K1, K_stored_action)) &&
        (Kinds::eq(K2, K_description_of_action)))
        return TRUE;
    return FALSE;
}

§6. Though K_action_name is very like an enumeration kind, its possible values, which correspond to action_name objects, are not strictly speaking instances in the Inform world model. (Because they do not have properties: see Action Variables for what they have instead.)

Two actions in particular are sacred: "going", because it has additional clauses in action patterns, and "waiting", because it is the default value for K_action_name values: waiting it is the zero of actions.

action_name *going_action = NULL;
action_name *waiting_action = NULL;

§7. These are recognised by their English names when defined by the Standard Rules. (So there is no need to translate this to other languages.)

<notable-actions> ::=
    waiting |
    going

§8. Because action_name values are not instances, we cannot recognise them when instances are created, and instead have to do it directly when this is called by the function creating them:

void ActionsPlugin::notice_new_action_name(action_name *an) {
    if (<notable-actions>(ActionNameNames::tensed(an, IS_TENSE))) {
        if ((<<r>> == 1) && (going_action == NULL)) going_action = an;
        if ((<<r>> == 0) && (waiting_action == NULL)) waiting_action = an;
    }
}

action_name *ActionsPlugin::default_action_name(void) {
    if (waiting_action == NULL) internal_error("wait action not ready");
    return waiting_action;
}

§9. And because K_action_name values have no properties, they cannot store a "specification" text as one, and have to make their own arrangements:

int ActionsPlugin::actions_offered_specification(parse_node *owner, wording W) {
    if (Rvalues::is_CONSTANT_of_kind(owner, K_action_name)) {
        IXActions::actions_set_specification_text(
            Rvalues::to_action_name(owner), Wordings::first_wn(W));
        return TRUE;
    }
    return FALSE;
}

§10. The rest of this section is given over to the Preform grammar for dealing with the special meaning "X is an action...", which creates new action names. These can be quite complicated:

Inserting it into is an action applying to two things.

Verifying the story file is an action out of world and applying to nothing.

int ActionsPlugin::make_special_meanings(void) {
    SpecialMeanings::declare(ActionsPlugin::new_action_SMF, I"new-action", 2);
    return FALSE;
}

action_name *an_being_parsed = NULL;
int ActionsPlugin::new_action_SMF(int task, parse_node *V, wording *NPs) {
    wording SW = (NPs)?(NPs[0]):EMPTY_WORDING;
    wording OW = (NPs)?(NPs[1]):EMPTY_WORDING;
    switch (task) {  "Taking something is an action."
        case ACCEPT_SMFT: Check that this validly declares an action10.1; break;
        case PASS_1_SMFT: Parse the subject and object phrases10.2; break;
    }
    return FALSE;
}

§10.1. Check that this validly declares an action10.1 =

    if (<new-action-sentence-object>(OW)) {
        if (<<r>> == FALSE) return FALSE;
        parse_node *O = <<rp>>;
        <np-unparsed>(SW);
        V->next = <<rp>>;
        V->next->next = O;
        return TRUE;
    }

§11. <nounphrase-actionable> here is an awkward necessity, designed to prevent the regular sentence "The impulse is an action name that varies" from being parsed as an instance of "... is an action ...", creating a new action.

<new-action-sentence-object> ::=
    <indefinite-article> <new-action-sentence-object-unarticled> |  ==> { pass 2 }
    <new-action-sentence-object-unarticled>                         ==> { pass 1 }

<new-action-sentence-object-unarticled> ::=
    action <nounphrase-actionable> |    ==> { TRUE, RP[1] }
    action                              ==> Issue PM_BadActionDeclaration problem11.1

<nounphrase-actionable> ::=
    ^<variable-creation-tail>           ==> { 0, Diagrams::new_UNPARSED_NOUN(W) }

<variable-creation-tail> ::=
    *** that/which vary/varies |
    *** variable

§11.1. Issue PM_BadActionDeclaration problem11.1 =

    Problems::Using::assertion_problem(Task::syntax_tree(), _p_(PM_BadActionDeclaration),
        "it is not sufficient to say that something is an 'action'",
        "without giving the necessary details: for example, 'Unclamping "
        "is an action applying to one thing.'");
    ==> { FALSE, NULL };

§10.2. Supposing that all that worked, the SP of the sentence is the name for the new action, and the OP can include a wide range of details about it.

Parse the subject and object phrases10.2 =

    if ((V->next) && (V->next->next))
        if (<action-sentence-subject>(Node::get_text(V->next))) {
            an_being_parsed = <<rp>>;
            an_being_parsed->indexing_data.designers_specification = V->next->next;
            ActionsPlugin::clear_clauses();
            <action-sentence-object>(Node::get_text(V->next->next));
        }

§12. The subject noun phrase needs little further parsing — it's the name of the action-to-be. A successful match here causes the new action_name structure to be created.

<action-sentence-subject> ::=
    <action-name> |  ==> Issue PM_ActionAlreadyExists problem12.1
    ...              ==> { 0, PL::Actions::act_new(W) }

§12.1. Issue PM_ActionAlreadyExists problem12.1 =

    StandardProblems::sentence_problem(Task::syntax_tree(),
        _p_(PM_ActionAlreadyExists),
        "that seems to be an action already existing",
        "so it cannot be redefined now. If you would like to reconfigure "
        "an action in the standard set - for instance if you prefer "
        "'unlocking' to apply to only one thing, not two - create a new "
        "action for what you need ('keyless unlocking', perhaps) and then "
        "change the grammar to use the new action rather than the old "
        "('Understand \"unlock [something]\" as keyless unlocking.').");
    ==> { fail nonterminal };

§13. The object NP is a sequence of "action clauses" which can occur in any order, which are allowed but not required to be delimited as a list, and which can inconveniently contain the word "and"; not only that, but note that in

applying to one thing and one number

the initial text "applying to one thing" would be valid as it stands.

<action-sentence-object> ::=
    <action-clauses> |                   ==> { 0, - }
    ...                                  ==> Issue PM_ActionClauseUnknown problem13.9

<action-clauses> ::=
    ... |                                         ==> { lookahead }
    <action-clauses> <action-clause-terminated> | ==> { R[2], - }; ActionsPlugin::clause(R[2]);
    <action-clause-terminated>                    ==> { R[1], - }; ActionsPlugin::clause(R[1]);

<action-clause-terminated> ::=
    <action-clause> , and |              ==> { pass 1 }
    <action-clause> and |                ==> { pass 1 }
    <action-clause> , |                  ==> { pass 1 }
    <action-clause>                      ==> { pass 1 }

<action-clause> ::=
    out of world |                       ==> { OOW_ACT_CLAUSE, - }; Make out of world13.1
    abbreviable |                        ==> { ABBREV_ACT_CLAUSE, - }; Make abbreviable13.2
    with past participle ... |           ==> { PP_ACT_CLAUSE, - }; Give irregular participle13.3
    applying to <action-applications> |  ==> { APPLYING_ACT_CLAUSE, - }
    requiring light                      ==> { LIGHT_ACT_CLAUSE, - }; Require light13.4

<action-applications> ::=
    nothing |
    one <act-req> and one <act-req> |    ==> Set kind and access for two13.7
    one <act-req> and <act-req> |        ==> Set kind and access for two13.7
    <act-req> and one <act-req> |        ==> Set kind and access for two13.7
    <act-req> and <act-req> |            ==> Set kind and access for two13.7
    nothing or one <act-req> |           ==> Set kind and access for an optional one13.5
    one <act-req> |                      ==> Set kind and access for one13.6
    two <act-req>   |                    ==> Set kind and access for one, doubling up13.8
    <act-req> |                          ==> Set kind and access for one13.6
    ...                                  ==> Issue PM_ActionMisapplied problem13.10;

<act-req> ::=
    <act-req-inner>                      ==> Check action kind13.11;

<act-req-inner> ::=
    <action-access> <k-kind> |           ==> { R[1], RP[2] }
    <k-kind>                             ==> { UNRESTRICTED_ACCESS, RP[1] }

<action-access> ::=
    visible |                            ==> { DOESNT_REQUIRE_ACCESS, - }
    touchable |                          ==> { REQUIRES_ACCESS, - }
    carried                              ==> { REQUIRES_POSSESSION, - }

§13.1. Make out of world13.1 =

    ActionSemantics::make_action_out_of_world(an_being_parsed);

§13.2. Make abbreviable13.2 =

    ActionNameNames::make_abbreviable(an_being_parsed);

§13.3. Give irregular participle13.3 =

    ActionNameNames::set_irregular_past(an_being_parsed, GET_RW(<action-clause>, 1));

§13.4. Require light13.4 =

    ActionSemantics::make_action_require_light(an_being_parsed);

§13.5. Set kind and access for an optional one13.5 =

    ActionSemantics::give_action_an_optional_noun(an_being_parsed, R[1], RP[1]);

§13.6. Set kind and access for one13.6 =

    ActionSemantics::give_action_one_noun(an_being_parsed, R[1], RP[1]);

§13.7. Set kind and access for two13.7 =

    ActionSemantics::give_action_two_nouns(an_being_parsed, R[1], RP[1], R[2], RP[2]);

§13.8. Set kind and access for one, doubling up13.8 =

    ActionSemantics::give_action_two_nouns(an_being_parsed, R[1], RP[1], R[1], RP[1]);

§13.9. Issue PM_ActionClauseUnknown problem13.9 =

    StandardProblems::sentence_problem(Task::syntax_tree(),
        _p_(PM_ActionClauseUnknown),
        "the action definition contained text I couldn't follow",
        "and may be too complicated.");

§13.10. Issue PM_ActionMisapplied problem13.10 =

    StandardProblems::sentence_problem(Task::syntax_tree(),
        _p_(PM_ActionMisapplied),
        "an action can only apply to things or to kinds of value",
        "for instance: 'photographing is an action applying to "
        "one visible thing'.");
    ==> { REQUIRES_ACCESS, K_thing };

§13.11. Check action kind13.11 =

    int A = R[1]; kind *K = RP[1];
    if (Kinds::eq(K, K_thing)) {
        if (A == UNRESTRICTED_ACCESS) A = REQUIRES_ACCESS;
        ==> { A, K_object };
    } else if (Kinds::Behaviour::is_subkind_of_object(K)) {
        Issue PM_ActionMisapplied problem13.10;
    } else if (A != UNRESTRICTED_ACCESS) {
        Issue PM_ActionMisapplied problem13.10;
    } else {
        ==> { A, K };
    }

§14. For years this was not erroneous, but you now can't write, say, "X is an action applying to nothing, applying to nothing, requiring light and applying to nothing".

define OOW_ACT_CLAUSE 1
define PP_ACT_CLAUSE 2
define APPLYING_ACT_CLAUSE 3
define LIGHT_ACT_CLAUSE 4
define ABBREV_ACT_CLAUSE 5
int an_clauses_filled[6];
void ActionsPlugin::clear_clauses(void) {
    for (int i=1; i<=5; i++) an_clauses_filled[i] = FALSE;
}
void ActionsPlugin::clause(int N) {
    if (an_clauses_filled[N])
        StandardProblems::sentence_problem(Task::syntax_tree(),
            _p_(PM_ActionClauseRepeated),
            "that seems to repeat a clause",
            "or to specify something twice over.");
    an_clauses_filled[N] = TRUE;
}