/* 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 .
*
*/
#include "common/debug.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/savefile.h"
#include "qdengine/qdengine.h"
#include "qdengine/minigames/adv/common.h"
#include "qdengine/minigames/adv/qdMath.h"
#include "qdengine/minigames/adv/RunTime.h"
#include "qdengine/minigames/adv/HoldData.h"
#include "qdengine/system/input/keyboard_input.h"
#include "qdengine/minigames/adv/TextManager.h"
#include "qdengine/minigames/adv/EventManager.h"
#include "qdengine/minigames/adv/EffectManager.h"
#include "qdengine/minigames/adv/MinigameInterface.h"
namespace QDEngine {
MinigameManager *g_runtime = 0;
class TimeManager {
enum Direction {
UP,
LEFT,
RIGHT,
DOWN
};
public:
TimeManager(HoldData &data, MinigameManager *runtime);
~TimeManager();
bool timeIsOut() const;
float leftTime() const;
float timeCost() const {
return _timeCost;
}
void quant(float dt);
private:
float _gameTime;
float _timeCost;
int _lastEventTime;
mgVect3f _startPos;
mgVect2f _size;
Direction _direction;
QDObject _timeBar;
MinigameManager *_runtime;
};
MinigameManager::MinigameManager(MinigameConsCallback callback)
: _currentGameIndex(-1, -1) {
_state_container_name = Common::String::format("%s.min", g_engine->getTargetName().c_str());
_engine = 0;
_scene = 0;
_timeManager = 0;
_textManager = 0;
_eventManager = 0;
_effectManager = 0;
_state_flag = 0;
_pause_flag = 0;
_complete_help = 0;
_complete_help_miniature = 0;
_game_help = 0;
_game_help_trigger = 0;
_game_help_enabled = true;
_game = 0;
_gameTime = 0;
_currentGameInfo = 0;
_invertMouseButtons = false;
_debugMode = false;
_seed = 0;
for (int idx = 0; idx < 256; ++idx)
_lastKeyChecked[idx] = false;
_callback = callback;
}
MinigameManager::~MinigameManager() {
assert(!_engine && !_scene);
for (auto &it : _gameInfos) {
debugC(5, kDebugMinigames, "~MinigameManager(): free: (%d,%d)", it._key._gameLevel, it._key._gameNum);
it._value.free();
}
}
bool MinigameManager::init(const qdEngineInterface *engine_interface) {
debugC(1, kDebugMinigames, "MinigameManager::init(): init game");
if (g_runtime != this)
warning("MinigameManager::init(): Attempt to instantiate double minigame");
if (g_runtime != this)
return false;
assert(!_engine && !_scene);
assert(engine_interface);
if (!engine_interface)
return false;
_engine = engine_interface;
_scene = _engine->current_scene_interface();
assert(_scene);
if (!_scene) {
_engine = 0;
return false;
}
if (!createGame()) {
warning("MinigameManager::init(): Game could not be initialized");
finit();
return false;
}
saveState();
return true;
}
bool MinigameManager::createGame() {
assert(_engine && _scene);
assert(!_game);
_screenSize = _engine->screen_size();
#ifdef _DEBUG
_debugMode = getParameter("debug_mode", false);
#endif
_seed = 0;
if (!loadState())
return false;
if (_currentGameInfo) {
debugC(2, kDebugMinigames, "MinigameManager::createGame(): level: %d, game: %d, index: %d", _currentGameIndex._gameLevel, _currentGameIndex._gameNum, _currentGameInfo->_game._sequenceIndex);
debugC(2, kDebugMinigames, "MinigameManager::createGame(): %s", _currentGameInfo->_game._sequenceIndex == -1 ? "FIRST TIME PLAY" : "RePlay game");
}
int s = getParameter("random_seed", -1);
_seed = _debugMode ? 0 : (s >= 0 ? s : _seed);
_engine->rnd_init(_seed);
debugC(2, kDebugMinigames, "MinigameManager::createGame(): seed = %d", _seed);
_invertMouseButtons = getParameter("invert_mouse_buttons", false);
_mouseAdjast = getParameter("ajast_mouse", mgVect2f());
HoldData timeData(_currentGameInfo ? &_currentGameInfo->_timeManagerData : 0, !_currentGameInfo || _currentGameInfo->_empty);
_timeManager = new TimeManager(timeData, this);
_textManager = new TextManager(this);
_eventManager = new EventManager(this);
HoldData effectData(_currentGameInfo ? &_currentGameInfo->_effectManagerData : 0, !_currentGameInfo || _currentGameInfo->_empty);
_effectManager = new EffectManager(effectData, this);
const char *stateFlagName = parameter("_state_flagname", "state_flag");
if ((_state_flag = _scene->object_interface(stateFlagName))) {
if (!_state_flag->has_state("game") || !_state_flag->has_state("win") || !_state_flag->has_state("lose")) {
warning("MinigameManager::createGame(): The object %s must have state: game, win, lose", transCyrillic(stateFlagName));
return false;
}
} else {
warning("MinigameManager::createGame(): Object '%s' for state flag is missing", transCyrillic(stateFlagName));
return false;
}
const char *pauseFlagName = parameter("_pause_flagname", "BackHelp");
if ((_pause_flag = _scene->object_interface(pauseFlagName))) {
if (!_pause_flag->has_state("on")) {
warning("MinigameManager::createGame(): The object %s must have state: on", transCyrillic(pauseFlagName));
return false;
}
}
_complete_help_state_name = "01";
if (testObject(parameter("complete_help_miniatute", "miniature"))) {
_complete_help_miniature = getObject(parameter("complete_help_miniatute", "miniature"));
if ((_complete_help = getObject(parameter("complete_help", "complete")))) {
if (!_complete_help->has_state("off") || !_complete_help->has_state("01")) {
warning("MinigameManager::createGame(): The object for completed game must have state: off, 01");
return false;
}
} else {
warning("MinigameManager::createGame(): Object completed game is missing");
return false;
}
}
_game_help_state_name = "off";
if (testObject(parameter("tips_object", "tips"))) {
_game_help = getObject(parameter("tips_object", "tips"));
_game_help.setState(_game_help_state_name.c_str());
}
if (testObject(parameter("tips_switcher", "tips_button"))) {
_game_help_trigger = getObject(parameter("tips_switcher", "tips_button"));
_game_help_trigger.setState(_game_help_enabled ? "01" : "02");
}
// Here we instantiate the specific game
_game = _callback(this);
if (_currentGameInfo)
_currentGameInfo->_empty = false;
if (_game && _game->state() != MinigameInterface::NOT_INITED) {
_textManager->updateScore(_eventManager->score());
_state_flag->set_state("game");
return true;
}
return false;
}
#define SAFE_RELEASE(name) \
if (name) { \
_scene->release_object_interface(name); \
name = 0; \
}
bool MinigameManager::finit() {
debugC(2, kDebugMinigames, "MinigameManager::finit(): finit game");
if (!_engine)
return false;
delete _game;
_game = 0;
delete _effectManager;
_effectManager = 0;
delete _eventManager;
_eventManager = 0;
delete _textManager;
_textManager = 0;
delete _timeManager;
_timeManager = 0;
if (_scene) {
SAFE_RELEASE(_state_flag)
SAFE_RELEASE(_pause_flag)
release(_complete_help_miniature);
release(_complete_help);
release(_game_help);
release(_game_help_trigger);
}
_game_help_enabled = true;
_complete_help_state_name.clear();
_game_help_state_name.clear();
_completeCounters.clear();
_currentGameInfo = 0;
_currentGameIndex = GameInfoIndex(-1, -1);
_gameInfos.clear();
_seed = 0;
_debugMode = false;
_invertMouseButtons = false;
_mouseAdjast = mgVect2f();
if (_scene) {
_engine->release_scene_interface(_scene);
_scene = 0;
}
_gameTime = 0;
_engine = 0;
return true;
}
#undef SAFE_RELEASE
bool MinigameManager::new_game(const qdEngineInterface *engine_interface) {
if (!loadState(false)) {
debugC(2, kDebugMinigames, "MinigameManager::new_game(): new game skiped");
return false;
}
debugC(2, kDebugMinigames, "MinigameManager::new_game(): new game");
for (auto &it : _gameInfos) {
debugC(3, kDebugMinigames, "MinigameManager::new_game(): clean game data (%d, %d)", it._key._gameLevel, it._key._gameNum);
it._value._game = MinigameData();
}
saveState(true);
return true;
}
class TempValue {
const qdEngineInterface *_pre_engine;
qdMinigameSceneInterface *_pre_scene;
MinigameManager *_pre_runtime;
public:
TempValue(MinigameManager *new_runtime, const qdEngineInterface *new_engine, qdMinigameSceneInterface *new_scene) {
assert(new_runtime);
_pre_runtime = g_runtime;
g_runtime = new_runtime;
assert(new_engine && new_scene);
_pre_engine = g_runtime->_engine;
_pre_scene = g_runtime->_scene;
g_runtime->_engine = new_engine;
g_runtime->_scene = new_scene;
}
~TempValue() {
g_runtime->_engine = _pre_engine;
g_runtime->_scene = _pre_scene;
g_runtime = _pre_runtime;
}
};
#define TEMP_scene_ENTER() TempValue tempSceneObject(this, engine, const_cast(scene))
int MinigameManager::save_game(const qdEngineInterface *engine, const qdMinigameSceneInterface *scene, char *buffer, int buffer_size) {
debugC(2, kDebugMinigames, "MinigameManager::save_game(): save game");
TEMP_scene_ENTER();
loadState();
if (_currentGameInfo && !_currentGameInfo->empty()) {
debugC(2, kDebugMinigames, "MinigameManager::save_game(): save game (%d, %d)", _currentGameIndex._gameLevel, _currentGameIndex._gameNum);
Common::MemoryWriteStream out((byte *)buffer, buffer_size);
out.writeUint32LE(GameInfo::version());
_currentGameInfo->_game.write(out);
return out.pos();
}
return 0;
}
int MinigameManager::load_game(const qdEngineInterface *engine, const qdMinigameSceneInterface *scene, const char *buffer, int buffer_size) {
assert(!_game);
if (_game) {
debugC(2, kDebugMinigames, "MinigameManager::load_game(): load game skiped");
return buffer_size;
}
debugC(2, kDebugMinigames, "MinigameManager::load_game(): load game");
TEMP_scene_ENTER();
loadState();
if (_currentGameInfo) {
if (buffer_size > 0) {
debugC(2, kDebugMinigames, "MinigameManager::load_game(): load game (%d, %d)", _currentGameIndex._gameLevel, _currentGameIndex._gameNum);
Common::MemoryReadStream in((const byte *)buffer, buffer_size);
int version;
version = in.readUint32LE();
if (version == GameInfo::version()) {
_currentGameInfo->_game.read(in);
if (_currentGameInfo->_empty)
warning("MinigameManager::load_game(): Attempt to load minigame without a scene");
if (in.pos() != buffer_size) {
_currentGameInfo->_game = MinigameData();
warning("MinigameManager::load_game(): Data size mismatch");
return 0;
}
} else {
warning("MinigameManager::load_game(): Incompatible savegame version for minigame");
return 0;
}
} else {
debugC(2, kDebugMinigames, "MinigameManager::load_game(): clean game (%d, %d)", _currentGameIndex._gameLevel, _currentGameIndex._gameNum);
_currentGameInfo->_game = MinigameData();
}
saveState();
}
return buffer_size;
}
bool MinigameManager::loadState(bool current) {
if (_game) {
debugC(2, kDebugMinigames, "MinigameManager::loadState(): load state skiped");
return false;
}
debugC(2, kDebugMinigames, "MinigameManager::loadState(): load state");
if (current) {
int gameNumber = getParameter("game_number", -1);
int gameLevel = -1;
if (gameNumber >= 0)
if (!getParameter("game_level", gameLevel, true))
return false;
_currentGameIndex = GameInfoIndex(gameNumber, gameLevel);
} else
_currentGameIndex = GameInfoIndex(-1, -1);
if (!current || _currentGameIndex._gameNum >= 0) {
if (current)
debugC(2, kDebugMinigames, "MinigameManager::loadState(): current game: (%d,%d)", _currentGameIndex._gameLevel, _currentGameIndex._gameNum);
Common::InSaveFile *file = g_engine->getSaveFileManager()->openForLoading(_state_container_name);
if (file) {
int version = file->readUint32LE();
if (version != GameInfo::version()) {
warning("MinigameManager::loadState(): Minigame savestate version mismatch. Remove '%s'", _state_container_name.c_str());
delete file;
return false;
}
_seed = file->readUint32LE();
GameInfoIndex index(0, 0);
while (!file->eos() && file->pos() < file->size()) {
index.read(*file);
if (file->eos() || file->pos() >= file->size()) {
delete file;
return false;
}
GameInfo data;
data.read(*file);
debugC(2, kDebugMinigames, "MinigameManager::loadState(): read game info: (%d,%d), index: %d, game data:%d", index._gameLevel, index._gameNum, data._game._sequenceIndex, data._empty ? 0 : 1);
if (data._game._sequenceIndex >= 0)
_completeCounters[index._gameLevel]++;
_gameInfos[index] = data;
}
delete file;
}
_currentGameInfo = current ? &_gameInfos[_currentGameIndex] : 0;
}
return true;
}
void MinigameManager::saveState(bool force) {
debugC(2, kDebugMinigames, "MinigameManager::save_state(): save state");
if (force || _currentGameIndex._gameNum >= 0) {
Common::OutSaveFile *file = g_engine->getSaveFileManager()->openForSaving(_state_container_name);
if (file) {
file->writeUint32LE(GameInfo::version());
file->writeUint32LE(_engine ? _engine->rnd(999999) : _seed);
for (auto &it : _gameInfos) {
if (!it._value.empty()) {
debugC(2, kDebugMinigames, "MinigameManager::save_state(): write game info: (%d,%d), index: %d, game data: %d", it._key._gameLevel, it._key._gameNum, it._value._game._sequenceIndex, it._value._empty ? 0 : 1);
it._key.write(*file);
it._value.write(*file);
}
}
file->finalize();
delete file;
} else {
warning("MinigameManager::saveState(): Failed to save file '%s'", _state_container_name.c_str());
}
}
}
bool MinigameManager::quant(float dt) {
if (!_game)
return false;
if (_pause_flag && _pause_flag->is_state_active("on"))
return true;
_gameTime += dt;
mgVect2i pos = _engine->mouse_cursor_position();
_mousePos = mgVect2f(pos.x, pos.y);
_mousePos += _mouseAdjast;
if (_game->state() == MinigameInterface::RUNNING) {
_timeManager->quant(dt);
if (_complete_help_miniature) {
assert(_complete_help);
if (_complete_help_miniature.hit(_mousePos))
_complete_help.setState(_complete_help_state_name.c_str());
else
_complete_help.setState("off");
}
if (_game_help_trigger) {
if (_game_help_trigger.hit(mousePosition())) {
_game_help_trigger.setState(_game_help_enabled ? "01_sel" : "02_sel");
if (mouseLeftPressed())
_game_help_enabled = !_game_help_enabled;
} else
_game_help_trigger.setState(_game_help_enabled ? "01" : "02");
}
if (_timeManager->timeIsOut()) {
signal(EVENT_TIME_OUT);
_game->setState(MinigameInterface::GAME_LOST);
} else
_game->quant(dt);
if (_game_help)
_game_help.setState(_game_help_enabled ? _game_help_state_name.c_str() : "off");
if (keyPressed(VK_MULTIPLY) || (keyPressed(VK_LSHIFT) && keyPressed('8')))
_game->setState(MinigameInterface::GAME_WIN);
switch (_game->state()) {
case MinigameInterface::GAME_LOST:
if (!_timeManager->timeIsOut())
signal(EVENT_GAME_LOSE);
// fallthrough
case MinigameInterface::NOT_INITED:
gameLose();
break;
case MinigameInterface::GAME_WIN:
signal(EVENT_GAME_WIN);
gameWin();
break;
default:
break;
}
}
for (int vKey = 0; vKey < 256; ++vKey)
if (_lastKeyChecked[vKey])
_lastKeyChecked[vKey] = _engine->is_key_pressed(vKey);
if (_game->state() != MinigameInterface::NOT_INITED) {
_textManager->quant(dt);
_effectManager->quant(dt);
return true;
}
return false;
}
void MinigameManager::setCompleteHelpVariant(int idx) {
assert(idx >= 0);
char buf[32];
buf[31] = 0;
snprintf(buf, 31, "%02d", idx + 1);
_complete_help_state_name = buf;
}
void MinigameManager::setGameHelpVariant(int idx) {
if (idx >= 0) {
char buf[32];
buf[31] = 0;
snprintf(buf, 31, "%02d", idx + 1);
_game_help_state_name = buf;
} else
_game_help_state_name = "off";
}
void MinigameManager::event(int eventID, const mgVect2f& pos, int factor) {
_eventManager->event(eventID, pos, factor);
}
void MinigameManager::signal(SystemEvent id) {
_eventManager->sysEvent(id);
}
const MinigameData *MinigameManager::getScore(int level, int game) const {
GameInfoMap::const_iterator it = _gameInfos.find(GameInfoIndex(game, level));
if (it != _gameInfos.end())
return &it->_value._game;
return 0;
}
bool MinigameManager::testAllGamesWin() {
Common::File file;
if (!file.open(Common::Path(gameListFileName())))
return false;
char read_buf[512];
while (!file.eos()) {
file.readLine(read_buf, 512);
Common::MemoryReadStream buf((const byte *)&read_buf[0], strlen(read_buf));
int level = buf.readByte() - '0';
byte ch = buf.readByte();
if (ch != ':') {
warning("MinigameManager::testAllGamesWin(): incorrect file format: '%s'", gameListFileName());
return false;
}
while (buf.pos() < buf.size()) {
ch = buf.readByte();
if (Common::isDigit(ch)) {
int game = ch - '0';
const MinigameData *data = getScore(level, game);
if (!data || data->_sequenceIndex == -1)
return false;
}
}
}
return true;
}
void MinigameManager::gameWin() {
debugC(2, kDebugMinigames, "MinigameManager::gameWin(): Game Win");
_state_flag->set_state("win");
if (debugMode() || !_currentGameInfo)
return;
assert(_currentGameIndex._gameNum >= 0);
_effectManager->start(EFFECT_1);
if (_currentGameIndex._gameNum == 0)
return;
int gameTime = round(getTime());
_eventManager->addScore(round(_timeManager->leftTime() * _timeManager->timeCost()));
_currentGameInfo->_game._lastTime = gameTime;
_currentGameInfo->_game._lastScore = _eventManager->score();
if (_currentGameInfo->_game._sequenceIndex >= 0) { // это переигровка
if (_eventManager->score() > _currentGameInfo->_game._bestScore) {
debugC(2, kDebugMinigames, "MinigameManager::gameWin(): new high score");
_currentGameInfo->_game._bestScore = _eventManager->score();
_currentGameInfo->_game._bestTime = gameTime;
}
} else {
debugC(2, kDebugMinigames, "MinigameManager::gameWin(): adding score to the sum: %d", _eventManager->score());
_currentGameInfo->_game._sequenceIndex = _completeCounters[_currentGameIndex._gameLevel];
_currentGameInfo->_game._bestScore = _eventManager->score();
_currentGameInfo->_game._bestTime = gameTime;
if (QDCounter all_score = getCounter("all_score")) {
all_score->add_value(_eventManager->score());
if (testAllGamesWin()) {
debugC(2, kDebugMinigames, "MinigameManager::gameWin(): All games are won, adding record to the score table: %d", all_score->value());
_engine->add_hall_of_fame_entry(all_score->value());
}
release(all_score);
}
if (QDCounter all_time = getCounter("all_time")) {
all_time->add_value(gameTime);
release(all_time);
}
}
saveState();
}
void MinigameManager::gameLose() {
debugC(2, kDebugMinigames, "MinigameManager: Game Lose");
_state_flag->set_state("lose");
}
const char *MinigameManager::parameter(const char *name, bool required) const {
if (!_scene)
error("MinigameManager::parameter(): Scene is undefined");
const char *txt = _scene->minigame_parameter(name);
if (required && !txt)
warning("MinigameManager::parameter(): Required parameter '%s' is missing in the ini file", transCyrillic(name));
return txt;
}
const char *MinigameManager::parameter(const char *name, const char *def) const {
if (!def)
warning("MinigameManager::parameter(): Default value for parameter '%s' is missing", transCyrillic(name));
const char *txt = _scene->minigame_parameter(name);
if (!def && !txt)
warning("MinigameManager::parameter(): Required parameter '%s' is missing in the ini file", transCyrillic(name));
return txt ? txt : (def ? def : "");
}
bool MinigameManager::mouseLeftPressed() const {
if (_invertMouseButtons)
return _engine->is_mouse_event_active(qdEngineInterface::MOUSE_EV_RIGHT_DOWN);
return _engine->is_mouse_event_active(qdEngineInterface::MOUSE_EV_LEFT_DOWN);
}
bool MinigameManager::mouseRightPressed() const {
if (_invertMouseButtons)
return _engine->is_mouse_event_active(qdEngineInterface::MOUSE_EV_LEFT_DOWN);
return _engine->is_mouse_event_active(qdEngineInterface::MOUSE_EV_RIGHT_DOWN);
}
bool MinigameManager::keyPressed(int vKey, bool once) const {
assert(vKey >= 0 && vKey <= 255);
if (_engine->is_key_pressed(vKey)) {
if (once && _lastKeyChecked[vKey])
return false;
return _lastKeyChecked[vKey] = true;
}
return _lastKeyChecked[vKey] = false;
}
mgVect3f MinigameManager::game2world(const mgVect3i& coord) const {
return _scene->screen2world_coords(reinterpret_cast(coord), coord.z);
}
mgVect3f MinigameManager::game2world(const mgVect3f& coord) const {
return _scene->screen2world_coords(mgVect2i(round(coord.x), round(coord.y)), round(coord.z));
}
mgVect3f MinigameManager::game2world(const mgVect2i& coord, int depth) const {
return _scene->screen2world_coords(coord, depth);
}
mgVect3f MinigameManager::game2world(const mgVect2f& coord, int depth) const {
return _scene->screen2world_coords(mgVect2i(round(coord.x), round(coord.y)), depth);
}
mgVect2f MinigameManager::world2game(const mgVect3f& pos) const {
mgVect2i scr = _scene->world2screen_coords(pos);
return mgVect2f(scr.x, scr.y);
}
mgVect3f MinigameManager::world2game(qdMinigameObjectInterface *obj) const {
mgVect2i scr = obj->screen_R();
return mgVect3f(scr.x, scr.y, round(getDepth(obj)));
}
mgVect2f MinigameManager::getSize(qdMinigameObjectInterface *obj) const {
if (obj) {
mgVect2i size = obj->screen_size();
return mgVect2f(size.x, size.y);
}
return mgVect2f();
}
void MinigameManager::setDepth(qdMinigameObjectInterface *obj, int depth) const {
mgVect2i scr = obj->screen_R();
obj->set_R(_scene->screen2world_coords(scr, depth));
}
float MinigameManager::getDepth(qdMinigameObjectInterface *obj) const {
return _scene->screen_depth(obj->R());
}
float MinigameManager::getDepth(const mgVect3f& pos) const {
return _scene->screen_depth(pos);
}
QDObject MinigameManager::getObject(const char *name) const {
if (!name || !*name) {
warning("MinigameManager::getObject(): null name");
return QDObject(0, "ZERO OBJECT");
}
qdMinigameObjectInterface *obj = _scene->object_interface(name);
if (!obj)
warning("MinigameManager::getObject(): Object '%s' not found", transCyrillic(name));
if (obj)
return QDObject(obj, name);
return QDObject(0, "ZERO OBJECT");
}
bool MinigameManager::testObject(const char *name) const {
if (qdMinigameObjectInterface *obj = _scene->object_interface(name, true)) {
_scene->release_object_interface(obj);
return true;
}
return false;
}
void MinigameManager::release(QDObject& obj) {
if (obj) {
_scene->release_object_interface(obj);
obj = 0;
}
}
QDCounter MinigameManager::getCounter(const char *name) {
qdMinigameCounterInterface *counter = _engine->counter_interface(name);
if (!counter)
warning("MinigameManager::getCounter(): Counter '%s' not found", transCyrillic(name));
return counter;
}
void MinigameManager::release(QDCounter& counter) {
if (!counter)
warning("MinigameManager::release(): Null counter");
_engine->release_counter_interface(counter);
counter = 0;
}
void MinigameManager::setText(const char *name, const char *text) const {
_engine->set_interface_text(0, name, text);
}
void MinigameManager::setText(const char *name, int toText, const char *format) const {
char text[16];
text[15] = 0;
snprintf(text, 15, format, toText);
setText(name, text);
}
void MinigameManager::hide(qdMinigameObjectInterface *obj) const {
obj->set_R(_scene->screen2world_coords(mgVect2i(-10000, -10000), getDepth(obj)));
}
float MinigameManager::rnd(float min, float max) const {
return min + _engine->fabs_rnd(max - min);
}
int MinigameManager::rnd(int min, int max) const {
return min + round(_engine->fabs_rnd(max - min));
}
int MinigameManager::rnd(const Std::vector &prob) const {
float rnd = this->rnd(0.f, .9999f);
float accum = 0.f;
int idx = 0;
int size = prob.size();
for (; idx < size; ++idx) {
accum += prob[idx];
if (rnd <= accum)
break;
}
assert(idx >= 0 && idx < (int)prob.size());
#ifdef _DEBUG
float sum = 0.f;
for (auto &pit : prob)
sum += pit;
assert(abs(sum - 1.f) < 0.0001f);
#endif
return idx;
}
//========================================================================================================================
// если данные еще ни разу не сохранялись - запоминаем
// если уже есть запомненные, то заменяем на них
bool MinigameManager::processGameData(Common::SeekableReadStream &data) {
data.seek(0);
if (_currentGameInfo) {
if (_currentGameInfo->_empty) {
_currentGameInfo->_empty = false;
assert(data.size());
_currentGameInfo->persist(data);
} else {
if (data.size() != _currentGameInfo->_dataSize) {
warning("MinigameManager::processGameData(): Old minigame save detected. Remove '%s'", _state_container_name.c_str());
data.seek(0);
return false;
}
}
}
data.seek(0);
return true;
}
MinigameData::MinigameData() {
_sequenceIndex = -1;
_lastScore = 0;
_lastTime = 0;
_bestTime = 0;
_bestScore = 0;
}
void MinigameData::write(Common::WriteStream &out) const {
out.writeSint32LE(_sequenceIndex);
out.writeSint32LE(_lastScore);
out.writeSint32LE(_lastTime);
out.writeSint32LE(_bestTime);
out.writeSint32LE(_bestScore);
}
void MinigameData::read(Common::ReadStream &out) {
_sequenceIndex = out.readSint32LE();
_lastScore = out.readSint32LE();
_lastTime = out.readSint32LE();
_bestTime = out.readSint32LE();
_bestScore = out.readSint32LE();
}
GameInfo::GameInfo() {
_empty = true;
_dataSize = 0;
_gameData = 0;
}
void GameInfo::free() {
if (_gameData) {
assert(_dataSize > 0);
::free(_gameData);
_gameData = 0;
}
_dataSize = 0;
}
void GameInfo::persist(Common::SeekableReadStream &in) {
if (_dataSize != in.size()) {
free();
if (in.size() > 0) {
_dataSize = in.size();
_gameData = malloc(_dataSize);
}
}
if (_dataSize > 0)
in.read(_gameData, _dataSize);
}
void GameInfo::write(Common::WriteStream &out) const {
_game.write(out);
out.writeByte(_empty);
if (!_empty) {
_timeManagerData.crd.write(out);
_effectManagerData.crd.write(out);
out.writeUint32LE(_dataSize);
if (_dataSize > 0)
out.write(_gameData, _dataSize);
}
}
void GameInfo::read(Common::ReadStream &in) {
_game.read(in);
_empty = in.readByte();
if (!_empty) {
_timeManagerData.crd.read(in);
_effectManagerData.crd.read(in);
free();
_dataSize = in.readUint32LE();
if (_dataSize) {
_gameData = malloc(_dataSize);
in.read(_gameData, _dataSize);
}
}
}
void MinigameManager::GameInfoIndex::write(Common::WriteStream &out) const {
out.writeUint32LE(_gameNum);
out.writeUint32LE(_gameLevel);
}
void MinigameManager::GameInfoIndex::read(Common::ReadStream &in) {
_gameNum = in.readUint32LE();
_gameLevel = in.readUint32LE();
}
int MinigameManager::getParameter(const char* name, const int& defValue) {
return round(getParameter(name, (float)defValue));
}
bool MinigameManager::getParameter(const char* name, int& out, bool obligatory) {
float retValue = out;
if (getParameter(name, retValue, obligatory)) {
out = round(retValue);
return true;
}
return false;
}
float MinigameManager::getParameter(const char* name, const float &defValue) {
if (const char *data = parameter(name, false)) {
float retValue = defValue;
if (sscanf(data, "%f", &retValue) == 1)
return retValue;
error("The parameter [%s] contains wrong data type. It must be a number", name);
}
return defValue;
}
bool MinigameManager::getParameter(const char* name, float &out, bool obligatory) {
if (const char * data = parameter(name, obligatory)) {
float retValue = out;
if (sscanf(data, "%f", &retValue) == 1) {
out = retValue;
return true;
}
error("The parameter [%s] contains wrong data type. It must be a number", name);
}
return false;
}
mgVect2f MinigameManager::getParameter(const char* name, const mgVect2f& defValue) {
if (const char * data = parameter(name, false)) {
mgVect2f retValue = defValue;
if (sscanf(data, "%f %f", &retValue.x, &retValue.y) == 2)
return retValue;
error("The parameter [%s] contains wrong data type. It must be a pair of numbers", name);
}
return defValue;
}
bool MinigameManager::getParameter(const char* name, mgVect2f& out, bool obligatory) {
if (const char * data = parameter(name, obligatory)) {
mgVect2f retValue = out;
if (sscanf(data, "%f %f", &retValue.x, &retValue.y) == 2) {
out = retValue;
return true;
}
error("The parameter [%s] contains wrong data type. It must be a pair of numbers", name);
}
return false;
}
mgVect2i MinigameManager::getParameter(const char* name, const mgVect2i& defValue) {
mgVect2f retValue = getParameter(name, mgVect2f(defValue.x, defValue.y));
return mgVect2i(round(retValue.x), round(retValue.y));
}
bool MinigameManager::getParameter(const char* name, mgVect2i& out, bool obligatory) {
mgVect2f retValue = mgVect2f(out.x, out.y);
if (getParameter(name, retValue, obligatory)) {
out = mgVect2i(round(retValue.x), round(retValue.y));
return true;
}
return false;
}
//========================================================================================================================
TimeManager::TimeManager(HoldData &data_, MinigameManager *runtime) {
_runtime = runtime;
if (const char *data = _runtime->parameter("game_time", false)) {
if (sscanf(data, "%f", &_gameTime) != 1)
_gameTime = -1.f;
} else
_gameTime = -1.f;
_timeCost = 0.f;
if (_gameTime > 0) {
if (const char *data = _runtime->parameter("time_bar"))
_timeBar = _runtime->getObject(data);
if (const char *data = _runtime->parameter("time_cost"))
sscanf(data, "%f", &_timeCost);
}
_direction = DOWN; // Default value
if (_timeBar) {
TimeManagerData myData;
myData.crd = _runtime->world2game(_timeBar);
data_.process(myData);
_startPos = myData.crd;
_size = _runtime->getSize(_timeBar);
if (const char *data = _runtime->parameter("time_bar_direction")) {
int dir;
if (sscanf(data, "%d", &dir) == 1) {
assert(dir >= 0 && dir <= 3);
_direction = Direction(dir);
}
}
} else
_size = mgVect2f(-1.f, -1.f);
assert(_runtime->getTime() == 0.f);
_lastEventTime = 0;
}
TimeManager::~TimeManager() {
if (_timeBar)
_runtime->release(_timeBar);
}
bool TimeManager::timeIsOut() const {
if (_gameTime > 0.f)
return _runtime->getTime() > _gameTime;
return false;
}
float TimeManager::leftTime() const {
if (_gameTime <= 0.f)
return 0;
return _runtime->getTime() > _gameTime ? 0 : _gameTime - _runtime->getTime();
}
void TimeManager::quant(float dt) {
int seconds = round(_runtime->getTime());
if (seconds != _lastEventTime) {
_lastEventTime = seconds;
_runtime->textManager().updateTime(seconds);
int amountSeconds = round(leftTime());
if (_gameTime < 0.f || amountSeconds > 10)
if (seconds % 60 == 0)
_runtime->signal(EVENT_TIME_60_SECOND_TICK);
else if (seconds % 10 == 0)
_runtime->signal(EVENT_TIME_10_SECOND_TICK);
else
_runtime->signal(EVENT_TIME_1_SECOND_TICK);
else if (amountSeconds == 10)
_runtime->signal(EVENT_TIME_10_SECOND_LEFT);
else
_runtime->signal(EVENT_TIME_LESS_10_SECOND_LEFT_SECOND_TICK);
}
if (_gameTime <= 0.f || !_timeBar)
return;
float phase = clamp(_runtime->getTime() / _gameTime, 0.f, 1.f);
mgVect3f pos;
switch (_direction) {
case UP:
pos.y = -_size.y * phase;
break;
case DOWN:
pos.y = _size.y * phase;
break;
case LEFT:
pos.x = -_size.x * phase;
break;
case RIGHT:
pos.x = _size.x * phase;
break;
}
pos += _startPos;
_timeBar->set_R(_runtime->game2world(pos));
}
} // namespace QDEngine