scummvm/saga/actor.h
2005-05-08 21:49:52 +00:00

665 lines
17 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004-2005 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
// Actor management module header file
#ifndef SAGA_ACTOR_H__
#define SAGA_ACTOR_H__
#include "common/file.h"
#include "saga/sprite.h"
#include "saga/itedata.h"
#include "saga/list.h"
#include "saga/saga.h"
namespace Saga {
class HitZone;
#define ACTOR_DEBUG
#define ACTOR_BARRIERS_MAX 16
#define ACTOR_MAX_STEPS_COUNT 32
#define ACTOR_DIALOGUE_HEIGHT 100
#define ACTOR_LMULT 4
#define ACTOR_COLLISION_WIDTH 32
#define ACTOR_COLLISION_HEIGHT 8
#define ACTOR_DIRECTIONS_COUNT 4 // for ActorFrameSequence
#define ACTOR_DIRECTION_RIGHT 0
#define ACTOR_DIRECTION_LEFT 1
#define ACTOR_DIRECTION_BACK 2
#define ACTOR_DIRECTION_FORWARD 3
#define ACTOR_SPEECH_STRING_MAX 16 // speech const
#define ACTOR_SPEECH_ACTORS_MAX 8
#define ACTOR_NO_ENTRANCE -1
#define PATH_NODE_EMPTY -1
enum ActorActions {
kActionWait = 0,
kActionWalkToPoint = 1,
kActionWalkToLink = 2,
kActionWalkDir = 3,
kActionSpeak = 4,
kActionAccept = 5,
kActionStoop = 6,
kActionLook = 7,
kActionCycleFrames = 8,
kActionPongFrames = 9,
kActionFreeze = 10,
kActionFall = 11,
kActionClimb = 12
};
enum SpeechFlags {
kSpeakNoAnimate = 1,
kSpeakAsync = 2,
kSpeakSlow = 4
};
enum ActorFrameTypes {
kFrameStand = 0,
kFrameWalk = 1,
kFrameSpeak = 2,
kFrameGive = 3,
kFrameGesture = 4,
kFrameWait = 5,
kFramePickUp = 6,
kFrameLook = 7
//...some special
};
enum ActorFlagsEx {
kActorNoCollide = (1 << 0),
kActorNoFollow = (1 << 1),
kActorCollided = (1 << 2),
kActorBackwards = (1 << 3),
kActorContinuous = (1 << 4),
kActorFinalFace = (1 << 5),
kActorFinishLeft = ((1 << 5) | (kDirLeft << 6)),
kActorFinishRight = ((1 << 5) | (kDirRight << 6)),
kActorFinishUp = ((1 << 5) | (kDirUp << 6)),
kActorFinishDown = ((1 << 5) | (kDirDown << 6)),
kActorFacingMask = (0xf << 5),
kActorRandom = (1 << 10)
};
enum PathCellType {
kPathCellEmpty = -1,
//kDirUp = 0 .... kDirUpLeft = 7
kPathCellBarrier = 0x57
};
struct PathDirectionData {
int8 direction;
int16 x;
int16 y;
};
struct ActorFrameRange {
int frameIndex;
int frameCount;
};
struct ActorFrameSequence {
ActorFrameRange directions[ACTOR_DIRECTIONS_COUNT];
};
struct Location {
int32 x; // logical coordinates
int32 y; //
int32 z; //
Location() {
x = y = z = 0;
}
void saveState(File& out) {
out.writeSint32LE(x);
out.writeSint32LE(y);
out.writeSint32LE(z);
}
void loadState(File& in) {
x = in.readSint32LE();
y = in.readSint32LE();
z = in.readSint32LE();
}
int distance(const Location &location) const {
return MAX(ABS(x - location.x), ABS(y - location.y));
}
int32 &u() {
return x;
}
int32 &v() {
return y;
}
int32 u() const {
return x;
}
int32 v() const {
return y;
}
int32 uv() const {
return u() + v();
}
void delta(const Location &location, Location &result) const {
result.x = x - location.x;
result.y = y - location.y;
result.z = z - location.z;
}
void addXY(const Location &location) {
x += location.x;
y += location.y;
}
void add(const Location &location) {
x += location.x;
y += location.y;
z += location.z;
}
void fromScreenPoint(const Point &screenPoint) {
x = (screenPoint.x * ACTOR_LMULT);
y = (screenPoint.y * ACTOR_LMULT);
z = 0;
}
void toScreenPointXY(Point &screenPoint) const {
screenPoint.x = x / ACTOR_LMULT;
screenPoint.y = y / ACTOR_LMULT;
}
void toScreenPointUV(Point &screenPoint) const {
screenPoint.x = u();
screenPoint.y = v();
}
void toScreenPointXYZ(Point &screenPoint) const {
screenPoint.x = x / ACTOR_LMULT;
screenPoint.y = y / ACTOR_LMULT - z;
}
};
class CommonObjectData {
public:
//constant
bool disabled; // disabled in init section
int32 index; // index in local array
uint16 id; // object id
int32 scriptEntrypointNumber; // script entrypoint number
int32 spriteListResourceId; // sprite list resource id
//variables
uint16 flags; // initial flags
int32 nameIndex; // index in name string list
int32 sceneNumber; // scene
Location location; // logical coordinates
Point screenPosition; // screen coordinates
int32 screenDepth; //
int32 screenScale; //
void saveState(File& out) {
out.writeUint16LE(flags);
out.writeSint32LE(nameIndex);
out.writeSint32LE(sceneNumber);
location.saveState(out);
out.writeSint16LE(screenPosition.x);
out.writeSint16LE(screenPosition.y);
out.writeSint32LE(screenDepth);
out.writeSint32LE(screenScale);
}
void loadState(File& in) {
flags = in.readUint16LE();
nameIndex = in.readSint32LE();
sceneNumber = in.readSint32LE();
location.loadState(in);
screenPosition.x = in.readSint16LE();
screenPosition.y = in.readSint16LE();
screenDepth = in.readSint32LE();
screenScale = in.readSint32LE();
}
};
typedef CommonObjectData *CommonObjectDataPointer;
typedef SortedList<CommonObjectDataPointer> CommonObjectOrderList;
class ObjectData: public CommonObjectData {
public:
//constant
uint16 interactBits;
ObjectData() {
memset(this, 0, sizeof(*this));
}
};
class ActorData: public CommonObjectData {
public:
//constant
SpriteList spriteList; // sprite list data
ActorFrameSequence *frames; // Actor's frames
int framesCount; // Actor's frames count
int frameListResourceId; // Actor's frame list resource id
byte speechColor; // Actor dialogue color
//variables
uint16 actorFlags; // dynamic flags
int32 currentAction; // ActorActions type
int32 facingDirection; // orientation
int32 actionDirection;
int32 actionCycle;
uint16 targetObject;
const HitZone *lastZone;
int32 cycleFrameSequence;
uint8 cycleDelay;
uint8 cycleTimeCount;
uint8 cycleFlags;
int32 frameNumber; // current frame number
int32 tileDirectionsAlloced;
byte *tileDirections;
int32 walkStepsAlloced;
Point *walkStepsPoints;
int32 walkStepsCount;
int32 walkStepIndex;
Location finalTarget;
Location partialTarget;
int32 walkFrameSequence;
void saveState(File& out) {
CommonObjectData::saveState(out);
out.writeUint16LE(actorFlags);
out.writeSint32LE(currentAction);
out.writeSint32LE(facingDirection);
out.writeSint32LE(actionDirection);
out.writeSint32LE(actionCycle);
out.writeUint16LE(targetObject);
//TODO: write lastZone
out.writeSint32LE(cycleFrameSequence);
out.writeByte(cycleDelay);
out.writeByte(cycleTimeCount);
out.writeByte(cycleFlags);
out.writeSint32LE(frameNumber);
out.writeSint32LE(tileDirectionsAlloced);
for (int i = 0; i < tileDirectionsAlloced; i++) {
out.writeByte(tileDirections[i]);
}
out.writeSint32LE(walkStepsAlloced);
for (int i = 0; i < walkStepsAlloced; i++) {
out.writeSint16LE(walkStepsPoints[i].x);
out.writeSint16LE(walkStepsPoints[i].y);
}
out.writeSint32LE(walkStepsCount);
out.writeSint32LE(walkStepIndex);
finalTarget.saveState(out);
partialTarget.saveState(out);
out.writeSint32LE(walkFrameSequence);
}
void loadState(File& in) {
CommonObjectData::loadState(in);
actorFlags = in.readUint16LE();
currentAction = in.readSint32LE();
facingDirection = in.readSint32LE();
actionDirection = in.readSint32LE();
actionCycle = in.readSint32LE();
targetObject = in.readUint16LE();
//TODO: read lastZone
lastZone = NULL;
cycleFrameSequence = in.readSint32LE();
cycleDelay = in.readByte();
cycleTimeCount = in.readByte();
cycleFlags = in.readByte();
frameNumber = in.readSint32LE();
setTileDirectionsSize(in.readSint32LE(), true);
for (int i = 0; i < tileDirectionsAlloced; i++) {
tileDirections[i] = in.readByte();
}
setWalkStepsPointsSize(in.readSint32LE(), true);
for (int i = 0; i < walkStepsAlloced; i++) {
walkStepsPoints[i].x = in.readSint16LE();
walkStepsPoints[i].y = in.readSint16LE();
}
walkStepsCount = in.readSint32LE();
walkStepIndex = in.readSint32LE();
finalTarget.loadState(in);
partialTarget.loadState(in);
walkFrameSequence = in.readSint32LE();
}
void setTileDirectionsSize(int size, bool forceRealloc) {
if ((size <= tileDirectionsAlloced) && !forceRealloc) {
return;
}
tileDirectionsAlloced = size;
tileDirections = (byte*)realloc(tileDirections, tileDirectionsAlloced * sizeof(*tileDirections));
}
void cycleWrap(int cycleLimit) {
if (actionCycle >= cycleLimit)
actionCycle = 0;
}
void setWalkStepsPointsSize(int size, bool forceRealloc) {
if ((size <= walkStepsAlloced) && !forceRealloc) {
return;
}
walkStepsAlloced = size;
walkStepsPoints = (Point*)realloc(walkStepsPoints, walkStepsAlloced * sizeof(*walkStepsPoints));
}
void addWalkStepPoint(const Point &point) {
setWalkStepsPointsSize(walkStepsCount + 1, false);
walkStepsPoints[walkStepsCount++] = point;
}
ActorData() {
memset(this, 0, sizeof(*this));
}
~ActorData() {
free(frames);
free(tileDirections);
free(walkStepsPoints);
spriteList.freeMem();
}
};
struct SpeechData {
int speechColor[ACTOR_SPEECH_ACTORS_MAX];
int outlineColor[ACTOR_SPEECH_ACTORS_MAX];
int speechFlags;
const char *strings[ACTOR_SPEECH_STRING_MAX];
Point speechCoords[ACTOR_SPEECH_ACTORS_MAX];
int stringsCount;
int slowModeCharIndex;
uint16 actorIds[ACTOR_SPEECH_ACTORS_MAX];
int actorsCount;
int sampleResourceId;
bool playing;
int playingTime;
SpeechData() {
memset(this, 0, sizeof(*this));
}
};
class Actor {
friend class IsoMap;
friend class SagaEngine;
public:
Actor(SagaEngine *vm);
~Actor();
void cmdActorWalkTo(int argc, const char **argv);
bool validActorId(uint16 id) { return (id == ID_PROTAG) || ((id >= objectIndexToId(kGameObjectActor, 0)) && (id < objectIndexToId(kGameObjectActor, _actorsCount))); }
int actorIdToIndex(uint16 id) { return (id == ID_PROTAG ) ? 0 : objectIdToIndex(id); }
uint16 actorIndexToId(int index) { return (index == 0 ) ? ID_PROTAG : objectIndexToId(kGameObjectActor, index); }
ActorData *getActor(uint16 actorId);
// clarification: Obj - means game object, such Hat, Spoon etc, Object - means Actor,Obj,HitZone,StepZone
bool validObjId(uint16 id) { return (id >= objectIndexToId(kGameObjectObject, 0)) && (id < objectIndexToId(kGameObjectObject, _objsCount)); }
int objIdToIndex(uint16 id) { return objectIdToIndex(id); }
uint16 objIndexToId(int index) { return objectIndexToId(kGameObjectObject, index); }
ObjectData *getObj(uint16 objId);
int getObjectScriptEntrypointNumber(uint16 id) {
int objectType;
objectType = objectTypeId(id);
if (!(objectType & (kGameObjectObject | kGameObjectActor))) {
error("Actor::getObjectScriptEntrypointNumber wrong id 0x%X", id);
}
return (objectType == kGameObjectObject) ? getObj(id)->scriptEntrypointNumber : getActor(id)->scriptEntrypointNumber;
}
int getObjectFlags(uint16 id) {
int objectType;
objectType = objectTypeId(id);
if (!(objectType & (kGameObjectObject | kGameObjectActor))) {
error("Actor::getObjectFlags wrong id 0x%X", id);
}
return (objectType == kGameObjectObject) ? getObj(id)->flags : getActor(id)->flags;
}
int direct(int msec);
void drawActors();
void updateActorsScene(int actorsEntrance); // calls from scene loading to update Actors info
void drawPathTest();
uint16 hitTest(const Point &testPoint, bool skipProtagonist);
void takeExit(uint16 actorId, const HitZone *hitZone);
bool actorEndWalk(uint16 actorId, bool recurse);
bool actorWalkTo(uint16 actorId, const Location &toLocation);
ActorFrameRange *getActorFrameRange(uint16 actorId, int frameType);
void actorFaceTowardsPoint(uint16 actorId, const Location &toLocation);
void actorFaceTowardsObject(uint16 actorId, uint16 objectId);
void realLocation(Location &location, uint16 objectId, uint16 walkFlags);
// speech
void actorSpeech(uint16 actorId, const char **strings, int stringsCount, uint16 sampleResourceId, int speechFlags);
void nonActorSpeech(const char **strings, int stringsCount, int speechFlags);
void simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags);
void setSpeechColor(int speechColor, int outlineColor) {
_activeSpeech.speechColor[0] = speechColor;
_activeSpeech.outlineColor[0] = outlineColor;
}
void abortAllSpeeches();
void abortSpeech();
bool isSpeaking() {
return _activeSpeech.stringsCount > 0;
}
void saveState(File& out);
void loadState(File& in);
void setProtagState(int state);
int getProtagState() { return _protagState; }
private:
bool loadActorResources(ActorData *actor);
void stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped);
void createDrawOrderList();
void calcScreenPosition(CommonObjectData *commonObjectData);
bool getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber, SpriteList *&spriteList);
bool followProtagonist(ActorData *actor);
void findActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint);
void handleSpeech(int msec);
void handleActions(int msec, bool setup);
bool validPathCellPoint(const Point &testPoint) {
return !((testPoint.x < 0) || (testPoint.x >= _xCellCount) ||
(testPoint.y < 0) || (testPoint.y >= _yCellCount));
}
void setPathCell(const Point &testPoint, int8 value) {
if (!validPathCellPoint(testPoint)) {
error("Actor::setPathCell wrong point");
}
_pathCell[testPoint.x + testPoint.y * _xCellCount] = value;
}
int8 getPathCell(const Point &testPoint) {
if (!validPathCellPoint(testPoint)) {
error("Actor::getPathCell wrong point");
}
return _pathCell[testPoint.x + testPoint.y * _xCellCount];
}
bool scanPathLine(const Point &point1, const Point &point2);
int fillPathArray(const Point &fromPoint, const Point &toPoint, Point &bestPoint);
void setActorPath(ActorData *actor, const Point &fromPoint, const Point &toPoint);
void pathToNode();
void condenseNodeList();
void removeNodes();
void nodeToPath();
void removePathPoints();
bool validFollowerLocation(const Location &location);
protected:
//constants
int _actorsCount;
ActorData **_actors;
int _objsCount;
ObjectData **_objs;
SagaEngine *_vm;
RSCFILE_CONTEXT *_actorContext;
StringsTable _actorsStrings;
int _lastTickMsec;
CommonObjectOrderList _drawOrderList;
//variables
public:
ActorData *_centerActor;
ActorData *_protagonist;
protected:
SpeechData _activeSpeech;
int _protagState;
private:
//path stuff
struct PathNode {
Point point;
int link;
};
Rect _barrierList[ACTOR_BARRIERS_MAX];
int _barrierCount;
int8 *_pathCell;
int _xCellCount;
int _yCellCount;
Rect _pathRect;
PathDirectionData *_pathDirectionList;
int _pathDirectionListCount;
int _pathDirectionListAlloced;
PathDirectionData * addPathDirectionListData() {
if (_pathDirectionListCount + 1 >= _pathDirectionListAlloced) {
_pathDirectionListAlloced += 100;
_pathDirectionList = (PathDirectionData*) realloc(_pathDirectionList, _pathDirectionListAlloced * sizeof(*_pathDirectionList));
}
return &_pathDirectionList[_pathDirectionListCount++];
}
Point *_pathList;
int _pathListIndex;
int _pathListAlloced;
void addPathListPoint(const Point &point) {
++_pathListIndex;
if (_pathListIndex >= _pathListAlloced) {
_pathListAlloced += 100;
_pathList = (Point*) realloc(_pathList, _pathListAlloced * sizeof(*_pathList));
}
_pathList[_pathListIndex] = point;
}
int _pathNodeListIndex;
int _pathNodeListAlloced;
PathNode *_pathNodeList;
void addPathNodeListPoint(const Point &point) {
++_pathNodeListIndex;
if (_pathNodeListIndex >= _pathNodeListAlloced) {
_pathNodeListAlloced += 100;
_pathNodeList = (PathNode*) realloc(_pathNodeList, _pathNodeListAlloced * sizeof(*_pathNodeList));
}
_pathNodeList[_pathNodeListIndex].point = point;
}
int _newPathNodeListIndex;
int _newPathNodeListAlloced;
PathNode *_newPathNodeList;
void incrementNewPathNodeListIndex() {
++_newPathNodeListIndex;
if (_newPathNodeListIndex >= _newPathNodeListAlloced) {
_newPathNodeListAlloced += 100;
_newPathNodeList = (PathNode*) realloc(_newPathNodeList, _newPathNodeListAlloced * sizeof(*_newPathNodeList));
}
}
void addNewPathNodeListPoint(const PathNode &pathNode) {
incrementNewPathNodeListIndex();
_newPathNodeList[_newPathNodeListIndex] = pathNode;
}
public:
#ifdef ACTOR_DEBUG
//path debug - use with care
struct DebugPoint {
Point point;
byte color;
};
DebugPoint *_debugPoints;
int _debugPointsCount;
int _debugPointsAlloced;
void addDebugPoint(const Point &point, byte color) {
if (_debugPointsCount + 1 > _debugPointsAlloced) {
_debugPointsAlloced += 1000;
_debugPoints = (DebugPoint*) realloc(_debugPoints, _debugPointsAlloced * sizeof(*_debugPoints));
}
_debugPoints[_debugPointsCount].color = color;
_debugPoints[_debugPointsCount++].point = point;
}
#endif
};
inline int16 quickDistance(const Point &point1, const Point &point2) {
Point delta;
delta.x = ABS(point1.x - point2.x);
delta.y = ABS(point1.y - point2.y);
return ((delta.x < delta.y) ? (delta.y + delta.x / 2) : (delta.x + delta.y / 2));
}
} // End of namespace Saga
#endif