Initial commit
This commit is contained in:
94
engines/qdengine/minigames/adv/EffectManager.cpp
Normal file
94
engines/qdengine/minigames/adv/EffectManager.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/* 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, MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
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(_runtime->getParameter("effect_time", 3.f), 0.5f, 10.f);
|
||||
_phaseTime = clamp(_runtime->getParameter("effect_phase_time", _effectTime / 20.f), 0.03f, 1.f);
|
||||
_phaseSpeed = clamp(_runtime->getParameter("effect_phase_speed", 1.5f), 1.05f, 10.f);
|
||||
|
||||
_current = EFFECT_COUNT;
|
||||
|
||||
_effectTimer = 0;
|
||||
_phaseTimer = 0;
|
||||
}
|
||||
|
||||
EffectManager::~EffectManager() {
|
||||
_runtime->release(_effect);
|
||||
}
|
||||
|
||||
void EffectManager::quant(float dt) {
|
||||
if (_current == EFFECT_COUNT)
|
||||
return;
|
||||
|
||||
if (_runtime->getTime() > _effectTimer) {
|
||||
stop(_current);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_runtime->getTime() > _phaseTimer) {
|
||||
_phaseTimer = _runtime->getTime() + _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->getTime() + _effectTime;
|
||||
_current = id;
|
||||
_phaseTimer = _runtime->getTime();
|
||||
_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
|
||||
61
engines/qdengine/minigames/adv/EffectManager.h
Normal file
61
engines/qdengine/minigames/adv/EffectManager.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* 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, MinigameManager *runtime);
|
||||
~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;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_EFFECT_MANAGER_H
|
||||
151
engines/qdengine/minigames/adv/EventManager.cpp
Normal file
151
engines/qdengine/minigames/adv/EventManager.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#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(MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
_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;
|
||||
}
|
||||
debugC(2, kDebugMinigames, "EventManager(): registered %d trigger objects", _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);
|
||||
|
||||
if (read != 2)
|
||||
warning("EventManager(): Incorrect description string: %s", str_cache);
|
||||
|
||||
if (read == 2) {
|
||||
if (preset.triggerEventID >= (int)_triggerEvents.size())
|
||||
error("EventManager(): Reference to an unregistered trigger in %s", str_cache);
|
||||
|
||||
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);
|
||||
|
||||
if (read != 4)
|
||||
warning("EventManager(): Incorrect event description string: %d", idx);
|
||||
|
||||
if (preset.triggerEventID >= (int)_triggerEvents.size())
|
||||
error("EventManager(): Reference to an unregistered trigger in %s", str_cache);
|
||||
|
||||
if (read == 4 && preset.triggerEventID < (int)_triggerEvents.size())
|
||||
_eventPresets.push_back(preset);
|
||||
else
|
||||
_eventPresets.push_back(EventPreset());
|
||||
} else
|
||||
break;
|
||||
}
|
||||
debugC(2, kDebugMinigames, "EventManager(): registered %d events", _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) {
|
||||
assert(eventID >= 0);
|
||||
debugC(6, kDebugMinigames, "EventManager() System event: %d", eventID);
|
||||
|
||||
assert(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) {
|
||||
debugC(6, kDebugMinigames, "EventManager() Event: %d, pos=(%5.1f, %5.1f), fartor=%d", eventID, pos.x, pos.y, factor);
|
||||
|
||||
eventID += SYSTEM_EVENTS_SIZE;
|
||||
|
||||
if (eventID >= (int)_eventPresets.size())
|
||||
return;
|
||||
|
||||
const EventPreset& pr = _eventPresets[eventID];
|
||||
|
||||
if (pr.triggerEventID >= 0) {
|
||||
assert(pr.triggerEventID < (int)_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
|
||||
59
engines/qdengine/minigames/adv/EventManager.h
Normal file
59
engines/qdengine/minigames/adv/EventManager.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 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 {
|
||||
|
||||
class EventManager {
|
||||
public:
|
||||
EventManager(MinigameManager *runtime);
|
||||
|
||||
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 Std::vector<EventPreset> EventPresets;
|
||||
EventPresets _eventPresets;
|
||||
|
||||
QDObjects _triggerEvents;
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_EVENT_MANAGER_H
|
||||
49
engines/qdengine/minigames/adv/ExportInterface.cpp
Normal file
49
engines/qdengine/minigames/adv/ExportInterface.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#include "qdengine/minigames/adv/common.h"
|
||||
#include "qdengine/minigames/adv/RunTime.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
qdMiniGameInterface *create_adv_minigame(const char *name, MinigameConsCallback callback) {
|
||||
debugC(3, kDebugMinigames, "open_game_interface: %s, runtime%s", name, g_runtime ? "!=0" : "==0");
|
||||
|
||||
if (!g_runtime)
|
||||
return g_runtime = new MinigameManager(callback);
|
||||
|
||||
return new MinigameManager(callback);
|
||||
}
|
||||
|
||||
bool close_adv_minigame(qdMiniGameInterface *game) {
|
||||
debugC(3, kDebugMinigames, "close_game_interface, runtime%s%s", g_runtime == game ? "==game" : "!=game", g_runtime ? "!=0" : "==0");
|
||||
|
||||
delete game;
|
||||
if (game == g_runtime)
|
||||
g_runtime = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
56
engines/qdengine/minigames/adv/FlyObject.cpp
Normal file
56
engines/qdengine/minigames/adv/FlyObject.cpp
Normal file
@@ -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, MinigameManager *runtime) {
|
||||
bool ret = FlyObjectBase::quant(dt);
|
||||
obj->set_R(runtime->game2world(current, depth));
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
54
engines/qdengine/minigames/adv/FlyObject.h
Normal file
54
engines/qdengine/minigames/adv/FlyObject.h
Normal file
@@ -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, MinigameManager *runtime);
|
||||
|
||||
float depth;
|
||||
int data;
|
||||
};
|
||||
|
||||
typedef Std::vector<FlyQDObject> FlyQDObjects;
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_FLYOBJECT_H
|
||||
55
engines/qdengine/minigames/adv/HoldData.h
Normal file
55
engines/qdengine/minigames/adv/HoldData.h
Normal file
@@ -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 {
|
||||
assert(!_empty);
|
||||
return _data;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_HOLD_DATA_H
|
||||
54
engines/qdengine/minigames/adv/MinigameInterface.h
Normal file
54
engines/qdengine/minigames/adv/MinigameInterface.h
Normal file
@@ -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
|
||||
113
engines/qdengine/minigames/adv/ObjectContainer.cpp
Normal file
113
engines/qdengine/minigames/adv/ObjectContainer.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/* 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/qdengine.h"
|
||||
#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(MinigameManager *runtime) {
|
||||
for (auto &it : _objects)
|
||||
runtime->release(it);
|
||||
|
||||
_objects.clear();
|
||||
_current = 0;
|
||||
}
|
||||
|
||||
void ObjectContainer::pushObject(QDObject& obj) {
|
||||
assert(Common::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, MinigameManager *runtime, bool hide) {
|
||||
if (!runtime->testObject(base_name)) {
|
||||
warning("ObjectContainer::load(): Object '%s' not found", transCyrillic(base_name));
|
||||
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(MinigameManager *runtime) {
|
||||
for (auto &it : _objects)
|
||||
runtime->hide(it);
|
||||
}
|
||||
|
||||
QDObject ObjectContainer::getObject() {
|
||||
if (_current < (int)_objects.size())
|
||||
return _objects[_current++];
|
||||
|
||||
return _objects[0]; // bad, but better than crashing
|
||||
|
||||
}
|
||||
|
||||
void ObjectContainer::releaseObject(QDObject& obj, MinigameManager *runtime) {
|
||||
QDObjects::iterator it = Common::find(_objects.begin(), _objects.end(), obj);
|
||||
if (it != _objects.end()) {
|
||||
if ((int)Common::distance(_objects.begin(), it) >= _current)
|
||||
error("ObjectContainer::releaseObject(): Object released more than once in to the pool: %s", transCyrillic(name()));
|
||||
|
||||
runtime->hide(obj);
|
||||
if (_current > 0)
|
||||
SWAP(*it, _objects[--_current]);
|
||||
obj = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
54
engines/qdengine/minigames/adv/ObjectContainer.h
Normal file
54
engines/qdengine/minigames/adv/ObjectContainer.h
Normal file
@@ -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
|
||||
Common::String _name;
|
||||
#endif
|
||||
const char *name() const;
|
||||
void pushObject(QDObject& obj);
|
||||
|
||||
public:
|
||||
ObjectContainer();
|
||||
void release(MinigameManager *runtime);
|
||||
|
||||
bool load(const char *_name, MinigameManager *runtime, bool hide = true);
|
||||
void hideAll(MinigameManager *runtime);
|
||||
|
||||
const mgVect3f &coord() const {
|
||||
return _coord;
|
||||
}
|
||||
|
||||
QDObject getObject();
|
||||
void releaseObject(QDObject& obj, MinigameManager *runtime);
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_OBJECT_CONTAINER_H
|
||||
98
engines/qdengine/minigames/adv/Range.cpp
Normal file
98
engines/qdengine/minigames/adv/Range.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
141
engines/qdengine/minigames/adv/Range.h
Normal file
141
engines/qdengine/minigames/adv/Range.h
Normal file
@@ -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
|
||||
364
engines/qdengine/minigames/adv/Rect.h
Normal file
364
engines/qdengine/minigames/adv/Rect.h
Normal file
@@ -0,0 +1,364 @@
|
||||
/* 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/xmath.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 vect_type VectType;
|
||||
typedef 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& _topleft, const VectType& size) :
|
||||
_left(_topleft.x),
|
||||
_top(_topleft.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 _lefttop() const {
|
||||
return VectType(_left, _top);
|
||||
}
|
||||
VectType right_top() const {
|
||||
return VectType(_left + _width, _top);
|
||||
}
|
||||
VectType _leftbottom() 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;
|
||||
|
||||
#if 0
|
||||
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()));
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
1196
engines/qdengine/minigames/adv/RunTime.cpp
Normal file
1196
engines/qdengine/minigames/adv/RunTime.cpp
Normal file
File diff suppressed because it is too large
Load Diff
318
engines/qdengine/minigames/adv/RunTime.h
Normal file
318
engines/qdengine/minigames/adv/RunTime.h
Normal file
@@ -0,0 +1,318 @@
|
||||
/* 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
|
||||
|
||||
#include "common/hashmap.h"
|
||||
|
||||
#include "qdengine/minigames/adv/common.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class SeekableWriteStream;
|
||||
}
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
class qdEngineInterface;
|
||||
class qdMinigameSceneInterface;
|
||||
|
||||
class MinigameInterface;
|
||||
class TextManager;
|
||||
class TimeManager;
|
||||
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;
|
||||
|
||||
void write(Common::WriteStream &out) const;
|
||||
void read(Common::ReadStream &out);
|
||||
};
|
||||
|
||||
struct GameInfo {
|
||||
GameInfo();
|
||||
void persist(Common::SeekableReadStream &in);
|
||||
|
||||
void write(Common::WriteStream &out) const;
|
||||
void read(Common::ReadStream &in);
|
||||
|
||||
void free();
|
||||
static int version() {
|
||||
return 9;
|
||||
}
|
||||
bool empty() const {
|
||||
return _empty && _game._sequenceIndex < 0;
|
||||
}
|
||||
|
||||
MinigameData _game;
|
||||
bool _empty;
|
||||
TimeManagerData _timeManagerData;
|
||||
EffectManagerData _effectManagerData;
|
||||
uint _dataSize;
|
||||
void *_gameData;
|
||||
};
|
||||
|
||||
typedef MinigameInterface *(*MinigameConsCallback)(MinigameManager *runtime);
|
||||
|
||||
qdMiniGameInterface *create_adv_minigame(const char *name, MinigameConsCallback callback);
|
||||
|
||||
bool close_adv_minigame(qdMiniGameInterface *game);
|
||||
|
||||
class MinigameManager : public qdMiniGameInterface {
|
||||
friend class TempValue;
|
||||
public:
|
||||
MinigameManager(MinigameConsCallback callback);
|
||||
~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(Common::SeekableReadStream &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 getTime() const {
|
||||
return _gameTime;
|
||||
}
|
||||
|
||||
GameInfo *getCurrentGameInfo() const { return _currentGameInfo; };
|
||||
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 Std::vector<float> &prob) const;
|
||||
|
||||
// файл со списком игр по уровням
|
||||
const char *gameListFileName() const {
|
||||
return "resource/minigames.lst";
|
||||
}
|
||||
|
||||
int getParameter(const char* name, const int& defValue);
|
||||
bool getParameter(const char* name, int& out, bool obligatory);
|
||||
float getParameter(const char* name, const float &defValue);
|
||||
bool getParameter(const char* name, float &out, bool obligatory);
|
||||
mgVect2f getParameter(const char* name, const mgVect2f& defValue);
|
||||
bool getParameter(const char* name, mgVect2f& out, bool obligatory);
|
||||
mgVect2i getParameter(const char* name, const mgVect2i& defValue);
|
||||
bool getParameter(const char* name, mgVect2i& out, bool obligatory);
|
||||
|
||||
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;
|
||||
// текущее состояние для включения справки
|
||||
Common::String _complete_help_state_name;
|
||||
// справка по игре
|
||||
QDObject _game_help;
|
||||
QDObject _game_help_trigger;
|
||||
bool _game_help_enabled;
|
||||
// текущее состояние для включения справки
|
||||
Common::String _game_help_state_name;
|
||||
|
||||
// интерфейс к движку
|
||||
const qdEngineInterface *_engine;
|
||||
// интерфейс к текущей сцене
|
||||
qdMinigameSceneInterface *_scene;
|
||||
|
||||
// игра запущена для отладки
|
||||
bool _debugMode;
|
||||
// rnd seed
|
||||
int _seed;
|
||||
|
||||
// кнопки мыши инвертированы
|
||||
bool _invertMouseButtons;
|
||||
|
||||
// имя файла и информацией о минииграх
|
||||
Common::String _state_container_name;
|
||||
// количество пройденных игр на каждом уровне
|
||||
typedef Common::HashMap<int, int> Counters;
|
||||
Counters _completeCounters;
|
||||
|
||||
struct GameInfoIndex {
|
||||
GameInfoIndex(int idx, int level) : _gameNum(idx), _gameLevel(level) {}
|
||||
int _gameNum;
|
||||
int _gameLevel;
|
||||
|
||||
void write(Common::WriteStream &out) const;
|
||||
void read(Common::ReadStream &in);
|
||||
|
||||
bool operator< (const GameInfoIndex& rs) const {
|
||||
return _gameLevel == rs._gameLevel ? _gameNum < rs._gameNum : _gameLevel < rs._gameLevel;
|
||||
}
|
||||
};
|
||||
|
||||
struct GameInfoIndex_Hash {
|
||||
uint operator()(const GameInfoIndex& x) const {
|
||||
return (x._gameNum << 16) + x._gameLevel;
|
||||
}
|
||||
};
|
||||
|
||||
struct GameInfoIndex_EqualTo {
|
||||
uint operator()(const GameInfoIndex& x, const GameInfoIndex& y) const {
|
||||
return x._gameNum == y._gameNum && x._gameLevel == y._gameLevel;
|
||||
}
|
||||
};
|
||||
|
||||
// информация о пройденных играх
|
||||
typedef Common::HashMap<GameInfoIndex, GameInfo, GameInfoIndex_Hash, GameInfoIndex_EqualTo> 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);
|
||||
|
||||
MinigameConsCallback _callback = nullptr;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_RUNTIME_H
|
||||
381
engines/qdengine/minigames/adv/TextManager.cpp
Normal file
381
engines/qdengine/minigames/adv/TextManager.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#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(MinigameManager *runtime) {
|
||||
char str_cache[256];
|
||||
|
||||
_runtime = runtime;
|
||||
|
||||
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, _runtime))
|
||||
break;
|
||||
|
||||
debugCN(2, kDebugMinigames, "TextManager(): %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);
|
||||
if (read != 2)
|
||||
warning("TextManager(): incorrect font size definition in [%s]", str_cache);
|
||||
} else {
|
||||
QDObject obj = digit.pool.getObject();
|
||||
obj.setState("0");
|
||||
digit.size = _runtime->getSize(obj);
|
||||
digit.pool.releaseObject(obj, _runtime);
|
||||
}
|
||||
debugC(2, kDebugMinigames, "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);
|
||||
|
||||
if (read != 11) {
|
||||
warning("TextManager(): incorrect particle definition in [%s]", str_cache);
|
||||
break;
|
||||
}
|
||||
_escapes.push_back(escape);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
debugCN(2, kDebugMinigames, "TextManager(): registered %d particle escapes", _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 = _runtime->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);
|
||||
|
||||
if (read != 3) {
|
||||
warning("TextManager::getStaticPreset(): Incorrect text format description in %s", transCyrillic(name));
|
||||
return false;
|
||||
}
|
||||
|
||||
char *pos_obj = strchr(str, '|');
|
||||
|
||||
if (!pos_obj) {
|
||||
warning("TextManager::getStaticPreset(): Incorrect text format description (2) in %s", transCyrillic(name));
|
||||
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() {
|
||||
for (auto &mit : _flowMsgs)
|
||||
mit.release();
|
||||
|
||||
for (auto &sit : _staticMsgs)
|
||||
sit.release();
|
||||
|
||||
for (auto &dit : _fonts)
|
||||
dit.pool.release(_runtime);
|
||||
}
|
||||
|
||||
int TextManager::createStaticText(const mgVect3f& pos, int fontID, TextAlign align) {
|
||||
assert(fontID >= 0 && fontID < (int)_fonts.size());
|
||||
|
||||
StaticMessage msg(_runtime, &_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) {
|
||||
assert(textID >= 0 && textID < (int)_staticMsgs.size());
|
||||
|
||||
_staticMsgs[textID].setText(txt);
|
||||
}
|
||||
|
||||
void TextManager::showText(const char *txt, const mgVect2f& pos, int fontID, int escapeID) {
|
||||
assert(fontID >= 0 && fontID < (int)_fonts.size());
|
||||
assert(escapeID >= 0 && escapeID < (int)_escapes.size());
|
||||
|
||||
Escape& es = _escapes[escapeID];
|
||||
|
||||
Message msg(_runtime, &_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) {
|
||||
assert(fontID >= 0 && fontID < (int)_fonts.size());
|
||||
assert(escapeID >= 0 && escapeID < (int)_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;
|
||||
textID = 0;
|
||||
}
|
||||
|
||||
TextManager::StaticMessage::StaticMessage(MinigameManager *runtime, Font *font, TextAlign align) {
|
||||
_font = font;
|
||||
_align = align;
|
||||
_depth = 0.f;
|
||||
_runtime = runtime;
|
||||
}
|
||||
|
||||
void TextManager::StaticMessage::release() {
|
||||
for (auto &it : _objects)
|
||||
_font->pool.releaseObject(it, _runtime);
|
||||
|
||||
_objects.clear();
|
||||
}
|
||||
|
||||
void TextManager::StaticMessage::setText(const char *str) {
|
||||
assert(_font);
|
||||
|
||||
if (!str) {
|
||||
release();
|
||||
return;
|
||||
}
|
||||
|
||||
int len = (int)strlen(str);
|
||||
|
||||
if ((int)_objects.size() < len)
|
||||
_objects.resize(len);
|
||||
else
|
||||
while ((int)_objects.size() > len) {
|
||||
if (_objects.back())
|
||||
_font->pool.releaseObject(_objects.back(), _runtime);
|
||||
_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], _runtime);
|
||||
}
|
||||
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (y < -_font->size.y || y > _runtime->screenSize().y + _font->size.y
|
||||
|| x < -2 * width || x > _runtime->screenSize().x + 2 * width) {
|
||||
release();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &it : _objects) {
|
||||
if (it)
|
||||
it->set_R(_runtime->game2world(mgVect2f(x, y), _depth));
|
||||
x += _font->size.x;
|
||||
}
|
||||
}
|
||||
|
||||
TextManager::Message::Message(MinigameManager *runtime, Font *font)
|
||||
: StaticMessage(runtime, 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->getTime()) {
|
||||
int sgn = _targetScore - _currentScore < 0 ? -1 : 1;
|
||||
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->getTime() + _scoreUpdateTime : -1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextManager::updateScore(int score) {
|
||||
_targetScore = score;
|
||||
if (_scoreUpdateTimer < 0.f)
|
||||
_scoreUpdateTimer = _runtime->getTime();
|
||||
}
|
||||
|
||||
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
|
||||
137
engines/qdengine/minigames/adv/TextManager.h
Normal file
137
engines/qdengine/minigames/adv/TextManager.h
Normal file
@@ -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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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(MinigameManager *runtime);
|
||||
~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 Std::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 Std::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(MinigameManager *runtime, 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;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
typedef Std::vector<StaticMessage> StaticMessages;
|
||||
|
||||
struct Message : public StaticMessage {
|
||||
Message(MinigameManager *runtime, Font *font = 0);
|
||||
void release();
|
||||
|
||||
void quant(float dt);
|
||||
|
||||
float _time;
|
||||
mgVect2f _vel;
|
||||
mgVect2f _accel;
|
||||
};
|
||||
typedef Std::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;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_TEXT_MANAGER_H
|
||||
48
engines/qdengine/minigames/adv/common.cpp
Normal file
48
engines/qdengine/minigames/adv/common.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* 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 {
|
||||
|
||||
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(MinigameManager *runtime) const {
|
||||
return runtime->getDepth(_obj);
|
||||
}
|
||||
|
||||
void QDObject::setState(const char* name) {
|
||||
if (!_obj->is_state_active(name))
|
||||
_obj->set_state(name);
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
92
engines/qdengine/minigames/adv/common.h
Normal file
92
engines/qdengine/minigames/adv/common.h
Normal file
@@ -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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QDENGINE_MINIGAMES_ADV_COMMON_H
|
||||
#define QDENGINE_MINIGAMES_ADV_COMMON_H
|
||||
|
||||
#include "qdengine/qd_fwd.h"
|
||||
#include "qdengine/qdcore/qd_minigame_interface.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
class MinigameManager;
|
||||
|
||||
typedef mgVect3<int> mgVect3i;
|
||||
|
||||
class QDObject {
|
||||
qdMinigameObjectInterface *_obj;
|
||||
|
||||
#ifdef _DEBUG
|
||||
Common::String _name;
|
||||
#endif
|
||||
|
||||
public:
|
||||
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(MinigameManager *runtime) 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 Std::vector<QDObject> QDObjects;
|
||||
typedef Std::vector<int> Indexes;
|
||||
typedef Std::vector<mgVect3f> Coords;
|
||||
|
||||
class MinigameManager;
|
||||
extern MinigameManager *g_runtime;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_COMMON_H
|
||||
216
engines/qdengine/minigames/adv/m_karaoke.cpp
Normal file
216
engines/qdengine/minigames/adv/m_karaoke.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#include "qdengine/minigames/adv/common.h"
|
||||
#include "qdengine/minigames/adv/m_karaoke.h"
|
||||
#include "qdengine/minigames/adv/RunTime.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
MinigameInterface *createMinigameKaraoke(MinigameManager *runtime) {
|
||||
return new Karaoke(runtime);
|
||||
}
|
||||
|
||||
Karaoke::Node::Node() {
|
||||
type = Karaoke::CLEAR;
|
||||
time = 0.f;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Karaoke::Karaoke(MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
|
||||
_controlName = _runtime->parameter("control_name", true);
|
||||
if (!_controlName || !*_controlName)
|
||||
return;
|
||||
|
||||
_colorReaded = _runtime->parameter("color_first", true);
|
||||
if (!_colorReaded || !*_colorReaded)
|
||||
return;
|
||||
|
||||
struct Parse {
|
||||
Common::File &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 = Common::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(Common::File& _file, Nodes& _nodes) : file(_file), nodes(_nodes) {
|
||||
currentTime = 0.f;
|
||||
begin = cur = buf;
|
||||
bool prevNumber = false;
|
||||
while (!file.eos()) {
|
||||
switch (read()) {
|
||||
case 0:
|
||||
return;
|
||||
case '/': {
|
||||
if (read() == '/') {
|
||||
--cur;
|
||||
break;
|
||||
}
|
||||
cur -= 2;
|
||||
putLine(prevNumber);
|
||||
prevNumber = true;
|
||||
|
||||
file.seek(-1, SEEK_CUR);
|
||||
float tm = 0;
|
||||
|
||||
char c;
|
||||
do {
|
||||
c = read();
|
||||
} while ((c >= '0' && c <= '9') || c == '.');
|
||||
|
||||
cur = buf;
|
||||
tm = strtod(cur, nullptr);
|
||||
|
||||
if (tm <= 0.f) {
|
||||
currentTime = 0.f;
|
||||
nodes.push_back(Node());
|
||||
} else
|
||||
currentTime = tm;
|
||||
file.seek(-1, SEEK_CUR);
|
||||
continue;
|
||||
}
|
||||
case '>':
|
||||
if (prevNumber)
|
||||
--cur;
|
||||
}
|
||||
prevNumber = false;
|
||||
}
|
||||
putLine();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const char *fileName = _runtime->parameter("text_file", true);
|
||||
if (!fileName)
|
||||
return;
|
||||
|
||||
Common::File file;
|
||||
Common::Path path((const char *)transCyrillic(fileName), '\\');
|
||||
|
||||
if (!file.open(path)) {
|
||||
// Try with punyencoded path
|
||||
if (!file.open(path.punycodeEncode())) {
|
||||
assert(!(Common::String() + "Не удалось открыть файл \"" + fileName + "\"").c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Parse(file, _nodes);
|
||||
debugC(2, kDebugMinigames, "read %d tags", _nodes.size());
|
||||
|
||||
_startScreenTag = 0;
|
||||
_currentTag = 0;
|
||||
|
||||
_startTime = 0.001f * g_system->getMillis();
|
||||
_startTagTime = 0.f;
|
||||
|
||||
setState(MinigameInterface::RUNNING);
|
||||
|
||||
}
|
||||
|
||||
void Karaoke::quant(float dt) {
|
||||
float curTime = 0.001f * g_system->getMillis() - _startTime;
|
||||
if (curTime < 0.f)
|
||||
curTime = 0.f;
|
||||
|
||||
Node& node = _nodes[_currentTag];
|
||||
if (node.type == CLEAR) {
|
||||
++_currentTag;
|
||||
if ((uint)_currentTag == _nodes.size())
|
||||
setState(MinigameInterface::GAME_WIN);
|
||||
_startScreenTag = _currentTag;
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String outText;
|
||||
outText += _colorReaded;
|
||||
int idx = _startScreenTag;
|
||||
while (idx < _currentTag) {
|
||||
assert((uint)idx < _nodes.size());
|
||||
assert(_nodes[idx].type == STRING);
|
||||
outText += _nodes[idx].text.c_str();
|
||||
++idx;
|
||||
}
|
||||
|
||||
float phase = (curTime - _startTagTime) / node.time;
|
||||
assert(phase >= 0.f);
|
||||
if (phase >= 1.f) {
|
||||
outText += node.text + "&>";
|
||||
++_currentTag;
|
||||
_startTagTime += node.time;
|
||||
if ((uint)_currentTag == _nodes.size())
|
||||
setState(MinigameInterface::GAME_WIN);
|
||||
} else {
|
||||
int part = phase * node.text.size();
|
||||
outText += Common::String(node.text.begin(), node.text.begin() + part) + "&>";
|
||||
outText += Common::String(node.text.begin() + part, node.text.end()).c_str();
|
||||
}
|
||||
|
||||
++idx;
|
||||
while ((uint)idx < _nodes.size()) {
|
||||
if (_nodes[idx].type == CLEAR)
|
||||
break;
|
||||
outText += _nodes[idx].text.c_str();
|
||||
++idx;
|
||||
}
|
||||
|
||||
if (_runtime->mouseRightPressed())
|
||||
debugC(2, kDebugMinigames, "%s", outText.c_str());
|
||||
|
||||
_runtime->setText(_controlName, outText.c_str());
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
66
engines/qdengine/minigames/adv/m_karaoke.h
Normal file
66
engines/qdengine/minigames/adv/m_karaoke.h
Normal file
@@ -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_M_KARAOKE_H
|
||||
#define QDENGINE_MINIGAMES_ADV_M_KARAOKE_H
|
||||
|
||||
#include "qdengine/minigames/adv/common.h"
|
||||
#include "qdengine/minigames/adv/MinigameInterface.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
MinigameInterface *createMinigameKaraoke(MinigameManager *runtime);
|
||||
|
||||
class Karaoke : public MinigameInterface {
|
||||
public:
|
||||
Karaoke(MinigameManager *runtime);
|
||||
void quant(float dt);
|
||||
|
||||
enum TagType {
|
||||
STRING,
|
||||
CLEAR
|
||||
};
|
||||
|
||||
private:
|
||||
const char *_controlName = nullptr;
|
||||
const char *_colorReaded = nullptr;
|
||||
|
||||
struct Node {
|
||||
Node();
|
||||
TagType type;
|
||||
float time;
|
||||
Common::String text;
|
||||
};
|
||||
|
||||
typedef Std::vector<Node> Nodes;
|
||||
Nodes _nodes;
|
||||
|
||||
float _startTime = 0.f;
|
||||
int _startScreenTag = 0;
|
||||
int _currentTag = 0;
|
||||
float _startTagTime = 0.f;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_M_KARAOKE_H
|
||||
476
engines/qdengine/minigames/adv/m_puzzle.cpp
Normal file
476
engines/qdengine/minigames/adv/m_puzzle.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#include "qdengine/minigames/adv/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 *createMinigamePuzzle(MinigameManager *runtime) {
|
||||
return new Puzzle(runtime);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
assert(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(MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
|
||||
if (!_runtime->getParameter("game_size", _gameSize, true))
|
||||
return;
|
||||
assert(_gameSize > 0 && _gameSize < 100);
|
||||
|
||||
_field.resize(_gameSize, -1);
|
||||
_globalAngle = 0;
|
||||
|
||||
_singleSize = _runtime->getParameter("small_objects", false);
|
||||
|
||||
_angles = _runtime->getParameter("angles", 4);
|
||||
assert(_angles > 0 && _angles < 10);
|
||||
|
||||
if (!(_stackBottom = _runtime->getObject(_runtime->parameter("inventory_bottom"))))
|
||||
return;
|
||||
if (!_runtime->getParameter("inventory_size", _stackSize, true))
|
||||
return;
|
||||
|
||||
if (_runtime->getParameter("rotate_period", _rotateTimePeriod, false)) {
|
||||
assert(sqr(sqrt((float)_gameSize)) == _gameSize);
|
||||
if (sqr(sqrt((float)_gameSize)) != _gameSize)
|
||||
return;
|
||||
} else
|
||||
_rotateTimePeriod = 86400; // сутки
|
||||
_nextRotateTime = _runtime->getTime() + _rotateTimePeriod;
|
||||
|
||||
_flySpeed = _runtime->getParameter("inventory_drop_speed", 240.f);
|
||||
assert(_flySpeed > 0.f);
|
||||
_returnSpeed = _runtime->getParameter("inventory_return_speed", -1.f);
|
||||
|
||||
const char *name_begin = _runtime->parameter("obj_name_begin", "obj_");
|
||||
|
||||
char buf[128];
|
||||
buf[127] = 0;
|
||||
|
||||
Common::MemoryReadWriteStream gameData(DisposeAfterUse::YES);
|
||||
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));
|
||||
|
||||
node.obj->R().write(gameData);
|
||||
|
||||
_nodes.push_back(node);
|
||||
}
|
||||
|
||||
if (!_runtime->processGameData(gameData))
|
||||
return;
|
||||
|
||||
GameInfo *gameInfo = _runtime->getCurrentGameInfo();
|
||||
if (gameInfo) {
|
||||
Common::MemoryReadStream data((byte *)gameInfo->_gameData, gameInfo->_dataSize);
|
||||
for (int idx = 0; idx < _gameSize; ++idx) {
|
||||
mgVect3f crd;
|
||||
crd.read(data);
|
||||
_nodes[idx].obj->set_R(crd);
|
||||
_positions.push_back(crd);
|
||||
}
|
||||
} else {
|
||||
for (int idx = 0; idx < _gameSize; ++idx) {
|
||||
mgVect3f crd;
|
||||
crd.read(gameData);
|
||||
_nodes[idx].obj->set_R(crd);
|
||||
_positions.push_back(crd);
|
||||
}
|
||||
}
|
||||
|
||||
if (_runtime->debugMode())
|
||||
_nodes[0].angle = _angles - 1;
|
||||
|
||||
_size = _runtime->getSize(_nodes[0].obj);
|
||||
debugC(2, kDebugMinigames, "size = (%6.2f,%6.2f)", _size.x, _size.y);
|
||||
|
||||
_depth = _nodes[0].obj.depth(runtime);
|
||||
|
||||
_stackPlaceSize = _runtime->getParameter("inventory_place_size", _size * 1.2f);
|
||||
assert(_stackPlaceSize.x > 0.f && _stackPlaceSize.x < 500.f && _stackPlaceSize.y > 0.f && _stackPlaceSize.y < 500.f);
|
||||
debugC(2, kDebugMinigames, "stackPlaceSize = (%5.1f, %5.1f)", _stackPlaceSize.x, _stackPlaceSize.y);
|
||||
|
||||
_prevPlace = -1;
|
||||
_pickedItem = -1;
|
||||
_mouseObjPose = stidx(_stackSize + 1);
|
||||
|
||||
_inField = _runtime->debugMode() ? _nodes.size() : 0;
|
||||
_nextObjTime = _runtime->getTime();
|
||||
|
||||
setState(MinigameInterface::RUNNING);
|
||||
}
|
||||
|
||||
Puzzle::~Puzzle() {
|
||||
for (auto &it : _nodes)
|
||||
_runtime->release(it.obj);
|
||||
|
||||
_runtime->release(_stackBottom);
|
||||
}
|
||||
|
||||
void Puzzle::rotate(int item) {
|
||||
assert(item >= 0 && item < (int)_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 {
|
||||
assert(item >= 0 && item < (int)_nodes.size());
|
||||
return _nodes[item].pos == item && _nodes[item].angle == 0;
|
||||
}
|
||||
|
||||
bool Puzzle::isFlying(int idx) const {
|
||||
for (auto &it : _flyObjs)
|
||||
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) {
|
||||
assert(where < (int)_field.size());
|
||||
assert(what >= 0 && what < (int)_nodes.size());
|
||||
|
||||
Node& node = _nodes[what];
|
||||
int start = node.pos;
|
||||
|
||||
if (flowSpeed > 0.f || isFlying(what)) {
|
||||
FlyQDObject* flyObj = 0;
|
||||
|
||||
FlyQDObjects::iterator fit;
|
||||
for (fit = _flyObjs.begin(); fit != _flyObjs.end(); 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() {
|
||||
assert(_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->getTime() > _nextRotateTime) {
|
||||
_runtime->event(EVENT_FIELD_ROTATE, mgVect2f(400, 300));
|
||||
_nextRotateTime = _runtime->getTime() + _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, _runtime))
|
||||
++fit;
|
||||
else
|
||||
fit = _flyObjs.erase(fit);
|
||||
|
||||
if (_inField < (int)_nodes.size() && _runtime->getTime() > _nextObjTime &&
|
||||
((int)_stack.size() < _stackSize - 1 || ((int)_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 = Common::distance(_nodes.begin(), it);
|
||||
|
||||
++_inField;
|
||||
_nextObjTime = _runtime->getTime() + _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 < (int)_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 < (int)_stack.size()) { // взять фрагмент из стека на мышь
|
||||
_runtime->event(EVENT_GET, mouse);
|
||||
Indexes::iterator it = _stack.begin() + hovStack;
|
||||
assert(*it >= 0);
|
||||
_prevPlace = -1;
|
||||
_pickedItem = *it;
|
||||
_nodes[_pickedItem].pos = _mouseObjPose;
|
||||
_stack.erase(it);
|
||||
for (int idx = hovStack; idx < (int)_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 < (int)_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 < (int)_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(_runtime) - 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 {
|
||||
assert(num >= 0 && num < (int)_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;
|
||||
}
|
||||
assert(num >= 0 && num < (int)_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
|
||||
121
engines/qdengine/minigames/adv/m_puzzle.h
Normal file
121
engines/qdengine/minigames/adv/m_puzzle.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/* 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 {
|
||||
|
||||
MinigameInterface *createMinigamePuzzle(MinigameManager *runtime);
|
||||
|
||||
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 Std::vector<Node> Nodes;
|
||||
|
||||
public:
|
||||
Puzzle(MinigameManager *runtime);
|
||||
~Puzzle();
|
||||
|
||||
void quant(float dt);
|
||||
|
||||
private:
|
||||
int _gameSize = 0;
|
||||
int _angles = 0;
|
||||
|
||||
int _globalAngle = 0;
|
||||
float _rotateTimePeriod = 0.f;
|
||||
float _nextRotateTime = 0.f;
|
||||
|
||||
bool _singleSize = false;
|
||||
mgVect2f _size;
|
||||
float _depth = 0.f;
|
||||
|
||||
Nodes _nodes;
|
||||
/// Номер места с которого взяли фрагмент
|
||||
int _prevPlace = -1;
|
||||
/// Индекс фрагмента на мыши
|
||||
int _pickedItem = -1;
|
||||
|
||||
int _inField = 0; // количество фрагментов на поле
|
||||
|
||||
float _nextObjTime = 0.f;
|
||||
int _mouseObjPose = -1; // индекс положения фрагмента на мыши в стеке
|
||||
|
||||
QDObject _stackBottom;
|
||||
int _stackSize = 0;
|
||||
mgVect2f _stackPlaceSize;
|
||||
|
||||
Indexes _stack;
|
||||
Indexes _field;
|
||||
|
||||
FlyQDObjects _flyObjs;
|
||||
/// скорость падения новых в стек
|
||||
float _flySpeed = 0.f;
|
||||
/// скорость возврата в стек
|
||||
float _returnSpeed = -1.f;
|
||||
|
||||
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;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_M_PUZZLE_H
|
||||
245
engines/qdengine/minigames/adv/m_scores.cpp
Normal file
245
engines/qdengine/minigames/adv/m_scores.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#include "qdengine/minigames/adv/common.h"
|
||||
#include "qdengine/minigames/adv/m_scores.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
MinigameInterface *createMinigameScores(MinigameManager *runtime) {
|
||||
return new Scores(runtime);
|
||||
}
|
||||
|
||||
Scores::Scores(MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
|
||||
const char *fileName = _runtime->parameter("minigame_list");
|
||||
if (!fileName || !*fileName)
|
||||
return;
|
||||
|
||||
if (!scumm_stricmp(fileName, _runtime->gameListFileName())) {
|
||||
error("[minigame_list] must refer to \"%s\"", transCyrillic(_runtime->gameListFileName()));
|
||||
}
|
||||
|
||||
const char *gameButtonName = _runtime->parameter("game_miniature_button");
|
||||
if (!gameButtonName || !*gameButtonName)
|
||||
return;
|
||||
|
||||
Common::MemoryReadWriteStream gameData(DisposeAfterUse::YES);
|
||||
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);
|
||||
obj->R().write(gameData);
|
||||
_games.push_back(_runtime->getObject(name));
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (_games.empty()) {
|
||||
error("Game images not found '%s'", transCyrillic(gameButtonName));
|
||||
}
|
||||
|
||||
if (!_runtime->processGameData(gameData))
|
||||
return;
|
||||
|
||||
_positions.resize(_games.size());
|
||||
GameInfo *gameInfo = _runtime->getCurrentGameInfo();
|
||||
if (gameInfo) {
|
||||
Common::MemoryReadStream buf((byte *)gameInfo->_gameData, gameInfo->_dataSize);
|
||||
for (auto &it : _positions)
|
||||
it.read(buf);
|
||||
} else {
|
||||
for (auto &it : _positions)
|
||||
it.read(gameData);
|
||||
}
|
||||
|
||||
Common::File file;
|
||||
if (!file.open(Common::Path(_runtime->gameListFileName()))) {
|
||||
error("Failed to open games list file '%s'", transCyrillic(fileName));
|
||||
}
|
||||
|
||||
char read_buf[512];
|
||||
while (!file.eos()) {
|
||||
file.readLine(read_buf, 512);
|
||||
Common::MemoryReadStream buf((const byte *)&read_buf[0], strlen(read_buf));
|
||||
int level = buf.readByte() - '0';
|
||||
byte ch = buf.readByte();
|
||||
if (ch != ':') {
|
||||
error("Wrong file format");
|
||||
}
|
||||
Level lvl(level);
|
||||
debugCN(2, kDebugMinigames, "%d: ", level);
|
||||
while (buf.pos() < buf.size()) {
|
||||
ch = buf.readByte();
|
||||
if (Common::isDigit(ch)) {
|
||||
int game = ch - '0';
|
||||
lvl.games.push_back(game);
|
||||
debugCN(2, kDebugMinigames, "%d, ", game);
|
||||
if (const MinigameData *data = _runtime->getScore(level, game)) {
|
||||
if (data->_sequenceIndex != -1)
|
||||
lvl.data.push_back(GameData(game, *data));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lvl.games.size() > _games.size()) {
|
||||
error("Not enough game images");
|
||||
}
|
||||
Common::sort(lvl.data.begin(), lvl.data.end());
|
||||
levels_.push_back(lvl);
|
||||
debugC(2, kDebugMinigames, "%s", "");
|
||||
}
|
||||
|
||||
if (levels_.empty())
|
||||
return;
|
||||
Common::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 (!(_timeFormat = _runtime->parameter("time_format")))
|
||||
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;
|
||||
|
||||
if (!(_gameBorder = _runtime->getObject(_runtime->parameter("game_border"))))
|
||||
return;
|
||||
|
||||
_outMaxLevel = _runtime->getObject(_runtime->parameter("for_game_level"));
|
||||
if (_outMaxLevel) {
|
||||
uint level = 0;
|
||||
for (; level < levels_.size(); ++level)
|
||||
if (levels_[level].data.size() < levels_[level].games.size())
|
||||
break;
|
||||
if (level < levels_.size())
|
||||
_outMaxLevel.setState(Common::String::format("%d", levels_[level].level).c_str());
|
||||
else
|
||||
_outMaxLevel.setState("all");
|
||||
}
|
||||
|
||||
setState(MinigameInterface::RUNNING);
|
||||
|
||||
}
|
||||
|
||||
Scores::~Scores() {
|
||||
_runtime->release(_prev);
|
||||
_runtime->release(_next);
|
||||
|
||||
for (auto &it : _games)
|
||||
_runtime->release(it);
|
||||
|
||||
}
|
||||
|
||||
void Scores::quant(float dt) {
|
||||
assert(_level >= 0 && _level < (int)levels_.size());
|
||||
const Level& lvl = levels_[_level];
|
||||
|
||||
char timeBuf[32];
|
||||
|
||||
if (_level != _preLevel) {
|
||||
_preLevel = _level;
|
||||
|
||||
|
||||
_runtime->setText(_currentLevel, lvl.level);
|
||||
|
||||
for (int idx = 0; idx < (int)_games.size(); ++idx)
|
||||
_runtime->hide(_games[idx]);
|
||||
|
||||
for (int idx = 0; idx < (int)_games.size(); ++idx) {
|
||||
if (idx < (int)lvl.data.size()) {
|
||||
const GameData& data = lvl.data[idx];
|
||||
int gameId = data.num;
|
||||
int gameNum;
|
||||
for (gameNum = 0; gameNum < (int)lvl.games.size(); ++gameNum)
|
||||
if (gameId == lvl.games[gameNum])
|
||||
break;
|
||||
assert(gameNum < (int)lvl.games.size());
|
||||
assert(gameNum < (int)_games.size());
|
||||
_games[gameNum].setState(Common::String::format("%d", _level).c_str());
|
||||
_games[gameNum]->set_R(_positions[idx]);
|
||||
_runtime->setText(getName(_bestScore, idx), data.info._bestScore);
|
||||
|
||||
if (!_timeFormat || !*_timeFormat)
|
||||
snprintf(timeBuf, 31, "%d", data.info._bestTime);
|
||||
else
|
||||
snprintf(timeBuf, 31, _timeFormat, data.info._bestTime / 60, data.info._bestTime % 60);
|
||||
|
||||
_runtime->setText(getName(_bestTime, idx), timeBuf);
|
||||
_runtime->setText(getName(_lastScore, idx), data.info._lastScore);
|
||||
|
||||
if (!_timeFormat || !*_timeFormat)
|
||||
snprintf(timeBuf, 31, "%d", data.info._lastTime);
|
||||
else
|
||||
snprintf(timeBuf, 31, _timeFormat, data.info._lastTime / 60, data.info._lastTime % 60);
|
||||
|
||||
_runtime->setText(getName(_lastTime, idx), timeBuf);
|
||||
} else {
|
||||
_runtime->setText(getName(_bestScore, idx), "");
|
||||
_runtime->setText(getName(_bestTime, idx), "");
|
||||
_runtime->setText(getName(_lastScore, idx), "");
|
||||
_runtime->setText(getName(_lastTime, idx), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_runtime->hide(_gameBorder);
|
||||
for (int idx = 0; idx < (int)_games.size(); ++idx) {
|
||||
if (_games[idx].hit(_runtime->mousePosition())) {
|
||||
_gameBorder->set_R(_runtime->game2world(_runtime->world2game(_games[idx])));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_runtime->mouseLeftPressed()) {
|
||||
if (_level < (int)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
|
||||
88
engines/qdengine/minigames/adv/m_scores.h
Normal file
88
engines/qdengine/minigames/adv/m_scores.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* 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 {
|
||||
|
||||
MinigameInterface *createMinigameScores(MinigameManager *runtime);
|
||||
|
||||
class Scores : public MinigameInterface {
|
||||
public:
|
||||
Scores(MinigameManager *runtime);
|
||||
~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 Std::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 Std::vector<Level> Levels;
|
||||
Levels levels_;
|
||||
|
||||
const char *_currentLevel = nullptr;
|
||||
const char *_bestScore = nullptr;
|
||||
const char *_bestTime = nullptr;
|
||||
const char *_lastScore = nullptr;
|
||||
const char *_lastTime = nullptr;
|
||||
|
||||
const char *_timeFormat = nullptr;
|
||||
|
||||
QDObject _prev;
|
||||
QDObject _next;
|
||||
QDObject _gameBorder;
|
||||
QDObject _outMaxLevel;
|
||||
|
||||
QDObjects _games;
|
||||
|
||||
int _preLevel = -1;
|
||||
int _level = -1;
|
||||
|
||||
Coords _positions;
|
||||
|
||||
const char *getName(const char *begin, int idx) const;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_M_SCORES_H
|
||||
301
engines/qdengine/minigames/adv/m_swap.cpp
Normal file
301
engines/qdengine/minigames/adv/m_swap.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#include "qdengine/minigames/adv/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 *createMinigameSwap(MinigameManager *runtime) {
|
||||
return new Swap(runtime);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
assert(angle >= 0 && angle < _angles);
|
||||
|
||||
snprintf(buf, 31, "%02d%s", angle + 1, selected ? selected_suf : "");
|
||||
return buf;
|
||||
}
|
||||
|
||||
Swap::Swap(MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
|
||||
if (!_runtime->getParameter("game_size", _gameSize, true) || _gameSize < 2)
|
||||
return;
|
||||
|
||||
if ((_angles = _runtime->getParameter("angles", 4)) < 1)
|
||||
return;
|
||||
|
||||
if ((_rotateTimePeriod = _runtime->getParameter("rotate_period", 86400.f)) < 10.f)
|
||||
return;
|
||||
_nextRotateTime = _runtime->getTime() + _rotateTimePeriod;
|
||||
|
||||
const char *name_begin = _runtime->parameter("obj_name_begin", "obj_");
|
||||
|
||||
Common::MemoryReadWriteStream gameData(DisposeAfterUse::YES);
|
||||
|
||||
for (int idx = 0; idx < _gameSize; ++idx) {
|
||||
Common::String buf = Common::String::format("%s%02d", name_begin, idx + 1);
|
||||
|
||||
Node node(idx);
|
||||
node.obj = _runtime->getObject(buf.c_str());
|
||||
node.angle = 0;
|
||||
node.obj.setState(getStateName(node.angle, false));
|
||||
_nodes.push_back(node);
|
||||
|
||||
node.obj->R().write(gameData);
|
||||
}
|
||||
|
||||
if (!_runtime->processGameData(gameData))
|
||||
return;
|
||||
|
||||
_positions.resize(_gameSize);
|
||||
GameInfo *gameInfo = _runtime->getCurrentGameInfo();
|
||||
if (gameInfo) {
|
||||
Common::MemoryReadStream buf((byte *)gameInfo->_gameData, gameInfo->_dataSize);
|
||||
for (auto &it : _positions)
|
||||
it.read(buf);
|
||||
} else {
|
||||
for (auto &it : _positions)
|
||||
it.read(gameData);
|
||||
}
|
||||
|
||||
_size = _runtime->getParameter("element_size", _runtime->getSize(_nodes[0].obj));
|
||||
assert(_size.x > 0.f && _size.y > 0.f && _size.x < 500.f && _size.y < 500.f);
|
||||
debugC(2, kDebugMinigames, "element_size = (%6.2f,%6.2f)", _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() {
|
||||
for (auto &it : _nodes)
|
||||
_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->getTime() > _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->getTime() + _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) {
|
||||
for (auto &it : _nodes)
|
||||
if (it.obj.hit(mouse)) {
|
||||
hovPlace = Common::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;
|
||||
}
|
||||
}
|
||||
|
||||
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 == (int)_nodes.size()) {
|
||||
deactivate();
|
||||
setState(MinigameInterface::GAME_WIN);
|
||||
}
|
||||
}
|
||||
|
||||
const mgVect3f &Swap::position(int num) const {
|
||||
assert(num >= 0 && num < (int)_positions.size());
|
||||
return _positions[num];
|
||||
}
|
||||
|
||||
void Swap::put(int item, bool hl) {
|
||||
assert(item >= 0 && item < (int)_nodes.size());
|
||||
_nodes[item].obj->set_R(position(item));
|
||||
_nodes[item].obj.setState(getStateName(_nodes[item].angle, hl));
|
||||
|
||||
}
|
||||
|
||||
void Swap::deactivate() {
|
||||
if (_last1 >= 0) {
|
||||
assert(_last2 >= 0);
|
||||
put(_last1, false);
|
||||
put(_last2, false);
|
||||
}
|
||||
_last1 = -1;
|
||||
_last2 = -1;
|
||||
}
|
||||
|
||||
bool Swap::testPlace(int item) const {
|
||||
assert(item >= 0 && item < (int)_nodes.size());
|
||||
return _nodes[item].home == item && _nodes[item].angle == 0;
|
||||
}
|
||||
|
||||
void Swap::swap(int item1, int item2, bool silent) {
|
||||
assert(item1 >= 0 && item1 < (int)_nodes.size());
|
||||
assert(item2 >= 0 && item2 < (int)_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) {
|
||||
assert(item1 >= 0 && item1 < (int)_nodes.size());
|
||||
assert(item2 >= 0 && item2 < (int)_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
|
||||
81
engines/qdengine/minigames/adv/m_swap.h
Normal file
81
engines/qdengine/minigames/adv/m_swap.h
Normal file
@@ -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_SWAP_H
|
||||
#define QDENGINE_MINIGAMES_ADV_M_SWAP_H
|
||||
|
||||
#include "qdengine/minigames/adv/MinigameInterface.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
MinigameInterface *createMinigameSwap(MinigameManager *runtime);
|
||||
|
||||
class Swap : public MinigameInterface {
|
||||
public:
|
||||
Swap(MinigameManager *runtime);
|
||||
~Swap();
|
||||
|
||||
void quant(float dt);
|
||||
private:
|
||||
int _gameSize = 0;
|
||||
int _angles = 0;
|
||||
|
||||
float _rotateTimePeriod = 0.5f; // время поворота фишки
|
||||
float _nextRotateTime = 0.f; // время следующего поворота
|
||||
|
||||
mgVect2f _size;
|
||||
|
||||
struct Node {
|
||||
Node(int idx = -1) : home(idx), angle(0) {}
|
||||
QDObject obj;
|
||||
int angle;
|
||||
int home;
|
||||
};
|
||||
typedef Std::vector<Node> Nodes;
|
||||
Nodes _nodes;
|
||||
|
||||
// Индекс фрагмента на мыши
|
||||
int _pickedItem = -1;
|
||||
// активные фрагменты после обмена
|
||||
int _last1 = 0, _last2 = 0;
|
||||
|
||||
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;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_M_SWAP_H
|
||||
639
engines/qdengine/minigames/adv/m_triangles.cpp
Normal file
639
engines/qdengine/minigames/adv/m_triangles.cpp
Normal file
@@ -0,0 +1,639 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
|
||||
#include "qdengine/qdengine.h"
|
||||
#include "qdengine/minigames/adv/m_triangles.h"
|
||||
#include "qdengine/minigames/adv/RunTime.h"
|
||||
#include "qdengine/minigames/adv/EventManager.h"
|
||||
#include "qdengine/minigames/adv/qdMath.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
MinigameInterface *createMinigameTriangle(MinigameManager *runtime) {
|
||||
return new MinigameTriangle(runtime);
|
||||
}
|
||||
|
||||
enum {
|
||||
EVENT_TURN,
|
||||
EVENT_GET_RIGHT,
|
||||
EVENT_PUT_RIGHT
|
||||
};
|
||||
|
||||
MinigameTriangle::Node::Node(int number, int rot) {
|
||||
_number = number;
|
||||
_rotation = rot;
|
||||
_isBack = false;
|
||||
_highlight = false;
|
||||
_animated = false;
|
||||
_flip = 0;
|
||||
}
|
||||
|
||||
void MinigameTriangle::Node::release(MinigameManager *runtime) {
|
||||
for (auto &it : _face)
|
||||
runtime->release(it);
|
||||
}
|
||||
|
||||
bool MinigameTriangle::Node::hit(const mgVect2f &pos) const {
|
||||
return obj().hit(pos);
|
||||
}
|
||||
|
||||
MinigameTriangle::MinigameTriangle(MinigameManager *runtime) {
|
||||
_runtime = runtime;
|
||||
|
||||
int type = 0;
|
||||
if (!_runtime->getParameter("game_type", type, true))
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case 1:
|
||||
_gameType = RECTANGLE;
|
||||
break;
|
||||
case 2:
|
||||
_gameType = HEXAGON;
|
||||
break;
|
||||
default:
|
||||
_gameType = TRIANGLE;
|
||||
}
|
||||
|
||||
_fieldLines = _fieldWidth = 0;
|
||||
|
||||
if (!_runtime->getParameter("size", _fieldLines, true))
|
||||
return;
|
||||
if (_fieldLines < 2)
|
||||
return;
|
||||
|
||||
if (_gameType == RECTANGLE) {
|
||||
if (!_runtime->getParameter("width", _fieldWidth, true))
|
||||
return;
|
||||
if (_fieldWidth < 2)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_gameType) {
|
||||
case TRIANGLE:
|
||||
_fieldSize = sqr(_fieldLines);
|
||||
break;
|
||||
case RECTANGLE:
|
||||
_fieldSize = _fieldLines * _fieldWidth;
|
||||
break;
|
||||
case HEXAGON:
|
||||
assert(_fieldLines % 2 == 0);
|
||||
if (_fieldLines % 2 != 0)
|
||||
return;
|
||||
_fieldSize = 3 * sqr(_fieldLines) / 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_runtime->getParameter("animation_time", _animationTime, true))
|
||||
return;
|
||||
|
||||
_quickReselect = _runtime->getParameter("quick_reselect", false);
|
||||
|
||||
const char *faceNameBegin = _runtime->parameter("object_name_begin", "obj_");
|
||||
const char *backNameBegin = _runtime->parameter("backg_name_begin", "element__back");
|
||||
const char *selectNameBegin = _runtime->parameter("select_name_begin", "element_select_");
|
||||
|
||||
char name[64];
|
||||
name[63] = 0;
|
||||
for (int num = 0; num < _fieldSize; ++num) {
|
||||
_nodes.push_back(Node(num, 0));
|
||||
Node &node = _nodes.back();
|
||||
for (int angle = 1; angle <= 3; ++angle) {
|
||||
snprintf(name, 63, "%s%02d_%1d", faceNameBegin, num + 1, angle);
|
||||
debugC(5, kDebugMinigames, "Loading face object: %s", name);
|
||||
QDObject obj = _runtime->getObject(name);
|
||||
node._face.push_back(obj);
|
||||
_positions.push_back(obj->R());
|
||||
}
|
||||
}
|
||||
|
||||
Common::MemoryReadWriteStream gameData(DisposeAfterUse::YES);
|
||||
|
||||
for (auto &it : _positions)
|
||||
it.write(gameData);
|
||||
|
||||
if (!_runtime->processGameData(gameData))
|
||||
return;
|
||||
|
||||
GameInfo *gameInfo = _runtime->getCurrentGameInfo();
|
||||
if (gameInfo) {
|
||||
Common::MemoryReadStream buf((byte *)gameInfo->_gameData, gameInfo->_dataSize);
|
||||
for (auto &it : _positions)
|
||||
it.read(buf);
|
||||
} else {
|
||||
for (auto &it : _positions)
|
||||
it.read(gameData);
|
||||
}
|
||||
|
||||
for (int num = 1; num <= 2; ++num) {
|
||||
for (int angle = 1; angle <= 3; ++angle) {
|
||||
snprintf(name, 63, "%s%1d_%1d", backNameBegin, num, angle);
|
||||
debugC(5, kDebugMinigames, "Loading back object: %s", name);
|
||||
if (!_backSides[(num - 1) * 3 + angle - 1].load(name, _runtime))
|
||||
return;
|
||||
}
|
||||
snprintf(name, 63, "%s%1d", selectNameBegin, num);
|
||||
debugC(5, kDebugMinigames, "Loading select object: %s", name);
|
||||
if (!_selectBorders[num - 1].load(name, _runtime))
|
||||
return;
|
||||
}
|
||||
|
||||
_selectDepth = _nodes[0]._face[0].depth(_runtime) - 1000;
|
||||
|
||||
_selected = -1;
|
||||
_hovered = -1;
|
||||
|
||||
_animationState = NO_ANIMATION;
|
||||
_animatedNodes[0] = _animatedNodes[1] = -1;
|
||||
_animationTimer = 0.f;
|
||||
|
||||
if (!_runtime->debugMode())
|
||||
for (int i = 0; i < 150; ++i) {
|
||||
int pos1 = _runtime->rnd(0, _nodes.size() - 1);
|
||||
for (int j = 0; j < 20; ++j) {
|
||||
int pos2 = _runtime->rnd(pos1 - 10, pos1 + 10);
|
||||
if (compatible(pos1, pos2)) {
|
||||
swapNodes(pos1, pos2, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < _fieldSize; ++idx)
|
||||
updateNode(_nodes[idx], idx);
|
||||
|
||||
setState(RUNNING);
|
||||
}
|
||||
|
||||
MinigameTriangle::~MinigameTriangle() {
|
||||
for (auto &it : _nodes)
|
||||
it.release(_runtime);
|
||||
|
||||
for (int idx = 0; idx < 2; ++idx)
|
||||
_selectBorders[idx].release(_runtime);
|
||||
|
||||
for (int idx = 0; idx < 6; ++idx)
|
||||
_backSides[idx].release(_runtime);
|
||||
}
|
||||
|
||||
void MinigameTriangle::Node::debugInfo() const {
|
||||
debugC(5, kDebugMinigames, "name:\"%s\" state:\"%s\" number:%d rotation:%d flip:%d isBack:%d highlight:%d animated:%d", obj().getName(), obj()->current_state_name(), _number, _rotation, _flip, _isBack, _highlight, _animated);
|
||||
}
|
||||
|
||||
const Common::String MinigameTriangle::Node::getFaceStateName(int angle, bool selected, bool animated, bool instantaneous) {
|
||||
assert(!selected || !animated); // анимированные выделенными быть не могут
|
||||
|
||||
static const char *angleNames[3] = {"0", "120", "240"};
|
||||
assert(angle >= 0 && angle < ARRAYSIZE(angleNames));
|
||||
|
||||
Common::String out;
|
||||
|
||||
out = Common::String::format("%s%s%s", (animated ? "02_" : "01_"), angleNames[angle], (selected || instantaneous ? "_sel" : ""));
|
||||
return out;
|
||||
}
|
||||
|
||||
const char *MinigameTriangle::Node::getBackStateName(bool selected, bool animated, bool instantaneous) {
|
||||
assert(!selected || !animated); // анимированные выделенными быть не могут
|
||||
|
||||
if (animated)
|
||||
return selected || instantaneous ? "02_sel" : "02";
|
||||
else
|
||||
return selected || instantaneous ? "01_sel" : "01";
|
||||
}
|
||||
|
||||
const char *MinigameTriangle::Node::getBorderStateName(bool selected) {
|
||||
return selected ? "01" : "02";
|
||||
}
|
||||
|
||||
void MinigameTriangle::releaseNodeBack(Node &node) {
|
||||
if (node._back) {
|
||||
node._back.setState(Node::getBackStateName(false, false, false));
|
||||
for (int type = 0; type < 6; ++type)
|
||||
_backSides[type].releaseObject(node._back, _runtime);
|
||||
}
|
||||
}
|
||||
|
||||
void MinigameTriangle::updateNode(Node &node, int position, int flip, bool quick) {
|
||||
for (auto &fit : node._face)
|
||||
_runtime->hide(fit);
|
||||
|
||||
node._flip = flip;
|
||||
|
||||
if (node._isBack) {
|
||||
if (!node._back)
|
||||
node._back = _backSides[orientation(position) * 3 + flip].getObject();
|
||||
node._back->set_R(slotCoord(position, flip));
|
||||
node._back->update_screen_R();
|
||||
node._back.setState(Node::getBackStateName(node._highlight, node._animated, quick));
|
||||
} else {
|
||||
releaseNodeBack(node);
|
||||
|
||||
QDObject &face = node._face[flip];
|
||||
face->set_R(slotCoord(position, flip));
|
||||
face->update_screen_R();
|
||||
face.setState(Node::getFaceStateName(node._rotation, node._highlight, node._animated, quick).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void MinigameTriangle::highlight(int idx, bool hl) {
|
||||
if (idx >= 0) {
|
||||
assert(idx < (int)_nodes.size());
|
||||
_nodes[idx]._highlight = hl;
|
||||
updateNode(_nodes[idx], idx);
|
||||
}
|
||||
}
|
||||
|
||||
void MinigameTriangle::beginSwapNodes(int pos1, int pos2) {
|
||||
assert(compatible(pos1, pos2));
|
||||
|
||||
if (pos1 > pos2)
|
||||
SWAP(pos1, pos2);
|
||||
|
||||
_animationState = FIRST_PHASE;
|
||||
_animationTimer = _animationTime;
|
||||
|
||||
_animatedNodes[0] = pos1;
|
||||
_animatedNodes[1] = pos2;
|
||||
|
||||
Node &node1 = _nodes[pos1];
|
||||
Node &node2 = _nodes[pos2];
|
||||
|
||||
node1._animated = true;
|
||||
node2._animated = true;
|
||||
|
||||
releaseNodeBack(node1);
|
||||
releaseNodeBack(node2);
|
||||
|
||||
updateNode(node1, pos1, destination(pos1, pos2));
|
||||
updateNode(node2, pos2, destination(pos1, pos2));
|
||||
|
||||
debugC(5, kDebugMinigames, ">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 1st phase <<<<<<<<<<<<<<<<<<<<<<<<<<<<", pos1, pos2);
|
||||
_nodes[pos1].debugInfo();
|
||||
_nodes[pos2].debugInfo();
|
||||
}
|
||||
|
||||
void MinigameTriangle::endSwapNodes(int pos1, int pos2) {
|
||||
Node &node1 = _nodes[pos1];
|
||||
Node &node2 = _nodes[pos2];
|
||||
|
||||
bool counted = false;
|
||||
if (node1._number == pos1) { // поставили на свое место
|
||||
assert(!node1._isBack);
|
||||
counted = true;
|
||||
_runtime->event(EVENT_PUT_RIGHT, node1.obj()->screen_R());
|
||||
}
|
||||
|
||||
if (node2._number == pos1) { // сняли со своего места
|
||||
assert(node2._isBack);
|
||||
counted = true;
|
||||
_runtime->event(EVENT_GET_RIGHT, node1.obj()->screen_R());
|
||||
}
|
||||
|
||||
if (node2._number == pos2) { // поставили на свое место
|
||||
assert(!node2._isBack);
|
||||
counted = true;
|
||||
_runtime->event(EVENT_PUT_RIGHT, node2.obj()->screen_R());
|
||||
}
|
||||
|
||||
if (node1._number == pos2) { // сняли со своего места
|
||||
assert(node1._isBack);
|
||||
counted = true;
|
||||
_runtime->event(EVENT_GET_RIGHT, node2.obj()->screen_R());
|
||||
}
|
||||
|
||||
if (!counted) { // просто сделали ход
|
||||
mgVect2i pos = node1.obj()->screen_R();
|
||||
pos += node2.obj()->screen_R();
|
||||
pos /= 2;
|
||||
_runtime->event(EVENT_TURN, pos);
|
||||
}
|
||||
|
||||
bool isWin = true;
|
||||
int position = 0;
|
||||
|
||||
for (auto &it : _nodes) {
|
||||
if (it._number != position++) {
|
||||
isWin = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWin) {
|
||||
setState(GAME_WIN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool MinigameTriangle::animate(float dt) {
|
||||
|
||||
if (_animationState == NO_ANIMATION)
|
||||
return false;
|
||||
|
||||
_animationTimer -= dt;
|
||||
if (_animationTimer > 0)
|
||||
return true;
|
||||
|
||||
Node &node1 = _nodes[_animatedNodes[0]];
|
||||
Node &node2 = _nodes[_animatedNodes[1]];
|
||||
|
||||
switch (_animationState) {
|
||||
case FIRST_PHASE: {
|
||||
node1._rotation = getRotate(_animatedNodes[0], _animatedNodes[1]);
|
||||
node2._rotation = getRotate(_animatedNodes[1], _animatedNodes[0]);
|
||||
|
||||
node1._isBack = !node1._isBack;
|
||||
node2._isBack = !node2._isBack;
|
||||
|
||||
releaseNodeBack(node1);
|
||||
releaseNodeBack(node2);
|
||||
|
||||
for (auto &it : node1._face)
|
||||
it.setState(Node::getFaceStateName(0, false, false, false).c_str());
|
||||
|
||||
for (auto &it : node2._face)
|
||||
it.setState(Node::getFaceStateName(0, false, false, false).c_str());
|
||||
|
||||
updateNode(node1, _animatedNodes[1], destination(_animatedNodes[0], _animatedNodes[1]), true);
|
||||
updateNode(node2, _animatedNodes[0], destination(_animatedNodes[1], _animatedNodes[0]), true);
|
||||
|
||||
_animationTimer = 0.f;
|
||||
_animationState = SECOND_PHASE;
|
||||
|
||||
debugC(5, kDebugMinigames, ">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 2nd phase 1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<", _animatedNodes[0], _animatedNodes[1]);
|
||||
node1.debugInfo();
|
||||
node2.debugInfo();
|
||||
|
||||
return true;
|
||||
}
|
||||
case SECOND_PHASE:
|
||||
node1._animated = false;
|
||||
node2._animated = false;
|
||||
|
||||
updateNode(node1, _animatedNodes[1], destination(_animatedNodes[0], _animatedNodes[1]));
|
||||
updateNode(node2, _animatedNodes[0], destination(_animatedNodes[1], _animatedNodes[0]));
|
||||
|
||||
SWAP(node1, node2);
|
||||
|
||||
_animationTimer = _animationTime;
|
||||
_animationState = FIRD_PHASE;
|
||||
|
||||
debugC(5, kDebugMinigames, ">>>>>>>>>>>>>>>>>>>>>>>>>>> change %d <> %d, 2nd phase 2 <<<<<<<<<<<<<<<<<<<<<<<<<<<<", _animatedNodes[0], _animatedNodes[1]);
|
||||
node2.debugInfo();
|
||||
node1.debugInfo();
|
||||
|
||||
return true;
|
||||
|
||||
case FIRD_PHASE:
|
||||
_animationTimer = 0.f;
|
||||
_animationState = NO_ANIMATION;
|
||||
|
||||
releaseNodeBack(node1);
|
||||
releaseNodeBack(node2);
|
||||
|
||||
updateNode(node1, _animatedNodes[0]);
|
||||
updateNode(node2, _animatedNodes[1]);
|
||||
|
||||
endSwapNodes(_animatedNodes[0], _animatedNodes[1]);
|
||||
debugC(5, kDebugMinigames, "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change %d <> %d, finished ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^", _animatedNodes[0], _animatedNodes[1]);
|
||||
|
||||
_animatedNodes[0] = -1;
|
||||
_animatedNodes[1] = -1;
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MinigameTriangle::swapNodes(int pos1, int pos2, bool silentQuick) {
|
||||
if (silentQuick) {
|
||||
Node &node1 = _nodes[pos1];
|
||||
Node &node2 = _nodes[pos2];
|
||||
|
||||
node1._rotation = getRotate(pos1, pos2);
|
||||
node2._rotation = getRotate(pos2, pos1);
|
||||
|
||||
node1._isBack = !node1._isBack;
|
||||
node2._isBack = !node2._isBack;
|
||||
|
||||
releaseNodeBack(node1);
|
||||
releaseNodeBack(node2);
|
||||
|
||||
SWAP(node1, node2);
|
||||
|
||||
updateNode(node1, pos1, 0, true);
|
||||
updateNode(node2, pos2, 0, true);
|
||||
} else
|
||||
beginSwapNodes(pos1, pos2);
|
||||
}
|
||||
|
||||
void MinigameTriangle::quant(float dt) {
|
||||
if (_selected >= 0)
|
||||
_runtime->setGameHelpVariant(0);
|
||||
else
|
||||
_runtime->setGameHelpVariant(1);
|
||||
|
||||
if (animate(dt))
|
||||
return;
|
||||
|
||||
int mousePos = -1;
|
||||
for (int idx = 0; idx < _fieldSize; ++idx)
|
||||
if (_nodes[idx].hit(_runtime->mousePosition())) {
|
||||
mousePos = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
int startAnimation = -1;
|
||||
int lastSelected = _selected;
|
||||
|
||||
if (_runtime->mouseLeftPressed()) {
|
||||
if (mousePos < 0) // кликнули мимо - снимаем выделение
|
||||
_selected = -1;
|
||||
else if (_selected < 0) // ничего выделено небыло, просто выделяем
|
||||
_selected = mousePos;
|
||||
else if (_selected == mousePos) // кликнули на выделенном - снимаем выделение
|
||||
_selected = -1;
|
||||
else if (compatible(_selected, mousePos)) { // поменять фишки местами
|
||||
startAnimation = _selected;
|
||||
_selected = -1;
|
||||
} else {
|
||||
_selected = -1;
|
||||
if (_quickReselect)
|
||||
_selected = mousePos;
|
||||
}
|
||||
}
|
||||
|
||||
if (_selected != lastSelected) {
|
||||
for (int idx = 0; idx < _fieldSize; ++idx) {
|
||||
Node &node = _nodes[idx];
|
||||
if (idx == _selected || compatible(_selected, idx)) { // с этой фишкой можно поменяться
|
||||
if (!node._border)
|
||||
node._border = _selectBorders[orientation(idx)].getObject();
|
||||
node._border.setState(Node::getBorderStateName(idx == _selected));
|
||||
node._border->set_R(slotCoord(idx));
|
||||
node._border->update_screen_R();
|
||||
_runtime->setDepth(node._border, _selectDepth);
|
||||
} else if (node._border) {
|
||||
_selectBorders[0].releaseObject(node._border, _runtime);
|
||||
_selectBorders[1].releaseObject(node._border, _runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_hovered != mousePos || _selected != lastSelected) {
|
||||
highlight(_hovered, false);
|
||||
highlight(_selected >= 0 ? _selected : lastSelected, false);
|
||||
|
||||
_hovered = mousePos;
|
||||
|
||||
if (_hovered >= 0 && startAnimation < 0) {
|
||||
if (_selected >= 0) {
|
||||
if (compatible(_selected, _hovered)) {
|
||||
highlight(_hovered, true);
|
||||
highlight(_selected, true);
|
||||
}
|
||||
} else
|
||||
highlight(_hovered, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (startAnimation >= 0) {
|
||||
_hovered = -1;
|
||||
swapNodes(startAnimation, mousePos, false);
|
||||
}
|
||||
|
||||
if (_runtime->mouseRightPressed() && mousePos >= 0) {
|
||||
debugC(2, kDebugMinigames, "----- DUBUG INFO FOR %d POSITION --------------------", mousePos);
|
||||
debugC(2, kDebugMinigames, "row = %d, begin = %d, orientation = %d", rowByNum(mousePos), rowBegin(rowByNum(mousePos)), orientation(mousePos));
|
||||
_nodes[mousePos].debugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
int MinigameTriangle::rowBegin(int row) const {
|
||||
if (row == _fieldLines)
|
||||
return _fieldSize;
|
||||
|
||||
switch (_gameType) {
|
||||
case TRIANGLE:
|
||||
return sqr(row);
|
||||
case RECTANGLE:
|
||||
return row * _fieldWidth;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//case HEXAGON:
|
||||
assert(row >= 0 && row < _fieldLines);
|
||||
if (row >= _fieldLines / 2) {
|
||||
row -= _fieldLines / 2;
|
||||
return _fieldSize / 2 + (2 * _fieldLines - row) * row;
|
||||
}
|
||||
return (_fieldLines + row) * row;
|
||||
|
||||
}
|
||||
|
||||
int MinigameTriangle::rowByNum(int num) const {
|
||||
if (num >= _fieldSize)
|
||||
return _fieldLines;
|
||||
|
||||
switch (_gameType) {
|
||||
case TRIANGLE:
|
||||
return floor(sqrt((float)num));
|
||||
case RECTANGLE:
|
||||
return num / _fieldWidth;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//case HEXAGON:
|
||||
int row = num < _fieldSize / 2 ? 0 : _fieldLines / 2;
|
||||
while (row < _fieldLines && num >= rowBegin(row))
|
||||
++row;
|
||||
return row > 0 ? row - 1 : 0;
|
||||
}
|
||||
|
||||
int MinigameTriangle::orientation(int num) const {
|
||||
switch (_gameType) {
|
||||
case TRIANGLE:
|
||||
return (rowByNum(num) + num) % 2;
|
||||
case RECTANGLE:
|
||||
return num % 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//case HEXAGON:
|
||||
return (num + rowByNum(num) + (num >= _fieldSize / 2 ? 1 : 0)) % 2;
|
||||
}
|
||||
|
||||
bool MinigameTriangle::compatible(int num1, int num2) const {
|
||||
if (num1 > num2)
|
||||
SWAP(num1, num2);
|
||||
|
||||
if (num1 < 0)
|
||||
return false;
|
||||
|
||||
int row1 = rowByNum(num1);
|
||||
int row2 = rowByNum(num2);
|
||||
|
||||
if (row2 >= _fieldLines)
|
||||
return false;
|
||||
|
||||
if (row1 == row2) // в одном слое
|
||||
return num2 - num1 == 1; // должны быть рядом
|
||||
else if (row2 - row1 != 1) // или на соседних слоях
|
||||
return false;
|
||||
else if (orientation(num1) != 0) // широкими сторонами друг к другу
|
||||
return false;
|
||||
|
||||
int center1 = (rowBegin(row1) + rowBegin(row1 + 1) - 1) / 2;
|
||||
int center2 = (rowBegin(row2) + rowBegin(row2 + 1) - 1) / 2;
|
||||
|
||||
return center1 - num1 == center2 - num2; // и точно друг под другом
|
||||
}
|
||||
|
||||
int MinigameTriangle::getRotate(int num1, int num2) const {
|
||||
static int solves[3][2][3] = {
|
||||
{{0, 2, 1}, {0, 2, 1}},
|
||||
{{2, 1, 0}, {1, 0, 2}},
|
||||
{{1, 0, 2}, {2, 1, 0}}
|
||||
};
|
||||
assert(compatible(num1, num2));
|
||||
return solves[rowByNum(num1) != rowByNum(num2) ? 0 : (num2 < num1 ? 1 : 2)]
|
||||
[orientation(num1)][_nodes[num1]._rotation];
|
||||
}
|
||||
|
||||
int MinigameTriangle::destination(int num1, int num2) const {
|
||||
if (orientation(num1) == 0)
|
||||
return rowByNum(num1) != rowByNum(num2) ? 0 : (num2 < num1 ? 1 : 2);
|
||||
else
|
||||
return rowByNum(num1) != rowByNum(num2) ? 0 : (num2 < num1 ? 2 : 1);
|
||||
}
|
||||
|
||||
mgVect3f MinigameTriangle::slotCoord(int pos, int angle) const {
|
||||
assert(pos * 3 + angle < (int)_positions.size());
|
||||
return _positions[pos * 3 + angle];
|
||||
}
|
||||
|
||||
} // namespace QDEngine
|
||||
137
engines/qdengine/minigames/adv/m_triangles.h
Normal file
137
engines/qdengine/minigames/adv/m_triangles.h
Normal file
@@ -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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 {
|
||||
|
||||
MinigameInterface *createMinigameTriangle(MinigameManager *runtime);
|
||||
|
||||
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(MinigameManager *runtime);
|
||||
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 Common::String 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 Std::vector<Node> Nodes;
|
||||
|
||||
public:
|
||||
MinigameTriangle(MinigameManager *runtime);
|
||||
~MinigameTriangle();
|
||||
void quant(float dt);
|
||||
|
||||
private:
|
||||
GameType _gameType = TRIANGLE;
|
||||
Coords _positions;
|
||||
int _selectDepth = 0;
|
||||
|
||||
int _fieldLines = 0;
|
||||
int _fieldWidth = 0;
|
||||
int _fieldSize = 0;
|
||||
Nodes _nodes;
|
||||
ObjectContainer _selectBorders[2];
|
||||
ObjectContainer _backSides[6];
|
||||
int _selected = 0;
|
||||
int _hovered = 0;
|
||||
|
||||
bool _quickReselect = false;
|
||||
|
||||
AnimationState _animationState = NO_ANIMATION;
|
||||
int _animatedNodes[2] = { 0 };
|
||||
float _animationTime = 0.0;
|
||||
float _animationTimer = 0.0;
|
||||
|
||||
/// очистить рубашку фишки
|
||||
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;
|
||||
|
||||
MinigameManager *_runtime;
|
||||
};
|
||||
|
||||
} // namespace QDEngine
|
||||
|
||||
#endif // QDENGINE_MINIGAMES_ADV_M_TRIANGLES_H
|
||||
65
engines/qdengine/minigames/adv/qdMath.h
Normal file
65
engines/qdengine/minigames/adv/qdMath.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* 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
|
||||
|
||||
#include "qdengine/xmath.h"
|
||||
|
||||
namespace QDEngine {
|
||||
|
||||
#define SQRT2 1.41421356f
|
||||
#define SQRT3 1.73205081f
|
||||
|
||||
inline float dist(const mgVect2f& v1, const mgVect2f& v2) {
|
||||
return sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y));
|
||||
}
|
||||
|
||||
inline float abs(const mgVect2f& v) {
|
||||
return sqrt(v.x * v.x + v.y * 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
|
||||
Reference in New Issue
Block a user