scummvm/engines/asylum/puzzles/tictactoe.cpp
Littleboy b9cb91d016
ASYLUM: Cleanup puzzles
- Add missing casts
 - Remove unused parameters
 - Add consts modifiers
 - Fix formating
 - etc.
2021-05-17 15:37:15 +02:00

591 lines
16 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 "asylum/puzzles/tictactoe.h"
#include "asylum/resources/worldstats.h"
#include "asylum/system/cursor.h"
#include "asylum/system/screen.h"
#include "asylum/views/scene.h"
#include "asylum/asylum.h"
namespace Asylum {
extern int g_debugPolygons;
const Common::Point puzzleTicTacToePolygons[36] = {
Common::Point( 27, 381), Common::Point(172, 368), Common::Point(190, 474), Common::Point( 36, 476),
Common::Point(176, 362), Common::Point(294, 328), Common::Point(331, 456), Common::Point(191, 472),
Common::Point(304, 327), Common::Point(426, 306), Common::Point(457, 440), Common::Point(340, 460),
Common::Point( 26, 257), Common::Point(151, 238), Common::Point(169, 356), Common::Point( 27, 373),
Common::Point(162, 234), Common::Point(275, 214), Common::Point(299, 321), Common::Point(173, 355),
Common::Point(283, 210), Common::Point(403, 173), Common::Point(437, 294), Common::Point(305, 317),
Common::Point( 22, 120), Common::Point(132, 126), Common::Point(146, 223), Common::Point( 25, 247),
Common::Point(144, 119), Common::Point(247, 87), Common::Point(268, 205), Common::Point(159, 222),
Common::Point(259, 84), Common::Point(380, 73), Common::Point(405, 169), Common::Point(281, 201)
};
const Common::Point puzzleTicTacToePositions[9] = {
Common::Point( 62, 367),
Common::Point(193, 343),
Common::Point(329, 319),
Common::Point( 38, 247),
Common::Point(167, 217),
Common::Point(296, 193),
Common::Point( 19, 121),
Common::Point(147, 98),
Common::Point(269, 70)
};
static const struct {
uint32 field1;
uint32 field2;
uint32 field3;
uint32 strikeOutPositionX;
uint32 strikeOutPositionO;
uint32 frameCount;
} puzzleTicTacToeFieldsToCheck[8] = {
{0, 1, 2, 1, 9, 14},
{3, 4, 5, 2, 10, 14},
{6, 7, 8, 3, 11, 14},
{0, 3, 6, 4, 12, 10},
{8, 5, 2, 6, 14, 10},
{0, 4, 8, 8, 16, 4},
{4, 1, 7, 5, 13, 10},
{4, 6, 2, 7, 15, 4}
};
PuzzleTicTacToe::PuzzleTicTacToe(AsylumEngine *engine) : Puzzle(engine) {
_ticker = 0;
_frameIndex = 0;
_frameCount = 0;
_lastMarkedField = 0;
_needToInitialize = false;
_strikeOutPosition = 0;
// Field
memset(&_gameField, 0, sizeof(_gameField));
memset(&_field, 0, sizeof(_field));
_emptyCount = 0;
_counter = 0;
_counter2 = 0;
}
PuzzleTicTacToe::~PuzzleTicTacToe() {
}
//////////////////////////////////////////////////////////////////////////
// Event Handling
//////////////////////////////////////////////////////////////////////////
bool PuzzleTicTacToe::init(const AsylumEvent &) {
_ticker = 0;
_vm->clearGameFlag(kGameFlag114);
_vm->clearGameFlag(kGameFlag215);
_frameIndex = 0;
_lastMarkedField = -1;
_needToInitialize = false;
_strikeOutPosition = -1;
getScreen()->setPalette(getWorld()->graphicResourceIds[3]);
getScreen()->setGammaLevel(getWorld()->graphicResourceIds[3]);
getCursor()->show();
getCursor()->set(getWorld()->graphicResourceIds[12], 4, kCursorAnimationLinear, 4);
initField();
return true;
}
bool PuzzleTicTacToe::update(const AsylumEvent &) {
if (_ticker) {
++_ticker;
if (_ticker <= 25) {
if (_ticker > 20) {
if (check())
placeOpponentMark();
_ticker = 0;
}
} else {
if (_ticker > 40) {
getSound()->playSound(getWorld()->soundResourceIds[13], false, Config.sfxVolume - 100);
_ticker = 0;
}
}
}
getScreen()->draw(getWorld()->graphicResourceIds[0]);
drawField();
getScene()->updateAmbientSounds();
getScreen()->copyBackBufferToScreen();
return true;
}
bool PuzzleTicTacToe::mouseLeftDown(const AsylumEvent &evt) {
if (!_vm->isGameFlagNotSet(kGameFlag215) || !_vm->isGameFlagNotSet(kGameFlag114)) {
getCursor()->show();
exitPuzzle();
return true;
}
if (_needToInitialize) {
_needToInitialize = false;
_frameIndex = 0;
_lastMarkedField = -1;
_strikeOutPosition = -1;
initField();
return true;
}
for (uint32 i = 0; i < ARRAYSIZE(_gameField); i++) {
if (hitTest(&puzzleTicTacToePolygons[i * 4], evt.mouse, 0)) {
if (_gameField[i] == ' ') {
getSound()->playSound(getWorld()->soundResourceIds[11], false, Config.sfxVolume - 100);
_gameField[i] = 'X';
_lastMarkedField = i;
_frameIndex = 0;
getCursor()->hide();
}
}
}
return true;
}
bool PuzzleTicTacToe::mouseRightDown(const AsylumEvent &) {
exitPuzzle();
return true;
}
//////////////////////////////////////////////////////////////////////////
// Init & update
//////////////////////////////////////////////////////////////////////////
void PuzzleTicTacToe::initField() {
_counter2 = 0;
memset(&_gameField, 32, sizeof(_gameField)); // ' ' == 32
}
void PuzzleTicTacToe::drawField() {
if (_counter > 0) {
--_counter;
if (_counter < 2) {
getCursor()->show();
exitPuzzle();
_counter = 0;
return;
}
}
if (g_debugPolygons) {
for (uint32 p = 0; p < ARRAYSIZE(puzzleTicTacToePolygons) - 4; p += 4) {
getScreen()->drawLine(puzzleTicTacToePolygons[p], puzzleTicTacToePolygons[p + 1]);
getScreen()->drawLine(puzzleTicTacToePolygons[p + 1], puzzleTicTacToePolygons[p + 2]);
getScreen()->drawLine(puzzleTicTacToePolygons[p + 2], puzzleTicTacToePolygons[p + 3]);
getScreen()->drawLine(puzzleTicTacToePolygons[p + 3], puzzleTicTacToePolygons[p]);
}
}
// Draw X & O
for (int32 i = 0; i < ARRAYSIZE(puzzleTicTacToePositions); i++) {
char mark = _gameField[i];
Common::Point point = puzzleTicTacToePositions[i];
if (_lastMarkedField == i) {
if (mark == 'O')
getScreen()->draw(getWorld()->graphicResourceIds[2], _frameIndex, point);
else if (mark == 'X')
getScreen()->draw(getWorld()->graphicResourceIds[1], _frameIndex, point);
// Update _frameIndex
++_frameIndex;
if (_frameIndex > 14 && mark == 'X') {
_lastMarkedField = -1;
_frameIndex = 0;
_ticker = 1;
if (checkWinner())
_needToInitialize = true;
}
if (_frameIndex > 12 && mark == 'O') {
_lastMarkedField = -1;
_frameIndex = 0;
if (!checkWinner() || !checkWinnerHelper())
getCursor()->show();
}
} else {
if (mark == 'O')
getScreen()->draw(getWorld()->graphicResourceIds[2], 12, point);
else if (mark == 'X')
getScreen()->draw(getWorld()->graphicResourceIds[1], 14, point);
}
}
if (_lastMarkedField == -1 && checkWinner())
_needToInitialize = true;
// Draw strikeOut
if (_strikeOutPosition > 0 && !_ticker) {
switch (_strikeOutPosition) {
default:
break;
case 1:
getScreen()->draw(getWorld()->graphicResourceIds[5], _frameIndex, Common::Point(38, 345));
break;
case 2:
getScreen()->draw(getWorld()->graphicResourceIds[5], _frameIndex, Common::Point(17, 226));
break;
case 3:
getScreen()->draw(getWorld()->graphicResourceIds[5], _frameIndex, Common::Point(1, 104));
break;
case 4:
getScreen()->draw(getWorld()->graphicResourceIds[6], _frameIndex, Common::Point(43, 117));
break;
case 5:
getScreen()->draw(getWorld()->graphicResourceIds[6], _frameIndex, Common::Point(176, 104));
break;
case 6:
getScreen()->draw(getWorld()->graphicResourceIds[6], _frameIndex, Common::Point(299, 85));
break;
case 7:
if (_counter2 == 0)
getScreen()->draw(getWorld()->graphicResourceIds[8], _frameIndex, Common::Point(30, 149));
else if (_counter2 == 1)
getScreen()->draw(getWorld()->graphicResourceIds[8], _frameIndex, Common::Point(180, 249));
else {
getScreen()->draw(getWorld()->graphicResourceIds[8], 6, Common::Point(30, 149));
getScreen()->draw(getWorld()->graphicResourceIds[8], 6, Common::Point(180, 249));
getScreen()->draw(getWorld()->graphicResourceIds[8], _frameIndex, Common::Point(330, 349));
}
break;
case 8:
if (_counter2 == 0)
getScreen()->draw(getWorld()->graphicResourceIds[10], _frameIndex, Common::Point(69, 66));
else if (_counter2 == 1)
getScreen()->draw(getWorld()->graphicResourceIds[10], _frameIndex, Common::Point(-22, 220));
else {
getScreen()->draw(getWorld()->graphicResourceIds[10], 6, Common::Point( 69, 66));
getScreen()->draw(getWorld()->graphicResourceIds[10], 6, Common::Point(-22, 220));
getScreen()->draw(getWorld()->graphicResourceIds[10], _frameIndex, Common::Point(-110, 370));
}
break;
case 9:
getScreen()->draw(getWorld()->graphicResourceIds[4], _frameIndex, Common::Point(38, 345));
break;
case 10:
getScreen()->draw(getWorld()->graphicResourceIds[4], _frameIndex, Common::Point(17, 226));
break;
case 11:
getScreen()->draw(getWorld()->graphicResourceIds[4], _frameIndex, Common::Point(1, 104));
break;
case 12:
getScreen()->draw(getWorld()->graphicResourceIds[11], _frameIndex, Common::Point(43, 117));
break;
case 13:
getScreen()->draw(getWorld()->graphicResourceIds[11], _frameIndex, Common::Point(176, 104));
break;
case 14:
getScreen()->draw(getWorld()->graphicResourceIds[11], _frameIndex, Common::Point(299, 85));
break;
case 15:
if (_counter2 == 0)
getScreen()->draw(getWorld()->graphicResourceIds[7], _frameIndex, Common::Point(30, 149));
else if (_counter2 == 1)
getScreen()->draw(getWorld()->graphicResourceIds[7], _frameIndex, Common::Point(180, 249));
else {
getScreen()->draw(getWorld()->graphicResourceIds[7], 6, Common::Point(30, 149));
getScreen()->draw(getWorld()->graphicResourceIds[7], 6, Common::Point(180, 249));
getScreen()->draw(getWorld()->graphicResourceIds[7], _frameIndex, Common::Point(330, 349));
}
break;
case 16:
if (_counter2 == 0)
getScreen()->draw(getWorld()->graphicResourceIds[9], _frameIndex, Common::Point(69, 66));
else if (_counter2 == 1)
getScreen()->draw(getWorld()->graphicResourceIds[9], _frameIndex, Common::Point(-22, 220));
else {
getScreen()->draw(getWorld()->graphicResourceIds[9], 6, Common::Point(69, 66));
getScreen()->draw(getWorld()->graphicResourceIds[9], 6, Common::Point(-22, 220));
getScreen()->draw(getWorld()->graphicResourceIds[9], _frameIndex, Common::Point(-110, 370));
}
break;
}
if (_frameIndex >= _frameCount) {
if (_strikeOutPosition == 7 || _strikeOutPosition == 8 || _strikeOutPosition == 15 || _strikeOutPosition == 16) {
if (_counter2 < 2) {
_frameIndex = 0;
++_counter2;
}
}
} else {
++_frameIndex;
}
if (!_counter)
_counter = 30;
}
getScreen()->draw(getWorld()->graphicResourceIds[17], 0, Common::Point(0, 0));
}
void PuzzleTicTacToe::updatePositions(uint32 field1, uint32 field2, uint32 field3) {
if (_gameField[field1] != ' ') {
_field[_emptyCount] = field3;
_field[_emptyCount + 1] = field2;
_emptyCount += 2;
}
if (_gameField[field3] != ' ') {
_field[_emptyCount] = field1;
_field[_emptyCount + 1] = field2;
_emptyCount += 2;
}
if (_gameField[field2] != ' ') {
_field[_emptyCount] = field3;
_field[_emptyCount + 1] = field1;
_emptyCount += 2;
}
}
//////////////////////////////////////////////////////////////////////////
// Game
//////////////////////////////////////////////////////////////////////////
bool PuzzleTicTacToe::check() {
if (_needToInitialize)
return false;
if (!checkWinning('X')
&& !checkWinning('O')
&& !checkFieldsUpdatePositions()
&& !checkFields()
&& !countEmptyFields()) {
if (!_counter)
getCursor()->show();
_needToInitialize = true;
return false;
}
return true;
}
PuzzleTicTacToe::GameStatus PuzzleTicTacToe::checkField(uint32 field1, uint32 field2, uint32 field3, char mark, uint32 *counterX, uint32 *counterO) const {
*counterX = 0;
*counterO = 0;
GameStatus status = kStatus0;
if (_gameField[field1] == 'X')
++*counterX;
if (_gameField[field2] == 'X')
++*counterX;
if (_gameField[field3] == 'X')
++*counterX;
if (_gameField[field1] == 'O')
++*counterO;
if (_gameField[field2] == 'O')
++*counterO;
if (_gameField[field3] == 'O')
++*counterO;
if (mark == 'O') {
if (*counterO == 1 && !*counterX)
status = kStatusFree;
} else if (mark == 'X') {
if (!*counterO && *counterX == 1)
status = kStatusFree;
}
if (mark == 'O') {
if (!*counterO && *counterX == 2)
status = kStatusNeedBlocking;
} else if (mark == 'X') {
if (*counterO == 2 && !*counterX)
status = kStatusNeedBlocking;
}
return status;
}
bool PuzzleTicTacToe::checkFieldsUpdatePositions() {
uint32 counterX = 0;
uint32 counterO = 0;
for (uint32 i = 0; i < ARRAYSIZE(puzzleTicTacToeFieldsToCheck); i++)
if (checkField(puzzleTicTacToeFieldsToCheck[i].field1, puzzleTicTacToeFieldsToCheck[i].field2, puzzleTicTacToeFieldsToCheck[i].field3, 'O', &counterX, &counterO) == kStatusFree)
updatePositions(puzzleTicTacToeFieldsToCheck[i].field1, puzzleTicTacToeFieldsToCheck[i].field2, puzzleTicTacToeFieldsToCheck[i].field3);
return (_emptyCount != 0);
}
bool PuzzleTicTacToe::checkFields() {
uint32 counterX = 0;
uint32 counterO = 0;
for (uint32 i = 0; i < ARRAYSIZE(puzzleTicTacToeFieldsToCheck); i++) {
checkField(puzzleTicTacToeFieldsToCheck[i].field1, puzzleTicTacToeFieldsToCheck[i].field2, puzzleTicTacToeFieldsToCheck[i].field3, 'O', &counterX, &counterO);
if (counterX || counterO)
continue;
_field[_emptyCount] = puzzleTicTacToeFieldsToCheck[i].field1;
_field[_emptyCount + 1] = puzzleTicTacToeFieldsToCheck[i].field2;
_field[_emptyCount + 2] = puzzleTicTacToeFieldsToCheck[i].field3;
_emptyCount += 3;
}
return (_emptyCount != 0);
}
uint32 PuzzleTicTacToe::checkPosition(uint32 position1, uint32 position2, uint position3) const {
if (_gameField[position1] == ' ')
return position1;
if (_gameField[position2] == ' ')
return position2;
return position3;
}
bool PuzzleTicTacToe::checkWinner() {
if (_needToInitialize)
return true;
if (checkWinnerHelper() == 1) {
_vm->setGameFlag(kGameFlag114);
_ticker = 30;
return true;
}
if (checkWinnerHelper() == -1) {
_vm->setGameFlag(kGameFlag215);
_ticker = 30;
return true;
}
return false;
}
int32 PuzzleTicTacToe::checkWinnerHelper() {
uint32 counterX = 0;
uint32 counterO = 0;
for (uint32 i = 0; i < ARRAYSIZE(puzzleTicTacToeFieldsToCheck); i++) {
checkField(puzzleTicTacToeFieldsToCheck[i].field1, puzzleTicTacToeFieldsToCheck[i].field2, puzzleTicTacToeFieldsToCheck[i].field3, 'O', &counterX, &counterO);
if (counterX == 3) {
_strikeOutPosition = puzzleTicTacToeFieldsToCheck[i].strikeOutPositionX;
_frameCount = puzzleTicTacToeFieldsToCheck[i].frameCount;
_frameIndex = 0;
return 1;
}
if (counterO == 3) {
_strikeOutPosition = puzzleTicTacToeFieldsToCheck[i].strikeOutPositionO;
_frameCount = puzzleTicTacToeFieldsToCheck[i].frameCount;
_frameIndex = 0;
return -1;
}
}
return 0;
}
bool PuzzleTicTacToe::checkWinning(char mark) {
uint32 counterX = 0;
uint32 counterO = 0;
_emptyCount = 0;
for (uint32 i = 0; i < ARRAYSIZE(puzzleTicTacToeFieldsToCheck); i++) {
if (checkField(puzzleTicTacToeFieldsToCheck[i].field1, puzzleTicTacToeFieldsToCheck[i].field2, puzzleTicTacToeFieldsToCheck[i].field3, mark, &counterX, &counterO) == kStatusNeedBlocking) {
_field[_emptyCount] = checkPosition(puzzleTicTacToeFieldsToCheck[i].field1, puzzleTicTacToeFieldsToCheck[i].field2, puzzleTicTacToeFieldsToCheck[i].field3);
++_emptyCount;
}
}
return (_emptyCount != 0);
}
bool PuzzleTicTacToe::countEmptyFields() {
_emptyCount = 0;
for (uint32 i = 0; i < ARRAYSIZE(_gameField); i++) {
if (_gameField[i] == ' ') {
_field[i] = i;
++_emptyCount;
}
}
return (_emptyCount != 0);
}
void PuzzleTicTacToe::placeOpponentMark() {
_frameIndex = 0;
_lastMarkedField = _field[rnd(_emptyCount - 1)];
if (_gameField[_lastMarkedField] != ' ')
error("[PuzzleTicTacToe::placeOpponentMark] Field is already occupied (%d)!", _lastMarkedField);
_gameField[_lastMarkedField] = 'O';
getSound()->playSound(getWorld()->soundResourceIds[12], false, Config.sfxVolume - 100);
}
} // End of namespace Asylum