From fe9ba9171ef1f5461d5ed45cb19abd86bbcad2b4 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 10 Nov 2022 18:07:23 +0300 Subject: [PATCH] Add custom message lists support Closes #130 See #200 --- src/combat.cc | 3 + src/combat_ai.cc | 3 + src/critter.cc | 3 + src/game.cc | 9 ++ src/item.cc | 3 + src/map.cc | 4 + src/message.cc | 210 +++++++++++++++++++++++++++++++++++++++++++ src/message.h | 59 ++++++++++++ src/perk.cc | 3 + src/proto.cc | 8 ++ src/scripts.cc | 7 ++ src/sfall_config.cc | 1 + src/sfall_config.h | 1 + src/sfall_opcodes.cc | 11 +++ src/skill.cc | 3 + src/stat.cc | 3 + src/trait.cc | 3 + src/worldmap.cc | 3 + 18 files changed, 337 insertions(+) diff --git a/src/combat.cc b/src/combat.cc index 6da77dd..93fbfbc 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -2022,6 +2022,8 @@ int combatInit() return -1; } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_COMBAT, &gCombatMessageList); + // SFALL criticalsInit(); burstModInit(); @@ -2061,6 +2063,7 @@ void combatReset() // 0x420E14 void combatExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_COMBAT, nullptr); messageListFree(&gCombatMessageList); // SFALL diff --git a/src/combat_ai.cc b/src/combat_ai.cc index 5fe3625..04cef5d 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -3433,6 +3433,8 @@ static int aiMessageListInit() messageListFilterBadwords(&gCombatAiMessageList); } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_COMBAT_AI, &gCombatAiMessageList); + return 0; } @@ -3441,6 +3443,7 @@ static int aiMessageListInit() // 0x42BBD8 static int aiMessageListFree() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_COMBAT_AI, nullptr); if (!messageListFree(&gCombatAiMessageList)) { return -1; } diff --git a/src/critter.cc b/src/critter.cc index f351e5a..c3345bf 100644 --- a/src/critter.cc +++ b/src/critter.cc @@ -178,6 +178,8 @@ int critterInit() return -1; } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRNAME, &gCritterMessageList); + return 0; } @@ -193,6 +195,7 @@ void critterReset() // 0x42D004 void critterExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRNAME, nullptr); messageListFree(&gCritterMessageList); } diff --git a/src/game.cc b/src/game.cc index 6f8af68..6df6d17 100644 --- a/src/game.cc +++ b/src/game.cc @@ -151,6 +151,10 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 return -1; } + // Message list repository is considered a specialized file manager, so + // it should be initialized early in the process. + messageListRepositoryInit(); + runElectronicRegistration(); programWindowSetTitle(windowTitle); _initWindow(1, a4); @@ -358,6 +362,8 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 return -1; } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList); + return 0; } @@ -402,6 +408,7 @@ void gameReset() // SFALL sfallGlobalVarsReset(); sfallListsReset(); + messageListRepositoryReset(); } // 0x442C34 @@ -415,6 +422,7 @@ void gameExit() premadeCharactersExit(); tileDisable(); + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, nullptr); messageListFree(&gMiscMessageList); combatExit(); gameDialogExit(); @@ -448,6 +456,7 @@ void gameExit() endgameDeathEndingExit(); interfaceFontsExit(); _windowClose(); + messageListRepositoryExit(); dbExit(); settingsExit(true); sfallConfigExit(); diff --git a/src/item.cc b/src/item.cc index a75aa08..75583e3 100644 --- a/src/item.cc +++ b/src/item.cc @@ -201,6 +201,8 @@ int itemsInit() return -1; } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_ITEM, &gItemsMessageList); + // SFALL booksInit(); explosionsInit(); @@ -219,6 +221,7 @@ void itemsReset() // 0x477148 void itemsExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_ITEM, nullptr); messageListFree(&gItemsMessageList); // SFALL diff --git a/src/map.cc b/src/map.cc index ca4d214..ce5304d 100644 --- a/src/map.cc +++ b/src/map.cc @@ -306,6 +306,8 @@ void _map_init() tickersAdd(gameMouseRefresh); _gmouse_disable(0); windowUnhide(gIsoWindow); + + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MAP, &gMapMessageList); } // 0x482084 @@ -314,6 +316,8 @@ void _map_exit() windowHide(gIsoWindow); gameMouseSetCursor(MOUSE_CURSOR_ARROW); tickersRemove(gameMouseRefresh); + + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MAP, nullptr); if (!messageListFree(&gMapMessageList)) { debugPrint("\nError exiting map_msg_file!"); } diff --git a/src/message.cc b/src/message.cc index acefd06..ce87165 100644 --- a/src/message.cc +++ b/src/message.cc @@ -5,6 +5,9 @@ #include #include +#include +#include + #include "debug.h" #include "memory.h" #include "platform_compat.h" @@ -17,11 +20,33 @@ namespace fallout { #define BADWORD_LENGTH_MAX 80 +static constexpr int kFirstStandardMessageListId = 0; +static constexpr int kLastStandardMessageListId = kFirstStandardMessageListId + STANDARD_MESSAGE_LIST_COUNT - 1; + +static constexpr int kFirstProtoMessageListId = 0x1000; +static constexpr int kLastProtoMessageListId = kFirstProtoMessageListId + PROTO_MESSAGE_LIST_COUNT - 1; + +static constexpr int kFirstPersistentMessageListId = 0x2000; +static constexpr int kLastPersistentMessageListId = 0x2FFF; + +static constexpr int kFirstTemporaryMessageListId = 0x3000; +static constexpr int kLastTemporaryMessageListId = 0x3FFF; + +struct MessageListRepositoryState { + std::array standardMessageLists; + std::array protoMessageLists; + std::unordered_map persistentMessageLists; + std::unordered_map temporaryMessageLists; + int nextTemporaryMessageListId = kFirstTemporaryMessageListId; +}; + static bool _message_find(MessageList* msg, int num, int* out_index); static bool _message_add(MessageList* msg, MessageListItem* new_entry); static bool _message_parse_number(int* out_num, const char* str); static int _message_load_field(File* file, char* str); +static MessageList* messageListRepositoryLoad(const char* path); + // 0x50B79C static char _Error_1[] = "Error"; @@ -47,6 +72,8 @@ static char* _message_error_str = _Error_1; // 0x63207C static char _bad_copy[MESSAGE_LIST_ITEM_FIELD_MAX_SIZE]; +static MessageListRepositoryState* _messageListRepositoryState; + // 0x484770 int badwordsInit() { @@ -604,4 +631,187 @@ void messageListFilterGenderWords(MessageList* messageList, int gender) } } +bool messageListRepositoryInit() +{ + _messageListRepositoryState = new (std::nothrow) MessageListRepositoryState(); + if (_messageListRepositoryState == nullptr) { + return false; + } + + char* fileList; + configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, &fileList); + if (fileList != nullptr && *fileList == '\0') { + fileList = nullptr; + } + + char path[COMPAT_MAX_PATH]; + int nextMessageListId = 0; + while (fileList != nullptr) { + char* pch = strchr(fileList, ','); + if (pch != nullptr) { + *pch = '\0'; + } + + char* sep = strchr(fileList, ':'); + if (sep != nullptr) { + *sep = '\0'; + nextMessageListId = atoi(sep + 1); + } + + sprintf(path, "%s\\%s.msg", "game", fileList); + + if (sep != nullptr) { + *sep = ':'; + } + + MessageList* messageList = messageListRepositoryLoad(path); + if (messageList != nullptr) { + _messageListRepositoryState->persistentMessageLists[kFirstPersistentMessageListId + nextMessageListId] = messageList; + } + + if (pch != nullptr) { + *pch = ','; + fileList = pch + 1; + } else { + fileList = nullptr; + } + + // Sfall's implementation is a little bit odd. |nextMessageListId| can + // be set via "key:value" pair in the config, so if the first pair is + // "msg:12287", then this check will think it's the end of the loop. + // In order to maintain compatibility we'll use the same approach, + // however it looks like the whole idea of auto-numbering extra message + // lists is a bad one. To use these extra message lists we need to + // specify their ids from user-space scripts. Without explicitly + // specifying message list ids as "key:value" pairs a mere change of + // order in the config will break such scripts in an unexpected way. + nextMessageListId++; + if (nextMessageListId == kLastPersistentMessageListId - kFirstPersistentMessageListId + 1) { + break; + } + } + + return true; +} + +void messageListRepositoryReset() +{ + for (auto& pair : _messageListRepositoryState->temporaryMessageLists) { + messageListFree(pair.second); + delete pair.second; + } + _messageListRepositoryState->temporaryMessageLists.clear(); + _messageListRepositoryState->nextTemporaryMessageListId = kFirstTemporaryMessageListId; +} + +void messageListRepositoryExit() +{ + if (_messageListRepositoryState != nullptr) { + for (auto& pair : _messageListRepositoryState->temporaryMessageLists) { + messageListFree(pair.second); + delete pair.second; + } + + for (auto& pair : _messageListRepositoryState->persistentMessageLists) { + messageListFree(pair.second); + delete pair.second; + } + + delete _messageListRepositoryState; + _messageListRepositoryState = nullptr; + } +} + +void messageListRepositorySetStandardMessageList(int standardMessageList, MessageList* messageList) +{ + _messageListRepositoryState->standardMessageLists[standardMessageList] = messageList; +} + +void messageListRepositorySetProtoMessageList(int protoMessageList, MessageList* messageList) +{ + _messageListRepositoryState->protoMessageLists[protoMessageList] = messageList; +} + +int messageListRepositoryAddExtra(int messageListId, const char* path) +{ + if (messageListId != 0) { + // CE: Probably there is a bug in Sfall, when |messageListId| is + // non-zero, it is enforced to be within persistent id range. That is + // the scripting engine is allowed to add persistent message lists. + // Everything added/changed by scripting engine should be temporary by + // design. + if (messageListId < kFirstPersistentMessageListId || messageListId > kLastPersistentMessageListId) { + return -1; + } + + // CE: Sfall stores both persistent and temporary message lists in + // one map, however since we've passed check above, we should only + // check in persistent message lists. + if (_messageListRepositoryState->persistentMessageLists.find(messageListId) != _messageListRepositoryState->persistentMessageLists.end()) { + return 0; + } + } else { + if (_messageListRepositoryState->nextTemporaryMessageListId > kLastTemporaryMessageListId) { + return -3; + } + } + + MessageList* messageList = messageListRepositoryLoad(path); + if (messageList == nullptr) { + return -2; + } + + if (messageListId == 0) { + messageListId == _messageListRepositoryState->nextTemporaryMessageListId++; + } + + _messageListRepositoryState->temporaryMessageLists[messageListId] = messageList; + + return messageListId; +} + +char* messageListRepositoryGetMsg(int messageListId, int messageId) +{ + MessageList* messageList = nullptr; + + if (messageListId >= kFirstStandardMessageListId && messageListId <= kLastStandardMessageListId) { + messageList = _messageListRepositoryState->standardMessageLists[messageListId - kFirstStandardMessageListId]; + } else if (messageListId >= kFirstProtoMessageListId && messageListId <= kLastProtoMessageListId) { + messageList = _messageListRepositoryState->protoMessageLists[messageListId - kFirstProtoMessageListId]; + } else if (messageListId >= kFirstPersistentMessageListId && messageListId <= kLastPersistentMessageListId) { + auto it = _messageListRepositoryState->persistentMessageLists.find(messageListId); + if (it != _messageListRepositoryState->persistentMessageLists.end()) { + messageList = it->second; + } + } else if (messageListId >= kFirstTemporaryMessageListId && messageListId <= kLastTemporaryMessageListId) { + auto it = _messageListRepositoryState->temporaryMessageLists.find(messageListId); + if (it != _messageListRepositoryState->temporaryMessageLists.end()) { + messageList = it->second; + } + } + + MessageListItem messageListItem; + return getmsg(messageList, &messageListItem, messageId); +} + +static MessageList* messageListRepositoryLoad(const char* path) +{ + MessageList* messageList = new (std::nothrow) MessageList(); + if (messageList == nullptr) { + return nullptr; + } + + if (!messageListInit(messageList)) { + delete messageList; + return nullptr; + } + + if (!messageListLoad(messageList, path)) { + delete messageList; + return nullptr; + } + + return messageList; +} + } // namespace fallout diff --git a/src/message.h b/src/message.h index b39d591..e52250f 100644 --- a/src/message.h +++ b/src/message.h @@ -7,6 +7,57 @@ namespace fallout { #define MESSAGE_LIST_ITEM_FIELD_MAX_SIZE 1024 +// CE: Working with standard message lists is tricky in Sfall. Many message +// lists are initialized only for the duration of appropriate modal window. This +// is not documented in Sfall and shifts too much responsibility to scripters +// (who should check game mode before accessing volatile message lists). For now +// CE only exposes persistent standard message lists: +// - combat.msg +// - combatai.msg +// - scrname.msg +// - misc.msg +// - item.msg +// - map.msg +// - proto.msg +// - script.msg +// - skill.msg +// - stat.msg +// - trait.msg +// - worldmap.msg +enum StandardMessageList { + STANDARD_MESSAGE_LIST_COMBAT, + STANDARD_MESSAGE_LIST_COMBAT_AI, + STANDARD_MESSAGE_LIST_SCRNAME, + STANDARD_MESSAGE_LIST_MISC, + STANDARD_MESSAGE_LIST_CUSTOM, + STANDARD_MESSAGE_LIST_INVENTORY, + STANDARD_MESSAGE_LIST_ITEM, + STANDARD_MESSAGE_LIST_LSGAME, + STANDARD_MESSAGE_LIST_MAP, + STANDARD_MESSAGE_LIST_OPTIONS, + STANDARD_MESSAGE_LIST_PERK, + STANDARD_MESSAGE_LIST_PIPBOY, + STANDARD_MESSAGE_LIST_QUESTS, + STANDARD_MESSAGE_LIST_PROTO, + STANDARD_MESSAGE_LIST_SCRIPT, + STANDARD_MESSAGE_LIST_SKILL, + STANDARD_MESSAGE_LIST_SKILLDEX, + STANDARD_MESSAGE_LIST_STAT, + STANDARD_MESSAGE_LIST_TRAIT, + STANDARD_MESSAGE_LIST_WORLDMAP, + STANDARD_MESSAGE_LIST_COUNT, +}; + +enum { + PROTO_MESSAGE_LIST_ITEMS, + PROTO_MESSAGE_LIST_CRITTERS, + PROTO_MESSAGE_LIST_SCENERY, + PROTO_MESSAGE_LIST_TILES, + PROTO_MESSAGE_LIST_WALLS, + PROTO_MESSAGE_LIST_MISC, + PROTO_MESSAGE_LIST_COUNT, +}; + typedef struct MessageListItem { int num; int flags; @@ -31,6 +82,14 @@ bool messageListFilterBadwords(MessageList* messageList); void messageListFilterGenderWords(MessageList* messageList, int gender); +bool messageListRepositoryInit(); +void messageListRepositoryReset(); +void messageListRepositoryExit(); +void messageListRepositorySetStandardMessageList(int messageListId, MessageList* messageList); +void messageListRepositorySetProtoMessageList(int messageListId, MessageList* messageList); +int messageListRepositoryAddExtra(int messageListId, const char* path); +char* messageListRepositoryGetMsg(int messageListId, int messageId); + } // namespace fallout #endif /* MESSAGE_H */ diff --git a/src/perk.cc b/src/perk.cc index 3b9fc54..71cea0e 100644 --- a/src/perk.cc +++ b/src/perk.cc @@ -212,6 +212,8 @@ int perksInit() } } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PERK, &gPerksMessageList); + return 0; } @@ -224,6 +226,7 @@ void perksReset() // 0x4966B8 void perksExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PERK, nullptr); messageListFree(&gPerksMessageList); if (gPartyMemberPerkRanks != NULL) { diff --git a/src/proto.cc b/src/proto.cc index 40fe210..1167dbb 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -1111,6 +1111,10 @@ int protoInit() } } + for (i = 0; i < 6; i++) { + messageListRepositorySetProtoMessageList(i, &(_proto_msg_files[i])); + } + _mp_critter_stats_list = _aDrugStatSpecia; _critter_stats_list = _critter_stats_list_strs; _critter_stats_list_None = _aNone_1; @@ -1181,6 +1185,8 @@ int protoInit() gBodyTypeNames[i] = getmsg(&gProtoMessageList, &messageListItem, 400 + i); } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PROTO, &gProtoMessageList); + return 0; } @@ -1218,9 +1224,11 @@ void protoExit() } for (i = 0; i < 6; i++) { + messageListRepositorySetProtoMessageList(i, nullptr); messageListFree(&(_proto_msg_files[i])); } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_PROTO, nullptr); messageListFree(&gProtoMessageList); } diff --git a/src/scripts.cc b/src/scripts.cc index 882bfa0..b757e19 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -1521,6 +1521,8 @@ int scriptsInit() return -1; } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, &gScrMessageList); + return 0; } @@ -1576,6 +1578,8 @@ int _scr_game_init() // NOTE: Uninline. scriptsClearPendingRequests(); + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, &gScrMessageList); + return 0; } @@ -1595,6 +1599,8 @@ int scriptsExit() { gScriptsEnabled = false; _script_engine_run_critters = 0; + + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, nullptr); if (!messageListFree(&gScrMessageList)) { debugPrint("\nError exiting script message file!"); return -1; @@ -1646,6 +1652,7 @@ int _scr_game_exit() _scr_remove_all(); programListFree(); tickersRemove(_doBkProcesses); + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, nullptr); messageListFree(&gScrMessageList); if (scriptsClearDudeScript() == -1) { return -1; diff --git a/src/sfall_config.cc b/src/sfall_config.cc index 3a6b0af..0470ce9 100644 --- a/src/sfall_config.cc +++ b/src/sfall_config.cc @@ -49,6 +49,7 @@ bool sfallConfigInit(int argc, char** argv) configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_CENTER_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR); + configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, ""); char path[COMPAT_MAX_PATH]; char* executable = argv[0]; diff --git a/src/sfall_config.h b/src/sfall_config.h index b2c8859..7ee9cd7 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -59,6 +59,7 @@ namespace fallout { #define SFALL_CONFIG_TWEAKS_FILE_KEY "TweaksFile" #define SFALL_CONFIG_GAME_DIALOG_GENDER_WORDS_KEY "DialogGenderWords" #define SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX_KEY "TownMapHotkeysFix" +#define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 6972614..de44a7f 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -6,6 +6,7 @@ #include "interface.h" #include "interpreter.h" #include "item.h" +#include "message.h" #include "mouse.h" #include "object.h" #include "sfall_global_vars.h" @@ -223,6 +224,15 @@ static void opGetStringLength(Program* program) programStackPushInteger(program, static_cast(strlen(string))); } +// message_str_game +static void opGetMessage(Program* program) +{ + int messageId = programStackPopInteger(program); + int messageListId = programStackPopInteger(program); + char* text = messageListRepositoryGetMsg(messageListId, messageId); + programStackPushString(program, text); +} + // round static void opRound(Program* program) { @@ -262,6 +272,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x824F, opGetStringLength); + interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x8267, opRound); interpreterRegisterOpcode(0x8274, opArtExists); } diff --git a/src/skill.cc b/src/skill.cc index c62886c..1d55804 100644 --- a/src/skill.cc +++ b/src/skill.cc @@ -158,6 +158,8 @@ int skillsInit() // NOTE: Uninline. skill_use_slot_clear(); + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILL, &gSkillsMessageList); + return 0; } @@ -175,6 +177,7 @@ void skillsReset() // 0x4AA478 void skillsExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SKILL, nullptr); messageListFree(&gSkillsMessageList); } diff --git a/src/stat.cc b/src/stat.cc index 1532548..bc6a5d3 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -131,6 +131,8 @@ int statsInit() gStatValueDescriptions[index] = getmsg(&gStatsMessageList, &messageListItem, 301 + index); } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_STAT, &gStatsMessageList); + return 0; } @@ -146,6 +148,7 @@ int statsReset() // 0x4AEEE4 int statsExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_STAT, nullptr); messageListFree(&gStatsMessageList); return 0; diff --git a/src/trait.cc b/src/trait.cc index 636c58c..16cf73d 100644 --- a/src/trait.cc +++ b/src/trait.cc @@ -85,6 +85,8 @@ int traitsInit() // NOTE: Uninline. traitsReset(); + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_TRAIT, &gTraitsMessageList); + return true; } @@ -99,6 +101,7 @@ void traitsReset() // 0x4B3AF8 void traitsExit() { + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_TRAIT, nullptr); messageListFree(&gTraitsMessageList); } diff --git a/src/worldmap.cc b/src/worldmap.cc index 3d81704..74943ac 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -858,6 +858,8 @@ int wmWorldMap_init() citySizeDescription->fid = buildFid(OBJ_TYPE_INTERFACE, 336 + citySize, 0, 0, 0); } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_WORLDMAP, &wmMsgFile); + return 0; } @@ -1010,6 +1012,7 @@ void wmWorldMap_exit() circleBlendTable = NULL; } + messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_WORLDMAP, nullptr); messageListFree(&wmMsgFile); }