📄 pacman.cpp
字号:
// Pacman.cpp
//
/////////////////////////////////////////////////////////////////////////////
#include "Pacman.h"
#include "PacmanDeadSubstates.h"
#include "GameState.h"
#include "Ghost.h"
#include "../InputHandler.h"
#include "../Operations.h"
#include "Path.h"
#include "Task.h"
#include "Video.h"
#include "Writer.h"
#include "Game.h"
/////////////////////////////////////////////////////////////////////////////
// initialization and cleanup
/////////////////////////////////////////////////////////////////////////////
Pacman::Pacman()
{
resetState();
// creates dead substates
deadSubstates[0x00] = new PacmanAliveSubstate();
deadSubstates[0x01] = new PacmanWaitABitSubstate();
deadSubstates[0x02] = new PacmanWaitABitSubstate();
deadSubstates[0x03] = new PacmanWaitABitSubstate();
deadSubstates[0x04] = new PacmanWaitABitSubstate();
deadSubstates[0x05] = new PacmanDead0Substate();
deadSubstates[0x06] = new PacmanDead1Substate();
deadSubstates[0x07] = new PacmanDead2Substate();
deadSubstates[0x08] = new PacmanDead3Substate();
deadSubstates[0x09] = new PacmanDead4Substate();
deadSubstates[0x0a] = new PacmanDead5Substate();
deadSubstates[0x0b] = new PacmanDead6Substate();
deadSubstates[0x0c] = new PacmanDead7Substate();
deadSubstates[0x0d] = new PacmanDead8Substate();
deadSubstates[0x0e] = new PacmanDead9Substate();
deadSubstates[0x0f] = new PacmanDead10Substate();
deadSubstates[0x10] = new PacmanDead11Substate();
}
Pacman::~Pacman()
{
// deletes dead substates
for (int i = 0; i < 0x11; i++){
delete deadSubstates[i];
}
}
void Pacman::resetState()
{
Character::resetState();
wantedOrientation = (Orientation)0;
demoTilePosX = demoTilePosY = 0;
wantedMovOffsX = wantedMovOffsY = 0;
superPillEffect = nearSides = hasToChangeOrientation = false;
movNormalPattern = movSuperPillPattern = 0;
movementDelay = pillsInLastMove = 0;
deadAnimState = deadAnimCnt = 0;
}
void Pacman::initState(bool special)
{
sprite = 0x2c;
color = 9;
if (!special){
posX = 0x80;
posY = 0xc4;
tilePosX = demoTilePosX = 0x2e;
tilePosY = demoTilePosY = 0x38;
movOffsX = wantedMovOffsX = 1;
movOffsY = wantedMovOffsY = 0;
orientation = wantedOrientation = LEFT;
} else {
posX = 0x08;
posY = 0x94;
tilePosX = demoTilePosX = 0x1f;
tilePosY = demoTilePosY = 0x32;
movOffsX = wantedMovOffsX = 1;
movOffsY = wantedMovOffsY = 0;
orientation = wantedOrientation = LEFT;
}
}
/////////////////////////////////////////////////////////////////////////////
// movement
/////////////////////////////////////////////////////////////////////////////
void Pacman::run()
{
// pacman only moves when movement delay is -1
if (movementDelay != -1){
movementDelay--;
return;
}
// check if we have to move according to the movement bit pattern
if (superPillEffect){
movSuperPillPattern = Operations::rotateLeft<UINT32, 32>(movSuperPillPattern);
if ((movSuperPillPattern & 0x01) == 0) return;
} else {
movNormalPattern = Operations::rotateLeft<UINT32, 32>(movNormalPattern);
if ((movNormalPattern & 0x01) == 0) return;
}
// keep track of the number of pills before the move
pillsInLastMove = theGame->levelState[0].eatenPills;
nearSides = (tilePosX < 0x21) || (tilePosX >= 0x3b);
// if we're not in a demo or a cutscene
if (!((theGame->state == DEMO) || (theGame->states[PLAYING]->substate >= 0x10))){
moveNormal();
} else {
moveDemo();
}
}
void Pacman::moveNormal()
{
UINT32 input = theInputHandler->getInput(0);
// if the position is near a tunnel, we can move only in the X axis
if (nearSides){
if ((input & 0x02) == 0){
// left pressed
orientation = LEFT;
movOffsX = movementOffsets[LEFT][0];
movOffsY = movementOffsets[LEFT][1];
} else if ((input & 0x04) == 0){
// right pressed
orientation = RIGHT;
movOffsX = movementOffsets[RIGHT][0];
movOffsY = movementOffsets[RIGHT][1];
}
advanceAndAdjustPacman();
} else {
bool noDirectionPressed = false;
if ((input & 0x02) == 0){
// left pressed
wantedOrientation = LEFT;
wantedMovOffsX = movementOffsets[LEFT][0];
wantedMovOffsY = movementOffsets[LEFT][1];
} else if ((input & 0x04) == 0){
// right pressed
wantedOrientation = RIGHT;
wantedMovOffsX = movementOffsets[RIGHT][0];
wantedMovOffsY = movementOffsets[RIGHT][1];
} else if ((input & 0x01) == 0){
// up pressed
wantedOrientation = UP;
wantedMovOffsX = movementOffsets[UP][0];
wantedMovOffsY = movementOffsets[UP][1];
} else if ((input & 0x08) == 0){
// down pressed
wantedOrientation = DOWN;
wantedMovOffsX = movementOffsets[DOWN][0];
wantedMovOffsY = movementOffsets[DOWN][1];
} else {
// nothing pressed
wantedMovOffsX = movOffsX;
wantedMovOffsY = movOffsY;
wantedOrientation = orientation;
noDirectionPressed = true;
}
// gets tile in pacman desired destination location
int data = theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX + wantedMovOffsX, tilePosY + wantedMovOffsY)];
// it's a blocking tile
if (data >= 0xc0) {
// if the player hasn't pressed a direction
if (noDirectionPressed) {
// if pacman has reached the blocking tile, exit
if ((orientation & 0x01) != 0x01){
if ((posX & 0x07) == 0x04) return;
} else {
if ((posY & 0x07) == 0x04) return;
}
// otherwise advance
advanceAndAdjustPacman();
} else {
// if the player has pressed a direction
// get the associated tile if pacman continues moving as if no direction was pressed
data = theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX + movOffsX, tilePosY + movOffsY)];
// if it's a blocking tile
if (data >= 0xc0){
// if pacman has reached the middle of the tile, it can't move more, exit
if ((orientation & 0x01) != 0x01){
if ((posX & 0x07) == 0x04) return;
} else {
if ((posY & 0x07) == 0x04) return;
}
}
// otherwise advance
advanceAndAdjustPacman();
}
} else {
// if it wasn't a blocking tile, update pacman orientation and movement offset
movOffsX = wantedMovOffsX;
movOffsY = wantedMovOffsY;
orientation = wantedOrientation;
// advance
advanceAndAdjustPacman();
}
}
}
/////////////////////////////////////////////////////////////////////////////
// animation
/////////////////////////////////////////////////////////////////////////////
void Pacman::selectProperAnimation()
{
static int oriAnim[4][8] = {
{ 0x2e, 0x2e, 0x2c, 0x2c, 0x2e, 0x2e, 0x30, 0x30 },
{ 0x30, 0x30, 0x2f, 0x2f, 0x2d, 0x2d, 0x2f, 0x2f },
{ 0x30, 0x30, 0x2e, 0x2e, 0x2c, 0x2c, 0x2e, 0x2e },
{ 0x30, 0x30, 0x2f, 0x2f, 0x2d, 0x2d, 0x2f, 0x2f }
};
static int bigAnim[16] = {
0x14, 0x14, 0x14, 0x14, 0x14, 0x10, 0x10, 0x10, 0x10, 0x14, 0x14, 0x14, 0x14, 0x18, 0x18, 0x18
};
// select proper animation based on orientation and position
// in the first cutscene, animate big pacman
if (theGame->cutsceneState[0] >= 5){
sprite = bigAnim[posX & 0x0f];
color = 0x16;
} else {
// animate normal pacman
int pos = ((orientation & 0x01) == 0x01) ? posY : posX;
sprite = oriAnim[orientation][pos & 0x07];
flipX = orientation == LEFT;
flipY = orientation == UP;
}
}
void Pacman::animateDead()
{
// execute specific code for pacman dead state
deadSubstates[deadAnimState]->run();
}
void Pacman::draw()
{
// in the first cutscene, show big pacman
if (theGame->cutsceneState[0] >= 5){
// draw pacman's sprites
Sprite spr1(posX + 8, posY - 16, sprite + 0, color);
Sprite spr2(posX - 8, posY - 16, sprite + 1, color);
Sprite spr3(posX + 8, posY, sprite + 2, color);
Sprite spr4(posX - 8, posY, sprite + 3, color);
theGame->sprites.push_back(spr1);
theGame->sprites.push_back(spr2);
theGame->sprites.push_back(spr3);
theGame->sprites.push_back(spr4);
} else {
// otherwise, show normal pacman
// draw pacman's sprites
Sprite spr(posX, posY, sprite, color, flipX, flipY, superPillEffect ? 1 : 0);
theGame->sprites.push_back(spr);
}
// clear sprite attributes
flipX = flipY = false;
priority = 0;
}
/////////////////////////////////////////////////////////////////////////////
// IA in demo mode
/////////////////////////////////////////////////////////////////////////////
void Pacman::moveDemo()
{
// if pacman is moving in the Y axis
if (movOffsY != 0){
// if we are in the middle of a tile in the Y axis
if ((posY & 0x07) == 0x04){
moveDemoMiddle();
} else {
moveDemoNoMiddle();
}
} else {
// if we are in the middle of a tile in the Y axis
if ((posX & 0x07) == 0x04){
moveDemoMiddle();
} else {
moveDemoNoMiddle();
}
}
}
// updates position and tile position if it's in the middle of a tile
void Pacman::moveDemoMiddle()
{
// if wraparound has happened or it's going to happen, don't search a better move
if (!checkWrapAround()){
// search a better movement
moveInDemoState();
}
// updates tile position
demoTilePosX += wantedMovOffsX;
demoTilePosY += wantedMovOffsY;
movOffsX = wantedMovOffsX;
movOffsY = wantedMovOffsY;
orientation = wantedOrientation;
moveDemoNoMiddle();
}
// updates position and tile position if it's not in the middle of a tile
void Pacman::moveDemoNoMiddle()
{
// moves
posX += movOffsX;
posY += movOffsY;
posX &= 0xff;
posY &= 0xff;
checkForEatedItems();
}
void Pacman::moveInDemoState()
{
// if red ghost is in panic mode
if (theGame->ghost[RED]->panicMode){
// try to go to pink ghost position
Path::calcPath(demoTilePosX, demoTilePosY, wantedOrientation,
theGame->ghost[PINK]->tilePosX, theGame->ghost[PINK]->tilePosY,
wantedMovOffsX, wantedMovOffsY, wantedOrientation);
} else {
// try to go away from pink ghost
Path::calcPath(demoTilePosX, demoTilePosY, wantedOrientation,
2*tilePosX - theGame->ghost[PINK]->tilePosX,
2*tilePosY - theGame->ghost[PINK]->tilePosY,
wantedMovOffsX, wantedMovOffsY, wantedOrientation);
}
}
/////////////////////////////////////////////////////////////////////////////
// helper methods
/////////////////////////////////////////////////////////////////////////////
void Pacman::advanceAndAdjustPacman()
{
// moves pacman
posX += movOffsX;
posY += movOffsY;
posX &= 0xff;
posY &= 0xff;
// if pacman is moving through the X axis, center its Y position or viceversa
if ((orientation & 0x01) != 0x01){
if ((posY & 0x07) < 0x04) posY++;
if ((posY & 0x07) > 0x04) posY--;
} else {
if ((posX & 0x07) < 0x04) posX++;
if ((posX & 0x07) > 0x04) posX--;
}
// check if has eaten a pill or a fruit
checkForEatedItems();
}
// check if pacman has eaten a fruit or a pill and update score accordingly
void Pacman::checkForEatedItems()
{
// set tile position
Video::pos2TilePos(posX, posY, tilePosX, tilePosY);
// if pacman was near the sides, return
if (nearSides){
nearSides = false;
return;
}
// if there's a fruit displayed
if ((theGame->fruitPosition != 0) && (theGame->fruitEntry != 0)){
// if we're on the fruit position
if ((posX == 0x80) && (posY == 0x94)){
// update the score
theGame->updateScore(theGame->fruitEntry);
// print fruit points in screen
Writer::printString(0x15 + theGame->fruitEntry);
theGame->fruitPosition = 0;
// schedules a new task to clean the fruit points in 2 seconds
theScheduler->addScheduledTask(new Task(5, Task::TEN_HUNDREDTHS, 20));
// TODOSOUND: play eaten fruit sound in channel 2
}
}
// sets no delay to move pacman
movementDelay = -1;
// gets current tile
int data = theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX, tilePosY)];
// if it's a pill
if ((data == 0x10) || (data == 0x14)){
// update eaten pills
theGame->levelState[0].eatenPills++;
// clears eaten pill tile
theGame->VRAM[Video::tilePos2ScreenCoord(tilePosX, tilePosY)] = 0x40;
// update score
theGame->updateScore((data == 0x10) ? 0 : 1);
// set movement delay
movementDelay = (data == 0x10) ? 1 : 6;
// check if a ghost has to leave home
theGame->checkGhostsExitHomeCounters();
// if pacman has eaten a super pill, notify the world of the state change
if (movementDelay == 6){
theGame->initSuperPillVars();
}
// TODOSOUND: depending if current number of eaten pills it's even or odd changes eated pill sound
}
}
// checks if pacman has to change it's orientation (because pacman has eaten a big pill in the demo)
void Pacman::checkOrientationChanges()
{
if (hasToChangeOrientation){
hasToChangeOrientation = false;
// set the opposite orientation and update movement offsets
wantedOrientation = (Orientation) (orientation ^ 0x02);
wantedMovOffsX = movementOffsets[wantedOrientation][0];
wantedMovOffsY = movementOffsets[wantedOrientation][1];
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -