[PlotElement::] Plot Element. To write the Plot element (Pl) in the index. @ The mapping of time is on the one hand simpler than the mapping of space since there is only one dimension, but on the other hand more complex since scenes can be multiply present at the same instant of time (whereas rooms cannot be multiply present at the same point in space). We resolve this with a notation which takes a little bit of on-screen explanation, but seems natural enough to learn in practice. = void PlotElement::render(OUTPUT_STREAM, index_session *session) { tree_inventory *inv = Indexing::get_inventory(session); inter_tree *I = Indexing::get_tree(session); localisation_dictionary *LD = Indexing::get_localisation(session); linked_list *L = Indexing::get_list_of_scenes(session); @; @; @; } @ The sorted ordering is used as-is later on, when we get to the details, but for the tabulation it's refined further. First we have the start-of-play scenes, in sorted order; then scenes with a condition for their beginning (end 0), in sorted order; then scenes that don't, and which haven't been covered as a result of one of the earlier ones, also in sorted order. (This third category is usually empty except for scenes the author has forgotten about and created but never made use of.) @ = simplified_scene *ssc; LOOP_OVER_LINKED_LIST(ssc, simplified_scene, L) ssc->indexed_already = FALSE; LOOP_OVER_LINKED_LIST(ssc, simplified_scene, L) if ((FauxScenes::starts_at_start_of_play(ssc)) || (FauxScenes::is_entire_game(ssc))) PlotElement::index_from_scene(OUT, ssc, 0, START_OF_PLAY_END, NULL, session); LOOP_OVER_LINKED_LIST(ssc, simplified_scene, L) if ((FauxScenes::starts_on_condition(ssc)) && (FauxScenes::is_entire_game(ssc) == FALSE)) PlotElement::index_from_scene(OUT, ssc, 0, START_OF_PLAY_END, NULL, session); LOOP_OVER_LINKED_LIST(ssc, simplified_scene, L) if (ssc->indexed_already == FALSE) PlotElement::index_from_scene(OUT, ssc, 0, NEVER_HAPPENS_END, NULL, session); @ = HTML_OPEN("p"); Localisation::italic(OUT, LD, I"Index.Elements.Pl.LegendHeading"); WRITE(": "); PlotElement::scene_icon_legend(OUT, "WPB", LD, I"Index.Elements.Pl.WPBLegend"); WRITE("; "); PlotElement::scene_icon_legend(OUT, "WhenC", LD, I"Index.Elements.Pl.WhenCLegend"); WRITE("; "); PlotElement::scene_icon_legend(OUT, "Segue", LD, I"Index.Elements.Pl.SegueLegend"); WRITE("; "); PlotElement::scene_icon_legend(OUT, "Simul", LD, I"Index.Elements.Pl.SimulLegend"); WRITE("; "); PlotElement::scene_icon_legend(OUT, "WNever", LD, I"Index.Elements.Pl.WNeverLegend"); WRITE("; "); PlotElement::scene_icon_legend(OUT, "ENever", LD, I"Index.Elements.Pl.ENeverLegend"); WRITE("; "); PlotElement::scene_icon_legend(OUT, "Recurring", LD, I"Index.Elements.Pl.RecurringLegend"); WRITE(". "); Localisation::italic(OUT, LD, I"Index.Elements.Pl.LegendNote"); HTML_CLOSE("p"); @ = IndexUtilities::anchor(OUT, I"SDETAILS"); simplified_scene *ssc; LOOP_OVER_LINKED_LIST(ssc, simplified_scene, L) { HTML_TAG("hr"); @; } @ The curious condition about end 1 here is to avoid printing "Ends: Never" in cases where fancier ends for the scene exist, so that the scene can, in fact, end. @ = @; if (FauxScenes::is_entire_game(ssc)) @; for (int end=0; end 2) && (FauxScenes::has_anchor_condition(ssc->ends[1]) == FALSE) && (ssc->ends[1]->anchor_connectors==NULL)) continue; @; @; if (end == 0) @; } @ = HTML::open_indented_p(OUT, 1, "hanging"); IndexUtilities::anchor_numbered(OUT, ssc->allocation_id); Localisation::bold_t(OUT, LD, I"Index.Elements.Pl.SceneName", Metadata::read_textual(ssc->pack, I"^name")); IndexUtilities::link_package(OUT, ssc->pack); if (FauxScenes::recurs(ssc)) { WRITE("  "); Localisation::italic(OUT, LD, I"Index.Elements.Pl.Recurring"); } HTML_CLOSE("p"); @ = HTML::open_indented_p(OUT, 1, "tight"); Localisation::roman(OUT, LD, I"Index.Elements.Pl.EntireGame"); HTML_CLOSE("p"); @ = int rbc = 0; inter_package *pack; LOOP_OVER_INVENTORY_PACKAGES(pack, i, inv->rulebook_nodes) if (IndexRules::is_contextually_empty(I, pack, IndexRules::scene_context(ssc)) == FALSE) { if (rbc++ == 0) { HTML::open_indented_p(OUT, 1, "hanging"); Localisation::italic(OUT, LD, I"Index.Elements.Pl.During"); HTML_CLOSE("p"); } HTML::open_indented_p(OUT, 2, "hanging"); WRITE("%S", Metadata::read_textual(pack, I"^printed_name")); HTML_CLOSE("p"); IndexRules::rulebook_list(OUT, I, pack, I"", IndexRules::scene_context(ssc), session); } @ = HTML::open_indented_p(OUT, 1, "hanging"); if (end == 0) Localisation::italic(OUT, LD, I"Index.Elements.Pl.BeginsWhen"); if (end == 1) Localisation::italic(OUT, LD, I"Index.Elements.Pl.EndsWhen"); if (end >= 2) Localisation::italic_t(OUT, LD, I"Index.Elements.Pl.EndsUnusuallyWhen", FauxScenes::end_name(ssc->ends[end])); WRITE(" "); int count = 0; @; @; @; if (count == 0) Localisation::bold(OUT, LD, I"Index.Elements.Pl.Never"); HTML_CLOSE("p"); @ = if ((end==0) && (FauxScenes::starts_at_start_of_play(ssc))) { if (count > 0) { HTML_TAG("br"); Localisation::italic(OUT, LD, I"Index.Elements.Pl.OrWhen"); WRITE(" "); } WRITE("play begins"); count++; } @ = if (FauxScenes::has_anchor_condition(ssc->ends[end])) { if (count > 0) { HTML_TAG("br"); Localisation::italic(OUT, LD, I"Index.Elements.Pl.OrWhen"); WRITE(" "); } WRITE("%S", FauxScenes::anchor_condition(ssc->ends[end])); int at = FauxScenes::anchor_condition_set_at(ssc->ends[end]); if (at > 0) IndexUtilities::link(OUT, at); count++; } @ = for (simplified_connector *scon = ssc->ends[end]->anchor_connectors; scon; scon=scon->next) { if (count > 0) { HTML_TAG("br"); Localisation::italic(OUT, LD, I"Index.Elements.Pl.OrWhen"); WRITE(" "); } simplified_scene *to_ssc = FauxScenes::connects_to(scon, session); text_stream *NW = FauxScenes::scene_name(to_ssc); WRITE("%S %s", NW, (FauxScenes::scon_end(scon)==0)?"begins":"ends"); if (FauxScenes::scon_end(scon) >= 2) WRITE(" %S", FauxScenes::end_name(to_ssc->ends[FauxScenes::scon_end(scon)])); IndexUtilities::link(OUT, FauxScenes::scon_at(scon)); count++; } @ = inter_symbol *rb = FauxScenes::end_rulebook(ssc->ends[end]); inter_package *rb_pack = Inter::Packages::container(rb->definition); if (IndexRules::is_empty(I, rb_pack) == FALSE) { HTML::open_indented_p(OUT, 1, "hanging"); Localisation::italic(OUT, LD, I"Index.Elements.Pl.WhatHappens"); WRITE(":"); HTML_CLOSE("p"); IndexRules::rulebook_list(OUT, I, rb_pack, I"", IndexRules::no_rule_context(), session); } @h Table of Scenes. We finally return to the table of scenes. The following is recursive, and is called at the top level for each scene in turn which starts at the start of play (see above). A scene entry can be arrived at in three ways: through one of its ends, in which case |end| is the number (0 for begins, 1 for standard ends, and so on), or through being already active at the start of play, or through being covered in the index even though it never happens in play. This means we need two additional |end| numbers. They are only ever used at the top level, that is, on the initial call when |depth| is 0. @d START_OF_PLAY_END -1 @d NEVER_HAPPENS_END -2 = void PlotElement::index_from_scene(OUTPUT_STREAM, simplified_scene *ssc, int depth, int end, simplified_scene *sc_from, index_session *session) { linked_list *L = Indexing::get_list_of_scenes(session); HTML::open_indented_p(OUT, depth+1, "tight"); @; @; if (ssc->indexed_already == FALSE) { @; @; } HTML_CLOSE("p"); if (ssc->indexed_already) return; ssc->indexed_already = TRUE; @; } @ = switch(end) { case 0: PlotElement::scene_icon(OUT, "Simul"); break; case 1: PlotElement::scene_icon(OUT, "Segue"); break; case START_OF_PLAY_END: break; case NEVER_HAPPENS_END: PlotElement::scene_icon(OUT, "WNever"); break; default: PlotElement::scene_icon(OUT, "Segue"); WRITE("[ends %S] ", FauxScenes::end_name(sc_from->ends[end])); break; } if ((ssc->indexed_already == FALSE) || (depth == 0)) { if (FauxScenes::is_entire_game(ssc)) PlotElement::scene_icon(OUT, "WPB"); else if (FauxScenes::starts_on_condition(ssc)) PlotElement::scene_icon(OUT, "WhenC"); if (FauxScenes::starts_at_start_of_play(ssc)) PlotElement::scene_icon(OUT, "WPB"); } @ = if (ssc->indexed_already) WRITE(""); WRITE("%S", Metadata::read_textual(ssc->pack, I"^name")); if (ssc->indexed_already) WRITE(""); else IndexUtilities::below_link_numbered(OUT, ssc->allocation_id); @ = if (FauxScenes::never_ends(ssc)) PlotElement::scene_icon_append(OUT, "ENever"); @ = if (FauxScenes::recurs(ssc)) PlotElement::scene_icon_append(OUT, "Recurring"); @ And this is where the routine recurses, so that consequent scenes are tabulated underneath the present one, indented one step further in (since indentation is coupled to |depth|). First we recurse to scenes which end when this one does; then to scenes which begin when this one ends. @ = simplified_scene *ssc2; LOOP_OVER_LINKED_LIST(ssc2, simplified_scene, L) for (simplified_connector *scon = ssc2->ends[0]->anchor_connectors; scon; scon=scon->next) if ((FauxScenes::connects_to(scon, session) == ssc) && (FauxScenes::scon_end(scon) >= 1)) PlotElement::index_from_scene(OUT, ssc2, depth + 1, FauxScenes::scon_end(scon), ssc, session); LOOP_OVER_LINKED_LIST(ssc2, simplified_scene, L) for (simplified_connector *scon = ssc2->ends[0]->anchor_connectors; scon; scon=scon->next) if ((FauxScenes::connects_to(scon, session) == ssc) && (FauxScenes::scon_end(scon) == 0)) PlotElement::index_from_scene(OUT, ssc2, depth, FauxScenes::scon_end(scon), ssc, session); @ We have been using: = void PlotElement::scene_icon(OUTPUT_STREAM, char *si) { PlotElement::scene_icon_unspaced(OUT, si); WRITE("  "); } void PlotElement::scene_icon_append(OUTPUT_STREAM, char *si) { WRITE("  "); PlotElement::scene_icon_unspaced(OUT, si); } void PlotElement::scene_icon_legend(OUTPUT_STREAM, char *si, localisation_dictionary *LD, text_stream *gloss) { PlotElement::scene_icon_unspaced(OUT, si); WRITE(" "); Localisation::italic(OUT, LD, gloss); } void PlotElement::scene_icon_unspaced(OUTPUT_STREAM, char *si) { HTML_TAG_WITH("img", "border=0 src=inform:/scene_icons/%s.png", si); }