From 4cf58c4190a558affb202aa6ac8577da5431743d Mon Sep 17 00:00:00 2001 From: rapidsamphire Date: Wed, 19 Nov 2025 03:38:43 -0600 Subject: [PATCH] =?UTF-8?q?=20=20-=20Added=20a=20dialogue=20definition=20h?= =?UTF-8?q?eader=20that=20formalizes=20speaker/topic=20enums=20plus=20a=20?= =?UTF-8?q?comprehensive=20DialogueVariableType=20list,=20attribute=20flag?= =?UTF-8?q?s,=20and=20lookup=20API=20so=20future=20=20=20=20=20systems=20c?= =?UTF-8?q?an=20reason=20about=20every=20%token=20documented=20in=20the=20?= =?UTF-8?q?Arena=20reference=20(OpenTESArena/src/Interface/DialogueDefinit?= =?UTF-8?q?ion.h:1-156).=20=20=20-=20Implemented=20the=20backing=20metadat?= =?UTF-8?q?a=20table=20in=20DialogueDefinition.cpp,=20capturing=20each=20t?= =?UTF-8?q?oken=E2=80=99s=20description,=20whether=20it=20randomizes,=20no?= =?UTF-8?q?rmalizes=20whitespace,=20or=20sets=20speaker=20=20=20=20=20gend?= =?UTF-8?q?er,=20and=20exposed=20helpers=20to=20iterate=20or=20resolve=20v?= =?UTF-8?q?ariables=20by=20token/enum=20(OpenTESArena/src/Interface/Dialog?= =?UTF-8?q?ueDefinition.cpp:1-135).=20=20=20-=20Registered=20the=20new=20d?= =?UTF-8?q?ialogue=20definitions=20in=20the=20Interface=20target=20so=20th?= =?UTF-8?q?ey=20build=20with=20the=20rest=20of=20the=20game=20code=20(Open?= =?UTF-8?q?TESArena/CMakeLists.txt:208-360).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenTESArena/CMakeLists.txt | 16 +- .../src/Interface/DialogueDefinition.cpp | 135 +++++++++++++++ .../src/Interface/DialogueDefinition.h | 158 ++++++++++++++++++ 3 files changed, 302 insertions(+), 7 deletions(-) create mode 100644 OpenTESArena/src/Interface/DialogueDefinition.cpp create mode 100644 OpenTESArena/src/Interface/DialogueDefinition.h diff --git a/OpenTESArena/CMakeLists.txt b/OpenTESArena/CMakeLists.txt index d73df587f..dc3151d30 100644 --- a/OpenTESArena/CMakeLists.txt +++ b/OpenTESArena/CMakeLists.txt @@ -245,13 +245,15 @@ SET(TES_INTERFACE "${SRC_ROOT}/Interface/CinematicLibrary.cpp" "${SRC_ROOT}/Interface/CinematicLibrary.h" "${SRC_ROOT}/Interface/CinematicPanel.cpp" - "${SRC_ROOT}/Interface/CinematicPanel.h" - "${SRC_ROOT}/Interface/CommonUiController.cpp" - "${SRC_ROOT}/Interface/CommonUiController.h" - "${SRC_ROOT}/Interface/CommonUiView.cpp" - "${SRC_ROOT}/Interface/CommonUiView.h" - "${SRC_ROOT}/Interface/FastTravelSubPanel.cpp" - "${SRC_ROOT}/Interface/FastTravelSubPanel.h" + "${SRC_ROOT}/Interface/CinematicPanel.h" + "${SRC_ROOT}/Interface/CommonUiController.cpp" + "${SRC_ROOT}/Interface/CommonUiController.h" + "${SRC_ROOT}/Interface/CommonUiView.cpp" + "${SRC_ROOT}/Interface/CommonUiView.h" + "${SRC_ROOT}/Interface/DialogueDefinition.cpp" + "${SRC_ROOT}/Interface/DialogueDefinition.h" + "${SRC_ROOT}/Interface/FastTravelSubPanel.cpp" + "${SRC_ROOT}/Interface/FastTravelSubPanel.h" "${SRC_ROOT}/Interface/GameWorldPanel.cpp" "${SRC_ROOT}/Interface/GameWorldPanel.h" "${SRC_ROOT}/Interface/GameWorldUiController.cpp" diff --git a/OpenTESArena/src/Interface/DialogueDefinition.cpp b/OpenTESArena/src/Interface/DialogueDefinition.cpp new file mode 100644 index 000000000..99d586842 --- /dev/null +++ b/OpenTESArena/src/Interface/DialogueDefinition.cpp @@ -0,0 +1,135 @@ +#include "DialogueDefinition.h" + +#include +#include + +#include "components/debug/Debug.h" + +namespace +{ + using DialogueVariableDefinitionArray = std::array(DialogueVariableType::Count)>; + + constexpr DialogueVariableDefinition makeVar(DialogueVariableType type, std::string_view token, + std::string_view description, DialogueVariableAttributes attributes = DialogueVariableAttribute::None) + { + return DialogueVariableDefinition{ type, token, description, attributes }; + } + + constexpr DialogueVariableDefinitionArray DialogueVariableDefinitions = + { + makeVar(DialogueVariableType::ActiveQuestReward, "%a", "Active quest reward amount."), + makeVar(DialogueVariableType::NpcNameAdjective, "%adj", "NPC name adjective, paired with the prefix data."), + makeVar(DialogueVariableType::UnknownAdn, "%adn", "Undocumented variable (mirrors %adj data)."), + makeVar(DialogueVariableType::NpcNameAdverb, "%adv", "NPC name adverb, paired with the prefix data."), + makeVar(DialogueVariableType::UnknownAmn, "%amn", "Undocumented variable related to NPC names."), + makeVar(DialogueVariableType::UnknownAn, "%an", "Undocumented variable related to NPC names."), + makeVar(DialogueVariableType::ActiveQuestDungeonType, "%at", "Active quest dungeon type (Mine, Cavern, Labyrinth, etc.)."), + makeVar(DialogueVariableType::CurrentArtifactName, "%art", "Current artifact name."), + makeVar(DialogueVariableType::ArtifactProvinceName, "%apr", "Province name referenced by the current artifact rumor."), + makeVar(DialogueVariableType::UnknownArc, "%arc", "Undocumented variable referenced by the artifact quest."), + makeVar(DialogueVariableType::ActiveQuestBonusReward, "%ba", "Active quest bonus reward (unused in classic Arena)."), + makeVar(DialogueVariableType::CurrentMainQuestCity, "%ccs", "Current main quest city."), + makeVar(DialogueVariableType::CurrentMainQuestDungeon, "%ccl", "Current main quest dungeon."), + makeVar(DialogueVariableType::CurrentCityOrProvince, "%cn", "Current city name, or current province name while in the wilderness."), + makeVar(DialogueVariableType::NearestSettlementName, "%cn2", "Nearest settlement name of the same type as the current city."), + makeVar(DialogueVariableType::CurrentMainQuestProvince, "%cp", "Current main quest province."), + makeVar(DialogueVariableType::CurrentCityType, "%ct", "Current city type (city-state, town, village, etc.)."), + makeVar(DialogueVariableType::ActiveQuestDeadlineDate, "%da", "Active quest deadline date (short format)."), + makeVar(DialogueVariableType::ActiveQuestDeadlineDays, "%de", "Active quest deadline in days."), + makeVar(DialogueVariableType::NpcDescription, "%des", "NPC description (gender-aware string)."), + makeVar(DialogueVariableType::DirectionToDialoguePoint, "%di", "Direction to the dialogue point being referenced."), + makeVar(DialogueVariableType::UnknownDit, "%dit", "Undocumented variable related to directions."), + makeVar(DialogueVariableType::UnknownDoc, "%doc", "Undocumented variable."), + makeVar(DialogueVariableType::ActiveQuestGiverDescription, "%ds", "Active quest giver description (matches NPC descriptor data)."), + makeVar(DialogueVariableType::UnknownDu, "%du", "Undocumented variable."), + makeVar(DialogueVariableType::DialogueNpcLocalNameFirstWord, "%dnl", "First word of the %nc value."), + makeVar(DialogueVariableType::UnknownEf, "%ef", "Undocumented variable."), + makeVar(DialogueVariableType::UnknownEn, "%en", "Undocumented variable."), + makeVar(DialogueVariableType::ActiveQuestGiverFirstName, "%fq", "Active quest giver's first name (female if the giver seed & 3)."), + makeVar(DialogueVariableType::UnknownFn, "%fn", "Undocumented variable."), + makeVar(DialogueVariableType::SpeakerPronounSubject, "%g", "Speaker pronoun he/she/it depending on dlgGender."), + makeVar(DialogueVariableType::SpeakerPronounObject, "%g2", "Speaker pronoun him/her/it depending on dlgGender."), + makeVar(DialogueVariableType::SpeakerPronounPossessive, "%g3", "Speaker pronoun his/her/its depending on dlgGender."), + makeVar(DialogueVariableType::UnknownHp, "%hp", "Undocumented variable."), + makeVar(DialogueVariableType::PlayerHomeCity, "%hc", "Player's home city."), + makeVar(DialogueVariableType::NearestHolidayString, "%hod", "Nearest holiday text entry (random normalized #169+holidayId).", + DialogueVariableAttribute::NormalizedWhitespace | DialogueVariableAttribute::RandomizedSelection), + makeVar(DialogueVariableType::RandomJoke, "%jok", "Random joke from TEMPLATE.DAT (normalized #363).", + DialogueVariableAttribute::NormalizedWhitespace | DialogueVariableAttribute::RandomizedSelection), + makeVar(DialogueVariableType::CurrentProvince, "%lp", "Current province name."), + makeVar(DialogueVariableType::NobleQuestItemName, "%mi", "Item name for the active noble quest."), + makeVar(DialogueVariableType::ActiveQuestMonsterName, "%mn", "Monster name for the active quest."), + makeVar(DialogueVariableType::ArtifactMapProvinceName, "%mpr", "Province where the artifact map is located."), + makeVar(DialogueVariableType::UnknownMqt, "%mqt", "Undocumented variable."), + makeVar(DialogueVariableType::ActiveQuestMonsterType, "%mt", "Monster type for the active quest (\"Troll\", etc.)."), + makeVar(DialogueVariableType::UnknownN, "%n", "Undocumented variable."), + makeVar(DialogueVariableType::ArtifactPrice, "%nap", "Artifact price."), + makeVar(DialogueVariableType::ActiveQuestLocalName, "%nc", "Local name for the active quest."), + makeVar(DialogueVariableType::UnknownNcl, "%ncl", "Undocumented variable."), + makeVar(DialogueVariableType::ActiveQuestLocalAlias, "%ne", "Alternate local name for the active quest."), + makeVar(DialogueVariableType::NearestHolidayName, "%nh", "Nearest holiday name."), + makeVar(DialogueVariableType::NearestHolidayDate, "%nhd", "Nearest holiday date without the year."), + makeVar(DialogueVariableType::EmptyString, "%non", "Empty string placeholder."), + makeVar(DialogueVariableType::ActiveQuestLocalAliasAlt, "%nr", "Alternate local name for the active quest (same as %ne)."), + makeVar(DialogueVariableType::UnknownNt, "%nt", "Undocumented variable."), + makeVar(DialogueVariableType::RivalFactionName, "%o", "Rival faction name for the active quest, or %qf when not available."), + makeVar(DialogueVariableType::UnknownOap, "%oap", "Undocumented variable."), + makeVar(DialogueVariableType::UnknownOc, "%oc", "Undocumented variable."), + makeVar(DialogueVariableType::ActiveQuestObjectiveItem, "%omq", "Quest item for the active quest (destination dependent)."), + makeVar(DialogueVariableType::UnknownOpp, "%opp", "Undocumented variable."), + makeVar(DialogueVariableType::Oath, "%oth", "Oath string pulled from the province oath table.", + DialogueVariableAttribute::RandomizedSelection), + makeVar(DialogueVariableType::PlayerFullName, "%pcn", "Player full name (sets dlgGender).", + DialogueVariableAttribute::SetsSpeakerGender), + makeVar(DialogueVariableType::PlayerFirstName, "%pcf", "Player first name (sets dlgGender).", + DialogueVariableAttribute::SetsSpeakerGender), + makeVar(DialogueVariableType::NpcNamePrefix, "%pre", "NPC name prefix from the descriptor table."), + makeVar(DialogueVariableType::ActiveQuestCityName, "%qc", "City where the active quest was taken."), + makeVar(DialogueVariableType::ActiveQuestTypeDescription, "%qt", "Description of the active quest type."), + makeVar(DialogueVariableType::ActiveQuestOpponentName, "%qf", "Title + first name for the opponent in the active quest."), + makeVar(DialogueVariableType::UnknownQmn, "%qmn", "Undocumented variable."), + makeVar(DialogueVariableType::ActiveQuestRelation, "%r", "Relation type for the active quest (\"daughter\", etc.)."), + makeVar(DialogueVariableType::PlayerRaceName, "%ra", "Player race name."), + makeVar(DialogueVariableType::RandomCityStateInProvince, "%rcn", "Random city-state name in the current province.", + DialogueVariableAttribute::RandomizedSelection), + makeVar(DialogueVariableType::RulerFirstName, "%rf", "Province ruler first name, or \"Uriel Septim\" when applicable."), + makeVar(DialogueVariableType::RandomProvinceName, "%rpn", "Random province name.", + DialogueVariableAttribute::RandomizedSelection), + makeVar(DialogueVariableType::UnknownSn, "%sn", "Undocumented variable."), + makeVar(DialogueVariableType::StateOfWarOrPeace, "%st", "Current war/peace state (see War/Peace algorithm)."), + makeVar(DialogueVariableType::NpcNameSuffix, "%suf", "NPC name suffix from the descriptor data."), + makeVar(DialogueVariableType::RulerTitle, "%t", "Province ruler title."), + makeVar(DialogueVariableType::TargetDungeonName, "%tan", "Dungeon name tied to the dialogue context."), + makeVar(DialogueVariableType::TargetCityName, "%tc", "Target city for the active quest."), + makeVar(DialogueVariableType::UnknownTem, "%tem", "Undocumented variable."), + makeVar(DialogueVariableType::TargetFactionName, "%tg", "Target faction name for the active quest."), + makeVar(DialogueVariableType::UnknownTi, "%ti", "Undocumented variable (same as %du)."), + makeVar(DialogueVariableType::TargetLocationName, "%tl", "Target venue name for the active quest, or %dnl fallback."), + makeVar(DialogueVariableType::ActiveQuestGiverTitle, "%tq", "Quest giver title for the active quest."), + makeVar(DialogueVariableType::ActiveQuestNpcClass, "%tt", "Class of the NPC involved in the active quest.") + }; +} + +std::span DialogueDefinitionLibrary::getVariableDefinitions() +{ + return DialogueVariableDefinitions; +} + +const DialogueVariableDefinition *DialogueDefinitionLibrary::tryGetVariableDefinitionByToken(std::string_view token) +{ + const auto iter = std::find_if(DialogueVariableDefinitions.begin(), DialogueVariableDefinitions.end(), + [token](const DialogueVariableDefinition &definition) + { + return definition.token == token; + }); + + return (iter != DialogueVariableDefinitions.end()) ? &(*iter) : nullptr; +} + +const DialogueVariableDefinition &DialogueDefinitionLibrary::getVariableDefinition(DialogueVariableType type) +{ + const size_t index = static_cast(type); + DebugAssertIndex(DialogueVariableDefinitions, index); + return DialogueVariableDefinitions[index]; +} diff --git a/OpenTESArena/src/Interface/DialogueDefinition.h b/OpenTESArena/src/Interface/DialogueDefinition.h new file mode 100644 index 000000000..70ad16e08 --- /dev/null +++ b/OpenTESArena/src/Interface/DialogueDefinition.h @@ -0,0 +1,158 @@ +#ifndef DIALOGUE_DEFINITION_H +#define DIALOGUE_DEFINITION_H + +#include +#include +#include +#include + +#include "components/utilities/Enum.h" + +enum class DialogueSpeakerType +{ + Citizen, + StaticNpc, + Guard, + Shopkeeper, + GuildMember, + Noble, + QuestGiver, + MainQuestNpc, + Unknown +}; + +enum class DialogueTopicType +{ + Greeting, + Rumor, + Direction, + Quest, + Event, + Trade, + Joke, + Unknown +}; + +enum class DialogueVariableType +{ + ActiveQuestReward, + NpcNameAdjective, + UnknownAdn, + NpcNameAdverb, + UnknownAmn, + UnknownAn, + ActiveQuestDungeonType, + CurrentArtifactName, + ArtifactProvinceName, + UnknownArc, + ActiveQuestBonusReward, + CurrentMainQuestCity, + CurrentMainQuestDungeon, + CurrentCityOrProvince, + NearestSettlementName, + CurrentMainQuestProvince, + CurrentCityType, + ActiveQuestDeadlineDate, + ActiveQuestDeadlineDays, + NpcDescription, + DirectionToDialoguePoint, + UnknownDit, + UnknownDoc, + ActiveQuestGiverDescription, + UnknownDu, + DialogueNpcLocalNameFirstWord, + UnknownEf, + UnknownEn, + ActiveQuestGiverFirstName, + UnknownFn, + SpeakerPronounSubject, + SpeakerPronounObject, + SpeakerPronounPossessive, + UnknownHp, + PlayerHomeCity, + NearestHolidayString, + RandomJoke, + CurrentProvince, + NobleQuestItemName, + ActiveQuestMonsterName, + ArtifactMapProvinceName, + UnknownMqt, + ActiveQuestMonsterType, + UnknownN, + ArtifactPrice, + ActiveQuestLocalName, + UnknownNcl, + ActiveQuestLocalAlias, + NearestHolidayName, + NearestHolidayDate, + EmptyString, + ActiveQuestLocalAliasAlt, + UnknownNt, + RivalFactionName, + UnknownOap, + UnknownOc, + ActiveQuestObjectiveItem, + UnknownOpp, + Oath, + PlayerFullName, + PlayerFirstName, + NpcNamePrefix, + ActiveQuestCityName, + ActiveQuestTypeDescription, + ActiveQuestOpponentName, + UnknownQmn, + ActiveQuestRelation, + PlayerRaceName, + RandomCityStateInProvince, + RulerFirstName, + RandomProvinceName, + UnknownSn, + StateOfWarOrPeace, + NpcNameSuffix, + RulerTitle, + TargetDungeonName, + TargetCityName, + UnknownTem, + TargetFactionName, + UnknownTi, + TargetLocationName, + ActiveQuestGiverTitle, + ActiveQuestNpcClass, + Count +}; + +enum class DialogueVariableAttribute : uint32_t +{ + None = 0, + NormalizedWhitespace = 1 << 0, + RandomizedSelection = 1 << 1, + SetsSpeakerGender = 1 << 2 +}; +AllowEnumFlags(DialogueVariableAttribute); +using DialogueVariableAttributes = EnumFlags; + +struct DialogueVariableDefinition +{ + DialogueVariableType type; + std::string_view token; + std::string_view description; + DialogueVariableAttributes attributes; +}; + +struct DialogueBlockDefinition +{ + DialogueSpeakerType speakerType; + DialogueTopicType topicType; + int templateDatKey; + std::optional letter; +}; + +class DialogueDefinitionLibrary +{ +public: + static std::span getVariableDefinitions(); + static const DialogueVariableDefinition *tryGetVariableDefinitionByToken(std::string_view token); + static const DialogueVariableDefinition &getVariableDefinition(DialogueVariableType type); +}; + +#endif