Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

walk.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2003-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: https://svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-9-1/engines/queen/walk.cpp $
 * $Id: walk.cpp 20975 2006-02-28 22:20:13Z cyx $
 *
 */

#include "common/stdafx.h"
#include "queen/walk.h"

#include "queen/bankman.h"
#include "queen/input.h"
#include "queen/logic.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/queen.h"

namespace Queen {

const MovePersonData Walk::_moveData[] = {
      { "COMPY",       -1,  -6,  1,  6,  0,  0,  0,  0, 12, 12, 1, 14 },
      { "DEINO",       -1,  -8,  1,  8,  0,  0,  0,  0, 11, 11, 1, 10 },
      { "FAYE",        -1,  -6,  1,  6, 13, 18,  7, 12, 19, 22, 2,  5 },
      { "GUARDS",      -1,  -6,  1,  6,  0,  0,  0,  0,  7,  7, 2,  5 },
      { "PRINCESS1",   -1,  -6,  1,  6, 13, 18,  7, 12, 19, 21, 2,  5 },
      { "PRINCESS2",   -1,  -6,  1,  6, 13, 18,  7, 12, 19, 21, 2,  5 },
      { "AMGUARD",     -1,  -6,  1,  6, 13, 18,  7, 12, 19, 21, 2,  5 },
      { "SPARKY",      -1,  -6,  1,  6, 13, 18,  7, 12, 21, 20, 2,  5 },
      { "LOLA_SHOWER", -1,  -6, 55, 60,  0,  0,  0,  0,  7,  7, 2,  5 },
      { "LOLA",       -24, -29, 24, 29,  0,  0,  0,  0, 30, 30, 2,  5 },
      { "BOB",        -15, -20, 15, 20, 21, 26,  0,  0, 27, 29, 2,  5 },
      { "CHEF",        -1,  -4,  1,  4,  0,  0,  0,  0,  1,  5, 2,  4 },
      { "HENRY",       -1,  -6,  1,  6,  0,  0,  0,  0,  7,  7, 2,  6 },
      { "ANDERSON",    -1,  -6,  1,  6,  0,  0,  0,  0,  7,  7, 2,  5 },
      { "JASPAR",      -4,  -9,  4,  9, 16, 21, 10, 15,  1,  3, 1, 10 },
      { "PYGMY",       -7, -12,  7, 12,  0,  0,  0,  0, 27, 27, 2,  5 },
      { "FRANK",        7,  12,  1,  6,  0,  0,  0,  0, 13, 13, 2,  4 },
      { "WEDGEWOOD",  -20, -25, 20, 25,  0,  0,  0,  0,  1,  1, 1,  5 },
      { "TMPD",        -1,  -6,  1,  6, 13, 18,  7, 12, 19, 21, 2,  5 },
      { "IAN",         -1,  -6,  1,  6,  0,  0,  0,  0,  7,  7, 2,  6 },
      { "*",            0,   0,  0,  0,  0,  0,  0,  0,  0,  0, 0,  0 }
};

Walk::Walk(QueenEngine *vm)
      : _vm(vm) {
}

void Walk::animateJoePrepare() {
      // queen.c l.2748-2788
      uint16 i;
      for (i = 1; i <= _walkDataCount; ++i) {

            WalkData *pwd = &_walkData[i];

            if (pwd->dx < 0) {
                  pwd->anim.set(11, 18, DIR_LEFT);
            } else {
                  pwd->anim.set(11, 18, DIR_RIGHT);
            }

            int16 k = ABS(pwd->dy);
            int16 ds = pwd->area->scaleDiff();
            if (ds > 0) {
                  k *= ((k * ds) / pwd->area->box.yDiff()) / 2;
            }

            if (ABS(pwd->dx) < k) {
                  if (pwd->dy < 0) {
                        if (ds < 0) {
                              pwd->anim.set(19, 24, DIR_FRONT);
                        } else {
                              pwd->anim.set(25, 30, DIR_BACK);
                        }
                  } else if (pwd->dy > 0) {
                        if (ds < 0) {
                              pwd->anim.set(25, 30, DIR_BACK);
                        } else {
                              pwd->anim.set(19, 24, DIR_FRONT);
                        }
                  }
            }
      }
}

void Walk::animateJoe() {
      // queen.c l.2789-2835
      uint16 lastDirection = 0;
      uint16 i;
      BobSlot *pbs = _vm->graphics()->bob(0);
      _vm->logic()->joeFacing(_walkData[1].anim.facing);
      _vm->logic()->joeScale(_walkData[1].area->calcScale(pbs->y));
      _vm->logic()->joeFace();
      for (i = 1; i <= _walkDataCount && !_joeInterrupted; ++i) {

            WalkData *pwd = &_walkData[i];

            // area has been turned off, see if we should execute a cutaway
            if (pwd->area->mapNeighbours < 0) {
                  // queen.c l.2838-2911
                  _vm->logic()->handleSpecialArea(pwd->anim.facing, pwd->areaNum, i);
                  _joeMoveBlock = true;
                  return;
            }
            if (lastDirection != pwd->anim.facing) {
                  pbs->animNormal(pwd->anim.firstFrame, pwd->anim.lastFrame, 1, false, false);
            }

            uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * 6 / 100;
            pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed);
            pbs->xflip = (pbs->xdir < 0);
            while (pbs->moving) {
                  // adjust Joe's movespeed according to scale
                  pbs->scale = pwd->area->calcScale(pbs->y);
                  _vm->logic()->joeScale(pbs->scale);
                  pbs->scaleWalkSpeed(6);
                  _vm->update(true);
                  if (_vm->input()->cutawayQuit() || _vm->logic()->joeWalk() == JWM_EXECUTE) {
                        stopJoe();
                        break;
                  }
            }
            lastDirection = pwd->anim.facing;
      }
      _vm->logic()->joeFacing(lastDirection);
}

void Walk::animatePersonPrepare(const MovePersonData *mpd, int direction) {
      // queen.c l.2469-2572
      int i;
      for (i = 1; i <= _walkDataCount; ++i) {

            WalkData *pwd = &_walkData[i];

            if (pwd->dx < 0) {
                  pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT);
            } else if (pwd->dx > 0) {
                  pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
            } else {
                  if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
                        pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
                  } else {
                        // we have specific moves for this actor, see what direction they were last facing
                        if (direction == -3) {
                              // previously facing right
                              pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT);
                        } else {
                              // previously facing left
                              pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
                        }
                  }
            }

            int16 k = ABS(pwd->dy);
            int16 ds = pwd->area->scaleDiff();
            if (ds > 0) {
                  k *= ((k * ds) / pwd->area->box.yDiff()) / 2;
            }

            if (ABS(pwd->dx) < k) {
                  if (pwd->dy < 0) {
                        if (mpd->walkBack1 > 0) {
                              pwd->anim.set(mpd->walkBack1, mpd->walkBack2, DIR_BACK);
                        } else if (pwd->dx < 0) {
                              pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_BACK);
                        } else {
                              pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_BACK);
                        }
                  } else if (pwd->dy > 0) {
                        if (mpd->walkFront1 > 0) {
                              pwd->anim.set(mpd->walkFront1, mpd->walkFront2, DIR_FRONT);
                        } else if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
                              if (pwd->dx < 0) {
                                    pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT);
                              } else {
                                    pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT);
                              }
                        } else {
                              // we have a special move for left/right, so select that instead!
                              if (direction == -3) {
                                    // previously facing right
                                    pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT);
                              } else {
                                    // previously facing left
                                    pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT);
                              }
                        }
                  }
            }
      }
}

void Walk::animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction) {
      // queen.c l.2572-2651
      BobSlot *pbs = _vm->graphics()->bob(bobNum);

      // check to see which way person should be facing
      if (mpd->walkLeft1 == mpd->walkRight1) {
            pbs->xflip = (direction == -3);
      } else {
            // they have special walk for left and right, so don't flip
            pbs->xflip = false;
      }

      uint16 i;
      for (i = 1; i <= _walkDataCount; ++i) {
            WalkData *pwd = &_walkData[i];

            // unpack necessary frames for bob animation
            uint16 dstFrame = image;
            uint16 srcFrame = ABS(pwd->anim.firstFrame);
            while (srcFrame <= ABS(pwd->anim.lastFrame)) {
                  _vm->bankMan()->unpack(srcFrame, dstFrame, bankNum);
                  ++dstFrame;
                  ++srcFrame;
            }
            // pass across bobs direction ONLY if walk is a mirror flip!
            if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
                  pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, pbs->xflip);
            } else {
                  pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, false);
            }

            // move other actors at correct speed relative to scale
            uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * mpd->moveSpeed / 100;
            pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed);

            // flip if one set of frames for actor
            if (mpd->walkLeft1 < 0 || ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
                  pbs->xflip = pwd->dx < 0;
            }

            while (pbs->moving) {
                  _vm->update();
                  pbs->scale = pwd->area->calcScale(pbs->y);
                  pbs->scaleWalkSpeed(mpd->moveSpeed);
                  if (_vm->input()->cutawayQuit()) {
                        stopPerson(bobNum);
                        break;
                  }
            }
      }
}

int16 Walk::moveJoe(int direction, int16 endx, int16 endy, bool inCutaway) {
      _joeInterrupted = false;
      _joeMoveBlock = false;
      int16 can = 0;
      initWalkData();

      uint16 oldx = _vm->graphics()->bob(0)->x;
      uint16 oldy = _vm->graphics()->bob(0)->y;

      _vm->logic()->joeWalk(JWM_MOVE);

      uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
      uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy);

      debug(9, "Walk::moveJoe(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos);

      // if in cutaway, allow Joe to walk anywhere
      if (newPos == 0 && inCutaway) {
            incWalkData(oldx, oldy, endx, endy, oldPos);
      } else {
            if (calc(oldPos, newPos, oldx, oldy, endx, endy)) {
                  if (_walkDataCount > 0) {
                        animateJoePrepare();
                        animateJoe();
                        if (_joeInterrupted) {
                              can = -1;
                        }
                  }
            } else {
                  // path has been blocked, make Joe say so
                  _vm->logic()->makeJoeSpeak(4);
                  can = -1;
            }
      }

      _vm->graphics()->bob(0)->animating = false;
      if (_joeMoveBlock) {
            can = -2;
            _joeMoveBlock = false;
      } else if (direction > 0) {
            _vm->logic()->joeFacing(direction);
      }
      _vm->logic()->joePrevFacing(_vm->logic()->joeFacing());
      _vm->logic()->joeFace();
      return can;
}

int16 Walk::movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction) {
      if (endx == 0 && endy == 0) {
            warning("Walk::movePerson() - endx == 0 && endy == 0");
            return 0;
      }

      int16 can = 0;
      initWalkData();

      uint16 bobNum = pp->actor->bobNum;
      uint16 bankNum = pp->actor->bankNum;

      uint16 oldx = _vm->graphics()->bob(bobNum)->x;
      uint16 oldy = _vm->graphics()->bob(bobNum)->y;

      uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
      uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy);

      debug(9, "Walk::movePerson(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos);

      // find MovePersonData associated to Person
      const MovePersonData *mpd = _moveData;
      while (mpd->name[0] != '*') {
            if (scumm_stricmp(mpd->name, pp->name) == 0) {
                  break;
            }
            ++mpd;
      }

      if (calc(oldPos, newPos, oldx, oldy, endx, endy)) {
            if (_walkDataCount > 0) {
                  animatePersonPrepare(mpd, direction);
                  animatePerson(mpd, curImage, bobNum, bankNum, direction);
            }
      } else {
            can = -1;
      }

      uint16 standingFrame = 31 + bobNum;

      // make other person face the right direction
      BobSlot *pbs = _vm->graphics()->bob(bobNum);
      pbs->endx = endx;
      pbs->endy = endy;
      pbs->animating = false;
      pbs->scale = _walkData[_walkDataCount].area->calcScale(endy);
      if (_walkData[_walkDataCount].anim.facing == DIR_BACK) {
            _vm->bankMan()->unpack(mpd->backStandingFrame, standingFrame, bankNum);
      } else {
            _vm->bankMan()->unpack(mpd->frontStandingFrame, standingFrame, bankNum);
      }
      uint16 obj = _vm->logic()->objectForPerson(bobNum);
      if (_walkData[_walkDataCount].dx < 0) {
            _vm->logic()->objectData(obj)->image = -3;
            pbs->xflip = true;
      } else {
            _vm->logic()->objectData(obj)->image = -4;
            pbs->xflip = false;
      }
      pbs->frameNum = standingFrame;
      return can;
}

void Walk::stopJoe() {
      BobSlot *pbs = _vm->graphics()->bob(0);
      pbs->moving = false;
      _joeInterrupted = true;
}

void Walk::stopPerson(uint16 bobNum) {
      BobSlot *pbs = _vm->graphics()->bob(bobNum);
      pbs->x = pbs->endx;
      pbs->y = pbs->endy;
      pbs->moving = false;
}

bool Walk::calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y) {
      // if newPos is outside of an AREA then traverse Y axis until an AREA is found
      if (newPos == 0) {
            newPos = findAreaPosition(&x, &y, true);
      }

      // do the same for oldPos in case Joe somehow sits on the border of an AREA
      // and does not register
      if (oldPos == 0) {
            oldPos = findAreaPosition(&oldx, &oldy, false);
      }

      if (oldPos == newPos) {
            incWalkData(oldx, oldy, x, y, newPos);
            return true;
      } else if (calcPath(oldPos, newPos)) {
            uint16 i;
            int16 px = oldx;
            int16 py = oldy;
            for (i = 2; i <= _areaListCount; ++i) {
                  uint16 a1 = _areaList[i - 1];
                  uint16 a2 = _areaList[i];
                  const Area *pa1 = &_roomArea[a1];
                  const Area *pa2 = &_roomArea[a2];
                  uint16 x1 = calcC(pa1->box.x1, pa1->box.x2, pa2->box.x1, pa2->box.x2, px);
                  uint16 y1 = calcC(pa1->box.y1, pa1->box.y2, pa2->box.y1, pa2->box.y2, py);
                  incWalkData(px, py, x1, y1, a1);
                  px = x1;
                  py = y1;
            }
            incWalkData(px, py, x, y, newPos);
            return true;
      }
      return false;
}

int16 Walk::calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc) {
      int16 s1 = MAX(c1, c3);
      int16 s2 = MIN(c2, c4);
      int16 c;
      if ((lastc >= s1 && lastc <= s2) || (lastc >= s2 && lastc <= s1)) {
            c = lastc;
      } else {
            c = (s1 + s2) / 2;
      }
      return c;
}

int16 Walk::findAreaPosition(int16 *x, int16 *y, bool recalibrate) {
      // In order to locate the nearest available area, the original algorithm 
      // computes the horizontal and vertical distances for each available area.
      // Unlike the original, we also compute the diagonal distance.
      // To get an example of this in action, in the room D1, make Joe walking
      // to the wall at the right of the window (just above the radiator). On the
      // original game, Joe will go to the left door...
      uint16 i;
      uint16 pos = 1;
      uint32 minDist = (uint32)~0;
      const Box *b = &_roomArea[1].box;
      for (i = 1; i <= _roomAreaCount; ++i) {

            b = &_roomArea[i].box;

            uint16 dx1 = ABS(b->x1 - *x);
            uint16 dx2 = ABS(b->x2 - *x);
            uint16 dy1 = ABS(b->y1 - *y);
            uint16 dy2 = ABS(b->y2 - *y);
            uint16 csx = MIN(dx1, dx2);
            uint16 csy = MIN(dy1, dy2);

            bool inX = (*x >= b->x1) && (*x <= b->x2);
            bool inY = (*y >= b->y1) && (*y <= b->y2);

            uint32 dist = minDist;
            if (!inX && !inY) {
                  dist = csx * csx + csy * csy;
            } else if (inX) {
                  dist = csy * csy;
            } else if (inY) {
                  dist = csx * csx;
            }

            if (dist < minDist) {
                  minDist = dist;
                  pos = i;
            }
      }
      // we now have the closest area near X,Y, so we can recalibrate
      // the X,Y coord to be in this area
      if (recalibrate) {
            b = &_roomArea[pos].box;
            if (*x < b->x1) *x = b->x1;
            if (*x > b->x2) *x = b->x2;
            if (*y < b->y1) *y = b->y1;
            if (*y > b->y2) *y = b->y2;
      }
      return pos;
}

uint16 Walk::findFreeArea(uint16 area) const {
      uint16 testArea;
      uint16 freeArea = 0;
      uint16 map = ABS(_roomArea[area].mapNeighbours);
      for (testArea = 1; testArea <= _roomAreaCount; ++testArea) {
            int b = _roomAreaCount - testArea;
            if (map & (1 << b)) {
                  // connecting area, check if it's been struck off
                  if (!isAreaStruck(testArea)) {
                        // legitimate connecting area, keep it
                        freeArea = testArea;
                        break;
                  }
            }
      }
      return freeArea;
}

bool Walk::isAreaStruck(uint16 area) const {
      uint16 i;
      bool found = false;
      for (i = 1; i <= _areaStrikeCount; ++i) {
            if (_areaStrike[i] == area) {
                  found = true;
                  break;
            }
      }
      return found;
}

bool Walk::calcPath(uint16 oldArea, uint16 newArea) {
      debug(9, "Walk::calcPath(%d, %d)", oldArea, newArea);
      _areaList[1] = _areaStrike[1] = oldArea;
      _areaListCount = _areaStrikeCount = 1;
      uint16 area = oldArea;
      while (_areaListCount > 0 && area != newArea) {
            area = findFreeArea(area);
            if (!area) {
                  // wrong path, rolling back
                  _areaList[_areaListCount] = 0;
                  --_areaListCount;
                  area = _areaList[_areaListCount];
            } else {
                  ++_areaListCount;
                  assert(_areaListCount < MAX_WALK_DATA);
                  _areaList[_areaListCount] = area;
                  if (!isAreaStruck(area)) {
                        ++_areaStrikeCount;
                        assert(_areaStrikeCount < MAX_WALK_DATA);
                        _areaStrike[_areaStrikeCount] = area;
                  }
            }
      }
      return _areaList[1] != 0;
}

void Walk::initWalkData() {
      uint16 curRoom = _vm->logic()->currentRoom();
      _roomArea = _vm->grid()->area(curRoom, 0);
      _roomAreaCount = _vm->grid()->areaMax(curRoom);

      _walkDataCount = 0;
      memset(_walkData, 0, sizeof(_walkData));
      _areaStrikeCount = 0;
      memset(_areaStrike, 0, sizeof(_areaStrike));
      _areaListCount = 0;
      memset(_areaList, 0, sizeof(_areaList));
}

void Walk::incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 areaNum) {
      debug(9, "Walk::incWalkData(%d, %d, %d)", (x - px), (y - py), areaNum);
      if (px != x || py != y) {
            ++_walkDataCount;
            assert(_walkDataCount < MAX_WALK_DATA);
            WalkData *pwd = &_walkData[_walkDataCount];
            pwd->dx = x - px;
            pwd->dy = y - py;
            pwd->area = &_roomArea[areaNum];
            pwd->areaNum = areaNum;
      }
}

} // End of namespace Queen

Generated by  Doxygen 1.6.0   Back to index