mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 22:28:10 +00:00
Implemented and debugged the walking framework.
The hero does not walk yet (it still teleports to the target immediately), but that is just because the actual walking algorithm is left trivial first. However, the main game loop, callbacks, and waiting all already work with the general framework. svn-id: r45648
This commit is contained in:
parent
692aea8e8d
commit
14f2685134
@ -84,6 +84,7 @@ DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc)
|
||||
Common::addDebugChannel(kDraciLogicDebugLevel, "logic", "Game logic debug info");
|
||||
Common::addDebugChannel(kDraciAnimationDebugLevel, "animation", "Animation debug info");
|
||||
Common::addDebugChannel(kDraciSoundDebugLevel, "sound", "Sound debug info");
|
||||
Common::addDebugChannel(kDraciWalkingDebugLevel, "walking", "Walking debug info");
|
||||
|
||||
// Don't forget to register your random source
|
||||
g_eventRec.registerRandomSource(_rnd, "draci");
|
||||
|
@ -105,7 +105,8 @@ enum {
|
||||
kDraciArchiverDebugLevel = 1 << 2,
|
||||
kDraciLogicDebugLevel = 1 << 3,
|
||||
kDraciAnimationDebugLevel = 1 << 4,
|
||||
kDraciSoundDebugLevel = 1 << 5
|
||||
kDraciSoundDebugLevel = 1 << 5,
|
||||
kDraciWalkingDebugLevel = 1 << 6
|
||||
};
|
||||
|
||||
} // End of namespace Draci
|
||||
|
@ -248,16 +248,17 @@ void Game::handleOrdinaryLoop(int x, int y) {
|
||||
|
||||
_walkingState.setCallback(&obj->_program, obj->_look);
|
||||
|
||||
if (!obj->_imLook) {
|
||||
if (obj->_imLook || !_currentRoom._heroOn) {
|
||||
_walkingState.callback();
|
||||
} else {
|
||||
if (obj->_lookDir == kDirectionLast) {
|
||||
walkHero(x, y, obj->_lookDir);
|
||||
} else {
|
||||
walkHero(obj->_lookX, obj->_lookY, obj->_lookDir);
|
||||
}
|
||||
}
|
||||
|
||||
_walkingState.callback();
|
||||
} else {
|
||||
_walkingState.setCallback(NULL, 0);
|
||||
walkHero(x, y, kDirectionLast);
|
||||
}
|
||||
}
|
||||
@ -272,16 +273,17 @@ void Game::handleOrdinaryLoop(int x, int y) {
|
||||
if (_vm->_script->testExpression(obj->_program, obj->_canUse)) {
|
||||
_walkingState.setCallback(&obj->_program, obj->_use);
|
||||
|
||||
if (!obj->_imUse) {
|
||||
if (obj->_imUse || !_currentRoom._heroOn) {
|
||||
_walkingState.callback();
|
||||
} else {
|
||||
if (obj->_useDir == kDirectionLast) {
|
||||
walkHero(x, y, obj->_useDir);
|
||||
} else {
|
||||
walkHero(obj->_useX, obj->_useY, obj->_useDir);
|
||||
}
|
||||
}
|
||||
|
||||
_walkingState.callback();
|
||||
} else {
|
||||
_walkingState.setCallback(NULL, 0);
|
||||
walkHero(x, y, kDirectionLast);
|
||||
}
|
||||
} else {
|
||||
@ -289,6 +291,7 @@ void Game::handleOrdinaryLoop(int x, int y) {
|
||||
_walkingState.setCallback(&_currentRoom._program, _currentRoom._use);
|
||||
_walkingState.callback();
|
||||
} else {
|
||||
_walkingState.setCallback(NULL, 0);
|
||||
walkHero(x, y, kDirectionLast);
|
||||
}
|
||||
}
|
||||
@ -436,6 +439,29 @@ void Game::advanceAnimationsAndTestLoopExit() {
|
||||
setExitLoop(true);
|
||||
}
|
||||
|
||||
// Walk the hero. The WalkingState class handles everything including
|
||||
// proper timing.
|
||||
if (_walkingState.isActive()) {
|
||||
if (!_walkingState.continueWalking()) {
|
||||
// Walking has finished.
|
||||
bool exitLoop = false;
|
||||
if (_loopSubstatus == kInnerUntilExit) {
|
||||
// The callback may run another inner loop (for
|
||||
// example, a dialogue). Reset the loop
|
||||
// substatus temporarily to the outer one.
|
||||
exitLoop = true;
|
||||
setLoopSubstatus(kOuterLoop);
|
||||
}
|
||||
debugC(2, kDraciWalkingDebugLevel, "Finished walking");
|
||||
_walkingState.callback(); // clears callback pointer first
|
||||
if (exitLoop) {
|
||||
debugC(3, kDraciWalkingDebugLevel, "Exiting from the inner loop");
|
||||
setExitLoop(true);
|
||||
setLoopSubstatus(kInnerUntilExit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advance animations (this may also call setExitLoop(true) in the
|
||||
// callbacks) and redraw screen
|
||||
_vm->_anims->drawScene(_vm->_screen->getSurface());
|
||||
@ -942,7 +968,7 @@ void Game::redrawWalkingPath(int id, byte colour, const WalkingPath &path) {
|
||||
}
|
||||
|
||||
void Game::positionHero(const Common::Point &p, SightDirection dir) {
|
||||
debugC(3, kDraciLogicDebugLevel, "Jump to x: %d y: %d", p.x, p.y);
|
||||
debugC(3, kDraciWalkingDebugLevel, "Jump to x: %d y: %d", p.x, p.y);
|
||||
|
||||
_hero = p;
|
||||
Movement movement = kStopRight;
|
||||
@ -965,33 +991,38 @@ void Game::positionHero(const Common::Point &p, SightDirection dir) {
|
||||
playHeroAnimation(movement);
|
||||
}
|
||||
|
||||
void Game::walkHero(int x, int y, SightDirection dir) {
|
||||
// Needed for the map room with empty walking map. For some reason,
|
||||
// findNearestWalkable() takes several seconds with 100% CPU to finish
|
||||
// (correctly).
|
||||
if (!_currentRoom._heroOn)
|
||||
return;
|
||||
|
||||
Common::Point target;
|
||||
Common::Point Game::findNearestWalkable(int x, int y) const {
|
||||
Surface *surface = _vm->_screen->getSurface();
|
||||
target = _walkingMap.findNearestWalkable(x, y, surface->getDimensions());
|
||||
debugC(3, kDraciLogicDebugLevel, "Walk to x: %d y: %d", target.x, target.y);
|
||||
return _walkingMap.findNearestWalkable(x, y, surface->getDimensions());
|
||||
}
|
||||
|
||||
void Game::walkHero(int x, int y, SightDirection dir) {
|
||||
if (!_currentRoom._heroOn) {
|
||||
// Nothing to do. Happens for example in the map.
|
||||
return;
|
||||
}
|
||||
|
||||
Common::Point target = findNearestWalkable(x, y);
|
||||
|
||||
// Compute the shortest and obliqued path.
|
||||
WalkingPath shortestPath, obliquePath;
|
||||
_walkingMap.findShortestPath(_hero, target, &shortestPath);
|
||||
// TODO: test reachability and react
|
||||
_walkingMap.obliquePath(shortestPath, &obliquePath);
|
||||
debugC(2, kDraciWalkingDebugLevel, "Walking path lengths: shortest=%d oblique=%d", shortestPath.size(), obliquePath.size());
|
||||
if (_vm->_showWalkingMap) {
|
||||
redrawWalkingPath(kWalkingShortestPathOverlay, kWalkingShortestPathOverlayColour, shortestPath);
|
||||
redrawWalkingPath(kWalkingObliquePathOverlay, kWalkingObliquePathOverlayColour, obliquePath);
|
||||
}
|
||||
|
||||
_walkingState.setPath(_hero, target, Common::Point(x, y),
|
||||
// Start walking. Walking will be gradually advanced by
|
||||
// advanceAnimationsAndTestLoopExit(), which also handles calling the
|
||||
// callback and stopping the walk at the end. If the hero is already
|
||||
// walking at this point, this command will cancel the previous path
|
||||
// and replace it by the current one (the callback has already been
|
||||
// reset by our caller).
|
||||
_walkingState.startWalking(_hero, target, Common::Point(x, y), dir,
|
||||
_walkingMap.getDelta(), obliquePath);
|
||||
|
||||
// FIXME: Need to add proper walking (this only warps the dragon to position)
|
||||
positionHero(target, dir);
|
||||
}
|
||||
|
||||
void Game::loadItem(int itemID) {
|
||||
|
@ -206,9 +206,10 @@ public:
|
||||
return n;
|
||||
}
|
||||
|
||||
void clearPath() { _walkingState.clearPath(); }
|
||||
void positionHero(const Common::Point &p, SightDirection dir);
|
||||
void walkHero(int x, int y, SightDirection dir);
|
||||
Common::Point findNearestWalkable(int x, int y) const;
|
||||
void stopWalking() { _walkingState.stopWalking(); } // and clear callback
|
||||
void positionHero(const Common::Point &p, SightDirection dir); // teleport the dragon
|
||||
void walkHero(int x, int y, SightDirection dir); // start walking and leave callback as is
|
||||
int getHeroX() const { return _hero.x; }
|
||||
int getHeroY() const { return _hero.y; }
|
||||
void positionAnimAsHero(Animation *anim);
|
||||
|
@ -660,8 +660,8 @@ void Script::stayOn(Common::Queue<int> ¶ms) {
|
||||
SightDirection dir = static_cast<SightDirection> (params.pop());
|
||||
|
||||
// Jumps into the given position regardless of the walking map.
|
||||
_vm->_game->positionHero(Common::Point(x, y), dir);
|
||||
_vm->_game->clearPath();
|
||||
_vm->_game->stopWalking();
|
||||
_vm->_game->positionHero(_vm->_game->findNearestWalkable(x, y), dir);
|
||||
}
|
||||
|
||||
void Script::walkOn(Common::Queue<int> ¶ms) {
|
||||
@ -675,6 +675,7 @@ void Script::walkOn(Common::Queue<int> ¶ms) {
|
||||
|
||||
// Constructs an optimal path and starts walking there. No callback
|
||||
// will be called at the end nor will the loop-body exit.
|
||||
_vm->_game->stopWalking();
|
||||
_vm->_game->walkHero(x, y, dir);
|
||||
}
|
||||
|
||||
@ -687,12 +688,12 @@ void Script::walkOnPlay(Common::Queue<int> ¶ms) {
|
||||
int y = params.pop();
|
||||
SightDirection dir = static_cast<SightDirection> (params.pop());
|
||||
|
||||
_vm->_game->stopWalking();
|
||||
_vm->_game->walkHero(x, y, dir);
|
||||
|
||||
// HACK: This (shouldExit==true) should be an onDest action when hero
|
||||
// walking is properly implemented For now, we just go throught the
|
||||
// loop-body once to redraw the screen.
|
||||
_vm->_game->loop(kInnerUntilExit, true);
|
||||
// Walk in an inner loop until the hero has arrived at the target
|
||||
// point. Then the loop-body will exit.
|
||||
_vm->_game->loop(kInnerUntilExit, false);
|
||||
}
|
||||
|
||||
void Script::newRoom(Common::Queue<int> ¶ms) {
|
||||
|
@ -422,14 +422,17 @@ bool WalkingMap::managedToOblique(WalkingPath *path) const {
|
||||
return improved;
|
||||
}
|
||||
|
||||
void WalkingState::clearPath() {
|
||||
void WalkingState::stopWalking() {
|
||||
_path.clear();
|
||||
_callback = NULL;
|
||||
}
|
||||
|
||||
void WalkingState::setPath(const Common::Point &p1, const Common::Point &p2, const Common::Point &mouse, const Common::Point &delta, const WalkingPath& path) {
|
||||
void WalkingState::startWalking(const Common::Point &p1, const Common::Point &p2,
|
||||
const Common::Point &mouse, SightDirection dir,
|
||||
const Common::Point &delta, const WalkingPath& path) {
|
||||
_path = path;
|
||||
_mouse = mouse;
|
||||
_dir = dir;
|
||||
|
||||
if (!_path.size()) {
|
||||
return;
|
||||
@ -440,6 +443,8 @@ void WalkingState::setPath(const Common::Point &p1, const Common::Point &p2, con
|
||||
// they are different pixels.
|
||||
_path.push_back(p2);
|
||||
}
|
||||
debugC(2, kDraciWalkingDebugLevel, "Starting walking [%d,%d] -> [%d,%d] in %d segments",
|
||||
p1.x, p1.y, p2.x, p2.y, _path.size());
|
||||
|
||||
// The first and last point are available with pixel accurracy.
|
||||
_path[0] = p1;
|
||||
@ -461,6 +466,7 @@ void WalkingState::callback() {
|
||||
if (!_callback) {
|
||||
return;
|
||||
}
|
||||
debugC(2, kDraciWalkingDebugLevel, "Calling walking callback");
|
||||
|
||||
// Fetch the dedicated objects' title animation / current frame
|
||||
Animation *titleAnim = _vm->_anims->getAnimation(kTitleText);
|
||||
@ -470,8 +476,21 @@ void WalkingState::callback() {
|
||||
titleAnim->markDirtyRect(_vm->_screen->getSurface());
|
||||
title->setText("");
|
||||
|
||||
_vm->_script->run(*_callback, _callbackOffset);
|
||||
const GPL2Program *originalCallback = _callback;
|
||||
_callback = NULL;
|
||||
_vm->_script->run(*originalCallback, _callbackOffset);
|
||||
|
||||
_vm->_mouse->cursorOn();
|
||||
}
|
||||
|
||||
bool WalkingState::continueWalking() {
|
||||
// FIXME: do real walking instead of immediately exiting. Compare the
|
||||
// current dragon's animation phase with the stored one, and if they
|
||||
// differ, walk another step.
|
||||
debugC(2, kDraciWalkingDebugLevel, "Continuing walking");
|
||||
_vm->_game->positionHero(_path[_path.size() - 1], _dir);
|
||||
_path.clear();
|
||||
return false; // finished
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -99,21 +99,32 @@ struct GPL2Program;
|
||||
|
||||
class WalkingState {
|
||||
public:
|
||||
explicit WalkingState(DraciEngine *vm) : _vm(vm) { clearPath(); }
|
||||
explicit WalkingState(DraciEngine *vm) : _vm(vm) { stopWalking(); }
|
||||
~WalkingState() {}
|
||||
|
||||
void clearPath();
|
||||
void setPath(const Common::Point &p1, const Common::Point &p2, const Common::Point &mouse, const Common::Point &delta, const WalkingPath& path);
|
||||
void stopWalking();
|
||||
void startWalking(const Common::Point &p1, const Common::Point &p2,
|
||||
const Common::Point &mouse, SightDirection dir,
|
||||
const Common::Point &delta, const WalkingPath& path);
|
||||
const WalkingPath& getPath() const { return _path; }
|
||||
|
||||
void setCallback(const GPL2Program *program, uint16 offset);
|
||||
void callback();
|
||||
|
||||
bool isActive() const { return _path.size() > 0; }
|
||||
|
||||
// Advances the hero along the path and changes animation accordingly.
|
||||
// Walking MUST be active when calling this method. When the hero has
|
||||
// arrived to the target, clears the path and returns false, but leaves
|
||||
// the callback untouched (the caller must call it).
|
||||
bool continueWalking();
|
||||
|
||||
private:
|
||||
DraciEngine *_vm;
|
||||
|
||||
WalkingPath _path;
|
||||
Common::Point _mouse;
|
||||
SightDirection _dir;
|
||||
|
||||
const GPL2Program *_callback;
|
||||
uint16 _callbackOffset;
|
||||
|
Loading…
Reference in New Issue
Block a user