#include #include "Gameplay.h" #include "PacMan.h" #include "Enemy.h" #include "Bonus.h" #include "EnemyReward.h" #include "constant.h" #include "InputLabel.h" #include "InputMenu.h" extern PALETTE currentColorPalette; extern PALETTE energizerPalette; extern RGB black, blue, white, red, pink, cyan, orange; extern char oldPauseKey; extern bool joystickInstalled; extern int enableJoystick; extern int joystickPollDelay; extern InputLabel *joyLitPointer; extern int installSoundReturnValue; extern int soundFx, music; extern FONT *joystix; extern FONT *pacmania; extern SAMPLE *dieSound; extern SAMPLE *dotSound; extern SAMPLE *energizerSound; extern SAMPLE *enemySound; extern SAMPLE *bonusSound; extern SAMPLE *gainSound; extern MIDI *popcorn; extern int layerCount; extern BITMAP *layer[maxLayers]; extern bool forgotScreen; extern int lives, gains, score, levelNumber; extern volatile bool heartBeat; extern volatile int countUp; extern void unload(); extern void drawSpritesForBlitting(); extern void showFrame(); extern void drawFrame(); extern void go(int *x, int *y, int step, int xMax, int yMax, direction way); extern void go(double *x, double *y, double step, int xMax, int yMax, direction way); extern bool actorCollision(int x1, int y1, int x2, int y2, int threshold); extern void reportScore(); extern void reportLives(); extern void guiMessage(BITMAP *canvas, const char *msg); extern void setHeartBeat(); extern void incrementCountUp(); static const int yesNoMenuLength = 2; static const char *yesNoMenuCommand[yesNoMenuLength] = { "Yes", "No" }; static const int yesNoMenuAcceleratorKey[yesNoMenuLength] = { 0, 0 }; void Gameplay::flood(int map[boardW][boardH], int x, int y, int value) { if (map[x][y] != 1 && map[x][y] != value) { map[x][y] = value; flood(map, x, (y + boardH - 1) % boardH, value); flood(map, (x + boardW - 1) % boardW, y, value); flood(map, (x + 1) % boardW, y, value); flood(map, x, (y + 1) % boardH, value); } } Level* Gameplay::getLevelPointer() { return ¤tLevel; } PacMan* Gameplay::getPlayerPointer() { return &player; } int Gameplay::getCagePosition() { return cagePosition; } RLE_SPRITE* Gameplay::getRleBonus() { return rleBonus; } bool Gameplay::energized() { return framesRendered < energyExpires; } void Gameplay::installTimers() { countUp = framesRendered; install_int(incrementCountUp, 1000 / frameRate); heartBeat = false; install_int(setHeartBeat, energyBlinkDelay); } void Gameplay::uninstallTimers() { remove_int(incrementCountUp); remove_int(setHeartBeat); } bool Gameplay::parse() { int x, y; int map[boardW][boardH]; for (x = 0; x < boardW; x++) for (y = 0; y < boardH; y++) map[x][y] = currentLevel.getTile(x, y) == wallTile; x = currentLevel.getEnemyStartX(); y = currentLevel.getEnemyStartY(); for (vector::iterator i = currentLevel.enemyRouteBegin(); i != currentLevel.enemyRouteEnd(); i++) { map[x][(y + boardH - 1) % boardH] = 1; map[(x + boardW - 1) % boardW][y] = 1; map[(x + 1) % boardW][y] = 1; map[x][(y + 1) % boardH] = 1; if (currentLevel.getTile(x, y) == wallTile) return false; go(&x, &y, 1, boardW, boardH, *i); } if (x != currentLevel.getEnemyStartX() || y != currentLevel.getEnemyStartY()) return false; flood(map, currentLevel.getPacManStartX(), currentLevel.getPacManStartY(), 2); bool createdGate = false; for (vector::iterator i = currentLevel.enemyRouteBegin(); i != currentLevel.enemyRouteEnd(); i++) { map[x][y] = 0; if (map[(x + boardW - 1) % boardW][(y + boardH - 1) % boardH] == 2 || map[x][(y + boardH - 2) % boardH] == 2 || map[(x + 1) % boardW][(y + boardH - 1) % boardH] == 2) switch (currentLevel.getTile(x, (y + boardH - 1) % boardH)) { case dotTile: currentLevel.setTile(x, (y + boardH - 1) % boardH, gateTile); createdGate = true; break; case energyTile: currentLevel.setTile(x, (y + boardH - 1) % boardH, gateEnergyTile); createdGate = true; } else switch (currentLevel.getTile(x, (y + boardH - 1) % boardH)) { case dotTile: currentLevel.setTile(x, (y + boardH - 1) % boardH, forbiddenTile); break; case energyTile: currentLevel.setTile(x, (y + boardH - 1) % boardH, forbiddenEnergyTile); } if (map[(x + boardW - 1) % boardW][(y + boardH - 1) % boardH] == 2 || map[(x + boardW - 2) % boardW][y] == 2 || map[(x + boardW - 1) % boardW][(y + 1) % boardH] == 2) switch (currentLevel.getTile((x + boardW - 1) % boardW, y)) { case dotTile: currentLevel.setTile((x + boardW - 1) % boardW, y, gateTile); createdGate = true; break; case energyTile: currentLevel.setTile((x + boardW - 1) % boardW, y, gateEnergyTile); createdGate = true; } else switch (currentLevel.getTile((x + boardW - 1) % boardW, y)) { case dotTile: currentLevel.setTile((x + boardW - 1) % boardW, y, forbiddenTile); break; case energyTile: currentLevel.setTile((x + boardW - 1) % boardW, y, forbiddenEnergyTile); } if (map[(x + 1) % boardW][(y + boardH - 1) % boardH] == 2 || map[(x + 2) % boardW][y] == 2 || map[(x + 1) % boardW][(y + 1) % boardH] == 2) switch (currentLevel.getTile((x + 1) % boardW, y)) { case dotTile: currentLevel.setTile((x + 1) % boardW, y, gateTile); createdGate = true; break; case energyTile: currentLevel.setTile((x + 1) % boardW, y, gateEnergyTile); createdGate = true; } else switch (currentLevel.getTile((x + 1) % boardW, y)) { case dotTile: currentLevel.setTile((x + 1) % boardW, y, forbiddenTile); break; case energyTile: currentLevel.setTile((x + 1) % boardW, y, forbiddenEnergyTile); } if (map[(x + boardW - 1) % boardW][(y + 1) % boardH] == 2 || map[x][(y + 2) % boardH] == 2 || map[(x + 1) % boardW][(y + 1) % boardH] == 2) switch (currentLevel.getTile(x, (y + 1) % boardH)) { case dotTile: currentLevel.setTile(x, (y + 1) % boardH, gateTile); createdGate = true; break; case energyTile: currentLevel.setTile(x, (y + 1) % boardH, gateEnergyTile); createdGate = true; } else switch (currentLevel.getTile(x, (y + 1) % boardH)) { case dotTile: currentLevel.setTile(x, (y + 1) % boardH, forbiddenTile); break; case energyTile: currentLevel.setTile(x, (y + 1) % boardH, forbiddenEnergyTile); } go(&x, &y, 1, boardW, boardH, *i); } bool foundAccessibleBonusPosition = false; x = currentLevel.getBonusStartX(); y = currentLevel.getBonusStartY(); for (vector::iterator i = currentLevel.bonusRouteBegin(); ; i++) { if (currentLevel.getTile(x, y) == wallTile) return false; if (map[x][y] == 2) foundAccessibleBonusPosition = true; if (i == currentLevel.bonusRouteEnd()) break; switch (*i) { case up: if (y == 0) return false; else y--; break; case left: if (x == 0) return false; else x--; break; case right: if (x == boardW - 1) return false; else x++; break; case down: if (y == boardH - 1) return false; else y++; } } if (x != 0 && x != boardW - 1 && y != 0 && y != boardH - 1) return false; dots = 0; for (x = 0; x < boardW; x++) for (y = 0; y < boardH; y++) switch (map[x][y]) { case 0: switch (currentLevel.getTile(x, y)) { case dotTile: currentLevel.setTile(x, y, forbiddenTile); break; case energyTile: currentLevel.setTile(x, y, forbiddenEnergyTile); } break; case 2: if (currentLevel.getTile(x, y) == dotTile) dots++; } return dots && createdGate && foundAccessibleBonusPosition; } void Gameplay::play() { if (keyboard_needs_poll()) if (poll_keyboard()) { unload(); allegro_message("Couldn't poll the keyboard!"); exit(0); } oldPauseKey = key[KEY_PAUSE]; PALETTE fadePalette; energyShown = true; currentColorPalette[energyColor] = white; currentColorPalette[blinkyColor] = red; currentColorPalette[pinkyColor] = pink; currentColorPalette[inkyColor] = cyan; currentColorPalette[clydeColor] = orange; layerCount = 3; clear_bitmap(layer[0]); clear_bitmap(layer[1]); clear_bitmap(layer[2]); set_clip_rect(layer[1], 0, 0, boardW * tileSize - 1, boardH * tileSize - 1); if (!installSoundReturnValue && music) if (play_midi(popcorn, 1)) { unload(); allegro_message("Couldn't start the music."); exit(0); } drawGame(); drawFrame(); showFrame(); fade_in(currentColorPalette, fadeSpeed); clear_keybuf(); do { if (forgotScreen) { drawSpritesForBlitting(); drawGame(); drawFrame(); showFrame(); forgotScreen = false; } rest(0); } while (!keypressed()); installTimers(); while (lives >= 0 && dots > 0) { while (countUp == framesRendered) { if (heartBeat) { energyShown = !energyShown; currentColorPalette[energyColor] = energyShown ? white : black; heartBeat = false; } if (forgotScreen) { drawSpritesForBlitting(); drawGame(); forgotScreen = false; } } bool die = false; while (countUp > framesRendered && !die) { if (keyboard_needs_poll()) if (poll_keyboard()) { unload(); allegro_message("Couldn't poll the keyboard!"); exit(0); } if (joystickInstalled && enableJoystick) if (poll_joystick()) { guiMessage(layer[2], "Couldn't poll joystick."); joyLitPointer->setCaption("Polling failed"); joystickInstalled = false; } if (fruit.getDud() && rand() % bonusExpectedWait == 0) fruit = Bonus(this, false); bool monstersTurnAround = energyAttained == framesRendered - 1 || energyExpires == framesRendered || rand() % monstersTurnAroundExpectedWait == 0; player.move(); for (int i = 0; i < 4; i++) { if (monstersTurnAround) monster[i].turnAroundIfAcceptable(); monster[i].move(); } cagePosition = ((cagePosition + enemyStep) % (currentLevel.getEnemyRouteSize() * tileSize)) / enemyStep * enemyStep; fruit.move(); const int gridX = (player.getX() + halfTileSize) / tileSize; const int gridY = (player.getY() + halfTileSize) / tileSize; switch (currentLevel.getTile(gridX, gridY)) { case dotTile: currentLevel.setTile(gridX, gridY, emptyTile); currentLevel.drawTile(layer[0], gridX, gridY); score += dotValue; reportScore(); dots--; if (!installSoundReturnValue && soundFx) { stop_sample(dotSound); play_sample(dotSound, 255, gridX * 255 / (boardW - 1), 1000, 0); } break; case energyTile: currentLevel.setTile(gridX, gridY, emptyTile); currentLevel.drawTile(layer[0], gridX, gridY); score += energizerValue; reportScore(); const int lifespan = frameRate * currentLevel.getEnergizerLifespan() / 1000; if (energized()) energyExpires += lifespan; else { energyAttained = framesRendered; energyExpires = energyAttained + lifespan; enemiesConsumed = 0; } if (!installSoundReturnValue && soundFx) { stop_sample(energizerSound); play_sample(energizerSound, 255, gridX * 255 / (boardW - 1), 1000, 0); } } for (int i = 0; i < 4; i++) if (actorCollision(player.getX(), player.getY(), monster[i].getX(), monster[i].getY(), tileSize)) if (energized()) { reward[enemiesConsumed] = EnemyReward(monster[i].getX(), monster[i].getY(), enemiesConsumed, true); monster[i].goToCage(); score += enemyValue[enemiesConsumed]; reportScore(); enemiesConsumed++; if (!installSoundReturnValue && soundFx) play_sample(enemySound, 255, monster[i].getX() * 255 / (SCREEN_W - 1), 1000, 0); } else die = true; if (!fruit.getDud() && actorCollision(player.getX(), player.getY(), fruit.getX(), fruit.getY(), halfTileSize + bonusSize / 2)) { fruit.remove(); score += 100 * levelNumber; reportScore(); if (!installSoundReturnValue && soundFx) { int pan = fruit.getX() * 255 / (SCREEN_W - 1); if (pan < 0) pan = 0; if (pan > 255) pan = 255; play_sample(bonusSound, 255, pan, 1000, 0); } } if (score / 10000 > gains) { gains++; lives++; reportLives(); if (!installSoundReturnValue && soundFx) play_sample(gainSound, 255, 128, 1000, 0); } if (heartBeat) { energyShown = !energyShown; currentColorPalette[energyColor] = energyShown ? white : black; heartBeat = false; } if (forgotScreen) { drawSpritesForBlitting(); drawGame(); forgotScreen = false; } framesRendered++; } const int sinceAttained = framesRendered - energyAttained; const int untilExpires = energyExpires - framesRendered; const int fadePosition = energized() ? (untilExpires <= 64 ? untilExpires : (sinceAttained <= 64 ? sinceAttained : 64 ) ) : 0; fade_interpolate(currentColorPalette, energizerPalette, fadePalette, fadePosition, blinkyColor, clydeColor); drawActors(); drawFrame(); showFrame(); set_color(energyColor, currentColorPalette + energyColor); set_color(blinkyColor, fadePalette + blinkyColor); set_color(pinkyColor, fadePalette + pinkyColor); set_color(inkyColor, fadePalette + inkyColor); set_color(clydeColor, fadePalette + clydeColor); if (die) { uninstallTimers(); if (!installSoundReturnValue && soundFx) play_sample(dieSound, 255, player.getX() * 255 / (SCREEN_W - 1), 1000, 0); player.playDeathAnimation(layer[1]); lives--; if (lives < 0) { char buf[bufSizeForItoa + 11] = "Dots left: "; itoa(dots, buf + 11, 10); textout_centre_ex(layer[2], pacmania, "Game", SCREEN_W / 2, SCREEN_H / 2 - text_height(pacmania), 14, -1); textout_centre_ex(layer[2], pacmania, "Over", SCREEN_W / 2, SCREEN_H / 2, 14, -1); textout_right_ex(layer[0], joystix, buf, livesDisplayX2, livesDisplayY1, 15, -1); drawFrame(); showFrame(); clear_keybuf(); while (!keypressed()) { if (forgotScreen) { drawSpritesForBlitting(); drawGame(); player.playDeathAnimation(layer[1]); textout_centre_ex(layer[2], pacmania, "Game", SCREEN_W / 2, SCREEN_H / 2 - text_height(pacmania), 14, -1); textout_centre_ex(layer[2], pacmania, "Over", SCREEN_W / 2, SCREEN_H / 2, 14, -1); textout_right_ex(layer[0], joystix, buf, livesDisplayX2, livesDisplayY1, 15, -1); drawFrame(); showFrame(); forgotScreen = false; } rest(0); } } else { clear_keybuf(); while (!keypressed()) { if (forgotScreen) { drawSpritesForBlitting(); drawGame(); player.playDeathAnimation(layer[1]); drawFrame(); showFrame(); forgotScreen = false; } rest(0); } reportLives(); enemiesConsumed = 0; cagePosition = 0; energyAttained = -1; energyExpires = 0; player = PacMan(this); monster[blinky] = Enemy(this, blinky); monster[pinky] = Enemy(this, pinky); monster[inky] = Enemy(this, inky); monster[clyde] = Enemy(this, clyde); fruit = Bonus(this, true); for (int i = 0; i < 4; i++) reward[i] = EnemyReward(0, 0, i, false); energyShown = true; currentColorPalette[energyColor] = white; drawActors(); drawFrame(); showFrame(); set_color(energyColor, &white); set_color(blinkyColor, &red); set_color(pinkyColor, &pink); set_color(inkyColor, &cyan); set_color(clydeColor, &orange); clear_keybuf(); while (!keypressed()) { if (forgotScreen) { drawSpritesForBlitting(); drawGame(); drawFrame(); showFrame(); forgotScreen = false; } rest(0); } } installTimers(); } while (keypressed()) if (readkey() >> 8 == KEY_ESC) { uninstallTimers(); rectfill(layer[0], 0, barY, SCREEN_W - 1, SCREEN_H - 1, 0); textout_ex(layer[0], joystix, "Really quit?", 0, barY, 15, -1); const bool choice = InputMenu(layer[0], 13 * charWidth, barY, yesNoMenuLength, yesNoMenuCommand, yesNoMenuAcceleratorKey, false, true).block(false); rectfill(layer[0], 0, barY, SCREEN_W - 1, SCREEN_H - 1, 0); draw_rle_sprite(layer[0], rleBonus, bonusDisplayX, bonusDisplayY); reportScore(); reportLives(); drawFrame(); showFrame(); if (choice == 0) lives = -1; installTimers(); } const char pauseKey = key[KEY_PAUSE]; if (pauseKey != oldPauseKey) { uninstallTimers(); if (midi_pos >= 0l) midi_pause(); textout_centre_ex(layer[2], pacmania, "Paused", SCREEN_W / 2, SCREEN_H / 2 - text_height(pacmania) / 2, 14, -1); drawFrame(); showFrame(); clear_keybuf(); while (!keypressed()) { if (forgotScreen) { drawSpritesForBlitting(); drawGame(); textout_centre_ex(layer[2], pacmania, "Paused", SCREEN_W / 2, SCREEN_H / 2 - text_height(pacmania) / 2, 14, -1); drawFrame(); showFrame(); forgotScreen = false; } rest(0); } clear_bitmap(layer[2]); drawFrame(); showFrame(); if (midi_pos >= 0l) midi_resume(); installTimers(); } oldPauseKey = pauseKey; } uninstallTimers(); fade_out(fadeSpeed); set_clip_rect(layer[1], 0, 0, SCREEN_W - 1, SCREEN_H - 1); if (midi_pos >= 0l) stop_midi(); } void Gameplay::drawActors() { clear_bitmap(layer[1]); player.draw(layer[1]); for (int i = 0; i < 4; i++) monster[i].draw(layer[1]); fruit.draw(layer[1], layer[0]); for (int i = 0; i < 4; i++) reward[i].draw(layer[1]); } void Gameplay::drawGame() { clear_bitmap(layer[0]); currentLevel.drawBoard(layer[0]); draw_rle_sprite(layer[0], rleBonus, bonusDisplayX, bonusDisplayY); reportScore(); reportLives(); drawActors(); clear_bitmap(layer[2]); }