Rename Demolish to Deconstruct; add build button grid icons
Rename the Demolish* terminology to Deconstruct* across the simulation, events, UI, tests, and docs (DemolishCommand and the DemolishMode* events become their Deconstruct* equivalents), aligning with the deconstruction queue at HEAD. Add per-building SVG icons for the build button grid (REQ-UI-BUILD-GRID): one color-chip icon per placeable building under data/icons/buildings, loaded from disk at runtime and rendered with QtSvg onto each build button. An unaffordable building's button shows a grey-recolored variant via a QIcon Disabled-mode pixmap (REQ-UI-BUILD-DISABLED). Adds the Qt5::Svg dependency and deploys Qt5Svg.dll. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
This commit is contained in:
@@ -117,6 +117,7 @@ target_link_libraries(${TARGET_UI_NAME}
|
||||
Qt5::Network
|
||||
Qt5::Multimedia
|
||||
Qt5::Charts
|
||||
Qt5::Svg
|
||||
)
|
||||
target_compile_definitions(${TARGET_UI_NAME} PRIVATE TOML_FLOAT_CHARCONV=0)
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ struct WorldScroll
|
||||
struct WorldConfig
|
||||
{
|
||||
int heightTiles; // REQ-GW-HEIGHT
|
||||
int refundPercentage; // REQ-BLD-DEMOLISH
|
||||
int refundPercentage; // REQ-BLD-DECONSTRUCT
|
||||
double deconstructionTimeSeconds; // REQ-BLD-DECON-QUEUE
|
||||
int startingBuildingBlocks; // REQ-HQ-STARTING-BLOCKS
|
||||
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
||||
|
||||
@@ -16,11 +16,11 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuilderModeExitedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintModeExitedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/EscapeMenuRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DemolishModeChangedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DeconstructModeChangedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingTypeSelectedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildHotkeyPressedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ExitBuilderModeRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DemolishModeToggleRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DeconstructModeToggleRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintPlacementRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ExitBlueprintModeRequestedEvent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TemporaryBlueprintRequestedEvent.h
|
||||
|
||||
10
src/lib/eventsystem/event/DeconstructModeChangedEvent.h
Normal file
10
src/lib/eventsystem/event/DeconstructModeChangedEvent.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
class DeconstructModeChangedEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit DeconstructModeChangedEvent(bool active) : active(active) {}
|
||||
const bool active;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
class DeconstructModeToggleRequestedEvent : public Event
|
||||
{
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
class DemolishModeChangedEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit DemolishModeChangedEvent(bool active) : active(active) {}
|
||||
const bool active;
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
class DemolishModeToggleRequestedEvent : public Event
|
||||
{
|
||||
};
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
// tile are held and routed to one of the two outputs.
|
||||
void placeSplitter(QPoint tile, Rotation outputA, Rotation outputB);
|
||||
|
||||
// Remove a belt or splitter tile (on demolish). Items are discarded.
|
||||
// Remove a belt or splitter tile (on deconstruct). Items are discarded.
|
||||
void removeTile(QPoint tile);
|
||||
|
||||
// -- Splitter filter configuration (REQ-BLD-SPLITTER) -------------------
|
||||
|
||||
@@ -497,13 +497,13 @@ bool BuildingSystem::isPlacementValid(BuildingType type, QPoint anchor,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Demolish
|
||||
// Deconstruct
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int BuildingSystem::demolish(BuildingId id, Tick currentTick)
|
||||
int BuildingSystem::deconstruct(BuildingId id, Tick currentTick)
|
||||
{
|
||||
// Construction site? Removed instantly with the full refund; never queued
|
||||
// for deconstruction (REQ-BLD-DEMOLISH).
|
||||
// for deconstruction (REQ-BLD-DECONSTRUCT).
|
||||
for (std::deque<ConstructionSite>::iterator it = m_constructionQueue.begin();
|
||||
it != m_constructionQueue.end();
|
||||
++it)
|
||||
@@ -887,9 +887,9 @@ void BuildingSystem::tickDeconstruction(Tick currentTick)
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the building from the world and credit its refund (REQ-BLD-DEMOLISH).
|
||||
// Remove the building from the world and credit its refund (REQ-BLD-DECONSTRUCT).
|
||||
// Belt/tunnel/splitter tiles were already unregistered when the building was
|
||||
// queued (see demolish), so only tile occupancy and the record remain.
|
||||
// queued (see deconstruct), so only tile occupancy and the record remain.
|
||||
for (std::vector<Building>::iterator it = m_buildings.begin();
|
||||
it != m_buildings.end();
|
||||
++it)
|
||||
@@ -1632,7 +1632,7 @@ std::optional<BuildingId> BuildingSystem::findRotateInPlaceTarget(
|
||||
BuildingType type, QPoint anchor, Rotation rot) const
|
||||
{
|
||||
// Tunnel Entries and Tunnel Exits cannot be rotated in place; re-orienting a
|
||||
// tunnel requires demolishing and re-placing it (REQ-BLD-ROTATE-IN-PLACE).
|
||||
// tunnel requires deconstructing and re-placing it (REQ-BLD-ROTATE-IN-PLACE).
|
||||
if (type == BuildingType::TunnelEntry || type == BuildingType::TunnelExit)
|
||||
{
|
||||
return std::nullopt;
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
std::function<bool(const std::string&)> isItemUnlocked,
|
||||
std::mt19937& rng);
|
||||
|
||||
// -- Placement / demolish ------------------------------------------------
|
||||
// -- Placement / deconstruct ------------------------------------------------
|
||||
// Returns the new entity id, or nullopt if the placement falls outside the
|
||||
// world bounds (vertical extent and asteroid left edge). Belt and Splitter
|
||||
// register with BeltSystem directly; other types enter the construction
|
||||
@@ -77,13 +77,13 @@ public:
|
||||
// Defaults to world.regions.asteroid_width_tiles at construction.
|
||||
void setAsteroidWidth_tiles(int widthTiles) { m_asteroidWidth_tiles = widthTiles; }
|
||||
|
||||
// Mark a building or construction site for demolition (REQ-BLD-DEMOLISH).
|
||||
// Mark a building or construction site for demolition (REQ-BLD-DECONSTRUCT).
|
||||
// A construction site is removed instantly and the full cost is returned.
|
||||
// A fully-built building is instead appended to the deconstruction queue
|
||||
// (REQ-BLD-DECON-QUEUE) and stops operating at once; its (partial) refund is
|
||||
// credited later, on completion in tickDeconstruction, so this returns 0 for
|
||||
// it. Returns 0 for unknown ids and for a building already queued.
|
||||
int demolish(BuildingId id, Tick currentTick);
|
||||
int deconstruct(BuildingId id, Tick currentTick);
|
||||
|
||||
// Take a building back out of the deconstruction queue before it is removed
|
||||
// (REQ-BLD-DECON-QUEUE). Clears its queued flag and resumes operation
|
||||
|
||||
@@ -27,7 +27,7 @@ class GameConfig;
|
||||
enum class CommandKind
|
||||
{
|
||||
PlaceBuilding,
|
||||
Demolish,
|
||||
Deconstruct,
|
||||
CancelDeconstruction,
|
||||
RotateInPlace,
|
||||
SetRecipe,
|
||||
@@ -74,15 +74,15 @@ struct PlaceBuildingCommand : Command
|
||||
};
|
||||
|
||||
// Marks a building for demolition: a construction site is removed instantly, a
|
||||
// built building is queued for deconstruction (REQ-BLD-DEMOLISH, REQ-BLD-DECON-QUEUE).
|
||||
struct DemolishCommand : Command
|
||||
// built building is queued for deconstruction (REQ-BLD-DECONSTRUCT, REQ-BLD-DECON-QUEUE).
|
||||
struct DeconstructCommand : Command
|
||||
{
|
||||
DemolishCommand() : Command(CommandKind::Demolish) {}
|
||||
DeconstructCommand() : Command(CommandKind::Deconstruct) {}
|
||||
std::optional<BuildingId> id;
|
||||
};
|
||||
|
||||
// Takes a building back out of the deconstruction queue (REQ-BLD-DEMOLISH-CLICK,
|
||||
// REQ-BLD-DEMOLISH-BOX). No-op if the id is not currently queued.
|
||||
// Takes a building back out of the deconstruction queue (REQ-BLD-DECONSTRUCT-CLICK,
|
||||
// REQ-BLD-DECONSTRUCT-BOX). No-op if the id is not currently queued.
|
||||
struct CancelDeconstructionCommand : Command
|
||||
{
|
||||
CancelDeconstructionCommand() : Command(CommandKind::CancelDeconstruction) {}
|
||||
|
||||
@@ -128,8 +128,8 @@ std::string serializeCommand(const Command& command)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CommandKind::Demolish:
|
||||
out << "demolish " << static_cast<const DemolishCommand&>(command).id.value();
|
||||
case CommandKind::Deconstruct:
|
||||
out << "deconstruct " << static_cast<const DeconstructCommand&>(command).id.value();
|
||||
break;
|
||||
case CommandKind::CancelDeconstruction:
|
||||
out << "cancel_deconstruct "
|
||||
@@ -243,9 +243,9 @@ std::shared_ptr<Command> parseCommand(const std::string& tokens)
|
||||
}
|
||||
return c;
|
||||
}
|
||||
if (verb == "demolish")
|
||||
if (verb == "deconstruct")
|
||||
{
|
||||
std::shared_ptr<DemolishCommand> c = std::make_shared<DemolishCommand>();
|
||||
std::shared_ptr<DeconstructCommand> c = std::make_shared<DeconstructCommand>();
|
||||
BuildingId id = 0;
|
||||
if (!(in >> id)) { return nullptr; }
|
||||
c->id = id;
|
||||
|
||||
@@ -231,8 +231,8 @@ void Simulation::apply(const Command& command)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CommandKind::Demolish:
|
||||
demolish(*static_cast<const DemolishCommand&>(command).id);
|
||||
case CommandKind::Deconstruct:
|
||||
deconstruct(*static_cast<const DeconstructCommand&>(command).id);
|
||||
break;
|
||||
case CommandKind::CancelDeconstruction:
|
||||
cancelDeconstruction(*static_cast<const CancelDeconstructionCommand&>(command).id);
|
||||
@@ -1198,9 +1198,9 @@ std::optional<BuildingId> Simulation::tryPlaceBuilding(BuildingType type, QPoint
|
||||
return m_buildingSystem->place(type, anchor, rotation, m_currentTick);
|
||||
}
|
||||
|
||||
void Simulation::demolish(BuildingId id)
|
||||
void Simulation::deconstruct(BuildingId id)
|
||||
{
|
||||
m_buildingBlocksStock += m_buildingSystem->demolish(id, m_currentTick);
|
||||
m_buildingBlocksStock += m_buildingSystem->deconstruct(id, m_currentTick);
|
||||
}
|
||||
|
||||
void Simulation::cancelDeconstruction(BuildingId id)
|
||||
|
||||
@@ -142,7 +142,7 @@ private:
|
||||
// Marks the building with the given id for demolition: a construction site is
|
||||
// removed instantly (full refund), a built building is queued for timed
|
||||
// deconstruction (REQ-BLD-DECON-QUEUE), refunded on completion.
|
||||
void demolish(BuildingId id);
|
||||
void deconstruct(BuildingId id);
|
||||
|
||||
// Takes a queued building back out of the deconstruction queue (REQ-BLD-DECON-QUEUE).
|
||||
void cancelDeconstruction(BuildingId id);
|
||||
|
||||
@@ -974,7 +974,7 @@ TEST_CASE("BeltSystem: unpaired entry blocks items at front", "[belt]")
|
||||
REQUIRE(bs.peekItem(Port{QPoint(3, 0), Rotation::East}).has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("BeltSystem: demolish entry discards transit items", "[belt]")
|
||||
TEST_CASE("BeltSystem: deconstruct entry discards transit items", "[belt]")
|
||||
{
|
||||
BeltSystem bs(kFastBeltSpeed);
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ TEST_CASE("BuildingSystem: placed building enters construction queue", "[buildin
|
||||
REQUIRE(bs.findSite(id) != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: demolishing a construction site removes it instantly with full refund",
|
||||
TEST_CASE("BuildingSystem: deconstructing a construction site removes it instantly with full refund",
|
||||
"[building]")
|
||||
{
|
||||
PlacementFixture f;
|
||||
@@ -274,8 +274,8 @@ TEST_CASE("BuildingSystem: demolishing a construction site removes it instantly
|
||||
f.bs.place(BuildingType::Miner, QPoint(0, 0), Rotation::East, 0).value();
|
||||
|
||||
// Still queued for construction (not yet built): instant removal, full cost
|
||||
// refunded immediately, never entering the deconstruction queue (REQ-BLD-DEMOLISH).
|
||||
const int refund = f.bs.demolish(id, 0);
|
||||
// refunded immediately, never entering the deconstruction queue (REQ-BLD-DECONSTRUCT).
|
||||
const int refund = f.bs.deconstruct(id, 0);
|
||||
|
||||
REQUIRE(refund == 15); // Miner cost = 15
|
||||
REQUIRE_FALSE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
@@ -366,7 +366,7 @@ static void runUntilBuilt(PlacementFixture& f, BuildingId id, Tick& tick)
|
||||
REQUIRE(f.bs.findBuilding(id) != nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("BuildingSystem: demolishing a built building queues it; refund credited on completion",
|
||||
TEST_CASE("BuildingSystem: deconstructing a built building queues it; refund credited on completion",
|
||||
"[building][decon]")
|
||||
{
|
||||
PlacementFixture f;
|
||||
@@ -376,9 +376,9 @@ TEST_CASE("BuildingSystem: demolishing a built building queues it; refund credit
|
||||
Tick tick = 0;
|
||||
runUntilBuilt(f, id, tick);
|
||||
|
||||
// Demolishing a built building returns nothing immediately and queues it,
|
||||
// Deconstructing a built building returns nothing immediately and queues it,
|
||||
// stopping it operating while its tiles stay occupied (REQ-BLD-DECON-QUEUE).
|
||||
const int refund = f.bs.demolish(id, tick);
|
||||
const int refund = f.bs.deconstruct(id, tick);
|
||||
REQUIRE(refund == 0);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(id));
|
||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
@@ -403,8 +403,8 @@ TEST_CASE("BuildingSystem: deconstruction queue removes one building at a time",
|
||||
runUntilBuilt(f, b, tick);
|
||||
|
||||
// Queue both in one tick; 'a' is at the front of the deconstruction queue.
|
||||
f.bs.demolish(a, tick);
|
||||
f.bs.demolish(b, tick);
|
||||
f.bs.deconstruct(a, tick);
|
||||
f.bs.deconstruct(b, tick);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(a));
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(b));
|
||||
|
||||
@@ -432,7 +432,7 @@ TEST_CASE("BuildingSystem: cancelling deconstruction resumes the building with n
|
||||
Tick tick = 0;
|
||||
runUntilBuilt(f, id, tick);
|
||||
|
||||
f.bs.demolish(id, tick);
|
||||
f.bs.deconstruct(id, tick);
|
||||
REQUIRE(f.bs.isQueuedForDeconstruction(id));
|
||||
|
||||
// Un-queue before it drains: it operates again, no refund, tiles still occupied.
|
||||
@@ -460,7 +460,7 @@ TEST_CASE("BuildingSystem: queued belt stops transporting; cancel restores it",
|
||||
|
||||
// Queuing a belt unregisters its tile from the belt subsystem, so it no longer
|
||||
// accepts or transports items, though the tile stays occupied (REQ-BLD-DECON-QUEUE).
|
||||
f.bs.demolish(id, tick);
|
||||
f.bs.deconstruct(id, tick);
|
||||
REQUIRE_FALSE(f.belts.tryPutItem(QPoint(0, 0), makeItem("iron_ore"), Rotation::East));
|
||||
REQUIRE(f.bs.isTileOccupied(QPoint(0, 0)));
|
||||
|
||||
@@ -482,7 +482,7 @@ TEST_CASE("BuildingSystem: splitter filters survive a queue/un-queue round-trip"
|
||||
|
||||
// Queue: the belt subsystem tile (and its filters) are unregistered, but the
|
||||
// filters are captured so an un-queue can restore them.
|
||||
f.bs.demolish(id, tick);
|
||||
f.bs.deconstruct(id, tick);
|
||||
REQUIRE_FALSE(f.belts.getSplitterInfo(QPoint(0, 0)).has_value());
|
||||
|
||||
f.bs.cancelDeconstruction(id);
|
||||
|
||||
@@ -59,7 +59,7 @@ TEST_CASE("apply(PlaceBuildingCommand) with recipe matches place-then-setRecipe"
|
||||
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
||||
}
|
||||
|
||||
TEST_CASE("apply(DemolishCommand) matches direct demolish", "[command]")
|
||||
TEST_CASE("apply(DeconstructCommand) matches direct deconstruct", "[command]")
|
||||
{
|
||||
Simulation viaCommand(loadConfig(), 99);
|
||||
Simulation viaDirect(loadConfig(), 99);
|
||||
@@ -70,11 +70,11 @@ TEST_CASE("apply(DemolishCommand) matches direct demolish", "[command]")
|
||||
SimulationTestAccess::place(viaDirect, BuildingType::Miner, QPoint(-3, 0), Rotation::East).value();
|
||||
REQUIRE(idA == idB);
|
||||
|
||||
DemolishCommand command;
|
||||
DeconstructCommand command;
|
||||
command.id = idA;
|
||||
viaCommand.apply(command);
|
||||
|
||||
SimulationTestAccess::demolish(viaDirect, idB);
|
||||
SimulationTestAccess::deconstruct(viaDirect, idB);
|
||||
|
||||
REQUIRE(viaCommand.computeStateChecksum() == viaDirect.computeStateChecksum());
|
||||
}
|
||||
@@ -96,8 +96,8 @@ TEST_CASE("apply(CancelDeconstructionCommand) matches direct cancelDeconstructio
|
||||
viaCommand.tick();
|
||||
viaDirect.tick();
|
||||
}
|
||||
SimulationTestAccess::demolish(viaCommand, idA);
|
||||
SimulationTestAccess::demolish(viaDirect, idB);
|
||||
SimulationTestAccess::deconstruct(viaCommand, idA);
|
||||
SimulationTestAccess::deconstruct(viaDirect, idB);
|
||||
|
||||
CancelDeconstructionCommand command;
|
||||
command.id = idA;
|
||||
|
||||
@@ -22,7 +22,7 @@ GameConfig loadConfig()
|
||||
constexpr int kScriptTicks = 2000;
|
||||
|
||||
// Runs a fixed scripted session and returns the full-state checksum after every
|
||||
// tick. The script places a small factory, demolishes part of it mid-run, and
|
||||
// tick. The script places a small factory, deconstructs part of it mid-run, and
|
||||
// otherwise lets waves/combat run so the RNG stream and ECS state are exercised.
|
||||
std::vector<std::uint64_t> runScriptedSession(unsigned int seed)
|
||||
{
|
||||
@@ -40,7 +40,7 @@ std::vector<std::uint64_t> runScriptedSession(unsigned int seed)
|
||||
{
|
||||
if (t == 500)
|
||||
{
|
||||
// Demolish the second belt mid-run to exercise the removal paths.
|
||||
// Deconstruct the second belt mid-run to exercise the removal paths.
|
||||
SimulationTestAccess::place(sim, BuildingType::Smelter, QPoint(-3, 3), Rotation::East);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,12 +77,12 @@ TEST_CASE("parseCommand round-trips every command verb", "[replay]")
|
||||
ClearBeltTilesCommand clear;
|
||||
clear.tiles = { QPoint(0, 0), QPoint(-1, 4) };
|
||||
|
||||
DemolishCommand demolish;
|
||||
demolish.id = 5;
|
||||
DeconstructCommand deconstruct;
|
||||
deconstruct.id = 5;
|
||||
|
||||
for (const Command* command : { static_cast<const Command*>(&filters),
|
||||
static_cast<const Command*>(&clear),
|
||||
static_cast<const Command*>(&demolish) })
|
||||
static_cast<const Command*>(&deconstruct) })
|
||||
{
|
||||
const std::string text = serializeCommand(*command);
|
||||
const std::shared_ptr<Command> parsed = parseCommand(text);
|
||||
|
||||
@@ -83,11 +83,11 @@ TEST_CASE("serializeCommand: splitter filters are length-prefixed", "[replay]")
|
||||
== "splitterfilters 4 7 2 iron_ore copper_ore 1 coal");
|
||||
}
|
||||
|
||||
TEST_CASE("serializeCommand: demolish / rotate / schematic / clearbelt", "[replay]")
|
||||
TEST_CASE("serializeCommand: deconstruct / rotate / schematic / clearbelt", "[replay]")
|
||||
{
|
||||
DemolishCommand demolish;
|
||||
demolish.id = 12;
|
||||
REQUIRE(serializeCommand(demolish) == "demolish 12");
|
||||
DeconstructCommand deconstruct;
|
||||
deconstruct.id = 12;
|
||||
REQUIRE(serializeCommand(deconstruct) == "deconstruct 12");
|
||||
|
||||
RotateInPlaceCommand rotate;
|
||||
rotate.id = 7;
|
||||
|
||||
@@ -33,7 +33,7 @@ struct SimulationTestAccess
|
||||
return sim.tryPlaceBuilding(type, anchor, rotation);
|
||||
}
|
||||
|
||||
static void demolish(Simulation& sim, BuildingId id) { sim.demolish(id); }
|
||||
static void deconstruct(Simulation& sim, BuildingId id) { sim.deconstruct(id); }
|
||||
|
||||
static void cancelDeconstruction(Simulation& sim, BuildingId id)
|
||||
{
|
||||
|
||||
@@ -2,23 +2,81 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <QGridLayout>
|
||||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSignalMapper>
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
#include <QSvgRenderer>
|
||||
|
||||
#include "BuildingType.h"
|
||||
#include "BuildingTypeSelectedEvent.h"
|
||||
#include "DemolishModeToggleRequestedEvent.h"
|
||||
#include "DeconstructModeToggleRequestedEvent.h"
|
||||
#include "DisplayName.h"
|
||||
#include "EventManager.h"
|
||||
#include "ExitBuilderModeRequestedEvent.h"
|
||||
#include "Simulation.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Pixel size the SVG chips are rasterized at; downscaled to the button icon size.
|
||||
const int kIconRenderSize = 64;
|
||||
const QSize kIconSize(28, 28);
|
||||
|
||||
BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWidget* parent)
|
||||
QPixmap renderChip(const QByteArray& svg)
|
||||
{
|
||||
QSvgRenderer renderer(svg);
|
||||
QPixmap pixmap(kIconRenderSize, kIconRenderSize);
|
||||
pixmap.fill(Qt::transparent);
|
||||
QPainter painter(&pixmap);
|
||||
renderer.render(&painter);
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
// Builds a build-button icon from a "<id>.svg" chip file. The returned QIcon also
|
||||
// carries a Disabled-mode pixmap whose chip background is recolored grey, so an
|
||||
// unaffordable (disabled) button shows the grey variant automatically
|
||||
// (REQ-UI-BUILD-DISABLED) without any extra work in updateAffordability().
|
||||
QIcon loadBuildingIcon(const QString& path)
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return QIcon();
|
||||
}
|
||||
const QByteArray svg = file.readAll();
|
||||
|
||||
QIcon icon;
|
||||
icon.addPixmap(renderChip(svg), QIcon::Normal);
|
||||
|
||||
// Recolor only the chip background: the first "#rrggbb" fill in the file is the
|
||||
// rounded background rect; the white glyph uses fill="none" and is left alone.
|
||||
QString greyed = QString::fromUtf8(svg);
|
||||
static const QRegularExpression fillPattern(QStringLiteral("fill=\"#[0-9a-fA-F]{6}\""));
|
||||
const QRegularExpressionMatch match = fillPattern.match(greyed);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
greyed.replace(match.capturedStart(), match.capturedLength(),
|
||||
QStringLiteral("fill=\"#5f636e\""));
|
||||
}
|
||||
icon.addPixmap(renderChip(greyed.toUtf8()), QIcon::Disabled);
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config,
|
||||
const std::string& iconDir, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_sim(sim)
|
||||
, m_config(config)
|
||||
, m_iconDir(iconDir)
|
||||
{
|
||||
QGridLayout* layout = new QGridLayout(this);
|
||||
layout->setSpacing(4);
|
||||
@@ -53,6 +111,12 @@ BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWid
|
||||
QPushButton* btn = new QPushButton(label, this);
|
||||
btn->setCheckable(true);
|
||||
btn->setFixedHeight(48);
|
||||
// Icon file name matches the building id (REQ-UI-BUILD-GRID); Tunnel Entry's
|
||||
// "tunnel_entry.svg" serves the shared Tunnel button.
|
||||
const QString iconPath = QString::fromStdString(m_iconDir) + "/"
|
||||
+ QString::fromStdString(def.id) + ".svg";
|
||||
btn->setIcon(loadBuildingIcon(iconPath));
|
||||
btn->setIconSize(kIconSize);
|
||||
if (def.tooltip)
|
||||
{
|
||||
btn->setToolTip(QString::fromStdString(*def.tooltip));
|
||||
@@ -73,13 +137,16 @@ BuildButtonGrid::BuildButtonGrid(Simulation* sim, const GameConfig* config, QWid
|
||||
}
|
||||
connect(mapper, qOverload<int>(&QSignalMapper::mapped), this, &BuildButtonGrid::onBuildButton);
|
||||
|
||||
m_demolishButton = new QPushButton(tr("Demolish"), this);
|
||||
m_demolishButton->setCheckable(true);
|
||||
m_demolishButton->setFixedHeight(48);
|
||||
layout->addWidget(m_demolishButton, row, col);
|
||||
connect(m_demolishButton, &QPushButton::clicked, this, [this]() {
|
||||
m_deconstructButton = new QPushButton(tr("Deconstruct"), this);
|
||||
m_deconstructButton->setCheckable(true);
|
||||
m_deconstructButton->setFixedHeight(48);
|
||||
m_deconstructButton->setIcon(loadBuildingIcon(
|
||||
QString::fromStdString(m_iconDir) + "/deconstruct.svg"));
|
||||
m_deconstructButton->setIconSize(kIconSize);
|
||||
layout->addWidget(m_deconstructButton, row, col);
|
||||
connect(m_deconstructButton, &QPushButton::clicked, this, [this]() {
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<DemolishModeToggleRequestedEvent>());
|
||||
std::make_shared<DeconstructModeToggleRequestedEvent>());
|
||||
});
|
||||
|
||||
updateVisibility();
|
||||
@@ -183,9 +250,9 @@ void BuildButtonGrid::handleEvent(std::shared_ptr<const UnlockedBuildingsChanged
|
||||
updateAffordability();
|
||||
}
|
||||
|
||||
void BuildButtonGrid::handleEvent(std::shared_ptr<const DemolishModeChangedEvent> event)
|
||||
void BuildButtonGrid::handleEvent(std::shared_ptr<const DeconstructModeChangedEvent> event)
|
||||
{
|
||||
m_demolishButton->setChecked(event->active);
|
||||
m_deconstructButton->setChecked(event->active);
|
||||
}
|
||||
|
||||
void BuildButtonGrid::handleEvent(std::shared_ptr<const BuildHotkeyPressedEvent> event)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
@@ -10,7 +11,7 @@
|
||||
#include "BuildHotkeyPressedEvent.h"
|
||||
#include "BuildingBlocksChangedEvent.h"
|
||||
#include "BuildingType.h"
|
||||
#include "DemolishModeChangedEvent.h"
|
||||
#include "DeconstructModeChangedEvent.h"
|
||||
#include "EventHandler.h"
|
||||
#include "GameConfig.h"
|
||||
#include "UnlockedBuildingsChangedEvent.h"
|
||||
@@ -20,7 +21,7 @@ class Simulation;
|
||||
|
||||
class BuildButtonGrid : public QWidget,
|
||||
public CombinedEventHandler<BuilderModeExitedEvent,
|
||||
DemolishModeChangedEvent,
|
||||
DeconstructModeChangedEvent,
|
||||
BuildHotkeyPressedEvent,
|
||||
BuildingBlocksChangedEvent,
|
||||
UnlockedBuildingsChangedEvent>
|
||||
@@ -28,7 +29,10 @@ class BuildButtonGrid : public QWidget,
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BuildButtonGrid(Simulation* sim, const GameConfig* config, QWidget* parent = nullptr);
|
||||
// iconDir is the directory holding the per-building "<id>.svg" chip icons
|
||||
// (REQ-UI-BUILD-GRID); read from disk at runtime, like the config files.
|
||||
BuildButtonGrid(Simulation* sim, const GameConfig* config,
|
||||
const std::string& iconDir, QWidget* parent = nullptr);
|
||||
~BuildButtonGrid() override;
|
||||
|
||||
void clearActiveButton();
|
||||
@@ -44,7 +48,7 @@ private:
|
||||
void updateVisibility();
|
||||
|
||||
void handleEvent(std::shared_ptr<const BuilderModeExitedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DemolishModeChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DeconstructModeChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const BuildHotkeyPressedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const UnlockedBuildingsChangedEvent> event) override;
|
||||
@@ -55,9 +59,10 @@ private slots:
|
||||
private:
|
||||
Simulation* m_sim;
|
||||
const GameConfig* m_config;
|
||||
std::string m_iconDir;
|
||||
std::vector<BuildingType> m_types;
|
||||
std::vector<QPushButton*> m_buttons;
|
||||
std::map<BuildingType, int> m_costs;
|
||||
std::optional<std::size_t> m_activeIndex;
|
||||
QPushButton* m_demolishButton;
|
||||
QPushButton* m_deconstructButton;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "ReplayPlayer.h"
|
||||
#include "ReplayReader.h"
|
||||
#include "ReplayRecorder.h"
|
||||
#include "DemolishModeChangedEvent.h"
|
||||
#include "DeconstructModeChangedEvent.h"
|
||||
#include "EntityHitTest.h"
|
||||
#include "EntitySelectionChangedEvent.h"
|
||||
#include "EventManager.h"
|
||||
@@ -157,7 +157,7 @@ GameWorldView::GameWorldView(Simulation* sim, const GameConfig* config,
|
||||
, m_ghostRotation(Rotation::East)
|
||||
, m_ghostValid(false)
|
||||
, m_dragging(false)
|
||||
, m_demolishMode(false)
|
||||
, m_deconstructMode(false)
|
||||
, m_debugDraw(false)
|
||||
, m_rng(std::random_device{}())
|
||||
, m_boxSelecting(false)
|
||||
@@ -463,7 +463,7 @@ void GameWorldView::paintGL()
|
||||
drawOverlays(painter);
|
||||
drawScreenSpace(painter);
|
||||
drawPauseBorder(painter);
|
||||
drawDemolishBorder(painter);
|
||||
drawDeconstructBorder(painter);
|
||||
drawReplayOverlay(painter);
|
||||
}
|
||||
|
||||
@@ -1984,13 +1984,13 @@ void GameWorldView::drawOverlays(QPainter& painter)
|
||||
if (!b.queuedForDeconstruction) { continue; }
|
||||
for (const QPoint& cell : b.bodyCells)
|
||||
{
|
||||
painter.fillRect(tileRect(cell), m_visuals->overlays.demolishTint);
|
||||
painter.fillRect(tileRect(cell), m_visuals->overlays.deconstructTint);
|
||||
}
|
||||
}
|
||||
|
||||
// Demolish tint: while dragging a demolish box, tint every covered
|
||||
// building/site (REQ-BLD-DEMOLISH-BOX); otherwise tint the hovered one.
|
||||
if (m_demolishMode && m_boxSelecting)
|
||||
// Deconstruct tint: while dragging a deconstruct box, tint every covered
|
||||
// building/site (REQ-BLD-DECONSTRUCT-BOX); otherwise tint the hovered one.
|
||||
if (m_deconstructMode && m_boxSelecting)
|
||||
{
|
||||
for (BuildingId id : buildingsInBox(m_boxStartTile, m_boxCurrentTile))
|
||||
{
|
||||
@@ -2004,19 +2004,19 @@ void GameWorldView::drawOverlays(QPainter& painter)
|
||||
{
|
||||
for (const QPoint& cell : *cells)
|
||||
{
|
||||
painter.fillRect(tileRect(cell), m_visuals->overlays.demolishTint);
|
||||
painter.fillRect(tileRect(cell), m_visuals->overlays.deconstructTint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_demolishMode && m_demolishHoverBuildingId.has_value())
|
||||
else if (m_deconstructMode && m_deconstructHoverBuildingId.has_value())
|
||||
{
|
||||
const Building* b = m_sim->getBuildings().findBuilding(*m_demolishHoverBuildingId);
|
||||
const Building* b = m_sim->getBuildings().findBuilding(*m_deconstructHoverBuildingId);
|
||||
if (b)
|
||||
{
|
||||
for (const QPoint& cell : b->bodyCells)
|
||||
{
|
||||
painter.fillRect(tileRect(cell), m_visuals->overlays.demolishTint);
|
||||
painter.fillRect(tileRect(cell), m_visuals->overlays.deconstructTint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2124,11 +2124,11 @@ void GameWorldView::drawPauseBorder(QPainter& painter)
|
||||
drawVignetteBorder(painter, QColor(0, 0, 0, 128)); // 50% black at the edge
|
||||
}
|
||||
|
||||
void GameWorldView::drawDemolishBorder(QPainter& painter)
|
||||
void GameWorldView::drawDeconstructBorder(QPainter& painter)
|
||||
{
|
||||
if (!m_demolishMode) { return; }
|
||||
// Reuse the demolish overlay color (with its configured alpha) as the edge color.
|
||||
drawVignetteBorder(painter, m_visuals->overlays.demolishTint);
|
||||
if (!m_deconstructMode) { return; }
|
||||
// Reuse the deconstruct overlay color (with its configured alpha) as the edge color.
|
||||
drawVignetteBorder(painter, m_visuals->overlays.deconstructTint);
|
||||
}
|
||||
|
||||
void GameWorldView::drawVignetteBorder(QPainter& painter, const QColor& edgeColor)
|
||||
@@ -2325,7 +2325,7 @@ void GameWorldView::keyPressEvent(QKeyEvent* event)
|
||||
case Qt::Key_Q:
|
||||
if (m_builderType.has_value()) { exitBuilderMode(); }
|
||||
else if (m_blueprintMode.has_value()) { exitBlueprintMode(); }
|
||||
else { toggleDemolishMode(); }
|
||||
else { toggleDeconstructMode(); }
|
||||
break;
|
||||
case Qt::Key_T:
|
||||
// Request a temporary blueprint from the current selection (REQ-UI-BLUEPRINT-TEMP).
|
||||
@@ -2387,7 +2387,7 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
}
|
||||
}
|
||||
else if (m_blueprintMode.has_value()) { exitBlueprintMode(); }
|
||||
else if (m_demolishMode) { toggleDemolishMode(); }
|
||||
else if (m_deconstructMode) { toggleDeconstructMode(); }
|
||||
else if (event->modifiers() & Qt::ShiftModifier)
|
||||
{
|
||||
// Shift + right-click copies a building's settings, but only in the
|
||||
@@ -2424,10 +2424,10 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
placeBlueprintAtTile(tile);
|
||||
}
|
||||
else if (m_demolishMode)
|
||||
else if (m_deconstructMode)
|
||||
{
|
||||
// Start a demolish box drag; a plain click resolves as a 1x1 box on
|
||||
// release (REQ-BLD-DEMOLISH-CLICK, REQ-BLD-DEMOLISH-BOX).
|
||||
// Start a deconstruct box drag; a plain click resolves as a 1x1 box on
|
||||
// release (REQ-BLD-DECONSTRUCT-CLICK, REQ-BLD-DECONSTRUCT-BOX).
|
||||
m_boxSelecting = true;
|
||||
m_boxStartTile = tile;
|
||||
m_boxCurrentTile = tile;
|
||||
@@ -2601,9 +2601,9 @@ void GameWorldView::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
m_blueprintGhostTile = tile;
|
||||
}
|
||||
else if (m_demolishMode)
|
||||
else if (m_deconstructMode)
|
||||
{
|
||||
m_demolishHoverBuildingId = buildingAtTile(tile);
|
||||
m_deconstructHoverBuildingId = buildingAtTile(tile);
|
||||
if (m_boxSelecting) { m_boxCurrentTile = tile; }
|
||||
}
|
||||
else if (m_boxSelecting)
|
||||
@@ -2632,12 +2632,12 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
||||
const std::vector<BuildingId> boxIds =
|
||||
buildingsInBox(m_boxStartTile, m_boxCurrentTile);
|
||||
|
||||
if (m_demolishMode)
|
||||
if (m_deconstructMode)
|
||||
{
|
||||
const BuildingSystem& buildings = m_sim->getBuildings();
|
||||
|
||||
// Split covered ids into construction sites (removed instantly) and
|
||||
// operational demolishable buildings (the HQ is protected; player
|
||||
// operational deconstructible buildings (the HQ is protected; player
|
||||
// defence stations are not Buildings and never appear in the box).
|
||||
std::vector<BuildingId> sites;
|
||||
std::vector<BuildingId> operational;
|
||||
@@ -2654,11 +2654,11 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
// Sites: instant demolition with the full refund (REQ-BLD-DEMOLISH).
|
||||
// Sites: instant demolition with the full refund (REQ-BLD-DECONSTRUCT).
|
||||
for (BuildingId id : sites)
|
||||
{
|
||||
std::shared_ptr<DemolishCommand> command =
|
||||
std::make_shared<DemolishCommand>();
|
||||
std::shared_ptr<DeconstructCommand> command =
|
||||
std::make_shared<DeconstructCommand>();
|
||||
command->id = id;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
@@ -2666,7 +2666,7 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
||||
// Operational buildings: if every covered one is already queued,
|
||||
// un-queue them all; otherwise queue each one not yet queued. A plain
|
||||
// click is the one-element case, giving the queue/un-queue toggle
|
||||
// (REQ-BLD-DEMOLISH-BOX, REQ-BLD-DEMOLISH-CLICK).
|
||||
// (REQ-BLD-DECONSTRUCT-BOX, REQ-BLD-DECONSTRUCT-CLICK).
|
||||
bool allQueued = !operational.empty();
|
||||
for (BuildingId id : operational)
|
||||
{
|
||||
@@ -2683,14 +2683,14 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
||||
}
|
||||
else if (!buildings.isQueuedForDeconstruction(id))
|
||||
{
|
||||
std::shared_ptr<DemolishCommand> command =
|
||||
std::make_shared<DemolishCommand>();
|
||||
std::shared_ptr<DeconstructCommand> command =
|
||||
std::make_shared<DeconstructCommand>();
|
||||
command->id = id;
|
||||
enqueueCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
m_demolishHoverBuildingId = std::nullopt;
|
||||
m_deconstructHoverBuildingId = std::nullopt;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2786,21 +2786,21 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
||||
// Methods (formerly slots)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void GameWorldView::toggleDemolishMode()
|
||||
void GameWorldView::toggleDeconstructMode()
|
||||
{
|
||||
if (m_demolishMode)
|
||||
if (m_deconstructMode)
|
||||
{
|
||||
m_demolishMode = false;
|
||||
m_demolishHoverBuildingId = std::nullopt;
|
||||
m_deconstructMode = false;
|
||||
m_deconstructHoverBuildingId = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_builderType.has_value()) { exitBuilderMode(); }
|
||||
if (m_blueprintMode.has_value()) { exitBlueprintMode(); }
|
||||
m_demolishMode = true;
|
||||
m_deconstructMode = true;
|
||||
}
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<DemolishModeChangedEvent>(m_demolishMode));
|
||||
std::make_shared<DeconstructModeChangedEvent>(m_deconstructMode));
|
||||
}
|
||||
|
||||
void GameWorldView::rotateGhost(bool clockwise)
|
||||
@@ -2931,18 +2931,18 @@ void GameWorldView::enterBuilderMode(BuildingType type)
|
||||
m_ghostValid = false;
|
||||
m_tunnelGhostType = BuildingType::TunnelEntry;
|
||||
m_tunnelPartnerTile.reset();
|
||||
m_demolishMode = false;
|
||||
m_deconstructMode = false;
|
||||
m_blueprintMode.reset();
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<DemolishModeChangedEvent>(false));
|
||||
std::make_shared<DeconstructModeChangedEvent>(false));
|
||||
}
|
||||
|
||||
void GameWorldView::enterBlueprintMode(Blueprint blueprint)
|
||||
{
|
||||
if (m_builderType.has_value()) { exitBuilderMode(); }
|
||||
m_demolishMode = false;
|
||||
m_deconstructMode = false;
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<DemolishModeChangedEvent>(false));
|
||||
std::make_shared<DeconstructModeChangedEvent>(false));
|
||||
m_blueprintGhostTile = m_ghostTile;
|
||||
m_blueprintMode = std::move(blueprint);
|
||||
}
|
||||
@@ -2993,10 +2993,10 @@ void GameWorldView::resetForNewGame()
|
||||
m_schematicChoiceShown = false;
|
||||
m_ghostRotation = Rotation::East;
|
||||
m_ghostValid = false;
|
||||
m_demolishMode = false;
|
||||
m_demolishHoverBuildingId = std::nullopt;
|
||||
m_deconstructMode = false;
|
||||
m_deconstructHoverBuildingId = std::nullopt;
|
||||
EventManager::getInstance()->sendEventImmediately(
|
||||
std::make_shared<DemolishModeChangedEvent>(false));
|
||||
std::make_shared<DeconstructModeChangedEvent>(false));
|
||||
m_selectedBuildingIds.clear();
|
||||
clearEntitySelection();
|
||||
clearScrapSelection();
|
||||
@@ -3072,9 +3072,9 @@ void GameWorldView::handleEvent(std::shared_ptr<const ExitBuilderModeRequestedEv
|
||||
exitBuilderMode();
|
||||
}
|
||||
|
||||
void GameWorldView::handleEvent(std::shared_ptr<const DemolishModeToggleRequestedEvent> /*event*/)
|
||||
void GameWorldView::handleEvent(std::shared_ptr<const DeconstructModeToggleRequestedEvent> /*event*/)
|
||||
{
|
||||
toggleDemolishMode();
|
||||
toggleDeconstructMode();
|
||||
}
|
||||
|
||||
void GameWorldView::handleEvent(std::shared_ptr<const BlueprintPlacementRequestedEvent> event)
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include "BuildingType.h"
|
||||
#include "BuildingTypeSelectedEvent.h"
|
||||
#include "BuildingId.h"
|
||||
#include "DemolishModeChangedEvent.h"
|
||||
#include "DemolishModeToggleRequestedEvent.h"
|
||||
#include "DeconstructModeChangedEvent.h"
|
||||
#include "DeconstructModeToggleRequestedEvent.h"
|
||||
#include "EventHandler.h"
|
||||
#include "ExitBlueprintModeRequestedEvent.h"
|
||||
#include "ExitBuilderModeRequestedEvent.h"
|
||||
@@ -66,7 +66,7 @@ class GameWorldView : public QOpenGLWidget,
|
||||
public CombinedEventHandler<BeamFiredEvent,
|
||||
BuildingTypeSelectedEvent,
|
||||
ExitBuilderModeRequestedEvent,
|
||||
DemolishModeToggleRequestedEvent,
|
||||
DeconstructModeToggleRequestedEvent,
|
||||
BlueprintPlacementRequestedEvent,
|
||||
ExitBlueprintModeRequestedEvent,
|
||||
SpeedChangeRequestedEvent,
|
||||
@@ -102,7 +102,7 @@ private:
|
||||
void handleEvent(std::shared_ptr<const BeamFiredEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const BuildingTypeSelectedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const ExitBuilderModeRequestedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DemolishModeToggleRequestedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const DeconstructModeToggleRequestedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const BlueprintPlacementRequestedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const ExitBlueprintModeRequestedEvent> event) override;
|
||||
void handleEvent(std::shared_ptr<const SpeedChangeRequestedEvent> event) override;
|
||||
@@ -139,11 +139,11 @@ private:
|
||||
// to make the paused state hard to miss: a black frame whose alpha fades from 50% at
|
||||
// the viewport edges to 0% toward the center.
|
||||
void drawPauseBorder(QPainter& painter);
|
||||
// Vignette-style border shown while demolish mode is active (REQ-UI-DEMOLISH-BORDER),
|
||||
// tinted with the demolish overlay color (including its configured alpha) from
|
||||
// Vignette-style border shown while deconstruct mode is active (REQ-UI-DECONSTRUCT-BORDER),
|
||||
// tinted with the deconstruct overlay color (including its configured alpha) from
|
||||
// visuals.toml instead of black.
|
||||
void drawDemolishBorder(QPainter& painter);
|
||||
// Shared drawing for the pause/demolish vignettes: a mitred picture-frame border
|
||||
void drawDeconstructBorder(QPainter& painter);
|
||||
// Shared drawing for the pause/deconstruct vignettes: a mitred picture-frame border
|
||||
// whose alpha fades from `edgeColor` (with its own alpha) at the viewport edge to
|
||||
// fully transparent toward the center.
|
||||
void drawVignetteBorder(QPainter& painter, const QColor& edgeColor);
|
||||
@@ -252,7 +252,7 @@ private:
|
||||
void exitBuilderMode();
|
||||
void enterBlueprintMode(Blueprint blueprint);
|
||||
void exitBlueprintMode();
|
||||
void toggleDemolishMode();
|
||||
void toggleDeconstructMode();
|
||||
void rotateGhost(bool clockwise);
|
||||
|
||||
struct ActiveBeam
|
||||
@@ -328,8 +328,8 @@ private:
|
||||
std::vector<CopyConfigFlash> m_copyConfigFlashes;
|
||||
static constexpr qint64 kCopyFlashDurationMs = 300;
|
||||
|
||||
bool m_demolishMode;
|
||||
std::optional<BuildingId> m_demolishHoverBuildingId;
|
||||
bool m_deconstructMode;
|
||||
std::optional<BuildingId> m_deconstructHoverBuildingId;
|
||||
bool m_debugDraw;
|
||||
|
||||
std::vector<BuildingId> m_selectedBuildingIds;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
@@ -51,8 +52,13 @@ MainWindow::MainWindow(Simulation* sim, const std::string& configDir,
|
||||
sideLayout->setContentsMargins(1, 1, 1, 1);
|
||||
sideLayout->setSpacing(1);
|
||||
|
||||
// Building icons live alongside the config (a sibling of the config dir), read
|
||||
// from disk at runtime the same way visuals.toml is.
|
||||
const std::string iconDir = QDir::cleanPath(
|
||||
QString::fromStdString(m_configDir) + "/../icons/buildings").toStdString();
|
||||
|
||||
m_selectedBuildingPanel = new SelectedBuildingPanel(sim, &sim->getConfig(), m_sidePanel);
|
||||
m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), m_sidePanel);
|
||||
m_buildButtonGrid = new BuildButtonGrid(sim, &sim->getConfig(), iconDir, m_sidePanel);
|
||||
m_blueprintPanel = new BlueprintPanel(sim, &sim->getConfig(), m_sidePanel);
|
||||
|
||||
sideLayout->addWidget(m_selectedBuildingPanel, 1);
|
||||
|
||||
@@ -44,7 +44,7 @@ struct OverlayVisuals
|
||||
{
|
||||
QColor ghostValid;
|
||||
QColor ghostInvalid;
|
||||
QColor demolishTint;
|
||||
QColor deconstructTint;
|
||||
QColor selectionRect;
|
||||
QColor tileHighlight;
|
||||
QColor selectedOutline;
|
||||
|
||||
@@ -220,7 +220,7 @@ VisualsConfig VisualsLoader::load(const std::string& path)
|
||||
toml::table& ov = requireSubtable(tbl, "overlays", "root");
|
||||
cfg.overlays.ghostValid = parseColor(requireString(ov, "ghost_valid", "overlays"), "overlays.ghost_valid");
|
||||
cfg.overlays.ghostInvalid = parseColor(requireString(ov, "ghost_invalid", "overlays"), "overlays.ghost_invalid");
|
||||
cfg.overlays.demolishTint = parseColor(requireString(ov, "demolish_tint", "overlays"), "overlays.demolish_tint");
|
||||
cfg.overlays.deconstructTint = parseColor(requireString(ov, "deconstruct_tint", "overlays"), "overlays.deconstruct_tint");
|
||||
cfg.overlays.selectionRect = parseColor(requireString(ov, "selection_rect", "overlays"), "overlays.selection_rect");
|
||||
cfg.overlays.tileHighlight = parseColor(requireString(ov, "tile_highlight", "overlays"), "overlays.tile_highlight");
|
||||
cfg.overlays.selectedOutline = parseColor(requireString(ov, "selected_outline", "overlays"), "overlays.selected_outline");
|
||||
|
||||
Reference in New Issue
Block a user