/
trilirium
/
Pacman
Обзор
Документация
Войти
/
trilirium
/
Pacman
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Pacman.cpp
468 строк
13 KB
Trilirium
first commit
19 июн 2026, 11:07
19 июн 2026, 11:07
d44591e
Код
Авторство
О чём код?
// 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" using namespace Puckman; ///////////////////////////////////////////////////////////////////////////// // 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]; } }