scummvm/engines/cge/walk.cpp
2011-09-14 08:15:30 +02:00

270 lines
5.8 KiB
C++

/* 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.
*
*/
/*
* This code is based on original Soltys source code
* Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
*/
#include "cge/walk.h"
#include "cge/cge_main.h"
namespace CGE {
Walk *_hero;
uint8 Cluster::_map[kMapZCnt][kMapXCnt];
CGEEngine *Cluster::_vm;
void Cluster::init(CGEEngine *vm) {
_vm = vm;
}
uint8 &Cluster::cell() {
return _map[_pt.y][_pt.x];
}
bool Cluster::isValid() const {
return (_pt.x >= 0) && (_pt.x < kMapXCnt) && (_pt.y >= 0) && (_pt.y < kMapZCnt);
}
Cluster XZ(int16 x, int16 y) {
if (y < kMapTop)
y = kMapTop;
if (y > kMapTop + kMapHig - kMapGridZ)
y = kMapTop + kMapHig - kMapGridZ;
return Cluster(x / kMapGridX, (y - kMapTop) / kMapGridZ);
}
Walk::Walk(CGEEngine *vm, BitmapPtr *shpl)
: Sprite(vm, shpl), _dir(kDirNone), _tracePtr(-1), _level(0), _target(-1, -1), _findLevel(-1), _vm(vm) {
}
void Walk::tick() {
if (_flags._hide)
return;
_here = XZ(_x + _w / 2, _y + _h);
if (_dir != kDirNone) {
_sys->funTouch();
for (Sprite *spr = _vga->_showQ->first(); spr; spr = spr->_next) {
if (distance(spr) < 2) {
if (!spr->_flags._near) {
_vm->feedSnail(spr, kNear);
spr->_flags._near = true;
}
} else {
spr->_flags._near = false;
}
}
}
if (_flags._hold || _tracePtr < 0) {
park();
} else {
if (_here._pt == _trace[_tracePtr]._pt) {
if (--_tracePtr < 0)
park();
} else {
Common::Point tmpPoint = _trace[_tracePtr]._pt - _here._pt;
int16 dx = tmpPoint.x;
int16 dz = tmpPoint.y;
Dir d = (dx) ? ((dx > 0) ? kDirEast : kDirWest) : ((dz > 0) ? kDirSouth : kDirNorth);
turn(d);
}
}
step();
if ((_dir == kDirWest && _x <= 0) ||
(_dir == kDirEast && _x + _w >= kScrWidth) ||
(_dir == kDirSouth && _y + _w >= kWorldHeight - 2)) {
park();
} else {
// take current Z position
_z = _here._pt.y;
_snail_->addCom(kSnZTrim, -1, 0, this); // update Hero's pos in show queue
}
}
int Walk::distance(Sprite *spr) {
int dx = spr->_x - (_x + _w - kWalkSide);
if (dx < 0)
dx = (_x + kWalkSide) - (spr->_x + spr->_w);
if (dx < 0)
dx = 0;
dx /= kMapGridX;
int dz = spr->_z - _z;
if (dz < 0)
dz = - dz;
dx = dx * dx + dz * dz;
for (dz = 1; dz * dz < dx; dz++)
;
return dz - 1;
}
void Walk::turn(Dir d) {
Dir dir = (_dir == kDirNone) ? kDirSouth : _dir;
if (d != _dir) {
step((d == dir) ? (1 + dir + dir) : (9 + 4 * dir + d));
_dir = d;
}
}
void Walk::park() {
if (_time == 0)
_time++;
if (_dir != kDirNone) {
step(9 + 4 * _dir + _dir);
_dir = kDirNone;
_tracePtr = -1;
}
}
void Walk::findWay(Cluster c) {
if (c._pt == _here._pt)
return;
for (_findLevel = 1; _findLevel <= kMaxFindLevel; _findLevel++) {
_target = _here._pt;
int16 x = c._pt.x;
int16 z = c._pt.y;
if (find1Way(Cluster(x, z)))
break;
}
_tracePtr = (_findLevel > kMaxFindLevel) ? -1 : (_findLevel - 1);
if (_tracePtr < 0)
noWay();
_time = 1;
}
void Walk::findWay(Sprite *spr) {
if (!spr || spr == this)
return;
int x = spr->_x;
int z = spr->_z;
if (spr->_flags._east)
x += spr->_w + _w / 2 - kWalkSide;
else
x -= _w / 2 - kWalkSide;
findWay(Cluster((x / kMapGridX),
((z < kMapZCnt - kDistMax) ? (z + 1)
: (z - 1))));
}
bool Walk::lower(Sprite *spr) {
return (spr->_y > _y + (_h * 3) / 5);
}
void Walk::reach(Sprite *spr, int mode) {
if (spr) {
_hero->findWay(spr);
if (mode < 0) {
mode = spr->_flags._east;
if (lower(spr))
mode += 2;
}
}
// note: insert SNAIL commands in reverse order
_snail->insCom(kSnPause, -1, 64, NULL);
_snail->insCom(kSnSeq, -1, kTSeq + mode, this);
if (spr) {
_snail->insCom(kSnWait, -1, -1, _hero);
//SNINSERT(SNWALK, -1, -1, spr);
}
// sequence is not finished,
// now it is just at sprite appear (disappear) point
}
void Walk::noWay() {
_vm->trouble(kSeqNoWay, kNoWay);
}
bool Cluster::chkBar() const {
assert(_vm->_now <= kSceneMax);
return (_pt.x == _vm->_barriers[_vm->_now]._horz) || (_pt.y == _vm->_barriers[_vm->_now]._vert);
}
bool Walk::find1Way(Cluster c) {
const Cluster tab[4] = { Cluster(-1, 0), Cluster(1, 0), Cluster(0, -1), Cluster(0, 1)};
const int tabLen = 4;
if (c._pt == _target)
// Found destination
return true;
if (_level >= _findLevel)
// Nesting limit
return false;
// Look for barriers
if (c.chkBar())
return false;
if (c.cell())
// Location is occupied
return false;
// Loop through each direction
Cluster start = c;
for (int i = 0; i < tabLen; i++) {
// Reset to starting position
c = start;
do {
c._pt += tab[i]._pt;
if (!c.isValid())
// Break to check next direction
break;
// Recursively check for further paths
++_level;
++start.cell();
bool foundPath = find1Way(c);
--start.cell();
--_level;
if (foundPath) {
// Set route point
_trace[_level] = start;
return true;
}
} while (!c.chkBar() && !c.cell());
}
return false;
}
} // End of namespace CGE