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 "sherlock/tattoo/tattoo_darts.h"
24 #include "sherlock/tattoo/tattoo_fixed_text.h"
25 #include "sherlock/tattoo/tattoo.h"
26 
27 namespace Sherlock {
28 
29 namespace Tattoo {
30 
31 enum {
32 	DART_COLOR_FORE	= 5,
33 	PLAYER_COLOR	= 11,
34 	DART_BAR_FORE	= 208
35 };
36 
37 static const int STATUS_INFO_X = 430;
38 static const int STATUS_INFO_Y = 50;
39 static const int STATUS_INFO_WIDTH = 205;
40 static const int STATUS_INFO_HEIGHT = 330;
41 static const int STATUS2_INFO_X = 510;
42 static const int STATUS2_X_ADD = STATUS2_INFO_X - STATUS_INFO_X;
43 static const int DART_BAR_VX = 10;
44 static const int DART_HEIGHT_Y = 121;
45 static const int DART_BAR_SIZE = 150;
46 static const int DARTBOARD_LEFT = 73;
47 static const int DARTBOARD_TOP = 68;
48 static const int DARTBOARD_WIDTH = 257;
49 static const int DARTBOARD_HEIGHT = 256;
50 static const int DARTBOARD_TOTALX = DARTBOARD_WIDTH * 120 / 100;
51 static const int DARTBOARD_TOTALY = DARTBOARD_HEIGHT * 120 / 100;
52 static const int DARTBOARD_TOTALTOP = DARTBOARD_TOP - DARTBOARD_WIDTH / 10;
53 static const int DARTBOARD_TOTALLEFT = DARTBOARD_LEFT - DARTBOARD_HEIGHT / 10;
54 static const int CRICKET_VALUE[7] = { 20, 19, 18, 17, 16, 15, 25 };
55 
Darts(SherlockEngine * vm)56 Darts::Darts(SherlockEngine *vm) : _vm(vm) {
57 	_gameType = GAME_301;
58 	_hand1 = _hand2 = nullptr;
59 	_dartGraphics = nullptr;
60 	_dartsLeft = nullptr;
61 	_dartMap = nullptr;
62 	_dartBoard = nullptr;
63 	Common::fill(&_cricketScore[0][0], &_cricketScore[0][7], 0);
64 	Common::fill(&_cricketScore[1][0], &_cricketScore[1][7], 0);
65 	_score1 = _score2 = 0;
66 	_roundNum = 0;
67 	_roundScore = 0;
68 	_level = 0;
69 	_oldDartButtons = false;
70 	_handX = 0;
71 	_compPlay = 1;
72 	_escapePressed = false;
73 	_spacing = 0;
74 }
75 
playDarts(GameType gameType)76 void Darts::playDarts(GameType gameType) {
77 	Events &events = *_vm->_events;
78 	Scene &scene = *_vm->_scene;
79 	Screen &screen = *_vm->_screen;
80 	int oldFontType = screen.fontNumber();
81 	int playerNum = 0;
82 	int lastDart;
83 	int numHits = 0;
84 	bool gameOver = false;
85 	bool done = false;
86 
87 	// Set the game mode
88 	_gameType = gameType;
89 
90 	screen.setFont(7);
91 	_spacing = screen.fontHeight() + 2;
92 
93 	// Load dart graphics and initialize values
94 	loadDarts();
95 	initDarts();
96 	events.hideCursor();
97 
98 	while (!done && !_vm->shouldQuit()) {
99 		int roundStart, score;
100 		roundStart = score = (playerNum == 0) ? _score1 : _score2;
101 
102 		showNames(playerNum);
103 		showStatus(playerNum);
104 		_roundScore = 0;
105 
106 		for (int idx = 0; idx < 3 && !_vm->shouldQuit(); ++idx) {
107 			if (_compPlay == 1)
108 				lastDart = throwDart(idx + 1, playerNum * 2);  /* Throw one dart */
109 			else
110 				if (_compPlay == 2)
111 					lastDart = throwDart(idx + 1, playerNum + 1);  /* Throw one dart */
112 				else
113 					lastDart = throwDart(idx + 1, 0);    /* Throw one dart */
114 
115 			if (_gameType == GAME_301) {
116 				score -= lastDart;
117 				_roundScore += lastDart;
118 			} else {
119 				numHits = lastDart >> 16;
120 				if (numHits == 0)
121 					numHits = 1;
122 				if (numHits > 3)
123 					numHits = 3;
124 
125 				lastDart = lastDart & 0xffff;
126 				updateCricketScore(playerNum, lastDart, numHits);
127 				score = (playerNum == 0) ? _score1 : _score2;
128 			}
129 
130 			// Special case for ScummVM: I'm making pressing Escape to exit out of the Darts game as a way to skip
131 			// it entirely if you don't want to play all the way through it
132 			if (_escapePressed) {
133 				gameOver = true;
134 				done = true;
135 				playerNum = 0;
136 			}
137 
138 
139 			if (_gameType == GAME_301) {
140 				if (playerNum == 0)
141 					_score1 = score;
142 				else
143 					_score2 = score;
144 
145 				if (score == 0)
146 					// Someone won
147 					gameOver = true;
148 			} else {
149 				// check for cricket game over
150 				bool allClosed = true;
151 
152 				for (int y = 0; y < 7; y++) {
153 					if (_cricketScore[playerNum][y] < 3)
154 						allClosed = false;
155 				}
156 
157 				if (allClosed) {
158 					int nOtherScore = (playerNum == 0) ? _score2 : _score1;
159 					if (score >= nOtherScore)
160 						gameOver = true;
161 				}
162 			}
163 
164 			// Show scores
165 			showStatus(playerNum);
166 			screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
167 				Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
168 			screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, FIXED(DartsCurrentDart), idx + 1);
169 
170 			if (_gameType == GAME_301) {
171 				// "Scored x points"
172 				Common::String scoredPoints;
173 
174 				// original treated 1 point and multiple points the same. Wrote "Scored 1 points"
175 				if (lastDart == 1) {
176 					scoredPoints = Common::String::format(FIXED(DartsScoredPoint), lastDart);
177 				} else {
178 					scoredPoints = Common::String::format(FIXED(DartsScoredPoints), lastDart);
179 				}
180 
181 				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", scoredPoints.c_str());
182 			} else {
183 				Common::String hitText;
184 
185 				if (lastDart != 25) {
186 					// Regular hit
187 					switch (numHits) {
188 					case 1: // "Hit a X"
189 						hitText = Common::String::format(FIXED(DartsHitSingle), lastDart);
190 						break;
191 					case 2: // "Hit double X"
192 						hitText = Common::String::format(FIXED(DartsHitDouble), lastDart);
193 						break;
194 					case 3: // "Hit triple X"
195 						hitText = Common::String::format(FIXED(DartsHitTriple), lastDart);
196 						break;
197 					default:
198 						break;
199 					}
200 				} else {
201 					// Bullseye
202 					switch (numHits) {
203 					case 1:
204 						hitText = Common::String(FIXED(DartsHitSingleBullseye));
205 						break;
206 					case 2:
207 						hitText = Common::String(FIXED(DartsHitDoubleBullseye));
208 						break;
209 					case 3:
210 						hitText = Common::String(FIXED(DartsHitTripleBullseye));
211 						break;
212 					default:
213 						break;
214 					}
215 				}
216 				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", hitText.c_str());
217 			}
218 
219 			if (score != 0 && playerNum == 0 && !gameOver)
220 				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 3), 0,
221 					"%s", FIXED(DartsPressKey));
222 
223 			if (gameOver) {
224 				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 3),
225 					0, "%s", FIXED(DartsGameOver));
226 				if (playerNum == 0) {
227 					screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 4), 0,
228 						FIXED(DartsWins), FIXED(DartsPlayerHolmes));
229 					_vm->setFlagsDirect(531);
230 				} else {
231 					screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 4), 0,
232 						FIXED(DartsWins), _opponent.c_str());
233 					_vm->setFlagsDirect(530);
234 				}
235 
236 				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 5), 0,
237 					"%s", FIXED(DartsPressKey));
238 
239 				done = true;
240 				idx = 10;
241 			} else if (_gameType == GAME_301 && score < 0) {
242 				screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0,
243 					"%s!", FIXED(DartsBusted));
244 
245 				// End turn
246 				idx = 10;
247 				score = roundStart;
248 				if (playerNum == 0)
249 					_score1 = score;
250 				else
251 					_score2 = score;
252 			}
253 
254 			// Clear keyboard events
255 			events.clearEvents();
256 
257 			if ((playerNum == 0 && _compPlay == 1) || _compPlay == 0 || done) {
258 				if (_escapePressed) {
259 					done = true;
260 					break;
261 				}
262 				// Wait for keypress
263 				do {
264 					events.pollEventsAndWait();
265 					events.setButtonState();
266 				} while (!_vm->shouldQuit() && !events.kbHit() && !events._pressed);
267 			} else {
268 				events.wait(40);
269 			}
270 
271 			// Clears the status part of the board
272 			screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
273 				Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
274 			screen.SHblitFrom(screen._backBuffer1);
275 		}
276 
277 		playerNum ^= 1;
278 		if (!playerNum)
279 			++_roundNum;
280 
281 		if (!done) {
282 			screen._backBuffer2.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0));
283 			screen._backBuffer1.SHblitFrom(screen._backBuffer2);
284 			screen.SHblitFrom(screen._backBuffer2);
285 		}
286 	}
287 
288 	// Wait for a keypress
289 	do {
290 		events.pollEventsAndWait();
291 		events.setButtonState();
292 	} while (!_vm->shouldQuit() && !events.kbHit() && !events._pressed);
293 	events.clearEvents();
294 
295 	closeDarts();
296 	screen.fadeToBlack();
297 	screen.setFont(oldFontType);
298 
299 	// Flag to return to the Billard's Academy scene
300 	scene._goToScene = 26;
301 }
302 
initDarts()303 void Darts::initDarts() {
304 	_dartInfo = Common::Rect(430, 245, 430 + 205, 245 + 150);
305 	_escapePressed = false;
306 
307 	if (_gameType == GAME_CRICKET) {
308 		_dartInfo = Common::Rect(430, 245, 430 + 205, 245 + 150);
309 	}
310 
311 	Common::fill(&_cricketScore[0][0], &_cricketScore[0][7], 0);
312 	Common::fill(&_cricketScore[1][0], &_cricketScore[1][7], 0);
313 
314 	switch (_gameType) {
315 	case GAME_501:
316 		_score1 = _score2 = 501;
317 		_gameType = GAME_301;
318 		break;
319 
320 	case GAME_301:
321 		_score1 = _score2 = 301;
322 		break;
323 
324 	default:
325 		// Cricket
326 		_score1 = _score2 = 0;
327 		break;
328 	}
329 
330 	_roundNum = 1;
331 
332 	if (_level == 9) {
333 		// No computer players
334 		_compPlay = 0;
335 		_level = 0;
336 	} else if (_level == 8) {
337 		_level = _vm->getRandomNumber(3);
338 		_compPlay = 2;
339 	} else {
340 		// Check for opponent flags
341 		for (int idx = 0; idx < 4; ++idx) {
342 			if (_vm->readFlags(314 + idx))
343 				_level = idx;
344 		}
345 	}
346 
347 	_opponent = FIXED(DartsPlayerJock);
348 }
349 
loadDarts()350 void Darts::loadDarts() {
351 	Resources &res = *_vm->_res;
352 	Screen &screen = *_vm->_screen;
353 	byte palette[PALETTE_SIZE];
354 
355 	// Load images
356 	_hand1 = new ImageFile("hand1.vgs");
357 	_hand2 = new ImageFile("hand2.vgs");
358 	_dartGraphics = new ImageFile("darts.vgs");
359 	_dartsLeft = new ImageFile("DartsLft.vgs");
360 	_dartMap = new ImageFile("DartMap.vgs");
361 	_dartBoard = new ImageFile("DartBd.vgs");
362 
363 	// Load and set the palette
364 	Common::SeekableReadStream *stream = res.load("DartBd.pal");
365 	stream->read(palette, PALETTE_SIZE);
366 	screen.translatePalette(palette);
367 	screen.setPalette(palette);
368 	delete stream;
369 
370 	// Load the initial background
371 	screen._backBuffer1.SHblitFrom((*_dartBoard)[0], Common::Point(0, 0));
372 	screen._backBuffer2.SHblitFrom(screen._backBuffer1);
373 	screen.SHblitFrom(screen._backBuffer1);
374 }
375 
closeDarts()376 void Darts::closeDarts() {
377 	delete _dartBoard;
378 	delete _dartsLeft;
379 	delete _dartGraphics;
380 	delete _dartMap;
381 	delete _hand1;
382 	delete _hand2;
383 }
384 
showNames(int playerNum)385 void Darts::showNames(int playerNum) {
386 	Screen &screen = *_vm->_screen;
387 	byte color;
388 
389 	color = playerNum == 0 ? PLAYER_COLOR : DART_COLOR_FORE;
390 	screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y), 0, "%s", FIXED(DartsPlayerHolmes));
391 	screen._backBuffer1.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + _spacing + 1,
392 		STATUS_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
393 	screen.fillRect(Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + _spacing + 1,
394 		STATUS_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
395 
396 	color = playerNum == 1 ? PLAYER_COLOR : DART_COLOR_FORE;
397 	screen.print(Common::Point(STATUS2_INFO_X, STATUS_INFO_Y), 0, "%s", _opponent.c_str());
398 	screen._backBuffer1.fillRect(Common::Rect(STATUS2_INFO_X, STATUS_INFO_Y + _spacing + 1,
399 		STATUS2_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
400 	screen.fillRect(Common::Rect(STATUS2_INFO_X, STATUS_INFO_Y + _spacing + 1,
401 		STATUS2_INFO_X + 50, STATUS_INFO_Y + _spacing + 3), color);
402 
403 	screen._backBuffer2.SHblitFrom(screen._backBuffer1);
404 }
405 
showStatus(int playerNum)406 void Darts::showStatus(int playerNum) {
407 	Screen &screen = *_vm->_screen;
408 	const char *const CRICKET_SCORE_NAME[7] = { "20", "19", "18", "17", "16", "15", FIXED(DartsBull) };
409 
410 	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
411 		Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH,
412 		STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10));
413 	screen.print(Common::Point(STATUS_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score1);
414 
415 	screen.print(Common::Point(STATUS2_INFO_X + 30, STATUS_INFO_Y + _spacing + 4), 0, "%d", _score2);
416 
417 	int temp = (_gameType == GAME_CRICKET) ? STATUS_INFO_Y + 10 * _spacing + 5 : STATUS_INFO_Y + 55;
418 
419 	// "Round: x"
420 	Common::String dartsRoundStatus = Common::String::format(FIXED(DartsCurrentRound), _roundNum);
421 	screen.print(Common::Point(STATUS_INFO_X, temp), 0, "%s", dartsRoundStatus.c_str());
422 
423 	if (_gameType == GAME_301) {
424 		// "Turn Total: x"
425 		Common::String dartsTotalPoints = Common::String::format(FIXED(DartsCurrentTotalPoints), _roundScore);
426 		screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, "%s", dartsTotalPoints.c_str());
427 	} else {
428 		// Show cricket scores
429 		for (int x = 0; x < 7; ++x) {
430 			screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 40 + x * _spacing), 0, "%s:", CRICKET_SCORE_NAME[x]);
431 
432 			for (int y = 0; y < 2; ++y) {
433 				switch (CRICKET_SCORE_NAME[y][x]) {
434 				case 1:
435 					screen.print(Common::Point(STATUS_INFO_X + 38 + y*STATUS2_X_ADD, STATUS_INFO_Y + 40 + x * _spacing), 0, "/");
436 					break;
437 				case 2:
438 					screen.print(Common::Point(STATUS_INFO_X + 38 + y*STATUS2_X_ADD, STATUS_INFO_Y + 40 + x * _spacing), 0, "X");
439 					break;
440 				case 3:
441 					screen.print(Common::Point(STATUS_INFO_X + 38 + y * STATUS2_X_ADD - 1, STATUS_INFO_Y + 40 + x * _spacing), 0, "X");
442 					screen.print(Common::Point(STATUS_INFO_X + 37 + y * STATUS2_X_ADD, STATUS_INFO_Y + 40 + x * _spacing), 0, "O");
443 					break;
444 				default:
445 					break;
446 				}
447 			}
448 		}
449 	}
450 
451 	screen.SHblitFrom(screen._backBuffer1, Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 10),
452 		Common::Rect(STATUS_INFO_X, STATUS_INFO_Y + 10, STATUS_INFO_X + STATUS_INFO_WIDTH,
453 			STATUS_INFO_Y + STATUS_INFO_HEIGHT - 10));
454 }
455 
erasePowerBars()456 void Darts::erasePowerBars() {
457 	Screen &screen = *_vm->_screen;
458 
459 	// Erase the old power bars and replace them with empty ones
460 	screen._backBuffer1.fillRect(Common::Rect(DART_BAR_VX, DART_HEIGHT_Y, DART_BAR_VX + 9, DART_HEIGHT_Y + DART_BAR_SIZE), 0);
461 	screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1));
462 	screen.slamArea(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, 10, DART_BAR_SIZE + 2);
463 }
464 
dartHit()465 bool Darts::dartHit() {
466 	Events &events = *_vm->_events;
467 	events.pollEvents();
468 	events.setButtonState();
469 
470 	// Keyboard check
471 	if (events.kbHit()) {
472 		if (events.getKey().keycode == Common::KEYCODE_ESCAPE)
473 			_escapePressed = true;
474 
475 		events.clearEvents();
476 		return true;
477 	}
478 
479 	bool result = events._pressed && !_oldDartButtons;
480 	_oldDartButtons = events._pressed;
481 	return result;
482 }
483 
doPowerBar(const Common::Point & pt,byte color,int goToPower,int orientation)484 int Darts::doPowerBar(const Common::Point &pt, byte color, int goToPower, int orientation) {
485 	Events &events = *_vm->_events;
486 	Screen &screen = *_vm->_screen;
487 	int idx = 0;
488 
489 	events.clearEvents();
490 	events.delay(100);
491 
492 	while (!_vm->shouldQuit() && idx < DART_BAR_SIZE) {
493 		if ((goToPower - 1) == idx)
494 			break;
495 		else if (goToPower == 0) {
496 			if (dartHit())
497 				break;
498 		}
499 
500 		screen._backBuffer1.hLine(pt.x, pt.y + DART_BAR_SIZE- 1 - idx, pt.x + 8, color);
501 		screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[0], Common::Point(pt.x - 1, pt.y - 1));
502 		screen.slamArea(pt.x, pt.y + DART_BAR_SIZE - 1 - idx, 8, 2);
503 
504 		if (!(idx % 8))
505 			events.wait(1);
506 
507 		++idx;
508 	}
509 
510 	return MIN(idx * 100 / DART_BAR_SIZE, 100);
511 }
512 
drawHand(int goToPower,int computer)513 int Darts::drawHand(int goToPower, int computer) {
514 	Events &events = *_vm->_events;
515 	Screen &screen = *_vm->_screen;
516 	const int HAND_OFFSET[2] = { 72, 44 };
517 	ImageFile *hands;
518 	int hand;
519 
520 	goToPower = (goToPower * DARTBOARD_WIDTH) / 150;
521 
522 	if (!computer) {
523 		hand = 0;
524 		hands = _hand1;
525 	} else {
526 		hand = 1;
527 		hands = _hand2;
528 	}
529 
530 	_handSize.x = (*hands)[0]._offset.x + (*hands)[0]._width;
531 	_handSize.y = (*hands)[0]._offset.y + (*hands)[0]._height;
532 
533 	// Clear keyboard buffer
534 	events.clearEvents();
535 	events.delay(100);
536 
537 	Common::Point pt(DARTBOARD_LEFT - HAND_OFFSET[hand], SHERLOCK_SCREEN_HEIGHT - _handSize.y);
538 	int x = 0;
539 
540 	while (!_vm->shouldQuit() && x < DARTBOARD_WIDTH) {
541 		if (computer && x >= (goToPower - 1))
542 			break;
543 		else if (goToPower == 0) {
544 			if (dartHit())
545 				break;
546 		}
547 
548 		screen._backBuffer1.SHtransBlitFrom((*hands)[0], pt);
549 		screen.slamArea(pt.x - 1, pt.y, _handSize.x + 1, _handSize.y);
550 		screen.restoreBackground(Common::Rect(pt.x, pt.y, pt.x + _handSize.x, pt.y + _handSize.y));
551 
552 		if (!(x % 8))
553 			events.wait(1);
554 
555 		++x;
556 		++pt.x;
557 	}
558 
559 	_handX = pt.x - 1;
560 
561 	return MIN(x * 100 / DARTBOARD_WIDTH, 100);
562 }
563 
convertFromScreenToScoreCoords(const Common::Point & pt) const564 Common::Point Darts::convertFromScreenToScoreCoords(const Common::Point &pt) const {
565 	return Common::Point(CLIP((int)pt.x, 0, DARTBOARD_WIDTH), CLIP((int)pt.y, 0, DARTBOARD_HEIGHT));
566 }
567 
dartScore(const Common::Point & pt)568 int Darts::dartScore(const Common::Point &pt) {
569 	Common::Point pos(pt.x - DARTBOARD_LEFT, pt.y - DARTBOARD_TOP);
570 	if (pos.x < 0 || pos.y < 0)
571 		return 0;
572 	int score;
573 
574 	if (pos.x < DARTBOARD_WIDTH && pos.y < DARTBOARD_HEIGHT) {
575 		pos = convertFromScreenToScoreCoords(pos);
576 		score = *(const byte *)(*_dartMap)[0]._frame.getBasePtr(pos.x, pos.y);
577 
578 		if (_gameType == GAME_301) {
579 			if (score >= 100) {
580 				if (score <= 120)
581 					// Hit a double
582 					score = (score - 100) * 2;
583 				else
584 					// Hit a triple
585 					score = (score - 120) * 3;
586 			}
587 		} else if (score >= 100) {
588 			if (score >= 120)
589 				// Hit a double
590 				score = (2 << 16) + (score - 100);
591 			else
592 				// Hit a triple
593 				score = (3 << 16) + (score - 120);
594 		}
595 	} else {
596 		score = 0;
597 	}
598 
599 	return score;
600 }
601 
drawDartThrow(const Common::Point & dartPos,int computer)602 void Darts::drawDartThrow(const Common::Point &dartPos, int computer) {
603 	Events &events = *_vm->_events;
604 	Screen &screen = *_vm->_screen;
605 	int cx, cy;
606 	int xSize = 0, ySize = 0, oldxSize = 0, oldySize = 0;
607 	int handOCx = 0, handOCy = 0;
608 	int ocx = 0, ocy = 0;
609 	int handOldxSize, handOldySize;
610 	int delta = 9;
611 	int dartNum;
612 	int hddy;
613 	Common::Point drawPos, oldDrawPos;
614 
615 	// Draw the animation of the hand throwing the dart first
616 	// See which hand animation to use
617 	ImageFile &hands = !computer ? *_hand1 : *_hand2;
618 	int numFrames = !computer ? 14 : 13;
619 
620 	oldxSize = oldySize = handOldxSize = handOldySize = 1;
621 	cx = dartPos.x;
622 	cy = SHERLOCK_SCREEN_HEIGHT - _handSize.y - 20;
623 
624 	hddy = (cy - dartPos.y) / (numFrames - 7);
625 	hddy += 2;
626 	hddy = hddy * 10 / 8;
627 	if (dartPos.y > 275)
628 		hddy += 3;
629 
630 	for (int idx = 0; idx < numFrames; ++idx) {
631 		_handSize.x = hands[idx]._offset.x + hands[idx]._width;
632 		_handSize.y = hands[idx]._offset.y + hands[idx]._height;
633 		int handCy = SHERLOCK_SCREEN_HEIGHT - _handSize.y;
634 
635 		screen._backBuffer1.SHtransBlitFrom(hands[idx], Common::Point(_handX, handCy));
636 		screen.slamArea(_handX, handCy, _handSize.x + 1, _handSize.y);
637 		screen.slamArea(handOCx, handOCy, handOldxSize, handOldySize);
638 		screen.restoreBackground(Common::Rect(_handX, handCy, _handX + _handSize.x, handCy + _handSize.y));
639 
640 		handOCx = _handX;
641 		handOCy = handCy;
642 		handOldxSize = _handSize.x;
643 		handOldySize = _handSize.y;
644 
645 		if (idx > 6) {
646 			dartNum = idx - 6;
647 			if (computer)
648 				dartNum += 19;
649 
650 			xSize = (*_dartGraphics)[dartNum]._width;
651 			ySize = (*_dartGraphics)[dartNum]._height;
652 
653 			ocx = drawPos.x = cx - (*_dartGraphics)[dartNum]._width / 2;
654 			ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height;
655 
656 			// Draw dart
657 			screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], drawPos);
658 
659 			if (drawPos.x < 0) {
660 				xSize += drawPos.x;
661 				if (xSize < 0)
662 					xSize = 1;
663 				drawPos.x = 0;
664 			}
665 
666 			if (drawPos.y < 0) {
667 				ySize += drawPos.y;
668 				if (ySize < 0)
669 					ySize = 1;
670 				drawPos.y = 0;
671 			}
672 
673 			// Flush the drawn dart to the screen
674 			screen.slamArea(drawPos.x, drawPos.y, xSize, ySize);
675 			if (oldDrawPos.x != -1)
676 				// Flush the erased dart area
677 				screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);
678 
679 			screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
680 				Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize));
681 
682 			oldDrawPos.x = drawPos.x;
683 			oldDrawPos.y = drawPos.y;
684 			oldxSize = xSize;
685 			oldySize = ySize;
686 
687 			cy -= hddy;
688 		}
689 
690 		events.wait(1);
691 	}
692 
693 	// Clear the last little bit of the hand from the screen
694 	screen.slamArea(handOCx, handOCy, handOldxSize, handOldySize);
695 
696 	// Erase the old dart
697 	if (oldDrawPos.x != -1)
698 		screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);
699 
700 	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(drawPos.x, drawPos.y),
701 		Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize));
702 
703 	cx = dartPos.x;
704 	cy = dartPos.y + 2;
705 	oldDrawPos.x = oldDrawPos.y = -1;
706 
707 	for (int idx = 5; idx <= 23; ++idx) {
708 		dartNum = idx - 4;
709 		if (computer)
710 			dartNum += 19;
711 
712 		if (idx < 14)
713 			cy -= delta--;
714 		else
715 			if (idx == 14)
716 				delta = 1;
717 		if (idx > 14)
718 			cy += delta++;
719 
720 		xSize = (*_dartGraphics)[dartNum]._width;
721 		ySize = (*_dartGraphics)[dartNum]._height;
722 
723 		ocx = drawPos.x = cx - (*_dartGraphics)[dartNum]._width / 2;
724 		ocy = drawPos.y = cy - (*_dartGraphics)[dartNum]._height;
725 
726 		screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(drawPos.x, drawPos.y));
727 
728 		if (drawPos.x < 0) {
729 			xSize += drawPos.x;
730 			if (xSize < 0)
731 				xSize = 1;
732 			drawPos.x = 0;
733 		}
734 
735 		if (drawPos.y < 0) {
736 			ySize += drawPos.y;
737 			if (ySize < 0)
738 				ySize = 1;
739 			drawPos.y = 0;
740 		}
741 
742 		// flush the dart
743 		screen.slamArea(drawPos.x, drawPos.y, xSize, ySize);
744 		if (oldDrawPos.x != -1)
745 			screen.slamArea(oldDrawPos.x, oldDrawPos.y, oldxSize, oldySize);
746 
747 		if (idx != 23)
748 			screen._backBuffer1.SHblitFrom(screen._backBuffer2, drawPos,
749 				Common::Rect(drawPos.x, drawPos.y, drawPos.x + xSize, drawPos.y + ySize)); // erase dart
750 
751 		events.wait(1);
752 
753 		oldDrawPos = drawPos;
754 		oldxSize = xSize;
755 		oldySize = ySize;
756 	}
757 
758 	dartNum = 19;
759 	if (computer)
760 		dartNum += 19;
761 	xSize = (*_dartGraphics)[dartNum]._width;
762 	ySize = (*_dartGraphics)[dartNum]._height;
763 
764 	// Draw final dart on the board
765 	screen._backBuffer1.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
766 	screen._backBuffer2.SHtransBlitFrom((*_dartGraphics)[dartNum], Common::Point(ocx, ocy));
767 	screen.slamArea(ocx, ocy, xSize, ySize);
768 }
769 
findNumberOnBoard(int aim,Common::Point & pt)770 int Darts::findNumberOnBoard(int aim, Common::Point &pt) {
771 	ImageFrame &img = (*_dartMap)[0];
772 
773 	if ((aim > 20) && ((aim != 25) && (aim != 50))) {
774 		if ((aim <= 40) && ((aim & 1) == 0)) {
775 			aim /= 2;
776 			aim += 100;
777 		} else {
778 			aim /= 3;
779 			aim += 120;
780 		}
781 	}
782 
783 	bool done = false;
784 	for (int y = 0; y < img._width && !done; ++y) {
785 		for (int x = 0; x < img._height && !done; ++x) {
786 			byte score = *(const byte *)img._frame.getBasePtr(x, y);
787 
788 			if (score == aim) {
789 				// Found a match. Aim at non-double/triple numbers whenever possible.
790 				// ie. Aim at 18 instead of triple 6 or double 9
791 				done = true;
792 
793 				if (aim < 21) {
794 					pt.x = x + 10;
795 					pt.y = y + 10;
796 
797 					score = *(const byte *)img._frame.getBasePtr(x, y);
798 					if (score != aim)
799 						done = false;
800 				} else {
801 					// Aiming at double or triple
802 					pt.x = x + 3;
803 					pt.y = y + 3;
804 				}
805 			}
806 		}
807 	}
808 
809 	pt = convertFromScreenToScoreCoords(pt);
810 
811 	if (aim == 3)
812 		pt.y += 30;
813 	if (aim == 17)
814 		pt.y += 10;
815 
816 	if (aim == 15) {
817 		pt.y += 5;
818 		pt.x += 5;
819 	}
820 
821 	pt.y = DARTBOARD_HEIGHT - pt.y;
822 	return done;
823 }
824 
getComputerNumber(int playerNum,Common::Point & targetPos)825 void Darts::getComputerNumber(int playerNum, Common::Point &targetPos) {
826 	int score;
827 	int aim = 0;
828 	Common::Point pt;
829 	bool shootBull = false;
830 
831 	score = (playerNum == 0) ? _score1 : _score2;
832 
833 	if (_gameType == GAME_301) {
834 		// Try to hit number
835 		aim = score;
836 		if(score > 60)
837 			shootBull = true;
838 	} else {
839 		bool cricketaimset = false;
840 		if (_cricketScore[playerNum][6] < 3) {
841 			// shoot at bull first
842 			aim = CRICKET_VALUE[6];
843 			cricketaimset = true;
844 		} else {
845 			// Now check and shoot in this order: 20,19,18,17,16,15
846 			for (int idx = 0; idx < 7; ++idx) {
847 				if (_cricketScore[playerNum][idx] < 3) {
848 					aim = CRICKET_VALUE[idx];
849 					cricketaimset = true;
850 					break;
851 				}
852 			}
853 		}
854 
855 		if (!cricketaimset) {
856 			// Everything is closed
857 			// just in case we don't get set in loop below, which should never happen
858 			aim = 14;
859 			for (int idx = 0; idx < 7; ++idx) {
860 				if (_cricketScore[playerNum^1][idx] < 3) {
861 					// Opponent has this open
862 					aim = CRICKET_VALUE[idx];
863 
864 					if (idx == 6)
865 						shootBull = true;
866 				}
867 			}
868 		}
869 	}
870 
871 	if (shootBull) {
872 		// Aim at bulls eye
873 		targetPos.x = targetPos.y = 75;
874 
875 		if (_level <= 1) {
876 			if (_vm->getRandomNumber(1) == 1) {
877 				targetPos.x += (_vm->getRandomNumber(20)-10);
878 				targetPos.y += (_vm->getRandomNumber(20)-10);
879 			}
880 		}
881 	} else {
882 		// Loop in case number does not exist on board
883 		bool done = false;
884 		do {
885 			done = findNumberOnBoard(aim, pt);
886 			--aim;
887 		} while (!done);
888 
889 		pt.x += DARTBOARD_TOTALLEFT * 70 / 100;
890 		pt.y += DARTBOARD_TOTALTOP * 70 / 100;
891 
892 		// old * 3/2
893 		targetPos.x = pt.x * 100 / DARTBOARD_TOTALX * 3 / 2;
894 		targetPos.y = pt.y * 100 / DARTBOARD_TOTALY * 3 / 2;
895 	}
896 
897 	// the higher the level, the more accurate the throw
898 	int v = _vm->getRandomNumber(9);
899 	v += _level * 2;
900 
901 	if (v <= 2) {
902 		targetPos.x += _vm->getRandomNumber(70) - 35;
903 		targetPos.y += _vm->getRandomNumber(70) - 35;
904 	} else if (v <= 4) {
905 		targetPos.x += _vm->getRandomNumber(50) - 25;
906 		targetPos.y += _vm->getRandomNumber(50) - 25;
907 	} else if (v <= 6) {
908 		targetPos.x += _vm->getRandomNumber(30) - 15;
909 		targetPos.y += _vm->getRandomNumber(30) - 15;
910 	} else if (v <= 8) {
911 		targetPos.x += _vm->getRandomNumber(20) -10;
912 		targetPos.y += _vm->getRandomNumber(20) -10;
913 	} else if (v <= 10) {
914 		targetPos.x += _vm->getRandomNumber(11) - 5;
915 		targetPos.y += _vm->getRandomNumber(11) - 5;
916 	}
917 
918 	if (targetPos.x < 1)
919 		targetPos.x = 1;
920 	if (targetPos.y < 1)
921 		targetPos.y = 1;
922 }
923 
throwDart(int dartNum,int computer)924 int Darts::throwDart(int dartNum, int computer) {
925 	Events &events = *_vm->_events;
926 	Screen &screen = *_vm->_screen;
927 	int height;
928 	int horiz;
929 	Common::Point targetPos;
930 	Common::String temp;
931 
932 	/* clear keyboard buffer */
933 	events.clearEvents();
934 
935 	erasePowerBars();
936 
937 	// "Dart # x"
938 	Common::String currentDart = Common::String::format(FIXED(DartsCurrentDart), dartNum);
939 	screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s", currentDart.c_str());
940 
941 	drawDartsLeft(dartNum, computer);
942 
943 	if (!computer) {
944 		screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", FIXED(DartsStartPressKey1));
945 		screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing * 2), 0, "%s", FIXED(DartsStartPressKey2));
946 	}
947 
948 	if (!computer) {
949 		// Wait for a hit
950 		while (!dartHit() && !_vm->shouldQuit())
951 			events.wait(1);
952 		if (_escapePressed)
953 			return 0;
954 	} else {
955 		events.wait(1);
956 	}
957 
958 	drawDartsLeft(dartNum + 1, computer);
959 	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(_dartInfo.left, _dartInfo.top - 1),
960 		Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
961 	screen.SHblitFrom(screen._backBuffer1, Common::Point(_dartInfo.left, _dartInfo.top - 1),
962 		Common::Rect(_dartInfo.left, _dartInfo.top - 1, _dartInfo.right, _dartInfo.bottom - 1));
963 
964 	if (computer) {
965 		getComputerNumber(computer - 1, targetPos);
966 	} else {
967 		// Keyboard control
968 		targetPos = Common::Point(0, 0);
969 	}
970 
971 	horiz = drawHand(targetPos.x, computer);
972 	if (_escapePressed)
973 		return 0;
974 
975 	height = doPowerBar(Common::Point(DART_BAR_VX, DART_HEIGHT_Y), DART_BAR_FORE, targetPos.y, 1);
976 	if (_escapePressed)
977 		return 0;
978 
979 	// Invert height
980 	height = 101 - height;
981 
982 	// Copy power bars to the secondary back buffer
983 	screen._backBuffer2.SHblitFrom(screen._backBuffer1, Common::Point(DART_BAR_VX - 1, DART_HEIGHT_Y - 1),
984 		Common::Rect(DART_BAR_VX - 1, DART_HEIGHT_Y - 1, DART_BAR_VX - 1 + 10,
985 		DART_HEIGHT_Y - 1 + DART_BAR_SIZE + 2));
986 
987 	Common::Point dartPos(DARTBOARD_TOTALLEFT + horiz*DARTBOARD_TOTALX / 100,
988 		DARTBOARD_TOTALTOP + height * DARTBOARD_TOTALY / 100);
989 
990 	dartPos.x += 2 - _vm->getRandomNumber(4);
991 	dartPos.y += 2 - _vm->getRandomNumber(4);
992 
993 	drawDartThrow(dartPos, computer);
994 	return dartScore(dartPos);
995 }
996 
doCricketScoreHits(int player,int scoreIndex,int numHits)997 void Darts::doCricketScoreHits(int player, int scoreIndex, int numHits) {
998 	while (numHits--) {
999 		if (_cricketScore[player][scoreIndex] < 3)
1000 			_cricketScore[player][scoreIndex]++;
1001 		else if (_cricketScore[player ^ 1][scoreIndex] < 3) {
1002 			if (player == 0)
1003 				_score1 += CRICKET_VALUE[scoreIndex];
1004 			else
1005 				_score2 += CRICKET_VALUE[scoreIndex];
1006 		}
1007 	}
1008 }
1009 
updateCricketScore(int player,int dartVal,int multiplier)1010 void Darts::updateCricketScore(int player, int dartVal, int multiplier) {
1011 	if (dartVal < 15)
1012 		return;
1013 
1014 	if (dartVal <= 20)
1015 		doCricketScoreHits(player, 20 - dartVal, multiplier);
1016 	else if (dartVal == 25)
1017 		doCricketScoreHits(player, 6, multiplier);
1018 }
1019 
drawDartsLeft(int dartNum,int computer)1020 void Darts::drawDartsLeft(int dartNum, int computer) {
1021 	Screen &screen = *_vm->_screen;
1022 	const int DART_X1[3] = { 391, 451, 507 };
1023 	const int DART_Y1[3] = { 373, 373, 373 };
1024 	const int DART_X2[3] = { 393, 441, 502 };
1025 	const int DART_Y2[3] = { 373, 373, 373 };
1026 
1027 	screen._backBuffer1.SHblitFrom(screen._backBuffer2, Common::Point(DART_X1[0], DART_Y1[0]),
1028 		Common::Rect(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));
1029 
1030 	for (int idx = 2; idx >= dartNum - 1; --idx) {
1031 		if (computer)
1032 			screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx + 3], Common::Point(DART_X2[idx], DART_Y2[idx]));
1033 		else
1034 			screen._backBuffer1.SHtransBlitFrom((*_dartsLeft)[idx], Common::Point(DART_X1[idx], DART_Y1[idx]));
1035 	}
1036 
1037 	screen.slamArea(DART_X1[0], DART_Y1[0], SHERLOCK_SCREEN_WIDTH - DART_X1[0], SHERLOCK_SCREEN_HEIGHT - DART_Y1[0]);
1038 }
1039 
1040 } // End of namespace Tattoo
1041 
1042 } // End of namespace Sherlock
1043