Add SFall arrays (#269)
Co-authored-by: Alexander Batalov <alex.batalov@gmail.com>
This commit is contained in:
parent
62c5c4757c
commit
fe0d767521
|
@ -271,6 +271,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
|
||||||
"src/sfall_lists.h"
|
"src/sfall_lists.h"
|
||||||
"src/sfall_opcodes.cc"
|
"src/sfall_opcodes.cc"
|
||||||
"src/sfall_opcodes.h"
|
"src/sfall_opcodes.h"
|
||||||
|
"src/sfall_arrays.cc"
|
||||||
|
"src/sfall_arrays.h"
|
||||||
"src/touch.cc"
|
"src/touch.cc"
|
||||||
"src/touch.h"
|
"src/touch.h"
|
||||||
)
|
)
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "scripts.h"
|
#include "scripts.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "sfall_arrays.h"
|
||||||
#include "sfall_config.h"
|
#include "sfall_config.h"
|
||||||
#include "sfall_global_vars.h"
|
#include "sfall_global_vars.h"
|
||||||
#include "sfall_lists.h"
|
#include "sfall_lists.h"
|
||||||
|
@ -348,6 +349,11 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sfallArraysInit()) {
|
||||||
|
debugPrint("Failed on sfallArraysInit");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList);
|
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -395,6 +401,7 @@ void gameReset()
|
||||||
sfallGlobalVarsReset();
|
sfallGlobalVarsReset();
|
||||||
sfallListsReset();
|
sfallListsReset();
|
||||||
messageListRepositoryReset();
|
messageListRepositoryReset();
|
||||||
|
sfallArraysReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x442C34
|
// 0x442C34
|
||||||
|
@ -403,6 +410,7 @@ void gameExit()
|
||||||
debugPrint("\nGame Exit\n");
|
debugPrint("\nGame Exit\n");
|
||||||
|
|
||||||
// SFALL
|
// SFALL
|
||||||
|
sfallArraysExit();
|
||||||
sfallListsExit();
|
sfallListsExit();
|
||||||
sfallGlobalVarsExit();
|
sfallGlobalVarsExit();
|
||||||
premadeCharactersExit();
|
premadeCharactersExit();
|
||||||
|
|
|
@ -3252,7 +3252,7 @@ void* programReturnStackPopPointer(Program* program)
|
||||||
return programValue.pointerValue;
|
return programValue.pointerValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProgramValue::isEmpty()
|
bool ProgramValue::isEmpty() const
|
||||||
{
|
{
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case VALUE_TYPE_INT:
|
case VALUE_TYPE_INT:
|
||||||
|
@ -3269,19 +3269,19 @@ bool ProgramValue::isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches Sfall implementation.
|
// Matches Sfall implementation.
|
||||||
bool ProgramValue::isInt()
|
bool ProgramValue::isInt() const
|
||||||
{
|
{
|
||||||
return opcode == VALUE_TYPE_INT;
|
return opcode == VALUE_TYPE_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches Sfall implementation.
|
// Matches Sfall implementation.
|
||||||
bool ProgramValue::isFloat()
|
bool ProgramValue::isFloat() const
|
||||||
{
|
{
|
||||||
return opcode == VALUE_TYPE_FLOAT;
|
return opcode == VALUE_TYPE_FLOAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches Sfall implementation.
|
// Matches Sfall implementation.
|
||||||
float ProgramValue::asFloat()
|
float ProgramValue::asFloat() const
|
||||||
{
|
{
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case VALUE_TYPE_INT:
|
case VALUE_TYPE_INT:
|
||||||
|
@ -3293,4 +3293,42 @@ float ProgramValue::asFloat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProgramValue::isString() const
|
||||||
|
{
|
||||||
|
return opcode == VALUE_TYPE_STRING || opcode == VALUE_TYPE_DYNAMIC_STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue::ProgramValue()
|
||||||
|
{
|
||||||
|
opcode = VALUE_TYPE_INT;
|
||||||
|
integerValue = 0;
|
||||||
|
}
|
||||||
|
ProgramValue::ProgramValue(int value)
|
||||||
|
{
|
||||||
|
opcode = VALUE_TYPE_INT;
|
||||||
|
integerValue = value;
|
||||||
|
};
|
||||||
|
ProgramValue::ProgramValue(Object* value)
|
||||||
|
{
|
||||||
|
opcode = VALUE_TYPE_PTR;
|
||||||
|
pointerValue = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ProgramValue::isPointer() const
|
||||||
|
{
|
||||||
|
return opcode == VALUE_TYPE_PTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ProgramValue::asInt() const
|
||||||
|
{
|
||||||
|
switch (opcode) {
|
||||||
|
case VALUE_TYPE_INT:
|
||||||
|
return integerValue;
|
||||||
|
case VALUE_TYPE_FLOAT:
|
||||||
|
return static_cast<int>(floatValue);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fallout
|
} // namespace fallout
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef INTERPRETER_H
|
#ifndef INTERPRETER_H
|
||||||
#define INTERPRETER_H
|
#define INTERPRETER_H
|
||||||
|
|
||||||
|
#include "object.h"
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -140,7 +141,12 @@ typedef struct Procedure {
|
||||||
int field_14;
|
int field_14;
|
||||||
} Procedure;
|
} Procedure;
|
||||||
|
|
||||||
typedef struct ProgramValue {
|
class ProgramValue {
|
||||||
|
public:
|
||||||
|
ProgramValue();
|
||||||
|
ProgramValue(int value);
|
||||||
|
ProgramValue(Object* value);
|
||||||
|
|
||||||
opcode_t opcode;
|
opcode_t opcode;
|
||||||
union {
|
union {
|
||||||
int integerValue;
|
int integerValue;
|
||||||
|
@ -148,11 +154,14 @@ typedef struct ProgramValue {
|
||||||
void* pointerValue;
|
void* pointerValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isEmpty();
|
bool isEmpty() const;
|
||||||
bool isInt();
|
bool isInt() const;
|
||||||
bool isFloat();
|
bool isFloat() const;
|
||||||
float asFloat();
|
bool isString() const;
|
||||||
} ProgramValue;
|
float asFloat() const;
|
||||||
|
bool isPointer() const;
|
||||||
|
int asInt() const;
|
||||||
|
};
|
||||||
|
|
||||||
typedef std::vector<ProgramValue> ProgramStack;
|
typedef std::vector<ProgramValue> ProgramStack;
|
||||||
|
|
||||||
|
|
|
@ -64,22 +64,22 @@ typedef struct STRU_519DBC {
|
||||||
int field_8; // early what?
|
int field_8; // early what?
|
||||||
} STRU_519DBC;
|
} STRU_519DBC;
|
||||||
|
|
||||||
typedef struct STRUCT_519DA8 {
|
typedef struct PartyMemberListItem {
|
||||||
Object* object;
|
Object* object;
|
||||||
Script* script;
|
Script* script;
|
||||||
int* vars;
|
int* vars;
|
||||||
struct STRUCT_519DA8* next;
|
struct PartyMemberListItem* next;
|
||||||
} STRUCT_519DA8;
|
} PartyMemberListItem;
|
||||||
|
|
||||||
static int partyMemberGetDescription(Object* object, PartyMemberDescription** partyMemberDescriptionPtr);
|
static int partyMemberGetDescription(Object* object, PartyMemberDescription** partyMemberDescriptionPtr);
|
||||||
static void partyMemberDescriptionInit(PartyMemberDescription* partyMemberDescription);
|
static void partyMemberDescriptionInit(PartyMemberDescription* partyMemberDescription);
|
||||||
static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1);
|
static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1);
|
||||||
static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1);
|
static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1);
|
||||||
static int _partyMemberNewObjID();
|
static int _partyMemberNewObjID();
|
||||||
static int _partyMemberNewObjIDRecurseFind(Object* object, int objectId);
|
static int _partyMemberNewObjIDRecurseFind(Object* object, int objectId);
|
||||||
static int _partyMemberPrepItemSave(Object* object);
|
static int _partyMemberPrepItemSave(Object* object);
|
||||||
static int _partyMemberItemSave(Object* object);
|
static int _partyMemberItemSave(Object* object);
|
||||||
static int _partyMemberItemRecover(STRUCT_519DA8* a1);
|
static int _partyMemberItemRecover(PartyMemberListItem* a1);
|
||||||
static int _partyMemberClearItemList();
|
static int _partyMemberClearItemList();
|
||||||
static int partyFixMultipleMembers();
|
static int partyFixMultipleMembers();
|
||||||
static int _partyMemberCopyLevelInfo(Object* object, int a2);
|
static int _partyMemberCopyLevelInfo(Object* object, int a2);
|
||||||
|
@ -91,12 +91,12 @@ int gPartyMemberDescriptionsLength = 0;
|
||||||
int* gPartyMemberPids = NULL;
|
int* gPartyMemberPids = NULL;
|
||||||
|
|
||||||
//
|
//
|
||||||
static STRUCT_519DA8* _itemSaveListHead = NULL;
|
static PartyMemberListItem* _itemSaveListHead = NULL;
|
||||||
|
|
||||||
// List of party members, it's length is [gPartyMemberDescriptionsLength] + 20.
|
// List of party members, it's length is [gPartyMemberDescriptionsLength] + 20.
|
||||||
//
|
//
|
||||||
// 0x519DA8
|
// 0x519DA8
|
||||||
static STRUCT_519DA8* gPartyMembers = NULL;
|
PartyMemberListItem* gPartyMembers = NULL;
|
||||||
|
|
||||||
// Number of critters added to party.
|
// Number of critters added to party.
|
||||||
//
|
//
|
||||||
|
@ -150,7 +150,7 @@ int partyMembersInit()
|
||||||
|
|
||||||
memset(gPartyMemberPids, 0, sizeof(*gPartyMemberPids) * gPartyMemberDescriptionsLength);
|
memset(gPartyMemberPids, 0, sizeof(*gPartyMemberPids) * gPartyMemberDescriptionsLength);
|
||||||
|
|
||||||
gPartyMembers = (STRUCT_519DA8*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20));
|
gPartyMembers = (PartyMemberListItem*)internal_malloc(sizeof(*gPartyMembers) * (gPartyMemberDescriptionsLength + 20));
|
||||||
if (gPartyMembers == NULL) {
|
if (gPartyMembers == NULL) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ int partyMemberAdd(Object* object)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
if (partyMember->object == object || partyMember->object->pid == object->pid) {
|
if (partyMember->object == object || partyMember->object->pid == object->pid) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -390,7 +390,7 @@ int partyMemberAdd(Object* object)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[gPartyMembersLength]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[gPartyMembersLength]);
|
||||||
partyMember->object = object;
|
partyMember->object = object;
|
||||||
partyMember->script = NULL;
|
partyMember->script = NULL;
|
||||||
partyMember->vars = NULL;
|
partyMember->vars = NULL;
|
||||||
|
@ -435,7 +435,7 @@ int partyMemberRemove(Object* object)
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
for (index = 1; index < gPartyMembersLength; index++) {
|
for (index = 1; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
if (partyMember->object == object) {
|
if (partyMember->object == object) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -480,7 +480,7 @@ int _partyMemberPrepSave()
|
||||||
_partyStatePrepped = 1;
|
_partyStatePrepped = 1;
|
||||||
|
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||||
|
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
ptr->object->flags &= ~(OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
ptr->object->flags &= ~(OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
||||||
|
@ -499,7 +499,7 @@ int _partyMemberPrepSave()
|
||||||
int _partyMemberUnPrepSave()
|
int _partyMemberUnPrepSave()
|
||||||
{
|
{
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||||
|
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
ptr->object->flags |= (OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
ptr->object->flags |= (OBJECT_NO_REMOVE | OBJECT_NO_SAVE);
|
||||||
|
@ -523,7 +523,7 @@ int partyMembersSave(File* stream)
|
||||||
if (fileWriteInt32(stream, _partyMemberItemCount) == -1) return -1;
|
if (fileWriteInt32(stream, _partyMemberItemCount) == -1) return -1;
|
||||||
|
|
||||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
if (fileWriteInt32(stream, partyMember->object->id) == -1) return -1;
|
if (fileWriteInt32(stream, partyMember->object->id) == -1) return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +547,7 @@ int _partyMemberPrepLoad()
|
||||||
_partyStatePrepped = 1;
|
_partyStatePrepped = 1;
|
||||||
|
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* ptr_519DA8 = &(gPartyMembers[index]);
|
PartyMemberListItem* ptr_519DA8 = &(gPartyMembers[index]);
|
||||||
if (_partyMemberPrepLoadInstance(ptr_519DA8) != 0) {
|
if (_partyMemberPrepLoadInstance(ptr_519DA8) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -558,7 +558,7 @@ int _partyMemberPrepLoad()
|
||||||
|
|
||||||
// partyMemberPrepLoadInstance
|
// partyMemberPrepLoadInstance
|
||||||
// 0x49480C
|
// 0x49480C
|
||||||
static int _partyMemberPrepLoadInstance(STRUCT_519DA8* a1)
|
static int _partyMemberPrepLoadInstance(PartyMemberListItem* a1)
|
||||||
{
|
{
|
||||||
Object* obj = a1->object;
|
Object* obj = a1->object;
|
||||||
|
|
||||||
|
@ -643,7 +643,7 @@ int _partyMemberRecoverLoad()
|
||||||
debugPrint("[Party Member %d]: %s\n", index, critterGetName(gPartyMembers[index].object));
|
debugPrint("[Party Member %d]: %s\n", index, critterGetName(gPartyMembers[index].object));
|
||||||
}
|
}
|
||||||
|
|
||||||
STRUCT_519DA8* v6 = _itemSaveListHead;
|
PartyMemberListItem* v6 = _itemSaveListHead;
|
||||||
while (v6 != NULL) {
|
while (v6 != NULL) {
|
||||||
_itemSaveListHead = v6->next;
|
_itemSaveListHead = v6->next;
|
||||||
|
|
||||||
|
@ -664,7 +664,7 @@ int _partyMemberRecoverLoad()
|
||||||
|
|
||||||
// partyMemberRecoverLoadInstance
|
// partyMemberRecoverLoadInstance
|
||||||
// 0x494A88
|
// 0x494A88
|
||||||
static int _partyMemberRecoverLoadInstance(STRUCT_519DA8* a1)
|
static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1)
|
||||||
{
|
{
|
||||||
if (a1->script == NULL) {
|
if (a1->script == NULL) {
|
||||||
showMesageBox("\n Error!: partyMemberRecoverLoadInstance: No script!");
|
showMesageBox("\n Error!: partyMemberRecoverLoadInstance: No script!");
|
||||||
|
@ -801,7 +801,7 @@ int _partyMemberSyncPosition()
|
||||||
int n = 0;
|
int n = 0;
|
||||||
int distance = 2;
|
int distance = 2;
|
||||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
Object* partyMemberObj = partyMember->object;
|
Object* partyMemberObj = partyMember->object;
|
||||||
if ((partyMemberObj->flags & OBJECT_HIDDEN) == 0 && PID_TYPE(partyMemberObj->pid) == OBJ_TYPE_CRITTER) {
|
if ((partyMemberObj->flags & OBJECT_HIDDEN) == 0 && PID_TYPE(partyMemberObj->pid) == OBJ_TYPE_CRITTER) {
|
||||||
int rotation;
|
int rotation;
|
||||||
|
@ -833,7 +833,7 @@ int _partyMemberRestingHeal(int a1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
if (PID_TYPE(partyMember->object->pid) == OBJ_TYPE_CRITTER) {
|
if (PID_TYPE(partyMember->object->pid) == OBJ_TYPE_CRITTER) {
|
||||||
int healingRate = critterGetStat(partyMember->object, STAT_HEALING_RATE);
|
int healingRate = critterGetStat(partyMember->object, STAT_HEALING_RATE);
|
||||||
critterAdjustHitPoints(partyMember->object, v1 * healingRate);
|
critterAdjustHitPoints(partyMember->object, v1 * healingRate);
|
||||||
|
@ -860,7 +860,7 @@ Object* partyMemberFindByPid(int pid)
|
||||||
bool _isPotentialPartyMember(Object* object)
|
bool _isPotentialPartyMember(Object* object)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
if (partyMember->object->pid == gPartyMemberPids[index]) {
|
if (partyMember->object->pid == gPartyMemberPids[index]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -976,7 +976,7 @@ static int _partyMemberNewObjIDRecurseFind(Object* obj, int objectId)
|
||||||
int _partyMemberPrepItemSaveAll()
|
int _partyMemberPrepItemSaveAll()
|
||||||
{
|
{
|
||||||
for (int partyMemberIndex = 0; partyMemberIndex < gPartyMembersLength; partyMemberIndex++) {
|
for (int partyMemberIndex = 0; partyMemberIndex < gPartyMembersLength; partyMemberIndex++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[partyMemberIndex]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[partyMemberIndex]);
|
||||||
|
|
||||||
Inventory* inventory = &(partyMember->object->data.inventory);
|
Inventory* inventory = &(partyMember->object->data.inventory);
|
||||||
for (int inventoryItemIndex = 0; inventoryItemIndex < inventory->length; inventoryItemIndex++) {
|
for (int inventoryItemIndex = 0; inventoryItemIndex < inventory->length; inventoryItemIndex++) {
|
||||||
|
@ -1025,7 +1025,7 @@ static int _partyMemberItemSave(Object* object)
|
||||||
object->id = script->field_1C;
|
object->id = script->field_1C;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRUCT_519DA8* node = (STRUCT_519DA8*)internal_malloc(sizeof(*node));
|
PartyMemberListItem* node = (PartyMemberListItem*)internal_malloc(sizeof(*node));
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
showMesageBox("\n Error!: partyMemberItemSave: Out of memory!");
|
showMesageBox("\n Error!: partyMemberItemSave: Out of memory!");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -1053,7 +1053,7 @@ static int _partyMemberItemSave(Object* object)
|
||||||
node->vars = NULL;
|
node->vars = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRUCT_519DA8* temp = _itemSaveListHead;
|
PartyMemberListItem* temp = _itemSaveListHead;
|
||||||
_itemSaveListHead = node;
|
_itemSaveListHead = node;
|
||||||
node->next = temp;
|
node->next = temp;
|
||||||
}
|
}
|
||||||
|
@ -1069,7 +1069,7 @@ static int _partyMemberItemSave(Object* object)
|
||||||
|
|
||||||
// partyMemberItemRecover
|
// partyMemberItemRecover
|
||||||
// 0x495388
|
// 0x495388
|
||||||
static int _partyMemberItemRecover(STRUCT_519DA8* a1)
|
static int _partyMemberItemRecover(PartyMemberListItem* a1)
|
||||||
{
|
{
|
||||||
int sid = -1;
|
int sid = -1;
|
||||||
if (scriptAdd(&sid, SCRIPT_TYPE_ITEM) == -1) {
|
if (scriptAdd(&sid, SCRIPT_TYPE_ITEM) == -1) {
|
||||||
|
@ -1108,7 +1108,7 @@ static int _partyMemberItemRecover(STRUCT_519DA8* a1)
|
||||||
static int _partyMemberClearItemList()
|
static int _partyMemberClearItemList()
|
||||||
{
|
{
|
||||||
while (_itemSaveListHead != NULL) {
|
while (_itemSaveListHead != NULL) {
|
||||||
STRUCT_519DA8* node = _itemSaveListHead;
|
PartyMemberListItem* node = _itemSaveListHead;
|
||||||
_itemSaveListHead = _itemSaveListHead->next;
|
_itemSaveListHead = _itemSaveListHead->next;
|
||||||
|
|
||||||
if (node->script != NULL) {
|
if (node->script != NULL) {
|
||||||
|
@ -1262,7 +1262,7 @@ static int partyFixMultipleMembers()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < gPartyMembersLength; index++) {
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* partyMember = &(gPartyMembers[index]);
|
PartyMemberListItem* partyMember = &(gPartyMembers[index]);
|
||||||
|
|
||||||
Script* script;
|
Script* script;
|
||||||
if (scriptGetScript(partyMember->object->sid, &script) != -1) {
|
if (scriptGetScript(partyMember->object->sid, &script) != -1) {
|
||||||
|
@ -1454,7 +1454,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse)
|
||||||
int _partyMemberIncLevels()
|
int _partyMemberIncLevels()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
STRUCT_519DA8* ptr;
|
PartyMemberListItem* ptr;
|
||||||
Object* obj;
|
Object* obj;
|
||||||
PartyMemberDescription* party_member;
|
PartyMemberDescription* party_member;
|
||||||
const char* name;
|
const char* name;
|
||||||
|
@ -1622,7 +1622,7 @@ static int _partyMemberCopyLevelInfo(Object* critter, int a2)
|
||||||
bool partyIsAnyoneCanBeHealedByRest()
|
bool partyIsAnyoneCanBeHealedByRest()
|
||||||
{
|
{
|
||||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||||
Object* object = ptr->object;
|
Object* object = ptr->object;
|
||||||
|
|
||||||
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
||||||
|
@ -1649,7 +1649,7 @@ int partyGetMaxWoundToHealByRest()
|
||||||
int maxWound = 0;
|
int maxWound = 0;
|
||||||
|
|
||||||
for (int index = 1; index < gPartyMembersLength; index++) {
|
for (int index = 1; index < gPartyMembersLength; index++) {
|
||||||
STRUCT_519DA8* ptr = &(gPartyMembers[index]);
|
PartyMemberListItem* ptr = &(gPartyMembers[index]);
|
||||||
Object* object = ptr->object;
|
Object* object = ptr->object;
|
||||||
|
|
||||||
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
if (PID_TYPE(object->pid) != OBJ_TYPE_CRITTER) continue;
|
||||||
|
@ -1670,4 +1670,20 @@ int partyGetMaxWoundToHealByRest()
|
||||||
return maxWound;
|
return maxWound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Object*> get_all_party_members_objects(bool include_hidden)
|
||||||
|
{
|
||||||
|
std::vector<Object*> value;
|
||||||
|
value.reserve(gPartyMembersLength);
|
||||||
|
for (int index = 0; index < gPartyMembersLength; index++) {
|
||||||
|
auto object = gPartyMembers[index].object;
|
||||||
|
if (include_hidden
|
||||||
|
|| (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER
|
||||||
|
&& !critterIsDead(object)
|
||||||
|
&& (object->flags & OBJECT_HIDDEN) == 0)) {
|
||||||
|
value.push_back(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fallout
|
} // namespace fallout
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
#ifndef PARTY_MEMBER_H
|
#ifndef PARTY_MEMBER_H
|
||||||
#define PARTY_MEMBER_H
|
#define PARTY_MEMBER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "obj_types.h"
|
#include "obj_types.h"
|
||||||
|
#include "scripts.h"
|
||||||
|
|
||||||
namespace fallout {
|
namespace fallout {
|
||||||
|
|
||||||
|
@ -42,6 +45,7 @@ bool partyMemberSupportsChemUse(Object* object, int chemUse);
|
||||||
int _partyMemberIncLevels();
|
int _partyMemberIncLevels();
|
||||||
bool partyIsAnyoneCanBeHealedByRest();
|
bool partyIsAnyoneCanBeHealedByRest();
|
||||||
int partyGetMaxWoundToHealByRest();
|
int partyGetMaxWoundToHealByRest();
|
||||||
|
std::vector<Object*> get_all_party_members_objects(bool include_hidden);
|
||||||
|
|
||||||
} // namespace fallout
|
} // namespace fallout
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "proto_instance.h"
|
#include "proto_instance.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
#include "sfall_arrays.h"
|
||||||
#include "sfall_config.h"
|
#include "sfall_config.h"
|
||||||
#include "stat.h"
|
#include "stat.h"
|
||||||
#include "svga.h"
|
#include "svga.h"
|
||||||
|
@ -1027,6 +1028,8 @@ int scriptsHandleRequests()
|
||||||
inventoryOpenStealing(gScriptsRequestedStealingBy, gScriptsRequestedStealingFrom);
|
inventoryOpenStealing(gScriptsRequestedStealingBy, gScriptsRequestedStealingFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeleteAllTempArrays();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,697 @@
|
||||||
|
#include "sfall_arrays.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <random>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
namespace fallout {
|
||||||
|
|
||||||
|
static constexpr ArrayId kInitialArrayId = 1;
|
||||||
|
|
||||||
|
#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value
|
||||||
|
#define ARRAY_MAX_SIZE (100000) // maximum number of array elements,
|
||||||
|
|
||||||
|
// special actions for arrays using array_resize operator
|
||||||
|
#define ARRAY_ACTION_SORT (-2)
|
||||||
|
#define ARRAY_ACTION_RSORT (-3)
|
||||||
|
#define ARRAY_ACTION_REVERSE (-4)
|
||||||
|
#define ARRAY_ACTION_SHUFFLE (-5)
|
||||||
|
|
||||||
|
template <class T, typename Compare>
|
||||||
|
static void ListSort(std::vector<T>& arr, int type, Compare cmp)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ARRAY_ACTION_SORT: // sort ascending
|
||||||
|
std::sort(arr.begin(), arr.end(), cmp);
|
||||||
|
break;
|
||||||
|
case ARRAY_ACTION_RSORT: // sort descending
|
||||||
|
std::sort(arr.rbegin(), arr.rend(), cmp);
|
||||||
|
break;
|
||||||
|
case ARRAY_ACTION_REVERSE: // reverse elements
|
||||||
|
std::reverse(arr.rbegin(), arr.rend());
|
||||||
|
break;
|
||||||
|
case ARRAY_ACTION_SHUFFLE: // shuffle elements
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 g(rd());
|
||||||
|
std::shuffle(arr.begin(), arr.end(), g);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ArrayElementType {
|
||||||
|
INT,
|
||||||
|
FLOAT,
|
||||||
|
STRING,
|
||||||
|
POINTER
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is mostly the same as ProgramValue but it owns strings.
|
||||||
|
*
|
||||||
|
* This is done because when we pop dynamic string element from
|
||||||
|
* the stack we decrease ref count for this string and it memory
|
||||||
|
* can be freed.
|
||||||
|
*
|
||||||
|
* In theory arrays can be shared between programs so we also
|
||||||
|
* have to copy static strings.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ArrayElement {
|
||||||
|
public:
|
||||||
|
ArrayElement()
|
||||||
|
: type(ArrayElementType::INT)
|
||||||
|
, value({ 0 })
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayElement(const ArrayElement& other) = delete;
|
||||||
|
|
||||||
|
ArrayElement& operator=(const ArrayElement& rhs) = delete;
|
||||||
|
|
||||||
|
ArrayElement(ArrayElement&& other) noexcept
|
||||||
|
: type(ArrayElementType::INT)
|
||||||
|
, value({ 0 })
|
||||||
|
{
|
||||||
|
std::swap(type, other.type);
|
||||||
|
std::swap(value, other.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayElement& operator=(ArrayElement&& other) noexcept
|
||||||
|
{
|
||||||
|
std::swap(type, other.type);
|
||||||
|
std::swap(value, other.value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayElement(ProgramValue programValue, Program* program)
|
||||||
|
{
|
||||||
|
switch (programValue.opcode) {
|
||||||
|
case VALUE_TYPE_INT:
|
||||||
|
type = ArrayElementType::INT;
|
||||||
|
value.integerValue = programValue.integerValue;
|
||||||
|
break;
|
||||||
|
case VALUE_TYPE_FLOAT:
|
||||||
|
type = ArrayElementType::FLOAT;
|
||||||
|
value.floatValue = programValue.floatValue;
|
||||||
|
break;
|
||||||
|
case VALUE_TYPE_PTR:
|
||||||
|
type = ArrayElementType::POINTER;
|
||||||
|
value.pointerValue = programValue.pointerValue;
|
||||||
|
break;
|
||||||
|
case VALUE_TYPE_STRING:
|
||||||
|
case VALUE_TYPE_DYNAMIC_STRING:
|
||||||
|
setString(programGetString(program, programValue.opcode, programValue.integerValue), -1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = ArrayElementType::INT;
|
||||||
|
value.integerValue = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayElement(const char* str)
|
||||||
|
{
|
||||||
|
setString(str, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayElement(const char* str, size_t sLen)
|
||||||
|
{
|
||||||
|
setString(str, sLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue toValue(Program* program) const
|
||||||
|
{
|
||||||
|
ProgramValue out;
|
||||||
|
switch (type) {
|
||||||
|
case ArrayElementType::INT:
|
||||||
|
out.opcode = VALUE_TYPE_INT;
|
||||||
|
out.integerValue = value.integerValue;
|
||||||
|
break;
|
||||||
|
case ArrayElementType::FLOAT:
|
||||||
|
out.opcode = VALUE_TYPE_FLOAT;
|
||||||
|
out.floatValue = value.floatValue;
|
||||||
|
break;
|
||||||
|
case ArrayElementType::POINTER:
|
||||||
|
out.opcode = VALUE_TYPE_PTR;
|
||||||
|
out.pointerValue = value.pointerValue;
|
||||||
|
break;
|
||||||
|
case ArrayElementType::STRING:
|
||||||
|
out.opcode = VALUE_TYPE_DYNAMIC_STRING;
|
||||||
|
out.integerValue = programPushString(program, value.stringValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(ArrayElement const& other) const
|
||||||
|
{
|
||||||
|
if (type != other.type) {
|
||||||
|
return type < other.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ArrayElementType::INT:
|
||||||
|
return value.integerValue < other.value.integerValue;
|
||||||
|
case ArrayElementType::FLOAT:
|
||||||
|
return value.floatValue < other.value.floatValue;
|
||||||
|
case ArrayElementType::POINTER:
|
||||||
|
return value.pointerValue < other.value.pointerValue;
|
||||||
|
case ArrayElementType::STRING:
|
||||||
|
return strcmp(value.stringValue, other.value.stringValue) < 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(ArrayElement const& other) const
|
||||||
|
{
|
||||||
|
if (type != other.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ArrayElementType::INT:
|
||||||
|
return value.integerValue == other.value.integerValue;
|
||||||
|
case ArrayElementType::FLOAT:
|
||||||
|
return value.floatValue == other.value.floatValue;
|
||||||
|
case ArrayElementType::POINTER:
|
||||||
|
return value.pointerValue == other.value.pointerValue;
|
||||||
|
case ArrayElementType::STRING:
|
||||||
|
return strcmp(value.stringValue, other.value.stringValue) == 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ArrayElement()
|
||||||
|
{
|
||||||
|
if (type == ArrayElementType::STRING) {
|
||||||
|
free(value.stringValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setString(const char* str, size_t sLen)
|
||||||
|
{
|
||||||
|
type = ArrayElementType::STRING;
|
||||||
|
|
||||||
|
if (sLen == -1) {
|
||||||
|
sLen = strlen(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sLen >= ARRAY_MAX_STRING) {
|
||||||
|
sLen = ARRAY_MAX_STRING - 1; // memory safety
|
||||||
|
}
|
||||||
|
|
||||||
|
value.stringValue = (char*)malloc(sLen + 1);
|
||||||
|
memcpy(value.stringValue, str, sLen);
|
||||||
|
value.stringValue[sLen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayElementType type;
|
||||||
|
union {
|
||||||
|
int integerValue;
|
||||||
|
float floatValue;
|
||||||
|
char* stringValue;
|
||||||
|
void* pointerValue;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SFallArray {
|
||||||
|
public:
|
||||||
|
SFallArray(unsigned int flags)
|
||||||
|
: flags(flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~SFallArray() = default;
|
||||||
|
virtual ProgramValue GetArrayKey(int index, Program* program) = 0;
|
||||||
|
virtual ProgramValue GetArray(const ProgramValue& key, Program* program) = 0;
|
||||||
|
virtual void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) = 0;
|
||||||
|
virtual void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) = 0;
|
||||||
|
virtual void ResizeArray(int newLen) = 0;
|
||||||
|
virtual ProgramValue ScanArray(const ProgramValue& value, Program* program) = 0;
|
||||||
|
virtual int size() = 0;
|
||||||
|
|
||||||
|
bool isReadOnly() const
|
||||||
|
{
|
||||||
|
return (flags & SFALL_ARRAYFLAG_CONSTVAL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SFallArrayList : public SFallArray {
|
||||||
|
public:
|
||||||
|
SFallArrayList() = delete;
|
||||||
|
|
||||||
|
SFallArrayList(unsigned int len, unsigned int flags)
|
||||||
|
: SFallArray(flags)
|
||||||
|
{
|
||||||
|
values.resize(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size()
|
||||||
|
{
|
||||||
|
return static_cast<int>(values.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue GetArrayKey(int index, Program* program)
|
||||||
|
{
|
||||||
|
if (index < -1 || index > size()) {
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == -1) { // special index to indicate if array is associative
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ProgramValue(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue GetArray(const ProgramValue& key, Program* program)
|
||||||
|
{
|
||||||
|
auto index = key.asInt();
|
||||||
|
if (index < 0 || index >= size()) {
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return values[index].toValue(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||||
|
{
|
||||||
|
SetArray(key, ArrayElement { val, program }, allowUnset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
|
||||||
|
{
|
||||||
|
if (key.isInt()) {
|
||||||
|
auto index = key.asInt();
|
||||||
|
if (index >= 0 && index < size()) {
|
||||||
|
std::swap(values[index], val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeArray(int newLen)
|
||||||
|
{
|
||||||
|
if (newLen == -1 || size() == newLen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLen == 0) {
|
||||||
|
values.clear();
|
||||||
|
} else if (newLen > 0) {
|
||||||
|
if (newLen > ARRAY_MAX_SIZE) {
|
||||||
|
newLen = ARRAY_MAX_SIZE; // safety
|
||||||
|
}
|
||||||
|
|
||||||
|
values.resize(newLen);
|
||||||
|
} else if (newLen >= ARRAY_ACTION_SHUFFLE) {
|
||||||
|
ListSort(values, newLen, std::less<ArrayElement>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue ScanArray(const ProgramValue& value, Program* program)
|
||||||
|
{
|
||||||
|
auto element = ArrayElement { value, program };
|
||||||
|
for (int i = 0; i < size(); i++) {
|
||||||
|
if (element == values[i]) {
|
||||||
|
return ProgramValue(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ProgramValue(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<ArrayElement> values;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SFallArrayAssoc : public SFallArray {
|
||||||
|
public:
|
||||||
|
SFallArrayAssoc() = delete;
|
||||||
|
|
||||||
|
SFallArrayAssoc(unsigned int flags)
|
||||||
|
: SFallArray(flags)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int size()
|
||||||
|
{
|
||||||
|
return static_cast<int>(pairs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue GetArrayKey(int index, Program* program)
|
||||||
|
{
|
||||||
|
if (index < -1 || index > size()) {
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == -1) { // special index to indicate if array is associative
|
||||||
|
return ProgramValue(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairs[index].key.toValue(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue GetArray(const ProgramValue& key, Program* program)
|
||||||
|
{
|
||||||
|
auto keyEl = ArrayElement { key, program };
|
||||||
|
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
|
||||||
|
return pair.key == keyEl;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == pairs.end()) {
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto index = it - pairs.begin();
|
||||||
|
return pairs[index].value.toValue(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||||
|
{
|
||||||
|
auto keyEl = ArrayElement { key, program };
|
||||||
|
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
|
||||||
|
return pair.key == keyEl;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != pairs.end() && isReadOnly()) {
|
||||||
|
// don't update value of key
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowUnset && !isReadOnly() && val.isInt() && val.asInt() == 0) {
|
||||||
|
// after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset
|
||||||
|
if (it != pairs.end()) {
|
||||||
|
pairs.erase(it);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (it == pairs.end()) {
|
||||||
|
// size check
|
||||||
|
if (size() >= ARRAY_MAX_SIZE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs.push_back(KeyValuePair { std::move(keyEl), ArrayElement { val, program } });
|
||||||
|
} else {
|
||||||
|
auto index = it - pairs.begin();
|
||||||
|
pairs[index].value = ArrayElement { val, program };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
|
||||||
|
{
|
||||||
|
assert(false && "This method is not used for associative arrays thus it is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeArray(int newLen)
|
||||||
|
{
|
||||||
|
if (newLen == -1 || size() == newLen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allow to reduce number of elements (adding range of elements is meaningless for maps)
|
||||||
|
if (newLen >= 0 && newLen < size()) {
|
||||||
|
pairs.resize(newLen);
|
||||||
|
} else if (newLen < 0) {
|
||||||
|
if (newLen < (ARRAY_ACTION_SHUFFLE - 2)) return;
|
||||||
|
MapSort(newLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue ScanArray(const ProgramValue& value, Program* program)
|
||||||
|
{
|
||||||
|
auto valueEl = ArrayElement { value, program };
|
||||||
|
auto it = std::find_if(pairs.begin(), pairs.end(), [&valueEl](const KeyValuePair& pair) {
|
||||||
|
return pair.value == valueEl;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == pairs.end()) {
|
||||||
|
return ProgramValue(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->key.toValue(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct KeyValuePair {
|
||||||
|
ArrayElement key;
|
||||||
|
ArrayElement value;
|
||||||
|
};
|
||||||
|
|
||||||
|
void MapSort(int type)
|
||||||
|
{
|
||||||
|
bool sortByValue = false;
|
||||||
|
if (type < ARRAY_ACTION_SHUFFLE) {
|
||||||
|
type += 4;
|
||||||
|
sortByValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortByValue) {
|
||||||
|
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
|
||||||
|
return a.value < b.value;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
|
||||||
|
return a.key < b.key;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<KeyValuePair> pairs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SfallArraysState {
|
||||||
|
std::unordered_map<ArrayId, std::unique_ptr<SFallArray>> arrays;
|
||||||
|
std::unordered_set<ArrayId> temporaryArrayIds;
|
||||||
|
int nextArrayId = kInitialArrayId;
|
||||||
|
};
|
||||||
|
|
||||||
|
static SfallArraysState* _state = nullptr;
|
||||||
|
|
||||||
|
bool sfallArraysInit()
|
||||||
|
{
|
||||||
|
_state = new (std::nothrow) SfallArraysState();
|
||||||
|
if (_state == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfallArraysReset()
|
||||||
|
{
|
||||||
|
if (_state != nullptr) {
|
||||||
|
_state->arrays.clear();
|
||||||
|
_state->temporaryArrayIds.clear();
|
||||||
|
_state->nextArrayId = kInitialArrayId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfallArraysExit()
|
||||||
|
{
|
||||||
|
if (_state != nullptr) {
|
||||||
|
delete _state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayId CreateArray(int len, unsigned int flags)
|
||||||
|
{
|
||||||
|
flags = (flags & ~1); // reset 1 bit
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
flags |= SFALL_ARRAYFLAG_ASSOC;
|
||||||
|
} else if (len > ARRAY_MAX_SIZE) {
|
||||||
|
len = ARRAY_MAX_SIZE; // safecheck
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayId arrayId = _state->nextArrayId++;
|
||||||
|
|
||||||
|
if (flags & SFALL_ARRAYFLAG_ASSOC) {
|
||||||
|
_state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayAssoc>(flags)));
|
||||||
|
} else {
|
||||||
|
_state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayList>(len, flags)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayId CreateTempArray(int len, unsigned int flags)
|
||||||
|
{
|
||||||
|
ArrayId arrayId = CreateArray(len, flags);
|
||||||
|
_state->temporaryArrayIds.insert(arrayId);
|
||||||
|
return arrayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
SFallArray* get_array_by_id(ArrayId arrayId)
|
||||||
|
{
|
||||||
|
auto it = _state->arrays.find(arrayId);
|
||||||
|
if (it == _state->arrays.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program)
|
||||||
|
{
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr->GetArrayKey(index, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LenArray(ArrayId arrayId)
|
||||||
|
{
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program)
|
||||||
|
{
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return ProgramValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr->GetArray(key, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
|
||||||
|
{
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr->SetArray(key, val, allowUnset, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeArray(ArrayId arrayId)
|
||||||
|
{
|
||||||
|
// TODO: remove from saved_arrays
|
||||||
|
_state->arrays.erase(arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixArray(ArrayId arrayId)
|
||||||
|
{
|
||||||
|
_state->temporaryArrayIds.erase(arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteAllTempArrays()
|
||||||
|
{
|
||||||
|
for (auto it = _state->temporaryArrayIds.begin(); it != _state->temporaryArrayIds.end(); ++it) {
|
||||||
|
FreeArray(*it);
|
||||||
|
}
|
||||||
|
_state->temporaryArrayIds.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResizeArray(ArrayId arrayId, int newLen)
|
||||||
|
{
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr->ResizeArray(newLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program)
|
||||||
|
{
|
||||||
|
// CE: Sfall uses eponymous global variable which is always the id of the
|
||||||
|
// last created array.
|
||||||
|
ArrayId stackArrayId = _state->nextArrayId - 1;
|
||||||
|
|
||||||
|
auto arr = get_array_by_id(stackArrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size = arr->size();
|
||||||
|
if (size >= ARRAY_MAX_SIZE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.asInt() >= size) {
|
||||||
|
arr->ResizeArray(size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetArray(stackArrayId, key, val, false, program);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program)
|
||||||
|
{
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
if (arr == nullptr) {
|
||||||
|
return ProgramValue(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr->ScanArray(val, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayId StringSplit(const char* str, const char* split)
|
||||||
|
{
|
||||||
|
size_t splitLen = strlen(split);
|
||||||
|
|
||||||
|
ArrayId arrayId = CreateTempArray(0, 0);
|
||||||
|
auto arr = get_array_by_id(arrayId);
|
||||||
|
|
||||||
|
if (splitLen == 0) {
|
||||||
|
int count = static_cast<int>(strlen(str));
|
||||||
|
|
||||||
|
arr->ResizeArray(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
arr->SetArray(ProgramValue { i }, ArrayElement { &str[i], 1 }, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int count = 1;
|
||||||
|
const char* ptr = str;
|
||||||
|
while (true) {
|
||||||
|
const char* newptr = strstr(ptr, split);
|
||||||
|
if (newptr == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
ptr = newptr + splitLen;
|
||||||
|
}
|
||||||
|
arr->ResizeArray(count);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
ptr = str;
|
||||||
|
while (true) {
|
||||||
|
const char* newptr = strstr(ptr, split);
|
||||||
|
size_t len = (newptr != nullptr) ? newptr - ptr : strlen(ptr);
|
||||||
|
|
||||||
|
arr->SetArray(ProgramValue { count }, ArrayElement { ptr, len }, false);
|
||||||
|
|
||||||
|
if (newptr == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
ptr = newptr + splitLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arrayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fallout
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef FALLOUT_SFALL_ARRAYS_H_
|
||||||
|
#define FALLOUT_SFALL_ARRAYS_H_
|
||||||
|
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
namespace fallout {
|
||||||
|
|
||||||
|
#define SFALL_ARRAYFLAG_ASSOC (1) // is map
|
||||||
|
#define SFALL_ARRAYFLAG_CONSTVAL (2) // don't update value of key if the key exists in map
|
||||||
|
#define SFALL_ARRAYFLAG_RESERVED (4)
|
||||||
|
|
||||||
|
using ArrayId = unsigned int;
|
||||||
|
|
||||||
|
bool sfallArraysInit();
|
||||||
|
void sfallArraysReset();
|
||||||
|
void sfallArraysExit();
|
||||||
|
ArrayId CreateArray(int len, unsigned int flags);
|
||||||
|
ArrayId CreateTempArray(int len, unsigned int flags);
|
||||||
|
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program);
|
||||||
|
int LenArray(ArrayId arrayId);
|
||||||
|
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program);
|
||||||
|
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program);
|
||||||
|
void FreeArray(ArrayId arrayId);
|
||||||
|
void FixArray(ArrayId id);
|
||||||
|
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 StringSplit(const char* str, const char* split);
|
||||||
|
|
||||||
|
} // namespace fallout
|
||||||
|
|
||||||
|
#endif /* FALLOUT_SFALL_ARRAYS_H_ */
|
|
@ -1,5 +1,7 @@
|
||||||
#include "sfall_opcodes.h"
|
#include "sfall_opcodes.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "animation.h"
|
#include "animation.h"
|
||||||
#include "art.h"
|
#include "art.h"
|
||||||
#include "combat.h"
|
#include "combat.h"
|
||||||
|
@ -12,8 +14,10 @@
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "mouse.h"
|
#include "mouse.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
|
#include "party_member.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "scripts.h"
|
#include "scripts.h"
|
||||||
|
#include "sfall_arrays.h"
|
||||||
#include "sfall_global_vars.h"
|
#include "sfall_global_vars.h"
|
||||||
#include "sfall_lists.h"
|
#include "sfall_lists.h"
|
||||||
#include "stat.h"
|
#include "stat.h"
|
||||||
|
@ -453,6 +457,53 @@ static void op_tile_under_cursor(Program* program)
|
||||||
programStackPushInteger(program, tile);
|
programStackPushInteger(program, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// substr
|
||||||
|
static void opSubstr(Program* program)
|
||||||
|
{
|
||||||
|
auto length = programStackPopInteger(program);
|
||||||
|
auto startPos = programStackPopInteger(program);
|
||||||
|
const char* str = programStackPopString(program);
|
||||||
|
|
||||||
|
char buf[5120] = { 0 };
|
||||||
|
|
||||||
|
int len = strlen(str);
|
||||||
|
|
||||||
|
if (startPos < 0) {
|
||||||
|
startPos += len; // start from end
|
||||||
|
if (startPos < 0) {
|
||||||
|
startPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
length += len - startPos; // cutoff at end
|
||||||
|
if (length == 0) {
|
||||||
|
programStackPushString(program, buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
length = abs(length); // length can't be negative
|
||||||
|
}
|
||||||
|
|
||||||
|
// check position
|
||||||
|
if (startPos >= len) {
|
||||||
|
// start position is out of string length, return empty string
|
||||||
|
programStackPushString(program, buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == 0 || length + startPos > len) {
|
||||||
|
length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > sizeof(buf) - 1) {
|
||||||
|
length = sizeof(buf) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf, &str[startPos], length);
|
||||||
|
buf[length] = '\0';
|
||||||
|
programStackPushString(program, buf);
|
||||||
|
}
|
||||||
|
|
||||||
// strlen
|
// strlen
|
||||||
static void opGetStringLength(Program* program)
|
static void opGetStringLength(Program* program)
|
||||||
{
|
{
|
||||||
|
@ -485,6 +536,146 @@ static void opGetMessage(Program* program)
|
||||||
programStackPushString(program, text);
|
programStackPushString(program, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get_array_key
|
||||||
|
static void opGetArrayKey(Program* program)
|
||||||
|
{
|
||||||
|
auto index = programStackPopInteger(program);
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
auto value = GetArrayKey(arrayId, index, program);
|
||||||
|
programStackPushValue(program, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create_array
|
||||||
|
static void opCreateArray(Program* program)
|
||||||
|
{
|
||||||
|
auto flags = programStackPopInteger(program);
|
||||||
|
auto len = programStackPopInteger(program);
|
||||||
|
auto arrayId = CreateArray(len, flags);
|
||||||
|
programStackPushInteger(program, arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// temp_array
|
||||||
|
static void opTempArray(Program* program)
|
||||||
|
{
|
||||||
|
auto flags = programStackPopInteger(program);
|
||||||
|
auto len = programStackPopInteger(program);
|
||||||
|
auto arrayId = CreateTempArray(len, flags);
|
||||||
|
programStackPushInteger(program, arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix_array
|
||||||
|
static void opFixArray(Program* program)
|
||||||
|
{
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
FixArray(arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// string_split
|
||||||
|
static void opStringSplit(Program* program)
|
||||||
|
{
|
||||||
|
auto split = programStackPopString(program);
|
||||||
|
auto str = programStackPopString(program);
|
||||||
|
auto arrayId = StringSplit(str, split);
|
||||||
|
programStackPushInteger(program, arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set_array
|
||||||
|
static void opSetArray(Program* program)
|
||||||
|
{
|
||||||
|
auto value = programStackPopValue(program);
|
||||||
|
auto key = programStackPopValue(program);
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
SetArray(arrayId, key, value, true, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrayexpr
|
||||||
|
static void opStackArray(Program* program)
|
||||||
|
{
|
||||||
|
auto value = programStackPopValue(program);
|
||||||
|
auto key = programStackPopValue(program);
|
||||||
|
auto returnValue = StackArray(key, value, program);
|
||||||
|
programStackPushInteger(program, returnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scan array
|
||||||
|
static void opScanArray(Program* program)
|
||||||
|
{
|
||||||
|
auto value = programStackPopValue(program);
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
auto returnValue = ScanArray(arrayId, value, program);
|
||||||
|
programStackPushValue(program, returnValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get_array
|
||||||
|
static void opGetArray(Program* program)
|
||||||
|
{
|
||||||
|
auto key = programStackPopValue(program);
|
||||||
|
auto arrayId = programStackPopValue(program);
|
||||||
|
|
||||||
|
if (arrayId.isInt()) {
|
||||||
|
auto value = GetArray(arrayId.integerValue, key, program);
|
||||||
|
programStackPushValue(program, value);
|
||||||
|
} else if (arrayId.isString() && key.isInt()) {
|
||||||
|
auto pos = key.asInt();
|
||||||
|
auto str = programGetString(program, arrayId.opcode, arrayId.integerValue);
|
||||||
|
|
||||||
|
char buf[2] = { 0 };
|
||||||
|
if (pos < strlen(str)) {
|
||||||
|
buf[0] = str[pos];
|
||||||
|
programStackPushString(program, buf);
|
||||||
|
} else {
|
||||||
|
programStackPushString(program, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// free_array
|
||||||
|
static void opFreeArray(Program* program)
|
||||||
|
{
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
FreeArray(arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// len_array
|
||||||
|
static void opLenArray(Program* program)
|
||||||
|
{
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
programStackPushInteger(program, LenArray(arrayId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize_array
|
||||||
|
static void opResizeArray(Program* program)
|
||||||
|
{
|
||||||
|
auto newLen = programStackPopInteger(program);
|
||||||
|
auto arrayId = programStackPopInteger(program);
|
||||||
|
ResizeArray(arrayId, newLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// party_member_list
|
||||||
|
static void opPartyMemberList(Program* program)
|
||||||
|
{
|
||||||
|
auto includeHidden = programStackPopInteger(program);
|
||||||
|
auto objects = get_all_party_members_objects(includeHidden);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
programStackPushInteger(program, arrayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// type_of
|
||||||
|
static void opTypeOf(Program* program)
|
||||||
|
{
|
||||||
|
auto value = programStackPopValue(program);
|
||||||
|
if (value.isInt()) {
|
||||||
|
programStackPushInteger(program, 1);
|
||||||
|
} else if (value.isFloat()) {
|
||||||
|
programStackPushInteger(program, 2);
|
||||||
|
} else {
|
||||||
|
programStackPushInteger(program, 3);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// round
|
// round
|
||||||
static void opRound(Program* program)
|
static void opRound(Program* program)
|
||||||
{
|
{
|
||||||
|
@ -619,15 +810,30 @@ void sfallOpcodesInit()
|
||||||
interpreterRegisterOpcode(0x8220, opGetScreenWidth);
|
interpreterRegisterOpcode(0x8220, opGetScreenWidth);
|
||||||
interpreterRegisterOpcode(0x8221, opGetScreenHeight);
|
interpreterRegisterOpcode(0x8221, opGetScreenHeight);
|
||||||
interpreterRegisterOpcode(0x8228, op_get_attack_type);
|
interpreterRegisterOpcode(0x8228, op_get_attack_type);
|
||||||
|
interpreterRegisterOpcode(0x822D, opCreateArray);
|
||||||
|
interpreterRegisterOpcode(0x822E, opSetArray);
|
||||||
|
interpreterRegisterOpcode(0x822F, opGetArray);
|
||||||
|
interpreterRegisterOpcode(0x8230, opFreeArray);
|
||||||
|
interpreterRegisterOpcode(0x8231, opLenArray);
|
||||||
|
interpreterRegisterOpcode(0x8232, opResizeArray);
|
||||||
|
interpreterRegisterOpcode(0x8233, opTempArray);
|
||||||
|
interpreterRegisterOpcode(0x8234, opFixArray);
|
||||||
|
interpreterRegisterOpcode(0x8235, opStringSplit);
|
||||||
interpreterRegisterOpcode(0x8237, opParseInt);
|
interpreterRegisterOpcode(0x8237, opParseInt);
|
||||||
interpreterRegisterOpcode(0x8238, op_atof);
|
interpreterRegisterOpcode(0x8238, op_atof);
|
||||||
|
interpreterRegisterOpcode(0x8239, opScanArray);
|
||||||
interpreterRegisterOpcode(0x824B, op_tile_under_cursor);
|
interpreterRegisterOpcode(0x824B, op_tile_under_cursor);
|
||||||
|
interpreterRegisterOpcode(0x824E, opSubstr);
|
||||||
interpreterRegisterOpcode(0x824F, opGetStringLength);
|
interpreterRegisterOpcode(0x824F, opGetStringLength);
|
||||||
|
interpreterRegisterOpcode(0x8253, opTypeOf);
|
||||||
|
interpreterRegisterOpcode(0x8256, opGetArrayKey);
|
||||||
|
interpreterRegisterOpcode(0x8257, opStackArray);
|
||||||
interpreterRegisterOpcode(0x8263, op_power);
|
interpreterRegisterOpcode(0x8263, op_power);
|
||||||
interpreterRegisterOpcode(0x826B, opGetMessage);
|
|
||||||
interpreterRegisterOpcode(0x8267, opRound);
|
interpreterRegisterOpcode(0x8267, opRound);
|
||||||
|
interpreterRegisterOpcode(0x826B, opGetMessage);
|
||||||
interpreterRegisterOpcode(0x826E, op_make_straight_path);
|
interpreterRegisterOpcode(0x826E, op_make_straight_path);
|
||||||
interpreterRegisterOpcode(0x826F, op_obj_blocking_at);
|
interpreterRegisterOpcode(0x826F, op_obj_blocking_at);
|
||||||
|
interpreterRegisterOpcode(0x8271, opPartyMemberList);
|
||||||
interpreterRegisterOpcode(0x8274, opArtExists);
|
interpreterRegisterOpcode(0x8274, opArtExists);
|
||||||
interpreterRegisterOpcode(0x827F, op_div);
|
interpreterRegisterOpcode(0x827F, op_div);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue