mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Improved the party control and fixed mod related bugs
Updated gl_partycontrol script.
This commit is contained in:
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<BYTE>(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<BYTE>(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)) {
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user