A plugin to provide support for backdrop objects, which are present as scenery in multiple rooms at once.


§1. Definitions.

§2. While we normally assume that nothing can be in more than one place at once, backdrops are an exception. These are intended to represent widely spread, probably background, things, such as the sky, and they placing one inside something generates FOUND_IN_INF rather than PARENTAGE_INF inferences to avoid piling up bogus inconsistencies.

    define FOUND_IN_INF 56     for backdrop things in many places
    define FOUND_EVERYWHERE_INF 57     ditto
    kind *K_backdrop = NULL;
    property *P_scenery = NULL;     an I7 either/or property marking something as scenery
    property *P_absent = NULL;     an I6-only property for backdrops out of play

§3.

    typedef struct backdrop_found_in_notice {
        struct instance *backdrop;
        struct inter_name *found_in_routine_iname;
        int many_places;
        MEMORY_MANAGEMENT
    } backdrop_found_in_notice;

The structure backdrop_found_in_notice is private to this section.

§4. Initialisation.

    void PL::Backdrops::start(void) {
        PLUGIN_REGISTER(PLUGIN_NEW_BASE_KIND_NOTIFY, PL::Backdrops::backdrops_new_base_kind_notify);
        PLUGIN_REGISTER(PLUGIN_NEW_PROPERTY_NOTIFY, PL::Backdrops::backdrops_new_property_notify);
        PLUGIN_REGISTER(PLUGIN_COMPLETE_MODEL, PL::Backdrops::backdrops_complete_model);
        PLUGIN_REGISTER(PLUGIN_LOG_INFERENCE_TYPE, PL::Backdrops::backdrops_log_inference_type);
        PLUGIN_REGISTER(PLUGIN_ESTIMATE_PROPERTY_USAGE, PL::Backdrops::backdrops_estimate_property_usage);
        PLUGIN_REGISTER(PLUGIN_INTERVENE_IN_ASSERTION, PL::Backdrops::backdrops_intervene_in_assertion);
    }

The function PL::Backdrops::start appears nowhere else.

§5. Kinds. This a kind name to do with backdrops which Inform provides special support for; it recognises the English name when defined by the Standard Rules. (So there is no need to translate this to other languages.)

    <notable-backdrops-kinds> ::=
        backdrop

§6.

    int PL::Backdrops::backdrops_new_base_kind_notify(kind *new_base, text_stream *name, wording W) {
        if (<notable-backdrops-kinds>(W)) {
            K_backdrop = new_base; return TRUE;
        }
        return FALSE;
    }

The function PL::Backdrops::backdrops_new_base_kind_notify is used in §4.

§7.

    int PL::Backdrops::object_is_a_backdrop(instance *I) {
        if ((Plugins::Manage::plugged_in(regions_plugin)) && (K_backdrop) && (I) &&
            (Instances::of_kind(I, K_backdrop))) return TRUE;
        return FALSE;
    }

The function PL::Backdrops::object_is_a_backdrop is used in §13.

§8. Properties. This is a property name to do with backdrops which Inform provides special support for; it recognises the English name when it is defined by the Standard Rules. (So there is no need to translate this to other languages.)

    <notable-backdrops-properties> ::=
        scenery

§9.

    int PL::Backdrops::backdrops_new_property_notify(property *prn) {
        if (<notable-backdrops-properties>(prn->name))
            P_scenery = prn;
        return FALSE;
    }

    int PL::Backdrops::object_is_scenery(instance *I) {
        if (World::Inferences::get_EO_state(Instances::as_subject(I), P_scenery) > 0)
            return TRUE;
        return FALSE;
    }

The function PL::Backdrops::backdrops_new_property_notify is used in §4.

The function PL::Backdrops::object_is_scenery is used in 3/sm (§19).

§10. Every backdrop needs a single-word property (found_in at the I6 level):

    int PL::Backdrops::backdrops_estimate_property_usage(kind *k, int *words_used) {
        if (Kinds::Compare::eq(k, K_backdrop)) *words_used += 2;
        return FALSE;
    }

The function PL::Backdrops::backdrops_estimate_property_usage is used in §4.

§11. Inferences.

    int PL::Backdrops::backdrops_log_inference_type(int it) {
        switch(it) {
            case FOUND_IN_INF: LOG("FOUND_IN_INF"); return TRUE;
            case FOUND_EVERYWHERE_INF: LOG("FOUND_EVERYWHERE_INF"); return TRUE;
        }
        return FALSE;
    }

The function PL::Backdrops::backdrops_log_inference_type is used in §4.

§12. Here we look at "in" and "part of" relationships to see if they concern backdrops; if they do, then they need to become FOUND_IN_INF inferences. Without this intervention, they'd be subject to the usual spatial rules and text like

The sky is in the Grand Balcony. The sky is in the Vizier's Lawn.

would lead to contradiction problem messages.

    int PL::Backdrops::assert_relations(binary_predicate *relation,
        instance *I0, instance *I1) {

        if ((Instances::of_kind(I1, K_backdrop)) &&
            ((relation == R_incorporation) ||
                (relation == R_containment) ||
                (relation == R_regional_containment))) {
            inference_subject *bd = Instances::as_subject(I1);
            inference_subject *loc = Instances::as_subject(I0);
            World::Inferences::draw(PART_OF_INF, bd, IMPOSSIBLE_CE, loc, NULL);
            World::Inferences::draw(IS_ROOM_INF, bd, IMPOSSIBLE_CE, NULL, NULL);
            World::Inferences::draw(FOUND_IN_INF, bd, CERTAIN_CE, loc, loc);
            return TRUE;
        }

        return FALSE;
    }

The function PL::Backdrops::assert_relations is used in 3/sr (§6.1).

§13. For indexing purposes, the following loops are useful:

    define LOOP_OVER_BACKDROPS_IN(B, P, I)
        LOOP_OVER_OBJECT_INSTANCES(B)
            if (PL::Backdrops::object_is_a_backdrop(B))
                POSITIVE_KNOWLEDGE_LOOP(I, Instances::as_subject(B), FOUND_IN_INF)
                    if (World::Inferences::get_reference_as_object(I) == P)
    define LOOP_OVER_BACKDROPS_EVERYWHERE(B, I)
        LOOP_OVER_OBJECT_INSTANCES(B)
            if (PL::Backdrops::object_is_a_backdrop(B))
                POSITIVE_KNOWLEDGE_LOOP(I, Instances::as_subject(B), FOUND_EVERYWHERE_INF)

§14. Since backdrops are contained using different mechanisms, the following (which does nothing if Backdrops isn't plugged in) adds backdrop contents to a room called loc, or lists backdrops which are "everywhere" if loc is NULL.

    void PL::Backdrops::index_object_further(OUTPUT_STREAM, instance *loc, int depth,
        int details, int how) {
        int discoveries = 0;
        instance *bd;
        inference *inf;
        if (loc) {
            LOOP_OVER_BACKDROPS_IN(bd, loc, inf) {
                if (++discoveries == 1) <Insert fore-matter 14.1>;
                Data::Objects::index(OUT, bd, NULL, depth+1, details);
            }
        } else {
            LOOP_OVER_BACKDROPS_EVERYWHERE(bd, inf) {
                if (++discoveries == 1) <Insert fore-matter 14.1>;
                Data::Objects::index(OUT, bd, NULL, depth+1, details);
            }
        }
        if (discoveries > 0) <Insert after-matter 14.2>;
    }

The function PL::Backdrops::index_object_further is used in 3/sm (§44).

§14.1. <Insert fore-matter 14.1> =

        switch (how) {
            case 1: HTML_OPEN("p"); WRITE("<b>Present everywhere:</b>"); HTML_TAG("br"); break;
            case 2: HTML_TAG("br"); break;
        }

This code is used in §14 (twice).

§14.2. <Insert after-matter 14.2> =

        switch (how) {
            case 1: HTML_CLOSE("p"); HTML_TAG("hr"); HTML_OPEN("p"); break;
            case 2: break;
        }

This code is used in §14.

§15. Everywhere. Here we defines a form of noun phrase special to Backdrops (because a backdrop can be said to be "everywhere", which nothing else can).

    <notable-backdrops-noun-phrases> ::=
        everywhere

§16.

    int PL::Backdrops::backdrops_intervene_in_assertion(parse_node *px, parse_node *py) {
        if ((ParseTree::get_type(py) == EVERY_NT) &&
            (<notable-backdrops-noun-phrases>(ParseTree::get_text(py)))) {
            inference_subject *left_subject = ParseTree::get_subject(px);
            if (left_subject == NULL)
                Problems::Issue::assertion_problem(_p_(PM_ValueEverywhere),
                    "'everywhere' can only be used to place individual backdrops",
                    "so although 'The mist is a backdrop. The mist is everywhere.' "
                    "would be fine, 'Corruption is everywhere.' would not.");
            else if (InferenceSubjects::domain(left_subject))
                Problems::Issue::subject_problem_at_sentence(_p_(PM_KindOfBackdropEverywhere),
                    left_subject,
                    "seems to be said to be 'everywhere' in some way",
                    "which doesn't make sense. An individual backdrop can be 'everywhere', "
                    "but here we're talking about a whole kind, and it's not allowed "
                    "to talk about general locations of a whole kind of things at once.");
            else Calculus::Propositions::Assert::assert_true_about(
                Calculus::Propositions::Abstract::to_put_everywhere(), left_subject, prevailing_mood);
            return TRUE;
        }
        return FALSE;
    }

The function PL::Backdrops::backdrops_intervene_in_assertion is used in §4.

§17. And this is where it makes the necessary inference after such a request has been asserted true:

    void PL::Backdrops::infer_presence_everywhere(instance *I) {
        if ((I == NULL) || (Instances::of_kind(I, K_backdrop) == FALSE)) {
            Problems::Issue::sentence_problem(_p_(PM_EverywhereNonBackdrop),
                "only a backdrop can be everywhere",
                "and no other kind of object will do. For instance, 'The sky is "
                "a backdrop which is everywhere.' is allowed, but 'The travelator "
                "is a vehicle which is everywhere.' is not.");
            return;
        }
        World::Inferences::draw(FOUND_EVERYWHERE_INF,
            Instances::as_subject(I), prevailing_mood, NULL, NULL);
    }

The function PL::Backdrops::infer_presence_everywhere appears nowhere else.

§18. Model completion. We intervene only at Stage II, the spatial modelling stage.

    int PL::Backdrops::backdrops_complete_model(int stage) {
        if (stage == 2) {
            P_absent = Properties::EitherOr::new_nameless(L"absent");
            Properties::EitherOr::implement_as_attribute(P_absent, TRUE);
            instance *I;
            LOOP_OVER_OBJECT_INSTANCES(I) {
                inter_name *FOUNDIN = NULL;
                <If the object is found everywhere, make a found-in property accordingly 18.1>;
                int room_count = 0, region_count = 0;
                <Find how many rooms or regions the object is found inside 18.2>;
                if ((FOUNDIN == NULL) && (room_count > 0) && (room_count < 16) && (region_count == 0))
                    <The object is found only in a few rooms, and no regions, so make it a list 18.3>;
                if ((FOUNDIN == NULL) && (room_count + region_count > 0))
                    <The object is found in many rooms or in whole regions, so make it a routine 18.4>;
                if ((FOUNDIN == NULL) && (Instances::of_kind(I, K_backdrop)))
                    <The object is found nowhere, so give it a stub found-in property and mark it absent 18.5>;
                if (FOUNDIN) PL::Map::set_found_in(I, FOUNDIN);
            }
        }
        return FALSE;
    }

The function PL::Backdrops::backdrops_complete_model is used in §4.

§18.1. <If the object is found everywhere, make a found-in property accordingly 18.1> =

        inference *inf;
        POSITIVE_KNOWLEDGE_LOOP(inf, Instances::as_subject(I), FOUND_EVERYWHERE_INF) {
            FOUNDIN = Hierarchy::find(FOUND_EVERYWHERE_HL);
            break;
        }

This code is used in §18.

§18.2. <Find how many rooms or regions the object is found inside 18.2> =

        inference *inf;
        POSITIVE_KNOWLEDGE_LOOP(inf, Instances::as_subject(I), FOUND_IN_INF) {
            instance *loc = World::Inferences::get_reference_as_object(inf);
            if ((K_region) && (Instances::of_kind(loc, K_region))) region_count++;
            else room_count++;
        }

This code is used in §18.

§18.3. <The object is found only in a few rooms, and no regions, so make it a list 18.3> =

        package_request *PR = Hierarchy::package_within(INLINE_PROPERTIES_HAP, Instances::package(I));
        FOUNDIN = Hierarchy::make_iname_in(INLINE_PROPERTY_HL, PR);
        packaging_state save = Packaging::enter_home_of(FOUNDIN);
        Emit::named_array_begin(FOUNDIN, K_value);
        inference *inf;
        POSITIVE_KNOWLEDGE_LOOP(inf, Instances::as_subject(I), FOUND_IN_INF)
            Emit::array_iname_entry(Instances::iname(World::Inferences::get_reference_as_object(inf)));
        Emit::array_end();
        InterNames::annotate_i(FOUNDIN, INLINE_ARRAY_IANN, 1);
        Packaging::exit(save);

This code is used in §18.

§18.4. <The object is found in many rooms or in whole regions, so make it a routine 18.4> =

        backdrop_found_in_notice *notice = CREATE(backdrop_found_in_notice);
        notice->backdrop = I;
        package_request *R = Instances::package(I);
        notice->found_in_routine_iname = Hierarchy::make_iname_in(BACKDROP_FOUND_IN_FN_HL, R);
        notice->many_places = TRUE;
        FOUNDIN = notice->found_in_routine_iname;

This code is used in §18.

§18.5. absent is an I6-only attribute which marks a backdrop has having been removed from the world model. It's not sufficient for an object's found_in always to say no to the question "are you in the current location?"; the I6 template code, derived from the old I6 library, requires absent to be set. So:

<The object is found nowhere, so give it a stub found-in property and mark it absent 18.5> =

        backdrop_found_in_notice *notice = CREATE(backdrop_found_in_notice);
        notice->backdrop = I;
        package_request *R = Instances::package(I);
        notice->found_in_routine_iname = Hierarchy::make_iname_in(BACKDROP_FOUND_IN_FN_HL, R);
        InterNames::to_symbol(notice->found_in_routine_iname);
        notice->many_places = FALSE;
        FOUNDIN = notice->found_in_routine_iname;
        Properties::EitherOr::assert(
            P_absent, Instances::as_subject(I), TRUE, CERTAIN_CE);

This code is used in §18.

§19.

    void PL::Backdrops::write_found_in_routines(void) {
        backdrop_found_in_notice *notice;
        LOOP_OVER(notice, backdrop_found_in_notice) {
            instance *I = notice->backdrop;
            if (notice->many_places)
                <The object is found in many rooms or in whole regions 19.1>
            else
                <The object is found nowhere 19.2>;
        }
    }

The function PL::Backdrops::write_found_in_routines appears nowhere else.

§19.1. <The object is found in many rooms or in whole regions 19.1> =

        packaging_state save = Routines::begin(notice->found_in_routine_iname);
        inference *inf;
        POSITIVE_KNOWLEDGE_LOOP(inf, Instances::as_subject(I), FOUND_IN_INF) {
            instance *loc = World::Inferences::get_reference_as_object(inf);
            Emit::inv_primitive(if_interp);
            Emit::down();
            if ((K_region) && (Instances::of_kind(loc, K_region))) {
                Emit::inv_call(InterNames::to_symbol(Hierarchy::find(TESTREGIONALCONTAINMENT_HL)));
                Emit::down();
                    Emit::val_iname(K_object, Hierarchy::find(LOCATION_HL));
                    Emit::val_iname(K_object, Instances::iname(loc));
                Emit::up();
            } else {
                Emit::inv_primitive(eq_interp);
                Emit::down();
                    Emit::val_iname(K_object, Hierarchy::find(LOCATION_HL));
                    Emit::val_iname(K_object, Instances::iname(loc));
                Emit::up();
            }
                Emit::code();
                Emit::down();
                    Emit::rtrue();
                Emit::up();
            Emit::up();
            Emit::rfalse();
            break;
        }
        Routines::end(save);

This code is used in §19.

§19.2. <The object is found nowhere 19.2> =

        packaging_state save = Routines::begin(notice->found_in_routine_iname);
        Emit::rfalse();
        Routines::end(save);

This code is used in §19.