Files
2026-02-02 04:50:13 +01:00

382 lines
9.5 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "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