diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 65c085e8..82e07699 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -244,6 +244,7 @@ #define ADD_PERK_MODE_REMOVE (4) // remove from the list of selectable perks // sfall_funcX macros +#define add_extra_msg_file(name, number) sfall_func2("add_extra_msg_file", name, number) #define add_iface_tag sfall_func0("add_iface_tag") #define art_cache_clear sfall_func0("art_cache_clear") #define attack_is_aimed sfall_func0("attack_is_aimed") @@ -280,6 +281,7 @@ #define item_weight(obj) sfall_func1("item_weight", obj) #define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj) #define loot_obj sfall_func0("loot_obj") +#define metarule_exist(metarule) sfall_func1("metarule_exist", metarule) #define npc_engine_level_up(toggle) sfall_func1("npc_engine_level_up", toggle) #define obj_under_cursor(crSwitch, inclDude) sfall_func2("obj_under_cursor", crSwitch, inclDude) #define outlined_object sfall_func0("outlined_object") diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 52f3eb35..fd6deaf7 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -319,7 +319,7 @@ Some utility/math functions are available: > string message_str_game(int fileId, int messageId) - works exactly the same as message_str, except you get messages from files in "text\\game\" directory - use GAME_MSG_* defines or mstr_* macros from sfall.h to use specific msg file -- Additional game msg files added by ExtraGameMsgFileList setting will have consecutive fileIds assigned beginning from 0x2000. (e.g. if you set ExtraGameMsgFileList=foo,bar in ddraw.ini, foo.msg will be associated with 0x2000 and bar.msg with 0x2001.) +- Additional game msg files added by ExtraGameMsgFileList setting will have consecutive fileIds assigned beginning from 0x2000 to 0x2FFF. (e.g. if you set ExtraGameMsgFileList=foo,bar in ddraw.ini, foo.msg will be associated with 0x2000 and bar.msg with 0x2001.) - if a file has a specific number assigned in ExtraGameMsgFileList, its fileId will be (0x2000 + assigned number). (e.g. with ExtraGameMsgFileList=foo,bar:2,foobar in ddraw.ini, bar.msg will be associated with 0x2002 and foobar.msg with 0x2003.) > int sneak_success @@ -374,8 +374,11 @@ Some utility/math functions are available: > array sfall_func0("get_metarule_table") - returns names of all currently available scripting functions (via sfall_funcX) +> bool sfall_func1("metarule_exist", string metaruleName) +- returns True if the specified name of metarule (sfall_funcX) function exists in the current version of sfall + > int sfall_func1("spatial_radius", object object) -- return radius of spatial script, associated with given dummy-object (returned by create_spatial) +- returns radius of spatial script, associated with given dummy-object (returned by create_spatial) > object sfall_func2("critter_inven_obj2", object invenObj, int type) - works just like vanilla critter_inven_obj, but correctly reports item in player's inactive hand slot @@ -606,6 +609,11 @@ optional arguments: > int sfall_func2("has_fake_trait_npc", object npc, string nameTrait) - these functions are similar to has_fake_*/set_fake_*/set_selectable_perk functions, but apply to the specified party member NPC (including dude_obj) +> void sfall_func2("add_extra_msg_file", string fileName, int fileNumber) +- loads the custom message file and assigns it with the specified ID number +- fileName: the name of the custom message file (including the .msg extension) in "text\\game\" directory +- fileNumber: the file ID number for the message_str_game function to get messages from the file. The available range is from 0x3000 to 0x3FFF + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/Modules/Message.cpp b/sfall/Modules/Message.cpp index e2d39b51..7887c106 100644 --- a/sfall/Modules/Message.cpp +++ b/sfall/Modules/Message.cpp @@ -54,7 +54,7 @@ const fo::MessageList* gameMsgFiles[] = { #undef CASTMSG ExtraGameMessageListsMap gExtraGameMsgLists; -std::vector msgFileList; +static std::vector msgFileList; fo::MessageNode *GetMsgNode(fo::MessageList *msgList, int msgRef) { if (msgList != nullptr && msgList->numMsgs > 0) { @@ -89,11 +89,11 @@ char* GetMsg(fo::MessageList *msgList, int msgRef, int msgNum) { return nullptr; } -void ReadExtraGameMsgFiles() { - if (msgFileList.size() > 0) { +static void ReadExtraGameMsgFiles() { + if (!msgFileList.empty()) { int number = 0; for (auto& msgName : msgFileList) { - std::string path = "game\\"; + std::string path("game\\"); auto n = msgName.find(':'); if (n == std::string::npos) { path += msgName; @@ -108,25 +108,47 @@ void ReadExtraGameMsgFiles() { } else { delete list; } - number++; + if (++number == 4096) break; + } + msgFileList.clear(); + } +} + +long Message::AddExtraMsgFile(const char* msgName, long msgNumber) { + if (msgNumber < 0x3000 || msgNumber > 0x3FFF || gExtraGameMsgLists.count(msgNumber)) return -1; + std::string path("game\\"); + path += msgName; + fo::MessageList* list = new fo::MessageList(); + if (fo::func::message_load(list, path.c_str())) { + gExtraGameMsgLists.emplace(msgNumber, list); + } else { + delete list; + } + return 0; +} + +static void ClearScriptAddedExtraGameMsg() { // C++11 + for (auto it = gExtraGameMsgLists.begin(); it != gExtraGameMsgLists.end();) { + if (it->first >= 0x3000 && it->first <= 0x3FFF) { + fo::func::message_exit(it->second.get()); + it = gExtraGameMsgLists.erase(it); + } else { + ++it; } } } -void ClearReadExtraGameMsgFiles() { - ExtraGameMessageListsMap::iterator it; - - for (it = gExtraGameMsgLists.begin(); it != gExtraGameMsgLists.end(); ++it) { +static void ClearReadExtraGameMsgFiles() { + for (auto it = gExtraGameMsgLists.begin(); it != gExtraGameMsgLists.end(); ++it) { fo::func::message_exit(it->second.get()); } - - gExtraGameMsgLists.clear(); - msgFileList.clear(); + //gExtraGameMsgLists.clear(); } void Message::init() { msgFileList = GetConfigList("Misc", "ExtraGameMsgFileList", "", 512); LoadGameHook::OnBeforeGameStart() += ReadExtraGameMsgFiles; + LoadGameHook::OnGameReset() += ClearScriptAddedExtraGameMsg; } void Message::exit() { diff --git a/sfall/Modules/Message.h b/sfall/Modules/Message.h index f84cacca..0b9f9898 100644 --- a/sfall/Modules/Message.h +++ b/sfall/Modules/Message.h @@ -37,6 +37,8 @@ public: const char* name() { return "Message"; } void init(); void exit() override; + + static long AddExtraMsgFile(const char* msgName, long msgNumber); }; fo::MessageNode *GetMsgNode(fo::MessageList *msgList, int msgRef); diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 7ee89042..aac514f6 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -59,6 +59,7 @@ static MetaruleTableType metaruleTable; - arg1, arg2, ... - argument types for automatic validation */ static const SfallMetarule metarules[] = { + {"add_extra_msg_file", sf_add_extra_msg_file, 2, 2, {ARG_STRING, ARG_INT}}, {"add_iface_tag", sf_add_iface_tag, 0, 0}, {"art_cache_clear", sf_art_cache_flush, 0, 0}, {"attack_is_aimed", sf_attack_is_aimed, 0, 0}, @@ -95,6 +96,7 @@ static const SfallMetarule metarules[] = { {"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}}, {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, {"loot_obj", sf_get_loot_object, 0, 0}, + {"metarule_exist", sf_metarule_exist, 1, 1}, {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1, {ARG_ANY}}, {"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}}, {"outlined_object", sf_outlined_object, 0, 0}, @@ -136,6 +138,20 @@ static void sf_get_metarule_table(OpcodeContext& ctx) { ctx.setReturn(arrId, DataType::INT); } +static void sf_metarule_exist(OpcodeContext& ctx) { + bool result = false; + auto funcXName = ctx.arg(0).asString(); + if (funcXName[0] != '\0') { + for (auto it = metaruleTable.begin(); it != metaruleTable.end(); it++) { + if (it->first == funcXName) { + result = true; + break; + } + } + } + ctx.setReturn(result); +} + void InitMetaruleTable() { for (auto metarule = std::begin(metarules); metarule != std::end(metarules); ++metarule) { metaruleTable[metarule->name] = metarule; diff --git a/sfall/Modules/Scripting/Handlers/Metarule.h b/sfall/Modules/Scripting/Handlers/Metarule.h index d864844e..de7c86ee 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.h +++ b/sfall/Modules/Scripting/Handlers/Metarule.h @@ -38,6 +38,8 @@ void sf_test(OpcodeContext&); // returns current contents of metarule table void sf_get_metarule_table(OpcodeContext&); +void sf_metarule_exist(OpcodeContext&); + void InitMetaruleTable(); void HandleMetarule(OpcodeContext& ctx); diff --git a/sfall/Modules/Scripting/Handlers/Utils.cpp b/sfall/Modules/Scripting/Handlers/Utils.cpp index 7dd9c063..ddef6a85 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.cpp +++ b/sfall/Modules/Scripting/Handlers/Utils.cpp @@ -287,18 +287,22 @@ void sf_message_str_game(OpcodeContext& ctx) { msg = fo::GetMessageStr(&fo::var::proto_msg_files[fileId - 0x1000], msgId); } else if (fileId >= 0x2000) { // Extra game message files. ExtraGameMessageListsMap::iterator it = gExtraGameMsgLists.find(fileId); - if (it != gExtraGameMsgLists.end()) { msg = GetMsg(it->second.get(), msgId, 2); } } - if (msg == nullptr) { msg = "Error"; } ctx.setReturn(msg); } +void sf_add_extra_msg_file(OpcodeContext& ctx) { + long result = Message::AddExtraMsgFile(ctx.arg(0).strValue(), ctx.arg(1).rawValue()); + if (result == -1) ctx.printOpcodeError("%s() - cannot add message file with the specified number.", ctx.getMetaruleName()); + ctx.setReturn(result); +} + void sf_floor2(OpcodeContext& ctx) { ctx.setReturn(static_cast(floor(ctx.arg(0).asFloat()))); } diff --git a/sfall/Modules/Scripting/Handlers/Utils.h b/sfall/Modules/Scripting/Handlers/Utils.h index dd9aa14c..a2b8bbd6 100644 --- a/sfall/Modules/Scripting/Handlers/Utils.h +++ b/sfall/Modules/Scripting/Handlers/Utils.h @@ -65,6 +65,8 @@ void sf_round(OpcodeContext&); void sf_message_str_game(OpcodeContext&); +void sf_add_extra_msg_file(OpcodeContext&); + void sf_floor2(OpcodeContext&); void sf_get_string_pointer(OpcodeContext&); diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index 90ee1a88..bf70b633 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -555,7 +555,7 @@ void WorldMapInterfacePatch() { SafeWrite32(0x4C2C92, 181); // index of DNARWOFF.FRM SafeWrite8(0x4C2D04, 0x46); // dec esi > inc esi - // Fix the position of the target marker for location circles + // Fix the position of the target marker for small/medium location circles MakeCall(0x4C03AA, wmWorldMap_hack, 2); }