Compare commits

...
Author SHA1 Message Date
phobos2077 1933eda447 Code review fixes 2026-03-20 15:27:54 +01:00
phobos2077 b30f75ba49 Revert preset change 2026-03-20 13:05:57 +01:00
phobos2077 005d64d1f0 cmake: add COPY_TO_PATH variable and windows-x86-xp preset 2026-03-20 11:36:19 +01:00
Vlad K 5860ee2ffd Merge pull request #319 from fallout2-ce/feature/hook-ongamemodechange-rebase
Implement sfall HOOK_ONGAMEMODECHANGE
2026-03-20 11:23:44 +01:00
Mike Klaas 550e6f035a Implement sfall HOOK_ONGAMEMODECHANGE
Pretty straight forward one
2026-03-20 11:12:28 +01:00
Vlad Kandgithub-actions[bot] 26978dedc5 Hook scripts, related opcodes and some deobfuscation (#312)
* hooks: an empty cc and a list of hook types with useful comments

* Deobfuscate some script-related procedures and flags

* Script hooks WIP

* Interpreter: add programPrintError(), Program->procedureCount(), rename string-related functions

* ScriptHookCall refactoring and all basic hook script opcode implementation

* HOOK_KEYPRESS and first working hooks

* mf_obj_under_cursor + some test script

* HOOK_TOHIT

* chore: auto-format with clang-format

* Add hooks: useobj, useobjon

* chore: auto-format with clang-format

* opcode: get_sfall_args

* Code review fixes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-20 11:08:49 +01:00
Mike Klaas 47434c4038 Starting game_dialogc/h deobfuscation (#307) 2026-03-18 22:50:11 -07:00
36 changed files with 1536 additions and 525 deletions
+26
View File
@@ -286,6 +286,7 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/sfall_metarules.h"
"src/sfall_opcodes.cc"
"src/sfall_opcodes.h"
"src/sfall_script_hooks.cc"
"src/sfall_arrays.cc"
"src/sfall_arrays.h"
"src/sfall_callbacks.cc"
@@ -508,3 +509,28 @@ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
endif()
# Optional path to which the built executable and PDB will be copied after build.
# Can be set via -DCOPY_TO_PATH=/some/path or through the CMake/CLion GUI.
set(COPY_TO_PATH "" CACHE PATH "Destination directory for copying the executable and PDB after build")
if(DEFINED COPY_TO_PATH AND NOT "${COPY_TO_PATH}" STREQUAL "")
add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${COPY_TO_PATH}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_FILE:${EXECUTABLE_NAME}>"
"${COPY_TO_PATH}/"
COMMAND ${CMAKE_COMMAND} -E echo "Copied executable to ${COPY_TO_PATH}"
)
if(MSVC)
add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<TARGET_PDB_FILE:${EXECUTABLE_NAME}>"
"${COPY_TO_PATH}/"
COMMAND ${CMAKE_COMMAND} -E echo "Copied PDB to ${COPY_TO_PATH}"
)
message(STATUS "Will copy executable and PDB to: ${COPY_TO_PATH}")
else()
message(STATUS "Will copy executable to: ${COPY_TO_PATH}")
endif()
endif()
+1
View File
@@ -1 +1,2 @@
*.int
sfall
+120
View File
@@ -0,0 +1,120 @@
#include "../sfall/sfall.h"
#include "../sfall/dik.h"
#include "../sfall/define_lite.h"
#include "../test_utils.h"
variable last_game_mode;
variable gamemodechange_assertions_enabled := false;
variable gamemodechange_reported := false;
procedure test_with_args(variable arg1) begin
display_msg("test_w_arg" + arg1);
end
procedure test_wo_args begin
display_msg("test_wo_args");
end
procedure tohit_handler begin
variable
attacker := get_sfall_arg_at(1),
target := get_sfall_arg_at(2),
chanceRaw := get_sfall_arg_at(7);
display_msg(string_format("tohit %s -> %s (%d)", obj_name(attacker), obj_name(target), chanceRaw));
if (attacker == dude_obj) then
set_sfall_return(100);
else if (target == dude_obj) then
set_sfall_return(0);
end
procedure useobj_handler begin
variable
user := get_sfall_arg,
item := get_sfall_arg;
display_msg(string_format("useobj %s (%s)", obj_name(user), obj_name(item)));
set_sfall_return(2);
end
procedure useobjon_handler begin
variable args := get_sfall_args,
target := args[0],
user := args[1],
item := args[2];
display_msg(string_format("useobjon %s (%s) -> %s [len: %d]", obj_name(user), obj_name(item), obj_name(target), len_array(args)));
set_sfall_return(0);
end
procedure keypress_handler begin
variable
pressed := get_sfall_arg_at(0),
key := get_sfall_arg_at(1);
display_msg(string_format("keypress %d dx: %d", pressed, key));
if (not pressed) then return;
if (key == DIK_F2) then begin
display_msg(string_format("tile %d, elev %d, map %d", tile_under_cursor, elevation(dude_obj), cur_map_index));
end if (key == DIK_F3) then begin
if (key_pressed(DIK_LCONTROL)) then begin
variable cr := obj_under_cursor(true, false);
if (cr) then
critter_dmg(cr, 1000, DMG_BYPASS_ARMOR);
end else
move_to(dude_obj, tile_under_cursor, elevation(dude_obj));
end
end
procedure gamemodechange_handler begin
variable
event_type := get_sfall_arg_at(0),
previous_mode := get_sfall_arg_at(1),
current_mode := get_game_mode;
display_msg(string_format("gamemodechange event=%d prev=%d cur=%d", event_type, previous_mode, current_mode));
if (not gamemodechange_assertions_enabled) then begin
last_game_mode := current_mode;
return;
end
call assertTrue("gamemodechange event_type is 0 or 1", event_type == 0 or event_type == 1);
call assertEquals("gamemodechange previous mode", previous_mode, last_game_mode);
if (event_type == 0) then
call assertNotEquals("gamemodechange mode changed", current_mode, previous_mode);
else if (not gamemodechange_reported) then begin
gamemodechange_reported := true;
call report_test_results("hook_gamemodechange");
end
last_game_mode := current_mode;
end
procedure start begin
//call test_with_args(777);
display_msg("start hook test");
if (not game_loaded) then return;
add_obj_to_inven(dude_obj, create_object_sid(40, 0, 0, -1));
add_obj_to_inven(dude_obj, create_object_sid(76, 0, 0, -1));
add_obj_to_inven(dude_obj, create_object_sid(85, 0, 0, -1));
last_game_mode := get_game_mode;
gamemodechange_assertions_enabled := true;
register_hook_proc(HOOK_KEYPRESS, keypress_handler);
register_hook_proc(HOOK_TOHIT, tohit_handler);
register_hook_proc(HOOK_USEOBJ, useobj_handler);
register_hook_proc(HOOK_USEOBJON, useobjon_handler);
register_hook_proc(HOOK_GAMEMODECHANGE, gamemodechange_handler);
end
procedure map_enter_p_proc begin
display_msg("Map Enter hook test");
end
+3
View File
@@ -39,6 +39,7 @@
#include "sfall_callbacks.h"
#include "sfall_config.h"
#include "sfall_global_scripts.h"
#include "sfall_script_hooks.h"
#include "skill.h"
#include "stat.h"
#include "svga.h"
@@ -4491,6 +4492,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
}
}
int toHitUncapped = toHit;
if (toHit > 95) {
toHit = 95;
}
@@ -4499,6 +4501,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
debugPrint("Whoa! Bad skill value in determine_to_hit!\n");
}
toHit = scriptHooks_ToHit(attacker, defender, tile, hitMode, hitLocation, toHit, toHitUncapped, useDistance);
return toHit;
}
+21
View File
@@ -59,6 +59,7 @@
#include "sfall_global_vars.h"
#include "sfall_ini.h"
#include "sfall_lists.h"
#include "sfall_script_hooks.h"
#include "skill.h"
#include "skilldex.h"
#include "stat.h"
@@ -120,6 +121,8 @@ int _game_user_wants_to_quit = 0;
// 0x58E940
MessageList gMiscMessageList;
bool gGameLoaded = false;
int gSplashScreenScaling = 0;
// CE: Sonora folks like to store objects in global variables.
@@ -388,6 +391,11 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int fl
return -1;
}
if (!scriptHooksInit()) {
debugPrint("Failed on scriptHooksInit");
return -1;
}
char* customConfigBasePath;
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, &customConfigBasePath);
sfall_ini_set_base_path(customConfigBasePath);
@@ -442,9 +450,11 @@ void gameReset()
sfall_gl_vars_reset();
sfallListsReset();
messageListRepositoryReset();
scriptHooksReset();
sfallArraysReset();
sfall_gl_scr_reset();
sfallOnGameReset();
gGameLoaded = false;
}
// 0x442C34
@@ -452,7 +462,10 @@ void gameExit()
{
debugPrint("\nGame Exit\n");
sfallOnGameModeChange(1, GameMode::getCurrentGameMode());
// SFALL
scriptHooksExit();
sfall_gl_scr_exit();
sfallArraysExit();
sfallListsExit();
@@ -1613,12 +1626,20 @@ int GameMode::currentGameMode = 0;
void GameMode::enterGameMode(int gameMode)
{
int previousGameMode = currentGameMode;
currentGameMode |= gameMode;
if (currentGameMode != previousGameMode) {
sfallOnGameModeChange(0, previousGameMode);
}
}
void GameMode::exitGameMode(int gameMode)
{
int previousGameMode = currentGameMode;
currentGameMode &= ~gameMode;
if (currentGameMode != previousGameMode) {
sfallOnGameModeChange(0, previousGameMode);
}
}
bool GameMode::isInGameMode(int gameMode)
+2
View File
@@ -24,6 +24,8 @@ extern int gSplashScreenScaling;
extern MessageList gMiscMessageList;
extern bool gGameLoaded;
int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int flags, int argc, char** argv);
void gameReset();
void gameExit();
+280 -242
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -15,26 +15,26 @@ int gameDialogInit();
int gameDialogReset();
int gameDialogExit();
bool _gdialogActive();
void gameDialogEnter(Object* speaker, int a2);
void gameDialogEnter(Object* speaker, int mode);
void _gdialogSystemEnter();
void gameDialogStartLips(const char* a1);
void gameDialogStartLips(const char* audioFileName);
int gameDialogEnable();
int gameDialogDisable();
int _gdialogInitFromScript(int headFid, int reaction);
int _gdialogExitFromScript();
void gameDialogSetBackground(int a1);
void gameDialogSetBackground(int background);
void gameDialogRenderSupplementaryMessage(char* msg);
int _gdialogStart();
int _gdialogSayMessage();
int gameDialogAddMessageOptionWithProcIdentifier(int messageListId, int messageId, const char* a3, int reaction);
int gameDialogAddTextOptionWithProcIdentifier(int messageListId, const char* text, const char* a3, int reaction);
int gameDialogAddMessageOptionWithProcIdentifier(int messageListId, int messageId, const char* procName, int reaction);
int gameDialogAddTextOptionWithProcIdentifier(int messageListId, const char* text, const char* procName, int reaction);
int gameDialogAddMessageOptionWithProc(int messageListId, int messageId, int proc, int reaction);
int gameDialogAddTextOptionWithProc(int messageListId, const char* text, int proc, int reaction);
int gameDialogSetMessageReply(Program* a1, int a2, int a3);
int gameDialogSetTextReply(Program* a1, int a2, const char* a3);
int gameDialogSetMessageReply(Program* program, int messageListId, int messageId);
int gameDialogSetTextReply(Program* program, int messageListId, const char* text);
int _gdialogGo();
void _gdialogUpdatePartyStatus();
void _talk_to_critter_reacts(int a1);
void _talk_to_critter_reacts(int reaction);
void gameDialogSetBarterModifier(int modifier);
int gameDialogBarter(int modifier);
void _barter_end_to_talk_to();
-1
View File
@@ -312,7 +312,6 @@ static Object* gGameMousePointedObject;
static int _gmouse_get_click_to_scroll();
static void _gmouse_3d_enable_modes();
static int gameMouseSetBouncingCursorFid(int fid);
static Object* gameMouseGetObjectUnderCursor(int objectType, bool includeDude, int elevation);
static int gameMouseRenderAccuracy(const char* string, int color);
static int gameMouseRenderActionPoints(const char* string, int color);
static int gameMouseObjectsInit();
+1
View File
@@ -86,6 +86,7 @@ void gameMouseRefresh();
void _gmouse_handle_event(int mouseX, int mouseY, int mouseState);
int gameMouseSetCursor(int cursor);
int gameMouseGetCursor();
Object* gameMouseGetObjectUnderCursor(int objectType, bool includeDude, int elevation);
void gmouse_set_mapper_mode(int mode);
void gameMouseSetMode(int mode);
int gameMouseGetMode();
+6
View File
@@ -11,6 +11,7 @@
#include "kb.h"
#include "memory.h"
#include "mouse.h"
#include "sfall_kb_helpers.h"
#include "svga.h"
#include "text_font.h"
#include "touch.h"
@@ -977,6 +978,11 @@ void _GNW95_process_message()
if (!keyboardIsDisabled()) {
keyboardData.key = e.key.keysym.scancode;
keyboardData.down = (e.key.state & SDL_PRESSED) != 0;
int keyOverride = sfall_kb_handle_key_pressed(keyboardData.key, keyboardData.down);
if (keyOverride != SDL_SCANCODE_UNKNOWN) {
keyboardData.key = keyOverride;
}
_GNW95_process_key(&keyboardData);
}
break;
+90 -74
View File
File diff suppressed because it is too large Load Diff
+18 -9
View File
@@ -108,10 +108,14 @@ typedef enum ProgramFlags {
// Program is in waiting state with `checkWaitFunc` set.
PROGRAM_IS_WAITING = 0x10,
PROGRAM_FLAG_0x20 = 0x20,
PROGRAM_FLAG_0x40 = 0x40,
// Program has an attached child created via O_CALLSTART.
// Also set in many opcodes that might trigger event procedures (probably to prevent them from disrupting current execution).
PROGRAM_FLAG_CHILD_CALL = 0x20,
// Set when current synchronous procedure call is finished.
PROGRAM_FLAG_FINISHED = 0x40,
PROGRAM_FLAG_CRITICAL_SECTION = 0x80,
PROGRAM_FLAG_0x0100 = 0x0100,
// Program has an attached child created via O_SPAWN.
PROGRAM_FLAG_CHILD_SPAWN = 0x0100,
} ProgramFlags;
enum RawValueType {
@@ -185,13 +189,15 @@ typedef struct Program {
jmp_buf env;
unsigned int waitEnd; // end time of timer (field_74 + wait time)
unsigned int waitStart; // time when wait was called
int field_78; // time when program begin execution (for the first time)?, -1 - program never executed
int startTime; // time when program begin execution (for the first time), in seconds, -1 - program never executed; not used
InterpretCheckWaitFunc* checkWaitFunc;
int flags; // flags
int windowId;
bool exited;
ProgramStack* stackValues;
ProgramStack* returnStackValues;
int procedureCount() const;
} Program;
typedef unsigned int(InterpretTimerFunc)();
@@ -205,7 +211,8 @@ char* _interpretMangleName(char* s);
void _interpretOutputFunc(int (*func)(char*));
int _interpretOutput(const char* format, ...);
[[noreturn]] void programFatalError(const char* str, ...);
void _interpretDecStringRef(Program* program, opcode_t a2, int a3);
void programPrintError(const char* format, ...);
void interpreterStringRefCountDecrease(Program* program, opcode_t a2, int a3);
void programFree(Program* program);
Program* programCreateByPath(const char* path);
char* programGetString(Program* program, opcode_t opcode, int offset);
@@ -213,10 +220,12 @@ char* programGetIdentifier(Program* program, int offset);
int programPushString(Program* program, const char* const string);
void interpreterRegisterOpcodeHandlers();
void _interpretClose();
void _interpret(Program* program, int a2);
void _executeProc(Program* program, int procedureIndex);
// Runs interpreter for a given number of instructions.
// For a typical script, if instructionPointer is 0 and numInstructions < 0, will execute script header, then 'start' procedure and then exit via O_EXIT_PROG.
void programInterpret(Program* program, int numInstructions);
void programExecuteProcedureAsync(Program* program, int procedureIndex);
int programFindProcedure(Program* prg, const char* name);
void _executeProcedure(Program* program, int procedureIndex);
void programExecuteProcedure(Program* program, int procedureIndex);
void programListNodeCreate(Program* program);
void runProgram(Program* program);
Program* runScript(char* name);
@@ -224,7 +233,7 @@ void _updatePrograms();
void programListFree();
void interpreterRegisterOpcode(int opcode, OpcodeHandler* handler);
void programStackPushValue(Program* program, ProgramValue& programValue);
void programStackPushValue(Program* program, const ProgramValue& programValue);
void programStackPushInteger(Program* program, int value);
void programStackPushFloat(Program* program, float value);
void programStackPushString(Program* program, const char* const string);
+51 -51
View File
@@ -521,7 +521,7 @@ static void opSetMapStart(Program* program)
// 0x4543F4
static void opOverrideMapStart(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int rotation = programStackPopInteger(program);
int elevation = programStackPopInteger(program);
@@ -552,7 +552,7 @@ static void opOverrideMapStart(Program* program)
tileWindowRefresh();
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// has_skill
@@ -950,20 +950,20 @@ out:
// 0x4551E4
static void opDestroyObject(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
Object* object = static_cast<Object*>(programStackPopPointer(program));
if (object == nullptr) {
scriptPredefinedError(program, "destroy_object", SCRIPT_ERROR_OBJECT_IS_NULL);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) {
if (_isLoadingGame()) {
debugPrint("\nError: attempt to destroy critter in load/save-game: %s!", program->name);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
}
@@ -1001,10 +1001,10 @@ static void opDestroyObject(Program* program)
tileWindowRefreshRect(&rect, gElevation);
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (isSelf) {
program->flags |= PROGRAM_FLAG_0x0100;
program->flags |= PROGRAM_FLAG_CHILD_SPAWN;
}
}
@@ -1824,29 +1824,29 @@ static void opAttackComplex(Program* program)
return;
}
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
Object* self = scriptGetSelf(program);
if (self == nullptr) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
if (!critterIsActive(self) || (self->flags & OBJECT_HIDDEN) != 0) {
debugPrint("\n But is already Inactive (Dead/Stunned/Invisible)");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
if (!critterIsActive(target) || (target->flags & OBJECT_HIDDEN) != 0) {
debugPrint("\n But target is already dead or invisible");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
if ((target->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) != 0) {
debugPrint("\n But target is AFRAID");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
@@ -1885,7 +1885,7 @@ static void opAttackComplex(Program* program)
scriptsRequestCombat(&combat);
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// start_gdialog
@@ -2312,7 +2312,7 @@ static void opKillCritter(Program* program)
debugPrint("\nError: attempt to destroy critter in load/save-game: %s!", program->name);
}
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
Object* self = scriptGetSelf(program);
bool isSelf = self == object;
@@ -2321,10 +2321,10 @@ static void opKillCritter(Program* program)
_combat_delete_critter(object);
critterKill(object, deathFrame, 1);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (isSelf) {
program->flags |= PROGRAM_FLAG_0x0100;
program->flags |= PROGRAM_FLAG_CHILD_SPAWN;
}
}
@@ -2386,7 +2386,7 @@ static void opKillCritterType(Program* program)
return;
}
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
Object* previousObj = nullptr;
int count = 0;
@@ -2399,7 +2399,7 @@ static void opKillCritterType(Program* program)
if (obj == previousObj || count > 200) {
scriptPredefinedError(program, "kill_critter_type", SCRIPT_ERROR_FOLLOWS);
debugPrint(" Infinite loop destroying critters!");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
@@ -2463,14 +2463,14 @@ static void opKillCritterType(Program* program)
obj = objectFindNext();
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// critter_dmg
// 0x457EB4
static void opCritterDamage(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int damageTypeWithFlags = programStackPopInteger(program);
int amount = programStackPopInteger(program);
@@ -2497,10 +2497,10 @@ static void opCritterDamage(Program* program)
int damageType = damageTypeWithFlags & ~(0x100 | 0x200);
actionDamage(object->tile, object->elevation, amount, amount, damageType, animate, bypassArmor);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (self == object) {
program->flags |= PROGRAM_FLAG_0x0100;
program->flags |= PROGRAM_FLAG_CHILD_SPAWN;
}
}
@@ -3602,7 +3602,7 @@ static void opPlayGameMovie(Program* program)
GAME_MOVIE_FADE_IN | GAME_MOVIE_FADE_OUT | GAME_MOVIE_PAUSE_MUSIC,
};
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int movie = programStackPopInteger(program);
@@ -3623,7 +3623,7 @@ static void opPlayGameMovie(Program* program)
isoEnable();
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// add_mult_objs_to_inven
@@ -3749,30 +3749,30 @@ static void opGetDaysSinceLastVisit(Program* program)
// 0x45A56C
static void _op_gsay_start(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
if (_gdialogStart() != 0) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
programFatalError("Error starting dialog.");
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// gsay_end
// 0x45A5B0
static void _op_gsay_end(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
_gdialogGo();
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// gsay_reply
// 0x45A5D4
static void _op_gsay_reply(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
ProgramValue msg = programStackPopValue(program);
int messageListId = programStackPopInteger(program);
@@ -3786,14 +3786,14 @@ static void _op_gsay_reply(Program* program)
programFatalError("script error: %s: invalid arg %d to gsay_reply", program->name, 0);
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// gsay_option
// 0x45A6C4
static void _op_gsay_option(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int reaction = programStackPopInteger(program);
ProgramValue proc = programStackPopValue(program);
@@ -3823,14 +3823,14 @@ static void _op_gsay_option(Program* program)
programFatalError("Invalid arg 3 to sayOption");
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// gsay_message
// 0x45A8AC
static void _op_gsay_message(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int reaction = programStackPopInteger(program);
ProgramValue msg = programStackPopValue(program);
@@ -3848,14 +3848,14 @@ static void _op_gsay_message(Program* program)
gameDialogAddMessageOptionWithProcIdentifier(-2, -2, nullptr, 50);
_gdialogSayMessage();
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// giq_option
// 0x45A9B4
static void _op_giq_option(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int reaction = programStackPopInteger(program);
ProgramValue proc = programStackPopValue(program);
@@ -3868,12 +3868,12 @@ static void _op_giq_option(Program* program)
if (iq < 0) {
if (-intelligence < iq) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
} else {
if (intelligence < iq) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
}
@@ -3901,7 +3901,7 @@ static void _op_giq_option(Program* program)
programFatalError("script error: %s: invalid arg %d to giq_option", program->name, 3);
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// poison
@@ -4423,24 +4423,24 @@ static void opAttackSetup(Program* program)
Object* defender = static_cast<Object*>(programStackPopPointer(program));
Object* attacker = static_cast<Object*>(programStackPopPointer(program));
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
if (attacker != nullptr) {
if (!critterIsActive(attacker) || (attacker->flags & OBJECT_HIDDEN) != 0) {
debugPrint("\n But is already dead or invisible");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
if (!critterIsActive(defender) || (defender->flags & OBJECT_HIDDEN) != 0) {
debugPrint("\n But target is already dead or invisible");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
if ((defender->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) != 0) {
debugPrint("\n But target is AFRAID");
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
return;
}
@@ -4464,14 +4464,14 @@ static void opAttackSetup(Program* program)
}
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// destroy_mult_objs
// 0x45C0E8
static void opDestroyMultipleObjects(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int quantity = programStackPopInteger(program);
Object* object = static_cast<Object*>(programStackPopPointer(program));
@@ -4520,10 +4520,10 @@ static void opDestroyMultipleObjects(Program* program)
programStackPushInteger(program, result);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (isSelf) {
program->flags |= PROGRAM_FLAG_0x0100;
program->flags |= PROGRAM_FLAG_CHILD_SPAWN;
}
}
@@ -4573,9 +4573,9 @@ static void opUseObjectOnObject(Program* program)
// 0x45C3B0
static void opEndgameSlideshow(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
scriptsRequestEndgame();
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// move_obj_inven_to_obj
@@ -4634,9 +4634,9 @@ static void opMoveObjectInventoryToObject(Program* program)
// 0x45C54C
static void opEndgameMovie(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
endgamePlayMovie();
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// obj_art_fid
+27 -27
View File
@@ -243,7 +243,7 @@ static void opPrint(Program* program)
// 0x461B10
void opSelectFileList(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
char* pattern = programStackPopString(program);
char* title = programStackPopString(program);
@@ -270,7 +270,7 @@ void opSelectFileList(Program* program)
programStackPushInteger(program, 0);
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// tokenize
@@ -525,7 +525,7 @@ void opFadeIn(Program* program)
{
int data = programStackPopInteger(program);
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
_setSystemPalette(gIntLibFadePalette);
@@ -534,7 +534,7 @@ void opFadeIn(Program* program)
gIntLibIsPaletteFaded = true;
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// fadeout
@@ -543,7 +543,7 @@ void opFadeOut(Program* program)
{
int data = programStackPopInteger(program);
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
bool cursorWasHidden = cursorIsHidden();
mouseHideCursor();
@@ -557,7 +557,7 @@ void opFadeOut(Program* program)
gIntLibIsPaletteFaded = false;
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// 0x462570
@@ -636,7 +636,7 @@ void opPlayMovieRect(Program* program)
static void opStopMovie(Program* program)
{
scriptWindowStopMovie();
program->flags |= PROGRAM_FLAG_0x40;
program->flags |= PROGRAM_FLAG_FINISHED;
}
// deleteregion
@@ -828,9 +828,9 @@ static void opSayStart(Program* program)
{
gIntLibSayStartingPosition = 0;
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int rc = _dialogStart(program);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (rc != 0) {
programFatalError("Error starting dialog.");
@@ -843,9 +843,9 @@ static void opSayStartPos(Program* program)
{
gIntLibSayStartingPosition = programStackPopInteger(program);
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int rc = _dialogStart(program);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (rc != 0) {
programFatalError("Error starting dialog.");
@@ -888,7 +888,7 @@ static void opSayGoToReply(Program* program)
// 0x463584
void opSayReply(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
ProgramValue v3 = programStackPopValue(program);
ProgramValue v2 = programStackPopValue(program);
@@ -903,25 +903,25 @@ void opSayReply(Program* program)
if ((v3.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) {
const char* v2 = programGetString(program, v3.opcode, v3.integerValue);
if (_dialogOption(v1, v2) != 0) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
programFatalError("Error setting option.");
}
} else if ((v3.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_INT) {
if (_dialogOptionProc(v1, v3.integerValue) != 0) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
programFatalError("Error setting option.");
}
} else {
programFatalError("Invalid arg 2 to sayOption");
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// sayoption
void opSayOption(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
ProgramValue v3 = programStackPopValue(program);
ProgramValue v4 = programStackPopValue(program);
@@ -941,26 +941,26 @@ void opSayOption(Program* program)
}
if (_dialogReply(v1, v2) != 0) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
programFatalError("Error setting option.");
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// 0x46378C
int intLibCheckDialog(Program* program)
{
program->flags |= PROGRAM_FLAG_0x40;
program->flags |= PROGRAM_FLAG_FINISHED;
return _dialogGetDialogDepth() != -1;
}
// 0x4637A4
void opSayEnd(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
int rc = dialogGo(gIntLibSayStartingPosition);
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
if (rc == -2) {
program->checkWaitFunc = intLibCheckDialog;
@@ -1019,7 +1019,7 @@ static void opSayMessageTimeout(Program* program)
// 0x463890
void opSayMessage(Program* program)
{
program->flags |= PROGRAM_FLAG_0x20;
program->flags |= PROGRAM_FLAG_CHILD_CALL;
ProgramValue v3 = programStackPopValue(program);
ProgramValue v4 = programStackPopValue(program);
@@ -1039,11 +1039,11 @@ void opSayMessage(Program* program)
}
if (dialogMessage(v1, v2, _TimeOut) != 0) {
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
programFatalError("Error setting option.");
}
program->flags &= ~PROGRAM_FLAG_0x20;
program->flags &= ~PROGRAM_FLAG_CHILD_CALL;
}
// gotoxy
@@ -1442,7 +1442,7 @@ void opRefreshMouse(Program* program)
int data = programStackPopInteger(program);
if (!scriptWindowRefreshRegions()) {
_executeProc(program, data);
programExecuteProcedureAsync(program, data);
}
}
@@ -2175,7 +2175,7 @@ static bool intLibDoInput(int key)
if (gIntLibGenericKeyHandlerProgram != nullptr) {
if (gIntLibGenericKeyHandlerProc != 0) {
_executeProc(gIntLibGenericKeyHandlerProgram, gIntLibGenericKeyHandlerProc);
programExecuteProcedureAsync(gIntLibGenericKeyHandlerProgram, gIntLibGenericKeyHandlerProc);
}
return true;
}
@@ -2186,7 +2186,7 @@ static bool intLibDoInput(int key)
}
if (entry->proc != 0) {
_executeProc(entry->program, entry->proc);
programExecuteProcedureAsync(entry->program, entry->proc);
}
return true;
+2 -2
View File
@@ -3999,14 +3999,14 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType)
itemRemove(owner, item, 1);
}
int useResult;
UseItemResultCode useResult;
if (_obj_action_can_use(item)) {
useResult = objectUseItemInternal(_stack[0], item);
} else {
useResult = objectUseItemOnInternal(_stack[0], _stack[0], item);
}
if (useResult == 1) {
if (useResult == USE_ITEM_RESULT_REMOVE) {
if (itemSlot != nullptr) {
*itemSlot = nullptr;
}
+15 -7
View File
@@ -28,6 +28,7 @@
#include "queue.h"
#include "random.h"
#include "sfall_config.h"
#include "sfall_script_hooks.h"
#include "skill.h"
#include "stat.h"
#include "tile.h"
@@ -2248,7 +2249,7 @@ bool miscItemUsesCharges(Object* miscItem)
}
// 0x4794A4
int miscItemUseCharged(Object* critter, Object* miscItem)
UseItemResultCode miscItemUseCharged(Object* critter, Object* miscItem)
{
int pid = miscItem->pid;
if (pid == PROTO_ID_STEALTH_BOY_I
@@ -2280,7 +2281,7 @@ int miscItemUseCharged(Object* critter, Object* miscItem)
}
}
return 0;
return USE_ITEM_RESULT_OK;
}
// 0x4795A4
@@ -2777,14 +2778,21 @@ static bool _drug_effect_allowed(Object* critter, int pid)
}
// 0x479F60
int drugItemTakeDrug(Object* critter, Object* item)
UseItemResultCode drugItemTakeDrug(Object* critter, Object* item)
{
// This matches original HOOK_USEOBJON implementation from sfall.
// This was needed because normally objectUseItemOnInternal won't get called for drugs.
int hookResult = scriptHooks_UseObjOn(critter, critter, item);
if (hookResult != -1) {
return static_cast<UseItemResultCode>(hookResult);
}
if (critterIsDead(critter)) {
return -1;
return USE_ITEM_RESULT_ERROR;
}
if (critterGetBodyType(critter) == BODY_TYPE_ROBOTIC) {
return -1;
return USE_ITEM_RESULT_ERROR;
}
Proto* proto;
@@ -2800,7 +2808,7 @@ int drugItemTakeDrug(Object* critter, Object* item)
}
// SFALL: Fix for Jet antidote not being removed.
return 1;
return USE_ITEM_RESULT_REMOVE;
}
}
@@ -2849,7 +2857,7 @@ int drugItemTakeDrug(Object* critter, Object* item)
}
}
return 1;
return USE_ITEM_RESULT_REMOVE;
}
// 0x47A178
+3 -2
View File
@@ -3,6 +3,7 @@
#include "db.h"
#include "obj_types.h"
#include "proto_instance.h"
namespace fallout {
@@ -105,7 +106,7 @@ int miscItemSetCharges(Object* miscItem, int charges);
int miscItemGetPowerType(Object* miscItem);
int miscItemGetPowerTypePid(Object* miscItem);
bool miscItemUsesCharges(Object* obj);
int miscItemUseCharged(Object* critter, Object* item);
UseItemResultCode miscItemUseCharged(Object* critter, Object* item);
int miscItemConsumeCharge(Object* miscItem);
int miscItemTrickleEventProcess(Object* item_obj, void* data);
bool miscItemIsOn(Object* obj);
@@ -118,7 +119,7 @@ int ammoGetArmorClassModifier(Object* armor);
int ammoGetDamageResistanceModifier(Object* armor);
int ammoGetDamageMultiplier(Object* armor);
int ammoGetDamageDivisor(Object* armor);
int drugItemTakeDrug(Object* critter_obj, Object* item_obj);
UseItemResultCode drugItemTakeDrug(Object* critter_obj, Object* item_obj);
int drugItemClear(Object* obj, void* data);
int drugEffectEventProcess(Object* obj, void* data);
int drugEffectEventRead(File* stream, void** dataPtr);
+1
View File
@@ -2014,6 +2014,7 @@ static int lsgLoadGameInSlot(int slot)
sfall_gl_scr_exec_start_proc();
// SFALL: Call "after start" event
sfallOnAfterGameStarted();
gGameLoaded = true;
return 0;
}
+1
View File
@@ -135,6 +135,7 @@ int falloutMain(int argc, char** argv)
// SFALL: Call "after loading" event
sfallOnAfterNewGame();
sfallOnAfterGameStarted();
gGameLoaded = true;
mainLoop();
paletteFadeTo(gPaletteWhite);

Some files were not shown because too many files have changed in this diff Show More