aboutsummaryrefslogtreecommitdiff
path: root/engines/saga/actor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/saga/actor.cpp')
-rw-r--r--engines/saga/actor.cpp295
1 files changed, 163 insertions, 132 deletions
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index 6ea8674e8e..e0e9415cab 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -93,9 +93,9 @@ inline int16 int16Compare(int16 i1, int16 i2) {
return ((i1) > (i2) ? 1 : ((i1) < (i2) ? -1 : 0));
}
-inline int16 quickDistance(const Point &point1, const Point &point2) {
+inline int16 quickDistance(const Point &point1, const Point &point2, int16 compressX) {
Point delta;
- delta.x = ABS(point1.x - point2.x) / 2;
+ delta.x = ABS(point1.x - point2.x) / compressX;
delta.y = ABS(point1.y - point2.y);
return ((delta.x < delta.y) ? (delta.y + delta.x / 2) : (delta.x + delta.y / 2));
}
@@ -243,7 +243,7 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) {
_debugPointsAlloced = _debugPointsCount = 0;
#endif
- _protagStates = 0;
+ _protagStates = NULL;
_protagStatesCount = 0;
_pathNodeList = _newPathNodeList = NULL;
@@ -356,56 +356,70 @@ Actor::~Actor() {
free(_pathCell);
_actorsStrings.freeMem();
//release resources
+ freeProtagStates();
freeActorList();
freeObjList();
}
-bool Actor::loadActorResources(ActorData *actor) {
+void Actor::freeProtagStates() {
+ int i;
+ for (i = 0; i < _protagStatesCount; i++) {
+ free(_protagStates[i]._frames);
+ }
+ free(_protagStates);
+ _protagStates = NULL;
+ _protagStatesCount = 0;
+}
+
+void Actor::loadFrameList(int frameListResourceId, ActorFrameSequence *&framesPointer, int &framesCount) {
byte *resourcePointer;
size_t resourceLength;
- int framesCount;
- ActorFrameSequence *framesPointer;
- bool gotSomething = false;
- if (actor->_frameListResourceId) {
- debug(9, "Loading frame resource id %d", actor->_frameListResourceId);
- _vm->_resource->loadResource(_actorContext, actor->_frameListResourceId, resourcePointer, resourceLength);
+ debug(9, "Loading frame resource id %d", frameListResourceId);
+ _vm->_resource->loadResource(_actorContext, frameListResourceId, resourcePointer, resourceLength);
- framesCount = resourceLength / 16;
- debug(9, "Frame resource contains %d frames (res length is %d)", framesCount, (int)resourceLength);
+ framesCount = resourceLength / 16;
+ debug(9, "Frame resource contains %d frames (res length is %d)", framesCount, (int)resourceLength);
- framesPointer = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * framesCount);
- if (framesPointer == NULL && framesCount != 0) {
- memoryError("Actor::loadActorResources");
- }
+ framesPointer = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * framesCount);
+ if (framesPointer == NULL && framesCount != 0) {
+ memoryError("Actor::loadFrameList");
+ }
- MemoryReadStreamEndian readS(resourcePointer, resourceLength, _actorContext->isBigEndian);
+ MemoryReadStreamEndian readS(resourcePointer, resourceLength, _actorContext->isBigEndian);
- for (int i = 0; i < framesCount; i++) {
- debug(9, "frameType %d", i);
- for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
- // Load all four orientations
- framesPointer[i].directions[orient].frameIndex = readS.readUint16();
- if (_vm->getGameType() == GType_ITE) {
- framesPointer[i].directions[orient].frameCount = readS.readSint16();
- } else {
- framesPointer[i].directions[orient].frameCount = readS.readByte();
- readS.readByte();
- }
- if (framesPointer[i].directions[orient].frameCount < 0)
- warning("frameCount < 0 (%d)", framesPointer[i].directions[orient].frameCount);
- debug(9, "frameIndex %d frameCount %d", framesPointer[i].directions[orient].frameIndex, framesPointer[i].directions[orient].frameCount);
+ for (int i = 0; i < framesCount; i++) {
+ debug(9, "frameType %d", i);
+ for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
+ // Load all four orientations
+ framesPointer[i].directions[orient].frameIndex = readS.readUint16();
+ if (_vm->getGameType() == GType_ITE) {
+ framesPointer[i].directions[orient].frameCount = readS.readSint16();
+ } else {
+ framesPointer[i].directions[orient].frameCount = readS.readByte();
+ readS.readByte();
}
+ if (framesPointer[i].directions[orient].frameCount < 0)
+ warning("frameCount < 0 (%d)", framesPointer[i].directions[orient].frameCount);
+ debug(9, "frameIndex %d frameCount %d", framesPointer[i].directions[orient].frameIndex, framesPointer[i].directions[orient].frameCount);
}
+ }
- free(resourcePointer);
+ free(resourcePointer);
+}
- actor->_frames = framesPointer;
- actor->_framesCount = framesCount;
+bool Actor::loadActorResources(ActorData *actor) {
+ bool gotSomething = false;
+
+ if (actor->_frameListResourceId) {
+ loadFrameList(actor->_frameListResourceId, actor->_frames, actor->_framesCount);
+
+ actor->_shareFrames = false;
gotSomething = true;
} else {
- warning("Frame List ID = 0 for actor index %d", actor->_index);
+ // It's normal for some actors to have no frames
+ //warning("Frame List ID = 0 for actor index %d", actor->_index);
//if (_vm->getGameType() == GType_ITE)
return true;
@@ -469,6 +483,7 @@ void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResource
int movementSpeed;
int walkStepIndex;
int walkStepCount;
+ int stateResourceId;
freeActorList();
@@ -566,28 +581,34 @@ void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResource
_protagState = 0;
if (protagStatesResourceID) {
- free(_protagStates);
+ if (!_protagonist->_shareFrames)
+ free(_protagonist->_frames);
+ freeProtagStates();
- _protagStates = (ActorFrameSequence *)malloc(sizeof(ActorFrameSequence) * protagStatesCount);
+ _protagStates = (ProtagStateData *)malloc(sizeof(ProtagStateData) * protagStatesCount);
- byte *resourcePointer;
- size_t resourceLength;
+ byte *idsResourcePointer;
+ size_t idsResourceLength;
_vm->_resource->loadResource(_actorContext, protagStatesResourceID,
- resourcePointer, resourceLength);
+ idsResourcePointer, idsResourceLength);
+ if (idsResourceLength < (size_t)protagStatesCount * 4) {
+ error("Wrong protagonist states resource");
+ }
- MemoryReadStream statesS(resourcePointer, resourceLength);
+ MemoryReadStream statesIds(idsResourcePointer, idsResourceLength);
for (i = 0; i < protagStatesCount; i++) {
- for (j = 0; j < ACTOR_DIRECTIONS_COUNT; j++) {
- _protagStates[i].directions[j].frameIndex = statesS.readUint16LE();
- _protagStates[i].directions[j].frameCount = statesS.readUint16LE();
- }
+ stateResourceId = statesIds.readUint32LE();
+
+ loadFrameList(stateResourceId, _protagStates[i]._frames, _protagStates[i]._framesCount);
}
- free(resourcePointer);
+ free(idsResourcePointer);
- _protagonist->_frames = &_protagStates[_protagState];
+ _protagonist->_frames = _protagStates[_protagState]._frames;
+ _protagonist->_framesCount = _protagStates[_protagState]._framesCount;
+ _protagonist->_shareFrames = true;
}
_protagStatesCount = protagStatesCount;
@@ -822,8 +843,14 @@ bool Actor::validFollowerLocation(const Location &location) {
void Actor::setProtagState(int state) {
_protagState = state;
- if (_vm->getGameType() == GType_IHNM)
- _protagonist->_frames = &_protagStates[state];
+ if (_vm->getGameType() == GType_IHNM) {
+ if (!_protagonist->_shareFrames)
+ free(_protagonist->_frames);
+
+ _protagonist->_frames = _protagStates[state]._frames;
+ _protagonist->_framesCount = _protagStates[state]._framesCount;
+ _protagonist->_shareFrames = true;
+ }
}
void Actor::updateActorsScene(int actorsEntrance) {
@@ -1023,7 +1050,7 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
if ((actor->_facingDirection < kDirUp) || (actor->_facingDirection > kDirUpLeft))
error("Actor::getActorFrameRange Wrong direction 0x%X actorId 0x%X", actor->_facingDirection, actorId);
- //if (_vm->getGameType() == GType_ITE) {
+ if (_vm->getGameType() == GType_ITE) {
if (frameType >= actor->_framesCount) {
warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, actor->_framesCount, actorId);
return &def;
@@ -1032,21 +1059,18 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
fourDirection = actorDirectectionsLUT[actor->_facingDirection];
return &actor->_frames[frameType].directions[fourDirection];
-/*
- } else {
+ }
+
+ if (_vm->getGameType() == GType_IHNM) {
+ // It is normal for some actors to have no frames for a given frameType
+ // These are mainly actors with no frames at all (e.g. narrators or immovable actors)
+ // Examples are AM and the boy when he is talking to Benny via the computer screen.
+ // Both of them are invisible and immovable
+ // There is no point to keep throwing warnings about this, the original checks for
+ // a valid framecount too
if (0 == actor->_framesCount) {
return &def;
}
-
- //TEST
- if (actor->_id == 0x2000) {
- if (actor->_framesCount <= _currentFrameIndex) {
- _currentFrameIndex = 0;
- }
- fr = actor->_frames[_currentFrameIndex].directions;
- return fr;
- }
- //TEST
if (frameType >= actor->_framesCount) {
frameType = actor->_framesCount - 1;
}
@@ -1054,63 +1078,10 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
frameType = 0;
}
- if (frameType == kFrameIHNMWalk ) {
- switch (actor->_facingDirection) {
- case kDirUpRight:
- if (frameType > 0)
- fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_RIGHT];
- else
- fr = &def;
- if (!fr->frameCount)
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_RIGHT];
- break;
- case kDirDownRight:
- if (frameType > 0)
- fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_FORWARD];
- else
- fr = &def;
- if (!fr->frameCount)
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_RIGHT];
- break;
- case kDirUpLeft:
- if (frameType > 0)
- fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_LEFT];
- else
- fr = &def;
- if (!fr->frameCount)
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_LEFT];
- break;
- case kDirDownLeft:
- if (frameType > 0)
- fr = &actor->_frames[frameType - 1].directions[ACTOR_DIRECTION_BACK];
- else
- fr = &def;
- if (!fr->frameCount)
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_LEFT];
- break;
- case kDirRight:
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_RIGHT];
- break;
- case kDirLeft:
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_LEFT];
- break;
- case kDirUp:
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_BACK];
- break;
- case kDirDown:
- fr = &actor->_frames[frameType].directions[ACTOR_DIRECTION_FORWARD];
- break;
- }
- return fr;
- }
- else {
- if (frameType >= actor->_framesCount) {
- error("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, actor->_framesCount, actorId);
- }
- fourDirection = actorDirectectionsLUT[actor->_facingDirection];
- return &actor->_frames[frameType].directions[fourDirection];
- }
- }*/
+ fourDirection = actorDirectectionsLUT[actor->_facingDirection];
+ return &actor->_frames[frameType].directions[fourDirection];
+ }
+ return NULL;
}
void Actor::handleSpeech(int msec) {
@@ -1135,6 +1106,8 @@ void Actor::handleSpeech(int msec) {
removeFirst = true;
}
_activeSpeech.playing = false;
+ if (_activeSpeech.speechFlags & kSpeakForceText)
+ _activeSpeech.speechFlags = 0;
if (_activeSpeech.actorIds[0] != 0) {
actor = getActor(_activeSpeech.actorIds[0]);
if (!(_activeSpeech.speechFlags & kSpeakNoAnimate)) {
@@ -1242,7 +1215,10 @@ void Actor::handleSpeech(int msec) {
}
height2 = actor->_screenPosition.y - 50;
- _activeSpeech.speechBox.top = _activeSpeech.drawRect.top = MAX(10, (height2 - height) / 2);
+ if (height2 > _vm->_scene->getHeight())
+ _activeSpeech.speechBox.top = _activeSpeech.drawRect.top = _vm->_scene->getHeight() - 1 - height - 10;
+ else
+ _activeSpeech.speechBox.top = _activeSpeech.drawRect.top = MAX(10, (height2 - height) / 2);
} else {
_activeSpeech.drawRect.left = _activeSpeech.speechBox.left;
_activeSpeech.drawRect.top = _activeSpeech.speechBox.top + (_activeSpeech.speechBox.height() - height) / 2;
@@ -1472,8 +1448,14 @@ void Actor::handleActions(int msec, bool setup) {
actor->cycleWrap(frameRange->frameCount);
actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
} else {
- actor->_location.x += directionLUT[actor->_actionDirection][0] * 2;
- actor->_location.y += directionLUT[actor->_actionDirection][1] * 2;
+ if (_vm->getGameType() == GType_ITE) {
+ actor->_location.x += directionLUT[actor->_actionDirection][0] * 2;
+ actor->_location.y += directionLUT[actor->_actionDirection][1] * 2;
+ } else {
+ // FIXME: The original does not multiply by 8 here, but we do
+ actor->_location.x += (directionLUT[actor->_actionDirection][0] * 8 * actor->_screenScale + 128) >> 8;
+ actor->_location.y += (directionLUT[actor->_actionDirection][1] * 8 * actor->_screenScale + 128) >> 8;
+ }
frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
actor->_actionCycle++;
@@ -1626,6 +1608,8 @@ void Actor::handleActions(int msec, bool setup) {
}
}
}
+ // Update frameCount for sfWaitFrames in IHNM
+ _vm->_frameCount++;
}
void Actor::direct(int msec) {
@@ -1667,6 +1651,10 @@ bool Actor::calcScreenPosition(CommonObjectData *commonObjectData) {
if (middle <= beginSlope) {
commonObjectData->_screenScale = 256;
+ } else if (_vm->getGameType() == GType_IHNM && (objectTypeId(commonObjectData->_id) & kGameObjectObject)) {
+ commonObjectData->_screenScale = 256;
+ } else if (_vm->getGameType() == GType_IHNM && (commonObjectData->_flags & kNoScale)) {
+ commonObjectData->_screenScale = 256;
} else if (middle >= endSlope) {
commonObjectData->_screenScale = 1;
} else {
@@ -1694,6 +1682,20 @@ uint16 Actor::hitTest(const Point &testPoint, bool skipProtagonist) {
// fine to interact with. For example, the door entrance at the glass
// makers's house in ITE's ferret village.
+ // Note that in IHNM, there are some items that overlap on other items
+ // Since we're checking the draw list from the FIRST item drawn to the
+ // LAST one, sometimes the object drawn first is incorrectly returned.
+ // An example is the chalk on the magic circle in Ted's chapter, which
+ // is drawn AFTER the circle, but HitTest incorrectly returns the circle
+ // id in this case, even though the chalk was drawn after the circle.
+ // Therefore, for IHNM, we iterate through the whole draw list and
+ // return the last match found, not the first one.
+ // Unfortunately, it is only possible to search items in the sorted draw
+ // list from start to end, not reverse, so it's necessary to search
+ // through the whole list to get the item drawn last
+
+ uint16 result = ID_NOTHING;
+
if (!_vm->_scene->getSceneClip().contains(testPoint))
return ID_NOTHING;
@@ -1713,10 +1715,12 @@ uint16 Actor::hitTest(const Point &testPoint, bool skipProtagonist) {
continue;
}
if (_vm->_sprite->hitTest(*spriteList, frameNumber, drawObject->_screenPosition, drawObject->_screenScale, testPoint)) {
- return drawObject->_id;
+ result = drawObject->_id;
+ if (_vm->getGameType() == GType_ITE)
+ return result; // in ITE, return the first result found (read above)
}
}
- return ID_NOTHING;
+ return result; // in IHNM, return the last result found (read above)
}
void Actor::createDrawOrderList() {
@@ -1777,6 +1781,10 @@ bool Actor::getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber
frameNumber = commonObjectData->_spriteListResourceId;
}
+ if (spriteList->spriteCount == 0) {
+ return false;
+ }
+
if ((frameNumber < 0) || (spriteList->spriteCount <= frameNumber)) {
debug(1, "Actor::getSpriteParams frameNumber invalid for %s id 0x%X (%d)",
validObjId(commonObjectData->_id) ? "object" : "actor",
@@ -1830,7 +1838,8 @@ void Actor::drawActors() {
void Actor::drawSpeech(void) {
if (!isSpeaking() || !_activeSpeech.playing || _vm->_script->_skipSpeeches
- || (!_vm->_subtitlesEnabled && (_vm->getFeatures() & GF_CD_FX)))
+ || (!_vm->_subtitlesEnabled && (_vm->getFeatures() & GF_CD_FX))
+ || (!_vm->_subtitlesEnabled && (_vm->getGameType() == GType_IHNM)))
return;
int i;
@@ -2003,9 +2012,12 @@ bool Actor::actorEndWalk(uint16 actorId, bool recurse) {
actor = getActor(actorId);
actor->_actorFlags &= ~kActorBackwards;
- if (actor->_location.distance(actor->_finalTarget) > 8 && (actor->_flags & kProtagonist) && recurse && !(actor->_actorFlags & kActorNoCollide)) {
- actor->_actorFlags |= kActorNoCollide;
- return actorWalkTo(actorId, actor->_finalTarget);
+ if (_vm->getGameType() == GType_ITE) {
+
+ if (actor->_location.distance(actor->_finalTarget) > 8 && (actor->_flags & kProtagonist) && recurse && !(actor->_actorFlags & kActorNoCollide)) {
+ actor->_actorFlags |= kActorNoCollide;
+ return actorWalkTo(actorId, actor->_finalTarget);
+ }
}
actor->_currentAction = kActionWait;
@@ -2256,7 +2268,7 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
_activeSpeech.actorsCount = 1;
_activeSpeech.actorIds[0] = actorId;
_activeSpeech.speechColor[0] = actor->_speechColor;
- _activeSpeech.outlineColor[0] = (_vm->getGameType() == GType_ITE ? kITEColorBlack : kIHNMColorBlack);
+ _activeSpeech.outlineColor[0] = _vm->KnownColor2ColorId(kKnownColorBlack);
_activeSpeech.sampleResourceId = sampleResourceId;
_activeSpeech.playing = false;
_activeSpeech.slowModeCharIndex = 0;
@@ -2275,6 +2287,22 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
_activeSpeech.speechBox.left -= _activeSpeech.speechBox.right - _vm->getDisplayWidth() - 10;
_activeSpeech.speechBox.right = _vm->getDisplayWidth() - 10;
}
+
+ // WORKAROUND for the compact disk in Ellen's chapter
+ // Once Ellen starts saying that "Something is different", bring the compact disk in the
+ // scene. After speaking with AM, the compact disk is visible. She always says this line
+ // when entering room 59, after speaking with AM, if the compact disk is not picked up yet
+ // Check Script::sfDropObject for the other part of this workaround
+ if (_vm->getGameType() == GType_IHNM && _vm->_scene->currentChapterNumber() == 3 &&
+ _vm->_scene->currentSceneNumber() == 59 && _activeSpeech.sampleResourceId == 286) {
+ for (i = 0; i < _objsCount; i++) {
+ if (_objs[i]->_id == 16385) { // the compact disk
+ _objs[i]->_sceneNumber = 59;
+ break;
+ }
+ }
+ }
+
}
void Actor::nonActorSpeech(const Common::Rect &box, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
@@ -2719,10 +2747,11 @@ int Actor::fillPathArray(const Point &fromPoint, const Point &toPoint, Point &be
const PathDirectionData *samplePathDirection;
Point nextPoint;
int directionCount;
+ int16 compressX = (_vm->getGameType() == GType_ITE) ? 2 : 1;
_pathDirectionListCount = 0;
pointCounter = 0;
- bestRating = quickDistance(fromPoint, toPoint);
+ bestRating = quickDistance(fromPoint, toPoint, compressX);
bestPath = fromPoint;
for (startDirection = 0; startDirection < 4; startDirection++) {
@@ -2770,7 +2799,7 @@ int Actor::fillPathArray(const Point &fromPoint, const Point &toPoint, Point &be
bestPoint = toPoint;
return pointCounter;
}
- currentRating = quickDistance(nextPoint, toPoint);
+ currentRating = quickDistance(nextPoint, toPoint, compressX);
if (currentRating < bestRating) {
bestRating = currentRating;
bestPath = nextPoint;
@@ -3106,7 +3135,9 @@ void Actor::saveState(Common::OutSaveFile *out) {
void Actor::loadState(Common::InSaveFile *in) {
int32 i;
- setProtagState(in->readSint16LE());
+ int16 protagState = in->readSint16LE();
+ if (protagState != 0)
+ setProtagState(protagState);
for (i = 0; i < _actorsCount; i++) {
ActorData *a = _actors[i];