Compare commits
2 Commits
cd966daba9
...
0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 271855781c | |||
| 5f6ecbf6c8 |
@@ -4,10 +4,20 @@ message(STATUS "Using CMake ${CMAKE_VERSION}")
|
||||
|
||||
include(cmake/add_files.cmake)
|
||||
include(cmake/create_source_groups.cmake)
|
||||
include(cmake/version.cmake)
|
||||
|
||||
# Project ----------------------------------------------------------------------
|
||||
|
||||
project(DotaFactory)
|
||||
# Product identity — anything that depends on the product/project name is defined
|
||||
# here so it lives in a single place and can change in the future. These values
|
||||
# feed the build targets in src/CMakeLists.txt and the Windows version resource
|
||||
# (see cmake/version.rc.in).
|
||||
set(PRODUCT_NAME "DotaFactory") # internal name and executable base name
|
||||
set(PRODUCT_DISPLAY_NAME "Dota Factory") # human-readable product / file description
|
||||
set(PRODUCT_COMPANY "TODO: company") # placeholder
|
||||
set(PRODUCT_COPYRIGHT "TODO: copyright") # placeholder
|
||||
|
||||
project(${PRODUCT_NAME})
|
||||
|
||||
set(CMAKE_BUILD_TYPE_INIT "Release")
|
||||
|
||||
|
||||
@@ -348,6 +348,7 @@ demolish_tint = "#ff000033" # demolish-mode hover tint
|
||||
selection_rect = "#00ff00" # box-drag selection rectangle (REQ-UI-MULTI-SELECT)
|
||||
tile_highlight = "#ffffff22" # tile under cursor
|
||||
selected_outline = "#ffff00" # outline drawn around currently-selected building(s)
|
||||
copy_config = "#33ccff66" # copy-settings eligible-target tint + copy/paste flash (REQ-BLD-COPY-CONFIG-FEEDBACK)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Schematic-drop toasts (REQ-UI-SCHEMATIC-TOAST)
|
||||
|
||||
60
cmake/version.cmake
Normal file
60
cmake/version.cmake
Normal file
@@ -0,0 +1,60 @@
|
||||
# simple check for a git repo
|
||||
if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
find_package(Git)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log -1 --format=%h
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log -1 --format=%ci
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_TIME
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} describe --long --match "[0-9]*" HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_VERSION_NUMBER
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GIT_VERSION_NUMBER}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${GIT_VERSION_NUMBER}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${GIT_VERSION_NUMBER}")
|
||||
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.[0-9]+-([0-9]+).*" "\\1" VERSION_COMMIT "${GIT_VERSION_NUMBER}")
|
||||
|
||||
else(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
set(GIT_BRANCH "")
|
||||
set(GIT_COMMIT_HASH "")
|
||||
set(GIT_VERSION_NUMBER "")
|
||||
set(VERSION_MAJOR "0")
|
||||
set(VERSION_MINOR "0")
|
||||
set(VERSION_PATCH "0")
|
||||
set(VERSION_COMMIT "0")
|
||||
set(BUILD_TYPE "")
|
||||
|
||||
endif(EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
|
||||
set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_COMMIT}")
|
||||
|
||||
message(STATUS "Version: ${VERSION_STRING}")
|
||||
|
||||
# message(STATUS "Git current branch: ${GIT_BRANCH}")
|
||||
# message(STATUS "Git version number: " ${GIT_VERSION_NUMBER} )
|
||||
# message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
# message(STATUS "Git commit time: ${GIT_COMMIT_TIME}")
|
||||
# message(STATUS "Version major: ${VERSION_MAJOR}")
|
||||
# message(STATUS "Version minor: ${VERSION_MINOR}")
|
||||
# message(STATUS "Version patch: ${VERSION_PATCH}")
|
||||
# message(STATUS "Version commit: ${VERSION_COMMIT}")
|
||||
35
cmake/version.rc.in
Normal file
35
cmake/version.rc.in
Normal file
@@ -0,0 +1,35 @@
|
||||
// Windows version resource. Generated by CMake via configure_file() from this
|
||||
// template; @VAR@ placeholders are filled from cmake/version.cmake (version
|
||||
// numbers) and the product identity variables in the top-level CMakeLists.txt.
|
||||
// Shows up on the executable's Details tab (right-click -> Properties).
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_COMMIT@
|
||||
PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_COMMIT@
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04b0)
|
||||
BEGIN
|
||||
VALUE "CompanyName", "@PRODUCT_COMPANY@"
|
||||
VALUE "FileDescription", "@PRODUCT_DISPLAY_NAME@"
|
||||
VALUE "FileVersion", "@VERSION_STRING@"
|
||||
VALUE "InternalName", "@PRODUCT_NAME@"
|
||||
VALUE "OriginalFilename", "@PRODUCT_NAME@.exe"
|
||||
VALUE "ProductName", "@PRODUCT_DISPLAY_NAME@"
|
||||
VALUE "ProductVersion", "@VERSION_STRING@"
|
||||
VALUE "LegalCopyright", "@PRODUCT_COPYRIGHT@"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // 0x409 = en-US, 1200 = Unicode code page
|
||||
END
|
||||
END
|
||||
@@ -1,4 +1,4 @@
|
||||
set(TARGET_BASE_NAME "DotaFactory")
|
||||
set(TARGET_BASE_NAME "${PRODUCT_NAME}")
|
||||
|
||||
set(TARGET_APP_NAME "${TARGET_BASE_NAME}")
|
||||
set(TARGET_LIB_NAME "${TARGET_BASE_NAME}_lib")
|
||||
@@ -183,6 +183,19 @@ target_compile_definitions(${TARGET_APP_NAME} PRIVATE
|
||||
)
|
||||
target_link_libraries(${TARGET_APP_NAME} ${TARGET_UI_NAME})
|
||||
|
||||
# Embed the Windows version resource so the version shows on the executable's
|
||||
# Details tab (right-click -> Properties). Values come from cmake/version.cmake
|
||||
# (version numbers) and the product identity variables in the top-level
|
||||
# CMakeLists.txt. MSVC compiles the .rc automatically once it is a target source.
|
||||
if (WIN32)
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/cmake/version.rc.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/version.rc"
|
||||
@ONLY
|
||||
)
|
||||
target_sources(${TARGET_APP_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
|
||||
endif ()
|
||||
|
||||
unset(APP_FILES)
|
||||
unset(RELATIVE_HDRS)
|
||||
unset(RELATIVE_SRCS)
|
||||
|
||||
@@ -271,6 +271,20 @@ void GameWorldView::onFrame()
|
||||
m_activeBeams = std::move(live);
|
||||
}
|
||||
|
||||
// Expire copy/paste flashes. Lifetime is wall-clock (the frame delta), so the
|
||||
// flash plays for a fixed real duration regardless of game speed, including
|
||||
// while the game is paused (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
if (!m_copyConfigFlashes.empty())
|
||||
{
|
||||
std::vector<CopyConfigFlash> live;
|
||||
for (CopyConfigFlash flash : m_copyConfigFlashes)
|
||||
{
|
||||
flash.remainingMs -= elapsed;
|
||||
if (flash.remainingMs > 0) { live.push_back(flash); }
|
||||
}
|
||||
m_copyConfigFlashes = std::move(live);
|
||||
}
|
||||
|
||||
// Apply held scroll
|
||||
{
|
||||
// Pan speed depends on where the view is centered (REQ-UI-SCROLL-SPEED).
|
||||
@@ -384,6 +398,7 @@ void GameWorldView::paintGL()
|
||||
|
||||
drawTiles(painter);
|
||||
drawBuildings(painter);
|
||||
drawCopyConfigFeedback(painter);
|
||||
drawStations(painter);
|
||||
drawBeltItems(painter);
|
||||
drawScrap(painter);
|
||||
@@ -998,6 +1013,29 @@ void GameWorldView::drawBuildings(QPainter& painter)
|
||||
drawSelectionHighlights(painter);
|
||||
}
|
||||
|
||||
std::optional<QRectF> GameWorldView::footprintWidgetRect(BuildingId id) const
|
||||
{
|
||||
std::optional<QPoint> anchor;
|
||||
std::optional<QSize> footprint;
|
||||
|
||||
if (const Building* b = m_sim->buildings().findBuilding(id))
|
||||
{
|
||||
anchor = b->anchor;
|
||||
footprint = b->footprint;
|
||||
}
|
||||
else if (const ConstructionSite* s = m_sim->buildings().findSite(id))
|
||||
{
|
||||
anchor = s->anchor;
|
||||
footprint = s->footprint;
|
||||
}
|
||||
if (!anchor.has_value() || !footprint.has_value()) { return std::nullopt; }
|
||||
|
||||
const QPointF tl = tileToWidget(*anchor);
|
||||
return QRectF(tl.x(), tl.y(),
|
||||
footprint->width() * static_cast<qreal>(tilePx()),
|
||||
footprint->height() * static_cast<qreal>(tilePx()));
|
||||
}
|
||||
|
||||
void GameWorldView::drawSelectionHighlights(QPainter& painter)
|
||||
{
|
||||
painter.setPen(QPen(m_visuals->overlays.selectedOutline, 2));
|
||||
@@ -1005,26 +1043,47 @@ void GameWorldView::drawSelectionHighlights(QPainter& painter)
|
||||
|
||||
for (BuildingId selId : m_selectedBuildingIds)
|
||||
{
|
||||
std::optional<QPoint> anchor;
|
||||
std::optional<QSize> footprint;
|
||||
|
||||
if (const Building* b = m_sim->buildings().findBuilding(selId))
|
||||
{
|
||||
anchor = b->anchor;
|
||||
footprint = b->footprint;
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(selId);
|
||||
if (!rect.has_value()) { continue; }
|
||||
// Outline sits 1px outside the footprint (into adjacent tiles).
|
||||
painter.drawRect(rect->adjusted(-1, -1, 1, 1));
|
||||
}
|
||||
else if (const ConstructionSite* s = m_sim->buildings().findSite(selId))
|
||||
{
|
||||
anchor = s->anchor;
|
||||
footprint = s->footprint;
|
||||
}
|
||||
if (!anchor.has_value() || !footprint.has_value()) { continue; }
|
||||
}
|
||||
|
||||
const QPointF tl = tileToWidget(*anchor);
|
||||
const QRectF bboxRect(tl.x(), tl.y(),
|
||||
footprint->width() * static_cast<qreal>(tilePx()),
|
||||
footprint->height() * static_cast<qreal>(tilePx()));
|
||||
painter.drawRect(bboxRect.adjusted(-1, -1, 1, 1));
|
||||
void GameWorldView::drawCopyConfigFeedback(QPainter& painter)
|
||||
{
|
||||
const QColor color = m_visuals->overlays.copyConfig;
|
||||
|
||||
// Eligible-target tint: while a configuration is cached, every same-type
|
||||
// building and site (the source included) is a valid paste target and is
|
||||
// washed in the copy-settings color (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
if (m_copiedConfig.has_value())
|
||||
{
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(color);
|
||||
const BuildingType type = m_copiedConfig->type;
|
||||
for (const Building& b : m_sim->buildings().allBuildings())
|
||||
{
|
||||
if (b.type != type) { continue; }
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(b.id);
|
||||
if (rect.has_value()) { painter.drawRect(*rect); }
|
||||
}
|
||||
for (const ConstructionSite& s : m_sim->buildings().allSites())
|
||||
{
|
||||
if (s.type != type) { continue; }
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(s.id);
|
||||
if (rect.has_value()) { painter.drawRect(*rect); }
|
||||
}
|
||||
}
|
||||
|
||||
// Copy / paste flashes: a brief outline in the same color, drawn like the
|
||||
// selection outline (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
painter.setPen(QPen(color, 2));
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
for (const CopyConfigFlash& flash : m_copyConfigFlashes)
|
||||
{
|
||||
const std::optional<QRectF> rect = footprintWidgetRect(flash.id);
|
||||
if (rect.has_value()) { painter.drawRect(rect->adjusted(-1, -1, 1, 1)); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1905,6 +1964,7 @@ void GameWorldView::copyConfigFrom(BuildingId id)
|
||||
if (!config->recipeId.has_value() && !config->isSplitter) { return; }
|
||||
|
||||
m_copiedConfig = config;
|
||||
m_copyConfigFlashes.push_back({ id, kCopyFlashDurationMs });
|
||||
}
|
||||
|
||||
void GameWorldView::pasteConfigTo(BuildingId id)
|
||||
@@ -1914,6 +1974,9 @@ void GameWorldView::pasteConfigTo(BuildingId id)
|
||||
const std::optional<BuildingConfig> target = readBuildingConfig(*m_sim, id);
|
||||
if (!target.has_value() || target->type != m_copiedConfig->type) { return; }
|
||||
|
||||
// The paste applies below; flash the target to confirm (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
m_copyConfigFlashes.push_back({ id, kCopyFlashDurationMs });
|
||||
|
||||
const BuildingConfig& source = *m_copiedConfig;
|
||||
|
||||
// The cached settings were valid on a same-type source building, so they are
|
||||
@@ -2041,6 +2104,7 @@ void GameWorldView::resetForNewGame()
|
||||
std::make_shared<DemolishModeChangedEvent>(false));
|
||||
m_selectedBuildingIds.clear();
|
||||
m_copiedConfig = std::nullopt;
|
||||
m_copyConfigFlashes.clear();
|
||||
m_boxSelecting = false;
|
||||
m_scrollXTiles = 0.0f;
|
||||
m_scrollLeft = false;
|
||||
|
||||
@@ -117,6 +117,7 @@ private:
|
||||
void drawTiles(QPainter& painter);
|
||||
void drawBuildings(QPainter& painter);
|
||||
void drawSelectionHighlights(QPainter& painter);
|
||||
void drawCopyConfigFeedback(QPainter& painter);
|
||||
void drawStations(QPainter& painter);
|
||||
void drawBeltItems(QPainter& painter);
|
||||
void drawScrap(QPainter& painter);
|
||||
@@ -138,6 +139,10 @@ private:
|
||||
QPoint widgetToTile(QPoint widgetPt) const;
|
||||
QRectF tileRect(QPoint tile) const;
|
||||
QRect viewportRect() const;
|
||||
// Widget-space rectangle covering a building or construction site's footprint,
|
||||
// or nullopt if the id resolves to neither. Shared by the selection highlight
|
||||
// and the copy-settings feedback (REQ-BLD-COPY-CONFIG-FEEDBACK).
|
||||
std::optional<QRectF> footprintWidgetRect(BuildingId id) const;
|
||||
|
||||
float asteroidLeftEdge() const;
|
||||
float enemyStationRightEdge() const;
|
||||
@@ -226,6 +231,18 @@ private:
|
||||
// only while Shift is down and cleared on Shift release.
|
||||
std::optional<BuildingConfig> m_copiedConfig;
|
||||
|
||||
// Brief outline flash shown on a building when settings are copied from it or
|
||||
// pasted onto it (REQ-BLD-COPY-CONFIG-FEEDBACK). remainingMs counts down in
|
||||
// wall-clock time so the flash plays at a fixed length regardless of game speed
|
||||
// (and while paused).
|
||||
struct CopyConfigFlash
|
||||
{
|
||||
BuildingId id;
|
||||
qint64 remainingMs;
|
||||
};
|
||||
std::vector<CopyConfigFlash> m_copyConfigFlashes;
|
||||
static constexpr qint64 kCopyFlashDurationMs = 300;
|
||||
|
||||
bool m_demolishMode;
|
||||
BuildingId m_demolishHoverBuildingId;
|
||||
bool m_debugDraw;
|
||||
|
||||
@@ -48,6 +48,7 @@ struct OverlayVisuals
|
||||
QColor selectionRect;
|
||||
QColor tileHighlight;
|
||||
QColor selectedOutline;
|
||||
QColor copyConfig;
|
||||
};
|
||||
|
||||
struct ToastVisuals
|
||||
|
||||
@@ -224,6 +224,7 @@ VisualsConfig VisualsLoader::load(const std::string& path)
|
||||
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");
|
||||
cfg.overlays.copyConfig = parseColor(requireString(ov, "copy_config", "overlays"), "overlays.copy_config");
|
||||
}
|
||||
|
||||
// Toast
|
||||
|
||||
Reference in New Issue
Block a user