diff --git a/README.md b/README.md index 57fd6d4fe..d95dc7393 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -[Version](notes/versioning.md): 10.2.0-beta+6V62 'Krypton' (16 September 2022) +[Version](notes/versioning.md): 10.2.0-beta+6V63 'Krypton' (17 September 2022) ## About Inform diff --git a/build.txt b/build.txt index d2c7adc39..739d7bd10 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: beta -Build Date: 16 September 2022 -Build Number: 6V62 +Build Date: 17 September 2022 +Build Number: 6V63 diff --git a/inbuild/supervisor-module/Chapter 5/Project Services.w b/inbuild/supervisor-module/Chapter 5/Project Services.w index 30f23669c..9b533ef5b 100644 --- a/inbuild/supervisor-module/Chapter 5/Project Services.w +++ b/inbuild/supervisor-module/Chapter 5/Project Services.w @@ -3,8 +3,7 @@ Behaviour specific to copies of either the projectbundle or projectfile genres. @h Scanning metadata. -Metadata for pipelines -- or rather, the complete lack of same -- is stored -in the following structure. +Metadata for projects is stored in the following structure. = typedef struct inform_project { @@ -563,7 +562,8 @@ because of the following sort: kit_dependency *kd; LOOP_OVER_LINKED_LIST(kd, kit_dependency, project->kits_to_include) if ((Str::eq(kd->kit->as_copy->edition->work->title, I"CommandParserKit")) || - (Str::eq(kd->kit->as_copy->edition->work->title, I"WorldModelKit"))) + (Str::eq(kd->kit->as_copy->edition->work->title, I"WorldModelKit")) || + (Str::eq(kd->kit->as_copy->edition->work->title, I"DialogueKit"))) basic = FALSE; if (basic == FALSE) { TEMPORARY_TEXT(err) @@ -699,6 +699,25 @@ filename *Projects::get_primary_output(inform_project *proj) { } } +@h Detecting dialogue. +There's an awkward timing issue with detecting dialogue in the source text. +The rule is that an Inform project should depend on DialogueKit if it contains +content under a dialogue section, but not otherwise. That in turn activates +the "dialogue" compiler feature. On the other hand, the source text also has +material placed under headings which are for use with dialogue only. So we +can't read the entire source text first and then decide: we have to switch +on the dialogue feature the moment any dialogue matter is found. This is +done by having the //syntax// module call the following: + += +inform_project *project_being_scanned = NULL; +void Projects::dialogue_present(void) { + if (project_being_scanned) { + Projects::add_kit_dependency(project_being_scanned, I"DialogueKit", NULL, NULL, NULL); + Projects::activate_elements(project_being_scanned); + } +} + @h The full graph. This can be quite grandiose even though most of it will never come to anything, rather like a family tree for a minor European royal family. @@ -708,11 +727,9 @@ void Projects::construct_graph(inform_project *proj) { if (proj == NULL) return; if (proj->chosen_build_target == NULL) { Projects::finalise_kit_dependencies(proj); + project_being_scanned = proj; Copies::get_source_text(proj->as_copy); - if (proj->syntax_tree->contains_dialogue) { - Projects::add_kit_dependency(proj, I"DialogueKit", NULL, NULL, NULL); - Projects::activate_elements(proj); - } + project_being_scanned = NULL; build_vertex *V = proj->as_copy->vertex; @; @; diff --git a/inbuild/supervisor-module/Chapter 6/Source Text.w b/inbuild/supervisor-module/Chapter 6/Source Text.w index 0309fb282..98653720f 100644 --- a/inbuild/supervisor-module/Chapter 6/Source Text.w +++ b/inbuild/supervisor-module/Chapter 6/Source Text.w @@ -290,6 +290,11 @@ void SourceText::new_beginend(parse_node *pn, inbuild_copy *C) { if (Node::get_type(pn) == ENDHERE_NT) Inclusions::check_ends_here(pn, E); } +@ This callback is called by //syntax// when it first reaches a dialogue line +or beat. + +@d DIALOGUE_WARNING_SYNTAX_CALLBACK Projects::dialogue_present + @ Lastly, this callback is called by //syntax// when it hits a sentence like: >> Use interactive fiction language element. diff --git a/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json index d28e0b220..41e77609a 100644 --- a/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json +++ b/inform7/Internal/Inter/BasicInformExtrasKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "BasicInformExtrasKit", - "version": "10.2.0-beta+6V62" + "version": "10.2.0-beta+6V63" }, "kit-details": { "has-priority": 1 diff --git a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json index d5063a322..77a570da3 100644 --- a/inform7/Internal/Inter/BasicInformKit/kit_metadata.json +++ b/inform7/Internal/Inter/BasicInformKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "BasicInformKit", - "version": "10.2.0-beta+6V62" + "version": "10.2.0-beta+6V63" }, "needs": [ { "unless": { diff --git a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json index ac508704f..e38f4a861 100644 --- a/inform7/Internal/Inter/CommandParserKit/kit_metadata.json +++ b/inform7/Internal/Inter/CommandParserKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "CommandParserKit", - "version": "10.2.0-beta+6V62" + "version": "10.2.0-beta+6V63" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/DialogueKit/Sections/Director.i6t b/inform7/Internal/Inter/DialogueKit/Sections/Director.i6t index 122df21f7..70d1f2096 100644 --- a/inform7/Internal/Inter/DialogueKit/Sections/Director.i6t +++ b/inform7/Internal/Inter/DialogueKit/Sections/Director.i6t @@ -5,6 +5,108 @@ Run-time support for dialogue. @h Placeholder. = +Global latest_performed_beat = 0; +Array DialogueTopicPool --> 20; + +[ DirectorEmptyDialogueTopicPool; + DialogueTopicPool-->0 = 0; +]; + +[ DirectorAddLiveSubjectList obj i; + for (i=18:i>0:i--) DialogueTopicPool-->i = DialogueTopicPool-->(i-1); + DialogueTopicPool-->0 = obj; + DialogueTopicPool-->19 = 0; +]; + +[ DirectorRemoveLiveSubjectList obj i j; + for (i=0:i<20:i++) { + if (DialogueTopicPool-->i == 0) return; + if (DialogueTopicPool-->i == obj) { + for (j=i:j<19:j++) DialogueTopicPool-->j = DialogueTopicPool-->(j+1); + } + } + DialogueTopicPool-->19 = 0; +]; + +[ DirectorLiveSubjectList list len i; + if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; + len = 0; + while (DialogueTopicPool-->len) len++; + LIST_OF_TY_SetLength(list, len); + for (i=0: ii); + return list; +]; + +[ DirectorAlterLiveSubjectList list len i; + if ((list==0) || (BlkValueWeakKind(list) ~= LIST_OF_TY)) return 0; + len = BlkValueRead(list, LIST_LENGTH_F); + if (len > 19) len = 19; + for (i=0: ii = BlkValueRead(list, LIST_ITEM_BASE+i); + DialogueTopicPool-->len = 0; + DialogueTopicPool-->19 = 0; +]; + [ DirectorBegin; + DirectorEmptyDialogueTopicPool(); + latest_performed_beat = 0; rfalse; ]; + +[ DirectorPerform db; + print "Performing beat ", (PrintDialogueBeatName) db, "^"; + latest_performed_beat = db; +]; + +[ DirectorListLiveTopics i t; + for (i=0: i<20: i++) { + t = DialogueTopicPool-->i; + if (t == 0) return; + print (name) t, "^"; + } +]; + +[ DirectorBeatAvailable db fn; + if ((db == 0) || (db > TableOfDialogueBeats-->0)) rfalse; + fn = TableOfDialogueBeats-->(3*db - 2); + if (fn) { + return (fn)(latest_performed_beat); + } + rtrue; +]; + +[ DirectorBeatRelevant db fn; + if ((db == 0) || (db > TableOfDialogueBeats-->0)) rfalse; + fn = TableOfDialogueBeats-->(3*db - 1); + if (fn) { + return (fn)(DialogueTopicPool); + } + rfalse; +]; + +[ DirectorBeatPrintStructure db tab pc which depth i; + if ((db == 0) || (db > TableOfDialogueBeats-->0)) return; + print (PrintDialogueBeatName) db; + print " ("; + if (DirectorBeatAvailable(db)) print "available"; else print "unavailable"; + print ", "; + if (DirectorBeatRelevant(db)) print "relevant"; else print "irrelevant"; + print "):^"; + tab = TableOfDialogueBeats-->(3*db); + if (tab) { + pc = 0; + while (tab-->pc) { + which = (tab-->pc)/100; + depth = (tab-->pc)%100; + for (i=0: i(pc+1), "^"; + 2: print "Choice ", (PrintDialogueChoiceName) tab-->(pc+1), "^"; + 3: print "Decision of type ", tab-->(pc+1), "^"; + default: print "*** Unimplemented ***^"; + } + pc = pc + 2; + } + } +]; diff --git a/inform7/Internal/Inter/DialogueKit/arch-16.interb b/inform7/Internal/Inter/DialogueKit/arch-16.interb index a02d30e0c..771ab469f 100644 Binary files a/inform7/Internal/Inter/DialogueKit/arch-16.interb and b/inform7/Internal/Inter/DialogueKit/arch-16.interb differ diff --git a/inform7/Internal/Inter/DialogueKit/arch-16d.interb b/inform7/Internal/Inter/DialogueKit/arch-16d.interb index 71bd495f9..a8ecdb129 100644 Binary files a/inform7/Internal/Inter/DialogueKit/arch-16d.interb and b/inform7/Internal/Inter/DialogueKit/arch-16d.interb differ diff --git a/inform7/Internal/Inter/DialogueKit/arch-32.interb b/inform7/Internal/Inter/DialogueKit/arch-32.interb index b9d247615..629caef3d 100644 Binary files a/inform7/Internal/Inter/DialogueKit/arch-32.interb and b/inform7/Internal/Inter/DialogueKit/arch-32.interb differ diff --git a/inform7/Internal/Inter/DialogueKit/arch-32d.interb b/inform7/Internal/Inter/DialogueKit/arch-32d.interb index 67bf802f1..f4900a589 100644 Binary files a/inform7/Internal/Inter/DialogueKit/arch-32d.interb and b/inform7/Internal/Inter/DialogueKit/arch-32d.interb differ diff --git a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json index 7b237451b..65761b33e 100644 --- a/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json +++ b/inform7/Internal/Inter/EnglishLanguageKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "EnglishLanguageKit", - "version": "10.2.0-beta+6V62" + "version": "10.2.0-beta+6V63" }, "needs": [ { "need": { diff --git a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json index ddeb0585c..ef9699b9b 100644 --- a/inform7/Internal/Inter/WorldModelKit/kit_metadata.json +++ b/inform7/Internal/Inter/WorldModelKit/kit_metadata.json @@ -2,7 +2,7 @@ "is": { "type": "kit", "title": "WorldModelKit", - "version": "10.2.0-beta+6V62" + "version": "10.2.0-beta+6V63" }, "needs": [ { "need": { diff --git a/inform7/Tests/Test Cases/DialogueBeats.txt b/inform7/Tests/Test Cases/DialogueBeats.txt new file mode 100644 index 000000000..ea5bb778d --- /dev/null +++ b/inform7/Tests/Test Cases/DialogueBeats.txt @@ -0,0 +1,96 @@ +The Cricket History Museum is a room. The bat, the ball and the pads are here. Daphne is a woman in the Museum. The trophy case is in the Museum. The urn and the scoring pencil are in the case. + +Daphne can be honest or disingenuous. + +A dialogue beat can be dramatic, quiet or subdued. + +Spoken rapidly is a performance style. + +When play begins: + say "== The stock..."; + showme the list of dialogue beats; + showme the list of dialogue lines; + showme the list of dialogue choices; + say "== Dual scene and beat naming..."; + showme the alpha scene; + showme the alpha beat; + say "== Properties..."; + showme the list of dramatic dialogue beats; + showme the list of subdued dialogue beats; + say "== Performance..."; + showme the list of unperformed dialogue beats; + perform the alpha beat; + showme the list of unperformed dialogue beats; + say "== The live conversational subject list..."; + showme the live conversational subject list; + make the bat a live conversational subject; + make the ball a live conversational subject; + showme the live conversational subject list; + make the bat a dead conversational subject; + showme the live conversational subject list; + make the ball a dead conversational subject; + showme the live conversational subject list; + make the ball a dead conversational subject; + showme the live conversational subject list; + alter the live conversational subject list to { Daphne, the pads }; + showme the live conversational subject list; + say "== Relevance..."; + alter the live conversational subject list to { the bat }; + showme the live conversational subject list; + showme the list of relevant dialogue beats; + alter the live conversational subject list to { the ball }; + showme the live conversational subject list; + showme the list of relevant dialogue beats; + alter the live conversational subject list to { the bat, the scoring pencil }; + showme the live conversational subject list; + showme the list of relevant dialogue beats; + say "== Availability..."; + now every dialogue beat is unperformed; + showme the list of available dialogue beats; + perform the gamma beat; + showme the list of available dialogue beats; + perform the nu beat; + showme the list of available dialogue beats; + now Daphne is honest; + showme the list of available dialogue beats; + say "== Structure..."; + repeat with B running through dialogue beats: + showme the beat structure of B; + +Section 1 - Meeting (dialogue) + +(This is the alpha scene.) + +Daphne: "Welcome to the Museum of Cricket." + +(This is the beta beat. About the bat.) + +Daphne: "Made of willow, the cricket bat is sprung to provide a good drive." + +(This is the gamma beat. About the ball and everything in the case.) + +Daphne: "A cricket ball is made with a core of cork, which is layered with tightly wound string, and covered by a leather case with a slightly raised sewn seam." + +(This is the delta beat. Immediately after the gamma beat. Quiet.) + +Daphne: "I forgot to mention, British Standard BS 5993 specifies the construction details, dimensions, quality and performance of cricket balls." + +(This is the epsilon beat. After the gamma beat. Subdued.) + +Daphne: "Dukes balls have a prouder seam and will tend to swing more than Kookaburra balls." + +(This is the nu beat.) + +Daphne: "Cricket's kind of dull, when you think about it." + + Ball (this is the disgruntled ball line): "I disagree!" + + Bat: "I especially enjoy it." + +(This is the mu beat. If Daphne is honest.) + +Daphne: "I prefer softball, honestly." + +(This is the eta beat. Before the nu beat.) + +Daphne: "This is the foremost museum in New South Wales." diff --git a/inform7/Tests/Test Cases/_Results_Ideal/DialogueBeats.txt b/inform7/Tests/Test Cases/_Results_Ideal/DialogueBeats.txt new file mode 100644 index 000000000..743488f0e --- /dev/null +++ b/inform7/Tests/Test Cases/_Results_Ideal/DialogueBeats.txt @@ -0,0 +1,65 @@ + Cricket History Museum + == The stock... + "list of dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, delta beat, epsilon beat, nu beat, mu beat, eta beat} + "list of dialogue lines" = list of dialogue lines: {line-1, line-2, line-3, line-4, line-5, line-6, disgruntled ball line, line-8, line-9, line-10} + "list of dialogue choices" = list of dialogue choices: {} + == Dual scene and beat naming... + scene: alpha scene + dialogue beat: alpha beat + == Properties... + "list of dramatic dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, nu beat, mu beat, eta beat} + "list of subdued dialogue beats" = list of dialogue beats: {epsilon beat} + == Performance... + "list of unperformed dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, delta beat, epsilon beat, nu beat, mu beat, eta beat} + Performing beat alpha beat + "list of unperformed dialogue beats" = list of dialogue beats: {beta beat, gamma beat, delta beat, epsilon beat, nu beat, mu beat, eta beat} + == The live conversational subject list... + "live conversational subject list" = list of objects: {} + "live conversational subject list" = list of objects: {ball, bat} + "live conversational subject list" = list of objects: {ball} + "live conversational subject list" = list of objects: {} + "live conversational subject list" = list of objects: {} + "live conversational subject list" = list of objects: {Daphne, pads} + == Relevance... + "live conversational subject list" = list of objects: {bat} + "list of relevant dialogue beats" = list of dialogue beats: {beta beat, gamma beat} + "live conversational subject list" = list of objects: {ball} + "list of relevant dialogue beats" = list of dialogue beats: {gamma beat} + "live conversational subject list" = list of objects: {bat, scoring pencil} + "list of relevant dialogue beats" = list of dialogue beats: {beta beat, gamma beat} + == Availability... + "list of available dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, nu beat, eta beat} + Performing beat gamma beat + "list of available dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, epsilon beat, nu beat, eta beat} + Performing beat nu beat + "list of available dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, epsilon beat, nu beat} + "list of available dialogue beats" = list of dialogue beats: {alpha beat, beta beat, gamma beat, epsilon beat, nu beat, mu beat} + == Structure... + alpha beat (available, irrelevant): + Line line-1 + beta beat (available, relevant): + Line line-2 + gamma beat (available, relevant): + Line line-3 + delta beat (unavailable, irrelevant): + Line line-4 + epsilon beat (available, irrelevant): + Line line-5 + nu beat (available, irrelevant): + Line line-6 + Line disgruntled ball line + Line line-8 + mu beat (available, irrelevant): + Line line-9 + eta beat (unavailable, irrelevant): + Line line-10 + + Welcome + An Interactive Fiction + Release 1 / Serial number 160428 / Inform 7 v10.2.0 / D + + Cricket History Museum + You can see a bat, a ball, a pads, Daphne and a trophy case (in which are an urn and a scoring pencil) here. + +> > Cricket History Museum +> Are you sure you want to quit? \ No newline at end of file diff --git a/inform7/core-module/Chapter 1/How To Compile.w b/inform7/core-module/Chapter 1/How To Compile.w index e9fee677f..4030caebc 100644 --- a/inform7/core-module/Chapter 1/How To Compile.w +++ b/inform7/core-module/Chapter 1/How To Compile.w @@ -211,7 +211,9 @@ here, which only happens when special runs are made for compiler testing. BENCH(RTTableColumns::compile) BENCH(RTEquations::compile) BENCH(ImperativeDefinitions::compile_first_block) - BENCH(RTDialogue::compile) + BENCH(RTDialogueBeats::compile) + BENCH(RTDialogueLines::compile) + BENCH(RTDialogueChoices::compile) BENCH(RTRules::compile) BENCH(RTRulebooks::compile) BENCH(RTRulebooks::compile_nros) diff --git a/inform7/extensions/standard_rules/Sections/Variables and Rulebooks.w b/inform7/extensions/standard_rules/Sections/Variables and Rulebooks.w index cc020e2a0..f1dfca2b2 100644 --- a/inform7/extensions/standard_rules/Sections/Variables and Rulebooks.w +++ b/inform7/extensions/standard_rules/Sections/Variables and Rulebooks.w @@ -1269,6 +1269,36 @@ The print protagonist internal rule translates into Inter as @h Dialogue support. = -Section 10 - Performance Styles (for dialogue language element only) +Section 10 - Dialogue (for dialogue language element only) There is a performance style called spoken normally. + +A dialogue beat can be performed or unperformed. A dialogue beat is usually +unperformed. +A dialogue beat can be recurring or non-recurring. A dialogue beat is usually +non-recurring. + +To make (T - an object) a live conversational subject: + (- DirectorAddLiveSubjectList({T}); -). +To make (T - an object) a dead conversational subject: + (- DirectorRemoveLiveSubjectList({T}); -). + +To decide what list of objects is the live conversational subject list: + (- DirectorLiveSubjectList({-new:list of objects}) -). +To alter the live conversational subject list to (L - list of objects): + (- DirectorAlterLiveSubjectList({-by-reference:L}); -). + +To perform (B - a dialogue beat): + ask DialogueKit to perform B; + now B is performed. + +To ask DialogueKit to perform (B - a dialogue beat): + (- DirectorPerform({B}); -). + +To showme the beat structure of (B - dialogue beat): (- DirectorBeatPrintStructure({B}); -). + +Definition: A dialogue beat is available rather than unavailable if Inter routine + "DirectorBeatAvailable" says so (it meets all its after or before, if and unless conditions). + +Definition: A dialogue beat is relevant rather than irrelevant if Inter routine + "DirectorBeatRelevant" says so (one of the topics it is about is currently live). diff --git a/inform7/if-module/Chapter 6/Dialogue Beats.w b/inform7/if-module/Chapter 6/Dialogue Beats.w index d0d4882e8..ca72be6f1 100644 --- a/inform7/if-module/Chapter 6/Dialogue Beats.w +++ b/inform7/if-module/Chapter 6/Dialogue Beats.w @@ -106,7 +106,7 @@ typedef struct dialogue_beat { db->some_time_before = NEW_LINKED_LIST(parse_node); db->about_list = NEW_LINKED_LIST(parse_node); db->root = NULL; - db->compilation_data = RTDialogue::new_beat(PN, db); + db->compilation_data = RTDialogueBeats::new_beat(PN, db); @ Each clause can be one of about 10 possibilities, as follows, and the wording tells us immediately which possibility it is, even early in the run. diff --git a/inform7/if-module/Chapter 6/Dialogue Choices.w b/inform7/if-module/Chapter 6/Dialogue Choices.w index 910470b67..cb8b26ef3 100644 --- a/inform7/if-module/Chapter 6/Dialogue Choices.w +++ b/inform7/if-module/Chapter 6/Dialogue Choices.w @@ -50,7 +50,7 @@ typedef struct dialogue_choice { dc->selection_parameter = EMPTY_WORDING; dc->to_perform = NULL; dc->selection_type = BLANK_DSEL; - dc->compilation_data = RTDialogue::new_choice(PN, dc); + dc->compilation_data = RTDialogueChoices::new(PN, dc); @ Each choice produces an instance of the kind |dialogue choice|, using the name given in its clauses if one was. diff --git a/inform7/if-module/Chapter 6/Dialogue Lines.w b/inform7/if-module/Chapter 6/Dialogue Lines.w index 2b85988bb..30de03554 100644 --- a/inform7/if-module/Chapter 6/Dialogue Lines.w +++ b/inform7/if-module/Chapter 6/Dialogue Lines.w @@ -56,7 +56,7 @@ typedef struct dialogue_line { dl->speaker_text = EMPTY_WORDING; dl->speaker_description = NULL; dl->without_speaking = FALSE; - dl->compilation_data = RTDialogue::new_line(PN, dl); + dl->compilation_data = RTDialogueLines::new(PN, dl); dl->speech_text = EMPTY_WORDING; dl->mentioning = NEW_LINKED_LIST(parse_node); dl->how_performed = PerformanceStyles::default(); diff --git a/inform7/if-module/Chapter 6/Dialogue.w b/inform7/if-module/Chapter 6/Dialogue.w index 632863209..7bd7e6213 100644 --- a/inform7/if-module/Chapter 6/Dialogue.w +++ b/inform7/if-module/Chapter 6/Dialogue.w @@ -11,6 +11,7 @@ handles that kind, so it won't be dealt with in the code for this feature. = void Dialogue::start(void) { Dialogue::declare_annotations(); + PluginCalls::plug(NEW_PROPERTY_NOTIFY_PLUG, Dialogue::new_property_notify); PluginCalls::plug(NEW_BASE_KIND_NOTIFY_PLUG, Dialogue::new_base_kind_notify); PluginCalls::plug(COMPARE_CONSTANT_PLUG, Dialogue::compare_CONSTANT); } @@ -37,6 +38,27 @@ int Dialogue::new_base_kind_notify(kind *new_base, text_stream *name, wording W) return FALSE; } +@ The following either/or property needs some compiler support: + += (early code) +property *P_performed = NULL; + +@ We will need to compile code using this. + += + ::= + performed + +@ = +int Dialogue::new_property_notify(property *prn) { + if ((prn->name)) { + switch (<>) { + case 0: P_performed = prn; break; + } + } + return FALSE; +} + @ The rest of this section is to some extent boiler-plate code: it provides for specific beats and lines to be represented as rvalues, both inside the compiler (and when typechecking) and at runtime. diff --git a/inform7/runtime-module/Chapter 2/Hierarchy.w b/inform7/runtime-module/Chapter 2/Hierarchy.w index cbb0f13a7..e323ff620 100644 --- a/inform7/runtime-module/Chapter 2/Hierarchy.w +++ b/inform7/runtime-module/Chapter 2/Hierarchy.w @@ -959,7 +959,15 @@ void Hierarchy::establish(void) { @e INLINE_PROPERTIES_HAP @e INLINE_PROPERTY_HL @e DIRECTION_HL -@e BEAT_FILTER_FN_HL +@e INSTANCE_IS_DB_MD_HL +@e INSTANCE_IS_DL_MD_HL +@e INSTANCE_IS_DC_MD_HL +@e BEAT_AVAILABLE_MD_HL +@e BEAT_AVAILABLE_FN_HL +@e BEAT_RELEVANT_MD_HL +@e BEAT_RELEVANT_FN_HL +@e BEAT_STRUCTURE_MD_HL +@e BEAT_STRUCTURE_HL @ = submodule_identity *instances = LargeScale::register_submodule_identity(I"instances"); @@ -1052,7 +1060,15 @@ void Hierarchy::establish(void) { H_F_G(REGION_FOUND_IN_FN_HL, I"region_found_in_fn", I"RFI_for_I") H_F_G(TSD_DOOR_DIR_FN_HL, I"tsd_door_dir_fn", I"TSD_door_dir_value") H_F_G(TSD_DOOR_TO_FN_HL, I"tsd_door_to_fn", I"TSD_door_to_value") - H_F_G(BEAT_FILTER_FN_HL, I"beat_filter_fn", I"DB_filter") + H_C_U(INSTANCE_IS_DB_MD_HL, I"^is_dialogue_beat") + H_C_U(INSTANCE_IS_DL_MD_HL, I"^is_dialogue_line") + H_C_U(INSTANCE_IS_DC_MD_HL, I"^is_dialogue_choice") + H_C_U(BEAT_AVAILABLE_MD_HL, I"^available") + H_F_U(BEAT_AVAILABLE_FN_HL, I"available_fn") + H_C_U(BEAT_RELEVANT_MD_HL, I"^relevant") + H_F_U(BEAT_RELEVANT_FN_HL, I"relevant_fn") + H_C_U(BEAT_STRUCTURE_MD_HL, I"^structure") + H_C_U(BEAT_STRUCTURE_HL, I"structure") H_F_U(INST_SHOWME_FN_HL, I"showme_fn") H_BEGIN_AP(INLINE_PROPERTIES_HAP, I"inline_property", I"_inline_property") H_C_U(INLINE_PROPERTY_HL, I"inline") diff --git a/inform7/runtime-module/Chapter 5/Dialogue Beat Instances.w b/inform7/runtime-module/Chapter 5/Dialogue Beat Instances.w new file mode 100644 index 000000000..6b0ef8977 --- /dev/null +++ b/inform7/runtime-module/Chapter 5/Dialogue Beat Instances.w @@ -0,0 +1,340 @@ +[RTDialogueBeats::] Dialogue Beat Instances. + +To compile any dialogue details in the instances submodule. + +@h Compilation data for dialogue beats. +Each |dialogue_beat| object contains this data: + += +typedef struct dialogue_beat_compilation_data { + struct parse_node *where_created; + struct inter_name *available_function; + struct inter_name *relevant_function; + struct inter_name *structure_array; +} dialogue_beat_compilation_data; + +dialogue_beat_compilation_data RTDialogueBeats::new_beat(parse_node *PN, dialogue_beat *db) { + dialogue_beat_compilation_data dbcd; + dbcd.where_created = PN; + dbcd.available_function = NULL; + dbcd.relevant_function = NULL; + dbcd.structure_array = NULL; + return dbcd; +} + +package_request *RTDialogueBeats::package(dialogue_beat *db) { + if (db->as_instance == NULL) internal_error("not available yet"); + return RTInstances::package(db->as_instance); +} + +inter_name *RTDialogueBeats::available_fn_iname(dialogue_beat *db) { + if (db->compilation_data.available_function == NULL) + db->compilation_data.available_function = + Hierarchy::make_iname_in(BEAT_AVAILABLE_FN_HL, RTDialogueBeats::package(db)); + return db->compilation_data.available_function; +} + +inter_name *RTDialogueBeats::relevant_fn_iname(dialogue_beat *db) { + if (db->compilation_data.relevant_function == NULL) + db->compilation_data.relevant_function = + Hierarchy::make_iname_in(BEAT_RELEVANT_FN_HL, RTDialogueBeats::package(db)); + return db->compilation_data.relevant_function; +} + +inter_name *RTDialogueBeats::structure_fn_iname(dialogue_beat *db) { + if (db->compilation_data.structure_array == NULL) + db->compilation_data.structure_array = + Hierarchy::make_iname_in(BEAT_STRUCTURE_HL, RTDialogueBeats::package(db)); + return db->compilation_data.structure_array; +} + +@h Compilation of dialogue. + += +void RTDialogueBeats::compile(void) { + dialogue_beat *db; + LOOP_OVER(db, dialogue_beat) { + text_stream *desc = Str::new(); + WRITE_TO(desc, "dialogue beat %d", db->allocation_id); + Sequence::queue(&RTDialogueBeats::beat_compilation_agent, STORE_POINTER_dialogue_beat(db), desc); + } +} + +void RTDialogueBeats::beat_compilation_agent(compilation_subtask *ct) { + dialogue_beat *db = RETRIEVE_POINTER_dialogue_beat(ct->data); + current_sentence = db->compilation_data.where_created; + package_request *PR = RTDialogueBeats::package(db); + @; + @; + @; +} + +@ = + int conditions = 0; + for (parse_node *clause = db->cue_at->down; clause; clause = clause->next) { + int c = Annotations::read_int(clause, dialogue_beat_clause_ANNOT); + if ((c == IF_DBC) || (c == UNLESS_DBC)) conditions++; + } + if ((db->immediately_after) || + (LinkedLists::len(db->some_time_after) > 0) || + (LinkedLists::len(db->some_time_before) > 0) || + (conditions > 0)) { + Hierarchy::apply_metadata_from_iname(PR, + BEAT_AVAILABLE_MD_HL, RTDialogueBeats::available_fn_iname(db)); + @; + } + +@ = + packaging_state save = Functions::begin(RTDialogueBeats::available_fn_iname(db)); + local_variable *latest = LocalVariables::new_internal_commented(I"latest", I"most recently performed beat"); + LocalVariables::set_kind(latest, K_dialogue_beat); + inter_symbol *latest_s = LocalVariables::declare(latest); + if (db->immediately_after) @; + @; + @; + EmitCode::rtrue(); + Functions::end(save); + +@ = + EmitCode::inv(IF_BIP); + EmitCode::down(); + EmitCode::inv(NE_BIP); + EmitCode::down(); + EmitCode::val_symbol(K_value, latest_s); + EmitCode::val_number(0); + EmitCode::up(); + EmitCode::code(); + EmitCode::down(); + @; + EmitCode::rfalse(); + EmitCode::up(); + EmitCode::up(); + +@ = + EmitCode::inv(IF_BIP); + EmitCode::down(); + instance *I = Rvalues::to_instance(db->immediately_after); + if (I) { + EmitCode::inv(NE_BIP); + EmitCode::down(); + EmitCode::val_symbol(K_value, latest_s); + EmitCode::val_iname(K_dialogue_beat, RTInstances::value_iname(I)); + EmitCode::up(); + } else { + pcalc_prop *prop = Propositions::negate(Descriptions::to_proposition(db->immediately_after)); + if (prop) { + TypecheckPropositions::type_check(prop, + TypecheckPropositions::tc_no_problem_reporting()); + CompilePropositions::to_test_as_condition(Lvalues::new_LOCAL_VARIABLE(EMPTY_WORDING, latest), prop); + } else { + internal_error("cannot test"); + } + } + EmitCode::code(); + EmitCode::down(); + EmitCode::rfalse(); + EmitCode::up(); + EmitCode::up(); + +@ = + parse_node *desc; + LOOP_OVER_LINKED_LIST(desc, parse_node, db->some_time_after) { + int negate_me = FALSE; + @; + } + LOOP_OVER_LINKED_LIST(desc, parse_node, db->some_time_before) { + int negate_me = TRUE; + @; + } + +@ = + instance *I = Rvalues::to_instance(desc); + pcalc_prop *prop = NULL; + adjective *adj = EitherOrProperties::as_adjective(P_performed); + if (I) { + prop = AdjectivalPredicates::new_atom(adj, negate_me, Terms::new_constant(desc)); + } else { + pcalc_prop *exists = Atoms::QUANTIFIER_new(exists_quantifier, 0, 0); + pcalc_prop *domain = KindPredicates::new_atom(K_dialogue_beat, Terms::new_variable(0)); + pcalc_prop *performed = AdjectivalPredicates::new_atom(adj, negate_me, Terms::new_variable(0)); + pcalc_prop *desc_prop = Descriptions::to_proposition(desc); + prop = Propositions::concatenate(exists, + Propositions::concatenate(domain, + Propositions::concatenate(performed, + desc_prop))); + } + prop = Propositions::negate(prop); + EmitCode::inv(IF_BIP); + EmitCode::down(); + TypecheckPropositions::type_check(prop, + TypecheckPropositions::tc_no_problem_reporting()); + CompilePropositions::to_test_as_condition(NULL, prop); + EmitCode::code(); + EmitCode::down(); + EmitCode::rfalse(); + EmitCode::up(); + EmitCode::up(); + +@ = + current_sentence = db->cue_at; + for (parse_node *clause = db->cue_at->down; clause; clause = clause->next) { + wording CW = Node::get_text(clause); + int c = Annotations::read_int(clause, dialogue_beat_clause_ANNOT); + switch (c) { + case IF_DBC: + case UNLESS_DBC: { + (CW); + wording A = GET_RW(, 1); + if ((A)) { + parse_node *cond = <>; + if (Dash::validate_conditional_clause(cond)) { + EmitCode::inv(IF_BIP); + EmitCode::down(); + if (c == IF_DBC) { + EmitCode::inv(NOT_BIP); + EmitCode::down(); + } + CompileValues::to_code_val_of_kind(cond, K_truth_state); + if (c == IF_DBC) { + EmitCode::up(); + } + EmitCode::code(); + EmitCode::down(); + EmitCode::rfalse(); + EmitCode::up(); + EmitCode::up(); + } + } + break; + } + } + } + +@ = + if (LinkedLists::len(db->about_list) > 0) { + Hierarchy::apply_metadata_from_iname(PR, + BEAT_RELEVANT_MD_HL, RTDialogueBeats::relevant_fn_iname(db)); + @; + } + +@ = + packaging_state save = Functions::begin(RTDialogueBeats::relevant_fn_iname(db)); + local_variable *pool = LocalVariables::new_internal_commented(I"pool", I"pool of live topics"); + local_variable *iv = LocalVariables::new_internal_commented(I"iv", I"index variable"); + local_variable *topic = LocalVariables::new_internal_commented(I"topic", I"live topic"); + inter_symbol *pool_s = LocalVariables::declare(pool); + inter_symbol *iv_s = LocalVariables::declare(iv); + inter_symbol *topic_s = LocalVariables::declare(topic); + @; + EmitCode::rfalse(); + Functions::end(save); + +@ = + inter_symbol *loop_label = EmitCode::reserve_label(I"about_loop"); + EmitCode::place_label(loop_label); + EmitCode::inv(STORE_BIP); + EmitCode::down(); + EmitCode::ref_symbol(K_value, topic_s); + EmitCode::inv(LOOKUP_BIP); + EmitCode::down(); + EmitCode::val_symbol(K_value, pool_s); + EmitCode::val_symbol(K_value, iv_s); + EmitCode::up(); + EmitCode::up(); + EmitCode::inv(IF_BIP); + EmitCode::down(); + EmitCode::inv(NE_BIP); + EmitCode::down(); + EmitCode::val_symbol(K_value, topic_s); + EmitCode::val_number(0); + EmitCode::up(); + EmitCode::code(); + EmitCode::down(); + parse_node *desc; + LOOP_OVER_LINKED_LIST(desc, parse_node, db->about_list) { + instance *I = Rvalues::to_instance(desc); + EmitCode::inv(IF_BIP); + EmitCode::down(); + if (I) { + EmitCode::inv(EQ_BIP); + EmitCode::down(); + EmitCode::val_symbol(K_value, topic_s); + EmitCode::val_iname(K_value, RTInstances::value_iname(I)); + EmitCode::up(); + } else { + pcalc_prop *prop = Descriptions::to_proposition(desc); + if (prop) { + TypecheckPropositions::type_check(prop, + TypecheckPropositions::tc_no_problem_reporting()); + CompilePropositions::to_test_as_condition( + Lvalues::new_LOCAL_VARIABLE(EMPTY_WORDING, topic), prop); + } else { + internal_error("cannot test"); + } + } + EmitCode::code(); + EmitCode::down(); + EmitCode::rtrue(); + EmitCode::up(); + EmitCode::up(); + } + EmitCode::inv(POSTINCREMENT_BIP); + EmitCode::down(); + EmitCode::ref_symbol(K_value, iv_s); + EmitCode::up(); + EmitCode::inv(JUMP_BIP); + EmitCode::down(); + EmitCode::lab(loop_label); + EmitCode::up(); + EmitCode::up(); + EmitCode::up(); + EmitCode::rfalse(); + +@ And this is always present. + +@ = + Hierarchy::apply_metadata_from_iname(PR, + BEAT_STRUCTURE_MD_HL, RTDialogueBeats::structure_fn_iname(db)); + packaging_state save = + EmitArrays::begin_word(RTDialogueBeats::structure_fn_iname(db), K_value); + RTDialogueBeats::compile_structure_r(db->root, 1); + EmitArrays::numeric_entry(0); + EmitArrays::end(save); + +@ = +void RTDialogueBeats::compile_structure_r(dialogue_node *dn, inter_ti depth) { + while (dn) { + if (dn->if_line) { + EmitArrays::numeric_entry(depth + 100); + EmitArrays::iname_entry(RTInstances::value_iname(dn->if_line->as_instance)); + } else if (dn->if_choice) { + EmitArrays::numeric_entry(depth + 200); + EmitArrays::iname_entry(RTInstances::value_iname(dn->if_choice->as_instance)); + } else if (dn->if_decision) { + EmitArrays::numeric_entry(depth + 300); + EmitArrays::numeric_entry((inter_ti) (dn->if_decision->decision_type)); + } else internal_error("unimplemented dialogue node compilation"); + if (dn->child_node) + RTDialogueBeats::compile_structure_r(dn->child_node, depth+1); + dn = dn->next_node; + } +} + +@ = +void RTDialogueBeats::log_r(dialogue_node *dn) { + while (dn) { + if (dn->if_line) + LOG("Line %d = %W\n", + dn->if_line->allocation_id, Node::get_text(dn->if_line->compilation_data.where_created)); + if (dn->if_choice) + LOG("Choice %d = %W\n", + dn->if_choice->allocation_id, Node::get_text(dn->if_choice->compilation_data.where_created)); + if (dn->child_node) { + if (dn->child_node->parent_node != dn) LOG("*** Broken parentage ***\n"); + LOG_INDENT; + RTDialogueBeats::log_r(dn->child_node); + LOG_OUTDENT; + } + dn = dn->next_node; + } +} diff --git a/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w b/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w new file mode 100644 index 000000000..7dc9c912d --- /dev/null +++ b/inform7/runtime-module/Chapter 5/Dialogue Choice Instances.w @@ -0,0 +1,36 @@ +[RTDialogueChoices::] Dialogue Choice Instances. + +To compile any dialogue details in the instances submodule. + +@h Compilation data for dialogue choices. +Each |dialogue_choice| object contains this data: + += +typedef struct dialogue_choice_compilation_data { + struct parse_node *where_created; +} dialogue_choice_compilation_data; + +dialogue_choice_compilation_data RTDialogueChoices::new(parse_node *PN, dialogue_choice *dc) { + dialogue_choice_compilation_data dlcd; + dlcd.where_created = PN; + return dlcd; +} + +@h Compilation of dialogue. + += +void RTDialogueChoices::compile(void) { + dialogue_choice *dc; + LOOP_OVER(dc, dialogue_choice) { + text_stream *desc = Str::new(); + WRITE_TO(desc, "dialogue choice %d", dc->allocation_id); + Sequence::queue(&RTDialogueChoices::choice_compilation_agent, + STORE_POINTER_dialogue_choice(dc), desc); + } +} + +@ = +void RTDialogueChoices::choice_compilation_agent(compilation_subtask *ct) { + dialogue_choice *dc = RETRIEVE_POINTER_dialogue_choice(ct->data); + current_sentence = dc->compilation_data.where_created; +} diff --git a/inform7/runtime-module/Chapter 5/Dialogue Line Instances.w b/inform7/runtime-module/Chapter 5/Dialogue Line Instances.w new file mode 100644 index 000000000..50062b9dd --- /dev/null +++ b/inform7/runtime-module/Chapter 5/Dialogue Line Instances.w @@ -0,0 +1,35 @@ +[RTDialogueLines::] Dialogue. + +To compile any dialogue details in the instances submodule. + +@h Compilation data for dialogue lines. +Each |dialogue_line| object contains this data: + += +typedef struct dialogue_line_compilation_data { + struct parse_node *where_created; +} dialogue_line_compilation_data; + +dialogue_line_compilation_data RTDialogueLines::new(parse_node *PN, dialogue_line *dl) { + dialogue_line_compilation_data dlcd; + dlcd.where_created = PN; + return dlcd; +} + +@h Compilation of dialogue. + += +void RTDialogueLines::compile(void) { + dialogue_line *dl; + LOOP_OVER(dl, dialogue_line) { + text_stream *desc = Str::new(); + WRITE_TO(desc, "dialogue line %d", dl->allocation_id); + Sequence::queue(&RTDialogueLines::line_compilation_agent, STORE_POINTER_dialogue_line(dl), desc); + } +} + +@ = +void RTDialogueLines::line_compilation_agent(compilation_subtask *ct) { + dialogue_line *dl = RETRIEVE_POINTER_dialogue_line(ct->data); + current_sentence = dl->compilation_data.where_created; +} diff --git a/inform7/runtime-module/Chapter 5/Dialogue.w b/inform7/runtime-module/Chapter 5/Dialogue.w deleted file mode 100644 index 03246cac9..000000000 --- a/inform7/runtime-module/Chapter 5/Dialogue.w +++ /dev/null @@ -1,160 +0,0 @@ -[RTDialogue::] Dialogue. - -To compile the dialogue submodule for a compilation unit, which contains -something to be worked out. - -@h Compilation data for dialogue beats. -Each |dialogue_beat| object contains this data: - -= -typedef struct dialogue_beat_compilation_data { - struct parse_node *where_created; - struct inter_name *usage_filter_function; -} dialogue_beat_compilation_data; - -dialogue_beat_compilation_data RTDialogue::new_beat(parse_node *PN, dialogue_beat *db) { - dialogue_beat_compilation_data dbcd; - dbcd.where_created = PN; - dbcd.usage_filter_function = NULL; - - return dbcd; -} - -inter_name *RTDialogue::beat_filter(dialogue_beat *db) { - if (db->compilation_data.usage_filter_function == NULL) - db->compilation_data.usage_filter_function = - Hierarchy::make_iname_in(BEAT_FILTER_FN_HL, RTInstances::package(db->as_instance)); - return db->compilation_data.usage_filter_function; -} - -@h Compilation data for dialogue lines. -Each |dialogue_line| object contains this data: - -= -typedef struct dialogue_line_compilation_data { - struct parse_node *where_created; -} dialogue_line_compilation_data; - -dialogue_line_compilation_data RTDialogue::new_line(parse_node *PN, dialogue_line *dl) { - dialogue_line_compilation_data dlcd; - dlcd.where_created = PN; - return dlcd; -} - -@h Compilation data for dialogue choices. -Each |dialogue_choice| object contains this data: - -= -typedef struct dialogue_choice_compilation_data { - struct parse_node *where_created; -} dialogue_choice_compilation_data; - -dialogue_choice_compilation_data RTDialogue::new_choice(parse_node *PN, dialogue_choice *dc) { - dialogue_choice_compilation_data dlcd; - dlcd.where_created = PN; - return dlcd; -} - -@h Compilation of dialogue. - -= -void RTDialogue::compile(void) { - dialogue_beat *db; - LOOP_OVER(db, dialogue_beat) { - text_stream *desc = Str::new(); - WRITE_TO(desc, "dialogue beat %d", db->allocation_id); - Sequence::queue(&RTDialogue::beat_compilation_agent, STORE_POINTER_dialogue_beat(db), desc); - } - dialogue_line *dl; - LOOP_OVER(dl, dialogue_line) { - text_stream *desc = Str::new(); - WRITE_TO(desc, "dialogue line %d", dl->allocation_id); - Sequence::queue(&RTDialogue::line_compilation_agent, STORE_POINTER_dialogue_line(dl), desc); - } -} - -void RTDialogue::beat_compilation_agent(compilation_subtask *ct) { - dialogue_beat *db = RETRIEVE_POINTER_dialogue_beat(ct->data); - current_sentence = db->compilation_data.where_created; - LOG("Beat %d = %W name '%W' scene '%W'\n", - db->allocation_id, Node::get_text(current_sentence), db->beat_name, db->scene_name); - RTDialogue::log_r(db->root); - packaging_state save = Functions::begin(RTDialogue::beat_filter(db)); - local_variable *latest = LocalVariables::new_internal_commented(I"latest", I"most recently performed beat"); - LocalVariables::set_kind(latest, K_dialogue_beat); - local_variable *pool = LocalVariables::new_internal_commented(I"pool", I"pool of live topics"); - inter_symbol *latest_s = LocalVariables::declare(latest); - inter_symbol *pool_s = LocalVariables::declare(pool); - if (db->immediately_after) { - EmitCode::inv(IF_BIP); - EmitCode::down(); - EmitCode::inv(NE_BIP); - EmitCode::down(); - EmitCode::val_symbol(K_value, latest_s); - EmitCode::val_number(0); - EmitCode::up(); - EmitCode::code(); - EmitCode::down(); - @; - EmitCode::rfalse(); - EmitCode::up(); - EmitCode::up(); - } - EmitCode::inv(STORE_BIP); - EmitCode::down(); - EmitCode::ref_symbol(K_value, pool_s); - EmitCode::val_number(0); - EmitCode::up(); - EmitCode::rfalse(); - Functions::end(save); -} - -@ = - EmitCode::inv(IF_BIP); - EmitCode::down(); - LOG("IA cond is $T\n", db->immediately_after); - instance *I = Rvalues::to_instance(db->immediately_after); - if (I) { - EmitCode::inv(EQ_BIP); - EmitCode::down(); - EmitCode::val_symbol(K_value, latest_s); - EmitCode::val_iname(K_dialogue_beat, RTInstances::value_iname(I)); - EmitCode::up(); - } else { - pcalc_prop *prop = Descriptions::to_proposition(db->immediately_after); - if (prop) { - CompilePropositions::to_test_as_condition(Lvalues::new_LOCAL_VARIABLE(EMPTY_WORDING, latest), prop); - } else { - internal_error("cannot test"); - } - } - EmitCode::code(); - EmitCode::down(); - EmitCode::rtrue(); - EmitCode::up(); - EmitCode::up(); - -@ = -void RTDialogue::line_compilation_agent(compilation_subtask *ct) { - dialogue_line *dl = RETRIEVE_POINTER_dialogue_line(ct->data); - current_sentence = dl->compilation_data.where_created; - LOG("Line %d = %W name '%W'\n", dl->allocation_id, Node::get_text(current_sentence), dl->line_name); -} - -void RTDialogue::log_r(dialogue_node *dn) { - while (dn) { - if (dn->if_line) - LOG("Line %d = %W\n", - dn->if_line->allocation_id, Node::get_text(dn->if_line->compilation_data.where_created)); - if (dn->if_choice) - LOG("Choice %d = %W\n", - dn->if_choice->allocation_id, Node::get_text(dn->if_choice->compilation_data.where_created)); - if (dn->child_node) { - if (dn->child_node->parent_node != dn) LOG("*** Broken parentage ***\n"); - LOG_INDENT; - RTDialogue::log_r(dn->child_node); - LOG_OUTDENT; - } - dn = dn->next_node; - } -} diff --git a/inform7/runtime-module/Chapter 5/Instances.w b/inform7/runtime-module/Chapter 5/Instances.w index 799a12dae..69f91542c 100644 --- a/inform7/runtime-module/Chapter 5/Instances.w +++ b/inform7/runtime-module/Chapter 5/Instances.w @@ -157,6 +157,15 @@ void RTInstances::compilation_agent(compilation_subtask *t) { if ((K_sound_name) && (Kinds::eq(K, K_sound_name))) Hierarchy::apply_metadata_from_number(pack, INSTANCE_IS_SOUND_MD_HL, 1); + if ((K_dialogue_beat) && (Kinds::eq(K, K_dialogue_beat))) + Hierarchy::apply_metadata_from_number(pack, + INSTANCE_IS_DB_MD_HL, 1); + if ((K_dialogue_line) && (Kinds::eq(K, K_dialogue_line))) + Hierarchy::apply_metadata_from_number(pack, + INSTANCE_IS_DL_MD_HL, 1); + if ((K_dialogue_choice) && (Kinds::eq(K, K_dialogue_choice))) + Hierarchy::apply_metadata_from_number(pack, + INSTANCE_IS_DC_MD_HL, 1); if ((K_figure_name) && (Kinds::eq(K, K_figure_name))) Hierarchy::apply_metadata_from_number(pack, INSTANCE_IS_FIGURE_MD_HL, 1); diff --git a/inform7/runtime-module/Contents.w b/inform7/runtime-module/Contents.w index 34cd7f633..c4e3b8cfc 100644 --- a/inform7/runtime-module/Contents.w +++ b/inform7/runtime-module/Contents.w @@ -61,7 +61,9 @@ Chapter 5: Provision Submodules Multimedia Tables Table Columns - Dialogue + Dialogue Beat Instances + Dialogue Line Instances + Dialogue Choice Instances Rules Rulebooks Variables diff --git a/inter/pipeline-module/Chapter 5/Dialogue.w b/inter/pipeline-module/Chapter 5/Dialogue.w new file mode 100644 index 000000000..11acd7c13 --- /dev/null +++ b/inter/pipeline-module/Chapter 5/Dialogue.w @@ -0,0 +1,46 @@ +[SynopticDialogue::] Dialogue. + +To compile the main/synoptic/dialogue submodule. + +@ Our inventory |inv| already contains a list |inv->instance_nodes| of all packages +in the tree with type |_instance|. + +For the moment, at least, it seems too ambitious to dynamically renumber instances +in the linking stage. Until then, this section is something of a placeholder, +making only a debugging function. + += +void SynopticDialogue::compile(inter_tree *I, pipeline_step *step, tree_inventory *inv) { + if (InterNodeList::array_len(inv->instance_nodes) > 0) + InterNodeList::array_sort(inv->instance_nodes, MakeSynopticModuleStage::module_order); + @; +} + +@ = + inter_ti count = 0; + for (int i=0; iinstance_nodes); i++) { + inter_package *pack = + PackageInstruction::at_this_head(inv->instance_nodes->list[i].node); + if (Metadata::read_optional_numeric(pack, I"^is_dialogue_beat")) count++; + } + + inter_name *iname = HierarchyLocations::iname(I, DIALOGUEBEATS_HL); + Synoptic::begin_array(I, step, iname); + Synoptic::numeric_entry(count); + if (count == 0) Synoptic::numeric_entry(0); + for (int i=0; iinstance_nodes); i++) { + inter_package *pack = + PackageInstruction::at_this_head(inv->instance_nodes->list[i].node); + if (Metadata::read_optional_numeric(pack, I"^is_dialogue_beat")) { + inter_symbol *filter = Metadata::optional_symbol(pack, I"^available"); + if (filter) Synoptic::symbol_entry(filter); + else Synoptic::numeric_entry(0); + inter_symbol *rel = Metadata::optional_symbol(pack, I"^relevant"); + if (rel) Synoptic::symbol_entry(rel); + else Synoptic::numeric_entry(0); + inter_symbol *str = Metadata::optional_symbol(pack, I"^structure"); + if (str) Synoptic::symbol_entry(str); + else Synoptic::numeric_entry(0); + } + } + Synoptic::end_array(I); diff --git a/inter/pipeline-module/Chapter 5/Make Synoptic Module Stage.w b/inter/pipeline-module/Chapter 5/Make Synoptic Module Stage.w index 2742c9c9b..2c34babdf 100644 --- a/inter/pipeline-module/Chapter 5/Make Synoptic Module Stage.w +++ b/inter/pipeline-module/Chapter 5/Make Synoptic Module Stage.w @@ -28,6 +28,7 @@ int MakeSynopticModuleStage::run(pipeline_step *step) { SynopticChronology::compile(I, step, inv); SynopticExtensions::compile(I, step, inv); SynopticInstances::compile(I, step, inv); + SynopticDialogue::compile(I, step, inv); SynopticKinds::compile(I, step, inv); SynopticMultimedia::compile(I, step, inv); SynopticProperties::compile(I, step, inv); diff --git a/inter/pipeline-module/Chapter 5/Synoptic Hierarchy.w b/inter/pipeline-module/Chapter 5/Synoptic Hierarchy.w index fd51987d3..bdcc8537e 100644 --- a/inter/pipeline-module/Chapter 5/Synoptic Hierarchy.w +++ b/inter/pipeline-module/Chapter 5/Synoptic Hierarchy.w @@ -21,6 +21,7 @@ void SynopticHierarchy::establish(inter_tree *I) { @; @; @; + @; @; @; @; @@ -117,6 +118,15 @@ The |/main/synoptic/conjugations| submodule. SYN_SUBMD(I"conjugations") SYN_CONST(TABLEOFVERBS_HL, I"TableOfVerbs") +@h Dialogue. +The |/main/synoptic/dialogue| submodule. + +@e DIALOGUEBEATS_HL + +@ = + SYN_SUBMD(I"dialogue") + SYN_CONST(DIALOGUEBEATS_HL, I"TableOfDialogueBeats") + @h Extensions. The |/main/synoptic/extensions| submodule. diff --git a/inter/pipeline-module/Contents.w b/inter/pipeline-module/Contents.w index 606ebccba..0e2c1dc5a 100644 --- a/inter/pipeline-module/Contents.w +++ b/inter/pipeline-module/Contents.w @@ -41,6 +41,7 @@ Chapter 5: Synoptic Module Activities Actions Instances + Dialogue Kinds Properties Relations diff --git a/services/syntax-module/Chapter 3/Sentences.w b/services/syntax-module/Chapter 3/Sentences.w index a76c8ae4b..a57916a7d 100644 --- a/services/syntax-module/Chapter 3/Sentences.w +++ b/services/syntax-module/Chapter 3/Sentences.w @@ -706,6 +706,11 @@ here. This hand-tooled parser is annoyingly long to write out, but only in order to catch improbable unmatched-bracket errors with tidy error messages. @ = + #ifdef DIALOGUE_WARNING_SYNTAX_CALLBACK + if (T->contains_dialogue == FALSE) { + DIALOGUE_WARNING_SYNTAX_CALLBACK(); + } + #endif T->contains_dialogue = TRUE; if ((Lexer::word(Wordings::first_wn(W)) == OPENBRACKET_V) && (Lexer::word(Wordings::last_wn(W)) == CLOSEBRACKET_V))