Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fa453a0
Radius overlay & tooltips
morganchristiansson Jun 14, 2026
3175b05
Fix clipping, mine radius
morganchristiansson Jun 14, 2026
d489b4c
Cleaner DrawRadiusOutline
morganchristiansson Jun 14, 2026
5728636
FoW and cleaner DrawRadiusOutline
morganchristiansson Jun 14, 2026
d6c53b8
Tidy
morganchristiansson Jun 15, 2026
11df35a
Improve radius constants
morganchristiansson Jun 15, 2026
0572598
Replace boost::optional and boost::none
morganchristiansson Jun 15, 2026
cfebdce
Simpify UI/mouse changes
morganchristiansson Jun 15, 2026
795a110
Fix map wrap clipping again
morganchristiansson Jun 15, 2026
bdfd9b1
Fix "Invalid job id" from GetWorkRadius()
morganchristiansson Jun 16, 2026
7957d83
Toggle via addon
morganchristiansson Jun 16, 2026
1f70192
Ensure hover fires after leave
morganchristiansson Jun 16, 2026
4f4fa6e
Fix formatting
morganchristiansson Jun 16, 2026
7ec4ce5
Better radius ouline drawing
morganchristiansson Jun 18, 2026
ab91acd
Enable addon by default
morganchristiansson Jun 18, 2026
16840a8
Ignore game buildings when mouse over window
morganchristiansson Jun 20, 2026
0ed0c34
Revert isEnabled change: restore original semantics
morganchristiansson Jun 30, 2026
e4df373
Revert "Revert isEnabled change: restore original semantics"
morganchristiansson Jun 30, 2026
675e474
Move OnHover() callsite to Msg_MouseMove
morganchristiansson Jun 30, 2026
0f408f0
UpdateRadiusPreviewForMousePos
morganchristiansson Jun 30, 2026
caef896
SnapToNearestCopy
morganchristiansson Jun 30, 2026
ae81945
DrawRadiusOutline
morganchristiansson Jun 30, 2026
93246c4
GetBuildingRadius comment wording
morganchristiansson Jun 30, 2026
83752d2
clang-format
morganchristiansson Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libs/s25main/FOWObjects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions libs/s25main/GlobalGameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ void GlobalGameSettings::registerAllAddons()
AddonForesterFarmFieldAvoidance,
AddonForesterReachRadius,
AddonWoodcutterReachRadius,
AddonStonemasonReachRadius
AddonStonemasonReachRadius,
AddonBuildingRadius
>;
// clang-format on
using namespace boost::mp11;
Expand Down Expand Up @@ -155,7 +156,7 @@ const GlobalGameSettings::AddonWithState* GlobalGameSettings::getAddon(AddonId i
bool GlobalGameSettings::isEnabled(AddonId id) const
{
const auto* addon = getAddon(id);
return addon && addon->status != addon->addon->getDefaultStatus();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem correct. Please revert

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to allow default enabled addons.
Ok I find it very useful to have it default enabled for testing. Can we revert this last thing before merge.

return addon && addon->status != 0;
}

unsigned GlobalGameSettings::getSelection(AddonId id) const
Expand Down
5 changes: 3 additions & 2 deletions libs/s25main/addons/AddonBool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
#include "Window.h"
#include "controls/ctrlCheck.h"

AddonBool::AddonBool(const AddonId id, AddonGroup groups, const std::string& name, const std::string& description)
: Addon(id, groups, name, description, 0)
AddonBool::AddonBool(const AddonId id, AddonGroup groups, const std::string& name, const std::string& description,
unsigned defaultStatus)
: Addon(id, groups, name, description, defaultStatus)
{}

std::unique_ptr<AddonGui> AddonBool::createGui(Window& window, bool readonly) const
Expand Down
3 changes: 2 additions & 1 deletion libs/s25main/addons/AddonBool.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class AddonBool : public Addon
};

public:
AddonBool(AddonId id, AddonGroup groups, const std::string& name, const std::string& description);
AddonBool(AddonId id, AddonGroup groups, const std::string& name, const std::string& description,
unsigned defaultStatus = 0);

unsigned getNumOptions() const override;

Expand Down
22 changes: 22 additions & 0 deletions libs/s25main/addons/AddonBuildingRadius.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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."),
1) // Enabled by default
Comment on lines +19 to +20

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"when hovering over a building icon or selecting a building."),
1) // Enabled by default
"when hovering over a building icon or selecting a building."), 0)

The convention is that the default is S2-like behavior, so you can "disable" all addons and get vanilla S2

@morganchristiansson morganchristiansson Jun 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah can revert this. Making it default enabled allowed me to continue using my save game for testing tho so it's been nice. Also playing campaign with it enabled is lovely.

I'm addicted I can't play without it.

Let's revert to default disable last thing before merge ok?

{}
};
2 changes: 2 additions & 0 deletions libs/s25main/addons/Addons.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,5 @@
#include "addons/AddonForesterReachRadius.h"
#include "addons/AddonStonemasonReachRadius.h"
#include "addons/AddonWoodcutterReachRadius.h"

#include "addons/AddonBuildingRadius.h"
5 changes: 4 additions & 1 deletion libs/s25main/addons/const_addons.h
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -82,7 +83,9 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x
FORESTER_FARM_FIELD_AVOIDANCE = 0x01100000,

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
Expand Down
11 changes: 11 additions & 0 deletions libs/s25main/controls/ctrlBuildingIcon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
}
10 changes: 10 additions & 0 deletions libs/s25main/controls/ctrlBuildingIcon.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#pragma once

#include "ctrlButton.h"
#include <functional>

struct MouseCoords;
class Window;

class ctrlBuildingIcon : public ctrlButton
Expand All @@ -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<void(bool)> 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<void(bool)> onHoverChanged_;
};
3 changes: 3 additions & 0 deletions libs/s25main/desktops/dskGameInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,9 @@ bool dskGameInterface::Msg_LeftUp(const MouseCoords& mc)

bool dskGameInterface::Msg_MouseMove(const MouseCoords& mc)
{
// Update radius preview for buildings under the cursor (event-driven, not in draw loop)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to clean such "intermediate" comments from AI

Suggested change
// Update radius preview for buildings under the cursor (event-driven, not in draw loop)
// Update radius preview for buildings under the cursor

gwv.UpdateRadiusPreviewForMousePos(mc.pos);

if(!isScrolling)
{
if(mc.num_tfingers == 1)
Expand Down
2 changes: 1 addition & 1 deletion libs/s25main/figures/nofCatapultMan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
3 changes: 3 additions & 0 deletions libs/s25main/figures/nofCatapultMan.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
class SerializedGameData;
class nobUsual;

/// Maximum distance to a target the catapult can attack (distance < 14 -> max 13)
constexpr unsigned CATAPULT_MAX_TARGET_RANGE = 13;

/// Arbeiter im Katapult
class nofCatapultMan : public nofBuildingWorker
{
Expand Down
6 changes: 2 additions & 4 deletions libs/s25main/figures/nofHunter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,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)
Comment thread
Flamefire marked this conversation as resolved.
const int SQUARE_SIZE = 19;

// Liste mit den gefundenen Tieren
std::vector<noAnimal*> 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);

Expand Down
3 changes: 3 additions & 0 deletions libs/s25main/figures/nofHunter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include "nofBuildingWorker.h"
#include "gameTypes/Direction.h"

/// Half-side length of the square the hunter scans for animals (centered on the building)
constexpr int HUNTER_SEARCH_HALFSIDE = 19;

class noAnimal;
class SerializedGameData;
class nobUsual;
Expand Down
51 changes: 51 additions & 0 deletions libs/s25main/gameData/BuildingConsts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later

#include "BuildingConsts.h"
#include "figures/nofCatapultMan.h"
#include "figures/nofFarmhand.h"
#include "figures/nofHunter.h"
#include "mygettext/mygettext.h"
#include "gameTypes/BuildingTypes.h"
#include "gameData/GameConsts.h"
#include "gameData/MilitaryConsts.h"
#include <type_traits>

const helpers::EnumArray<const char*, BuildingType> BUILDING_NAMES = {
Expand Down Expand Up @@ -409,3 +415,48 @@ const helpers::MultiEnumArray<DrawPoint, Nation, BuildingType> BUILDING_ARMOR_SI
babylonians[BuildingType::Fortress] = DrawPoint(20, -34);
return result;
}();

unsigned GetBuildingRadius(BuildingType bld)
{
switch(bld)
{
// Military buildings (territory influence radius) — from MilitaryConsts.h
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];
// Headquarters
case BuildingType::Headquarters: return HQ_RADIUS;
// Harbor building
case BuildingType::HarborBuilding: return HARBOR_RADIUS;
// Lookout tower — 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: return MINER_RADIUS;
// Farmhand-based buildings — worker goes out to gather resources from the map.
// Map each building type to its job via BLD_WORK_DESC, then query the work
// radius from nofFarmhand::GetWorkRadius.
case BuildingType::Woodcutter:
case BuildingType::Forester:
case BuildingType::Fishery:
case BuildingType::Quarry:
case BuildingType::Farm:
case BuildingType::Vineyard:
case BuildingType::Charburner:
{
const auto job = BLD_WORK_DESC[bld].job;
if(job)
return nofFarmhand::GetWorkRadius(*job);
return 0;
}
// Remaining building types have no relevant radius
default: return 0;
}
}
3 changes: 3 additions & 0 deletions libs/s25main/gameData/BuildingConsts.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,6 @@ constexpr std::array<DrawPoint, 4> SUPPRESS_UNUSED NUBIAN_MINE_FIRE = {{

/// Hilfetexte für Gebäude
extern const helpers::EnumArray<const char*, BuildingType> BUILDING_HELP_STRINGS;

/// Get the radius in tiles for a building type (worker reach, territory influence, attack range, etc.)
unsigned GetBuildingRadius(BuildingType bld);
36 changes: 33 additions & 3 deletions libs/s25main/ingameWindows/iwAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "nodeObjs/noFlag.h"
#include "gameData/BuildingConsts.h"
#include "gameData/const_gui_ids.h"
#include <boost/format.hpp>
#include <sstream>

// Tab - Flags
Expand All @@ -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
Expand Down Expand Up @@ -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<unsigned, BuildTab> NUM_TABS = {1, 2, 3, 1, 3};

for(unsigned char i = 0; i < NUM_TABS[tabs.build_tabs]; ++i)
Expand All @@ -175,6 +177,13 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP
std::stringstream tooltip;
tooltip << _(BUILDING_NAMES[bld]);

// Radius anzeigen falls vorhanden

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

English comments only (at least for new/changed ones)

unsigned radius = 0;
if(showBuildingRadius)
radius = GetBuildingRadius(bld);
Comment on lines +181 to +183

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer const variables:

Suggested change
unsigned radius = 0;
if(showBuildingRadius)
radius = GetBuildingRadius(bld);
const unsigned radius = showBuildingRadius ? 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");
Expand All @@ -186,8 +195,26 @@ iwAction::iwAction(GameInterface& gi, GameWorldView& gwv, const Tabs& tabs, MapP
}

DrawPoint iconPos((k % 5) * 36, (k / 5) * 36 + 45);
build_tab->GetGroup(static_cast<int>(bt))
->AddBuildingIcon(k, iconPos, bld, player.nation, 36, tooltip.str());
ctrlBuildingIcon* icon = build_tab->GetGroup(static_cast<int>(bt))
->AddBuildingIcon(k, iconPos, bld, player.nation, 36, tooltip.str());

// Store hover callback; activeHoveredIcon_ guards against stale leaves from
// reversed child iteration order in Msg_MouseMove dispatch
Comment on lines +201 to +202

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar AI comment unless I'm missing something this is completely redundant

Suggested change
// Store hover callback; activeHoveredIcon_ guards against stale leaves from
// reversed child iteration order in Msg_MouseMove dispatch

if(radius > 0)
{
icon->SetOnHoverChanged([this, icon, radius](bool hovered) noexcept {
if(hovered)
{
this->activeHoveredIcon_ = icon;
this->gwv.SetRadiusPreview(std::make_pair(this->selectedPt, radius));
} else if(this->activeHoveredIcon_ == icon)
{
this->activeHoveredIcon_ = nullptr;
this->gwv.SetRadiusPreview(std::nullopt);
}
// else: stale leave from a previously-hovered icon, ignore
});
}

++k;
}
Expand Down Expand Up @@ -407,6 +434,8 @@ void iwAction::Close()
{
if(ShouldBeClosed())
return;
activeHoveredIcon_ = nullptr;
gwv.SetRadiusPreview(std::nullopt);
IngameWindow::Close();
if(mousePosAtOpen_.isValid())
VIDEODRIVER.SetMousePos(mousePosAtOpen_);
Expand Down Expand Up @@ -526,6 +555,7 @@ void iwAction::Msg_Group_TabChange(const unsigned /*group_id*/, const unsigned c
void iwAction::Msg_PaintAfter()
{
IngameWindow::Msg_PaintAfter();

auto* tab = GetCtrl<ctrlTab>(0);
if(tab)
{
Expand Down
4 changes: 4 additions & 0 deletions libs/s25main/ingameWindows/iwAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

class GameInterface;
class GameWorldView;
class ctrlBuildingIcon;
class ctrlGroup;

class iwAction : public IngameWindow
Expand Down Expand Up @@ -69,6 +70,9 @@ class iwAction : public IngameWindow
/// Die einzelnen Höhen für die einzelnen Tabs im Bautab
std::array<unsigned short, 4> 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);
Expand Down
Loading