Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 after each stage: normal radius, then 2x, then 4x for the final stage.
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