Logo Search packages:      
Sourcecode: scummvm version File versions

checks.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-13-1/engines/agi/checks.cpp $
 * $Id: checks.cpp 28966 2007-09-19 08:40:12Z peres001 $
 *
 */


#include "agi/agi.h"

namespace Agi {

int AgiEngine::checkPosition(VtEntry *v) {
      debugC(4, kDebugLevelSprites, "check position @ %d, %d", v->xPos, v->yPos);

      if (v->xPos < 0 ||
                  v->xPos + v->xSize > _WIDTH ||
                  v->yPos - v->ySize + 1 < 0 ||
                  v->yPos >= _HEIGHT ||
                  ((~v->flags & IGNORE_HORIZON) && v->yPos <= _game.horizon)) {
            debugC(4, kDebugLevelSprites, "check position failed: x=%d, y=%d, h=%d, w=%d",
                        v->xPos, v->yPos, v->xSize, v->ySize);
            return 0;
      }

      /* MH1 needs this, but it breaks LSL1 */
      if (agiGetRelease() >= 0x3000) {
            if (v->yPos < v->ySize)
                  return 0;
      }

      return 1;
}

/**
 * Check if there's another object on the way
 */
int AgiEngine::checkCollision(VtEntry *v) {
      VtEntry *u;

      if (v->flags & IGNORE_OBJECTS)
            return 0;

      for (u = _game.viewTable; u < &_game.viewTable[MAX_VIEWTABLE]; u++) {
            if ((u->flags & (ANIMATED | DRAWN)) != (ANIMATED | DRAWN))
                  continue;

            if (u->flags & IGNORE_OBJECTS)
                  continue;

            /* Same object, check next */
            if (v->entry == u->entry)
                  continue;

            /* No horizontal overlap, check next */
            if (v->xPos + v->xSize < u->xPos || v->xPos > u->xPos + u->xSize)
                  continue;

            /* Same y, return error! */
            if (v->yPos == u->yPos) {
                  debugC(4, kDebugLevelSprites, "check returns 1 (object %d)", v->entry);
                  return 1;
            }

            /* Crossed the baseline, return error! */
            if ((v->yPos > u->yPos && v->yPos2 < u->yPos2) ||
                        (v->yPos < u->yPos && v->yPos2 > u->yPos2)) {
                  debugC(4, kDebugLevelSprites, "check returns 1 (object %d)", v->entry);
                  return 1;
            }
      }

      return 0;

}

int AgiEngine::checkPriority(VtEntry *v) {
      int i, trigger, water, pass, pri;
      uint8 *p0;

      if (~v->flags & FIXED_PRIORITY) {
            /* Priority bands */
            v->priority = _game.priTable[v->yPos];
      }

      trigger = 0;
      water = 0;
      pass = 1;

      if (v->priority == 0x0f) {
            // Check ego
            if (v->entry == 0) {
                  setflag(fEgoTouchedP2, trigger ? true : false);
                  setflag(fEgoWater, water ? true : false);
            }

            return pass;
      }

      water = 1;

      p0 = &_game.sbuf16c[v->xPos + v->yPos * _WIDTH];

      for (i = 0; i < v->xSize; i++, p0++) {
            pri = *p0 >> 4;

            if (pri == 0) {   /* unconditional black. no go at all! */
                  pass = 0;
                  break;
            }

            if (pri == 3)     /* water surface */
                  continue;

            water = 0;

            if (pri == 1) {   /* conditional blue */
                  if (v->flags & IGNORE_BLOCKS)
                        continue;

                  debugC(4, kDebugLevelSprites, "Blocks observed!");
                  pass = 0;
                  break;
            }

            if (pri == 2) {   /* trigger */
                  debugC(4, kDebugLevelSprites, "stepped on trigger");
                  if (!_debug.ignoretriggers)
                        trigger = 1;
            }
      }

      if (pass) {
            if (!water && v->flags & ON_WATER)
                  pass = 0;
            if (water && v->flags & ON_LAND)
                  pass = 0;
      }

      // Check ego
      if (v->entry == 0) {
            setflag(fEgoTouchedP2, trigger ? true : false);
            setflag(fEgoWater, water ? true : false);
      }

      return pass;
}

/*
 * Public functions
 */

/**
 * Update position of objects
 * This function updates the position of all animated, updating view
 * table entries according to its motion type, step size, etc. The
 * new position must be valid according to the sprite positioning
 * rules, otherwise the previous position will be kept.
 */
void AgiEngine::updatePosition() {
      VtEntry *v;
      int x, y, oldX, oldY, border;

      _game.vars[vBorderCode] = 0;
      _game.vars[vBorderTouchEgo] = 0;
      _game.vars[vBorderTouchObj] = 0;

      for (v = _game.viewTable; v < &_game.viewTable[MAX_VIEWTABLE]; v++) {
            if ((v->flags & (ANIMATED | UPDATE | DRAWN)) != (ANIMATED | UPDATE | DRAWN)) {
                  continue;
            }

            if (v->stepTimeCount != 0) {
                  if (--v->stepTimeCount != 0)
                        continue;
            }

            v->stepTimeCount = v->stepTime;

            x = oldX = v->xPos;
            y = oldY = v->yPos;

            /* If object has moved, update its position */
            if (~v->flags & UPDATE_POS) {
                  int dx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
                  int dy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
                  x += v->stepSize * dx[v->direction];
                  y += v->stepSize * dy[v->direction];
            }

            /* Now check if it touched the borders */
            border = 0;

            /* Check left/right borders */
            if (x < 0) {
                  x = 0;
                  border = 4;
            } else if (x <= 0 && agiGetRelease() == 0x3086) {     /* KQ4 */
                  x = 0;      /* See Sarien bug #590462 */
                  border = 4;
            } else if (v->entry == 0 && x == 0 && v->flags & ADJ_EGO_XY) {
                  /* Extra test to walk west clicking the mouse */
                  x = 0;
                  border = 4;
            } else if (x + v->xSize > _WIDTH) {
                  x = _WIDTH - v->xSize;
                  border = 2;
            }

            /* Check top/bottom borders. */
            if (y - v->ySize + 1 < 0) {
                  y = v->ySize - 1;
                  border = 1;
            } else if (y > _HEIGHT - 1) {
                  y = _HEIGHT - 1;
                  border = 3;
            } else if ((~v->flags & IGNORE_HORIZON) && y <= _game.horizon) {
                  debugC(4, kDebugLevelSprites, "y = %d, horizon = %d", y, _game.horizon);
                  y = _game.horizon + 1;
                  border = 1;
            }

            /* Test new position. rollback if test fails */
            v->xPos = x;
            v->yPos = y;
            if (checkCollision(v) || !checkPriority(v)) {
                  v->xPos = oldX;
                  v->yPos = oldY;
                  border = 0;
                  fixPosition(v->entry);
            }

            if (border != 0) {
                  if (isEgoView(v)) {
                        _game.vars[vBorderTouchEgo] = border;
                  } else {
                        _game.vars[vBorderCode] = v->entry;
                        _game.vars[vBorderTouchObj] = border;
                  }
                  if (v->motion == MOTION_MOVE_OBJ) {
                        inDestination(v);
                  }
            }

            v->flags &= ~UPDATE_POS;
      }
}

/**
 * Adjust position of a sprite
 * This function adjusts the position of a sprite moving it until
 * certain criteria is matched. According to priority and control line
 * data, a sprite may not always appear at the location we specified.
 * This behaviour is also known as the "Budin-Sonneveld effect".
 *
 * @param n view table entry number
 */
void AgiEngine::fixPosition(int n) {
      VtEntry *v = &_game.viewTable[n];
      int count, dir, size;

      debugC(4, kDebugLevelSprites, "adjusting view table entry #%d (%d,%d)", n, v->xPos, v->yPos);

      /* test horizon */
      if ((~v->flags & IGNORE_HORIZON) && v->yPos <= _game.horizon)
            v->yPos = _game.horizon + 1;

      dir = 0;
      count = size = 1;

      while (!checkPosition(v) || checkCollision(v) || !checkPriority(v)) {
            switch (dir) {
            case 0:     /* west */
                  v->xPos--;
                  if (--count)
                        continue;
                  dir = 1;
                  break;
            case 1:     /* south */
                  v->yPos++;
                  if (--count)
                        continue;
                  dir = 2;
                  size++;
                  break;
            case 2:     /* east */
                  v->xPos++;
                  if (--count)
                        continue;
                  dir = 3;
                  break;
            case 3:     /* north */
                  v->yPos--;
                  if (--count)
                        continue;
                  dir = 0;
                  size++;
                  break;
            }

            count = size;
      }

      debugC(4, kDebugLevelSprites, "view table entry #%d position adjusted to (%d,%d)", n, v->xPos, v->yPos);
}

} // End of namespace Agi

Generated by  Doxygen 1.6.0   Back to index