mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
Draw::_vgaSmallPalette was supposed to be a 16-color VGA palette / EGA palette, but we've been using the first 16 entries of Draw::_vgaPalette for that for ages now. The only parts where we didn't were codepaths never actually taken in the gob games, so for the sake of clarity (and fixing Coverity issues), this commit removes those as well. Fixes Coverity issues 1192648 and 1192649.
832 lines
20 KiB
C++
832 lines
20 KiB
C++
/* 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/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(0),
|
|
_objects(0), _gui(0), _okoAnim(0), _water(0), _lungs(0), _heart(0),
|
|
_blackPearl(0), _airMeter(0), _healthMeter(0), _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 = 0;
|
|
|
|
for (uint i = 0; i < kDecorFishCount; i++)
|
|
_decorFish[i].decorFish = 0;
|
|
|
|
for (uint i = 0; i < kPlantCount; i++)
|
|
_plant[i].plant = 0;
|
|
|
|
for (uint i = 0; i < kMaxShotCount; i++)
|
|
_shot[i] = 0;
|
|
|
|
_pearl.pearl = 0;
|
|
|
|
_oko = 0;
|
|
}
|
|
|
|
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(0, 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] = 0;
|
|
}
|
|
|
|
for (uint i = 0; i < kEvilFishCount; i++) {
|
|
delete _evilFish[i].evilFish;
|
|
|
|
_evilFish[i].evilFish = 0;
|
|
}
|
|
|
|
for (uint i = 0; i < kDecorFishCount; i++) {
|
|
delete _decorFish[i].decorFish;
|
|
|
|
_decorFish[i].decorFish = 0;
|
|
}
|
|
|
|
for (uint i = 0; i < kPlantCount; i++) {
|
|
delete _plant[i].plant;
|
|
|
|
_plant[i].plant = 0;
|
|
}
|
|
|
|
delete _pearl.pearl;
|
|
_pearl.pearl = 0;
|
|
|
|
delete _oko;
|
|
_oko = 0;
|
|
|
|
delete _heart;
|
|
delete _lungs;
|
|
delete _water;
|
|
|
|
delete _okoAnim;
|
|
delete _gui;
|
|
delete _objects;
|
|
delete _background;
|
|
|
|
_water = 0;
|
|
_heart = 0;
|
|
_lungs = 0;
|
|
|
|
_okoAnim = 0;
|
|
_gui = 0;
|
|
_objects = 0;
|
|
_background = 0;
|
|
}
|
|
|
|
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
|