aboutsummaryrefslogtreecommitdiff
path: root/engines/gob/minigames/geisha/penetration.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/gob/minigames/geisha/penetration.cpp')
-rw-r--r--engines/gob/minigames/geisha/penetration.cpp1421
1 files changed, 1396 insertions, 25 deletions
diff --git a/engines/gob/minigames/geisha/penetration.cpp b/engines/gob/minigames/geisha/penetration.cpp
index 121a45bc40..3be9f1f651 100644
--- a/engines/gob/minigames/geisha/penetration.cpp
+++ b/engines/gob/minigames/geisha/penetration.cpp
@@ -20,87 +20,1458 @@
*
*/
+#include "common/events.h"
+
#include "gob/global.h"
#include "gob/util.h"
+#include "gob/palanim.h"
#include "gob/draw.h"
#include "gob/video.h"
#include "gob/decfile.h"
+#include "gob/cmpfile.h"
#include "gob/anifile.h"
+#include "gob/aniobject.h"
+
+#include "gob/sound/sound.h"
#include "gob/minigames/geisha/penetration.h"
+#include "gob/minigames/geisha/meter.h"
+#include "gob/minigames/geisha/mouth.h"
namespace Gob {
namespace Geisha {
-static const byte kPalette[48] = {
- 0x16, 0x16, 0x16,
- 0x12, 0x14, 0x16,
- 0x34, 0x00, 0x25,
- 0x1D, 0x1F, 0x22,
- 0x24, 0x27, 0x2A,
- 0x2C, 0x0D, 0x22,
- 0x2B, 0x2E, 0x32,
- 0x12, 0x09, 0x20,
- 0x3D, 0x3F, 0x00,
- 0x3F, 0x3F, 0x3F,
- 0x00, 0x00, 0x00,
- 0x15, 0x15, 0x3F,
- 0x25, 0x22, 0x2F,
- 0x1A, 0x14, 0x28,
- 0x3F, 0x00, 0x00,
- 0x15, 0x3F, 0x15
+static const int kColorShield = 11;
+static const int kColorHealth = 15;
+static const int kColorBlack = 10;
+static const int kColorFloor = 13;
+static const int kColorFloorText = 14;
+static const int kColorExitText = 15;
+
+enum Sprite {
+ kSpriteFloorShield = 25,
+ kSpriteExit = 29,
+ kSpriteFloor = 30,
+ kSpriteWall = 31,
+ kSpriteMouthBite = 32,
+ kSpriteMouthKiss = 33,
+ kSpriteBulletN = 65,
+ kSpriteBulletS = 66,
+ kSpriteBulletW = 67,
+ kSpriteBulletE = 68,
+ kSpriteBulletSW = 85,
+ kSpriteBulletSE = 86,
+ kSpriteBulletNW = 87,
+ kSpriteBulletNE = 88
+};
+
+enum Animation {
+ kAnimationEnemyRound = 0,
+ kAnimationEnemyRoundExplode = 1,
+ kAnimationEnemySquare = 2,
+ kAnimationEnemySquareExplode = 3,
+ kAnimationMouthKiss = 33,
+ kAnimationMouthBite = 34
+};
+
+static const int kMapTileWidth = 24;
+static const int kMapTileHeight = 24;
+
+static const int kPlayAreaX = 120;
+static const int kPlayAreaY = 7;
+static const int kPlayAreaWidth = 192;
+static const int kPlayAreaHeight = 113;
+
+static const int kPlayAreaBorderWidth = kPlayAreaWidth / 2;
+static const int kPlayAreaBorderHeight = kPlayAreaHeight / 2;
+
+static const int kTextAreaLeft = 9;
+static const int kTextAreaTop = 7;
+static const int kTextAreaRight = 104;
+static const int kTextAreaBottom = 107;
+
+static const int kTextAreaBigBottom = 142;
+
+const byte Penetration::kPalettes[kFloorCount][3 * kPaletteSize] = {
+ {
+ 0x16, 0x16, 0x16,
+ 0x12, 0x14, 0x16,
+ 0x34, 0x00, 0x25,
+ 0x1D, 0x1F, 0x22,
+ 0x24, 0x27, 0x2A,
+ 0x2C, 0x0D, 0x22,
+ 0x2B, 0x2E, 0x32,
+ 0x12, 0x09, 0x20,
+ 0x3D, 0x3F, 0x00,
+ 0x3F, 0x3F, 0x3F,
+ 0x00, 0x00, 0x00,
+ 0x15, 0x15, 0x3F,
+ 0x25, 0x22, 0x2F,
+ 0x1A, 0x14, 0x28,
+ 0x3F, 0x00, 0x00,
+ 0x15, 0x3F, 0x15
+ },
+ {
+ 0x16, 0x16, 0x16,
+ 0x12, 0x14, 0x16,
+ 0x37, 0x00, 0x24,
+ 0x1D, 0x1F, 0x22,
+ 0x24, 0x27, 0x2A,
+ 0x30, 0x0E, 0x16,
+ 0x2B, 0x2E, 0x32,
+ 0x22, 0x0E, 0x26,
+ 0x3D, 0x3F, 0x00,
+ 0x3F, 0x3F, 0x3F,
+ 0x00, 0x00, 0x00,
+ 0x15, 0x15, 0x3F,
+ 0x36, 0x28, 0x36,
+ 0x30, 0x1E, 0x2A,
+ 0x3F, 0x00, 0x00,
+ 0x15, 0x3F, 0x15
+ },
+ {
+ 0x16, 0x16, 0x16,
+ 0x12, 0x14, 0x16,
+ 0x3F, 0x14, 0x22,
+ 0x1D, 0x1F, 0x22,
+ 0x24, 0x27, 0x2A,
+ 0x30, 0x10, 0x10,
+ 0x2B, 0x2E, 0x32,
+ 0x2A, 0x12, 0x12,
+ 0x3D, 0x3F, 0x00,
+ 0x3F, 0x3F, 0x3F,
+ 0x00, 0x00, 0x00,
+ 0x15, 0x15, 0x3F,
+ 0x3F, 0x23, 0x31,
+ 0x39, 0x20, 0x2A,
+ 0x3F, 0x00, 0x00,
+ 0x15, 0x3F, 0x15
+ }
+};
+
+const byte Penetration::kMaps[kModeCount][kFloorCount][kMapWidth * kMapHeight] = {
+ {
+ { // Real mode, floor 0
+ 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0,
+ 50, 50, 0, 0, 52, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 50,
+ 50, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 0, 50,
+ 50, 0, 0, 50, 0, 0, 52, 53, 0, 0, 0, 0, 0, 0, 50, 0, 50,
+ 50, 0, 50, 0, 0, 50, 50, 50, 50, 0, 54, 55, 0, 0, 50, 0, 50,
+ 50, 0, 50, 49, 0, 50, 0, 52, 53, 0, 50, 50, 50, 0, 0, 0, 50,
+ 50, 57, 0, 50, 0, 0, 0, 50, 50, 50, 0, 0, 56, 50, 54, 55, 50,
+ 50, 50, 0, 0, 50, 50, 50, 0, 0, 0, 0, 50, 0, 0, 50, 0, 50,
+ 50, 51, 50, 0, 54, 55, 0, 0, 50, 50, 50, 50, 52, 53, 50, 0, 50,
+ 50, 0, 50, 0, 0, 0, 0, 0, 54, 55, 0, 0, 0, 50, 0, 0, 50,
+ 50, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 0, 0, 50,
+ 50, 50, 0, 52, 53, 0, 0, 0, 0, 0, 0, 52, 53, 0, 0, 50, 50,
+ 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0
+ },
+ { // Real mode, floor 1
+ 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0,
+ 50, 0, 52, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50,
+ 50, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 0, 50,
+ 50, 0, 50, 51, 52, 53, 0, 0, 52, 53, 0, 0, 0, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 50, 0, 50, 0, 50, 0, 50, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 52, 53, 0, 0, 0, 0, 0, 52, 53, 0, 52, 53, 50,
+ 50, 57, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 0, 50,
+ 50, 0, 50, 52, 53, 0, 0, 52, 53, 0, 0, 0, 0, 0, 54, 55, 50,
+ 50, 0, 50, 0, 50, 0, 50, 50, 0, 50, 50, 0, 50, 0, 50, 50, 50,
+ 50, 0, 50, 49, 0, 0, 52, 53, 0, 52, 53, 0, 0, 0, 50, 56, 50,
+ 50, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 0, 50,
+ 50, 0, 0, 0, 0, 0, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 50,
+ 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0
+ },
+ { // Real mode, floor 2
+ 0, 50, 50, 50, 50, 50, 50, 50, 0, 50, 50, 50, 50, 50, 50, 50, 0,
+ 50, 52, 53, 0, 0, 0, 0, 50, 50, 50, 0, 0, 0, 0, 52, 53, 50,
+ 50, 0, 50, 50, 50, 0, 0, 0, 50, 0, 0, 0, 50, 50, 50, 0, 50,
+ 50, 0, 50, 52, 53, 50, 50, 52, 53, 0, 50, 50, 54, 55, 50, 0, 50,
+ 50, 0, 50, 0, 0, 0, 0, 50, 0, 50, 0, 0, 0, 0, 50, 0, 50,
+ 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 52, 53, 50,
+ 0, 50, 0, 50, 50, 50, 0, 57, 50, 51, 0, 50, 50, 50, 0, 50, 0,
+ 50, 0, 0, 0, 50, 0, 0, 0, 50, 0, 52, 53, 50, 0, 0, 0, 50,
+ 50, 0, 50, 0, 0, 0, 0, 50, 56, 50, 0, 0, 0, 0, 50, 0, 50,
+ 50, 0, 50, 54, 55, 50, 50, 0, 0, 0, 50, 50, 54, 55, 50, 0, 50,
+ 50, 0, 50, 50, 50, 0, 0, 0, 50, 0, 0, 0, 50, 50, 50, 0, 50,
+ 50, 52, 53, 0, 0, 0, 0, 50, 50, 50, 0, 0, 0, 0, 52, 53, 50,
+ 0, 50, 50, 50, 50, 50, 50, 50, 0, 50, 50, 50, 50, 50, 50, 50, 0
+ }
+ },
+ {
+ { // Test mode, floor 0
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 56, 0, 50, 0, 0, 52, 53, 0, 0, 0, 0, 52, 53, 0, 51, 50,
+ 50, 0, 0, 50, 0, 0, 0, 50, 0, 54, 55, 50, 0, 50, 50, 50, 50,
+ 50, 52, 53, 50, 50, 0, 0, 50, 50, 50, 50, 50, 0, 50, 0, 0, 50,
+ 50, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 50, 49, 50, 0, 0, 50,
+ 50, 0, 54, 55, 0, 50, 50, 54, 55, 0, 50, 50, 50, 0, 0, 0, 50,
+ 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 53, 0, 0, 54, 55, 50,
+ 50, 0, 50, 0, 50, 0, 0, 50, 0, 0, 0, 50, 0, 0, 0, 0, 50,
+ 50, 0, 50, 0, 50, 54, 55, 50, 0, 50, 50, 50, 0, 50, 0, 0, 50,
+ 50, 50, 50, 50, 50, 0, 0, 50, 0, 0, 0, 0, 0, 50, 54, 55, 50,
+ 50, 0, 0, 0, 0, 0, 0, 0, 50, 50, 50, 50, 50, 0, 0, 0, 50,
+ 50, 57, 0, 52, 53, 0, 0, 0, 0, 54, 55, 0, 0, 0, 0, 56, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50
+ },
+ { // Test mode, floor 1
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 52, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50,
+ 50, 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 54, 55, 0, 50,
+ 50, 0, 50, 52, 53, 0, 0, 50, 0, 0, 54, 55, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 0, 52, 53, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 50, 50, 50, 50, 49, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 0, 50, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 51, 0, 0, 52, 53, 50, 0, 50, 0, 50,
+ 50, 57, 50, 0, 50, 0, 50, 50, 50, 50, 50, 50, 50, 0, 50, 0, 50,
+ 50, 50, 50, 0, 50, 56, 0, 0, 0, 54, 55, 0, 0, 0, 50, 0, 50,
+ 50, 56, 0, 0, 0, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 50,
+ 50, 50, 50, 50, 0, 0, 0, 0, 52, 53, 0, 0, 0, 0, 0, 0, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50
+ },
+ { // Test mode, floor 2
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 57, 50, 54, 55, 0, 50, 54, 55, 0, 50, 0, 52, 53, 50, 51, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 0, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 52, 53, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50, 0, 50,
+ 50, 0, 0, 0, 50, 0, 50, 0, 50, 0, 0, 0, 50, 0, 50, 0, 50,
+ 50, 0, 0, 0, 50, 52, 53, 0, 50, 52, 53, 56, 50, 0, 54, 55, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50
+ }
+ }
+};
+
+static const int kLanguageCount = 5;
+static const int kFallbackLanguage = 2; // English
+
+enum String {
+ kString3rdBasement = 0,
+ kString2ndBasement,
+ kString1stBasement,
+ kStringNoExit,
+ kStringYouHave,
+ kString2Exits,
+ kString1Exit,
+ kStringToReach,
+ kStringUpperLevel1,
+ kStringUpperLevel2,
+ kStringLevel0,
+ kStringPenetration,
+ kStringSuccessful,
+ kStringDanger,
+ kStringGynoides,
+ kStringActivated,
+ kStringCount
};
-Penetration::Penetration(GobEngine *vm) : _vm(vm), _background(0), _objects(0) {
+static const char *kStrings[kLanguageCount][kStringCount] = {
+ { // French
+ "3EME SOUS-SOL",
+ "2EME SOUS-SOL",
+ "1ER SOUS-SOL",
+ "SORTIE REFUSEE",
+ "Vous disposez",
+ "de deux sorties",
+ "d\'une sortie",
+ "pour l\'acc\212s au",
+ "niveau",
+ "sup\202rieur",
+ "- NIVEAU 0 -",
+ "PENETRATION",
+ "REUSSIE",
+ "DANGER",
+ "GYNOIDES",
+ "ACTIVEES"
+ },
+ { // German
+ // NOTE: The original had very broken German there. We provide proper(ish) German instead.
+ // B0rken text in the comments after each line
+ "3. UNTERGESCHOSS", // "3. U.-GESCHOSS""
+ "2. UNTERGESCHOSS", // "2. U.-GESCHOSS"
+ "1. UNTERGESCHOSS", // "1. U.-GESCHOSS"
+ "AUSGANG GESPERRT",
+ "Sie haben",
+ "zwei Ausg\204nge", // "zwei Ausgang"
+ "einen Ausgang", // "Fortsetztung"
+ "um das obere", // ""
+ "Stockwerk zu", // ""
+ "erreichen", // ""
+ "- STOCKWERK 0 -", // "0 - HOHE"
+ "PENETRATION", // "DURCHDRIGEN"
+ "ERFOLGREICH", // "ERFOLG"
+ "GEFAHR",
+ "GYNOIDE",
+ "AKTIVIERT",
+ },
+ { // English
+ "3RD BASEMENT",
+ "2ND BASEMENT",
+ "1ST BASEMENT",
+ "NO EXIT",
+ "You have",
+ "2 exits",
+ "1 exit",
+ "to reach upper",
+ "level",
+ "",
+ "- 0 LEVEL -",
+ "PENETRATION",
+ "SUCCESSFUL",
+ "DANGER",
+ "GYNOIDES",
+ "ACTIVATED",
+ },
+ { // Spanish
+ "3ER. SUBSUELO",
+ "2D. SUBSUELO",
+ "1ER. SUBSUELO",
+ "SALIDA RECHAZADA",
+ "Dispones",
+ "de dos salidas",
+ "de una salida",
+ "para acceso al",
+ "nivel",
+ "superior",
+ "- NIVEL 0 -",
+ "PENETRACION",
+ "CONSEGUIDA",
+ "PELIGRO",
+ "GYNOIDAS",
+ "ACTIVADAS",
+ },
+ { // Italian
+ "SOTTOSUOLO 3",
+ "SOTTOSUOLO 2",
+ "SOTTOSUOLO 1",
+ "NON USCITA",
+ "avete",
+ "due uscite",
+ "un\' uscita",
+ "per accedere al",
+ "livello",
+ "superiore",
+ "- LIVELLO 0 -",
+ "PENETRAZIONE",
+ "RIUSCITA",
+ "PERICOLO",
+ "GYNOIDI",
+ "ATTIVATE",
+ }
+};
+
+
+Penetration::MapObject::MapObject(uint16 tX, uint16 tY, uint16 mX, uint16 mY, uint16 w, uint16 h) :
+ tileX(tX), tileY(tY), mapX(mX), mapY(mY), width(w), height(h) {
+
+ isBlocking = true;
+}
+
+Penetration::MapObject::MapObject(uint16 tX, uint16 tY, uint16 w, uint16 h) :
+ tileX(tX), tileY(tY), width(w), height(h) {
+
+ isBlocking = true;
+
+ setMapFromTilePosition();
+}
+
+void Penetration::MapObject::setTileFromMapPosition() {
+ tileX = (mapX + (width / 2)) / kMapTileWidth;
+ tileY = (mapY + (height / 2)) / kMapTileHeight;
+}
+
+void Penetration::MapObject::setMapFromTilePosition() {
+ mapX = tileX * kMapTileWidth;
+ mapY = tileY * kMapTileHeight;
+}
+
+bool Penetration::MapObject::isIn(uint16 mX, uint16 mY) const {
+ if ((mX < mapX) || (mY < mapY))
+ return false;
+ if ((mX > (mapX + width - 1)) || (mY > (mapY + height - 1)))
+ return false;
+
+ return true;
+}
+
+bool Penetration::MapObject::isIn(uint16 mX, uint16 mY, uint16 w, uint16 h) const {
+ return isIn(mX , mY ) ||
+ isIn(mX + w - 1, mY ) ||
+ isIn(mX , mY + h - 1) ||
+ isIn(mX + w - 1, mY + h - 1);
+}
+
+bool Penetration::MapObject::isIn(const MapObject &obj) const {
+ return isIn(obj.mapX, obj.mapY, obj.width, obj.height);
+}
+
+
+Penetration::ManagedMouth::ManagedMouth(uint16 tX, uint16 tY, MouthType t) :
+ MapObject(tX, tY, 0, 0), mouth(0), type(t) {
+
+}
+
+Penetration::ManagedMouth::~ManagedMouth() {
+ delete mouth;
+}
+
+
+Penetration::ManagedSub::ManagedSub(uint16 tX, uint16 tY) :
+ MapObject(tX, tY, kMapTileWidth, kMapTileHeight), sub(0) {
+
+}
+
+Penetration::ManagedSub::~ManagedSub() {
+ delete sub;
+}
+
+
+Penetration::ManagedEnemy::ManagedEnemy() : MapObject(0, 0, 0, 0), enemy(0), dead(false) {
+}
+
+Penetration::ManagedEnemy::~ManagedEnemy() {
+ delete enemy;
+}
+
+void Penetration::ManagedEnemy::clear() {
+ delete enemy;
+
+ enemy = 0;
+}
+
+
+Penetration::ManagedBullet::ManagedBullet() : MapObject(0, 0, 0, 0), bullet(0) {
+}
+
+Penetration::ManagedBullet::~ManagedBullet() {
+ delete bullet;
+}
+
+void Penetration::ManagedBullet::clear() {
+ delete bullet;
+
+ bullet = 0;
+}
+
+
+Penetration::Penetration(GobEngine *vm) : _vm(vm), _background(0), _sprites(0), _objects(0), _sub(0),
+ _shieldMeter(0), _healthMeter(0), _floor(0), _isPlaying(false) {
+
_background = new Surface(320, 200, 1);
+
+ _shieldMeter = new Meter(11, 119, 92, 3, kColorShield, kColorBlack, 920, Meter::kFillToRight);
+ _healthMeter = new Meter(11, 137, 92, 3, kColorHealth, kColorBlack, 920, Meter::kFillToRight);
+
+ _map = new Surface(kMapWidth * kMapTileWidth + kPlayAreaWidth ,
+ kMapHeight * kMapTileHeight + kPlayAreaHeight, 1);
}
Penetration::~Penetration() {
deinit();
+ delete _map;
+
+ delete _shieldMeter;
+ delete _healthMeter;
+
delete _background;
}
-bool Penetration::play(uint16 var1, uint16 var2, uint16 var3) {
+bool Penetration::play(bool hasAccessPass, bool hasMaxEnergy, bool testMode) {
+ _hasAccessPass = hasAccessPass;
+ _hasMaxEnergy = hasMaxEnergy;
+ _testMode = testMode;
+
+ _isPlaying = true;
+
init();
initScreen();
+ drawFloorText();
+
_vm->_draw->blitInvalidated();
_vm->_video->retrace();
- while (!_vm->_util->keyPressed() && !_vm->shouldQuit())
- _vm->_util->longDelay(1);
+
+ while (!_vm->shouldQuit() && !_quit && !isDead() && !hasWon()) {
+ enemiesCreate();
+ bulletsMove();
+ updateAnims();
+
+ // Draw, fade in if necessary and wait for the end of the frame
+ _vm->_draw->blitInvalidated();
+ fadeIn();
+ _vm->_util->waitEndFrame();
+
+ // Handle the input
+ checkInput();
+
+ // Handle the sub movement
+ handleSub();
+
+ // Handle the enemies movement
+ enemiesMove();
+
+ checkExited();
+
+ if (_shotCoolDown > 0)
+ _shotCoolDown--;
+ }
deinit();
- return true;
+ drawEndText();
+
+ _isPlaying = false;
+
+ return hasWon();
+}
+
+bool Penetration::isPlaying() const {
+ return _isPlaying;
+}
+
+void Penetration::cheatWin() {
+ _floor = 3;
}
void Penetration::init() {
+ // Load sounds
+ _vm->_sound->sampleLoad(&_soundShield , SOUND_SND, "boucl.snd");
+ _vm->_sound->sampleLoad(&_soundBite , SOUND_SND, "pervet.snd");
+ _vm->_sound->sampleLoad(&_soundKiss , SOUND_SND, "baise.snd");
+ _vm->_sound->sampleLoad(&_soundShoot , SOUND_SND, "tirgim.snd");
+ _vm->_sound->sampleLoad(&_soundExit , SOUND_SND, "trouve.snd");
+ _vm->_sound->sampleLoad(&_soundExplode, SOUND_SND, "virmor.snd");
+
+ _quit = false;
+ for (int i = 0; i < kKeyCount; i++)
+ _keys[i] = false;
+
_background->clear();
_vm->_video->drawPackedSprite("hyprmef2.cmp", *_background);
+ _sprites = new CMPFile(_vm, "tcifplai.cmp", 320, 200);
_objects = new ANIFile(_vm, "tcite.ani", 320);
+
+ // The shield starts down
+ _shieldMeter->setValue(0);
+
+ // If we don't have the max energy tokens, the health starts at 1/3 strength
+ if (_hasMaxEnergy)
+ _healthMeter->setMaxValue();
+ else
+ _healthMeter->setValue(_healthMeter->getMaxValue() / 3);
+
+ _floor = 0;
+
+ _shotCoolDown = 0;
+
+ createMap();
}
void Penetration::deinit() {
+ _soundShield.free();
+ _soundBite.free();
+ _soundKiss.free();
+ _soundShoot.free();
+ _soundExit.free();
+ _soundExplode.free();
+
+ clearMap();
+
delete _objects;
+ delete _sprites;
_objects = 0;
+ _sprites = 0;
+}
+
+void Penetration::clearMap() {
+ _mapAnims.clear();
+ _anims.clear();
+
+ _blockingObjects.clear();
+
+ _walls.clear();
+ _exits.clear();
+ _shields.clear();
+ _mouths.clear();
+
+ for (int i = 0; i < kEnemyCount; i++)
+ _enemies[i].clear();
+ for (int i = 0; i < kMaxBulletCount; i++)
+ _bullets[i].clear();
+
+ delete _sub;
+
+ _sub = 0;
+
+ _map->fill(kColorBlack);
+}
+
+void Penetration::createMap() {
+ if (_floor >= kFloorCount)
+ error("Geisha: Invalid floor %d in minigame penetration", _floor);
+
+ clearMap();
+
+ const byte *mapTiles = kMaps[_testMode ? 1 : 0][_floor];
+
+ bool exitWorks;
+
+ // Draw the map tiles
+ for (int y = 0; y < kMapHeight; y++) {
+ for (int x = 0; x < kMapWidth; x++) {
+ const byte mapTile = mapTiles[y * kMapWidth + x];
+
+ const int posX = kPlayAreaBorderWidth + x * kMapTileWidth;
+ const int posY = kPlayAreaBorderHeight + y * kMapTileHeight;
+
+ switch (mapTile) {
+ case 0: // Floor
+ _sprites->draw(*_map, kSpriteFloor, posX, posY);
+ break;
+
+ case 49: // Emergency exit (needs access pass)
+
+ exitWorks = _hasAccessPass;
+ if (exitWorks) {
+ _sprites->draw(*_map, kSpriteExit, posX, posY);
+ _exits.push_back(MapObject(x, y, 0, 0));
+ } else {
+ _sprites->draw(*_map, kSpriteWall, posX, posY);
+ _walls.push_back(MapObject(x, y, kMapTileWidth, kMapTileHeight));
+ }
+
+ break;
+
+ case 50: // Wall
+ _sprites->draw(*_map, kSpriteWall, posX, posY);
+ _walls.push_back(MapObject(x, y, kMapTileWidth, kMapTileHeight));
+ break;
+
+ case 51: // Regular exit
+
+ // A regular exit works always in test mode.
+ // But if we're in real mode, and on the last floor, it needs an access pass
+ exitWorks = _testMode || (_floor < 2) || _hasAccessPass;
+
+ if (exitWorks) {
+ _sprites->draw(*_map, kSpriteExit, posX, posY);
+ _exits.push_back(MapObject(x, y, 0, 0));
+ } else {
+ _sprites->draw(*_map, kSpriteWall, posX, posY);
+ _walls.push_back(MapObject(x, y, kMapTileWidth, kMapTileHeight));
+ }
+
+ break;
+
+ case 52: // Left side of biting mouth
+ _mouths.push_back(ManagedMouth(x, y, kMouthTypeBite));
+
+ _mouths.back().mouth =
+ new Mouth(*_objects, *_sprites, kAnimationMouthBite, kSpriteMouthBite, kSpriteFloor);
+
+ _mouths.back().mouth->setPosition(posX, posY);
+ break;
+
+ case 53: // Right side of biting mouth
+ break;
+
+ case 54: // Left side of kissing mouth
+ _mouths.push_back(ManagedMouth(x, y, kMouthTypeKiss));
+
+ _mouths.back().mouth =
+ new Mouth(*_objects, *_sprites, kAnimationMouthKiss, kSpriteMouthKiss, kSpriteFloor);
+
+ _mouths.back().mouth->setPosition(posX, posY);
+ break;
+
+ case 55: // Right side of kissing mouth
+ break;
+
+ case 56: // Shield lying on the floor
+ _sprites->draw(*_map, kSpriteFloor , posX , posY ); // Floor
+ _sprites->draw(*_map, kSpriteFloorShield, posX + 4, posY + 8); // Shield
+
+ _map->fillRect(posX + 4, posY + 8, posX + 7, posY + 18, kColorFloor); // Area left to shield
+ _map->fillRect(posX + 17, posY + 8, posX + 20, posY + 18, kColorFloor); // Area right to shield
+
+ _shields.push_back(MapObject(x, y, 0, 0));
+ break;
+
+ case 57: // Start position
+ _sprites->draw(*_map, kSpriteFloor, posX, posY);
+
+ delete _sub;
+
+ _sub = new ManagedSub(x, y);
+
+ _sub->sub = new Submarine(*_objects);
+ _sub->sub->setPosition(kPlayAreaX + kPlayAreaBorderWidth, kPlayAreaY + kPlayAreaBorderHeight);
+ break;
+ }
+ }
+ }
+
+ if (!_sub)
+ error("Geisha: No starting position in floor %d (testmode: %d)", _floor, _testMode);
+
+ // Walls
+ for (Common::List<MapObject>::iterator w = _walls.begin(); w != _walls.end(); ++w)
+ _blockingObjects.push_back(&*w);
+
+ // Mouths
+ for (Common::List<ManagedMouth>::iterator m = _mouths.begin(); m != _mouths.end(); ++m)
+ _mapAnims.push_back(m->mouth);
+
+ // Sub
+ _blockingObjects.push_back(_sub);
+ _anims.push_back(_sub->sub);
+
+ // Moving enemies
+ for (int i = 0; i < kEnemyCount; i++) {
+ _enemies[i].enemy = new ANIObject(*_objects);
+
+ _enemies[i].enemy->setPause(true);
+ _enemies[i].enemy->setVisible(false);
+
+ _enemies[i].isBlocking = false;
+
+ _blockingObjects.push_back(&_enemies[i]);
+ _mapAnims.push_back(_enemies[i].enemy);
+ }
+
+ // Bullets
+ for (int i = 0; i < kMaxBulletCount; i++) {
+ _bullets[i].bullet = new ANIObject(*_sprites);
+
+ _bullets[i].bullet->setPause(true);
+ _bullets[i].bullet->setVisible(false);
+
+ _bullets[i].isBlocking = false;
+
+ _mapAnims.push_back(_bullets[i].bullet);
+ }
+}
+
+void Penetration::drawFloorText() {
+ _vm->_draw->_backSurface->fillRect(kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBottom, kColorBlack);
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBottom);
+
+ const Font *font = _vm->_draw->_fonts[2];
+ if (!font)
+ return;
+
+ const char **strings = kStrings[getLanguage()];
+
+ const char *floorString = 0;
+ if (_floor == 0)
+ floorString = strings[kString3rdBasement];
+ else if (_floor == 1)
+ floorString = strings[kString2ndBasement];
+ else if (_floor == 2)
+ floorString = strings[kString1stBasement];
+
+ if (floorString)
+ _vm->_draw->drawString(floorString, 10, 15, kColorFloorText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+
+ if (_exits.size() > 0) {
+ int exitCount = kString2Exits;
+ if (_exits.size() == 1)
+ exitCount = kString1Exit;
+
+ _vm->_draw->drawString(strings[kStringYouHave] , 10, 38, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[exitCount] , 10, 53, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringToReach] , 10, 68, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringUpperLevel1], 10, 84, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringUpperLevel2], 10, 98, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+
+ } else
+ _vm->_draw->drawString(strings[kStringNoExit], 10, 53, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+}
+
+void Penetration::drawEndText() {
+ // Only draw the end text when we've won and this isn't a test run
+ if (!hasWon() || _testMode)
+ return;
+
+ _vm->_draw->_backSurface->fillRect(kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBigBottom, kColorBlack);
+
+ const Font *font = _vm->_draw->_fonts[2];
+ if (!font)
+ return;
+
+ const char **strings = kStrings[getLanguage()];
+
+ _vm->_draw->drawString(strings[kStringLevel0] , 11, 21, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringPenetration], 11, 42, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringSuccessful] , 11, 58, kColorExitText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+
+ _vm->_draw->drawString(strings[kStringDanger] , 11, 82, kColorFloorText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringGynoides] , 11, 98, kColorFloorText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+ _vm->_draw->drawString(strings[kStringActivated], 11, 113, kColorFloorText, kColorBlack, 1,
+ *_vm->_draw->_backSurface, *font);
+
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kTextAreaLeft, kTextAreaTop, kTextAreaRight, kTextAreaBigBottom);
+ _vm->_draw->blitInvalidated();
+ _vm->_video->retrace();
+}
+
+void Penetration::fadeIn() {
+ if (!_needFadeIn)
+ return;
+
+ // Fade to palette
+ _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0);
+ _needFadeIn = false;
+}
+
+void Penetration::setPalette() {
+ // Fade to black
+ _vm->_palAnim->fade(0, 0, 0);
+
+ // Set palette
+ memcpy(_vm->_draw->_vgaPalette , kPalettes[_floor], 3 * kPaletteSize);
+ memcpy(_vm->_draw->_vgaSmallPalette, kPalettes[_floor], 3 * kPaletteSize);
+
+ _needFadeIn = true;
}
void Penetration::initScreen() {
_vm->_util->setFrameRate(15);
- memcpy(_vm->_draw->_vgaPalette , kPalette, 48);
- memcpy(_vm->_draw->_vgaSmallPalette, kPalette, 48);
+ setPalette();
+
+ // Draw the shield meter
+ _sprites->draw(*_background, 0, 0, 95, 6, 9, 117, 0); // Meter frame
+ _sprites->draw(*_background, 271, 176, 282, 183, 9, 108, 0); // Shield
- _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
+ // Draw the health meter
+ _sprites->draw(*_background, 0, 0, 95, 6, 9, 135, 0); // Meter frame
+ _sprites->draw(*_background, 283, 176, 292, 184, 9, 126, 0); // Heart
_vm->_draw->_backSurface->blit(*_background);
_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199);
}
+void Penetration::enemiesCreate() {
+ for (int i = 0; i < kEnemyCount; i++) {
+ ManagedEnemy &enemy = _enemies[i];
+
+ if (enemy.enemy->isVisible())
+ continue;
+
+ enemy.enemy->setAnimation((i & 1) ? kAnimationEnemySquare : kAnimationEnemyRound);
+ enemy.enemy->setMode(ANIObject::kModeContinuous);
+ enemy.enemy->setPause(false);
+ enemy.enemy->setVisible(true);
+
+ int16 width, height;
+ enemy.enemy->getFrameSize(width, height);
+
+ enemy.width = width;
+ enemy.height = height;
+
+ do {
+ enemy.mapX = _vm->_util->getRandom(kMapWidth) * kMapTileWidth + 2;
+ enemy.mapY = _vm->_util->getRandom(kMapHeight) * kMapTileHeight + 4;
+ enemy.setTileFromMapPosition();
+ } while (isBlocked(enemy, enemy.mapX, enemy.mapY));
+
+ const int posX = kPlayAreaBorderWidth + enemy.mapX;
+ const int posY = kPlayAreaBorderHeight + enemy.mapY;
+
+ enemy.enemy->setPosition(posX, posY);
+
+ enemy.isBlocking = true;
+ enemy.dead = false;
+ }
+}
+
+void Penetration::enemyMove(ManagedEnemy &enemy, int x, int y) {
+ if ((x == 0) && (y == 0))
+ return;
+
+ MapObject *blockedBy;
+ findPath(enemy, x, y, &blockedBy);
+
+ enemy.setTileFromMapPosition();
+
+ const int posX = kPlayAreaBorderWidth + enemy.mapX;
+ const int posY = kPlayAreaBorderHeight + enemy.mapY;
+
+ enemy.enemy->setPosition(posX, posY);
+
+ if (blockedBy == _sub)
+ enemyAttack(enemy);
+}
+
+void Penetration::enemiesMove() {
+ for (int i = 0; i < kEnemyCount; i++) {
+ ManagedEnemy &enemy = _enemies[i];
+
+ if (!enemy.enemy->isVisible() || enemy.dead)
+ continue;
+
+ int x = 0, y = 0;
+
+ if (enemy.mapX > _sub->mapX)
+ x = -8;
+ else if (enemy.mapX < _sub->mapX)
+ x = 8;
+
+ if (enemy.mapY > _sub->mapY)
+ y = -8;
+ else if (enemy.mapY < _sub->mapY)
+ y = 8;
+
+ enemyMove(enemy, x, y);
+ }
+}
+
+void Penetration::enemyAttack(ManagedEnemy &enemy) {
+ // If we have shields, the enemy explodes at them, taking a huge chunk of energy with it.
+ // Otherwise, the enemy nibbles a small amount of health away.
+
+ if (_shieldMeter->getValue() > 0) {
+ enemyExplode(enemy);
+
+ healthLose(80);
+ } else
+ healthLose(5);
+}
+
+void Penetration::enemyExplode(ManagedEnemy &enemy) {
+ enemy.dead = true;
+ enemy.isBlocking = false;
+
+ bool isSquare = enemy.enemy->getAnimation() == kAnimationEnemySquare;
+
+ enemy.enemy->setAnimation(isSquare ? kAnimationEnemySquareExplode : kAnimationEnemyRoundExplode);
+ enemy.enemy->setMode(ANIObject::kModeOnce);
+
+ _vm->_sound->blasterPlay(&_soundExplode, 1, 0);
+}
+
+void Penetration::checkInput() {
+ Common::Event event;
+ Common::EventManager *eventMan = g_system->getEventManager();
+
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+ _quit = true;
+ else if (event.kbd.keycode == Common::KEYCODE_UP)
+ _keys[kKeyUp ] = true;
+ else if (event.kbd.keycode == Common::KEYCODE_DOWN)
+ _keys[kKeyDown ] = true;
+ else if (event.kbd.keycode == Common::KEYCODE_LEFT)
+ _keys[kKeyLeft ] = true;
+ else if (event.kbd.keycode == Common::KEYCODE_RIGHT)
+ _keys[kKeyRight] = true;
+ else if (event.kbd.keycode == Common::KEYCODE_SPACE)
+ _keys[kKeySpace] = true;
+ else if (event.kbd.keycode == Common::KEYCODE_d) {
+ _vm->getDebugger()->attach();
+ _vm->getDebugger()->onFrame();
+ }
+ break;
+
+ case Common::EVENT_KEYUP:
+ if (event.kbd.keycode == Common::KEYCODE_UP)
+ _keys[kKeyUp ] = false;
+ else if (event.kbd.keycode == Common::KEYCODE_DOWN)
+ _keys[kKeyDown ] = false;
+ else if (event.kbd.keycode == Common::KEYCODE_LEFT)
+ _keys[kKeyLeft ] = false;
+ else if (event.kbd.keycode == Common::KEYCODE_RIGHT)
+ _keys[kKeyRight] = false;
+ else if (event.kbd.keycode == Common::KEYCODE_SPACE)
+ _keys[kKeySpace] = false;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void Penetration::handleSub() {
+ int x, y;
+ Submarine::Direction direction = getDirection(x, y);
+
+ subMove(x, y, direction);
+
+ if (_keys[kKeySpace])
+ subShoot();
+}
+
+bool Penetration::isBlocked(const MapObject &self, int16 x, int16 y, MapObject **blockedBy) {
+
+ if ((x < 0) || (y < 0))
+ return true;
+ if (((x + self.width - 1) >= (kMapWidth * kMapTileWidth)) ||
+ ((y + self.height - 1) >= (kMapHeight * kMapTileHeight)))
+ return true;
+
+ MapObject checkSelf(0, 0, self.width, self.height);
+
+ checkSelf.mapX = x;
+ checkSelf.mapY = y;
+
+ for (Common::List<MapObject *>::iterator o = _blockingObjects.begin(); o != _blockingObjects.end(); ++o) {
+ MapObject &obj = **o;
+
+ if (&obj == &self)
+ continue;
+
+ if (!obj.isBlocking)
+ continue;
+
+ if (obj.isIn(checkSelf) || checkSelf.isIn(obj)) {
+ if (blockedBy && !*blockedBy)
+ *blockedBy = &obj;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Penetration::findPath(MapObject &obj, int x, int y, MapObject **blockedBy) {
+ if (blockedBy)
+ *blockedBy = 0;
+
+ while ((x != 0) || (y != 0)) {
+ uint16 oldX = obj.mapX;
+ uint16 oldY = obj.mapY;
+
+ uint16 newX = obj.mapX;
+ if (x > 0) {
+ newX++;
+ x--;
+ } else if (x < 0) {
+ newX--;
+ x++;
+ }
+
+ if (!isBlocked(obj, newX, obj.mapY, blockedBy))
+ obj.mapX = newX;
+
+ uint16 newY = obj.mapY;
+ if (y > 0) {
+ newY++;
+ y--;
+ } else if (y < 0) {
+ newY--;
+ y++;
+ }
+
+ if (!isBlocked(obj, obj.mapX, newY, blockedBy))
+ obj.mapY = newY;
+
+ if ((obj.mapX == oldX) && (obj.mapY == oldY))
+ break;
+ }
+}
+
+void Penetration::subMove(int x, int y, Submarine::Direction direction) {
+ if (!_sub->sub->canMove())
+ return;
+
+ if ((x == 0) && (y == 0))
+ return;
+
+ findPath(*_sub, x, y);
+
+ _sub->setTileFromMapPosition();
+
+ _sub->sub->turn(direction);
+
+ checkShields();
+ checkMouths();
+ checkExits();
+}
+
+void Penetration::subShoot() {
+ if (!_sub->sub->canMove() || _sub->sub->isShooting())
+ return;
+
+ if (_shotCoolDown > 0)
+ return;
+
+ // Creating a bullet
+ int slot = findEmptyBulletSlot();
+ if (slot < 0)
+ return;
+
+ ManagedBullet &bullet = _bullets[slot];
+
+ bullet.bullet->setAnimation(directionToBullet(_sub->sub->getDirection()));
+
+ setBulletPosition(*_sub, bullet);
+
+ const int posX = kPlayAreaBorderWidth + bullet.mapX;
+ const int posY = kPlayAreaBorderHeight + bullet.mapY;
+
+ bullet.bullet->setPosition(posX, posY);
+ bullet.bullet->setVisible(true);
+
+ // Shooting
+ _sub->sub->shoot();
+ _vm->_sound->blasterPlay(&_soundShoot, 1, 0);
+
+ _shotCoolDown = 3;
+}
+
+void Penetration::setBulletPosition(const ManagedSub &sub, ManagedBullet &bullet) const {
+ bullet.mapX = sub.mapX;
+ bullet.mapY= sub.mapY;
+
+ int16 sWidth, sHeight;
+ sub.sub->getFrameSize(sWidth, sHeight);
+
+ int16 bWidth, bHeight;
+ bullet.bullet->getFrameSize(bWidth, bHeight);
+
+ switch (sub.sub->getDirection()) {
+ case Submarine::kDirectionN:
+ bullet.mapX += sWidth / 2;
+ bullet.mapY -= bHeight;
+
+ bullet.deltaX = 0;
+ bullet.deltaY = -8;
+ break;
+
+ case Submarine::kDirectionNE:
+ bullet.mapX += sWidth;
+ bullet.mapY -= bHeight * 2;
+
+ bullet.deltaX = 8;
+ bullet.deltaY = -8;
+ break;
+
+ case Submarine::kDirectionE:
+ bullet.mapX += sWidth;
+ bullet.mapY += sHeight / 2 - bHeight;
+
+ bullet.deltaX = 8;
+ bullet.deltaY = 0;
+ break;
+
+ case Submarine::kDirectionSE:
+ bullet.mapX += sWidth;
+ bullet.mapY += sHeight;
+
+ bullet.deltaX = 8;
+ bullet.deltaY = 8;
+ break;
+
+ case Submarine::kDirectionS:
+ bullet.mapX += sWidth / 2;
+ bullet.mapY += sHeight;
+
+ bullet.deltaX = 0;
+ bullet.deltaY = 8;
+ break;
+
+ case Submarine::kDirectionSW:
+ bullet.mapX -= bWidth;
+ bullet.mapY += sHeight;
+
+ bullet.deltaX = -8;
+ bullet.deltaY = 8;
+ break;
+
+ case Submarine::kDirectionW:
+ bullet.mapX -= bWidth;
+ bullet.mapY += sHeight / 2 - bHeight;
+
+ bullet.deltaX = -8;
+ bullet.deltaY = 0;
+ break;
+
+ case Submarine::kDirectionNW:
+ bullet.mapX -= bWidth;
+ bullet.mapY -= bHeight;
+
+ bullet.deltaX = -8;
+ bullet.deltaY = -8;
+ break;
+
+ default:
+ break;
+ }
+}
+
+uint16 Penetration::directionToBullet(Submarine::Direction direction) const {
+ switch (direction) {
+ case Submarine::kDirectionN:
+ return kSpriteBulletN;
+
+ case Submarine::kDirectionNE:
+ return kSpriteBulletNE;
+
+ case Submarine::kDirectionE:
+ return kSpriteBulletE;
+
+ case Submarine::kDirectionSE:
+ return kSpriteBulletSE;
+
+ case Submarine::kDirectionS:
+ return kSpriteBulletS;
+
+ case Submarine::kDirectionSW:
+ return kSpriteBulletSW;
+
+ case Submarine::kDirectionW:
+ return kSpriteBulletW;
+
+ case Submarine::kDirectionNW:
+ return kSpriteBulletNW;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int Penetration::findEmptyBulletSlot() const {
+ for (int i = 0; i < kMaxBulletCount; i++)
+ if (!_bullets[i].bullet->isVisible())
+ return i;
+
+ return -1;
+}
+
+void Penetration::bulletsMove() {
+ for (int i = 0; i < kMaxBulletCount; i++)
+ if (_bullets[i].bullet->isVisible())
+ bulletMove(_bullets[i]);
+}
+
+void Penetration::bulletMove(ManagedBullet &bullet) {
+ MapObject *blockedBy;
+ findPath(bullet, bullet.deltaX, bullet.deltaY, &blockedBy);
+
+ if (blockedBy) {
+ checkShotEnemy(*blockedBy);
+ bullet.bullet->setVisible(false);
+ return;
+ }
+
+ const int posX = kPlayAreaBorderWidth + bullet.mapX;
+ const int posY = kPlayAreaBorderHeight + bullet.mapY;
+
+ bullet.bullet->setPosition(posX, posY);
+}
+
+void Penetration::checkShotEnemy(MapObject &shotObject) {
+ for (int i = 0; i < kEnemyCount; i++) {
+ ManagedEnemy &enemy = _enemies[i];
+
+ if ((&enemy == &shotObject) && !enemy.dead && enemy.enemy->isVisible()) {
+ enemyExplode(enemy);
+ return;
+ }
+ }
+}
+
+Submarine::Direction Penetration::getDirection(int &x, int &y) const {
+ x = _keys[kKeyRight] ? 3 : (_keys[kKeyLeft] ? -3 : 0);
+ y = _keys[kKeyDown ] ? 3 : (_keys[kKeyUp ] ? -3 : 0);
+
+ if ((x > 0) && (y > 0))
+ return Submarine::kDirectionSE;
+ if ((x > 0) && (y < 0))
+ return Submarine::kDirectionNE;
+ if ((x < 0) && (y > 0))
+ return Submarine::kDirectionSW;
+ if ((x < 0) && (y < 0))
+ return Submarine::kDirectionNW;
+ if (x > 0)
+ return Submarine::kDirectionE;
+ if (x < 0)
+ return Submarine::kDirectionW;
+ if (y > 0)
+ return Submarine::kDirectionS;
+ if (y < 0)
+ return Submarine::kDirectionN;
+
+ return Submarine::kDirectionNone;
+}
+
+void Penetration::checkShields() {
+ for (Common::List<MapObject>::iterator s = _shields.begin(); s != _shields.end(); ++s) {
+ if ((s->tileX == _sub->tileX) && (s->tileY == _sub->tileY)) {
+ // Charge shields
+ _shieldMeter->setMaxValue();
+
+ // Play the shield sound
+ _vm->_sound->blasterPlay(&_soundShield, 1, 0);
+
+ // Erase the shield from the map
+ _sprites->draw(*_map, 30, s->mapX + kPlayAreaBorderWidth, s->mapY + kPlayAreaBorderHeight);
+ _shields.erase(s);
+ break;
+ }
+ }
+}
+
+void Penetration::checkMouths() {
+ for (Common::List<ManagedMouth>::iterator m = _mouths.begin(); m != _mouths.end(); ++m) {
+ if (!m->mouth->isDeactivated())
+ continue;
+
+ if ((( m->tileX == _sub->tileX) && (m->tileY == _sub->tileY)) ||
+ (((m->tileX + 1) == _sub->tileX) && (m->tileY == _sub->tileY))) {
+
+ m->mouth->activate();
+
+ // Play the mouth sound and do health gain/loss
+ if (m->type == kMouthTypeBite) {
+ _vm->_sound->blasterPlay(&_soundBite, 1, 0);
+ healthLose(230);
+ } else if (m->type == kMouthTypeKiss) {
+ _vm->_sound->blasterPlay(&_soundKiss, 1, 0);
+ healthGain(120);
+ }
+ }
+ }
+}
+
+void Penetration::checkExits() {
+ if (!_sub->sub->canMove())
+ return;
+
+ for (Common::List<MapObject>::iterator e = _exits.begin(); e != _exits.end(); ++e) {
+ if ((e->tileX == _sub->tileX) && (e->tileY == _sub->tileY)) {
+ _sub->setMapFromTilePosition();
+
+ _sub->sub->leave();
+
+ _vm->_sound->blasterPlay(&_soundExit, 1, 0);
+ break;
+ }
+ }
+}
+
+void Penetration::healthGain(int amount) {
+ if (_shieldMeter->getValue() > 0)
+ _healthMeter->increase(_shieldMeter->increase(amount));
+ else
+ _healthMeter->increase(amount);
+}
+
+void Penetration::healthLose(int amount) {
+ _healthMeter->decrease(_shieldMeter->decrease(amount));
+
+ if (_healthMeter->getValue() == 0)
+ _sub->sub->die();
+}
+
+void Penetration::checkExited() {
+ if (_sub->sub->hasExited()) {
+ _floor++;
+
+ if (_floor >= kFloorCount)
+ return;
+
+ setPalette();
+ createMap();
+ drawFloorText();
+ }
+}
+
+bool Penetration::isDead() const {
+ return _sub && _sub->sub->isDead();
+}
+
+bool Penetration::hasWon() const {
+ return _floor >= kFloorCount;
+}
+
+int Penetration::getLanguage() const {
+ if (_vm->_global->_language < kLanguageCount)
+ return _vm->_global->_language;
+
+ return kFallbackLanguage;
+}
+
+void Penetration::updateAnims() {
+ int16 left = 0, top = 0, right = 0, bottom = 0;
+
+ // Clear the previous map animation frames
+ for (Common::List<ANIObject *>::iterator a = _mapAnims.reverse_begin();
+ a != _mapAnims.end(); --a) {
+
+ (*a)->clear(*_map, left, top, right, bottom);
+ }
+
+ // Draw the current map animation frames
+ for (Common::List<ANIObject *>::iterator a = _mapAnims.begin();
+ a != _mapAnims.end(); ++a) {
+
+ (*a)->draw(*_map, left, top, right, bottom);
+ (*a)->advance();
+ }
+
+ // Clear the previous animation frames
+ for (Common::List<ANIObject *>::iterator a = _anims.reverse_begin();
+ a != _anims.end(); --a) {
+
+ if ((*a)->clear(*_vm->_draw->_backSurface, left, top, right, bottom))
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
+ }
+
+ if (_sub) {
+ // Draw the map
+
+ _vm->_draw->_backSurface->blit(*_map, _sub->mapX, _sub->mapY,
+ _sub->mapX + kPlayAreaWidth - 1, _sub->mapY + kPlayAreaHeight - 1, kPlayAreaX, kPlayAreaY);
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kPlayAreaX, kPlayAreaY,
+ kPlayAreaX + kPlayAreaWidth - 1, kPlayAreaY + kPlayAreaHeight - 1);
+ }
+
+ // Draw the current animation frames
+ for (Common::List<ANIObject *>::iterator a = _anims.begin();
+ a != _anims.end(); ++a) {
+
+ if ((*a)->draw(*_vm->_draw->_backSurface, left, top, right, bottom))
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
+
+ (*a)->advance();
+ }
+
+ // Draw the meters
+ _shieldMeter->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
+
+ _healthMeter->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
+ _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
+}
+
} // End of namespace Geisha
} // End of namespace Gob