[Scummvm-git-logs] scummvm master -> 6acd8cb65c5d56a2fe7674da44df8820f73b72f8
sev-
noreply at scummvm.org
Thu Oct 17 19:50:33 UTC 2024
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
6acd8cb65c QDENGINE: Added code for "advanced" minigames
Commit: 6acd8cb65c5d56a2fe7674da44df8820f73b72f8
https://github.com/scummvm/scummvm/commit/6acd8cb65c5d56a2fe7674da44df8820f73b72f8
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2024-10-17T21:49:50+02:00
Commit Message:
QDENGINE: Added code for "advanced" minigames
The code is added as is, with the following simple transformations:
o windows-1251 -> utf8
o astyle
o Added copyright header
o Added namespace
o Fixed include paths
o Removed references to the system-wide includes
o Fixed guard defines
Also, couple of obviously redundant files were removed.
The files contain unused code and are not yet plugged into the build
system. This is the beginning for traceability of changes.
Changed paths:
A engines/qdengine/minigames/adv/EffectManager.cpp
A engines/qdengine/minigames/adv/EffectManager.h
A engines/qdengine/minigames/adv/EventManager.cpp
A engines/qdengine/minigames/adv/EventManager.h
A engines/qdengine/minigames/adv/ExportInterface.cpp
A engines/qdengine/minigames/adv/FlyObject.cpp
A engines/qdengine/minigames/adv/FlyObject.h
A engines/qdengine/minigames/adv/HoldData.h
A engines/qdengine/minigames/adv/MinigameInterface.h
A engines/qdengine/minigames/adv/ObjectContainer.cpp
A engines/qdengine/minigames/adv/ObjectContainer.h
A engines/qdengine/minigames/adv/Range.cpp
A engines/qdengine/minigames/adv/Range.h
A engines/qdengine/minigames/adv/Rect.h
A engines/qdengine/minigames/adv/RunTime.cpp
A engines/qdengine/minigames/adv/RunTime.h
A engines/qdengine/minigames/adv/TextManager.cpp
A engines/qdengine/minigames/adv/TextManager.h
A engines/qdengine/minigames/adv/common.cpp
A engines/qdengine/minigames/adv/common.h
A engines/qdengine/minigames/adv/m_karaoke.cpp
A engines/qdengine/minigames/adv/m_karaoke.h
A engines/qdengine/minigames/adv/m_puzzle.cpp
A engines/qdengine/minigames/adv/m_puzzle.h
A engines/qdengine/minigames/adv/m_scores.cpp
A engines/qdengine/minigames/adv/m_scores.h
A engines/qdengine/minigames/adv/m_swap.cpp
A engines/qdengine/minigames/adv/m_swap.h
A engines/qdengine/minigames/adv/m_triangles.cpp
A engines/qdengine/minigames/adv/m_triangles.h
A engines/qdengine/minigames/adv/qdMath.h
diff --git a/engines/qdengine/minigames/adv/EffectManager.cpp b/engines/qdengine/minigames/adv/EffectManager.cpp
new file mode 100644
index 00000000000..8a8c745bf61
--- /dev/null
+++ b/engines/qdengine/minigames/adv/EffectManager.cpp
@@ -0,0 +1,92 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/EffectManager.h"
+#include "qdengine/minigames/adv/qdMath.h"
+
+namespace QDEngine {
+
+EffectManager::EffectManager(HoldData<EffectManagerData> &data) {
+ const char *effectName = runtime->parameter("effect_name", "effect");
+ if (runtime->testObject(effectName)) {
+ effect_ = runtime->getObject(effectName);
+ data_.crd = effect_->R();
+ effect_->set_screen_scale(mgVect2f(0.01f, 0.01f), mgVect2f(10000.f, 10000.f));
+ runtime->hide(effect_);
+ }
+
+ data.process(data_);
+
+ effectTime_ = clamp(getParameter("effect_time", 3.f), 0.5f, 10.f);
+ phaseTime_ = clamp(getParameter("effect_phase_time", effectTime_ / 20.f), 0.03f, 1.f);
+ phaseSpeed_ = clamp(getParameter("effect_phase_speed", 1.5f), 1.05f, 10.f);
+
+ current_ = EFFECT_COUNT;
+
+}
+
+EffectManager::~EffectManager() {
+ runtime->release(effect_);
+
+}
+
+void EffectManager::quant(float dt) {
+ if (current_ == EFFECT_COUNT)
+ return;
+
+ if (runtime->time() > effectTimer_) {
+ stop(current_);
+ return;
+ }
+
+ if (runtime->time() > phaseTimer_) {
+ phaseTimer_ = runtime->time() + phaseTime_;
+ mgVect2f scale = effect_->screen_scale();
+ mgVect2f speed = scale;
+ scale *= phaseSpeed_;
+ speed = scale - speed;
+ speed /= phaseTime_;
+ effect_->set_screen_scale(scale, speed);
+ }
+
+}
+
+void EffectManager::start(EffectType id) {
+ if (current_ != EFFECT_COUNT || !effect_)
+ return;
+ effectTimer_ = runtime->time() + effectTime_;
+ current_ = id;
+ phaseTimer_ = runtime->time();
+ effect_->set_screen_scale(mgVect2f(0.02f, 0.02f), mgVect2f(10000.f, 10000.f));
+ effect_->set_R(data_.crd);
+
+}
+
+void EffectManager::stop(EffectType id) {
+ if (current_ == EFFECT_COUNT)
+ return;
+ runtime->hide(effect_);
+ effect_->set_screen_scale(mgVect2f(0.01f, 0.01f), mgVect2f(10000.f, 10000.f));
+ current_ = EFFECT_COUNT;
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/EffectManager.h b/engines/qdengine/minigames/adv/EffectManager.h
new file mode 100644
index 00000000000..c1a8bec78a3
--- /dev/null
+++ b/engines/qdengine/minigames/adv/EffectManager.h
@@ -0,0 +1,60 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_EFFECT_MANAGER_H
+#define QDENGINE_MINIGAMES_ADV_EFFECT_MANAGER_H
+
+#include "qdengine/minigames/adv/RunTime.h"
+#include "qdengine/minigames/adv/HoldData.h"
+
+namespace QDEngine {
+
+enum EffectType {
+ EFFECT_1,
+ EFFECT_COUNT
+};
+
+class EffectManager {
+public:
+ EffectManager(HoldData<EffectManagerData> &data);
+ ~EffectManager();
+
+ void quant(float dt);
+
+ void start(EffectType id);
+ void stop(EffectType id);
+
+private:
+ EffectType current_;
+ EffectManagerData data_;
+ float phaseTime_;
+ float effectTime_;
+ float phaseSpeed_;
+
+ float effectTimer_;
+ float phaseTimer_;
+ QDObject effect_;
+
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_EFFECT_MANAGER_H
diff --git a/engines/qdengine/minigames/adv/EventManager.cpp b/engines/qdengine/minigames/adv/EventManager.cpp
new file mode 100644
index 00000000000..35b68e7208d
--- /dev/null
+++ b/engines/qdengine/minigames/adv/EventManager.cpp
@@ -0,0 +1,137 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/EventManager.h"
+#include "qdengine/minigames/adv/RunTime.h"
+#include "qdengine/minigames/adv/TextManager.h"
+
+namespace QDEngine {
+
+EventManager::EventPreset::EventPreset() {
+ score = 0;
+ fontID = -1;
+ escapeID = -1;
+ triggerEventID = -1;
+}
+
+EventManager::EventManager() {
+ score_ = 0;
+
+ char str_cache[256];
+
+ for (int idx = 0;; ++idx) {
+ _snprintf(str_cache, 127, "register_trigger_%d", idx);
+ if (const char * descr = runtime->parameter(str_cache, false))
+ triggerEvents_.push_back(runtime->getObject(descr));
+ else
+ break;
+ }
+ dprintf("registered %d trigger objects\n", triggerEvents_.size());
+
+ eventPresets_.resize(SYSTEM_EVENTS_SIZE);
+ for (int idx = 0; idx < SYSTEM_EVENTS_SIZE; ++idx) {
+ _snprintf(str_cache, 127, "system_event_%d", idx);
+ if (const char * descr = runtime->parameter(str_cache, false)) {
+ EventPreset preset;
+ int read = sscanf(descr, "%d %d", &preset.score, &preset.triggerEventID);
+ xxassert(read == 2, (XBuffer() < "ÐевеÑÐ½Ð°Ñ ÑÑÑока Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑаниÑ" < str_cache).c_str());
+ if (read == 2) {
+ xxassert(preset.triggerEventID < (int)triggerEvents_.size(), (XBuffer() < "СÑÑлка на незаÑегиÑÑÑиÑованнÑй ÑÑÐ¸Ð³Ð³ÐµÑ Ð² " < str_cache).c_str());
+ if (preset.triggerEventID < (int)triggerEvents_.size())
+ eventPresets_[idx] = preset;
+ }
+ }
+ }
+
+ for (int idx = 0;; ++idx) {
+ _snprintf(str_cache, 127, "register_event_%d", idx);
+ if (const char * descr = runtime->parameter(str_cache, false)) {
+ EventPreset preset;
+ int read = sscanf(descr, "%d %d %d %d", &preset.score, &preset.fontID, &preset.escapeID, &preset.triggerEventID);
+ xxassert(read == 4, (XBuffer() < "ÐевеÑÐ½Ð°Ñ ÑÑÑока Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ ÑобÑÑÐ¸Ñ " < idx).c_str());
+ xxassert(preset.triggerEventID < (int)triggerEvents_.size(), (XBuffer() < "СÑÑлка на незаÑегиÑÑÑиÑованнÑй ÑÑÐ¸Ð³Ð³ÐµÑ Ð² " < str_cache).c_str());
+ if (read == 4 && preset.triggerEventID < (int)triggerEvents_.size())
+ eventPresets_.push_back(preset);
+ else
+ eventPresets_.push_back(EventPreset());
+ } else
+ break;
+ }
+ dprintf("registered %d events\n", eventPresets_.size());
+
+ if (const char * data = runtime->parameter("allow_negative", false)) {
+ int tmp;
+ sscanf(data, "%d", &tmp);
+ enableNegative_ = tmp;
+ } else
+ enableNegative_ = false;
+}
+
+void EventManager::sysEvent(int eventID) {
+ xassert(eventID >= 0);
+ //dprintf("System event: %d\n", eventID);
+
+ xassert(eventID < SYSTEM_EVENTS_SIZE);
+
+ mgVect2i pos = runtime->screenSize() / 2;
+ event(eventID - SYSTEM_EVENTS_SIZE, mgVect2f(pos.x, pos.y), 1);
+}
+
+void EventManager::event(int eventID, const mgVect2f& pos, int factor) {
+ //dprintf("Event: %d, pos=(%5.1f, %5.1f), fartor=%d\n", eventID, pos.x, pos.y, factor);
+
+ eventID += SYSTEM_EVENTS_SIZE;
+
+ if (eventID >= eventPresets_.size())
+ return;
+
+ const EventPreset& pr = eventPresets_[eventID];
+
+ if (pr.triggerEventID >= 0) {
+ xassert(pr.triggerEventID < triggerEvents_.size());
+ triggerEvents_[pr.triggerEventID]->set_state("on");
+ }
+
+ if (pr.score) {
+ int diff = addScore(pr.score);
+
+ if (pr.fontID >= 0 && pr.escapeID >= 0 && diff != 0)
+ runtime->textManager().showNumber(diff, pos, pr.fontID, pr.escapeID);
+ }
+}
+
+int EventManager::addScore(int sc) {
+ int diff = score_;
+
+ score_ += sc;
+ if (score_ < 0 && !enableNegative_)
+ score_ = 0;
+
+ diff = score_ - diff;
+
+ if (diff)
+ runtime->textManager().updateScore(score_);
+
+ return diff;
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/EventManager.h b/engines/qdengine/minigames/adv/EventManager.h
new file mode 100644
index 00000000000..a42de3d1391
--- /dev/null
+++ b/engines/qdengine/minigames/adv/EventManager.h
@@ -0,0 +1,70 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_EVENT_MANAGER_H
+#define QDENGINE_MINIGAMES_ADV_EVENT_MANAGER_H
+
+namespace QDEngine {
+
+enum SystemEvent {
+ EVENT_TIME_1_SECOND_TICK,
+ EVENT_TIME_10_SECOND_TICK,
+ EVENT_TIME_60_SECOND_TICK,
+ EVENT_TIME_10_SECOND_LEFT,
+ EVENT_TIME_LESS_10_SECOND_LEFT_SECOND_TICK,
+ EVENT_TIME_OUT,
+ EVENT_GAME_LOSE,
+ EVENT_GAME_WIN,
+ SYSTEM_EVENTS_SIZE
+};
+
+class EventManager {
+public:
+ EventManager();
+
+ void sysEvent(int eventID);
+ void event(int eventID, const mgVect2f& pos, int factor);
+
+ int score() const {
+ return score_;
+ }
+ int addScore(int sc);
+
+private:
+ int score_;
+ bool enableNegative_;
+
+ struct EventPreset {
+ EventPreset();
+ int score;
+ int fontID;
+ int escapeID;
+ int triggerEventID;
+ };
+ typedef vector<EventPreset> EventPresets;
+ EventPresets eventPresets_;
+
+ QDObjects triggerEvents_;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_EVENT_MANAGER_H
diff --git a/engines/qdengine/minigames/adv/ExportInterface.cpp b/engines/qdengine/minigames/adv/ExportInterface.cpp
new file mode 100644
index 00000000000..aea96013aaa
--- /dev/null
+++ b/engines/qdengine/minigames/adv/ExportInterface.cpp
@@ -0,0 +1,46 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/RunTime.h"
+
+namespace QDEngine {
+
+qdMiniGameInterface *open_game_interface(const char* name) {
+ dprintf("open_game_interface: %s, runtime%s\n", name, runtime ? "!=0" : "==0");
+
+ if (!runtime)
+ return runtime = new MinigameManager;
+
+ return new MinigameManager;
+}
+
+bool close_game_interface(qdMiniGameInterface* game) {
+ dprintf("close_game_interface, runtime%s%s\n", runtime == game ? "==game" : "!=game", runtime ? "!=0" : "==0");
+
+ delete game;
+ if (game == runtime)
+ runtime = 0;
+
+ return true;
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/FlyObject.cpp b/engines/qdengine/minigames/adv/FlyObject.cpp
new file mode 100644
index 00000000000..e34f5974e7b
--- /dev/null
+++ b/engines/qdengine/minigames/adv/FlyObject.cpp
@@ -0,0 +1,56 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/FlyObject.h"
+#include "qdengine/minigames/adv/qdMath.h"
+#include "qdengine/minigames/adv/RunTime.h"
+
+namespace QDEngine {
+
+FlyObjectBase::FlyObjectBase(const mgVect2f& _c, const mgVect2f& _t, float _s)
+ : current(_c)
+ , target(_t)
+ , speed(_s) {
+}
+
+bool FlyObjectBase::quant(float dt) {
+ mgVect2f dir = target;
+ dir -= current;
+ float step = speed * dt;
+ if (abs(dir) < step) {
+ current = target;
+ return false;
+ }
+ norm(dir);
+ dir *= step;
+ current += dir;
+ return true;
+}
+
+
+bool FlyQDObject::quant(float dt, QDObject& obj) {
+ bool ret = FlyObjectBase::quant(dt);
+ obj->set_R(runtime->game2world(current, depth));
+ return ret;
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/FlyObject.h b/engines/qdengine/minigames/adv/FlyObject.h
new file mode 100644
index 00000000000..f2ab50e0dfb
--- /dev/null
+++ b/engines/qdengine/minigames/adv/FlyObject.h
@@ -0,0 +1,54 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_FLYOBJECT_H
+#define QDENGINE_MINIGAMES_ADV_FLYOBJECT_H
+
+namespace QDEngine {
+
+struct FlyObjectBase {
+ FlyObjectBase(const mgVect2f& _c = mgVect2f(), const mgVect2f& _t = mgVect2f(), float _s = 1.f);
+ bool quant(float dt);
+
+ mgVect2f current;
+ mgVect2f target;
+ float speed;
+};
+
+struct FlyQDObject : public FlyObjectBase {
+ FlyQDObject(float dp = 0.f) : depth(dp), data(-1) {}
+ FlyQDObject(const FlyObjectBase& crd, float dp, int dat) : FlyObjectBase(crd), depth(dp), data(dat) {}
+
+ bool operator== (int dat) const {
+ return data == dat;
+ }
+
+ bool quant(float dt, QDObject& obj);
+
+ float depth;
+ int data;
+};
+
+typedef vector<FlyQDObject> FlyQDObjects;
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_FLYOBJECT_H
diff --git a/engines/qdengine/minigames/adv/HoldData.h b/engines/qdengine/minigames/adv/HoldData.h
new file mode 100644
index 00000000000..cf250c9dff9
--- /dev/null
+++ b/engines/qdengine/minigames/adv/HoldData.h
@@ -0,0 +1,55 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_HOLD_DATA_H
+#define QDENGINE_MINIGAMES_ADV_HOLD_DATA_H
+
+namespace QDEngine {
+
+template <class T>
+class HoldData {
+ T emptyData_;
+ T &data_;
+ bool empty_;
+public:
+ HoldData() : data_(emptyData_), empty_(true) {}
+ HoldData(T* data, bool empty)
+ : data_(data ? * data : emptyData_) {
+ empty_ = data ? empty : true;
+ }
+
+ void process(T& current) {
+ if (empty_) {
+ data_ = current;
+ empty_ = false;
+ } else
+ current = data_;
+ }
+
+ const T &get() const {
+ xassert(!empty_);
+ return data_;
+ }
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_HOLD_DATA_H
diff --git a/engines/qdengine/minigames/adv/MinigameInterface.h b/engines/qdengine/minigames/adv/MinigameInterface.h
new file mode 100644
index 00000000000..f3608f74e09
--- /dev/null
+++ b/engines/qdengine/minigames/adv/MinigameInterface.h
@@ -0,0 +1,54 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_MINIGAME_INTERFACE_H
+#define QDENGINE_MINIGAMES_ADV_MINIGAME_INTERFACE_H
+
+namespace QDEngine {
+
+class MinigameInterface {
+public:
+ enum StateType {
+ NOT_INITED,
+ RUNNING,
+ GAME_WIN,
+ GAME_LOST
+ };
+
+ MinigameInterface() : state_(NOT_INITED) {}
+ virtual ~MinigameInterface() {}
+
+ virtual void quant(float dt) = 0;
+
+ void setState(StateType state) {
+ state_ = state;
+ }
+ StateType state() const {
+ return state_;
+ }
+
+private:
+ StateType state_;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_MINIGAME_INTERFACE_H
diff --git a/engines/qdengine/minigames/adv/ObjectContainer.cpp b/engines/qdengine/minigames/adv/ObjectContainer.cpp
new file mode 100644
index 00000000000..1ca2e90eecb
--- /dev/null
+++ b/engines/qdengine/minigames/adv/ObjectContainer.cpp
@@ -0,0 +1,117 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/ObjectContainer.h"
+#include "qdengine/minigames/adv/RunTime.h"
+
+namespace QDEngine {
+
+ObjectContainer::ObjectContainer() {
+ current_ = 0;
+
+}
+
+void ObjectContainer::release() {
+ QDObjects::iterator it;
+ FOR_EACH(objects_, it)
+ runtime->release(*it);
+
+ objects_.clear();
+ current_ = 0;
+}
+
+void ObjectContainer::pushObject(QDObject& obj) {
+ xassert(find(objects_.begin(), objects_.end(), obj) == objects_.end());
+ objects_.push_back(obj);
+}
+
+const char *ObjectContainer::name() const {
+ #ifdef _DEBUG
+ return name_.c_str();
+ #else
+ return "";
+ #endif
+
+}
+
+bool ObjectContainer::load(const char* base_name, bool hide) {
+ if (!runtime->testObject(base_name)) {
+ xxassert(false, (XBuffer() < "Ðе найден обÑекÑ: \"" < base_name < "\"").c_str());
+ return false;
+ }
+
+ #ifdef _DEBUG
+ name_ = base_name;
+ #endif
+
+ QDObject obj = runtime->getObject(base_name);
+ coord_ = runtime->world2game(obj);
+ pushObject(obj);
+ if (hide)
+ runtime->hide(obj);
+
+ char name[128];
+ name[127] = 0;
+ for (int dubl = 0; ; ++dubl) {
+ _snprintf(name, 127, "%s%04d", base_name, dubl);
+ if (runtime->testObject(name)) {
+ obj = runtime->getObject(name);
+ pushObject(obj);
+ if (hide)
+ runtime->hide(obj);
+ } else
+ break;
+ }
+
+ return true;
+}
+
+void ObjectContainer::hideAll() {
+ QDObjects::iterator it;
+ FOR_EACH(objects_, it)
+ runtime->hide(*it);
+}
+
+QDObject ObjectContainer::getObject() {
+ if (current_ < objects_.size())
+ return objects_[current_++];
+ xxassert(0, (XBuffer() < "конÑилиÑÑ Ð¾Ð±ÑекÑÑ \"" < name() < "\" в пÑле").c_str());
+//#ifdef _DEBUG
+// return QDObject::ZERO;
+//#else
+ return objects_[0]; // плоÑ
о, но алÑÑеÑнаÑива ÑÑо вообÑе ÑпаÑÑÑ
+//#endif
+
+}
+
+void ObjectContainer::releaseObject(QDObject& obj) {
+ QDObjects::iterator it = find(objects_.begin(), objects_.end(), obj);
+ if (it != objects_.end()) {
+ xxassert((int)distance(objects_.begin(), it) < current_, (XBuffer() < "обÑÐµÐºÑ Ð² пÑл \"" < name() < "\" возвÑаÑен неÑколÑко Ñаз").c_str());
+ runtime->hide(obj);
+ if (current_ > 0)
+ swap(*it, objects_[--current_]);
+ obj = 0;
+ }
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/ObjectContainer.h b/engines/qdengine/minigames/adv/ObjectContainer.h
new file mode 100644
index 00000000000..92194e6ad52
--- /dev/null
+++ b/engines/qdengine/minigames/adv/ObjectContainer.h
@@ -0,0 +1,54 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_OBJECT_CONTAINER_H
+#define QDENGINE_MINIGAMES_ADV_OBJECT_CONTAINER_H
+
+namespace QDEngine {
+
+class ObjectContainer {
+ QDObjects objects_;
+ int current_;
+ mgVect3f coord_;
+ #ifdef _DEBUG
+ string name_;
+ #endif
+ const char *name() const;
+ void pushObject(QDObject& obj);
+
+public:
+ ObjectContainer();
+ void release();
+
+ bool load(const char* name, bool hide = true);
+ void hideAll();
+
+ const mgVect3f &coord() const {
+ return coord_;
+ }
+
+ QDObject getObject();
+ void releaseObject(QDObject& obj);
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_OBJECT_CONTAINER_H
diff --git a/engines/qdengine/minigames/adv/Range.cpp b/engines/qdengine/minigames/adv/Range.cpp
new file mode 100644
index 00000000000..744c082b623
--- /dev/null
+++ b/engines/qdengine/minigames/adv/Range.cpp
@@ -0,0 +1,205 @@
+/* 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 "qdengine/minigames/adv/Range.h"
+
+namespace QDEngine {
+
+void Rangef::set(float _min, float _max) {
+ min_ = _min;
+ max_ = _max;
+}
+
+Rangef Rangef::intersection(const Rangef& _range) {
+ float begin;
+ float end;
+ if (maximum() < _range.minimum() || minimum() > _range.maximum())
+ return Rangef(0.f, 0.f);
+
+ if (include(_range.minimum()))
+ begin = _range.minimum();
+ else
+ begin = minimum();
+
+ if (include(_range.maximum()))
+ end = _range.maximum();
+ else
+ end = maximum();
+ return Rangef(begin, end);
+}
+
+
+float Rangef::clip(float &_value) const {
+ if (include(_value))
+ return _value;
+ else {
+ if (_value < minimum())
+ return minimum();
+ else
+ return maximum();
+ }
+}
+
+// --------------------- Rangei
+
+void Rangei::set(int _min, int _max) {
+ min_ = _min;
+ max_ = _max;
+}
+
+Rangei Rangei::intersection(const Rangei& _range) {
+ int begin;
+ int end;
+ if (maximum() < _range.minimum() || minimum() > _range.maximum())
+ return Rangei(0, 0);
+
+ if (include(_range.minimum()))
+ begin = _range.minimum();
+ else
+ begin = minimum();
+
+ if (include(_range.maximum()))
+ end = _range.maximum();
+ else
+ end = maximum();
+ return Rangei(begin, end);
+}
+
+
+int Rangei::clip(int &_value) {
+ if (include(_value))
+ return _value;
+ else {
+ if (_value < minimum())
+ return minimum();
+ else
+ return maximum();
+ }
+}
+
+/*
+/// ÐбÑÑÑакÑÐ¸Ñ Ð·Ð°ÐºÑÑÑого инÑеÑвала (оÑÑезка).
+template<typename ScalarType = float>
+class Range
+{
+public:
+ typedef Range<ScalarType> RangeType;
+
+ Range (ScalarType _min = ScalarType(0), ScalarType _max = ScalarType(0)) :
+ min_ (_min),
+ max_ (_max)
+ {}
+
+ inline ScalarType minimum () const
+ {
+ return min_;
+ }
+ inline void minimum (ScalarType _min)
+ {
+ min_ = _min;
+ }
+ inline ScalarType maximum () const
+ {
+ return max_;
+ }
+ inline void maximum (ScalarType _max)
+ {
+ max_ = _max;
+ }
+ inline void set (ScalarType _min, ScalarType _max)
+ {
+ min_ = _min;
+ max_ = _max;
+ }
+
+ inline ScalarType length () const
+ {
+ return (maximum () - minimum ());
+ }
+
+ inline ScalarType center() const
+ {
+ return (maximum() + minimum()) / 2;
+ }
+
+ /// ÐоÑÑекÑен ли инÑеÑвал (Ð½ÐµÑ - в ÑлÑÑае когда minimum > maximum);
+ inline bool is_valid () const
+ {
+ return (minimum () <= maximum ());
+ }
+
+ /// ÐклÑÑÐ°ÐµÑ Ð»Ð¸ оÑÑезок (закÑÑÑÑй инÑеÑвал) ÑоÑÐºÑ \c _value.
+ inline bool include (ScalarType _value) const
+ {
+ return (minimum () <= _value) && (maximum () >= _value);
+ }
+ /// ÐклÑÑÐ°ÐµÑ Ð»Ð¸ инÑеÑвал в ÑÐµÐ±Ñ \c _range.
+ inline bool include (const RangeType& _range) const
+ {
+ return (minimum () <= _range.minimum ()) && (maximum () >= _range.maximum ());
+ }
+
+ /// ÐозвÑаÑÐ°ÐµÑ Ð¿ÐµÑеÑеÑение инÑеÑвала *this и \c _range.
+ inline RangeType intersection (const RangeType& _range)
+ {
+ ScalarType begin;
+ ScalarType end;
+ if (maximum () < _range.minimum () || minimum () > _range.maximum ())
+ return RangeType (0, 0);
+
+ if (include (_range.minimum ()))
+ begin = _range.minimum ();
+ else
+ begin = minimum ();
+
+ if (include (_range.maximum ()))
+ end = _range.maximum ();
+ else
+ end = maximum ();
+ return RangeType (begin, end);
+ }
+
+ /// ÐозвÑаÑÐ°ÐµÑ \c _value в пÑеделаÑ
инÑеÑвала [minimum, maximum].
+ inline ScalarType clip (ScalarType& _value)
+ {
+ if (include (_value))
+ return _value;
+ else
+ {
+ if (_value < minimum ())
+ return minimum ();
+ else
+ return maximum ();
+ }
+ }
+
+ void serialize(Archive& ar){
+ ar.serialize(min_, "min_", "ÐинимÑм");
+ ar.serialize(max_, "max_", "ÐакÑимÑм");
+ }
+
+private:
+ ScalarType min_;
+ ScalarType max_;
+};
+*/
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/Range.h b/engines/qdengine/minigames/adv/Range.h
new file mode 100644
index 00000000000..19843890ceb
--- /dev/null
+++ b/engines/qdengine/minigames/adv/Range.h
@@ -0,0 +1,141 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_RANGE_H
+#define QDENGINE_MINIGAMES_ADV_RANGE_H
+
+namespace QDEngine {
+
+class Rangef {
+public:
+ Rangef(float _min = 0.f, float _max = 0.f)
+ : min_(_min)
+ , max_(_max)
+ {}
+
+ float minimum() const {
+ return min_;
+ }
+ void setMinimum(float _min) {
+ min_ = _min;
+ }
+
+ float maximum() const {
+ return max_;
+ }
+ void setMaximum(float _max) {
+ max_ = _max;
+ }
+
+ void set(float _min, float _max);
+
+ float length() const {
+ return max_ - min_;
+ }
+ float center() const {
+ return (max_ + min_) / 2.f;
+ }
+
+ /// ÐоÑÑекÑен ли инÑеÑвал (Ð½ÐµÑ - в ÑлÑÑае когда minimum > maximum);
+ bool is_valid() const {
+ return min_ <= max_;
+ }
+
+ /// ÐклÑÑÐ°ÐµÑ Ð»Ð¸ оÑÑезок (закÑÑÑÑй инÑеÑвал) ÑоÑÐºÑ \c _value.
+ bool include(float _value) const {
+ return (min_ <= _value) && (max_ >= _value);
+ }
+ /// ÐклÑÑÐ°ÐµÑ Ð»Ð¸ инÑеÑвал в ÑÐµÐ±Ñ \c _range.
+ bool include(const Rangef& _range) const {
+ return min_ <= _range.min_ && max_ >= _range.max_;
+ }
+
+ /// ÐозвÑаÑÐ°ÐµÑ Ð¿ÐµÑеÑеÑение инÑеÑвала *this и \c _range.
+ Rangef intersection(const Rangef& _range);
+
+ /// ÐозвÑаÑÐ°ÐµÑ \c _value в пÑеделаÑ
инÑеÑвала [minimum, maximum].
+ float clip(float &_value) const;
+
+private:
+ float min_;
+ float max_;
+};
+
+// --------------------- Rangei
+
+class Rangei {
+public:
+ Rangei(int _min = 0.f, int _max = 0.f)
+ : min_(_min)
+ , max_(_max)
+ {}
+
+ int minimum() const {
+ return min_;
+ }
+ void setMinimum(int _min) {
+ min_ = _min;
+ }
+
+ int maximum() const {
+ return max_;
+ }
+ void setMaximum(int _max) {
+ max_ = _max;
+ }
+
+ void set(int _min, int _max);
+
+ int length() const {
+ return max_ - min_;
+ }
+ int center() const {
+ return (max_ + min_) / 2;
+ }
+
+ /// ÐоÑÑекÑен ли инÑеÑвал (Ð½ÐµÑ - в ÑлÑÑае когда minimum > maximum);
+ bool is_valid() const {
+ return min_ <= max_;
+ }
+
+ /// ÐклÑÑÐ°ÐµÑ Ð»Ð¸ оÑÑезок (закÑÑÑÑй инÑеÑвал) ÑоÑÐºÑ \c _value.
+ bool include(int _value) const {
+ return (min_ <= _value) && (max_ >= _value);
+ }
+ /// ÐклÑÑÐ°ÐµÑ Ð»Ð¸ инÑеÑвал в ÑÐµÐ±Ñ \c _range.
+ bool include(const Rangei& _range) const {
+ return min_ <= _range.min_ && max_ >= _range.max_;
+ }
+
+ /// ÐозвÑаÑÐ°ÐµÑ Ð¿ÐµÑеÑеÑение инÑеÑвала *this и \c _range.
+ Rangei intersection(const Rangei& _range);
+
+ /// ÐозвÑаÑÐ°ÐµÑ \c _value в пÑеделаÑ
инÑеÑвала [minimum, maximum].
+ int clip(int &_value);
+
+private:
+ int min_;
+ int max_;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_RANGE_H
diff --git a/engines/qdengine/minigames/adv/Rect.h b/engines/qdengine/minigames/adv/Rect.h
new file mode 100644
index 00000000000..54f12cd7f64
--- /dev/null
+++ b/engines/qdengine/minigames/adv/Rect.h
@@ -0,0 +1,362 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_RECT_H
+#define QDENGINE_MINIGAMES_ADV_RECT_H
+
+#include "qdengine/minigames/adv/Range.h"
+
+namespace QDEngine {
+
+/*
+ * FIXME: ÐодÑазÑмеваеÑÑÑ, ÑÑо left < right и top < bottom, добавиÑÑ
+ * ÑÑÑаÑÐµÐ³Ð¸Ñ Ð´Ð»Ñ ÐºÑÑÑомизаÑии ÑÑого понÑÑиÑ?
+ */
+
+/// ÐбÑÑÑакÑÑй пÑÑмоÑголÑник.
+/**
+ * @param ScalarType - ÑкалÑÑнÑй Ñип
+ * @param VectType - векÑоÑнÑй Ñип
+ */
+template<typename scalar_type, class vect_type>
+struct Rect {
+ typedef typename vect_type VectType;
+ typedef typename scalar_type ScalarType;
+ typedef Rect<ScalarType, VectType> RectType;
+ typedef Rangef RangeType;
+
+ // конÑÑÑÑкÑоÑÑ
+ Rect() :
+ left_(ScalarType(0)),
+ top_(ScalarType(0)),
+ width_(ScalarType(0)),
+ height_(ScalarType(0)) {}
+
+ /// СоздаÑÑ Rect ÑазмеÑа \a _size, левÑй-веÑÑ
ний Ñгол оÑÑаÑÑÑÑ Ð² ÑоÑке (0, 0).
+ Rect(const VectType& _size) :
+ top_(ScalarType(0)),
+ left_(ScalarType(0)),
+ width_(_size.x),
+ height_(_size.y) {}
+
+ Rect(ScalarType _left, ScalarType _top, ScalarType _width, ScalarType _height) :
+ left_(_left),
+ top_(_top),
+ width_(_width),
+ height_(_height) {}
+
+ Rect(const VectType& _top_left, const VectType& _size) :
+ left_(_top_left.x),
+ top_(_top_left.y),
+ width_(_size.x),
+ height_(_size.y) {}
+
+ void set(ScalarType _left, ScalarType _top, ScalarType _width, ScalarType _height) {
+ left_ = _left;
+ top_ = _top;
+ width_ = _width;
+ height_ = _height;
+ }
+
+ inline ScalarType left() const {
+ return left_;
+ }
+ inline ScalarType top() const {
+ return top_;
+ }
+ inline ScalarType width() const {
+ return width_;
+ }
+ inline ScalarType height() const {
+ return height_;
+ }
+
+ VectType left_top() const {
+ return VectType(left_, top_);
+ }
+ VectType right_top() const {
+ return VectType(left + width_, top_);
+ }
+ VectType left_bottom() const {
+ return VectType(left_, top_ + height_);
+ }
+ VectType right_bottom() const {
+ return VectType(left_ + width_, top_ + height_);
+ }
+
+ // акÑеÑÑоÑÑ (вÑÑиÑлÑÑÑие):
+ inline ScalarType right() const {
+ return left_ + width_;
+ }
+ inline ScalarType bottom() const {
+ return top_ + height_;
+ }
+
+ /*
+ * FIXME: Ð´Ð»Ñ float и double деление на 2 лÑÑÑе замениÑÑ Ð½Ð° Ñмножение на 0.5,
+ * Ð´Ð»Ñ ÑелÑÑ
Ñипов лÑÑÑе иÑползоваÑÑ Ñдвиг.
+ */
+
+ /// ÐозвÑаÑÐ°ÐµÑ ÐºÐ¾Ð¾ÑдинаÑÑ ÑеÑÑа пÑÑмоÑголÑника.
+ inline VectType center() const {
+ return VectType(left_ + width_ / ScalarType(2),
+ top_ + height_ / ScalarType(2));
+ }
+ /// ÐозвÑаÑÐ°ÐµÑ ÑÐ°Ð·Ð¼ÐµÑ Ð¿ÑÑмоÑголÑника.
+ inline VectType size() const {
+ return VectType(width_, height_);
+ }
+
+ // ÑеÑÑеÑÑ:
+ inline void left(ScalarType _left) {
+ left_ = _left;
+ }
+ inline void top(ScalarType _top) {
+ top_ = _top;
+ }
+ inline void width(ScalarType _width) {
+ width_ = _width;
+ }
+ inline void height(ScalarType _height) {
+ height_ = _height;
+ }
+
+ // ÑеÑÑеÑÑ (вÑÑиÑлÑÑÑие):
+ inline void right(ScalarType _right) {
+ left_ = _right - width_;
+ }
+ inline void bottom(ScalarType _bottom) {
+ top_ = _bottom - height_;
+ }
+
+ /// ÐеÑеноÑÐ¸Ñ ÑенÑÑ Ð¿ÑÑмоÑголÑника в ÑоÑÐºÑ \a _center не изменÑÑ ÐµÐ³Ð¾ ÑазмеÑ.
+ inline void center(const VectType& _center) {
+ left_ = _center.x - width_ / ScalarType(2);
+ top_ = _center.y - height_ / ScalarType(2);
+ }
+ /*
+ * FIXME: ÑÐ°Ð·Ð¼ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ менÑÑÑÑ Ð¾ÑноÑиÑелÑно левого-веÑÑ
него Ñгла (как Ñ
+ * ÑеÑÑеÑов width и height) или оÑноÑиÑелÑно ÑенÑÑа? ÐобавиÑÑ
+ * клаÑÑ-ÑÑÑаÑÐµÐ³Ð¸Ñ Ð´Ð»Ñ ÑÑиÑ
Ñелей? ФÑнÑÐ¸Ñ Ñ Ð´ÑÑгим именем (напÑ
+ * scale (), коÑоÑÐ°Ñ Ð¿ÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ ÑенÑÑ, оÑноÑиÑелÑно коÑоÑого пÑоиÑÑ
одиÑ
+ * ÑкÑлинг)?
+ */
+ /// УÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Ð½Ð¾Ð²Ñе ÑазмеÑÑ, ÑоÑ
ÑанÑÑ Ð»ÐµÐ²Ñй-веÑÑ
ний Ñгол в пÑеждней ÑоÑке.
+ inline void size(const VectType& _size) {
+ width_ = _size.x;
+ height_ = _size.y;
+ }
+
+ // ÑÑилиÑÑ:
+
+ /// ÐÑовеÑÑÐµÑ Ð½Ðµ наÑ
одиÑÑÑ Ð»Ð¸ ÑоÑка \a _point внÑÑÑи пÑÑмоÑголÑника
+ inline bool point_inside(const VectType& _point) const {
+ if (_point.x >= left() && _point.y >= top() &&
+ _point.x <= right() && _point.y <= bottom())
+ return true;
+ else
+ return false;
+ }
+ /// ÐÑовеÑÑÐµÑ Ð½Ðµ наÑ
одиÑÑÑ Ð»Ð¸ пÑÑмоÑголÑник \a _rect внÑÑÑи пÑÑмоÑголÑника
+ inline bool rect_inside(const RectType& _rect) const {
+ if (_rect.left() >= left() && _rect.top() >= top() &&
+ _rect.bottom() <= bottom() && _rect.right() <= right())
+ return true;
+ else
+ return false;
+ }
+
+ inline bool rect_overlap(const RectType& _rect) const {
+ if (left() > _rect.right() || right() < _rect.left()
+ || top() > _rect.bottom() || bottom() < _rect.top())
+ return false;
+
+ return true;
+ }
+
+ /// ÐÑÐ¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ ÑкÑлинг.
+ /**
+ * ÐозвÑаÑÐ°ÐµÑ ÐºÐ¾Ð¿Ð¸Ñ Ð¿ÑÑмоÑголÑника, над коÑоÑой пÑоизведÑн ÑкÑлинг
+ * оÑноÑиÑелÑно ÑоÑки \a _origin.
+ */
+ inline RectType scaled(const VectType& _scale, const VectType& _origin) const {
+ return (*this - _origin) * _scale + _origin;
+ }
+
+ /// ÐÑпÑавлÑÐµÑ Ð¾ÑÑиÑаÑелÑнÑÑ ÑиÑинÑ/вÑÑоÑÑ
+ inline void validate() {
+ if (width() < ScalarType(0)) {
+ left(left() + width());
+ width(-width());
+ }
+ if (height() < ScalarType(0)) {
+ top(top() + height());
+ height(-height());
+ }
+ }
+
+ inline RectType intersection(const RectType& _rect) const {
+ RangeType xRange = RangeType(left(), right()).intersection(RangeType(_rect.left(), _rect.right()));
+ RangeType yRange = RangeType(top(), bottom()).intersection(RangeType(_rect.top(), _rect.bottom()));
+ return RectType(xRange.minimum(), yRange.minimum(), xRange.length(), yRange.length());
+ }
+
+ // ÐпеÑаÑоÑÑ
+ RectType operator+(const VectType& _point) const {
+ return RectType(left() + _point.x, top() + _point.y,
+ width(), height());
+ }
+
+ RectType operator-(const VectType& _point) const {
+ return RectType(left() - _point.x, top() - _point.y,
+ width(), height());
+ }
+
+ RectType operator*(const VectType& point) const {
+ return RectType(left() * point.x, top() * point.y,
+ width() * point.x, height() * point.y);
+ }
+
+ RectType operator*(const RectType& rhs) const {
+ VectType leftTop(left() + width() * rhs.left(), top() + height() * rhs.top());
+ VectType size(this->size() * rhs.size());
+ return RectType(leftTop, size);
+ }
+
+ RectType operator/(const RectType& rhs) const {
+ VectType leftTop((left() - rhs.left()) / rhs.width(), (top() - rhs.top()) / rhs.height());
+ VectType size(width() / rhs.width(), height() / rhs.height());
+ return RectType(leftTop, size);
+ }
+
+ RectType operator/(const VectType& _point) const {
+ return RectType(left() / _point.x, top() / _point.y,
+ width() / _point.x, height() / _point.y);
+ }
+
+ bool operator==(const RectType& rect) const {
+ return (left_ == rect.left_ && top_ == rect.top_ &&
+ width_ == rect.width_ && height_ == rect.height_);
+ }
+
+ bool eq(const RectType& rect, ScalarType eps = FLT_COMPARE_TOLERANCE) const {
+ return (abs(left_ - rect.left_) < eps && abs(top_ - rect.top_) < eps &&
+ abs(width_ - rect.width_) < eps && abs(height_ - rect.height_) < eps);
+ }
+
+ bool operator!=(const RectType& rect) const {
+ return (left_ != rect.left_ || top_ != rect.top_ ||
+ width_ != rect.width_ || height_ != rect.height_);
+ }
+
+protected:
+ ScalarType left_;
+ ScalarType top_;
+ ScalarType width_;
+ ScalarType height_;
+
+public:
+ // SideKick на ÑÑом обламÑваеÑÑÑ:
+ template<class ST, class VT>
+ operator ::Rect<ST, VT>() const {
+ return ::Rect<ST, VT>(static_cast<ST>(left()),
+ static_cast<ST>(top()),
+ static_cast<ST>(width()),
+ static_cast<ST>(height()));
+ }
+
+
+ bool clipLine(VectType& pos0, VectType& pos1) const;
+};
+
+template<typename ScalarType, class VectType>
+bool Rect<ScalarType, VectType>::clipLine(VectType& pos0, VectType& pos1) const {
+ VectType p0(pos0), p1(pos1);
+
+ bool b0 = point_inside(p0);
+ bool b1 = point_inside(p1);
+
+ if (b0 && b1) // вÑÑ Ð»Ð¸Ð½Ð¸Ñ Ð²Ð½ÑÑÑи clip
+ return true;
+ else {
+ float tc;
+ float t[4] = {-1.0f, -1.0f, -1.0f, -1.0f};
+ int find = 0;
+ ScalarType dx = p1.x - p0.x;
+ ScalarType dy = p1.y - p0.y;
+
+ ScalarType crd;
+
+ if (abs(dy) > 0) {
+ tc = (float)(top() - p0.y) / dy;
+ if (tc >= 0.0f && tc <= 1.0f) {
+ crd = p0.x + tc * dx;
+ if (crd >= left() && crd <= right())
+ t[find++] = tc;
+ }
+
+ tc = (float)(bottom() - p0.y) / dy;
+ if (tc >= 0.0f && tc <= 1.0f) {
+ crd = p0.x + tc * dx;
+ if (crd >= left() && crd <= right())
+ t[find++] = tc;
+ }
+ }
+
+ if (abs(dx) > 0) {
+ tc = (float)(left() - p0.x) / dx;
+ if (tc >= 0.0f && tc <= 1.0f) {
+ crd = p0.y + tc * dy;
+ if (crd >= top() && crd <= bottom())
+ t[find++] = tc;
+ }
+
+ tc = (float)(right() - p0.x) / dx;
+ if (tc >= 0.0f && tc <= 1.0f) {
+ crd = p0.y + tc * dy;
+ if (crd >= top() && crd <= bottom())
+ t[find++] = tc;
+ }
+ }
+
+ if (b0) { //внÑÑÑи ÑолÑко ÑоÑка p0
+ pos1.set(p0.x + t[0]*dx, p0.y + t[0]*dy);
+ pos0.set(p0.x, p0.y);
+ } else if (b1) { //внÑÑÑи ÑолÑко ÑоÑка p1
+ pos0.set(p0.x + t[0]*dx, p0.y + t[0]*dy);
+ pos1.set(p1.x, p1.y);
+ } else if (find) { //обе ÑоÑки ÑнаÑÑжи, но ÑаÑÑÑ Ð¾ÑÑезка внÑÑÑи
+ if (t[0] < t[1]) {
+ pos0.set(p0.x + t[0]*dx, p0.y + t[0]*dy);
+ pos1.set(p0.x + t[1]*dx, p0.y + t[1]*dy);
+ } else {
+ pos1.set(p0.x + t[0]*dx, p0.y + t[0]*dy);
+ pos0.set(p0.x + t[1]*dx, p0.y + t[1]*dy);
+ }
+ } else
+ return false;
+ }
+ return true;
+}
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_RECT_H
diff --git a/engines/qdengine/minigames/adv/RunTime.cpp b/engines/qdengine/minigames/adv/RunTime.cpp
new file mode 100644
index 00000000000..3556ce88428
--- /dev/null
+++ b/engines/qdengine/minigames/adv/RunTime.cpp
@@ -0,0 +1,1041 @@
+/* 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 "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 *runtime = 0;
+// createGame() должна ÑеализоваÑÑÑÑ Ð½ÐµÐ¿Ð¾ÑÑедÑÑвенно в ÐÐÐÐÐРпÑоекÑе игÑÑ
+MinigameInterface *createGame();
+
+class TimeManager {
+ enum Direction {
+ UP,
+ LEFT,
+ RIGHT,
+ DOWN
+ };
+public:
+ TimeManager(HoldData<TimeManagerData> &data);
+ ~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::MinigameManager()
+ : currentGameIndex_(-1, -1) {
+ state_container_name_ = "Saves\\minigames.dat";
+
+ 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;
+}
+
+MinigameManager::~MinigameManager() {
+ xassert(!engine_ && !scene_);
+
+ GameInfoMap::iterator it;
+ FOR_EACH(gameInfos_, it) {
+ //dprintf("free: (%d,%d)\n", it->first.gameLevel_, it->first.gameNum_);
+ it->second.free();
+ }
+}
+
+bool MinigameManager::init(const qdEngineInterface* engine_interface) {
+ dprintf("init game\n");
+
+ xxassert(runtime == this, "ÐопÑÑка одновÑеменного запÑÑка дÑÐ±Ð»Ñ Ð¼Ð¸Ð½Ð¸Ð¸Ð³ÑÑ");
+ if (runtime != this)
+ return false;
+ xassert(!engine_ && !scene_);
+
+ xassert(engine_interface);
+ if (!engine_interface)
+ return false;
+
+ engine_ = engine_interface;
+ scene_ = engine_->current_scene_interface();
+
+ xassert(scene_);
+ if (!scene_) {
+ engine_ = 0;
+ return false;
+ }
+
+ if (!createGame()) {
+ xxassert(0, "ÐгÑа не Ñмогла пÑоиниÑиализиÑоваÑÑÑÑ");
+ finit();
+ return false;
+ }
+
+ saveState();
+
+ return true;
+}
+
+bool MinigameManager::createGame() {
+ xassert(engine_ && scene_);
+ xassert(!game_);
+
+ screenSize_ = engine_->screen_size();
+
+ #ifdef _DEBUG
+ debugMode_ = getParameter("debug_mode", false);
+ dprintf("%s", debugMode_ ? "DEBUG MODE\n" : "");
+ #endif
+
+ seed_ = 0;
+
+ if (!loadState())
+ return false;
+
+ if (currentGameInfo_) {
+ dprintf("level: %d, game: %d, index: %d\n", currentGameIndex_.gameLevel_, currentGameIndex_.gameNum_, currentGameInfo_->game_.sequenceIndex_);
+ dprintf("%s\n", 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_);
+ dprintf("seed = %d\n", seed_);
+
+ invertMouseButtons_ = getParameter("invert_mouse_buttons", false);
+ mouseAdjast_ = getParameter("ajast_mouse", mgVect2f());
+
+ HoldData<TimeManagerData> timeData(currentGameInfo_ ? ¤tGameInfo_->timeManagerData_ : 0, !currentGameInfo_ || currentGameInfo_->empty_);
+ timeManager_ = new TimeManager(timeData);
+
+ textManager_ = new TextManager();
+
+ eventManager_ = new EventManager();
+
+ HoldData<EffectManagerData> effectData(currentGameInfo_ ? ¤tGameInfo_->effectManagerData_ : 0, !currentGameInfo_ || currentGameInfo_->empty_);
+ effectManager_ = new EffectManager(effectData);
+
+ const char *stateFlagName = parameter("state_flag_name", "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")) {
+ xxassert(false, (XBuffer() < "У обÑекÑа \"" < stateFlagName < "\" Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ ÑоÑÑоÑниÑ: game, win, lose").c_str());
+ return false;
+ }
+ } else {
+ xxassert(false, (XBuffer() < "ÐÑÑÑÑÑÑвÑÐµÑ Ð¾Ð±ÑÐµÐºÑ Ð¿ÐµÑедаÑи ÑоÑÑоÑÐ½Ð¸Ñ \"" < stateFlagName < "\"").c_str());
+ return false;
+ }
+
+ const char *pauseFlagName = parameter("pause_flag_name", "BackHelp");
+
+ if (pause_flag_ = scene_->object_interface(pauseFlagName)) {
+ if (!pause_flag_->has_state("on")) {
+ xxassert(false, (XBuffer() < "У обÑекÑа \"" < pauseFlagName < "\" должно бÑÑÑ ÑоÑÑоÑние: on").c_str());
+ 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")) {
+ xxassert(false, (XBuffer() < "У обÑекÑа Ð´Ð»Ñ Ð¾ÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ ÑобÑанной игÑÑ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð±ÑÑÑ ÑоÑÑоÑниÑ: off, 01").c_str());
+ return false;
+ }
+ } else {
+ xxassert(false, (XBuffer() < "Ðе найден обÑÐµÐºÑ Ð´Ð»Ñ Ð¾ÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ ÑобÑанной игÑÑ").c_str());
+ 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");
+ }
+
+ game_ = ::createGame();
+
+ 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() {
+ dprintf("finit game\n");
+ if (!engine_)
+ return false;
+
+ delete game_;
+ game_ = 0;
+
+ delete effectManager_;
+ effectManager_ = 0;
+
+ delete eventManager_;
+ eventManager_ = 0;
+
+ delete textManager_;
+ textManager_ = 0;
+
+ delete timeManager_;
+ timeManager_ = 0;
+
+ 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)) {
+ dprintf("new game skiped\n");
+ return false;
+ }
+ dprintf("new game\n");
+
+ GameInfoMap::iterator it;
+ FOR_EACH(gameInfos_, it) {
+ dprintf("clean game data (%d, %d)\n", it->first.gameLevel_, it->first.gameNum_);
+ it->second.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) {
+ xassert(new_runtime);
+ pre_runtime_ = runtime;
+ runtime = new_runtime;
+
+ xassert(new_engine && new_scene);
+ pre_engine_ = runtime->engine_;
+ pre_scene_ = runtime->scene_;
+
+ runtime->engine_ = new_engine;
+ runtime->scene_ = new_scene;
+ }
+ ~TempValue() {
+ runtime->engine_ = pre_engine_;
+ runtime->scene_ = pre_scene_;
+
+ runtime = pre_runtime_;
+ }
+};
+
+#define TEMP_SCENE_ENTER() TempValue tempSceneObject(this, engine, const_cast<qdMinigameSceneInterface*>(scene))
+
+int MinigameManager::save_game(const qdEngineInterface* engine, const qdMinigameSceneInterface* scene, char* buffer, int buffer_size) {
+ dprintf("save game\n");
+ TEMP_SCENE_ENTER();
+ loadState();
+ if (currentGameInfo_ && !currentGameInfo_->empty()) {
+ dprintf("save game (%d, %d)\n", currentGameIndex_.gameLevel_, currentGameIndex_.gameNum_);
+ XBuffer out((void*)buffer, buffer_size);
+ out.write(GameInfo::version());
+ out.write(currentGameInfo_->game_);
+ return out.tell();
+ }
+ return 0;
+
+}
+
+int MinigameManager::load_game(const qdEngineInterface* engine, const qdMinigameSceneInterface* scene, const char* buffer, int buffer_size) {
+ xassert(!game_);
+ if (game_) {
+ dprintf("load game skiped\n");
+ return buffer_size;
+ }
+ dprintf("load game\n");
+ TEMP_SCENE_ENTER();
+ loadState();
+ if (currentGameInfo_) {
+ if (buffer_size > 0) {
+ dprintf("load game (%d, %d)\n", currentGameIndex_.gameLevel_, currentGameIndex_.gameNum_);
+ XBuffer in((void*)buffer, buffer_size);
+ int version;
+ in.read(version);
+ if (version == GameInfo::version()) {
+ in.read(currentGameInfo_->game_);
+ xxassert(!currentGameInfo_->empty_, "ÐагÑÑзка даннÑÑ
по миниигÑе без даннÑÑ
о ÑÑене. РекомендÑеÑÑÑ ÑдалиÑÑ Ð²Ñе ÑоÑ
ÑанениÑ.");
+ if (in.tell() != buffer_size) {
+ currentGameInfo_->game_ = MinigameData();
+ xxassert(0, "Ðе ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ ÑÐ°Ð·Ð¼ÐµÑ Ð´Ð°Ð½Ð½ÑÑ
в ÑоÑ
Ñанении и в миниигÑе.");
+ return 0;
+ }
+ } else {
+ xxassert(0, "ÐеÑовмеÑÑÐ¸Ð¼Ð°Ñ Ð²ÐµÑÑÐ¸Ñ ÑоÑ
ÑÐ°Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð¼Ð¸Ð½Ð¸Ð¸Ð³ÑÑ.");
+ return 0;
+ }
+ } else {
+ dprintf("clean game (%d, %d)\n", currentGameIndex_.gameLevel_, currentGameIndex_.gameNum_);
+ currentGameInfo_->game_ = MinigameData();
+ }
+ saveState();
+ }
+ return buffer_size;
+
+}
+
+bool MinigameManager::loadState(bool current) {
+ if (game_) {
+ dprintf("load state skiped\n");
+ return false;
+ }
+ dprintf("load state\n");
+ 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)
+ dprintf("current game: (%d,%d)\n", currentGameIndex_.gameLevel_, currentGameIndex_.gameNum_);
+
+ XStream file(false);
+ if (file.open(state_container_name_, XS_IN)) {
+ int version;
+ file > version;
+ if (version != GameInfo::version()) {
+ xxassert(0, (XBuffer() < "Ðе ÑÐ¾Ð²Ð¿Ð°Ð´Ð°ÐµÑ Ð²ÐµÑÑÐ¸Ñ ÑоÑ
ÑÐ°Ð½ÐµÐ½Ð¸Ñ ÑоÑÑоÑÐ½Ð¸Ñ Ð¼Ð¸Ð½Ð¸Ð¸Ð³ÑÑ. УдалиÑе " < state_container_name_).c_str());
+ return false;
+ }
+ file > seed_;
+ GameInfoIndex index(0, 0);
+ while (!file.eof()) {
+ file.read(index);
+ xassert(gameInfos_.find(index) == gameInfos_.end());
+ if (file.eof())
+ return false;
+ {
+ GameInfo data;
+ file > data;
+ dprintf("read game info: (%d,%d), index: %d, game data:%d\n", index.gameLevel_, index.gameNum_, data.game_.sequenceIndex_, data.empty_ ? 0 : 1);
+ if (data.game_.sequenceIndex_ >= 0)
+ completeCounters_[index.gameLevel_]++;
+ gameInfos_[index] = data;
+ }
+ }
+ }
+
+ currentGameInfo_ = current ? &gameInfos_[currentGameIndex_] : 0;
+ }
+ return true;
+}
+
+extern bool createDirForFile(const char* partialPath);
+void MinigameManager::saveState(bool force) {
+ dprintf("save state\n");
+ if (force || currentGameIndex_.gameNum_ >= 0) {
+ XStream file(false);
+ if (createDirForFile(state_container_name_) && file.open(state_container_name_, XS_OUT)) {
+ file < GameInfo::version();
+ file < (engine_ ? engine_->rnd(999999) : seed_);
+ GameInfoMap::const_iterator it;
+ FOR_EACH(gameInfos_, it)
+ if (!it->second.empty()) {
+ dprintf("write game info: (%d,%d), index: %d, game data:%d\n", it->first.gameLevel_, it->first.gameNum_, it->second.game_.sequenceIndex_, it->second.empty_ ? 0 : 1);
+ file.write(it->first);
+ file < it->second;
+ }
+ } else {
+ xxassert(0, (XBuffer() < "Ðе ÑдалоÑÑ ÑоÑ
ÑаниÑÑ Ð¿ÑогÑеÑÑ Ð² Ñайл: \"" < 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_) {
+ xassert(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");
+
+ #ifdef _DEBUG
+ if (keyPressed(VK_MULTIPLY, true))
+ game_->setState(MinigameInterface::GAME_WIN);
+ #endif
+
+ switch (game_->state()) {
+ case MinigameInterface::GAME_LOST:
+ if (!timeManager_->timeIsOut())
+ signal(EVENT_GAME_LOSE);
+ case MinigameInterface::NOT_INITED:
+ gameLose();
+ break;
+
+ case MinigameInterface::GAME_WIN:
+ signal(EVENT_GAME_WIN);
+ gameWin();
+ 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) {
+ xassert(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->second.game_;
+ return 0;
+}
+
+bool MinigameManager::testAllGamesWin() {
+ XStream file(false);
+ if (!file.open(gameListFileName(), XS_IN))
+ return false;
+
+ char read_buf[512];
+ while (!file.eof()) {
+ file.getline(read_buf, 512);
+ XBuffer xbuf((void*)read_buf, strlen(read_buf));
+ int level;
+ xbuf >= level;
+ unsigned char ch;
+ xbuf > ch;
+ if (ch != ':') {
+ xxassert(ch != ':', (XBuffer() < "ÐепÑавилÑнÑй ÑоÑÐ¼Ð°Ñ Ñайла \"" < gameListFileName() < "\"").c_str());
+ return false;
+ }
+ while (xbuf.tell() < xbuf.size()) {
+ xbuf > ch;
+ if (isdigit(ch)) {
+ --xbuf;
+ int game;
+ xbuf >= game;
+ const MinigameData* data = getScore(level, game);
+ if (!data || data->sequenceIndex_ == -1)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void MinigameManager::gameWin() {
+ dprintf("Game Win\n");
+ state_flag_->set_state("win");
+
+ if (debugMode() || !currentGameInfo_)
+ return;
+
+ xassert(currentGameIndex_.gameNum_ >= 0);
+
+ effectManager_->start(EFFECT_1);
+
+ if (currentGameIndex_.gameNum_ == 0)
+ return;
+
+ int gameTime = round(time());
+ 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_) {
+ dprintf("ÑÑÑановлен новÑй ÑекоÑд оÑков.\n");
+ currentGameInfo_->game_.bestScore_ = eventManager_->score();
+ currentGameInfo_->game_.bestTime_ = gameTime;
+ }
+ } else {
+ dprintf("добавлÑем оÑки к ÑÑмме пÑоÑ
ождениÑ: %d\n", 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()) {
+ dprintf("ÐÑе игÑÑ Ð¿ÑойденÑ, добавлена запиÑÑ Ð² ÑаблиÑÑ ÑекоÑдов: %d\n", 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() {
+ dprintf("Game Lose\n");
+ state_flag_->set_state("lose");
+}
+
+const char *MinigameManager::parameter(const char* name, bool required) const {
+ xxassert(scene_, "СÑена не опÑеделена");
+ const char *txt = scene_->minigame_parameter(name);
+ xxassert(!required || txt, (XBuffer() < "Ðе задан обÑзаÑелÑнÑй паÑамеÑÑ [" < name < "] в ini Ñайле").c_str());
+ return txt;
+}
+
+const char *MinigameManager::parameter(const char* name, const char* def) const {
+ xxassert(def, (XBuffer() < "Ðе задано знаÑение по ÑмолÑÐ°Ð½Ð¸Ñ Ð´Ð»Ñ Ð¿Ð°ÑамеÑÑа [" < name < "]").c_str());
+ const char *txt = scene_->minigame_parameter(name);
+ xxassert(def || txt, (XBuffer() < "Ðе задан обÑзаÑелÑнÑй паÑамеÑÑ [" < name < "] в ini Ñайле").c_str());
+ 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 {
+ xassert(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<const mgVect2i &>(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 {
+ xxassert(name && *name, "ÐÑлевое Ð¸Ð¼Ñ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑÑение обÑекÑа");
+ if (!name || !*name)
+ return QDObject::ZERO;
+ qdMinigameObjectInterface* obj = scene_->object_interface(name);
+ xxassert(obj, (XBuffer() < "Ðе найден обÑекÑ: \"" < name < "\"").c_str());
+ if (obj)
+ return QDObject(obj, name);
+ return QDObject::ZERO;
+}
+
+bool MinigameManager::testObject(const char* name) const {
+ if (qdMinigameObjectInterface * obj = scene_->object_interface(name)) {
+ 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);
+ xxassert(counter, (XBuffer() < "Ðе найден ÑÑеÑÑик: \"" < name < "\"").c_str());
+ return counter;
+}
+
+void MinigameManager::release(QDCounter& counter) {
+ xxassert(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 vector<float> &prob) const {
+ float rnd = runtime->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;
+ }
+ xassert(idx >= 0 && idx < prob.size());
+ #ifdef _DEBUG
+ float sum = 0.f;
+ vector<float>::const_iterator pit;
+ FOR_EACH(prob, pit)
+ sum += *pit;
+ xassert(abs(sum - 1.f) < 0.0001f);
+ #endif
+ return idx;
+}
+
+
+//========================================================================================================================
+
+
+// еÑли даннÑе еÑе ни ÑÐ°Ð·Ñ Ð½Ðµ ÑоÑ
ÑанÑлиÑÑ - запоминаем
+// еÑли Ñже еÑÑÑ Ð·Ð°Ð¿Ð¾Ð¼Ð½ÐµÐ½Ð½Ñе, Ñо заменÑем на ниÑ
+bool MinigameManager::processGameData(XBuffer& data) {
+ if (currentGameInfo_) {
+ if (currentGameInfo_->empty_) {
+ currentGameInfo_->empty_ = false;
+ xassert(data.tell());
+ currentGameInfo_->write(data.buffer(), data.tell());
+ } else {
+ xxassert(data.tell() == currentGameInfo_->dataSize_, (XBuffer() < "СилÑно ÑÑÑаÑевÑее ÑоÑ
Ñанение ÑоÑÑоÑÐ½Ð¸Ñ Ð¼Ð¸Ð½Ð¸Ð¸Ð³ÑÑ. УдалиÑе " < state_container_name_).c_str());
+ if (data.tell() == currentGameInfo_->dataSize_) {
+ data.set(0);
+ data.write(currentGameInfo_->gameData_, currentGameInfo_->dataSize_, true);
+ } else {
+ data.set(0);
+ return false;
+ }
+ }
+ }
+ data.set(0);
+ return true;
+}
+
+MinigameData::MinigameData() {
+ sequenceIndex_ = -1;
+ lastScore_ = 0;
+ lastTime_ = 0;
+ bestTime_ = 0;
+ bestScore_ = 0;
+}
+
+GameInfo::GameInfo() {
+ empty_ = true;
+ dataSize_ = 0;
+ gameData_ = 0;
+}
+
+void GameInfo::free() {
+ if (gameData_) {
+ xassert(dataSize_ > 0);
+ //dprintf("memory free: %#x\n", gameData_);
+ ::free(gameData_);
+ gameData_ = 0;
+ }
+ dataSize_ = 0;
+}
+
+void GameInfo::write(void* data, unsigned int size) {
+ if (dataSize_ != size) {
+ free();
+ if (size > 0) {
+ gameData_ = malloc(size);
+ dataSize_ = size;
+ //dprintf("memory alloc: %#x, %d bytes\n", gameData_, size);
+ }
+ }
+ if (dataSize_ > 0)
+ memcpy(gameData_, data, dataSize_);
+}
+
+XStream &operator< (XStream& out, const GameInfo& info) {
+ out.write(info.game_);
+ out.write(info.empty_);
+ if (!info.empty_) {
+ out.write(info.timeManagerData_);
+ out.write(info.effectManagerData_);
+ out < info.dataSize_;
+ if (info.dataSize_ > 0)
+ out.write(info.gameData_, info.dataSize_);
+ }
+ return out;
+}
+
+XStream &operator> (XStream& in, GameInfo& info) {
+ in.read(info.game_);
+ in.read(info.empty_);
+ if (!info.empty_) {
+ in.read(info.timeManagerData_);
+ in.read(info.effectManagerData_);
+ unsigned int size;
+ in > size;
+ XBuffer buf(size);
+ in.read(buf.buffer(), size);
+ info.write(buf.buffer(), size);
+ }
+ return in;
+}
+
+
+//========================================================================================================================
+
+
+TimeManager::TimeManager(HoldData<TimeManagerData> &data) {
+ 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_);
+ }
+
+ 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) {
+ xassert(dir >= 0 && dir <= 3);
+ direction_ = Direction(dir);
+ } else
+ direction_ = DOWN;
+ } else
+ direction_ = DOWN;
+ } else
+ size_ = mgVect2f(-1.f, -1.f);
+
+ xassert(runtime->time() == 0.f);
+
+ lastEventTime_ = 0;
+
+}
+
+TimeManager::~TimeManager() {
+ if (timeBar_)
+ runtime->release(timeBar_);
+
+}
+
+bool TimeManager::timeIsOut() const {
+ if (gameTime_ > 0.f)
+ return runtime->time() > gameTime_;
+ return false;
+
+}
+
+float TimeManager::leftTime() const {
+ if (gameTime_ <= 0.f)
+ return 0;
+ return runtime->time() > gameTime_ ? 0 : gameTime_ - runtime->time();
+
+}
+
+void TimeManager::quant(float dt) {
+ int seconds = round(runtime->time());
+ 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->time() / 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
diff --git a/engines/qdengine/minigames/adv/RunTime.h b/engines/qdengine/minigames/adv/RunTime.h
new file mode 100644
index 00000000000..69ffa02410b
--- /dev/null
+++ b/engines/qdengine/minigames/adv/RunTime.h
@@ -0,0 +1,272 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_RUNTIME_H
+#define QDENGINE_MINIGAMES_ADV_RUNTIME_H
+
+namespace QDEngine {
+
+class qdEngineInterface;
+class qdMinigameSceneInterface;
+
+class MinigameInterface;
+class TextManager;
+class TimeManager;
+enum SystemEvent;
+class EventManager;
+class EffectManager;
+
+struct TimeManagerData {
+ mgVect3f crd;
+};
+
+struct EffectManagerData {
+ mgVect3f crd;
+};
+
+struct MinigameData {
+ MinigameData();
+ int sequenceIndex_;
+ int lastScore_;
+ int lastTime_;
+ int bestTime_;
+ int bestScore_;
+};
+
+struct GameInfo {
+ GameInfo();
+ void write(void* data, unsigned int size);
+ void free();
+ static int version() {
+ return 9;
+ }
+ bool empty() const {
+ return empty_ && game_.sequenceIndex_ < 0;
+ }
+
+ MinigameData game_;
+ bool empty_;
+ TimeManagerData timeManagerData_;
+ EffectManagerData effectManagerData_;
+ unsigned int dataSize_;
+ void *gameData_;
+};
+
+class XStream;
+XStream &operator< (XStream& out, const GameInfo& info);
+XStream &operator> (XStream& in, GameInfo& info);
+
+class MinigameManager : public qdMiniGameInterface {
+ friend class TempValue;
+public:
+ MinigameManager();
+ ~MinigameManager();
+
+ // begin MiniGame virtual interface
+ bool init(const qdEngineInterface* engine_interface);
+ bool quant(float dt);
+ bool finit();
+
+ bool new_game(const qdEngineInterface* engine);
+ int save_game(const qdEngineInterface* engine, const qdMinigameSceneInterface* scene, char* buffer, int buffer_size);
+ int load_game(const qdEngineInterface* engine, const qdMinigameSceneInterface* scene, const char* buffer, int buffer_size);
+ // finish MiniGame virtual interface
+
+ // пÑи необÑ
одимоÑÑи заменÑÐµÑ Ð½Ð° неизмененнÑе пÑедÑдÑÑим пÑоÑ
ождением даннÑе
+ bool processGameData(XBuffer& data);
+
+ mgVect2f mousePosition() const {
+ return mousePos_;
+ }
+ bool mouseLeftPressed() const;
+ bool mouseRightPressed() const;
+ bool keyPressed(int vKey, bool once = false) const;
+
+ mgVect2i screenSize() const {
+ return screenSize_;
+ }
+ float time() const {
+ return gameTime_;
+ }
+
+ const MinigameData *getScore(int level, int game) const;
+
+ bool debugMode() const {
+ return debugMode_;
+ }
+
+ TextManager &textManager() const {
+ return *textManager_;
+ }
+
+ void signal(SystemEvent id);
+ void event(int eventID, const mgVect2f& pos, int factor = 1);
+ void event(int eventID, const mgVect2i& pos, int factor = 1) {
+ event(eventID, mgVect2f(pos.x, pos.y), factor);
+ }
+
+ // ÑказÑÐ²Ð°ÐµÑ Ð²Ð°ÑÐ¸Ð°Ð½Ñ Ð¿Ð¾ÐºÐ°Ð·Ð° инÑоÑмаÑии о победе (повоÑÐ¾Ñ ÑобÑанной каÑÑинки и Ñ.д.)
+ void setCompleteHelpVariant(int idx);
+ // ÑказÑÐ²Ð°ÐµÑ Ð½Ð¾Ð¼ÐµÑ Ð¿Ð¾Ð´Ñказки Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°, -1 - ÑпÑÑÑаÑÑ Ð¿Ð¾Ð´ÑказкÑ
+ void setGameHelpVariant(int idx);
+
+ // ÐозвÑаÑÐ°ÐµÑ Ð¿Ð°ÑамеÑÑ Ð¸Ð· пÑикÑепленного к игÑе ini Ñайла
+ const char *parameter(const char* name, bool required = true) const;
+ const char *parameter(const char* name, const char* def) const;
+
+ // ÐеÑеÑÑиÑÑÐ²Ð°ÐµÑ Ð¸Ð· ÑкÑаннÑÑ
кооÑÐ´Ð¸Ð½Ð°Ñ UI игÑÑ Ð² 3D кооÑдинаÑÑ R() обÑекÑа на миÑе
+ mgVect3f game2world(const mgVect3i& coord) const;
+ mgVect3f game2world(const mgVect3f& coord) const;
+ mgVect3f game2world(const mgVect2i& coord, int depth = 0) const;
+ mgVect3f game2world(const mgVect2f& coord, int depth = 0) const;
+ // ÐеÑеÑÑиÑÑÐ²Ð°ÐµÑ Ð¸Ð· миÑовÑÑ
кооÑÐ´Ð¸Ð½Ð°Ñ R() в 2D UI кооÑдинаÑÑ Ð¸ глÑбинÑ
+ mgVect2f world2game(const mgVect3f& pos) const;
+ mgVect3f world2game(qdMinigameObjectInterface* obj) const;
+ // ÑÐ°Ð·Ð¼ÐµÑ Ð¾Ð±ÑекÑа
+ mgVect2f getSize(qdMinigameObjectInterface* obj) const;
+
+ // ÐенÑÐµÑ Ð³Ð»ÑÐ±Ð¸Ð½Ñ Ð¾Ð±ÑекÑа, не менÑÑ ÐµÐ³Ð¾ 2D Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° ÑкÑане
+ void setDepth(qdMinigameObjectInterface* obj, int depth) const;
+ // ÐолÑÑÐ°ÐµÑ Ð³Ð»ÑÐ±Ð¸Ð½Ñ Ð¾Ð±ÑекÑа, Ñем менÑÑе, Ñем ближе к игÑокÑ
+ float getDepth(qdMinigameObjectInterface* obj) const;
+ // ÐолÑÑÐ°ÐµÑ Ð³Ð»ÑÐ±Ð¸Ð½Ñ ÑоÑки, Ñем менÑÑе, Ñем ближе к игÑокÑ
+ float getDepth(const mgVect3f& pos) const;
+
+ // полÑÑÐ°ÐµÑ Ð¸Ð½ÑеÑÑÐµÐ¹Ñ Ðº динамиÑеÑÐºÐ¾Ð¼Ñ Ð¸Ð³ÑÐ¾Ð²Ð¾Ð¼Ñ Ð¾Ð±ÑекÑÑ Ð¿Ð¾ имени
+ QDObject getObject(const char* name) const;
+ // пÑовеÑÑÐµÑ ÑÑÑеÑÑвование динамиÑеÑкого обÑекÑа в ÑÑене
+ bool testObject(const char* name) const;
+ // оÑÐ²Ð¾Ð±Ð¾Ð¶Ð´Ð°ÐµÑ Ð¸Ð½ÑеÑÑейÑ
+ void release(QDObject& obj);
+
+ // задаÑÑ ÑекÑÑ Ð´Ð»Ñ ÐºÐ¾Ð½ÑÑола
+ void setText(const char* name, const char* text) const;
+ void setText(const char* name, int toText, const char* format = "%d") const;
+
+ // ÑпÑÑÑаÑÑ Ð¾Ð±ÑÐµÐºÑ Ð·Ð° пÑеделами ÑкÑана
+ void hide(qdMinigameObjectInterface* obj) const;
+
+ // ÑлÑÑайное знаÑение в диапазоне [min, max]
+ float rnd(float min, float max) const;
+ int rnd(int min, int max) const;
+ // ÑлÑÑайнÑй диапазон, из набоÑа веÑоÑÑноÑÑей
+ int rnd(const vector<float> &prob) const;
+
+ // Ñайл Ñо ÑпиÑком Ð¸Ð³Ñ Ð¿Ð¾ ÑÑовнÑм
+ const char *gameListFileName() const {
+ return "resource\\minigames.lst";
+ }
+
+private:
+ MinigameInterface *game_;
+
+ // ÐÑвод ÑекÑÑа Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ Ð¾Ð±ÑекÑов
+ TextManager *textManager_;
+ // ÐодÑÑÐµÑ Ð¸ визÑализаÑÐ¸Ñ Ð²Ñемени
+ TimeManager *timeManager_;
+ // ÐбÑабоÑка ÑобÑÑий игÑÑ
+ EventManager *eventManager_;
+ // вÑводимÑе ÑÑÑекÑÑ
+ EffectManager *effectManager_;
+
+ // ÐÑÐµÐ¼Ñ Ð² ÑекÑндаÑ
Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñа ÑÑаÑа игÑÑ
+ float gameTime_;
+ // ÐºÐµÑ Ð¿ÑовеÑеннÑÑ
на нажаÑие клавиÑ, Ð´Ð»Ñ Ð¾ÑÑÐ»ÐµÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð½ÐµÐ¿Ð¾ÑÑедÑÑвенно нажаÑиÑ
+ mutable bool lastKeyChecked_[256];
+ // Ð Ð°Ð·Ð¼ÐµÑ Ð¸Ð³Ñана
+ mgVect2i screenSize_;
+ // ÑекÑÑее положение мÑÑи
+ mgVect2f mousePos_;
+ // подÑÑÑойка мÑÑи
+ mgVect2f mouseAdjast_;
+
+ // обÑÐµÐºÑ Ð´Ð»Ñ Ð¿ÐµÑедаÑи Ñигнала об оконÑании игÑÑ Ð² ÑÑиггеÑÑ
+ qdMinigameObjectInterface *state_flag_;
+ // обÑÐµÐºÑ Ð´Ð»Ñ Ð¿Ð¾Ð»ÑÑÐµÐ½Ð¸Ñ Ñигнала о поÑÑановке на паÑзÑ
+ qdMinigameObjectInterface *pause_flag_;
+ // ÑпÑавка по победе
+ QDObject complete_help_;
+ QDObject complete_help_miniature_;
+ // ÑекÑÑее ÑоÑÑоÑние Ð´Ð»Ñ Ð²ÐºÐ»ÑÑÐµÐ½Ð¸Ñ ÑпÑавки
+ string complete_help_state_name_;
+ // ÑпÑавка по игÑе
+ QDObject game_help_;
+ QDObject game_help_trigger_;
+ bool game_help_enabled_;
+ // ÑекÑÑее ÑоÑÑоÑние Ð´Ð»Ñ Ð²ÐºÐ»ÑÑÐµÐ½Ð¸Ñ ÑпÑавки
+ string game_help_state_name_;
+
+ // инÑеÑÑÐµÐ¹Ñ Ðº движкÑ
+ const qdEngineInterface *engine_;
+ // инÑеÑÑÐµÐ¹Ñ Ðº ÑекÑÑей ÑÑене
+ qdMinigameSceneInterface *scene_;
+
+ // игÑа запÑÑена Ð´Ð»Ñ Ð¾Ñладки
+ bool debugMode_;
+ // rnd seed
+ int seed_;
+
+ // кнопки мÑÑи инвеÑÑиÑованÑ
+ bool invertMouseButtons_;
+
+ // Ð¸Ð¼Ñ Ñайла и инÑоÑмаÑией о миниигÑаÑ
+ const char *state_container_name_;
+ // колиÑеÑÑво пÑойденнÑÑ
Ð¸Ð³Ñ Ð½Ð° каждом ÑÑовне
+ typedef map<int, int> Counters;
+ Counters completeCounters_;
+
+ struct GameInfoIndex {
+ GameInfoIndex(int idx, int level) : gameNum_(idx), gameLevel_(level) {}
+ int gameNum_;
+ int gameLevel_;
+ bool operator< (const GameInfoIndex& rs) const {
+ return gameLevel_ == rs.gameLevel_ ? gameNum_ < rs.gameNum_ : gameLevel_ < rs.gameLevel_;
+ }
+ };
+ // инÑоÑмаÑÐ¸Ñ Ð¾ пÑойденнÑÑ
игÑаÑ
+ typedef map<GameInfoIndex, GameInfo> GameInfoMap;
+ GameInfoMap gameInfos_;
+ // ÐнÑоÑмаÑÐ¸Ñ Ð¾ ÑекÑÑей игÑе, пÑи вÑÑ
оде запиÑеÑÑÑ
+ GameInfoIndex currentGameIndex_;
+ GameInfo *currentGameInfo_;
+
+ // пÑовеÑиÑÑ ÑÑо вÑе необÑ
одимÑе игÑÑ Ð¿ÑойденÑ
+ bool testAllGamesWin();
+ // ÐепоÑÑедÑÑвенно ÑÐ¾Ð·Ð´Ð°ÐµÑ Ð¸ иниÑиализиÑÑÐµÑ Ð¸Ð³ÑÑ
+ bool createGame();
+ // обÑабоÑка победÑ
+ void gameWin();
+ // обÑабоÑка поÑажениÑ
+ void gameLose();
+ // ÑÑение даннÑÑ
об игÑаÑ
+ bool loadState(bool current = true);
+ // ÑоÑ
Ñанение даннÑÑ
в Ñайл
+ void saveState(bool force = false);
+
+ // ÐолÑиÑÑ Ð¾Ð±ÑекÑ-ÑÑеÑÑик
+ QDCounter getCounter(const char* name);
+ // ÐÑвободиÑÑ ÑÑеÑÑик
+ void release(QDCounter& counter);
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_RUNTIME_H
diff --git a/engines/qdengine/minigames/adv/TextManager.cpp b/engines/qdengine/minigames/adv/TextManager.cpp
new file mode 100644
index 00000000000..1888dcbd378
--- /dev/null
+++ b/engines/qdengine/minigames/adv/TextManager.cpp
@@ -0,0 +1,367 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/TextManager.h"
+#include "qdengine/minigames/adv/RunTime.h"
+#include "qdengine/minigames/adv/qdMath.h"
+
+namespace QDEngine {
+
+TextManager::TextManager() {
+ char str_cache[256];
+
+ for (int idx = 0;; ++idx) {
+ _snprintf(str_cache, 127, "register_font_%d", idx);
+ if (const char * descr = runtime->parameter(str_cache, false)) {
+ sscanf(descr, "%255s", str_cache);
+ Font digit;
+ if (!digit.pool.load(str_cache))
+ break;
+ dprintf("%d character set \"%s\" loaded, ", idx, str_cache);
+ _snprintf(str_cache, 127, "font_size_%d", idx);
+ if (descr = runtime->parameter(str_cache, false)) {
+ int read = sscanf(descr, "%f %f", &digit.size.x, &digit.size.y);
+ xxassert(read == 2, (XBuffer() < "ÐевеÑÐ½Ð°Ñ ÑÑÑока Ñ ÑазмеÑами ÑÑиÑÑа в [" < str_cache < "]").c_str());
+ dprintf("re");
+ } else {
+ QDObject& obj = digit.pool.getObject();
+ obj->set_state("0");
+ digit.size = runtime->getSize(obj);
+ digit.pool.releaseObject(obj);
+ }
+ dprintf("set size to (%5.1f, %5.1f)\n", digit.size.x, digit.size.y);
+ fonts_.push_back(digit);
+ } else
+ break;
+ }
+
+ for (int idx = 0;; ++idx) {
+ _snprintf(str_cache, 127, "register_particle_escape_%d", idx);
+ if (const char * descr = runtime->parameter(str_cache, false)) {
+ Escape escape;
+ int read = sscanf(descr, "%d (%f><%f, %f><%f) (%f><%f, %f><%f) %f '%15s",
+ &escape.depth,
+ &escape.vel_min.x, &escape.vel_max.x, &escape.vel_min.y, &escape.vel_max.y,
+ &escape.accel_min.x, &escape.accel_max.x, &escape.accel_min.y, &escape.accel_max.y,
+ &escape.aliveTime, escape.format);
+ xxassert(read == 11, (XBuffer() < "ÐевеÑÐ½Ð°Ñ ÑÑÑока Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð¿Ð¾Ð»ÐµÑа в [" < str_cache < "]").c_str());
+ if (read != 11)
+ break;
+ escapes_.push_back(escape);
+ } else
+ break;
+ }
+ dprintf("registered %d particle escapes\n", escapes_.size());
+
+ if (getStaticPreset(show_scores_, "show_scores"))
+ show_scores_.textID = createStaticText(show_scores_.pos, show_scores_.font, show_scores_.align);
+ else
+ show_scores_.textID = -1;
+
+ if (getStaticPreset(show_time_, "show_time"))
+ show_time_.textID = createStaticText(show_time_.pos, show_time_.font, show_time_.align);
+ else
+ show_time_.textID = -1;
+
+ targetScore_ = 0;
+ currentScore_ = 0;
+ scoreUpdateTimer_ = 0.f;
+
+ scoreUpdateTime_ = getParameter("score_update_time", 0.1f);
+}
+
+bool TextManager::getStaticPreset(StaticTextPreset& preset, const char* name) const {
+ if (const char * descr = runtime->parameter(name, false)) {
+ int align = 0;
+ char str[64];
+ str[63] = 0;
+ int read = sscanf(descr, "%d %d |%63s", &align, &preset.font, str);
+ xxassert(read == 3, (XBuffer() < "ÐевеÑÐ½Ð°Ñ ÑÑÑока Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ ÑоÑмаÑа ÑекÑÑа в [" < name < "]").c_str());
+ if (read != 3)
+ return false;
+
+ char *pos_obj = strchr(str, '|');
+ xxassert(pos_obj, (XBuffer() < "ÐевеÑÐ½Ð°Ñ ÑÑÑока Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ ÑоÑмаÑа ÑекÑÑа в [" < name < "]").c_str());
+ if (!pos_obj)
+ return false;
+ *pos_obj = 0;
+ ++pos_obj;
+
+ strncpy(preset.format, str, 15);
+
+ switch (align) {
+ case 0:
+ preset.align = ALIGN_RIGHT;
+ break;
+ case 1:
+ preset.align = ALIGN_LEFT;
+ break;
+ default:
+ preset.align = ALIGN_CENTER;
+ break;
+ }
+
+ if (QDObject obj = runtime->getObject(pos_obj)) {
+ preset.pos = runtime->world2game(obj);
+ runtime->release(obj);
+ } else
+ return false;
+ } else
+ return false;
+
+ return true;
+}
+
+TextManager::~TextManager() {
+ Messages::iterator mit;
+ FOR_EACH(flowMsgs_, mit)
+ mit->release();
+
+ StaticMessages::iterator sit;
+ FOR_EACH(staticMsgs_, sit)
+ sit->release();
+
+ Fonts::iterator dit;
+ FOR_EACH(fonts_, dit)
+ dit->pool.release();
+}
+
+int TextManager::createStaticText(const mgVect3f& pos, int fontID, TextAlign align) {
+ xassert(fontID >= 0 && fontID < fonts_.size());
+
+ StaticMessage msg(&fonts_[fontID]);
+
+ msg.align_ = align;
+ msg.depth_ = pos.z;
+ msg.pos_ = mgVect2f(pos.x, pos.y);
+
+ staticMsgs_.push_back(msg);
+ return (int)staticMsgs_.size() - 1;
+}
+
+void TextManager::updateStaticText(int textID, const char* txt) {
+ xassert(textID >= 0 && textID < staticMsgs_.size());
+
+ staticMsgs_[textID].setText(txt);
+}
+
+void TextManager::showText(const char* txt, const mgVect2f& pos, int fontID, int escapeID) {
+ xassert(fontID >= 0 && fontID < fonts_.size());
+ xassert(escapeID >= 0 && escapeID < escapes_.size());
+
+ Escape& es = escapes_[escapeID];
+
+ Message msg(&fonts_[fontID]);
+
+ msg.setText(txt);
+ if (msg.empty())
+ return;
+
+ msg.time_ = es.aliveTime > 0 ? es.aliveTime : 1.e6f;
+
+ msg.depth_ = es.depth;
+ msg.pos_ = pos;
+
+ msg.vel_.x = runtime->rnd(es.vel_min.x, es.vel_max.x);
+ msg.vel_.y = runtime->rnd(es.vel_min.y, es.vel_max.y);
+ msg.accel_.x = runtime->rnd(es.accel_min.x, es.accel_max.x);
+ msg.accel_.y = runtime->rnd(es.accel_min.y, es.accel_max.y);
+
+ flowMsgs_.push_back(msg);
+}
+
+void TextManager::showNumber(int num, const mgVect2f& pos, int fontID, int escapeID) {
+ xassert(fontID >= 0 && fontID < fonts_.size());
+ xassert(escapeID >= 0 && escapeID < escapes_.size());
+
+ char buf[16];
+ buf[15] = 0;
+ _snprintf(buf, 15, escapes_[escapeID].format, num);
+
+ showText(buf, pos, fontID, escapeID);
+}
+
+TextManager::Escape::Escape() {
+ depth = 0;
+ aliveTime = -1;
+ format[15] = 0;
+}
+
+TextManager::StaticTextPreset::StaticTextPreset() {
+ font = -1;
+ align = ALIGN_CENTER;
+ format[15] = 0;
+}
+
+TextManager::StaticMessage::StaticMessage(Font* font, TextAlign align_) {
+ font_ = font;
+ align_ = align_;
+ depth_ = 0.f;
+}
+
+void TextManager::StaticMessage::release() {
+ QDObjects::iterator it;
+ FOR_EACH(objects_, it)
+ font_->pool.releaseObject(*it);
+ objects_.clear();
+}
+
+void TextManager::StaticMessage::setText(const char* str) {
+ xassert(font_);
+
+ if (!str) {
+ release();
+ return;
+ }
+
+ int len = (int)strlen(str);
+
+ if (objects_.size() < len)
+ objects_.resize(len);
+ else
+ while (objects_.size() > len) {
+ if (objects_.back())
+ font_->pool.releaseObject(objects_.back());
+ objects_.pop_back();
+ }
+
+ for (int idx = 0; idx < len; ++idx) {
+ if (validSymbol(str[idx])) {
+ if (!objects_[idx])
+ objects_[idx] = font_->pool.getObject();
+ } else if (objects_[idx])
+ font_->pool.releaseObject(objects_[idx]);
+ }
+
+ char name[2];
+ name[1] = 0;
+ for (int idx = 0; idx < len; ++idx) {
+ if (objects_[idx]) {
+ name[0] = str[idx];
+ objects_[idx].setState(name);
+ }
+ }
+
+ update();
+}
+
+void TextManager::StaticMessage::update() {
+ if (objects_.empty())
+ return;
+
+ float width = font_->size.x * (objects_.size() - 1);
+ float x = pos_.x;
+ float y = pos_.y;
+ switch (align_) {
+ case ALIGN_RIGHT:
+ x -= width;
+ break;
+ case ALIGN_CENTER:
+ x -= width / 2.f;
+ break;
+ }
+ if (y < -font_->size.y || y > runtime->screenSize().y + font_->size.y
+ || x < -2 * width || x > runtime->screenSize().x + 2 * width) {
+ release();
+ return;
+ }
+
+ QDObjects::iterator it;
+ FOR_EACH(objects_, it) {
+ if (*it)
+ (*it)->set_R(runtime->game2world(mgVect2f(x, y), depth_));
+ x += font_->size.x;
+ }
+}
+
+TextManager::Message::Message(Font* font)
+ : StaticMessage(font) {
+ time_ = 0.f;
+}
+
+void TextManager::Message::release() {
+ StaticMessage::release();
+ time_ = 0.f;
+}
+
+void TextManager::Message::quant(float dt) {
+ if (empty())
+ return;
+
+ time_ -= dt;
+ if (time_ < 0.f) {
+ release();
+ return;
+ }
+
+ vel_ += accel_ * dt;
+ pos_ += vel_ * dt;
+
+ update();
+}
+
+void TextManager::quant(float dt) {
+ Messages::iterator it = flowMsgs_.begin();
+ while (it != flowMsgs_.end()) {
+ it->quant(dt);
+ if (it->empty())
+ it = flowMsgs_.erase(it);
+ else
+ ++it;
+ }
+
+ if (show_scores_.textID >= 0) {
+ if (scoreUpdateTimer_ >= 0.f && scoreUpdateTimer_ <= runtime->time()) {
+ int sgn = SIGN(targetScore_ - currentScore_);
+ int mod = abs(currentScore_ - targetScore_);
+ currentScore_ += sgn * (mod / 10 + 1);
+
+ char buf[16];
+ buf[15] = 0;
+ _snprintf(buf, 15, show_scores_.format, currentScore_);
+ updateStaticText(show_scores_.textID, buf);
+
+ scoreUpdateTimer_ = currentScore_ != targetScore_ ? runtime->time() + scoreUpdateTime_ : -1.f;
+ }
+ }
+}
+
+void TextManager::updateScore(int score) {
+ targetScore_ = score;
+ if (scoreUpdateTimer_ < 0.f)
+ scoreUpdateTimer_ = runtime->time();
+}
+
+void TextManager::updateTime(int seconds) {
+ if (show_time_.textID >= 0) {
+ char buf[16];
+ buf[15] = 0;
+ int h = seconds / 3600;
+ seconds -= 3600 * h;
+ int minutes = seconds / 60;
+ seconds -= 60 * minutes;
+ _snprintf(buf, 15, show_time_.format, h, minutes, seconds);
+ updateStaticText(show_time_.textID, buf);
+ }
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/TextManager.h b/engines/qdengine/minigames/adv/TextManager.h
new file mode 100644
index 00000000000..e7b37bd3a64
--- /dev/null
+++ b/engines/qdengine/minigames/adv/TextManager.h
@@ -0,0 +1,134 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_TEXT_MANAGER_H
+#define QDENGINE_MINIGAMES_ADV_TEXT_MANAGER_H
+
+#include "qdengine/minigames/adv/ObjectContainer.h"
+
+namespace QDEngine {
+
+enum TextAlign {
+ ALIGN_LEFT,
+ ALIGN_RIGHT,
+ ALIGN_CENTER
+};
+
+class TextManager {
+public:
+ TextManager();
+ ~TextManager();
+
+ int createStaticText(const mgVect3f& screen_pos, int fontID, TextAlign align);
+ void updateStaticText(int textID, const char* txt);
+
+ void showText(const char* txt, const mgVect2f& pos, int fontID, int escapeID);
+ void showNumber(int num, const mgVect2f& pos, int fontID, int escapeID);
+
+ void quant(float dt);
+ void updateScore(int score);
+ void updateTime(int seconds);
+
+private:
+ struct Font {
+ mgVect2f size;
+ ObjectContainer pool;
+ };
+ typedef vector<Font> Fonts;
+
+ struct Escape {
+ Escape();
+ int depth;
+ float aliveTime;
+ mgVect2f vel_min;
+ mgVect2f vel_max;
+ mgVect2f accel_min;
+ mgVect2f accel_max;
+ char format[16];
+ };
+ typedef vector<Escape> Escapes;
+
+ struct StaticTextPreset {
+ StaticTextPreset();
+ mgVect3f pos;
+ int font;
+ TextAlign align;
+ char format[16];
+ int textID;
+ };
+ bool getStaticPreset(StaticTextPreset& preset, const char* name) const;
+
+ struct StaticMessage {
+ StaticMessage(Font* font = 0, TextAlign align_ = ALIGN_CENTER);
+ void release();
+
+ bool empty() const {
+ return objects_.empty();
+ }
+
+ void setText(const char* str);
+
+ int depth_;
+ mgVect2f pos_;
+ TextAlign align_;
+
+ protected:
+ void update();
+ bool validSymbol(unsigned char ch) const {
+ return ch > ' ' && ch != '_';
+ }
+
+ private:
+ Font *font_;
+
+ QDObjects objects_;
+ };
+ typedef vector<StaticMessage> StaticMessages;
+
+ struct Message : public StaticMessage {
+ Message(Font* font = 0);
+ void release();
+
+ void quant(float dt);
+
+ float time_;
+ mgVect2f vel_;
+ mgVect2f accel_;
+
+ };
+ typedef vector<Message> Messages;
+
+ Fonts fonts_;
+ Escapes escapes_;
+ StaticTextPreset show_scores_;
+ StaticTextPreset show_time_;
+ StaticMessages staticMsgs_;
+ Messages flowMsgs_;
+
+ int targetScore_;
+ int currentScore_;
+ float scoreUpdateTime_;
+ float scoreUpdateTimer_;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_TEXT_MANAGER_H
diff --git a/engines/qdengine/minigames/adv/common.cpp b/engines/qdengine/minigames/adv/common.cpp
new file mode 100644
index 00000000000..dc377fd39e2
--- /dev/null
+++ b/engines/qdengine/minigames/adv/common.cpp
@@ -0,0 +1,156 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/RunTime.h"
+
+namespace QDEngine {
+
+QDObject QDObject::ZERO(0, "ZERO OBJECT");
+
+const char *QDObject::getName() const {
+ #ifdef _DEBUG
+ return name_.c_str();
+ #else
+ return "";
+ #endif
+}
+
+bool QDObject::hit(const mgVect2f& point) const {
+ return obj_->hit_test(mgVect2i(round(point.x), round(point.y)));
+}
+
+float QDObject::depth() const {
+ return runtime->getDepth(obj_);
+}
+
+void QDObject::setState(const char* name) {
+ if (!obj_->is_state_active(name))
+ obj_->set_state(name);
+}
+
+template<class T>
+T getParameter(const char* name, const T& defValue) {
+ return round(getParameter<float>(name, (float)defValue));
+}
+
+template<class T>
+bool getParameter(const char* name, T& out, bool obligatory) {
+ float retValue = out;
+ if (getParameter<float>(name, retValue, obligatory)) {
+ out = round(retValue);
+ return true;
+ }
+ return false;
+}
+
+template<>
+float getParameter(const char* name, const float &defValue) {
+ if (const char * data = runtime->parameter(name, false)) {
+ float retValue = defValue;
+ if (_snscanf(data, 8, "%f", &retValue) == 1)
+ return retValue;
+ xxassert(false, (XBuffer() < "РпаÑамеÑÑе [" < name < "] невеÑнÑй Ñип даннÑÑ
. Ðолжно бÑÑÑ ÑиÑло.").c_str());
+ }
+ return defValue;
+
+}
+
+template<>
+bool getParameter(const char* name, float &out, bool obligatory) {
+ if (const char * data = runtime->parameter(name, obligatory)) {
+ float retValue = out;
+ if (_snscanf(data, 8, "%f", &retValue) == 1) {
+ out = retValue;
+ return true;
+ }
+ xxassert(false, (XBuffer() < "РпаÑамеÑÑе [" < name < "] невеÑнÑй Ñип даннÑÑ
. Ðолжно бÑÑÑ ÑиÑло.").c_str());
+ }
+ return false;
+
+}
+
+template<>
+mgVect2f getParameter(const char* name, const mgVect2f& defValue) {
+ if (const char * data = runtime->parameter(name, false)) {
+ mgVect2f retValue = defValue;
+ if (_snscanf(data, 16, "%f %f", &retValue.x, &retValue.y) == 2)
+ return retValue;
+ xxassert(false, (XBuffer() < "РпаÑамеÑÑе [" < name < "] невеÑнÑй Ñип даннÑÑ
. Ðолжна бÑÑÑ Ð¿Ð°Ñа ÑиÑел.").c_str());
+ }
+ return defValue;
+
+}
+
+template<>
+bool getParameter(const char* name, mgVect2f& out, bool obligatory) {
+ if (const char * data = runtime->parameter(name, obligatory)) {
+ mgVect2f retValue = out;
+ if (_snscanf(data, 16, "%f %f", &retValue.x, &retValue.y) == 2) {
+ out = retValue;
+ return true;
+ }
+ xxassert(false, (XBuffer() < "РпаÑамеÑÑе [" < name < "] невеÑнÑй Ñип даннÑÑ
. Ðолжна бÑÑÑ Ð¿Ð°Ñа ÑиÑел.").c_str());
+ }
+ return false;
+
+}
+
+template<>
+mgVect2i getParameter(const char* name, const mgVect2i& defValue) {
+ mgVect2f retValue = getParameter(name, mgVect2f(defValue.x, defValue.y));
+ return mgVect2i(round(retValue.x), round(retValue.y));
+
+}
+
+template<>
+bool getParameter(const char* name, mgVect2i& out, bool obligatory) {
+ mgVect2f retValue = mgVect2f(out.x, out.y);
+ if (getParameter<mgVect2f>(name, retValue, obligatory)) {
+ out = mgVect2i(round(retValue.x), round(retValue.y));
+ return true;
+ }
+ return false;
+}
+
+void dummyInstanceGetParameter() {
+ bool db = false;
+ getParameter("", db);
+ getParameter("", db, false);
+
+ int di = 0;
+ getParameter("", di);
+ getParameter("", di, false);
+
+ float df = 0.f;
+ getParameter("", df);
+ getParameter("", df, false);
+
+ mgVect2i d2i;
+ getParameter("", d2i);
+ getParameter("", d2i, false);
+
+ mgVect2i d2f;
+ getParameter("", d2f);
+ getParameter("", d2f, false);
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/common.h b/engines/qdengine/minigames/adv/common.h
new file mode 100644
index 00000000000..b4b01109dc1
--- /dev/null
+++ b/engines/qdengine/minigames/adv/common.h
@@ -0,0 +1,89 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_COMMON_H
+#define QDENGINE_MINIGAMES_ADV_COMMON_H
+
+#include "qdengine/qdcore/qd_minigame_interface.h"
+
+namespace QDEngine {
+
+void dprintf(const char *format, ...);
+
+typedef mgVect3<int> mgVect3i;
+
+using namespace std;
+
+class QDObject {
+ qdMinigameObjectInterface *obj_;
+
+ #ifdef _DEBUG
+ string name_;
+ #endif
+
+public:
+ static QDObject ZERO;
+
+ QDObject(qdMinigameObjectInterface* obj = 0, const char* name = "") : obj_(obj) {
+ #ifdef _DEBUG
+ name_ = name;
+ #endif
+ }
+
+ const char *getName() const; // DEBUG ONLY
+ bool hit(const mgVect2f& point) const;
+ float depth() const;
+
+ void setState(const char* name);
+
+ bool operator==(const QDObject& obj) const {
+ return obj_ == obj.obj_;
+ }
+ bool operator==(const qdMinigameObjectInterface* obj) const {
+ return obj_ == obj;
+ }
+
+ operator qdMinigameObjectInterface* () const {
+ return obj_;
+ }
+ qdMinigameObjectInterface* operator->() const {
+ return obj_;
+ }
+};
+
+typedef qdMinigameCounterInterface *QDCounter;
+
+typedef vector<QDObject> QDObjects;
+typedef vector<int> Indexes;
+typedef vector<mgVect3f> Coords;
+
+class MinigameManager;
+extern MinigameManager *runtime;
+
+template<class T>
+T getParameter(const char* name, const T& defValue);
+
+template<class T>
+bool getParameter(const char* name, T& out, bool obligatory);
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_COMMON_H
diff --git a/engines/qdengine/minigames/adv/m_karaoke.cpp b/engines/qdengine/minigames/adv/m_karaoke.cpp
new file mode 100644
index 00000000000..858e1cb6e4e
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_karaoke.cpp
@@ -0,0 +1,197 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/m_karaoke.h"
+#include "qdengine/minigames/adv/RunTime.h"
+
+namespace QDEngine {
+
+MinigameInterface *createGame() {
+ return new Karaoke;
+}
+
+Karaoke::Node::Node() {
+ type = Karaoke::CLEAR;
+ time = 0.f;
+
+}
+
+
+Karaoke::Karaoke() {
+ controlName_ = runtime->parameter("control_name", true);
+ if (!controlName_ || !*controlName_)
+ return;
+
+ colorReaded_ = runtime->parameter("color_first", true);
+ if (!colorReaded_ || !*colorReaded_)
+ return;
+
+ struct Parse {
+ XStream &file;
+ Nodes &nodes;
+
+ float currentTime;
+
+ char buf[1024];
+ const char *begin;
+ const char *cur;
+
+ void putLine(bool putEmpty = false) {
+ if (cur > begin && *begin) {
+ Node node;
+ node.type = STRING;
+ node.time = currentTime;
+ node.text = string(begin, cur);
+ nodes.push_back(node);
+ } else if (putEmpty) {
+ Node node;
+ node.type = STRING;
+ node.time = currentTime;
+ nodes.push_back(node);
+ }
+ begin = cur = buf;
+ }
+
+ char read() {
+ if (!file.read((void *)cur, 1)) {
+ putLine();
+ return 0;
+ }
+ return *cur++;
+ }
+
+ Parse(XStream& _file, Nodes& _nodes) : file(_file), nodes(_nodes) {
+ currentTime = 0.f;
+ begin = cur = buf;
+ bool prevNumber = false;
+ while (!file.eof()) {
+ switch (read()) {
+ case 0:
+ return;
+ case '/': {
+ if (read() == '/') {
+ --cur;
+ break;
+ }
+ cur -= 2;
+ putLine(prevNumber);
+ prevNumber = true;
+
+ file.seek(-1, XS_CUR);
+ float tm = 0;
+ file >= tm;
+
+ if (tm <= 0.f) {
+ currentTime = 0.f;
+ nodes.push_back(Node());
+ } else
+ currentTime = tm;
+ file.seek(-1, XS_CUR);
+ continue;
+ }
+ case '>':
+ if (prevNumber)
+ --cur;
+ }
+ prevNumber = false;
+ }
+ putLine();
+
+ }
+ };
+
+ const char *fileName = runtime->parameter("text_file", true);
+ if (!fileName)
+ return;
+
+ XStream file(false);
+ if (!file.open(fileName, XS_IN)) {
+ xxassert(false, (XBuffer() < "Ðе ÑдалоÑÑ Ð¾ÑкÑÑÑÑ Ñайл \"" < fileName < "\"").c_str());
+ return;
+ }
+
+ Parse(file, nodes_);
+ dprintf("read %d tags\n", nodes_.size());
+
+ startScreenTag_ = 0;
+ currentTag_ = 0;
+
+ startTime_ = 0.001f * GetTickCount();
+ startTagTime_ = 0.f;
+
+ setState(MinigameInterface::RUNNING);
+
+}
+
+void Karaoke::quant(float dt) {
+ float curTime = 0.001f * GetTickCount() - startTime_;
+ if (curTime < 0.f)
+ curTime = 0.f;
+
+ Node& node = nodes_[currentTag_];
+ if (node.type == CLEAR) {
+ ++currentTag_;
+ if (currentTag_ == nodes_.size())
+ setState(MinigameInterface::GAME_WIN);
+ startScreenTag_ = currentTag_;
+ return;
+ }
+
+ XBuffer outText;
+ outText < colorReaded_;
+ int idx = startScreenTag_;
+ while (idx < currentTag_) {
+ xassert(idx < nodes_.size());
+ xassert(nodes_[idx].type == STRING);
+ outText < nodes_[idx].text.c_str();
+ ++idx;
+ }
+
+ float phase = (curTime - startTagTime_) / node.time;
+ xassert(phase >= 0.f);
+ if (phase >= 1.f) {
+ outText < node.text.c_str() < "&>";
+ ++currentTag_;
+ startTagTime_ += node.time;
+ if (currentTag_ == nodes_.size())
+ setState(MinigameInterface::GAME_WIN);
+ } else {
+ int part = phase * node.text.size();
+ outText < string(node.text.begin(), node.text.begin() + part).c_str() < "&>";
+ outText < string(node.text.begin() + part, node.text.end()).c_str();
+ }
+
+ ++idx;
+ while (idx < nodes_.size()) {
+ if (nodes_[idx].type == CLEAR)
+ break;
+ outText < nodes_[idx].text.c_str();
+ ++idx;
+ }
+
+ if (runtime->mouseRightPressed())
+ dprintf("%s\n", outText.c_str());
+
+ runtime->setText(controlName_, outText.c_str());
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/m_karaoke.h b/engines/qdengine/minigames/adv/m_karaoke.h
new file mode 100644
index 00000000000..6580074241a
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_karaoke.h
@@ -0,0 +1,62 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_M_KARAOKE_H
+#define QDENGINE_MINIGAMES_ADV_M_KARAOKE_H
+
+#include "qdengine/minigames/adv/MinigameInterface.h"
+
+namespace QDEngine {
+
+class Karaoke : public MinigameInterface {
+public:
+ Karaoke();
+ void quant(float dt);
+
+ enum TagType {
+ STRING,
+ CLEAR
+ };
+
+private:
+ const char *controlName_;
+ const char *colorReaded_;
+
+ struct Node {
+ Node();
+ TagType type;
+ float time;
+ string text;
+ };
+
+ typedef vector<Node> Nodes;
+ Nodes nodes_;
+
+ float startTime_;
+ int startScreenTag_;
+ int currentTag_;
+ float startTagTime_;
+
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_M_KARAOKE_H
diff --git a/engines/qdengine/minigames/adv/m_puzzle.cpp b/engines/qdengine/minigames/adv/m_puzzle.cpp
new file mode 100644
index 00000000000..41e818de85d
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_puzzle.cpp
@@ -0,0 +1,460 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/m_puzzle.h"
+#include "qdengine/minigames/adv/RunTime.h"
+#include "qdengine/minigames/adv/Rect.h"
+#include "qdengine/minigames/adv/qdMath.h"
+#include "qdengine/system/input/keyboard_input.h"
+
+namespace QDEngine {
+
+typedef Rect<float, mgVect2f> Rectf;
+
+MinigameInterface *createGame() {
+ return new Puzzle;
+}
+
+enum {
+ EVENT_GET,
+ EVENT_PUT,
+ EVENT_SWAP,
+ EVENT_ROTATE_IN_FIELD,
+ EVENT_RETURN,
+ EVENT_PUT_RIGHT,
+ EVENT_CLICK_RIGHT,
+ EVENT_CLICK,
+ EVENT_ROTATE_IN_STACK,
+ EVENT_FIELD_ROTATE
+};
+
+const char *Puzzle::getStateName(int angle, bool selected, bool small) const {
+ static const char *small_pref = "inv_";
+ static const char *selected_suf = "_sel";
+
+ static char buf[32];
+ buf[31] = 0;
+
+ xassert(angle >= 0 && angle < angles_);
+ angle = (angle + globalAngle_) % angles_;
+
+ _snprintf(buf, 31, "%s%02d%s", !singleSize_ && small ? small_pref : "", angle + 1, selected ? selected_suf : "");
+ return buf;
+}
+
+Puzzle::Puzzle() {
+ if (!getParameter("game_size", gameSize_, true))
+ return;
+ xassert(gameSize_ > 0 && gameSize_ < 100);
+
+ field_.resize(gameSize_, -1);
+ globalAngle_ = 0;
+
+ singleSize_ = getParameter("small_objects", false);
+
+ angles_ = getParameter("angles", 4);
+ xassert(angles_ > 0 && angles_ < 10);
+
+ if (!(stackBottom_ = runtime->getObject(runtime->parameter("inventory_bottom"))))
+ return;
+ if (!getParameter("inventory_size", stackSize_, true))
+ return;
+
+ if (getParameter("rotate_period", rotateTimePeriod_, false)) {
+ xassert(sqr(sqrt((float)gameSize_)) == gameSize_);
+ if (sqr(sqrt((float)gameSize_)) != gameSize_)
+ return;
+ } else
+ rotateTimePeriod_ = 86400; // ÑÑÑки
+ nextRotateTime_ = runtime->time() + rotateTimePeriod_;
+
+ flySpeed_ = getParameter("inventory_drop_speed", 240.f);
+ xassert(flySpeed_ > 0.f);
+ returnSpeed_ = getParameter("inventory_return_speed", -1.f);
+
+ const char *name_begin = runtime->parameter("obj_name_begin", "obj_");
+
+ char buf[128];
+ buf[127] = 0;
+
+ XBuffer gameData;
+ for (int idx = 0; idx < gameSize_; ++idx) {
+ _snprintf(buf, 127, "%s%02d", name_begin, idx + 1);
+
+ Node node;
+ node.obj = runtime->getObject(buf);
+
+ if (runtime->debugMode()) {
+ node.pos = nodes_.size();
+ node.angle = 0;
+ field_[node.pos] = node.pos;
+ } else
+ node.angle = runtime->rnd(0, angles_ - 1);
+ node.obj.setState(getStateName(node.angle, false, true));
+
+ gameData.write(node.obj->R());
+
+ nodes_.push_back(node);
+ }
+
+ if (!runtime->processGameData(gameData))
+ return;
+
+ for (int idx = 0; idx < gameSize_; ++idx) {
+ mgVect3f crd;
+ gameData.read(crd);
+ nodes_[idx].obj->set_R(crd);
+ positions_.push_back(crd);
+ }
+
+ if (runtime->debugMode())
+ nodes_[0].angle = angles_ - 1;
+
+ size_ = runtime->getSize(nodes_[0].obj);
+ dprintf("size = (%6.2f,%6.2f)\n", size_.x, size_.y);
+
+ depth_ = nodes_[0].obj.depth();
+
+ stackPlaceSize_ = getParameter("inventory_place_size", size_ * 1.2f);
+ xassert(stackPlaceSize_.x > 0.f && stackPlaceSize_.x < 500.f && stackPlaceSize_.y > 0.f && stackPlaceSize_.y < 500.f);
+ dprintf("stackPlaceSize = (%5.1f, %5.1f)\n", stackPlaceSize_.x, stackPlaceSize_.y);
+
+ prevPlace_ = -1;
+ pickedItem_ = -1;
+ mouseObjPose_ = stidx(stackSize_ + 1);
+
+ inField_ = runtime->debugMode() ? nodes_.size() : 0;
+ nextObjTime_ = runtime->time();
+
+ setState(MinigameInterface::RUNNING);
+}
+
+Puzzle::~Puzzle() {
+ Nodes::iterator it;
+ FOR_EACH(nodes_, it)
+ runtime->release(it->obj);
+
+ runtime->release(stackBottom_);
+}
+
+void Puzzle::rotate(int item) {
+ xassert(item >= 0 && item < nodes_.size());
+ nodes_[item].angle = (nodes_[item].angle + 1) % angles_;
+}
+
+int Puzzle::stidx(int idx) const {
+ return -idx - 2;
+}
+
+bool Puzzle::testPlace(int item) const {
+ xassert(item >= 0 && item < nodes_.size());
+ return nodes_[item].pos == item && nodes_[item].angle == 0;
+}
+
+bool Puzzle::isFlying(int idx) const {
+ FlyQDObjects::const_iterator it;
+ FOR_EACH(flyObjs_, it)
+ if (it->data == idx)
+ return true;
+ return false;
+}
+
+bool Puzzle::isOnMouse(const Node& node) const {
+ if (node.pos == mouseObjPose_) {
+ return true;
+ }
+ return false;
+}
+
+void Puzzle::put(int where, int what, float flowSpeed) {
+ xassert(where < (int)field_.size());
+ xassert(what >= 0 && what < nodes_.size());
+
+ Node& node = nodes_[what];
+ int start = node.pos;
+
+ if (flowSpeed > 0.f || isFlying(what)) {
+ FlyQDObject* flyObj = 0;
+
+ FlyQDObjects::iterator fit;
+ FOR_EACH(flyObjs_, fit)
+ if (fit->data == what)
+ break;
+ if (fit != flyObjs_.end()) // ÐÑÐ¾Ñ ÑÑÐ°Ð³Ð¼ÐµÐ½Ñ Ñже леÑиÑ, пÑоÑÑо поменÑÑÑ ÑоÑÐºÑ Ð½Ð°Ð·Ð½Ð°ÑениÑ
+ flyObj = &*fit;
+ else { // ÐобавлÑем новÑй леÑÑÑий ÑÑагменÑ
+ flyObjs_.push_back(FlyQDObject());
+ flyObj = &flyObjs_.back();
+
+ flyObj->data = what;
+
+ mgVect3f from = isOnMouse(node) ? node.obj->R() : start < -1 ? stackPosition(stidx(start)) : position(start);
+ flyObj->current = runtime->world2game(from);
+ node.obj->set_R(from);
+
+ flyObj->speed = flowSpeed;
+ }
+
+ mgVect3f to = where < -1 ? stackPosition(stidx(where)) : position(where);
+ flyObj->target = runtime->world2game(to);
+ flyObj->depth = runtime->getDepth(to);
+ }
+
+ if (where >= 0)
+ field_[where] = what;
+
+ node.pos = where;
+}
+
+void Puzzle::putOnStack(int what, float speed) {
+ put(stidx((int)stack_.size()), what, speed);
+ stack_.push_back(what);
+}
+
+void Puzzle::returnToStack() {
+ xassert(pickedItem_ != -1);
+ runtime->event(EVENT_RETURN, runtime->mousePosition());
+ if (prevPlace_ >= 0)
+ put(prevPlace_, pickedItem_);
+ else
+ putOnStack(pickedItem_, returnSpeed_);
+ prevPlace_ = -1;
+ pickedItem_ = -1;
+ runtime->event(EVENT_CLICK, runtime->mousePosition());
+}
+
+void Puzzle::quant(float dt) {
+ if (pickedItem_ == -1)
+ runtime->setGameHelpVariant(0);
+ else
+ runtime->setGameHelpVariant(1);
+
+ if (runtime->time() > nextRotateTime_) {
+ runtime->event(EVENT_FIELD_ROTATE, mgVect2f(400, 300));
+ nextRotateTime_ = runtime->time() + rotateTimePeriod_;
+ globalAngle_ = (globalAngle_ + 1) % angles_;
+ runtime->setCompleteHelpVariant(globalAngle_);
+ }
+
+ FlyQDObjects::iterator fit = flyObjs_.begin();
+ while (fit != flyObjs_.end())
+ if (!isOnMouse(nodes_[fit->data]) && fit->quant(dt, nodes_[fit->data].obj))
+ ++fit;
+ else
+ fit = flyObjs_.erase(fit);
+
+ if (inField_ < nodes_.size() && runtime->time() > nextObjTime_ &&
+ (stack_.size() < stackSize_ - 1 || stack_.size() < stackSize_ && pickedItem_ == -1)) { // нÑжно добавиÑÑ Ð² инвенÑоÑи ÑиÑкÑ
+ // иÑем ÑлÑÑайнÑй не вÑÑÑавленнÑй ÑÑагменÑ
+ int freeIdx = round(runtime->rnd(0.f, nodes_.size() - 1));
+ Nodes::iterator it = nodes_.begin();
+ for (;;) {
+ if (++it == nodes_.end())
+ it = nodes_.begin();
+ if (it->isFree())
+ if (!freeIdx--)
+ break;
+ }
+ int idx = distance(nodes_.begin(), it);
+
+ ++inField_;
+ nextObjTime_ = runtime->time() + stackPlaceSize_.y / flySpeed_;
+
+ it->pos = stidx(stackSize_);
+ it->obj.setState(getStateName(it->angle, false, true));
+
+ putOnStack(idx, flySpeed_);
+ }
+
+ mgVect2f mouse = runtime->mousePosition();
+
+ int hovPlace = -1; // ÐÐ¾Ð¼ÐµÑ Ð¼ÐµÑÑа коÑоÑое ÑейÑÐ°Ñ Ð¿Ð¾Ð´ мÑÑкой
+ for (int idx = 0; idx < stack_.size(); ++idx)
+ if (nodes_[stack_[idx]].obj.hit(mouse)) {
+ hovPlace = stidx(idx);
+ break;
+ }
+ if (hovPlace == -1) {
+ float radius = 0.5f * size_.x;
+ for (int idx = 0; idx < gameSize_; ++idx)
+ if (dist(runtime->world2game(position(idx)), mouse) < radius) {
+ hovPlace = idx;
+ break;
+ }
+ }
+ if (hovPlace == -1) {
+ mgVect2i st = stackBottom_->screen_R();
+ st.y -= stackPlaceSize_.y * stackSize_ - 0.5f * stackPlaceSize_.x;
+ Rectf stackPos(st.x - 0.5f * stackPlaceSize_.x, st.y, stackPlaceSize_.x, stackPlaceSize_.y * stackSize_);
+ if (stackPos.point_inside(mouse))
+ hovPlace = stidx(stackSize_);
+ }
+
+ if (runtime->mouseLeftPressed()) {
+ if (hovPlace >= 0) { // клик по полÑ
+ Indexes::value_type& hovItem = field_[hovPlace];
+ if (hovItem == -1) // клик по пÑÑÑой ÑÑейке
+ if (pickedItem_ == -1) // на мÑÑи ниÑего неÑ
+ runtime->event(EVENT_CLICK, mouse);
+ else { // кладем ÑÑÐ°Ð³Ð¼ÐµÐ½Ñ Ñ Ð¼ÑÑи
+ put(hovPlace, pickedItem_);
+ if (testPlace(pickedItem_)) // положили на Ñвое Ñвое меÑÑо
+ runtime->event(EVENT_PUT_RIGHT, mouse);
+ else // пÑоÑÑо положили
+ runtime->event(EVENT_PUT, mouse);
+ pickedItem_ = -1;
+ prevPlace_ = -1;
+ } else { // клик по непÑÑÑой ÑÑейке
+ if (testPlace(hovPlace)) // клик по пÑавилÑно Ñложенной ÑиÑке
+ runtime->event(EVENT_CLICK_RIGHT, mouse);
+ else if (pickedItem_ != -1) { // поменÑÑÑ Ñ Ñем ÑÑо на мÑÑе
+ bool swap = true;
+ if (prevPlace_ >= 0)
+ put(prevPlace_, hovItem);
+ else
+ putOnStack(hovItem, returnSpeed_);
+ if (testPlace(hovItem)) { // оказалаÑÑ Ð¿Ñи обмене на Ñвоем меÑÑе
+ runtime->event(EVENT_PUT_RIGHT, runtime->world2game(position(prevPlace_)));
+ swap = false;
+ }
+ put(hovPlace, pickedItem_);
+ if (testPlace(pickedItem_)) { // положили на Ñвое Ñвое меÑÑо
+ runtime->event(EVENT_PUT_RIGHT, mouse);
+ swap = false;
+ }
+ if (swap) // пÑоÑÑо обменÑли
+ runtime->event(EVENT_SWAP, mouse);
+ pickedItem_ = -1;
+ prevPlace_ = -1;
+ } else { // взÑÑÑ ÑÑÐ°Ð³Ð¼ÐµÐ½Ñ Ð½Ð° мÑÑÑ
+ runtime->event(EVENT_GET, mouse);
+ prevPlace_ = hovPlace;
+ pickedItem_ = hovItem;
+ nodes_[pickedItem_].pos = mouseObjPose_;
+ hovItem = -1;
+ }
+ }
+ } else if (hovPlace < -1) { // клик по ÑÑекÑ
+ int hovStack = stidx(hovPlace);
+ if (pickedItem_ == -1) // на мÑÑи ниÑего неÑ
+ if (hovStack < stack_.size()) { // взÑÑÑ ÑÑÐ°Ð³Ð¼ÐµÐ½Ñ Ð¸Ð· ÑÑека на мÑÑÑ
+ runtime->event(EVENT_GET, mouse);
+ Indexes::iterator it = stack_.begin() + hovStack;
+ xassert(*it >= 0);
+ prevPlace_ = -1;
+ pickedItem_ = *it;
+ nodes_[pickedItem_].pos = mouseObjPose_;
+ stack_.erase(it);
+ for (int idx = hovStack; idx < stack_.size(); ++idx)
+ put(stidx(idx), stack_[idx], flySpeed_);
+ } else // пÑÑÑой клик в облаÑÑи ÑÑека
+ runtime->event(EVENT_CLICK, mouse);
+ else // веÑнÑÑÑ ÑиÑÐºÑ Ð½Ð° меÑÑо
+ returnToStack();
+ } else // пÑÑÑой клик мимо игÑового полÑ
+ runtime->event(EVENT_CLICK, mouse);
+ } else if (runtime->mouseRightPressed()) {
+ if (pickedItem_ == -1) {
+ if (hovPlace >= 0) { // клик по полÑ
+ if (testPlace(hovPlace)) // клик по пÑавилÑно Ñложенной ÑиÑке
+ runtime->event(EVENT_CLICK_RIGHT, mouse);
+ else {
+ Indexes::value_type& hovItem = field_[hovPlace];
+ if (hovItem >= 0) {
+ rotate(hovItem);
+ if (testPlace(hovItem)) // повеÑнÑли на пÑавилÑнÑй Ñгол
+ runtime->event(EVENT_PUT_RIGHT, mouse);
+ else // пÑоÑÑо положили
+ runtime->event(EVENT_ROTATE_IN_FIELD, mouse);
+ } else // попÑÑка пÑокÑÑÑиÑÑ Ð¿ÑÑÑое меÑÑо
+ runtime->event(EVENT_CLICK, mouse);
+ }
+ } else if (hovPlace < -1) { // клик по ÑÑекÑ
+ int hovStack = stidx(hovPlace);
+ if (hovStack < stack_.size()) { // покÑÑÑиÑÑ Ð²Ð½ÑÑÑи ÑÑека
+ runtime->event(EVENT_ROTATE_IN_STACK, mouse);
+ rotate(stack_[hovStack]);
+ } else // попÑÑка пÑокÑÑÑиÑÑ Ð¿ÑÑÑое меÑÑо
+ runtime->event(EVENT_CLICK, mouse);
+ } else // пÑÑÑой клик мимо игÑового полÑ
+ runtime->event(EVENT_CLICK, mouse);
+ } else // веÑнÑÑÑ ÑиÑÐºÑ Ð½Ð° меÑÑо
+ returnToStack();
+ }
+
+ bool iWin = true;
+ for (int idx = 0; idx < nodes_.size(); ++idx) {
+ Node& node = nodes_[idx];
+ if (node.pos != -1) {
+ if (node.pos >= 0) {
+ if (isFlying(idx))
+ node.obj.setState(getStateName(node.angle, false, false));
+ else {
+ node.obj.setState(getStateName(node.angle, node.pos == hovPlace && !testPlace(idx), false));
+ node.obj->set_R(position(node.pos));
+ }
+ } else if (idx == pickedItem_) {
+ node.obj.setState(getStateName(node.angle, hovPlace >= 0 && !testPlace(hovPlace), false));
+ node.obj->set_R(runtime->game2world(mouse, stackBottom_.depth() - 200));
+ } else {
+ node.obj.setState(getStateName(node.angle, node.pos == hovPlace && pickedItem_ == -1, true));
+ if (!isFlying(idx))
+ node.obj->set_R(stackPosition(stidx(node.pos)));
+ }
+ iWin = iWin && testPlace(idx);
+ } else {
+ runtime->hide(node.obj);
+ iWin = false;
+ }
+ }
+
+ if (iWin)
+ setState(GAME_WIN);
+
+}
+
+const mgVect3f &Puzzle::position(int num) const {
+ xassert(num >= 0 && num < positions_.size());
+ // ÐÑли глобалÑнÑй повоÑÐ¾Ñ Ð½ÐµÐ½Ñлевой, пеÑеÑÑиÑÑваем индекÑ
+ if (globalAngle_ > 0) {
+ int size = sqrt((float)gameSize_);
+ int y = num / size;
+ int x = num - y * size;
+ --size;
+ for (int angle = 0; angle < globalAngle_; ++angle) {
+ int tmp = x;
+ x = size - y;
+ y = tmp;
+ }
+ num = y * (size + 1) + x;
+ }
+ xassert(num >= 0 && num < positions_.size());
+ return positions_[num];
+}
+
+mgVect3f Puzzle::stackPosition(int num) const {
+ mgVect3f bottom = runtime->world2game(stackBottom_);
+ bottom.y -= stackPlaceSize_.y * num;
+ return runtime->game2world(bottom);
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/m_puzzle.h b/engines/qdengine/minigames/adv/m_puzzle.h
new file mode 100644
index 00000000000..37d9127add9
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_puzzle.h
@@ -0,0 +1,117 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_M_PUZZLE_H
+#define QDENGINE_MINIGAMES_ADV_M_PUZZLE_H
+
+#include "qdengine/minigames/adv/MinigameInterface.h"
+#include "qdengine/minigames/adv/FlyObject.h"
+
+namespace QDEngine {
+
+class Puzzle : public MinigameInterface {
+ struct Node {
+ QDObject obj;
+ int angle;
+ int pos;
+
+ bool inStack() const {
+ return pos < -1;
+ }
+ bool isFree() const {
+ return pos == -1;
+ }
+
+ Node() : angle(1), pos(-1) {}
+ };
+
+ typedef vector<Node> Nodes;
+
+public:
+ Puzzle();
+ ~Puzzle();
+
+ void quant(float dt);
+
+private:
+ int gameSize_;
+ int angles_;
+
+ int globalAngle_;
+ float rotateTimePeriod_;
+ float nextRotateTime_;
+
+ bool singleSize_;
+ mgVect2f size_;
+ float depth_;
+
+ Nodes nodes_;
+ /// ÐÐ¾Ð¼ÐµÑ Ð¼ÐµÑÑа Ñ ÐºÐ¾ÑоÑого взÑли ÑÑагменÑ
+ int prevPlace_;
+ /// ÐÐ½Ð´ÐµÐºÑ ÑÑагменÑа на мÑÑи
+ int pickedItem_;
+
+ int inField_;
+
+ float nextObjTime_;
+ int mouseObjPose_;
+
+ QDObject stackBottom_;
+ int stackSize_;
+ mgVect2f stackPlaceSize_;
+
+ Indexes stack_;
+ Indexes field_;
+
+ FlyQDObjects flyObjs_;
+ /// ÑкоÑоÑÑÑ Ð¿Ð°Ð´ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²ÑÑ
в ÑÑек
+ float flySpeed_;
+ /// ÑкоÑоÑÑÑ Ð²Ð¾Ð·Ð²ÑаÑа в ÑÑек
+ float returnSpeed_;
+
+ Coords positions_;
+
+ const char *getStateName(int angle, bool selected, bool small) const;
+ /// повеÑнÑÑÑ ÑиÑкÑ
+ void rotate(int hovItem);
+ /// пÑовеÑиÑÑ Ð½Ð°Ñ
ождение ÑиÑки на Ñвоем меÑÑе
+ bool testPlace(int idx) const;
+ /// ÑиÑка на мÑÑи?
+ bool isOnMouse(const Node& node) const;
+ /// пÑовеÑиÑÑ ÑиÑÐºÑ Ð½Ð° пÑÐµÐ´Ð¼ÐµÑ ÑамоÑÑоÑÑелÑного ÑпÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð·Ð¸ÑиониÑованием
+ bool isFlying(int idx) const;
+ /// конвеÑÑÐ¸Ñ Ð¼ÐµÐ¶Ð´Ñ Ð½Ð¾Ð¼ÐµÑом в ÑÑеке и индекÑом положениÑ
+ int stidx(int idx) const;
+ /// положиÑÑ what в ÑÑÐµÐ¹ÐºÑ where
+ void put(int where, int what, float flowSpeed = -1.f);
+ /// положиÑÑ Ð½Ð° веÑÑÐ¸Ð½Ñ Ð¸Ð½Ð²ÐµÐ½ÑоÑи
+ void putOnStack(int what, float speed);
+ /// веÑнÑÑÑ Ñ Ð¼ÑÑи в инвенÑоÑи
+ void returnToStack();
+ /// миÑовÑе кооÑдинаÑÑ ÑлоÑа на поле
+ const mgVect3f &position(int num) const;
+ /// положение N-ой ÑиÑки в инвенÑоÑи
+ mgVect3f stackPosition(int N) const;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_M_PUZZLE_H
diff --git a/engines/qdengine/minigames/adv/m_scores.cpp b/engines/qdengine/minigames/adv/m_scores.cpp
new file mode 100644
index 00000000000..5ed9a912c64
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_scores.cpp
@@ -0,0 +1,211 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/m_scores.h"
+
+namespace QDEngine {
+
+MinigameInterface *createGame() {
+ return new Scores;
+}
+
+Scores::Scores() {
+ const char *fileName = runtime->parameter("minigame_list");
+ if (!fileName || !*fileName)
+ return;
+
+ if (!_stricmp(fileName, runtime->gameListFileName())) {
+ xxassert(0, (XBuffer() < "[minigame_list] должен ÑÑÑлаÑÑÑÑ Ð½Ð° \"" < runtime->gameListFileName() < "\"").c_str());
+ return;
+ }
+
+ const char *gameButtonName = runtime->parameter("game_miniature_button");
+ if (!gameButtonName || !*gameButtonName)
+ return;
+
+ XBuffer gameData;
+ char name[128];
+ name[127] = 0;
+ for (int num = 1; ; ++num) {
+ _snprintf(name, 127, "%s%02d", gameButtonName, num);
+ if (runtime->testObject(name)) {
+ QDObject obj = runtime->getObject(name);
+ gameData.write(obj->R());
+ games_.push_back(runtime->getObject(name));
+ } else
+ break;
+ }
+
+ if (games_.empty()) {
+ xxassert(false, (XBuffer() < "Ðе Ð½Ð°Ð¹Ð´ÐµÐ½Ñ Ð¾Ð±ÑÐ°Ð·Ñ Ð¸Ð³Ñ \"" < gameButtonName < "\"").c_str());
+ return;
+ }
+
+ if (!runtime->processGameData(gameData))
+ return;
+
+ positions_.resize(games_.size());
+ for (int idx = 0; idx < games_.size(); ++idx)
+ gameData.read(positions_[idx]);
+
+ XStream file(false);
+ if (!file.open(fileName, XS_IN)) {
+ xxassert(false, (XBuffer() < "Ðе ÑдалоÑÑ Ð¾ÑкÑÑÑÑ Ñайл Ñо ÑпиÑком Ð¸Ð³Ñ \"" < fileName < "\"").c_str());
+ return;
+ }
+
+ char read_buf[512];
+ while (!file.eof()) {
+ file.getline(read_buf, 512);
+ XBuffer xbuf((void*)read_buf, strlen(read_buf));
+ int level;
+ xbuf >= level;
+ unsigned char ch;
+ xbuf > ch;
+ if (ch != ':') {
+ xxassert(ch != ':', "ÐепÑавилÑнÑй ÑоÑÐ¼Ð°Ñ Ñайла.");
+ return;
+ }
+ Level lvl(level);
+ dprintf("%d: ", level);
+ while (xbuf.tell() < xbuf.size()) {
+ xbuf > ch;
+ if (isdigit(ch)) {
+ --xbuf;
+ int game;
+ xbuf >= game;
+ lvl.games.push_back(game);
+ dprintf("%d, ", game);
+ if (const MinigameData * data = runtime->getScore(level, game))
+ lvl.data.push_back(GameData(game, *data));
+ }
+ }
+ if (lvl.games.size() > games_.size()) {
+ xxassert(lvl.games.size() <= games_.size(), "Ðало обÑазов игÑ");
+ return;
+ }
+ sort(lvl.data.begin(), lvl.data.end());
+ levels_.push_back(lvl);
+ dprintf("\n");
+ }
+ if (levels_.empty())
+ return;
+ sort(levels_.begin(), levels_.end());
+ level_ = 0;
+ preLevel_ = -1;
+
+ if (!(bestScore_ = runtime->parameter("best_score")))
+ return;
+ if (!(bestTime_ = runtime->parameter("best_time")))
+ return;
+ if (!(lastScore_ = runtime->parameter("last_score")))
+ return;
+ if (!(lastTime_ = runtime->parameter("last_time")))
+ return;
+ if (!(currentLevel_ = runtime->parameter("current_level")))
+ return;
+
+ if (!(prev_ = runtime->getObject(runtime->parameter("prev_button"))))
+ return;
+ if (!(next_ = runtime->getObject(runtime->parameter("next_button"))))
+ return;
+
+ outMaxLevel_ = runtime->getObject(runtime->parameter("for_game_level"));
+ if (outMaxLevel_) {
+ int level = 0;
+ for (; level < levels_.size(); ++level)
+ if (levels_[level].data.size() < levels_[level].games.size())
+ break;
+ if (level < levels_.size())
+ outMaxLevel_.setState((XBuffer() <= levels_[level].level).c_str());
+ else
+ outMaxLevel_.setState("all");
+ }
+
+ setState(MinigameInterface::RUNNING);
+
+}
+
+Scores::~Scores() {
+ runtime->release(prev_);
+ runtime->release(next_);
+
+ QDObjects::iterator it;
+ FOR_EACH(games_, it)
+ runtime->release(*it);
+
+}
+
+void Scores::quant(float dt) {
+ xassert(level_ >= 0 && level_ < levels_.size());
+ const Level& lvl = levels_[level_];
+
+ if (level_ != preLevel_) {
+ preLevel_ = level_;
+
+
+ runtime->setText(currentLevel_, lvl.level);
+
+ for (int idx = 0; idx < games_.size(); ++idx)
+ runtime->hide(games_[idx]);
+
+ for (int idx = 0; idx < games_.size(); ++idx) {
+ if (idx < lvl.data.size()) {
+ const GameData& data = lvl.data[idx];
+ int gameId = data.num;
+ int gameNum;
+ for (gameNum = 0; gameNum < lvl.games.size(); ++gameNum)
+ if (gameId == lvl.games[gameNum])
+ break;
+ xassert(gameNum < lvl.games.size());
+ xassert(gameNum < games_.size());
+ games_[gameNum].setState((XBuffer() < level_).c_str());
+ games_[gameNum]->set_R(positions_[idx]);
+ runtime->setText(getName(bestScore_, idx), data.info.bestScore_);
+ runtime->setText(getName(bestTime_, idx), data.info.bestTime_);
+ runtime->setText(getName(lastScore_, idx), data.info.lastScore_);
+ runtime->setText(getName(lastTime_, idx), data.info.lastTime_);
+ } else {
+ runtime->setText(getName(bestScore_, idx), "");
+ runtime->setText(getName(bestTime_, idx), "");
+ runtime->setText(getName(lastScore_, idx), "");
+ runtime->setText(getName(lastTime_, idx), "");
+ }
+ }
+ }
+
+ if (runtime->mouseLeftPressed()) {
+ if (level_ < levels_.size() - 1 && lvl.data.size() == lvl.games.size() && next_.hit(runtime->mousePosition()))
+ ++level_;
+ else if (level_ > 0 && prev_.hit(runtime->mousePosition()))
+ --level_;
+ }
+}
+
+const char *Scores::getName(const char* begin, int idx) const {
+ static char buf[32];
+ buf[31] = 0;
+ _snprintf(buf, 31, "%s%02d", begin, idx + 1);
+ return buf;
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/m_scores.h b/engines/qdengine/minigames/adv/m_scores.h
new file mode 100644
index 00000000000..6e775c5a835
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_scores.h
@@ -0,0 +1,81 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_M_SCORES_H
+#define QDENGINE_MINIGAMES_ADV_M_SCORES_H
+
+#include "qdengine/minigames/adv/MinigameInterface.h"
+#include "qdengine/minigames/adv/RunTime.h"
+
+namespace QDEngine {
+
+class Scores : public MinigameInterface {
+public:
+ Scores();
+ ~Scores();
+
+ void quant(float dt);
+
+private:
+ struct GameData {
+ GameData(int gameNum, const MinigameData& inf) : num(gameNum), info(inf) {}
+ int num;
+ MinigameData info;
+ bool operator< (const GameData& rsh) const {
+ return info.sequenceIndex_ < rsh.info.sequenceIndex_;
+ }
+ };
+ typedef vector<GameData> GameDatas;
+ struct Level {
+ Level(int lvl = 0) : level(lvl) {}
+ int level;
+ Indexes games;
+ GameDatas data;
+ bool operator< (const Level& rsh) const {
+ return level < rsh.level;
+ }
+ };
+ typedef vector<Level> Levels;
+ Levels levels_;
+
+ const char *currentLevel_;
+ const char *bestScore_;
+ const char *bestTime_;
+ const char *lastScore_;
+ const char *lastTime_;
+
+ QDObject prev_;
+ QDObject next_;
+ QDObject outMaxLevel_;
+
+ QDObjects games_;
+
+ int preLevel_;
+ int level_;
+
+ Coords positions_;
+
+ const char *getName(const char* begin, int idx) const;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_M_SCORES_H
diff --git a/engines/qdengine/minigames/adv/m_swap.cpp b/engines/qdengine/minigames/adv/m_swap.cpp
new file mode 100644
index 00000000000..4f064c7aaa1
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_swap.cpp
@@ -0,0 +1,294 @@
+/* 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 "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/m_swap.h"
+#include "qdengine/minigames/adv/RunTime.h"
+#include "qdengine/minigames/adv/Rect.h"
+
+namespace QDEngine {
+
+typedef Rect<float, mgVect2f> Rectf;
+
+MinigameInterface *createGame() {
+ return new Swap;
+}
+
+enum {
+ EVENT_GET,
+ EVENT_SWAP,
+ EVENT_ROTATE,
+ EVENT_RETURN,
+ EVENT_PUT_RIGHT,
+ EVENT_GET_RIGHT,
+ EVENT_CLICK,
+ EVENT_AUTO_ROTATE
+};
+
+const char *Swap::getStateName(int angle, bool selected) const {
+ static const char *selected_suf = "_sel";
+
+ static char buf[32];
+ buf[31] = 0;
+
+ xassert(angle >= 0 && angle < angles_);
+
+ _snprintf(buf, 31, "%02d%s", angle + 1, selected ? selected_suf : "");
+ return buf;
+}
+
+Swap::Swap() {
+ if (!getParameter("game_size", gameSize_, true) || gameSize_ < 2)
+ return;
+
+ if ((angles_ = getParameter("angles", 4)) < 1)
+ return;
+
+ if ((rotateTimePeriod_ = getParameter("rotate_period", 86400.f)) < 10.f)
+ return;
+ nextRotateTime_ = runtime->time() + rotateTimePeriod_;
+
+ const char *name_begin = runtime->parameter("obj_name_begin", "obj_");
+
+ char buf[128];
+ buf[127] = 0;
+
+ XBuffer gameData;
+
+ for (int idx = 0; idx < gameSize_; ++idx) {
+ _snprintf(buf, 127, "%s%02d", name_begin, idx + 1);
+
+ Node node(idx);
+ node.obj = runtime->getObject(buf);
+ node.angle = 0;
+ node.obj.setState(getStateName(node.angle, false));
+ nodes_.push_back(node);
+
+ gameData.write(node.obj->R());
+ }
+
+ if (!runtime->processGameData(gameData))
+ return;
+
+ positions_.resize(gameSize_);
+ for (int idx = 0; idx < gameSize_; ++idx)
+ gameData.read(positions_[idx]);
+
+ size_ = getParameter("element_size", runtime->getSize(nodes_[0].obj));
+ xassert(size_.x > 0.f && size_.y > 0.f && size_.x < 500.f && size_.y < 500.f);
+ dprintf("element_size = (%6.2f,%6.2f)\n", size_.x, size_.y);
+
+ pickedItem_ = -1;
+ last1_ = last2_ = -1;
+
+ if (runtime->debugMode()) {
+ last1_ = 0;
+ last2_ = 1;
+ rotate(last1_, last2_, false);
+ } else
+ for (int cnt = 0; cnt < 50; ++cnt) {
+ rotate(runtime->rnd(0, gameSize_ - 1), runtime->rnd(0, gameSize_ - 1), true, true);
+ swap(runtime->rnd(0, gameSize_ - 1), runtime->rnd(0, gameSize_ - 1), true);
+ }
+
+
+ setState(MinigameInterface::RUNNING);
+
+}
+
+Swap::~Swap() {
+ Nodes::iterator it;
+ FOR_EACH(nodes_, it)
+ runtime->release(it->obj);
+
+}
+
+void Swap::quant(float dt) {
+ if (pickedItem_ >= 0)
+ runtime->setGameHelpVariant(1);
+ else if (last1_ >= 0)
+ runtime->setGameHelpVariant(2);
+ else
+ runtime->setGameHelpVariant(0);
+
+ if (runtime->time() > nextRotateTime_) {
+ int item1 = runtime->rnd(0, gameSize_ - 1);
+ int item2 = runtime->rnd(0, gameSize_ - 1);
+ if (item1 != last1_ && item1 != last2_ && item1 != pickedItem_ && item2 != last1_ && item2 != last2_ && item2 != pickedItem_) {
+ nextRotateTime_ = runtime->time() + rotateTimePeriod_;
+ rotate(item1, item2, false, true);
+ runtime->event(EVENT_AUTO_ROTATE, mgVect2f(400, 300));
+ return;
+ }
+ }
+
+ mgVect2f mouse = runtime->mousePosition();
+
+ int hovPlace = -1; // ÐÐ¾Ð¼ÐµÑ Ð¼ÐµÑÑа коÑоÑое ÑейÑÐ°Ñ Ð¿Ð¾Ð´ мÑÑкой
+ if (pickedItem_ == -1) {
+ Nodes::iterator it;
+ FOR_EACH(nodes_, it)
+ if (it->obj.hit(mouse)) {
+ hovPlace = distance(nodes_.begin(), it);
+ break;
+ }
+ }
+ if (hovPlace == -1)
+ for (int idx = 0; idx < gameSize_; ++idx) {
+ Rectf rect(size_ * 0.9f);
+ rect.center(runtime->world2game(position(idx)));
+ if (rect.point_inside(mouse)) {
+ hovPlace = idx;
+ break;
+ }
+ }
+
+ //dprintf("%d\n", hovPlace);
+ if (runtime->mouseLeftPressed()) {
+ if (hovPlace >= 0) { // клик по полÑ
+ if (pickedItem_ == -1) { // мÑÑÑ Ð¿ÑÑÑаÑ, беÑем
+ deactivate();
+ runtime->event(EVENT_GET, mouse);
+ pickedItem_ = hovPlace;
+ } else if (pickedItem_ == hovPlace) { // веÑнÑÑÑ Ð½Ð° меÑÑо
+ runtime->event(EVENT_RETURN, mouse);
+ put(pickedItem_, false);
+ pickedItem_ = -1;
+ } else { // поменÑÑÑ Ð¼ÐµÑÑами
+ last1_ = pickedItem_;
+ last2_ = hovPlace;
+ swap(last1_, last2_, false);
+ pickedItem_ = -1;
+ }
+ } else { // пÑÑÑой клик мимо игÑового полÑ
+ deactivate();
+ runtime->event(EVENT_CLICK, mouse);
+ }
+ } else if (runtime->mouseRightPressed()) {
+ if (pickedItem_ >= 0) // еÑли на мÑÑи ÑÑÐ°Ð³Ð¼ÐµÐ½Ñ Ð½Ð¸Ñего не делаем
+ runtime->event(EVENT_CLICK, mouse);
+ else if (hovPlace == last1_ || hovPlace == last2_) // клик по вÑделеннÑм
+ rotate(last1_, last2_, false);
+ else // пÑÑÑой клик мимо акÑивного меÑÑа
+ runtime->event(EVENT_CLICK, mouse);
+ }
+
+ if (pickedItem_ >= 0)
+ nodes_[pickedItem_].obj->set_R(runtime->game2world(mouse, -5000));
+
+ int idx = 0;
+ for (; idx < gameSize_; ++idx)
+ if (!testPlace(idx))
+ break;
+
+ if (idx == nodes_.size()) {
+ deactivate();
+ setState(MinigameInterface::GAME_WIN);
+ }
+}
+
+const mgVect3f &Swap::position(int num) const {
+ xassert(num >= 0 && num < positions_.size());
+ return positions_[num];
+}
+
+void Swap::put(int item, bool hl) {
+ xassert(item >= 0 && item < nodes_.size());
+ nodes_[item].obj->set_R(position(item));
+ nodes_[item].obj.setState(getStateName(nodes_[item].angle, hl));
+
+}
+
+void Swap::deactivate() {
+ if (last1_ >= 0) {
+ xassert(last2_ >= 0);
+ put(last1_, false);
+ put(last2_, false);
+ }
+ last1_ = -1;
+ last2_ = -1;
+}
+
+bool Swap::testPlace(int item) const {
+ xassert(item >= 0 && item < nodes_.size());
+ return nodes_[item].home == item && nodes_[item].angle == 0;
+}
+
+void Swap::swap(int item1, int item2, bool silent) {
+ xassert(item1 >= 0 && item1 < nodes_.size());
+ xassert(item2 >= 0 && item2 < nodes_.size());
+
+ bool res = false;
+ if (!silent) {
+ if (testPlace(item1)) { // ÑнÑли Ñо Ñвоего меÑÑа
+ runtime->event(EVENT_GET_RIGHT, runtime->world2game(position(item1)));
+ res = true;
+ }
+ if (testPlace(item2)) { // ÑнÑли Ñо Ñвоего меÑÑа
+ runtime->event(EVENT_GET_RIGHT, runtime->world2game(position(item2)));
+ res = true;
+ }
+ }
+
+ ::swap(nodes_[item1], nodes_[item2]);
+ put(item1, !silent);
+ put(item2, !silent);
+
+ if (!silent) {
+ if (testPlace(item1)) { // оказалаÑÑ Ð¿Ñи обмене на Ñвоем меÑÑе
+ runtime->event(EVENT_PUT_RIGHT, runtime->world2game(position(item1)));
+ res = true;
+ }
+ if (testPlace(item2)) { // положили на Ñвое Ñвое меÑÑо
+ runtime->event(EVENT_PUT_RIGHT, runtime->world2game(position(item2)));
+ res = true;
+ }
+ if (!res) // пÑоÑÑо обменÑли
+ runtime->event(EVENT_SWAP, runtime->mousePosition());
+ }
+}
+
+void Swap::rotate(int item1, int item2, bool silent, bool avto) {
+ xassert(item1 >= 0 && item1 < nodes_.size());
+ xassert(item2 >= 0 && item2 < nodes_.size());
+
+ if (!silent) {
+ if (testPlace(item1)) // ÑнÑли Ñо Ñвоего меÑÑа
+ runtime->event(EVENT_GET_RIGHT, runtime->world2game(position(item1)));
+ if (testPlace(item2)) // ÑнÑли Ñо Ñвоего меÑÑа
+ runtime->event(EVENT_GET_RIGHT, runtime->world2game(position(item2)));
+ }
+
+ nodes_[item1].angle = (nodes_[item1].angle + 1) % angles_;
+ nodes_[item2].angle = (nodes_[item2].angle + 1) % angles_;
+ put(item1, !avto);
+ put(item2, !avto);
+
+ if (!silent) {
+ if (testPlace(item1)) // оказалаÑÑ Ð¿Ñи обмене на Ñвоем меÑÑе
+ runtime->event(EVENT_PUT_RIGHT, runtime->world2game(position(item1)));
+ if (testPlace(item2)) // положили на Ñвое Ñвое меÑÑо
+ runtime->event(EVENT_PUT_RIGHT, runtime->world2game(position(item2)));
+ runtime->event(EVENT_ROTATE, runtime->mousePosition());
+ }
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/m_swap.h b/engines/qdengine/minigames/adv/m_swap.h
new file mode 100644
index 00000000000..7c8642ed473
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_swap.h
@@ -0,0 +1,78 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_M_SWAP_H
+#define QDENGINE_MINIGAMES_ADV_M_SWAP_H
+
+#include "qdengine/minigames/adv/MinigameInterface.h"
+
+namespace QDEngine {
+
+class Swap : public MinigameInterface {
+public:
+ Swap();
+ ~Swap();
+
+ void quant(float dt);
+private:
+ int gameSize_;
+ int angles_;
+
+ float rotateTimePeriod_;
+ float nextRotateTime_;
+
+ mgVect2f size_;
+
+ struct Node {
+ Node(int idx = -1) : home(idx), angle(0) {}
+ QDObject obj;
+ int angle;
+ int home;
+ };
+ typedef vector<Node> Nodes;
+ Nodes nodes_;
+
+ // ÐÐ½Ð´ÐµÐºÑ ÑÑагменÑа на мÑÑи
+ int pickedItem_;
+ // акÑивнÑе ÑÑагменÑÑ Ð¿Ð¾Ñле обмена
+ int last1_, last2_;
+
+ Coords positions_;
+
+ const char *getStateName(int angle, bool selected) const;
+ // поменÑÑÑ Ð¼ÐµÑÑами, еÑли бÑло ÑнÑÑие или Ñкладка на/Ñ Ð¿ÑавилÑного меÑÑа, Ñо true
+ void swap(int item1, int item2, bool silent);
+ // повеÑнÑÑÑ ÑиÑкÑ
+ void rotate(int item1, int item2, bool silent, bool avto = false);
+ // погаÑиÑÑ Ð²ÑделеннÑе
+ void deactivate();
+ // пÑовеÑиÑÑ Ð½Ð°Ñ
ождение ÑиÑки на Ñвоем меÑÑе
+ bool testPlace(int idx) const;
+ // поÑÑавиÑÑ Ð¾Ð±ÑÐµÐºÑ Ð½Ð° Ñвое меÑÑо и вклÑÑиÑÑ Ð½Ñжное ÑоÑÑоÑние
+ void put(int idx, bool hl);
+ // миÑовÑе кооÑдинаÑÑ ÑлоÑа на поле
+ const mgVect3f &position(int num) const;
+
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_M_SWAP_H
diff --git a/engines/qdengine/minigames/adv/m_triangles.cpp b/engines/qdengine/minigames/adv/m_triangles.cpp
new file mode 100644
index 00000000000..ce509b1a86a
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_triangles.cpp
@@ -0,0 +1,610 @@
+/* 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 "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 *createGame() {
+ return new MinigameTriangle;
+}
+
+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() {
+ QDObjects::iterator it;
+ FOR_EACH(face_, it)
+ runtime->release(*it);
+}
+
+bool MinigameTriangle::Node::hit(const mgVect2f& pos) const {
+ return obj().hit(pos);
+}
+
+MinigameTriangle::MinigameTriangle() {
+ int type = 0;
+ if (!getParameter("game_type", type, true))
+ return;
+
+ switch (type) {
+ case 1:
+ gameType_ = RECTANGLE;
+ break;
+ case 2:
+ gameType_ = HEXAGON;
+ break;
+ default:
+ gameType_ = TRIANGLE;
+ }
+
+ if (!getParameter("size", fieldLines_, true))
+ return;
+ if (fieldLines_ < 2)
+ return;
+
+ if (gameType_ == RECTANGLE) {
+ if (!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:
+ xassert(fieldLines_ % 2 == 0);
+ if (fieldLines_ % 2 != 0)
+ return;
+ fieldSize_ = 3 * sqr(fieldLines_) / 2;
+ break;
+ }
+
+ if (!getParameter("animation_time", animationTime_, true))
+ return;
+
+ 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) {
+ sprintf(name, "%s%02d_%1d", faceNameBegin, num + 1, angle);
+ QDObject obj = runtime->getObject(name);
+ node.face_.push_back(obj);
+ positions_.push_back(obj->R());
+ }
+ }
+
+ XBuffer gameData;
+
+ Coords::iterator it;
+ FOR_EACH(positions_, it)
+ gameData.write(*it);
+
+ if (!runtime->processGameData(gameData))
+ return;
+
+ FOR_EACH(positions_, it)
+ gameData.read(*it);
+
+ for (int num = 1; num <= 2; ++num) {
+ for (int angle = 1; angle <= 3; ++angle) {
+ sprintf(name, "%s%1d_%1d", backNameBegin, num, angle);
+ if (!backSides_[(num - 1) * 3 + angle - 1].load(name))
+ return;
+ }
+ sprintf(name, "%s%1d", selectNameBegin, num);
+ if (!selectBorders_[num - 1].load(name))
+ return;
+ }
+
+ selectDepth_ = nodes_[0].face_[0].depth() - 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() {
+ Nodes::iterator it;
+ FOR_EACH(nodes_, it)
+ it->release();
+
+ for (int idx = 0; idx < 2; ++idx)
+ selectBorders_[idx].release();
+
+ for (int idx = 0; idx < 6; ++idx)
+ backSides_[idx].release();
+}
+
+void MinigameTriangle::Node::debugInfo() const {
+ dprintf("name:\"%s\" state:\"%s\" number:%d rotation:%d flip:%d isBack:%d highlight:%d animated:%d\n", obj().getName(), obj()->current_state_name(), number_, rotation_, flip, isBack_, highlight_, animated_);
+}
+
+const char *MinigameTriangle::Node::getFaceStateName(int angle, bool selected, bool animated, bool instantaneous) {
+ xassert(!selected || !animated); // анимиÑованнÑе вÑделеннÑми бÑÑÑ Ð½Ðµ могÑÑ
+
+ static char *angleNames[3] = {"0", "120", "240"};
+ xassert(angle >= 0 && angle < sizeof(angleNames) / sizeof(angleNames[0]));
+
+ static XBuffer out;
+ out.init();
+
+ out < (animated ? "02_" : "01_") < angleNames[angle] < (selected || instantaneous ? "_sel" : "") < '\0';
+ return out.c_str();
+}
+
+const char *MinigameTriangle::Node::getBackStateName(bool selected, bool animated, bool instantaneous) {
+ xassert(!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_);
+ }
+}
+
+void MinigameTriangle::updateNode(Node& node, int position, int flip, bool quick) {
+ QDObjects::iterator fit;
+ FOR_EACH(node.face_, fit)
+ 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));
+ }
+}
+
+void MinigameTriangle::highlight(int idx, bool hl) {
+ if (idx >= 0) {
+ xassert(idx < (int)nodes_.size());
+ nodes_[idx].highlight_ = hl;
+ updateNode(nodes_[idx], idx);
+ }
+}
+
+void MinigameTriangle::beginSwapNodes(int pos1, int pos2) {
+ xassert(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));
+
+ //dprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 1st phase <<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 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) { // поÑÑавили на Ñвое меÑÑо
+ xassert(!node1.isBack_);
+ counted = true;
+ runtime->event(EVENT_PUT_RIGHT, node1.obj()->screen_R());
+ }
+
+ if (node2.number_ == pos1) { // ÑнÑли Ñо Ñвоего меÑÑа
+ xassert(node2.isBack_);
+ counted = true;
+ runtime->event(EVENT_GET_RIGHT, node1.obj()->screen_R());
+ }
+
+ if (node2.number_ == pos2) { // поÑÑавили на Ñвое меÑÑо
+ xassert(!node2.isBack_);
+ counted = true;
+ runtime->event(EVENT_PUT_RIGHT, node2.obj()->screen_R());
+ }
+
+ if (node1.number_ == pos2) { // ÑнÑли Ñо Ñвоего меÑÑа
+ xassert(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;
+ Nodes::const_iterator it;
+ FOR_EACH(nodes_, it)
+ 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);
+
+ QDObjects::iterator it;
+ FOR_EACH(node1.face_, it)
+ (*it).setState(Node::getFaceStateName(0, false, false, false));
+ FOR_EACH(node2.face_, it)
+ (*it).setState(Node::getFaceStateName(0, false, false, false));
+
+ 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;
+
+ //dprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 2nd phase 1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 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;
+
+ //dprintf(">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 2nd phase 2 <<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", 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]);
+ //dprintf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change %d <> %d, finished ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", animatedNodes_[0], animatedNodes_[1]);
+
+ animatedNodes_[0] = -1;
+ animatedNodes_[1] = -1;
+
+ return true;
+ }
+
+ 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 (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_);
+ selectBorders_[1].releaseObject(node.border_);
+ }
+ }
+ }
+
+ 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) {
+ dprintf("----- DUBUG INFO FOR %d POSITION --------------------\n", mousePos);
+ dprintf("row = %d, begin = %d, orientation = %d\n", 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_;
+ }
+ //case HEXAGON:
+ xassert(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_;
+ }
+ //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;
+ }
+ //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}}
+ };
+ xassert(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 {
+ xassert(pos * 3 + angle < positions_.size());
+ return positions_[pos * 3 + angle];
+}
+
+} // namespace QDEngine
diff --git a/engines/qdengine/minigames/adv/m_triangles.h b/engines/qdengine/minigames/adv/m_triangles.h
new file mode 100644
index 00000000000..2323b714a54
--- /dev/null
+++ b/engines/qdengine/minigames/adv/m_triangles.h
@@ -0,0 +1,131 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_M_TRIANGLES_H
+#define QDENGINE_MINIGAMES_ADV_M_TRIANGLES_H
+
+#include "qdengine/minigames/adv/common.h"
+#include "qdengine/minigames/adv/MinigameInterface.h"
+#include "qdengine/minigames/adv/ObjectContainer.h"
+
+namespace QDEngine {
+
+class MinigameTriangle : public MinigameInterface {
+ enum GameType {
+ TRIANGLE,
+ RECTANGLE,
+ HEXAGON
+ };
+
+ enum AnimationState {
+ NO_ANIMATION,
+ FIRST_PHASE,
+ SECOND_PHASE,
+ FIRD_PHASE
+ };
+
+ struct Node {
+ Node(int number = -1, int rot = -1);
+
+ void release();
+ void debugInfo() const;
+
+ const QDObject &obj() const {
+ return isBack_ ? back_ : face_[flip];
+ }
+
+ bool hit(const mgVect2f& pos) const;
+
+ int number_; // пÑавилÑÐ½Ð°Ñ Ð¿Ð¾Ð·Ð¸ÑÐ¸Ñ (Ð½Ð¾Ð¼ÐµÑ ÑлоÑа)
+ int rotation_; // ÑекÑÑий Ñгол повоÑоÑа (пÑавилÑнÑй Ñгол = 0)
+ int flip;
+ QDObjects face_; // Ð½Ð°Ð±Ð¾Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½ÑÑ
Ñглов пеÑевоÑоÑа Ð´Ð»Ñ Ð»Ð¸Ñевой ÑÑоÑонÑ
+ QDObject back_; // обÑаÑÐ½Ð°Ñ ÑÑоÑона
+ QDObject border_; // Ñамка
+ bool isBack_; // повеÑнÑÑ Ð»Ð¸Ñом (true) или ÑÑбаÑкой (false)
+ bool highlight_;
+ bool animated_;
+
+ static const char *getFaceStateName(int angle, bool selected, bool animated, bool instantaneous);
+ static const char *getBackStateName(bool selected, bool animated, bool instantaneous);
+ static const char *getBorderStateName(bool selected);
+ };
+ typedef vector<Node> Nodes;
+
+public:
+ MinigameTriangle();
+ ~MinigameTriangle();
+ void quant(float dt);
+
+private:
+ GameType gameType_;
+ Coords positions_;
+ int selectDepth_;
+
+ int fieldLines_;
+ int fieldWidth_;
+ int fieldSize_;
+ Nodes nodes_;
+ ObjectContainer selectBorders_[2];
+ ObjectContainer backSides_[6];
+ int selected_;
+ int hovered_;
+
+ AnimationState animationState_;
+ int animatedNodes_[2];
+ float animationTime_;
+ float animationTimer_;
+
+ /// оÑиÑÑиÑÑ ÑÑбаÑÐºÑ ÑиÑки
+ void releaseNodeBack(Node& node);
+ /// вÑÑÑавиÑÑ Ð³ÑаÑиÑеÑкие ÑоÑÑоÑÐ½Ð¸Ñ ÑооÑвеÑÑÑвÑÑÑие ÑекÑÑÐµÐ¼Ñ Ð»Ð¾Ð³Ð¸ÑеÑкомÑ
+ void updateNode(Node& node, int position, int flip = 0, bool quick = false);
+ /// подÑвеÑиÑÑ/поÑÑÑиÑÑ ÑÑагменÑ
+ void highlight(int idx, bool hl);
+
+ /// поменÑÑÑ Ð¼ÐµÑÑами ÑиÑки
+ void swapNodes(int pos1, int pos2, bool quick);
+ /// наÑаÑÑ Ð°Ð½Ð¸Ð¼Ð°ÑÐ¸Ñ Ð¾Ð±Ð¼ÐµÐ½Ð°
+ void beginSwapNodes(int pos1, int pos2);
+ /// оÑÑабоÑка анимаÑии пеÑевоÑоÑа ÑиÑек
+ bool animate(float dt);
+ /// вÑзÑваеÑÑÑ Ð¿Ð¾Ñле оконÑÐ°Ð½Ð¸Ñ Ð¿ÐµÑевоÑоÑа
+ void endSwapNodes(int pos1, int pos2);
+
+ /// по номеÑÑ ÑиÑки вÑÑиÑлÑÐµÑ Ñлой
+ int rowByNum(int num) const;
+ /// возвÑаÑÐ°ÐµÑ Ñ ÐºÐ°ÐºÐ¾Ð¹ ÑиÑки наÑинаеÑÑÑ Ñлой
+ int rowBegin(int row) const;
+ /// 0 - Ñгол ввеÑÑ
+ int orientation(int num) const;
+ /// можно поменÑÑÑ Ð¼ÐµÑÑами
+ bool compatible(int num1, int num2) const;
+ /// опÑеделиÑÑ ÐºÐ°ÐºÐ¾Ð¹ бÑÐ´ÐµÑ Ñгол повоÑоÑа Ñ num1 пÑи пеÑеÑ
оде в num2
+ int getRotate(int num1, int num2) const;
+ /// напÑавление пеÑевоÑоÑа
+ int destination(int num1, int num2) const;
+ /// по номеÑÑ ÑлоÑа и ÑÐ³Ð»Ñ Ð¿ÐµÑевоÑоÑа (Ñ ÑÑеÑом Ñипа игÑÑ) возвÑаÑÐ°ÐµÑ ÑкÑаннÑе кооÑдинаÑÑ
+ mgVect3f slotCoord(int pos, int angle = 0) const;
+};
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_M_TRIANGLES_H
diff --git a/engines/qdengine/minigames/adv/qdMath.h b/engines/qdengine/minigames/adv/qdMath.h
new file mode 100644
index 00000000000..8acd888032f
--- /dev/null
+++ b/engines/qdengine/minigames/adv/qdMath.h
@@ -0,0 +1,66 @@
+/* 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/>.
+ *
+ */
+
+#ifndef QDENGINE_MINIGAMES_ADV_QDMATH_H
+#define QDENGINE_MINIGAMES_ADV_QDMATH_H
+
+namespace QDEngine {
+
+const float FLT_EPS = 1.192092896e-07f;
+const float FLT_INF = 1.e+30f;
+
+#define SQRT2 1.41421356f
+#define SQRT3 1.73205081f
+
+inline float dist(const mgVect2f& v1, const mgVect2f& v2) {
+ return sqrt(sqr(v1.x - v2.x) + sqr(v1.y - v2.y));
+}
+
+inline float abs(const mgVect2f& v) {
+ return sqrt(sqr(v.x) + sqr(v.y));
+}
+
+inline void norm(mgVect2f& v) {
+ float mod = abs(v);
+ if (mod < FLT_EPS) {
+ v = mgVect2f(0, 1);
+ return;
+ }
+ v.x /= mod;
+ v.y /= mod;
+}
+
+template<class T, class T1, class T2>
+inline T clamp(const T& x, const T1& xmin, const T2& xmax) {
+ if (x < xmin) return xmin;
+ if (x > xmax) return xmax;
+ return x;
+}
+
+template<class T>
+inline T abs(const T& x) {
+ if (x < 0) return -x;
+ return x;
+}
+
+} // namespace QDEngine
+
+#endif // QDENGINE_MINIGAMES_ADV_QDMATH_H
More information about the Scummvm-git-logs
mailing list