aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/actor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/scumm/actor.cpp')
-rw-r--r--engines/scumm/actor.cpp2253
1 files changed, 2253 insertions, 0 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
new file mode 100644
index 0000000000..7e6696680f
--- /dev/null
+++ b/engines/scumm/actor.cpp
@@ -0,0 +1,2253 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/actor.h"
+#include "scumm/akos.h"
+#include "scumm/boxes.h"
+#include "scumm/charset.h"
+#include "scumm/costume.h"
+#include "scumm/intern.h"
+#include "scumm/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/saveload.h"
+#include "scumm/sound.h"
+#include "scumm/sprite_he.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+byte Actor::kInvalidBox = 0;
+ScummEngine *Actor::_vm = 0;
+
+void Actor::initActorClass(ScummEngine *scumm) {
+ _vm = scumm;
+ if (_vm->_features & GF_SMALL_HEADER) {
+ kInvalidBox = 255;
+ }
+}
+
+Actor::Actor() {
+ assert(_vm != 0);
+ _number = 0;
+
+ initActor(-1);
+}
+
+void Actor::initActor(int mode) {
+ if (mode == -1) {
+ _offsX = _offsY = 0;
+ _top = _bottom = 0;
+ _needRedraw = _needBgReset = _costumeNeedsInit = _visible = false;
+ _flip = false;
+ _speedx = 8;
+ _speedy = 2;
+ _frame = 0;
+ _walkbox = 0;
+ _animProgress = 0;
+ _heSkipLimbs = false;
+ _drawToBackBuf = false;
+ memset(_animVariable, 0, sizeof(_animVariable));
+ memset(_palette, 0, sizeof(_palette));
+ memset(_sound, 0, sizeof(_sound));
+ memset(&_cost, 0, sizeof(CostumeData));
+ memset(&_walkdata, 0, sizeof(ActorWalkData));
+ _walkdata.point3.x = 32000;
+ _walkScript = 0;
+ memset(_heTalkQueue, 0, sizeof(_heTalkQueue));
+
+ mode = 1;
+ }
+
+ if (mode == 1) {
+ _costume = 0;
+ _room = 0;
+ _pos.x = 0;
+ _pos.y = 0;
+ _facing = 180;
+ _heCondMask = 1;
+ _heNoTalkAnimation = 0;
+ if (_vm->_version >= 7)
+ _visible = false;
+ _heSkipLimbs = false;
+ } else if (mode == 2) {
+ _facing = 180;
+ _heCondMask = 1;
+ _heSkipLimbs = false;
+ }
+ _elevation = 0;
+ _width = 24;
+ _talkColor = 15;
+ _talkPosX = 0;
+ _talkPosY = -80;
+ _boxscale = _scaley = _scalex = 0xFF;
+ _charset = 0;
+ memset(_sound, 0, sizeof(_sound));
+ _targetFacing = _facing;
+
+ stopActorMoving();
+
+ _heXmapNum = 0;
+ _shadowMode = 0;
+ _layer = 0;
+
+ setActorWalkSpeed(8, 2);
+ _animSpeed = 0;
+ if (_vm->_version >= 6)
+ _animProgress = 0;
+
+ _ignoreBoxes = false;
+ _forceClip = (_vm->_version >= 7) ? 100 : 0;
+ _ignoreTurns = false;
+
+ if (_vm->_heversion >= 61)
+ _flip = 0;
+
+ _talkFrequency = 256;
+ _talkPan = 64;
+ _talkVolume = 127;
+
+ if (_vm->_version <= 2) {
+ _initFrame = 2;
+ _walkFrame = 0;
+ _standFrame = 1;
+ _talkStartFrame = 5;
+ _talkStopFrame = 4;
+ } else {
+ _initFrame = 1;
+ _walkFrame = 2;
+ _standFrame = 3;
+ _talkStartFrame = 4;
+ _talkStopFrame = 5;
+ }
+
+ _heTalking = false;
+ _walkScript = 0;
+ _talkScript = 0;
+
+ _clipOverride = _vm->_actorClipOverride;
+
+ _auxBlock.reset();
+ _hePaletteNum = 0;
+
+ _vm->_classData[_number] = (_vm->_version >= 7) ? _vm->_classData[0] : 0;
+}
+
+void Actor::stopActorMoving() {
+ if (_walkScript)
+ _vm->stopScript(_walkScript);
+ _moving = 0;
+}
+
+void Actor::setActorWalkSpeed(uint newSpeedX, uint newSpeedY) {
+ if (newSpeedX == _speedx && newSpeedY == _speedy)
+ return;
+
+ _speedx = newSpeedX;
+ _speedy = newSpeedY;
+
+ if (_moving) {
+ calcMovementFactor(_walkdata.next);
+ }
+}
+
+int ScummEngine::getAngleFromPos(int x, int y) const {
+ if (_gameId == GID_DIG || _gameId == GID_CMI) {
+ double temp = atan2((double)x, (double)-y);
+ return normalizeAngle((int)(temp * 180 / PI));
+ } else {
+ if (ABS(y) * 2 < ABS(x)) {
+ if (x > 0)
+ return 90;
+ return 270;
+ } else {
+ if (y > 0)
+ return 180;
+ return 0;
+ }
+ }
+}
+
+int Actor::calcMovementFactor(const Common::Point& next) {
+ Common::Point _actorPos(_pos);
+ int diffX, diffY;
+ int32 deltaXFactor, deltaYFactor;
+
+ if (_actorPos == next)
+ return 0;
+
+ diffX = next.x - _actorPos.x;
+ diffY = next.y - _actorPos.y;
+ deltaYFactor = _speedy << 16;
+
+ if (diffY < 0)
+ deltaYFactor = -deltaYFactor;
+
+ deltaXFactor = deltaYFactor * diffX;
+ if (diffY != 0) {
+ deltaXFactor /= diffY;
+ } else {
+ deltaYFactor = 0;
+ }
+
+ if ((uint) ABS((int)(deltaXFactor >> 16)) > _speedx) {
+ deltaXFactor = _speedx << 16;
+ if (diffX < 0)
+ deltaXFactor = -deltaXFactor;
+
+ deltaYFactor = deltaXFactor * diffY;
+ if (diffX != 0) {
+ deltaYFactor /= diffX;
+ } else {
+ deltaXFactor = 0;
+ }
+ }
+
+ _walkdata.cur = _actorPos;
+ _walkdata.next = next;
+ _walkdata.deltaXFactor = deltaXFactor;
+ _walkdata.deltaYFactor = deltaYFactor;
+ _walkdata.xfrac = 0;
+ _walkdata.yfrac = 0;
+
+ _targetFacing = _vm->getAngleFromPos(deltaXFactor, deltaYFactor);
+
+ return actorWalkStep();
+}
+
+int Actor::remapDirection(int dir, bool is_walking) {
+ int specdir;
+ byte flags;
+ bool flipX;
+ bool flipY;
+
+ // FIXME - It seems that at least in The Dig the original code does
+ // check _ignoreBoxes here. However, it breaks some animations in Loom,
+ // causing Bobbin to face towards the camera instead of away from it
+ // in some places: After the tree has been destroyed by lightning, and
+ // when entering the dark tunnels beyond the dragon's lair at the very
+ // least. Possibly other places as well.
+ //
+ // The Dig also checks if the actor is in the current room, but that's
+ // not necessary here because we never call the function unless the
+ // actor is in the current room anyway.
+
+ if (!_ignoreBoxes || _vm->_gameId == GID_LOOM) {
+ specdir = _vm->_extraBoxFlags[_walkbox];
+ if (specdir) {
+ if (specdir & 0x8000) {
+ dir = specdir & 0x3FFF;
+ } else {
+ specdir = specdir & 0x3FFF;
+ if (specdir - 90 < dir && dir < specdir + 90)
+ dir = specdir;
+ else
+ dir = specdir + 180;
+ }
+ }
+
+ flags = _vm->getBoxFlags(_walkbox);
+
+ flipX = (_walkdata.deltaXFactor > 0);
+ flipY = (_walkdata.deltaYFactor > 0);
+
+ // Check for X-Flip
+ if ((flags & kBoxXFlip) || isInClass(kObjectClassXFlip)) {
+ dir = 360 - dir;
+ flipX = !flipX;
+ }
+ // Check for Y-Flip
+ if ((flags & kBoxYFlip) || isInClass(kObjectClassYFlip)) {
+ dir = 180 - dir;
+ flipY = !flipY;
+ }
+
+ switch (flags & 7) {
+ case 1:
+ if (_vm->_version >= 7) {
+ if (dir < 180)
+ return 90;
+ else
+ return 270;
+ } else {
+ if (is_walking) // Actor is walking
+ return flipX ? 90 : 270;
+ else // Actor is standing/turning
+ return (dir == 90) ? 90 : 270;
+ }
+ case 2:
+ if (_vm->_version >= 7) {
+ if (dir > 90 && dir < 270)
+ return 180;
+ else
+ return 0;
+ } else {
+ if (is_walking) // Actor is walking
+ return flipY ? 180 : 0;
+ else // Actor is standing/turning
+ return (dir == 0) ? 0 : 180;
+ }
+ case 3:
+ return 270;
+ case 4:
+ return 90;
+ case 5:
+ return 0;
+ case 6:
+ return 180;
+ }
+ }
+ // OR 1024 in to signal direction interpolation should be done
+ return normalizeAngle(dir) | 1024;
+}
+
+int Actor::updateActorDirection(bool is_walking) {
+ int from;
+ bool dirType = false;
+ int dir;
+ bool shouldInterpolate;
+
+ if ((_vm->_version == 6) && _ignoreTurns)
+ return _facing;
+
+ dirType = (_vm->_version >= 7) ? _vm->_costumeLoader->hasManyDirections(_costume) : false;
+
+ from = toSimpleDir(dirType, _facing);
+ dir = remapDirection(_targetFacing, is_walking);
+
+ if (_vm->_version >= 7)
+ // Direction interpolation interfers with walk scripts in Dig; they perform
+ // (much better) interpolation themselves.
+ shouldInterpolate = false;
+ else
+ shouldInterpolate = (dir & 1024) ? true : false;
+ dir &= 1023;
+
+ if (shouldInterpolate) {
+ int to = toSimpleDir(dirType, dir);
+ int num = dirType ? 8 : 4;
+
+ // Turn left or right, depending on which is shorter.
+ int diff = to - from;
+ if (ABS(diff) > (num >> 1))
+ diff = -diff;
+
+ if (diff > 0) {
+ to = from + 1;
+ } else if (diff < 0){
+ to = from - 1;
+ }
+
+ dir = fromSimpleDir(dirType, (to + num) % num);
+ }
+
+ return dir;
+}
+
+void Actor::setBox(int box) {
+ _walkbox = box;
+ setupActorScale();
+}
+
+int Actor::actorWalkStep() {
+ int tmpX, tmpY;
+ Common::Point _actorPos;
+ int distX, distY;
+ int nextFacing;
+
+ _needRedraw = true;
+
+ nextFacing = updateActorDirection(true);
+ if (!(_moving & MF_IN_LEG) || _facing != nextFacing) {
+ if (_walkFrame != _frame || _facing != nextFacing) {
+ startWalkAnim(1, nextFacing);
+ }
+ _moving |= MF_IN_LEG;
+ }
+
+ _actorPos = _pos;
+
+ if (_walkbox != _walkdata.curbox && _vm->checkXYInBoxBounds(_walkdata.curbox, _actorPos.x, _actorPos.y)) {
+ setBox(_walkdata.curbox);
+ }
+
+ distX = ABS(_walkdata.next.x - _walkdata.cur.x);
+ distY = ABS(_walkdata.next.y - _walkdata.cur.y);
+
+ if (ABS(_actorPos.x - _walkdata.cur.x) >= distX && ABS(_actorPos.y - _walkdata.cur.y) >= distY) {
+ _moving &= ~MF_IN_LEG;
+ return 0;
+ }
+
+ tmpX = (_actorPos.x << 16) + _walkdata.xfrac + (_walkdata.deltaXFactor >> 8) * _scalex;
+ _walkdata.xfrac = (uint16)tmpX;
+ _actorPos.x = (tmpX >> 16);
+
+ tmpY = (_actorPos.y << 16) + _walkdata.yfrac + (_walkdata.deltaYFactor >> 8) * _scaley;
+ _walkdata.yfrac = (uint16)tmpY;
+ _actorPos.y = (tmpY >> 16);
+
+ if (ABS(_actorPos.x - _walkdata.cur.x) > distX) {
+ _actorPos.x = _walkdata.next.x;
+ }
+
+ if (ABS(_actorPos.y - _walkdata.cur.y) > distY) {
+ _actorPos.y = _walkdata.next.y;
+ }
+
+ _pos = _actorPos;
+ return 1;
+}
+
+
+void Actor::setupActorScale() {
+
+ if (_vm->_features & GF_NO_SCALING) {
+ _scalex = 0xFF;
+ _scaley = 0xFF;
+ return;
+ }
+
+ if (_ignoreBoxes)
+ return;
+
+ // For some boxes, we ignore the scaling and use whatever values the
+ // scripts set. This is used e.g. in the Mystery Vortex in Sam&Max.
+ // Older games used the flag 0x20 differently, though.
+ if (_vm->_gameId == GID_SAMNMAX && (_vm->getBoxFlags(_walkbox) & kBoxIgnoreScale))
+ return;
+
+ _boxscale = _vm->getBoxScale(_walkbox);
+
+ uint16 scale = _vm->getScale(_walkbox, _pos.x, _pos.y);
+ assert(scale <= 0xFF);
+
+ _scalex = _scaley = (byte)scale;
+}
+
+void Actor::startAnimActor(int f) {
+ if (_vm->_version >= 7 && !((_vm->_gameId == GID_FT) && (_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+ switch (f) {
+ case 1001:
+ f = _initFrame;
+ break;
+ case 1002:
+ f = _walkFrame;
+ break;
+ case 1003:
+ f = _standFrame;
+ break;
+ case 1004:
+ f = _talkStartFrame;
+ break;
+ case 1005:
+ f = _talkStopFrame;
+ break;
+ }
+
+ if (_costume != 0) {
+ _animProgress = 0;
+ _needRedraw = true;
+ if (f == _initFrame)
+ _cost.reset();
+ _vm->_costumeLoader->costumeDecodeData(this, f, (uint) - 1);
+ _frame = f;
+ }
+ } else {
+ switch (f) {
+ case 0x38:
+ f = _initFrame;
+ break;
+ case 0x39:
+ f = _walkFrame;
+ break;
+ case 0x3A:
+ f = _standFrame;
+ break;
+ case 0x3B:
+ f = _talkStartFrame;
+ break;
+ case 0x3C:
+ f = _talkStopFrame;
+ break;
+ }
+
+ assert(f != 0x3E);
+
+ if (isInCurrentRoom() && _costume != 0) {
+ _animProgress = 0;
+ _cost.animCounter = 0;
+ _needRedraw = true;
+ // V1 - V2 games don't seem to need a _cost.reset() at this point.
+ // Causes Zak to lose his body in several scenes, see bug #771508
+ if (_vm->_version >= 3 && f == _initFrame) {
+ _cost.reset();
+ _auxBlock.reset();
+ }
+ _vm->_costumeLoader->costumeDecodeData(this, f, (uint) - 1);
+ _frame = f;
+ }
+ }
+}
+
+void Actor::animateActor(int anim) {
+ int cmd, dir;
+
+ if (_vm->_version >= 7 && !((_vm->_gameId == GID_FT) && (_vm->_features & GF_DEMO) && (_vm->_platform == Common::kPlatformPC))) {
+
+ if (anim == 0xFF)
+ anim = 2000;
+
+ cmd = anim / 1000;
+ dir = anim % 1000;
+
+ } else {
+
+ cmd = anim / 4;
+ dir = oldDirToNewDir(anim % 4);
+
+ // Convert into old cmd code
+ cmd = 0x3F - cmd + 2;
+
+ }
+
+ switch (cmd) {
+ case 2: // stop walking
+ startAnimActor(_standFrame);
+ stopActorMoving();
+ break;
+ case 3: // change direction immediatly
+ _moving &= ~MF_TURN;
+ setDirection(dir);
+ break;
+ case 4: // turn to new direction
+ turnToDirection(dir);
+ break;
+ default:
+ if (_vm->_version <= 2)
+ startAnimActor(anim / 4);
+ else
+ startAnimActor(anim);
+ }
+}
+
+void Actor::setDirection(int direction) {
+ uint aMask;
+ int i;
+ uint16 vald;
+
+ // Do nothing if actor is already facing in the given direction
+ if (_facing == direction)
+ return;
+
+ // Normalize the angle
+ _facing = normalizeAngle(direction);
+
+ // If there is no costume set for this actor, we are finished
+ if (_costume == 0)
+ return;
+
+ // Update the costume for the new direction (and mark the actor for redraw)
+ aMask = 0x8000;
+ for (i = 0; i < 16; i++, aMask >>= 1) {
+ vald = _cost.frame[i];
+ if (vald == 0xFFFF)
+ continue;
+ _vm->_costumeLoader->costumeDecodeData(this, vald, (_vm->_version <= 2) ? 0xFFFF : aMask);
+ }
+
+ _needRedraw = true;
+}
+
+void Actor::drawActorToBackBuf(int x, int y) {
+ int curTop = _top;
+ int curBottom = _bottom;
+
+ _pos.x = x;
+ _pos.y = y;
+
+ _drawToBackBuf = true;
+ _needRedraw = true;
+ drawActorCostume();
+
+ _drawToBackBuf = false;
+ _needRedraw = true;
+ drawActorCostume();
+ _needRedraw = false;
+
+ if (_top > curTop)
+ _top = curTop;
+ if (_bottom < curBottom)
+ _bottom = curBottom;
+}
+
+
+void Actor::putActor(int dstX, int dstY, byte newRoom) {
+ if (_visible && _vm->_currentRoom != newRoom && _vm->getTalkingActor() == _number) {
+ _vm->stopTalk();
+ }
+
+ // WORKAROUND: The green transparency of the tank in the Hall of Oddities is
+ // is positioned one pixel too far to the left. This appears to be a
+ // bug in the original game as well.
+ if (_vm->_gameId == GID_SAMNMAX && newRoom == 16 && _number == 5 && dstX == 235 && dstY == 236)
+ dstX++;
+
+ _pos.x = dstX;
+ _pos.y = dstY;
+ _room = newRoom;
+ _needRedraw = true;
+
+ if (_vm->VAR(_vm->VAR_EGO) == _number) {
+ _vm->_egoPositioned = true;
+ }
+
+ if (_visible) {
+ if (isInCurrentRoom()) {
+ if (_moving) {
+ stopActorMoving();
+ startAnimActor(_standFrame);
+ }
+ adjustActorPos();
+ } else {
+#ifndef DISABLE_HE
+ if (_vm->_heversion >= 71)
+ ((ScummEngine_v71he *)_vm)->queueAuxBlock(this);
+#endif
+ hideActor();
+ }
+ } else {
+ if (isInCurrentRoom())
+ showActor();
+ }
+}
+
+int Actor::getActorXYPos(int &xPos, int &yPos) const {
+ if (!isInCurrentRoom())
+ return -1;
+
+ xPos = _pos.x;
+ yPos = _pos.y;
+ return 0;
+}
+
+AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY) {
+ const uint thresholdTable[] = { 30, 80, 0 };
+ AdjustBoxResult abr;
+ int16 tmpX, tmpY;
+ int tmpDist, bestDist, threshold, numBoxes;
+ byte flags, bestBox;
+ int box;
+ const int firstValidBox = (_vm->_features & GF_SMALL_HEADER) ? 0 : 1;
+
+ abr.x = dstX;
+ abr.y = dstY;
+ abr.box = kInvalidBox;
+
+ if (_ignoreBoxes)
+ return abr;
+
+ for (int tIdx = 0; tIdx < ARRAYSIZE(thresholdTable); tIdx++) {
+ threshold = thresholdTable[tIdx];
+
+ numBoxes = _vm->getNumBoxes() - 1;
+ if (numBoxes < firstValidBox)
+ return abr;
+
+ bestDist = (_vm->_version >= 7) ? 0x7FFFFFFF : 0xFFFF;
+ if (_vm->_version <= 2)
+ bestDist *= 8*2; // Adjust for the fact that we multiply x by 8 and y by 2
+ bestBox = kInvalidBox;
+
+ // We iterate (backwards) over all boxes, searching the one closest
+ // to the desired coordinates.
+ for (box = numBoxes; box >= firstValidBox; box--) {
+ flags = _vm->getBoxFlags(box);
+
+ // Skip over invisible boxes
+ if (flags & kBoxInvisible && !(flags & kBoxPlayerOnly && !isPlayer()))
+ continue;
+
+ // For increased performance, we perform a quick test if
+ // the coordinates can even be within a distance of 'threshold'
+ // pixels of the box.
+ if (threshold > 0 && _vm->inBoxQuickReject(box, dstX, dstY, threshold))
+ continue;
+
+ // Check if the point is contained in the box. If it is,
+ // we don't have to search anymore.
+ if (_vm->checkXYInBoxBounds(box, dstX, dstY)) {
+ abr.x = dstX;
+ abr.y = dstY;
+ abr.box = box;
+ return abr;
+ }
+
+ // Find the point in the box which is closest to our point.
+ tmpDist = _vm->getClosestPtOnBox(box, dstX, dstY, tmpX, tmpY);
+
+ // Check if the box is closer than the previous boxes.
+ if (tmpDist < bestDist) {
+ abr.x = tmpX;
+ abr.y = tmpY;
+
+ if (tmpDist == 0) {
+ abr.box = box;
+ return abr;
+ }
+ bestDist = tmpDist;
+ bestBox = box;
+ }
+ }
+
+ // If the closest ('best') box we found is within the threshold, or if
+ // we are on the last run (i.e. threshold == 0), return that box.
+ if (threshold == 0 || threshold * threshold >= bestDist) {
+ abr.box = bestBox;
+ return abr;
+ }
+ }
+
+ return abr;
+}
+
+void Actor::adjustActorPos() {
+ AdjustBoxResult abr;
+
+ abr = adjustXYToBeInBox(_pos.x, _pos.y);
+
+ _pos.x = abr.x;
+ _pos.y = abr.y;
+ _walkdata.destbox = abr.box;
+
+ setBox(abr.box);
+
+ _walkdata.dest.x = -1;
+
+ stopActorMoving();
+ _cost.soundCounter = 0;
+
+ if (_walkbox != kInvalidBox) {
+ byte flags = _vm->getBoxFlags(_walkbox);
+ if (flags & 7) {
+ turnToDirection(_facing);
+ }
+ }
+}
+
+void Actor::faceToObject(int obj) {
+ int x2, y2, dir;
+
+ if (!isInCurrentRoom())
+ return;
+
+ if (_vm->getObjectOrActorXY(obj, x2, y2) == -1)
+ return;
+
+ dir = (x2 > _pos.x) ? 90 : 270;
+ turnToDirection(dir);
+}
+
+void Actor::turnToDirection(int newdir) {
+ if (newdir == -1 || _ignoreTurns)
+ return;
+
+ _moving &= ~MF_TURN;
+
+ if (newdir != _facing) {
+ if (_vm->_version <= 3)
+ _moving = MF_TURN;
+ else
+ _moving |= MF_TURN;
+ _targetFacing = newdir;
+ }
+}
+
+void Actor::hideActor() {
+ if (!_visible)
+ return;
+
+ if (_moving) {
+ stopActorMoving();
+ startAnimActor(_standFrame);
+ }
+ _visible = false;
+ _cost.soundCounter = 0;
+ _needRedraw = false;
+ _needBgReset = true;
+ _auxBlock.reset();
+}
+
+void Actor::showActor() {
+ if (_vm->_currentRoom == 0 || _visible)
+ return;
+
+ adjustActorPos();
+
+ _vm->ensureResourceLoaded(rtCostume, _costume);
+
+ if (_costumeNeedsInit) {
+ startAnimActor(_initFrame);
+ if (_vm->_version <= 2) {
+ startAnimActor(_standFrame);
+ startAnimActor(_talkStopFrame);
+ }
+ _costumeNeedsInit = false;
+ }
+
+ // FIXME: Evil hack to work around bug #770717
+ if (!_moving && _vm->_version <= 2)
+ startAnimActor(_standFrame);
+
+ stopActorMoving();
+ _visible = true;
+ _needRedraw = true;
+}
+
+// V1 Maniac doesn't have a ScummVar for VAR_TALK_ACTOR, and just uses
+// an internal variable. Emulate this to prevent overwriting script vars...
+// Maniac NES (V1), however, DOES have a ScummVar for VAR_TALK_ACTOR
+int ScummEngine::getTalkingActor() {
+ if (_gameId == GID_MANIAC && _version == 1 && !(_platform == Common::kPlatformNES))
+ return _V1TalkingActor;
+ else
+ return VAR(VAR_TALK_ACTOR);
+}
+
+void ScummEngine::setTalkingActor(int value) {
+ if (_gameId == GID_MANIAC && _version == 1 && !(_platform == Common::kPlatformNES))
+ _V1TalkingActor = value;
+ else
+ VAR(VAR_TALK_ACTOR) = value;
+}
+
+void ScummEngine::putActors() {
+ Actor *a;
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ a = &_actors[i];
+ if (a && a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ }
+}
+
+static const int c64MMActorTalkColor[25] = {
+ 1, 7, 2, 14, 8, 15, 3, 7, 7, 15, 1, 13, 1, 4, 5, 5, 4, 3, 1, 5, 1, 1, 1, 1, 7
+};
+static const int v1MMActorTalkColor[25] = {
+ 1, 7, 2, 14, 8, 1, 3, 7, 7, 12, 1, 13, 1, 4, 5, 5, 4, 3, 1, 5, 1, 1, 1, 7, 7
+};
+
+void ScummEngine::setupV1ActorTalkColor() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_platform == Common::kPlatformC64) {
+ _actors[i]._talkColor = c64MMActorTalkColor[i];
+ } else {
+ _actors[i]._talkColor = v1MMActorTalkColor[i];
+ }
+ }
+}
+
+void ScummEngine::showActors() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom())
+ _actors[i].showActor();
+ }
+}
+
+void ScummEngine::walkActors() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom())
+ if (_version <= 3)
+ _actors[i].walkActorOld();
+ else
+ _actors[i].walkActor();
+ }
+}
+
+/* Used in Scumm v5 only. Play sounds associated with actors */
+void ScummEngine::playActorSounds() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i]._cost.soundCounter && _actors[i].isInCurrentRoom() && _actors[i]._sound) {
+ _currentScript = 0xFF;
+ _sound->addSoundToQueue(_actors[i]._sound[0]);
+ for (i = 1; i < _numActors; i++) {
+ _actors[i]._cost.soundCounter = 0;
+ }
+ return;
+ }
+ }
+}
+
+bool ScummEngine::isValidActor(int id) const {
+ return id >= 0 && id < _numActors && _actors[id]._number == id;
+}
+
+Actor *ScummEngine::derefActor(int id, const char *errmsg) const {
+ if (id == 0)
+ debugC(DEBUG_ACTORS, "derefActor(0, \"%s\") in script %d, opcode 0x%x",
+ errmsg, vm.slot[_currentScript].number, _opcode);
+
+ if (!isValidActor(id)) {
+ if (errmsg)
+ error("Invalid actor %d in %s", id, errmsg);
+ else
+ error("Invalid actor %d", id);
+ }
+ return &_actors[id];
+}
+
+Actor *ScummEngine::derefActorSafe(int id, const char *errmsg) const {
+ if (id == 0)
+ debugC(DEBUG_ACTORS, "derefActorSafe(0, \"%s\") in script %d, opcode 0x%x",
+ errmsg, vm.slot[_currentScript].number, _opcode);
+
+ if (!isValidActor(id)) {
+ debugC(DEBUG_ACTORS, "Invalid actor %d in %s (script %d, opcode 0x%x)",
+ id, errmsg, vm.slot[_currentScript].number, _opcode);
+ return NULL;
+ }
+ return &_actors[id];
+}
+
+void ScummEngine::processActors() {
+ int numactors = 0;
+
+ // Make a list of all actors in this room
+ for (int i = 1; i < _numActors; i++) {
+ if (_version == 8 && _actors[i]._layer < 0)
+ continue;
+ if (_actors[i].isInCurrentRoom()) {
+ _sortedActors[numactors++] = &_actors[i];
+ }
+ }
+ if (!numactors) {
+ return;
+ }
+
+ // Sort actors by position before drawing them (to ensure that actors
+ // in front are drawn after those "behind" them).
+ //
+ // Note: This algorithm works exactly the way the original engine did.
+ // Please resist any urge to 'optimize' this. Many of the games rely on
+ // the quirks of this particular sorting algorithm, and since we are
+ // dealing with far less than 100 objects being sorted here, any
+ // 'optimization' wouldn't yield a useful gain anyway.
+ //
+ // In particular, changing this loop caused a number of bugs in the
+ // past, including bugs #758167, #775097, and #1093867.
+ //
+ // Note that Sam & Max uses a stable sorting method. Older games don't
+ // and, according to cyx, neither do newer ones. At least not FT and
+ // COMI. See bug #1220168 for more details.
+
+ if (_gameId == GID_SAMNMAX) {
+ for (int j = 0; j < numactors; ++j) {
+ for (int i = 0; i < numactors; ++i) {
+ int sc_actor1 = _sortedActors[j]->_pos.y;
+ int sc_actor2 = _sortedActors[i]->_pos.y;
+ if (sc_actor1 == sc_actor2) {
+ sc_actor1 += _sortedActors[j]->_number;
+ sc_actor2 += _sortedActors[i]->_number;
+ }
+ if (sc_actor1 < sc_actor2) {
+ SWAP(_sortedActors[i], _sortedActors[j]);
+ }
+ }
+ }
+ } else {
+ for (int j = 0; j < numactors; ++j) {
+ for (int i = 0; i < numactors; ++i) {
+ int sc_actor1 = _sortedActors[j]->_pos.y - _sortedActors[j]->_layer * 2000;
+ int sc_actor2 = _sortedActors[i]->_pos.y - _sortedActors[i]->_layer * 2000;
+ if (sc_actor1 < sc_actor2) {
+ SWAP(_sortedActors[i], _sortedActors[j]);
+ }
+ }
+ }
+ }
+
+ // Finally draw the now sorted actors
+ Actor** end = _sortedActors + numactors;
+ for (Actor** ac = _sortedActors; ac != end; ++ac) {
+ Actor* a = *ac;
+ // Draw and animate the actors, except those w/o a costume.
+ // Note: We could 'optimize' this a little bit by only putting
+ // actors with a costume into the _sortedActors array in the
+ // first place. However, that would mess up the sorting, and
+ // would hence cause regressions. See also the other big
+ // comment further up in this method for some details.
+ if (a->_costume) {
+ a->drawActorCostume();
+ a->animateCostume();
+ }
+ }
+
+ if (_features & GF_NEW_COSTUMES)
+ akos_processQueue();
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v71he::processActors() {
+ preProcessAuxQueue();
+
+ if (!_skipProcessActors)
+ ScummEngine::processActors();
+
+ _fullRedraw = false;
+
+ postProcessAuxQueue();
+}
+
+void ScummEngine_v90he::processActors() {
+ preProcessAuxQueue();
+
+ _sprite->setRedrawFlags(false);
+ _sprite->processImages(true);
+
+ if (!_skipProcessActors)
+ ScummEngine::processActors();
+
+ _fullRedraw = false;
+
+ postProcessAuxQueue();
+
+ _sprite->setRedrawFlags(true);
+ _sprite->processImages(false);
+}
+#endif
+
+// Used in Scumm v8, to allow the verb coin to be drawn over the inventory
+// chest. I'm assuming that draw order won't matter here.
+void ScummEngine::processUpperActors() {
+ int i;
+
+ for (i = 1; i < _numActors; i++) {
+ if (_actors[i].isInCurrentRoom() && _actors[i]._costume && _actors[i]._layer < 0) {
+ CHECK_HEAP
+ _actors[i].drawActorCostume();
+ CHECK_HEAP
+ _actors[i].animateCostume();
+ }
+ }
+}
+
+void Actor::drawActorCostume(bool hitTestMode) {
+ if (_costume == 0)
+ return;
+
+ if (!hitTestMode) {
+ if (!_needRedraw)
+ return;
+
+ _needRedraw = false;
+ }
+
+ setupActorScale();
+
+ BaseCostumeRenderer* bcr = _vm->_costumeRenderer;
+
+ bcr->_actorID = _number;
+
+ bcr->_actorX = _pos.x + _offsX - _vm->virtscr[0].xstart;
+ bcr->_actorY = _pos.y + _offsY - _elevation;
+
+ if (_vm->_platform == Common::kPlatformNES) {
+ // In the NES version, when the actor is facing right,
+ // we need to shift it 8 pixels to the left
+ if (_facing == 90)
+ bcr->_actorX -= 8;
+ } else if (_vm->_version <= 2) {
+ // HACK: We have to adjust the x position by one strip (8 pixels) in
+ // V2 games. However, it is not quite clear to me why. And to fully
+ // match the original, it seems we have to offset by 2 strips if the
+ // actor is facing left (270 degree).
+ // V1 games are once again slightly different, here we only have
+ // to adjust the 270 degree case...
+ if (_facing == 270)
+ bcr->_actorX += 16;
+ else if (_vm->_version == 2)
+ bcr->_actorX += 8;
+ }
+
+ bcr->_clipOverride = _clipOverride;
+
+ if (_vm->_version == 4 && _boxscale & 0x8000) {
+ bcr->_scaleX = bcr->_scaleY = _vm->getScaleFromSlot((_boxscale & 0x7fff) + 1, _pos.x, _pos.y);
+ } else {
+ bcr->_scaleX = _scalex;
+ bcr->_scaleY = _scaley;
+ }
+
+ bcr->_shadow_mode = _shadowMode;
+ if (_vm->_version >= 5 && _vm->_heversion == 0) {
+ bcr->_shadow_table = _vm->_shadowPalette;
+ } else if (_vm->_heversion == 70) {
+ bcr->_shadow_table = _vm->_HEV7ActorPalette;
+ }
+
+ bcr->setCostume(_costume, _heXmapNum);
+ bcr->setPalette(_palette);
+ bcr->setFacing(this);
+
+ if (_vm->_version >= 7) {
+
+ bcr->_zbuf = _forceClip;
+ if (bcr->_zbuf == 100) {
+ bcr->_zbuf = _vm->getMaskFromBox(_walkbox);
+ if (bcr->_zbuf > _vm->gdi._numZBuffer-1)
+ bcr->_zbuf = _vm->gdi._numZBuffer-1;
+ }
+
+ } else {
+ if (_forceClip)
+ bcr->_zbuf = _forceClip;
+ else if (isInClass(kObjectClassNeverClip))
+ bcr->_zbuf = 0;
+ else {
+ bcr->_zbuf = _vm->getMaskFromBox(_walkbox);
+ if (bcr->_zbuf > _vm->gdi._numZBuffer-1)
+ bcr->_zbuf = _vm->gdi._numZBuffer-1;
+ }
+
+ }
+
+ bcr->_draw_top = 0x7fffffff;
+ bcr->_draw_bottom = 0;
+
+ bcr->_skipLimbs = (_heSkipLimbs != 0);
+ bcr->_paletteNum = _hePaletteNum;
+
+ if (_vm->_heversion >= 80 && _heNoTalkAnimation == 0 && _animProgress == 0) {
+ if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
+ int talkState = 0;
+
+ if (_vm->_sound->isSoundCodeUsed(1))
+ talkState = _vm->_sound->getSoundVar(1, 19);
+ if (talkState == 0)
+ talkState = _vm->_rnd.getRandomNumberRng(1, 10);
+
+ checkRange(13, 1, talkState, "Talk state %d out of range");
+ setTalkCondition(talkState);
+ } else {
+ setTalkCondition(1);
+ }
+ }
+ _heNoTalkAnimation = 0;
+
+ // If the actor is partially hidden, redraw it next frame.
+ // Only done for pre-AKOS, though.
+ if (bcr->drawCostume(_vm->virtscr[0], _vm->gdi._numStrips, this, _drawToBackBuf) & 1) {
+ _needRedraw = (_vm->_version <= 6);
+ }
+
+ if (!hitTestMode) {
+ // Record the vertical extent of the drawn actor
+ _top = bcr->_draw_top;
+ _bottom = bcr->_draw_bottom;
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+bool Actor::actorHitTest(int x, int y) {
+ AkosRenderer *ar = (AkosRenderer *)_vm->_costumeRenderer;
+
+ ar->_actorHitX = x;
+ ar->_actorHitY = y;
+ ar->_actorHitMode = true;
+ ar->_actorHitResult = false;
+
+ drawActorCostume(true);
+
+ ar->_actorHitMode = false;
+
+ return ar->_actorHitResult;
+}
+#endif
+
+void Actor::animateCostume() {
+ if (_costume == 0)
+ return;
+
+ _animProgress++;
+ if (_animProgress >= _animSpeed) {
+ _animProgress = 0;
+
+ _vm->_costumeLoader->loadCostume(_costume);
+ if (_vm->_costumeLoader->increaseAnims(this)) {
+ _needRedraw = true;
+ }
+ }
+}
+
+#ifndef DISABLE_SCUMM_7_8
+void Actor::animateLimb(int limb, int f) {
+ // This methods is very similiar to animateCostume().
+ // However, instead of animating *all* the limbs, it only animates
+ // the specified limb to be at the frame specified by "f".
+
+ if (!f)
+ return;
+
+ _animProgress++;
+ if (_animProgress >= _animSpeed) {
+ _animProgress = 0;
+
+ if (_costume == 0)
+ return;
+
+ const byte *aksq, *akfo;
+ uint size;
+ byte *akos = _vm->getResourceAddress(rtCostume, _costume);
+ assert(akos);
+
+ aksq = _vm->findResourceData(MKID('AKSQ'), akos);
+ akfo = _vm->findResourceData(MKID('AKFO'), akos);
+
+ size = _vm->getResourceDataSize(akfo) / 2;
+
+ while (f--) {
+ if (_cost.active[limb] != 0)
+ _vm->akos_increaseAnim(this, limb, aksq, (const uint16 *)akfo, size);
+ }
+
+// _needRedraw = true;
+// _needBgReset = true;
+ }
+}
+#endif
+
+void ScummEngine::redrawAllActors() {
+ int j;
+
+ for (j = 1; j < _numActors; j++) {
+ _actors[j]._needRedraw = true;
+ _actors[j]._needBgReset = true;
+ }
+}
+
+void ScummEngine::setActorRedrawFlags() {
+ int i, j;
+
+ // Redraw all actors if a full redraw was requested.
+ // Also redraw all actors in COMI (see bug #1066329 for details).
+ if (_fullRedraw || _version == 8 || (VAR_REDRAW_ALL_ACTORS != 0xFF && VAR(VAR_REDRAW_ALL_ACTORS) != 0)) {
+ for (j = 1; j < _numActors; j++) {
+ _actors[j]._needRedraw = true;
+ }
+ } else {
+ for (i = 0; i < gdi._numStrips; i++) {
+ int strip = _screenStartStrip + i;
+ if (testGfxAnyUsageBits(strip)) {
+ for (j = 1; j < _numActors; j++) {
+ if (testGfxUsageBit(strip, j) && testGfxOtherUsageBits(strip, j)) {
+ _actors[j]._needRedraw = true;
+ }
+ }
+ }
+ }
+ }
+}
+
+void ScummEngine::resetActorBgs() {
+ int i, j;
+
+ for (i = 0; i < gdi._numStrips; i++) {
+ int strip = _screenStartStrip + i;
+ clearGfxUsageBit(strip, USAGE_BIT_DIRTY);
+ clearGfxUsageBit(strip, USAGE_BIT_RESTORED);
+ for (j = 1; j < _numActors; j++) {
+ if (testGfxUsageBit(strip, j) &&
+ ((_actors[j]._top != 0x7fffffff && _actors[j]._needRedraw) || _actors[j]._needBgReset)) {
+ clearGfxUsageBit(strip, j);
+ if ((_actors[j]._bottom - _actors[j]._top) >= 0)
+ gdi.resetBackground(_actors[j]._top, _actors[j]._bottom, i);
+ }
+ }
+ }
+
+ for (i = 1; i < _numActors; i++) {
+ _actors[i]._needBgReset = false;
+ }
+}
+
+int ScummEngine::getActorFromPos(int x, int y) {
+ int i;
+
+ if (!testGfxAnyUsageBits(x / 8))
+ return 0;
+
+ for (i = 1; i < _numActors; i++) {
+ if (testGfxUsageBit(x / 8, i) && !getClass(i, kObjectClassUntouchable)
+ && y >= _actors[i]._top && y <= _actors[i]._bottom) {
+ if (_version > 2 || i != VAR(VAR_EGO))
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+#ifndef DISABLE_HE
+int ScummEngine_v70he::getActorFromPos(int x, int y) {
+ int curActor, i;
+
+ if (!testGfxAnyUsageBits(x / 8))
+ return 0;
+
+ curActor = 0;
+ for (i = 1; i < _numActors; i++) {
+ if (testGfxUsageBit(x / 8, i) && !getClass(i, kObjectClassUntouchable)
+ && y >= _actors[i]._top && y <= _actors[i]._bottom
+ && (_actors[i]._pos.y > _actors[curActor]._pos.y || curActor == 0))
+ curActor = i;
+ }
+
+ return curActor;
+}
+#endif
+
+#ifndef DISABLE_SCUMM_7_8
+void ScummEngine_v7::actorTalk(const byte *msg) {
+ Actor *a;
+
+ convertMessageToString(msg, _charsetBuffer, sizeof(_charsetBuffer));
+
+ // Play associated speech, if any
+ playSpeech((byte *)_lastStringTag);
+
+ if ((_version == 7 && !_keepText) || (_version == 8 && VAR(VAR_HAVE_MSG))) {
+ stopTalk();
+ }
+ if (_actorToPrintStrFor == 0xFF) {
+ setTalkingActor(0xFF);
+ } else {
+ a = derefActor(_actorToPrintStrFor, "actorTalk");
+ setTalkingActor(a->_number);
+ if (!_string[0].no_talk_anim) {
+ a->runActorTalkScript(a->_talkStartFrame);
+ _useTalkAnims = true;
+ }
+ }
+
+ if (getTalkingActor() > 0x7F) {
+ _charsetColor = (byte)_string[0].color;
+ } else {
+ a = derefActor(getTalkingActor(), "actorTalk(2)");
+ _charsetColor = a->_talkColor;
+ }
+ _charsetBufPos = 0;
+ _talkDelay = 0;
+ _haveMsg = 1;
+ if (_version == 7)
+ VAR(VAR_HAVE_MSG) = 0xFF;
+ _haveActorSpeechMsg = true;
+ CHARSET_1();
+ if (_version == 8)
+ VAR(VAR_HAVE_MSG) = (_string[0].no_talk_anim) ? 2 : 1;
+}
+#endif
+
+void ScummEngine::actorTalk(const byte *msg) {
+ Actor *a;
+
+ convertMessageToString(msg, _charsetBuffer, sizeof(_charsetBuffer));
+
+ // FIXME: Workaround for bugs #770039 and #770049
+ if (_gameId == GID_LOOM) {
+ if (!*_charsetBuffer)
+ return;
+ }
+
+ if (_actorToPrintStrFor == 0xFF) {
+ if (!_keepText) {
+ stopTalk();
+ }
+ setTalkingActor(0xFF);
+ } else {
+ int oldact;
+
+ // WORKAROUND bug #770724
+ if (_gameId == GID_LOOM && _roomResource == 23 &&
+ vm.slot[_currentScript].number == 232 && _actorToPrintStrFor == 0) {
+ _actorToPrintStrFor = 2; // Could be anything from 2 to 5. Maybe compare to original?
+ }
+
+ a = derefActor(_actorToPrintStrFor, "actorTalk");
+ if (!a->isInCurrentRoom()) {
+ oldact = 0xFF;
+ } else {
+ if (!_keepText) {
+ stopTalk();
+ }
+ setTalkingActor(a->_number);
+ a->_heTalking = true;
+ if (!_string[0].no_talk_anim) {
+ a->runActorTalkScript(a->_talkStartFrame);
+ _useTalkAnims = true;
+ }
+ oldact = getTalkingActor();
+ }
+ if (oldact >= 0x80)
+ return;
+ }
+
+ if (_heversion >= 72 || getTalkingActor() > 0x7F) {
+ _charsetColor = (byte)_string[0].color;
+ } else if (_platform == Common::kPlatformNES) {
+ if (_NES_lastTalkingActor != getTalkingActor())
+ _NES_talkColor ^= 1;
+ _NES_lastTalkingActor = getTalkingActor();
+ _charsetColor = _NES_talkColor;
+ } else {
+ a = derefActor(getTalkingActor(), "actorTalk(2)");
+ _charsetColor = a->_talkColor;
+ }
+ _charsetBufPos = 0;
+ _talkDelay = 0;
+ _haveMsg = 0xFF;
+ VAR(VAR_HAVE_MSG) = 0xFF;
+ if (VAR_CHARCOUNT != 0xFF)
+ VAR(VAR_CHARCOUNT) = 0;
+ _haveActorSpeechMsg = true;
+ CHARSET_1();
+}
+
+void Actor::runActorTalkScript(int f) {
+ if (_vm->_version == 8 && _vm->VAR(_vm->VAR_HAVE_MSG) == 2)
+ return;
+
+ if (_talkScript) {
+ int script = _talkScript;
+ int args[16];
+ memset(args, 0, sizeof(args));
+ args[1] = f;
+ args[0] = _number;
+
+ _vm->runScript(script, 1, 0, args);
+ } else {
+ if (_frame != f)
+ startAnimActor(f);
+ }
+}
+
+void ScummEngine::stopTalk() {
+ int act;
+
+ _sound->stopTalkSound();
+
+ _haveMsg = 0;
+ _talkDelay = 0;
+
+ act = getTalkingActor();
+ if (act && act < 0x80) {
+ Actor *a = derefActor(act, "stopTalk");
+ if ((_version >= 7 && !_string[0].no_talk_anim) ||
+ (_version <= 6 && a->isInCurrentRoom() && _useTalkAnims)) {
+ a->runActorTalkScript(a->_talkStopFrame);
+ _useTalkAnims = false;
+ }
+ if (_version <= 7 && _heversion == 0)
+ setTalkingActor(0xFF);
+ a->_heTalking = false;
+ }
+ if (_version == 8 || _heversion >= 60)
+ setTalkingActor(0);
+ if (_version == 8)
+ VAR(VAR_HAVE_MSG) = 0;
+ _keepText = false;
+ if (_version >= 7) {
+#ifndef DISABLE_SCUMM_7_8
+ ((ScummEngine_v7 *)this)->clearSubtitleQueue();
+#endif
+ } else {
+ _charset->restoreCharsetBg();
+ }
+}
+
+void Actor::setActorCostume(int c) {
+ int i;
+
+ if (_vm->_heversion >= 61 && (c == -1 || c == -2)) {
+ _heSkipLimbs = (c == -1);
+ _needRedraw = true;
+ return;
+ }
+
+ // Based on disassembly. It seems that high byte is not used at all, though
+ // it is attached to all horizontally flipped object, like left eye.
+ if (_vm->_heversion == 61)
+ c &= 0xff;
+
+ _costumeNeedsInit = true;
+
+ if (_vm->_features & GF_NEW_COSTUMES) {
+ memset(_animVariable, 0, sizeof(_animVariable));
+
+#ifndef DISABLE_HE
+ if (_vm->_heversion >= 71)
+ ((ScummEngine_v71he *)_vm)->queueAuxBlock(this);
+#endif
+
+ _costume = c;
+ _cost.reset();
+ _auxBlock.reset();
+
+ if (_visible) {
+ if (_costume) {
+ _vm->ensureResourceLoaded(rtCostume, _costume);
+ }
+ startAnimActor(_initFrame);
+ }
+ } else {
+ if (_visible) {
+ hideActor();
+ _cost.reset();
+ _costume = c;
+ showActor();
+ } else {
+ _costume = c;
+ _cost.reset();
+ }
+ }
+
+
+ // V1 zak uses palette[] as a dynamic costume color array.
+ if (_vm->_version == 1)
+ return;
+
+ if (_vm->_features & GF_NEW_COSTUMES) {
+ for (i = 0; i < 256; i++)
+ _palette[i] = 0xFF;
+ } else if (_vm->_features & GF_OLD_BUNDLE) {
+ for (i = 0; i < 16; i++)
+ _palette[i] = i;
+
+ // Make stuff more visible on CGA. Based on disassembly
+ if (_vm->_renderMode == Common::kRenderCGA && _vm->_version > 2) {
+ _palette[6] = 5;
+ _palette[7] = 15;
+ }
+ } else {
+ for (i = 0; i < 32; i++)
+ _palette[i] = 0xFF;
+ }
+
+ if (_vm->_heversion >= 71 && _vm->getTalkingActor() == _number) {
+ if (_vm->_heversion <= 95 || (_vm->_heversion >= 98 && _vm->VAR(_vm->VAR_SKIP_RESET_TALK_ACTOR) == 0)) {
+ //_vm->setTalkingActor(0);
+ }
+ }
+}
+
+void Actor::startWalkActor(int destX, int destY, int dir) {
+ AdjustBoxResult abr;
+
+ if (_vm->_version <= 3) {
+ abr.x = destX;
+ abr.y = destY;
+ } else {
+ abr = adjustXYToBeInBox(destX, destY);
+ }
+
+ if (!isInCurrentRoom()) {
+ _pos.x = abr.x;
+ _pos.y = abr.y;
+ if (!(_vm->_version == 6 && _ignoreTurns) && dir != -1)
+ setDirection(dir);
+ return;
+ }
+
+ if (_ignoreBoxes) {
+ abr.box = kInvalidBox;
+ _walkbox = kInvalidBox;
+ } else {
+ if (_vm->checkXYInBoxBounds(_walkdata.destbox, abr.x, abr.y)) {
+ abr.box = _walkdata.destbox;
+ } else {
+ abr = adjustXYToBeInBox(abr.x, abr.y);
+ }
+ if (_moving && _walkdata.destdir == dir && _walkdata.dest.x == abr.x && _walkdata.dest.y == abr.y)
+ return;
+ }
+
+ if (_pos.x == abr.x && _pos.y == abr.y) {
+ turnToDirection(dir);
+ return;
+ }
+
+ _walkdata.dest.x = abr.x;
+ _walkdata.dest.y = abr.y;
+ _walkdata.destbox = abr.box;
+ _walkdata.destdir = dir;
+ _moving = (_moving & MF_IN_LEG) | MF_NEW_LEG;
+ _walkdata.point3.x = 32000;
+
+ _walkdata.curbox = _walkbox;
+}
+
+void Actor::startWalkAnim(int cmd, int angle) {
+ if (angle == -1)
+ angle = _facing;
+
+ /* Note: walk scripts aren't required to make the Dig
+ * work as usual
+ */
+ if (_walkScript) {
+ int args[16];
+ memset(args, 0, sizeof(args));
+ args[0] = _number;
+ args[1] = cmd;
+ args[2] = angle;
+ _vm->runScript(_walkScript, 1, 0, args);
+ } else {
+ switch (cmd) {
+ case 1: /* start walk */
+ setDirection(angle);
+ startAnimActor(_walkFrame);
+ break;
+ case 2: /* change dir only */
+ setDirection(angle);
+ break;
+ case 3: /* stop walk */
+ turnToDirection(angle);
+ startAnimActor(_standFrame);
+ break;
+ }
+ }
+}
+
+void Actor::walkActor() {
+ int new_dir, next_box;
+ Common::Point foundPath;
+
+ if (_vm->_version >= 7) {
+ if (_moving & MF_FROZEN) {
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving &= ~MF_TURN;
+ }
+ return;
+ }
+ }
+
+ if (!_moving)
+ return;
+
+ if (!(_moving & MF_NEW_LEG)) {
+ if (_moving & MF_IN_LEG && actorWalkStep())
+ return;
+
+ if (_moving & MF_LAST_LEG) {
+ _moving = 0;
+ setBox(_walkdata.destbox);
+ startWalkAnim(3, _walkdata.destdir);
+ return;
+ }
+
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving = 0;
+ return;
+ }
+
+ setBox(_walkdata.curbox);
+ _moving &= MF_IN_LEG;
+ }
+
+ _moving &= ~MF_NEW_LEG;
+ do {
+
+ if (_walkbox == kInvalidBox) {
+ setBox(_walkdata.destbox);
+ _walkdata.curbox = _walkdata.destbox;
+ break;
+ }
+
+ if (_walkbox == _walkdata.destbox)
+ break;
+
+ next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ if (next_box < 0) {
+ _walkdata.destbox = _walkbox;
+ _moving |= MF_LAST_LEG;
+ return;
+ }
+
+ _walkdata.curbox = next_box;
+
+ if (findPathTowards(_walkbox, next_box, _walkdata.destbox, foundPath))
+ break;
+
+ if (calcMovementFactor(foundPath))
+ return;
+
+ setBox(_walkdata.curbox);
+ } while (1);
+
+ _moving |= MF_LAST_LEG;
+ calcMovementFactor(_walkdata.dest);
+}
+
+/*
+void Actor::walkActorV12() {
+ Common::Point foundPath, tmp;
+ int new_dir, next_box;
+
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving = 0;
+ return;
+ }
+
+ if (!_moving)
+ return;
+
+ if (_moving & MF_IN_LEG) {
+ actorWalkStep();
+ } else {
+ if (_moving & MF_LAST_LEG) {
+ _moving = 0;
+ startWalkAnim(3, _walkdata.destdir);
+ } else {
+ setBox(_walkdata.curbox);
+ if (_walkbox == _walkdata.destbox) {
+ foundPath = _walkdata.dest;
+ _moving |= MF_LAST_LEG;
+ } else {
+ next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+ if (next_box < 0) {
+ _moving |= MF_LAST_LEG;
+ return;
+ }
+
+ // Can't walk through locked boxes
+ int flags = _vm->getBoxFlags(next_box);
+ if (flags & kBoxLocked && !(flags & kBoxPlayerOnly && !isPlayer())) {
+ _moving |= MF_LAST_LEG;
+ }
+
+ _walkdata.curbox = next_box;
+
+ _vm->getClosestPtOnBox(_walkdata.curbox, x, y, tmp.x, tmp.y);
+ _vm->getClosestPtOnBox(_walkbox, tmp.x, tmp.y, foundPath.x, foundPath.y);
+ }
+ calcMovementFactor(foundPath);
+ }
+ }
+}
+*/
+
+void Actor::walkActorOld() {
+ Common::Point p2, p3; // Gate locations
+ int new_dir, next_box;
+
+ if (!_moving)
+ return;
+
+ if (!(_moving & MF_NEW_LEG)) {
+ if (_moving & MF_IN_LEG && actorWalkStep())
+ return;
+
+ if (_moving & MF_LAST_LEG) {
+ _moving = 0;
+ startWalkAnim(3, _walkdata.destdir);
+ return;
+ }
+
+ if (_moving & MF_TURN) {
+ new_dir = updateActorDirection(false);
+ if (_facing != new_dir)
+ setDirection(new_dir);
+ else
+ _moving = 0;
+ return;
+ }
+
+ if (_walkdata.point3.x != 32000) {
+ if (calcMovementFactor(_walkdata.point3)) {
+ _walkdata.point3.x = 32000;
+ return;
+ }
+ _walkdata.point3.x = 32000;
+ }
+
+ setBox(_walkdata.curbox);
+ _moving &= MF_IN_LEG;
+ }
+
+ _moving &= ~MF_NEW_LEG;
+ do {
+ if (_walkbox == kInvalidBox) {
+ setBox(_walkdata.destbox);
+ _walkdata.curbox = _walkdata.destbox;
+ break;
+ }
+
+ if (_walkbox == _walkdata.destbox)
+ break;
+
+ next_box = _vm->getPathToDestBox(_walkbox, _walkdata.destbox);
+
+ // WORKAROUND: To fully fix bug #774783, we add a special case
+ // here, resulting in a different next_box value for Hitler.
+ if ((_vm->_gameId == GID_INDY3) && _vm->_roomResource == 46 && _walkbox == 1 && _walkdata.destbox == 0 && _number == 9)
+ next_box = 1;
+
+ if (next_box < 0) {
+ _moving |= MF_LAST_LEG;
+ return;
+ }
+
+ // Can't walk through locked boxes
+ int flags = _vm->getBoxFlags(next_box);
+ if (flags & kBoxLocked && !(flags & kBoxPlayerOnly && !isPlayer())) {
+ _moving |= MF_LAST_LEG;
+// FIXME: Work in progress
+// _walkdata.destdir = _facing;
+ return;
+ }
+
+ _walkdata.curbox = next_box;
+
+ if (_vm->_version <= 2) {
+ _vm->getClosestPtOnBox(_walkdata.curbox, _pos.x, _pos.y, p2.x, p2.y);
+ _vm->getClosestPtOnBox(_walkbox, p2.x, p2.y, p3.x, p3.y);
+// FIXME: Work in progress
+// calcMovementFactor(p3);
+// return;
+ } else {
+ findPathTowardsOld(_walkbox, next_box, _walkdata.destbox, p2, p3);
+ if (p2.x == 32000 && p3.x == 32000) {
+ break;
+ }
+
+ if (p2.x != 32000) {
+ if (calcMovementFactor(p2)) {
+ _walkdata.point3 = p3;
+ return;
+ }
+ }
+ }
+ if (calcMovementFactor(p3))
+ return;
+
+ setBox(_walkdata.destbox);
+ } while (1);
+
+ _moving |= MF_LAST_LEG;
+ calcMovementFactor(_walkdata.dest);
+}
+
+byte *Actor::getActorName() {
+ byte *ptr = _vm->getResourceAddress(rtActorName, _number);
+ if (ptr == NULL) {
+ debug(0, "Failed to find name of actor %d", _number);
+ }
+ return ptr;
+}
+
+int Actor::getAnimVar(byte var) const {
+ checkRange(26, 0, var, "getAnimVar %d out of range(r)");
+ return _animVariable[var];
+}
+
+void Actor::setAnimVar(byte var, int value) {
+ checkRange(26, 0, var, "setAnimVar %d out of range(r)");
+ _animVariable[var] = value;
+}
+
+void Actor::remapActorPaletteColor(int color, int new_color) {
+ const byte *akos, *akpl;
+ int akpl_size, i;
+ byte akpl_color;
+
+ akos = _vm->getResourceAddress(rtCostume, _costume);
+ if (!akos) {
+ debug(0, "Can't remap actor %d, costume %d not found", _number, _costume);
+ return;
+ }
+
+ akpl = _vm->findResourceData(MKID('AKPL'), akos);
+ if (!akpl) {
+ debug(0, "Can't remap actor %d, costume %d doesn't contain an AKPL block", _number, _costume);
+ return;
+ }
+
+ // Get the number palette entries
+ akpl_size = _vm->getResourceDataSize(akpl);
+
+ for (i = 0; i < akpl_size; i++) {
+ akpl_color = *akpl++;
+ if (akpl_color == color) {
+ _palette[i] = new_color;
+ return;
+ }
+ }
+}
+
+void Actor::remapActorPalette(int r_fact, int g_fact, int b_fact, int threshold) {
+ const byte *akos, *rgbs, *akpl;
+ int akpl_size, i;
+ int r, g, b;
+ byte akpl_color;
+
+ if (!isInCurrentRoom()) {
+ debugC(DEBUG_ACTORS, "Remap actor %d not in current room", _number);
+ return;
+ } else if (_costume < 1 || _costume >= _vm->_numCostumes - 1) {
+ debugC(DEBUG_ACTORS, "Remap actor %d invalid costume %d", _number, _costume);
+ return;
+ }
+
+ akos = _vm->getResourceAddress(rtCostume, _costume);
+ if (!akos) {
+ debug(0, "Can't remap actor %d, costume %d not found", _number, _costume);
+ return;
+ }
+
+ akpl = _vm->findResourceData(MKID('AKPL'), akos);
+ if (!akpl) {
+ debug(0, "Can't remap actor %d, costume %d doesn't contain an AKPL block", _number, _costume);
+ return;
+ }
+
+ // Get the number palette entries
+ akpl_size = _vm->getResourceDataSize(akpl);
+
+ rgbs = _vm->findResourceData(MKID('RGBS'), akos);
+
+ if (!rgbs) {
+ debugC(DEBUG_ACTORS, "Can't remap actor %d costume %d doesn't contain an RGB block", _number, _costume);
+ return;
+ }
+
+ for (i = 0; i < akpl_size; i++) {
+ r = *rgbs++;
+ g = *rgbs++;
+ b = *rgbs++;
+
+ akpl_color = *akpl++;
+
+ // allow remap of generic palette entry?
+ if (!_shadowMode || akpl_color >= 16) {
+ r = (r * r_fact) >> 8;
+ g = (g * g_fact) >> 8;
+ b = (b * b_fact) >> 8;
+ _palette[i] = _vm->remapPaletteColor(r, g, b, threshold);
+ }
+ }
+}
+
+void Actor::classChanged(int cls, bool value) {
+ if (cls == kObjectClassAlwaysClip)
+ _forceClip = value;
+ if (cls == kObjectClassIgnoreBoxes)
+ _ignoreBoxes = value;
+}
+
+bool Actor::isInClass(int cls) {
+ return _vm->getClass(_number, cls);
+}
+
+bool Actor::isPlayer() {
+ if (_vm->_version <= 2)
+ return _vm->VAR(42) <= _number && _number <= _vm->VAR(43);
+ else
+ return isInClass(kObjectClassPlayer);
+}
+
+void Actor::setUserCondition(int slot, int set) {
+ const int condMaskCode = (_vm->_heversion >= 90) ? 0x1FFF : 0x3FF;
+ checkRange(32, 1, slot, "Condition %d out of range");
+ if (set == 0) {
+ _heCondMask &= ~(1 << (slot + 0xF));
+ } else {
+ _heCondMask |= 1 << (slot + 0xF);
+ }
+ if (_heCondMask & condMaskCode) {
+ _heCondMask &= ~1;
+ } else {
+ _heCondMask |= 1;
+ }
+}
+
+bool Actor::isUserConditionSet(int slot) const {
+ checkRange(32, 1, slot, "Condition %d out of range");
+ return (_heCondMask & (1 << (slot + 0xF))) != 0;
+}
+
+void Actor::setTalkCondition(int slot) {
+ const int condMaskCode = (_vm->_heversion >= 90) ? 0x1FFF : 0x3FF;
+ checkRange(32, 1, slot, "Condition %d out of range");
+ _heCondMask = (_heCondMask & ~condMaskCode) | 1;
+ if (slot != 1) {
+ _heCondMask |= 1 << (slot - 1);
+ if (_heCondMask & condMaskCode) {
+ _heCondMask &= ~1;
+ } else {
+ _heCondMask |= 1;
+ }
+ }
+}
+
+bool Actor::isTalkConditionSet(int slot) const {
+ checkRange(32, 1, slot, "Condition %d out of range");
+ return (_heCondMask & (1 << (slot - 1))) != 0;
+}
+
+#ifndef DISABLE_HE
+void ScummEngine_v71he::preProcessAuxQueue() {
+ if (!_skipProcessActors) {
+ for (int i = 0; i < _auxBlocksNum; ++i) {
+ AuxBlock *ab = &_auxBlocks[i];
+ if (ab->r.top <= ab->r.bottom) {
+ gdi.copyVirtScreenBuffers(ab->r);
+ }
+ }
+ }
+ _auxBlocksNum = 0;
+}
+
+void ScummEngine_v71he::postProcessAuxQueue() {
+ if (!_skipProcessActors) {
+ for (int i = 0; i < _auxEntriesNum; ++i) {
+ AuxEntry *ae = &_auxEntries[i];
+ if (ae->actorNum != -1) {
+ Actor *a = derefActor(ae->actorNum, "postProcessAuxQueue");
+ const uint8 *cost = getResourceAddress(rtCostume, a->_costume);
+ int dy = a->_offsY + a->_pos.y - a->getElevation();
+ int dx = a->_offsX + a->_pos.x;
+
+ const uint8 *akax = findResource(MKID('AKAX'), cost);
+ assert(akax);
+ const uint8 *auxd = findPalInPals(akax, ae->subIndex) - _resourceHeaderSize;
+ assert(auxd);
+ const uint8 *frel = findResourceData(MKID('FREL'), auxd);
+ if (frel) {
+ error("unhandled FREL block");
+ }
+ const uint8 *disp = findResourceData(MKID('DISP'), auxd);
+ if (disp) {
+ error("unhandled DISP block");
+ }
+ const uint8 *axfd = findResourceData(MKID('AXFD'), auxd);
+ assert(axfd);
+
+ uint16 comp = READ_LE_UINT16(axfd);
+ if (comp != 0) {
+ int x = (int16)READ_LE_UINT16(axfd + 2) + dx;
+ int y = (int16)READ_LE_UINT16(axfd + 4) + dy;
+ int w = (int16)READ_LE_UINT16(axfd + 6);
+ int h = (int16)READ_LE_UINT16(axfd + 8);
+ VirtScreen *pvs = &virtscr[kMainVirtScreen];
+ uint8 *dst1 = pvs->getPixels(0, pvs->topline);
+ uint8 *dst2 = pvs->getBackPixels(0, pvs->topline);
+ switch (comp) {
+ case 1:
+ Wiz::copyAuxImage(dst1, dst2, axfd + 10, pvs->w, pvs->h, x, y, w, h);
+ break;
+ default:
+ error("unimplemented compression type %d", comp);
+ }
+ }
+ const uint8 *axur = findResourceData(MKID('AXUR'), auxd);
+ if (axur) {
+ uint16 n = READ_LE_UINT16(axur); axur += 2;
+ while (n--) {
+ int x1 = (int16)READ_LE_UINT16(axur + 0) + dx;
+ int y1 = (int16)READ_LE_UINT16(axur + 2) + dy;
+ int x2 = (int16)READ_LE_UINT16(axur + 4) + dx;
+ int y2 = (int16)READ_LE_UINT16(axur + 6) + dy;
+ markRectAsDirty(kMainVirtScreen, x1, x2, y1, y2 + 1);
+ axur += 8;
+ }
+ }
+ const uint8 *axer = findResourceData(MKID('AXER'), auxd);
+ if (axer) {
+ a->_auxBlock.visible = true;
+ a->_auxBlock.r.left = (int16)READ_LE_UINT16(axer + 0) + dx;
+ a->_auxBlock.r.top = (int16)READ_LE_UINT16(axer + 2) + dy;
+ a->_auxBlock.r.right = (int16)READ_LE_UINT16(axer + 4) + dx;
+ a->_auxBlock.r.bottom = (int16)READ_LE_UINT16(axer + 6) + dy;
+ }
+ }
+ }
+ }
+ _auxEntriesNum = 0;
+}
+
+void ScummEngine_v71he::queueAuxBlock(Actor *a) {
+ if (!a->_auxBlock.visible)
+ return;
+
+ assert(_auxBlocksNum < ARRAYSIZE(_auxBlocks));
+ _auxBlocks[_auxBlocksNum] = a->_auxBlock;
+ ++_auxBlocksNum;
+}
+
+void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) {
+ assert(_auxEntriesNum < ARRAYSIZE(_auxEntries));
+ AuxEntry *ae = &_auxEntries[_auxEntriesNum];
+ ae->actorNum = actorNum;
+ ae->subIndex = subIndex;
+ ++_auxEntriesNum;
+}
+#endif
+
+
+void Actor::saveLoadWithSerializer(Serializer *ser) {
+ static const SaveLoadEntry actorEntries[] = {
+ MKLINE(Actor, _pos.x, sleInt16, VER(8)),
+ MKLINE(Actor, _pos.y, sleInt16, VER(8)),
+ MKLINE(Actor, _offsX, sleInt16, VER(32)),
+ MKLINE(Actor, _offsY, sleInt16, VER(32)),
+ MKLINE(Actor, _top, sleInt16, VER(8)),
+ MKLINE(Actor, _bottom, sleInt16, VER(8)),
+ MKLINE(Actor, _elevation, sleInt16, VER(8)),
+ MKLINE(Actor, _width, sleUint16, VER(8)),
+ MKLINE(Actor, _facing, sleUint16, VER(8)),
+ MKLINE(Actor, _costume, sleUint16, VER(8)),
+ MKLINE(Actor, _room, sleByte, VER(8)),
+ MKLINE(Actor, _talkColor, sleByte, VER(8)),
+ MKLINE(Actor, _talkFrequency, sleInt16, VER(16)),
+ MKLINE(Actor, _talkPan, sleInt16, VER(24)),
+ MKLINE(Actor, _talkVolume, sleInt16, VER(29)),
+ MKLINE(Actor, _boxscale, sleUint16, VER(34)),
+ MKLINE(Actor, _scalex, sleByte, VER(8)),
+ MKLINE(Actor, _scaley, sleByte, VER(8)),
+ MKLINE(Actor, _charset, sleByte, VER(8)),
+
+ // Actor sound grew from 8 to 32 bytes and switched to uint16 in HE games
+ MKARRAY_OLD(Actor, _sound[0], sleByte, 8, VER(8), VER(36)),
+ MKARRAY_OLD(Actor, _sound[0], sleByte, 32, VER(37), VER(61)),
+ MKARRAY(Actor, _sound[0], sleUint16, 32, VER(62)),
+
+ // Actor animVariable grew from 8 to 27
+ MKARRAY_OLD(Actor, _animVariable[0], sleUint16, 8, VER(8), VER(40)),
+ MKARRAY(Actor, _animVariable[0], sleUint16, 27, VER(41)),
+
+ MKLINE(Actor, _targetFacing, sleUint16, VER(8)),
+ MKLINE(Actor, _moving, sleByte, VER(8)),
+ MKLINE(Actor, _ignoreBoxes, sleByte, VER(8)),
+ MKLINE(Actor, _forceClip, sleByte, VER(8)),
+ MKLINE(Actor, _initFrame, sleByte, VER(8)),
+ MKLINE(Actor, _walkFrame, sleByte, VER(8)),
+ MKLINE(Actor, _standFrame, sleByte, VER(8)),
+ MKLINE(Actor, _talkStartFrame, sleByte, VER(8)),
+ MKLINE(Actor, _talkStopFrame, sleByte, VER(8)),
+ MKLINE(Actor, _speedx, sleUint16, VER(8)),
+ MKLINE(Actor, _speedy, sleUint16, VER(8)),
+ MKLINE(Actor, _cost.animCounter, sleUint16, VER(8)),
+ MKLINE(Actor, _cost.soundCounter, sleByte, VER(8)),
+ MKLINE(Actor, _drawToBackBuf, sleByte, VER(32)),
+ MKLINE(Actor, _flip, sleByte, VER(32)),
+ MKLINE(Actor, _heSkipLimbs, sleByte, VER(32)),
+
+ // Actor palette grew from 64 to 256 bytes
+ MKARRAY_OLD(Actor, _palette[0], sleByte, 64, VER(8), VER(9)),
+ MKARRAY(Actor, _palette[0], sleByte, 256, VER(10)),
+
+ MK_OBSOLETE(Actor, _mask, sleByte, VER(8), VER(9)),
+ MKLINE(Actor, _shadowMode, sleByte, VER(8)),
+ MKLINE(Actor, _visible, sleByte, VER(8)),
+ MKLINE(Actor, _frame, sleByte, VER(8)),
+ MKLINE(Actor, _animSpeed, sleByte, VER(8)),
+ MKLINE(Actor, _animProgress, sleByte, VER(8)),
+ MKLINE(Actor, _walkbox, sleByte, VER(8)),
+ MKLINE(Actor, _needRedraw, sleByte, VER(8)),
+ MKLINE(Actor, _needBgReset, sleByte, VER(8)),
+ MKLINE(Actor, _costumeNeedsInit, sleByte, VER(8)),
+ MKLINE(Actor, _heCondMask, sleUint32, VER(38)),
+ MKLINE(Actor, _hePaletteNum, sleUint32, VER(59)),
+ MKLINE(Actor, _heXmapNum, sleUint32, VER(59)),
+
+ MKLINE(Actor, _talkPosY, sleInt16, VER(8)),
+ MKLINE(Actor, _talkPosX, sleInt16, VER(8)),
+ MKLINE(Actor, _ignoreTurns, sleByte, VER(8)),
+
+ // Actor layer switched to int32 in HE games
+ MKLINE_OLD(Actor, _layer, sleByte, VER(8), VER(57)),
+ MKLINE(Actor, _layer, sleInt32, VER(58)),
+
+ MKLINE(Actor, _talkScript, sleUint16, VER(8)),
+ MKLINE(Actor, _walkScript, sleUint16, VER(8)),
+
+ MKLINE(Actor, _walkdata.dest.x, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.dest.y, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.destbox, sleByte, VER(8)),
+ MKLINE(Actor, _walkdata.destdir, sleUint16, VER(8)),
+ MKLINE(Actor, _walkdata.curbox, sleByte, VER(8)),
+ MKLINE(Actor, _walkdata.cur.x, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.cur.y, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.next.x, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.next.y, sleInt16, VER(8)),
+ MKLINE(Actor, _walkdata.deltaXFactor, sleInt32, VER(8)),
+ MKLINE(Actor, _walkdata.deltaYFactor, sleInt32, VER(8)),
+ MKLINE(Actor, _walkdata.xfrac, sleUint16, VER(8)),
+ MKLINE(Actor, _walkdata.yfrac, sleUint16, VER(8)),
+
+ MKLINE(Actor, _walkdata.point3.x, sleUint16, VER(42)),
+ MKLINE(Actor, _walkdata.point3.y, sleUint16, VER(42)),
+
+ MKARRAY(Actor, _cost.active[0], sleByte, 16, VER(8)),
+ MKLINE(Actor, _cost.stopped, sleUint16, VER(8)),
+ MKARRAY(Actor, _cost.curpos[0], sleUint16, 16, VER(8)),
+ MKARRAY(Actor, _cost.start[0], sleUint16, 16, VER(8)),
+ MKARRAY(Actor, _cost.end[0], sleUint16, 16, VER(8)),
+ MKARRAY(Actor, _cost.frame[0], sleUint16, 16, VER(8)),
+ MKEND()
+ };
+
+ if (ser->isLoading()) {
+ // Not all actor data is saved; so when loading, we first reset
+ // the actor, to ensure completely reproducible behaviour (else,
+ // some not saved value in the actor class can cause odd things)
+ initActor(-1);
+ }
+
+ ser->saveLoadEntries(this, actorEntries);
+}
+
+} // End of namespace Scumm