mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-13 21:31:53 +00:00
4181 lines
116 KiB
C++
4181 lines
116 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* aint32 with this program; if not, write to the Free Software
|
|
*
|
|
*
|
|
* Based on the original sources
|
|
* Faery Tale II -- The Halls of the Dead
|
|
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
|
|
*/
|
|
|
|
#include "saga2/saga2.h"
|
|
#include "saga2/fta.h"
|
|
#include "saga2/cmisc.h"
|
|
#include "saga2/actor.h"
|
|
#include "saga2/task.h"
|
|
#include "saga2/motion.h"
|
|
#include "saga2/band.h"
|
|
#include "saga2/sensor.h"
|
|
#include "saga2/tilemode.h"
|
|
#include "saga2/tile.h"
|
|
|
|
namespace Saga2 {
|
|
|
|
bool actorTasksPaused;
|
|
|
|
/* ===================================================================== *
|
|
Prototypes
|
|
* ===================================================================== */
|
|
|
|
void readTask(TaskID id, Common::InSaveFile *in);
|
|
|
|
// Return the number of bytes necessary to create an archive of the
|
|
// specified Task
|
|
int32 taskArchiveSize(Task *t);
|
|
|
|
void writeTask(Task *t, Common::MemoryWriteStreamDynamic *out);
|
|
|
|
/* ===================================================================== *
|
|
Utility functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Compute a repulsion vector based on an array of repulsor vectors
|
|
|
|
TilePoint computeRepulsionVector(
|
|
TilePoint *repulsorVectorArray,
|
|
int16 *repulsorStrengthArray,
|
|
int numRepulsors) {
|
|
int i;
|
|
TilePoint repulsionVector(0, 0, 0);
|
|
|
|
for (i = 0; i < numRepulsors; i++) {
|
|
int16 repulsorWeight,
|
|
repulsorDist;
|
|
|
|
repulsorDist = repulsorVectorArray[i].quickHDistance()
|
|
+ ABS(repulsorVectorArray[i].z);
|
|
repulsorWeight =
|
|
repulsorDist != 0
|
|
? 64 * 64 / (repulsorDist * repulsorDist)
|
|
: 64 * 64;
|
|
|
|
repulsionVector +=
|
|
(-repulsorVectorArray[i]
|
|
* repulsorStrengthArray[i]
|
|
* repulsorWeight)
|
|
/ 16;
|
|
}
|
|
|
|
return repulsionVector;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
TaskStackList class
|
|
* ===================================================================== */
|
|
|
|
const int numTaskStacks = 320;
|
|
|
|
// Manages the memory used for the TaskStack's. There will
|
|
// only be one global instantiation of this class
|
|
class TaskStackList {
|
|
TaskStack *_list[numTaskStacks];
|
|
|
|
public:
|
|
// Constructor -- initial construction
|
|
TaskStackList();
|
|
|
|
// Destructor
|
|
~TaskStackList();
|
|
|
|
void read(Common::InSaveFile *in);
|
|
|
|
// Return the number of bytes needed to make an archive of the
|
|
// TaskStackList
|
|
int32 archiveSize();
|
|
|
|
void write(Common::MemoryWriteStreamDynamic *out);
|
|
|
|
// Place a TaskStack from the inactive list into the active
|
|
// list.
|
|
TaskStack *newTaskStack(Actor *a);
|
|
|
|
void newTaskStack(TaskStack *p);
|
|
|
|
void newTaskStack(TaskStack *p, TaskID id);
|
|
|
|
// Place a TaskStack back into the inactive list.
|
|
void deleteTaskStack(TaskStack *p);
|
|
|
|
// Return the specified TaskStack's ID
|
|
TaskStackID getTaskStackID(TaskStack *ts) {
|
|
for (int i = 0; i < numTaskStacks; i++)
|
|
if (_list[i] == ts)
|
|
return i;
|
|
|
|
error("getTaskStackID(): Unknown stack %p", (void *)ts);
|
|
}
|
|
|
|
// Return a pointer to a TaskStack given a TaskStackID
|
|
TaskStack *getTaskStackAddress(TaskStackID id) {
|
|
assert(id >= 0 && id < numTaskStacks);
|
|
return _list[id];
|
|
}
|
|
|
|
// Run through the TaskStacks in the active list and update
|
|
// each.
|
|
void updateTaskStacks();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskStackList constructor -- simply place each element the array in
|
|
// the inactive list
|
|
|
|
TaskStackList::TaskStackList() {
|
|
for (int i = 0; i < numTaskStacks; i++)
|
|
_list[i] = nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskStackList destructor
|
|
|
|
TaskStackList::~TaskStackList() {
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
if (_list[i] == nullptr)
|
|
continue;
|
|
|
|
delete _list[i];
|
|
_list[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
void TaskStackList::read(Common::InSaveFile *in) {
|
|
int16 taskStackCount;
|
|
|
|
// Get the count of task stacks and increment the buffer pointer
|
|
taskStackCount = in->readSint16LE();
|
|
debugC(3, kDebugSaveload, "... taskStackCount = %d", taskStackCount);
|
|
|
|
// Iterate through the archive data, reconstructing the TaskStacks
|
|
for (int i = 0; i < taskStackCount; i++) {
|
|
TaskStackID id;
|
|
TaskStack *ts;
|
|
|
|
// Retreive the TaskStack's id number
|
|
id = in->readSint16LE();
|
|
debugC(3, kDebugSaveload, "Loading Task Stack %d", id);
|
|
|
|
ts = new TaskStack;
|
|
newTaskStack(ts, id);
|
|
|
|
ts->read(in);
|
|
|
|
// Plug this TaskStack into the Actor
|
|
ts->getActor()->_curTask = ts;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to archive this TaskStackList
|
|
|
|
int32 TaskStackList::archiveSize() {
|
|
int32 size = sizeof(int16);
|
|
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
size += sizeof(TaskStackID);
|
|
|
|
if (_list[i])
|
|
size += _list[i]->archiveSize();
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void TaskStackList::write(Common::MemoryWriteStreamDynamic *out) {
|
|
int16 taskStackCount = 0;
|
|
|
|
// Count the active task stacks
|
|
for (int i = 0; i < numTaskStacks; i++)
|
|
if (_list[i])
|
|
taskStackCount++;
|
|
|
|
// Store the task stack count in the archive buffer
|
|
out->writeSint16LE(taskStackCount);
|
|
debugC(3, kDebugSaveload, "... taskStackCount = %d", taskStackCount);
|
|
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
if (_list[i] == nullptr)
|
|
continue;
|
|
|
|
debugC(3, kDebugSaveload, "Saving Task Stack %d", i);
|
|
|
|
TaskStack *ts = _list[i];
|
|
out->writeSint16LE(i);
|
|
ts->write(out);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Place a TaskStack into the active list and return its address
|
|
|
|
TaskStack *TaskStackList::newTaskStack(Actor *a) {
|
|
for (int i = 0; i < numTaskStacks; i++)
|
|
if (!_list[i]) {
|
|
_list[i] = new TaskStack(a);
|
|
|
|
return _list[i];
|
|
}
|
|
|
|
warning("Too many task stacks in the list, > %d", numTaskStacks);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void TaskStackList::newTaskStack(TaskStack *p) {
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
if (_list[i] == p) {
|
|
warning("TaskStack %d (%p) already added", i, (void *)p);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
debugC(1, kDebugTasks, "List: %p Adding task stack %p", (void *)this, (void *)p);
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
if (!_list[i]) {
|
|
_list[i] = p;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TaskStackList::newTaskStack(TaskStack *p, TaskID id) {
|
|
if (_list[id])
|
|
error("Task already exists");
|
|
_list[id] = p;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Remove the specified TaskStack from the active list and place it
|
|
// back into the inactive list
|
|
|
|
void TaskStackList::deleteTaskStack(TaskStack *p) {
|
|
debugC(1, kDebugTasks, "List: %p Deleting task stack %p", (void *)this, (void *)p);
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
if (_list[i] == p) {
|
|
_list[i] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Iterate through all of the TaskStacks in the active list and call
|
|
// their update function
|
|
|
|
void TaskStackList::updateTaskStacks() {
|
|
for (int i = 0; i < numTaskStacks; i++) {
|
|
if (_list[i]) {
|
|
TaskStack *ts = _list[i];
|
|
TaskResult result;
|
|
|
|
// Update the task stack and delete it if it is done
|
|
if ((result = ts->update()) != kTaskNotDone) {
|
|
Actor *a = ts->getActor();
|
|
assert(a != nullptr);
|
|
|
|
a->handleTaskCompletion(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
Misc. task stack management functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Simply pass this call to the stackList member function,
|
|
// updateTaskStacks().
|
|
|
|
void updateActorTasks() {
|
|
if (!actorTasksPaused)
|
|
g_vm->_stackList->updateTaskStacks();
|
|
}
|
|
|
|
void pauseActorTasks() {
|
|
actorTasksPaused = true;
|
|
}
|
|
void resumeActorTasks() {
|
|
actorTasksPaused = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the stackList member function newTaskStack() to get a pointer
|
|
// to a new TaskStack
|
|
|
|
TaskStack *newTaskStack(Actor *a) {
|
|
return g_vm->_stackList->newTaskStack(a);
|
|
}
|
|
|
|
void newTaskStack(TaskStack *p) {
|
|
return g_vm->_stackList->newTaskStack(p);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the stackList member function deleteTaskStack() to dispose of
|
|
// a previously allocated TaskStack
|
|
|
|
void deleteTaskStack(TaskStack *p) {
|
|
g_vm->_stackList->deleteTaskStack(p);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the specified TaskStack's ID
|
|
|
|
TaskStackID getTaskStackID(TaskStack *ts) {
|
|
return g_vm->_stackList->getTaskStackID(ts);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return a pointer to a TaskStack given a TaskStackID
|
|
|
|
TaskStack *getTaskStackAddress(TaskStackID id) {
|
|
return g_vm->_stackList->getTaskStackAddress(id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Initialize the stackList
|
|
|
|
void initTaskStacks() {
|
|
g_vm->_stackList = new TaskStackList;
|
|
}
|
|
|
|
void saveTaskStacks(Common::OutSaveFile *outS) {
|
|
debugC(2, kDebugSaveload, "Saving Task Stacks");
|
|
|
|
outS->write("TSTK", 4);
|
|
CHUNK_BEGIN;
|
|
g_vm->_stackList->write(out);
|
|
CHUNK_END;
|
|
}
|
|
|
|
void loadTaskStacks(Common::InSaveFile *in, int32 chunkSize) {
|
|
debugC(2, kDebugSaveload, "Loading Task Stacks");
|
|
|
|
// If there is no saved data, simply call the default constructor
|
|
if (chunkSize == 0) {
|
|
g_vm->_stackList = new TaskStackList;
|
|
return;
|
|
}
|
|
|
|
// Reconstruct stackList from archived data
|
|
g_vm->_stackList = new TaskStackList;
|
|
g_vm->_stackList->read(in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Cleanup the stackList
|
|
|
|
void cleanupTaskStacks() {
|
|
// Simply call stackList's destructor
|
|
delete g_vm->_stackList;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
TaskList class
|
|
* ===================================================================== */
|
|
|
|
const int numTasks = 640;
|
|
|
|
// Manages the memory used for the Task's. There will only be one
|
|
// global instantiation of this class
|
|
class TaskList {
|
|
|
|
int _size;
|
|
Task *_list[numTasks];
|
|
|
|
public:
|
|
// Constructor -- initial construction
|
|
TaskList();
|
|
|
|
// Destructor
|
|
~TaskList();
|
|
|
|
void read(Common::InSaveFile *in);
|
|
|
|
// Return the number of bytes necessary to archive this task list
|
|
// in a buffer
|
|
int32 archiveSize();
|
|
|
|
void write(Common::MemoryWriteStreamDynamic *out);
|
|
|
|
// Place a Task from the inactive list into the active
|
|
// list.
|
|
void newTask(Task *t);
|
|
void newTask(Task *t, TaskID id);
|
|
|
|
// Place a Task back into the inactive list.
|
|
void deleteTask(Task *t);
|
|
|
|
// Return the specified Task's ID
|
|
TaskID getTaskID(Task *t) {
|
|
for (int i = 0; i < numTasks; i++)
|
|
if (_list[i] == t)
|
|
return i;
|
|
|
|
error("getTaskID: unknown task %p", (void *)t);
|
|
}
|
|
|
|
// Return a pointer to a Task given a TaskID
|
|
Task *getTaskAddress(TaskID id) {
|
|
assert(id >= 0 && id < numTasks);
|
|
return _list[id];
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskList constructor -- simply place each element of the array in
|
|
// the inactive list
|
|
|
|
TaskList::TaskList() {
|
|
_size = 0;
|
|
for (int i = 0; i < numTasks; i++)
|
|
_list[i] = nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskList destructor
|
|
|
|
TaskList::~TaskList() {
|
|
for (int i = 0; i < numTasks; i++) {
|
|
if (_list[i] == nullptr)
|
|
continue;
|
|
|
|
delete _list[i];
|
|
_list[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
void TaskList::read(Common::InSaveFile *in) {
|
|
int16 taskCount;
|
|
|
|
// Get the count of tasks and increment the buffer pointer
|
|
taskCount = in->readSint16LE();
|
|
debugC(3, kDebugSaveload, "... taskCount = %d", taskCount);
|
|
|
|
// Iterate through the archive data, reconstructing the Tasks
|
|
for (int i = 0; i < taskCount; i++) {
|
|
TaskID id;
|
|
|
|
// Retreive the Task's id number
|
|
id = in->readSint16LE();
|
|
debugC(3, kDebugSaveload, "Loading Task %d (%d)", i, id);
|
|
|
|
readTask(id, in);
|
|
}
|
|
|
|
// Iterate through the Tasks to fixup the subtask pointers
|
|
for (int i = 0; i < numTasks; ++i) {
|
|
if (_list[i] == nullptr)
|
|
continue;
|
|
|
|
_list[i]->fixup();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to archive this TaskList
|
|
|
|
int32 TaskList::archiveSize() {
|
|
int32 size = sizeof(int16);
|
|
|
|
for (int i = 0; i < numTasks; i++) {
|
|
size += sizeof(TaskID);
|
|
|
|
if (_list[i])
|
|
size += taskArchiveSize(_list[i]);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void TaskList::write(Common::MemoryWriteStreamDynamic *out) {
|
|
int16 taskCount = 0;
|
|
|
|
// Count the active tasks
|
|
for (int i = 0 ; i < numTasks; ++i)
|
|
if (_list[i])
|
|
taskCount++;
|
|
|
|
// Store the task count in the archive buffer
|
|
out->writeSint16LE(taskCount);
|
|
debugC(3, kDebugSaveload, "... taskCount = %d", taskCount);
|
|
|
|
for (int i = 0; i < numTasks; ++i) {
|
|
if (_list[i] == nullptr)
|
|
continue;
|
|
|
|
debugC(3, kDebugSaveload, "Saving Task %d", i);
|
|
|
|
out->writeSint16LE(i);
|
|
writeTask(_list[i], out);
|
|
}
|
|
}
|
|
|
|
void TaskList::newTask(Task *t) {
|
|
debugC(1, kDebugTasks, "List: %p Adding task %p (total %d)", (void *)this, (void *)t, ++_size);
|
|
for (int i = 0; i < numTasks; i++)
|
|
if (!_list[i]) {
|
|
_list[i] = t;
|
|
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < numTasks; i++)
|
|
debug("%d: %p (%s)", i, (void *)_list[i], _list[i]->_type.c_str());
|
|
error("Too many tasks in the list, > %d", numTasks);
|
|
}
|
|
|
|
void TaskList::newTask(Task *t, TaskID id) {
|
|
if (_list[id])
|
|
error("Task already exists");
|
|
_list[id] = t;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Remove the specified Task from the active list and place it back
|
|
// into the inactive list
|
|
|
|
void TaskList::deleteTask(Task *p) {
|
|
debugC(1, kDebugTasks, "List: %p Deleting task %p (%s) (total %d)", (void *)this, (void *)p, p->_type.c_str(), --_size);
|
|
for (int i = 0; i < numTasks; i++)
|
|
if (_list[i] == p) {
|
|
_list[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
Misc. task management functions
|
|
* ===================================================================== */
|
|
|
|
void newTask(Task *t) {
|
|
return g_vm->_taskList->newTask(t);
|
|
}
|
|
|
|
void newTask(Task *t, TaskID id) {
|
|
return g_vm->_taskList->newTask(t, id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the taskList member function deleteTask() to dispose of a
|
|
// previously allocated TaskStack
|
|
|
|
void deleteTask(Task *p) {
|
|
g_vm->_taskList->deleteTask(p);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the specified Task's ID
|
|
|
|
TaskID getTaskID(Task *t) {
|
|
return g_vm->_taskList->getTaskID(t);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return a pointer to a Task given a TaskID
|
|
|
|
Task *getTaskAddress(TaskID id) {
|
|
return g_vm->_taskList->getTaskAddress(id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Initialize the taskList
|
|
|
|
void initTasks() {
|
|
// Simply call the default constructor for the task list
|
|
g_vm->_taskList = new TaskList;
|
|
}
|
|
|
|
void saveTasks(Common::OutSaveFile *outS) {
|
|
debugC(2, kDebugSaveload, "Saving Tasks");
|
|
|
|
outS->write("TASK", 4);
|
|
CHUNK_BEGIN;
|
|
g_vm->_taskList->write(out);
|
|
CHUNK_END;
|
|
}
|
|
|
|
void loadTasks(Common::InSaveFile *in, int32 chunkSize) {
|
|
debugC(2, kDebugSaveload, "Loading Tasks");
|
|
|
|
// If there is no saved data, simply call the default constructor
|
|
if (chunkSize == 0) {
|
|
g_vm->_taskList = new TaskList;
|
|
return;
|
|
}
|
|
|
|
// Reconstruct taskList from archived data
|
|
g_vm->_taskList = new TaskList;
|
|
g_vm->_taskList->read(in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Cleanup the taskList
|
|
|
|
void cleanupTasks() {
|
|
// Simply call the taskList's destructor
|
|
delete g_vm->_taskList;
|
|
}
|
|
|
|
void readTask(TaskID id, Common::InSaveFile *in) {
|
|
int16 type;
|
|
|
|
// Get the Task type
|
|
type = in->readSint16LE();
|
|
|
|
// Reconstruct the Task based upon the type
|
|
switch (type) {
|
|
case kWanderTask:
|
|
new WanderTask(in, id);
|
|
break;
|
|
|
|
case kTetheredWanderTask:
|
|
new TetheredWanderTask(in, id);
|
|
break;
|
|
|
|
case kGotoLocationTask:
|
|
new GotoLocationTask(in, id);
|
|
break;
|
|
|
|
case kGotoRegionTask:
|
|
new GotoRegionTask(in, id);
|
|
break;
|
|
|
|
case kGotoObjectTask:
|
|
new GotoObjectTask(in, id);
|
|
break;
|
|
|
|
case kGotoActorTask:
|
|
new GotoActorTask(in, id);
|
|
break;
|
|
|
|
case kGoAwayFromObjectTask:
|
|
new GoAwayFromObjectTask(in, id);
|
|
break;
|
|
|
|
case kGoAwayFromActorTask:
|
|
new GoAwayFromActorTask(in, id);
|
|
break;
|
|
|
|
case kHuntToBeNearLocationTask:
|
|
new HuntToBeNearLocationTask(in, id);
|
|
break;
|
|
|
|
case kHuntToBeNearObjectTask:
|
|
new HuntToBeNearObjectTask(in, id);
|
|
break;
|
|
|
|
case kHuntToPossessTask:
|
|
new HuntToPossessTask(in, id);
|
|
break;
|
|
|
|
case kHuntToBeNearActorTask:
|
|
new HuntToBeNearActorTask(in, id);
|
|
break;
|
|
|
|
case kHuntToKillTask:
|
|
new HuntToKillTask(in, id);
|
|
break;
|
|
|
|
case kHuntToGiveTask:
|
|
new HuntToGiveTask(in, id);
|
|
break;
|
|
|
|
case kBandTask:
|
|
new BandTask(in, id);
|
|
break;
|
|
|
|
case kBandAndAvoidEnemiesTask:
|
|
new BandAndAvoidEnemiesTask(in, id);
|
|
break;
|
|
|
|
case kFollowPatrolRouteTask:
|
|
new FollowPatrolRouteTask(in, id);
|
|
break;
|
|
|
|
case kAttendTask:
|
|
new AttendTask(in, id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to create an archive of the
|
|
// specified Task
|
|
|
|
int32 taskArchiveSize(Task *t) {
|
|
return sizeof(int16) // Task type
|
|
+ t->archiveSize();
|
|
}
|
|
|
|
void writeTask(Task *t, Common::MemoryWriteStreamDynamic *out) {
|
|
// Store the task's type
|
|
out->writeSint16LE(t->getType());
|
|
|
|
// Store the task
|
|
t->write(out);
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
Task member functions
|
|
* ===================================================================== */
|
|
|
|
Task::Task(Common::InSaveFile *in, TaskID id) {
|
|
// Place the stack ID into the stack pointer field
|
|
_stackID = in->readSint16LE();
|
|
_stack = nullptr;
|
|
newTask(this, id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the Task pointers
|
|
|
|
void Task::fixup() {
|
|
// Convert the stack ID to a stack pointer
|
|
_stack = getTaskStackAddress(_stackID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to create an archive of this
|
|
// object's data
|
|
|
|
inline int32 Task::archiveSize() const {
|
|
return sizeof(TaskStackID); // stack's ID
|
|
}
|
|
|
|
void Task::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
out->writeSint16LE(getTaskStackID(_stack));
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
WanderTask member functions
|
|
* ===================================================================== */
|
|
|
|
WanderTask::WanderTask(Common::InSaveFile *in, TaskID id) : Task(in, id) {
|
|
// Restore the _paused flag
|
|
_paused = in->readUint16LE();
|
|
|
|
// Restore the _counter
|
|
_counter = in->readSint16LE();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 WanderTask::archiveSize() const {
|
|
return Task::archiveSize()
|
|
+ sizeof(_paused)
|
|
+ sizeof(_counter);
|
|
}
|
|
|
|
void WanderTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
Task::write(out);
|
|
|
|
// Store the _paused flag
|
|
out->writeUint16LE(_paused);
|
|
|
|
// Store the _counter
|
|
out->writeSint16LE(_counter);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 WanderTask::getType() const {
|
|
return kWanderTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void WanderTask::abortTask() {
|
|
// if the actor has a wander motion, abort it
|
|
MotionTask *actorMotion = _stack->getActor()->_moveTask;
|
|
|
|
if (actorMotion && actorMotion->isWander())
|
|
actorMotion->finishWalk();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult WanderTask::evaluate() {
|
|
// Wandering is never done. It must be stopped manually.
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult WanderTask::update() {
|
|
if (_counter == 0) {
|
|
if (!_paused)
|
|
pause();
|
|
else
|
|
wander();
|
|
} else
|
|
_counter--;
|
|
|
|
return !_paused ? handleWander() : handlePaused();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool WanderTask::operator == (const Task &t) const {
|
|
return t.getType() == kWanderTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used when task is not _paused
|
|
|
|
TaskResult WanderTask::handleWander() {
|
|
MotionTask *actorMotion = _stack->getActor()->_moveTask;
|
|
|
|
// If the actor is not already wandering, start a wander motion
|
|
// task
|
|
if (!actorMotion
|
|
|| !actorMotion->isWander())
|
|
MotionTask::wander(*_stack->getActor());
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set this task into the _paused state
|
|
|
|
void WanderTask::pause() {
|
|
// Call abort to stop the wandering motion
|
|
abortTask();
|
|
|
|
_paused = true;
|
|
_counter = (g_vm->_rnd->getRandomNumber(63) + g_vm->_rnd->getRandomNumber(63)) / 2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set this task into the wander state
|
|
|
|
void WanderTask::wander() {
|
|
_paused = false;
|
|
_counter = (g_vm->_rnd->getRandomNumber(255) + g_vm->_rnd->getRandomNumber(255)) / 2;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
TetheredWanderTask member functions
|
|
* ===================================================================== */
|
|
|
|
TetheredWanderTask::TetheredWanderTask(Common::InSaveFile *in, TaskID id) : WanderTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading TetheredWanderTask");
|
|
|
|
// Restore the tether coordinates
|
|
_minU = in->readSint16LE();
|
|
_minV = in->readSint16LE();
|
|
_maxU = in->readSint16LE();
|
|
_maxV = in->readSint16LE();
|
|
|
|
// Put the _gotoTether ID into the _gotoTether pointer field
|
|
_gotoTetherID = in->readSint16LE();
|
|
_gotoTether = nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void TetheredWanderTask::fixup() {
|
|
// Let the base class fixup it's pointers
|
|
WanderTask::fixup();
|
|
|
|
// Restore the _gotoTether pointer
|
|
_gotoTether = _gotoTetherID != NoTask
|
|
? (GotoRegionTask *)getTaskAddress(_gotoTetherID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in a buffer
|
|
|
|
inline int32 TetheredWanderTask::archiveSize() const {
|
|
return WanderTask::archiveSize()
|
|
+ sizeof(_minU)
|
|
+ sizeof(_minU)
|
|
+ sizeof(_minU)
|
|
+ sizeof(_minU)
|
|
+ sizeof(TaskID); // _gotoTether ID
|
|
}
|
|
|
|
void TetheredWanderTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving TetheredWanderTask");
|
|
|
|
// Let the base class archive its data
|
|
WanderTask::write(out);
|
|
|
|
// Archive tether coordinates
|
|
out->writeSint16LE(_minU);
|
|
out->writeSint16LE(_minV);
|
|
out->writeSint16LE(_maxU);
|
|
out->writeSint16LE(_maxV);
|
|
|
|
// Archive _gotoTether ID
|
|
if (_gotoTether != nullptr)
|
|
out->writeSint16LE(getTaskID(_gotoTether));
|
|
else
|
|
out->writeSint16LE(NoTask);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 TetheredWanderTask::getType() const {
|
|
return kTetheredWanderTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void TetheredWanderTask::abortTask() {
|
|
if (_gotoTether != nullptr) {
|
|
_gotoTether->abortTask();
|
|
delete _gotoTether;
|
|
_gotoTether = nullptr;
|
|
} else {
|
|
MotionTask *actorMotion = _stack->getActor()->_moveTask;
|
|
|
|
// if the actor has a tethered wander motion, abort it
|
|
if (actorMotion && actorMotion->isTethered())
|
|
actorMotion->finishWalk();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool TetheredWanderTask::operator == (const Task &t) const {
|
|
if (t.getType() != kTetheredWanderTask) return false;
|
|
|
|
const TetheredWanderTask *taskPtr = (const TetheredWanderTask *)&t;
|
|
|
|
return _minU == taskPtr->_minU && _minV == taskPtr->_minV
|
|
&& _maxU == taskPtr->_maxU && _maxV == taskPtr->_maxV;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used when task is not _paused
|
|
|
|
TaskResult TetheredWanderTask::handleWander() {
|
|
Actor *a = _stack->getActor();
|
|
TilePoint actorLoc = a->getLocation();
|
|
|
|
if (actorLoc.u < _minU || actorLoc.u >= _maxU
|
|
|| actorLoc.v < _minV || actorLoc.v >= _maxV) {
|
|
if (_gotoTether != nullptr)
|
|
_gotoTether->update();
|
|
else {
|
|
_gotoTether = new GotoRegionTask(_stack, _minU, _minV, _maxU, _maxV);
|
|
if (_gotoTether != nullptr)
|
|
_gotoTether->update();
|
|
}
|
|
} else {
|
|
if (_gotoTether != nullptr) {
|
|
_gotoTether->abortTask();
|
|
delete _gotoTether;
|
|
_gotoTether = nullptr;
|
|
}
|
|
|
|
bool startWander = false;
|
|
TileRegion motionTether;
|
|
|
|
MotionTask *actorMotion = a->_moveTask;
|
|
|
|
if (actorMotion) {
|
|
TileRegion motionTeth = actorMotion->getTether();
|
|
startWander = ((!actorMotion->isWander())
|
|
|| motionTeth.min.u != _minU
|
|
|| motionTeth.min.v != _minV
|
|
|| motionTeth.max.u != _maxU
|
|
|| motionTeth.max.v != _maxV);
|
|
|
|
} else
|
|
startWander = true;
|
|
|
|
// If the actor is not already wandering, start a wander motion
|
|
// task
|
|
|
|
// JeffL - prevent null pointer reference
|
|
/*
|
|
if ( !actorMotion
|
|
|| !actorMotion->isWander()
|
|
|| motionTether.min.u != _minU
|
|
|| motionTether.min.v != _minV
|
|
|| motionTether.max.u != _maxU
|
|
|| motionTether.max.v != _maxV )
|
|
*/
|
|
if (startWander) {
|
|
TileRegion reg;
|
|
|
|
reg.min = TilePoint(_minU, _minV, 0);
|
|
reg.max = TilePoint(_maxU, _maxV, 0);
|
|
MotionTask::tetheredWander(*_stack->getActor(), reg);
|
|
}
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoTask member functions
|
|
* ===================================================================== */
|
|
|
|
GotoTask::GotoTask(Common::InSaveFile *in, TaskID id) : Task(in, id) {
|
|
// Get the wander TaskID
|
|
_wanderID = in->readSint16LE();
|
|
_wander = nullptr;
|
|
|
|
// Restore prevRunState
|
|
_prevRunState = in->readUint16LE();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void GotoTask::fixup() {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
// Convert wanderID to a Task pointer
|
|
_wander = _wanderID != NoTask
|
|
? (WanderTask *)getTaskAddress(_wanderID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoTask::archiveSize() const {
|
|
return Task::archiveSize()
|
|
+ sizeof(TaskID) // wander ID
|
|
+ sizeof(_prevRunState);
|
|
}
|
|
|
|
void GotoTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
Task::write(out);
|
|
|
|
// Convert the wander Task pointer to a TaskID and store it
|
|
// in the buffer
|
|
if (_wander != nullptr)
|
|
out->writeSint16LE(getTaskID( _wander));
|
|
else
|
|
out->writeSint16LE(NoTask);
|
|
|
|
// Store _prevRunState
|
|
out->writeUint16LE(_prevRunState);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void GotoTask::abortTask() {
|
|
// If there is a wander subtask, delete it.
|
|
if ( _wander) {
|
|
_wander->abortTask();
|
|
delete _wander;
|
|
_wander = nullptr;
|
|
} else {
|
|
MotionTask *actorMotion = _stack->getActor()->_moveTask;
|
|
|
|
if (actorMotion && actorMotion->isWalk()) actorMotion->finishWalk();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult GotoTask::evaluate() {
|
|
// Determine if we have reach the target.
|
|
if (_stack->getActor()->getLocation() == destination()) {
|
|
abortTask();
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult GotoTask::update() {
|
|
// Check to see if we have reached the target
|
|
{
|
|
TaskResult result = evaluate();
|
|
if (result != kTaskNotDone) return result;
|
|
}
|
|
|
|
Actor *const a = _stack->getActor();
|
|
// Compute the immediate destination based upon whether or not the
|
|
// actor has a line of sight to the target.
|
|
TilePoint immediateDest = lineOfSight()
|
|
? destination()
|
|
: intermediateDest();
|
|
|
|
// If we have a destination, walk there, else wander
|
|
if (immediateDest != Nowhere) {
|
|
// If wandering, cut it out
|
|
if (_wander != nullptr) {
|
|
delete _wander;
|
|
_wander = nullptr;
|
|
}
|
|
|
|
// Determine if there is already a motion task, and if so,
|
|
// whether or not it needs to be modified.
|
|
MotionTask *actorMotion = a->_moveTask;
|
|
TilePoint actorLoc = a->getLocation();
|
|
|
|
if (actorMotion != nullptr && actorMotion->isWalkToDest()) {
|
|
bool runState = run();
|
|
TilePoint motionTarget = actorMotion->getTarget();
|
|
|
|
if ((actorLoc.u >> kTileUVShift)
|
|
== (immediateDest.u >> kTileUVShift)
|
|
&& (actorLoc.v >> kTileUVShift)
|
|
== (immediateDest.v >> kTileUVShift)) {
|
|
if (motionTarget != immediateDest
|
|
|| runState != _prevRunState)
|
|
actorMotion->changeDirectTarget(
|
|
immediateDest,
|
|
_prevRunState = runState);
|
|
} else {
|
|
if ((motionTarget.u >> kTileUVShift)
|
|
!= (immediateDest.u >> kTileUVShift)
|
|
|| (motionTarget.v >> kTileUVShift)
|
|
!= (immediateDest.v >> kTileUVShift)
|
|
|| ABS(motionTarget.z - immediateDest.z) > 16
|
|
|| runState != _prevRunState)
|
|
actorMotion->changeTarget(
|
|
immediateDest,
|
|
_prevRunState = runState);
|
|
}
|
|
} else {
|
|
if ((actorLoc.u >> kTileUVShift)
|
|
== (immediateDest.u >> kTileUVShift)
|
|
&& (actorLoc.v >> kTileUVShift)
|
|
== (immediateDest.v >> kTileUVShift)) {
|
|
MotionTask::walkToDirect(
|
|
*a,
|
|
immediateDest,
|
|
_prevRunState = run());
|
|
} else
|
|
MotionTask::walkTo(*a, immediateDest, _prevRunState = run());
|
|
}
|
|
} else {
|
|
// If wandering, update the wander task else set up a new
|
|
// wander task
|
|
if (_wander != nullptr)
|
|
_wander->update();
|
|
else {
|
|
_wander = new WanderTask(_stack);
|
|
if (_wander != nullptr) _wander->update();
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoLocationTask member functions
|
|
* ===================================================================== */
|
|
|
|
GotoLocationTask::GotoLocationTask(Common::InSaveFile *in, TaskID id) : GotoTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading GotoLocationTask");
|
|
|
|
// Restore the target location
|
|
_targetLoc.load(in);
|
|
|
|
// Restore the _runThreshold
|
|
_runThreshold = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoLocationTask::archiveSize() const {
|
|
return GotoTask::archiveSize()
|
|
+ sizeof(_targetLoc)
|
|
+ sizeof(_runThreshold);
|
|
}
|
|
|
|
void GotoLocationTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving GotoLocationTask");
|
|
|
|
// Let the base class archive its data
|
|
GotoTask::write(out);
|
|
|
|
// Archive the target location
|
|
_targetLoc.write(out);
|
|
|
|
// Archive the run threshold
|
|
out->writeByte(_runThreshold);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoLocationTask::getType() const {
|
|
return kGotoLocationTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoLocationTask::operator == (const Task &t) const {
|
|
if (t.getType() != kGotoLocationTask) return false;
|
|
|
|
const GotoLocationTask *taskPtr = (const GotoLocationTask *)&t;
|
|
|
|
return _targetLoc == taskPtr->_targetLoc
|
|
&& _runThreshold == taskPtr->_runThreshold;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoLocationTask::destination() {
|
|
// Simply return the target location
|
|
return _targetLoc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoLocationTask::intermediateDest() {
|
|
// GotoLocationTask's never have an intermediate destination
|
|
return _targetLoc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoLocationTask::lineOfSight() {
|
|
// Let's pretend that there is always a line of sight to the
|
|
// target location
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoLocationTask::run() {
|
|
TilePoint actorLoc = _stack->getActor()->getLocation();
|
|
|
|
return _runThreshold != maxuint8
|
|
? (_targetLoc - actorLoc).quickHDistance() > _runThreshold
|
|
|| ABS(_targetLoc.z - actorLoc.z) > _runThreshold
|
|
: false;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoRegionTask member functions
|
|
* ===================================================================== */
|
|
|
|
GotoRegionTask::GotoRegionTask(Common::InSaveFile *in, TaskID id) : GotoTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading GotoRegionTask");
|
|
|
|
// Restore the region coordinates
|
|
_regionMinU = in->readSint16LE();
|
|
_regionMinV = in->readSint16LE();
|
|
_regionMaxU = in->readSint16LE();
|
|
_regionMaxV = in->readSint16LE();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoRegionTask::archiveSize() const {
|
|
return GotoTask::archiveSize()
|
|
+ sizeof(_regionMinU)
|
|
+ sizeof(_regionMinV)
|
|
+ sizeof(_regionMaxU)
|
|
+ sizeof(_regionMaxV);
|
|
}
|
|
|
|
void GotoRegionTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving GotoRegionTask");
|
|
|
|
// Let the base class archive its data
|
|
GotoTask::write(out);
|
|
// Archive the region coordinates
|
|
out->writeSint16LE(_regionMinU);
|
|
out->writeSint16LE(_regionMinV);
|
|
out->writeSint16LE(_regionMaxU);
|
|
out->writeSint16LE(_regionMaxV);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoRegionTask::getType() const {
|
|
return kGotoRegionTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoRegionTask::operator == (const Task &t) const {
|
|
if (t.getType() != kGotoRegionTask) return false;
|
|
|
|
const GotoRegionTask *taskPtr = (const GotoRegionTask *)&t;
|
|
|
|
return _regionMinU == taskPtr->_regionMinU
|
|
&& _regionMinV == taskPtr->_regionMinV
|
|
&& _regionMaxU == taskPtr->_regionMaxU
|
|
&& _regionMaxV == taskPtr->_regionMaxV;
|
|
}
|
|
|
|
TilePoint GotoRegionTask::destination() {
|
|
TilePoint actorLoc = _stack->getActor()->getLocation();
|
|
|
|
return TilePoint(
|
|
clamp(_regionMinU, actorLoc.u, _regionMaxU - 1),
|
|
clamp(_regionMinV, actorLoc.v, _regionMaxV - 1),
|
|
actorLoc.z);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoRegionTask::intermediateDest() {
|
|
return destination();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoRegionTask::lineOfSight() {
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoRegionTask::run() {
|
|
return false;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoObjectTargetTask member functions
|
|
* ===================================================================== */
|
|
|
|
GotoObjectTargetTask::GotoObjectTargetTask(Common::InSaveFile *in, TaskID id) : GotoTask(in, id) {
|
|
// Restore _lastTestedLoc and increment pointer
|
|
_lastTestedLoc.load(in);
|
|
|
|
// Restore _sightCtr and increment pointer
|
|
_sightCtr = in->readSint16LE();
|
|
|
|
// Restore the flags and increment pointer
|
|
_flags = in->readByte();
|
|
|
|
// Restore _lastKnownLoc
|
|
_lastKnownLoc.load(in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoObjectTargetTask::archiveSize() const {
|
|
return GotoTask::archiveSize()
|
|
+ sizeof(_lastTestedLoc)
|
|
+ sizeof(_sightCtr)
|
|
+ sizeof(_flags)
|
|
+ sizeof(_lastKnownLoc);
|
|
}
|
|
|
|
void GotoObjectTargetTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
GotoTask::write(out);
|
|
|
|
// Archive _lastTestedLoc and increment pointer
|
|
_lastTestedLoc.write(out);
|
|
|
|
// Archive _sightCtr and increment pointer
|
|
out->writeSint16LE(_sightCtr);
|
|
|
|
// Archive the flags and increment pointer
|
|
out->writeByte(_flags);
|
|
|
|
// Archive _lastKnownLoc
|
|
_lastKnownLoc.write(out);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoObjectTargetTask::destination() {
|
|
// Return the object's true location
|
|
return getObject()->getLocation();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoObjectTargetTask::intermediateDest() {
|
|
// Return the last known location
|
|
return _lastKnownLoc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoObjectTargetTask::lineOfSight() {
|
|
if (_flags & kTrack) {
|
|
_flags |= kInSight;
|
|
_lastKnownLoc = getObject()->getLocation();
|
|
} else {
|
|
Actor *a = _stack->getActor();
|
|
GameObject *target = getObject();
|
|
ObjectID targetID = target->thisID();
|
|
TilePoint _targetLoc = target->getLocation();
|
|
SenseInfo info;
|
|
|
|
// Determine if we need to retest the line of sight
|
|
if (_flags & kInSight) {
|
|
// If the object was previously in sight, retest the line of
|
|
// sight if the target has moved beyond a certain range from
|
|
// the last location it was tested at.
|
|
if ((_targetLoc - _lastTestedLoc).quickHDistance() > 25
|
|
|| ABS(_targetLoc.z - _lastTestedLoc.z) > 25) {
|
|
if (a->canSenseSpecificObject(
|
|
info,
|
|
kMaxSenseRange,
|
|
targetID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
targetID))
|
|
_flags |= kInSight;
|
|
else
|
|
_flags &= ~kInSight;
|
|
_lastTestedLoc = _targetLoc;
|
|
}
|
|
} else {
|
|
// If the object was not privously in sight, retest the line
|
|
// of sight periodically
|
|
if (_sightCtr == 0) {
|
|
_sightCtr = kSightRate;
|
|
if (a->canSenseSpecificObject(
|
|
info,
|
|
kMaxSenseRange,
|
|
targetID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
targetID))
|
|
_flags |= kInSight;
|
|
else
|
|
_flags &= ~kInSight;
|
|
_lastTestedLoc = _targetLoc;
|
|
}
|
|
_sightCtr--;
|
|
}
|
|
|
|
if (_flags & kInSight) {
|
|
// If the target is in sight, the last known location is the
|
|
// objects current location.
|
|
_lastKnownLoc = _targetLoc;
|
|
} else {
|
|
// If the target is not in sight, determine if we've already
|
|
// reached the last know location and if so set the last
|
|
// known location to Nowhere
|
|
if (_lastKnownLoc != Nowhere
|
|
&& (_lastKnownLoc - a->getLocation()).quickHDistance() <= 4)
|
|
_lastKnownLoc = Nowhere;
|
|
}
|
|
}
|
|
|
|
return _flags & kInSight;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
GotoObjectTask::GotoObjectTask(Common::InSaveFile *in, TaskID id) :
|
|
GotoObjectTargetTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading GotoObjectTask");
|
|
|
|
ObjectID targetID = in->readUint16LE();
|
|
|
|
// Restore the _targetObj pointer
|
|
_targetObj = targetID != Nothing
|
|
? GameObject::objectAddress(targetID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoObjectTask::archiveSize() const {
|
|
return GotoObjectTargetTask::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
void GotoObjectTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving GotoObjectTask");
|
|
|
|
// Let the base class archive its data
|
|
GotoObjectTargetTask::write(out);
|
|
|
|
if (_targetObj != nullptr)
|
|
out->writeUint16LE(_targetObj->thisID());
|
|
else
|
|
out->writeUint16LE(Nothing);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoObjectTask::getType() const {
|
|
return kGotoObjectTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoObjectTask::operator == (const Task &t) const {
|
|
if (t.getType() != kGotoObjectTask) return false;
|
|
|
|
const GotoObjectTask *taskPtr = (const GotoObjectTask *)&t;
|
|
|
|
return tracking() == taskPtr->tracking()
|
|
&& _targetObj == taskPtr->_targetObj;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GameObject *GotoObjectTask::getObject() {
|
|
// Simply return the pointer to the target object
|
|
return _targetObj;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoObjectTask::run() {
|
|
// Running after objects has not been implemented yet
|
|
return false;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
GotoActorTask::GotoActorTask(Common::InSaveFile *in, TaskID id) :
|
|
GotoObjectTargetTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading GotoActorTask");
|
|
// Restore the _targetObj pointer
|
|
ObjectID targetID = in->readUint16LE();
|
|
_targetActor = targetID != Nothing
|
|
? (Actor *)GameObject::objectAddress(targetID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoActorTask::archiveSize() const {
|
|
return GotoObjectTargetTask::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
void GotoActorTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving GotoActorTask");
|
|
|
|
// Let the base class archive its data
|
|
GotoObjectTargetTask::write(out);
|
|
|
|
if (_targetActor != nullptr)
|
|
out->writeUint16LE(_targetActor->thisID());
|
|
else
|
|
out->writeUint16LE(Nothing);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoActorTask::getType() const {
|
|
return kGotoActorTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoActorTask::operator == (const Task &t) const {
|
|
if (t.getType() != kGotoActorTask) return false;
|
|
|
|
const GotoActorTask *taskPtr = (const GotoActorTask *)&t;
|
|
|
|
return tracking() == taskPtr->tracking()
|
|
&& _targetActor == taskPtr->_targetActor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GameObject *GotoActorTask::getObject() {
|
|
// Simply return the pointer to the target actor
|
|
return (GameObject *)_targetActor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoActorTask::run() {
|
|
if (isInSight()) {
|
|
TilePoint actorLoc = _stack->getActor()->getLocation(),
|
|
_targetLoc = getTarget()->getLocation();
|
|
|
|
return (actorLoc - _targetLoc).quickHDistance() >= kTileUVSize * 4;
|
|
} else
|
|
return _lastKnownLoc != Nowhere;
|
|
}
|
|
|
|
GoAwayFromTask::GoAwayFromTask(Common::InSaveFile *in, TaskID id) : Task(in, id) {
|
|
// Get the subtask ID
|
|
_goTaskID = in->readSint16LE();
|
|
_goTask = nullptr;
|
|
|
|
// Restore the flags
|
|
_flags = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointer
|
|
|
|
void GoAwayFromTask::fixup() {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
_goTask = _goTaskID != NoTask
|
|
? (GotoLocationTask *)getTaskAddress(_goTaskID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GoAwayFromTask::archiveSize() const {
|
|
return Task::archiveSize() + sizeof(TaskID) + sizeof(_flags);
|
|
}
|
|
|
|
void GoAwayFromTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
Task::write(out);
|
|
|
|
// Store the _subTask's ID
|
|
if (_goTask != nullptr)
|
|
out->writeSint16LE(getTaskID(_goTask));
|
|
else
|
|
out->writeSint16LE(NoTask);
|
|
|
|
// Store the flags
|
|
out->writeByte(_flags);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Abort this task
|
|
|
|
void GoAwayFromTask::abortTask() {
|
|
if (_goTask != nullptr) {
|
|
_goTask->abortTask();
|
|
delete _goTask;
|
|
_goTask = nullptr;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Evaluate this task
|
|
|
|
TaskResult GoAwayFromTask::evaluate() {
|
|
// Going away is never done, it must be stopped manually
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update this task
|
|
|
|
TaskResult GoAwayFromTask::update() {
|
|
static const TilePoint dirTable_[] = {
|
|
TilePoint(64, 64, 0),
|
|
TilePoint(0, 64, 0),
|
|
TilePoint(-64, 64, 0),
|
|
TilePoint(-64, 0, 0),
|
|
TilePoint(-64, -64, 0),
|
|
TilePoint(0, -64, 0),
|
|
TilePoint(64, -64, 0),
|
|
TilePoint(64, 0, 0),
|
|
};
|
|
|
|
Actor *a = _stack->getActor();
|
|
TilePoint actorLoc = a->getLocation(),
|
|
repulsionVector = getRepulsionVector(),
|
|
dest;
|
|
int16 repulsionDist = repulsionVector.quickHDistance();
|
|
|
|
// Compute a point for the actor to walk toward
|
|
if (repulsionDist != 0) {
|
|
dest.u = actorLoc.u + ((int32)repulsionVector.u * 64 / repulsionDist);
|
|
dest.v = actorLoc.v + ((int32)repulsionVector.v * 64 / repulsionDist);
|
|
dest.z = actorLoc.z;
|
|
} else
|
|
dest = actorLoc + dirTable_[a->_currentFacing];
|
|
|
|
if (_goTask != nullptr) {
|
|
if (_goTask->getTarget() != dest)
|
|
_goTask->changeTarget(dest);
|
|
|
|
_goTask->update();
|
|
} else {
|
|
if ((_goTask = _flags & kRun
|
|
? new GotoLocationTask(_stack, dest, 0)
|
|
: new GotoLocationTask(_stack, dest))
|
|
!= nullptr)
|
|
_goTask->update();
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GoAwayFromObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
GoAwayFromObjectTask::GoAwayFromObjectTask(Common::InSaveFile *in, TaskID id) :
|
|
GoAwayFromTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading GoAwayFromObjectTask");
|
|
|
|
// Get the object's ID
|
|
ObjectID objectID = in->readUint16LE();
|
|
|
|
// Convert the ID to an object pointer
|
|
_obj = objectID != Nothing
|
|
? GameObject::objectAddress(objectID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 GoAwayFromObjectTask::archiveSize() const {
|
|
return GoAwayFromTask::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
void GoAwayFromObjectTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving GoAwayFromObjectTask");
|
|
|
|
// Let the base class archive its data
|
|
GoAwayFromTask::write(out);
|
|
|
|
// Store the object's ID
|
|
if (_obj != nullptr)
|
|
out->writeUint16LE(_obj->thisID());
|
|
else
|
|
out->writeUint16LE(Nothing);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GoAwayFromObjectTask::getType() const {
|
|
return kGoAwayFromObjectTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GoAwayFromObjectTask::operator == (const Task &t) const {
|
|
if (t.getType() != kGoAwayFromObjectTask) return false;
|
|
|
|
const GoAwayFromObjectTask *taskPtr = (const GoAwayFromObjectTask *)&t;
|
|
|
|
return _obj == taskPtr->_obj;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Simply return the object's location
|
|
|
|
TilePoint GoAwayFromObjectTask::getRepulsionVector() {
|
|
return _stack->getActor()->getLocation() - _obj->getLocation();
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GoAwayFromActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
GoAwayFromActorTask::GoAwayFromActorTask(
|
|
TaskStack *ts,
|
|
Actor *a,
|
|
bool runFlag) :
|
|
GoAwayFromTask(ts, runFlag) {
|
|
debugC(2, kDebugTasks, " - GoAwayFromActorTask1");
|
|
SpecificActorTarget(a).clone(_targetMem);
|
|
}
|
|
|
|
GoAwayFromActorTask::GoAwayFromActorTask(
|
|
TaskStack *ts,
|
|
const ActorTarget &at,
|
|
bool runFlag) :
|
|
GoAwayFromTask(ts, runFlag) {
|
|
assert(at.size() <= sizeof(_targetMem));
|
|
debugC(2, kDebugTasks, " - GoAwayFromActorTask2");
|
|
// Copy the target to the target buffer
|
|
at.clone(_targetMem);
|
|
}
|
|
|
|
|
|
GoAwayFromActorTask::GoAwayFromActorTask(Common::InSaveFile *in, TaskID id) : GoAwayFromTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading GoAwayFromActorTask");
|
|
|
|
// Restore the target
|
|
readTarget(_targetMem, in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 GoAwayFromActorTask::archiveSize() const {
|
|
return GoAwayFromTask::archiveSize() + targetArchiveSize(getTarget());
|
|
}
|
|
|
|
void GoAwayFromActorTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving GoAwayFromActorTask");
|
|
|
|
// Let the base class archive its data
|
|
GoAwayFromTask::write(out);
|
|
|
|
// Store the target
|
|
writeTarget(getTarget(), out);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GoAwayFromActorTask::getType() const {
|
|
return kGoAwayFromActorTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GoAwayFromActorTask::operator == (const Task &t) const {
|
|
if (t.getType() != kGoAwayFromActorTask) return false;
|
|
|
|
const GoAwayFromActorTask *taskPtr = (const GoAwayFromActorTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GoAwayFromActorTask::getRepulsionVector() {
|
|
Actor *a = _stack->getActor();
|
|
TilePoint actorLoc = a->getLocation(),
|
|
repulsionVector;
|
|
int16 i;
|
|
TilePoint locArray[6];
|
|
int16 strengthArray[ARRAYSIZE(locArray)] =
|
|
{ 1, 1, 1, 1, 1, 1 };
|
|
int16 distArray[ARRAYSIZE(locArray)];
|
|
TargetLocationArray tla(
|
|
ARRAYSIZE(locArray),
|
|
locArray,
|
|
distArray);
|
|
|
|
getTarget()->where(a->world(), actorLoc, tla);
|
|
|
|
if (tla.locs == 0) return TilePoint(0, 0, 0);
|
|
|
|
for (i = 0; i < tla.locs; i++)
|
|
locArray[i] -= actorLoc;
|
|
|
|
repulsionVector = computeRepulsionVector(locArray, strengthArray, tla.locs);
|
|
|
|
return repulsionVector.quickHDistance() > 0
|
|
? repulsionVector
|
|
: -locArray[0];
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntTask member functions
|
|
* ===================================================================== */
|
|
|
|
HuntTask::HuntTask(Common::InSaveFile *in, TaskID id) : Task(in, id) {
|
|
// Restore the flags
|
|
_huntFlags = in->readByte();
|
|
_subTask = nullptr;
|
|
|
|
// If the flags say we have a sub task, restore it too
|
|
if (_huntFlags & (kHuntGoto| kHuntWander))
|
|
_subTaskID = in->readSint16LE();
|
|
else
|
|
_subTaskID = NoTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void HuntTask::fixup( void ) {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
if (_huntFlags & (kHuntGoto| kHuntWander))
|
|
_subTask = getTaskAddress(_subTaskID);
|
|
else
|
|
_subTask = nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntTask::archiveSize() const {
|
|
int32 size = 0;
|
|
|
|
size += Task::archiveSize() + sizeof(_huntFlags);
|
|
if (_huntFlags & (kHuntGoto| kHuntWander)) size += sizeof(TaskID);
|
|
|
|
return size;
|
|
}
|
|
|
|
void HuntTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
Task::write(out);
|
|
|
|
// Store the flags
|
|
out->writeByte(_huntFlags);
|
|
|
|
// If the flags say we have a sub task, store it too
|
|
if (_huntFlags & (kHuntGoto| kHuntWander))
|
|
out->writeSint16LE(getTaskID(_subTask));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntTask::abortTask() {
|
|
if (_huntFlags & (kHuntWander| kHuntGoto)) {
|
|
_subTask->abortTask();
|
|
delete _subTask;
|
|
}
|
|
|
|
// If we've reached the target call the atTargetabortTask() function
|
|
if (atTarget()) atTargetabortTask();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntTask::evaluate() {
|
|
if (atTarget()) {
|
|
// If we've reached the target abort any sub tasks
|
|
if (_huntFlags & kHuntWander)
|
|
removeWanderTask();
|
|
else if (_huntFlags & kHuntGoto)
|
|
removeGotoTask();
|
|
|
|
return atTargetEvaluate();
|
|
} else
|
|
// If we haven't reached the target, we know we're not done
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntTask::update() {
|
|
Actor *a = _stack->getActor();
|
|
|
|
if (a->_moveTask && a->_moveTask->isPrivledged()) return kTaskNotDone;
|
|
|
|
// Reevaluate the target
|
|
evaluateTarget();
|
|
|
|
// Determine if we have reached the target
|
|
if (atTarget()) {
|
|
// If we've reached the target abort any sub tasks
|
|
if (_huntFlags & kHuntWander)
|
|
removeWanderTask();
|
|
else if (_huntFlags & kHuntGoto)
|
|
removeGotoTask();
|
|
|
|
return atTargetUpdate();
|
|
} else {
|
|
// If we are going to a target, determine if the goto task
|
|
// is still valid. If not, abort it.
|
|
if ((_huntFlags & kHuntGoto)
|
|
&& targetHasChanged((GotoTask *)_subTask))
|
|
removeGotoTask();
|
|
|
|
// Determine if there is a goto subtask
|
|
if (!(_huntFlags & kHuntGoto)) {
|
|
GotoTask *gotoResult;
|
|
|
|
// Try to set up a goto subtask
|
|
if ((gotoResult = setupGoto()) != nullptr) {
|
|
if (_huntFlags & kHuntWander) removeWanderTask();
|
|
|
|
_subTask = gotoResult;
|
|
_huntFlags |= kHuntGoto;
|
|
} else {
|
|
// If we couldn't setup a goto task, setup a wander task
|
|
if (!(_huntFlags & kHuntWander)) {
|
|
if ((_subTask = new WanderTask(_stack)) != nullptr)
|
|
_huntFlags |= kHuntWander;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is a subtask, update it
|
|
if ((_huntFlags & (kHuntGoto| kHuntWander)) && _subTask)
|
|
_subTask->update();
|
|
|
|
// If we're not at the target, we know the hunt task is not
|
|
// done
|
|
return kTaskNotDone;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntTask::removeWanderTask() {
|
|
_subTask->abortTask();
|
|
delete _subTask;
|
|
_huntFlags &= ~kHuntWander;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntTask::removeGotoTask() {
|
|
_subTask->abortTask();
|
|
delete _subTask;
|
|
_subTask = nullptr;
|
|
_huntFlags &= ~kHuntGoto;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntLocationTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntLocationTask::HuntLocationTask(TaskStack *ts, const Target &t) :
|
|
HuntTask(ts),
|
|
_currentTarget(Nowhere) {
|
|
assert(t.size() <= sizeof(_targetMem));
|
|
debugC(2, kDebugTasks, " - HuntLocationTask");
|
|
// Copy the target to the target buffer
|
|
t.clone(_targetMem);
|
|
}
|
|
|
|
HuntLocationTask::HuntLocationTask(Common::InSaveFile *in, TaskID id) : HuntTask(in, id) {
|
|
// Restore the _currentTarget location
|
|
_currentTarget.load(in);
|
|
|
|
// Restore the target
|
|
readTarget(_targetMem, in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntLocationTask::archiveSize() const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(_currentTarget)
|
|
+ targetArchiveSize(getTarget());
|
|
}
|
|
|
|
void HuntLocationTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
HuntTask::write(out);
|
|
|
|
// Store the current target location
|
|
_currentTarget.write(out);
|
|
|
|
// Store the target
|
|
writeTarget(getTarget(), out);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntLocationTask::targetHasChanged(GotoTask *gotoTarget) {
|
|
// Determine if the specified goto task is going to the current
|
|
// target.
|
|
GotoLocationTask *gotoLoc = (GotoLocationTask *)gotoTarget;
|
|
return gotoLoc->getTarget() != _currentTarget;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GotoTask *HuntLocationTask::setupGoto() {
|
|
// If there is somewhere to go, setup a goto task, else return NULL
|
|
return _currentTarget != Nowhere
|
|
? new GotoLocationTask(_stack, _currentTarget)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint HuntLocationTask::currentTargetLoc() {
|
|
return _currentTarget;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToBeNearLocationTask member functions
|
|
* ===================================================================== */
|
|
|
|
HuntToBeNearLocationTask::HuntToBeNearLocationTask(Common::InSaveFile *in, TaskID id) :
|
|
HuntLocationTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading HuntToBeNearLocationTask");
|
|
|
|
// Restore the range
|
|
_range = in->readUint16LE();
|
|
|
|
// Restore the evaluation _counter
|
|
_targetEvaluateCtr = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToBeNearLocationTask::archiveSize() const {
|
|
return HuntLocationTask::archiveSize()
|
|
+ sizeof(_range)
|
|
+ sizeof(_targetEvaluateCtr);
|
|
}
|
|
|
|
void HuntToBeNearLocationTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving HuntToBeNearLocationTask");
|
|
|
|
// Let the base class archive its data
|
|
HuntLocationTask::write(out);
|
|
|
|
// Store the range
|
|
out->writeUint16LE(_range);
|
|
|
|
// Store the evaluation _counter
|
|
out->writeByte(_targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToBeNearLocationTask::getType() const {
|
|
return kHuntToBeNearLocationTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToBeNearLocationTask::operator == (const Task &t) const {
|
|
if (t.getType() != kHuntToBeNearLocationTask) return false;
|
|
|
|
const HuntToBeNearLocationTask *taskPtr = (const HuntToBeNearLocationTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& _range == taskPtr->_range;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearLocationTask::evaluateTarget() {
|
|
// If its time to reevaluate the target, simply get the nearest
|
|
// target location from the LocationTarget
|
|
if (_targetEvaluateCtr == 0) {
|
|
Actor *a = _stack->getActor();
|
|
|
|
_currentTarget =
|
|
getTarget()->where(a->world(), a->getLocation());
|
|
|
|
_targetEvaluateCtr = kTargetEvaluateRate;
|
|
}
|
|
_targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToBeNearLocationTask::atTarget() {
|
|
TilePoint _targetLoc = currentTargetLoc();
|
|
|
|
// Determine if we are within the specified range of the target
|
|
return _targetLoc != Nowhere
|
|
&& _stack->getActor()->inRange(_targetLoc, _range);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearLocationTask::atTargetabortTask() {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearLocationTask::atTargetEvaluate() {
|
|
// If we're at the target, we're done
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearLocationTask::atTargetUpdate() {
|
|
// If we're at the target, we're done
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntObjectTask::HuntObjectTask(TaskStack *ts, const ObjectTarget &ot) :
|
|
HuntTask(ts),
|
|
_currentTarget(nullptr) {
|
|
assert(ot.size() <= sizeof(_targetMem));
|
|
debugC(2, kDebugTasks, " - HuntObjectTask");
|
|
// Copy the target to the target buffer
|
|
ot.clone(_targetMem);
|
|
}
|
|
|
|
HuntObjectTask::HuntObjectTask(Common::InSaveFile *in, TaskID id) : HuntTask(in, id) {
|
|
// Restore the current target ID
|
|
ObjectID _currentTargetID = in->readUint16LE();
|
|
|
|
// Convert the ID to a GameObject pointer
|
|
_currentTarget = _currentTargetID != Nothing
|
|
? GameObject::objectAddress(_currentTargetID)
|
|
: nullptr;
|
|
|
|
// Reconstruct the object target
|
|
readTarget(_targetMem, in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntObjectTask::archiveSize() const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(ObjectID)
|
|
+ targetArchiveSize(getTarget());
|
|
}
|
|
|
|
void HuntObjectTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
HuntTask::write(out);
|
|
|
|
// Store the ID
|
|
if (_currentTarget != nullptr)
|
|
out->writeByte(_currentTarget->thisID());
|
|
else
|
|
out->writeByte(Nothing);
|
|
|
|
// Store the object target
|
|
writeTarget(getTarget(), out);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntObjectTask::targetHasChanged(GotoTask *gotoTarget) {
|
|
// Determine if the specified goto task's destination is the
|
|
// current target object
|
|
GotoObjectTask *gotoObj = (GotoObjectTask *)gotoTarget;
|
|
return gotoObj->getTarget() != _currentTarget;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GotoTask *HuntObjectTask::setupGoto() {
|
|
// If there is an object to goto, setup a GotoObjectTask, else
|
|
// return NULL
|
|
return _currentTarget
|
|
? new GotoObjectTask(_stack, _currentTarget)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint HuntObjectTask::currentTargetLoc() {
|
|
// If there is a current target object, return its locatio, else
|
|
// return Nowhere
|
|
return _currentTarget ? _currentTarget->getLocation() : Nowhere;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToBeNearObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
HuntToBeNearObjectTask::HuntToBeNearObjectTask(Common::InSaveFile *in, TaskID id) :
|
|
HuntObjectTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading HuntToBeNearObjectTask");
|
|
|
|
// Restore the range
|
|
_range = in->readUint16LE();
|
|
|
|
// Restore the evaluation _counter
|
|
_targetEvaluateCtr = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToBeNearObjectTask::archiveSize() const {
|
|
return HuntObjectTask::archiveSize()
|
|
+ sizeof(_range)
|
|
+ sizeof(_targetEvaluateCtr);
|
|
}
|
|
|
|
void HuntToBeNearObjectTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving HuntToBeNearObjectTask");
|
|
|
|
// Let the base class archive its data
|
|
HuntObjectTask::write(out);
|
|
|
|
// Store the range
|
|
out->writeUint16LE(_range);
|
|
|
|
// Store the evaluation _counter
|
|
out->writeByte(_targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToBeNearObjectTask::getType() const {
|
|
return kHuntToBeNearObjectTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToBeNearObjectTask::operator == (const Task &t) const {
|
|
if (t.getType() != kHuntToBeNearObjectTask) return false;
|
|
|
|
const HuntToBeNearObjectTask *taskPtr = (const HuntToBeNearObjectTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& _range == taskPtr->_range;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearObjectTask::evaluateTarget() {
|
|
// Determine if it is time to reevaluate the target object
|
|
if (_targetEvaluateCtr == 0) {
|
|
Actor *a = _stack->getActor();
|
|
int16 i;
|
|
GameObject *objArray[16];
|
|
int16 distArray[ARRAYSIZE(objArray)];
|
|
TargetObjectArray toa(
|
|
ARRAYSIZE(objArray),
|
|
objArray,
|
|
distArray);
|
|
SenseInfo info;
|
|
|
|
// Get an array of objects from the ObjectTarget
|
|
getTarget()->object(a->world(), a->getLocation(), toa);
|
|
|
|
// Iterate through each object in the array and determine if
|
|
// there is a line of sight to that object
|
|
for (i = 0; i < toa.objs; i++) {
|
|
ObjectID objID = objArray[i]->thisID();
|
|
|
|
if (a->canSenseSpecificObject(
|
|
info,
|
|
kMaxSenseRange,
|
|
objID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
objID)) {
|
|
_currentTarget = objArray[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
_targetEvaluateCtr = kTargetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluate _counter
|
|
_targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToBeNearObjectTask::atTarget() {
|
|
TilePoint _targetLoc = currentTargetLoc();
|
|
|
|
// Determine if we are within the specified range of the current
|
|
// target
|
|
return _targetLoc != Nowhere
|
|
&& _stack->getActor()->inRange(_targetLoc, _range);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearObjectTask::atTargetabortTask() {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearObjectTask::atTargetEvaluate() {
|
|
// If we're at the target, we're done
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearObjectTask::atTargetUpdate() {
|
|
// If we're at the target, we're done
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToPossessTask member functions
|
|
* ===================================================================== */
|
|
|
|
// Hunt to possess in not fully implemented yet
|
|
|
|
HuntToPossessTask::HuntToPossessTask(Common::InSaveFile *in, TaskID id) : HuntObjectTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading HuntToPossessTask");
|
|
|
|
// Restore evaluation _counter
|
|
_targetEvaluateCtr = in->readByte();
|
|
|
|
// Restore grab flag
|
|
_grabFlag = in->readUint16LE();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToPossessTask::archiveSize() const {
|
|
return HuntObjectTask::archiveSize()
|
|
+ sizeof(_targetEvaluateCtr)
|
|
+ sizeof(_grabFlag);
|
|
}
|
|
|
|
void HuntToPossessTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving HuntToPossessTask");
|
|
|
|
// Let the base class archive its data
|
|
HuntObjectTask::write(out);
|
|
|
|
// Store the evaluation _counter
|
|
out->writeByte(_targetEvaluateCtr);
|
|
|
|
// Store the grab flag
|
|
out->writeUint16LE(_grabFlag);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToPossessTask::getType() const {
|
|
return kHuntToPossessTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToPossessTask::operator == (const Task &t) const {
|
|
if (t.getType() != kHuntToPossessTask) return false;
|
|
|
|
const HuntToPossessTask *taskPtr = (const HuntToPossessTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToPossessTask::evaluateTarget() {
|
|
// Determine if it is time to reevaluate the target object
|
|
if (_targetEvaluateCtr == 0) {
|
|
Actor *a = _stack->getActor();
|
|
int16 i;
|
|
GameObject *objArray[16];
|
|
int16 distArray[ARRAYSIZE(objArray)];
|
|
TargetObjectArray toa(
|
|
ARRAYSIZE(objArray),
|
|
objArray,
|
|
distArray);
|
|
SenseInfo info;
|
|
|
|
// Get an array of objects from the ObjectTarget
|
|
getTarget()->object(a->world(), a->getLocation(), toa);
|
|
|
|
// Iterate through each object in the array and determine if
|
|
// there is a line of sight to that object
|
|
for (i = 0; i < toa.objs; i++) {
|
|
ObjectID objID = objArray[i]->thisID();
|
|
|
|
if (a->canSenseSpecificObject(
|
|
info,
|
|
kMaxSenseRange,
|
|
objID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
objID)) {
|
|
_currentTarget = objArray[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
_targetEvaluateCtr = kTargetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluate _counter
|
|
_targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToPossessTask::atTarget() {
|
|
Actor *a = _stack->getActor();
|
|
|
|
return _currentTarget
|
|
&& (a->inReach(_currentTarget->getLocation())
|
|
|| (_grabFlag
|
|
&& a->isContaining(_currentTarget)));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToPossessTask::atTargetabortTask() {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToPossessTask::atTargetEvaluate() {
|
|
if (_currentTarget && _stack->getActor()->isContaining(_currentTarget))
|
|
return kTaskSucceeded;
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToPossessTask::atTargetUpdate() {
|
|
// Hunt to possess in not implemented yet
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntActorTask::HuntActorTask(
|
|
TaskStack *ts,
|
|
const ActorTarget &at,
|
|
bool trackFlag) :
|
|
HuntTask(ts),
|
|
_flags(trackFlag ? kTrack : 0),
|
|
_currentTarget(nullptr) {
|
|
assert(at.size() <= sizeof(_targetMem));
|
|
debugC(2, kDebugTasks, " - HuntActorTask");
|
|
// Copy the target to the target buffer
|
|
at.clone(_targetMem);
|
|
}
|
|
|
|
HuntActorTask::HuntActorTask(Common::InSaveFile *in, TaskID id) : HuntTask(in, id) {
|
|
// Restore the flags
|
|
_flags = in->readByte();
|
|
|
|
// Restore the current target ID
|
|
ObjectID _currentTargetID = in->readUint16LE();
|
|
|
|
// Convert the ID to a GameObject pointer
|
|
_currentTarget = _currentTargetID != Nothing
|
|
? (Actor *)GameObject::objectAddress(_currentTargetID)
|
|
: nullptr;
|
|
|
|
// Reconstruct the object target
|
|
readTarget(_targetMem, in);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntActorTask::archiveSize() const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(_flags)
|
|
+ sizeof(ObjectID)
|
|
+ targetArchiveSize(getTarget());
|
|
}
|
|
|
|
void HuntActorTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
// Let the base class archive its data
|
|
HuntTask::write(out);
|
|
|
|
// Store the flags
|
|
out->writeByte(_flags);
|
|
|
|
// Store the ID
|
|
if (_currentTarget != nullptr)
|
|
out->writeUint16LE(_currentTarget->thisID());
|
|
else
|
|
out->writeUint16LE(Nothing);
|
|
|
|
// Store the object target
|
|
writeTarget(getTarget(), out);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntActorTask::targetHasChanged(GotoTask *gotoTarget) {
|
|
// Determine if the specified goto task's destination is the
|
|
// current target actor
|
|
GotoActorTask *gotoActor = (GotoActorTask *)gotoTarget;
|
|
return gotoActor->getTarget() != _currentTarget;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GotoTask *HuntActorTask::setupGoto() {
|
|
// If there is an actor to goto, setup a GotoActorTask, else
|
|
// return NULL
|
|
/* return _currentTarget
|
|
? new GotoActorTask( stack, _currentTarget, flags & kTrack )
|
|
: NULL;
|
|
*/
|
|
if (_currentTarget != nullptr) {
|
|
return new GotoActorTask(
|
|
_stack,
|
|
_currentTarget,
|
|
_flags & kTrack);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint HuntActorTask::currentTargetLoc() {
|
|
// If there is a current target actor, return its location, else
|
|
// return Nowhere
|
|
return _currentTarget ? _currentTarget->getLocation() : Nowhere;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToBeNearActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
HuntToBeNearActorTask::HuntToBeNearActorTask(Common::InSaveFile *in, TaskID id) :
|
|
HuntActorTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading HuntToBeNearActorTask");
|
|
|
|
// Get the _goAway task ID
|
|
_goAwayID = in->readSint16LE();
|
|
_goAway = nullptr;
|
|
|
|
// Restore the range
|
|
_range = in->readUint16LE();
|
|
|
|
// Restore the evaluation _counter
|
|
_targetEvaluateCtr = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void HuntToBeNearActorTask::fixup() {
|
|
// Let the base class fixup its pointers
|
|
HuntActorTask::fixup();
|
|
|
|
// Convert the task ID to a task pointer
|
|
_goAway = _goAwayID != NoTask
|
|
? (GoAwayFromObjectTask *)getTaskAddress(_goAwayID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToBeNearActorTask::archiveSize() const {
|
|
return HuntActorTask::archiveSize()
|
|
+ sizeof(TaskID) // _goAway ID
|
|
+ sizeof(_range)
|
|
+ sizeof(_targetEvaluateCtr);
|
|
}
|
|
|
|
void HuntToBeNearActorTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving HuntToBeNearActorTask");
|
|
|
|
// Let the base class archive its data
|
|
HuntActorTask::write(out);
|
|
|
|
// Store the task ID
|
|
if (_goAway != nullptr)
|
|
out->writeSint16LE(getTaskID(_goAway));
|
|
else
|
|
out->writeSint16LE(NoTask);
|
|
|
|
// Store the range
|
|
out->writeUint16LE(_range);
|
|
|
|
// Store the evaluation _counter
|
|
out->writeByte(_targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToBeNearActorTask::getType() const {
|
|
return kHuntToBeNearActorTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToBeNearActorTask::operator == (const Task &t) const {
|
|
if (t.getType() != kHuntToBeNearActorTask) return false;
|
|
|
|
const HuntToBeNearActorTask *taskPtr = (const HuntToBeNearActorTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& tracking() ? taskPtr->tracking() : !taskPtr->tracking()
|
|
&& _range == taskPtr->_range;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearActorTask::evaluateTarget() {
|
|
// Determine if its time to reevaluate the current target actor
|
|
if (_targetEvaluateCtr == 0) {
|
|
Actor *a = _stack->getActor();
|
|
int16 i;
|
|
Actor *_actorArray[16];
|
|
int16 distArray[ARRAYSIZE(_actorArray)];
|
|
TargetActorArray taa(
|
|
ARRAYSIZE(_actorArray),
|
|
_actorArray,
|
|
distArray);
|
|
SenseInfo info;
|
|
|
|
// Get an array of actor pointers from the ActorTarget
|
|
getTarget()->actor(a->world(), a->getLocation(), taa);
|
|
|
|
// Iterate through each actor in the array and determine if
|
|
// there is a line of sight to that actor
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])) {
|
|
if (_currentTarget != _actorArray[i]) {
|
|
if (atTarget()) atTargetabortTask();
|
|
_currentTarget = _actorArray[i];
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
_targetEvaluateCtr = kTargetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluation _counter.
|
|
_targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToBeNearActorTask::atTarget() {
|
|
TilePoint targetLoc = currentTargetLoc();
|
|
|
|
// Determine if we're within the specified range of the current
|
|
// target actor
|
|
if (targetLoc != Nowhere
|
|
&& _stack->getActor()->inRange(targetLoc, _range))
|
|
return true;
|
|
else {
|
|
if (_goAway != nullptr) {
|
|
_goAway->abortTask();
|
|
delete _goAway;
|
|
_goAway = nullptr;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearActorTask::atTargetabortTask() {
|
|
if (_goAway != nullptr) {
|
|
_goAway->abortTask();
|
|
delete _goAway;
|
|
_goAway = nullptr;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearActorTask::atTargetEvaluate() {
|
|
TilePoint _targetLoc = currentTargetLoc();
|
|
|
|
// If we're not TOO close, we're done
|
|
if (_stack->getActor()->inRange(_targetLoc, kTooClose))
|
|
return kTaskNotDone;
|
|
|
|
if (_goAway != nullptr) {
|
|
_goAway->abortTask();
|
|
delete _goAway;
|
|
_goAway = nullptr;
|
|
}
|
|
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearActorTask::atTargetUpdate() {
|
|
Actor *a = _stack->getActor();
|
|
TilePoint _targetLoc = currentTargetLoc();
|
|
|
|
// Determine if we're TOO close
|
|
if (a->inRange(_targetLoc, kTooClose)) {
|
|
// Setup a go away task if necessary and update it
|
|
if (_goAway == nullptr) {
|
|
_goAway = new GoAwayFromObjectTask(_stack, _currentTarget);
|
|
if (_goAway != nullptr) _goAway->update();
|
|
} else
|
|
_goAway->update();
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
// Delete the go away task if it exists
|
|
if (_goAway != nullptr) {
|
|
_goAway->abortTask();
|
|
delete _goAway;
|
|
_goAway = nullptr;
|
|
}
|
|
|
|
return kTaskSucceeded;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToKillTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntToKillTask::HuntToKillTask(
|
|
TaskStack *ts,
|
|
const ActorTarget &at,
|
|
bool trackFlag) :
|
|
HuntActorTask(ts, at, trackFlag),
|
|
_targetEvaluateCtr(0),
|
|
_specialAttackCtr(10),
|
|
_flags(kEvalWeapon) {
|
|
debugC(2, kDebugTasks, " - HuntToKillTask");
|
|
Actor *a = _stack->getActor();
|
|
|
|
if (isActor(a->_currentTarget))
|
|
_currentTarget = (Actor *)a->_currentTarget;
|
|
|
|
a->setFightStance(true);
|
|
}
|
|
|
|
HuntToKillTask::HuntToKillTask(Common::InSaveFile *in, TaskID id) : HuntActorTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading HuntToKillTask");
|
|
|
|
// Restore the evaluation _counter
|
|
_targetEvaluateCtr = in->readByte();
|
|
_specialAttackCtr = in->readByte();
|
|
_flags = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToKillTask::archiveSize() const {
|
|
return HuntActorTask::archiveSize()
|
|
+ sizeof(_targetEvaluateCtr)
|
|
+ sizeof(_specialAttackCtr)
|
|
+ sizeof(_flags);
|
|
}
|
|
|
|
void HuntToKillTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving HuntToKillTask");
|
|
|
|
// Let the base class archive its data
|
|
HuntActorTask::write(out);
|
|
|
|
// Store the evaluation _counter
|
|
out->writeByte(_targetEvaluateCtr);
|
|
out->writeByte(_specialAttackCtr);
|
|
out->writeByte(_flags);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToKillTask::getType() const {
|
|
return kHuntToKillTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToKillTask::operator == (const Task &t) const {
|
|
if (t.getType() != kHuntToKillTask) return false;
|
|
|
|
const HuntToKillTask *taskPtr = (const HuntToKillTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& tracking() ? taskPtr->tracking() : !taskPtr->tracking();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::abortTask() {
|
|
HuntActorTask::abortTask();
|
|
|
|
Actor *a = _stack->getActor();
|
|
|
|
a->_flags &= ~Actor::kAFSpecialAttack;
|
|
|
|
a->setFightStance(false);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToKillTask::update() {
|
|
if (_specialAttackCtr == 0) {
|
|
_stack->getActor()->_flags |= Actor::kAFSpecialAttack;
|
|
// A little hack to make monsters with 99 spellcraft cast spells more often
|
|
if (_stack->getActor()->getStats()->spellcraft >= 99)
|
|
_specialAttackCtr = 3;
|
|
else _specialAttackCtr = 10;
|
|
} else
|
|
_specialAttackCtr--;
|
|
|
|
return HuntActorTask::update();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::evaluateTarget() {
|
|
Actor *a = _stack->getActor();
|
|
|
|
if (_flags & kEvalWeapon && a->isInterruptable()) {
|
|
evaluateWeapon();
|
|
_flags &= ~kEvalWeapon;
|
|
}
|
|
|
|
|
|
// Determine if its time to reevaluate the current target actor
|
|
if (_targetEvaluateCtr == 0
|
|
|| (_currentTarget != nullptr
|
|
&& _currentTarget->isDead())) {
|
|
Actor *bestTarget = nullptr;
|
|
ActorProto *proto = (ActorProto *)a->proto();
|
|
int16 i;
|
|
Actor *_actorArray[16];
|
|
int16 distArray[ARRAYSIZE(_actorArray)];
|
|
TargetActorArray taa(
|
|
ARRAYSIZE(_actorArray),
|
|
_actorArray,
|
|
distArray);
|
|
SenseInfo info;
|
|
|
|
// Get an array of actor pointers from the ActorTarget
|
|
getTarget()->actor(a->world(), a->getLocation(), taa);
|
|
|
|
switch (proto->combatBehavior) {
|
|
case kBehaviorHungry:
|
|
// Iterate through each actor in the array and determine if
|
|
// there is a line of sight to that actor
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (_actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])) {
|
|
bestTarget = _actorArray[i];
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBehaviorCowardly: {
|
|
int16 bestScore = 0;
|
|
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (_actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])) {
|
|
int16 score;
|
|
|
|
score = closenessScore(distArray[i]) * 16
|
|
/ _actorArray[i]->defenseScore();
|
|
|
|
if (score > bestScore || bestTarget == nullptr) {
|
|
bestScore = score;
|
|
bestTarget = _actorArray[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBehaviorBerserk: {
|
|
int16 bestScore = 0;
|
|
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (_actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])) {
|
|
int16 score;
|
|
|
|
score = closenessScore(distArray[i])
|
|
* _actorArray[i]->offenseScore();
|
|
|
|
if (score > bestScore || bestTarget == nullptr) {
|
|
bestScore = score;
|
|
bestTarget = _actorArray[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kBehaviorSmart: {
|
|
int16 bestScore = 0;
|
|
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (_actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
kMaxSenseRange,
|
|
_actorArray[i])) {
|
|
int16 score;
|
|
|
|
score = closenessScore(distArray[i])
|
|
* _actorArray[i]->offenseScore()
|
|
/ _actorArray[i]->defenseScore();
|
|
|
|
if (score > bestScore || bestTarget == nullptr) {
|
|
bestScore = score;
|
|
bestTarget = _actorArray[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (bestTarget != _currentTarget) {
|
|
// If the current target has changed, abort any
|
|
// action currently taking place
|
|
if (atTarget()) atTargetabortTask();
|
|
_currentTarget = bestTarget;
|
|
a->_currentTarget = _currentTarget;
|
|
}
|
|
|
|
_flags |= kEvalWeapon;
|
|
|
|
_targetEvaluateCtr = kTargetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluation _counter
|
|
_targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToKillTask::atTarget() {
|
|
// Determine if we're in attack range of the current target
|
|
return _currentTarget != nullptr
|
|
&& _stack->getActor()->inAttackRange(
|
|
_currentTarget->getLocation());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::atTargetabortTask() {
|
|
// If the task is aborted while at the target actor, abort any
|
|
// attack currently taking place
|
|
_stack->getActor()->stopAttack(_currentTarget);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToKillTask::atTargetEvaluate() {
|
|
// This task is never done and must be aborted manually
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToKillTask::atTargetUpdate() {
|
|
assert(isActor(_currentTarget));
|
|
|
|
Actor *a = _stack->getActor();
|
|
|
|
// If we're ready to attack, attack
|
|
if (a->isInterruptable() && g_vm->_rnd->getRandomNumber(7) == 0) {
|
|
a->attack(_currentTarget);
|
|
_flags |= kEvalWeapon;
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::evaluateWeapon() {
|
|
Actor *a = _stack->getActor();
|
|
ObjectID actorID = a->thisID();
|
|
GameObject *obj,
|
|
*bestWeapon,
|
|
*currentWeapon;
|
|
int bestWeaponRating;
|
|
ContainerIterator iter(a);
|
|
|
|
bestWeapon = nullptr;
|
|
bestWeaponRating = 0;
|
|
currentWeapon = a->offensiveObject();
|
|
// If the current offensive object is the actor himself then there
|
|
// is no current weapon.
|
|
if (currentWeapon == a) currentWeapon = nullptr;
|
|
|
|
if (!isAutoWeaponSet() && isPlayerActor(a)) {
|
|
WeaponProto *weaponProto = currentWeapon != nullptr
|
|
? (WeaponProto *)currentWeapon->proto()
|
|
: nullptr;
|
|
|
|
if (_currentTarget == nullptr) {
|
|
warning("%s: _currentTarget = NULL (return)", a->objName());
|
|
return;
|
|
}
|
|
|
|
if (currentWeapon == nullptr
|
|
|| weaponProto->weaponRating(
|
|
a->thisID(),
|
|
actorID,
|
|
_currentTarget->thisID())
|
|
!= 0)
|
|
return;
|
|
}
|
|
|
|
while (iter.next(&obj) != Nothing) {
|
|
ProtoObj *proto = obj->proto();
|
|
uint16 cSet = proto->containmentSet();
|
|
|
|
// Simply use all armor objects
|
|
if (!isPlayerActor(a) && (cSet & ProtoObj::kIsArmor)) {
|
|
if (proto->useSlotAvailable(obj, a))
|
|
obj->use(actorID);
|
|
continue;
|
|
}
|
|
|
|
if (cSet & ProtoObj::kIsWeapon) {
|
|
WeaponProto *weaponProto = (WeaponProto *)proto;
|
|
int weaponRating;
|
|
|
|
if (_currentTarget) {
|
|
warning("%s: _currentTarget = NULL (weaponRating = 0)", a->objName());
|
|
weaponRating = weaponProto->weaponRating(obj->thisID(),
|
|
actorID,
|
|
_currentTarget->thisID());
|
|
} else
|
|
weaponRating = 0;
|
|
|
|
// a rating of zero means this weapon is useless
|
|
if (weaponRating == 0) continue;
|
|
|
|
if (obj == currentWeapon)
|
|
weaponRating += kCurrentWeaponBonus;
|
|
|
|
if (weaponRating > bestWeaponRating) {
|
|
bestWeaponRating = weaponRating;
|
|
bestWeapon = obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestWeapon != nullptr) {
|
|
if (bestWeapon != currentWeapon)
|
|
bestWeapon->use(actorID);
|
|
}
|
|
// If there is no useful best weapon and the actor is currently
|
|
// wielding a weapon, un-wield the weapon
|
|
else if (currentWeapon != nullptr)
|
|
currentWeapon->use(actorID);
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToGiveTask member functions
|
|
* ===================================================================== */
|
|
|
|
// Hunt to give is not implemented yet
|
|
|
|
HuntToGiveTask::HuntToGiveTask(Common::InSaveFile *in, TaskID id) : HuntActorTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading HuntToGiveTask");
|
|
|
|
// Get the object ID
|
|
ObjectID objToGiveID = in->readUint16LE();
|
|
|
|
// Convert the object ID to a pointer
|
|
_objToGive = objToGiveID != Nothing
|
|
? GameObject::objectAddress(objToGiveID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToGiveTask::archiveSize() const {
|
|
return HuntActorTask::archiveSize()
|
|
+ sizeof(ObjectID); // _objToGive ID
|
|
}
|
|
|
|
void HuntToGiveTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving HuntToGiveTask");
|
|
|
|
// Let base class archive its data
|
|
HuntActorTask::write(out);
|
|
|
|
// Store the ID
|
|
if (_objToGive != nullptr)
|
|
out->writeUint16LE(_objToGive->thisID());
|
|
else
|
|
out->writeUint16LE(Nothing);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToGiveTask::getType() const {
|
|
return kHuntToGiveTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToGiveTask::operator == (const Task &t) const {
|
|
if (t.getType() != kHuntToGiveTask) return false;
|
|
|
|
const HuntToGiveTask *taskPtr = (const HuntToGiveTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& tracking() ? taskPtr->tracking() : !taskPtr->tracking()
|
|
&& _objToGive == taskPtr->_objToGive;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToGiveTask::evaluateTarget() {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToGiveTask::atTarget() {
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToGiveTask::atTargetabortTask() {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToGiveTask::atTargetEvaluate() {
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToGiveTask::atTargetUpdate() {
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
BandTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool BandTask::BandingRepulsorIterator::first(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
assert(_a->_leader != nullptr && _a->_leader->_followers != nullptr);
|
|
|
|
_band = _a->_leader->_followers;
|
|
_bandIndex = 0;
|
|
|
|
while (_bandIndex < _band->size()) {
|
|
Actor *_bandMember = (*_band)[_bandIndex];
|
|
|
|
if (_bandMember != _a) {
|
|
repulsorVector = _bandMember->getLocation() - _a->getLocation();
|
|
repulsorStrength = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
_bandIndex++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool BandTask::BandingRepulsorIterator::next(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
assert(_a->_leader != nullptr && _a->_leader->_followers != nullptr);
|
|
assert(_band == _a->_leader->_followers);
|
|
assert(_bandIndex < _band->size());
|
|
|
|
_bandIndex++;
|
|
while (_bandIndex < _band->size()) {
|
|
Actor *_bandMember = (*_band)[_bandIndex];
|
|
|
|
if (_bandMember != _a) {
|
|
repulsorVector = _bandMember->getLocation() - _a->getLocation();
|
|
repulsorStrength = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
_bandIndex++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
BandTask::BandTask(Common::InSaveFile *in, TaskID id) : HuntTask(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading BandTask");
|
|
|
|
_attendID = in->readSint16LE();
|
|
_attend = nullptr;
|
|
|
|
// Restore the current target location
|
|
_currentTarget.load(in);
|
|
|
|
// Restore the target evaluation _counter
|
|
_targetEvaluateCtr = in->readByte();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void BandTask::fixup() {
|
|
// Let the base class fixup its pointers
|
|
HuntTask::fixup();
|
|
|
|
// Convert the TaskID to a Task pointer
|
|
_attend = _attendID != NoTask
|
|
? (AttendTask *)getTaskAddress(_attendID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 BandTask::archiveSize() const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(TaskID) // _attend ID
|
|
+ sizeof(_currentTarget)
|
|
+ sizeof(_targetEvaluateCtr);
|
|
}
|
|
|
|
void BandTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving BandTask");
|
|
|
|
// Let the base class archive its data
|
|
HuntTask::write(out);
|
|
|
|
// Store the _attend task ID
|
|
if (_attend != nullptr)
|
|
out->writeSint16LE(getTaskID(_attend));
|
|
else
|
|
out->writeSint16LE(NoTask);
|
|
|
|
// Store the current target location
|
|
_currentTarget.write(out);
|
|
|
|
// Store the target evaluation _counter
|
|
out->writeByte(_targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 BandTask::getType() const {
|
|
return kBandTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool BandTask::operator == (const Task &t) const {
|
|
return t.getType() == kBandTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void BandTask::evaluateTarget() {
|
|
if (_targetEvaluateCtr == 0) {
|
|
Actor *leader = _stack->getActor()->_leader;
|
|
TilePoint actorLoc = _stack->getActor()->getLocation(),
|
|
movementVector;
|
|
TilePoint repulsorVector;
|
|
int16 repulsorStrength;
|
|
TilePoint repulsorVectorArray[6];
|
|
int16 repulsorStrengthArray[ARRAYSIZE(repulsorVectorArray)];
|
|
int16 repulsorDistArray[ARRAYSIZE(repulsorVectorArray)];
|
|
int16 repulsorCount;
|
|
bool repulsorFlag;
|
|
|
|
RepulsorIterator *repulsorIter = getNewRepulsorIterator();
|
|
|
|
if (repulsorIter == nullptr) return;
|
|
|
|
// Count the leader as two _band members to double his
|
|
// repulsion
|
|
repulsorVectorArray[0] = leader->getLocation() - actorLoc;
|
|
repulsorStrengthArray[0] = 3;
|
|
repulsorDistArray[0] = repulsorVectorArray[0].quickHDistance();
|
|
repulsorCount = 1;
|
|
|
|
// Iterate through the _band members, adding their locations
|
|
// to the repulsor array sorted by distance.
|
|
for (repulsorFlag = repulsorIter->first(
|
|
repulsorVector,
|
|
repulsorStrength);
|
|
repulsorFlag;
|
|
repulsorFlag = repulsorIter->next(
|
|
repulsorVector,
|
|
repulsorStrength)) {
|
|
int16 repulsorDist = repulsorVector.quickHDistance();
|
|
int16 j = repulsorCount;
|
|
|
|
if (repulsorDist < repulsorDistArray[j - 1]) {
|
|
if (repulsorCount < (long)ARRAYSIZE(repulsorVectorArray)) {
|
|
repulsorDistArray[j] = repulsorDistArray[j - 1];
|
|
repulsorVectorArray[j] = repulsorVectorArray[j - 1];
|
|
repulsorStrengthArray[j] = repulsorStrengthArray[j - 1];
|
|
}
|
|
j--;
|
|
}
|
|
|
|
while (j > 0 && repulsorDist < repulsorDistArray[j - 1]) {
|
|
repulsorDistArray[j] = repulsorDistArray[j - 1];
|
|
repulsorVectorArray[j] = repulsorVectorArray[j - 1];
|
|
repulsorStrengthArray[j] = repulsorStrengthArray[j - 1];
|
|
j--;
|
|
}
|
|
|
|
if (j < (long)ARRAYSIZE(repulsorVectorArray)) {
|
|
if (repulsorCount < (long)ARRAYSIZE(repulsorVectorArray))
|
|
repulsorCount++;
|
|
repulsorDistArray[j] = repulsorDist;
|
|
repulsorVectorArray[j] = repulsorVector;
|
|
repulsorStrengthArray[j] = repulsorStrength;
|
|
}
|
|
}
|
|
|
|
delete repulsorIter;
|
|
|
|
// Compute the target location
|
|
movementVector = (leader->getLocation() - actorLoc)
|
|
+ computeRepulsionVector(
|
|
repulsorVectorArray,
|
|
repulsorStrengthArray,
|
|
repulsorCount);
|
|
|
|
_currentTarget = actorLoc + movementVector;
|
|
_currentTarget.z = leader->getLocation().z;
|
|
|
|
_targetEvaluateCtr = kTargetEvaluateRate;
|
|
}
|
|
|
|
_targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool BandTask::targetHasChanged(GotoTask *gotoTarget) {
|
|
GotoLocationTask *gotoLocation = (GotoLocationTask *)gotoTarget;
|
|
TilePoint actorLoc = _stack->getActor()->getLocation(),
|
|
oldTarget = gotoLocation->getTarget();
|
|
int16 slop;
|
|
|
|
slop = ((_currentTarget - actorLoc).quickHDistance()
|
|
+ ABS(_currentTarget.z - actorLoc.z))
|
|
/ 2;
|
|
|
|
if ((_currentTarget - oldTarget).quickHDistance()
|
|
+ ABS(_currentTarget.z - oldTarget.z)
|
|
> slop)
|
|
gotoLocation->changeTarget(_currentTarget);
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GotoTask *BandTask::setupGoto() {
|
|
return new GotoLocationTask(_stack, _currentTarget, getRunThreshold());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint BandTask::currentTargetLoc() {
|
|
return _currentTarget;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool BandTask::atTarget() {
|
|
TilePoint actorLoc = _stack->getActor()->getLocation();
|
|
|
|
if ((actorLoc - _currentTarget).quickHDistance() > 6
|
|
|| ABS(actorLoc.z - _currentTarget.z) > kMaxStepHeight) {
|
|
if (_attend != nullptr) {
|
|
_attend->abortTask();
|
|
delete _attend;
|
|
_attend = nullptr;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void BandTask::atTargetabortTask() {
|
|
if (_attend != nullptr) {
|
|
_attend->abortTask();
|
|
delete _attend;
|
|
_attend = nullptr;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult BandTask::atTargetEvaluate() {
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult BandTask::atTargetUpdate() {
|
|
Actor *a = _stack->getActor();
|
|
|
|
if (_attend != nullptr)
|
|
_attend->update();
|
|
else {
|
|
_attend = new AttendTask(_stack, a->_leader);
|
|
if (_attend != nullptr)
|
|
_attend->update();
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
int16 BandTask::getRunThreshold() {
|
|
return kTileUVSize * 3;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
BandTask::RepulsorIterator *BandTask::getNewRepulsorIterator() {
|
|
return new BandingRepulsorIterator(_stack->getActor());
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
BandAndAvoidEnemiesTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//bool BandAndAvoidEnemiesTask::BandAndAvoidEnemiesRepulsorIterator::firstEnemyRepulsor(
|
|
bool BandTask::BandAndAvoidEnemiesRepulsorIterator::firstEnemyRepulsor(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
assert(_iteratingThruEnemies);
|
|
|
|
int16 actorDistArray[ARRAYSIZE(_actorArray)];
|
|
TargetActorArray taa(ARRAYSIZE(_actorArray), _actorArray, actorDistArray);
|
|
ActorPropertyTarget target(kActorPropIDEnemy);
|
|
|
|
_numActors = target.actor(_a->world(), _a->getLocation(), taa);
|
|
|
|
assert(_numActors == taa.actors);
|
|
|
|
_actorIndex = 0;
|
|
|
|
if (_actorIndex < _numActors) {
|
|
repulsorVector =
|
|
_actorArray[_actorIndex]->getLocation() - _a->getLocation();
|
|
repulsorStrength = 6;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//bool BandAndAvoidEnemiesTask::BandAndAvoidEnemiesRepulsorIterator::nextEnemyRepulsor(
|
|
bool BandTask::BandAndAvoidEnemiesRepulsorIterator::nextEnemyRepulsor(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
assert(_iteratingThruEnemies);
|
|
|
|
_actorIndex++;
|
|
|
|
if (_actorIndex < _numActors) {
|
|
repulsorVector =
|
|
_actorArray[_actorIndex]->getLocation() - _a->getLocation();
|
|
repulsorStrength = 6;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//bool BandAndAvoidEnemiesTask::BandAndAvoidEnemiesRepulsorIterator::first(
|
|
bool BandTask::BandAndAvoidEnemiesRepulsorIterator::first(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
_iteratingThruEnemies = false;
|
|
|
|
if (BandingRepulsorIterator::first(repulsorVector, repulsorStrength))
|
|
return true;
|
|
|
|
_iteratingThruEnemies = true;
|
|
return firstEnemyRepulsor(repulsorVector, repulsorStrength);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//bool BandAndAvoidEnemiesTask::BandAndAvoidEnemiesRepulsorIterator::first(
|
|
bool BandTask::BandAndAvoidEnemiesRepulsorIterator::next(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
if (!_iteratingThruEnemies) {
|
|
if (BandingRepulsorIterator::next(repulsorVector, repulsorStrength))
|
|
return true;
|
|
|
|
_iteratingThruEnemies = true;
|
|
return firstEnemyRepulsor(repulsorVector, repulsorStrength);
|
|
}
|
|
|
|
return nextEnemyRepulsor(repulsorVector, repulsorStrength);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 BandAndAvoidEnemiesTask::getType() const {
|
|
return kBandAndAvoidEnemiesTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool BandAndAvoidEnemiesTask::operator == (const Task &t) const {
|
|
return t.getType() == kBandAndAvoidEnemiesTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
int16 BandAndAvoidEnemiesTask::getRunThreshold() {
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
BandTask::RepulsorIterator *BandAndAvoidEnemiesTask::getNewRepulsorIterator() {
|
|
return new BandAndAvoidEnemiesRepulsorIterator(_stack->getActor());
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
FollowPatrolRouteTask member functions
|
|
* ===================================================================== */
|
|
|
|
FollowPatrolRouteTask::FollowPatrolRouteTask(Common::InSaveFile *in, TaskID id) : Task(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading FollowPatrolRouteTask");
|
|
|
|
// Get the _gotoWayPoint TaskID
|
|
_gotoWayPointID = in->readSint16LE();
|
|
_gotoWayPoint = nullptr;
|
|
|
|
// Restore the patrol route iterator
|
|
_patrolIter.read(in);
|
|
|
|
// Restore the last waypoint number
|
|
_lastWayPointNum = in->readSint16LE();
|
|
|
|
// Restore the _paused flag
|
|
_paused = in->readUint16LE();
|
|
|
|
// Restore the _paused _counter
|
|
_counter = in->readSint16LE();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void FollowPatrolRouteTask::fixup() {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
// Convert the TaskID to a Task pointer
|
|
_gotoWayPoint = _gotoWayPointID != NoTask
|
|
? (GotoLocationTask *)getTaskAddress(_gotoWayPointID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 FollowPatrolRouteTask::archiveSize() const {
|
|
return Task::archiveSize()
|
|
+ sizeof(TaskID) // _gotoWayPoint ID
|
|
+ sizeof(_patrolIter)
|
|
+ sizeof(_lastWayPointNum)
|
|
+ sizeof(_paused)
|
|
+ sizeof(_counter);
|
|
}
|
|
|
|
void FollowPatrolRouteTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving FollowPatrolRouteTask");
|
|
|
|
// Let the base class archive its data
|
|
Task::write(out);
|
|
|
|
// Store the _gotoWayPoint ID
|
|
if (_gotoWayPoint != nullptr)
|
|
out->writeSint16LE(getTaskID(_gotoWayPoint));
|
|
else
|
|
out->writeSint16LE(NoTask);
|
|
|
|
// Store the PatrolRouteIterator
|
|
_patrolIter.write(out);
|
|
|
|
// Store the last waypoint number
|
|
out->writeSint16LE(_lastWayPointNum);
|
|
|
|
// Store the _paused flag
|
|
out->writeUint16LE(_paused);
|
|
|
|
// Store the _paused _counter
|
|
out->writeSint16LE(_counter);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 FollowPatrolRouteTask::getType() const {
|
|
return kFollowPatrolRouteTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void FollowPatrolRouteTask::abortTask() {
|
|
// If there is a subtask, get rid of it
|
|
if (_gotoWayPoint) {
|
|
_gotoWayPoint->abortTask();
|
|
delete _gotoWayPoint;
|
|
_gotoWayPoint = nullptr;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult FollowPatrolRouteTask::evaluate() {
|
|
// Simply check the patrol iterator to determine if there are
|
|
// any more waypoints
|
|
return *_patrolIter == Nowhere ? kTaskSucceeded : kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult FollowPatrolRouteTask::update() {
|
|
return !_paused ? handleFollowPatrolRoute() : handlePaused();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool FollowPatrolRouteTask::operator == (const Task &t) const {
|
|
if (t.getType() != kFollowPatrolRouteTask) return false;
|
|
|
|
const FollowPatrolRouteTask *taskPtr = (const FollowPatrolRouteTask *)&t;
|
|
|
|
return _patrolIter == taskPtr->_patrolIter
|
|
&& _lastWayPointNum == taskPtr->_lastWayPointNum;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used if this task is not _paused
|
|
|
|
TaskResult FollowPatrolRouteTask::handleFollowPatrolRoute() {
|
|
TilePoint currentWayPoint = *_patrolIter,
|
|
actorLoc = _stack->getActor()->getLocation();
|
|
|
|
if (currentWayPoint == Nowhere) return kTaskSucceeded;
|
|
|
|
// Determine if the actor has reached the waypoint tile position
|
|
if ((actorLoc.u >> kTileUVShift)
|
|
== (currentWayPoint.u >> kTileUVShift)
|
|
&& (actorLoc.v >> kTileUVShift)
|
|
== (currentWayPoint.v >> kTileUVShift)
|
|
&& ABS(actorLoc.z - currentWayPoint.z) <= kMaxStepHeight) {
|
|
// Delete the _gotoWayPoint task
|
|
if (_gotoWayPoint != nullptr) {
|
|
_gotoWayPoint->abortTask();
|
|
delete _gotoWayPoint;
|
|
_gotoWayPoint = nullptr;
|
|
}
|
|
|
|
// If this way point is the specified last way point,
|
|
// return success
|
|
if (_lastWayPointNum != -1
|
|
&& _patrolIter.wayPointNum() == _lastWayPointNum)
|
|
return kTaskSucceeded;
|
|
|
|
// If there are no more way points in the patrol route, return
|
|
// success
|
|
if ((currentWayPoint = *++_patrolIter) == Nowhere)
|
|
return kTaskSucceeded;
|
|
|
|
// We are at a way point so randomly determine if we should
|
|
// pause for a while.
|
|
if (g_vm->_rnd->getRandomNumber(3) == 0) {
|
|
pause();
|
|
return kTaskNotDone;
|
|
}
|
|
}
|
|
|
|
// Setup a _gotoWayPoint task if one doesn't already exist and
|
|
// update it
|
|
if (_gotoWayPoint != nullptr)
|
|
_gotoWayPoint->update();
|
|
else {
|
|
_gotoWayPoint = new GotoLocationTask(_stack, currentWayPoint);
|
|
if (_gotoWayPoint != nullptr) _gotoWayPoint->update();
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used if this task is _paused
|
|
|
|
TaskResult FollowPatrolRouteTask::handlePaused() {
|
|
TaskResult result;
|
|
|
|
if ((result = evaluate()) == kTaskNotDone) {
|
|
if (_counter == 0)
|
|
followPatrolRoute();
|
|
else
|
|
_counter--;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set this task into the _paused state
|
|
|
|
void FollowPatrolRouteTask::pause() {
|
|
_paused = true;
|
|
_counter = (g_vm->_rnd->getRandomNumber(63) + g_vm->_rnd->getRandomNumber(63)) / 2;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
AttendTask member functions
|
|
* ===================================================================== */
|
|
|
|
AttendTask::AttendTask(Common::InSaveFile *in, TaskID id) : Task(in, id) {
|
|
debugC(3, kDebugSaveload, "... Loading AttendTask");
|
|
|
|
// Get the object ID
|
|
ObjectID objID = in->readUint16LE();
|
|
|
|
// Convert the object ID to a pointer
|
|
_obj = objID != Nothing
|
|
? GameObject::objectAddress(objID)
|
|
: nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 AttendTask::archiveSize() const {
|
|
return Task::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
|
|
void AttendTask::write(Common::MemoryWriteStreamDynamic *out) const {
|
|
debugC(3, kDebugSaveload, "... Saving AttendTask");
|
|
|
|
// Let the base class archive its data
|
|
Task::write(out);
|
|
|
|
// Store the object ID
|
|
if (_obj != nullptr)
|
|
out->writeUint16LE(_obj->thisID());
|
|
else
|
|
out->writeUint16LE(Nothing);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 AttendTask::getType() const {
|
|
return kAttendTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void AttendTask::abortTask() {
|
|
MotionTask *actorMotion = _stack->getActor()->_moveTask;
|
|
|
|
// Determine if we need to abort the actor motion
|
|
if (actorMotion != nullptr && actorMotion->isTurn())
|
|
actorMotion->finishTurn();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult AttendTask::evaluate() {
|
|
// Attending must be stopped manually
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult AttendTask::update() {
|
|
Actor *a = _stack->getActor();
|
|
TilePoint _attendLoc = _obj->getWorldLocation();
|
|
|
|
// Determine if we are facing the object
|
|
if (a->_currentFacing != (_attendLoc - a->getLocation()).quickDir()) {
|
|
// If not, turn
|
|
if (!a->_moveTask || !a->_moveTask->isTurn())
|
|
MotionTask::turnTowards(*a, _attendLoc);
|
|
}
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool AttendTask::operator == (const Task &t) const {
|
|
if (t.getType() != kAttendTask) return false;
|
|
|
|
const AttendTask *taskPtr = (const AttendTask *)&t;
|
|
|
|
return _obj == taskPtr->_obj;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
TaskStack member functions
|
|
* ===================================================================== */
|
|
|
|
void TaskStack::write(Common::MemoryWriteStreamDynamic *out) {
|
|
// Store the stack bottom TaskID
|
|
out->writeSint16LE(_stackBottomID);
|
|
|
|
// Store the actor's id
|
|
out->writeUint16LE(_actor->thisID());
|
|
|
|
// Store the _evalCount and _evalRate
|
|
out->writeSint16LE(_evalCount);
|
|
|
|
out->writeSint16LE(_evalRate);
|
|
|
|
debugC(4, kDebugSaveload, "...... stackBottomID = %d", _stackBottomID);
|
|
debugC(4, kDebugSaveload, "...... actorID = %d", _actor->thisID());
|
|
debugC(4, kDebugSaveload, "...... evalCount = %d", _evalCount);
|
|
debugC(4, kDebugSaveload, "...... evalRate = %d", _evalRate);
|
|
}
|
|
|
|
void TaskStack::read(Common::InSaveFile *in) {
|
|
ObjectID actorID;
|
|
|
|
// Restore the stack bottom pointer
|
|
_stackBottomID = in->readSint16LE();
|
|
|
|
// Restore the actor pointer
|
|
actorID = in->readUint16LE();
|
|
_actor = (Actor *)GameObject::objectAddress(actorID);
|
|
|
|
// Restore the evaluation count
|
|
_evalCount = in->readSint16LE();
|
|
|
|
// Restore the evaluation rate
|
|
_evalRate = in->readSint16LE();
|
|
|
|
debugC(4, kDebugSaveload, "...... stackBottomID = %d", _stackBottomID);
|
|
debugC(4, kDebugSaveload, "...... actorID = %d", actorID);
|
|
debugC(4, kDebugSaveload, "...... evalCount = %d", _evalCount);
|
|
debugC(4, kDebugSaveload, "...... evalRate = %d", _evalRate);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set the bottom task of this task stack
|
|
|
|
void TaskStack::setTask(Task *t) {
|
|
assert(_stackBottomID == NoTask);
|
|
|
|
if (t->_stack == this) {
|
|
TaskID id = getTaskID(t);
|
|
|
|
_stackBottomID = id;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Abort all tasks in stack
|
|
|
|
void TaskStack::abortTask() {
|
|
if (_stackBottomID != NoTask) {
|
|
Task *stackBottom = getTaskAddress(_stackBottomID);
|
|
|
|
stackBottom->abortTask();
|
|
delete stackBottom;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Re-evaluate tasks in stack
|
|
|
|
TaskResult TaskStack::evaluate() {
|
|
if (_stackBottomID != -1) {
|
|
Task *stackBottom = getTaskAddress(_stackBottomID);
|
|
|
|
return stackBottom->evaluate();
|
|
} else
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update the state of the tasks in stack
|
|
|
|
TaskResult TaskStack::update() {
|
|
TaskResult result;
|
|
|
|
// If the actor is currently uniterruptable then this task is _paused
|
|
if (!_actor->isInterruptable())
|
|
return kTaskNotDone;
|
|
|
|
if (_stackBottomID != NoTask) {
|
|
Task *stackBottom = getTaskAddress(_stackBottomID);
|
|
|
|
// Determine if it is time to reevaluate the tasks
|
|
if (--_evalCount == 0) {
|
|
if ((result = stackBottom->evaluate()) != kTaskNotDone) {
|
|
delete stackBottom;
|
|
_stackBottomID = NoTask;
|
|
|
|
return result;
|
|
}
|
|
_evalCount = _evalRate;
|
|
}
|
|
|
|
// Update the tasks
|
|
if ((result = stackBottom->update()) != kTaskNotDone) {
|
|
delete stackBottom;
|
|
_stackBottomID = NoTask;
|
|
|
|
return result;
|
|
}
|
|
} else
|
|
return kTaskFailed;
|
|
|
|
return kTaskNotDone;
|
|
}
|
|
|
|
} // end of namespace Saga2
|