640 lines
17 KiB
C++
640 lines
17 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 3 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, see <http://www.gnu.org/licenses/>.
|
||
*
|
||
*/
|
||
|
||
#include "common/debug.h"
|
||
#include "common/memstream.h"
|
||
|
||
#include "qdengine/qdengine.h"
|
||
#include "qdengine/minigames/adv/m_triangles.h"
|
||
#include "qdengine/minigames/adv/RunTime.h"
|
||
#include "qdengine/minigames/adv/EventManager.h"
|
||
#include "qdengine/minigames/adv/qdMath.h"
|
||
|
||
namespace QDEngine {
|
||
|
||
MinigameInterface *createMinigameTriangle(MinigameManager *runtime) {
|
||
return new MinigameTriangle(runtime);
|
||
}
|
||
|
||
enum {
|
||
EVENT_TURN,
|
||
EVENT_GET_RIGHT,
|
||
EVENT_PUT_RIGHT
|
||
};
|
||
|
||
MinigameTriangle::Node::Node(int number, int rot) {
|
||
_number = number;
|
||
_rotation = rot;
|
||
_isBack = false;
|
||
_highlight = false;
|
||
_animated = false;
|
||
_flip = 0;
|
||
}
|
||
|
||
void MinigameTriangle::Node::release(MinigameManager *runtime) {
|
||
for (auto &it : _face)
|
||
runtime->release(it);
|
||
}
|
||
|
||
bool MinigameTriangle::Node::hit(const mgVect2f &pos) const {
|
||
return obj().hit(pos);
|
||
}
|
||
|
||
MinigameTriangle::MinigameTriangle(MinigameManager *runtime) {
|
||
_runtime = runtime;
|
||
|
||
int type = 0;
|
||
if (!_runtime->getParameter("game_type", type, true))
|
||
return;
|
||
|
||
switch (type) {
|
||
case 1:
|
||
_gameType = RECTANGLE;
|
||
break;
|
||
case 2:
|
||
_gameType = HEXAGON;
|
||
break;
|
||
default:
|
||
_gameType = TRIANGLE;
|
||
}
|
||
|
||
_fieldLines = _fieldWidth = 0;
|
||
|
||
if (!_runtime->getParameter("size", _fieldLines, true))
|
||
return;
|
||
if (_fieldLines < 2)
|
||
return;
|
||
|
||
if (_gameType == RECTANGLE) {
|
||
if (!_runtime->getParameter("width", _fieldWidth, true))
|
||
return;
|
||
if (_fieldWidth < 2)
|
||
return;
|
||
}
|
||
|
||
switch (_gameType) {
|
||
case TRIANGLE:
|
||
_fieldSize = sqr(_fieldLines);
|
||
break;
|
||
case RECTANGLE:
|
||
_fieldSize = _fieldLines * _fieldWidth;
|
||
break;
|
||
case HEXAGON:
|
||
assert(_fieldLines % 2 == 0);
|
||
if (_fieldLines % 2 != 0)
|
||
return;
|
||
_fieldSize = 3 * sqr(_fieldLines) / 2;
|
||
break;
|
||
}
|
||
|
||
if (!_runtime->getParameter("animation_time", _animationTime, true))
|
||
return;
|
||
|
||
_quickReselect = _runtime->getParameter("quick_reselect", false);
|
||
|
||
const char *faceNameBegin = _runtime->parameter("object_name_begin", "obj_");
|
||
const char *backNameBegin = _runtime->parameter("backg_name_begin", "element__back");
|
||
const char *selectNameBegin = _runtime->parameter("select_name_begin", "element_select_");
|
||
|
||
char name[64];
|
||
name[63] = 0;
|
||
for (int num = 0; num < _fieldSize; ++num) {
|
||
_nodes.push_back(Node(num, 0));
|
||
Node &node = _nodes.back();
|
||
for (int angle = 1; angle <= 3; ++angle) {
|
||
snprintf(name, 63, "%s%02d_%1d", faceNameBegin, num + 1, angle);
|
||
debugC(5, kDebugMinigames, "Loading face object: %s", name);
|
||
QDObject obj = _runtime->getObject(name);
|
||
node._face.push_back(obj);
|
||
_positions.push_back(obj->R());
|
||
}
|
||
}
|
||
|
||
Common::MemoryReadWriteStream gameData(DisposeAfterUse::YES);
|
||
|
||
for (auto &it : _positions)
|
||
it.write(gameData);
|
||
|
||
if (!_runtime->processGameData(gameData))
|
||
return;
|
||
|
||
GameInfo *gameInfo = _runtime->getCurrentGameInfo();
|
||
if (gameInfo) {
|
||
Common::MemoryReadStream buf((byte *)gameInfo->_gameData, gameInfo->_dataSize);
|
||
for (auto &it : _positions)
|
||
it.read(buf);
|
||
} else {
|
||
for (auto &it : _positions)
|
||
it.read(gameData);
|
||
}
|
||
|
||
for (int num = 1; num <= 2; ++num) {
|
||
for (int angle = 1; angle <= 3; ++angle) {
|
||
snprintf(name, 63, "%s%1d_%1d", backNameBegin, num, angle);
|
||
debugC(5, kDebugMinigames, "Loading back object: %s", name);
|
||
if (!_backSides[(num - 1) * 3 + angle - 1].load(name, _runtime))
|
||
return;
|
||
}
|
||
snprintf(name, 63, "%s%1d", selectNameBegin, num);
|
||
debugC(5, kDebugMinigames, "Loading select object: %s", name);
|
||
if (!_selectBorders[num - 1].load(name, _runtime))
|
||
return;
|
||
}
|
||
|
||
_selectDepth = _nodes[0]._face[0].depth(_runtime) - 1000;
|
||
|
||
_selected = -1;
|
||
_hovered = -1;
|
||
|
||
_animationState = NO_ANIMATION;
|
||
_animatedNodes[0] = _animatedNodes[1] = -1;
|
||
_animationTimer = 0.f;
|
||
|
||
if (!_runtime->debugMode())
|
||
for (int i = 0; i < 150; ++i) {
|
||
int pos1 = _runtime->rnd(0, _nodes.size() - 1);
|
||
for (int j = 0; j < 20; ++j) {
|
||
int pos2 = _runtime->rnd(pos1 - 10, pos1 + 10);
|
||
if (compatible(pos1, pos2)) {
|
||
swapNodes(pos1, pos2, true);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int idx = 0; idx < _fieldSize; ++idx)
|
||
updateNode(_nodes[idx], idx);
|
||
|
||
setState(RUNNING);
|
||
}
|
||
|
||
MinigameTriangle::~MinigameTriangle() {
|
||
for (auto &it : _nodes)
|
||
it.release(_runtime);
|
||
|
||
for (int idx = 0; idx < 2; ++idx)
|
||
_selectBorders[idx].release(_runtime);
|
||
|
||
for (int idx = 0; idx < 6; ++idx)
|
||
_backSides[idx].release(_runtime);
|
||
}
|
||
|
||
void MinigameTriangle::Node::debugInfo() const {
|
||
debugC(5, kDebugMinigames, "name:\"%s\" state:\"%s\" number:%d rotation:%d flip:%d isBack:%d highlight:%d animated:%d", obj().getName(), obj()->current_state_name(), _number, _rotation, _flip, _isBack, _highlight, _animated);
|
||
}
|
||
|
||
const Common::String MinigameTriangle::Node::getFaceStateName(int angle, bool selected, bool animated, bool instantaneous) {
|
||
assert(!selected || !animated); // анимированные выделенными быть не могут
|
||
|
||
static const char *angleNames[3] = {"0", "120", "240"};
|
||
assert(angle >= 0 && angle < ARRAYSIZE(angleNames));
|
||
|
||
Common::String out;
|
||
|
||
out = Common::String::format("%s%s%s", (animated ? "02_" : "01_"), angleNames[angle], (selected || instantaneous ? "_sel" : ""));
|
||
return out;
|
||
}
|
||
|
||
const char *MinigameTriangle::Node::getBackStateName(bool selected, bool animated, bool instantaneous) {
|
||
assert(!selected || !animated); // анимированные выделенными быть не могут
|
||
|
||
if (animated)
|
||
return selected || instantaneous ? "02_sel" : "02";
|
||
else
|
||
return selected || instantaneous ? "01_sel" : "01";
|
||
}
|
||
|
||
const char *MinigameTriangle::Node::getBorderStateName(bool selected) {
|
||
return selected ? "01" : "02";
|
||
}
|
||
|
||
void MinigameTriangle::releaseNodeBack(Node &node) {
|
||
if (node._back) {
|
||
node._back.setState(Node::getBackStateName(false, false, false));
|
||
for (int type = 0; type < 6; ++type)
|
||
_backSides[type].releaseObject(node._back, _runtime);
|
||
}
|
||
}
|
||
|
||
void MinigameTriangle::updateNode(Node &node, int position, int flip, bool quick) {
|
||
for (auto &fit : node._face)
|
||
_runtime->hide(fit);
|
||
|
||
node._flip = flip;
|
||
|
||
if (node._isBack) {
|
||
if (!node._back)
|
||
node._back = _backSides[orientation(position) * 3 + flip].getObject();
|
||
node._back->set_R(slotCoord(position, flip));
|
||
node._back->update_screen_R();
|
||
node._back.setState(Node::getBackStateName(node._highlight, node._animated, quick));
|
||
} else {
|
||
releaseNodeBack(node);
|
||
|
||
QDObject &face = node._face[flip];
|
||
face->set_R(slotCoord(position, flip));
|
||
face->update_screen_R();
|
||
face.setState(Node::getFaceStateName(node._rotation, node._highlight, node._animated, quick).c_str());
|
||
}
|
||
}
|
||
|
||
void MinigameTriangle::highlight(int idx, bool hl) {
|
||
if (idx >= 0) {
|
||
assert(idx < (int)_nodes.size());
|
||
_nodes[idx]._highlight = hl;
|
||
updateNode(_nodes[idx], idx);
|
||
}
|
||
}
|
||
|
||
void MinigameTriangle::beginSwapNodes(int pos1, int pos2) {
|
||
assert(compatible(pos1, pos2));
|
||
|
||
if (pos1 > pos2)
|
||
SWAP(pos1, pos2);
|
||
|
||
_animationState = FIRST_PHASE;
|
||
_animationTimer = _animationTime;
|
||
|
||
_animatedNodes[0] = pos1;
|
||
_animatedNodes[1] = pos2;
|
||
|
||
Node &node1 = _nodes[pos1];
|
||
Node &node2 = _nodes[pos2];
|
||
|
||
node1._animated = true;
|
||
node2._animated = true;
|
||
|
||
releaseNodeBack(node1);
|
||
releaseNodeBack(node2);
|
||
|
||
updateNode(node1, pos1, destination(pos1, pos2));
|
||
updateNode(node2, pos2, destination(pos1, pos2));
|
||
|
||
debugC(5, kDebugMinigames, ">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 1st phase <<<<<<<<<<<<<<<<<<<<<<<<<<<<", pos1, pos2);
|
||
_nodes[pos1].debugInfo();
|
||
_nodes[pos2].debugInfo();
|
||
}
|
||
|
||
void MinigameTriangle::endSwapNodes(int pos1, int pos2) {
|
||
Node &node1 = _nodes[pos1];
|
||
Node &node2 = _nodes[pos2];
|
||
|
||
bool counted = false;
|
||
if (node1._number == pos1) { // поставили на свое место
|
||
assert(!node1._isBack);
|
||
counted = true;
|
||
_runtime->event(EVENT_PUT_RIGHT, node1.obj()->screen_R());
|
||
}
|
||
|
||
if (node2._number == pos1) { // сняли со своего места
|
||
assert(node2._isBack);
|
||
counted = true;
|
||
_runtime->event(EVENT_GET_RIGHT, node1.obj()->screen_R());
|
||
}
|
||
|
||
if (node2._number == pos2) { // поставили на свое место
|
||
assert(!node2._isBack);
|
||
counted = true;
|
||
_runtime->event(EVENT_PUT_RIGHT, node2.obj()->screen_R());
|
||
}
|
||
|
||
if (node1._number == pos2) { // сняли со своего места
|
||
assert(node1._isBack);
|
||
counted = true;
|
||
_runtime->event(EVENT_GET_RIGHT, node2.obj()->screen_R());
|
||
}
|
||
|
||
if (!counted) { // просто сделали ход
|
||
mgVect2i pos = node1.obj()->screen_R();
|
||
pos += node2.obj()->screen_R();
|
||
pos /= 2;
|
||
_runtime->event(EVENT_TURN, pos);
|
||
}
|
||
|
||
bool isWin = true;
|
||
int position = 0;
|
||
|
||
for (auto &it : _nodes) {
|
||
if (it._number != position++) {
|
||
isWin = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (isWin) {
|
||
setState(GAME_WIN);
|
||
return;
|
||
}
|
||
}
|
||
|
||
bool MinigameTriangle::animate(float dt) {
|
||
|
||
if (_animationState == NO_ANIMATION)
|
||
return false;
|
||
|
||
_animationTimer -= dt;
|
||
if (_animationTimer > 0)
|
||
return true;
|
||
|
||
Node &node1 = _nodes[_animatedNodes[0]];
|
||
Node &node2 = _nodes[_animatedNodes[1]];
|
||
|
||
switch (_animationState) {
|
||
case FIRST_PHASE: {
|
||
node1._rotation = getRotate(_animatedNodes[0], _animatedNodes[1]);
|
||
node2._rotation = getRotate(_animatedNodes[1], _animatedNodes[0]);
|
||
|
||
node1._isBack = !node1._isBack;
|
||
node2._isBack = !node2._isBack;
|
||
|
||
releaseNodeBack(node1);
|
||
releaseNodeBack(node2);
|
||
|
||
for (auto &it : node1._face)
|
||
it.setState(Node::getFaceStateName(0, false, false, false).c_str());
|
||
|
||
for (auto &it : node2._face)
|
||
it.setState(Node::getFaceStateName(0, false, false, false).c_str());
|
||
|
||
updateNode(node1, _animatedNodes[1], destination(_animatedNodes[0], _animatedNodes[1]), true);
|
||
updateNode(node2, _animatedNodes[0], destination(_animatedNodes[1], _animatedNodes[0]), true);
|
||
|
||
_animationTimer = 0.f;
|
||
_animationState = SECOND_PHASE;
|
||
|
||
debugC(5, kDebugMinigames, ">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 2nd phase 1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<", _animatedNodes[0], _animatedNodes[1]);
|
||
node1.debugInfo();
|
||
node2.debugInfo();
|
||
|
||
return true;
|
||
}
|
||
case SECOND_PHASE:
|
||
node1._animated = false;
|
||
node2._animated = false;
|
||
|
||
updateNode(node1, _animatedNodes[1], destination(_animatedNodes[0], _animatedNodes[1]));
|
||
updateNode(node2, _animatedNodes[0], destination(_animatedNodes[1], _animatedNodes[0]));
|
||
|
||
SWAP(node1, node2);
|
||
|
||
_animationTimer = _animationTime;
|
||
_animationState = FIRD_PHASE;
|
||
|
||
debugC(5, kDebugMinigames, ">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 2nd phase 2 <<<<<<<<<<<<<<<<<<<<<<<<<<<<", _animatedNodes[0], _animatedNodes[1]);
|
||
node2.debugInfo();
|
||
node1.debugInfo();
|
||
|
||
return true;
|
||
|
||
case FIRD_PHASE:
|
||
_animationTimer = 0.f;
|
||
_animationState = NO_ANIMATION;
|
||
|
||
releaseNodeBack(node1);
|
||
releaseNodeBack(node2);
|
||
|
||
updateNode(node1, _animatedNodes[0]);
|
||
updateNode(node2, _animatedNodes[1]);
|
||
|
||
endSwapNodes(_animatedNodes[0], _animatedNodes[1]);
|
||
debugC(5, kDebugMinigames, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change %d <> %d, finished ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^", _animatedNodes[0], _animatedNodes[1]);
|
||
|
||
_animatedNodes[0] = -1;
|
||
_animatedNodes[1] = -1;
|
||
|
||
return true;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void MinigameTriangle::swapNodes(int pos1, int pos2, bool silentQuick) {
|
||
if (silentQuick) {
|
||
Node &node1 = _nodes[pos1];
|
||
Node &node2 = _nodes[pos2];
|
||
|
||
node1._rotation = getRotate(pos1, pos2);
|
||
node2._rotation = getRotate(pos2, pos1);
|
||
|
||
node1._isBack = !node1._isBack;
|
||
node2._isBack = !node2._isBack;
|
||
|
||
releaseNodeBack(node1);
|
||
releaseNodeBack(node2);
|
||
|
||
SWAP(node1, node2);
|
||
|
||
updateNode(node1, pos1, 0, true);
|
||
updateNode(node2, pos2, 0, true);
|
||
} else
|
||
beginSwapNodes(pos1, pos2);
|
||
}
|
||
|
||
void MinigameTriangle::quant(float dt) {
|
||
if (_selected >= 0)
|
||
_runtime->setGameHelpVariant(0);
|
||
else
|
||
_runtime->setGameHelpVariant(1);
|
||
|
||
if (animate(dt))
|
||
return;
|
||
|
||
int mousePos = -1;
|
||
for (int idx = 0; idx < _fieldSize; ++idx)
|
||
if (_nodes[idx].hit(_runtime->mousePosition())) {
|
||
mousePos = idx;
|
||
break;
|
||
}
|
||
|
||
int startAnimation = -1;
|
||
int lastSelected = _selected;
|
||
|
||
if (_runtime->mouseLeftPressed()) {
|
||
if (mousePos < 0) // кликнули мимо - снимаем выделение
|
||
_selected = -1;
|
||
else if (_selected < 0) // ничего выделено небыло, просто выделяем
|
||
_selected = mousePos;
|
||
else if (_selected == mousePos) // кликнули на выделенном - снимаем выделение
|
||
_selected = -1;
|
||
else if (compatible(_selected, mousePos)) { // поменять фишки местами
|
||
startAnimation = _selected;
|
||
_selected = -1;
|
||
} else {
|
||
_selected = -1;
|
||
if (_quickReselect)
|
||
_selected = mousePos;
|
||
}
|
||
}
|
||
|
||
if (_selected != lastSelected) {
|
||
for (int idx = 0; idx < _fieldSize; ++idx) {
|
||
Node &node = _nodes[idx];
|
||
if (idx == _selected || compatible(_selected, idx)) { // с этой фишкой можно поменяться
|
||
if (!node._border)
|
||
node._border = _selectBorders[orientation(idx)].getObject();
|
||
node._border.setState(Node::getBorderStateName(idx == _selected));
|
||
node._border->set_R(slotCoord(idx));
|
||
node._border->update_screen_R();
|
||
_runtime->setDepth(node._border, _selectDepth);
|
||
} else if (node._border) {
|
||
_selectBorders[0].releaseObject(node._border, _runtime);
|
||
_selectBorders[1].releaseObject(node._border, _runtime);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (_hovered != mousePos || _selected != lastSelected) {
|
||
highlight(_hovered, false);
|
||
highlight(_selected >= 0 ? _selected : lastSelected, false);
|
||
|
||
_hovered = mousePos;
|
||
|
||
if (_hovered >= 0 && startAnimation < 0) {
|
||
if (_selected >= 0) {
|
||
if (compatible(_selected, _hovered)) {
|
||
highlight(_hovered, true);
|
||
highlight(_selected, true);
|
||
}
|
||
} else
|
||
highlight(_hovered, true);
|
||
}
|
||
}
|
||
|
||
if (startAnimation >= 0) {
|
||
_hovered = -1;
|
||
swapNodes(startAnimation, mousePos, false);
|
||
}
|
||
|
||
if (_runtime->mouseRightPressed() && mousePos >= 0) {
|
||
debugC(2, kDebugMinigames, "----- DUBUG INFO FOR %d POSITION --------------------", mousePos);
|
||
debugC(2, kDebugMinigames, "row = %d, begin = %d, orientation = %d", rowByNum(mousePos), rowBegin(rowByNum(mousePos)), orientation(mousePos));
|
||
_nodes[mousePos].debugInfo();
|
||
}
|
||
}
|
||
|
||
int MinigameTriangle::rowBegin(int row) const {
|
||
if (row == _fieldLines)
|
||
return _fieldSize;
|
||
|
||
switch (_gameType) {
|
||
case TRIANGLE:
|
||
return sqr(row);
|
||
case RECTANGLE:
|
||
return row * _fieldWidth;
|
||
default:
|
||
break;
|
||
}
|
||
//case HEXAGON:
|
||
assert(row >= 0 && row < _fieldLines);
|
||
if (row >= _fieldLines / 2) {
|
||
row -= _fieldLines / 2;
|
||
return _fieldSize / 2 + (2 * _fieldLines - row) * row;
|
||
}
|
||
return (_fieldLines + row) * row;
|
||
|
||
}
|
||
|
||
int MinigameTriangle::rowByNum(int num) const {
|
||
if (num >= _fieldSize)
|
||
return _fieldLines;
|
||
|
||
switch (_gameType) {
|
||
case TRIANGLE:
|
||
return floor(sqrt((float)num));
|
||
case RECTANGLE:
|
||
return num / _fieldWidth;
|
||
default:
|
||
break;
|
||
}
|
||
//case HEXAGON:
|
||
int row = num < _fieldSize / 2 ? 0 : _fieldLines / 2;
|
||
while (row < _fieldLines && num >= rowBegin(row))
|
||
++row;
|
||
return row > 0 ? row - 1 : 0;
|
||
}
|
||
|
||
int MinigameTriangle::orientation(int num) const {
|
||
switch (_gameType) {
|
||
case TRIANGLE:
|
||
return (rowByNum(num) + num) % 2;
|
||
case RECTANGLE:
|
||
return num % 2;
|
||
default:
|
||
break;
|
||
}
|
||
//case HEXAGON:
|
||
return (num + rowByNum(num) + (num >= _fieldSize / 2 ? 1 : 0)) % 2;
|
||
}
|
||
|
||
bool MinigameTriangle::compatible(int num1, int num2) const {
|
||
if (num1 > num2)
|
||
SWAP(num1, num2);
|
||
|
||
if (num1 < 0)
|
||
return false;
|
||
|
||
int row1 = rowByNum(num1);
|
||
int row2 = rowByNum(num2);
|
||
|
||
if (row2 >= _fieldLines)
|
||
return false;
|
||
|
||
if (row1 == row2) // в одном слое
|
||
return num2 - num1 == 1; // должны быть рядом
|
||
else if (row2 - row1 != 1) // или на соседних слоях
|
||
return false;
|
||
else if (orientation(num1) != 0) // широкими сторонами друг к другу
|
||
return false;
|
||
|
||
int center1 = (rowBegin(row1) + rowBegin(row1 + 1) - 1) / 2;
|
||
int center2 = (rowBegin(row2) + rowBegin(row2 + 1) - 1) / 2;
|
||
|
||
return center1 - num1 == center2 - num2; // и точно друг под другом
|
||
}
|
||
|
||
int MinigameTriangle::getRotate(int num1, int num2) const {
|
||
static int solves[3][2][3] = {
|
||
{{0, 2, 1}, {0, 2, 1}},
|
||
{{2, 1, 0}, {1, 0, 2}},
|
||
{{1, 0, 2}, {2, 1, 0}}
|
||
};
|
||
assert(compatible(num1, num2));
|
||
return solves[rowByNum(num1) != rowByNum(num2) ? 0 : (num2 < num1 ? 1 : 2)]
|
||
[orientation(num1)][_nodes[num1]._rotation];
|
||
}
|
||
|
||
int MinigameTriangle::destination(int num1, int num2) const {
|
||
if (orientation(num1) == 0)
|
||
return rowByNum(num1) != rowByNum(num2) ? 0 : (num2 < num1 ? 1 : 2);
|
||
else
|
||
return rowByNum(num1) != rowByNum(num2) ? 0 : (num2 < num1 ? 2 : 1);
|
||
}
|
||
|
||
mgVect3f MinigameTriangle::slotCoord(int pos, int angle) const {
|
||
assert(pos * 3 + angle < (int)_positions.size());
|
||
return _positions[pos * 3 + angle];
|
||
}
|
||
|
||
} // namespace QDEngine
|