From 101238e1250754deac57dae5d724a42b05cea4a0 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 13 Oct 2021 13:01:24 +0800 Subject: [PATCH] Improved the party control and fixed mod related bugs Updated gl_partycontrol script. --- artifacts/mods/gl_partycontrol.int | Bin 4440 -> 4726 bytes artifacts/mods/gl_partycontrol.ssl | 84 +++++++++++++------- sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/FalloutEngine/Variables_def.h | 2 + sfall/Modules/Combat.cpp | 2 +- sfall/Modules/CritterStats.cpp | 2 +- sfall/Modules/HookScripts/CombatHs.cpp | 9 ++- sfall/Modules/Inventory.cpp | 2 +- sfall/Modules/PartyControl.cpp | 103 ++++++++++++++----------- sfall/Modules/Stats.cpp | 2 +- 10 files changed, 128 insertions(+), 79 deletions(-) diff --git a/artifacts/mods/gl_partycontrol.int b/artifacts/mods/gl_partycontrol.int index 56797cbd94744f237b2f34dda9774bad54a95635..2a33876509393a02c7d44cca5e5f26abf132de8e 100644 GIT binary patch delta 2044 zcmcbi^i4&ef$0Dv0|SFl122eX(VHkBThGnFz`(|U0$4hrTnQ)*rNH9M3=9k^Pyr~# z3ewNSz`$Sv6@XH#+ZY%aK;j-y0Vu_m2W12>Fw{etV2X1I0|Nudg$ZCToZu2-U|;}Q zoC6mEGq@_i6azyOl!j6Q6QGPaP#Q`JvOpPIpfm#mKbR#r4NNgGW=vLMjOOGDPA&1t zOwTBpT*a6_`6pwkJOf`!X-XigAU}z8m z)65NK4f-G!yV>M(Y>tdQlLgqt8Cxc6u&eX4H3)-bIQD=@2DZs@?AnYqle^f}m^efx zZ(>(r;?S9VgI!*|1R@VLk+s1bVk<{VgG7UCgMNb)M1HayhomCR%$^1ouqZ==5txRF zZoa;|{tWCa!#P*RyJ$0ad&5|>at zw-#6p6G#^*i@-dtKh-GcZj)#BIUEn=ttgw+A23u?7~fyJt0(HAqeF<5A4y zgxJIT50aR9|ABS0HV8q|3h%oH-iG~PF)pYb+)x@^JTR~|utGB&#HFkd^Y}J^^>D(> z;WL~3kH)523J`|OuS4^;Fog~cmuWs6cykSN#Fn^mo%;ii#s$pfN4-^ z1I>KzvE`Wid9x4W=I71j17(gye0CV94=Pf9+3`#>O z{t75#0+fbQ{Li3_6;K*V2|(2|`Y;ISLBM2Z#-Pay8FTpf^75TCQj@bCi;5B}876Zx zl}}#8)T5Z0SCW>PoEl%0no^Wl&cMJRke*r+pP!VKnp_f}l30?s*`HaHadI6CJEO_u zHWp{bO_NWt)JjHy0+@lR!RP?v|NsC0HyAfagG5;ECMU3FGH#fBidC0UYVr@(NbY?N zEFc+>+DTjzlk2#6csOkuQy}Wuk|y`C88fb!e1J`#k!$h?HZdl)Ka)Aw9o22Xniv{{ zptMDUK8VHc)xbLe6oM~6BqPJ*1ssa(O&}Qt_Q^BYwHcizA7NKxV!t){4ZBJ``zNU7 zj39?HFfgz*Sc26uHW-3v4l5AJzz(&AwZRZ6^e6XCKs?vigSP^890`p$Q4XB;Z&7oY7lAyJB@*%!5CyT z10z)Rr^#xZimuEM9s_fOP?HH*9vtorY)wgEo;;Xjhw>!AJPs&N63pXlN}8O&C8xl7 z2+U<{5Q6xba~g`DnI@m%R8r$y*B}8828ITsMsG+oFfcaoHb^#THE1^QG{{U&;3|pW z>I0j{)*u8?!c_w1v%vTaEDb^n7~LBf7#bjPo6=z2lnzw`3M~c(HfTJsKxx(np+@$} z7r4a2co%^c!h)9(mfpCT5boo81j+WGlm=GK2qsw@SRoz+t3^aALxa%dE!+xBJi8_* z@QHwv8FvI&2gnPq4f>Pqc$A&lpy3UPZZ=4=0f#;$mRKPoJRVS;Mqrw?!KlH!!2rx= zp8SAEl974xFCGgf9*xN-_%$cT^2&R0_cgG9k}yL@Q(1!)m^BNm9+p&dnkvA2)&^lL zvIS6CmImR;UA&6^To8BiynrM`o)=)fAfM<6<`z$8l}JD7&~2ORLc5SzJRQN^n; zS%l9-2rA4DOCh{5lcV@l{b9KUlpB_S4dZAKYN|x!mBu?@QAqG}LLA1Q0C5<90$2`i z72mSSYW#}spyUh*P!33d@+||aqqoy#hDlUi~2Ik3q{1!}nZIe&% zdqnWQf!N4*4y=X~<~dMqZ@dc@g9HNHg%B~QjSR4|fL{tZU+@b}F5*^m;7@4S4~~_J zrm_Z;27NF)158#ll{J+&NZ}84P!4q9hqw$B7T`?GzX+0;8`pzf;?Up#Ducjmh)bc7 z3@v-uCf^azvlD;>C@7}Eh6wOM4S53A$lPEI$&LaE4f`7z4lpt@Ff4@S9s#$>9)eSZ f1RbDCjUX9C;MwGNf--!9A`p{6l7b?WC4}MtWM+yf diff --git a/artifacts/mods/gl_partycontrol.ssl b/artifacts/mods/gl_partycontrol.ssl index 39babe9c..480bef2e 100644 --- a/artifacts/mods/gl_partycontrol.ssl +++ b/artifacts/mods/gl_partycontrol.ssl @@ -7,7 +7,7 @@ NOTE: this script requires compiler from sfall modderspack with -s option (short circuit evaluation) - version 1.1 + version 1.2 */ @@ -17,9 +17,11 @@ #define OBJ_DATA_LIGHT_DISTANCE (0x6C) #define OBJ_DATA_LIGHT_INTENSITY (0x70) +#define PID_PLAYER (16777216) procedure start; procedure AllowControl(variable pid); +procedure SetLight(variable critter, variable int, variable dist); procedure CombatTurn_Handler; procedure GameModeChange_Handler; procedure InventoryMove_Handler; @@ -31,12 +33,17 @@ variable pidList, perksList, lightInt, lightDist, npcControl, displayName, displayNameColor, isShowTag, - inControl := false; + inControl := false, + dudeLightInt, dudeLightDist; procedure AllowControl(variable pid) begin - if (noCheckArray or scan_array(pidList, pid bwand 0xFFFFFF) != -1) and (party_member_obj(pid)) then return true; - return false; + return ((pidList == 0 or scan_array(pidList, pid bwand 0xFFFFFF) != -1) and (party_member_obj(pid))); +end + +procedure SetLight(variable critter, variable lInt, variable lDist) begin + if (lInt) then lInt := round((lInt / 65536.0) * 100); // calc percent of intensity + obj_set_light_level(critter, lInt, lDist); // TODO: make sfall obj_set_light_level function end procedure CombatTurn_Handler begin @@ -46,67 +53,82 @@ procedure CombatTurn_Handler begin pid, perkID, level; //display_msg("Combat Turn: " + status + ", by " + obj_name(critter) + "/" + critter + ", arg3: " + get_sfall_arg); - if npcControl then begin - if lightInt then lightInt := round((lightInt / 65536.0) * 100); // calc percent of intensity - obj_set_light_level(npcControl, lightInt, lightDist); // restore light for prev. controlled NPC - end pid := obj_pid(critter); - if (status == 1 and (AllowControl(pid) or controlMode == 1)) then begin + if (pid == PID_PLAYER and inControl == false) then return; + if (pid != PID_PLAYER and controlMode != 1 and AllowControl(pid) == false) then return; + + if (status == 1) then begin set_dude_obj(critter); //display_msg("Take control of: " + obj_name(critter)); + if (critter != real_dude_obj) then begin - if not(npcControl) then obj_set_light_level(real_dude_obj, 0, 0); // dude off light + if (npcControl == 0) then begin + dudeLightDist := get_object_data(real_dude_obj, OBJ_DATA_LIGHT_DISTANCE); + dudeLightInt := get_object_data(real_dude_obj, OBJ_DATA_LIGHT_INTENSITY); + obj_set_light_level(real_dude_obj, 0, 0); // dude off light + end npcControl := critter; + + lightDist := get_object_data(critter, OBJ_DATA_LIGHT_DISTANCE); + lightInt := get_object_data(critter, OBJ_DATA_LIGHT_INTENSITY); + // set perks (only work with 4.1.8+) foreach (perkID in perksList) begin level := has_trait(TRAIT_PERK, real_dude_obj, perkID); if (level) then critter_add_trait(critter, TRAIT_PERK, perkID, level); end intface_redraw; - lightDist := get_object_data(critter, OBJ_DATA_LIGHT_DISTANCE); - lightInt := get_object_data(critter, OBJ_DATA_LIGHT_INTENSITY); + obj_set_light_level(critter, 100, 4); inControl := true; - end else if npcControl then begin - obj_set_light_level(dude_obj, 100, 4); // set dude default light + end else if (npcControl) then begin + call SetLight(dude_obj, dudeLightInt, dudeLightDist); // restore dude light npcControl := 0; // dude control end - if inControl then begin + + if (inControl) then begin // check preference setting _combat_highlight - if (read_byte(0x56D38C)) then set_outline(critter, OUTLINE_GREY); + if (read_byte(0x56D38C)) then set_outline(critter, OUTLINE_GREY); // TODO: replace read_byte with normal function + // center the screen on the controlled critter/dude and remove roof tiles move_to(dude_obj, dude_tile, dude_elevation); + if (displayName and critter != real_dude_obj) then begin set_iface_tag_text(displayName, obj_name(critter), displayNameColor); show_iface_tag(displayName); isShowTag := true; end end - end else if inControl then begin + end else begin + if (npcControl) then begin + call SetLight(critter, lightInt, lightDist); // restore light for controlled NPC + end + if (isShowTag) then begin + hide_iface_tag(displayName); + isShowTag := false; + end + // remove perks before switching control (only work with 4.1.8+) if (dude_obj != real_dude_obj) then begin foreach (perkID in perksList) begin level := has_trait(TRAIT_PERK, real_dude_obj, perkID); if (level) then critter_rm_trait(critter, TRAIT_PERK, perkID, level); end + + set_dude_obj(0); // w/o redraw interface bar + //display_msg("Return control to real dude!"); end - if isShowTag then begin - hide_iface_tag(displayName); - isShowTag := false; - end - if (status < 0 or AllowControl(pid) == false) then begin - if (dude_obj != real_dude_obj) then begin - set_dude_obj(real_dude_obj); - obj_set_light_level(dude_obj, 100, 4); - npcControl := 0; - end + + if (status < 0) then begin + set_dude_obj(real_dude_obj); + call SetLight(dude_obj, dudeLightInt, dudeLightDist); //obj_set_light_level(dude_obj, 100, 4); end end end procedure GameModeChange_Handler begin - if (inControl and not(get_game_mode BWAND COMBAT)) then begin + if (inControl and (get_game_mode bwand COMBAT) == 0) then begin inControl := false; npcControl := 0; move_to(dude_obj, dude_tile, dude_elevation); @@ -165,8 +187,10 @@ procedure start begin end pidList := GetConfigListInt("CombatControl", "PIDList"); - fix_array(pidList); - noCheckArray := (len_array(pidList) == 0); + if (len_array(pidList) > 0) then + fix_array(pidList); + else + pidList := 0; perksList := GetConfigListInt("CombatControl", "PerksList"); fix_array(perksList); diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index cb7abf90..93cc7901 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -39,6 +39,7 @@ #define FO_VAR_combat_end_due_to_load 0x517F98 #define FO_VAR_combat_free_move 0x56D39C #define FO_VAR_combat_list 0x56D390 +#define FO_VAR_combat_obj 0x51805C #define FO_VAR_combat_state 0x510944 #define FO_VAR_combat_turn_obj 0x56D388 #define FO_VAR_combat_turn_running 0x51093C diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 5b96356f..cbc76204 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -25,7 +25,9 @@ VAR_(colorTable, DWORD) VAR_(combat_end_due_to_load, DWORD) VAR_(combat_free_move, DWORD) VARD(combat_list, fo::GameObject*) +VAR_(combat_obj, fo::GameObject*) VAR_(combat_state, DWORD) +VAR_(combat_turn_obj, fo::GameObject*) VAR_(combat_turn_running, DWORD) VAR_(combatNumTurns, DWORD) VAR3(crit_succ_eff, fo::CritInfo, 20, 9, 6) // 20 critters with 9 body parts and 6 effects each diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index da0f4574..a21f7e51 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -373,7 +373,7 @@ void __stdcall SetHitChanceMax(fo::GameObject* critter, DWORD maximum, DWORD mod } void __stdcall SetNoBurstMode(fo::GameObject* critter, bool on) { - if (critter == fo::var::obj_dude || critter->IsNotCritter()) return; + if (critter->protoId == fo::PID_Player || critter->IsNotCritter()) return; long id = Objects::SetObjectUniqueID(critter); for (size_t i = 0; i < noBursts.size(); i++) { diff --git a/sfall/Modules/CritterStats.cpp b/sfall/Modules/CritterStats.cpp index 6568a77d..27a6babd 100644 --- a/sfall/Modules/CritterStats.cpp +++ b/sfall/Modules/CritterStats.cpp @@ -330,7 +330,7 @@ skip: // Returns the individual critter prototype, or null if it is missing long* __fastcall CritterStats::GetProto(fo::GameObject* critter) { - if (protoMem.empty() || critter->protoId == fo::PID_Player) return nullptr; + if (critter->protoId == fo::PID_Player || protoMem.empty()) return nullptr; if (lastGetProtoID.id == critter->id) return lastGetProtoID.proto; auto itMem = protoMem.find(critter->id); diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp index 527160e8..bdec6da5 100644 --- a/sfall/Modules/HookScripts/CombatHs.cpp +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -374,8 +374,9 @@ skip: } } -// hooks combat_turn function static long combatTurnResult = 0; + +// hooks combat_turn function static long __fastcall CombatTurnHook_Script(fo::GameObject* critter, long dudeBegin) { BeginHook(); argCount = 3; @@ -390,12 +391,16 @@ static long __fastcall CombatTurnHook_Script(fo::GameObject* critter, long dudeB if (cRet > 0 && rets[0] == 1) { // skip turn goto endHook; // exit hook } + // set_sfall_return is not used, proceed normally - combatTurnResult = args[0] = fo::func::combat_turn(critter, dudeBegin); + combatTurnResult = fo::func::combat_turn(critter, dudeBegin); + args[0] = combatTurnResult; + if (fo::var::combat_end_due_to_load && combatTurnResult == -1) goto endHook; // don't run end of turn hook when the game was loaded during the combat //cRet = 0; // reset number of return values RunHookScript(HOOK_COMBATTURN); // End of turn + if (cRet > 0 && rets[0] == -1) combatTurnResult = -1; // override result of turn endHook: diff --git a/sfall/Modules/Inventory.cpp b/sfall/Modules/Inventory.cpp index ea0150b2..f7899290 100644 --- a/sfall/Modules/Inventory.cpp +++ b/sfall/Modules/Inventory.cpp @@ -69,7 +69,7 @@ void InventoryKeyPressedHook(DWORD dxKey, bool pressed) { //////////////////////////////////////////////////////////////////////////////// static int __stdcall CritterGetMaxSize(fo::GameObject* critter) { - if (critter == fo::var::obj_dude) return invSizeMaxLimit; + if (critter->protoId == fo::PID_Player) return invSizeMaxLimit; if (sizeLimitMode != 3) { // selected mode 1 or 2 if (!(sizeLimitMode & 2) || !(fo::func::isPartyMember(critter))) return 0; // if mode 2 is selected, check this party member, otherwise 0 diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp index fe53162f..348c696c 100644 --- a/sfall/Modules/PartyControl.cpp +++ b/sfall/Modules/PartyControl.cpp @@ -1,20 +1,20 @@ /* -* sfall -* Copyright (C) 2008-2016 The sfall team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * sfall + * Copyright (C) 2008-2016 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" @@ -40,7 +40,7 @@ bool npcAutoLevelEnabled; bool npcEngineLevelUp = true; static bool isControllingNPC = false; -static bool skipCounterAnim = false; +static char skipCounterAnim; static int delayedExperience; static bool switchHandHookInjected = false; @@ -161,8 +161,10 @@ static void SaveRealDudeState() { realDude.isSaved = true; - if (skipCounterAnim) SafeWriteBatch(0, {0x422BDE, 0x4229EC}); // no animate - + if (skipCounterAnim == 1) { + skipCounterAnim++; + SafeWrite8(0x422BDE, 0); // no animate + } if (isDebug) fo::func::debug_printf("\n[SFALL] Save dude state."); } @@ -250,6 +252,7 @@ static void SetCurrentDude(fo::GameObject* npc) { fo::util::ToggleNpcFlag(npc, 4, isAddict); // for show/hide addiction box (fix bug) // switch main dude_obj pointers - this should be done last! + fo::var::combat_turn_obj = npc; fo::var::obj_dude = npc; fo::var::inven_dude = npc; fo::var::inven_pid = npc->protoId; @@ -323,9 +326,13 @@ static void RestoreRealDudeState(bool redraw = true) { fo::var::game_global_vars[fo::var::drugInfoList[8].addictGvar] = realDude.addictGvar[7]; if (realDude.extendAddictGvar) RestoreAddictGvarState(); - if (skipCounterAnim) SafeWriteBatch(1, {0x422BDE, 0x4229EC}); // restore - - if (redraw) fo::func::intface_redraw(); + if (redraw) { + if (skipCounterAnim == 2) { + skipCounterAnim--; + SafeWrite8(0x422BDE, 1); // restore + } + fo::func::intface_redraw(); + } realDude.isSaved = false; isControllingNPC = false; @@ -459,6 +466,10 @@ pcName: static void PartyControlReset() { if (realDude.obj_dude != nullptr && isControllingNPC) { RestoreRealDudeState(false); + if (skipCounterAnim == 2) { + skipCounterAnim = 1; // skipCounterAnim--; + SafeWrite8(0x422BDE, 1); // restore + } } realDude.obj_dude = nullptr; realDude.isSaved = false; @@ -528,6 +539,11 @@ static void NPCWeaponTweak() { } void PartyControl::SwitchToCritter(fo::GameObject* critter) { + if (skipCounterAnim == 2 && critter && critter == realDude.obj_dude) { + skipCounterAnim--; + SafeWrite8(0x422BDE, 1); // restore + } + if (isControllingNPC) { if (fo::util::IsNpcFlag(fo::var::obj_dude, 0)) { // sneak flag /* saves the sneak state for the currently controlled NPC and clears its events before switching */ @@ -555,32 +571,33 @@ void PartyControl::SwitchToCritter(fo::GameObject* critter) { } NPCWeaponTweak(); - if (critter == nullptr || critter == realDude.obj_dude) RestoreRealDudeState(); // return control to dude + if (critter == nullptr || critter == realDude.obj_dude) RestoreRealDudeState(critter != nullptr); // return control to dude } - if (critter != nullptr && critter != PartyControl::RealDudeObject()) { + if (critter && critter != PartyControl::RealDudeObject()) { if (!isControllingNPC && !realDude.isSaved) { SaveRealDudeState(); } SetCurrentDude(critter); - if (switchHandHookInjected) return; - switchHandHookInjected = true; -// if (!HookScripts::IsInjectHook(HOOK_INVENTORYMOVE)) Inject_SwitchHandHook(); + if (!switchHandHookInjected) { + switchHandHookInjected = true; + //if (!HookScripts::IsInjectHook(HOOK_INVENTORYMOVE)) Inject_SwitchHandHook(); - ScriptExtender::OnMapExit() += []() { - if (!partySneakWorking.empty()) { - // unset active sneak flags for controlled NPCs when exiting the map - for (const auto& member : partySneakWorking) fo::util::ToggleNpcFlag(member.object, 0, false); - partySneakWorking.clear(); - } - }; - HookCall(0x42E25B, pc_flag_off_hook); - HookCall(0x49EB09, proto_name_hook); + ScriptExtender::OnMapExit() += []() { + if (!partySneakWorking.empty()) { + // unset active sneak flags for controlled NPCs when exiting the map + for (const auto& member : partySneakWorking) fo::util::ToggleNpcFlag(member.object, 0, false); + partySneakWorking.clear(); + } + }; + HookCall(0x42E25B, pc_flag_off_hook); + HookCall(0x49EB09, proto_name_hook); - // Gets dude perks and traits from script while controlling another NPC - // WARNING: Handling dude perks/traits in the engine code while controlling another NPC remains impossible, this requires serious hacking of the engine code - HookCall(0x458242, GetRealDudePerk); // op_has_trait_ - HookCall(0x458326, GetRealDudeTrait); // op_has_trait_ + // Gets dude perks and traits from script while controlling another NPC + // WARNING: Handling dude perks/traits in the engine code while controlling another NPC remains impossible, this requires serious hacking of the engine code + HookCall(0x458242, GetRealDudePerk); // op_has_trait_ + HookCall(0x458326, GetRealDudeTrait); // op_has_trait_ + } } } @@ -796,7 +813,7 @@ static void __fastcall SetOrderTarget(fo::GameObject* attacker) { if (target) attacker->critter.whoHitMe = target; } -static void __declspec(naked) combat_ai_hook_targer() { +static void __declspec(naked) combat_ai_hook_target() { __asm { push ecx; mov ecx, eax; @@ -813,7 +830,7 @@ void PartyControl::OrderAttackPatch() { HookCall(0x44C69A, gmouse_handle_event_hook_restore); MakeCall(0x44B830, gmouse_bk_process_hack); - HookCall(0x42B235, combat_ai_hook_targer); + HookCall(0x42B235, combat_ai_hook_target); LoadGameHook::OnCombatEnd() += []() { partyOrderAttackTarget.clear(); }; @@ -849,7 +866,7 @@ void PartyControl::init() { NpcAutoLevelPatch(); - skipCounterAnim = (IniReader::GetConfigInt("Misc", "SpeedInterfaceCounterAnims", 0) == 3); + skipCounterAnim = (IniReader::GetConfigInt("Misc", "SpeedInterfaceCounterAnims", 0) == 3) ? 1 : 0; // Display party member's current level & AC & addict flag if (IniReader::GetConfigInt("Misc", "PartyMemberExtraInfo", 0)) { diff --git a/sfall/Modules/Stats.cpp b/sfall/Modules/Stats.cpp index 4af73640..56482ebb 100644 --- a/sfall/Modules/Stats.cpp +++ b/sfall/Modules/Stats.cpp @@ -61,7 +61,7 @@ static void __declspec(naked) stat_level_hack() { static int __fastcall check_stat_level(int value, DWORD stat) { int valLimit; - if (cCritter == fo::var::obj_dude) { + if (cCritter->protoId == fo::PID_Player) { valLimit = statMinimumsPC[stat]; if (value < valLimit) return valLimit; valLimit = statMaximumsPC[stat];