scummvm/engines/cge/walk.cpp

283 lines
5.9 KiB
C++
Raw Normal View History

/* 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 {
2011-07-24 21:42:03 +00:00
Walk *_hero;
uint8 Cluster::_map[kMapZCnt][kMapXCnt];
CGEEngine *Cluster::_vm;
void Cluster::init(CGEEngine *vm) {
_vm = vm;
}
uint8 &Cluster::cell() {
return _map[_b][_a];
}
2011-07-14 12:36:18 +00:00
bool Cluster::isValid() const {
return (_a >= 0) && (_a < kMapXCnt) && (_b >= 0) && (_b < kMapZCnt);
2011-07-14 12:36:18 +00:00
}
Cluster XZ(int x, int y) {
if (y < kMapTop)
y = kMapTop;
if (y > kMapTop + kMapHig - kMapGridZ)
y = kMapTop + kMapHig - kMapGridZ;
return Cluster(x / kMapGridX, (y - kMapTop) / kMapGridZ);
}
Cluster XZ(Couple xy) {
signed char x, y;
xy.split(x, y);
return XZ(x, y);
}
Walk::Walk(CGEEngine *vm, BitmapPtr *shpl)
: Sprite(vm, shpl), _dir(kDirNone), _tracePtr(-1), _level(0), _target(-1, -1), _findLevel(-1), _vm(vm) {
}
2011-07-24 21:42:03 +00:00
void Walk::tick() {
if (_flags._hide)
return;
_here = XZ(_x + _w / 2, _y + _h);
if (_dir != kDirNone) {
Sprite *spr;
_sys->funTouch();
for (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;
}
}
}
2011-08-19 14:04:10 +00:00
if (_flags._hold || _tracePtr < 0) {
park();
2011-08-19 14:04:10 +00:00
} else {
2011-07-14 12:36:18 +00:00
if (_here == _trace[_tracePtr]) {
if (--_tracePtr < 0)
park();
} else {
signed char dx, dz;
2011-07-14 12:36:18 +00:00
(_trace[_tracePtr] - _here).split(dx, dz);
Dir d = (dx) ? ((dx > 0) ? kDirEast : kDirWest) : ((dz > 0) ? kDirSouth : kDirNorth);
turn(d);
}
}
step();
if ((_dir == kDirWest && _x <= 0) ||
(_dir == kDirEast && _x + _w >= kScrWidth) ||
2011-08-19 14:04:10 +00:00
(_dir == kDirSouth && _y + _w >= kWorldHeight - 2)) {
park();
2011-08-19 14:04:10 +00:00
} else {
signed char x; // dummy var
_here.split(x, _z); // take current Z position
2011-07-23 12:31:39 +00:00
_snail_->addCom(kSnZTrim, -1, 0, this); // update Hero's pos in show queue
}
}
2011-07-24 21:42:03 +00:00
int Walk::distance(Sprite *spr) {
2011-08-19 14:04:10 +00:00
int dx = spr->_x - (_x + _w - kWalkSide);
if (dx < 0)
dx = (_x + kWalkSide) - (spr->_x + spr->_w);
if (dx < 0)
dx = 0;
dx /= kMapGridX;
2011-08-19 14:04:10 +00:00
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;
}
}
2011-07-24 21:42:03 +00:00
void Walk::park() {
if (_time == 0)
_time++;
if (_dir != kDirNone) {
step(9 + 4 * _dir + _dir);
_dir = kDirNone;
_tracePtr = -1;
}
}
2011-07-24 21:42:03 +00:00
void Walk::findWay(Cluster c) {
2011-08-19 14:04:10 +00:00
if (c == _here)
return;
for (_findLevel = 1; _findLevel <= kMaxFindLevel; _findLevel++) {
signed char x, z;
_here.split(x, z);
_target = Couple(x, z);
c.split(x, z);
if (find1Way(Cluster(x, z)))
break;
}
2011-08-19 14:04:10 +00:00
_tracePtr = (_findLevel > kMaxFindLevel) ? -1 : (_findLevel - 1);
if (_tracePtr < 0)
noWay();
_time = 1;
}
2011-07-24 21:42:03 +00:00
void Walk::findWay(Sprite *spr) {
2011-08-19 14:04:10 +00:00
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))));
}
2011-07-24 21:42:03 +00:00
bool Walk::lower(Sprite *spr) {
return (spr->_y > _y + (_h * 3) / 5);
}
2011-07-24 21:42:03 +00:00
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
2011-07-23 12:31:39 +00:00
_snail->insCom(kSnPause, -1, 64, NULL);
_snail->insCom(kSnSeq, -1, kTSeq + mode, this);
if (spr) {
2011-07-23 12:31:39 +00:00
_snail->insCom(kSnWait, -1, -1, _hero); /////--------$$$$$$$
//SNINSERT(SNWALK, -1, -1, spr);
}
// sequence is not finished,
// now it is just at sprite appear (disappear) point
}
2011-07-24 21:42:03 +00:00
void Walk::noWay() {
_vm->trouble(kSeqNoWay, kNoWay);
2011-07-14 12:36:18 +00:00
}
2011-08-19 22:03:44 +00:00
bool Cluster::chkBar() const {
assert(_vm->_now <= _vm->_caveMax);
return (_a == _vm->_barriers[_vm->_now]._horz) || (_b == _vm->_barriers[_vm->_now]._vert);
}
2011-07-24 21:42:03 +00:00
bool Walk::find1Way(Cluster c) {
2011-07-14 12:36:18 +00:00
Cluster start = c;
const Cluster tab[4] = { Cluster(-1, 0), Cluster(1, 0), Cluster(0, -1), Cluster(0, 1)};
const int tabLen = 4;
if (c == _target)
2011-07-14 12:36:18 +00:00
// Found destination
return true;
if (_level >= _findLevel)
// Nesting limit
return false;
// Look for barriers
if (c.chkBar())
2011-07-14 12:36:18 +00:00
return false;
if (c.cell())
// Location is occupied
return false;
// Loop through each direction
for (int i = 0; i < tabLen; i++) {
2011-07-14 12:36:18 +00:00
// Reset to starting position
c = start;
do {
c += tab[i];
if (!c.isValid())
// Break to check next direction
break;
// Recursively check for further paths
++_level;
2011-07-15 11:25:03 +00:00
++start.cell();
2011-07-14 12:36:18 +00:00
bool foundPath = find1Way(c);
2011-07-15 11:25:03 +00:00
--start.cell();
2011-07-14 12:36:18 +00:00
--_level;
if (foundPath) {
// Set route point
_trace[_level] = start;
return true;
}
2011-07-15 11:25:03 +00:00
} while (!c.chkBar() && !c.cell());
2011-07-14 12:36:18 +00:00
}
return false;
}
} // End of namespace CGE