From 25938316a881679a923bce1986f3a978432b0e76 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Tue, 5 Jun 2012 16:18:05 +0200 Subject: GOB: Animate mouths in Geisha's Penetration --- engines/gob/aniobject.h | 8 +- engines/gob/minigames/geisha/mouth.cpp | 170 +++++++++++++++++++++++++++ engines/gob/minigames/geisha/mouth.h | 75 ++++++++++++ engines/gob/minigames/geisha/penetration.cpp | 164 ++++++++++++++++++-------- engines/gob/minigames/geisha/penetration.h | 20 +++- engines/gob/module.mk | 1 + 6 files changed, 380 insertions(+), 58 deletions(-) create mode 100644 engines/gob/minigames/geisha/mouth.cpp create mode 100644 engines/gob/minigames/geisha/mouth.h (limited to 'engines') diff --git a/engines/gob/aniobject.h b/engines/gob/aniobject.h index bd4cf791a8..5ea1f75401 100644 --- a/engines/gob/aniobject.h +++ b/engines/gob/aniobject.h @@ -61,9 +61,9 @@ public: void setMode(Mode mode); /** Set the current position to the animation's default. */ - void setPosition(); + virtual void setPosition(); /** Set the current position. */ - void setPosition(int16 x, int16 y); + virtual void setPosition(int16 x, int16 y); /** Return the current position. */ void getPosition(int16 &x, int16 &y) const; @@ -93,9 +93,9 @@ public: bool lastFrame() const; /** Draw the current frame onto the surface and return the affected rectangle. */ - bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + virtual bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); /** Draw the current frame from the surface and return the affected rectangle. */ - bool clear(Surface &dest, int16 &left , int16 &top, int16 &right, int16 &bottom); + virtual bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); /** Advance the animation to the next frame. */ virtual void advance(); diff --git a/engines/gob/minigames/geisha/mouth.cpp b/engines/gob/minigames/geisha/mouth.cpp new file mode 100644 index 0000000000..605ffe420f --- /dev/null +++ b/engines/gob/minigames/geisha/mouth.cpp @@ -0,0 +1,170 @@ +/* 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 "common/util.h" +#include "common/textconsole.h" + +#include "gob/minigames/geisha/mouth.h" + +namespace Gob { + +namespace Geisha { + +Mouth::Mouth(const ANIFile &ani, const CMPFile &cmp, + uint16 mouthAnim, uint16 mouthSprite, uint16 floorSprite) : ANIObject(ani) { + + _sprite = new ANIObject(cmp); + _sprite->setAnimation(mouthSprite); + _sprite->setVisible(true); + + for (int i = 0; i < kFloorCount; i++) { + _floor[i] = new ANIObject(cmp); + _floor[i]->setAnimation(floorSprite); + _floor[i]->setVisible(true); + } + + _state = kStateDeactivated; + + setAnimation(mouthAnim); + setMode(kModeOnce); + setPause(true); + setVisible(true); +} + +Mouth::~Mouth() { + for (int i = 0; i < kFloorCount; i++) + delete _floor[i]; + + delete _sprite; +} + +void Mouth::advance() { + if (_state != kStateActivated) + return; + + // Animation finished, set state to dead + if (isPaused()) { + _state = kStateDead; + return; + } + + ANIObject::advance(); +} + +void Mouth::activate() { + if (_state != kStateDeactivated) + return; + + _state = kStateActivated; + + setPause(false); +} + +bool Mouth::isDeactivated() const { + return _state == kStateDeactivated; +} + +void Mouth::setPosition(int16 x, int16 y) { + ANIObject::setPosition(x, y); + + int16 floorWidth, floorHeight; + _floor[0]->getFrameSize(floorWidth, floorHeight); + + _sprite->setPosition(x, y); + + for (int i = 0; i < kFloorCount; i++) + _floor[i]->setPosition(x + (i * floorWidth), y); +} + +bool Mouth::draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + // If the mouth is deactivated, draw the default mouth sprite + if (_state == kStateDeactivated) + return _sprite->draw(dest, left, top, right, bottom); + + // If the mouth is activated, draw the current mouth animation sprite + if (_state == kStateActivated) + return ANIObject::draw(dest, left, top, right, bottom); + + // If the mouth is dead, draw the floor tiles + if (_state == kStateDead) { + int16 fLeft, fRight, fTop, fBottom; + bool drawn = false; + + left = 0x7FFF; + top = 0x7FFF; + right = 0; + bottom = 0; + + for (int i = 0; i < kFloorCount; i++) { + if (_floor[i]->draw(dest, fLeft, fTop, fRight, fBottom)) { + drawn = true; + left = MIN(left , fLeft); + top = MIN(top , fTop); + right = MAX(right , fRight); + bottom = MAX(bottom, fBottom); + } + } + + return drawn; + } + + return false; +} + +bool Mouth::clear(Surface &dest, int16 &left , int16 &top, int16 &right, int16 &bottom) { + // If the mouth is deactivated, clear the default mouth sprite + if (_state == kStateDeactivated) + return _sprite->clear(dest, left, top, right, bottom); + + // If the mouth is activated, clear the current mouth animation sprite + if (_state == kStateActivated) + return ANIObject::clear(dest, left, top, right, bottom); + + // If the mouth is clear, draw the floor tiles + if (_state == kStateDead) { + int16 fLeft, fRight, fTop, fBottom; + bool cleared = false; + + left = 0x7FFF; + top = 0x7FFF; + right = 0; + bottom = 0; + + for (int i = 0; i < kFloorCount; i++) { + if (_floor[i]->clear(dest, fLeft, fTop, fRight, fBottom)) { + cleared = true; + left = MIN(left , fLeft); + top = MIN(top , fTop); + right = MAX(right , fRight); + bottom = MAX(bottom, fBottom); + } + } + + return cleared; + } + + return false; +} + +} // End of namespace Geisha + +} // End of namespace Gob diff --git a/engines/gob/minigames/geisha/mouth.h b/engines/gob/minigames/geisha/mouth.h new file mode 100644 index 0000000000..2e0cfcd5d0 --- /dev/null +++ b/engines/gob/minigames/geisha/mouth.h @@ -0,0 +1,75 @@ +/* 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_MOUTH_H +#define GOB_MINIGAMES_GEISHA_MOUTH_H + +#include "gob/aniobject.h" + +namespace Gob { + +namespace Geisha { + +/** A kissing/biting mouth in Geisha's "Penetration" minigame. */ +class Mouth : public ANIObject { +public: + Mouth(const ANIFile &ani, const CMPFile &cmp, + uint16 mouthAnim, uint16 mouthSprite, uint16 floorSprite); + ~Mouth(); + + /** Advance the animation to the next frame. */ + void advance(); + + /** Active the mouth's animation. */ + void activate(); + + /** Is the mouth deactivated? */ + bool isDeactivated() const; + + /** Set the current position. */ + void setPosition(int16 x, int16 y); + + /** Draw the current frame onto the surface and return the affected rectangle. */ + bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + /** Draw the current frame from the surface and return the affected rectangle. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + +private: + static const int kFloorCount = 2; + + enum State { + kStateDeactivated, + kStateActivated, + kStateDead + }; + + ANIObject *_sprite; + ANIObject *_floor[kFloorCount]; + + State _state; +}; + +} // End of namespace Geisha + +} // End of namespace Gob + +#endif // GOB_MINIGAMES_GEISHA_MOUTH_H diff --git a/engines/gob/minigames/geisha/penetration.cpp b/engines/gob/minigames/geisha/penetration.cpp index 35802e6733..c8f96f825a 100644 --- a/engines/gob/minigames/geisha/penetration.cpp +++ b/engines/gob/minigames/geisha/penetration.cpp @@ -31,6 +31,7 @@ #include "gob/minigames/geisha/penetration.h" #include "gob/minigames/geisha/meter.h" +#include "gob/minigames/geisha/mouth.h" namespace Gob { @@ -60,28 +61,39 @@ static const int kColorHealth = 15; static const int kColorBlack = 10; static const int kColorFloor = 13; +enum Sprite { + kSpriteFloorShield = 25, + kSpriteExit = 29, + kSpriteFloor = 30, + kSpriteWall = 31, + kSpriteMouthBite = 32, + kSpriteMouthKiss = 33 +}; + 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 + 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 }; static const int kMapTileWidth = 24; @@ -197,8 +209,17 @@ Penetration::Position::Position(uint16 pX, uint16 pY) : x(pX), y(pY) { } +Penetration::ManagedMouth::ManagedMouth(uint16 pX, uint16 pY, MouthType t) : + Position(pX, pY), mouth(0), type(t) { +} + +Penetration::ManagedMouth::~ManagedMouth() { + delete mouth; +} + + Penetration::Penetration(GobEngine *vm) : _vm(vm), _background(0), _sprites(0), _objects(0), _sub(0), - _shieldMeter(0), _healthMeter(0), _floor(0), _mapUpdate(false), _mapX(0), _mapY(0), + _shieldMeter(0), _healthMeter(0), _floor(0), _mapX(0), _mapY(0), _subTileX(0), _subTileY(0) { _background = new Surface(320, 200, 1); @@ -279,6 +300,9 @@ void Penetration::init() { createMap(); + for (Common::List::iterator m = _mouths.begin(); m != _mouths.end(); m++) + _mapAnims.push_back(m->mouth); + _sub = new ANIObject(*_objects); _sub->setAnimation(kAnimationDriveN); @@ -289,8 +313,13 @@ void Penetration::init() { } void Penetration::deinit() { + _mapAnims.clear(); _anims.clear(); + _shields.clear(); + + _mouths.clear(); + delete _sub; delete _objects; @@ -311,6 +340,8 @@ void Penetration::createMap() { _shields.clear(); + _mouths.clear(); + _map->fill(kColorBlack); // Draw the map tiles @@ -323,23 +354,21 @@ void Penetration::createMap() { switch (*mapTile) { case 0: // Floor - _sprites->draw(*_map, 30, posX, posY); + _sprites->draw(*_map, kSpriteFloor, posX, posY); break; case 49: // Emergency exit (needs access pass) if (_hasAccessPass) { - // Draw an exit. Now works like a regular exit - _sprites->draw(*_map, 29, posX, posY); - *mapTile = 51; + _sprites->draw(*_map, kSpriteExit, posX, posY); + *mapTile = 51; // Now works like a normal exit } else - // Draw a wall - _sprites->draw(*_map, 31, posX, posY); + _sprites->draw(*_map, kSpriteWall, posX, posY); break; case 50: // Wall - _sprites->draw(*_map, 31, posX, posY); + _sprites->draw(*_map, kSpriteWall, posX, posY); break; case 51: // Regular exit @@ -349,23 +378,27 @@ void Penetration::createMap() { if (_floor == 2) { if (!_hasAccessPass) { - // It's now a wall - _sprites->draw(*_map, 31, posX, posY); - *mapTile = 50; + _sprites->draw(*_map, kSpriteWall, posX, posY); + *mapTile = 50; // It's now a wall } else - _sprites->draw(*_map, 29, posX, posY); + _sprites->draw(*_map, kSpriteExit, posX, posY); } else - _sprites->draw(*_map, 29, posX, posY); + _sprites->draw(*_map, kSpriteExit, posX, posY); } else // Always works in test mode - _sprites->draw(*_map, 29, posX, posY); + _sprites->draw(*_map, kSpriteExit, posX, posY); break; case 52: // Left side of biting mouth - _sprites->draw(*_map, 32, posX, posY); + _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 @@ -373,7 +406,12 @@ void Penetration::createMap() { break; case 54: // Left side of kissing mouth - _sprites->draw(*_map, 33, posX, posY); + _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 @@ -381,8 +419,8 @@ void Penetration::createMap() { break; case 56: // Shield lying on the floor - _sprites->draw(*_map, 30, posX , posY ); // Floor - _sprites->draw(*_map, 25, posX + 4, posY + 8); // Shield + _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 @@ -391,7 +429,7 @@ void Penetration::createMap() { break; case 57: // Start position - _sprites->draw(*_map, 30, posX, posY); + _sprites->draw(*_map, kSpriteFloor, posX, posY); *mapTile = 0; _subTileX = x; @@ -403,8 +441,6 @@ void Penetration::createMap() { } } } - - _mapUpdate = true; } void Penetration::initScreen() { @@ -479,12 +515,11 @@ void Penetration::moveSub(int x, int y, uint16 animation) { _subTileX = (_mapX + (kMapTileWidth / 2)) / kMapTileWidth; _subTileY = (_mapY + (kMapTileHeight / 2)) / kMapTileHeight; - _mapUpdate = true; - if (_sub->getAnimation() != animation) _sub->setAnimation(animation); checkShields(); + checkMouths(); } void Penetration::checkShields() { @@ -504,9 +539,37 @@ void Penetration::checkShields() { } } +void Penetration::checkMouths() { + for (Common::List::iterator m = _mouths.begin(); m != _mouths.end(); ++m) { + if (!m->mouth->isDeactivated()) + continue; + + if ((( m->x == _subTileX) && (m->y == _subTileY)) || + (((m->x + 1) == _subTileX) && (m->y == _subTileY))) { + + m->mouth->activate(); + } + } +} + void Penetration::updateAnims() { int16 left = 0, top = 0, right = 0, bottom = 0; + // Clear the previous map animation frames + for (Common::List::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::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::iterator a = _anims.reverse_begin(); a != _anims.end(); --a) { @@ -515,14 +578,11 @@ void Penetration::updateAnims() { _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); } - if (_mapUpdate) { - _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); - } - - _mapUpdate = false; + // 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); // 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 4d3455b638..ef0e3b10f0 100644 --- a/engines/gob/minigames/geisha/penetration.h +++ b/engines/gob/minigames/geisha/penetration.h @@ -36,6 +36,7 @@ class ANIFile; namespace Geisha { class Meter; +class Mouth; /** Geisha's "Penetration" minigame. */ class Penetration { @@ -61,6 +62,19 @@ private: Position(uint16 pX, uint16 pY); }; + enum MouthType { + kMouthTypeBite, + kMouthTypeKiss + }; + + struct ManagedMouth : public Position { + Mouth *mouth; + MouthType type; + + ManagedMouth(uint16 pX, uint16 pY, MouthType t); + ~ManagedMouth(); + }; + GobEngine *_vm; bool _hasAccessPass; @@ -74,6 +88,7 @@ private: ANIObject *_sub; Common::List _anims; + Common::List _mapAnims; Meter *_shieldMeter; Meter *_healthMeter; @@ -83,14 +98,14 @@ private: Surface *_map; byte _mapTiles[kMapWidth * kMapHeight]; - bool _mapUpdate; uint16 _mapX; uint16 _mapY; uint8 _subTileX; uint8 _subTileY; - Common::List _shields; + Common::List _shields; + Common::List _mouths; void init(); @@ -110,6 +125,7 @@ private: bool isWalkable(byte tile) const; void checkShields(); + void checkMouths(); }; } // End of namespace Geisha diff --git a/engines/gob/module.mk b/engines/gob/module.mk index 9da5a82de2..c5ae947a1c 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -80,6 +80,7 @@ MODULE_OBJS := \ minigames/geisha/oko.o \ minigames/geisha/meter.o \ minigames/geisha/diving.o \ + minigames/geisha/mouth.o \ minigames/geisha/penetration.o \ save/savefile.o \ save/savehandler.o \ -- cgit v1.2.3