1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/list.h"
24
25 #include "gob/global.h"
26 #include "gob/palanim.h"
27 #include "gob/draw.h"
28 #include "gob/video.h"
29 #include "gob/decfile.h"
30 #include "gob/anifile.h"
31
32 #include "gob/sound/sound.h"
33
34 #include "gob/minigames/geisha/evilfish.h"
35 #include "gob/minigames/geisha/oko.h"
36 #include "gob/minigames/geisha/meter.h"
37 #include "gob/minigames/geisha/diving.h"
38
39 namespace Gob {
40
41 namespace Geisha {
42
43 static const uint8 kAirDecreaseRate = 15;
44
45 static const byte kPalette[48] = {
46 0x00, 0x02, 0x12,
47 0x01, 0x04, 0x1D,
48 0x05, 0x08, 0x28,
49 0x0C, 0x0D, 0x33,
50 0x15, 0x14, 0x3F,
51 0x00, 0x3F, 0x00,
52 0x3F, 0x00, 0x00,
53 0x00, 0x00, 0x00,
54 0x21, 0x0D, 0x00,
55 0x2F, 0x1A, 0x04,
56 0x3D, 0x2B, 0x0D,
57 0x10, 0x10, 0x10,
58 0x1A, 0x1A, 0x1A,
59 0x24, 0x24, 0x24,
60 0x00, 0x01, 0x0F,
61 0x3F, 0x3F, 0x3F
62 };
63
64 enum Animation {
65 kAnimationLungs = 0,
66 kAnimationHeart = 1,
67 kAnimationPearl = 4,
68 kAnimationJellyfish = 6,
69 kAnimationWater = 7,
70 kAnimationShot = 17,
71 kAnimationSwarmRedGreen = 32,
72 kAnimationSwarmOrange = 33
73 };
74
75
76 const uint16 Diving::kEvilFishTypes[kEvilFishTypeCount][5] = {
77 { 0, 14, 8, 9, 3}, // Shark
78 {15, 1, 12, 13, 3}, // Moray
79 {16, 2, 10, 11, 3} // Ray
80 };
81
82 const uint16 Diving::kPlantLevel1[] = { 18, 19, 20, 21 };
83 const uint16 Diving::kPlantLevel2[] = { 22, 23, 24, 25 };
84 const uint16 Diving::kPlantLevel3[] = { 26, 27, 28, 29, 30 };
85
86 const Diving::PlantLevel Diving::kPlantLevels[] = {
87 { 150, ARRAYSIZE(kPlantLevel1), kPlantLevel1 },
88 { 120, ARRAYSIZE(kPlantLevel2), kPlantLevel2 },
89 { 108, ARRAYSIZE(kPlantLevel3), kPlantLevel3 },
90 };
91
92
Diving(GobEngine * vm)93 Diving::Diving(GobEngine *vm) : _vm(vm), _background(0),
94 _objects(0), _gui(0), _okoAnim(0), _water(0), _lungs(0), _heart(0),
95 _blackPearl(0), _airMeter(0), _healthMeter(0), _isPlaying(false) {
96
97 _blackPearl = new Surface(11, 8, 1);
98
99 _airMeter = new Meter(3 , 195, 40, 2, 5, 7, 40, Meter::kFillToLeft);
100 _healthMeter = new Meter(275, 195, 40, 2, 6, 7, 4, Meter::kFillToLeft);
101
102 for (uint i = 0; i < kEvilFishCount; i++)
103 _evilFish[i].evilFish = 0;
104
105 for (uint i = 0; i < kDecorFishCount; i++)
106 _decorFish[i].decorFish = 0;
107
108 for (uint i = 0; i < kPlantCount; i++)
109 _plant[i].plant = 0;
110
111 for (uint i = 0; i < kMaxShotCount; i++)
112 _shot[i] = 0;
113
114 _pearl.pearl = 0;
115
116 _oko = 0;
117 }
118
~Diving()119 Diving::~Diving() {
120 delete _airMeter;
121 delete _healthMeter;
122
123 delete _blackPearl;
124
125 deinit();
126 }
127
play(uint16 playerCount,bool hasPearlLocation)128 bool Diving::play(uint16 playerCount, bool hasPearlLocation) {
129 _hasPearlLocation = hasPearlLocation;
130 _isPlaying = true;
131
132 // Fade to black
133 _vm->_palAnim->fade(0, 0, 0);
134
135 // Initialize our playing field
136 init();
137 initScreen();
138 initCursor();
139 initPlants();
140
141 updateAirMeter();
142 updateAnims();
143
144 _vm->_draw->blitInvalidated();
145 _vm->_video->retrace();
146
147 // Fade in
148 _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0);
149
150 while (!_vm->shouldQuit()) {
151 checkShots(); // Check if a shot hit something
152 checkOkoHurt(); // Check if Oko was hurt
153
154 // Is Oko dead?
155 if (_oko->isPaused())
156 break;
157
158 // Update all objects and animations
159 updateAirMeter();
160 updateEvilFish();
161 updateDecorFish();
162 updatePlants();
163 updatePearl();
164 updateAnims();
165
166 _vm->_draw->animateCursor(1);
167
168 // Draw and wait for the end of the frame
169 _vm->_draw->blitInvalidated();
170 _vm->_util->waitEndFrame();
171
172 // Handle input
173 _vm->_util->processInput();
174
175 int16 mouseX, mouseY;
176 MouseButtons mouseButtons;
177
178 int16 key = checkInput(mouseX, mouseY, mouseButtons);
179
180 // Aborting the game
181 if (key == kKeyEscape)
182 break;
183
184 // Shoot the gun
185 if (mouseButtons == kMouseButtonsLeft)
186 shoot(mouseX, mouseY);
187
188 // Oko
189 handleOko(key);
190
191 // Game end check
192 if ((_whitePearlCount >= 20) || (_blackPearlCount >= 2))
193 break;
194 }
195
196 deinit();
197
198 _isPlaying = false;
199
200 // The game succeeded when we got 2 black pearls
201 return _blackPearlCount >= 2;
202 }
203
isPlaying() const204 bool Diving::isPlaying() const {
205 return _isPlaying;
206 }
207
cheatWin()208 void Diving::cheatWin() {
209 _blackPearlCount = 2;
210 }
211
init()212 void Diving::init() {
213 // Load sounds
214 _vm->_sound->sampleLoad(&_soundShoot , SOUND_SND, "tirgim.snd");
215 _vm->_sound->sampleLoad(&_soundBreathe , SOUND_SND, "respir.snd");
216 _vm->_sound->sampleLoad(&_soundWhitePearl, SOUND_SND, "virtou.snd");
217 _vm->_sound->sampleLoad(&_soundBlackPearl, SOUND_SND, "trouve.snd");
218
219 // Load and initialize sprites and animations
220 _background = new DECFile(_vm, "tperle.dec" , 320, 200);
221 _objects = new ANIFile(_vm, "tperle.ani" , 320);
222 _gui = new ANIFile(_vm, "tperlcpt.ani", 320);
223 _okoAnim = new ANIFile(_vm, "tplonge.ani" , 320);
224
225 _water = new ANIObject(*_objects);
226 _lungs = new ANIObject(*_gui);
227 _heart = new ANIObject(*_gui);
228
229 _water->setAnimation(kAnimationWater);
230 _water->setPosition();
231 _water->setVisible(true);
232
233 _lungs->setAnimation(kAnimationLungs);
234 _lungs->setPosition();
235 _lungs->setVisible(true);
236 _lungs->setPause(true);
237
238 _heart->setAnimation(kAnimationHeart);
239 _heart->setPosition();
240 _heart->setVisible(true);
241 _heart->setPause(true);
242
243 for (uint i = 0; i < kEvilFishCount; i++) {
244 _evilFish[i].enterAt = 0;
245 _evilFish[i].leaveAt = 0;
246
247 _evilFish[i].evilFish = new EvilFish(*_objects, 320, 0, 0, 0, 0, 0);
248 }
249
250 for (uint i = 0; i < kDecorFishCount; i++) {
251 _decorFish[i].enterAt = 0;
252
253 _decorFish[i].decorFish = new ANIObject(*_objects);
254 }
255
256 for (uint i = 0; i < kPlantCount; i++) {
257 _plant[i].level = i / kPlantPerLevelCount;
258 _plant[i].deltaX = (kPlantLevelCount - _plant[i].level) * -2;
259
260 _plant[i].x = -1;
261 _plant[i].y = -1;
262
263 _plant[i].plant = new ANIObject(*_objects);
264 }
265
266 _pearl.pearl = new ANIObject(*_objects);
267 _pearl.black = false;
268
269 _pearl.pearl->setAnimation(kAnimationPearl);
270
271 _decorFish[0].decorFish->setAnimation(kAnimationJellyfish);
272 _decorFish[0].deltaX = 0;
273
274 _decorFish[1].decorFish->setAnimation(kAnimationSwarmRedGreen);
275 _decorFish[1].deltaX = -5;
276
277 _decorFish[2].decorFish->setAnimation(kAnimationSwarmOrange);
278 _decorFish[2].deltaX = -5;
279
280 for (uint i = 0; i < kMaxShotCount; i++) {
281 _shot[i] = new ANIObject(*_objects);
282
283 _shot[i]->setAnimation(kAnimationShot);
284 _shot[i]->setMode(ANIObject::kModeOnce);
285 }
286
287 _oko = new Oko(*_okoAnim, *_vm->_sound, _soundBreathe);
288
289 Surface tmp(320, 103, 1);
290
291 _vm->_video->drawPackedSprite("tperlobj.cmp", tmp);
292
293 _blackPearl->blit(tmp, 282, 80, 292, 87, 0, 0);
294
295 _blackPearlCount = 0;
296
297 _currentShot = 0;
298
299 // Add the animations to our animation list
300 _anims.push_back(_water);
301 for (uint i = 0; i < kMaxShotCount; i++)
302 _anims.push_back(_shot[i]);
303 _anims.push_back(_pearl.pearl);
304 for (uint i = 0; i < kDecorFishCount; i++)
305 _anims.push_back(_decorFish[i].decorFish);
306 for (uint i = 0; i < kEvilFishCount; i++)
307 _anims.push_back(_evilFish[i].evilFish);
308 for (int i = kPlantCount - 1; i >= 0; i--)
309 _anims.push_back(_plant[i].plant);
310 _anims.push_back(_oko);
311 _anims.push_back(_lungs);
312 _anims.push_back(_heart);
313
314 // Air and health meter
315 _airMeter->setMaxValue();
316 _healthMeter->setMaxValue();
317
318 _airCycle = 0;
319 _hurtGracePeriod = 0;
320
321 _whitePearlCount = 0;
322 _blackPearlCount = 0;
323 }
324
deinit()325 void Diving::deinit() {
326 _vm->_draw->_cursorHotspotX = -1;
327 _vm->_draw->_cursorHotspotY = -1;
328
329 _soundShoot.free();
330 _soundBreathe.free();
331 _soundWhitePearl.free();
332 _soundBlackPearl.free();
333
334 _anims.clear();
335
336 _activeShots.clear();
337
338 for (uint i = 0; i < kMaxShotCount; i++) {
339 delete _shot[i];
340
341 _shot[i] = 0;
342 }
343
344 for (uint i = 0; i < kEvilFishCount; i++) {
345 delete _evilFish[i].evilFish;
346
347 _evilFish[i].evilFish = 0;
348 }
349
350 for (uint i = 0; i < kDecorFishCount; i++) {
351 delete _decorFish[i].decorFish;
352
353 _decorFish[i].decorFish = 0;
354 }
355
356 for (uint i = 0; i < kPlantCount; i++) {
357 delete _plant[i].plant;
358
359 _plant[i].plant = 0;
360 }
361
362 delete _pearl.pearl;
363 _pearl.pearl = 0;
364
365 delete _oko;
366 _oko = 0;
367
368 delete _heart;
369 delete _lungs;
370 delete _water;
371
372 delete _okoAnim;
373 delete _gui;
374 delete _objects;
375 delete _background;
376
377 _water = 0;
378 _heart = 0;
379 _lungs = 0;
380
381 _okoAnim = 0;
382 _gui = 0;
383 _objects = 0;
384 _background = 0;
385 }
386
initScreen()387 void Diving::initScreen() {
388 // Set framerate
389 _vm->_util->setFrameRate(15);
390
391 // Set palette
392 memcpy(_vm->_draw->_vgaPalette, kPalette, sizeof(kPalette));
393
394 // Draw background decal
395 _vm->_draw->_backSurface->clear();
396 _background->draw(*_vm->_draw->_backSurface);
397
398 // Draw heart and lung boxes
399 int16 left, top, right, bottom;
400 _lungs->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
401 _heart->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
402
403 // Mark everything as dirty
404 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199);
405 }
406
initCursor()407 void Diving::initCursor() {
408 const int index = _vm->_draw->_cursorIndex;
409
410 const int16 left = index * _vm->_draw->_cursorWidth;
411 const int16 top = 0;
412 const int16 right = left + _vm->_draw->_cursorWidth - 1;
413 const int16 bottom = _vm->_draw->_cursorHeight - 1;
414
415 _vm->_draw->_cursorSprites->fillRect(left, top, right, bottom, 0);
416
417 _objects->draw(*_vm->_draw->_cursorSprites, 31, 0, left, top);
418 _vm->_draw->_cursorAnimLow[index] = 0;
419
420 _vm->_draw->_cursorHotspotX = 8;
421 _vm->_draw->_cursorHotspotY = 8;
422 }
423
424
initPlants()425 void Diving::initPlants() {
426 // Create initial plantlife
427 for (uint i = 0; i < kPlantLevelCount; i++) {
428 for (uint j = 0; j < kPlantPerLevelCount; j++) {
429 int16 prevPlantX = -100;
430 if (j > 0)
431 prevPlantX = _plant[i * kPlantPerLevelCount + j - 1].x;
432
433 enterPlant(_plant[i * kPlantPerLevelCount + j], prevPlantX);
434 }
435 }
436 }
437
enterPlant(ManagedPlant & plant,int16 prevPlantX)438 void Diving::enterPlant(ManagedPlant &plant, int16 prevPlantX) {
439 // Create a new plant outside the borders of the screen to scroll in
440
441 const PlantLevel &level = kPlantLevels[plant.level];
442 const uint anim = level.plants[_vm->_util->getRandom(kPlantLevels[plant.level].plantCount)];
443
444 plant.plant->setAnimation(anim);
445 plant.plant->rewind();
446
447 int16 width, height;
448 plant.plant->getFrameSize(width, height);
449
450 // The new plant is created 140 - 160 pixels to the right of the right-most plant
451 plant.x = prevPlantX + 150 - 10 + _vm->_util->getRandom(21);
452 plant.y = kPlantLevels[plant.level].y - height;
453
454 plant.plant->setPosition(plant.x, plant.y);
455 plant.plant->setVisible(true);
456 plant.plant->setPause(false);
457
458 // If the plant is outside of the screen, create a pearl too if necessary
459 if (plant.x > 320)
460 enterPearl(plant.x);
461 }
462
enterPearl(int16 x)463 void Diving::enterPearl(int16 x) {
464 // Create a pearl outside the borders of the screen to scroll in
465
466 // Only one pearl is ever visible
467 if (_pearl.pearl->isVisible())
468 return;
469
470 // Only every 4th potential pearl position has a pearl
471 if (_vm->_util->getRandom(4) != 0)
472 return;
473
474 // Every 5th pearl is a black one, but only if the location is correct
475 _pearl.black = _hasPearlLocation && (_vm->_util->getRandom(5) == 0);
476
477 // Set the pearl about in the middle of two bottom-level plants
478 _pearl.pearl->setPosition(x + 80, 130);
479
480 _pearl.pearl->setVisible(true);
481 _pearl.pearl->setPause(false);
482 _pearl.picked = false;
483 }
484
updateAirMeter()485 void Diving::updateAirMeter() {
486 if (_oko->isBreathing()) {
487 // If Oko is breathing, increase the air meter and play the lungs animation
488 _airCycle = 0;
489 _airMeter->increase();
490 _lungs->setPause(false);
491 return;
492 } else
493 // Otherwise, don't play the lungs animation
494 _lungs->setPause(true);
495
496 // Update the air cycle and decrease the air meter when the cycle ended
497 _airCycle = (_airCycle + 1) % kAirDecreaseRate;
498
499 if (_airCycle == 0)
500 _airMeter->decrease();
501
502 // Without any air, Oko dies
503 if (_airMeter->getValue() == 0)
504 _oko->die();
505 }
506
updateEvilFish()507 void Diving::updateEvilFish() {
508 for (uint i = 0; i < kEvilFishCount; i++) {
509 ManagedEvilFish &fish = _evilFish[i];
510
511 if (fish.evilFish->isVisible()) {
512 // Evil fishes leave on their own after 30s - 40s
513
514 fish.enterAt = 0;
515
516 if (fish.leaveAt == 0)
517 fish.leaveAt = _vm->_util->getTimeKey() + 30000 + _vm->_util->getRandom(10000);
518
519 if (_vm->_util->getTimeKey() >= fish.leaveAt)
520 fish.evilFish->leave();
521
522 } else {
523 // Evil fishes enter the screen in 2s - 10s
524
525 fish.leaveAt = 0;
526
527 if (fish.enterAt == 0)
528 fish.enterAt = _vm->_util->getTimeKey() + 2000 + _vm->_util->getRandom(8000);
529
530 if (_vm->_util->getTimeKey() >= fish.enterAt) {
531 // The new fish has a random type
532 int fishType = _vm->_util->getRandom(kEvilFishTypeCount);
533 fish.evilFish->mutate(kEvilFishTypes[fishType][0], kEvilFishTypes[fishType][1],
534 kEvilFishTypes[fishType][2], kEvilFishTypes[fishType][3],
535 kEvilFishTypes[fishType][4]);
536
537 fish.evilFish->enter((EvilFish::Direction)_vm->_util->getRandom(2),
538 36 + _vm->_util->getRandom(3) * 40);
539 }
540 }
541 }
542 }
543
updateDecorFish()544 void Diving::updateDecorFish() {
545 for (uint i = 0; i < kDecorFishCount; i++) {
546 ManagedDecorFish &fish = _decorFish[i];
547
548 if (fish.decorFish->isVisible()) {
549 // Move the fish
550 int16 x, y;
551 fish.decorFish->getPosition(x, y);
552 fish.decorFish->setPosition(x + fish.deltaX, y);
553
554 // Check if the fish has left the screen
555 int16 width, height;
556 fish.decorFish->getFramePosition(x, y);
557 fish.decorFish->getFrameSize(width, height);
558
559 if ((x + width) <= 0) {
560 fish.decorFish->setVisible(false);
561 fish.decorFish->setPause(true);
562
563 fish.enterAt = 0;
564 }
565
566 } else {
567 // Decor fishes enter the screen every 0s - 10s
568
569 if (fish.enterAt == 0)
570 fish.enterAt = _vm->_util->getTimeKey() + _vm->_util->getRandom(10000);
571
572 if (_vm->_util->getTimeKey() >= fish.enterAt) {
573 fish.decorFish->rewind();
574 fish.decorFish->setPosition(320, 30 + _vm->_util->getRandom(100));
575 fish.decorFish->setVisible(true);
576 fish.decorFish->setPause(false);
577 }
578 }
579 }
580 }
581
updatePlants()582 void Diving::updatePlants() {
583 // When Oko isn't moving, the plants don't continue to scroll by
584 if (!_oko->isMoving())
585 return;
586
587 for (uint i = 0; i < kPlantCount; i++) {
588 ManagedPlant &plant = _plant[i];
589
590 if (plant.plant->isVisible()) {
591 // Move the plant
592 plant.plant->setPosition(plant.x += plant.deltaX, plant.y);
593
594 // Check if the plant has left the screen
595 int16 x, y, width, height;
596 plant.plant->getFramePosition(x, y);
597 plant.plant->getFrameSize(width, height);
598
599 if ((x + width) <= 0) {
600 plant.plant->setVisible(false);
601 plant.plant->setPause(true);
602
603 plant.x = 0;
604 }
605
606 } else {
607 // Find the right-most plant in this level and enter the plant to the right of it
608
609 int16 rightX = 320;
610 for (uint j = 0; j < kPlantPerLevelCount; j++)
611 rightX = MAX(rightX, _plant[plant.level * kPlantPerLevelCount + j].x);
612
613 enterPlant(plant, rightX);
614 }
615 }
616 }
617
updatePearl()618 void Diving::updatePearl() {
619 if (!_pearl.pearl->isVisible())
620 return;
621
622 // When Oko isn't moving, the pearl doesn't continue to scroll by
623 if (!_oko->isMoving())
624 return;
625
626 // Picking the pearl
627 if (_pearl.picked && (_oko->getState() == Oko::kStatePick) && (_oko->getFrame() == 8)) {
628 // Remove the pearl
629 _pearl.pearl->setVisible(false);
630 _pearl.pearl->setPause(true);
631
632 // Add the pearl to our found pearls repository
633 if (_pearl.black)
634 foundBlackPearl();
635 else
636 foundWhitePearl();
637
638 return;
639 }
640
641 // Move the pearl
642 int16 x, y, width, height;
643 _pearl.pearl->getPosition(x, y);
644 _pearl.pearl->setPosition(x - 5, y);
645
646 // Check if the pearl has left the screen
647 _pearl.pearl->getFramePosition(x, y);
648 _pearl.pearl->getFrameSize(width, height);
649
650 if ((x + width) <= 0) {
651 _pearl.pearl->setVisible(false);
652 _pearl.pearl->setPause(true);
653 }
654 }
655
getPearl()656 void Diving::getPearl() {
657 if (!_pearl.pearl->isVisible())
658 return;
659
660 // Make sure the pearl is within Oko's grasp
661
662 int16 x, y, width, height;
663 _pearl.pearl->getFramePosition(x, y);
664 _pearl.pearl->getFrameSize(width, height);
665
666 if ((x > 190) || ((x + width) < 140))
667 return;
668
669 _pearl.picked = true;
670 }
671
foundBlackPearl()672 void Diving::foundBlackPearl() {
673 _blackPearlCount++;
674
675 // Put the black pearl drawing into the black pearl box
676 if (_blackPearlCount == 1) {
677 _vm->_draw->_backSurface->blit(*_blackPearl, 0, 0, 10, 7, 147, 179, 0);
678 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 179, 157, 186);
679 } else if (_blackPearlCount == 2) {
680 _vm->_draw->_backSurface->blit(*_blackPearl, 0, 0, 10, 7, 160, 179, 0);
681 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 179, 160, 186);
682 }
683
684 _vm->_sound->blasterPlay(&_soundBlackPearl, 1, 0);
685 }
686
foundWhitePearl()687 void Diving::foundWhitePearl() {
688 _whitePearlCount++;
689
690 // Put the white pearl drawing into the white pearl box
691 int16 x = 54 + (_whitePearlCount - 1) * 8;
692 if (_whitePearlCount > 10)
693 x += 48;
694
695 _background->drawLayer(*_vm->_draw->_backSurface, 0, 2, x, 177, 0);
696 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, 177, x + 3, 180);
697
698 _vm->_sound->blasterPlay(&_soundWhitePearl, 1, 0);
699 }
700
updateAnims()701 void Diving::updateAnims() {
702 int16 left, top, right, bottom;
703
704 // Clear the previous animation frames
705 for (Common::List<ANIObject *>::iterator a = _anims.reverse_begin();
706 a != _anims.end(); --a) {
707
708 if ((*a)->clear(*_vm->_draw->_backSurface, left, top, right, bottom))
709 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
710 }
711
712 // Draw the current animation frames
713 for (Common::List<ANIObject *>::iterator a = _anims.begin();
714 a != _anims.end(); ++a) {
715
716 if ((*a)->draw(*_vm->_draw->_backSurface, left, top, right, bottom))
717 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
718
719 (*a)->advance();
720 }
721
722 // Draw the meters
723 _airMeter->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
724 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
725
726 _healthMeter->draw(*_vm->_draw->_backSurface, left, top, right, bottom);
727 _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom);
728 }
729
checkInput(int16 & mouseX,int16 & mouseY,MouseButtons & mouseButtons)730 int16 Diving::checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) {
731 _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons);
732
733 return _vm->_util->checkKey();
734 }
735
shoot(int16 mouseX,int16 mouseY)736 void Diving::shoot(int16 mouseX, int16 mouseY) {
737 // Outside the playable area?
738 if (mouseY > 157)
739 return;
740
741 // Too many shots still active?
742 if (_activeShots.size() >= kMaxShotCount)
743 return;
744
745 ANIObject &shot = *_shot[_currentShot];
746
747 shot.rewind();
748 shot.setVisible(true);
749 shot.setPause(false);
750 shot.setPosition(mouseX - 8, mouseY - 8);
751
752 _activeShots.push_back(_currentShot);
753
754 _currentShot = (_currentShot + 1) % kMaxShotCount;
755
756 _vm->_sound->blasterPlay(&_soundShoot, 1, 0);
757 }
758
checkShots()759 void Diving::checkShots() {
760 Common::List<int>::iterator activeShot = _activeShots.begin();
761
762 // Check if we hit something with our shots
763 while (activeShot != _activeShots.end()) {
764 ANIObject &shot = *_shot[*activeShot];
765
766 if (shot.lastFrame()) {
767 int16 x, y;
768
769 shot.getPosition(x, y);
770
771 // When we hit an evil fish, it dies
772 for (uint i = 0; i < kEvilFishCount; i++) {
773 EvilFish &evilFish = *_evilFish[i].evilFish;
774
775 if (evilFish.isIn(x + 8, y + 8)) {
776 evilFish.die();
777
778 break;
779 }
780 }
781
782 activeShot = _activeShots.erase(activeShot);
783 } else
784 ++activeShot;
785 }
786 }
787
handleOko(int16 key)788 void Diving::handleOko(int16 key) {
789 if (key == kKeyDown) {
790 // Oko sinks down a level or picks up a pearl if already at the bottom
791 _oko->sink();
792
793 if ((_oko->getState() == Oko::kStatePick) && (_oko->getFrame() == 0))
794 getPearl();
795
796 } else if (key == kKeyUp)
797 // Oko raises up a level or surfaces to breathe if already at the top
798 _oko->raise();
799 }
800
checkOkoHurt()801 void Diving::checkOkoHurt() {
802 if (_oko->getState() != Oko::kStateSwim)
803 return;
804
805 // Give Oko a grace period after being hurt
806 if (_hurtGracePeriod > 0) {
807 _hurtGracePeriod--;
808 return;
809 }
810
811 // Check for a fish/Oko-collision
812 for (uint i = 0; i < kEvilFishCount; i++) {
813 EvilFish &evilFish = *_evilFish[i].evilFish;
814
815 if (!evilFish.isDead() && evilFish.isIn(*_oko)) {
816 _healthMeter->decrease();
817
818 // If the health reached 0, Oko dies. Otherwise, she gets hurt
819 if (_healthMeter->getValue() == 0)
820 _oko->die();
821 else
822 _oko->hurt();
823
824 _hurtGracePeriod = 10;
825 break;
826 }
827 }
828 }
829
830 } // End of namespace Geisha
831
832 } // End of namespace Gob
833