mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-10 11:51:52 +00:00
5331 lines
146 KiB
C++
5331 lines
146 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 2
|
|
* 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
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*
|
|
* Based on the original sources
|
|
* Faery Tale II -- The Halls of the Dead
|
|
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
|
|
*/
|
|
|
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL // FIXME: Remove
|
|
|
|
#include "saga2/std.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/savefile.h"
|
|
#include "saga2/tile.h"
|
|
|
|
namespace Saga2 {
|
|
|
|
bool actorTasksPaused;
|
|
|
|
/* ===================================================================== *
|
|
Prototypes
|
|
* ===================================================================== */
|
|
|
|
// Reconstruct a Task from an archive buffer
|
|
void *constructTask(TaskID id, void *buf);
|
|
|
|
// Return the number of bytes necessary to create an archive of the
|
|
// specified Task
|
|
int32 taskArchiveSize(Task *t);
|
|
|
|
// Create an archive of the specified Task in the specified buffer
|
|
void *archiveTask(Task *t, void *buf);
|
|
|
|
#if DEBUG
|
|
// Debugging function used to check the integrity of the global task
|
|
// list
|
|
void checkTaskListIntegrity(void);
|
|
#endif
|
|
|
|
/* ===================================================================== *
|
|
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 = 32;
|
|
|
|
// Manages the memory used for the TaskStack's. There will
|
|
// only be one global instantiation of this class
|
|
class TaskStackList {
|
|
#if DEBUG
|
|
friend void checkTaskListIntegrity(void);
|
|
#endif
|
|
|
|
struct TaskStackPlaceHolder : public DNode {
|
|
uint8 buf[sizeof(TaskStack)];
|
|
|
|
TaskStackPlaceHolder *nextDeletion; // Pointer to next in lazy deletion list
|
|
bool deleted; // Deletion indicator
|
|
|
|
TaskStack *getTaskStack(void) {
|
|
return (TaskStack *)&buf;
|
|
}
|
|
};
|
|
|
|
DList list, // active TaskStacks
|
|
free; // inactive TaskStacks
|
|
|
|
TaskStackPlaceHolder array[numTaskStacks];
|
|
|
|
bool lazyDelete; // Flag indicating lazy deletion mode
|
|
TaskStackPlaceHolder *deletionList; // Singly linked list of all task stack
|
|
// placed holders marked for deletion
|
|
|
|
public:
|
|
// Constructor -- initial construction
|
|
TaskStackList(void);
|
|
|
|
// Destructor
|
|
~TaskStackList(void);
|
|
|
|
// Reconstruct from an archive buffer
|
|
void *restore(void *buf);
|
|
|
|
// Return the number of bytes needed to make an archive of the
|
|
// TaskStackList
|
|
int32 archiveSize(void);
|
|
|
|
// Make an archive of the TaskStackList in an archive buffer
|
|
void *archive(void *buf);
|
|
|
|
// Place a TaskStack from the inactive list into the active
|
|
// list.
|
|
void *newTaskStack(void);
|
|
void *newTaskStack(TaskStackID id);
|
|
|
|
// Place a TaskStack back into the inactive list.
|
|
void deleteTaskStack(void *p);
|
|
|
|
// Return the specified TaskStack's ID
|
|
TaskStackID getTaskStackID(TaskStack *ts) {
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
warning("FIXME: TaskStackList::getTaskStackID(): unsafe pointer arithmetics");
|
|
tsp = ((TaskStackPlaceHolder *)(
|
|
(uint8 *)ts
|
|
- offsetof(TaskStackPlaceHolder, buf)));
|
|
return tsp - array;
|
|
}
|
|
|
|
// Return a pointer to a TaskStack given a TaskStackID
|
|
TaskStack *getTaskStackAddress(TaskStackID id) {
|
|
assert(id >= 0 && id < numTaskStacks);
|
|
return array[id].getTaskStack();
|
|
}
|
|
|
|
// Run through the TaskStacks in the active list and update
|
|
// each.
|
|
void updateTaskStacks(void);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskStackList constructor -- simply place each element the array in
|
|
// the inactive list
|
|
|
|
TaskStackList::TaskStackList(void) {
|
|
int i;
|
|
|
|
for (i = 0; i < elementsof(array); i++) {
|
|
array[i].deleted = false;
|
|
free.addTail(array[i]);
|
|
}
|
|
|
|
lazyDelete = false;
|
|
deletionList = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskStackList destructor
|
|
|
|
TaskStackList::~TaskStackList(void) {
|
|
assert(!lazyDelete);
|
|
|
|
TaskStackPlaceHolder *tsp;
|
|
TaskStackPlaceHolder *nextTsp;
|
|
|
|
for (tsp = (TaskStackPlaceHolder *)list.first();
|
|
tsp != NULL;
|
|
tsp = nextTsp) {
|
|
// Save the address of the next in the list
|
|
nextTsp = (TaskStackPlaceHolder *)tsp->next();
|
|
|
|
delete tsp->getTaskStack();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Reconstruct the TaskStackList from an archive buffer
|
|
|
|
void *TaskStackList::restore(void *buf) {
|
|
int16 i,
|
|
taskStackCount;
|
|
|
|
// Get the count of task stacks and increment the buffer pointer
|
|
taskStackCount = *((int16 *)buf);
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Iterate through the archive data, reconstructing the TaskStacks
|
|
for (i = 0; i < taskStackCount; i++) {
|
|
TaskStackID id;
|
|
TaskStack *ts;
|
|
|
|
// Retreive the TaskStack's id number
|
|
id = *((TaskStackID *)buf);
|
|
buf = (TaskStackID *)buf + 1;
|
|
|
|
ts = new (id) TaskStack(&buf);
|
|
|
|
// Plug this TaskStack into the Actor
|
|
ts->getActor()->curTask = ts;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to archive this TaskStackList
|
|
|
|
int32 TaskStackList::archiveSize(void) {
|
|
int32 size = sizeof(int16);
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
for (tsp = (TaskStackPlaceHolder *)list.first();
|
|
tsp != NULL;
|
|
tsp = (TaskStackPlaceHolder *)tsp->next())
|
|
size += sizeof(TaskStackID) + tsp->getTaskStack()->archiveSize();
|
|
|
|
return size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Make an archive of the TaskStackList in an archive buffer
|
|
|
|
void *TaskStackList::archive(void *buf) {
|
|
int16 taskStackCount = 0;
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
// Count the active task stacks
|
|
for (tsp = (TaskStackPlaceHolder *)list.first();
|
|
tsp != NULL;
|
|
tsp = (TaskStackPlaceHolder *)tsp->next())
|
|
taskStackCount++;
|
|
|
|
// Store the task stack count in the archive buffer
|
|
*((int16 *)buf) = taskStackCount;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Iterate through the task stacks, archiving each
|
|
for (tsp = (TaskStackPlaceHolder *)list.first();
|
|
tsp != NULL;
|
|
tsp = (TaskStackPlaceHolder *)tsp->next()) {
|
|
TaskStack *ts = tsp->getTaskStack();
|
|
|
|
// Store the TaskStack's id number
|
|
*((TaskStackID *)buf) = tsp - array;
|
|
buf = (TaskStackID *)buf + 1;
|
|
|
|
buf = ts->archive(buf);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Place a TaskStack into the active list and return its address
|
|
|
|
void *TaskStackList::newTaskStack(void) {
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
// Grab a stack place holder from the inactive list
|
|
tsp = (TaskStackPlaceHolder *)free.remHead();
|
|
|
|
if (tsp != NULL) {
|
|
// Link the stack place holder into the active list
|
|
list.addTail(*tsp);
|
|
|
|
return tsp->buf;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Place a specific TaskStack into the active list and return its address
|
|
|
|
void *TaskStackList::newTaskStack(TaskStackID id) {
|
|
assert(id >= 0 && id < elementsof(array));
|
|
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
// Grab the stack place holder from the inactive list
|
|
tsp = (TaskStackPlaceHolder *)&array[id];
|
|
tsp->remove();
|
|
|
|
// Place the stack place holder into the active list
|
|
list.addTail(*tsp);
|
|
|
|
return tsp->buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Remove the specified TaskStack from the active list and place it
|
|
// back into the inactive list
|
|
|
|
void TaskStackList::deleteTaskStack(void *p) {
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
warning("FIXME: TaskStackList::deleteTaskStack(): unsafe pointer arithmetics");
|
|
|
|
// Convert the pointer to the TaskStack to a pointer to the
|
|
// TaskStackPlaceHolder
|
|
tsp = (TaskStackPlaceHolder *)(
|
|
(uint8 *)p
|
|
- offsetof(TaskStackPlaceHolder, buf));
|
|
|
|
if (lazyDelete) {
|
|
tsp->deleted = true;
|
|
tsp->nextDeletion = deletionList;
|
|
deletionList = tsp;
|
|
} else {
|
|
// Remove the stack place holder from the active list
|
|
tsp->remove();
|
|
|
|
// Place it into the inactive list
|
|
free.addTail(*tsp);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Iterate through all of the TaskStacks in the active list and call
|
|
// their update function
|
|
|
|
void TaskStackList::updateTaskStacks(void) {
|
|
TaskStackPlaceHolder *tsp;
|
|
|
|
// Make sure all deletions during task processing are lazy
|
|
lazyDelete = true;
|
|
|
|
for (tsp = (TaskStackPlaceHolder *)list.first();
|
|
tsp != NULL;
|
|
tsp = (TaskStackPlaceHolder *)tsp->next()) {
|
|
// Skip all task stacks which have been marked as deleted
|
|
if (tsp->deleted)
|
|
continue;
|
|
|
|
TaskStack *ts = tsp->getTaskStack();
|
|
TaskResult result;
|
|
|
|
// Update the task stack and delete it if it is done
|
|
if ((result = ts->update()) != taskNotDone) {
|
|
Actor *a = ts->getActor();
|
|
assert(a != NULL);
|
|
|
|
a->handleTaskCompletion(result);
|
|
}
|
|
}
|
|
|
|
// Process all lazy deletions
|
|
lazyDelete = false;
|
|
while (deletionList != NULL) {
|
|
TaskStackPlaceHolder *nextDeletion = deletionList->nextDeletion;
|
|
|
|
// Remove the stack place holder from the active list
|
|
deletionList->remove();
|
|
|
|
// Place it into the inactive list
|
|
free.addTail(*deletionList);
|
|
|
|
// Now that the real deletion has taken place reset the deleted
|
|
// flag
|
|
deletionList->deleted = false;
|
|
|
|
deletionList = nextDeletion;
|
|
}
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
Global TaskStackList instantiation
|
|
* ===================================================================== */
|
|
|
|
// This is a statically allocated buffer large enough to hold a
|
|
// TaskStackList. The stackList is a TaskStackList reference to this
|
|
// area of memory. The reason that I did this in this manner is to
|
|
// prevent the TaskStackList constructor from being called until it is
|
|
// expicitly called using an overloaded new call. The overloaded new
|
|
// call will simply return a pointer to the stackListBuffer in order
|
|
// to construct the TaskStackList in place.
|
|
|
|
static uint8 stackListBuffer[sizeof(TaskStackList)];
|
|
|
|
static TaskStackList &stackList = *((TaskStackList *)stackListBuffer);
|
|
|
|
/* ===================================================================== *
|
|
Misc. task stack management functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Simply pass this call to the stackList member function,
|
|
// updateTaskStacks().
|
|
|
|
void updateActorTasks(void) {
|
|
if (!actorTasksPaused) stackList.updateTaskStacks();
|
|
#if DEBUG
|
|
checkTaskListIntegrity();
|
|
#endif
|
|
}
|
|
|
|
void pauseActorTasks(void) {
|
|
actorTasksPaused = true;
|
|
}
|
|
void resumeActorTasks(void) {
|
|
actorTasksPaused = false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the stackList member function newTaskStack() to get a pointer
|
|
// to a new TaskStack
|
|
|
|
void *newTaskStack(void) {
|
|
return stackList.newTaskStack();
|
|
}
|
|
void *newTaskStack(TaskStackID id) {
|
|
return stackList.newTaskStack(id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the stackList member function deleteTaskStack() to dispose of
|
|
// a previously allocated TaskStack
|
|
|
|
void deleteTaskStack(void *p) {
|
|
stackList.deleteTaskStack(p);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the specified TaskStack's ID
|
|
|
|
TaskStackID getTaskStackID(TaskStack *ts) {
|
|
return stackList.getTaskStackID(ts);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return a pointer to a TaskStack given a TaskStackID
|
|
|
|
TaskStack *getTaskStackAddress(TaskStackID id) {
|
|
return stackList.getTaskStackAddress(id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Initialize the stackList
|
|
|
|
void initTaskStacks(void) {
|
|
// Simply call the TaskStackList default constructor
|
|
new (&stackList) TaskStackList;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Save the stackList to a save file
|
|
|
|
void saveTaskStacks(SaveFileConstructor &saveGame) {
|
|
int32 archiveBufSize;
|
|
void *archiveBuffer;
|
|
|
|
archiveBufSize = stackList.archiveSize();
|
|
|
|
archiveBuffer = RNewPtr(archiveBufSize, NULL, "archive buffer");
|
|
if (archiveBuffer == NULL)
|
|
error("Unable to allocate task stack archive buffer");
|
|
|
|
stackList.archive(archiveBuffer);
|
|
|
|
saveGame.writeChunk(
|
|
MakeID('T', 'S', 'T', 'K'),
|
|
archiveBuffer,
|
|
archiveBufSize);
|
|
|
|
RDisposePtr(archiveBuffer);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Load the stackList from a save file
|
|
|
|
void loadTaskStacks(SaveFileReader &saveGame) {
|
|
// If there is no saved data, simply call the default constructor
|
|
if (saveGame.getChunkSize() == 0) {
|
|
new (&stackList) TaskStackList;
|
|
return;
|
|
}
|
|
|
|
int32 archiveBufSize;
|
|
void *archiveBuffer;
|
|
void *bufferPtr;
|
|
|
|
archiveBufSize = saveGame.getChunkSize();
|
|
archiveBuffer = RNewPtr(archiveBufSize, NULL, "archive buffer");
|
|
if (archiveBuffer == NULL)
|
|
error("Unable to allocate task stack archive buffer");
|
|
|
|
// Read the archived task stack data
|
|
saveGame.read(archiveBuffer, archiveBufSize);
|
|
|
|
bufferPtr = archiveBuffer;
|
|
|
|
// Reconstruct stackList from archived data
|
|
new (&stackList) TaskStackList;
|
|
bufferPtr = stackList.restore(bufferPtr);
|
|
|
|
assert(bufferPtr == &((char *)archiveBuffer)[archiveBufSize]);
|
|
|
|
RDisposePtr(archiveBuffer);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Cleanup the stackList
|
|
|
|
void cleanupTaskStacks(void) {
|
|
// Simply call stackList's destructor
|
|
stackList.~TaskStackList();
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
TaskList class
|
|
* ===================================================================== */
|
|
|
|
const int numTasks = 64;
|
|
|
|
// Manages the memory used for the Task's. There will only be one
|
|
// global instantiation of this class
|
|
class TaskList {
|
|
|
|
struct TaskPlaceHolder : public DNode {
|
|
#if DEBUG
|
|
char *fileName;
|
|
int lineNo;
|
|
bool marked;
|
|
#endif
|
|
uint8 buf[maxTaskSize];
|
|
|
|
Task *getTask(void) {
|
|
return (Task *)&buf;
|
|
}
|
|
};
|
|
|
|
DList list, // active Tasks
|
|
free; // inactive Tasks
|
|
|
|
TaskPlaceHolder array[numTasks];
|
|
|
|
public:
|
|
// Constructor -- initial construction
|
|
TaskList(void);
|
|
|
|
// Destructor
|
|
~TaskList(void);
|
|
|
|
// Reconstruct from an archive buffer
|
|
void *restore(void *buf);
|
|
|
|
// Return the number of bytes necessary to archive this task list
|
|
// in a buffer
|
|
int32 archiveSize(void);
|
|
|
|
// Create an archive of the task list in an archive buffer
|
|
void *archive(void *buf);
|
|
|
|
#if DEBUG
|
|
// Place a Task from the inactive list into the active
|
|
// list.
|
|
void *newTask(char *file, int line);
|
|
void *newTask(char *file, int line, TaskID id);
|
|
#else
|
|
// Place a Task from the inactive list into the active
|
|
// list.
|
|
void *newTask(void);
|
|
void *newTask(TaskID id);
|
|
#endif
|
|
|
|
// Place a Task back into the inactive list.
|
|
void deleteTask(void *t);
|
|
|
|
// Return the specified Task's ID
|
|
TaskID getTaskID(Task *t) {
|
|
TaskPlaceHolder *tp;
|
|
|
|
warning("FIXME: TaskList::getTaskID(): unsafe pointer arithmetics");
|
|
tp = ((TaskPlaceHolder *)(
|
|
(uint8 *)t
|
|
- offsetof(TaskPlaceHolder, buf)));
|
|
return tp - array;
|
|
}
|
|
|
|
// Return a pointer to a Task given a TaskID
|
|
Task *getTaskAddress(TaskID id) {
|
|
assert(id >= 0 && id < numTasks);
|
|
return array[id].getTask();
|
|
}
|
|
|
|
#if DEBUG
|
|
// Mark the specified task
|
|
void markTask(Task *t) {
|
|
TaskPlaceHolder *tp;
|
|
|
|
warning("FIXME: TaskList::markTask(): unsafe pointer arithmetics");
|
|
tp = ((TaskPlaceHolder *)(
|
|
(uint8 *)t
|
|
- offsetof(TaskPlaceHolder, buf)));
|
|
tp->marked = true;
|
|
}
|
|
|
|
// Verify that all allocated tasks are marked
|
|
void checkMarks(void);
|
|
|
|
// Clear all debugging marks
|
|
void clearMarks(void);
|
|
#endif
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskList constructor -- simply place each element of the array in
|
|
// the inactive list
|
|
|
|
TaskList::TaskList(void) {
|
|
int i;
|
|
|
|
for (i = 0; i < elementsof(array); i++)
|
|
free.addTail(array[i]);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskList destructor
|
|
|
|
TaskList::~TaskList(void) {
|
|
TaskPlaceHolder *tp;
|
|
TaskPlaceHolder *nextTP;
|
|
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = nextTP) {
|
|
// Save the address of the next in the list
|
|
nextTP = (TaskPlaceHolder *)tp->next();
|
|
|
|
delete tp->getTask();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Reconstruct from an archive buffer
|
|
|
|
void *TaskList::restore(void *buf) {
|
|
assert(list.first() == NULL);
|
|
|
|
int16 i,
|
|
taskCount;
|
|
TaskPlaceHolder *tp;
|
|
|
|
// Get the count of tasks and increment the buffer pointer
|
|
taskCount = *((int16 *)buf);
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Iterate through the archive data, reconstructing the Tasks
|
|
for (i = 0; i < taskCount; i++) {
|
|
TaskID id;
|
|
|
|
// Retreive the Task's id number
|
|
id = *((TaskID *)buf);
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
buf = constructTask(id, buf);
|
|
}
|
|
|
|
// Iterate through the Tasks to fixup the subtask pointers
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = (TaskPlaceHolder *)tp->next())
|
|
tp->getTask()->fixup();
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to archive this TaskList
|
|
|
|
int32 TaskList::archiveSize(void) {
|
|
int32 size = sizeof(int16);
|
|
TaskPlaceHolder *tp;
|
|
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = (TaskPlaceHolder *)tp->next())
|
|
size += sizeof(TaskID) + taskArchiveSize(tp->getTask());
|
|
|
|
return size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Make an archive of the TaskList in an archive buffer
|
|
|
|
void *TaskList::archive(void *buf) {
|
|
int16 taskCount = 0;
|
|
TaskPlaceHolder *tp;
|
|
|
|
// Count the active tasks
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = (TaskPlaceHolder *)tp->next())
|
|
taskCount++;
|
|
|
|
// Store the task count in the archive buffer
|
|
*((int16 *)buf) = taskCount;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Iterate through the tasks, archiving each
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = (TaskPlaceHolder *)tp->next()) {
|
|
Task *t = tp->getTask();
|
|
|
|
// Store the Task's id number
|
|
*((TaskID *)buf) = tp - array;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
buf = archiveTask(t, buf);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Place a Task into the active list and return its address
|
|
|
|
#if DEBUG
|
|
void *TaskList::newTask(char *file, int line)
|
|
#else
|
|
void *TaskList::newTask(void)
|
|
#endif
|
|
{
|
|
TaskPlaceHolder *tp;
|
|
|
|
// Grab a task holder from the inactive list
|
|
tp = (TaskPlaceHolder *)free.remHead();
|
|
|
|
if (tp != NULL) {
|
|
#if DEBUG
|
|
tp->fileName = file;
|
|
tp->lineNo = line;
|
|
tp->marked = false;
|
|
#endif
|
|
// Place the place holder into the active list
|
|
list.addTail(*tp);
|
|
|
|
return tp->buf;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Place a specific Task into the active list and return its address
|
|
|
|
#if DEBUG
|
|
void *TaskList::newTask(char *file, int line, TaskID id)
|
|
#else
|
|
void *TaskList::newTask(TaskID id)
|
|
#endif
|
|
{
|
|
assert(id >= 0 && id < elementsof(array));
|
|
|
|
TaskPlaceHolder *tp;
|
|
|
|
// Grab the task place holder from the inactive list
|
|
tp = (TaskPlaceHolder *)&array[id];
|
|
tp->remove();
|
|
|
|
#if DEBUG
|
|
tp->fileName = file;
|
|
tp->lineNo = line;
|
|
tp->marked = false;
|
|
#endif
|
|
// Place the place holder into the active list
|
|
list.addTail(*tp);
|
|
|
|
return tp->buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Remove the specified Task from the active list and place it back
|
|
// into the inactive list
|
|
|
|
void TaskList::deleteTask(void *p) {
|
|
TaskPlaceHolder *tp;
|
|
|
|
warning("FIXME: TaskList::deleteTask(): unsafe pointer arithmetics");
|
|
// Convert the pointer to the Task to a pointer to the
|
|
// TaskPlaceHolder
|
|
tp = (TaskPlaceHolder *)(
|
|
(uint8 *)p
|
|
- offsetof(TaskPlaceHolder, buf));
|
|
|
|
// Remove the task place holder from the active list
|
|
tp->remove();
|
|
|
|
// Place it into the inactive list
|
|
free.addTail(*tp);
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Verify that all allocated tasks are marked
|
|
|
|
void TaskList::checkMarks(void) {
|
|
TaskPlaceHolder *tp;
|
|
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = (TaskPlaceHolder *)tp->next()) {
|
|
if (!tp->marked)
|
|
throw gError(
|
|
"Task leak detected: %05.5d \"%s\"\n",
|
|
tp->lineNo,
|
|
tp->fileName);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Clear all debugging marks
|
|
|
|
void TaskList::clearMarks(void) {
|
|
TaskPlaceHolder *tp;
|
|
|
|
for (tp = (TaskPlaceHolder *)list.first();
|
|
tp != NULL;
|
|
tp = (TaskPlaceHolder *)tp->next())
|
|
tp->marked = false;
|
|
}
|
|
#endif
|
|
|
|
/* ===================================================================== *
|
|
Global TaskList instantiation
|
|
* ===================================================================== */
|
|
|
|
// This is a statically allocated buffer large enough to hold a TaskList.
|
|
// The taskList is a TaskList reference to this area of memory. The
|
|
// reason that I did this in this manner is to prevent the TaskList
|
|
// constructor from being called until it is expicitly called using an
|
|
// overloaded new call. The overloaded new call will simply return a
|
|
// pointer to the taskListBuffer in order to construct the TaskList in
|
|
// place.
|
|
|
|
static uint8 taskListBuffer[sizeof(TaskList)];
|
|
|
|
static TaskList &taskList = *((TaskList *)taskListBuffer);
|
|
|
|
/* ===================================================================== *
|
|
Misc. task management functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the taskList member function newTask() to get a pointer to a
|
|
// new TaskStack
|
|
|
|
#if DEBUG
|
|
void *newTask(char *file, int line) {
|
|
return taskList.newTask(file, line);
|
|
}
|
|
#else
|
|
void *newTask(void) {
|
|
return taskList.newTask();
|
|
}
|
|
#endif
|
|
|
|
#if DEBUG
|
|
void *newTask(char *file, int line, TaskID id) {
|
|
return taskList.newTask(file, line, id);
|
|
}
|
|
#else
|
|
void *newTask(TaskID id) {
|
|
return taskList.newTask(id);
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// Call the taskList member function deleteTask() to dispose of a
|
|
// previously allocated TaskStack
|
|
|
|
void deleteTask(void *p) {
|
|
taskList.deleteTask(p);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the specified Task's ID
|
|
|
|
TaskID getTaskID(Task *t) {
|
|
return taskList.getTaskID(t);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return a pointer to a Task given a TaskID
|
|
|
|
Task *getTaskAddress(TaskID id) {
|
|
return taskList.getTaskAddress(id);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Initialize the taskList
|
|
|
|
void initTasks(void) {
|
|
// Simply call the default constructor for the task list
|
|
new (&taskList) TaskList;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Save the taskList to save file
|
|
|
|
void saveTasks(SaveFileConstructor &saveGame) {
|
|
int32 archiveBufSize;
|
|
void *archiveBuffer;
|
|
void *bufferPtr;
|
|
|
|
archiveBufSize = taskList.archiveSize();
|
|
|
|
archiveBuffer = RNewPtr(archiveBufSize, NULL, "archive buffer");
|
|
if (archiveBuffer == NULL)
|
|
error("Unable to allocate task archive buffer");
|
|
|
|
bufferPtr = archiveBuffer;
|
|
|
|
bufferPtr = taskList.archive(bufferPtr);
|
|
|
|
assert((uint8 *)bufferPtr - (uint8 *)archiveBuffer == archiveBufSize);
|
|
|
|
saveGame.writeChunk(
|
|
MakeID('T', 'A', 'S', 'K'),
|
|
archiveBuffer,
|
|
archiveBufSize);
|
|
|
|
RDisposePtr(archiveBuffer);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Load the taskList from a save file
|
|
|
|
void loadTasks(SaveFileReader &saveGame) {
|
|
// If there is no saved data, simply call the default constructor
|
|
if (saveGame.getChunkSize() == 0) {
|
|
new (&taskList) TaskList;
|
|
return;
|
|
}
|
|
|
|
int32 archiveBufSize;
|
|
void *archiveBuffer;
|
|
void *bufferPtr;
|
|
|
|
archiveBufSize = saveGame.getChunkSize();
|
|
archiveBuffer = RNewPtr(archiveBufSize, NULL, "archive buffer");
|
|
if (archiveBuffer == NULL)
|
|
error("Unable to allocate task archive buffer");
|
|
|
|
// Read the archived task data
|
|
saveGame.read(archiveBuffer, archiveBufSize);
|
|
|
|
bufferPtr = archiveBuffer;
|
|
|
|
// Reconstruct taskList from archived data
|
|
new (&taskList) TaskList;
|
|
bufferPtr = taskList.restore(bufferPtr);
|
|
|
|
assert(bufferPtr == &((char *)archiveBuffer)[archiveBufSize]);
|
|
|
|
RDisposePtr(archiveBuffer);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Cleanup the taskList
|
|
|
|
void cleanupTasks(void) {
|
|
// Simply call the taskList's destructor
|
|
taskList.~TaskList();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Reconstruct a Task from an archive buffer
|
|
|
|
void *constructTask(TaskID id, void *buf) {
|
|
int16 type;
|
|
|
|
// Get the Task type
|
|
type = *((int16 *)buf);
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Reconstruct the Task based upon the type
|
|
switch (type) {
|
|
case wanderTask:
|
|
new(id) WanderTask(&buf);
|
|
break;
|
|
|
|
case tetheredWanderTask:
|
|
new(id) TetheredWanderTask(&buf);
|
|
break;
|
|
|
|
case gotoLocationTask:
|
|
new(id) GotoLocationTask(&buf);
|
|
break;
|
|
|
|
case gotoRegionTask:
|
|
new(id) GotoRegionTask(&buf);
|
|
break;
|
|
|
|
case gotoObjectTask:
|
|
new(id) GotoObjectTask(&buf);
|
|
break;
|
|
|
|
case gotoActorTask:
|
|
new(id) GotoActorTask(&buf);
|
|
break;
|
|
|
|
case goAwayFromObjectTask:
|
|
new(id) GoAwayFromObjectTask(&buf);
|
|
break;
|
|
|
|
case goAwayFromActorTask:
|
|
new(id) GoAwayFromActorTask(&buf);
|
|
break;
|
|
|
|
case huntToBeNearLocationTask:
|
|
new(id) HuntToBeNearLocationTask(&buf);
|
|
break;
|
|
|
|
case huntToBeNearObjectTask:
|
|
new(id) HuntToBeNearObjectTask(&buf);
|
|
break;
|
|
|
|
case huntToPossessTask:
|
|
new(id) HuntToPossessTask(&buf);
|
|
break;
|
|
|
|
case huntToBeNearActorTask:
|
|
new(id) HuntToBeNearActorTask(&buf);
|
|
break;
|
|
|
|
case huntToKillTask:
|
|
new(id) HuntToKillTask(&buf);
|
|
break;
|
|
|
|
case huntToGiveTask:
|
|
new(id) HuntToGiveTask(&buf);
|
|
break;
|
|
|
|
case bandTask:
|
|
new(id) BandTask(&buf);
|
|
break;
|
|
|
|
case bandAndAvoidEnemiesTask:
|
|
new(id) BandAndAvoidEnemiesTask(&buf);
|
|
break;
|
|
|
|
case followPatrolRouteTask:
|
|
new(id) FollowPatrolRouteTask(&buf);
|
|
break;
|
|
|
|
case attendTask:
|
|
new(id) AttendTask(&buf);
|
|
break;
|
|
|
|
#if 0
|
|
case defendTask:
|
|
new(id) DefendTask(&buf);
|
|
break;
|
|
|
|
case parryTask:
|
|
new(id) ParryTask(&buf);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// 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();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of the specified Task in the specified buffer
|
|
|
|
void *archiveTask(Task *t, void *buf) {
|
|
// Store the task's type
|
|
*((int16 *)buf) = t->getType();
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Store the task
|
|
buf = t->archive(buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to check the integrity of the global task
|
|
// list
|
|
|
|
void checkTaskListIntegrity(void) {
|
|
TaskStackList::TaskStackPlaceHolder *tsp;
|
|
|
|
// Clear all task marks
|
|
taskList.clearMarks();
|
|
|
|
// Iterate through all active task stacks and mark their associated
|
|
// tasks
|
|
for (tsp = (TaskStackList::TaskStackPlaceHolder *)stackList.list.first();
|
|
tsp != NULL;
|
|
tsp = (TaskStackList::TaskStackPlaceHolder *)tsp->next())
|
|
tsp->getTaskStack()->mark();
|
|
|
|
// Check the task marks
|
|
taskList.checkMarks();
|
|
}
|
|
#endif
|
|
|
|
/* ===================================================================== *
|
|
Task member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from an archive buffer
|
|
|
|
Task::Task(void **buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Place the stack ID into the stack pointer field
|
|
*((TaskStackID *)&stack) = *((TaskStackID *)bufferPtr);
|
|
|
|
*buf = (TaskStackID *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the Task pointers
|
|
|
|
void Task::fixup(void) {
|
|
// Convert the stack ID to a stack pointer
|
|
stack = getTaskStackAddress(*((TaskStackID *)&stack));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes necessary to create an archive of this
|
|
// object's data
|
|
|
|
inline int32 Task::archiveSize(void) const {
|
|
return sizeof(TaskStackID); // stack's ID
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object's data in an archive buffer
|
|
|
|
void *Task::archive(void *buf) const {
|
|
*((TaskStackID *)buf) = getTaskStackID(stack);
|
|
|
|
return (TaskStackID *)buf + 1;
|
|
}
|
|
|
|
|
|
#if DEBUG
|
|
void *Task::operator new (size_t sz, char *file, int line) {
|
|
assert(sz <= maxTaskSize);
|
|
return newTask(file, line);
|
|
}
|
|
|
|
void *Task::operator new (size_t sz, char *file, int line, TaskID id) {
|
|
assert(sz <= maxTaskSize);
|
|
return newTask(file, line, id);
|
|
}
|
|
#endif
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void Task::mark(void) {
|
|
taskList.markTask(this);
|
|
}
|
|
#endif
|
|
|
|
/* ===================================================================== *
|
|
WanderTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
WanderTask::WanderTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the paused flag
|
|
paused = *((bool *)bufferPtr);
|
|
bufferPtr = (bool *)bufferPtr + 1;
|
|
|
|
// Restore the counter
|
|
counter = *((int16 *)bufferPtr);
|
|
bufferPtr = (int16 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 WanderTask::archiveSize(void) const {
|
|
return Task::archiveSize()
|
|
+ sizeof(paused)
|
|
+ sizeof(counter);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *WanderTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
// Store the paused flag
|
|
*((bool *)buf) = paused;
|
|
buf = (bool *)buf + 1;
|
|
|
|
// Store the counter
|
|
*((int16 *)buf) = counter;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 WanderTask::getType(void) const {
|
|
return wanderTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void WanderTask::abortTask(void) {
|
|
// if the actor has a wander motion, abort it
|
|
MotionTask *actorMotion = stack->getActor()->moveTask;
|
|
|
|
if (actorMotion && actorMotion->isWander()) actorMotion->finishWalk();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult WanderTask::evaluate(void) {
|
|
// Wandering is never done. It must be stopped manually.
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult WanderTask::update(void) {
|
|
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() == wanderTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used when task is not paused
|
|
|
|
TaskResult WanderTask::handleWander(void) {
|
|
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 taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set this task into the paused state
|
|
|
|
void WanderTask::pause(void) {
|
|
// 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(void) {
|
|
paused = false;
|
|
counter = (g_vm->_rnd->getRandomNumber(255) + g_vm->_rnd->getRandomNumber(255)) / 2;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
TetheredWanderTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
TetheredWanderTask::TetheredWanderTask(void **buf) : WanderTask(buf) {
|
|
int16 *bufferPtr = (int16 *)*buf;
|
|
|
|
// Restore the tether coordinates
|
|
minU = *bufferPtr++;
|
|
minV = *bufferPtr++;
|
|
maxU = *bufferPtr++;
|
|
maxV = *bufferPtr++;
|
|
|
|
// Put the gotoTether ID into the gotoTether pointer field
|
|
*((TaskID *)&gotoTether) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (int16 *)((TaskID *)bufferPtr + 1);
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void TetheredWanderTask::fixup(void) {
|
|
// Let the base class fixup it's pointers
|
|
WanderTask::fixup();
|
|
|
|
TaskID gotoTetherID = *((TaskID *)&gotoTether);
|
|
|
|
// Restore the gotoTether pointer
|
|
gotoTether = gotoTetherID != NoTask
|
|
? (GotoRegionTask *)getTaskAddress(gotoTetherID)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in a buffer
|
|
|
|
inline int32 TetheredWanderTask::archiveSize(void) const {
|
|
return WanderTask::archiveSize()
|
|
+ sizeof(minU)
|
|
+ sizeof(minU)
|
|
+ sizeof(minU)
|
|
+ sizeof(minU)
|
|
+ sizeof(TaskID); // gotoTether ID
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *TetheredWanderTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = WanderTask::archive(buf);
|
|
|
|
int16 *bufferPtr = (int16 *)buf;
|
|
|
|
// Archive tether coordinates
|
|
*bufferPtr++ = minU;
|
|
*bufferPtr++ = minV;
|
|
*bufferPtr++ = maxU;
|
|
*bufferPtr++ = maxV;
|
|
|
|
// Archive gotoTether ID
|
|
*((TaskID *)bufferPtr) = gotoTether != NULL
|
|
? getTaskID(gotoTether)
|
|
: NoTask;
|
|
|
|
return (TaskID *)bufferPtr + 1;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void TetheredWanderTask::mark(void) {
|
|
WanderTask::mark();
|
|
if (gotoTether != NULL)
|
|
gotoTether->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 TetheredWanderTask::getType(void) const {
|
|
return tetheredWanderTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void TetheredWanderTask::abortTask(void) {
|
|
if (gotoTether != NULL) {
|
|
gotoTether->abortTask();
|
|
delete gotoTether;
|
|
gotoTether = NULL;
|
|
} 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() != tetheredWanderTask) return false;
|
|
|
|
TetheredWanderTask *taskPtr = (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(void) {
|
|
Actor *a = stack->getActor();
|
|
TilePoint actorLoc = a->getLocation();
|
|
|
|
if (actorLoc.u < minU || actorLoc.u >= maxU
|
|
|| actorLoc.v < minV || actorLoc.v >= maxV) {
|
|
if (gotoTether != NULL)
|
|
gotoTether->update();
|
|
else {
|
|
gotoTether = new GotoRegionTask(stack, minU, minV, maxU, maxV);
|
|
if (gotoTether != NULL) gotoTether->update();
|
|
}
|
|
} else {
|
|
if (gotoTether != NULL) {
|
|
gotoTether->abortTask();
|
|
delete gotoTether;
|
|
gotoTether = NULL;
|
|
}
|
|
|
|
bool startWander = false;
|
|
TileRegion motionTether;
|
|
|
|
MotionTask *actorMotion = a->moveTask;
|
|
|
|
if (actorMotion) {
|
|
TileRegion motionTether = actorMotion->getTether();
|
|
startWander = ((!actorMotion->isWander())
|
|
|| motionTether.min.u != minU
|
|
|| motionTether.min.v != minV
|
|
|| motionTether.max.u != maxU
|
|
|| motionTether.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 taskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GotoTask::GotoTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Get the wander TaskID
|
|
*((TaskID *)&wander) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
// Restore prevRunState
|
|
prevRunState = *((bool *)bufferPtr);
|
|
|
|
*buf = (bool *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void GotoTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
TaskID wanderID = *((TaskID *)&wander);
|
|
|
|
// Convert wanderID to a Task pointer
|
|
wander = wanderID != NoTask
|
|
? (WanderTask *)getTaskAddress(wanderID)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoTask::archiveSize(void) const {
|
|
return Task::archiveSize()
|
|
+ sizeof(TaskID) // wander ID
|
|
+ sizeof(prevRunState);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GotoTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
// Convert the wander Task pointer to a TaskID and store it
|
|
// in the buffer
|
|
*((TaskID *)buf) = wander != NULL ? getTaskID(wander) : NoTask;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
// Store prevRunState
|
|
*((bool *)buf) = prevRunState;
|
|
buf = (bool *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void GotoTask::mark(void) {
|
|
Task::mark();
|
|
if (wander != NULL)
|
|
wander->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void GotoTask::abortTask(void) {
|
|
// If there is a wander subtask, delete it.
|
|
if (wander) {
|
|
wander->abortTask();
|
|
delete wander;
|
|
wander = NULL;
|
|
} else {
|
|
MotionTask *actorMotion = stack->getActor()->moveTask;
|
|
|
|
if (actorMotion && actorMotion->isWalk()) actorMotion->finishWalk();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult GotoTask::evaluate(void) {
|
|
// Determine if we have reach the target.
|
|
if (stack->getActor()->getLocation() == destination()) {
|
|
abortTask();
|
|
return taskSucceeded;
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult GotoTask::update(void) {
|
|
// Check to see if we have reached the target
|
|
{
|
|
TaskResult result = evaluate();
|
|
if (result != taskNotDone) return result;
|
|
}
|
|
|
|
Actor *const a = stack->getActor();
|
|
// Compute the immediate destination based upon wether 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 != NULL) {
|
|
delete wander;
|
|
wander = NULL;
|
|
}
|
|
|
|
// Determine if there is aready a motion task, and if so,
|
|
// wether or not it needs to be modified.
|
|
MotionTask *actorMotion = a->moveTask;
|
|
TilePoint actorLoc = a->getLocation();
|
|
|
|
if (actorMotion != NULL && 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 != NULL)
|
|
wander->update();
|
|
else {
|
|
wander = new WanderTask(stack);
|
|
if (wander != NULL) wander->update();
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoLocationTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GotoLocationTask::GotoLocationTask(void **buf) : GotoTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the target location
|
|
targetLoc = *((TilePoint *)bufferPtr);
|
|
bufferPtr = (TilePoint *)bufferPtr + 1;
|
|
|
|
// Restore the runThreshold
|
|
runThreshold = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoLocationTask::archiveSize(void) const {
|
|
return GotoTask::archiveSize()
|
|
+ sizeof(targetLoc)
|
|
+ sizeof(runThreshold);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GotoLocationTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GotoTask::archive(buf);
|
|
|
|
// Archive the target location
|
|
*((TilePoint *)buf) = targetLoc;
|
|
buf = (TilePoint *)buf + 1;
|
|
|
|
// Archive the run threshold
|
|
*((uint8 *)buf) = runThreshold;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoLocationTask::getType(void) const {
|
|
return gotoLocationTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoLocationTask::operator == (const Task &t) const {
|
|
if (t.getType() != gotoLocationTask) return false;
|
|
|
|
GotoLocationTask *taskPtr = (GotoLocationTask *)&t;
|
|
|
|
return targetLoc == taskPtr->targetLoc
|
|
&& runThreshold == taskPtr->runThreshold;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoLocationTask::destination(void) {
|
|
// Simply return the target location
|
|
return targetLoc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoLocationTask::intermediateDest(void) {
|
|
// GotoLocationTask's never have an intermediate destination
|
|
return targetLoc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoLocationTask::lineOfSight(void) {
|
|
// Let's pretend that there is always a line of sight to the
|
|
// target location
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoLocationTask::run(void) {
|
|
TilePoint actorLoc = stack->getActor()->getLocation();
|
|
|
|
return runThreshold != maxuint8
|
|
? (targetLoc - actorLoc).quickHDistance() > runThreshold
|
|
|| abs(targetLoc.z - actorLoc.z) > runThreshold
|
|
: false;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoRegionTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GotoRegionTask::GotoRegionTask(void **buf) : GotoTask(buf) {
|
|
int16 *bufferPtr = (int16 *)*buf;
|
|
|
|
// Restore the region coordinates
|
|
regionMinU = *bufferPtr++;
|
|
regionMinV = *bufferPtr++;
|
|
regionMaxU = *bufferPtr++;
|
|
regionMaxV = *bufferPtr++;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoRegionTask::archiveSize(void) const {
|
|
return GotoTask::archiveSize()
|
|
+ sizeof(regionMinU)
|
|
+ sizeof(regionMinV)
|
|
+ sizeof(regionMaxU)
|
|
+ sizeof(regionMaxV);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GotoRegionTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GotoTask::archive(buf);
|
|
|
|
int16 *bufferPtr = (int16 *)buf;
|
|
|
|
// Archive the region coordinates
|
|
*bufferPtr++ = regionMinU;
|
|
*bufferPtr++ = regionMinV;
|
|
*bufferPtr++ = regionMaxU;
|
|
*bufferPtr++ = regionMaxV;
|
|
|
|
return bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoRegionTask::getType(void) const {
|
|
return gotoRegionTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoRegionTask::operator == (const Task &t) const {
|
|
if (t.getType() != gotoRegionTask) return false;
|
|
|
|
GotoRegionTask *taskPtr = (GotoRegionTask *)&t;
|
|
|
|
return regionMinU == taskPtr->regionMinU
|
|
&& regionMinV == taskPtr->regionMinV
|
|
&& regionMaxU == taskPtr->regionMaxU
|
|
&& regionMaxV == taskPtr->regionMaxV;
|
|
}
|
|
|
|
TilePoint GotoRegionTask::destination(void) {
|
|
TilePoint actorLoc = stack->getActor()->getLocation();
|
|
|
|
return TilePoint(
|
|
clamp(regionMinU, actorLoc.u, regionMaxU - 1),
|
|
clamp(regionMinV, actorLoc.v, regionMaxV - 1),
|
|
actorLoc.z);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoRegionTask::intermediateDest(void) {
|
|
return destination();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoRegionTask::lineOfSight(void) {
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoRegionTask::run(void) {
|
|
return false;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoObjectTargetTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GotoObjectTargetTask::GotoObjectTargetTask(void **buf) : GotoTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore lastTestedLoc and increment pointer
|
|
lastTestedLoc = *((TilePoint *)bufferPtr);
|
|
bufferPtr = (TilePoint *)bufferPtr + 1;
|
|
|
|
// Restore sightCtr and increment pointer
|
|
sightCtr = *((int16 *)bufferPtr);
|
|
bufferPtr = (int16 *)bufferPtr + 1;
|
|
|
|
// Restore the flags and increment pointer
|
|
flags = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
// Restore lastKnownLoc
|
|
lastKnownLoc = *((TilePoint *)bufferPtr);
|
|
|
|
*buf = (TilePoint *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoObjectTargetTask::archiveSize(void) const {
|
|
return GotoTask::archiveSize()
|
|
+ sizeof(lastTestedLoc)
|
|
+ sizeof(sightCtr)
|
|
+ sizeof(flags)
|
|
+ sizeof(lastKnownLoc);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GotoObjectTargetTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GotoTask::archive(buf);
|
|
|
|
// Archive lastTestedLoc and increment pointer
|
|
*((TilePoint *)buf) = lastTestedLoc;
|
|
buf = (TilePoint *)buf + 1;
|
|
|
|
// Archive sightCtr and increment pointer
|
|
*((int16 *)buf) = sightCtr;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Archive the flags and increment pointer
|
|
*((uint8 *)buf) = flags;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
// Archive lastKnownLoc
|
|
*((TilePoint *)buf) = lastKnownLoc;
|
|
|
|
return (TilePoint *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoObjectTargetTask::destination(void) {
|
|
// Return the object's true location
|
|
return getObject()->getLocation();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GotoObjectTargetTask::intermediateDest(void) {
|
|
// Return the last known location
|
|
return lastKnownLoc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoObjectTargetTask::lineOfSight(void) {
|
|
if (flags & track) {
|
|
flags |= inSight;
|
|
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 & inSight) {
|
|
// 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,
|
|
maxSenseRange,
|
|
targetID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
targetID))
|
|
flags |= inSight;
|
|
else
|
|
flags &= ~inSight;
|
|
lastTestedLoc = targetLoc;
|
|
}
|
|
} else {
|
|
// If the object was not privously in sight, retest the line
|
|
// of sight periodically
|
|
if (sightCtr == 0) {
|
|
sightCtr = sightRate;
|
|
if (a->canSenseSpecificObject(
|
|
info,
|
|
maxSenseRange,
|
|
targetID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
targetID))
|
|
flags |= inSight;
|
|
else
|
|
flags &= ~inSight;
|
|
lastTestedLoc = targetLoc;
|
|
}
|
|
sightCtr--;
|
|
}
|
|
|
|
if (flags & inSight) {
|
|
// 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 & inSight;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GotoObjectTask::GotoObjectTask(void **buf) :
|
|
GotoObjectTargetTask(buf) {
|
|
ObjectID *bufferPtr = (ObjectID *)*buf;
|
|
|
|
// Restore the targetObj pointer
|
|
targetObj = *bufferPtr != Nothing
|
|
? GameObject::objectAddress(*bufferPtr)
|
|
: NULL;
|
|
|
|
*buf = bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoObjectTask::archiveSize(void) const {
|
|
return GotoObjectTargetTask::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GotoObjectTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GotoObjectTargetTask::archive(buf);
|
|
|
|
*((ObjectID *)buf) = targetObj != NULL
|
|
? targetObj->thisID()
|
|
: Nothing;
|
|
|
|
return (ObjectID *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoObjectTask::getType(void) const {
|
|
return gotoObjectTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoObjectTask::operator == (const Task &t) const {
|
|
if (t.getType() != gotoObjectTask) return false;
|
|
|
|
GotoObjectTask *taskPtr = (GotoObjectTask *)&t;
|
|
|
|
return tracking() == taskPtr->tracking()
|
|
&& targetObj == taskPtr->targetObj;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GameObject *GotoObjectTask::getObject(void) {
|
|
// Simply return the pointer to the target object
|
|
return targetObj;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoObjectTask::run(void) {
|
|
// Running after objects has not been implemented yet
|
|
return false;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GotoActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GotoActorTask::GotoActorTask(void **buf) :
|
|
GotoObjectTargetTask(buf) {
|
|
ObjectID *bufferPtr = (ObjectID *)*buf;
|
|
|
|
// Restore the targetObj pointer
|
|
targetActor = *bufferPtr != Nothing
|
|
? (Actor *)GameObject::objectAddress(*bufferPtr)
|
|
: NULL;
|
|
|
|
*buf = bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GotoActorTask::archiveSize(void) const {
|
|
return GotoObjectTargetTask::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GotoActorTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GotoObjectTargetTask::archive(buf);
|
|
|
|
*((ObjectID *)buf) = targetActor != NULL
|
|
? targetActor->thisID()
|
|
: Nothing;
|
|
|
|
return (ObjectID *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GotoActorTask::getType(void) const {
|
|
return gotoActorTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GotoActorTask::operator == (const Task &t) const {
|
|
if (t.getType() != gotoActorTask) return false;
|
|
|
|
GotoActorTask *taskPtr = (GotoActorTask *)&t;
|
|
|
|
return tracking() == taskPtr->tracking()
|
|
&& targetActor == taskPtr->targetActor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
GameObject *GotoActorTask::getObject(void) {
|
|
// Simply return the pointer to the target actor
|
|
return (GameObject *)targetActor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool GotoActorTask::run(void) {
|
|
if (isInSight()) {
|
|
TilePoint actorLoc = stack->getActor()->getLocation(),
|
|
targetLoc = getTarget()->getLocation();
|
|
|
|
return (actorLoc - targetLoc).quickHDistance() >= kTileUVSize * 4;
|
|
} else
|
|
return lastKnownLoc != Nowhere;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GoAwayFromTask member functions
|
|
* ===================================================================== */
|
|
|
|
GoAwayFromTask::GoAwayFromTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Get the subtask ID
|
|
*((TaskID *)&goTask) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
// Restore the flags
|
|
flags = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointer
|
|
|
|
void GoAwayFromTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
TaskID goTaskID;
|
|
|
|
// Get the subtask ID
|
|
goTaskID = *((TaskID *)&goTask);
|
|
|
|
// Convert the subtask ID to a pointer
|
|
goTask = goTaskID != NoTask
|
|
? (GotoLocationTask *)getTaskAddress(goTaskID)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 GoAwayFromTask::archiveSize(void) const {
|
|
return Task::archiveSize() + sizeof(TaskID) + sizeof(flags);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GoAwayFromTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
// Store the subTask's ID
|
|
*((TaskID *)buf) = goTask != NULL ? getTaskID(goTask) : NoTask;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
// Store the flags
|
|
*((uint8 *)buf) = flags;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void GoAwayFromTask::mark(void) {
|
|
Task::mark();
|
|
if (goTask != NULL)
|
|
goTask->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// Abort this task
|
|
|
|
void GoAwayFromTask::abortTask(void) {
|
|
if (goTask != NULL) {
|
|
goTask->abortTask();
|
|
delete goTask;
|
|
goTask = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Evaluate this task
|
|
|
|
TaskResult GoAwayFromTask::evaluate(void) {
|
|
// Going away is never done, it must be stopped manually
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update this task
|
|
|
|
TaskResult GoAwayFromTask::update(void) {
|
|
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();
|
|
MotionTask *actorMotion = a->moveTask;
|
|
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 != NULL) {
|
|
if (goTask->getTarget() != dest)
|
|
goTask->changeTarget(dest);
|
|
|
|
goTask->update();
|
|
} else {
|
|
if ((goTask = flags & run
|
|
? new GotoLocationTask(stack, dest, 0)
|
|
: new GotoLocationTask(stack, dest))
|
|
!= NULL)
|
|
goTask->update();
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GoAwayFromObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GoAwayFromObjectTask::GoAwayFromObjectTask(void **buf) :
|
|
GoAwayFromTask(buf) {
|
|
ObjectID *bufferPtr = (ObjectID *)*buf;
|
|
|
|
ObjectID objectID;
|
|
|
|
// Get the object's ID
|
|
objectID = *((ObjectID *)bufferPtr);
|
|
bufferPtr = (ObjectID *)bufferPtr + 1;
|
|
|
|
// Convert the ID to an object pointer
|
|
obj = objectID != Nothing ? GameObject::objectAddress(objectID) : NULL;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 GoAwayFromObjectTask::archiveSize(void) const {
|
|
return GoAwayFromTask::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GoAwayFromObjectTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GoAwayFromTask::archive(buf);
|
|
|
|
ObjectID objectID;
|
|
|
|
// Convert the object pointer to an object ID
|
|
objectID = obj != NULL ? obj->thisID() : Nothing;
|
|
|
|
// Store the object's ID
|
|
*((ObjectID *)buf) = objectID;
|
|
buf = (ObjectID *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GoAwayFromObjectTask::getType(void) const {
|
|
return goAwayFromObjectTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GoAwayFromObjectTask::operator == (const Task &t) const {
|
|
if (t.getType() != goAwayFromObjectTask) return false;
|
|
|
|
GoAwayFromObjectTask *taskPtr = (GoAwayFromObjectTask *)&t;
|
|
|
|
return obj == taskPtr->obj;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Simply return the object's location
|
|
|
|
TilePoint GoAwayFromObjectTask::getRepulsionVector(void) {
|
|
return stack->getActor()->getLocation() - obj->getLocation();
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
GoAwayFromActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
GoAwayFromActorTask::GoAwayFromActorTask(
|
|
TaskStack *ts,
|
|
Actor *a,
|
|
bool runFlag) :
|
|
GoAwayFromTask(ts, runFlag) {
|
|
SpecificActorTarget(a).clone(targetMem);
|
|
}
|
|
|
|
GoAwayFromActorTask::GoAwayFromActorTask(
|
|
TaskStack *ts,
|
|
const ActorTarget &at,
|
|
bool runFlag) :
|
|
GoAwayFromTask(ts, runFlag) {
|
|
assert(at.size() <= sizeof(targetMem));
|
|
// Copy the target to the target buffer
|
|
at.clone(targetMem);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
GoAwayFromActorTask::GoAwayFromActorTask(void **buf) : GoAwayFromTask(buf) {
|
|
// Restore the target
|
|
*buf = constructTarget(targetMem, *buf);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 GoAwayFromActorTask::archiveSize(void) const {
|
|
return GoAwayFromTask::archiveSize() + targetArchiveSize(getTarget());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *GoAwayFromActorTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = GoAwayFromTask::archive(buf);
|
|
|
|
// Store the target
|
|
buf = archiveTarget(getTarget(), buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 GoAwayFromActorTask::getType(void) const {
|
|
return goAwayFromActorTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool GoAwayFromActorTask::operator == (const Task &t) const {
|
|
if (t.getType() != goAwayFromActorTask) return false;
|
|
|
|
GoAwayFromActorTask *taskPtr = (GoAwayFromActorTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint GoAwayFromActorTask::getRepulsionVector(void) {
|
|
Actor *a = stack->getActor();
|
|
TilePoint actorLoc = a->getLocation(),
|
|
repulsionVector;
|
|
int16 i;
|
|
TilePoint locArray[6];
|
|
int16 strengthArray[elementsof(locArray)] =
|
|
{ 1, 1, 1, 1, 1, 1 };
|
|
int16 distArray[elementsof(locArray)];
|
|
TargetLocationArray tla(
|
|
elementsof(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
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntTask::HuntTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the flags
|
|
huntFlags = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
// If the flags say we have a sub task, restore it too
|
|
if (huntFlags & (huntGoto | huntWander)) {
|
|
*((TaskID *)&subTask) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
}
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void HuntTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
if (huntFlags & (huntGoto | huntWander)) {
|
|
TaskID subTaskID = *((TaskID *)&subTask);
|
|
subTask = getTaskAddress(subTaskID);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntTask::archiveSize(void) const {
|
|
int32 size = 0;
|
|
|
|
size += Task::archiveSize() + sizeof(huntFlags);
|
|
if (huntFlags & (huntGoto | huntWander)) size += sizeof(TaskID);
|
|
|
|
return size;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
// Store the flags
|
|
*((uint8 *)buf) = huntFlags;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
// If the flags say we have a sub task, store it too
|
|
if (huntFlags & (huntGoto | huntWander)) {
|
|
*((TaskID *)buf) = getTaskID(subTask);
|
|
buf = (TaskID *)buf + 1;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void HuntTask::mark(void) {
|
|
Task::mark();
|
|
if (huntFlags & (huntGoto | huntWander))
|
|
subTask->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntTask::abortTask(void) {
|
|
if (huntFlags & (huntWander | huntGoto)) {
|
|
subTask->abortTask();
|
|
delete subTask;
|
|
}
|
|
|
|
// If we've reached the target call the atTargetabortTask() function
|
|
if (atTarget()) atTargetabortTask();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntTask::evaluate(void) {
|
|
if (atTarget()) {
|
|
// If we've reached the target abort any sub tasks
|
|
if (huntFlags & huntWander)
|
|
removeWanderTask();
|
|
else if (huntFlags & huntGoto)
|
|
removeGotoTask();
|
|
|
|
return atTargetEvaluate();
|
|
} else
|
|
// If we haven't reached the target, we know we're not done
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntTask::update(void) {
|
|
Actor *a = stack->getActor();
|
|
|
|
if (a->moveTask && a->moveTask->isPrivledged()) return taskNotDone;
|
|
|
|
// 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 & huntWander)
|
|
removeWanderTask();
|
|
else if (huntFlags & huntGoto)
|
|
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 & huntGoto)
|
|
&& targetHasChanged((GotoTask *)subTask))
|
|
removeGotoTask();
|
|
|
|
// Determine if there is a goto subtask
|
|
if (!(huntFlags & huntGoto)) {
|
|
GotoTask *gotoResult;
|
|
|
|
// Try to set up a goto subtask
|
|
if ((gotoResult = setupGoto()) != NULL) {
|
|
if (huntFlags & huntWander) removeWanderTask();
|
|
|
|
subTask = gotoResult;
|
|
huntFlags |= huntGoto;
|
|
} else {
|
|
// If we couldn't setup a goto task, setup a wander task
|
|
if (!(huntFlags & huntWander)) {
|
|
if ((subTask = new WanderTask(stack)) != NULL)
|
|
huntFlags |= huntWander;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is a subtask, update it
|
|
if (huntFlags & (huntGoto | huntWander)) subTask->update();
|
|
|
|
// If we're not at the target, we know the hunt task is not
|
|
// done
|
|
return taskNotDone;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntTask::removeWanderTask(void) {
|
|
subTask->abortTask();
|
|
delete subTask;
|
|
huntFlags &= ~huntWander;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntTask::removeGotoTask(void) {
|
|
subTask->abortTask();
|
|
delete subTask;
|
|
huntFlags &= ~huntGoto;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntLocationTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntLocationTask::HuntLocationTask(TaskStack *ts, const Target &t) :
|
|
HuntTask(ts),
|
|
currentTarget(Nowhere) {
|
|
assert(t.size() <= sizeof(targetMem));
|
|
// Copy the target to the target buffer
|
|
t.clone(targetMem);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntLocationTask::HuntLocationTask(void **buf) : HuntTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the currentTarget location
|
|
currentTarget = *((TilePoint *)bufferPtr);
|
|
bufferPtr = (TilePoint *)bufferPtr + 1;
|
|
|
|
// Restore the target
|
|
*buf = constructTarget(targetMem, bufferPtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntLocationTask::archiveSize(void) const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(currentTarget)
|
|
+ targetArchiveSize(getTarget());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntLocationTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntTask::archive(buf);
|
|
|
|
// Store the current target location
|
|
*((TilePoint *)buf) = currentTarget;
|
|
buf = (TilePoint *)buf + 1;
|
|
|
|
// Store the target
|
|
return archiveTarget(getTarget(), buf);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
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(void) {
|
|
// If there is somewhere to go, setup a goto task, else return NULL
|
|
return currentTarget != Nowhere
|
|
? new GotoLocationTask(stack, currentTarget)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint HuntLocationTask::currentTargetLoc(void) {
|
|
return currentTarget;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToBeNearLocationTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntToBeNearLocationTask::HuntToBeNearLocationTask(void **buf) :
|
|
HuntLocationTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the range
|
|
range = *((uint16 *)bufferPtr);
|
|
bufferPtr = (uint16 *)bufferPtr + 1;
|
|
|
|
// Restore the evaluation counter
|
|
targetEvaluateCtr = *((uint8 *)bufferPtr);
|
|
|
|
*buf = (uint8 *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToBeNearLocationTask::archiveSize(void) const {
|
|
return HuntLocationTask::archiveSize()
|
|
+ sizeof(range)
|
|
+ sizeof(targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntToBeNearLocationTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntLocationTask::archive(buf);
|
|
|
|
// Store the range
|
|
*((uint16 *)buf) = range;
|
|
buf = (uint16 *)buf + 1;
|
|
|
|
// Store the evaluation counter
|
|
*((uint8 *)buf) = targetEvaluateCtr;
|
|
|
|
return (uint8 *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToBeNearLocationTask::getType(void) const {
|
|
return huntToBeNearLocationTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToBeNearLocationTask::operator == (const Task &t) const {
|
|
if (t.getType() != huntToBeNearLocationTask) return false;
|
|
|
|
HuntToBeNearLocationTask *taskPtr = (HuntToBeNearLocationTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& range == taskPtr->range;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearLocationTask::evaluateTarget(void) {
|
|
// 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 = targetEvaluateRate;
|
|
}
|
|
targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToBeNearLocationTask::atTarget(void) {
|
|
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(void) {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearLocationTask::atTargetEvaluate(void) {
|
|
// If we're at the target, we're done
|
|
return taskSucceeded;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearLocationTask::atTargetUpdate(void) {
|
|
// If we're at the target, we're done
|
|
return taskSucceeded;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntObjectTask::HuntObjectTask(TaskStack *ts, const ObjectTarget &ot) :
|
|
HuntTask(ts),
|
|
currentTarget(NULL) {
|
|
assert(ot.size() <= sizeof(targetMem));
|
|
// Copy the target to the target buffer
|
|
ot.clone(targetMem);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntObjectTask::HuntObjectTask(void **buf) : HuntTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
ObjectID currentTargetID;
|
|
|
|
// Restore the current target ID
|
|
currentTargetID = *((ObjectID *)bufferPtr);
|
|
bufferPtr = (ObjectID *)bufferPtr + 1;
|
|
|
|
// Convert the ID to a GameObject pointer
|
|
currentTarget = currentTargetID != Nothing
|
|
? GameObject::objectAddress(currentTargetID)
|
|
: NULL;
|
|
|
|
// Reconstruct the object target
|
|
*buf = constructTarget(targetMem, bufferPtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntObjectTask::archiveSize(void) const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(ObjectID)
|
|
+ targetArchiveSize(getTarget());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntObjectTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntTask::archive(buf);
|
|
|
|
ObjectID currentTargetID;
|
|
|
|
// Get the current target object's ID
|
|
currentTargetID = currentTarget != NULL
|
|
? currentTarget->thisID()
|
|
: Nothing;
|
|
|
|
// Store the ID
|
|
*((ObjectID *)buf) = currentTargetID;
|
|
buf = (ObjectID *)buf + 1;
|
|
|
|
// Store the object target
|
|
return archiveTarget(getTarget(), buf);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
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(void) {
|
|
// If there is an object to goto, setup a GotoObjectTask, else
|
|
// return NULL
|
|
return currentTarget
|
|
? new GotoObjectTask(stack, currentTarget)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint HuntObjectTask::currentTargetLoc(void) {
|
|
// If there is a current target object, return its locatio, else
|
|
// return Nowhere
|
|
return currentTarget ? currentTarget->getLocation() : Nowhere;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToBeNearObjectTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntToBeNearObjectTask::HuntToBeNearObjectTask(void **buf) :
|
|
HuntObjectTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the range
|
|
range = *((uint16 *)bufferPtr);
|
|
bufferPtr = (uint16 *)bufferPtr + 1;
|
|
|
|
// Restore the evaluation counter
|
|
targetEvaluateCtr = *((uint8 *)bufferPtr);
|
|
|
|
*buf = (uint8 *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToBeNearObjectTask::archiveSize(void) const {
|
|
return HuntObjectTask::archiveSize()
|
|
+ sizeof(range)
|
|
+ sizeof(targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntToBeNearObjectTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntObjectTask::archive(buf);
|
|
|
|
// Store the range
|
|
*((uint16 *)buf) = range;
|
|
buf = (uint16 *)buf + 1;
|
|
|
|
// Store the evaluation counter
|
|
*((uint8 *)buf) = targetEvaluateCtr;
|
|
|
|
return (uint8 *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToBeNearObjectTask::getType(void) const {
|
|
return huntToBeNearObjectTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToBeNearObjectTask::operator == (const Task &t) const {
|
|
if (t.getType() != huntToBeNearObjectTask) return false;
|
|
|
|
HuntToBeNearObjectTask *taskPtr = (HuntToBeNearObjectTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& range == taskPtr->range;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearObjectTask::evaluateTarget(void) {
|
|
// Determine if it is time to reevaluate the target object
|
|
if (targetEvaluateCtr == 0) {
|
|
Actor *a = stack->getActor();
|
|
int16 i;
|
|
GameObject *objArray[16];
|
|
int16 distArray[elementsof(objArray)];
|
|
TargetObjectArray toa(
|
|
elementsof(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,
|
|
maxSenseRange,
|
|
objID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
objID)) {
|
|
currentTarget = objArray[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
targetEvaluateCtr = targetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluate counter
|
|
targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToBeNearObjectTask::atTarget(void) {
|
|
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(void) {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearObjectTask::atTargetEvaluate(void) {
|
|
// If we're at the target, we're done
|
|
return taskSucceeded;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearObjectTask::atTargetUpdate(void) {
|
|
// If we're at the target, we're done
|
|
return taskSucceeded;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToPossessTask member functions
|
|
* ===================================================================== */
|
|
|
|
// Hunt to possess in not fully implemented yet
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntToPossessTask::HuntToPossessTask(void **buf) : HuntObjectTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore evaluation counter
|
|
targetEvaluateCtr = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr;
|
|
|
|
// Restore grab flag
|
|
grabFlag = *((bool *)bufferPtr);
|
|
|
|
*buf = (bool *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToPossessTask::archiveSize(void) const {
|
|
return HuntObjectTask::archiveSize()
|
|
+ sizeof(targetEvaluateCtr)
|
|
+ sizeof(grabFlag);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntToPossessTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntObjectTask::archive(buf);
|
|
|
|
// Store the evaluation counter
|
|
*((uint8 *)buf) = targetEvaluateCtr;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
// Store the grab flag
|
|
*((bool *)buf) = grabFlag;
|
|
|
|
return (bool *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToPossessTask::getType(void) const {
|
|
return huntToPossessTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToPossessTask::operator == (const Task &t) const {
|
|
if (t.getType() != huntToPossessTask) return false;
|
|
|
|
HuntToPossessTask *taskPtr = (HuntToPossessTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToPossessTask::evaluateTarget(void) {
|
|
// Determine if it is time to reevaluate the target object
|
|
if (targetEvaluateCtr == 0) {
|
|
Actor *a = stack->getActor();
|
|
int16 i;
|
|
GameObject *objArray[16];
|
|
int16 distArray[elementsof(objArray)];
|
|
TargetObjectArray toa(
|
|
elementsof(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,
|
|
maxSenseRange,
|
|
objID)
|
|
|| a->canSenseSpecificObjectIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
objID)) {
|
|
currentTarget = objArray[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
targetEvaluateCtr = targetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluate counter
|
|
targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToPossessTask::atTarget(void) {
|
|
Actor *a = stack->getActor();
|
|
|
|
return currentTarget
|
|
&& (a->inReach(currentTarget->getLocation())
|
|
|| (grabFlag
|
|
&& a->isContaining(currentTarget)));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToPossessTask::atTargetabortTask(void) {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToPossessTask::atTargetEvaluate(void) {
|
|
if (currentTarget && stack->getActor()->isContaining(currentTarget))
|
|
return taskSucceeded;
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToPossessTask::atTargetUpdate(void) {
|
|
// Hunt to possess in not implemented yet
|
|
return taskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntActorTask::HuntActorTask(
|
|
TaskStack *ts,
|
|
const ActorTarget &at,
|
|
bool trackFlag) :
|
|
HuntTask(ts),
|
|
flags(trackFlag ? track : 0),
|
|
currentTarget(NULL) {
|
|
assert(at.size() <= sizeof(targetMem));
|
|
// Copy the target to the target buffer
|
|
at.clone(targetMem);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntActorTask::HuntActorTask(void **buf) : HuntTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
ObjectID currentTargetID;
|
|
|
|
// Restore the flags
|
|
flags = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
// Restore the current target ID
|
|
currentTargetID = *((ObjectID *)bufferPtr);
|
|
bufferPtr = (ObjectID *)bufferPtr + 1;
|
|
|
|
// Convert the ID to a GameObject pointer
|
|
currentTarget = currentTargetID != Nothing
|
|
? (Actor *)GameObject::objectAddress(currentTargetID)
|
|
: NULL;
|
|
|
|
// Reconstruct the object target
|
|
*buf = constructTarget(targetMem, bufferPtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntActorTask::archiveSize(void) const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(flags)
|
|
+ sizeof(ObjectID)
|
|
+ targetArchiveSize(getTarget());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntActorTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntTask::archive(buf);
|
|
|
|
ObjectID currentTargetID;
|
|
|
|
// Store the flags
|
|
*((uint8 *)buf) = flags;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
// Get the current target actor's ID
|
|
currentTargetID = currentTarget != NULL
|
|
? currentTarget->thisID()
|
|
: Nothing;
|
|
|
|
// Store the ID
|
|
*((ObjectID *)buf) = currentTargetID;
|
|
buf = (ObjectID *)buf + 1;
|
|
|
|
// Store the object target
|
|
return archiveTarget(getTarget(), buf);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
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(void) {
|
|
// If there is an actor to goto, setup a GotoActorTask, else
|
|
// return NULL
|
|
/* return currentTarget
|
|
? new GotoActorTask( stack, currentTarget, flags & track )
|
|
: NULL;
|
|
*/
|
|
if (currentTarget != NULL) {
|
|
return new GotoActorTask(
|
|
stack,
|
|
currentTarget,
|
|
flags & track);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint HuntActorTask::currentTargetLoc(void) {
|
|
// If there is a current target actor, return its location, else
|
|
// return Nowhere
|
|
return currentTarget ? currentTarget->getLocation() : Nowhere;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToBeNearActorTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntToBeNearActorTask::HuntToBeNearActorTask(void **buf) :
|
|
HuntActorTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Get the goAway task ID
|
|
*((TaskID *)&goAway) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
// Restore the range
|
|
range = *((uint16 *)bufferPtr);
|
|
bufferPtr = (uint16 *)bufferPtr + 1;
|
|
|
|
// Restore the evaluation counter
|
|
targetEvaluateCtr = *((uint8 *)bufferPtr);
|
|
|
|
*buf = (uint8 *)bufferPtr + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void HuntToBeNearActorTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
HuntActorTask::fixup();
|
|
|
|
TaskID goAwayID = *((TaskID *)&goAway);
|
|
|
|
// Convert the task ID to a task pointer
|
|
goAway = goAwayID != NoTask
|
|
? (GoAwayFromObjectTask *)getTaskAddress(goAwayID)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToBeNearActorTask::archiveSize(void) const {
|
|
return HuntActorTask::archiveSize()
|
|
+ sizeof(TaskID) // goAway ID
|
|
+ sizeof(range)
|
|
+ sizeof(targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntToBeNearActorTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntActorTask::archive(buf);
|
|
|
|
TaskID goAwayID;
|
|
|
|
// Convert the task pointer to a task ID
|
|
goAwayID = goAway != NULL ? getTaskID(goAway) : NoTask;
|
|
|
|
// Store the task ID
|
|
*((TaskID *)buf) = goAwayID;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
// Store the range
|
|
*((uint16 *)buf) = range;
|
|
buf = (uint16 *)buf + 1;
|
|
|
|
// Store the evaluation counter
|
|
*((uint8 *)buf) = targetEvaluateCtr;
|
|
|
|
return (uint8 *)buf + 1;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void HuntToBeNearActorTask::mark(void) {
|
|
HuntActorTask::mark();
|
|
if (goAway != NULL)
|
|
goAway->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToBeNearActorTask::getType(void) const {
|
|
return huntToBeNearActorTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToBeNearActorTask::operator == (const Task &t) const {
|
|
if (t.getType() != huntToBeNearActorTask) return false;
|
|
|
|
HuntToBeNearActorTask *taskPtr = (HuntToBeNearActorTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& tracking() ? taskPtr->tracking() : !taskPtr->tracking()
|
|
&& range == taskPtr->range;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearActorTask::evaluateTarget(void) {
|
|
// Determine if its time to reevaluate the current target actor
|
|
if (targetEvaluateCtr == 0) {
|
|
Actor *a = stack->getActor();
|
|
int16 i;
|
|
Actor *actorArray[16];
|
|
int16 distArray[elementsof(actorArray)];
|
|
TargetActorArray taa(
|
|
elementsof(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,
|
|
maxSenseRange,
|
|
actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])) {
|
|
if (currentTarget != actorArray[i]) {
|
|
if (atTarget()) atTargetabortTask();
|
|
currentTarget = actorArray[i];
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
targetEvaluateCtr = targetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluation counter.
|
|
targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToBeNearActorTask::atTarget(void) {
|
|
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 != NULL) {
|
|
goAway->abortTask();
|
|
delete goAway;
|
|
goAway = NULL;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToBeNearActorTask::atTargetabortTask(void) {
|
|
if (goAway != NULL) {
|
|
goAway->abortTask();
|
|
delete goAway;
|
|
goAway = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearActorTask::atTargetEvaluate(void) {
|
|
TilePoint targetLoc = currentTargetLoc();
|
|
|
|
// If we're not TOO close, we're done
|
|
if (stack->getActor()->inRange(targetLoc, tooClose))
|
|
return taskNotDone;
|
|
|
|
if (goAway != NULL) {
|
|
goAway->abortTask();
|
|
delete goAway;
|
|
goAway = NULL;
|
|
}
|
|
|
|
return taskSucceeded;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToBeNearActorTask::atTargetUpdate(void) {
|
|
Actor *a = stack->getActor();
|
|
TilePoint targetLoc = currentTargetLoc();
|
|
|
|
// Determine if we're TOO close
|
|
if (a->inRange(targetLoc, tooClose)) {
|
|
// Setup a go away task if necessary and update it
|
|
if (goAway == NULL) {
|
|
goAway = new GoAwayFromObjectTask(stack, currentTarget);
|
|
if (goAway != NULL) goAway->update();
|
|
} else
|
|
goAway->update();
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
// Delete the go away task if it exists
|
|
if (goAway != NULL) {
|
|
goAway->abortTask();
|
|
delete goAway;
|
|
goAway = NULL;
|
|
}
|
|
|
|
return taskSucceeded;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToKillTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- initial construction
|
|
|
|
HuntToKillTask::HuntToKillTask(
|
|
TaskStack *ts,
|
|
const ActorTarget &at,
|
|
bool trackFlag) :
|
|
HuntActorTask(ts, at, trackFlag),
|
|
targetEvaluateCtr(0),
|
|
specialAttackCtr(10),
|
|
flags(evalWeapon) {
|
|
Actor *a = stack->getActor();
|
|
|
|
if (isActor(a->currentTarget))
|
|
currentTarget = (Actor *)a->currentTarget;
|
|
|
|
a->setFightStance(true);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntToKillTask::HuntToKillTask(void **buf) : HuntActorTask(buf) {
|
|
uint8 *bufferPtr = (uint8 *)*buf;
|
|
|
|
// Restore the evaluation counter
|
|
targetEvaluateCtr = *bufferPtr++;
|
|
specialAttackCtr = *bufferPtr++;
|
|
flags = *bufferPtr++;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToKillTask::archiveSize(void) const {
|
|
return HuntActorTask::archiveSize()
|
|
+ sizeof(targetEvaluateCtr)
|
|
+ sizeof(specialAttackCtr)
|
|
+ sizeof(flags);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntToKillTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntActorTask::archive(buf);
|
|
|
|
// Store the evaluation counter
|
|
*((uint8 *)buf) = targetEvaluateCtr;
|
|
*((uint8 *)buf + 1) = specialAttackCtr;
|
|
*((uint8 *)buf + 2) = flags;
|
|
buf = (uint8 *)buf + 3;
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToKillTask::getType(void) const {
|
|
return huntToKillTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToKillTask::operator == (const Task &t) const {
|
|
if (t.getType() != huntToKillTask) return false;
|
|
|
|
HuntToKillTask *taskPtr = (HuntToKillTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& tracking() ? taskPtr->tracking() : !taskPtr->tracking();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::abortTask(void) {
|
|
HuntActorTask::abortTask();
|
|
|
|
Actor *a = stack->getActor();
|
|
|
|
a->flags &= ~Actor::specialAttack;
|
|
|
|
a->setFightStance(false);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToKillTask::update(void) {
|
|
if (specialAttackCtr == 0) {
|
|
stack->getActor()->flags |= Actor::specialAttack;
|
|
// 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(void) {
|
|
Actor *a = stack->getActor();
|
|
|
|
if (flags & evalWeapon
|
|
&& a->isInterruptable()) {
|
|
evaluateWeapon();
|
|
flags &= ~evalWeapon;
|
|
}
|
|
|
|
|
|
// Determine if its time to reevaluate the current target actor
|
|
if (targetEvaluateCtr == 0
|
|
|| (currentTarget != NULL
|
|
&& currentTarget->isDead())) {
|
|
Actor *bestTarget = NULL;
|
|
ActorProto *proto = (ActorProto *)a->proto();
|
|
int16 i;
|
|
Actor *actorArray[16];
|
|
int16 distArray[elementsof(actorArray)];
|
|
TargetActorArray taa(
|
|
elementsof(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 behaviorHungry:
|
|
// 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,
|
|
maxSenseRange,
|
|
actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])) {
|
|
bestTarget = actorArray[i];
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case behaviorCowardly: {
|
|
int16 bestScore = 0;
|
|
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])) {
|
|
int16 score;
|
|
|
|
score = closenessScore(distArray[i]) * 16
|
|
/ actorArray[i]->defenseScore();
|
|
|
|
if (score > bestScore || bestTarget == NULL) {
|
|
bestScore = score;
|
|
bestTarget = actorArray[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case behaviorBerserk: {
|
|
int16 bestScore = 0;
|
|
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])) {
|
|
int16 score;
|
|
|
|
score = closenessScore(distArray[i])
|
|
* actorArray[i]->offenseScore();
|
|
|
|
if (score > bestScore || bestTarget == NULL) {
|
|
bestScore = score;
|
|
bestTarget = actorArray[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case behaviorSmart: {
|
|
int16 bestScore = 0;
|
|
|
|
for (i = 0; i < taa.actors; i++) {
|
|
if (actorArray[i]->isDead()) continue;
|
|
|
|
if (tracking()
|
|
|| a->canSenseSpecificActor(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])
|
|
|| a->canSenseSpecificActorIndirectly(
|
|
info,
|
|
maxSenseRange,
|
|
actorArray[i])) {
|
|
int16 score;
|
|
|
|
score = closenessScore(distArray[i])
|
|
* actorArray[i]->offenseScore()
|
|
/ actorArray[i]->defenseScore();
|
|
|
|
if (score > bestScore || bestTarget == NULL) {
|
|
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 |= evalWeapon;
|
|
|
|
targetEvaluateCtr = targetEvaluateRate;
|
|
}
|
|
|
|
// Decrement the target reevaluation counter
|
|
targetEvaluateCtr--;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToKillTask::atTarget(void) {
|
|
// Determine if we're in attack range of the current target
|
|
return currentTarget != NULL
|
|
&& stack->getActor()->inAttackRange(
|
|
currentTarget->getLocation());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::atTargetabortTask(void) {
|
|
// If the task is aborted while at the target actor, abort any
|
|
// attack currently taking place
|
|
stack->getActor()->stopAttack(currentTarget);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToKillTask::atTargetEvaluate(void) {
|
|
// This task is never done and must be aborted manually
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToKillTask::atTargetUpdate(void) {
|
|
assert(isActor(currentTarget));
|
|
|
|
Actor *a = stack->getActor();
|
|
|
|
// If we're ready to attack, attack
|
|
if (a->isInterruptable() && g_vm->_rnd->getRandomNumber(6) == 0) {
|
|
a->attack(currentTarget);
|
|
flags |= evalWeapon;
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToKillTask::evaluateWeapon(void) {
|
|
Actor *a = stack->getActor();
|
|
ObjectID actorID = a->thisID();
|
|
GameObject *obj,
|
|
*bestWeapon,
|
|
*currentWeapon;
|
|
int bestWeaponRating;
|
|
ContainerIterator iter(a);
|
|
|
|
bestWeapon = NULL;
|
|
bestWeaponRating = 0;
|
|
currentWeapon = a->offensiveObject();
|
|
// If the current offensive object is the actor himself then there
|
|
// is no current weapon.
|
|
if (currentWeapon == a) currentWeapon = NULL;
|
|
|
|
if (!isAutoWeaponSet() && isPlayerActor(a)) {
|
|
WeaponProto *weaponProto = currentWeapon != NULL
|
|
? (WeaponProto *)currentWeapon->proto()
|
|
: NULL;
|
|
|
|
if (currentWeapon == NULL
|
|
|| weaponProto->weaponRating(
|
|
obj->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::isArmor)) {
|
|
if (proto->useSlotAvailable(obj, a))
|
|
obj->use(actorID);
|
|
continue;
|
|
}
|
|
|
|
if (cSet & ProtoObj::isWeapon) {
|
|
WeaponProto *weaponProto = (WeaponProto *)proto;
|
|
int weaponRating;
|
|
|
|
weaponRating = weaponProto->weaponRating(
|
|
obj->thisID(),
|
|
actorID,
|
|
currentTarget->thisID());
|
|
|
|
// a rating of zero means this weapon is useless
|
|
if (weaponRating == 0) continue;
|
|
|
|
if (obj == currentWeapon)
|
|
weaponRating += currentWeaponBonus;
|
|
|
|
if (weaponRating > bestWeaponRating) {
|
|
bestWeaponRating = weaponRating;
|
|
bestWeapon = obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestWeapon != NULL) {
|
|
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 != NULL)
|
|
currentWeapon->use(actorID);
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
HuntToGiveTask member functions
|
|
* ===================================================================== */
|
|
|
|
// Hunt to give is not implemented yet
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
HuntToGiveTask::HuntToGiveTask(void **buf) : HuntActorTask(buf) {
|
|
ObjectID *bufferPtr = (ObjectID *)*buf;
|
|
ObjectID objToGiveID;
|
|
|
|
// Get the object ID
|
|
objToGiveID = *bufferPtr++;
|
|
|
|
// Convert the object ID to a pointer
|
|
objToGive = objToGiveID != Nothing
|
|
? GameObject::objectAddress(objToGiveID)
|
|
: NULL;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 HuntToGiveTask::archiveSize(void) const {
|
|
return HuntActorTask::archiveSize()
|
|
+ sizeof(ObjectID); // objToGive ID
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *HuntToGiveTask::archive(void *buf) const {
|
|
// Let base class archive its data
|
|
buf = HuntActorTask::archive(buf);
|
|
|
|
ObjectID objToGiveID;
|
|
|
|
// Convert the object pointer to an ID
|
|
objToGiveID = objToGive != NULL ? objToGive->thisID() : Nothing;
|
|
|
|
// Store the ID
|
|
*((ObjectID *)buf) = objToGiveID;
|
|
|
|
return (ObjectID *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 HuntToGiveTask::getType(void) const {
|
|
return huntToGiveTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool HuntToGiveTask::operator == (const Task &t) const {
|
|
if (t.getType() != huntToGiveTask) return false;
|
|
|
|
HuntToGiveTask *taskPtr = (HuntToGiveTask *)&t;
|
|
|
|
return *getTarget() == *taskPtr->getTarget()
|
|
&& tracking() ? taskPtr->tracking() : !taskPtr->tracking()
|
|
&& objToGive == taskPtr->objToGive;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToGiveTask::evaluateTarget(void) {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool HuntToGiveTask::atTarget(void) {
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void HuntToGiveTask::atTargetabortTask(void) {}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToGiveTask::atTargetEvaluate(void) {
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult HuntToGiveTask::atTargetUpdate(void) {
|
|
return taskNotDone;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
BandTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool BandTask::BandingRepulsorIterator::first(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
assert(a->leader != NULL && a->leader->followers != NULL);
|
|
|
|
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 != NULL && a->leader->followers != NULL);
|
|
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;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
BandTask::BandTask(void **buf) : HuntTask(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
*((TaskID *)&attend) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
// Restore the current target location
|
|
currentTarget = *((TilePoint *)bufferPtr);
|
|
bufferPtr = (TilePoint *)bufferPtr + 1;
|
|
|
|
// Restore the target evaluation counter
|
|
targetEvaluateCtr = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void BandTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
HuntTask::fixup();
|
|
|
|
TaskID attendID = *((TaskID *)&attend);
|
|
|
|
// Convert the TaskID to a Task pointer
|
|
attend = attendID != NoTask
|
|
? (AttendTask *)getTaskAddress(attendID)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 BandTask::archiveSize(void) const {
|
|
return HuntTask::archiveSize()
|
|
+ sizeof(TaskID) // attend ID
|
|
+ sizeof(currentTarget)
|
|
+ sizeof(targetEvaluateCtr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *BandTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = HuntTask::archive(buf);
|
|
|
|
// Store the attend task ID
|
|
*((TaskID *)buf) = attend != NULL ? getTaskID(attend) : NoTask;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
// Store the current target location
|
|
*((TilePoint *)buf) = currentTarget;
|
|
buf = (TilePoint *)buf + 1;
|
|
|
|
// Store the target evaluation counter
|
|
*((uint8 *)buf) = targetEvaluateCtr;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void BandTask::mark(void) {
|
|
HuntTask::mark();
|
|
if (attend != NULL)
|
|
attend->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 BandTask::getType(void) const {
|
|
return bandTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool BandTask::operator == (const Task &t) const {
|
|
return t.getType() == bandTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void BandTask::evaluateTarget(void) {
|
|
if (targetEvaluateCtr == 0) {
|
|
Actor *leader = stack->getActor()->leader;
|
|
TilePoint actorLoc = stack->getActor()->getLocation(),
|
|
movementVector;
|
|
TilePoint repulsorVector;
|
|
int16 repulsorStrength;
|
|
TilePoint repulsorVectorArray[6];
|
|
int16 repulsorStrengthArray[elementsof(repulsorVectorArray)];
|
|
int16 repulsorDistArray[elementsof(repulsorVectorArray)];
|
|
int16 repulsorCount;
|
|
bool repulsorFlag;
|
|
|
|
RepulsorIterator *repulsorIter = getNewRepulsorIterator();
|
|
|
|
if (repulsorIter == NULL) 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 < elementsof(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 < elementsof(repulsorVectorArray)) {
|
|
if (repulsorCount < elementsof(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 = targetEvaluateRate;
|
|
}
|
|
|
|
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(void) {
|
|
return new GotoLocationTask(stack, currentTarget, getRunThreshold());
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TilePoint BandTask::currentTargetLoc(void) {
|
|
return currentTarget;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
bool BandTask::atTarget(void) {
|
|
TilePoint actorLoc = stack->getActor()->getLocation();
|
|
|
|
if ((actorLoc - currentTarget).quickHDistance() > 6
|
|
|| abs(actorLoc.z - currentTarget.z) > kMaxStepHeight) {
|
|
if (attend != NULL) {
|
|
attend->abortTask();
|
|
delete attend;
|
|
attend = NULL;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void BandTask::atTargetabortTask(void) {
|
|
if (attend != NULL) {
|
|
attend->abortTask();
|
|
delete attend;
|
|
attend = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult BandTask::atTargetEvaluate(void) {
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult BandTask::atTargetUpdate(void) {
|
|
Actor *a = stack->getActor();
|
|
|
|
if (attend != NULL)
|
|
attend->update();
|
|
else {
|
|
attend = new AttendTask(stack, a->leader);
|
|
if (attend != NULL)
|
|
attend->update();
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
int16 BandTask::getRunThreshold(void) {
|
|
return kTileUVSize * 3;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
BandTask::RepulsorIterator *BandTask::getNewRepulsorIterator(void) {
|
|
return new BandingRepulsorIterator(stack->getActor());
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
BandAndAvoidEnemiesTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
//bool BandAndAvoidEnemiesTask::BandAndAvoidEnemiesRepulsorIterator::firstEnemyRepulsor(
|
|
bool BandTask::BandAndAvoidEnemiesRepulsorIterator::firstEnemyRepulsor(
|
|
TilePoint &repulsorVector,
|
|
int16 &repulsorStrength) {
|
|
assert(iteratingThruEnemies);
|
|
|
|
int16 actorDistArray[elementsof(actorArray)];
|
|
TargetActorArray taa(elementsof(actorArray), actorArray, actorDistArray);
|
|
ActorPropertyTarget target(actorPropIDEnemy);
|
|
|
|
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(void) const {
|
|
return bandAndAvoidEnemiesTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool BandAndAvoidEnemiesTask::operator == (const Task &t) const {
|
|
return t.getType() == bandAndAvoidEnemiesTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
int16 BandAndAvoidEnemiesTask::getRunThreshold(void) {
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
BandTask::RepulsorIterator *BandAndAvoidEnemiesTask::getNewRepulsorIterator(void) {
|
|
return new BandAndAvoidEnemiesRepulsorIterator(stack->getActor());
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
FollowPatrolRouteTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
FollowPatrolRouteTask::FollowPatrolRouteTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Get the gotoWayPoint TaskID
|
|
*((TaskID *)&gotoWayPoint) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
// Restore the patrol route iterator
|
|
patrolIter = *((PatrolRouteIterator *)bufferPtr);
|
|
bufferPtr = (PatrolRouteIterator *)bufferPtr + 1;
|
|
|
|
// Restore the last waypoint number
|
|
lastWayPointNum = *((int16 *)bufferPtr);
|
|
bufferPtr = (int16 *)bufferPtr + 1;
|
|
|
|
// Restore the paused flag
|
|
paused = *((bool *)bufferPtr);
|
|
bufferPtr = (bool *)bufferPtr + 1;
|
|
|
|
// Restore the paused counter
|
|
counter = *((int16 *)bufferPtr);
|
|
bufferPtr = (int16 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointers
|
|
|
|
void FollowPatrolRouteTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
TaskID gotoWayPointID = *((TaskID *)&gotoWayPoint);
|
|
|
|
// Convert the TaskID to a Task pointer
|
|
gotoWayPoint = gotoWayPointID != NoTask
|
|
? (GotoLocationTask *)getTaskAddress(gotoWayPointID)
|
|
: NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 FollowPatrolRouteTask::archiveSize(void) const {
|
|
return Task::archiveSize()
|
|
+ sizeof(TaskID) // gotoWayPoint ID
|
|
+ sizeof(patrolIter)
|
|
+ sizeof(lastWayPointNum)
|
|
+ sizeof(paused)
|
|
+ sizeof(counter);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *FollowPatrolRouteTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
TaskID gotoWayPointID;
|
|
|
|
// Convert the gotoWayPoint pointer to a TaskID
|
|
gotoWayPointID = gotoWayPoint != NULL
|
|
? getTaskID(gotoWayPoint)
|
|
: NoTask;
|
|
|
|
// Store the gotoWayPoint ID
|
|
*((TaskID *)buf) = gotoWayPointID;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
// Store the PatrolRouteIterator
|
|
*((PatrolRouteIterator *)buf) = patrolIter;
|
|
buf = (PatrolRouteIterator *)buf + 1;
|
|
|
|
// Store the last waypoint number
|
|
*((int16 *)buf) = lastWayPointNum;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
// Store the paused flag
|
|
*((bool *)buf) = paused;
|
|
buf = (bool *)buf + 1;
|
|
|
|
// Store the paused counter
|
|
*((int16 *)buf) = counter;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void FollowPatrolRouteTask::mark(void) {
|
|
Task::mark();
|
|
if (gotoWayPoint != NULL)
|
|
gotoWayPoint->mark();
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 FollowPatrolRouteTask::getType(void) const {
|
|
return followPatrolRouteTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void FollowPatrolRouteTask::abortTask(void) {
|
|
// If there is a subtask, get rid of it
|
|
if (gotoWayPoint) {
|
|
gotoWayPoint->abortTask();
|
|
delete gotoWayPoint;
|
|
gotoWayPoint = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult FollowPatrolRouteTask::evaluate(void) {
|
|
// Simply check the patrol iterator to determine if there are
|
|
// any more waypoints
|
|
return *patrolIter == Nowhere ? taskSucceeded : taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult FollowPatrolRouteTask::update(void) {
|
|
return !paused ? handleFollowPatrolRoute() : handlePaused();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool FollowPatrolRouteTask::operator == (const Task &t) const {
|
|
if (t.getType() != followPatrolRouteTask) return false;
|
|
|
|
FollowPatrolRouteTask *taskPtr = (FollowPatrolRouteTask *)&t;
|
|
|
|
return patrolIter == taskPtr->patrolIter
|
|
&& lastWayPointNum == taskPtr->lastWayPointNum;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used if this task is not paused
|
|
|
|
TaskResult FollowPatrolRouteTask::handleFollowPatrolRoute(void) {
|
|
TilePoint currentWayPoint = *patrolIter,
|
|
actorLoc = stack->getActor()->getLocation();
|
|
|
|
if (currentWayPoint == Nowhere) return taskSucceeded;
|
|
|
|
// 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 != NULL) {
|
|
gotoWayPoint->abortTask();
|
|
delete gotoWayPoint;
|
|
gotoWayPoint = NULL;
|
|
}
|
|
|
|
// If this way point is the specified last way point,
|
|
// return success
|
|
if (lastWayPointNum != -1
|
|
&& patrolIter.wayPointNum() == lastWayPointNum)
|
|
return taskSucceeded;
|
|
|
|
// If there are no more way points in the patrol route, return
|
|
// success
|
|
if ((currentWayPoint = *++patrolIter) == Nowhere)
|
|
return taskSucceeded;
|
|
|
|
// 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 taskNotDone;
|
|
}
|
|
}
|
|
|
|
// Setup a gotoWayPoint task if one doesn't already exist and
|
|
// update it
|
|
if (gotoWayPoint != NULL)
|
|
gotoWayPoint->update();
|
|
else {
|
|
gotoWayPoint = new GotoLocationTask(stack, currentWayPoint);
|
|
if (gotoWayPoint != NULL) gotoWayPoint->update();
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update function used if this task is paused
|
|
|
|
TaskResult FollowPatrolRouteTask::handlePaused(void) {
|
|
TaskResult result;
|
|
|
|
if ((result = evaluate()) == taskNotDone) {
|
|
if (counter == 0)
|
|
followPatrolRoute();
|
|
else
|
|
counter--;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Set this task into the paused state
|
|
|
|
void FollowPatrolRouteTask::pause(void) {
|
|
paused = true;
|
|
counter = (g_vm->_rnd->getRandomNumber(63) + g_vm->_rnd->getRandomNumber(63)) / 2;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
AttendTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
AttendTask::AttendTask(void **buf) : Task(buf) {
|
|
ObjectID *bufferPtr = (ObjectID *)*buf;
|
|
ObjectID objID;
|
|
|
|
// Get the object ID
|
|
objID = *bufferPtr++;
|
|
|
|
// Convert the object ID to a pointer
|
|
obj = objID != Nothing ? GameObject::objectAddress(objID) : NULL;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 AttendTask::archiveSize(void) const {
|
|
return Task::archiveSize() + sizeof(ObjectID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *AttendTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
ObjectID objID;
|
|
|
|
// Convert the object pointer to an object ID
|
|
objID = obj != NULL ? obj->thisID() : Nothing;
|
|
|
|
// Store the object ID
|
|
*((ObjectID *)buf) = objID;
|
|
|
|
return (ObjectID *)buf + 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 AttendTask::getType(void) const {
|
|
return attendTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void AttendTask::abortTask(void) {
|
|
MotionTask *actorMotion = stack->getActor()->moveTask;
|
|
|
|
// Determine if we need to abort the actor motion
|
|
if (actorMotion != NULL && actorMotion->isTurn())
|
|
actorMotion->finishTurn();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult AttendTask::evaluate(void) {
|
|
// Attending must be stopped manually
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult AttendTask::update(void) {
|
|
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 taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool AttendTask::operator == (const Task &t) const {
|
|
if (t.getType() != attendTask) return false;
|
|
|
|
AttendTask *taskPtr = (AttendTask *)&t;
|
|
|
|
return obj == taskPtr->obj;
|
|
}
|
|
|
|
#if 0
|
|
|
|
// This code should no longer be necessary
|
|
|
|
/* ===================================================================== *
|
|
DefendTask member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
DefendTask::DefendTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
ObjectID attackerID;
|
|
|
|
// Get the attacker's ID
|
|
attackerID = *((ObjectID *)bufferPtr);
|
|
bufferPtr = (ObjectID *)bufferPtr + 1;
|
|
|
|
// Convert the ID to a pointer
|
|
attacker = attackerID != Nothing
|
|
? (Actor *)GameObject::objectAddress(attackerID)
|
|
: NULL;
|
|
|
|
// Get the subTask id
|
|
*((TaskID *)&subTask) = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fixup the subtask pointer
|
|
|
|
void DefendTask::fixup(void) {
|
|
// Let the base class fixup its pointers
|
|
Task::fixup();
|
|
|
|
TaskID subTaskID = *((TaskID *)&subTask);
|
|
|
|
// Convert the subTaskID to a pointer
|
|
subTask = subTaskID != NoTask ? getTaskAddress(subTaskID) : NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
inline int32 DefendTask::archiveSize(void) const {
|
|
return Task::archiveSize() + sizeof(ObjectID) + sizeof(TaskID);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *DefendTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
ObjectID attackerID;
|
|
TaskID subTaskID;
|
|
|
|
// Convert the attacker pointer to an ID
|
|
attackerID = attacker != NULL ? attacker->thisID() : Nothing;
|
|
|
|
// Store the attacker's ID
|
|
*((ObjectID *)buf) = attackerID;
|
|
buf = (ObjectID *)buf + 1;
|
|
|
|
// Convert the subtask pointer to an ID
|
|
subTaskID = subTask != NULL ? getTaskID(subTask) : NoTask;
|
|
|
|
// Store the subtask ID
|
|
*((TaskID *)buf) = subTaskID;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 DefendTask::getType(void) const {
|
|
return defendTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void DefendTask::abortTask(void) {
|
|
// If we have a sub-task, kill it
|
|
if (subTask != NULL) {
|
|
subTask->abortTask();
|
|
delete subTask;
|
|
subTask = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult DefendTask::evaluate(void) {
|
|
// If there currently is no sub-task and the attacker is no longer
|
|
// attacking, we're done
|
|
if (subTask == NULL
|
|
&& (attacker->moveTask == NULL
|
|
|| !attacker->moveTask->isAttack()))
|
|
return taskSucceeded;
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult DefendTask::update(void) {
|
|
Actor *a = stack->getActor();
|
|
|
|
// If there is no sub-task, try to set up a new one
|
|
if (subTask == NULL) {
|
|
GameObject *defensiveObj;
|
|
|
|
a->defensiveObject(&defensiveObj);
|
|
|
|
if (defensiveObj != NULL) {
|
|
Direction relDir;
|
|
|
|
relDir = ((attacker->getLocation() - a->getLocation()).quickDir()
|
|
- a->currentFacing)
|
|
& 0x7;
|
|
|
|
if (relDir == 7 || relDir <= 1)
|
|
subTask = new ParryTask(stack, attacker, defensiveObj);
|
|
else
|
|
return taskFailed;
|
|
}
|
|
}
|
|
|
|
if (subTask != NULL) {
|
|
TaskResult result;
|
|
|
|
// Run the sub-task until its done
|
|
if ((result = subTask->update()) != taskNotDone) {
|
|
delete subTask;
|
|
subTask = NULL;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// If there currently is no sub-task and the attacker is no longer
|
|
// attacking, we're done
|
|
if (subTask == NULL
|
|
&& (attacker->moveTask == NULL
|
|
|| !attacker->moveTask->isMeleeAttack()))
|
|
return taskSucceeded;
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool DefendTask::operator == (const Task &t) const {
|
|
if (t.getType() != defendTask) return false;
|
|
|
|
DefendTask *taskPtr = (DefendTask *)&t;
|
|
|
|
return attacker == taskPtr->attacker;
|
|
}
|
|
|
|
/* ===================================================================== *
|
|
ParryTask Class
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// Constructor -- reconstruct from archive buffer
|
|
|
|
ParryTask::ParryTask(void **buf) : Task(buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
ObjectID attackerID,
|
|
defenseObjID;
|
|
|
|
// Get the attacker ID and the defense object ID
|
|
attackerID = *((ObjectID *)bufferPtr);
|
|
defenseObjID = *((ObjectID *)bufferPtr + 1);
|
|
bufferPtr = (ObjectID *)bufferPtr + 2;
|
|
|
|
// Convert the attacker ID to a pointer
|
|
attacker = attackerID != Nothing
|
|
? (Actor *)GameObject::objectAddress(attackerID)
|
|
: NULL;
|
|
|
|
// Convert the defense object ID to a pointer
|
|
defenseObj = defenseObjID != Nothing
|
|
? GameObject::objectAddress(defenseObjID)
|
|
: NULL;
|
|
|
|
// Restore the flags
|
|
flags = *((uint8 *)bufferPtr);
|
|
bufferPtr = (uint8 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return the number of bytes needed to archive this object in
|
|
// a buffer
|
|
|
|
int32 ParryTask::archiveSize(void) const {
|
|
return Task::archiveSize()
|
|
+ sizeof(ObjectID) // attacker ID
|
|
+ sizeof(ObjectID) // defenseObj ID
|
|
+ sizeof(flags);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this object in a buffer
|
|
|
|
void *ParryTask::archive(void *buf) const {
|
|
// Let the base class archive its data
|
|
buf = Task::archive(buf);
|
|
|
|
ObjectID attackerID,
|
|
defenseObjID;
|
|
|
|
// Convert the attacker pointer to an ID
|
|
attackerID = attacker != NULL ? attacker->thisID() : Nothing;
|
|
|
|
// Convert the defense object pointer to an ID
|
|
defenseObjID = defenseObj != NULL ? defenseObj->thisID() : Nothing;
|
|
|
|
// Store the attacker ID and the defense object ID
|
|
*((ObjectID *)buf) = attackerID;
|
|
*((ObjectID *)buf + 1) = defenseObjID;
|
|
buf = (ObjectID *)buf + 2;
|
|
|
|
// Store the flags
|
|
*((uint8 *)buf) = flags;
|
|
buf = (uint8 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Return an integer representing the type of this task
|
|
|
|
int16 ParryTask::getType(void) const {
|
|
return parryTask;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
void ParryTask::abortTask(void) {
|
|
MotionTask *actorMotion = stack->getActor()->moveTask;
|
|
|
|
// Kill the defense motion, if there is one
|
|
if ((flags & motionStarted)
|
|
&& actorMotion != NULL
|
|
&& actorMotion->isDefense())
|
|
actorMotion->finishDefense();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult ParryTask::evaluate(void) {
|
|
Actor *a = stack->getActor();
|
|
MotionTask *defenderMotion = a->moveTask,
|
|
*attackerMotion = attacker->moveTask;
|
|
|
|
// If the attacker is no longer attacking and we are no longer
|
|
// defending, we're done
|
|
if (attackerMotion == NULL || !attackerMotion->isMeleeAttack()
|
|
&& (!(flags & blockStarted)
|
|
|| defenderMotion == NULL || !defenderMotion->isDefense()))
|
|
return taskSucceeded;
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TaskResult ParryTask::update(void) {
|
|
Actor *a = stack->getActor();
|
|
MotionTask *defenderMotion = a->moveTask,
|
|
*attackerMotion = attacker->moveTask;
|
|
|
|
// If the attacker is no longer attacking and we are no longer
|
|
// defending, we're done
|
|
if (attackerMotion == NULL || !attackerMotion->isMeleeAttack()
|
|
&& (!(flags & blockStarted)
|
|
|| defenderMotion == NULL || !defenderMotion->isDefense()))
|
|
return taskSucceeded;
|
|
|
|
// Try to start a parry motion with the specified defensive object
|
|
if (!(flags & motionStarted)) {
|
|
ProtoObj *defenseObjProto = defenseObj->proto();
|
|
|
|
defenseObjProto->initiateDefense(a->thisID(), attacker->thisID());
|
|
|
|
if ((defenderMotion = a->moveTask) != NULL)
|
|
flags |= motionStarted;
|
|
else
|
|
return taskNotDone;
|
|
}
|
|
|
|
assert(defenderMotion != NULL);
|
|
|
|
// If the blow is about to strike, start the actual block
|
|
if (!(flags & blockStarted)
|
|
&& attackerMotion->framesUntilStrike() < 1) {
|
|
defenderMotion->startBlock();
|
|
flags |= blockStarted;
|
|
}
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Determine if the specified task is equivalent to this task
|
|
|
|
bool ParryTask::operator == (const Task &t) const {
|
|
if (t.getType() != parryTask) return false;
|
|
|
|
ParryTask *taskPtr = (ParryTask *)&t;
|
|
|
|
return attacker == taskPtr->attacker
|
|
&& defenseObj == taskPtr->defenseObj;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ===================================================================== *
|
|
TaskStack member functions
|
|
* ===================================================================== */
|
|
|
|
//----------------------------------------------------------------------
|
|
// TaskStack constructor -- reconstruct from an archive buffer
|
|
|
|
TaskStack::TaskStack(void **buf) {
|
|
void *bufferPtr = *buf;
|
|
|
|
// Restore the stack bottom pointer
|
|
stackBottomID = *((TaskID *)bufferPtr);
|
|
bufferPtr = (TaskID *)bufferPtr + 1;
|
|
|
|
// Restore the actor pointer
|
|
actor = (Actor *)GameObject::objectAddress(*((ObjectID *)bufferPtr));
|
|
bufferPtr = (ObjectID *)bufferPtr + 1;
|
|
|
|
// Restore the evaluation count
|
|
evalCount = *((int16 *)bufferPtr);
|
|
bufferPtr = (int16 *)bufferPtr + 1;
|
|
|
|
// Restore the evaluation rate
|
|
evalRate = *((int16 *)bufferPtr);
|
|
bufferPtr = (int16 *)bufferPtr + 1;
|
|
|
|
*buf = bufferPtr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create an archive of this TaskStack in the specified buffer
|
|
|
|
void *TaskStack::archive(void *buf) {
|
|
// Store the stack bottom TaskID
|
|
*((TaskID *)buf) = stackBottomID;
|
|
buf = (TaskID *)buf + 1;
|
|
|
|
// Store the actor's id
|
|
*((ObjectID *)buf) = actor->thisID();
|
|
buf = (ObjectID *)buf + 1;
|
|
|
|
// Store the evalCount and evalRate
|
|
*((int16 *)buf) = evalCount;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
*((int16 *)buf) = evalRate;
|
|
buf = (int16 *)buf + 1;
|
|
|
|
return buf;
|
|
}
|
|
|
|
#if DEBUG
|
|
//----------------------------------------------------------------------
|
|
// Debugging function used to mark this task and any sub tasks as being
|
|
// used. This is used to find task leaks.
|
|
|
|
void TaskStack::mark(void) {
|
|
if (stackBottomID != NoTask) {
|
|
Task *stackBottom = getTaskAddress(stackBottomID);
|
|
|
|
stackBottom->mark();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
// 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(void) {
|
|
if (stackBottomID != NoTask) {
|
|
Task *stackBottom = getTaskAddress(stackBottomID);
|
|
|
|
stackBottom->abortTask();
|
|
delete stackBottom;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Re-evaluate tasks in stack
|
|
|
|
TaskResult TaskStack::evaluate(void) {
|
|
if (stackBottomID != -1) {
|
|
Task *stackBottom = getTaskAddress(stackBottomID);
|
|
|
|
return stackBottom->evaluate();
|
|
} else
|
|
return taskNotDone;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Update the state of the tasks in stack
|
|
|
|
TaskResult TaskStack::update(void) {
|
|
TaskResult result;
|
|
|
|
// If the actor is currently uniterruptable then this task is paused
|
|
if (!actor->isInterruptable()) return taskNotDone;
|
|
|
|
if (stackBottomID != NoTask) {
|
|
Task *stackBottom = getTaskAddress(stackBottomID);
|
|
|
|
// Determine if it is time to reevaluate the tasks
|
|
if (--evalCount == 0) {
|
|
if ((result = stackBottom->evaluate()) != taskNotDone) {
|
|
delete stackBottom;
|
|
stackBottomID = NoTask;
|
|
|
|
return result;
|
|
}
|
|
evalCount = evalRate;
|
|
}
|
|
|
|
// Update the tasks
|
|
if ((result = stackBottom->update()) != taskNotDone) {
|
|
delete stackBottom;
|
|
stackBottomID = NoTask;
|
|
|
|
return result;
|
|
}
|
|
} else
|
|
return taskFailed;
|
|
|
|
return taskNotDone;
|
|
}
|
|
|
|
} // end of namespace Saga2
|