From 671065baed427ec8f200aa18ee720fb9ba2e23d1 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 22 Jan 2021 14:11:43 +0800 Subject: [PATCH] Updated the code for party order attack --- artifacts/config_files/Translations.ini | 4 +++ artifacts/config_files/sfall-mods.ini | 2 +- artifacts/sfall.dat | Bin 158410 -> 158367 bytes sfall/FalloutEngine/EngineUtils.cpp | 12 +++++++ sfall/FalloutEngine/EngineUtils.h | 2 ++ sfall/FalloutEngine/Functions_def.h | 2 ++ sfall/FalloutEngine/VariableOffsets.h | 4 +-- sfall/FalloutEngine/Variables_def.h | 2 ++ sfall/Modules/MetaruleExtender.cpp | 2 +- sfall/Modules/PartyControl.cpp | 42 +++++++++++++++++------- sfall/Modules/PartyControl.h | 2 +- sfall/Utils.cpp | 5 +++ sfall/Utils.h | 2 ++ sfall/main.cpp | 5 +++ sfall/main.h | 2 ++ 15 files changed, 71 insertions(+), 17 deletions(-) diff --git a/artifacts/config_files/Translations.ini b/artifacts/config_files/Translations.ini index 6471fc09..bae1a534 100644 --- a/artifacts/config_files/Translations.ini +++ b/artifacts/config_files/Translations.ini @@ -12,6 +12,10 @@ PartyACMsg=AC: PartyAddictMsg=Addict NPCPickupFail=%s cannot pick up the item. +PartyOrderAttackHuman=I'll take care of it.|Okay, I got it.|Sounds like a plan. +PartyOrderAttackCreature=::Growl:: +PartyOrderAttackRobot=::Beep:: + [AppearanceMod] RaceText=Race StyleText=Style diff --git a/artifacts/config_files/sfall-mods.ini b/artifacts/config_files/sfall-mods.ini index 93d8ec8b..f29e6617 100644 --- a/artifacts/config_files/sfall-mods.ini +++ b/artifacts/config_files/sfall-mods.ini @@ -35,7 +35,7 @@ MotionScanner=0 ;Set to 0 to disable ;Set to 1 to control all critters in combat ;Set to 2 to control all party members -;Set to 3 to order party members to attack a specified target instead of controlling them by yourself +;Set to 3 to let you order party members to attack a specified target instead of controlling them directly after your first attack Mode=0 ;If you want to control only specific critters, uncomment the PIDList line and set a comma delimited list of PIDs ;PIDList=62,89,97,107,160,161 diff --git a/artifacts/sfall.dat b/artifacts/sfall.dat index 8a0cd91260d071f5be2a6b38dd2cc7809e13df05..fa5c1281ef7eb90fd884cb19141aa06f12593ddd 100644 GIT binary patch delta 1909 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 Delevation); + } +} + // print text to surface void PrintText(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface) { DWORD posOffset = yPos * toWidth + xPos; diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 263b88b1..437440a6 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -121,6 +121,8 @@ void WinFillRect(long winID, long x, long y, long width, long height, BYTE index // Fills the specified interface window with index color 0 (black color) void ClearWindow(long winID, bool refresh = true); +void PrintFloatText(fo::GameObject* object, const char* text, long colorText, long colorOutline = 207, long font = 101); + // Print text to surface void PrintText(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface); void PrintTextFM(const char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface); diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 5292c1d9..381760a9 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -49,6 +49,7 @@ WRAP_WATCOM_FFUNC4(long, register_object_move_to_object, GameObject*, source, Ga WRAP_WATCOM_FFUNC4(long, register_object_run_to_object, GameObject*, source, GameObject*, target, long, distance, long, delay) WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value) WRAP_WATCOM_FFUNC3(long, scr_set_local_var, long, sid, long, varId, long, value) +WRAP_WATCOM_FFUNC6(long, text_object_create, GameObject*, object, const char*, text, long, font, long, colorText, long, colorOutline, BoundRect*, rect) WRAP_WATCOM_FFUNC3(long, tile_num_in_direction, long, tile, long, rotation,long, distance) WRAP_WATCOM_FFUNC8(void, trans_cscale, void*, fromBuff, long, width, long, height, long, fromPitch, void*, toBuff, long, toWidth, long, toHeight, long, toPitch) WRAP_WATCOM_FFUNC3(void, win_clip, Window*, window, RectList**, rects, void*, buffer) @@ -105,6 +106,7 @@ WRAP_WATCOM_FUNC1(void, dbase_close, void*, dbPtr) WRAP_WATCOM_FUNC1(void, display_print, const char*, msg) // Displays message in main UI console window WRAP_WATCOM_FUNC0(void, display_stats) WRAP_WATCOM_FUNC2(long, combat_turn, GameObject*, critter, long, isDudeTurn) // Perform combat turn for a given critter +WRAP_WATCOM_FUNC1(long, critter_body_type, GameObject*, critter) WRAP_WATCOM_FUNC1(long, critter_is_dead, GameObject*, critter) WRAP_WATCOM_FUNC1(void, EndLoad, DbFile*, file) // Execute script proc by internal proc number (from script's proc table, basically a sequential number of a procedure as defined in code, starting from 1) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 70870f5b..dec75c6b 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -28,6 +28,7 @@ #define FO_VAR_btncnt 0x43EA1C #define FO_VAR_carCurrentArea 0x672E68 #define FO_VAR_carGasAmount 0x672E6C +#define FO_VAR_card_old_fid1 0x5709EC #define FO_VAR_character_points 0x518538 #define FO_VAR_cmap 0x51DF34 #define FO_VAR_colorTable 0x6A38D0 @@ -178,7 +179,6 @@ #define FO_VAR_num_game_global_vars 0x5186C4 #define FO_VAR_num_map_global_vars 0x519574 #define FO_VAR_num_windows 0x6ADF24 -#define FO_VAR_card_old_fid1 0x5709EC #define FO_VAR_obj_dude 0x6610B8 #define FO_VAR_obj_seen 0x662445 #define FO_VAR_obj_seen_check 0x6610BC @@ -217,8 +217,8 @@ #define FO_VAR_pipmesg 0x664338 #define FO_VAR_preload_list_index 0x519640 #define FO_VAR_procTableStrs 0x51C758 // table of procId (from define.h) => procName map -#define FO_VAR_proto_msg_files 0x6647AC #define FO_VAR_proto_main_msg_file 0x6647FC +#define FO_VAR_proto_msg_files 0x6647AC #define FO_VAR_ptable 0x59E934 #define FO_VAR_pud 0x59E960 #define FO_VAR_quest_count 0x51C12C diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index ab1d864c..0a219115 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -120,6 +120,7 @@ VAR_(master_volume, DWORD) VAR_(max, DWORD) VAR_(maxScriptNum, long) VAR_(Meet_Frank_Horrigan, bool) +VAR_(mouse_buttons, DWORD) VAR_(mouse_hotx, DWORD) VAR_(mouse_hoty, DWORD) VAR_(mouse_is_hidden, DWORD) @@ -224,6 +225,7 @@ VARA(trait_data, fo::TraitInfo, fo::TRAIT_count) VAR_(view_page, DWORD) VAR_(wd_obj, DWORD) VARA(window, fo::Window*, 50) +VARA(window_index, DWORD, 50) VAR_(WhiteColor, BYTE) VAR_(wmAreaInfoList, DWORD) VARC(wmBkWin, DWORD) diff --git a/sfall/Modules/MetaruleExtender.cpp b/sfall/Modules/MetaruleExtender.cpp index 102d541e..a9fcbcff 100644 --- a/sfall/Modules/MetaruleExtender.cpp +++ b/sfall/Modules/MetaruleExtender.cpp @@ -64,7 +64,7 @@ static long __fastcall op_metarule3_ext(long metafunc, long* args) { __asm call fo::funcoffs::kb_clear_; break; case MetaruleFunction::EXT_PARTY_ORDER_ATTACK: - PartyControl::MemberOrderAttackPatch(); + PartyControl::OrderAttackPatch(); break; default: fo::func::debug_printf("\nOPCODE ERROR: metarule3(%d, ...) - metarule function number does not exist.\n > Script: %s, procedure %s.", diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp index 51c9e542..3786593d 100644 --- a/sfall/Modules/PartyControl.cpp +++ b/sfall/Modules/PartyControl.cpp @@ -18,6 +18,7 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "..\Utils.h" #include "HookScripts\InventoryHs.h" #include "Drugs.h" #include "HookScripts.h" @@ -627,7 +628,7 @@ static void __declspec(naked) gdControlUpdateInfo_hook() { } static bool partyOrderPickTargetLoop; -static char partyOrderAttackMsg[33]; +static std::vector partyOrderAttackMsg; // disables the display of the hit chance value when picking a target static void __declspec(naked) gmouse_bk_process_hack() { @@ -670,22 +671,22 @@ static void __fastcall action_attack_to(long unused, fo::GameObject* partyMember if (underObject->critter.IsNotDead()) { outlineColor = underObject->outline; - if (underObject->critter.combatState) { - underObject->outline = 254 << 8; + if (fo::var::combatNumTurns || underObject->critter.combatState) { + underObject->outline = 254 << 8; // flashing red validTarget = underObject; } else { - underObject->outline = 10 << 8; + underObject->outline = 10 << 8; // grey } fo::func::obj_bound(underObject, &rect); fo::func::tile_refresh_rect(&rect, fo::var::map_elevation); targetObject = underObject; } } - if (validTarget && *(DWORD*)FO_VAR_mouse_buttons == 1) break; // left mouse button + if (validTarget && fo::var::mouse_buttons == 1) break; // left mouse button - } while (*(DWORD*)FO_VAR_mouse_buttons != 2 && fo::func::get_input() != 27); // 27 - escape code + } while (fo::var::mouse_buttons != 2 && fo::func::get_input() != 27); // 27 - escape code - if (validTarget && *(DWORD*)FO_VAR_mouse_buttons == 1) { + if (validTarget && fo::var::mouse_buttons == 1) { partyMember->critter.whoHitMe = validTarget; validTarget->outline = outlineColor; @@ -693,12 +694,26 @@ static void __fastcall action_attack_to(long unused, fo::GameObject* partyMember if (cap->disposition == fo::AIpref::disposition::custom) { cap->attack_who = (long)fo::AIpref::attack_who::whomever; } - std::strcpy((char*)FO_VAR_attack_str, partyOrderAttackMsg); - fo::func::ai_print_msg(partyMember, 0); // float message + + // floating text messages + const char* message; + switch (fo::func::critter_body_type(partyMember)) { + case fo::BodyType::Quadruped: // creatures + message = partyOrderAttackMsg[0].c_str(); + break; + case fo::BodyType::Robotic: + message = partyOrderAttackMsg[1].c_str(); + break; + default: // biped + long max = partyOrderAttackMsg.size() - 1; + long rnd = (max > 2) ? GetRandom(2, max) : 2; + message = partyOrderAttackMsg[rnd].c_str(); + } + fo::PrintFloatText(partyMember, message, cap->color, cap->outline_color, cap->font); } partyOrderPickTargetLoop = false; - //*(DWORD*)FO_VAR_mouse_buttons = 0; + fo::var::mouse_buttons = 0; fo::func::gmouse_set_cursor(0); fo::func::gmouse_3d_set_mode(1); @@ -739,7 +754,7 @@ static void __declspec(naked) gmouse_handle_event_hook_restore() { } } -void PartyControl::MemberOrderAttackPatch() { +void PartyControl::OrderAttackPatch() { MakeCall(0x44C4A7, gmouse_handle_event_hack, 2); HookCall(0x44C75F, gmouse_handle_event_hook); HookCall(0x44C69A, gmouse_handle_event_hook_restore); @@ -788,7 +803,10 @@ void PartyControl::init() { dlogr(" Done", DL_INIT); } - Translate("sfall", "PartyOrderAttack", "I'll take care of it.", partyOrderAttackMsg, 33); + partyOrderAttackMsg.push_back(std::move(Translate("sfall", "PartyOrderAttackCreature", "::Growl::", 33))); + partyOrderAttackMsg.push_back(std::move(Translate("sfall", "PartyOrderAttackRobot", "::Beep::", 33))); + auto msgs = TranslateList("sfall", "PartyOrderAttackHuman", "I'll take care of it.|Okay, I got it.", '|', 512); + partyOrderAttackMsg.insert(partyOrderAttackMsg.cend(), msgs.cbegin(), msgs.cend()); } void PartyControl::exit() { diff --git a/sfall/Modules/PartyControl.h b/sfall/Modules/PartyControl.h index 9f1fb27b..33adfa7e 100644 --- a/sfall/Modules/PartyControl.h +++ b/sfall/Modules/PartyControl.h @@ -38,7 +38,7 @@ public: // Returns pointer to "real" dude, which is different from "dude_obj" when controlling another critter static fo::GameObject* RealDudeObject(); - static void MemberOrderAttackPatch(); + static void OrderAttackPatch(); }; extern bool npcAutoLevelEnabled; diff --git a/sfall/Utils.cpp b/sfall/Utils.cpp index a7ace7f0..b0f56dc2 100644 --- a/sfall/Utils.cpp +++ b/sfall/Utils.cpp @@ -69,4 +69,9 @@ void StrNormalizePath(char* path) { } while (*(++path) != 0); } +// max range 0-32767 +long GetRandom(long min, long max) { + return (min + (std::rand() % (max - (min - 1)))); +} + } diff --git a/sfall/Utils.h b/sfall/Utils.h index d3b37e52..1ebc78f2 100644 --- a/sfall/Utils.h +++ b/sfall/Utils.h @@ -36,4 +36,6 @@ const char* strfind(const char* source, const char* word); void StrNormalizePath(char* path); +long GetRandom(long min, long max); + } diff --git a/sfall/main.cpp b/sfall/main.cpp index 250ae616..721d8feb 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -149,6 +149,10 @@ std::vector GetConfigList(const char* section, const char* setting, return GetIniList(section, setting, defaultValue, bufSize, ',', ini); } +std::vector TranslateList(const char* section, const char* setting, const char* defaultValue, char delimiter, size_t bufSize) { + return GetIniList(section, setting, defaultValue, bufSize, delimiter, translationIni); +} + std::string Translate(const char* section, const char* setting, const char* defaultValue, size_t bufSize) { return GetIniString(section, setting, defaultValue, bufSize, translationIni); } @@ -346,6 +350,7 @@ defaultIni: } } } + std::srand(GetTickCount()); InitModules(); } diff --git a/sfall/main.h b/sfall/main.h index 8b7f377c..f309a85a 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -104,6 +104,8 @@ size_t GetConfigString(const char* section, const char* setting, const char* def // Parses the comma-separated list from the settings from Sfall configuration INI file. std::vector GetConfigList(const char* section, const char* setting, const char* defaultValue, size_t bufSize = 128); +std::vector TranslateList(const char* section, const char* setting, const char* defaultValue, char delimiter, size_t bufSize = 256); + // Translates given string using Sfall translation INI file. std::string Translate(const char* section, const char* setting, const char* defaultValue, size_t bufSize = 128);