diff options
-rw-r--r-- | scumm/actor.cpp | 265 | ||||
-rw-r--r-- | scumm/actor.h | 4 | ||||
-rw-r--r-- | scumm/boxes.cpp | 26 | ||||
-rw-r--r-- | scumm/scummvm.cpp | 3 |
4 files changed, 126 insertions, 172 deletions
diff --git a/scumm/actor.cpp b/scumm/actor.cpp index 94ed658fef..e6f4b0bddc 100644 --- a/scumm/actor.cpp +++ b/scumm/actor.cpp @@ -32,6 +32,9 @@ #include <math.h> +byte Actor::INVALID_BOX = 0; + + void Actor::initActor(int mode) { if (mode == 1) { costume = 0; @@ -64,7 +67,7 @@ void Actor::initActor(int mode) { setActorWalkSpeed(8, 2); animSpeed = 0; - ignoreBoxes = 0; + ignoreBoxes = false; forceClip = 0; ignoreTurns = false; @@ -199,9 +202,6 @@ int Actor::remapDirection(int dir, bool is_walking) { if (specdir & 0x8000) { dir = specdir & 0x3FFF; } else { - // FIXME - I am not 100% if this code is right (Fingolfin) - warning("remapDirection: special dir"); - specdir = specdir & 0x3FFF; if (specdir - 90 < dir && dir < specdir + 90) dir = specdir; @@ -345,11 +345,9 @@ int Actor::actorWalkStep() { return 0; } - // FIXME: Fingolfin asks: what do these 8000 here ?!? That looks wrong... maybe - // a 0x8000 was meant? But even that makes not much sense to me - tmpX = ((actorX + 8000) << 16) + walkdata.xfrac + (walkdata.deltaXFactor >> 8) * scalex; + tmpX = (actorX << 16) + walkdata.xfrac + (walkdata.deltaXFactor >> 8) * scalex; walkdata.xfrac = (uint16)tmpX; - actorX = (tmpX >> 16) - 8000; + actorX = (tmpX >> 16); tmpY = (actorY << 16) + walkdata.yfrac + (walkdata.deltaYFactor >> 8) * scaley; walkdata.yfrac = (uint16)tmpY; @@ -378,7 +376,7 @@ void Actor::setupActorScale() { return; } - if (ignoreBoxes != 0) + if (ignoreBoxes) return; if (_vm->getBoxFlags(walkbox) & kBoxPlayerOnly) @@ -596,97 +594,103 @@ int Actor::getActorXYPos(int &xPos, int &yPos) { } AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY, int pathfrom) { + const uint thresholdTable[] = { 30, 80, 0 }; 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; + uint bestDist; + int numBoxes; + int box; + byte flags, bestBox; + const int firstValidBox = (_vm->_features & GF_SMALL_HEADER) ? 0 : 1; abr.x = dstX; abr.y = dstY; - abr.dist = 0; + abr.dist = INVALID_BOX; - if (ignoreBoxes == 0) { - threshold = 30; + if (ignoreBoxes) + return abr; - while (1) { - iterations++; - if (iterations > 1000) - return abr; /* Safety net */ - box = _vm->getNumBoxes() - 1; - if (box < firstValidBox) - return abr; + for (int tIdx = 0; tIdx < ARRAYSIZE(thresholdTable); tIdx++) { + threshold = thresholdTable[tIdx]; - best = (uint) 0xFFFF; - b = 0; + numBoxes = _vm->getNumBoxes() - 1; + if (numBoxes < firstValidBox) + return abr; -// FIXME - why was that check here? It apparently causes bug #643001 -// if (!(_vm->_features & GF_OLD256) || box) - for (j = box; j >= firstValidBox; j--) { - flags = _vm->getBoxFlags(j); + bestDist = (uint) 0xFFFF; + bestBox = INVALID_BOX; - if (flags & kBoxInvisible && (!(flags & kBoxPlayerOnly) || isInClass(31))) - continue; - - if (pathfrom >= firstValidBox) { + // We iterate (backwards) over all boxes, searching the one closes + // to the desired coordinates. + for (box = numBoxes; box >= firstValidBox; box--) { + flags = _vm->getBoxFlags(box); - if (flags & kBoxLocked && (!(flags & kBoxPlayerOnly))) - continue; - - i = _vm->getPathToDestBox(pathfrom, j); - if (i == -1) - continue; + if (flags & kBoxInvisible && (!(flags & kBoxPlayerOnly) || isInClass(31))) + continue; + + // FIXME: the following is essentially a hack to fix issues in Zak256 + // and possibly elsewhere; but it seems the original did nothing like this + // so hopefully one day we'll find a 'proper' solution and can remove + // this hack. + if (pathfrom >= firstValidBox) { - 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 (flags & kBoxLocked && (!(flags & kBoxPlayerOnly))) + continue; - if (!_vm->inBoxQuickReject(j, dstX, dstY, threshold)) + int i = _vm->getPathToDestBox(pathfrom, box); + if (i == -1) continue; - if (_vm->checkXYInBoxBounds(j, dstX, dstY)) { - abr.x = dstX; - abr.y = dstY; - abr.dist = j; - return abr; + 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; } + } - tmp = _vm->getClosestPtOnBox(j, dstX, dstY); + // For increased performance, we perform a quick test if + // the coordinates can even be within a distance of 'threshold' + // pixels of the box. + if (!_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.dist = box; + return abr; + } - if (tmp.dist >= best) - continue; + // Find the point in the box which is closest to our point. + tmp = _vm->getClosestPtOnBox(box, dstX, dstY); + // Check if the box is closer than the previous boxes. + if (tmp.dist < bestDist) { abr.x = tmp.x; abr.y = tmp.y; - + if (tmp.dist == 0) { - abr.dist = j; + abr.dist = box; return abr; } - best = tmp.dist; - b = j; + bestDist = tmp.dist; + bestBox = box; } + } - if (threshold == 0 || threshold * threshold >= best) { - abr.dist = b; - return abr; - } - threshold = (threshold == 30) ? 80 : 0; + // 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.dist = bestBox; + return abr; } } @@ -695,7 +699,6 @@ AdjustBoxResult Actor::adjustXYToBeInBox(int dstX, int dstY, int pathfrom) { void Actor::adjustActorPos() { AdjustBoxResult abr; - byte flags; abr = adjustXYToBeInBox(x, y, -1); @@ -714,9 +717,11 @@ void Actor::adjustActorPos() { stopActorMoving(); } - flags = _vm->getBoxFlags(walkbox); - if (flags & 7) { - turnToDirection(facing); + if (walkbox != INVALID_BOX) { + byte flags = _vm->getBoxFlags(walkbox); + if (flags & 7) { + turnToDirection(facing); + } } } @@ -863,9 +868,10 @@ void Scumm::processActors() { for (ac = actors; ac != end; ++ac) { a = *ac; if (a->costume) { - CHECK_HEAP getMaskFromBox(a->walkbox); + CHECK_HEAP a->drawActorCostume(); - CHECK_HEAP a->animateCostume(); + CHECK_HEAP + a->animateCostume(); } } @@ -881,9 +887,10 @@ void Scumm::processUpperActors() { for (i = 1; i < _numActors; i++) { a = derefActor(i); if (a->isInCurrentRoom() && a->costume && a->layer < 0) { - CHECK_HEAP getMaskFromBox(a->walkbox); + CHECK_HEAP a->drawActorCostume(); - CHECK_HEAP a->animateCostume(); + CHECK_HEAP + a->animateCostume(); } } } @@ -906,58 +913,15 @@ void Actor::drawActorCostume() { cr._outheight = _vm->virtscr[0].height; - // FIXME - Hack to fix two glitches in the scene where Bobbin - // heals Rusty: Bobbin's feet get masked when Rusty shows him - // what happens to The Forge, and Rusty gets masked after - // Bobbin heals him. (Room 34) - // - // It also fixes a much less noticable glitch when Bobbin - // jumps out of Mandible's cage. (Room 43) - // - // When an actor is moved around without regards to walkboxes, - // its walkbox is set to 0. Unfortunately that's a valid - // walkbox in older games, and its mask may be completely - // wrong for this purpose. - // - // So instead use the mask of the box where the actor happens - // to be at the moment or, if it's not in any box, don't mask - // at all. - // - // This is similar to the _zbuf == 100 check used for AKOS - // costumes, except I haven't been able to figure out the - // proper check here. It's not quite enough to check if - // ignoreBoxes != 0 and checking if walkbox == 0 yields too - // many false positives, e.g. Bobbin leaving the sandy beach - // towards the forest, or Stoke leaving the room where he - // locks up "Rusty". - // - // Until someone can find the proper fix, only apply it to the - // rooms where it's actually known to be needed. - - if (_vm->_gameId == GID_LOOM256 && (_vm->_currentRoom == 34 || _vm->_currentRoom == 43) && walkbox == 0) { - int num_boxes, i; - - cr._zbuf = 0; - num_boxes = _vm->getNumBoxes(); - - // Sometimes boxes overlap, so the direction of this - // loop matters in some rooms. - - for (i = 0; i < num_boxes; i++) { - if (_vm->checkXYInBoxBounds(i, x, y)) { - cr._zbuf = _vm->getMaskFromBox(i); - break; - } - } - } else - 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; + else { + cr._zbuf = _vm->getMaskFromBox(walkbox); + if (cr._zbuf > _vm->gdi._numZBuffer) + cr._zbuf = _vm->gdi._numZBuffer; + } cr._shadow_mode = shadow_mode; if (_vm->_features & GF_SMALL_HEADER) @@ -1232,16 +1196,10 @@ void Actor::setActorCostume(int c) { void Actor::startWalkActor(int destX, int destY, int dir) { AdjustBoxResult abr; - // FIXME: Fingolfin isn't convinced calling adjustXYToBeInBox here is the - // right thing. In fact I think it might completly wrong... - abr = adjustXYToBeInBox(destX, destY, walkbox); + abr.x = destX; + abr.y = destY; if (!isInCurrentRoom()) { - // FIXME: Should we really use abr.x / abr.y here? Shouldn't it be destX / destY? - // Considering that abr was obtained by adjustXYToBeInBox which works on - // the boxes in the *current* room no in the room the actor actually is in. - // Occurs in Monkey Island 1 demo, after title name. - warning("When is this ever triggered anyway? (%d,%d) -> (%d,%d)", x, y, abr.x, abr.y); x = abr.x; y = abr.y; if (dir != -1) @@ -1249,27 +1207,10 @@ void Actor::startWalkActor(int destX, int destY, int dir) { return; } - if (ignoreBoxes != 0) { - // FIXME: this seems wrong for GF_SMALL_HEADER games where walkbox - // is a valid box. Rather, I'd think that for these games, we should - // set walkbox to -1 or some other "illegal" value. The same for - // abr.dist (which is used to set walkdata.destbox after all). - // Also together with my above FIXME comment that would mean that up to - // here there is no reason to even call adjustXYToBeInBox.... - abr.dist = 0; - walkbox = 0; + if (ignoreBoxes) { + abr.dist = INVALID_BOX; + walkbox = INVALID_BOX; } else { - // FIXME: this prevents part of bug #605970 (Loom) from - // occuring, and also fixes a walk bug with Rusty's ghost. - // Not sure if there is a better way to achieve this. - if (walkbox == 0) - adjustActorPos(); - - // Fingolfin remarks on the above FIXME: this is yet another case of the - // walbox 0 problem... In many parts of the code we use "0" to denote "illegal walkbox", - // which is correct for newer games but wrong for GF_SMALL_HEADER games - // which use -1 / 0xFF for the same purpose. - if (_vm->checkXYInBoxBounds(walkdata.destbox, abr.x, abr.y)) { abr.dist = walkdata.destbox; } else { @@ -1378,7 +1319,7 @@ void Actor::walkActor() { do { moving &= ~MF_NEW_LEG; - if ((!walkbox && (!(_vm->_features & GF_SMALL_HEADER)))) { + if (walkbox == INVALID_BOX) { setBox(walkdata.destbox); walkdata.curbox = walkdata.destbox; break; @@ -1388,7 +1329,7 @@ void Actor::walkActor() { break; box = _vm->getPathToDestBox(walkbox, walkdata.destbox); - if (box == -1 || box > 0xF0) { + if (box < 0 || box > 0xF0) { walkdata.destbox = walkbox; moving |= MF_LAST_LEG; return; @@ -1419,7 +1360,7 @@ void Actor::walkActorOld() { restart: moving &= ~MF_NEW_LEG; - if (walkbox == 0xFF) { + if (walkbox == INVALID_BOX) { walkbox = walkdata.destbox; walkdata.curbox = walkdata.destbox; moving |= MF_LAST_LEG; @@ -1435,7 +1376,7 @@ void Actor::walkActorOld() { next_box = _vm->getPathToDestBox(walkbox, walkdata.destbox); - if (next_box == -1) { + if (next_box < 0) { moving |= MF_LAST_LEG; return; } diff --git a/scumm/actor.h b/scumm/actor.h index d78cde17c8..c397799732 100644 --- a/scumm/actor.h +++ b/scumm/actor.h @@ -83,7 +83,7 @@ public: byte charset; int16 newDirection; byte moving; - byte ignoreBoxes; + bool ignoreBoxes; byte forceClip; byte initFrame, walkFrame, standFrame, talkFrame1, talkFrame2; bool needRedraw, needBgReset, costumeNeedsInit, visible; @@ -102,6 +102,8 @@ public: uint16 sound[8]; CostumeData cost; byte palette[256]; + + static byte INVALID_BOX; protected: Scumm *_vm; diff --git a/scumm/boxes.cpp b/scumm/boxes.cpp index 24d9d625f7..ec0d3ea3cd 100644 --- a/scumm/boxes.cpp +++ b/scumm/boxes.cpp @@ -135,6 +135,7 @@ byte Scumm::getBoxFlags(int box) { void Scumm::setBoxScale(int box, int scale) { Box *ptr = getBoxBaseAddr(box); + assert(ptr); if (_features & GF_AFTER_V8) ptr->v8.scale = TO_LE_32(scale); else if (_features & GF_AFTER_V2) @@ -144,13 +145,18 @@ void Scumm::setBoxScale(int box, int scale) { } void Scumm::setBoxScaleSlot(int box, int slot) { - Box *b = getBoxBaseAddr(box); - b->v8.scaleSlot = TO_LE_32(slot); + Box *ptr = getBoxBaseAddr(box); + assert(ptr); + ptr->v8.scaleSlot = TO_LE_32(slot); } int Scumm::getScale(int box, int x, int y) { + if (_features & GF_NO_SCALLING) + return 255; + Box *ptr = getBoxBaseAddr(box); - assert(ptr); + if (!ptr) + return 255; if (_features & GF_AFTER_V8) { int slot = FROM_LE_32(ptr->v8.scaleSlot); @@ -181,9 +187,6 @@ int Scumm::getScale(int box, int x, int y) { } } else return FROM_LE_32(ptr->v8.scale); - } else if (_features & GF_AFTER_V2) { - // FIXME - nothing ?!? - return 255; } else { uint16 scale = READ_LE_UINT16(&ptr->old.scale); @@ -227,15 +230,19 @@ byte Scumm::getNumBoxes() { Box *Scumm::getBoxBaseAddr(int box) { byte *ptr = getResourceAddress(rtMatrix, 2); - if (!ptr) + if (!ptr || box == 255) return NULL; + // FIXME: In "pass to adventure", the loom demo, when bobbin enters // the tent to the elders, box = 2, but ptr[0] = 2 -> errors out. // Hence we disable the check for now. Maybe in PASS (and other old games) // we shouldn't subtract 1 from ptr[0] when performing the check? // this also seems to be incorrect for atari st demo of zak // and assumingly other v2 games - if ((_gameId != GID_MONKEY_EGA) && (_gameId != GID_ZAK)) + if (_gameId == GID_MONKEY_EGA) { + if (box < 0 || box > ptr[0] - 1) + warning("Illegal box %d", box); + } else checkRange(ptr[0] - 1, 0, box, "Illegal box %d"); if (_features & GF_AFTER_V2) @@ -273,7 +280,7 @@ int Scumm::getSpecialBox(int x, int y) { bool Scumm::checkXYInBoxBounds(int b, int x, int y) { BoxCoords box; - if (b == 0 && (!(_features & GF_SMALL_HEADER))) + if (b < 0 || b == Actor::INVALID_BOX) return false; getBoxCoordinates(b, &box); @@ -316,6 +323,7 @@ bool Scumm::checkXYInBoxBounds(int b, int x, int y) { void Scumm::getBoxCoordinates(int boxnum, BoxCoords *box) { Box *bp = getBoxBaseAddr(boxnum); + assert(bp); if (_features & GF_AFTER_V8) { box->ul.x = (short)FROM_LE_32(bp->v8.ulx); diff --git a/scumm/scummvm.cpp b/scumm/scummvm.cpp index cef3d0f6b0..15cb9554df 100644 --- a/scumm/scummvm.cpp +++ b/scumm/scummvm.cpp @@ -542,6 +542,9 @@ Scumm::Scumm (GameDetector *detector, OSystem *syst) _hexdumpScripts = false; _showStack = false; + if (_features & GF_SMALL_HEADER) + Actor::INVALID_BOX = 255; + if (_gameId == GID_ZAK256) { // FmTowns is 320x240 _screenWidth = 320; _screenHeight = 240; |