diff --git a/src/sfall_arrays.cc b/src/sfall_arrays.cc index e074b4c..c0d064b 100644 --- a/src/sfall_arrays.cc +++ b/src/sfall_arrays.cc @@ -11,6 +11,7 @@ #include #include "interpreter.h" +#include "sfall_lists.h" namespace fallout { @@ -647,6 +648,25 @@ ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* progra return arr->ScanArray(val, program); } +ArrayId ListAsArray(int type) +{ + std::vector objects; + sfall_lists_fill(type, objects); + + int count = static_cast(objects.size()); + ArrayId arrayId = CreateTempArray(count, 0); + auto arr = get_array_by_id(arrayId); + + // A little bit ugly and likely inefficient. + for (int index = 0; index < count; index++) { + arr->SetArray(ProgramValue { index }, + ArrayElement { ProgramValue { objects[index] }, nullptr }, + false); + } + + return arrayId; +} + ArrayId StringSplit(const char* str, const char* split) { size_t splitLen = strlen(split); diff --git a/src/sfall_arrays.h b/src/sfall_arrays.h index bd96ad5..d976afb 100644 --- a/src/sfall_arrays.h +++ b/src/sfall_arrays.h @@ -26,6 +26,7 @@ void ResizeArray(ArrayId arrayId, int newLen); void DeleteAllTempArrays(); int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program); ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program); +ArrayId ListAsArray(int type); ArrayId StringSplit(const char* str, const char* split); diff --git a/src/sfall_lists.cc b/src/sfall_lists.cc index a582ae2..55150b1 100644 --- a/src/sfall_lists.cc +++ b/src/sfall_lists.cc @@ -1,7 +1,6 @@ #include "sfall_lists.h" #include -#include #include "object.h" #include "scripts.h" @@ -66,46 +65,7 @@ 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(); - } - } - } + sfall_lists_fill(listType, list.objects); return listId; } @@ -131,4 +91,54 @@ void sfallListsDestroy(int listId) } } +void sfall_lists_fill(int type, std::vector& objects) +{ + if (type == LIST_TILES) { + // For unknown reason this list type is not implemented in Sfall. + return; + } + + objects.reserve(100); + + if (type == 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); + } + 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 (type == LIST_ALL) { + Object* obj = objectFindFirst(); + while (obj != nullptr) { + objects.push_back(obj); + obj = objectFindNext(); + } + } else { + Object* obj = objectFindFirst(); + while (obj != nullptr) { + int objectType = PID_TYPE(obj->pid); + if (objectType < kObjectTypeToListTypeSize + && kObjectTypeToListType[objectType] == type) { + objects.push_back(obj); + } + obj = objectFindNext(); + } + } + } +} + } // namespace fallout diff --git a/src/sfall_lists.h b/src/sfall_lists.h index cbcd239..67f3455 100644 --- a/src/sfall_lists.h +++ b/src/sfall_lists.h @@ -1,6 +1,8 @@ #ifndef FALLOUT_SFALL_LISTS_H_ #define FALLOUT_SFALL_LISTS_H_ +#include + #include "obj_types.h" namespace fallout { @@ -22,6 +24,7 @@ void sfallListsExit(); int sfallListsCreate(int listType); Object* sfallListsGetNext(int listId); void sfallListsDestroy(int listId); +void sfall_lists_fill(int type, std::vector& objects); } // namespace fallout diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 8d8cd57..032e1db 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -556,6 +556,14 @@ static void op_force_encounter_with_flags(Program* program) wmForceEncounter(map, flags); } +// list_as_array +static void op_list_as_array(Program* program) +{ + int type = programStackPopInteger(program); + int arrayId = ListAsArray(type); + programStackPushInteger(program, arrayId); +} + // atoi static void opParseInt(Program* program) { @@ -953,6 +961,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8233, opTempArray); interpreterRegisterOpcode(0x8234, opFixArray); interpreterRegisterOpcode(0x8235, opStringSplit); + interpreterRegisterOpcode(0x8236, op_list_as_array); interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x8238, op_atof); interpreterRegisterOpcode(0x8239, opScanArray);