diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index f5c86376..eabd5115 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -290,6 +290,7 @@ #define exec_map_update_scripts sfall_func0("exec_map_update_scripts") #define floor2(value) sfall_func1("floor2", value) #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) @@ -309,6 +310,7 @@ #define get_npc_stat_max(stat) sfall_func2("get_stat_max", stat, 1) #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_terrain_name(x, y) sfall_func2("get_terrain_name", x, y) #define get_text_width(text) sfall_func1("get_text_width", text) #define hide_win sfall_func0("hide_window") #define hide_window(winName) sfall_func1("hide_window", winName) diff --git a/artifacts/scripting/sfall function notes.md b/artifacts/scripting/sfall function notes.md index 1ee7fbd9..7b54944f 100644 --- a/artifacts/scripting/sfall function notes.md +++ b/artifacts/scripting/sfall function notes.md @@ -859,6 +859,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 895087b6..c32c6589 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -374,7 +374,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 603a7680..7611d1ab 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -127,6 +127,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/FalloutEngine/VarPointers_def.h b/sfall/FalloutEngine/VarPointers_def.h index 91c86709..ad1ef636 100644 --- a/sfall/FalloutEngine/VarPointers_def.h +++ b/sfall/FalloutEngine/VarPointers_def.h @@ -237,6 +237,7 @@ PTR_(wmAreaInfoList, DWORD) PTRC(wmBkWin, DWORD) PTR_(wmBkWinBuf, BYTE*) PTR_(wmLastRndTime, DWORD) +PTR_(wmMaxTileNum, DWORD) PTR_(wmMsgFile, fo::MessageList) PTR_(wmNumHorizontalTiles, DWORD) PTR_(wmWorldOffsetX, long) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 397f4c59..0b358721 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -310,6 +310,7 @@ #define FO_VAR_wmLastRndTime 0x51DEA0 #define FO_VAR_wmMaxAreaNum 0x51DDFC #define FO_VAR_wmMaxMapNum 0x51DE10 +#define FO_VAR_wmMaxTileNum 0x51DDF0 #define FO_VAR_wmMsgFile 0x672FB0 #define FO_VAR_wmNumHorizontalTiles 0x51DDF4 #define FO_VAR_wmRndCursorFid 0x672E58 diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 58e5fee1..fe275c35 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1242,22 +1242,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; } } @@ -1326,140 +1326,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; } } @@ -1558,7 +1558,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: @@ -1569,9 +1569,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: @@ -1933,8 +1933,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; @@ -3177,8 +3177,7 @@ void BugFixes::OnGameLoad() { combat_ai_reset(); } -void BugFixes::init() -{ +void BugFixes::init() { #ifndef NDEBUG if (IniReader::GetIntDefaultConfig("Debugging", "BugFixes", 1) == 0) return; #endif diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index e9c881a8..dbd1592e 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -94,6 +94,7 @@ static const SfallMetarule metarules[] = { {"get_sfall_arg_at", mf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}}, {"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_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}}, {"hide_window", mf_hide_window, 0, 1, -1, {ARG_STRING}}, diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index 62df0da8..ff8d5b43 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -40,7 +40,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/Stats.cpp b/sfall/Modules/Scripting/Handlers/Stats.cpp index 5e2f8045..ad204091 100644 --- a/sfall/Modules/Scripting/Handlers/Stats.cpp +++ b/sfall/Modules/Scripting/Handlers/Stats.cpp @@ -33,8 +33,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 97d7482f..22e25152 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -202,8 +202,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::ptr::wmNumHorizontalTiles) || + y < 0 || y >= (long)(6 * (*fo::ptr::wmMaxTileNum / *fo::ptr::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::ptr::wmNumHorizontalTiles) || + y < 0 || y >= (long)(6 * (*fo::ptr::wmMaxTileNum / *fo::ptr::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 20797b7e..432044d4 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -52,6 +52,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 86bdf787..9789a281 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -528,13 +528,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::ptr::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::ptr::world_xpos / 50, *fo::ptr::world_ypos / 50); return (name) ? name : fo::util::GetMessageStr(fo::ptr::wmMsgFile, 1000 + fo::util::wmGetCurrentTerrainType()); diff --git a/sfall/Modules/Worldmap.h b/sfall/Modules/Worldmap.h index b1953635..ce55e75f 100644 --- a/sfall/Modules/Worldmap.h +++ b/sfall/Modules/Worldmap.h @@ -34,7 +34,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();