diff --git a/artifacts/scripting/functions.yml b/artifacts/scripting/functions.yml index 8b4a9aad..43c58a52 100644 --- a/artifacts/scripting/functions.yml +++ b/artifacts/scripting/functions.yml @@ -497,6 +497,11 @@ - name: set_terrain_name detail: void sfall_func3("set_terrain_name", int x, int y, string name) doc: Overrides the terrain type name for the sub-tile on the world map by the specified coordinates. + macro: sfall.h + - name: get_terrain_name + detail: string sfall_func2("get_terrain_name", int x, int y) + doc: Returns the terrain type name for the sub-tile on the world map by the specified coordinates, or by the player's current position if called without arguments. + macro: sfall.h - name: set_town_title detail: void sfall_func2("set_town_title", int areaID, string title) doc: | diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 203b5de2..52157a52 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -319,6 +319,7 @@ #define floor2(value) sfall_func1("floor2", value) #define get_can_rest_on_map(map, elev) sfall_func2("get_can_rest_on_map", map, elev) #define get_current_inven_size(obj) sfall_func1("get_current_inven_size", obj) +#define get_current_terrain_name sfall_func0("get_terrain_name") #define get_cursor_mode sfall_func0("get_cursor_mode") #define get_flags(obj) sfall_func1("get_flags", obj) #define get_ini_section(file, sect) sfall_func2("get_ini_section", file, sect) @@ -340,6 +341,7 @@ #define get_npc_stat_min(stat) sfall_func2("get_stat_min", stat, 1) #define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum) #define get_string_pointer(text) sfall_func1("get_string_pointer", text) +#define get_terrain_name(x, y) sfall_func2("get_terrain_name", x, y) #define get_text_width(text) sfall_func1("get_text_width", text) #define has_fake_perk_npc(npc, perk) sfall_func2("has_fake_perk_npc", npc, perk) #define has_fake_trait_npc(npc, trait) sfall_func2("has_fake_trait_npc", npc, trait) diff --git a/artifacts/scripting/sfall function notes.md b/artifacts/scripting/sfall function notes.md index 5d18f26e..1339b18b 100644 --- a/artifacts/scripting/sfall function notes.md +++ b/artifacts/scripting/sfall function notes.md @@ -936,6 +936,12 @@ sfall_funcX metarule functions `void sfall_func3("set_terrain_name", int x, int y, string name)` - Overrides the terrain type name for the sub-tile on the world map by the specified coordinates +---- +#### get_terrain_name +`string sfall_func0("get_terrain_name")`\ +`string sfall_func2("get_terrain_name", int x, int y)` +- Returns the terrain type name for the sub-tile on the world map by the specified coordinates, or by the player's current position if called without arguments + ---- #### get_window_attribute `int sfall_func1("get_window_attribute", int winType)`\ diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index f81d651a..7869db43 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -407,7 +407,19 @@ fo::GameObject* __fastcall MultiHexMoveIsBlocking(fo::GameObject* source, long d return nullptr; } -// Returns the type of the terrain sub tile at the the player's position on the world map +// Returns the terrain type of the sub-tile at the specified coordinates on the world map +long wmGetTerrainType(long xPos, long yPos) { + long* terrainId; + __asm { + lea ebx, terrainId; + mov edx, yPos; + mov eax, xPos; + call fo::funcoffs::wmFindCurSubTileFromPos_; + } + return *terrainId; +} + +// Returns the terrain type of the sub-tile at the the player's position on the world map long wmGetCurrentTerrainType() { long* terrainId = *(long**)FO_VAR_world_subtile; if (terrainId == nullptr) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 72e23fb0..7b502bd9 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -137,6 +137,8 @@ fo::GameObject* CheckAroundBlockingTiles(fo::GameObject* source, long dstTile); fo::GameObject* __fastcall MultiHexMoveIsBlocking(fo::GameObject* source, long dstTile); +long wmGetTerrainType(long xPos, long yPos); + long wmGetCurrentTerrainType(); void SurfaceCopyToMem(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromSurface, BYTE* toMem); diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 700393c7..875e99a7 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1258,22 +1258,22 @@ inCombat: static void __declspec(naked) action_explode_hack() { using namespace fo::Scripts; __asm { - mov edx, destroy_p_proc - mov eax, [esi + scriptId] // pobj.sid - call fo::funcoffs::exec_script_proc_ - xor edx, edx - dec edx - retn + mov edx, destroy_p_proc; + mov eax, [esi + scriptId]; // pobj.sid + call fo::funcoffs::exec_script_proc_; + xor edx, edx; + dec edx; + retn; } } static void __declspec(naked) action_explode_hack1() { __asm { - push esi - mov esi, [esi+0x40] // ctd.target# - call action_explode_hack - pop esi - retn + push esi; + mov esi, [esi + 0x40]; // ctd.target# + call action_explode_hack; + pop esi; + retn; } } @@ -1342,140 +1342,140 @@ end: static void __declspec(naked) db_get_file_list_hack() { __asm { - push edi - push edx - xchg edi, eax // edi = *filename - mov eax, [eax+4] // file_lists.filenames - lea esi, [eax+edx] - cld - push es - push ds - pop es - xor ecx, ecx - dec ecx - mov edx, ecx - mov ebx, ecx - xor eax, eax // searching for end of line - repne scasb - not ecx - dec ecx - xchg ebx, ecx // ebx = filename length - lea edi, [esp+0x200+4*6] - repne scasb - not ecx - xchg edx, ecx // edx = extension length +1 for "end of line" - mov edi, [esi] - repne scasb - not ecx // ecx = buffer line length +1 for "end of line" - pop es - lea eax, [ebx+edx] // eax = new line length - cmp eax, ecx // new line length <= buffer line length? - jbe end // Yes - mov edx, [esi] - xchg edx, eax - call fo::funcoffs::nrealloc_ // eax = mem, edx = size - test eax, eax - jnz skip - push 0x50B2F0 // "Error: Ran out of memory!" - call fo::funcoffs::debug_printf_ - add esp, 4 - jmp end + push edi; + push edx; + xchg edi, eax; // edi = *filename + mov eax, [eax + 4]; // file_lists.filenames + lea esi, [eax + edx]; + cld; + push es; + push ds; + pop es; + xor ecx, ecx; + dec ecx; + mov edx, ecx; + mov ebx, ecx; + xor eax, eax; // searching for end of line + repne scasb; + not ecx; + dec ecx; + xchg ebx, ecx; // ebx = filename length + lea edi, [esp + 0x200 + 6*4]; + repne scasb; + not ecx; + xchg edx, ecx; // edx = extension length +1 for "end of line" + mov edi, [esi]; + repne scasb; + not ecx; // ecx = buffer line length +1 for "end of line" + pop es; + lea eax, [ebx + edx]; // eax = new line length + cmp eax, ecx; // new line length <= buffer line length? + jbe end; // Yes + mov edx, [esi]; + xchg edx, eax; + call fo::funcoffs::nrealloc_; // eax = mem, edx = size + test eax, eax; + jnz skip; + push 0x50B2F0; // "Error: Ran out of memory!" + call fo::funcoffs::debug_printf_; + add esp, 4; + jmp end; skip: - mov [esi], eax + mov [esi], eax; end: - xchg esi, eax - pop edx - pop edi - retn + xchg esi, eax; + pop edx; + pop edi; + retn; } } static void __declspec(naked) gdActivateBarter_hook() { __asm { - call fo::funcoffs::gdialog_barter_pressed_ - cmp ds:[FO_VAR_dialogue_state], ecx - jne skip - cmp ds:[FO_VAR_dialogue_switch_mode], esi - je end + call fo::funcoffs::gdialog_barter_pressed_; + cmp ds:[FO_VAR_dialogue_state], ecx; + jne skip; + cmp ds:[FO_VAR_dialogue_switch_mode], esi; + je end; skip: - push ecx - push esi - push edi - push ebp - sub esp, 0x18 - push 0x44A5CC + push ecx; + push esi; + push edi; + push ebp; + sub esp, 0x18; + push 0x44A5CC; end: - retn + retn; } } static void __declspec(naked) switch_hand_hack() { __asm { - mov eax, ds:[FO_VAR_inven_dude] - push eax - mov [edi], ebp - inc ecx // if ecx == -1 - jz skip - xor ebx, ebx - inc ebx - mov edx, ebp - call fo::funcoffs::item_remove_mult_ + mov eax, ds:[FO_VAR_inven_dude]; + push eax; + mov [edi], ebp; + inc ecx; // if ecx == -1 + jz skip; + xor ebx, ebx; + inc ebx; + mov edx, ebp; + call fo::funcoffs::item_remove_mult_; skip: - pop edx // _inven_dude - mov eax, ebp - call fo::funcoffs::item_get_type_ - cmp eax, item_type_container - jne end - mov [ebp + owner], edx // iobj.owner = _inven_dude + pop edx; // _inven_dude + mov eax, ebp; + call fo::funcoffs::item_get_type_; + cmp eax, item_type_container; + jne end; + mov [ebp + owner], edx; // iobj.owner = _inven_dude end: - pop ebp - pop edi - pop esi - retn + pop ebp; + pop edi; + pop esi; + retn; } } static void __declspec(naked) inven_item_wearing() { __asm { - mov esi, ds:[FO_VAR_inven_dude] - xchg ebx, eax // ebx = source - mov eax, [esi + artFid] - and eax, 0xF000000 - sar eax, 0x18 - test eax, eax // check if object FID type flag is set to item - jnz skip // No - mov eax, esi - call fo::funcoffs::item_get_type_ - cmp eax, item_type_container // Bag/Backpack? - jne skip // No - mov eax, esi - call fo::funcoffs::obj_top_environment_ - test eax, eax // has an owner? - jz skip // No - mov ecx, [eax + artFid] - and ecx, 0xF000000 - sar ecx, 0x18 - cmp ecx, OBJ_TYPE_CRITTER // check if object FID type flag is set to critter - jne skip // No - cmp eax, ebx // the owner of the bag == source? - je end // Yes + mov esi, ds:[FO_VAR_inven_dude]; + xchg ebx, eax; // ebx = source + mov eax, [esi + artFid]; + and eax, 0xF000000; + sar eax, 0x18; + test eax, eax; // check if object FID type flag is set to item + jnz skip; // No + mov eax, esi; + call fo::funcoffs::item_get_type_; + cmp eax, item_type_container; // Bag/Backpack? + jne skip; // No + mov eax, esi; + call fo::funcoffs::obj_top_environment_; + test eax, eax; // has an owner? + jz skip; // No + mov ecx, [eax + artFid]; + and ecx, 0xF000000; + sar ecx, 0x18; + cmp ecx, OBJ_TYPE_CRITTER; // check if object FID type flag is set to critter + jne skip; // No + cmp eax, ebx; // the owner of the bag == source? + je end; // Yes skip: - xchg ebx, eax - cmp eax, esi + xchg ebx, eax; + cmp eax, esi; end: - retn + retn; } } static void __declspec(naked) inven_action_cursor_hack() { __asm { - cmp dword ptr [esp+0x44+0x4], item_type_container - jne end - cmp eax, ds:[FO_VAR_stack] - je end - cmp eax, ds:[FO_VAR_target_stack] + cmp dword ptr [esp + 0x44 + 4], item_type_container; + jne end; + cmp eax, ds:[FO_VAR_stack]; + je end; + cmp eax, ds:[FO_VAR_target_stack]; end: - retn + retn; } } @@ -1574,7 +1574,7 @@ end: static void __declspec(naked) apply_damage_hack() { __asm { xchg edx, eax; - test [esi+0x15], dl; // ctd.flags2Source & DAM_HIT_? + test [esi + 0x15], dl; // ctd.flags2Source & DAM_HIT_? jz end; // No inc ebx; end: @@ -1585,9 +1585,9 @@ end: static void __declspec(naked) compute_attack_hook() { __asm { call fo::funcoffs::attack_crit_success_; - test [esi+0x15], 2; // ctd.flags2Source & DAM_CRITICAL_? + test [esi + 0x15], 2; // ctd.flags2Source & DAM_CRITICAL_? jz end; // No - cmp dword ptr [esp+0x4+0x20], 4; // Has Silent Death perk? + cmp dword ptr [esp + 0x20 + 4], 4; // Has Silent Death perk? jne end; // No shl eax, 1; // Multiply by 2 for the perk effect end: @@ -1949,8 +1949,8 @@ fix: } } -static DWORD op_start_gdialog_ret = 0x456F4B; static void __declspec(naked) op_start_gdialog_hack() { + static const DWORD op_start_gdialog_ret = 0x456F4B; __asm { cmp eax, -1; // check mood arg jnz useMood; @@ -3179,8 +3179,7 @@ static void __declspec(naked) action_can_be_pushed_hook() { } } -void BugFixes::init() -{ +void BugFixes::init() { #ifndef NDEBUG LoadGameHook::OnBeforeGameClose() += PrintAddrList; if (IniReader::GetIntDefaultConfig("Debugging", "BugFixes", 1) == 0) return; diff --git a/sfall/Modules/Scripting/Handlers/Inventory.cpp b/sfall/Modules/Scripting/Handlers/Inventory.cpp index e432d457..a21c0054 100644 --- a/sfall/Modules/Scripting/Handlers/Inventory.cpp +++ b/sfall/Modules/Scripting/Handlers/Inventory.cpp @@ -170,6 +170,5 @@ void mf_unwield_slot(OpcodeContext& ctx) { if (update) fo::func::intface_update_items(0, -1, -1); } - } } diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 83a98327..7aed46a3 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -98,6 +98,7 @@ static const SfallMetarule metarules[] = { {"get_stat_max", mf_get_stat_max, 1, 2, 0, {ARG_INT, ARG_INT}}, {"get_stat_min", mf_get_stat_min, 1, 2, 0, {ARG_INT, ARG_INT}}, {"get_string_pointer", mf_get_string_pointer, 1, 1, 0, {ARG_STRING}}, + {"get_terrain_name", mf_get_terrain_name, 0, 2, -1, {ARG_INT, ARG_INT}}, {"get_text_width", mf_get_text_width, 1, 1, 0, {ARG_STRING}}, {"get_window_attribute", mf_get_window_attribute, 1, 2, -1, {ARG_INT, ARG_INT}}, {"has_fake_perk_npc", mf_has_fake_perk_npc, 2, 2, 0, {ARG_OBJECT, ARG_STRING}}, diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 13ec9aed..f13ab82b 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -42,7 +42,7 @@ namespace sfall namespace script { -const char* stringTooLong = "%s() - the string exceeds maximum length of 64 characters."; +static const char* stringTooLong = "%s() - the string exceeds maximum length of 64 characters."; void __declspec(naked) op_stop_game() { __asm { diff --git a/sfall/Modules/Scripting/Handlers/Perks.cpp b/sfall/Modules/Scripting/Handlers/Perks.cpp index 19801607..c2c7c7cc 100644 --- a/sfall/Modules/Scripting/Handlers/Perks.cpp +++ b/sfall/Modules/Scripting/Handlers/Perks.cpp @@ -112,7 +112,7 @@ void op_set_fake_trait(OpcodeContext& ctx) { Perks::SetFakeTrait(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), ctx.arg(2).rawValue(), ctx.arg(3).strValue()); } -const char* notPartyMemberErr = "%s() - the object is not a party member."; +static const char* notPartyMemberErr = "%s() - the object is not a party member."; void mf_set_selectable_perk_npc(OpcodeContext& ctx) { auto obj = ctx.arg(0).object(); diff --git a/sfall/Modules/Scripting/Handlers/Stats.cpp b/sfall/Modules/Scripting/Handlers/Stats.cpp index 2898a416..f7ae0d57 100644 --- a/sfall/Modules/Scripting/Handlers/Stats.cpp +++ b/sfall/Modules/Scripting/Handlers/Stats.cpp @@ -34,8 +34,8 @@ namespace sfall namespace script { -const char* invalidStat = "%s() - stat number out of range."; -const char* objNotCritter = "%s() - the object is not a critter."; +static const char* invalidStat = "%s() - stat number out of range."; +static const char* objNotCritter = "%s() - the object is not a critter."; void __declspec(naked) op_set_hp_per_level_mod() { __asm { diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 1a3c3461..4dd2bab6 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -237,8 +237,37 @@ void mf_tile_by_position(OpcodeContext& ctx) { ctx.setReturn(fo::func::tile_num(ctx.arg(0).rawValue(), ctx.arg(1).rawValue())); } +static const char* invalidSubTilePos = "%s() - invalid x/y coordinates for the sub-tile."; + void mf_set_terrain_name(OpcodeContext& ctx) { - Worldmap::SetTerrainTypeName(ctx.arg(0).rawValue(), ctx.arg(1).rawValue(), ctx.arg(2).strValue()); + long x = ctx.arg(0).rawValue(); + long y = ctx.arg(1).rawValue(); + + if (x < 0 || x >= (long)(7 * fo::var::wmNumHorizontalTiles) || + y < 0 || y >= (long)(6 * (fo::var::wmMaxTileNum / fo::var::wmNumHorizontalTiles))) + { + ctx.printOpcodeError(invalidSubTilePos, ctx.getMetaruleName()); + } else { + Worldmap::SetTerrainTypeName(x, y, ctx.arg(2).strValue()); + } +} + +void mf_get_terrain_name(OpcodeContext& ctx) { + if (ctx.numArgs() < 2) { + ctx.setReturn(Worldmap::GetCurrentTerrainName()); + } else { + long x = ctx.arg(0).rawValue(); + long y = ctx.arg(1).rawValue(); + + if (x < 0 || x >= (long)(7 * fo::var::wmNumHorizontalTiles) || + y < 0 || y >= (long)(6 * (fo::var::wmMaxTileNum / fo::var::wmNumHorizontalTiles))) + { + ctx.printOpcodeError(invalidSubTilePos, ctx.getMetaruleName()); + ctx.setReturn("Error"); + } else { + ctx.setReturn(Worldmap::GetTerrainTypeName(x, y)); + } + } } void mf_set_town_title(OpcodeContext& ctx) { diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index 362e6e52..1ab5115d 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -60,6 +60,8 @@ void mf_tile_by_position(OpcodeContext&); void mf_set_terrain_name(OpcodeContext&); +void mf_get_terrain_name(OpcodeContext&); + void mf_set_town_title(OpcodeContext&); } diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index 35eba9b4..bd0f269d 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -672,13 +672,13 @@ void Worldmap::SetTerrainTypeName(long x, long y, const char* name) { wmTerrainTypeNames.push_back(std::make_pair(subTileID, name)); } -// TODO: someone might need to know the name of a terrain type? -/*const char* Worldmap::GetTerrainTypeName(long x, long y) { +// Returns the name of the terrain type at the specified coordinates on the world map +const char* Worldmap::GetTerrainTypeName(long x, long y) { const char* name = GetOverrideTerrainName(x, y); - return (name) ? name : fo::util::GetMessageStr(&fo::var::wmMsgFile, 1000 + fo::wmGetTerrainType(x, y)); -}*/ + return (name) ? name : fo::util::GetMessageStr(&fo::var::wmMsgFile, 1000 + fo::util::wmGetTerrainType(x * 50, y * 50)); +} -// Returns the name of the terrain type in the position of the player's marker on the world map +// Returns the name of the terrain type at the position of the player's marker on the world map const char* Worldmap::GetCurrentTerrainName() { const char* name = GetOverrideTerrainName(fo::var::world_xpos / 50, fo::var::world_ypos / 50); return (name) ? name : fo::util::GetMessageStr(&fo::var::wmMsgFile, 1000 + fo::util::wmGetCurrentTerrainType()); diff --git a/sfall/Modules/Worldmap.h b/sfall/Modules/Worldmap.h index 51b7f91f..cd6f4a16 100644 --- a/sfall/Modules/Worldmap.h +++ b/sfall/Modules/Worldmap.h @@ -44,7 +44,7 @@ public: static void SetAddedYears(DWORD years); static void SetTerrainTypeName(long x, long y, const char* name); - //static const char* GetTerrainTypeName(long x, long y); + static const char* GetTerrainTypeName(long x, long y); static const char* GetCurrentTerrainName(); static bool AreaTitlesIsEmpty();