/* 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 * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ // Actor management module header file #ifndef SAGA_ACTOR_H #define SAGA_ACTOR_H #include "common/savefile.h" #include "saga/sprite.h" #include "saga/itedata.h" #include "saga/saga.h" #include "saga/font.h" namespace Saga { class HitZone; //#define ACTOR_DEBUG 1 //only for actor pathfinding debug! #define ACTOR_BARRIERS_MAX 16 #define ACTOR_MAX_STEPS_COUNT 32 #define ACTOR_DIALOGUE_HEIGHT 100 #define ACTOR_LMULT 4 #define ACTOR_SPEED 72 #define ACTOR_CLIMB_SPEED 8 #define ACTOR_COLLISION_WIDTH 32 #define ACTOR_COLLISION_HEIGHT 8 #define ACTOR_DIRECTIONS_COUNT 4 // for ActorFrameSequence #define ACTOR_SPEECH_STRING_MAX 16 // speech const #define ACTOR_SPEECH_ACTORS_MAX 8 #define ACTOR_DRAGON_TURN_MOVES 4 #define ACTOR_DRAGON_INDEX 133 #define ACTOR_NO_ENTRANCE -1 #define ACTOR_EXP_KNOCK_RIF 24 #define PATH_NODE_EMPTY -1 #define ACTOR_INHM_SIZE 228 enum ActorDirections { kDirectionRight = 0, kDirectionLeft = 1, kDirectionUp = 2, kDirectionDown = 3 }; 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 ActorFrameIds { //ITE kFrameITEStand = 0, kFrameITEWalk = 1, kFrameITESpeak = 2, kFrameITEGive = 3, kFrameITEGesture = 4, kFrameITEWait = 5, kFrameITEPickUp = 6, kFrameITELook = 7, //IHNM kFrameIHNMStand = 0, kFrameIHNMSpeak = 1, kFrameIHNMWait = 2, kFrameIHNMGesture = 3, kFrameIHNMWalk = 4 }; enum SpeechFlags { kSpeakNoAnimate = 1, kSpeakAsync = 2, kSpeakSlow = 4, kSpeakForceText = 8 }; enum ActorFrameTypes { kFrameStand, kFrameWalk, kFrameSpeak, kFrameGive, kFrameGesture, kFrameWait, kFramePickUp, kFrameLook }; // Lookup table to convert 8 cardinal directions to 4 static const int actorDirectionsLUT[8] = { kDirectionUp, // kDirUp kDirectionRight, // kDirUpRight kDirectionRight, // kDirRight kDirectionRight, // kDirDownRight kDirectionDown, // kDirDown kDirectionLeft, // kDirDownLeft kDirectionLeft, // kDirLeft kDirectionLeft // kDirUpLeft }; 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 }; enum DragonMoveTypes { kDragonMoveUpLeft = 0, kDragonMoveUpRight = 1, kDragonMoveDownLeft = 2, kDragonMoveDownRight = 3, kDragonMoveUpLeft_Left = 4, kDragonMoveUpLeft_Right = 5, kDragonMoveUpRight_Left = 6, kDragonMoveUpRight_Right = 7, kDragonMoveDownLeft_Left = 8, kDragonMoveDownLeft_Right = 9, kDragonMoveDownRight_Left = 10, kDragonMoveDownRight_Right = 11, kDragonMoveInvalid = 12 }; struct PathDirectionData { int8 direction; int16 x; int16 y; }; struct ActorFrameRange { int frameIndex; int frameCount; }; struct ActorFrameSequence { ActorFrameRange directions[ACTOR_DIRECTIONS_COUNT]; }; typedef Common::Array<ActorFrameSequence> ActorFrameSequences; uint pathLine(PointList &pointList, uint idx, const Point &point1, const Point &point2); struct Location { int32 x; // logical coordinates int32 y; // int32 z; // Location() { x = y = z = 0; } void saveState(Common::OutSaveFile *out) { out->writeSint32LE(x); out->writeSint32LE(y); out->writeSint32LE(z); } void loadState(Common::InSaveFile *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; } void fromStream(Common::ReadStream &stream) { x = stream.readUint16LE(); y = stream.readUint16LE(); z = stream.readUint16LE(); } #if 0 // Obsolete function, throws warnings in older versions of GCC // (warning: int format, int32 arg) // Keeping it around for debug purposes void debugPrint(int debuglevel = 0, const char *loc = "Loc:") const { debug(debuglevel, "%s %d, %d, %d", loc, x, y, z); } #endif }; class CommonObjectData { public: //constant int32 _index; // index in local array uint16 _id; // object id int32 _scriptEntrypointNumber; // script entrypoint number //variables uint16 _flags; // initial flags int32 _nameIndex; // index in name string list int32 _sceneNumber; // scene int32 _spriteListResourceId; // sprite list resource id Location _location; // logical coordinates Point _screenPosition; // screen coordinates int32 _screenDepth; // int32 _screenScale; // void saveState(Common::OutSaveFile *out) { out->writeUint16LE(_flags); out->writeSint32LE(_nameIndex); out->writeSint32LE(_sceneNumber); out->writeSint32LE(_spriteListResourceId); _location.saveState(out); out->writeSint16LE(_screenPosition.x); out->writeSint16LE(_screenPosition.y); out->writeSint32LE(_screenDepth); out->writeSint32LE(_screenScale); } void loadState(Common::InSaveFile *in) { _flags = in->readUint16LE(); _nameIndex = in->readSint32LE(); _sceneNumber = in->readSint32LE(); _spriteListResourceId = in->readSint32LE(); _location.loadState(in); _screenPosition.x = in->readSint16LE(); _screenPosition.y = in->readSint16LE(); _screenDepth = in->readSint32LE(); _screenScale = in->readSint32LE(); } CommonObjectData() { _index = 0; _id = 0; _scriptEntrypointNumber = 0; _flags = 0; _nameIndex = 0; _sceneNumber = 0; _spriteListResourceId = 0; _screenDepth = 0; _screenScale = 0; } }; typedef CommonObjectData *CommonObjectDataPointer; typedef Common::List<CommonObjectDataPointer> CommonObjectOrderList; class ObjectData: public CommonObjectData { public: //constant uint16 _interactBits; ObjectData() { _interactBits = 0; } }; typedef Common::Array<ObjectData> ObjectDataArray; class ActorData: public CommonObjectData { public: //constant SpriteList _spriteList; // sprite list data ActorFrameSequences *_frames; // Actor's frames ActorFrameSequences _framesContainer; // Actor's frames int _frameListResourceId; // Actor's frame list resource id byte _speechColor; // Actor dialogue color // bool _inScene; //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; int16 _fallVelocity; int16 _fallAcceleration; int16 _fallPosition; uint8 _dragonBaseFrame; uint8 _dragonStepCycle; uint8 _dragonMoveType; int32 _frameNumber; // current frame number ByteArray _tileDirections; Common::Array<Point> _walkStepsPoints; int32 _walkStepsCount; int32 _walkStepIndex; Location _finalTarget; Location _partialTarget; int32 _walkFrameSequence; public: ActorData(); void saveState(Common::OutSaveFile *out); void loadState(uint32 version, Common::InSaveFile *in); void cycleWrap(int cycleLimit); void addWalkStepPoint(const Point &point); bool shareFrames() { return ((_frames != NULL) && (_frames != &_framesContainer)); } }; typedef Common::Array<ActorData> ActorDataArray; struct ProtagStateData { ActorFrameSequences _frames; // Actor's frames }; struct SpeechData { int speechColor[ACTOR_SPEECH_ACTORS_MAX]; int outlineColor[ACTOR_SPEECH_ACTORS_MAX]; int speechFlags; const char *strings[ACTOR_SPEECH_STRING_MAX]; Rect speechBox; Rect drawRect; int stringsCount; int slowModeCharIndex; uint16 actorIds[ACTOR_SPEECH_ACTORS_MAX]; int actorsCount; int sampleResourceId; bool playing; int playingTime; SpeechData() { memset(this, 0, sizeof(*this)); } FontEffectFlags getFontFlags(int i) { if (outlineColor[i] != 0) { return kFontOutline; } else { return kFontNormal; } } }; typedef int (*CompareFunction) (const CommonObjectDataPointer& a, const CommonObjectDataPointer& b); class Actor { friend class IsoMap; friend class SagaEngine; friend class Puzzle; 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, _actors.size()))); } 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, _objs.size())); } 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; } void direct(int msec); void drawActors(); void updateActorsScene(int actorsEntrance); // calls from scene loading to update Actors info void drawSpeech(); #ifdef ACTOR_DEBUG void drawPathTest(); #endif 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); int getFrameType(ActorFrameTypes frameType); 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, int sampleResourceId, int speechFlags); void nonActorSpeech(const Common::Rect &box, const char **strings, int stringsCount, int sampleResourceId, int speechFlags); void simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags, int sampleResourceId); void setSpeechColor(int speechColor, int outlineColor) { _activeSpeech.speechColor[0] = speechColor; _activeSpeech.outlineColor[0] = outlineColor; } void abortAllSpeeches(); void abortSpeech(); bool isSpeaking() { return _activeSpeech.stringsCount > 0; } int isForcedTextShown() { return _activeSpeech.speechFlags & kSpeakForceText; } void saveState(Common::OutSaveFile *out); void loadState(Common::InSaveFile *in); void setProtagState(int state); int getProtagState() { return _protagState; } void loadActorList(int protagonistIdx, int actorCount, int actorsResourceID, int protagStatesCount, int protagStatesResourceID); void loadObjList(int objectCount, int objectsResourceID); protected: friend class Script; void loadActorResources(ActorData *actor); void loadFrameList(int frameListResourceId, ActorFrameSequences &frames); private: void stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped); void loadActorSpriteList(ActorData *actor); void drawOrderListAdd(const CommonObjectDataPointer& element, CompareFunction compareFunction); void createDrawOrderList(); bool 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) { #ifdef ACTOR_DEBUG if (!validPathCellPoint(testPoint)) { error("Actor::setPathCell wrong point"); } #endif _pathCell[testPoint.x + testPoint.y * _xCellCount] = value; } int8 getPathCell(const Point &testPoint) { #ifdef ACTOR_DEBUG if (!validPathCellPoint(testPoint)) { error("Actor::getPathCell wrong point"); } #endif 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); void moveDragon(ActorData *actor); protected: //constants ActorDataArray _actors; ObjectDataArray _objs; SagaEngine *_vm; ResourceContext *_actorContext; int _lastTickMsec; CommonObjectOrderList _drawOrderList; //variables public: ActorData *_centerActor; ActorData *_protagonist; int _handleActionDiv; Rect _speechBoxScript; StringsTable _objectsStrings; StringsTable _actorsStrings; protected: SpeechData _activeSpeech; int _protagState; bool _dragonHunt; private: Common::Array<ProtagStateData> _protagStates; //path stuff struct PathNode { Point point; int link; PathNode() : link(0) {} PathNode(const Point &p) : point(p), link(0) {} PathNode(const Point &p, int l) : point(p), link(l) {} }; typedef Common::Array<PathNode> PathNodeList; Rect _barrierList[ACTOR_BARRIERS_MAX]; int _barrierCount; Common::Array<int8> _pathCell; int _xCellCount; int _yCellCount; Rect _pathRect; PointList _pathList; uint _pathListIndex; PathNodeList _pathNodeList; public: #ifdef ACTOR_DEBUG #ifndef SAGA_DEBUG #error You must also define SAGA_DEBUG #endif //path debug - use with care struct DebugPoint { Point point; byte color; DebugPoint() : color(0) {} DebugPoint(const Point &p, byte c): point(p), color(c) {} }; Common::Array<DebugPoint> _debugPoints; uint _debugPointsCount; // we still need this trick to speedup debug points addition void addDebugPoint(const Point &point, byte color) { if (_debugPointsCount < _debugPoints.size()) { _debugPoints[_debugPointsCount].point = point; _debugPoints[_debugPointsCount].color = color; } else { _debugPoints.push_back(DebugPoint(point, color)); } ++_debugPointsCount; } #endif }; } // End of namespace Saga #endif