Initial commit
This commit is contained in:
837
engines/gob/minigames/geisha/diving.cpp
Normal file
837
engines/gob/minigames/geisha/diving.cpp
Normal file
@@ -0,0 +1,837 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
#include "gob/global.h"
|
||||
#include "gob/palanim.h"
|
||||
#include "gob/draw.h"
|
||||
#include "gob/video.h"
|
||||
#include "gob/decfile.h"
|
||||
#include "gob/anifile.h"
|
||||
|
||||
#include "gob/sound/sound.h"
|
||||
|
||||
#include "gob/minigames/geisha/evilfish.h"
|
||||
#include "gob/minigames/geisha/oko.h"
|
||||
#include "gob/minigames/geisha/meter.h"
|
||||
#include "gob/minigames/geisha/diving.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
static const uint8 kAirDecreaseRate = 15;
|
||||
|
||||
static const byte kPalette[48] = {
|
||||
0x00, 0x02, 0x12,
|
||||
0x01, 0x04, 0x1D,
|
||||
0x05, 0x08, 0x28,
|
||||
0x0C, 0x0D, 0x33,
|
||||
0x15, 0x14, 0x3F,
|
||||
0x00, 0x3F, 0x00,
|
||||
0x3F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00,
|
||||
0x21, 0x0D, 0x00,
|
||||
0x2F, 0x1A, 0x04,
|
||||
0x3D, 0x2B, 0x0D,
|
||||
0x10, 0x10, 0x10,
|
||||
0x1A, 0x1A, 0x1A,
|
||||
0x24, 0x24, 0x24,
|
||||
0x00, 0x01, 0x0F,
|
||||
0x3F, 0x3F, 0x3F
|
||||
};
|
||||
|
||||
enum Animation {
|
||||
kAnimationLungs = 0,
|
||||
kAnimationHeart = 1,
|
||||
kAnimationPearl = 4,
|
||||
kAnimationJellyfish = 6,
|
||||
kAnimationWater = 7,
|
||||
kAnimationShot = 17,
|
||||
kAnimationSwarmRedGreen = 32,
|
||||
kAnimationSwarmOrange = 33
|
||||
};
|
||||
|
||||
|
||||
const uint16 Diving::kEvilFishTypes[kEvilFishTypeCount][5] = {
|
||||
{ 0, 14, 8, 9, 3}, // Shark
|
||||
{15, 1, 12, 13, 3}, // Moray
|
||||
{16, 2, 10, 11, 3} // Ray
|
||||
};
|
||||
|
||||
const uint16 Diving::kPlantLevel1[] = { 18, 19, 20, 21 };
|
||||
const uint16 Diving::kPlantLevel2[] = { 22, 23, 24, 25 };
|
||||
const uint16 Diving::kPlantLevel3[] = { 26, 27, 28, 29, 30 };
|
||||
|
||||
const Diving::PlantLevel Diving::kPlantLevels[] = {
|
||||
{ 150, ARRAYSIZE(kPlantLevel1), kPlantLevel1 },
|
||||
{ 120, ARRAYSIZE(kPlantLevel2), kPlantLevel2 },
|
||||
{ 108, ARRAYSIZE(kPlantLevel3), kPlantLevel3 },
|
||||
};
|
||||
|
||||
|
||||
Diving::Diving(GobEngine *vm) : _vm(vm), _background(nullptr),
|
||||
_objects(nullptr), _gui(nullptr), _okoAnim(nullptr), _water(nullptr), _lungs(nullptr), _heart(nullptr),
|
||||
_blackPearl(nullptr), _airMeter(nullptr), _healthMeter(nullptr), _isPlaying(false) {
|
||||
|
||||
_blackPearl = new Surface(11, 8, 1);
|
||||
|
||||
_airMeter = new Meter(3 , 195, 40, 2, 5, 7, 40, Meter::kFillToLeft);
|
||||
_healthMeter = new Meter(275, 195, 40, 2, 6, 7, 4, Meter::kFillToLeft);
|
||||
|
||||
for (uint i = 0; i < kEvilFishCount; i++)
|
||||
_evilFish[i].evilFish = nullptr;
|
||||
|
||||
for (uint i = 0; i < kDecorFishCount; i++)
|
||||
_decorFish[i].decorFish = nullptr;
|
||||
|
||||
for (uint i = 0; i < kPlantCount; i++)
|
||||
_plant[i].plant = nullptr;
|
||||
|
||||
for (uint i = 0; i < kMaxShotCount; i++)
|
||||
_shot[i] = nullptr;
|
||||
|
||||
_pearl.pearl = nullptr;
|
||||
|
||||
_oko = nullptr;
|
||||
}
|
||||
|
||||
Diving::~Diving() {
|
||||
delete _airMeter;
|
||||
delete _healthMeter;
|
||||
|
||||
delete _blackPearl;
|
||||
|
||||
deinit();
|
||||
}
|
||||
|
||||
bool Diving::play(uint16 playerCount, bool hasPearlLocation) {
|
||||
_hasPearlLocation = hasPearlLocation;
|
||||
_isPlaying = true;
|
||||
|
||||
// Fade to black
|
||||
_vm->_palAnim->fade(nullptr, 0, 0);
|
||||
|
||||
// Initialize our playing field
|
||||
init();
|
||||
initScreen();
|
||||
initCursor();
|
||||
initPlants();
|
||||
|
||||
updateAirMeter();
|
||||
updateAnims();
|
||||
|
||||
_vm->_draw->blitInvalidated();
|
||||
_vm->_video->retrace();
|
||||
|
||||
// Fade in
|
||||
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0);
|
||||
|
||||
while (!_vm->shouldQuit()) {
|
||||
checkShots(); // Check if a shot hit something
|
||||
checkOkoHurt(); // Check if Oko was hurt
|
||||
|
||||
// Is Oko dead?
|
||||
if (_oko->isPaused())
|
||||
break;
|
||||
|
||||
// Update all objects and animations
|
||||
updateAirMeter();
|
||||
updateEvilFish();
|
||||
updateDecorFish();
|
||||
updatePlants();
|
||||
updatePearl();
|
||||
updateAnims();
|
||||
|
||||
_vm->_draw->animateCursor(1);
|
||||
|
||||
// Draw and wait for the end of the frame
|
||||
_vm->_draw->blitInvalidated();
|
||||
_vm->_util->waitEndFrame();
|
||||
|
||||
// Handle input
|
||||
_vm->_util->processInput();
|
||||
|
||||
int16 mouseX, mouseY;
|
||||
MouseButtons mouseButtons;
|
||||
|
||||
int16 key = checkInput(mouseX, mouseY, mouseButtons);
|
||||
|
||||
// Aborting the game
|
||||
if (key == kKeyEscape)
|
||||
break;
|
||||
|
||||
// Shoot the gun
|
||||
if (mouseButtons == kMouseButtonsLeft)
|
||||
shoot(mouseX, mouseY);
|
||||
|
||||
// Oko
|
||||
handleOko(key);
|
||||
|
||||
// Game end check
|
||||
if ((_whitePearlCount >= 20) || (_blackPearlCount >= 2))
|
||||
break;
|
||||
}
|
||||
|
||||
deinit();
|
||||
|
||||
_isPlaying = false;
|
||||
|
||||
// The game succeeded when we got 2 black pearls
|
||||
return _blackPearlCount >= 2;
|
||||
}
|
||||
|
||||
bool Diving::isPlaying() const {
|
||||
return _isPlaying;
|
||||
}
|
||||
|
||||
void Diving::cheatWin() {
|
||||
_blackPearlCount = 2;
|
||||
}
|
||||
|
||||
void Diving::init() {
|
||||
// Load sounds
|
||||
_vm->_sound->sampleLoad(&_soundShoot , SOUND_SND, "tirgim.snd");
|
||||
_vm->_sound->sampleLoad(&_soundBreathe , SOUND_SND, "respir.snd");
|
||||
_vm->_sound->sampleLoad(&_soundWhitePearl, SOUND_SND, "virtou.snd");
|
||||
_vm->_sound->sampleLoad(&_soundBlackPearl, SOUND_SND, "trouve.snd");
|
||||
|
||||
// Load and initialize sprites and animations
|
||||
_background = new DECFile(_vm, "tperle.dec" , 320, 200);
|
||||
_objects = new ANIFile(_vm, "tperle.ani" , 320);
|
||||
_gui = new ANIFile(_vm, "tperlcpt.ani", 320);
|
||||
_okoAnim = new ANIFile(_vm, "tplonge.ani" , 320);
|
||||
|
||||
_water = new ANIObject(*_objects);
|
||||
_lungs = new ANIObject(*_gui);
|
||||
_heart = new ANIObject(*_gui);
|
||||
|
||||
_water->setAnimation(kAnimationWater);
|
||||
_water->setPosition();
|
||||
_water->setVisible(true);
|
||||
|
||||
_lungs->setAnimation(kAnimationLungs);
|
||||
_lungs->setPosition();
|
||||
_lungs->setVisible(true);
|
||||
_lungs->setPause(true);
|
||||
|
||||
_heart->setAnimation(kAnimationHeart);
|
||||
_heart->setPosition();
|
||||
_heart->setVisible(true);
|
||||
_heart->setPause(true);
|
||||
|
||||
for (uint i = 0; i < kEvilFishCount; i++) {
|
||||
_evilFish[i].enterAt = 0;
|
||||
_evilFish[i].leaveAt = 0;
|
||||
|
||||
_evilFish[i].evilFish = new EvilFish(*_objects, 320, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < kDecorFishCount; i++) {
|
||||
_decorFish[i].enterAt = 0;
|
||||
|
||||
_decorFish[i].decorFish = new ANIObject(*_objects);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < kPlantCount; i++) {
|
||||
_plant[i].level = i / kPlantPerLevelCount;
|
||||
_plant[i].deltaX = (kPlantLevelCount - _plant[i].level) * -2;
|
||||
|
||||
_plant[i].x = -1;
|
||||
_plant[i].y = -1;
|
||||
|
||||
_plant[i].plant = new ANIObject(*_objects);
|
||||
}
|
||||
|
||||
_pearl.pearl = new ANIObject(*_objects);
|
||||
_pearl.black = false;
|
||||
|
||||
_pearl.pearl->setAnimation(kAnimationPearl);
|
||||
|
||||
_decorFish[0].decorFish->setAnimation(kAnimationJellyfish);
|
||||
_decorFish[0].deltaX = 0;
|
||||
|
||||
_decorFish[1].decorFish->setAnimation(kAnimationSwarmRedGreen);
|
||||
_decorFish[1].deltaX = -5;
|
||||
|
||||
_decorFish[2].decorFish->setAnimation(kAnimationSwarmOrange);
|
||||
_decorFish[2].deltaX = -5;
|
||||
|
||||
for (uint i = 0; i < kMaxShotCount; i++) {
|
||||
_shot[i] = new ANIObject(*_objects);
|
||||
|
||||
_shot[i]->setAnimation(kAnimationShot);
|
||||
_shot[i]->setMode(ANIObject::kModeOnce);
|
||||
}
|
||||
|
||||
_oko = new Oko(*_okoAnim, *_vm->_sound, _soundBreathe);
|
||||
|
||||
Surface tmp(320, 103, 1);
|
||||
|
||||
_vm->_video->drawPackedSprite("tperlobj.cmp", tmp);
|
||||
|
||||
_blackPearl->blit(tmp, 282, 80, 292, 87, 0, 0);
|
||||
|
||||
_blackPearlCount = 0;
|
||||
|
||||
_currentShot = 0;
|
||||
|
||||
// Add the animations to our animation list
|
||||
_anims.push_back(_water);
|
||||
for (uint i = 0; i < kMaxShotCount; i++)
|
||||
_anims.push_back(_shot[i]);
|
||||
_anims.push_back(_pearl.pearl);
|
||||
for (uint i = 0; i < kDecorFishCount; i++)
|
||||
_anims.push_back(_decorFish[i].decorFish);
|
||||
for (uint i = 0; i < kEvilFishCount; i++)
|
||||
_anims.push_back(_evilFish[i].evilFish);
|
||||
for (int i = kPlantCount - 1; i >= 0; i--)
|
||||
_anims.push_back(_plant[i].plant);
|
||||
_anims.push_back(_oko);
|
||||
_anims.push_back(_lungs);
|
||||
_anims.push_back(_heart);
|
||||
|
||||
// Air and health meter
|
||||
_airMeter->setMaxValue();
|
||||
_healthMeter->setMaxValue();
|
||||
|
||||
_airCycle = 0;
|
||||
_hurtGracePeriod = 0;
|
||||
|
||||
_whitePearlCount = 0;
|
||||
_blackPearlCount = 0;
|
||||
}
|
||||
|
||||
void Diving::deinit() {
|
||||
_vm->_draw->_cursorHotspotX = -1;
|
||||
_vm->_draw->_cursorHotspotY = -1;
|
||||
|
||||
_soundShoot.free();
|
||||
_soundBreathe.free();
|
||||
_soundWhitePearl.free();
|
||||
_soundBlackPearl.free();
|
||||
|
||||
_anims.clear();
|
||||
|
||||
_activeShots.clear();
|
||||
|
||||
for (uint i = 0; i < kMaxShotCount; i++) {
|
||||
delete _shot[i];
|
||||
|
||||
_shot[i] = nullptr;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < kEvilFishCount; i++) {
|
||||
delete _evilFish[i].evilFish;
|
||||
|
||||
_evilFish[i].evilFish = nullptr;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < kDecorFishCount; i++) {
|
||||
delete _decorFish[i].decorFish;
|
||||
|
||||
_decorFish[i].decorFish = nullptr;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < kPlantCount; i++) {
|
||||
delete _plant[i].plant;
|
||||
|
||||
_plant[i].plant = nullptr;
|
||||
}
|
||||
|
||||
delete _pearl.pearl;
|
||||
_pearl.pearl = nullptr;
|
||||
|
||||
delete _oko;
|
||||
_oko = nullptr;
|
||||
|
||||
delete _heart;
|
||||
delete _lungs;
|
||||
delete _water;
|
||||
|
||||
delete _okoAnim;
|
||||
delete _gui;
|
||||
delete _objects;
|
||||
delete _background;
|
||||
|
||||
_water = nullptr;
|
||||
_heart = nullptr;
|
||||
_lungs = nullptr;
|
||||
|
||||
_okoAnim = nullptr;
|
||||
_gui = nullptr;
|
||||
_objects = nullptr;
|
||||
_background = nullptr;
|
||||
}
|
||||
|
||||
void Diving::initScreen() {
|
||||
// Set framerate
|
||||
_vm->_util->setFrameRate(15);
|
||||
|
||||
// Set palette
|
||||
memcpy(_vm->_draw->_vgaPalette, kPalette, sizeof(kPalette));
|
||||
|
||||
// Draw background decal
|
||||
_vm->_draw->_backSurface->clear();
|
||||
_background->draw(*_vm->_draw->_backSurface);
|
||||
|
||||
// Draw heart and lung boxes
|
||||
int16 left, top, right, bottom;
|
||||
_lungs->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
|
||||
_heart->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
|
||||
|
||||
// Mark everything as dirty
|
||||
_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199);
|
||||
}
|
||||
|
||||
void Diving::initCursor() {
|
||||
const int index = _vm->_draw->_cursorIndex;
|
||||
|
||||
const int16 left = index * _vm->_draw->_cursorWidth;
|
||||
const int16 top = 0;
|
||||
const int16 right = left + _vm->_draw->_cursorWidth - 1;
|
||||
const int16 bottom = _vm->_draw->_cursorHeight - 1;
|
||||
|
||||
_vm->_draw->_cursorSprites->fillRect(left, top, right, bottom, 0);
|
||||
|
||||
_objects->draw(*_vm->_draw->_cursorSprites, 31, 0, left, top);
|
||||
_vm->_draw->_cursorAnimLow[index] = 0;
|
||||
|
||||
_vm->_draw->_cursorHotspotX = 8;
|
||||
_vm->_draw->_cursorHotspotY = 8;
|
||||
}
|
||||
|
||||
|
||||
void Diving::initPlants() {
|
||||
// Create initial plantlife
|
||||
for (uint i = 0; i < kPlantLevelCount; i++) {
|
||||
for (uint j = 0; j < kPlantPerLevelCount; j++) {
|
||||
int16 prevPlantX = -100;
|
||||
if (j > 0)
|
||||
prevPlantX = _plant[i * kPlantPerLevelCount + j - 1].x;
|
||||
|
||||
enterPlant(_plant[i * kPlantPerLevelCount + j], prevPlantX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Diving::enterPlant(ManagedPlant &plant, int16 prevPlantX) {
|
||||
// Create a new plant outside the borders of the screen to scroll in
|
||||
|
||||
const PlantLevel &level = kPlantLevels[plant.level];
|
||||
const uint anim = level.plants[_vm->_util->getRandom(kPlantLevels[plant.level].plantCount)];
|
||||
|
||||
plant.plant->setAnimation(anim);
|
||||
plant.plant->rewind();
|
||||
|
||||
int16 width, height;
|
||||
plant.plant->getFrameSize(width, height);
|
||||
|
||||
// The new plant is created 140 - 160 pixels to the right of the right-most plant
|
||||
plant.x = prevPlantX + 150 - 10 + _vm->_util->getRandom(21);
|
||||
plant.y = kPlantLevels[plant.level].y - height;
|
||||
|
||||
plant.plant->setPosition(plant.x, plant.y);
|
||||
plant.plant->setVisible(true);
|
||||
plant.plant->setPause(false);
|
||||
|
||||
// If the plant is outside of the screen, create a pearl too if necessary
|
||||
if (plant.x > 320)
|
||||
enterPearl(plant.x);
|
||||
}
|
||||
|
||||
void Diving::enterPearl(int16 x) {
|
||||
// Create a pearl outside the borders of the screen to scroll in
|
||||
|
||||
// Only one pearl is ever visible
|
||||
if (_pearl.pearl->isVisible())
|
||||
return;
|
||||
|
||||
// Only every 4th potential pearl position has a pearl
|
||||
if (_vm->_util->getRandom(4) != 0)
|
||||
return;
|
||||
|
||||
// Every 5th pearl is a black one, but only if the location is correct
|
||||
_pearl.black = _hasPearlLocation && (_vm->_util->getRandom(5) == 0);
|
||||
|
||||
// Set the pearl about in the middle of two bottom-level plants
|
||||
_pearl.pearl->setPosition(x + 80, 130);
|
||||
|
||||
_pearl.pearl->setVisible(true);
|
||||
_pearl.pearl->setPause(false);
|
||||
_pearl.picked = false;
|
||||
}
|
||||
|
||||
void Diving::updateAirMeter() {
|
||||
if (_oko->isBreathing()) {
|
||||
// If Oko is breathing, increase the air meter and play the lungs animation
|
||||
_airCycle = 0;
|
||||
_airMeter->increase();
|
||||
_lungs->setPause(false);
|
||||
return;
|
||||
} else
|
||||
// Otherwise, don't play the lungs animation
|
||||
_lungs->setPause(true);
|
||||
|
||||
// Update the air cycle and decrease the air meter when the cycle ended
|
||||
_airCycle = (_airCycle + 1) % kAirDecreaseRate;
|
||||
|
||||
if (_airCycle == 0)
|
||||
_airMeter->decrease();
|
||||
|
||||
// Without any air, Oko dies
|
||||
if (_airMeter->getValue() == 0)
|
||||
_oko->die();
|
||||
}
|
||||
|
||||
void Diving::updateEvilFish() {
|
||||
for (uint i = 0; i < kEvilFishCount; i++) {
|
||||
ManagedEvilFish &fish = _evilFish[i];
|
||||
|
||||
if (fish.evilFish->isVisible()) {
|
||||
// Evil fishes leave on their own after 30s - 40s
|
||||
|
||||
fish.enterAt = 0;
|
||||
|
||||
if (fish.leaveAt == 0)
|
||||
fish.leaveAt = _vm->_util->getTimeKey() + 30000 + _vm->_util->getRandom(10000);
|
||||
|
||||
if (_vm->_util->getTimeKey() >= fish.leaveAt)
|
||||
fish.evilFish->leave();
|
||||
|
||||
} else {
|
||||
// Evil fishes enter the screen in 2s - 10s
|
||||
|
||||
fish.leaveAt = 0;
|
||||
|
||||
if (fish.enterAt == 0)
|
||||
fish.enterAt = _vm->_util->getTimeKey() + 2000 + _vm->_util->getRandom(8000);
|
||||
|
||||
if (_vm->_util->getTimeKey() >= fish.enterAt) {
|
||||
// The new fish has a random type
|
||||
int fishType = _vm->_util->getRandom(kEvilFishTypeCount);
|
||||
fish.evilFish->mutate(kEvilFishTypes[fishType][0], kEvilFishTypes[fishType][1],
|
||||
kEvilFishTypes[fishType][2], kEvilFishTypes[fishType][3],
|
||||
kEvilFishTypes[fishType][4]);
|
||||
|
||||
fish.evilFish->enter((EvilFish::Direction)_vm->_util->getRandom(2),
|
||||
36 + _vm->_util->getRandom(3) * 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Diving::updateDecorFish() {
|
||||
for (uint i = 0; i < kDecorFishCount; i++) {
|
||||
ManagedDecorFish &fish = _decorFish[i];
|
||||
|
||||
if (fish.decorFish->isVisible()) {
|
||||
// Move the fish
|
||||
int16 x, y;
|
||||
fish.decorFish->getPosition(x, y);
|
||||
fish.decorFish->setPosition(x + fish.deltaX, y);
|
||||
|
||||
// Check if the fish has left the screen
|
||||
int16 width, height;
|
||||
fish.decorFish->getFramePosition(x, y);
|
||||
fish.decorFish->getFrameSize(width, height);
|
||||
|
||||
if ((x + width) <= 0) {
|
||||
fish.decorFish->setVisible(false);
|
||||
fish.decorFish->setPause(true);
|
||||
|
||||
fish.enterAt = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Decor fishes enter the screen every 0s - 10s
|
||||
|
||||
if (fish.enterAt == 0)
|
||||
fish.enterAt = _vm->_util->getTimeKey() + _vm->_util->getRandom(10000);
|
||||
|
||||
if (_vm->_util->getTimeKey() >= fish.enterAt) {
|
||||
fish.decorFish->rewind();
|
||||
fish.decorFish->setPosition(320, 30 + _vm->_util->getRandom(100));
|
||||
fish.decorFish->setVisible(true);
|
||||
fish.decorFish->setPause(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Diving::updatePlants() {
|
||||
// When Oko isn't moving, the plants don't continue to scroll by
|
||||
if (!_oko->isMoving())
|
||||
return;
|
||||
|
||||
for (uint i = 0; i < kPlantCount; i++) {
|
||||
ManagedPlant &plant = _plant[i];
|
||||
|
||||
if (plant.plant->isVisible()) {
|
||||
// Move the plant
|
||||
plant.plant->setPosition(plant.x += plant.deltaX, plant.y);
|
||||
|
||||
// Check if the plant has left the screen
|
||||
int16 x, y, width, height;
|
||||
plant.plant->getFramePosition(x, y);
|
||||
plant.plant->getFrameSize(width, height);
|
||||
|
||||
if ((x + width) <= 0) {
|
||||
plant.plant->setVisible(false);
|
||||
plant.plant->setPause(true);
|
||||
|
||||
plant.x = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Find the right-most plant in this level and enter the plant to the right of it
|
||||
|
||||
int16 rightX = 320;
|
||||
for (uint j = 0; j < kPlantPerLevelCount; j++)
|
||||
rightX = MAX(rightX, _plant[plant.level * kPlantPerLevelCount + j].x);
|
||||
|
||||
enterPlant(plant, rightX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Diving::updatePearl() {
|
||||
if (!_pearl.pearl->isVisible())
|
||||
return;
|
||||
|
||||
// When Oko isn't moving, the pearl doesn't continue to scroll by
|
||||
if (!_oko->isMoving())
|
||||
return;
|
||||
|
||||
// Picking the pearl
|
||||
if (_pearl.picked && (_oko->getState() == Oko::kStatePick) && (_oko->getFrame() == 8)) {
|
||||
// Remove the pearl
|
||||
_pearl.pearl->setVisible(false);
|
||||
_pearl.pearl->setPause(true);
|
||||
|
||||
// Add the pearl to our found pearls repository
|
||||
if (_pearl.black)
|
||||
foundBlackPearl();
|
||||
else
|
||||
foundWhitePearl();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the pearl
|
||||
int16 x, y, width, height;
|
||||
_pearl.pearl->getPosition(x, y);
|
||||
_pearl.pearl->setPosition(x - 5, y);
|
||||
|
||||
// Check if the pearl has left the screen
|
||||
_pearl.pearl->getFramePosition(x, y);
|
||||
_pearl.pearl->getFrameSize(width, height);
|
||||
|
||||
if ((x + width) <= 0) {
|
||||
_pearl.pearl->setVisible(false);
|
||||
_pearl.pearl->setPause(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Diving::getPearl() {
|
||||
if (!_pearl.pearl->isVisible())
|
||||
return;
|
||||
|
||||
// Make sure the pearl is within Oko's grasp
|
||||
|
||||
int16 x, y, width, height;
|
||||
_pearl.pearl->getFramePosition(x, y);
|
||||
_pearl.pearl->getFrameSize(width, height);
|
||||
|
||||
if ((x > 190) || ((x + width) < 140))
|
||||
return;
|
||||
|
||||
_pearl.picked = true;
|
||||
}
|
||||
|
||||
void Diving::foundBlackPearl() {
|
||||
_blackPearlCount++;
|
||||
|
||||
// Put the black pearl drawing into the black pearl box
|
||||
if (_blackPearlCount == 1) {
|
||||
_vm->_draw->_backSurface->blit(*_blackPearl, 0, 0, 10, 7, 147, 179, 0);
|
||||
_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 179, 157, 186);
|
||||
} else if (_blackPearlCount == 2) {
|
||||
_vm->_draw->_backSurface->blit(*_blackPearl, 0, 0, 10, 7, 160, 179, 0);
|
||||
_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 179, 160, 186);
|
||||
}
|
||||
|
||||
_vm->_sound->blasterPlay(&_soundBlackPearl, 1, 0);
|
||||
}
|
||||
|
||||
void Diving::foundWhitePearl() {
|
||||
_whitePearlCount++;
|
||||
|
||||
// Put the white pearl drawing into the white pearl box
|
||||
int16 x = 54 + (_whitePearlCount - 1) * 8;
|
||||
if (_whitePearlCount > 10)
|
||||
x += 48;
|
||||
|
||||
_background->drawLayer(*_vm->_draw->_backSurface, 0, 2, x, 177, 0);
|
||||
_vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, 177, x + 3, 180);
|
||||
|
||||
_vm->_sound->blasterPlay(&_soundWhitePearl, 1, 0);
|
||||
}
|
||||
|
||||
void Diving::updateAnims() {
|
||||
int16 left, top, right, bottom;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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
|
||||
_airMeter->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);
|
||||
}
|
||||
|
||||
int16 Diving::checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) {
|
||||
_vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons);
|
||||
|
||||
return _vm->_util->checkKey();
|
||||
}
|
||||
|
||||
void Diving::shoot(int16 mouseX, int16 mouseY) {
|
||||
// Outside the playable area?
|
||||
if (mouseY > 157)
|
||||
return;
|
||||
|
||||
// Too many shots still active?
|
||||
if (_activeShots.size() >= kMaxShotCount)
|
||||
return;
|
||||
|
||||
ANIObject &shot = *_shot[_currentShot];
|
||||
|
||||
shot.rewind();
|
||||
shot.setVisible(true);
|
||||
shot.setPause(false);
|
||||
shot.setPosition(mouseX - 8, mouseY - 8);
|
||||
|
||||
_activeShots.push_back(_currentShot);
|
||||
|
||||
_currentShot = (_currentShot + 1) % kMaxShotCount;
|
||||
|
||||
_vm->_sound->blasterPlay(&_soundShoot, 1, 0);
|
||||
}
|
||||
|
||||
void Diving::checkShots() {
|
||||
Common::List<int>::iterator activeShot = _activeShots.begin();
|
||||
|
||||
// Check if we hit something with our shots
|
||||
while (activeShot != _activeShots.end()) {
|
||||
ANIObject &shot = *_shot[*activeShot];
|
||||
|
||||
if (shot.lastFrame()) {
|
||||
int16 x, y;
|
||||
|
||||
shot.getPosition(x, y);
|
||||
|
||||
// When we hit an evil fish, it dies
|
||||
for (uint i = 0; i < kEvilFishCount; i++) {
|
||||
EvilFish &evilFish = *_evilFish[i].evilFish;
|
||||
|
||||
if (evilFish.isIn(x + 8, y + 8)) {
|
||||
evilFish.die();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
activeShot = _activeShots.erase(activeShot);
|
||||
} else
|
||||
++activeShot;
|
||||
}
|
||||
}
|
||||
|
||||
void Diving::handleOko(int16 key) {
|
||||
if (key == kKeyDown) {
|
||||
// Oko sinks down a level or picks up a pearl if already at the bottom
|
||||
_oko->sink();
|
||||
|
||||
if ((_oko->getState() == Oko::kStatePick) && (_oko->getFrame() == 0))
|
||||
getPearl();
|
||||
|
||||
} else if (key == kKeyUp)
|
||||
// Oko raises up a level or surfaces to breathe if already at the top
|
||||
_oko->raise();
|
||||
}
|
||||
|
||||
void Diving::checkOkoHurt() {
|
||||
if (_oko->getState() != Oko::kStateSwim)
|
||||
return;
|
||||
|
||||
// Give Oko a grace period after being hurt
|
||||
if (_hurtGracePeriod > 0) {
|
||||
_hurtGracePeriod--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for a fish/Oko-collision
|
||||
for (uint i = 0; i < kEvilFishCount; i++) {
|
||||
EvilFish &evilFish = *_evilFish[i].evilFish;
|
||||
|
||||
if (!evilFish.isDead() && evilFish.isIn(*_oko)) {
|
||||
_healthMeter->decrease();
|
||||
|
||||
// If the health reached 0, Oko dies. Otherwise, she gets hurt
|
||||
if (_healthMeter->getValue() == 0)
|
||||
_oko->die();
|
||||
else
|
||||
_oko->hurt();
|
||||
|
||||
_hurtGracePeriod = 10;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
198
engines/gob/minigames/geisha/diving.h
Normal file
198
engines/gob/minigames/geisha/diving.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GOB_MINIGAMES_GEISHA_DIVING_H
|
||||
#define GOB_MINIGAMES_GEISHA_DIVING_H
|
||||
|
||||
#include "common/system.h"
|
||||
|
||||
#include "gob/util.h"
|
||||
|
||||
#include "gob/sound/sounddesc.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
class GobEngine;
|
||||
class Surface;
|
||||
class DECFile;
|
||||
class ANIFile;
|
||||
class ANIObject;
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
class EvilFish;
|
||||
class Oko;
|
||||
class Meter;
|
||||
|
||||
/** Geisha's "Diving" minigame. */
|
||||
class Diving {
|
||||
public:
|
||||
Diving(GobEngine *vm);
|
||||
~Diving();
|
||||
|
||||
bool play(uint16 playerCount, bool hasPearlLocation);
|
||||
|
||||
bool isPlaying() const;
|
||||
void cheatWin();
|
||||
|
||||
private:
|
||||
static const uint kEvilFishCount = 3;
|
||||
static const uint kDecorFishCount = 3;
|
||||
static const uint kMaxShotCount = 10;
|
||||
|
||||
static const uint kEvilFishTypeCount = 3;
|
||||
static const uint16 kEvilFishTypes[kEvilFishTypeCount][5];
|
||||
|
||||
struct PlantLevel {
|
||||
int16 y;
|
||||
uint plantCount;
|
||||
const uint16 *plants;
|
||||
};
|
||||
|
||||
static const uint kPlantLevelCount = 3;
|
||||
static const uint kPlantPerLevelCount = 5;
|
||||
|
||||
static const uint16 kPlantLevel1[];
|
||||
static const uint16 kPlantLevel2[];
|
||||
static const uint16 kPlantLevel3[];
|
||||
|
||||
static const PlantLevel kPlantLevels[kPlantLevelCount];
|
||||
|
||||
static const uint kPlantCount = kPlantLevelCount * kPlantPerLevelCount;
|
||||
|
||||
struct ManagedEvilFish {
|
||||
EvilFish *evilFish;
|
||||
|
||||
uint32 enterAt;
|
||||
uint32 leaveAt;
|
||||
};
|
||||
|
||||
struct ManagedDecorFish {
|
||||
ANIObject *decorFish;
|
||||
|
||||
uint32 enterAt;
|
||||
int8 deltaX;
|
||||
};
|
||||
|
||||
struct ManagedPlant {
|
||||
ANIObject *plant;
|
||||
|
||||
uint level;
|
||||
int8 deltaX;
|
||||
int16 x, y;
|
||||
};
|
||||
|
||||
struct ManagedPearl {
|
||||
ANIObject *pearl;
|
||||
|
||||
bool picked;
|
||||
bool black;
|
||||
};
|
||||
|
||||
GobEngine *_vm;
|
||||
|
||||
DECFile *_background;
|
||||
ANIFile *_objects;
|
||||
ANIFile *_gui;
|
||||
ANIFile *_okoAnim;
|
||||
|
||||
ANIObject *_water;
|
||||
ANIObject *_lungs;
|
||||
ANIObject *_heart;
|
||||
|
||||
ManagedEvilFish _evilFish[kEvilFishCount];
|
||||
ManagedDecorFish _decorFish[kDecorFishCount];
|
||||
ManagedPlant _plant[kPlantCount];
|
||||
ManagedPearl _pearl;
|
||||
|
||||
Oko *_oko;
|
||||
|
||||
ANIObject *_shot[kMaxShotCount];
|
||||
|
||||
Common::List<int> _activeShots;
|
||||
|
||||
Common::List<ANIObject *> _anims;
|
||||
|
||||
Surface *_blackPearl;
|
||||
|
||||
uint8 _whitePearlCount;
|
||||
uint8 _blackPearlCount;
|
||||
|
||||
Meter *_airMeter;
|
||||
Meter *_healthMeter;
|
||||
|
||||
uint8 _airCycle;
|
||||
uint8 _hurtGracePeriod;
|
||||
|
||||
uint8 _currentShot;
|
||||
|
||||
SoundDesc _soundShoot;
|
||||
SoundDesc _soundBreathe;
|
||||
SoundDesc _soundWhitePearl;
|
||||
SoundDesc _soundBlackPearl;
|
||||
|
||||
bool _hasPearlLocation;
|
||||
bool _isPlaying;
|
||||
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
void initScreen();
|
||||
void initCursor();
|
||||
void initPlants();
|
||||
|
||||
void enterPlant(ManagedPlant &plant, int16 prevPlantX);
|
||||
void enterPearl(int16 x);
|
||||
|
||||
void getPearl();
|
||||
|
||||
void foundBlackPearl();
|
||||
void foundWhitePearl();
|
||||
|
||||
void updateAirMeter();
|
||||
void updateEvilFish();
|
||||
void updateDecorFish();
|
||||
void updatePlants();
|
||||
void updatePearl();
|
||||
void updateAnims();
|
||||
|
||||
int16 checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons);
|
||||
|
||||
void shoot(int16 mouseX, int16 mouseY);
|
||||
void checkShots();
|
||||
|
||||
void handleOko(int16 key);
|
||||
|
||||
void checkOkoHurt();
|
||||
};
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
|
||||
#endif // GOB_MINIGAMES_GEISHA_DIVING_H
|
||||
186
engines/gob/minigames/geisha/evilfish.cpp
Normal file
186
engines/gob/minigames/geisha/evilfish.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gob/minigames/geisha/evilfish.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
EvilFish::EvilFish(const ANIFile &ani, uint16 screenWidth,
|
||||
uint16 animSwimLeft, uint16 animSwimRight,
|
||||
uint16 animTurnLeft, uint16 animTurnRight, uint16 animDie) :
|
||||
ANIObject(ani), _screenWidth(screenWidth),
|
||||
_animSwimLeft(animSwimLeft), _animSwimRight(animSwimRight),
|
||||
_animTurnLeft(animTurnLeft), _animTurnRight(animTurnRight), _animDie(animDie),
|
||||
_shouldLeave(false), _state(kStateNone) {
|
||||
|
||||
}
|
||||
|
||||
EvilFish::~EvilFish() {
|
||||
}
|
||||
|
||||
void EvilFish::enter(Direction from, int16 y) {
|
||||
_shouldLeave = false;
|
||||
|
||||
bool left = from == kDirectionLeft;
|
||||
|
||||
setAnimation(left ? _animSwimLeft : _animSwimRight);
|
||||
|
||||
int16 width, height;
|
||||
getFrameSize(width, height);
|
||||
|
||||
setPosition(left ? -width : _screenWidth, y);
|
||||
setVisible(true);
|
||||
|
||||
_state = left ? kStateSwimLeft : kStateSwimRight;
|
||||
}
|
||||
|
||||
void EvilFish::leave() {
|
||||
if (_state == kStateNone)
|
||||
return;
|
||||
|
||||
_shouldLeave = true;
|
||||
}
|
||||
|
||||
void EvilFish::die() {
|
||||
if ((_state == kStateNone) || (_state == kStateDie))
|
||||
return;
|
||||
|
||||
int16 x, y;
|
||||
getFramePosition(x, y);
|
||||
|
||||
setAnimation(_animDie);
|
||||
setPosition(x, y);
|
||||
|
||||
_state = kStateDie;
|
||||
}
|
||||
|
||||
void EvilFish::advance() {
|
||||
if (_state == kStateNone)
|
||||
return;
|
||||
|
||||
bool wasLastFrame = lastFrame();
|
||||
|
||||
int16 oldX, oldY;
|
||||
getPosition(oldX, oldY);
|
||||
|
||||
ANIObject::advance();
|
||||
|
||||
int16 x, y, width, height;
|
||||
getFramePosition(x, y);
|
||||
getFrameSize(width, height);
|
||||
|
||||
switch (_state) {
|
||||
case kStateNone:
|
||||
default:
|
||||
break;
|
||||
|
||||
case kStateSwimLeft:
|
||||
if (!_shouldLeave && (x >= _screenWidth - width)) {
|
||||
setAnimation(_animTurnRight);
|
||||
setPosition(x, oldY);
|
||||
_state = kStateTurnRight;
|
||||
}
|
||||
|
||||
if (_shouldLeave && (x >= _screenWidth)) {
|
||||
setVisible(false);
|
||||
|
||||
_shouldLeave = false;
|
||||
_state = kStateNone;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStateSwimRight:
|
||||
if (!_shouldLeave && (x <= 0)) {
|
||||
setAnimation(_animTurnLeft);
|
||||
setPosition(x, oldY);
|
||||
_state = kStateTurnLeft;
|
||||
}
|
||||
|
||||
if (_shouldLeave && (x < -width)) {
|
||||
setVisible(false);
|
||||
|
||||
_shouldLeave = false;
|
||||
_state = kStateNone;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStateTurnLeft:
|
||||
if (wasLastFrame) {
|
||||
setAnimation(_animSwimLeft);
|
||||
_state = kStateSwimLeft;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStateTurnRight:
|
||||
if (wasLastFrame) {
|
||||
setAnimation(_animSwimRight);
|
||||
_state = kStateSwimRight;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStateDie:
|
||||
if (wasLastFrame) {
|
||||
setVisible(false);
|
||||
|
||||
_state = kStateNone;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EvilFish::mutate(uint16 animSwimLeft, uint16 animSwimRight,
|
||||
uint16 animTurnLeft, uint16 animTurnRight, uint16 animDie) {
|
||||
|
||||
_animSwimLeft = animSwimLeft;
|
||||
_animSwimRight = animSwimRight;
|
||||
_animTurnLeft = animTurnLeft;
|
||||
_animTurnRight = animTurnRight;
|
||||
_animDie = animDie;
|
||||
|
||||
switch (_state) {
|
||||
case kStateSwimLeft:
|
||||
setAnimation(_animSwimLeft);
|
||||
break;
|
||||
|
||||
case kStateSwimRight:
|
||||
setAnimation(_animSwimRight);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool EvilFish::isDead() const {
|
||||
return !isVisible() || (_state == kStateNone) || (_state == kStateDie);
|
||||
}
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
95
engines/gob/minigames/geisha/evilfish.h
Normal file
95
engines/gob/minigames/geisha/evilfish.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GOB_MINIGAMES_GEISHA_EVILFISH_H
|
||||
#define GOB_MINIGAMES_GEISHA_EVILFISH_H
|
||||
|
||||
#include "gob/aniobject.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
/** An "evil" fish in Geisha's "Diving" minigame. */
|
||||
class EvilFish : public ANIObject {
|
||||
public:
|
||||
enum Direction {
|
||||
kDirectionLeft = 0,
|
||||
kDirectionRight = 1
|
||||
};
|
||||
|
||||
EvilFish(const ANIFile &ani, uint16 screenWidth,
|
||||
uint16 animSwimLeft, uint16 animSwimRight,
|
||||
uint16 animTurnLeft, uint16 animTurnRight, uint16 animDie);
|
||||
~EvilFish() override;
|
||||
|
||||
/** Enter from this direction / screen edge. */
|
||||
void enter(Direction from, int16 y);
|
||||
/** Leave the screen in the current direction. */
|
||||
void leave();
|
||||
|
||||
/** Kill the fish. */
|
||||
void die();
|
||||
|
||||
/** Advance the animation to the next frame. */
|
||||
void advance() override;
|
||||
|
||||
/** Change the fish's animations, effectively making it a different fish type. */
|
||||
void mutate(uint16 animSwimLeft, uint16 animSwimRight,
|
||||
uint16 animTurnLeft, uint16 animTurnRight, uint16 animDie);
|
||||
|
||||
/** Is the fish dead? */
|
||||
bool isDead() const;
|
||||
|
||||
private:
|
||||
enum State {
|
||||
kStateNone,
|
||||
kStateSwimLeft,
|
||||
kStateSwimRight,
|
||||
kStateTurnLeft,
|
||||
kStateTurnRight,
|
||||
kStateDie
|
||||
};
|
||||
|
||||
uint16 _screenWidth;
|
||||
|
||||
uint16 _animSwimLeft;
|
||||
uint16 _animSwimRight;
|
||||
uint16 _animTurnLeft;
|
||||
uint16 _animTurnRight;
|
||||
uint16 _animDie;
|
||||
|
||||
bool _shouldLeave;
|
||||
|
||||
State _state;
|
||||
};
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
|
||||
#endif // GOB_MINIGAMES_GEISHA_EVILFISH_H
|
||||
139
engines/gob/minigames/geisha/meter.cpp
Normal file
139
engines/gob/minigames/geisha/meter.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/util.h"
|
||||
|
||||
#include "gob/surface.h"
|
||||
|
||||
#include "gob/minigames/geisha/meter.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
Meter::Meter(int16 x, int16 y, int16 width, int16 height, uint8 frontColor,
|
||||
uint8 backColor, int32 maxValue, Direction direction) :
|
||||
_x(x), _y(y), _width(width), _height(height), _frontColor(frontColor),
|
||||
_backColor(backColor), _value(0), _maxValue(maxValue), _direction(direction),
|
||||
_needUpdate(true), _surface(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
Meter::~Meter() {
|
||||
delete _surface;
|
||||
}
|
||||
|
||||
int32 Meter::getMaxValue() const {
|
||||
return _maxValue;
|
||||
}
|
||||
|
||||
int32 Meter::getValue() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
void Meter::setValue(int32 value) {
|
||||
value = CLIP<int32>(value, 0, _maxValue);
|
||||
if (_value == value)
|
||||
return;
|
||||
|
||||
_value = value;
|
||||
_needUpdate = true;
|
||||
}
|
||||
|
||||
void Meter::setMaxValue() {
|
||||
setValue(_maxValue);
|
||||
}
|
||||
|
||||
int32 Meter::increase(int32 n) {
|
||||
if (n < 0)
|
||||
return decrease(-n);
|
||||
|
||||
int32 overflow = MAX<int32>(0, (_value + n) - _maxValue);
|
||||
|
||||
int32 value = CLIP<int32>(_value + n, 0, _maxValue);
|
||||
if (_value == value)
|
||||
return overflow;
|
||||
|
||||
_value = value;
|
||||
_needUpdate = true;
|
||||
|
||||
return overflow;
|
||||
}
|
||||
|
||||
int32 Meter::decrease(int32 n) {
|
||||
if (n < 0)
|
||||
return increase(-n);
|
||||
|
||||
int32 underflow = -MIN<int32>(0, _value - n);
|
||||
|
||||
int32 value = CLIP<int32>(_value - n, 0, _maxValue);
|
||||
if (_value == value)
|
||||
return underflow;
|
||||
|
||||
_value = value;
|
||||
_needUpdate = true;
|
||||
|
||||
return underflow;
|
||||
}
|
||||
|
||||
void Meter::draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) {
|
||||
if (!_surface) {
|
||||
_surface = new Surface(_width, _height, dest.getBPP());
|
||||
_needUpdate = true;
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
left = CLIP<int16>(_x , 0, dest.getWidth () - 1);
|
||||
top = CLIP<int16>(_y , 0, dest.getHeight() - 1);
|
||||
right = CLIP<int16>(_x + _width - 1, 0, dest.getWidth () - 1);
|
||||
bottom = CLIP<int16>(_y + _height - 1, 0, dest.getHeight() - 1);
|
||||
|
||||
dest.blit(*_surface, left - _x, top - _y, _width, _height, left, top);
|
||||
}
|
||||
|
||||
void Meter::update() {
|
||||
if (!_needUpdate)
|
||||
return;
|
||||
|
||||
_needUpdate = false;
|
||||
|
||||
_surface->fill(_backColor);
|
||||
|
||||
int32 n = (int32)floor((((float) _width) / _maxValue * _value) + 0.5);
|
||||
if (n <= 0)
|
||||
return;
|
||||
|
||||
if (_direction == kFillToLeft)
|
||||
_surface->fillRect(_width - n, 0, _width - 1, _height - 1, _frontColor);
|
||||
else
|
||||
_surface->fillRect(0 , 0, n - 1, _height - 1, _frontColor);
|
||||
}
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
96
engines/gob/minigames/geisha/meter.h
Normal file
96
engines/gob/minigames/geisha/meter.h
Normal file
@@ -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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GOB_MINIGAMES_GEISHA_METER_H
|
||||
#define GOB_MINIGAMES_GEISHA_METER_H
|
||||
|
||||
#include "gob/aniobject.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
class Surface;
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
/** A meter measuring a value. */
|
||||
class Meter {
|
||||
public:
|
||||
enum Direction {
|
||||
kFillToLeft,
|
||||
kFillToRight
|
||||
};
|
||||
|
||||
Meter(int16 x, int16 y, int16 width, int16 height,
|
||||
uint8 frontColor, uint8 backColor, int32 maxValue,
|
||||
Direction direction);
|
||||
~Meter();
|
||||
|
||||
/** Return the max value the meter is measuring. */
|
||||
int32 getMaxValue() const;
|
||||
/** Return the current value the meter is measuring. */
|
||||
int32 getValue() const;
|
||||
|
||||
/** Set the current value the meter is measuring. */
|
||||
void setValue(int32 value);
|
||||
|
||||
/** Set the current value the meter is measuring to the max value. */
|
||||
void setMaxValue();
|
||||
|
||||
/** Increase the current value the meter is measuring, returning the overflow. */
|
||||
int32 increase(int32 n = 1);
|
||||
/** Decrease the current value the meter is measuring, returning the underflow. */
|
||||
int32 decrease(int32 n = 1);
|
||||
|
||||
/** Draw the meter onto the surface and return the affected rectangle. */
|
||||
void draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom);
|
||||
|
||||
private:
|
||||
int16 _x;
|
||||
int16 _y;
|
||||
int16 _width;
|
||||
int16 _height;
|
||||
|
||||
uint8 _frontColor;
|
||||
uint8 _backColor;
|
||||
|
||||
int32 _value;
|
||||
int32 _maxValue;
|
||||
|
||||
Direction _direction;
|
||||
|
||||
bool _needUpdate;
|
||||
|
||||
Surface *_surface;
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
|
||||
#endif // GOB_MINIGAMES_GEISHA_METER_H
|
||||
174
engines/gob/minigames/geisha/mouth.cpp
Normal file
174
engines/gob/minigames/geisha/mouth.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/util.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
|
||||
80
engines/gob/minigames/geisha/mouth.h
Normal file
80
engines/gob/minigames/geisha/mouth.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#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() override;
|
||||
|
||||
/** Advance the animation to the next frame. */
|
||||
void advance() override;
|
||||
|
||||
/** Active the mouth's animation. */
|
||||
void activate();
|
||||
|
||||
/** Is the mouth deactivated? */
|
||||
bool isDeactivated() const;
|
||||
|
||||
/** Set the current position. */
|
||||
void setPosition(int16 x, int16 y) override;
|
||||
|
||||
/** Draw the current frame onto the surface and return the affected rectangle. */
|
||||
bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) override;
|
||||
/** Draw the current frame from the surface and return the affected rectangle. */
|
||||
bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) override;
|
||||
|
||||
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
|
||||
176
engines/gob/minigames/geisha/oko.cpp
Normal file
176
engines/gob/minigames/geisha/oko.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gob/sound/sound.h"
|
||||
|
||||
#include "gob/minigames/geisha/oko.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
enum kOkoAnimation {
|
||||
kOkoAnimationEnter = 0,
|
||||
kOkoAnimationSwim = 1,
|
||||
kOkoAnimationSink = 8,
|
||||
kOkoAnimationRaise = 7,
|
||||
kOkoAnimationBreathe = 2,
|
||||
kOkoAnimationPick = 3,
|
||||
kOkoAnimationHurt = 4,
|
||||
kOkoAnimationDie0 = 17,
|
||||
kOkoAnimationDie1 = 18,
|
||||
kOkoAnimationDie2 = 19
|
||||
};
|
||||
|
||||
static const int16 kOkoPositionX = 110;
|
||||
|
||||
static const uint kLevelCount = 3;
|
||||
static const int16 kLevelPositionX[kLevelCount] = { 44, 84, 124 };
|
||||
|
||||
|
||||
Oko::Oko(const ANIFile &ani, Sound &sound, SoundDesc &breathe) :
|
||||
ANIObject(ani), _sound(&sound), _breathe(&breathe), _state(kStateEnter), _level(0) {
|
||||
|
||||
setAnimation(kOkoAnimationEnter);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
Oko::~Oko() {
|
||||
}
|
||||
|
||||
void Oko::advance() {
|
||||
bool wasLastFrame = lastFrame();
|
||||
|
||||
if ((_state == kStateDead) && wasLastFrame) {
|
||||
setPause(true);
|
||||
return;
|
||||
}
|
||||
|
||||
ANIObject::advance();
|
||||
|
||||
switch (_state) {
|
||||
case kStateEnter:
|
||||
if (wasLastFrame) {
|
||||
setAnimation(kOkoAnimationSwim);
|
||||
setPosition(kOkoPositionX, kLevelPositionX[_level]);
|
||||
_state = kStateSwim;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStateBreathe:
|
||||
if ((getFrame() == 6) || (getFrame() == 23))
|
||||
_sound->blasterPlay(_breathe, 1, 0);
|
||||
// fall through
|
||||
case kStateSink:
|
||||
case kStateRaise:
|
||||
case kStateHurt:
|
||||
if (wasLastFrame) {
|
||||
setAnimation(kOkoAnimationSwim);
|
||||
setPosition(kOkoPositionX, kLevelPositionX[_level]);
|
||||
_state = kStateSwim;
|
||||
}
|
||||
break;
|
||||
|
||||
case kStatePick:
|
||||
if (wasLastFrame) {
|
||||
_level = 1;
|
||||
setAnimation(kOkoAnimationSwim);
|
||||
setPosition(kOkoPositionX, kLevelPositionX[_level]);
|
||||
_state = kStateSwim;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Oko::sink() {
|
||||
if (_state != kStateSwim)
|
||||
return;
|
||||
|
||||
if (_level >= (kLevelCount - 1)) {
|
||||
setAnimation(kOkoAnimationPick);
|
||||
_state = kStatePick;
|
||||
return;
|
||||
}
|
||||
|
||||
setAnimation(kOkoAnimationSink);
|
||||
setPosition(kOkoPositionX, kLevelPositionX[_level]);
|
||||
_state = kStateSink;
|
||||
|
||||
_level++;
|
||||
}
|
||||
|
||||
void Oko::raise() {
|
||||
if (_state != kStateSwim)
|
||||
return;
|
||||
|
||||
if (_level == 0) {
|
||||
setAnimation(kOkoAnimationBreathe);
|
||||
_state = kStateBreathe;
|
||||
return;
|
||||
}
|
||||
|
||||
setAnimation(kOkoAnimationRaise);
|
||||
setPosition(kOkoPositionX, kLevelPositionX[_level]);
|
||||
_state = kStateSink;
|
||||
|
||||
_level--;
|
||||
}
|
||||
|
||||
void Oko::hurt() {
|
||||
if (_state != kStateSwim)
|
||||
return;
|
||||
|
||||
setAnimation(kOkoAnimationHurt);
|
||||
_state = kStateHurt;
|
||||
}
|
||||
|
||||
void Oko::die() {
|
||||
if (_state != kStateSwim)
|
||||
return;
|
||||
|
||||
setAnimation(kOkoAnimationDie0 + _level);
|
||||
_state = kStateDead;
|
||||
}
|
||||
|
||||
Oko::State Oko::getState() const {
|
||||
return _state;
|
||||
}
|
||||
|
||||
bool Oko::isBreathing() const {
|
||||
return (_state == kStateBreathe) && ((getFrame() >= 9) && (getFrame() <= 30));
|
||||
}
|
||||
|
||||
bool Oko::isMoving() const {
|
||||
return (_state != kStateBreathe) && (_state != kStateHurt) && (_state != kStateDead);
|
||||
}
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
89
engines/gob/minigames/geisha/oko.h
Normal file
89
engines/gob/minigames/geisha/oko.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GOB_MINIGAMES_GEISHA_OKO_H
|
||||
#define GOB_MINIGAMES_GEISHA_OKO_H
|
||||
|
||||
#include "gob/aniobject.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
class Sound;
|
||||
class SoundDesc;
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
/** Oko, the person you control, in Geisha's "Diving" minigame. */
|
||||
class Oko : public ANIObject {
|
||||
public:
|
||||
enum State {
|
||||
kStateEnter,
|
||||
kStateSwim,
|
||||
kStateSink,
|
||||
kStateRaise,
|
||||
kStateBreathe,
|
||||
kStatePick,
|
||||
kStateHurt,
|
||||
kStateDead
|
||||
};
|
||||
|
||||
Oko(const ANIFile &ani, Sound &sound, SoundDesc &breathe);
|
||||
~Oko() override;
|
||||
|
||||
/** Advance the animation to the next frame. */
|
||||
void advance() override;
|
||||
|
||||
/** Oko should sink a level. */
|
||||
void sink();
|
||||
/** Oko should raise a level. */
|
||||
void raise();
|
||||
|
||||
/** Oko should get hurt. */
|
||||
void hurt();
|
||||
|
||||
/** Oko should die. */
|
||||
void die();
|
||||
|
||||
State getState() const;
|
||||
|
||||
bool isBreathing() const;
|
||||
bool isMoving() const;
|
||||
|
||||
private:
|
||||
Sound *_sound;
|
||||
SoundDesc *_breathe;
|
||||
|
||||
State _state;
|
||||
|
||||
uint8 _level;
|
||||
};
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
|
||||
#endif // GOB_MINIGAMES_GEISHA_OKO_H
|
||||
1471
engines/gob/minigames/geisha/penetration.cpp
Normal file
1471
engines/gob/minigames/geisha/penetration.cpp
Normal file
File diff suppressed because it is too large
Load Diff
261
engines/gob/minigames/geisha/penetration.h
Normal file
261
engines/gob/minigames/geisha/penetration.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GOB_MINIGAMES_GEISHA_PENETRATION_H
|
||||
#define GOB_MINIGAMES_GEISHA_PENETRATION_H
|
||||
|
||||
#include "common/system.h"
|
||||
#include "common/list.h"
|
||||
|
||||
#include "gob/sound/sounddesc.h"
|
||||
|
||||
#include "gob/minigames/geisha/submarine.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
class GobEngine;
|
||||
class Surface;
|
||||
class CMPFile;
|
||||
class ANIFile;
|
||||
|
||||
namespace Geisha {
|
||||
|
||||
class Meter;
|
||||
class Mouth;
|
||||
|
||||
/** Geisha's "Penetration" minigame. */
|
||||
class Penetration {
|
||||
public:
|
||||
Penetration(GobEngine *vm);
|
||||
~Penetration();
|
||||
|
||||
bool play(bool hasAccessPass, bool hasMaxEnergy, bool testMode);
|
||||
|
||||
bool isPlaying() const;
|
||||
void cheatWin();
|
||||
|
||||
private:
|
||||
static const int kModeCount = 2;
|
||||
static const int kFloorCount = 3;
|
||||
|
||||
static const int kMapWidth = 17;
|
||||
static const int kMapHeight = 13;
|
||||
|
||||
static const int kPaletteSize = 16;
|
||||
|
||||
static const byte kPalettes[kFloorCount][3 * kPaletteSize];
|
||||
static const byte kMaps[kModeCount][kFloorCount][kMapWidth * kMapHeight];
|
||||
|
||||
static const int kEnemyCount = 9;
|
||||
static const int kMaxBulletCount = 10;
|
||||
|
||||
struct MapObject {
|
||||
uint16 tileX;
|
||||
uint16 tileY;
|
||||
|
||||
uint16 mapX;
|
||||
uint16 mapY;
|
||||
|
||||
uint16 width;
|
||||
uint16 height;
|
||||
|
||||
bool isBlocking;
|
||||
|
||||
MapObject(uint16 tX, uint16 tY, uint16 mX, uint16 mY, uint16 w, uint16 h);
|
||||
MapObject(uint16 tX, uint16 tY, uint16 w, uint16 h);
|
||||
|
||||
void setTileFromMapPosition();
|
||||
void setMapFromTilePosition();
|
||||
|
||||
bool isIn(uint16 mX, uint16 mY) const;
|
||||
bool isIn(uint16 mX, uint16 mY, uint16 w, uint16 h) const;
|
||||
bool isIn(const MapObject &obj) const;
|
||||
};
|
||||
|
||||
enum MouthType {
|
||||
kMouthTypeBite,
|
||||
kMouthTypeKiss
|
||||
};
|
||||
|
||||
struct ManagedMouth : public MapObject {
|
||||
Mouth *mouth;
|
||||
|
||||
MouthType type;
|
||||
|
||||
ManagedMouth(uint16 tX, uint16 tY, MouthType t);
|
||||
~ManagedMouth();
|
||||
};
|
||||
|
||||
struct ManagedSub : public MapObject {
|
||||
Submarine *sub;
|
||||
|
||||
ManagedSub(uint16 tX, uint16 tY);
|
||||
~ManagedSub();
|
||||
};
|
||||
|
||||
struct ManagedEnemy : public MapObject {
|
||||
ANIObject *enemy;
|
||||
|
||||
bool dead;
|
||||
|
||||
ManagedEnemy();
|
||||
~ManagedEnemy();
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct ManagedBullet : public MapObject {
|
||||
ANIObject *bullet;
|
||||
|
||||
int16 deltaX;
|
||||
int16 deltaY;
|
||||
|
||||
ManagedBullet();
|
||||
~ManagedBullet();
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
enum Keys {
|
||||
kKeyUp = 0,
|
||||
kKeyDown,
|
||||
kKeyLeft,
|
||||
kKeyRight,
|
||||
kKeySpace,
|
||||
kKeyCount
|
||||
};
|
||||
|
||||
GobEngine *_vm;
|
||||
|
||||
bool _hasAccessPass;
|
||||
bool _hasMaxEnergy;
|
||||
bool _testMode;
|
||||
|
||||
bool _needFadeIn;
|
||||
|
||||
bool _quit;
|
||||
bool _keys[kKeyCount];
|
||||
|
||||
Surface *_background;
|
||||
CMPFile *_sprites;
|
||||
ANIFile *_objects;
|
||||
|
||||
Common::List<ANIObject *> _anims;
|
||||
Common::List<ANIObject *> _mapAnims;
|
||||
|
||||
Meter *_shieldMeter;
|
||||
Meter *_healthMeter;
|
||||
|
||||
uint8 _floor;
|
||||
|
||||
Surface *_map;
|
||||
|
||||
ManagedSub *_sub;
|
||||
|
||||
Common::List<MapObject> _walls;
|
||||
Common::List<MapObject> _exits;
|
||||
Common::List<MapObject> _shields;
|
||||
Common::List<ManagedMouth> _mouths;
|
||||
|
||||
ManagedEnemy _enemies[kEnemyCount];
|
||||
ManagedBullet _bullets[kMaxBulletCount];
|
||||
|
||||
Common::List<MapObject *> _blockingObjects;
|
||||
|
||||
uint8 _shotCoolDown;
|
||||
|
||||
SoundDesc _soundShield;
|
||||
SoundDesc _soundBite;
|
||||
SoundDesc _soundKiss;
|
||||
SoundDesc _soundShoot;
|
||||
SoundDesc _soundExit;
|
||||
SoundDesc _soundExplode;
|
||||
|
||||
bool _isPlaying;
|
||||
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
|
||||
void clearMap();
|
||||
void createMap();
|
||||
|
||||
void initScreen();
|
||||
|
||||
void setPalette();
|
||||
void fadeIn();
|
||||
|
||||
void drawFloorText();
|
||||
void drawEndText();
|
||||
|
||||
bool isBlocked(const MapObject &self, int16 x, int16 y, MapObject **blockedBy = 0);
|
||||
void findPath(MapObject &obj, int x, int y, MapObject **blockedBy = 0);
|
||||
|
||||
void updateAnims();
|
||||
|
||||
void checkInput();
|
||||
|
||||
Submarine::Direction getDirection(int &x, int &y) const;
|
||||
|
||||
void handleSub();
|
||||
void subMove(int x, int y, Submarine::Direction direction);
|
||||
void subShoot();
|
||||
|
||||
int findEmptyBulletSlot() const;
|
||||
uint16 directionToBullet(Submarine::Direction direction) const;
|
||||
void setBulletPosition(const ManagedSub &sub, ManagedBullet &bullet) const;
|
||||
|
||||
void bulletsMove();
|
||||
void bulletMove(ManagedBullet &bullet);
|
||||
void checkShotEnemy(MapObject &shotObject);
|
||||
|
||||
void checkExits();
|
||||
void checkShields();
|
||||
void checkMouths();
|
||||
|
||||
void healthGain(int amount);
|
||||
void healthLose(int amount);
|
||||
|
||||
void checkExited();
|
||||
|
||||
void enemiesCreate();
|
||||
void enemiesMove();
|
||||
void enemyMove(ManagedEnemy &enemy, int x, int y);
|
||||
void enemyAttack(ManagedEnemy &enemy);
|
||||
void enemyExplode(ManagedEnemy &enemy);
|
||||
|
||||
bool isDead() const;
|
||||
bool hasWon() const;
|
||||
|
||||
int getLanguage() const;
|
||||
};
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
|
||||
#endif // GOB_MINIGAMES_GEISHA_PENETRATION_H
|
||||
261
engines/gob/minigames/geisha/submarine.cpp
Normal file
261
engines/gob/minigames/geisha/submarine.cpp
Normal file
@@ -0,0 +1,261 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#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(kStateMove), _direction(kDirectionNone) {
|
||||
turn(kDirectionN);
|
||||
}
|
||||
|
||||
Submarine::~Submarine() {
|
||||
}
|
||||
|
||||
Submarine::Direction Submarine::getDirection() const {
|
||||
return _direction;
|
||||
}
|
||||
|
||||
void Submarine::turn(Direction to) {
|
||||
// Nothing to do
|
||||
if ((to == kDirectionNone) || ((_state == kStateMove) && (_direction == to)))
|
||||
return;
|
||||
|
||||
_direction = to;
|
||||
|
||||
move();
|
||||
}
|
||||
|
||||
void Submarine::move() {
|
||||
uint16 frame = getFrame();
|
||||
uint16 anim = (_state == kStateShoot) ? directionToShoot(_direction) : directionToMove(_direction);
|
||||
|
||||
setAnimation(anim);
|
||||
setFrame(frame);
|
||||
setPause(false);
|
||||
setVisible(true);
|
||||
|
||||
setMode((_state == kStateShoot) ? kModeOnce : kModeContinuous);
|
||||
}
|
||||
|
||||
void Submarine::shoot() {
|
||||
_state = kStateShoot;
|
||||
|
||||
setAnimation(directionToShoot(_direction));
|
||||
setMode(kModeOnce);
|
||||
setPause(false);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
void Submarine::die() {
|
||||
if (!canMove())
|
||||
return;
|
||||
|
||||
_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()) {
|
||||
_state = kStateMove;
|
||||
|
||||
move();
|
||||
}
|
||||
break;
|
||||
|
||||
case kStateExit:
|
||||
if (isPaused())
|
||||
_state = kStateExited;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool Submarine::isShooting() const {
|
||||
return _state == kStateShoot;
|
||||
}
|
||||
|
||||
bool Submarine::hasExited() const {
|
||||
return _state == kStateExited;
|
||||
}
|
||||
|
||||
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
|
||||
112
engines/gob/minigames/geisha/submarine.h
Normal file
112
engines/gob/minigames/geisha/submarine.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 {
|
||||
kDirectionNone,
|
||||
kDirectionN,
|
||||
kDirectionNE,
|
||||
kDirectionE,
|
||||
kDirectionSE,
|
||||
kDirectionS,
|
||||
kDirectionSW,
|
||||
kDirectionW,
|
||||
kDirectionNW
|
||||
};
|
||||
|
||||
Submarine(const ANIFile &ani);
|
||||
~Submarine() override;
|
||||
|
||||
Direction getDirection() const;
|
||||
|
||||
/** 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() override;
|
||||
|
||||
/** Can the submarine move at the moment? */
|
||||
bool canMove() const;
|
||||
|
||||
/** Is the submarine dead? */
|
||||
bool isDead() const;
|
||||
|
||||
/** Is the submarine shooting? */
|
||||
bool isShooting() const;
|
||||
|
||||
/** Has the submarine finished exiting the level? */
|
||||
bool hasExited() 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;
|
||||
|
||||
void move();
|
||||
};
|
||||
|
||||
} // End of namespace Geisha
|
||||
|
||||
} // End of namespace Gob
|
||||
|
||||
#endif // GOB_MINIGAMES_GEISHA_SUBMARINE_H
|
||||
Reference in New Issue
Block a user