mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
241 lines
5.8 KiB
C++
241 lines
5.8 KiB
C++
#include "sfall_global_scripts.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "animation.h"
|
|
#include "db.h"
|
|
#include "debug.h"
|
|
#include "input.h"
|
|
#include "platform_compat.h"
|
|
#include "scripts.h"
|
|
#include "sfall_config.h"
|
|
|
|
namespace fallout {
|
|
|
|
struct GlobalScript {
|
|
Program* program = nullptr;
|
|
int procs[SCRIPT_PROC_COUNT] = { 0 };
|
|
int repeat = 0;
|
|
int count = 0;
|
|
int mode = 0;
|
|
bool once = true;
|
|
};
|
|
|
|
struct GlobalScriptsState {
|
|
std::vector<std::string> paths;
|
|
std::vector<GlobalScript> globalScripts;
|
|
};
|
|
|
|
static GlobalScriptsState* state = nullptr;
|
|
|
|
bool sfall_gl_scr_init()
|
|
{
|
|
state = new (std::nothrow) GlobalScriptsState();
|
|
if (state == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
char* paths;
|
|
configGetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_GLOBAL_SCRIPT_PATHS, &paths);
|
|
|
|
char* curr = paths;
|
|
while (curr != nullptr && *curr != '\0') {
|
|
char* end = strchr(curr, ',');
|
|
if (end != nullptr) {
|
|
*end = '\0';
|
|
}
|
|
|
|
char normalizedPattern[COMPAT_MAX_PATH];
|
|
compat_strlcpy(normalizedPattern, curr, sizeof(normalizedPattern));
|
|
compat_path_to_native(normalizedPattern);
|
|
|
|
char drive[COMPAT_MAX_DRIVE];
|
|
char dir[COMPAT_MAX_DIR];
|
|
compat_splitpath(normalizedPattern, drive, dir, nullptr, nullptr);
|
|
|
|
char** files = nullptr;
|
|
int filesLength = fileNameListInit(normalizedPattern, &files);
|
|
debugPrint("GlobalScriptPaths mask %s matched %d script(s)\n", curr, filesLength);
|
|
if (filesLength != 0) {
|
|
for (int index = 0; index < filesLength; index++) {
|
|
char path[COMPAT_MAX_PATH];
|
|
compat_makepath(path, drive, dir, files[index], nullptr);
|
|
state->paths.emplace_back(path);
|
|
}
|
|
}
|
|
|
|
fileNameListFree(&files, 0);
|
|
|
|
if (end != nullptr) {
|
|
*end = ',';
|
|
curr = end + 1;
|
|
} else {
|
|
curr = nullptr;
|
|
}
|
|
}
|
|
|
|
std::sort(state->paths.begin(), state->paths.end());
|
|
debugPrint("GlobalScriptPaths resolved %d script path(s)\n", static_cast<int>(state->paths.size()));
|
|
|
|
return true;
|
|
}
|
|
|
|
void sfall_gl_scr_reset()
|
|
{
|
|
if (state != nullptr) {
|
|
sfall_gl_scr_remove_all();
|
|
}
|
|
}
|
|
|
|
void sfall_gl_scr_exit()
|
|
{
|
|
if (state != nullptr) {
|
|
sfall_gl_scr_remove_all();
|
|
|
|
delete state;
|
|
state = nullptr;
|
|
}
|
|
}
|
|
|
|
void sfall_gl_scr_exec_start_proc()
|
|
{
|
|
int loadedScripts = 0;
|
|
|
|
for (auto& path : state->paths) {
|
|
Program* program = programCreateByPath(path.c_str());
|
|
if (program != nullptr) {
|
|
GlobalScript scr;
|
|
scr.program = program;
|
|
|
|
for (int action = 0; action < SCRIPT_PROC_COUNT; action++) {
|
|
scr.procs[action] = programFindProcedure(program, gScriptProcNames[action]);
|
|
}
|
|
|
|
state->globalScripts.push_back(std::move(scr));
|
|
loadedScripts++;
|
|
|
|
programInterpret(program, -1);
|
|
}
|
|
}
|
|
|
|
debugPrint("Loaded %d global script(s)\n", loadedScripts);
|
|
|
|
tickersAdd(sfall_gl_scr_process_input);
|
|
}
|
|
|
|
void sfall_gl_scr_remove_all()
|
|
{
|
|
tickersRemove(sfall_gl_scr_process_input);
|
|
|
|
for (auto& scr : state->globalScripts) {
|
|
programFree(scr.program);
|
|
}
|
|
|
|
state->globalScripts.clear();
|
|
}
|
|
|
|
void sfall_gl_scr_exec_map_update_scripts(int action)
|
|
{
|
|
for (auto& scr : state->globalScripts) {
|
|
if (scr.mode == 0 || scr.mode == 3) {
|
|
if (scr.procs[action] != -1) {
|
|
programExecuteProcedure(scr.program, scr.procs[action]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sfall_gl_scr_process_simple(int mode1, int mode2)
|
|
{
|
|
for (auto& scr : state->globalScripts) {
|
|
if (scr.repeat != 0 && (scr.mode == mode1 || scr.mode == mode2)) {
|
|
scr.count++;
|
|
if (scr.count >= scr.repeat) {
|
|
programExecuteProcedure(scr.program, scr.procs[SCRIPT_PROC_START]);
|
|
scr.count = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// reg_anim_combat_check flips the global gRegAnimCombatCheck flag in
|
|
// animation.cc. Resetting it only once after the whole loop means one
|
|
// global script can disable the combat check and leak that setting into
|
|
// later global scripts executed in the same tick. Might be better to save/restore.
|
|
animationResetCombatCheck();
|
|
}
|
|
|
|
void sfall_gl_scr_process_main()
|
|
{
|
|
sfall_gl_scr_process_simple(0, 3);
|
|
}
|
|
|
|
void sfall_gl_scr_process_input()
|
|
{
|
|
sfall_gl_scr_process_simple(1, 1);
|
|
}
|
|
|
|
void sfall_gl_scr_process_worldmap()
|
|
{
|
|
sfall_gl_scr_process_simple(2, 3);
|
|
}
|
|
|
|
static GlobalScript* sfall_gl_scr_map_program_to_scr(Program* program)
|
|
{
|
|
auto it = std::find_if(state->globalScripts.begin(),
|
|
state->globalScripts.end(),
|
|
[&program](const GlobalScript& scr) {
|
|
return scr.program == program;
|
|
});
|
|
return it != state->globalScripts.end() ? &(*it) : nullptr;
|
|
}
|
|
|
|
void sfall_gl_scr_set_repeat(Program* program, int frames)
|
|
{
|
|
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
|
if (scr != nullptr) {
|
|
scr->repeat = frames;
|
|
}
|
|
}
|
|
|
|
void sfall_gl_scr_set_type(Program* program, int type)
|
|
{
|
|
if (type < 0 || type > 3) {
|
|
return;
|
|
}
|
|
|
|
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
|
if (scr != nullptr) {
|
|
scr->mode = type;
|
|
}
|
|
}
|
|
|
|
bool sfall_gl_scr_is_loaded(Program* program)
|
|
{
|
|
GlobalScript* scr = sfall_gl_scr_map_program_to_scr(program);
|
|
if (scr != nullptr) {
|
|
if (scr->once) {
|
|
scr->once = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Not a global script.
|
|
return true;
|
|
}
|
|
|
|
void sfall_gl_scr_update(int burstSize)
|
|
{
|
|
for (auto& scr : state->globalScripts) {
|
|
programInterpret(scr.program, burstSize);
|
|
}
|
|
}
|
|
|
|
} // namespace fallout
|