mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 21:59:17 +00:00
273 lines
6.0 KiB
C++
273 lines
6.0 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This code is based on original Soltys source code
|
|
* Copyright (c) 1994-1995 Janusz B. Wisniewski and L.K. Avalon
|
|
*/
|
|
|
|
#include "cge/walk.h"
|
|
#include "cge/cge_main.h"
|
|
|
|
namespace CGE {
|
|
|
|
Cluster::Cluster(CGEEngine *vm, int16 a, int16 b) : _vm(vm) {
|
|
_pt = Common::Point(a, b);
|
|
}
|
|
|
|
Cluster::Cluster(CGEEngine *vm) : _vm(vm) {
|
|
_pt = Common::Point(-1, -1);
|
|
}
|
|
|
|
uint8 &Cluster::cell() {
|
|
return _vm->_clusterMap[_pt.y][_pt.x];
|
|
}
|
|
|
|
bool Cluster::isValid() const {
|
|
return (_pt.x >= 0) && (_pt.x < kMapXCnt) && (_pt.y >= 0) && (_pt.y < kMapZCnt);
|
|
}
|
|
|
|
Walk::Walk(CGEEngine *vm, BitmapPtr *shpl)
|
|
: Sprite(vm, shpl), _dir(kDirNone), _tracePtr(-1), _level(0), _target(-1, -1), _findLevel(-1), _here(vm), _vm(vm) {
|
|
for (int i = 0; i < kMaxFindLevel; i++) {
|
|
Cluster *tmpClust = new Cluster(_vm);
|
|
_trace.push_back(tmpClust);
|
|
}
|
|
}
|
|
|
|
Walk::~Walk() {
|
|
for (uint idx = 0; idx < _trace.size(); ++idx)
|
|
delete _trace[idx];
|
|
}
|
|
|
|
void Walk::tick() {
|
|
if (_flags._hide)
|
|
return;
|
|
|
|
_here = _vm->XZ(_x + _w / 2, _y + _h);
|
|
|
|
if (_dir != kDirNone) {
|
|
_vm->_sys->funTouch();
|
|
for (Sprite *spr = _vm->_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;
|
|
_vm->_commandHandlerTurbo->addCommand(kCmdZTrim, -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;
|
|
}
|
|
/**
|
|
* Turn the character to a given direction
|
|
* @param d Direction
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop the character and reset his direction
|
|
*/
|
|
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(_vm, 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(_vm, (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) {
|
|
_vm->_hero->findWay(spr);
|
|
if (mode < 0) {
|
|
mode = spr->_flags._east;
|
|
if (lower(spr))
|
|
mode += 2;
|
|
}
|
|
}
|
|
// note: insert SNAIL commands in reverse order
|
|
_vm->_commandHandler->insertCommand(kCmdPause, -1, 64, nullptr);
|
|
_vm->_commandHandler->insertCommand(kCmdSeq, -1, kTSeq + mode, this);
|
|
if (spr) {
|
|
_vm->_commandHandler->insertCommand(kCmdWait, -1, -1, _vm->_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(_vm, -1, 0), Cluster(_vm, 1, 0), Cluster(_vm, 0, -1), Cluster(_vm, 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]->_pt = start._pt;
|
|
return true;
|
|
}
|
|
} while (!c.chkBar() && !c.cell());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // End of namespace CGE
|