diff --git a/libs/s25main/FOWObjects.h b/libs/s25main/FOWObjects.h index e50a9b8d8d..a206f94cce 100644 --- a/libs/s25main/FOWObjects.h +++ b/libs/s25main/FOWObjects.h @@ -32,6 +32,7 @@ class fowBuilding : public FOWObject void Serialize(SerializedGameData& sgd) const override; void Draw(DrawPoint drawPt) const override; FoW_Type GetType() const override { return FoW_Type::Building; } + BuildingType GetBuildingType() const { return type; } }; /// Baustelle diff --git a/libs/s25main/GlobalGameSettings.cpp b/libs/s25main/GlobalGameSettings.cpp index d0fa0d3e0c..b60cb4e173 100644 --- a/libs/s25main/GlobalGameSettings.cpp +++ b/libs/s25main/GlobalGameSettings.cpp @@ -120,7 +120,8 @@ void GlobalGameSettings::registerAllAddons() AddonForesterFarmFieldAvoidance, AddonForesterReachRadius, AddonWoodcutterReachRadius, - AddonStonemasonReachRadius + AddonStonemasonReachRadius, + AddonBuildingRadius >; // clang-format on using namespace boost::mp11; diff --git a/libs/s25main/Pathfinding.cpp b/libs/s25main/Pathfinding.cpp index 76cc75327c..21677e7db3 100644 --- a/libs/s25main/Pathfinding.cpp +++ b/libs/s25main/Pathfinding.cpp @@ -14,7 +14,7 @@ #include "pathfinding/ShipPathData.h" #include "world/GameWorld.h" #include "gameTypes/ShipDirection.h" -#include "gameData/GameConsts.h" +#include /// Findet einen Weg für Figuren helpers::OptionalEnum GameWorldBase::FindHumanPath(const MapPoint start, const MapPoint dest, diff --git a/libs/s25main/TradePathCache.cpp b/libs/s25main/TradePathCache.cpp index d8cb75f509..9f72142f31 100644 --- a/libs/s25main/TradePathCache.cpp +++ b/libs/s25main/TradePathCache.cpp @@ -6,7 +6,7 @@ #include "EventManager.h" #include "GamePlayer.h" #include "world/GameWorld.h" -#include "gameData/GameConsts.h" +#include bool TradePathCache::pathExists(const MapPoint start, const MapPoint goal, const PlayerIdx player) { diff --git a/libs/s25main/Ware.cpp b/libs/s25main/Ware.cpp index 99886d1063..78bcbf7366 100644 --- a/libs/s25main/Ware.cpp +++ b/libs/s25main/Ware.cpp @@ -15,7 +15,6 @@ #include "nodeObjs/noFlag.h" #include "nodeObjs/noRoadNode.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include "gameData/GoodConsts.h" #include "gameData/ShieldConsts.h" #include "s25util/Log.h" diff --git a/libs/s25main/addons/AddonBuildingRadius.h b/libs/s25main/addons/AddonBuildingRadius.h new file mode 100644 index 0000000000..de9d493872 --- /dev/null +++ b/libs/s25main/addons/AddonBuildingRadius.h @@ -0,0 +1,21 @@ +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) +// +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "AddonBool.h" +#include "mygettext/mygettext.h" + +/** + * Show building radius information in tooltips and as map overlay + */ +class AddonBuildingRadius : public AddonBool +{ +public: + AddonBuildingRadius() + : AddonBool(AddonId::BUILDING_RADIUS, AddonGroup::GamePlay, _("Show building radius"), + _("Shows the working radius of buildings in the build menu tooltip and as an overlay on the map " + "when hovering over a building icon or selecting a building.")) + {} +}; diff --git a/libs/s25main/addons/Addons.h b/libs/s25main/addons/Addons.h index b5f912e1de..20d8493407 100644 --- a/libs/s25main/addons/Addons.h +++ b/libs/s25main/addons/Addons.h @@ -72,3 +72,5 @@ #include "addons/AddonForesterReachRadius.h" #include "addons/AddonStonemasonReachRadius.h" #include "addons/AddonWoodcutterReachRadius.h" + +#include "addons/AddonBuildingRadius.h" diff --git a/libs/s25main/addons/const_addons.h b/libs/s25main/addons/const_addons.h index 31165b45e5..d27add09e3 100644 --- a/libs/s25main/addons/const_addons.h +++ b/libs/s25main/addons/const_addons.h @@ -28,6 +28,7 @@ // 010 aztimh // 011 DevOpsOfChaos // 012 MichalLabuda +// 013 Morgan // Do not forget to add your Addon to GlobalGameSettings::registerAllAddons @ GlobalGameSettings.cpp! // Never use a number twice! @@ -88,7 +89,9 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x STRANDED_SOLDIER_RETURN_SEARCH = 0x01100001, FORESTER_REACH_RADIUS = 0x01200000, WOODCUTTER_REACH_RADIUS = 0x01200001, - STONEMASON_REACH_RADIUS = 0x01200002) + STONEMASON_REACH_RADIUS = 0x01200002, + + BUILDING_RADIUS = 0x01300000) //-V:AddonId:801 enum class AddonGroup : unsigned diff --git a/libs/s25main/ai/aijh/AIPlayerJH.cpp b/libs/s25main/ai/aijh/AIPlayerJH.cpp index 6a665bf2a2..b9acdfbc9d 100644 --- a/libs/s25main/ai/aijh/AIPlayerJH.cpp +++ b/libs/s25main/ai/aijh/AIPlayerJH.cpp @@ -17,6 +17,7 @@ #include "buildings/nobHarborBuilding.h" #include "buildings/nobMilitary.h" #include "buildings/nobUsual.h" +#include "helpers/EnumArray.h" #include "helpers/IdRange.h" #include "helpers/MaxEnumValue.h" #include "helpers/containerUtils.h" @@ -35,7 +36,6 @@ #include "gameTypes/MineResourceBehavior.h" #include "gameData/BuildingConsts.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include "gameData/JobConsts.h" #include "gameData/TerrainDesc.h" #include "gameData/ToolConsts.h" diff --git a/libs/s25main/buildings/nobBaseMilitary.cpp b/libs/s25main/buildings/nobBaseMilitary.cpp index 815dc4a3e5..85ddec8343 100644 --- a/libs/s25main/buildings/nobBaseMilitary.cpp +++ b/libs/s25main/buildings/nobBaseMilitary.cpp @@ -17,7 +17,6 @@ #include "random/Random.h" #include "world/GameWorld.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include #include diff --git a/libs/s25main/buildings/nobHarborBuilding.cpp b/libs/s25main/buildings/nobHarborBuilding.cpp index 1a8d528773..a100631ac4 100644 --- a/libs/s25main/buildings/nobHarborBuilding.cpp +++ b/libs/s25main/buildings/nobHarborBuilding.cpp @@ -12,6 +12,7 @@ #include "figures/noFigure.h" #include "figures/nofAttacker.h" #include "figures/nofDefender.h" +#include "helpers/EnumArray.h" #include "helpers/containerUtils.h" #include "helpers/pointerContainerUtils.h" #include "network/GameClient.h" @@ -25,9 +26,9 @@ #include "world/GameWorld.h" #include "nodeObjs/noShip.h" #include "gameData/BuildingConsts.h" -#include "gameData/GameConsts.h" #include "gameData/MilitaryConsts.h" #include "gameData/ShieldConsts.h" +#include nobHarborBuilding::ExpeditionInfo::ExpeditionInfo(SerializedGameData& sgd) : boards(sgd.PopUnsignedInt()), stones(sgd.PopUnsignedInt()), active(sgd.PopBool()), builder(sgd.PopBool()) diff --git a/libs/s25main/buildings/nobMilitary.cpp b/libs/s25main/buildings/nobMilitary.cpp index 47c8a7b05e..e1f48441a3 100644 --- a/libs/s25main/buildings/nobMilitary.cpp +++ b/libs/s25main/buildings/nobMilitary.cpp @@ -32,7 +32,6 @@ #include "nodeObjs/noFlag.h" #include "gameData/BuildingConsts.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include "gameData/MilitaryConsts.h" #include "gameData/SettingTypeConv.h" #include "s25util/Log.h" diff --git a/libs/s25main/buildings/nobUsual.cpp b/libs/s25main/buildings/nobUsual.cpp index 32db3a2883..f1f9140662 100644 --- a/libs/s25main/buildings/nobUsual.cpp +++ b/libs/s25main/buildings/nobUsual.cpp @@ -12,6 +12,7 @@ #include "addons/const_addons.h" #include "figures/nofBuildingWorker.h" #include "figures/nofPigbreeder.h" +#include "helpers/EnumArray.h" #include "helpers/containerUtils.h" #include "network/GameClient.h" #include "notifications/BuildingNote.h" @@ -23,7 +24,6 @@ #include "gameTypes/Resource.h" #include "gameData/BuildingConsts.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include /// Number of GFs after which the productivity is recalculated, i.e. productivity is averaged over intervals of this diff --git a/libs/s25main/controls/ctrlBuildingIcon.cpp b/libs/s25main/controls/ctrlBuildingIcon.cpp index e43493f752..51ba42099c 100644 --- a/libs/s25main/controls/ctrlBuildingIcon.cpp +++ b/libs/s25main/controls/ctrlBuildingIcon.cpp @@ -4,6 +4,7 @@ #include "ctrlBuildingIcon.h" #include "Loader.h" +#include "driver/MouseCoords.h" #include "files.h" #include "ogl/glArchivItem_Bitmap.h" #include "gameTypes/BuildingType.h" @@ -28,3 +29,13 @@ void ctrlBuildingIcon::Draw_() } void ctrlBuildingIcon::DrawContent() const {} + +bool ctrlBuildingIcon::Msg_MouseMove(const MouseCoords& mc) +{ + const bool wasHovered = (state == ButtonState::Hover); + const bool result = ctrlButton::Msg_MouseMove(mc); + const bool nowHovered = (state == ButtonState::Hover); + if(wasHovered != nowHovered && onHoverChanged_) + onHoverChanged_(nowHovered); + return result; +} diff --git a/libs/s25main/controls/ctrlBuildingIcon.h b/libs/s25main/controls/ctrlBuildingIcon.h index 393d597c18..060cc70648 100644 --- a/libs/s25main/controls/ctrlBuildingIcon.h +++ b/libs/s25main/controls/ctrlBuildingIcon.h @@ -5,6 +5,9 @@ #pragma once #include "ctrlButton.h" +#include + +struct MouseCoords; class Window; class ctrlBuildingIcon : public ctrlButton @@ -15,11 +18,18 @@ class ctrlBuildingIcon : public ctrlButton /// liefert den GebäudeTyp des Icons. BuildingType GetType() const { return type; } + /// Set callback for hover-state changes (called with true when hovered, false when not) + void SetOnHoverChanged(std::function cb) { onHoverChanged_ = std::move(cb); } + protected: /// zeichnet das Fenster. void Draw_() override; void DrawContent() const override; + bool Msg_MouseMove(const MouseCoords& mc) override; const BuildingType type; /// der GebäudeType des Icons. const Nation nation; /// Volk + +private: + std::function onHoverChanged_; }; diff --git a/libs/s25main/figures/noFigure.cpp b/libs/s25main/figures/noFigure.cpp index e39a3e16df..d0b000fee0 100644 --- a/libs/s25main/figures/noFigure.cpp +++ b/libs/s25main/figures/noFigure.cpp @@ -29,7 +29,6 @@ #include "nodeObjs/noRoadNode.h" #include "nodeObjs/noSkeleton.h" #include "gameTypes/DirectionToImgDir.h" -#include "gameData/GameConsts.h" #include "gameData/JobConsts.h" #include "s25util/Log.h" #include "s25util/colors.h" diff --git a/libs/s25main/figures/nofCatapultMan.cpp b/libs/s25main/figures/nofCatapultMan.cpp index 80b11c5a5e..065d3fd536 100644 --- a/libs/s25main/figures/nofCatapultMan.cpp +++ b/libs/s25main/figures/nofCatapultMan.cpp @@ -121,7 +121,7 @@ void nofCatapultMan::HandleDerivedEvent(const unsigned /*id*/) unsigned distance = world->CalcDistance(pos, building->GetPos()); // Entfernung nicht zu hoch? - if(distance < 14) + if(distance <= CATAPULT_MAX_TARGET_RANGE) { // Mit in die Liste aufnehmen possibleTargets.push_back(PossibleTarget(building->GetPos(), distance)); diff --git a/libs/s25main/figures/nofCatapultMan.h b/libs/s25main/figures/nofCatapultMan.h index 737cf833d4..40a8776f4d 100644 --- a/libs/s25main/figures/nofCatapultMan.h +++ b/libs/s25main/figures/nofCatapultMan.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/libs/s25main/figures/nofFarmhand.cpp b/libs/s25main/figures/nofFarmhand.cpp index 39cad7c87c..8755fa4e25 100644 --- a/libs/s25main/figures/nofFarmhand.cpp +++ b/libs/s25main/figures/nofFarmhand.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -33,6 +33,11 @@ nofFarmhand::nofFarmhand(SerializedGameData& sgd, const unsigned obj_id) {} unsigned nofFarmhand::GetWorkRadius(const Job job) +{ + return GetWorkRadius(world->GetGGS(), job); +} + +unsigned nofFarmhand::GetWorkRadius(const GlobalGameSettings& ggs, const Job job) { switch(job) { @@ -43,18 +48,18 @@ unsigned nofFarmhand::GetWorkRadius(const Job job) case Job::CharBurner: return 3; case Job::Woodcutter: { - const unsigned sel = world->GetGGS().getSelection(AddonId::WOODCUTTER_REACH_RADIUS); + const unsigned sel = ggs.getSelection(AddonId::WOODCUTTER_REACH_RADIUS); return woodcutterRadiusValues[sel]; } case Job::Forester: { - const unsigned sel = world->GetGGS().getSelection(AddonId::FORESTER_REACH_RADIUS); + const unsigned sel = ggs.getSelection(AddonId::FORESTER_REACH_RADIUS); return foresterRadiusValues[sel]; } case Job::Fisher: return 7; case Job::Stonemason: { - const unsigned sel = world->GetGGS().getSelection(AddonId::STONEMASON_REACH_RADIUS); + const unsigned sel = ggs.getSelection(AddonId::STONEMASON_REACH_RADIUS); return stonemasonRadiusValues[sel]; } default: throw std::logic_error("Invalid job"); diff --git a/libs/s25main/figures/nofFarmhand.h b/libs/s25main/figures/nofFarmhand.h index 910fffe8fb..a9e35590f0 100644 --- a/libs/s25main/figures/nofFarmhand.h +++ b/libs/s25main/figures/nofFarmhand.h @@ -1,10 +1,11 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "nofBuildingWorker.h" +class GlobalGameSettings; class SerializedGameData; class nobUsual; @@ -54,7 +55,8 @@ class nofFarmhand : public nofBuildingWorker nofFarmhand(Job job, MapPoint pos, unsigned char player, nobUsual* workplace); nofFarmhand(SerializedGameData& sgd, unsigned obj_id); - static unsigned GetWorkRadius(Job job); + static unsigned GetWorkRadius(const GlobalGameSettings& ggs, Job job); + unsigned GetWorkRadius(Job job); void Serialize(SerializedGameData& sgd) const override; diff --git a/libs/s25main/figures/nofForester.cpp b/libs/s25main/figures/nofForester.cpp index 91b617e21e..62ea5d4897 100644 --- a/libs/s25main/figures/nofForester.cpp +++ b/libs/s25main/figures/nofForester.cpp @@ -26,7 +26,7 @@ bool IsPotentialNewFieldForOwnFarm(GameWorld& world, const MapPoint pt, const un return false; return world.CheckPointsInRadius( - pt, nofFarmhand::GetWorkRadius(Job::Farmer), + pt, nofFarmhand::GetWorkRadius(world.GetGGS(), Job::Farmer), [&world, player](const MapPoint farmPt, unsigned) { if(world.GetNO(farmPt)->GetType() != NodalObjectType::Building) return false; diff --git a/libs/s25main/figures/nofGeologist.cpp b/libs/s25main/figures/nofGeologist.cpp index e84a204d6b..9ee97eddd9 100644 --- a/libs/s25main/figures/nofGeologist.cpp +++ b/libs/s25main/figures/nofGeologist.cpp @@ -22,7 +22,6 @@ #include "world/GameWorld.h" #include "nodeObjs/noFlag.h" #include "nodeObjs/noSign.h" -#include "gameData/GameConsts.h" nofGeologist::nofGeologist(const MapPoint pos, const unsigned char player, noRoadNode* goal) : nofFlagWorker(Job::Geologist, pos, player, goal), signs(0), node_goal(0, 0) diff --git a/libs/s25main/figures/nofHunter.cpp b/libs/s25main/figures/nofHunter.cpp index c22745b8fb..bdc2fcf4d0 100644 --- a/libs/s25main/figures/nofHunter.cpp +++ b/libs/s25main/figures/nofHunter.cpp @@ -19,7 +19,6 @@ #include "random/Random.h" #include "world/GameWorld.h" #include "nodeObjs/noAnimal.h" -#include "gameData/GameConsts.h" #include "gameData/JobConsts.h" #include @@ -132,16 +131,14 @@ void nofHunter::HandleDerivedEvent(unsigned /*id*/) void nofHunter::TryStartHunting() { // Find animals in a square around building (actually should be circle, but animals are moving anyway) - const int SQUARE_SIZE = 19; - // Liste mit den gefundenen Tieren std::vector available_animals; // Durchgehen und nach Tieren suchen Position curPos; - for(curPos.y = pos.y - SQUARE_SIZE; curPos.y <= pos.y + SQUARE_SIZE; ++curPos.y) + for(curPos.y = pos.y - HUNTER_SEARCH_HALFSIDE; curPos.y <= pos.y + HUNTER_SEARCH_HALFSIDE; ++curPos.y) { - for(curPos.x = pos.x - SQUARE_SIZE; curPos.x <= pos.x + SQUARE_SIZE; ++curPos.x) + for(curPos.x = pos.x - HUNTER_SEARCH_HALFSIDE; curPos.x <= pos.x + HUNTER_SEARCH_HALFSIDE; ++curPos.x) { MapPoint curMapPos = world->MakeMapPoint(curPos); diff --git a/libs/s25main/figures/nofHunter.h b/libs/s25main/figures/nofHunter.h index 1f780ebd5d..5f5321a344 100644 --- a/libs/s25main/figures/nofHunter.h +++ b/libs/s25main/figures/nofHunter.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/libs/s25main/figures/nofMiner.cpp b/libs/s25main/figures/nofMiner.cpp index 5f5da8e384..2429750f48 100644 --- a/libs/s25main/figures/nofMiner.cpp +++ b/libs/s25main/figures/nofMiner.cpp @@ -15,7 +15,7 @@ #include "gameTypes/MineNoOutputFallback.h" #include "gameTypes/MineResourceBehavior.h" #include "gameTypes/Resource.h" -#include "gameData/GameConsts.h" +#include "gameData/JobConsts.h" #include #include diff --git a/libs/s25main/figures/nofScout_Free.cpp b/libs/s25main/figures/nofScout_Free.cpp index fcbbd41428..a65fec802b 100644 --- a/libs/s25main/figures/nofScout_Free.cpp +++ b/libs/s25main/figures/nofScout_Free.cpp @@ -9,7 +9,6 @@ #include "random/Random.h" #include "world/GameWorld.h" #include "nodeObjs/noFlag.h" -#include "gameData/GameConsts.h" #include "gameData/MilitaryConsts.h" #include class noRoadNode; diff --git a/libs/s25main/figures/nofShipWright.cpp b/libs/s25main/figures/nofShipWright.cpp index 17fca8fe2a..abe4dd55b7 100644 --- a/libs/s25main/figures/nofShipWright.cpp +++ b/libs/s25main/figures/nofShipWright.cpp @@ -15,7 +15,6 @@ #include "world/GameWorld.h" #include "nodeObjs/noShipBuildingSite.h" #include "gameTypes/Direction.h" -#include "gameData/GameConsts.h" #include "gameData/JobConsts.h" #include "s25util/colors.h" diff --git a/libs/s25main/figures/nofTradeDonkey.cpp b/libs/s25main/figures/nofTradeDonkey.cpp index d9332530ab..7dc95910c4 100644 --- a/libs/s25main/figures/nofTradeDonkey.cpp +++ b/libs/s25main/figures/nofTradeDonkey.cpp @@ -11,7 +11,6 @@ #include "ogl/glArchivItem_Bitmap.h" #include "world/GameWorld.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include "gameData/JobConsts.h" nofTradeDonkey::nofTradeDonkey(const MapPoint pos, const unsigned char player, diff --git a/libs/s25main/figures/nofTradeLeader.cpp b/libs/s25main/figures/nofTradeLeader.cpp index 737bd03887..18bd6c96c9 100644 --- a/libs/s25main/figures/nofTradeLeader.cpp +++ b/libs/s25main/figures/nofTradeLeader.cpp @@ -14,7 +14,6 @@ #include "postSystem/PostMsgWithBuilding.h" #include "world/GameWorld.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include "gameData/GoodConsts.h" #include "gameData/JobConsts.h" #include "gameData/NationConsts.h" diff --git a/libs/s25main/figures/nofWorkman.cpp b/libs/s25main/figures/nofWorkman.cpp index a0a0e30d9e..1def798a94 100644 --- a/libs/s25main/figures/nofWorkman.cpp +++ b/libs/s25main/figures/nofWorkman.cpp @@ -7,7 +7,6 @@ #include "SoundManager.h" #include "buildings/nobUsual.h" #include "world/GameWorld.h" -#include "gameData/GameConsts.h" #include "gameData/JobConsts.h" nofWorkman::nofWorkman(const Job job, const MapPoint pos, const unsigned char player, nobUsual* workplace) diff --git a/libs/s25main/gameData/BuildingConsts.cpp b/libs/s25main/gameData/BuildingConsts.cpp index 105ed23d7c..395f49eea4 100644 --- a/libs/s25main/gameData/BuildingConsts.cpp +++ b/libs/s25main/gameData/BuildingConsts.cpp @@ -1,9 +1,11 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later #include "BuildingConsts.h" #include "mygettext/mygettext.h" +#include "gameTypes/BuildingTypes.h" +#include "gameData/MilitaryConsts.h" #include const helpers::EnumArray BUILDING_NAMES = { diff --git a/libs/s25main/gameData/GameConsts.h b/libs/s25main/gameData/GameConsts.h index cbc24b7920..cc45ae7a4f 100644 --- a/libs/s25main/gameData/GameConsts.h +++ b/libs/s25main/gameData/GameConsts.h @@ -40,9 +40,6 @@ constexpr auto gfs_to_duration(const unsigned gfs) return gfs * REFERENCE_SPEED; } -/// Reichweite der Bergarbeiter -constexpr unsigned MINER_RADIUS = 2; - /// Konstante für die Pfadrichtung bei einer Schiffsverbindung constexpr unsigned char SHIP_DIR = 100; constexpr unsigned char INVALID_DIR = 0xFF; diff --git a/libs/s25main/gameData/JobConsts.h b/libs/s25main/gameData/JobConsts.h index 61761d2eaf..4e7a438a53 100644 --- a/libs/s25main/gameData/JobConsts.h +++ b/libs/s25main/gameData/JobConsts.h @@ -45,8 +45,15 @@ struct JobSpriteData extern const helpers::EnumArray JOB_CONSTS; extern const helpers::EnumArray JOB_SPRITE_CONSTS; -/// Katapultmann-Wartezeit -const unsigned CATAPULT_WAIT1_LENGTH = 1300; // eigenlich 310 - aber hochgestellt wegen zu schneller Warenverteilung +/// Distance in which miners use resources, also other "digging" jobs like wells +constexpr unsigned MINER_RADIUS = 2; +/// Maximum distance to a target the catapult can attack +constexpr unsigned CATAPULT_MAX_TARGET_RANGE = 13; +/// Half-side length of the square the hunter scans for animals (centered on the building) +constexpr int HUNTER_SEARCH_HALFSIDE = 19; + +/// Time in GFs the catapult needs to wait before shooting (after aiming) +const unsigned CATAPULT_WAIT1_LENGTH = 1300; // actually 310 - but increased due to too fast goods distribution /// Position of the ware on a donkey per direction and animation step const helpers::EnumArray, Direction> WARE_POS_DONKEY = {{ diff --git a/libs/s25main/gameTypes/MineResourceBehavior.cpp b/libs/s25main/gameTypes/MineResourceBehavior.cpp index 91ad66f940..f2ea65b182 100644 --- a/libs/s25main/gameTypes/MineResourceBehavior.cpp +++ b/libs/s25main/gameTypes/MineResourceBehavior.cpp @@ -10,7 +10,7 @@ #include "gameTypes/GoodTypes.h" #include "gameTypes/MapCoordinates.h" #include "gameTypes/Resource.h" -#include "gameData/GameConsts.h" +#include "gameData/JobConsts.h" #include namespace { diff --git a/libs/s25main/ingameWindows/iwAction.cpp b/libs/s25main/ingameWindows/iwAction.cpp index ec810eac1f..5e18949a72 100644 --- a/libs/s25main/ingameWindows/iwAction.cpp +++ b/libs/s25main/ingameWindows/iwAction.cpp @@ -28,6 +28,7 @@ #include "nodeObjs/noFlag.h" #include "gameData/BuildingConsts.h" #include "gameData/const_gui_ids.h" +#include #include // Tab - Flags @@ -46,7 +47,7 @@ enum TabID iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapPoint selectedPt, const DrawPoint& mousePos, Params params, bool military_buildings) : IngameWindow(CGI_ACTION, mousePos, Extent(200, 254), _("Activity window"), LOADER.GetImageN("io", 1)), gi(gi), - gwv(gwv), selectedPt(selectedPt), mousePosAtOpen_(mousePos) + gwv(gwv), selectedPt(selectedPt), mousePosAtOpen_(mousePos), activeHoveredIcon_(nullptr) { /* TAB_FLAG 1 = Land road @@ -159,6 +160,7 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP building_available[BuildingType::LeatherWorks] = false; } + const bool showBuildingRadius = gwv.GetWorld().GetGGS().isEnabled(AddonId::BUILDING_RADIUS); constexpr helpers::EnumArray NUM_TABS = {1, 2, 3, 1, 3}; for(unsigned char i = 0; i < NUM_TABS[tabs.build_tabs]; ++i) @@ -175,6 +177,11 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP std::stringstream tooltip; tooltip << _(BUILDING_NAMES[bld]); + // Show radius if any + const unsigned radius = showBuildingRadius ? gwv.GetBuildingRadius(bld) : 0; + if(radius > 0) + tooltip << boost::format(_("\nRange: %1% tiles")) % radius; + tooltip << _("\nCosts: "); if(BUILDING_COSTS[bld].boards > 0) tooltip << (int)BUILDING_COSTS[bld].boards << _(" boards"); @@ -186,8 +193,24 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP } DrawPoint iconPos((k % 5) * 36, (k / 5) * 36 + 45); - build_tab->GetGroup(static_cast(bt)) - ->AddBuildingIcon(k, iconPos, bld, player.nation, 36, tooltip.str()); + ctrlBuildingIcon* icon = build_tab->GetGroup(static_cast(bt)) + ->AddBuildingIcon(k, iconPos, bld, player.nation, 36, tooltip.str()); + + if(radius > 0) + { + icon->SetOnHoverChanged([this, icon, radius](bool hovered) noexcept { + if(hovered) + { + this->activeHoveredIcon_ = icon; + this->gwv.ShowRadiusPreview(this->selectedPt, radius); + } else if(this->activeHoveredIcon_ == icon) + { + this->activeHoveredIcon_ = nullptr; + this->gwv.HideRadiusPreview(); + } + // else: stale leave from a previously-hovered icon, ignore + }); + } ++k; } @@ -407,6 +430,8 @@ void iwAction::Close() { if(ShouldBeClosed()) return; + activeHoveredIcon_ = nullptr; + gwv.HideRadiusPreview(); IngameWindow::Close(); if(mousePosAtOpen_.isValid()) VIDEODRIVER.SetMousePos(mousePosAtOpen_); @@ -526,6 +551,7 @@ void iwAction::Msg_Group_TabChange(const unsigned /*group_id*/, const unsigned c void iwAction::Msg_PaintAfter() { IngameWindow::Msg_PaintAfter(); + auto* tab = GetCtrl(0); if(tab) { diff --git a/libs/s25main/ingameWindows/iwAction.h b/libs/s25main/ingameWindows/iwAction.h index 3007f4df09..2384f64189 100644 --- a/libs/s25main/ingameWindows/iwAction.h +++ b/libs/s25main/ingameWindows/iwAction.h @@ -11,6 +11,7 @@ class GameInterface; class GameWorldView; +class ctrlBuildingIcon; class ctrlGroup; class iwAction : public IngameWindow @@ -69,6 +70,9 @@ class iwAction : public IngameWindow /// Die einzelnen Höhen für die einzelnen Tabs im Bautab std::array building_tab_heights; + /// Track which icon currently owns the radius preview (to ignore stale leaves) + const ctrlBuildingIcon* activeHoveredIcon_; + public: iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapPoint selectedPt, const DrawPoint& mousePos, Params params, bool military_buildings); diff --git a/libs/s25main/nodeObjs/noAnimal.cpp b/libs/s25main/nodeObjs/noAnimal.cpp index 43139a83e2..f54c6b6a2b 100644 --- a/libs/s25main/nodeObjs/noAnimal.cpp +++ b/libs/s25main/nodeObjs/noAnimal.cpp @@ -17,7 +17,6 @@ #include "ogl/glSmartBitmap.h" #include "random/Random.h" #include "world/GameWorld.h" -#include "gameData/GameConsts.h" #include "gameData/TerrainDesc.h" #include "s25util/colors.h" diff --git a/libs/s25main/pathfinding/RoadPathFinder.cpp b/libs/s25main/pathfinding/RoadPathFinder.cpp index 681b0c44d3..7e628cd1f6 100644 --- a/libs/s25main/pathfinding/RoadPathFinder.cpp +++ b/libs/s25main/pathfinding/RoadPathFinder.cpp @@ -6,12 +6,13 @@ #include "EventManager.h" #include "RttrForeachPt.h" #include "buildings/nobHarborBuilding.h" +#include "helpers/EnumArray.h" #include "pathfinding/OpenListPrioQueue.h" #include "pathfinding/OpenListVector.h" #include "world/GameWorldBase.h" #include "nodeObjs/noRoadNode.h" -#include "gameData/GameConsts.h" #include "s25util/Log.h" +#include /// Comparison operator for road nodes that returns true if lhs > rhs (descending order) struct RoadNodeComperatorGreater diff --git a/libs/s25main/world/GameWorldBase.cpp b/libs/s25main/world/GameWorldBase.cpp index b0578519c4..72d0202882 100644 --- a/libs/s25main/world/GameWorldBase.cpp +++ b/libs/s25main/world/GameWorldBase.cpp @@ -25,8 +25,8 @@ #include "pathfinding/ShipPathData.h" #include "nodeObjs/noFlag.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" #include "gameData/TerrainDesc.h" +#include #include GameWorldBase::GameWorldBase(std::vector players, const GlobalGameSettings& gameSettings, EventManager& em) diff --git a/libs/s25main/world/GameWorldView.cpp b/libs/s25main/world/GameWorldView.cpp index 4b087267db..685bccf8d5 100644 --- a/libs/s25main/world/GameWorldView.cpp +++ b/libs/s25main/world/GameWorldView.cpp @@ -11,12 +11,16 @@ #include "GlobalGameSettings.h" #include "Loader.h" #include "MapGeometry.h" +#include "ReturnMapPointWithRadius.h" #include "Settings.h" +#include "Window.h" +#include "WindowManager.h" #include "addons/AddonMaxWaterwayLength.h" #include "buildings/noBuildingSite.h" #include "buildings/nobMilitary.h" #include "buildings/nobUsual.h" #include "drivers/VideoDriverWrapper.h" +#include "figures/nofFarmhand.h" #include "helpers/EnumArray.h" #include "helpers/Range.h" #include "helpers/containerUtils.h" @@ -30,16 +34,20 @@ #include "gameTypes/RoadBuildState.h" #include "gameData/BuildingConsts.h" #include "gameData/GuiConsts.h" +#include "gameData/JobConsts.h" #include "gameData/MapConsts.h" +#include "gameData/MilitaryConsts.h" #include "s25util/error.h" #include #include #include +#include GameWorldView::GameWorldView(const GameWorldViewer& gwv, const Position& pos, const Extent& size) - : selPt(0, 0), show_bq(SETTINGS.ingame.showBQ), show_names(SETTINGS.ingame.showNames), - show_productivity(SETTINGS.ingame.showProductivity), offset(0, 0), lastOffset(0, 0), gwv(gwv), origin_(pos), - size_(size), zoomFactor_(1.f), targetZoomFactor_(1.f), zoomSpeed_(0.f) + : gwv(gwv), origin_(pos), size_(size), selPt(0, 0), show_bq(SETTINGS.ingame.showBQ), + show_names(SETTINGS.ingame.showNames), show_productivity(SETTINGS.ingame.showProductivity), + isBuildingRadiusEnabled_(GetWorld().GetGGS().isEnabled(AddonId::BUILDING_RADIUS)), offset(0, 0), lastOffset(0, 0), + zoomFactor_(1.f), targetZoomFactor_(1.f), zoomSpeed_(0.f) { updateEffectiveZoomFactor(); MoveBy({0, 0}); @@ -219,6 +227,13 @@ void GameWorldView::Draw(const RoadBuildState& rb, const MapPoint selected, bool if(show_names || show_productivity) DrawNameProductivityOverlay(terrainRenderer); + if(isBuildingRadiusEnabled_) + { + if(drawMouse) + UpdateRadiusPreviewForMousePos(); + DrawRadiusOutline(); + } + DrawGUI(rb, terrainRenderer, selected, drawMouse); // Draw catapult stones @@ -699,6 +714,49 @@ void GameWorldView::MoveToLastPosition() lastOffset = newLastOffset; } +unsigned GameWorldView::GetBuildingRadius(BuildingType bld) const +{ + switch(bld) + { + // Military buildings (territory influence radius) + case BuildingType::Barracks: return MILITARY_RADIUS[0]; + case BuildingType::Guardhouse: return MILITARY_RADIUS[1]; + case BuildingType::Watchtower: return MILITARY_RADIUS[2]; + case BuildingType::Fortress: return MILITARY_RADIUS[3]; + case BuildingType::Headquarters: return HQ_RADIUS; + case BuildingType::HarborBuilding: return HARBOR_RADIUS; + + // Scouting visibility range + case BuildingType::LookoutTower: return VISUALRANGE_LOOKOUTTOWER; + // Catapult attack range + case BuildingType::Catapult: return CATAPULT_MAX_TARGET_RANGE; + // Hunter searches for animals in a square of this half-side length + case BuildingType::Hunter: return HUNTER_SEARCH_HALFSIDE; + // Mines — miner stays inside and extracts from adjacent tiles + case BuildingType::GraniteMine: + case BuildingType::CoalMine: + case BuildingType::IronMine: + case BuildingType::GoldMine: + case BuildingType::Well: // Well uses the same radius + return MINER_RADIUS; + // Farmhand-based buildings — worker goes out to gather resources from the map. + case BuildingType::Woodcutter: + case BuildingType::Forester: + case BuildingType::Fishery: + case BuildingType::Quarry: + case BuildingType::Farm: + case BuildingType::Vineyard: + case BuildingType::Charburner: + { + if(const auto job = BLD_WORK_DESC[bld].job) + return nofFarmhand::GetWorkRadius(GetWorld().GetGGS(), *job); + return 0; + } + // Remaining building types have no relevant radius + default: return 0; + } +} + void GameWorldView::AddDrawNodeCallback(IDrawNodeCallback* newCallback) { RTTR_Assert(newCallback); @@ -712,6 +770,85 @@ void GameWorldView::RemoveDrawNodeCallback(IDrawNodeCallback* callbackToRemove) drawNodeCallbacks.erase(itPos); } +void GameWorldView::ShowRadiusPreview(MapPoint center, unsigned radius) +{ + if(radiusPreview_ && radiusPreview_->center == center && radiusPreview_->radius == radius) + return; + std::vector pts; + for(const auto& [mapPt, dist] : GetWorld().GetPointsInRadius(center, radius, ReturnMapPointWithRadius{})) + { + if(dist == radius) + pts.push_back(GetWorld().GetNodePos(mapPt)); + } + radiusPreview_ = RadiusPreview{center, radius, std::move(pts)}; +} + +DrawPoint GameWorldView::SnapToNearestCopy(const DrawPoint pt, const DrawPoint ref, const Extent mapSize) +{ + // Find how many map sizes separate `pt` from `ref`, round to the nearest integer number of wraps. + const auto k = PointF(ref - pt) / mapSize; + const auto kRounded = PointF(std::round(k.x), std::round(k.y)); + // Shift `pt` by that many map sizes to get the nearest toroidal copy. + return pt + DrawPoint(kRounded) * mapSize; +} + +void GameWorldView::DrawRadiusOutline() +{ + if(!radiusPreview_) + return; + + const auto& world = GetWorld(); + + const MapExtent mapSize = world.GetSize(); + constexpr unsigned BORDER_COLOR = 0xFFFF0000; + constexpr auto BORDER_SIZE = Extent::all(5); + const Extent mapPxSize = mapSize * Extent(TR_W, TR_H); + + // Screen position of the center vertex. + // selPtOffset accounts for the pixel shift when the map wraps at the seam. + const DrawPoint centerScr = world.GetNodePos(radiusPreview_->center) + selPtOffset; + + for(auto screenPt : radiusPreview_->outline) + { + screenPt = SnapToNearestCopy(screenPt, centerScr, mapPxSize) - offset; + Window::DrawRectangle(Rect(screenPt - BORDER_SIZE / 2, BORDER_SIZE), BORDER_COLOR); + } +} + +void GameWorldView::UpdateRadiusPreviewForMousePos() +{ + const auto& world = GetWorld(); + std::optional bldType; + switch(gwv.GetVisibility(selPt)) + { + case Visibility::Visible: + { + const auto* bld = world.GetSpecObj(selPt); + if(bld) + bldType = bld->GetBuildingType(); + } + break; + case Visibility::FogOfWar: + { + const FOWObject* fow = gwv.GetYoungestFOWObject(selPt); + if(fow && fow->GetType() == FoW_Type::Building) + bldType = static_cast(*fow).GetBuildingType(); + } + break; + case Visibility::Invisible: break; + } + + if(bldType) + { + const unsigned bldRadius = GetBuildingRadius(*bldType); + if(bldRadius > 0) + ShowRadiusPreview(selPt, bldRadius); + else + HideRadiusPreview(); + } else + HideRadiusPreview(); +} + void GameWorldView::CalcFxLx() { // Calc first and last point in map units (with 1 extra for incomplete triangles) diff --git a/libs/s25main/world/GameWorldView.h b/libs/s25main/world/GameWorldView.h index c405ec803e..9b8b5bd039 100644 --- a/libs/s25main/world/GameWorldView.h +++ b/libs/s25main/world/GameWorldView.h @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,9 +6,11 @@ #include "Cheats.h" #include "DrawPoint.h" +#include "gameTypes/BuildingType.h" #include "gameTypes/MapCoordinates.h" #include "gameTypes/MapTypes.h" #include +#include #include class GameWorldBase; @@ -31,6 +33,20 @@ struct ObjectBetweenLines; class GameWorldView { + struct RadiusPreview + { + MapPoint center; + unsigned radius; + std::vector outline; + }; + + const GameWorldViewer& gwv; + + /// Top-Left position of the view (window) + Position origin_; + /// Size of the view + Extent size_; + /// Currently selected point (where the mouse points to) MapPoint selPt; /// Offset to selected point @@ -46,6 +62,11 @@ class GameWorldView /// Show productivities bool show_productivity; + /// Optional radius preview (center position, radius) drawn as outline on the map + std::optional radiusPreview_; + /// Cached addon status: whether building radius overlay is enabled + bool isBuildingRadiusEnabled_; + /// Offset from world origin in screen units (not map units): "scroll position" DrawPoint offset; /// Last scroll position (before jump) @@ -55,13 +76,6 @@ class GameWorldView /// Last drawn map point DrawPoint lastPt; - const GameWorldViewer& gwv; - - /// Top-Left position of the view (window) - Position origin_; - /// Size of the view - Extent size_; - /// How much the view is scaled (1=normal, >1=bigger, >0 && <1=smaller) float zoomFactor_; float effectiveZoomFactor_; ///< DPI scale corrected zoom factor @@ -113,10 +127,20 @@ class GameWorldView DrawPoint GetOffset() const { return offset; } + /// Get the radius in tiles for a building type (worker reach, territory influence, attack range, etc.) + unsigned GetBuildingRadius(BuildingType bld) const; + /// Add a debug node printer void AddDrawNodeCallback(IDrawNodeCallback* newCallback); void RemoveDrawNodeCallback(IDrawNodeCallback* callbackToRemove); + /// Show a radius preview outline on the map + void ShowRadiusPreview(MapPoint center, unsigned radius); + void HideRadiusPreview() { radiusPreview_ = std::nullopt; } + + /// Update radius preview based on which map node is selected + void UpdateRadiusPreviewForMousePos(); + /// Gibt selektierten Punkt zurück MapPoint GetSelectedPt() const { return selPt; } @@ -146,6 +170,12 @@ class GameWorldView void DrawGUI(const RoadBuildState& rb, const TerrainRenderer& terrainRenderer, const MapPoint& selectedPt, bool drawMouse); + /// Draw a radius outline ring + void DrawRadiusOutline(); + + /// Snap a point to the nearest toroidal copy relative to a reference position, given the map size in pixels + static DrawPoint SnapToNearestCopy(DrawPoint pt, DrawPoint ref, Extent mapSize); + void SaveIngameSettingsValues() const; void updateEffectiveZoomFactor(); }; diff --git a/libs/s25main/world/TradeRoute.cpp b/libs/s25main/world/TradeRoute.cpp index 66331ea7cd..97d9e27f56 100644 --- a/libs/s25main/world/TradeRoute.cpp +++ b/libs/s25main/world/TradeRoute.cpp @@ -5,7 +5,7 @@ #include "world/TradeRoute.h" #include "SerializedGameData.h" #include "world/GameWorld.h" -#include "gameData/GameConsts.h" +#include TradeRoute::TradeRoute(const GameWorld& world, unsigned char player, const MapPoint& start, const MapPoint& goal) : world(world), player(player) diff --git a/tests/s25Main/integration/testGamePlayer.cpp b/tests/s25Main/integration/testGamePlayer.cpp index 9aa8a8b561..cc3ed10a01 100644 --- a/tests/s25Main/integration/testGamePlayer.cpp +++ b/tests/s25Main/integration/testGamePlayer.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +15,7 @@ #include "gameTypes/MineResourceBehavior.h" #include "gameTypes/Resource.h" #include "gameData/BuildingProperties.h" -#include "gameData/GameConsts.h" +#include "gameData/JobConsts.h" #include "rttr/test/random.hpp" #include "s25util/warningSuppression.h" #include diff --git a/tests/s25Main/integration/testPathfinding.cpp b/tests/s25Main/integration/testPathfinding.cpp index e2e6b5bf5e..c94b393ec2 100644 --- a/tests/s25Main/integration/testPathfinding.cpp +++ b/tests/s25Main/integration/testPathfinding.cpp @@ -10,7 +10,6 @@ #include "worldFixtures/terrainHelpers.h" #include "nodeObjs/noGranite.h" #include "gameTypes/GameTypesOutput.h" -#include "gameData/GameConsts.h" #include #include #include