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