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

231 lines
7.7 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/>.
*
*/
#ifndef ALCACHOFA_DEBUG_H
#define ALCACHOFA_DEBUG_H
#include "alcachofa/alcachofa.h"
using namespace Common;
namespace Alcachofa {
class IDebugHandler {
public:
virtual ~IDebugHandler() {}
virtual void update() = 0;
};
class ClosestFloorPointDebugHandler final : public IDebugHandler {
int32 _polygonI;
public:
ClosestFloorPointDebugHandler(int32 polygonI) : _polygonI(polygonI) {}
void update() override {
auto mousePos2D = g_engine->input().debugInput().mousePos2D();
auto mousePos3D = g_engine->input().debugInput().mousePos3D();
auto floor = g_engine->player().currentRoom()->activeFloor();
auto renderer = dynamic_cast<IDebugRenderer *>(&g_engine->renderer());
if (floor == nullptr || renderer == nullptr)
return;
Point target3D;
if (_polygonI < 0 || (uint)_polygonI >= floor->polygonCount())
target3D = floor->closestPointTo(mousePos3D);
else
target3D = floor->at((uint)_polygonI).closestPointTo(mousePos3D);
renderer->debugPolyline(mousePos2D, g_engine->camera().transform3Dto2D(target3D));
}
};
class FloorIntersectionsDebugHandler final : public IDebugHandler {
int32 _polygonI;
Point _fromPos3D;
public:
FloorIntersectionsDebugHandler(int32 polygonI) : _polygonI(polygonI) {}
void update() override {
auto floor = g_engine->player().currentRoom()->activeFloor();
auto renderer = dynamic_cast<IDebugRenderer *>(&g_engine->renderer());
if (floor == nullptr || renderer == nullptr) {
g_engine->console().attach("Either the room has no floor or the renderer is not a debug renderer");
g_engine->setDebugMode(DebugMode::None, 0);
return;
}
if (g_engine->input().debugInput().wasMouseLeftPressed())
_fromPos3D = g_engine->input().debugInput().mousePos3D();
renderer->debugPolyline(
g_engine->camera().transform3Dto2D(_fromPos3D),
g_engine->input().debugInput().mousePos2D(),
kDebugRed);
if (_polygonI >= 0 && (uint)_polygonI < floor->polygonCount())
drawIntersectionsFor(floor->at((uint)_polygonI), renderer);
else {
for (uint i = 0; i < floor->polygonCount(); i++)
drawIntersectionsFor(floor->at(i), renderer);
}
}
private:
static constexpr float kMarkerLength = 16;
void drawIntersectionsFor(const Polygon &polygon, IDebugRenderer *renderer) {
auto &camera = g_engine->camera();
auto mousePos3D = g_engine->input().debugInput().mousePos3D();
for (uint i = 0; i < polygon._points.size(); i++) {
if (!polygon.intersectsEdge(i, _fromPos3D, mousePos3D))
continue;
auto a = camera.transform3Dto2D(polygon._points[i]);
auto b = camera.transform3Dto2D(polygon._points[(i + 1) % polygon._points.size()]);
auto mid = (a + b) / 2;
auto length = sqrtf(a.sqrDist(b));
auto normal = a - b;
normal = { normal.y, (int16)-normal.x };
auto inner = mid + normal * (kMarkerLength / length);
renderer->debugPolyline(a, b, kDebugGreen);
renderer->debugPolyline(mid, inner, kDebugGreen);
}
}
};
class TeleportCharacterDebugHandler final : public IDebugHandler {
MainCharacterKind _kind;
public:
TeleportCharacterDebugHandler(int32 kindI) : _kind((MainCharacterKind)kindI) {}
void update() override {
g_engine->drawQueue().clear();
g_engine->player().drawCursor(true);
g_engine->drawQueue().draw();
auto &input = g_engine->input().debugInput();
if (input.wasMouseRightPressed()) {
g_engine->setDebugMode(DebugMode::None, 0);
return;
}
if (!input.wasMouseLeftPressed())
return;
auto floor = g_engine->player().currentRoom()->activeFloor();
if (floor == nullptr || !floor->contains(input.mousePos3D()))
return;
if (_kind == MainCharacterKind::Filemon)
teleport(g_engine->world().filemon(), input.mousePos3D());
else if (_kind == MainCharacterKind::Mortadelo)
teleport(g_engine->world().mortadelo(), input.mousePos3D());
else {
teleport(g_engine->world().filemon(), input.mousePos3D());
teleport(g_engine->world().mortadelo(), input.mousePos3D());
}
g_engine->setDebugMode(DebugMode::None, 0);
}
private:
void teleport(MainCharacter &character, Point position) {
auto currentRoom = g_engine->player().currentRoom();
if (character.room() != currentRoom) {
character.resetTalking();
character.room() = currentRoom;
}
character.setPosition(position);
}
};
class FloorColorDebugHandler final : public IDebugHandler {
const FloorColorShape &_shape;
const bool _useColor;
Color _curColor = kDebugGreen;
bool _isOnFloor = false;
static constexpr size_t kBufferSize = 64;
char _buffer[kBufferSize] = { 0 };
FloorColorDebugHandler(const FloorColorShape &shape, bool useColor)
: _shape(shape)
, _useColor(useColor) {}
public:
static FloorColorDebugHandler *create(int32 objectI, bool useColor) {
const Room *room = g_engine->player().currentRoom();
uint floorCount = 0;
for (auto itObject = room->beginObjects(); itObject != room->endObjects(); ++itObject) {
FloorColor *floor = dynamic_cast<FloorColor *>(*itObject);
if (floor == nullptr)
continue;
if (objectI <= 0)
// dynamic_cast is not possible due to Shape not having virtual methods
return new FloorColorDebugHandler(*(FloorColorShape *)(floor->shape()), useColor);
floorCount++;
objectI--;
}
g_engine->console().debugPrintf("Invalid floor color index, there are %u floors in this room\n", floorCount);
return nullptr;
}
void update() override {
auto &input = g_engine->input().debugInput();
if (input.wasMouseRightPressed()) {
g_engine->setDebugMode(DebugMode::None, 0);
return;
}
if (input.isMouseLeftDown()) {
auto optColor = _shape.colorAt(input.mousePos3D());
_isOnFloor = optColor.first;
if (!_isOnFloor) {
uint8 roomAlpha = (uint)(g_engine->player().currentRoom()->characterAlphaTint() * 255 / 100);
optColor.second = Color { 255, 255, 255, roomAlpha };
}
_curColor = _useColor
? Color { optColor.second.r, optColor.second.g, optColor.second.b, 255 }
: Color { optColor.second.a, optColor.second.a, optColor.second.a, 255 };
g_engine->world().mortadelo().color() =
g_engine->world().filemon().color() =
_useColor ? optColor.second : Color { 255, 255, 255, optColor.second.a };
}
snprintf(_buffer, kBufferSize, "r:%3d g:%3d b:%3d a:%3d",
(int)_curColor.r, (int)_curColor.g, (int)_curColor.b, (int)_curColor.a);
auto *debugRenderer = dynamic_cast<IDebugRenderer *>(&g_engine->renderer());
g_engine->drawQueue().clear();
g_engine->player().drawCursor(true);
g_engine->renderer().setTexture(nullptr);
g_engine->renderer().quad({ 0, 0 }, { 50, 50 }, _isOnFloor ? _curColor : kDebugGreen);
g_engine->drawQueue().add<TextDrawRequest>(
g_engine->globalUI().dialogFont(), _buffer, Point { 70, 20 }, 500, false, kWhite, -kForegroundOrderCount + 1);
if (!_isOnFloor)
g_engine->renderer().quad({ 5, 5 }, { 40, 40 }, _curColor);
if (debugRenderer != nullptr)
debugRenderer->debugShape(_shape, kDebugBlue);
g_engine->drawQueue().draw();
}
};
}
#endif // ALCACHOFA_DEBUG_H