Compare commits

..
12 changed files with 299 additions and 449 deletions
-25
View File
@@ -509,28 +509,3 @@ 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 -34
View File
@@ -1,11 +1,6 @@
#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);
@@ -69,31 +64,6 @@ procedure keypress_handler begin
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");
@@ -104,17 +74,14 @@ procedure start begin
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
-10
View File
@@ -462,8 +462,6 @@ void gameExit()
{
debugPrint("\nGame Exit\n");
sfallOnGameModeChange(1, GameMode::getCurrentGameMode());
// SFALL
scriptHooksExit();
sfall_gl_scr_exit();
@@ -1626,20 +1624,12 @@ 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)
+241 -279
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 mode);
void gameDialogEnter(Object* speaker, int a2);
void _gdialogSystemEnter();
void gameDialogStartLips(const char* audioFileName);
void gameDialogStartLips(const char* a1);
int gameDialogEnable();
int gameDialogDisable();
int _gdialogInitFromScript(int headFid, int reaction);
int _gdialogExitFromScript();
void gameDialogSetBackground(int background);
void gameDialogSetBackground(int a1);
void gameDialogRenderSupplementaryMessage(char* msg);
int _gdialogStart();
int _gdialogSayMessage();
int gameDialogAddMessageOptionWithProcIdentifier(int messageListId, int messageId, const char* procName, int reaction);
int gameDialogAddTextOptionWithProcIdentifier(int messageListId, const char* text, const char* procName, int reaction);
int gameDialogAddMessageOptionWithProcIdentifier(int messageListId, int messageId, const char* a3, int reaction);
int gameDialogAddTextOptionWithProcIdentifier(int messageListId, const char* text, const char* a3, 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* program, int messageListId, int messageId);
int gameDialogSetTextReply(Program* program, int messageListId, const char* text);
int gameDialogSetMessageReply(Program* a1, int a2, int a3);
int gameDialogSetTextReply(Program* a1, int a2, const char* a3);
int _gdialogGo();
void _gdialogUpdatePartyStatus();
void _talk_to_critter_reacts(int reaction);
void _talk_to_critter_reacts(int a1);
void gameDialogSetBarterModifier(int modifier);
int gameDialogBarter(int modifier);
void _barter_end_to_talk_to();
+2 -3
View File
@@ -2,7 +2,6 @@
#include "display_monitor.h"
#include "sfall_config.h"
#include "sfall_script_hooks.h"
#include "worldmap.h"
namespace fallout {
@@ -54,9 +53,9 @@ void sfallOnAfterNewGame()
return;
}
void sfallOnGameModeChange(int exit, int previousGameMode)
void sfallOnGameModeChange()
{
scriptHooks_GameModeChange(exit, previousGameMode);
return;
}
void sfallOnBeforeGameClose()
+1 -1
View File
@@ -11,7 +11,7 @@ void sfallOnGameReset();
void sfallOnBeforeGameStart();
void sfallOnAfterGameStarted();
void sfallOnAfterNewGame();
void sfallOnGameModeChange(int exit, int previousGameMode);
void sfallOnGameModeChange();
void sfallOnBeforeGameClose();
void sfallOnCombatStart();
void sfallOnCombatEnd();
+13 -22
View File
@@ -279,29 +279,8 @@ static SDL_Scancode get_scancode_from_key(int key)
return kDiks[key & 0xFF];
}
/// Translates SDL scancode into DIK key constant, used by sfall.
static int get_key_from_scancode(SDL_Scancode scanCode)
{
if (kScanCodeToDik.empty()) {
for (int dik = DIK_MAP_COUNT - 1; dik >= 1; --dik) {
if (kDiks[dik] == SDL_SCANCODE_UNKNOWN) continue;
kScanCodeToDik[kDiks[dik]] = dik;
}
}
auto dikIt = kScanCodeToDik.find(scanCode);
if (dikIt == kScanCodeToDik.end()) {
return SDL_SCANCODE_UNKNOWN;
}
return dikIt->second;
}
bool sfall_kb_is_key_pressed(int key)
{
// todo: sfall uses this condition to check for VK key instead of DIK:
/* if ((key & 0x80000000) > 0) { // special flag to check by VK code directly
return GetAsyncKeyState(key & 0xFFFF) & 0x8000;
}*/
SDL_Scancode scancode = get_scancode_from_key(key);
if (scancode == SDL_SCANCODE_UNKNOWN) {
return false;
@@ -332,10 +311,22 @@ int sfall_kb_handle_key_pressed(int sdlScanCode, bool pressed)
{
if (!gGameLoaded) return SDL_SCANCODE_UNKNOWN;
if (kScanCodeToDik.empty()) {
for (int dik = 1; dik < DIK_MAP_COUNT; dik++) {
kScanCodeToDik[kDiks[dik]] = dik;
}
}
auto scanCode = static_cast<SDL_Scancode>(sdlScanCode);
auto dikIt = kScanCodeToDik.find(scanCode);
if (dikIt == kScanCodeToDik.end()) {
return SDL_SCANCODE_UNKNOWN;
}
int dik = dikIt->second;
ScriptHookCall hookCall(HOOK_KEYPRESS, 1);
hookCall
.addArg(pressed ? 1 : 0)
.addArg(get_key_from_scancode(static_cast<SDL_Scancode>(sdlScanCode)))
.addArg(dik)
.addArg(0) // TODO: sfall uses VK_ codes here; not sure any mod actually used it. If so, maybe it is better to use Key values from kb.h?
.call();
+11 -14
View File
@@ -244,16 +244,14 @@ void mf_get_sfall_arg_at(Program* program, int args)
{
const int argNum = programStackPopInteger(program);
ProgramValue result(0);
const auto hookCall = hookOpcodeGetCurrentCall(currentMetarule()->name);
if (hookCall != nullptr) {
if (argNum >= 0 && argNum < hookCall->numArgs()) {
result = hookCall->getArgAt(argNum);
} else {
programPrintError("%s: argNum %d out of range [0, %d]", currentMetarule()->name, argNum, hookCall->numArgs() - 1);
}
if (hookCall == nullptr) return;
if (argNum < 0 || argNum >= hookCall->numArgs()) {
programPrintError("%s: argNum %d out of range [0, %d]", currentMetarule()->name, argNum, hookCall->numArgs() - 1);
return;
}
programStackPushValue(program, result);
programStackPushValue(program, hookCall->getArgAt(argNum));
}
void mf_get_object_data(Program* program, int args)
@@ -696,24 +694,23 @@ void sfall_metarule(Program* program, int args)
programStackPushValue(program, values[index]);
}
currentMetaruleIndex = -1;
int metaruleIndex = -1;
for (int index = 0; index < kMetarulesMax; index++) {
if (strcmp(kMetarules[index].name, metarule) == 0) {
currentMetaruleIndex = index;
metaruleIndex = index;
break;
}
}
if (currentMetaruleIndex == -1) {
if (metaruleIndex == -1) {
programFatalError("op_sfall_func: '%s' is not implemented", metarule);
}
const auto& metaruleInfo = kMetarules[currentMetaruleIndex];
if (args < metaruleInfo.minArgs || args > metaruleInfo.maxArgs) {
if (args < kMetarules[metaruleIndex].minArgs || args > kMetarules[metaruleIndex].maxArgs) {
programFatalError("op_sfall_func: '%s': invalid number of args", metarule);
}
metaruleInfo.handler(program, args);
kMetarules[metaruleIndex].handler(program, args);
}
} // namespace fallout
+3 -4
View File
@@ -1229,7 +1229,6 @@ static void op_register_hook(Program* program)
int startProcIndex = programFindProcedure(program, gScriptProcNames[SCRIPT_PROC_START]);
if (startProcIndex == -1) {
programPrintError("%s: 'start' procedure not found", opcodeName);
return;
}
if (!scriptHooksRegister(program, static_cast<HookType>(hookId), startProcIndex)) {
programPrintError("%s(%d, %d): failed", opcodeName, hookId, startProcIndex);
@@ -1246,7 +1245,7 @@ static void op_register_hook_proc(Program* program)
programPrintError("%s: invalid hook ID: %d", opcodeName, hookId);
return;
}
if (procedureIndex < 0 || procedureIndex >= program->procedureCount()) {
if (procedureIndex < 0 || procedureIndex > program->procedureCount()) {
programPrintError("%s: procedure index %d is out of range [0; %d]", opcodeName, procedureIndex, program->procedureCount());
return;
}
@@ -1322,11 +1321,11 @@ static void op_set_sfall_return(Program* program)
const auto hookCall = hookOpcodeGetCurrentCall(opcodeName);
if (hookCall == nullptr) return;
if (hookCall->numScriptReturnValues() >= hookCall->maxReturnValues()) {
if (hookCall->numReturnValues() >= hookCall->maxReturnValues()) {
programPrintError("%s: trying to add next return value while only %d is expected", opcodeName, hookCall->maxReturnValues());
return;
}
hookCall->addReturnValueFromScript(value);
hookCall->addReturnValue(value);
}
// Note: opcodes should pop arguments off the stack in reverse order
+16 -36
View File
@@ -39,7 +39,7 @@ ScriptHookCall::ScriptHookCall(HookType hookType, int maxReturnValues)
ScriptHookCall& ScriptHookCall::addArg(ProgramValue value)
{
assert(_numArgs < HOOKS_MAX_ARGUMENTS);
assert(_numArgs < HOOKS_MAX_ARGUMENTS - 1);
_args[_numArgs++] = value;
return *this;
}
@@ -51,14 +51,16 @@ ScriptHookCall& ScriptHookCall::setArgAt(int idx, ProgramValue value)
return *this;
}
void ScriptHookCall::addReturnValueFromScript(ProgramValue value)
void ScriptHookCall::addReturnValue(ProgramValue value)
{
assert(_scriptRetVals < HOOKS_MAX_RETURN_VALUES);
_retVals[_scriptRetVals++] = value;
assert(_numRetVals < HOOKS_MAX_RETURN_VALUES - 1);
_retVals[_numRetVals++] = value;
}
if (_scriptRetVals > _numRetVals) {
_numRetVals = _scriptRetVals;
}
void ScriptHookCall::setReturnValueAt(int idx, ProgramValue value)
{
assert(idx >= 0 && idx < _numRetVals);
_retVals[idx] = value;
}
ProgramValue ScriptHookCall::getArgAt(int idx) const
@@ -76,7 +78,6 @@ ProgramValue ScriptHookCall::getReturnValueAt(int idx) const
int ScriptHookCall::numArgs() const { return _numArgs; }
int ScriptHookCall::maxReturnValues() const { return _maxRetVals; }
int ScriptHookCall::numReturnValues() const { return _numRetVals; }
int ScriptHookCall::numScriptReturnValues() const { return _scriptRetVals; }
void ScriptHookCall::call()
{
@@ -87,11 +88,7 @@ void ScriptHookCall::call()
_callStack.push_back(this);
const auto& hooksOfType = scriptHooks[_hookType];
// Iterate in reverse order. In case current hook is unregistered inside the call, we can just continue iteration.
for (int i = hooksOfType.size() - 1; i >= 0; --i) {
const auto& hook = hooksOfType[i];
_scriptArgs = 0;
_scriptRetVals = 0;
for (const auto& hook : hooksOfType) {
programExecuteProcedure(hook.program, hook.procedureIndex);
}
@@ -101,23 +98,22 @@ void ScriptHookCall::call()
ProgramValue ScriptHookCall::getNextArgFromScript()
{
if (_scriptArgs >= _numArgs) {
if (_scriptNextArg >= _numArgs) {
return { 0 };
}
return _args[_scriptArgs++];
return _args[_scriptNextArg++];
}
bool scriptHooksRegister(Program* program, const HookType hookType, const int procedureIndex)
{
assert(program != nullptr && hookType >= 0 && hookType < HOOK_COUNT && procedureIndex >= 0 && procedureIndex < program->procedureCount());
auto& hooksByType = scriptHooks[hookType];
const bool isUnregisterRequest = procedureIndex == 0;
// Check for existing registration.
for (auto it = hooksByType.begin(); it != hooksByType.end(); ++it) {
for (auto it = scriptHooks[hookType].begin(); it != scriptHooks[hookType].end(); ++it) {
if (it->program == program) {
if (isUnregisterRequest) {
hooksByType.erase(it);
scriptHooks[hookType].erase(it);
return true; // unregister success
}
// Skip: no more than 1 procedure in a script for a given hook type.
@@ -128,8 +124,7 @@ bool scriptHooksRegister(Program* program, const HookType hookType, const int pr
return false; // unregister fail
}
// Put new hooks to beginning, because we want to iterate them in reverse.
hooksByType.emplace(hooksByType.begin(), ScriptHook { program, procedureIndex });
scriptHooks[hookType].emplace_back(ScriptHook { program, procedureIndex });
return true; // register success
}
@@ -155,18 +150,6 @@ void scriptHooksExit()
scriptHooksClear();
}
/*
Runs once every time when the game mode was changed, like opening/closing the inventory, character screen, pipboy, etc.
int arg0 - event type: 1 - when the player exits the game, 0 - otherwise
int arg1 - the previous game mode
*/
void scriptHooks_GameModeChange(int exit, int previousGameMode)
{
ScriptHookCall hook(HOOK_GAMEMODECHANGE, 0);
hook.addArg(exit).addArg(previousGameMode).call();
}
/*
Runs when Fallout is calculating the chances of an attack striking a target.
Runs after the hit chance is fully calculated normally, including applying the 95% cap.
@@ -197,10 +180,7 @@ int scriptHooks_ToHit(Object* attacker, Object* defender, int tile, int hitMode,
hook.call();
if (hook.numReturnValues() <= 0) return hitChance;
hitChance = hook.getReturnValueAt(0).asInt();
return std::clamp(hitChance, -99, 999);
return hook.numReturnValues() > 0 ? hook.getReturnValueAt(0).asInt() : hitChance;
}
/*
+3 -13
View File
@@ -174,26 +174,18 @@ public:
ScriptHookCall& operator=(const ScriptHookCall& other) = delete;
ScriptHookCall& operator=(ScriptHookCall&& other) = delete;
// Adds an argument (should be called from engine code).
ScriptHookCall& addArg(ProgramValue value);
// Sets an argument value at given index.
ScriptHookCall& setArgAt(int idx, ProgramValue value);
// Adds return value from script.
// numReturnValues will only increase if current script called this more times than the last one.
void addReturnValueFromScript(ProgramValue value);
void addReturnValue(ProgramValue value);
void setReturnValueAt(int idx, ProgramValue value);
void call();
ProgramValue getNextArgFromScript();
// Number of arguments supplied from the engine.
int numArgs() const;
// Maximum expected number of return values by the engine.
int maxReturnValues() const;
// Number of actually supplied values from all scripts.
int numReturnValues() const;
// Number of supplied values from the last script.
int numScriptReturnValues() const;
ProgramValue getArgAt(int idx) const;
ProgramValue getReturnValueAt(int idx) const;
@@ -209,8 +201,7 @@ private:
ProgramValue _retVals[HOOKS_MAX_RETURN_VALUES] = {};
int _numRetVals = 0;
int _scriptArgs = 0;
int _scriptRetVals = 0;
int _scriptNextArg = 0;
};
bool scriptHooksRegister(Program* program, HookType hookType, int procedureIndex);
@@ -219,7 +210,6 @@ bool scriptHooksInit();
void scriptHooksReset();
void scriptHooksExit();
void scriptHooks_GameModeChange(int exit, int previousGameMode);
int scriptHooks_ToHit(Object* attacker, Object* defender, int tile, int hitMode, int hitLocation, int hitChance, int hitChanceUncapped, bool useDistance);
int scriptHooks_UseObj(Object* user, Object* objUsed);
int scriptHooks_UseObjOn(Object* user, Object* target, Object* objUsed);