📄 ghost.cpp
字号:
// Ghost.cpp
//
/////////////////////////////////////////////////////////////////////////////
#include "Ghost.h"
#include "GameState.h"
#include "../Operations.h"
#include "Path.h"
#include "GhostAction.h"
#include "Video.h"
#include "Game.h"
/////////////////////////////////////////////////////////////////////////////
// initialization and cleanup
/////////////////////////////////////////////////////////////////////////////
Ghost::Ghost(GhostIDs ghostId)
{
id = ghostId;
}
Ghost::~Ghost()
{
}
void Ghost::resetState()
{
Character::resetState();
prevOrientation = (Orientation)0;
actualTilePosX = actualTilePosY = 0;
prevMovOffsX = prevMovOffsY = 0;
destTilePosX = destTilePosY = 0;
movNormalPattern = movPanicPattern = movTunnelPattern = 0;
state = substate = 0;
panicMode = false;
hasToChangeOrientation = false;
}
void Ghost::initState(bool special)
{
sprite = 0x20;
color = 2*id + 1;
if (special){
posX = 0x00;
posY = 0x94;
tilePosX = actualTilePosX = 0x1e;
tilePosY = actualTilePosY = 0x32;
movOffsX = prevMovOffsX = 0x01;
movOffsY = prevMovOffsY = 0x00;
orientation = prevOrientation = LEFT;
}
}
/////////////////////////////////////////////////////////////////////////////
// movement
/////////////////////////////////////////////////////////////////////////////
void Ghost::run()
{
// if it's going for pacman and alive, check if we have to move
if ((state == 0) && (substate == 1)){
// 0x1b is set at the tunnels
if (theGame->CRAM[Video::tilePos2ScreenCoord(actualTilePosX, actualTilePosY)] == 0x1b) {
// check if we have to move according to the movement bit pattern
movTunnelPattern = Operations::rotateLeft<UINT32, 32>(movTunnelPattern);
if ((movTunnelPattern & 0x01) == 0) return;
} else if (panicMode){
// check if we have to move according to the movement bit pattern
movPanicPattern = Operations::rotateLeft<UINT32, 32>(movPanicPattern);
if ((movPanicPattern & 0x01) == 0) return;
} else {
// check if we have to move according to the movement bit pattern
movNormalPattern = Operations::rotateLeft<UINT32, 32>(movNormalPattern);
if ((movNormalPattern & 0x01) == 0) return;
}
// move
move();
}
}
void Ghost::move()
{
// if we are moving in the Y axis
if (prevMovOffsY != 0){
// if we are in the middle of a tile in the Y axis
if ((posY & 0x07) == 0x04){
moveMiddle();
} else {
moveNoMiddle();
}
} else {
// if we are in the middle of a tile in the X axis
if ((posX & 0x07) == 0x04){
moveMiddle();
} else {
moveNoMiddle();
}
}
}
// updates position and tile position if it's in the middle of a tile
void Ghost::moveMiddle()
{
bool hasToMove = false;
// if wraparound has happened or it's going to happen, don't search a better move
if (!checkWrapAround()){
if (panicMode){
// if we're in panic mode, move
hasToMove = true;
} else {
// get CRAM data in tile position
int data = theGame->CRAM[Video::tilePos2ScreenCoord(tilePosX, tilePosY)];
// 0x1a is set at ghost home and near pacman initial positions
if (data != 0x1a) {
hasToMove = true;
}
}
}
// check if the ghost has to change it's orientation and act accordingly
checkOrientationChanges();
// updates tile position
tilePosX += movOffsX;
tilePosY += movOffsY;
prevMovOffsX = movOffsX;
prevMovOffsY = movOffsY;
// keep a copy of the current orientation in case we have to
// change our orientation before the next move in the middle
prevOrientation = orientation;
moveNoMiddle();
// this need to be executed here because the original game moves after
// the IRQ. If this is executed where hasToMove is set to true, the ghost
// sometimes can't detect a wall (especially when data != 0x1a)
if (hasToMove){
if (panicMode){
moveInPanicState();
} else {
moveInNormalState();
}
}
}
// updates position and tile position if it's not in the middle of a tile
void Ghost::moveNoMiddle()
{
// moves
posX += prevMovOffsX;
posY += prevMovOffsY;
posX &= 0xff;
posY &= 0xff;
// sets destination tile position (used in collision detection)
Video::pos2TilePos(posX, posY, actualTilePosX, actualTilePosY);
}
void Ghost::moveAtHome()
{
moveAtHomeAction[substate]->run(this);
}
void Ghost::moveWhenKilled()
{
moveWhenKilledAction[state]->run(this);
}
void Ghost::moveInPanicState()
{
// if it's alive, moves randomly
if (state == 0){
Path::calcRandomPath(tilePosX, tilePosY, orientation, movOffsX, movOffsY, orientation);
} else { // otherwise, go home
Path::calcPath(tilePosX, tilePosY, orientation, 0x2e, 0x2c, movOffsX, movOffsY, orientation);
}
}
/////////////////////////////////////////////////////////////////////////////
// animation
/////////////////////////////////////////////////////////////////////////////
void Ghost::selectProperAnimation()
{
// set panic mode sprite
sprite = 0x1c + theGame->ghostsAnimationFrame;
// if the ghost is alive and in panic mode, we're done
if ((state == 0) && (panicMode)) return;
// otherwise set ghost sprite in normal or dead state
sprite = 0x20 + 2*((int)orientation) + theGame->ghostsAnimationFrame;
}
void Ghost::draw()
{
// draw ghost's sprites
Sprite spr(posX, posY, sprite, color, flipX, flipY, (theGame->collisionObject == id + 1) ? 1 : 0);
theGame->sprites.push_back(spr);
// clear sprite attributes
flipX = flipY = false;
priority = 0;
}
/////////////////////////////////////////////////////////////////////////////
// orientation methods
/////////////////////////////////////////////////////////////////////////////
// changes ghost orientation
void Ghost::changeOrientation()
{
// set the opposite orientation and update movement offsets
orientation = (Orientation) (prevOrientation ^ 0x02);
movOffsX = movementOffsets[orientation][0];
movOffsY = movementOffsets[orientation][1];
// special case in demo mode
if (theGame->states[DEMO]->substate == 0x22){
prevMovOffsX = movOffsX;
prevMovOffsY = movOffsY;
prevOrientation = orientation;
}
}
// checks if the ghost has to change it's orientation (because pacman has eaten a big pill)
void Ghost::checkOrientationChanges()
{
if (hasToChangeOrientation){
hasToChangeOrientation = false;
// change orientation
changeOrientation();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -