From e0e0a1ee7dce7d31cfddcb64d35e2ee8e86bf234 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 8 Nov 2022 22:55:25 +0300 Subject: [PATCH] Add Sfall list functions See #200 --- CMakeLists.txt | 2 + src/game.cc | 8 +++ src/sfall_lists.cc | 134 +++++++++++++++++++++++++++++++++++++++++++ src/sfall_lists.h | 28 +++++++++ src/sfall_opcodes.cc | 27 +++++++++ 5 files changed, 199 insertions(+) create mode 100644 src/sfall_lists.cc create mode 100644 src/sfall_lists.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cc05391..99a878f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,6 +271,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/sfall_config.h" "src/sfall_global_vars.cc" "src/sfall_global_vars.h" + "src/sfall_lists.cc" + "src/sfall_lists.h" "src/sfall_opcodes.cc" "src/sfall_opcodes.h" ) diff --git a/src/game.cc b/src/game.cc index 6d3290c..6f8af68 100644 --- a/src/game.cc +++ b/src/game.cc @@ -58,6 +58,7 @@ #include "settings.h" #include "sfall_config.h" #include "sfall_global_vars.h" +#include "sfall_lists.h" #include "skill.h" #include "skilldex.h" #include "stat.h" @@ -352,6 +353,11 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4 return -1; } + if (!sfallListsInit()) { + debugPrint("Failed on sfallListsInit"); + return -1; + } + return 0; } @@ -395,6 +401,7 @@ void gameReset() // SFALL sfallGlobalVarsReset(); + sfallListsReset(); } // 0x442C34 @@ -403,6 +410,7 @@ void gameExit() debugPrint("\nGame Exit\n"); // SFALL + sfallListsExit(); sfallGlobalVarsExit(); premadeCharactersExit(); diff --git a/src/sfall_lists.cc b/src/sfall_lists.cc new file mode 100644 index 0000000..a9022bd --- /dev/null +++ b/src/sfall_lists.cc @@ -0,0 +1,134 @@ +#include "sfall_lists.h" + +#include +#include + +#include "object.h" +#include "scripts.h" + +namespace fallout { + +// Due to bad design of |ListType| it's |LIST_ITEMS| and |LIST_CRITTERS| do not +// match |OBJ_TYPE_CRITTER| and |OBJ_TYPE_ITEM|. +static constexpr int kObjectTypeToListType[] = { + /* OBJ_TYPE_ITEM */ LIST_ITEMS, + /* OBJ_TYPE_CRITTER */ LIST_CRITTERS, + /* OBJ_TYPE_SCENERY */ LIST_SCENERY, + /* OBJ_TYPE_WALL */ LIST_WALLS, + /* OBJ_TYPE_TILE */ LIST_TILES, + /* OBJ_TYPE_MISC */ LIST_MISC, +}; + +static constexpr int kObjectTypeToListTypeSize = sizeof(kObjectTypeToListType) / sizeof(kObjectTypeToListType[0]); + +// As in Sfall. +static constexpr int kInitialListId = 0xCCCCCC; + +// Loosely based on [sList] from Sfall. +struct List { + std::vector objects; + size_t pos = 0; +}; + +struct State { + std::unordered_map lists; + int nextListId = kInitialListId; +}; + +static State* _state = nullptr; + +bool sfallListsInit() +{ + _state = new (std::nothrow) State(); + if (_state == nullptr) { + return false; + } + + return true; +} + +void sfallListsReset() +{ + _state->nextListId = kInitialListId; + _state->lists.clear(); +} + +void sfallListsExit() +{ + if (_state != nullptr) { + delete _state; + _state = nullptr; + } +} + +int sfallListsCreate(int listType) +{ + int listId = _state->nextListId++; + List& list = _state->lists[listId]; + + if (listType == LIST_TILES) { + // For unknown reason this list type is not implemented in Sfall. + } else if (listType == LIST_SPATIAL) { + for (int elevation = 0; elevation < ELEVATION_COUNT; elevation++) { + Script* script = scriptGetFirstSpatialScript(elevation); + while (script != nullptr) { + Object* obj = script->owner; + if (obj == nullptr) { + obj = scriptGetSelf(script->program); + } + list.objects.push_back(obj); + script = scriptGetNextSpatialScript(); + } + } + } else { + // CE: Implementation is slightly different. Sfall manually loops thru + // elevations (3) and hexes (40000) and use |objectFindFirstAtLocation| + // (originally |obj_find_first_at_tile|) to obtain next object. This + // functionality is already implemented in |objectFindFirst| and + // |objectFindNext|. + // + // As a small optimization |LIST_ALL| is handled separately since there + // is no need to check object type. + if (listType == LIST_ALL) { + Object* obj = objectFindFirst(); + while (obj != nullptr) { + list.objects.push_back(obj); + obj = objectFindNext(); + } + } else { + Object* obj = objectFindFirst(); + while (obj != nullptr) { + int objectType = PID_TYPE(obj->pid); + if (objectType < kObjectTypeToListTypeSize && kObjectTypeToListType[objectType] == listType) { + list.objects.push_back(obj); + } + obj = objectFindNext(); + } + } + } + + return listId; +} + +Object* sfallListsGetNext(int listId) +{ + auto it = _state->lists.find(listId); + if (it != _state->lists.end()) { + List& list = it->second; + if (list.pos < list.objects.size()) { + return list.objects[list.pos++]; + } + } + + return nullptr; +} + +void sfallListsDestroy(int listId) +{ + auto it = _state->lists.find(listId); + if (it != _state->lists.end()) { + _state->lists.erase(it); + } +} + +} // namespace fallout diff --git a/src/sfall_lists.h b/src/sfall_lists.h new file mode 100644 index 0000000..cbcd239 --- /dev/null +++ b/src/sfall_lists.h @@ -0,0 +1,28 @@ +#ifndef FALLOUT_SFALL_LISTS_H_ +#define FALLOUT_SFALL_LISTS_H_ + +#include "obj_types.h" + +namespace fallout { + +enum ListType { + LIST_CRITTERS = 0, + LIST_ITEMS = 1, + LIST_SCENERY = 2, + LIST_WALLS = 3, + LIST_TILES = 4, + LIST_MISC = 5, + LIST_SPATIAL = 6, + LIST_ALL = 9, +}; + +bool sfallListsInit(); +void sfallListsReset(); +void sfallListsExit(); +int sfallListsCreate(int listType); +Object* sfallListsGetNext(int listId); +void sfallListsDestroy(int listId); + +} // namespace fallout + +#endif /* FALLOUT_SFALL_LISTS_H_ */ diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 37b30d5..d3a7a57 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -7,6 +7,7 @@ #include "mouse.h" #include "object.h" #include "sfall_global_vars.h" +#include "sfall_lists.h" #include "stat.h" #include "svga.h" @@ -67,6 +68,29 @@ static void opGetGlobalInt(Program* program) programStackPushInteger(program, value); } +// list_begin +static void opListBegin(Program* program) +{ + int listType = programStackPopInteger(program); + int listId = sfallListsCreate(listType); + programStackPushInteger(program, listId); +} + +// list_next +static void opListNext(Program* program) +{ + int listId = programStackPopInteger(program); + Object* obj = sfallListsGetNext(listId); + programStackPushPointer(program, obj); +} + +// list_end +static void opListEnd(Program* program) +{ + int listId = programStackPopInteger(program); + sfallListsDestroy(listId); +} + // get_weapon_ammo_pid static void opGetWeaponAmmoPid(Program* program) { @@ -205,6 +229,9 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819D, opSetGlobalVar); interpreterRegisterOpcode(0x819E, opGetGlobalInt); + interpreterRegisterOpcode(0x820D, opListBegin); + interpreterRegisterOpcode(0x820E, opListNext); + interpreterRegisterOpcode(0x820F, opListEnd); interpreterRegisterOpcode(0x8217, opGetWeaponAmmoPid); interpreterRegisterOpcode(0x8219, opGetWeaponAmmoCount); interpreterRegisterOpcode(0x821A, opSetWeaponAmmoCount);