Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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/GlobalGameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void GlobalGameSettings::registerAllAddons()
AddonMoreAnimals,
AddonNoAlliedPush,
AddonNoCoinsDefault,
AddonStrandedSoldierReturnSearch,
AddonNumScoutsExploration,
AddonPeacefulMode,
AddonRefundMaterials,
Expand Down
24 changes: 24 additions & 0 deletions libs/s25main/addons/AddonStrandedSoldierReturnSearch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
//
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "AddonList.h"
#include "mygettext/mygettext.h"

class AddonStrandedSoldierReturnSearch : public AddonList
{
public:
AddonStrandedSoldierReturnSearch()
: AddonList(
AddonId::STRANDED_SOLDIER_RETURN_SEARCH, AddonGroup::Military, _("Stranded soldier return search"),
_("Controls the search radius used by stranded soldiers when looking for a way back to a warehouse."),
{
_("Default search range (1x)"),
_("Reduced search range (0.5x)"),
_("Extended search range (2x)"),
_("Very large search range (4x)"),
})
{}
};
1 change: 1 addition & 0 deletions libs/s25main/addons/Addons.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "addons/AddonDefenderBehavior.h"

#include "addons/AddonNoCoinsDefault.h"
#include "addons/AddonStrandedSoldierReturnSearch.h"

#include "addons/AddonAdjustMilitaryStrength.h"

Expand Down
5 changes: 4 additions & 1 deletion libs/s25main/addons/const_addons.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
// 00E Jonathan
// 00F Jarno
// 010 aztimh
// 011 DevOpsOfChaos

// Do not forget to add your Addon to GlobalGameSettings::registerAllAddons @ GlobalGameSettings.cpp!
// Never use a number twice!
Expand Down Expand Up @@ -75,7 +76,9 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x
AUTOFLAGS = 0x00F00000,

WINE = 0x01000000, LEATHER = 0x01000001, NO_ARMOR_DEFAULT = 0x01000002,
ARMOR_CAPTURED_BLD = 0x01000003)
ARMOR_CAPTURED_BLD = 0x01000003,

STRANDED_SOLDIER_RETURN_SEARCH = 0x01100001)
//-V:AddonId:801

enum class AddonGroup : unsigned
Expand Down
43 changes: 41 additions & 2 deletions libs/s25main/figures/noFigure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const unsigned short WANDER_RADIUS = 10;
/// Dasselbe nochmal für Soldaten
const unsigned short WANDER_TRYINGS_SOLDIERS = 6;
const unsigned short WANDER_RADIUS_SOLDIERS = 15;
const unsigned short STRANDED_SOLDIER_RETURN_SEARCH_RADIUS_REDUCED = WANDER_RADIUS_SOLDIERS / 2;

noFigure::noFigure(const Job job, const MapPoint pos, const unsigned char player, noRoadNode* const goal)
: noMovable(NodalObjectType::Figure, pos), fs(FigureState::GotToGoal), job_(job), player(player), cur_rs(nullptr),
Expand Down Expand Up @@ -464,7 +465,7 @@ void noFigure::StartWandering(const unsigned burned_wh_id)
// 3x rumirren und eine Flagge suchen, wenn dann keine gefunden wurde, stirbt die Figur
wander_way = WANDER_WAY_MIN + RANDOM_RAND(WANDER_WAY_MAX - WANDER_WAY_MIN);
// Soldaten sind härter im Nehmen
wander_tryings = IsSoldier() ? WANDER_TRYINGS_SOLDIERS : WANDER_TRYINGS;
wander_tryings = IsSoldier() ? GetStrandedSoldierReturnSearchTryings(world->GetGGS()) : WANDER_TRYINGS;

// Wenn wir stehen, zusätzlich noch loslaufen!
if(waiting_for_free_node)
Expand Down Expand Up @@ -492,8 +493,45 @@ struct IsValidFlag
IsValidFlag(const unsigned playerId) : playerId_(playerId) {}
bool operator()(const noFlag* const flag) const { return flag && flag->GetPlayer() == playerId_; }
};

enum class StrandedSoldierReturnSearchSelection
{
ReducedRadius = 1,
WideSecondStage = 2,
WideThirdStage = 3
};

} // namespace

unsigned short GetStrandedSoldierReturnSearchTryings(const GlobalGameSettings& ggs)
{
switch(static_cast<StrandedSoldierReturnSearchSelection>(ggs.getSelection(AddonId::STRANDED_SOLDIER_RETURN_SEARCH)))
{
case StrandedSoldierReturnSearchSelection::WideSecondStage: return 2 * WANDER_TRYINGS_SOLDIERS;
case StrandedSoldierReturnSearchSelection::WideThirdStage: return 3 * WANDER_TRYINGS_SOLDIERS;
default: return WANDER_TRYINGS_SOLDIERS;
}
}

unsigned short GetStrandedSoldierReturnSearchRadius(const GlobalGameSettings& ggs, const unsigned short wanderTriesLeft)
{
switch(static_cast<StrandedSoldierReturnSearchSelection>(ggs.getSelection(AddonId::STRANDED_SOLDIER_RETURN_SEARCH)))
{
case StrandedSoldierReturnSearchSelection::ReducedRadius: return STRANDED_SOLDIER_RETURN_SEARCH_RADIUS_REDUCED;
case StrandedSoldierReturnSearchSelection::WideSecondStage:
// Start with the normal soldier radius, then widen the final stage after the first six failed searches.
return wanderTriesLeft > WANDER_TRYINGS_SOLDIERS ? WANDER_RADIUS_SOLDIERS : 2 * WANDER_RADIUS_SOLDIERS;
case StrandedSoldierReturnSearchSelection::WideThirdStage:
// Escalate every six failed searches: normal radius, then 2x, then 4x for the final stage.
Comment thread
Flamefire marked this conversation as resolved.
Outdated
if(wanderTriesLeft > 2 * WANDER_TRYINGS_SOLDIERS)
return WANDER_RADIUS_SOLDIERS;
if(wanderTriesLeft > WANDER_TRYINGS_SOLDIERS)
return 2 * WANDER_RADIUS_SOLDIERS;
return 4 * WANDER_RADIUS_SOLDIERS;
default: return WANDER_RADIUS_SOLDIERS;
}
}

void noFigure::Wander()
{
// Sind wir noch auf der Suche nach einer Flagge?
Expand All @@ -508,7 +546,8 @@ void noFigure::Wander()
if(!wander_way)
{
// Soldaten sind härter im Nehmen
const unsigned short wander_radius = IsSoldier() ? WANDER_RADIUS_SOLDIERS : WANDER_RADIUS;
const unsigned short wander_radius =
IsSoldier() ? GetStrandedSoldierReturnSearchRadius(world->GetGGS(), wander_tryings) : WANDER_RADIUS;

// Flaggen sammeln und dann zufällig eine auswählen
const std::vector<noFlag*> flags =
Expand Down
7 changes: 7 additions & 0 deletions libs/s25main/figures/noFigure.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ constexpr auto maxEnumValue(FigureState)
}

class SerializedGameData;
class GlobalGameSettings;

/// Number of flag-search attempts for stranded soldiers looking for a return path to an own warehouse.
unsigned short GetStrandedSoldierReturnSearchTryings(const GlobalGameSettings& ggs);
/// Radius used only for stranded soldiers looking for a return path to an own warehouse.
/// Normal worker wandering keeps using WANDER_RADIUS and is not affected by this addon.
unsigned short GetStrandedSoldierReturnSearchRadius(const GlobalGameSettings& ggs, unsigned short wanderTriesLeft);

// Stellt einen Menschen dar
class noFigure : public noMovable
Expand Down
49 changes: 49 additions & 0 deletions tests/s25Main/integration/testFigures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later

#include "GamePlayer.h"
#include "GlobalGameSettings.h"
#include "PointOutput.h"
#include "RttrForeachPt.h"
#include "addons/const_addons.h"
#include "buildings/nobBaseWarehouse.h"
#include "factories/BuildingFactory.h"
#include "figures/noFigure.h"
Expand All @@ -21,6 +23,53 @@

BOOST_AUTO_TEST_SUITE(FigureTests)

BOOST_AUTO_TEST_CASE(StrandedSoldierReturnSearchDefaultKeepsNormalTryingsAndRadius)
{
GlobalGameSettings ggs;
ggs.setSelection(AddonId::STRANDED_SOLDIER_RETURN_SEARCH, 0);

BOOST_TEST(GetStrandedSoldierReturnSearchTryings(ggs) == 6u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 6) == 15u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 1) == 15u);
}

BOOST_AUTO_TEST_CASE(StrandedSoldierReturnSearchReducedKeepsNormalTryingsAndFixedHalfRadius)
{
GlobalGameSettings ggs;
ggs.setSelection(AddonId::STRANDED_SOLDIER_RETURN_SEARCH, 1);

BOOST_TEST(GetStrandedSoldierReturnSearchTryings(ggs) == 6u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 12) == 7u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 6) == 7u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 1) == 7u);
}

BOOST_AUTO_TEST_CASE(StrandedSoldierReturnSearchExtendedEscalatesAfterNormalTryings)
{
GlobalGameSettings ggs;
ggs.setSelection(AddonId::STRANDED_SOLDIER_RETURN_SEARCH, 2);

BOOST_TEST(GetStrandedSoldierReturnSearchTryings(ggs) == 12u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 12) == 15u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 7) == 15u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 6) == 30u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 1) == 30u);
}

BOOST_AUTO_TEST_CASE(StrandedSoldierReturnSearchVeryLargeEscalatesThroughThreeStages)
{
GlobalGameSettings ggs;
ggs.setSelection(AddonId::STRANDED_SOLDIER_RETURN_SEARCH, 3);

BOOST_TEST(GetStrandedSoldierReturnSearchTryings(ggs) == 18u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 18) == 15u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 13) == 15u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 12) == 30u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 7) == 30u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 6) == 60u);
BOOST_TEST(GetStrandedSoldierReturnSearchRadius(ggs, 1) == 60u);
}

BOOST_FIXTURE_TEST_CASE(DestroyWHWithFigure, WorldWithGCExecution2P)
{
world.GetPlayer(curPlayer).GetFirstWH()->AddToInventory(PeopleCounts::make(Job::Helper, 10), true);
Expand Down