diff options
Diffstat (limited to 'scumm/actor.cpp')
-rw-r--r-- | scumm/actor.cpp | 1418 |
1 files changed, 1418 insertions, 0 deletions
diff --git a/scumm/actor.cpp b/scumm/actor.cpp new file mode 100644 index 0000000000..697a1cdadf --- /dev/null +++ b/scumm/actor.cpp @@ -0,0 +1,1418 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001/2002 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "scumm.h" +#include "actor.h" +#include "akos.h" +#include "costume.h" +#include "resource.h" + +#include <math.h> + +void Actor::initActor(int mode) +{ + if (mode == 1) { + costume = 0; + room = 0; + x = 0; + y = 0; + facing = 180; + newDirection = 180; + } else if (mode == 2) { + facing = 180; + newDirection = 180; + } + + elevation = 0; + width = 24; + talkColor = 15; + new_2 = 0; + new_1 = -80; + scaley = scalex = 0xFF; + charset = 0; + sound[0] = 0; + sound[1] = 0; + sound[2] = 0; + sound[3] = 0; + sound[4] = 0; + sound[5] = 0; + sound[6] = 0; + sound[7] = 0; + newDirection = 0; + + stopActorMoving(); + + shadow_mode = 0; + layer = 0; + + setActorWalkSpeed(8, 2); + + ignoreBoxes = 0; + forceClip = 0; + new_3 = 0; + initFrame = 1; + walkFrame = 2; + standFrame = 3; + talkFrame1 = 4; + talkFrame2 = 5; + + walk_script = 0; + talk_script = 0; + + if (_vm->_features & GF_AFTER_V7) { + _vm->_classData[number] = _vm->_classData[0]; + } else { + _vm->_classData[number] = 0; + } +} + +void Actor::stopActorMoving() +{ + _vm->stopScriptNr(walk_script); + moving = 0; +} + +void Actor::setActorWalkSpeed(uint newSpeedX, uint newSpeedY) +{ + if (newSpeedX == speedx && newSpeedY == speedy) + return; + + speedx = newSpeedX; + speedy = newSpeedY; + + if (moving) { + calcMovementFactor(walkdata.newx, walkdata.newy); + } +} + +int Scumm::getAngleFromPos(int x, int y) +{ + if (_gameId == GID_DIG) { + double temp = atan2((double)x, (double)-y); + return normalizeAngle((int)(temp * 180 / 3.1415926535)); + } 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(int newX, int newY) +{ + int actorX, actorY; + int diffX, diffY; + int32 XYFactor, YXFactor; + + actorX = x; + actorY = y; + + if (actorX == newX && actorY == newY) + return 0; + + diffX = newX - actorX; + diffY = newY - actorY; + YXFactor = speedy << 16; + + if (diffY < 0) + YXFactor = -YXFactor; + + XYFactor = YXFactor * diffX; + if (diffY != 0) { + XYFactor /= diffY; + } else { + YXFactor = 0; + } + + if ((uint) abs((int)(XYFactor >> 16)) > speedx) { + XYFactor = speedx << 16; + if (diffX < 0) + XYFactor = -XYFactor; + + YXFactor = XYFactor * diffY; + if (diffX != 0) { + YXFactor /= diffX; + } else { + XYFactor = 0; + } + } + + walkdata.x = actorX; + walkdata.y = actorY; + walkdata.newx = newX; + walkdata.newy = newY; + walkdata.XYFactor = XYFactor; + walkdata.YXFactor = YXFactor; + walkdata.xfrac = 0; + walkdata.yfrac = 0; + + newDirection = _vm->getAngleFromPos(XYFactor, YXFactor); + + return actorWalkStep(); +} + +int Actor::remapDirection(int dir, bool is_walking) +{ + int specdir; + byte flags; + bool flipX; + bool flipY; + + if (!ignoreBoxes) { + specdir = _vm->_extraBoxFlags[walkbox]; + if (specdir) { + if (specdir & 0x8000) { + dir = specdir & 0x3FFF; + } else { + error("remapDirection: special dir not implemented"); + } + } + + flags = _vm->getBoxFlags(walkbox); + + flipX = (walkdata.XYFactor > 0); + flipY = (walkdata.YXFactor > 0); + + // FIXME - this special cases for the class might be necesary for other + // games besides Loom CD! + + // Check for X-Flip + if ((flags & kBoxXFlip) || isInClass(_vm->_gameId == GID_LOOM256 ? 19 : 30)) { + dir = 360 - dir; + flipX = !flipX; + } + // Check for Y-Flip + if ((flags & kBoxYFlip) || isInClass(_vm->_gameId == GID_LOOM256 ? 18 : 29)) { + dir = 180 - dir; + flipY = !flipY; + } + + switch (flags & 7) { + case 1: + if (is_walking) // Actor is walking + return flipX ? 90 : 270; + else // Actor is standing/turning + return (dir == 90) ? 90 : 270; + case 2: + 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, to; + int diff; + int dirType; + int dir; + int num; + bool shouldInterpolate; + + dirType = _vm->akos_hasManyDirections(this); + + from = toSimpleDir(dirType, facing); + dir = remapDirection(newDirection, is_walking); + shouldInterpolate = (dir & 1024); + to = toSimpleDir(dirType, dir & 1023); + num = numSimpleDirDirections(dirType); + + if (shouldInterpolate) { + // Turn left or right, depending on which is shorter. + 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; + int actorX, actorY; + int distX, distY; + int direction; + + needRedraw = true; + needBgReset = true; + + direction = updateActorDirection(true); + if (!(moving & MF_IN_LEG) || facing != direction) { + if (walkFrame != frame || facing != direction) { + startWalkAnim(walkFrame == frame ? 2 : 1, direction); + } + moving |= MF_IN_LEG; + } + + actorX = x; + actorY = y; + + if (walkbox != walkdata.curbox && _vm->checkXYInBoxBounds(walkdata.curbox, actorX, actorY)) { + setBox(walkdata.curbox); + } + + distX = abs(walkdata.newx - walkdata.x); + distY = abs(walkdata.newy - walkdata.y); + + if (abs(actorX - walkdata.x) >= distX && abs(actorY - walkdata.y) >= distY) { + moving &= ~MF_IN_LEG; + return 0; + } + + tmpX = ((actorX + 8000) << 16) + walkdata.xfrac + (walkdata.XYFactor >> 8) * scalex; + walkdata.xfrac = (uint16)tmpX; + actorX = (tmpX >> 16) - 8000; + + tmpY = (actorY << 16) + walkdata.yfrac + (walkdata.YXFactor >> 8) * scalex; + walkdata.yfrac = (uint16)tmpY; + actorY = (tmpY >> 16); + + if (abs(actorX - walkdata.x) > distX) { + actorX = walkdata.newx; + } + + if (abs(actorY - walkdata.y) > distY) { + actorY = walkdata.newy; + } + + x = actorX; + y = actorY; + return 1; +} + + +void Actor::setupActorScale() +{ + uint16 scale; + byte *resptr; + + if (_vm->_features & GF_NO_SCALLING) { + scalex = 0xFF; + scaley = 0xFF; + return; + } + + if (ignoreBoxes != 0) + return; + + if (_vm->getBoxFlags(walkbox) & kBoxPlayerOnly) + return; + + scale = _vm->getBoxScale(walkbox); + + if (scale & 0x8000) { + scale = (scale & 0x7FFF) + 1; + resptr = _vm->getResourceAddress(rtScaleTable, scale); + if (resptr == NULL) + error("Scale table %d not defined", scale); + int theY = y; + if (theY >= 200) + theY = 199; + else if (theY < 0) + theY = 0; + scale = resptr[theY]; + } + + if (scale > 255) + warning("Actor %d at %d, scale %d out of range", number, y, scale); + + // FIXME - Quick fix to ft's fuel tower bug (by yazoo) + if (scale == 1 && _vm->_currentRoom == 76) + scale = 0xFF; + + scalex = (byte)scale; + scaley = (byte)scale; +} + +void Actor::startAnimActor(int frame) +{ + if (_vm->_features & GF_NEW_COSTUMES) { + switch (frame) { + case 1001: + frame = initFrame; + break; + case 1002: + frame = walkFrame; + break; + case 1003: + frame = standFrame; + break; + case 1004: + frame = talkFrame1; + break; + case 1005: + frame = talkFrame2; + break; + } + + if (costume != 0) { + animProgress = 0; + needRedraw = true; + needBgReset = true; + if (frame == initFrame) + cost.reset(); + _vm->akos_decodeData(this, frame, (uint) - 1); + } + + } else { + switch (frame) { + case 0x38: + frame = initFrame; + break; + case 0x39: + frame = walkFrame; + break; + case 0x3A: + frame = standFrame; + break; + case 0x3B: + frame = talkFrame1; + break; + case 0x3C: + frame = talkFrame2; + break; + } + + if (isInCurrentRoom() && costume) { + animProgress = 0; + cost.animCounter1 = 0; + needRedraw = true; + + if (initFrame == frame) + cost.reset(); + + if (frame != 0x3E) { + _vm->cost_decodeData(this, frame, (uint) - 1); + } + } + + needBgReset = true; + } +} + +void Actor::animateActor(int anim) +{ + int cmd, dir; + + if (_vm->_features & GF_AFTER_V7) { + + if (anim == 0xFF) + anim = 2000; + + cmd = anim / 1000; + dir = anim % 1000; + + } else { + + cmd = anim >> 2; + dir = oldDirToNewDir(anim & 3); + + // Convert into old cmd code + cmd = 0x3F - cmd + 2; + + } + + switch (cmd) { + case 2: + stopActorMoving(); + startAnimActor(standFrame); + break; + case 3: + moving &= ~MF_TURN; + setDirection(dir); + break; + case 4: + turnToDirection(dir); + break; + default: + startAnimActor(anim); + } +} + +void Actor::setDirection(int direction) +{ + uint aMask; + int i; + uint16 vald; + + if (facing == direction) + return; + + facing = normalizeAngle(direction); + + if (costume == 0) + return; + + aMask = 0x8000; + for (i = 0; i < 16; i++, aMask >>= 1) { + vald = cost.frame[i]; + if (vald == 0xFFFF) + continue; + if (_vm->_features & GF_AFTER_V7) + _vm->akos_decodeData(this, vald, aMask); + else + _vm->cost_decodeData(this, vald, aMask); + } + + needRedraw = true; + needBgReset = true; +} + +void Actor::putActor(int dstX, int dstY, byte newRoom) +{ + if (visible && _vm->_currentRoom != newRoom && _vm->_vars[_vm->VAR_TALK_ACTOR] == number) { + _vm->clearMsgQueue(); + } + + x = dstX; + y = dstY; + room = newRoom; + needRedraw = true; + needBgReset = true; + + if (_vm->_vars[_vm->VAR_EGO] == number) { + _vm->_egoPositioned = true; + } + + if (visible) { + if (isInCurrentRoom()) { + if (moving) { + startAnimActor(standFrame); + moving = 0; + } + adjustActorPos(); + } else { + hideActor(); + } + } else { + if (isInCurrentRoom()) + showActor(); + } +} + +int Actor::getActorXYPos(int &xPos, int &yPos) +{ + if (!isInCurrentRoom()) + return -1; + + xPos = x; + yPos = y; + return 0; +} + +AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY, int pathfrom) +{ + AdjustBoxResult abr, tmp; + uint threshold; + uint best; + int box, iterations = 0; /* Use iterations for those odd times we get stuck in the loop */ + int firstValidBox, i, j; + byte flags, b; + + if (_vm->_features & GF_SMALL_HEADER) + firstValidBox = 0; + else + firstValidBox = 1; + + abr.x = dstX; + abr.y = dstY; + abr.dist = 0; + + if (ignoreBoxes == 0) { + threshold = 30; + + while (1) { + iterations++; + if (iterations > 1000) + return abr; /* Safety net */ + box = _vm->getNumBoxes() - 1; + if (box < firstValidBox) + return abr; + + best = (uint) 0xFFFF; + b = 0; + + if (!(_vm->_features & GF_OLD256) || box) + for (j = box; j >= firstValidBox; j--) { + flags = _vm->getBoxFlags(j); + + if (flags & kBoxInvisible && (!(flags & kBoxPlayerOnly) || isInClass(31))) + continue; + + if (pathfrom >= firstValidBox) { + + if (flags & kBoxLocked && (!(flags & kBoxPlayerOnly))) + continue; + + i = _vm->getPathToDestBox(pathfrom, j); + if (i == -1) + continue; + + if (_vm->_features & GF_OLD256) { + // FIXME - we check here if the box suggested by getPathToDestBox + // is locked or not. This prevents us from walking thru + // closed doors in some cases in Zak256. However a better fix + // would be to recompute the box matrix whenever flags change. + flags = _vm->getBoxFlags(i); + if (flags & kBoxLocked && (!(flags & kBoxPlayerOnly))) + continue; + if (flags & kBoxInvisible && (!(flags & kBoxPlayerOnly) || isInClass(31))) + continue; + } + } + + if (!_vm->inBoxQuickReject(j, dstX, dstY, threshold)) + continue; + + if (_vm->checkXYInBoxBounds(j, dstX, dstY)) { + abr.x = dstX; + abr.y = dstY; + abr.dist = j; + return abr; + } + + tmp = _vm->getClosestPtOnBox(j, dstX, dstY); + + if (tmp.dist >= best) + continue; + + abr.x = tmp.x; + abr.y = tmp.y; + + if (tmp.dist == 0) { + abr.dist = j; + return abr; + } + best = tmp.dist; + b = j; + } + + if (threshold == 0 || threshold * threshold >= best) { + abr.dist = b; + return abr; + } + threshold = (threshold == 30) ? 80 : 0; + } + } + + return abr; +} + +void Actor::adjustActorPos() +{ + AdjustBoxResult abr; + byte flags; + + abr = adjustXYToBeInBox(x, y, -1); + + x = abr.x; + y = abr.y; + walkdata.destbox = (byte)abr.dist; + + setBox(abr.dist); + + walkdata.destx = -1; + + moving = 0; + cost.animCounter2 = 0; + + if (_vm->_features & GF_AFTER_V7) { + stopActorMoving(); + } + + flags = _vm->getBoxFlags(walkbox); + if (flags & 7) { + turnToDirection(facing); + } +} + +void Actor::turnToDirection(int newdir) +{ + if (newdir == -1) + return; + + moving &= ~MF_TURN; + + if (newdir != facing) { + moving = MF_TURN; + newDirection = newdir; + + // FIXME - workaround for bug #558236 + if (_vm->_gameId == GID_INDY4 && room == 39 && x == 617 && y == 125 && newdir == 180) + startAnimActor(standFrame); + } +} + +void Actor::hideActor() +{ + if (!visible) + return; + + if (moving) { + startAnimActor(standFrame); + moving = 0; + } + visible = false; + cost.animCounter2 = 0; + needRedraw = false; + needBgReset = true; +} + +void Actor::showActor() +{ + if (_vm->_currentRoom == 0 || visible) + return; + + adjustActorPos(); + + _vm->ensureResourceLoaded(rtCostume, costume); + + if (costumeNeedsInit) { + startAnimActor(initFrame); + costumeNeedsInit = false; + } + moving = 0; + visible = true; + needRedraw = true; +} + +void Scumm::showActors() +{ + int i; + Actor *a; + + for (i = 1; i < NUM_ACTORS; i++) { + a = derefActor(i); + if (a->isInCurrentRoom()) + a->showActor(); + } +} + +void Scumm::stopTalk() +{ + int act; + + _sound->stopTalkSound(); + + _haveMsg = 0; + _talkDelay = 0; + + act = _vars[VAR_TALK_ACTOR]; + if (act && act < 0x80) { + Actor *a = derefActorSafe(act, "stopTalk"); + if (a->isInCurrentRoom() && _useTalkAnims) { + a->startAnimActor(a->talkFrame2); + _useTalkAnims = false; + } + _vars[VAR_TALK_ACTOR] = 0xFF; + } + _keepText = false; + restoreCharsetBg(); +} + +void Scumm::clearMsgQueue() +{ + _messagePtr = (byte *)" "; + stopTalk(); +} + +void Scumm::walkActors() +{ + int i; + Actor *a; + + for (i = 1; i < NUM_ACTORS; i++) { + a = derefActor(i); + if (a->isInCurrentRoom()) + if (_features & GF_OLD256) + a->walkActorOld(); + else + a->walkActor(); + } +} + +/* Used in Scumm v5 only. Play sounds associated with actors */ +void Scumm::playActorSounds() +{ + int i; + Actor *a; + + for (i = 1; i < NUM_ACTORS; i++) { + a = derefActor(i); + if (a->cost.animCounter2 && a->isInCurrentRoom() && a->sound) { + _currentScript = 0xFF; + _sound->addSoundToQueue(a->sound[0]); + for (i = 1; i < NUM_ACTORS; i++) { + a = derefActor(i); + a->cost.animCounter2 = 0; + } + return; + } + } +} + + +#define DRAW_ORDER(x) ((x)->y - ((x)->layer << 11)) + +void Scumm::processActors() +{ + int i; + Actor *actors[MAX_ACTORS], *a, **ac, **ac2, *tmp, **end; + int numactors = 0; + + // Make a list of all actors in this room + for (i = 1; i < NUM_ACTORS; i++) { + a = derefActor(i); + if (a->isInCurrentRoom()) + actors[numactors++] = a; + } + if (!numactors) + return; + + end = actors + numactors; + + // Sort actors by position before we draw them (to ensure that actors in + // front are drawn after those "behind" them). + for (ac = end - 1; ac >= actors; --ac) { + for (ac2 = actors; ac2 != ac; ++ac2) { + if (DRAW_ORDER(*ac2) > DRAW_ORDER(*(ac2 + 1))) { + tmp = *(ac2 + 1); + *(ac2 + 1) = *ac2; + *ac2 = tmp; + } + } + } + + // Finally draw the now sorted actors + for (ac = actors; ac != end; ++ac) { + a = *ac; + if (a->costume) { + CHECK_HEAP getMaskFromBox(a->walkbox); + a->drawActorCostume(); + CHECK_HEAP a->animateCostume(); + } + } +} + +void Actor::drawActorCostume() +{ + if (!needRedraw) + return; + + // FIXME: ugly fix for samnmax inventory + if (_vm->_gameId == GID_SAMNMAX && _vm->getState(995)) + return; + + needRedraw = false; + + setupActorScale(); + + if (!(_vm->_features & GF_AFTER_V7)) { + CostumeRenderer cr(_vm); + + cr._actorX = x - _vm->virtscr->xstart; + cr._actorY = y - elevation; + cr._scaleX = scalex; + cr._scaleY = scaley; + + cr._outheight = _vm->virtscr->height; + + cr._zbuf = _vm->getMaskFromBox(walkbox); + + if (forceClip) + cr._zbuf = forceClip; + else if (isInClass(20)) + cr._zbuf = 0; + else if (cr._zbuf > _vm->gdi._numZBuffer) + cr._zbuf = (byte)_vm->gdi._numZBuffer; + + cr._shadow_table = _vm->_shadowPalette; + + cr.setCostume(costume); + cr.setPalette(palette); + cr.setFacing(facing); + + top = 0xFF; + + bottom = 0; + + /* if the actor is partially hidden, redraw it next frame */ + if (cr.drawCostume(this) & 1) { + needBgReset = true; + needRedraw = true; + } + } else { + AkosRenderer ar(_vm); + + ar.x = x - _vm->virtscr->xstart; + ar.y = y - elevation; + ar.scale_x = scalex; + ar.scale_y = scaley; + ar.clipping = forceClip; + if (ar.clipping == 100) { + ar.clipping = _vm->getMaskFromBox(walkbox); + if (ar.clipping > (byte)_vm->gdi._numZBuffer) + ar.clipping = _vm->gdi._numZBuffer; + } + ar.charsetmask = _vm->_vars[_vm->VAR_CHARSET_MASK] != 0; + + ar.outptr = _vm->virtscr->screenPtr + _vm->virtscr->xstart; + ar.outwidth = _vm->virtscr->width; + ar.outheight = _vm->virtscr->height; + + ar.shadow_mode = shadow_mode; + ar.shadow_table = _vm->_shadowPalette; + + ar.setCostume(costume); + ar.setPalette(palette); + ar.setFacing(this); + + ar.dirty_id = number; + + ar.cd = &cost; + + ar.draw_top = top = 0x7fffffff; + ar.draw_bottom = bottom = 0; + ar.drawCostume(); + top = ar.draw_top; + bottom = ar.draw_bottom; + } +} + +void Actor::animateCostume() +{ + if (costume == 0) + return; + + animProgress++; + if (animProgress >= animSpeed) { + animProgress = 0; + + if (_vm->_features & GF_AFTER_V7) { + byte *akos = _vm->getResourceAddress(rtCostume, costume); + assert(akos); + if (_vm->akos_increaseAnims(akos, this)) { + needRedraw = true; + needBgReset = true; + } + } else { + LoadedCostume lc(_vm); + lc.loadCostume(costume); + if (lc.increaseAnims(this)) { + needRedraw = true; + needBgReset = true; + } + } + } +} + +void Scumm::setActorRedrawFlags() +{ + int i, j; + uint32 bits; + + if (_fullRedraw) { + for (j = 0; j < NUM_ACTORS; j++) { + Actor *a = derefActor(j); + a->needRedraw = true; + a->needBgReset = true; + } + } else { + for (i = 0; i < 40; i++) { + bits = gfxUsageBits[_screenStartStrip + i]; + if (bits & 0x3FFFFFFF) { + for (j = 0; j < NUM_ACTORS; j++) { + if ((bits & (1 << j)) && bits != (uint32)(1 << j)) { + Actor *a = derefActor(j); + a->needRedraw = true; + a->needBgReset = true; + } + } + } + } + } +} + +int Scumm::getActorFromPos(int x, int y) +{ + uint32 drawbits; + int i; + + drawbits = gfxUsageBits[x >> 3]; + if (!(drawbits & 0x3FFFFFFF)) + return 0; + for (i = 1; i < NUM_ACTORS; i++) { + Actor *a = derefActor(i); + if (drawbits & (1 << i) && !getClass(i, 32) && y >= a->top && y <= a->bottom) { + return i; + } + } + return 0; +} + +void Scumm::actorTalk() +{ + int oldact; + Actor *a; + + _msgPtrToAdd = charset._buffer; + _messagePtr = addMessageToStack(_messagePtr); + assert((int)(_msgPtrToAdd - charset._buffer) < (int)(sizeof(charset._buffer))); + + if (_actorToPrintStrFor == 0xFF) { + if (!_keepText) + stopTalk(); + _vars[VAR_TALK_ACTOR] = 0xFF; + oldact = 0; + } else { + a = derefActorSafe(_actorToPrintStrFor, "actorTalk"); + if (!a->isInCurrentRoom() && !(_features & GF_AFTER_V7)) { + oldact = 0xFF; + } else { + if (!_keepText) + stopTalk(); + _vars[VAR_TALK_ACTOR] = a->number; + if (!string[0].no_talk_anim) { + a->startAnimActor(a->talkFrame1); + _useTalkAnims = true; + } + oldact = _vars[VAR_TALK_ACTOR]; + } + } + if (oldact >= 0x80) + return; + + if (_vars[VAR_TALK_ACTOR] > 0x7F) { + _charsetColor = (byte)string[0].color; + } else { + a = derefActorSafe(_vars[VAR_TALK_ACTOR], "actorTalk(2)"); + _charsetColor = a->talkColor; + } + charset._bufPos = 0; + _talkDelay = 0; + _haveMsg = 0xFF; + _vars[VAR_HAVE_MSG] = 0xFF; + CHARSET_1(); +} + +void Actor::setActorCostume(int c) +{ + int i; + + costumeNeedsInit = true; + + if (visible) { + hideActor(); + cost.reset(); + costume = c; + showActor(); + } else { + costume = c; + cost.reset(); + } + + for (i = 0; i < 32; i++) + palette[i] = 0xFF; +} + +void Actor::startWalkActor(int destX, int destY, int dir) +{ + AdjustBoxResult abr; + + abr = adjustXYToBeInBox(destX, destY, walkbox); + + if (!isInCurrentRoom()) { + x = abr.x; + y = abr.y; + if (dir != -1) + setDirection(dir); + return; + } + + if (ignoreBoxes != 0) { + abr.dist = 0; + walkbox = 0; + } else { + if (_vm->checkXYInBoxBounds(walkdata.destbox, abr.x, abr.y)) { + abr.dist = walkdata.destbox; + } else { + abr = adjustXYToBeInBox(abr.x, abr.y, walkbox); + } + if (moving && walkdata.destdir == dir && walkdata.destx == abr.x && walkdata.desty == abr.y) + return; + } + + if (x == abr.x && y == abr.y) { + turnToDirection(dir); + return; + } + + walkdata.destx = abr.x; + walkdata.desty = abr.y; + walkdata.destbox = (byte)abr.dist; /* a box */ + walkdata.destdir = dir; + moving = (moving & MF_IN_LEG) | MF_NEW_LEG; + walkdata.point3x = 32000; + + walkdata.curbox = walkbox; +} + +void Actor::startWalkAnim(int cmd, int angle) +{ + if (angle == -1) + angle = facing; + +/*FIXME: (yazoo): the walk script are buggy in dig causing + * troubles while walking. It's disabled until I can + * find a proper fix + * note: walk scripts aren't required to make the game + * work as usual */ + +/* int16 args[16]; + + if (walk_script != 0) { + args[2] = angle; + args[0] = number; + args[1] = cmd; + _vm->runScript(walk_script, 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 j; + int16 foundPathX, foundPathY; + + 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) { + j = updateActorDirection(false); + if (facing != j) + setDirection(j); + else + moving = 0; + return; + } + + setBox(walkdata.curbox); + moving &= MF_IN_LEG; + } + + do { + moving &= ~MF_NEW_LEG; + if ((!walkbox && (!(_vm->_features & GF_SMALL_HEADER)))) { + setBox(walkdata.destbox); + walkdata.curbox = walkdata.destbox; + break; + } + if (walkbox == walkdata.destbox) + break; + j = _vm->getPathToDestBox(walkbox, walkdata.destbox); + if (j == -1 || j > 0xF0) { + walkdata.destbox = walkbox; + moving |= MF_LAST_LEG; + return; + } + walkdata.curbox = j; + + if (_vm->findPathTowards(this, walkbox, j, walkdata.destbox, foundPathX, foundPathY)) + break; + if (calcMovementFactor(foundPathX, foundPathY)) + return; + + setBox(walkdata.curbox); + } while (1); + + moving |= MF_LAST_LEG; + calcMovementFactor(walkdata.destx, walkdata.desty); +} + +void Actor::walkActorOld() +{ + ScummPoint gateLoc[5]; // Gate locations + int new_dir, next_box; + + if (!moving) + return; + + if (moving & MF_NEW_LEG) { + restart: + moving &= ~MF_NEW_LEG; + + if (walkbox == 0xFF) { + walkbox = walkdata.destbox; + walkdata.curbox = walkdata.destbox; + moving |= MF_LAST_LEG; + calcMovementFactor(walkdata.destx, walkdata.desty); + return; + } + + if (walkbox == walkdata.destbox) { + moving |= MF_LAST_LEG; + calcMovementFactor(walkdata.destx, walkdata.desty); + return; + } + + next_box = _vm->getPathToDestBox(walkbox, walkdata.destbox); + + if (next_box == -1) { + moving |= MF_LAST_LEG; + return; + } + + walkdata.curbox = next_box; + + _vm->findPathTowardsOld(this, walkbox, next_box, walkdata.destbox, gateLoc); + if (gateLoc[2].x == 32000 && gateLoc[3].x == 32000) { + moving |= MF_LAST_LEG; + calcMovementFactor(walkdata.destx, walkdata.desty); + return; + } + + if (gateLoc[2].x != 32000) { + if (calcMovementFactor(gateLoc[2].x, gateLoc[2].y)) { + walkdata.point3x = gateLoc[3].x; + walkdata.point3y = gateLoc[3].y; + return; + } + } + + if (calcMovementFactor(gateLoc[3].x, gateLoc[3].y)) + return; + + walkbox = walkdata.destbox; + goto restart; + + } + + if (moving & MF_IN_LEG) { + if (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); + return; + } + moving = 0; + return; + } + + if (walkdata.point3x != 32000) { + if (calcMovementFactor(walkdata.point3x, walkdata.point3y)) { + walkdata.point3x = 32000; + return; + } + walkdata.point3x = 32000; + } + + walkbox = walkdata.curbox; + moving &= MF_IN_LEG; + moving |= MF_NEW_LEG; + goto restart; +} + +byte *Actor::getActorName() +{ + byte *ptr = _vm->getResourceAddress(rtActorName, number); + if (ptr == NULL) + return (byte *)" "; + return ptr; +} + +void Actor::remapActorPalette(int r_fact, int g_fact, int b_fact, int threshold) +{ + byte *akos, *rgbs, *akpl; + int akpl_size, i; + int r, g, b; + byte akpl_color; + + if (!isInCurrentRoom()) { + warning("Remap actor %d not in current room", number); + return; + } + + if (costume < 1 || costume >= _vm->_numCostumes - 1) { + warning("Remap actor %d invalid costume", number, costume); + return; + } + + akos = _vm->getResourceAddress(rtCostume, costume); + akpl = findResource(MKID('AKPL'), akos); + + //get num palette entries + akpl_size = RES_SIZE(akpl) - 8; + + //skip resource header + akpl = RES_DATA(akpl); + + rgbs = findResource(MKID('RGBS'), akos); + + if (!rgbs) { + warning("Can't remap actor %d costume %d doesn't contain an RGB block", number, costume); + return; + } + // skip resource header + rgbs = RES_DATA(rgbs); + + for (i = 0; i < akpl_size; i++) { + r = *rgbs++; + g = *rgbs++; + b = *rgbs++; + + akpl_color = *akpl++; + + // allow remap of generic palette entry? + if (!shadow_mode || akpl_color >= 16) { + if (r_fact != 256) + r = (r * r_fact) >> 8; + if (g_fact != 256) + g = (g * g_fact) >> 8; + if (b_fact != 256) + b = (b * b_fact) >> 8; + palette[i] = _vm->remapPaletteColor(r, g, b, threshold); + } + } +} + +void Scumm::resetActorBgs() +{ + Actor *a; + int i; + uint32 onlyActorFlags, bitpos; + + for (i = 0; i < 40; i++) { + onlyActorFlags = (gfxUsageBits[_screenStartStrip + i] &= 0x3FFFFFFF); + a = getFirstActor(); + bitpos = 1; + + while (onlyActorFlags) { + if (onlyActorFlags & 1 && a->top != 0xFF && a->needBgReset) { + gfxUsageBits[_screenStartStrip + i] ^= bitpos; + + if ((a->bottom - a->top) >= 0) + gdi.resetBackground(a->top, a->bottom, i); + } + bitpos <<= 1; + onlyActorFlags >>= 1; + a++; + } + } + + for (i = 1, a = getFirstActor(); ++a, i < NUM_ACTORS; i++) { + a->needBgReset = false; + } +} + +void Actor::classChanged(int cls, bool value) +{ + switch(cls) { + case 20: // Never clip + break; + case 21: // Always clip + forceClip = value; + break; + case 22: // Ignore boxes + ignoreBoxes = value; + break; + case 29: // Y flip + break; + case 30: // X flip + break; + case 31: // ?? + break; + } +} + +bool Actor::isInClass(int cls) +{ + return _vm->getClass(number, cls); +} |