#include "sfall_metarules.h" #include "combat.h" #include "debug.h" #include "game.h" #include "game_dialog.h" #include "game_mouse.h" #include "interface.h" #include "inventory.h" #include "object.h" #include "sfall_ini.h" #include "text_font.h" #include "tile.h" #include "window.h" #include "worldmap.h" namespace fallout { typedef void(MetaruleHandler)(Program* program, int args); // Simplified cousin of `SfallMetarule` from Sfall. typedef struct MetaruleInfo { const char* name; MetaruleHandler* handler; int minArgs; int maxArgs; } MetaruleInfo; static void mf_car_gas_amount(Program* program, int args); static void mf_combat_data(Program* program, int args); static void mf_critter_inven_obj2(Program* program, int args); static void mf_dialog_obj(Program* program, int args); static void mf_get_cursor_mode(Program* program, int args); static void mf_get_flags(Program* program, int args); static void mf_get_object_data(Program* program, int args); static void mf_get_text_width(Program* program, int args); static void mf_intface_redraw(Program* program, int args); static void mf_loot_obj(Program* program, int args); 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); constexpr MetaruleInfo kMetarules[] = { { "car_gas_amount", mf_car_gas_amount, 0, 0 }, { "combat_data", mf_combat_data, 0, 0 }, { "critter_inven_obj2", mf_critter_inven_obj2, 2, 2 }, { "dialog_obj", mf_dialog_obj, 0, 0 }, { "get_cursor_mode", mf_get_cursor_mode, 0, 0 }, { "get_flags", mf_get_flags, 1, 1 }, { "get_object_data", mf_get_object_data, 2, 2 }, { "get_text_width", mf_get_text_width, 1, 1 }, { "intface_redraw", mf_intface_redraw, 0, 1 }, { "loot_obj", mf_loot_obj, 0, 0 }, { "metarule_exist", mf_metarule_exist, 1, 1 }, { "outlined_object", mf_outlined_object, 0, 0 }, { "set_cursor_mode", mf_set_cursor_mode, 1, 1 }, { "set_flags", mf_set_flags, 2, 2 }, { "set_ini_setting", mf_set_ini_setting, 2, 2 }, { "set_outline", mf_set_outline, 2, 2 }, { "show_window", mf_show_window, 0, 1 }, { "tile_refresh_display", mf_tile_refresh_display, 0, 0 }, }; constexpr int kMetarulesMax = sizeof(kMetarules) / sizeof(kMetarules[0]); void mf_car_gas_amount(Program* program, int args) { programStackPushInteger(program, wmCarGasAmount()); } void mf_combat_data(Program* program, int args) { if (isInCombat()) { programStackPushPointer(program, combat_get_data()); } else { programStackPushPointer(program, nullptr); } } void mf_critter_inven_obj2(Program* program, int args) { int slot = programStackPopInteger(program); Object* obj = static_cast(programStackPopPointer(program)); switch (slot) { case 0: programStackPushPointer(program, critterGetArmor(obj)); break; case 1: programStackPushPointer(program, critterGetItem2(obj)); break; case 2: programStackPushPointer(program, critterGetItem1(obj)); break; case -2: programStackPushInteger(program, obj->data.inventory.length); break; default: programFatalError("mf_critter_inven_obj2: invalid type"); } } void mf_dialog_obj(Program* program, int args) { if (GameMode::isInGameMode(GameMode::kDialog)) { programStackPushPointer(program, gGameDialogSpeaker); } else { programStackPushPointer(program, nullptr); } } void mf_get_cursor_mode(Program* program, int args) { programStackPushInteger(program, gameMouseGetMode()); } void mf_get_flags(Program* program, int args) { Object* object = static_cast(programStackPopPointer(program)); programStackPushInteger(program, object->flags); } void mf_get_object_data(Program* program, int args) { size_t offset = static_cast(programStackPopInteger(program)); void* ptr = programStackPopPointer(program); if (offset % 4 != 0) { programFatalError("mf_get_object_data: bad offset %d", offset); } int value = *reinterpret_cast(reinterpret_cast(ptr) + offset); programStackPushInteger(program, value); } void mf_get_text_width(Program* program, int args) { const char* string = programStackPopString(program); programStackPushInteger(program, fontGetStringWidth(string)); } void mf_intface_redraw(Program* program, int args) { if (args == 0) { interfaceBarRefresh(); } else { // TODO: Incomplete. programFatalError("mf_intface_redraw: not implemented"); } programStackPushInteger(program, -1); } void mf_loot_obj(Program* program, int args) { if (GameMode::isInGameMode(GameMode::kInventory)) { programStackPushPointer(program, inven_get_current_target_obj()); } else { programStackPushPointer(program, nullptr); } } void mf_metarule_exist(Program* program, int args) { const char* metarule = programStackPopString(program); for (int index = 0; index < kMetarulesMax; index++) { if (strcmp(kMetarules[index].name, metarule) == 0) { programStackPushInteger(program, 1); return; } } programStackPushInteger(program, 0); } void mf_outlined_object(Program* program, int args) { programStackPushPointer(program, gmouse_get_outlined_object()); } void mf_set_cursor_mode(Program* program, int args) { int mode = programStackPopInteger(program); gameMouseSetMode(mode); programStackPushInteger(program, -1); } void mf_set_flags(Program* program, int args) { int flags = programStackPopInteger(program); Object* object = static_cast(programStackPopPointer(program)); object->flags = flags; 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); Object* object = static_cast(programStackPopPointer(program)); object->outline = outline; programStackPushInteger(program, -1); } void mf_show_window(Program* program, int args) { if (args == 0) { _windowShow(); } else if (args == 1) { const char* windowName = programStackPopString(program); if (!_windowShowNamed(windowName)) { debugPrint("show_window: window '%s' is not found", windowName); } } programStackPushInteger(program, -1); } void mf_tile_refresh_display(Program* program, int args) { tileWindowRefresh(); programStackPushInteger(program, -1); } void sfall_metarule(Program* program, int args) { static ProgramValue values[6]; for (int index = 0; index < args; index++) { values[index] = programStackPopValue(program); } const char* metarule = programStackPopString(program); for (int index = 0; index < args; index++) { programStackPushValue(program, values[index]); } int metaruleIndex = -1; for (int index = 0; index < kMetarulesMax; index++) { if (strcmp(kMetarules[index].name, metarule) == 0) { metaruleIndex = index; break; } } if (metaruleIndex == -1) { programFatalError("op_sfall_func: '%s' is not implemented", metarule); } if (args < kMetarules[metaruleIndex].minArgs || args > kMetarules[metaruleIndex].maxArgs) { programFatalError("op_sfall_func: '%s': invalid number of args", metarule); } kMetarules[metaruleIndex].handler(program, args); } } // namespace fallout