fallout2-ce/src/queue.c

550 lines
14 KiB
C
Raw Normal View History

2022-05-19 01:51:26 -07:00
#include "queue.h"
#include "actions.h"
#include "critter.h"
#include "display_monitor.h"
#include "game.h"
#include "game_sound.h"
#include "item.h"
#include "map.h"
#include "memory.h"
#include "message.h"
#include "object.h"
#include "perk.h"
#include "proto.h"
#include "proto_instance.h"
#include "scripts.h"
// Last queue list node found during [queueFindFirstEvent] and
// [queueFindNextEvent] calls.
//
// 0x51C690
QueueListNode* gLastFoundQueueListNode = NULL;
// 0x6648C0
QueueListNode* gQueueListHead;
// 0x51C540
EventTypeDescription gEventTypeDescriptions[EVENT_TYPE_COUNT] = {
{ drugEffectEventProcess, internal_free, drugEffectEventRead, drugEffectEventWrite, true, _item_d_clear },
{ knockoutEventProcess, NULL, NULL, NULL, true, _critter_wake_clear },
{ withdrawalEventProcess, internal_free, withdrawalEventRead, withdrawalEventWrite, true, _item_wd_clear },
{ scriptEventProcess, internal_free, scriptEventRead, scriptEventWrite, true, NULL },
{ gameTimeEventProcess, NULL, NULL, NULL, true, NULL },
{ poisonEventProcess, NULL, NULL, NULL, false, NULL },
{ radiationEventProcess, internal_free, radiationEventRead, radiationEventWrite, false, NULL },
{ flareEventProcess, NULL, NULL, NULL, true, flareEventProcess },
{ explosionEventProcess, NULL, NULL, NULL, true, _queue_explode_exit },
{ miscItemTrickleEventProcess, NULL, NULL, NULL, true, _item_m_turn_off_from_queue },
{ sneakEventProcess, NULL, NULL, NULL, true, _critter_sneak_clear },
{ explosionFailureEventProcess, NULL, NULL, NULL, true, _queue_explode_exit },
{ mapUpdateEventProcess, NULL, NULL, NULL, true, NULL },
{ ambientSoundEffectEventProcess, internal_free, NULL, NULL, true, NULL },
};
// 0x4A2320
void queueInit()
{
gQueueListHead = NULL;
}
// 0x4A2330
int queueExit()
{
queueClear();
return 0;
}
// 0x4A2338
int queueLoad(File* stream)
{
int count;
if (fileReadInt32(stream, &count) == -1) {
return -1;
}
QueueListNode* oldListHead = gQueueListHead;
gQueueListHead = NULL;
QueueListNode** nextPtr = &gQueueListHead;
int rc = 0;
for (int index = 0; index < count; index += 1) {
QueueListNode* queueListNode = internal_malloc(sizeof(*queueListNode));
if (queueListNode == NULL) {
rc = -1;
break;
}
if (fileReadInt32(stream, &(queueListNode->time)) == -1) {
internal_free(queueListNode);
rc = -1;
break;
}
if (fileReadInt32(stream, &(queueListNode->type)) == -1) {
internal_free(queueListNode);
rc = -1;
break;
}
int objectId;
if (fileReadInt32(stream, &objectId) == -1) {
internal_free(queueListNode);
rc = -1;
break;
}
Object* obj;
if (objectId == -2) {
obj = NULL;
} else {
obj = objectFindFirst();
while (obj != NULL) {
obj = _inven_find_id(obj, objectId);
if (obj != NULL) {
break;
}
obj = objectFindNext();
}
}
queueListNode->owner = obj;
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[queueListNode->type]);
if (eventTypeDescription->readProc != NULL) {
if (eventTypeDescription->readProc(stream, &(queueListNode->data)) == -1) {
internal_free(queueListNode);
rc = -1;
break;
}
} else {
queueListNode->data = NULL;
}
queueListNode->next = NULL;
*nextPtr = queueListNode;
nextPtr = &(queueListNode->next);
}
if (rc == -1) {
while (gQueueListHead != NULL) {
QueueListNode* next = gQueueListHead->next;
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[gQueueListHead->type]);
if (eventTypeDescription->freeProc != NULL) {
eventTypeDescription->freeProc(gQueueListHead->data);
}
internal_free(gQueueListHead);
gQueueListHead = next;
}
}
if (oldListHead != NULL) {
QueueListNode** v13 = &gQueueListHead;
QueueListNode* v15;
do {
while (true) {
QueueListNode* v14 = *v13;
if (v14 == NULL) {
break;
}
if (v14->time > oldListHead->time) {
break;
}
v13 = &(v14->next);
}
v15 = oldListHead->next;
oldListHead->next = *v13;
*v13 = oldListHead;
oldListHead = v15;
} while (v15 != NULL);
}
return rc;
}
// 0x4A24E0
int queueSave(File* stream)
{
QueueListNode* queueListNode;
int count = 0;
queueListNode = gQueueListHead;
while (queueListNode != NULL) {
count += 1;
queueListNode = queueListNode->next;
}
if (fileWriteInt32(stream, count) == -1) {
return -1;
}
queueListNode = gQueueListHead;
while (queueListNode != NULL) {
Object* object = queueListNode->owner;
int objectId = object != NULL ? object->id : -2;
if (fileWriteInt32(stream, queueListNode->time) == -1) {
return -1;
}
if (fileWriteInt32(stream, queueListNode->type) == -1) {
return -1;
}
if (fileWriteInt32(stream, objectId) == -1) {
return -1;
}
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[queueListNode->type]);
if (eventTypeDescription->writeProc != NULL) {
if (eventTypeDescription->writeProc(stream, queueListNode->data) == -1) {
return -1;
}
}
queueListNode = queueListNode->next;
}
return 0;
}
// 0x4A258C
int queueAddEvent(int delay, Object* obj, void* data, int eventType)
{
QueueListNode* newQueueListNode = internal_malloc(sizeof(QueueListNode));
if (newQueueListNode == NULL) {
return -1;
}
int v1 = gameTimeGetTime();
int v2 = v1 + delay;
newQueueListNode->time = v2;
newQueueListNode->type = eventType;
newQueueListNode->owner = obj;
newQueueListNode->data = data;
if (obj != NULL) {
obj->flags |= OBJECT_FLAG_0x2000;
}
QueueListNode** v3 = &gQueueListHead;
if (gQueueListHead != NULL) {
QueueListNode* v4;
do {
v4 = *v3;
if (v2 < v4->time) {
break;
}
v3 = &(v4->next);
} while (v4->next != NULL);
}
newQueueListNode->next = *v3;
*v3 = newQueueListNode;
return 0;
}
// 0x4A25F4
int queueRemoveEvents(Object* owner)
{
QueueListNode* queueListNode = gQueueListHead;
QueueListNode** queueListNodePtr = &gQueueListHead;
while (queueListNode) {
if (queueListNode->owner == owner) {
QueueListNode* temp = queueListNode;
queueListNode = queueListNode->next;
*queueListNodePtr = queueListNode;
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[temp->type]);
if (eventTypeDescription->freeProc != NULL) {
eventTypeDescription->freeProc(temp->data);
}
internal_free(temp);
} else {
queueListNodePtr = &(queueListNode->next);
queueListNode = queueListNode->next;
}
}
return 0;
}
// 0x4A264C
int queueRemoveEventsByType(Object* owner, int eventType)
{
QueueListNode* queueListNode = gQueueListHead;
QueueListNode** queueListNodePtr = &gQueueListHead;
while (queueListNode) {
if (queueListNode->owner == owner && queueListNode->type == eventType) {
QueueListNode* temp = queueListNode;
queueListNode = queueListNode->next;
*queueListNodePtr = queueListNode;
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[temp->type]);
if (eventTypeDescription->freeProc != NULL) {
eventTypeDescription->freeProc(temp->data);
}
internal_free(temp);
} else {
queueListNodePtr = &(queueListNode->next);
queueListNode = queueListNode->next;
}
}
return 0;
}
// Returns true if there is at least one event of given type scheduled.
//
// 0x4A26A8
bool queueHasEvent(Object* owner, int eventType)
{
QueueListNode* queueListEvent = gQueueListHead;
while (queueListEvent != NULL) {
if (owner == queueListEvent->owner && eventType == queueListEvent->type) {
return true;
}
queueListEvent = queueListEvent->next;
}
return false;
}
// 0x4A26D0
int queueProcessEvents()
{
int time = gameTimeGetTime();
int v1 = 0;
while (gQueueListHead != NULL) {
QueueListNode* queueListNode = gQueueListHead;
if (time < queueListNode->time || v1 != 0) {
break;
}
gQueueListHead = queueListNode->next;
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[queueListNode->type]);
v1 = eventTypeDescription->handlerProc(queueListNode->owner, queueListNode->data);
if (eventTypeDescription->freeProc != NULL) {
eventTypeDescription->freeProc(queueListNode->data);
}
internal_free(queueListNode);
}
return v1;
}
// 0x4A2748
void queueClear()
{
QueueListNode* queueListNode = gQueueListHead;
while (queueListNode != NULL) {
QueueListNode* next = queueListNode->next;
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[queueListNode->type]);
if (eventTypeDescription->freeProc != NULL) {
eventTypeDescription->freeProc(queueListNode->data);
}
internal_free(queueListNode);
queueListNode = next;
}
gQueueListHead = NULL;
}
// 0x4A2790
void _queue_clear_type(int eventType, QueueEventHandler* fn)
{
QueueListNode** ptr = &gQueueListHead;
QueueListNode* curr = *ptr;
while (curr != NULL) {
if (eventType == curr->type) {
QueueListNode* tmp = curr;
*ptr = curr->next;
curr = *ptr;
if (fn != NULL && fn(tmp->owner, tmp->data) != 1) {
*ptr = tmp;
ptr = &(tmp->next);
} else {
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[tmp->type]);
if (eventTypeDescription->freeProc != NULL) {
eventTypeDescription->freeProc(tmp->data);
}
internal_free(tmp);
}
} else {
ptr = &(curr->next);
curr = *ptr;
}
}
}
// TODO: Make unsigned.
//
// 0x4A2808
int queueGetNextEventTime()
{
if (gQueueListHead == NULL) {
return 0;
}
return gQueueListHead->time;
}
// 0x4A281C
int flareEventProcess(Object* obj, void* data)
{
_obj_destroy(obj);
return 1;
}
// 0x4A2828
int explosionEventProcess(Object* obj, void* data)
{
return _queue_do_explosion_(obj, true);
}
// 0x4A2830
int _queue_explode_exit(Object* obj, void* data)
{
return _queue_do_explosion_(obj, false);
}
// 0x4A2834
int _queue_do_explosion_(Object* explosive, bool a2)
{
int tile;
int elevation;
Object* owner = objectGetOwner(explosive);
if (owner) {
tile = owner->tile;
elevation = owner->elevation;
} else {
tile = explosive->tile;
elevation = explosive->elevation;
}
int maxDamage;
int minDamage;
if (explosive->pid == PROTO_ID_DYNAMITE_I || explosive->pid == PROTO_ID_DYNAMITE_II) {
// Dynamite
minDamage = 30;
maxDamage = 50;
} else {
// Plastic explosive
minDamage = 40;
maxDamage = 80;
}
// FIXME: I guess this is a little bit wrong, dude can never be null, I
// guess it needs to check if owner is dude.
if (gDude != NULL) {
if (perkHasRank(gDude, PERK_DEMOLITION_EXPERT)) {
maxDamage += 10;
minDamage += 10;
}
}
if (actionExplode(tile, elevation, minDamage, maxDamage, gDude, a2) == -2) {
queueAddEvent(50, explosive, NULL, EVENT_TYPE_EXPLOSION);
} else {
_obj_destroy(explosive);
}
return 1;
}
// 0x4A28E4
int explosionFailureEventProcess(Object* obj, void* data)
{
MessageListItem msg;
// Due to your inept handling, the explosive detonates prematurely.
msg.num = 4000;
if (messageListGetItem(&gMiscMessageList, &msg)) {
displayMonitorAddMessage(msg.text);
}
return _queue_do_explosion_(obj, true);
}
// 0x4A2920
void _queue_leaving_map()
{
for (int eventType = 0; eventType < EVENT_TYPE_COUNT; eventType++) {
EventTypeDescription* eventTypeDescription = &(gEventTypeDescriptions[eventType]);
if (eventTypeDescription->field_10) {
_queue_clear_type(eventType, eventTypeDescription->field_14);
}
}
}
// 0x4A294C
bool queueIsEmpty()
{
return gQueueListHead == NULL;
}
// 0x4A295C
void* queueFindFirstEvent(Object* owner, int eventType)
{
QueueListNode* queueListNode = gQueueListHead;
while (queueListNode != NULL) {
if (owner == queueListNode->owner && eventType == queueListNode->type) {
gLastFoundQueueListNode = queueListNode;
return queueListNode->data;
}
queueListNode = queueListNode->next;
}
gLastFoundQueueListNode = NULL;
return NULL;
}
// 0x4A2994
void* queueFindNextEvent(Object* owner, int eventType)
{
if (gLastFoundQueueListNode != NULL) {
QueueListNode* queueListNode = gLastFoundQueueListNode->next;
while (queueListNode != NULL) {
if (owner == queueListNode->owner && eventType == queueListNode->type) {
gLastFoundQueueListNode = queueListNode;
return queueListNode->data;
}
queueListNode = queueListNode->next;
}
}
gLastFoundQueueListNode = NULL;
return NULL;
}