Compare commits

..
Author SHA1 Message Date
google-labs-jules[bot] 6c42bb2630 Here's the rewritten message:
Add opcodes for getting world map position

I've implemented `get_world_map_x_pos` (0x8173) and `get_world_map_y_pos` (0x8174) sfall opcodes. These functions allow your scripts to retrieve the player's current X and Y coordinates on the world map.

The implementation mirrors the existing `set_world_map_pos` functionality by calling the underlying `wmGetPartyWorldPos` engine function.

I've added a new test script `sfall_testing/gl_test_worldmap.ssl` which includes cases for:
- Basic retrieval of X and Y coordinates.
- Retrieval after multiple position sets.
- Retrieval of zero coordinates.
- Retrieval of negative coordinates (assuming engine support).

Note: I wasn't able to automatically register the new test script in CMakeLists.txt as the specified variable was not found. I also encountered an issue that prevented me from verifying the test execution.
2025-06-09 06:26:28 +00:00
15 changed files with 125 additions and 406 deletions
+5 -5
View File
@@ -87,7 +87,7 @@ jobs:
uses: actions/cache@v4
with:
path: os/android/app/.cxx
key: android-cmake-v100
key: android-cmake-v2
- name: Setup signing config
if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != ''
@@ -128,7 +128,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: ios-cmake-v100
key: ios-cmake-v5
- name: Configure
run: |
@@ -183,7 +183,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: linux-${{ matrix.arch }}-cmake-v100
key: linux-${{ matrix.arch }}-cmake-v3
- name: Configure
run: |
@@ -288,7 +288,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: macos-cmake-v100
key: macos-cmake-v6
- name: Configure
run: |
@@ -337,7 +337,7 @@ jobs:
uses: actions/cache@v4
with:
path: out
key: windows-${{ matrix.arch }}-cmake-v100
key: windows-${{ matrix.arch }}-cmake-v3
- name: Configure
shell: cmd
-69
View File
@@ -1,69 +0,0 @@
#include "lib.arrays.h" // For arrays_equal, len_array, etc.
#include "test_utils.h" // For assertEquals, VALTYPE_STR, report_test_results
// To test: copy test.ini into the game folder before running
procedure ini_test_suite begin
variable result_array, expected_array, key_type, value_type, count, key_val_pair;
display_msg("--- Testing ini functions");
// Test Case 1: Valid file, valid section
result_array := get_ini_section("test.ini", "ValidSection");
expected_array := {"Key1": "Value1", "Key2": "2"};
call assertEquals("TC1 Size", len_array(result_array), 2);
call assertEquals("TC1 Content", arrays_equal(result_array, expected_array), true);
count := 0;
foreach key_val_pair in result_array begin
count += 1;
call assertEquals("TC1 Key Type " + count, typeof(key_val_pair[0]), VALTYPE_STR);
call assertEquals("TC1 Value Type " + count, typeof(key_val_pair[1]), VALTYPE_STR);
end
// basic fetchers
call assertEquals("TC2 Specific Key1 Value", result_array["Key1"], "Value1");
call assertEquals("TC2 get_ini_setting string", get_ini_setting("test.ini|ValidSection|Key1"), 0);
call assertEquals("TC2 get_ini_string string", get_ini_string("test.ini|ValidSection|Key1"), "Value1");
call assertEquals("TC2 get_ini_setting int", get_ini_setting("test.ini|ValidSection|Key2"), 2);
call assertEquals("TC2 get_ini_string int", get_ini_string("test.ini|ValidSection|Key2"), "2");
// INI file not found
result_array := get_ini_section("nonexistent.ini", "AnySection");
call assertEquals("TC3 Size", len_array(result_array), 0);
call assertEquals("TC3 Is Map", array_key(result_array, -1), 1); // Should still be an associative array
call assertEquals("TC3 get_ini_setting", get_ini_setting("notexist.ini|ValidSection|Key2"), 0);
call assertEquals("TC3 get_ini_string", get_ini_string("notexist.ini|ValidSection|Key2"), "");
// Valid file, section not found
result_array := get_ini_section("test.ini", "NonExistentSection");
call assertEquals("TC4 Size", len_array(result_array), 0);
call assertEquals("TC4 Is Map", array_key(result_array, -1), 1);
call assertEquals("TC4 get_ini_setting", get_ini_setting("test.ini|NonExistentSection|Key2"), 0);
call assertEquals("TC4 get_ini_string", get_ini_string("test.ini|NonExistentSection|Key2"), "");
// Valid file, empty section
result_array := get_ini_section("test.ini", "EmptySection");
call assertEquals("TC5 Size", len_array(result_array), 0);
call assertEquals("TC5 Is Map", array_key(result_array, -1), 1);
call assertEquals("TC5 get_ini_setting", get_ini_setting("test.ini|EmptySection|Key2"), 0);
call assertEquals("TC5 get_ini_string", get_ini_string("test.ini|EmptySection|Key2"), "");
// set ini setting
set_ini_setting("test_set.ini|ValidSection|Key3", "OldValue");
call assertEquals("TC6 set_ini_setting 1", get_ini_string("test_set.ini|ValidSection|Key3"), "OldValue");
set_ini_setting("test_set.ini|ValidSection|Key3", "NewValue");
call assertEquals("TC6 set_ini_setting 2", get_ini_string("test_set.ini|ValidSection|Key3"), "NewValue");
set_ini_setting("test_set.ini|ValidSection|Key3", 2);
call assertEquals("TC6 set_ini_setting int", get_ini_setting("test_set.ini|ValidSection|Key3"), 2);
// get_init_sections
result_array := get_ini_sections("test.ini");
call assertEquals("TC7 get_ini_sections size", len_array(result_array), 3);
call assertTrue("TC7 get_ini_sections", scan_array(result_array, "TestMisc") >= 0);
call report_test_results("ini");
end
procedure start begin
call ini_test_suite();
end
-16
View File
@@ -1,16 +0,0 @@
[ValidSection]
Key1=Value1
Key2=2
[SectionWithSpaces]
LeadingSpaceKey = LeadingSpaceValue
TrailingSpaceKey = TrailingSpaceValue
Both Sides Space Key = Both Sides Space Value
SimpleKey=SimpleValue
[EmptySection]
; This is TestMisc, it's not empty but won't be directly tested for content here.
[TestMisc]
Alpha=Beta
Gamma=Delta
-11
View File
@@ -7,17 +7,6 @@ variable test_suite_errors := 0;
variable test_suite_verbose := false;
variable test_suite_assertions := 0;
procedure assertTrue(variable desc, variable a) begin
test_suite_assertions++;
if (not a) then begin
display_msg("Assertion failed \""+desc+"\": is not true");
test_suite_errors ++;
end else if (test_suite_verbose) then begin
display_msg("Assert \""+desc+"\" ok");
end
end
procedure assertEquals(variable desc, variable a, variable b) begin
test_suite_assertions++;
+3 -18
View File
@@ -6,7 +6,6 @@
#include "color.h"
#include "db.h"
#include "memory_manager.h"
#include "settings.h"
// The maximum number of interface fonts.
#define INTERFACE_FONT_MAX (16)
@@ -120,25 +119,11 @@ static int interfaceFontLoad(int font_index)
InterfaceFontDescriptor* fontDescriptor = &(gInterfaceFontDescriptors[font_index]);
char path[56];
File* stream = nullptr;
snprintf(path, sizeof(path), "font%d.aaf", font_index);
// Try set language path first
snprintf(path, sizeof(path), "text/%s/font%d.aaf", settings.system.language.c_str(), font_index);
stream = fileOpen(path, "rb");
// Fallback to English if needed
if (stream == nullptr && compat_stricmp(settings.system.language.c_str(), ENGLISH) != 0) {
snprintf(path, sizeof(path), "text/%s/font%d.aaf", ENGLISH, font_index);
stream = fileOpen(path, "rb");
}
// Fallback to original path
File* stream = fileOpen(path, "rb");
if (stream == nullptr) {
snprintf(path, sizeof(path), "font%d.aaf", font_index);
stream = fileOpen(path, "rb");
if (stream == nullptr) {
return -1;
}
return -1;
}
int fileSize = fileGetSize(stream);
+2 -2
View File
@@ -3241,12 +3241,12 @@ int _gdialog_barter_create_win()
backgroundFrmImage.unlock();
// TRADE
_gdialog_buttons[0] = buttonCreate(gGameDialogWindow, 40, 163, 14, 14, -1, -1, -1, KEY_LOWERCASE_M, _redButtonNormalFrmImage.getData(), _redButtonPressedFrmImage.getData(), nullptr, BUTTON_FLAG_TRANSPARENT);
_gdialog_buttons[0] = buttonCreate(gGameDialogWindow, 40, 162, 14, 14, -1, -1, -1, KEY_LOWERCASE_M, _redButtonNormalFrmImage.getData(), _redButtonPressedFrmImage.getData(), nullptr, BUTTON_FLAG_TRANSPARENT);
if (_gdialog_buttons[0] != -1) {
buttonSetCallbacks(_gdialog_buttons[0], _gsound_med_butt_press, _gsound_med_butt_release);
// TALK
_gdialog_buttons[1] = buttonCreate(gGameDialogWindow, 583, 162, 14, 14, -1, -1, -1, KEY_LOWERCASE_T, _redButtonNormalFrmImage.getData(), _redButtonPressedFrmImage.getData(), nullptr, BUTTON_FLAG_TRANSPARENT);
_gdialog_buttons[1] = buttonCreate(gGameDialogWindow, 583, 161, 14, 14, -1, -1, -1, KEY_LOWERCASE_T, _redButtonNormalFrmImage.getData(), _redButtonPressedFrmImage.getData(), nullptr, BUTTON_FLAG_TRANSPARENT);
if (_gdialog_buttons[1] != -1) {
buttonSetCallbacks(_gdialog_buttons[1], _gsound_med_butt_press, _gsound_med_butt_release);
-17
View File
@@ -3346,21 +3346,4 @@ int ProgramValue::asInt() const
}
}
// CE
ProgramValue programMakeString(Program* program, const char* str)
{
ProgramValue valuePv;
valuePv.opcode = VALUE_TYPE_DYNAMIC_STRING;
valuePv.integerValue = programPushString(program, str);
return valuePv;
}
ProgramValue programMakeInt(Program* program, int val)
{
ProgramValue valuePv;
valuePv.opcode = VALUE_TYPE_INT;
valuePv.integerValue = val;
return valuePv;
}
} // namespace fallout
-4
View File
@@ -241,10 +241,6 @@ ProgramValue programReturnStackPopValue(Program* program);
int programReturnStackPopInteger(Program* program);
void* programReturnStackPopPointer(Program* program);
// CE
ProgramValue programMakeString(Program* program, const char* str);
ProgramValue programMakeInt(Program* program, int val);
} // namespace fallout
#endif /* INTERPRETER_H */
+8 -25
View File
@@ -2078,51 +2078,34 @@ static void _display_body(int fid, int inventoryWindowType)
unsigned char* windowBuffer = windowGetBuffer(gInventoryWindow);
int windowPitch = windowGetWidth(gInventoryWindow);
FrmImage backgroundFrmImage;
int Fid = 114;
int sourceXOffset = 0;
if (index == 1) {
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) {
rect.left = 426; // loot right cha window (or container)
rect.left = 426;
rect.top = 39;
Fid = 114;
sourceXOffset = 538;
} else {
rect.left = 297; // inventory data window? ?not used?
rect.left = 297;
rect.top = 37;
Fid = 48;
sourceXOffset = 229;
}
} else {
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) {
rect.left = 48; // loot left cha window
rect.left = 48;
rect.top = 39;
Fid = 114;
sourceXOffset = 0;
} else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_USE_ITEM_ON) {
rect.left = 176; // Use item cha window
rect.top = 37;
Fid = 113;
sourceXOffset = 292;
} else {
rect.left = 176; // inventory cha window (same as use on)
rect.left = 176;
rect.top = 37;
Fid = 48;
sourceXOffset = 0;
}
}
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, Fid, 0, 0, 0);
rect.right = rect.left + INVENTORY_BODY_VIEW_WIDTH - 1;
rect.bottom = rect.top + INVENTORY_BODY_VIEW_HEIGHT - 1;
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0);
if (backgroundFrmImage.lock(backgroundFid)) {
blitBufferToBuffer(backgroundFrmImage.getData() + backgroundFrmImage.getWidth() * rect.top + rect.left + sourceXOffset,
blitBufferToBuffer(backgroundFrmImage.getData() + INVENTORY_LOOT_WINDOW_WIDTH * rect.top + rect.left,
INVENTORY_BODY_VIEW_WIDTH,
INVENTORY_BODY_VIEW_HEIGHT,
backgroundFrmImage.getWidth(),
INVENTORY_LOOT_WINDOW_WIDTH,
windowBuffer + windowPitch * rect.top + rect.left,
windowPitch);
}
+17 -190
View File
@@ -1,14 +1,10 @@
#include "sfall_ini.h"
#include <algorithm>
#include <cstdio> // for snprintf
#include <cstring> // for strncpy, strlen
#include <cstring>
#include "config.h"
#include "debug.h"
#include "interpreter.h"
#include "platform_compat.h"
#include "sfall_arrays.h"
namespace fallout {
@@ -72,38 +68,6 @@ static bool is_system_file_name(const char* fileName)
return false;
}
// Loads an INI file specified by 'ini_file_name' (e.g., "myconfig.ini" or "ddraw.ini")
// into the provided 'config_out' object.
// The 'config_out' object must be initialized by the caller (using configInit).
// The caller is also responsible for freeing 'config_out' (using configFree).
// Returns true if the file was successfully found and read, false otherwise.
static bool sfall_load_named_ini_file(const char* ini_file_name, Config* config_out)
{
if (ini_file_name == nullptr || config_out == nullptr) {
return false;
}
char path[COMPAT_MAX_PATH];
bool loaded = false;
if (basePath[0] != '\0' && !is_system_file_name(ini_file_name)) {
// Attempt to load requested file in base directory.
snprintf(path, sizeof(path), "%s\\%s", basePath, ini_file_name);
loaded = configRead(config_out, path, false);
}
if (!loaded) {
// There was no base path set, requested file is a system config, or
// non-system config file was not found the base path - attempt to load
// from current working directory.
strncpy(path, ini_file_name, sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
loaded = configRead(config_out, path, false);
}
return loaded;
}
void sfall_ini_set_base_path(const char* path)
{
if (path != nullptr) {
@@ -147,7 +111,22 @@ bool sfall_ini_get_string(const char* triplet, char* value, size_t size)
return false;
}
bool loaded = sfall_load_named_ini_file(fileName, &config);
char path[COMPAT_MAX_PATH];
bool loaded = false;
if (basePath[0] != '\0' && !is_system_file_name(fileName)) {
// Attempt to load requested file in base directory.
snprintf(path, sizeof(path), "%s\\%s", basePath, fileName);
loaded = configRead(&config, path, false);
}
if (!loaded) {
// There was no base path set, requested file is a system config, or
// non-system config file was not found the base path - attempt to load
// from current working directory.
strcpy(path, fileName);
loaded = configRead(&config, path, false);
}
// NOTE: Sfall's `GetIniSetting` returns error code (-1) only when it cannot
// parse triplet. Otherwise the default for string settings is empty string.
@@ -215,156 +194,4 @@ bool sfall_ini_set_string(const char* triplet, const char* value)
return saved;
}
static const ConfigSection* sfall_find_section_in_config(Config* config, const char* section_name)
{
if (config == nullptr || section_name == nullptr) {
return nullptr;
}
int sectionIndex = dictionaryGetIndexByKey(config, section_name);
if (sectionIndex == -1) {
return nullptr;
}
DictionaryEntry* sectionEntry = &(config->entries[sectionIndex]);
return static_cast<const ConfigSection*>(sectionEntry->value);
}
// set_ini_setting
void mf_set_ini_setting(Program* program, int args)
{
const char* triplet = programStackPopString(program);
ProgramValue value = programStackPopValue(program);
if (value.isString()) {
const char* stringValue = programGetString(program, value.opcode, value.integerValue);
if (!sfall_ini_set_string(triplet, stringValue)) {
debugPrint("set_ini_setting: unable to write '%s' to '%s'",
stringValue,
triplet);
}
} else {
int integerValue = value.asInt();
if (!sfall_ini_set_int(triplet, integerValue)) {
debugPrint("set_ini_setting: unable to write '%d' to '%s'",
integerValue,
triplet);
}
}
programStackPushInteger(program, -1);
}
// get_ini_section
void mf_get_ini_section(Program* program, int args)
{
// Arguments: file_path (string), section_name (string)
const char* filePath = programStackPopString(program);
const char* sectionName = programStackPopString(program);
ArrayId arrayId = CreateTempArray(-1, 0);
if (filePath == nullptr || sectionName == nullptr) {
programStackPushInteger(program, arrayId);
return;
}
Config iniConfig;
if (!configInit(&iniConfig)) {
debugPrint("mf_get_ini_section: Failed to initialize Config structure.");
programStackPushInteger(program, arrayId);
return;
}
if (sfall_load_named_ini_file(filePath, &iniConfig)) {
const ConfigSection* section = sfall_find_section_in_config(&iniConfig, sectionName);
if (section != nullptr) {
for (int i = 0; i < section->entriesLength; ++i) {
DictionaryEntry* entry = &(section->entries[i]);
const char* key = entry->key;
const char* value = *(static_cast<char**>(entry->value));
if (key != nullptr && value != nullptr) {
SetArray(arrayId, programMakeString(program, key), programMakeString(program, value), false, program);
}
}
}
}
configFree(&iniConfig);
programStackPushInteger(program, arrayId);
}
// get_ini_sections
void mf_get_ini_sections(Program* program, int args)
{
// Arguments: file_path (string)
const char* filePath = programStackPopString(program);
ArrayId arrayId = -1;
if (filePath == nullptr) {
programStackPushInteger(program, arrayId);
return;
}
Config iniConfig;
if (!configInit(&iniConfig)) {
debugPrint("mf_get_ini_sections: Failed to initialize Config structure.");
programStackPushInteger(program, arrayId);
return;
}
// note: seems to load sections in random order
if (sfall_load_named_ini_file(filePath, &iniConfig)) {
if (iniConfig.entriesLength > 0) {
arrayId = CreateTempArray(iniConfig.entriesLength, 0);
for (int i = 0; i < iniConfig.entriesLength; ++i) {
DictionaryEntry* entry = &(iniConfig.entries[i]);
const char* sectionName = entry->key;
if (sectionName != nullptr) {
SetArray(arrayId, programMakeInt(program, i), programMakeString(program, sectionName), false, program);
}
}
}
}
configFree(&iniConfig);
if (arrayId == -1) {
arrayId = CreateTempArray(0, 0);
}
programStackPushInteger(program, arrayId);
}
// get_ini_setting
void op_get_ini_setting(Program* program)
{
const char* string = programStackPopString(program);
int value;
if (sfall_ini_get_int(string, &value)) {
programStackPushInteger(program, value);
} else {
programStackPushInteger(program, -1);
}
}
// get_ini_string
void op_get_ini_string(Program* program)
{
const char* string = programStackPopString(program);
char value[256];
if (sfall_ini_get_string(string, value, sizeof(value))) {
programStackPushString(program, value);
} else {
programStackPushInteger(program, -1);
}
}
} // namespace fallout
-10
View File
@@ -1,9 +1,6 @@
#ifndef FALLOUT_SFALL_INI_H_
#define FALLOUT_SFALL_INI_H_
#include "config.h"
#include "dictionary.h"
#include "interpreter.h"
#include <cstddef>
namespace fallout {
@@ -23,13 +20,6 @@ bool sfall_ini_set_int(const char* triplet, int value);
/// Writes string key identified by "fileName|section|key" triplet.
bool sfall_ini_set_string(const char* triplet, const char* value);
// metarule and opcode implementations
void mf_set_ini_setting(Program* program, int args);
void mf_get_ini_section(Program* program, int args);
void mf_get_ini_sections(Program* program, int args);
void op_get_ini_setting(Program* program);
void op_get_ini_string(Program* program);
} // namespace fallout
#endif /* FALLOUT_SFALL_INI_H_ */
+33 -11
View File
@@ -7,7 +7,6 @@
#include <string>
#include "combat.h"
#include "config.h" // For Config, configInit, configFree
#include "debug.h"
#include "game.h"
#include "game_dialog.h"
@@ -16,7 +15,6 @@
#include "inventory.h"
#include "object.h"
#include "platform_compat.h"
#include "sfall_arrays.h" // For CreateTempArray, SetArray
#include "sfall_ini.h"
#include "text_font.h"
#include "tile.h"
@@ -49,6 +47,7 @@ static void mf_metarule_exist(Program* program, int args);
static void mf_outlined_object(Program* program, int args);
static void mf_set_cursor_mode(Program* program, int args);
static void mf_set_flags(Program* program, int args);
static void mf_set_ini_setting(Program* program, int args);
static void mf_set_outline(Program* program, int args);
static void mf_show_window(Program* program, int args);
static void mf_tile_refresh_display(Program* program, int args);
@@ -59,7 +58,6 @@ static void mf_string_format(Program* program, int args);
static void mf_floor2(Program* program, int args);
// ref. https://github.com/sfall-team/sfall/blob/42556141127895c27476cd5242a73739cbb0fade/sfall/Modules/Scripting/Handlers/Metarule.cpp#L72
// Note: metarules should pop arguments off the stack in natural order
constexpr MetaruleInfo kMetarules[] = {
// {"add_extra_msg_file", mf_add_extra_msg_file, 1, 2, -1, {ARG_STRING, ARG_INT}},
// {"add_iface_tag", mf_add_iface_tag, 0, 0},
@@ -71,7 +69,7 @@ constexpr MetaruleInfo kMetarules[] = {
{ "car_gas_amount", mf_car_gas_amount, 0, 0 },
{ "combat_data", mf_combat_data, 0, 0 },
// {"create_win", mf_create_win, 5, 6, -1, {ARG_STRING, ARG_INT, ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{ "critter_inven_obj2", mf_critter_inven_obj2, 2, 2 }, // XXX: likely parameter order mismatch
{ "critter_inven_obj2", mf_critter_inven_obj2, 2, 2 },
// {"dialog_message", mf_dialog_message, 1, 1, -1, {ARG_STRING}},
{ "dialog_obj", mf_dialog_obj, 0, 0 },
// {"display_stats", mf_display_stats, 0, 0}, // refresh
@@ -85,13 +83,13 @@ constexpr MetaruleInfo kMetarules[] = {
{ "get_cursor_mode", mf_get_cursor_mode, 0, 0 },
{ "get_flags", mf_get_flags, 1, 1 },
// {"get_ini_config", mf_get_ini_config, 2, 2, 0, {ARG_STRING, ARG_INT}},
{ "get_ini_section", mf_get_ini_section, 2, 2 },
{ "get_ini_sections", mf_get_ini_sections, 1, 1 },
// {"get_ini_section", mf_get_ini_section, 2, 2, -1, {ARG_STRING, ARG_STRING}},
// {"get_ini_sections", mf_get_ini_sections, 1, 1, -1, {ARG_STRING}},
// {"get_inven_ap_cost", mf_get_inven_ap_cost, 0, 0},
// {"get_map_enter_position", mf_get_map_enter_position, 0, 0},
// {"get_metarule_table", mf_get_metarule_table, 0, 0},
// {"get_object_ai_data", mf_get_object_ai_data, 2, 2, -1, {ARG_OBJECT, ARG_INT}},
{ "get_object_data", mf_get_object_data, 2, 2 }, // XXX: likely parameter order mismatch
{ "get_object_data", mf_get_object_data, 2, 2 },
// {"get_outline", mf_get_outline, 1, 1, 0, {ARG_OBJECT}},
// {"get_sfall_arg_at", mf_get_sfall_arg_at, 1, 1, 0, {ARG_INT}},
// {"get_stat_max", mf_get_stat_max, 1, 2, 0, {ARG_INT, ARG_INT}},
@@ -139,7 +137,7 @@ constexpr MetaruleInfo kMetarules[] = {
{ "set_ini_setting", mf_set_ini_setting, 2, 2 },
// {"set_map_enter_position", mf_set_map_enter_position, 3, 3, -1, {ARG_INT, ARG_INT, ARG_INT}},
// {"set_object_data", mf_set_object_data, 3, 3, -1, {ARG_OBJECT, ARG_INT, ARG_INT}},
{ "set_outline", mf_set_outline, 2, 2 }, // XXX: likely parameter order mismatch
{ "set_outline", mf_set_outline, 2, 2 },
// {"set_quest_failure_value", mf_set_quest_failure_value, 2, 2, -1, {ARG_INT, ARG_INT}},
// {"set_rest_heal_time", mf_set_rest_heal_time, 1, 1, -1, {ARG_INT}},
// {"set_worldmap_heal_time", mf_set_worldmap_heal_time, 1, 1, -1, {ARG_INT}},
@@ -300,6 +298,30 @@ void mf_set_flags(Program* program, int args)
programStackPushInteger(program, -1);
}
void mf_set_ini_setting(Program* program, int args)
{
ProgramValue value = programStackPopValue(program);
const char* triplet = programStackPopString(program);
if (value.isString()) {
const char* stringValue = programGetString(program, value.opcode, value.integerValue);
if (!sfall_ini_set_string(triplet, stringValue)) {
debugPrint("set_ini_setting: unable to write '%s' to '%s'",
stringValue,
triplet);
}
} else {
int integerValue = value.asInt();
if (!sfall_ini_set_int(triplet, integerValue)) {
debugPrint("set_ini_setting: unable to write '%d' to '%s'",
integerValue,
triplet);
}
}
programStackPushInteger(program, -1);
}
void mf_set_outline(Program* program, int args)
{
int outline = programStackPopInteger(program);
@@ -438,7 +460,7 @@ void mf_string_find(Program* program, int args)
const char* found = strstr(str + startPos, substr);
if (found) {
programStackPushInteger(program, static_cast<int>(found - str));
programStackPushInteger(program, found - str);
} else {
programStackPushInteger(program, -1);
}
@@ -480,7 +502,7 @@ void sprintf_lite(Program* program, int args, const char* infoOpcodeName)
formatArgs[index] = programStackPopValue(program);
}
int fmtLen = static_cast<int>(strlen(format));
int fmtLen = strlen(format);
if (fmtLen == 0) {
programStackPushString(program, "");
return;
@@ -536,7 +558,7 @@ void sprintf_lite(Program* program, int args, const char* infoOpcodeName)
if (c == 'S' || c == 'Z') {
c = 's'; // don't allow wide strings
}
if ((c == 's' && !arg.isString()) || // don't allow treating non-string values as string pointers
if (c == 's' && !arg.isString() || // don't allow treating non-string values as string pointers
c == 'n') // don't allow "n" specifier
{
c = 'd';
+47 -4
View File
@@ -207,6 +207,22 @@ static void op_set_world_map_pos(Program* program)
wmSetPartyWorldPos(x, y);
}
// get_world_map_x_pos
static void op_get_world_map_x_pos(Program* program)
{
int x;
wmGetPartyWorldPos(&x, nullptr);
programStackPushInteger(program, x);
}
// get_world_map_y_pos
static void op_get_world_map_y_pos(Program* program)
{
int y;
wmGetPartyWorldPos(nullptr, &y);
programStackPushInteger(program, y);
}
// active_hand
static void op_active_hand(Program* program)
{
@@ -256,6 +272,19 @@ static void op_get_sfall_global_int(Program* program)
programStackPushInteger(program, value);
}
// get_ini_setting
static void op_get_ini_setting(Program* program)
{
const char* string = programStackPopString(program);
int value;
if (sfall_ini_get_int(string, &value)) {
programStackPushInteger(program, value);
} else {
programStackPushInteger(program, -1);
}
}
// get_game_mode
static void op_get_game_mode(Program* program)
{
@@ -290,6 +319,19 @@ static void op_set_bodypart_hit_modifier(Program* program)
combat_set_hit_location_penalty(hit_location, penalty);
}
// get_ini_string
static void op_get_ini_string(Program* program)
{
const char* string = programStackPopString(program);
char value[256];
if (sfall_ini_get_string(string, value, sizeof(value))) {
programStackPushString(program, value);
} else {
programStackPushInteger(program, -1);
}
}
// sqrt
static void op_sqrt(Program* program)
{
@@ -719,7 +761,7 @@ static void op_substr(Program* program)
char buf[5120] = { 0 };
int len = static_cast<int>(strlen(str));
int len = strlen(str);
if (startPos < 0) {
startPos += len; // start from end
@@ -951,7 +993,7 @@ static void op_party_member_list(Program* program)
{
auto includeHidden = programStackPopInteger(program);
auto objects = get_all_party_members_objects(includeHidden);
auto arrayId = CreateTempArray(static_cast<int>(objects.size()), SFALL_ARRAYFLAG_RESERVED);
auto arrayId = CreateTempArray(objects.size(), SFALL_ARRAYFLAG_RESERVED);
for (int i = 0; i < LenArray(arrayId); i++) {
SetArray(arrayId, ProgramValue { i }, ProgramValue { objects[i] }, false, program);
}
@@ -975,7 +1017,7 @@ static void op_type_of(Program* program)
static void op_round(Program* program)
{
float floatValue = programStackPopValue(program).asFloat();
programStackPushInteger(program, static_cast<int>(lroundf(floatValue)));
programStackPushInteger(program, lroundf(floatValue));
}
enum BlockType {
@@ -1135,7 +1177,6 @@ static void op_charcode(Program* program)
}
}
// Note: opcodes should pop arguments off the stack in reverse order
void sfallOpcodesInit()
{
// ref. https://github.com/sfall-team/sfall/blob/71ecec3d405bd5e945f157954618b169e60068fe/artifacts/scripting/sfall%20opcode%20list.txt#L145
@@ -1244,7 +1285,9 @@ void sfallOpcodesInit()
// 0x8172 - void set_world_map_pos(int x, int y)
interpreterRegisterOpcode(0x8172, op_set_world_map_pos);
// 0x8173 - int get_world_map_x_pos()
interpreterRegisterOpcode(0x8173, op_get_world_map_x_pos);
// 0x8174 - int get_world_map_y_pos()
interpreterRegisterOpcode(0x8174, op_get_world_map_y_pos);
// 0x8175 - void set_dm_model(string name)
// 0x8176 - void set_df_model(string name)
+9 -23
View File
@@ -7,7 +7,6 @@
#include "db.h"
#include "memory.h"
#include "platform_compat.h"
#include "settings.h"
namespace fallout {
@@ -152,33 +151,20 @@ int textFontLoad(int font)
{
int rc = -1;
char path[COMPAT_MAX_PATH];
snprintf(path, sizeof(path), "font%d.fon", font);
// NOTE: Original code is slightly different. It uses deep nesting and
// unwinds everything from the point of failure.
TextFontDescriptor* textFontDescriptor = &(gTextFontDescriptors[font]);
textFontDescriptor->data = nullptr;
textFontDescriptor->glyphs = nullptr;
File* stream = nullptr;
char path[COMPAT_MAX_PATH];
// Try set language path first
snprintf(path, sizeof(path), "text/%s/font%d.fon", settings.system.language.c_str(), font);
stream = fileOpen(path, "rb");
// Fallback to English if needed
if (stream == nullptr && compat_stricmp(settings.system.language.c_str(), ENGLISH) != 0) {
snprintf(path, sizeof(path), "text/%s/font%d.fon", ENGLISH, font);
stream = fileOpen(path, "rb");
}
// fallback to original path
if (stream == nullptr) {
snprintf(path, sizeof(path), "font%d.fon", font);
stream = fileOpen(path, "rb");
if (stream == nullptr) {
goto out;
}
}
File* stream = fileOpen(path, "rb");
int dataSize;
if (stream == nullptr) {
goto out;
}
// NOTE: Original code reads entire descriptor in one go. This does not work
// in x64 because of the two pointers.
+1 -1
View File
@@ -13,7 +13,7 @@ include(FetchContent)
FetchContent_Declare(sdl2
GIT_REPOSITORY "https://github.com/libsdl-org/SDL"
GIT_TAG "release-2.32.8"
GIT_TAG "release-2.26.1"
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
EXCLUDE_FROM_ALL