From b83ac21f6008287414d59ad7f9c88b63bd93bac5 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Wed, 6 Jun 2012 02:52:19 +0200 Subject: GOB: Implement Penetration submarine shooting and dying Shots don't result in bullets yet, though --- engines/gob/minigames/geisha/mouth.cpp | 1 - engines/gob/minigames/geisha/penetration.cpp | 149 +++++++++-------- engines/gob/minigames/geisha/penetration.h | 29 +++- engines/gob/minigames/geisha/submarine.cpp | 233 +++++++++++++++++++++++++++ engines/gob/minigames/geisha/submarine.h | 96 +++++++++++ engines/gob/module.mk | 1 + 6 files changed, 435 insertions(+), 74 deletions(-) create mode 100644 engines/gob/minigames/geisha/submarine.cpp create mode 100644 engines/gob/minigames/geisha/submarine.h (limited to 'engines') diff --git a/engines/gob/minigames/geisha/mouth.cpp b/engines/gob/minigames/geisha/mouth.cpp index 605ffe420f..7ba9f86f8c 100644 --- a/engines/gob/minigames/geisha/mouth.cpp +++ b/engines/gob/minigames/geisha/mouth.cpp @@ -21,7 +21,6 @@ */ #include "common/util.h" -#include "common/textconsole.h" #include "gob/minigames/geisha/mouth.h" diff --git a/engines/gob/minigames/geisha/penetration.cpp b/engines/gob/minigames/geisha/penetration.cpp index a188995372..2c1a4918b9 100644 --- a/engines/gob/minigames/geisha/penetration.cpp +++ b/engines/gob/minigames/geisha/penetration.cpp @@ -73,27 +73,6 @@ enum Sprite { }; enum Animation { - kAnimationDriveS = 4, - kAnimationDriveE = 5, - kAnimationDriveN = 6, - kAnimationDriveW = 7, - kAnimationDriveSE = 8, - kAnimationDriveNE = 9, - kAnimationDriveSW = 10, - kAnimationDriveNW = 11, - kAnimationShootS = 12, - kAnimationShootN = 13, - kAnimationShootW = 14, - kAnimationShootE = 15, - kAnimationShootNE = 16, - kAnimationShootSE = 17, - kAnimationShootSW = 18, - kAnimationShootNW = 19, - kAnimationExplodeN = 28, - kAnimationExplodeS = 29, - kAnimationExplodeW = 30, - kAnimationExplodeE = 31, - kAnimationExit = 32, kAnimationMouthKiss = 33, kAnimationMouthBite = 34 }; @@ -220,9 +199,18 @@ Penetration::ManagedMouth::~ManagedMouth() { } +Penetration::ManagedSub::ManagedSub(uint16 pX, uint16 pY) : Position(pX, pY), sub(0) { + mapX = x * kMapTileWidth; + mapY = y * kMapTileHeight; +} + +Penetration::ManagedSub::~ManagedSub() { + delete sub; +} + + Penetration::Penetration(GobEngine *vm) : _vm(vm), _background(0), _sprites(0), _objects(0), _sub(0), - _shieldMeter(0), _healthMeter(0), _floor(0), _mapX(0), _mapY(0), - _subTileX(0), _subTileY(0) { + _shieldMeter(0), _healthMeter(0), _floor(0) { _background = new Surface(320, 200, 1); @@ -255,7 +243,7 @@ bool Penetration::play(bool hasAccessPass, bool hasMaxEnergy, bool testMode) { _vm->_draw->blitInvalidated(); _vm->_video->retrace(); - while (!_vm->shouldQuit()) { + while (!_vm->shouldQuit() && !isDead() && !hasWon()) { updateAnims(); // Draw and wait for the end of the frame @@ -278,7 +266,8 @@ bool Penetration::play(bool hasAccessPass, bool hasMaxEnergy, bool testMode) { } deinit(); - return false; + + return hasWon(); } void Penetration::init() { @@ -286,6 +275,7 @@ void Penetration::init() { _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"); _background->clear(); @@ -310,19 +300,14 @@ void Penetration::init() { for (Common::List::iterator m = _mouths.begin(); m != _mouths.end(); m++) _mapAnims.push_back(m->mouth); - _sub = new ANIObject(*_objects); - - _sub->setAnimation(kAnimationDriveN); - _sub->setPosition(kPlayAreaX + kPlayAreaBorderWidth, kPlayAreaY + kPlayAreaBorderHeight); - _sub->setVisible(true); - - _anims.push_back(_sub); + _anims.push_back(_sub->sub); } void Penetration::deinit() { _soundShield.free(); _soundBite.free(); _soundKiss.free(); + _soundShoot.free(); _mapAnims.clear(); _anims.clear(); @@ -349,8 +334,10 @@ void Penetration::createMap() { // Copy the correct map memcpy(_mapTiles, kMaps[_testMode ? 1 : 0][_floor], kMapWidth * kMapHeight); - _shields.clear(); + delete _sub; + _sub = 0; + _shields.clear(); _mouths.clear(); _map->fill(kColorBlack); @@ -441,17 +428,22 @@ void Penetration::createMap() { case 57: // Start position _sprites->draw(*_map, kSpriteFloor, posX, posY); + *mapTile = 0; - _subTileX = x; - _subTileY = y; + delete _sub; + + _sub = new ManagedSub(x, y); - _mapX = _subTileX * kMapTileWidth; - _mapY = _subTileY * kMapTileHeight; + _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); } void Penetration::initScreen() { @@ -491,51 +483,64 @@ bool Penetration::isWalkable(byte tile) const { void Penetration::handleSub(int16 key) { if (key == kKeyLeft) - moveSub(-5, 0, kAnimationDriveW); + subMove(-5, 0, Submarine::kDirectionW); else if (key == kKeyRight) - moveSub( 5, 0, kAnimationDriveE); + subMove( 5, 0, Submarine::kDirectionE); else if (key == kKeyUp) - moveSub( 0, -5, kAnimationDriveN); + subMove( 0, -5, Submarine::kDirectionN); else if (key == kKeyDown) - moveSub( 0, 5, kAnimationDriveS); + subMove( 0, 5, Submarine::kDirectionS); + else if (key == kKeySpace) + subShoot(); } -void Penetration::moveSub(int x, int y, uint16 animation) { +void Penetration::subMove(int x, int y, Submarine::Direction direction) { + if (!_sub->sub->canMove()) + return; + // Limit the movement to walkable tiles int16 minX = 0; - if ((_subTileX > 0) && !isWalkable(_mapTiles[_subTileY * kMapWidth + (_subTileX - 1)])) - minX = _subTileX * kMapTileWidth; + if ((_sub->x > 0) && !isWalkable(_mapTiles[_sub->y * kMapWidth + (_sub->x - 1)])) + minX = _sub->x * kMapTileWidth; int16 maxX = kMapWidth * kMapTileWidth; - if ((_subTileX < (kMapWidth - 1)) && !isWalkable(_mapTiles[_subTileY * kMapWidth + (_subTileX + 1)])) - maxX = _subTileX * kMapTileWidth; + if ((_sub->x < (kMapWidth - 1)) && !isWalkable(_mapTiles[_sub->y * kMapWidth + (_sub->x + 1)])) + maxX = _sub->x * kMapTileWidth; int16 minY = 0; - if ((_subTileY > 0) && !isWalkable(_mapTiles[(_subTileY - 1) * kMapWidth + _subTileX])) - minY = _subTileY * kMapTileHeight; + if ((_sub->y > 0) && !isWalkable(_mapTiles[(_sub->y - 1) * kMapWidth + _sub->x])) + minY = _sub->y * kMapTileHeight; int16 maxY = kMapHeight * kMapTileHeight; - if ((_subTileY < (kMapHeight - 1)) && !isWalkable(_mapTiles[(_subTileY + 1) * kMapWidth + _subTileX])) - maxY = _subTileY * kMapTileHeight; + if ((_sub->y < (kMapHeight - 1)) && !isWalkable(_mapTiles[(_sub->y + 1) * kMapWidth + _sub->x])) + maxY = _sub->y * kMapTileHeight; - _mapX = CLIP(_mapX + x, minX, maxX); - _mapY = CLIP(_mapY + y, minY, maxY); + _sub->mapX = CLIP(_sub->mapX + x, minX, maxX); + _sub->mapY = CLIP(_sub->mapY + y, minY, maxY); // The tile the sub is on is where its mid-point is - _subTileX = (_mapX + (kMapTileWidth / 2)) / kMapTileWidth; - _subTileY = (_mapY + (kMapTileHeight / 2)) / kMapTileHeight; + _sub->x = (_sub->mapX + (kMapTileWidth / 2)) / kMapTileWidth; + _sub->y = (_sub->mapY + (kMapTileHeight / 2)) / kMapTileHeight; - if (_sub->getAnimation() != animation) - _sub->setAnimation(animation); + _sub->sub->turn(direction); checkShields(); checkMouths(); } +void Penetration::subShoot() { + if (!_sub->sub->canMove()) + return; + + _sub->sub->shoot(); + + _vm->_sound->blasterPlay(&_soundShoot, 1, 0); +} + void Penetration::checkShields() { for (Common::List::iterator pos = _shields.begin(); pos != _shields.end(); ++pos) { - if ((pos->x == _subTileX) && (pos->y == _subTileY)) { + if ((pos->x == _sub->x) && (pos->y == _sub->y)) { // Charge shields _shieldMeter->setMaxValue(); @@ -558,13 +563,13 @@ void Penetration::checkMouths() { if (!m->mouth->isDeactivated()) continue; - if ((( m->x == _subTileX) && (m->y == _subTileY)) || - (((m->x + 1) == _subTileX) && (m->y == _subTileY))) { + if ((( m->x == _sub->x) && (m->y == _sub->y)) || + (((m->x + 1) == _sub->x) && (m->y == _sub->y))) { m->mouth->activate(); // Play the mouth sound and do health gain/loss - if (m->type == kMouthTypeBite) { + if (m->type == kMouthTypeBite) { _vm->_sound->blasterPlay(&_soundBite, 1, 0); healthLose(230); } else if (m->type == kMouthTypeKiss) { @@ -584,6 +589,17 @@ void Penetration::healthGain(int amount) { void Penetration::healthLose(int amount) { _healthMeter->decrease(_shieldMeter->decrease(amount)); + + if (_healthMeter->getValue() == 0) + _sub->sub->die(); +} + +bool Penetration::isDead() const { + return _sub && _sub->sub->isDead(); +} + +bool Penetration::hasWon() const { + return _floor > kFloorCount; } void Penetration::updateAnims() { @@ -612,11 +628,14 @@ void Penetration::updateAnims() { _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); } - // Draw the map - _vm->_draw->_backSurface->blit(*_map, _mapX, _mapY, - _mapX + kPlayAreaWidth - 1, _mapY + kPlayAreaHeight - 1, kPlayAreaX, kPlayAreaY); - _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, kPlayAreaX, kPlayAreaY, - kPlayAreaX + kPlayAreaWidth - 1, kPlayAreaY + kPlayAreaHeight - 1); + 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::iterator a = _anims.begin(); diff --git a/engines/gob/minigames/geisha/penetration.h b/engines/gob/minigames/geisha/penetration.h index 488396ea32..0582b99e83 100644 --- a/engines/gob/minigames/geisha/penetration.h +++ b/engines/gob/minigames/geisha/penetration.h @@ -28,6 +28,8 @@ #include "gob/sound/sounddesc.h" +#include "gob/minigames/geisha/submarine.h" + namespace Gob { class GobEngine; @@ -77,6 +79,18 @@ private: ~ManagedMouth(); }; + struct ManagedSub : public Position { + Submarine *sub; + + uint16 mapX; + uint16 mapY; + + ManagedSub(uint16 pX, uint16 pY); + ~ManagedSub(); + + void setPosition(uint16 pX, uint16 pY); + }; + GobEngine *_vm; bool _hasAccessPass; @@ -87,8 +101,6 @@ private: CMPFile *_sprites; ANIFile *_objects; - ANIObject *_sub; - Common::List _anims; Common::List _mapAnims; @@ -100,11 +112,7 @@ private: Surface *_map; byte _mapTiles[kMapWidth * kMapHeight]; - uint16 _mapX; - uint16 _mapY; - - uint8 _subTileX; - uint8 _subTileY; + ManagedSub *_sub; Common::List _shields; Common::List _mouths; @@ -112,6 +120,7 @@ private: SoundDesc _soundShield; SoundDesc _soundBite; SoundDesc _soundKiss; + SoundDesc _soundShoot; void init(); @@ -126,7 +135,8 @@ private: int16 checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); void handleSub(int16 key); - void moveSub(int x, int y, uint16 animation); + void subMove(int x, int y, Submarine::Direction direction); + void subShoot(); bool isWalkable(byte tile) const; @@ -135,6 +145,9 @@ private: void healthGain(int amount); void healthLose(int amount); + + bool isDead() const; + bool hasWon() const; }; } // End of namespace Geisha diff --git a/engines/gob/minigames/geisha/submarine.cpp b/engines/gob/minigames/geisha/submarine.cpp new file mode 100644 index 0000000000..4a18c6e043 --- /dev/null +++ b/engines/gob/minigames/geisha/submarine.cpp @@ -0,0 +1,233 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gob/minigames/geisha/submarine.h" + +namespace Gob { + +namespace Geisha { + +enum Animation { + kAnimationDriveS = 4, + kAnimationDriveE = 5, + kAnimationDriveN = 6, + kAnimationDriveW = 7, + kAnimationDriveSE = 8, + kAnimationDriveNE = 9, + kAnimationDriveSW = 10, + kAnimationDriveNW = 11, + kAnimationShootS = 12, + kAnimationShootN = 13, + kAnimationShootW = 14, + kAnimationShootE = 15, + kAnimationShootNE = 16, + kAnimationShootSE = 17, + kAnimationShootSW = 18, + kAnimationShootNW = 19, + kAnimationExplodeN = 28, + kAnimationExplodeS = 29, + kAnimationExplodeW = 30, + kAnimationExplodeE = 31, + kAnimationExit = 32 +}; + + +Submarine::Submarine(const ANIFile &ani) : ANIObject(ani), _state(kStateNone) { + turn(kDirectionN); +} + +Submarine::~Submarine() { +} + +void Submarine::turn(Direction to) { + // Nothing to do + if ((_state == kStateMove) && (_direction == to)) + return; + + _state = kStateMove; + _direction = to; + + setAnimation(directionToMove(_direction)); + setMode(kModeContinuous); + setPause(false); + setVisible(true); +} + +void Submarine::shoot() { + _state = kStateShoot; + + setAnimation(directionToShoot(_direction)); + setMode(kModeOnce); + setPause(false); + setVisible(true); +} + +void Submarine::die() { + _state = kStateDie; + + setAnimation(directionToExplode(_direction)); + setMode(kModeOnce); + setPause(false); + setVisible(true); +} + +void Submarine::leave() { + _state = kStateExit; + + setAnimation(kAnimationExit); + setMode(kModeOnce); + setPause(false); + setVisible(true); +} + +void Submarine::advance() { + ANIObject::advance(); + + switch (_state) { + case kStateShoot: + if (isPaused()) + turn(_direction); + break; + + case kStateExit: + if (isPaused()) { + _state = kStateExited; + + setVisible(true); + } + + break; + + case kStateDie: + if (isPaused()) + _state = kStateDead; + break; + + default: + break; + } +} + +bool Submarine::canMove() const { + return (_state == kStateMove) || (_state == kStateShoot); +} + +bool Submarine::isDead() const { + return _state == kStateDead; +} + +uint16 Submarine::directionToMove(Direction direction) const { + switch (direction) { + case kDirectionN: + return kAnimationDriveN; + + case kDirectionNE: + return kAnimationDriveNE; + + case kDirectionE: + return kAnimationDriveE; + + case kDirectionSE: + return kAnimationDriveSE; + + case kDirectionS: + return kAnimationDriveS; + + case kDirectionSW: + return kAnimationDriveSW; + + case kDirectionW: + return kAnimationDriveW; + + case kDirectionNW: + return kAnimationDriveNW; + + default: + break; + } + + return 0; +} + +uint16 Submarine::directionToShoot(Direction direction) const { + switch (direction) { + case kDirectionN: + return kAnimationShootN; + + case kDirectionNE: + return kAnimationShootNE; + + case kDirectionE: + return kAnimationShootE; + + case kDirectionSE: + return kAnimationShootSE; + + case kDirectionS: + return kAnimationShootS; + + case kDirectionSW: + return kAnimationShootSW; + + case kDirectionW: + return kAnimationShootW; + + case kDirectionNW: + return kAnimationShootNW; + + default: + break; + } + + return 0; +} + +uint16 Submarine::directionToExplode(Direction direction) const { + // Only 4 exploding animations (spinning clockwise) + + switch (direction) { + case kDirectionNW: + case kDirectionN: + return kAnimationExplodeN; + + case kDirectionNE: + case kDirectionE: + return kAnimationExplodeE; + + case kDirectionSE: + case kDirectionS: + return kAnimationExplodeS; + + case kDirectionSW: + case kDirectionW: + return kAnimationExplodeW; + + default: + break; + } + + return 0; +} + +} // End of namespace Geisha + +} // End of namespace Gob diff --git a/engines/gob/minigames/geisha/submarine.h b/engines/gob/minigames/geisha/submarine.h new file mode 100644 index 0000000000..e8ae72d996 --- /dev/null +++ b/engines/gob/minigames/geisha/submarine.h @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOB_MINIGAMES_GEISHA_SUBMARINE_H +#define GOB_MINIGAMES_GEISHA_SUBMARINE_H + +#include "gob/aniobject.h" + +namespace Gob { + +namespace Geisha { + +/** The submarine Geisha's "Penetration" minigame. */ +class Submarine : public ANIObject { +public: + enum Direction { + kDirectionN, + kDirectionNE, + kDirectionE, + kDirectionSE, + kDirectionS, + kDirectionSW, + kDirectionW, + kDirectionNW + }; + + Submarine(const ANIFile &ani); + ~Submarine(); + + /** Turn to the specified direction. */ + void turn(Direction to); + + /** Play the shoot animation. */ + void shoot(); + + /** Play the exploding animation. */ + void die(); + + /** Play the exiting animation. */ + void leave(); + + /** Advance the animation to the next frame. */ + void advance(); + + /** Can the submarine move at the moment? */ + bool canMove() const; + + /** Is the submarine dead? */ + bool isDead() const; + +private: + enum State { + kStateNone = 0, + kStateMove, + kStateShoot, + kStateExit, + kStateExited, + kStateDie, + kStateDead + }; + + State _state; + Direction _direction; + + /** Map the directions to move animation indices. */ + uint16 directionToMove(Direction direction) const; + /** Map the directions to shoot animation indices. */ + uint16 directionToShoot(Direction direction) const; + /** Map the directions to explode animation indices. */ + uint16 directionToExplode(Direction direction) const; +}; + +} // End of namespace Geisha + +} // End of namespace Gob + +#endif // GOB_MINIGAMES_GEISHA_SUBMARINE_H diff --git a/engines/gob/module.mk b/engines/gob/module.mk index c5ae947a1c..b9680fad6b 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -81,6 +81,7 @@ MODULE_OBJS := \ minigames/geisha/meter.o \ minigames/geisha/diving.o \ minigames/geisha/mouth.o \ + minigames/geisha/submarine.o \ minigames/geisha/penetration.o \ save/savefile.o \ save/savehandler.o \ -- cgit v1.2.3