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
41 changes: 31 additions & 10 deletions libs/s25main/BuildingRegister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@
#include "gameData/BuildingConsts.h"
#include "gameData/BuildingProperties.h"

namespace {
unsigned CalcAverageProductivity(const std::list<nobUsual*>& buildings,
unsigned short (nobUsual::*getProductivity)() const)
{
const unsigned numBlds = buildings.size();
if(numBlds == 0)
return 0;

unsigned productivity = 0;
for(const nobUsual* bld : buildings)
productivity += (bld->*getProductivity)();
return productivity / numBlds;
}
} // namespace

void BuildingRegister::Serialize(SerializedGameData& sgd) const
{
sgd.PushObjectContainer(warehouses);
Expand Down Expand Up @@ -155,20 +170,26 @@ helpers::EnumArray<uint16_t, BuildingType> BuildingRegister::CalcProductivities(
return productivities;
}

helpers::EnumArray<uint16_t, BuildingType> BuildingRegister::CalcDisplayProductivities() const
{
helpers::EnumArray<uint16_t, BuildingType> productivities;

for(const auto bld : helpers::enumRange<BuildingType>())
{
if(holds_alternative<boost::none_t>(BLD_WORK_DESC[bld].producedWare))
productivities[bld] = 0;
else
productivities[bld] =
static_cast<uint16_t>(::CalcAverageProductivity(GetBuildings(bld), &nobUsual::GetDisplayProductivity));
}
return productivities;
}

unsigned BuildingRegister::CalcAverageProductivity(BuildingType bldType) const
{
if(holds_alternative<boost::none_t>(BLD_WORK_DESC[bldType].producedWare))
return 0;
unsigned productivity = 0;
const auto& buildings = GetBuildings(bldType);
const unsigned numBlds = buildings.size();
if(numBlds > 0)
{
for(const nobUsual* bld : buildings)
productivity += bld->GetProductivity();
productivity /= numBlds;
}
return productivity;
return ::CalcAverageProductivity(GetBuildings(bldType), &nobUsual::GetProductivity);
}

unsigned short BuildingRegister::CalcAverageProductivity() const
Expand Down
2 changes: 2 additions & 0 deletions libs/s25main/BuildingRegister.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class BuildingRegister
BuildingCount GetBuildingNums() const;
/// Calculate and fill the average productivities for all buildings.
helpers::EnumArray<uint16_t, BuildingType> CalcProductivities() const;
/// Calculate and fill the average productivities shown in UI.
helpers::EnumArray<uint16_t, BuildingType> CalcDisplayProductivities() const;
/// Calculate the average productivity for a building type
unsigned CalcAverageProductivity(BuildingType bldType) const;
/// Calculate the average productivity for all buildings
Expand Down
66 changes: 64 additions & 2 deletions libs/s25main/GlobalGameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,44 @@
#include "addons/Addons.h"
#include "helpers/containerUtils.h"
#include "helpers/serializeEnums.h"
#include "gameTypes/BuildingType.h"
#include "gameTypes/MineResourceBehavior.h"
#include "gameData/MilitaryConsts.h"
#include "s25util/Log.h"
#include "s25util/Serializer.h"
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <algorithm>
#include <array>
#include <iostream>
#include <stdexcept>

namespace {
constexpr std::array<BuildingType, 4> MINE_BUILDING_TYPES = {BuildingType::GraniteMine, BuildingType::CoalMine,
BuildingType::IronMine, BuildingType::GoldMine};

helpers::OptionalEnum<BuildingType> GetMineBuildingTypeForAddonId(const AddonId id)
{
for(const BuildingType mineType : MINE_BUILDING_TYPES)
{
if(GetMineResourceBehaviorAddonId(mineType) == id)
return mineType;
}
return boost::none;
}

void MigrateLegacyInexhaustibleMines(GlobalGameSettings& settings,
const helpers::EnumArray<bool, BuildingType>& hasMineBehaviorSetting)
{
for(const BuildingType mineType : MINE_BUILDING_TYPES)
{
if(!hasMineBehaviorSetting[mineType])
settings.setSelection(GetMineResourceBehaviorAddonId(mineType),
static_cast<unsigned>(MineResourceBehavior::Inexhaustible));
}
}
} // namespace

GlobalGameSettings::GlobalGameSettings()
: speed(GameSpeed::Normal), objective(GameObjective::None), startWares(StartWares::Normal), lockedTeams(false),
exploration(Exploration::FogOfWar), teamView(true), randomStartPosition(false)
Expand Down Expand Up @@ -79,7 +108,10 @@ void GlobalGameSettings::registerAllAddons()
AddonHalfCostMilEquip,
AddonInexhaustibleFish,
AddonInexhaustibleGraniteMines,
AddonInexhaustibleMines,
AddonCoalMineResourceBehavior,
AddonIronMineResourceBehavior,
AddonGoldMineResourceBehavior,
AddonMineNoOutputFallback,
AddonLimitCatapults,
AddonManualRoadEnlargement,
AddonMaxRank,
Expand Down Expand Up @@ -187,8 +219,25 @@ void GlobalGameSettings::LoadSettings()
{
resetAddons();

bool migrateLegacyInexhaustibleMines = false;
helpers::EnumArray<bool, BuildingType> hasMineBehaviorSetting{};
for(const auto& it : SETTINGS.addons.configuration)
setSelection(static_cast<AddonId>(it.first), it.second);
{
const auto id = static_cast<AddonId>(it.first);
const unsigned status = it.second;
if(id == AddonId::INEXHAUSTIBLE_MINES)
{
migrateLegacyInexhaustibleMines = status != 0;
continue;
}

if(const auto mineType = GetMineBuildingTypeForAddonId(id))
hasMineBehaviorSetting[*mineType] = true;

setSelection(id, status);
}
if(migrateLegacyInexhaustibleMines)
MigrateLegacyInexhaustibleMines(*this, hasMineBehaviorSetting);
}

/**
Expand Down Expand Up @@ -243,12 +292,25 @@ void GlobalGameSettings::Deserialize(Serializer& ser)

resetAddons();

bool migrateLegacyInexhaustibleMines = false;
helpers::EnumArray<bool, BuildingType> hasMineBehaviorSetting{};
for(unsigned i = 0; i < count; ++i)
{
auto addon = static_cast<AddonId>(ser.PopUnsignedInt());
unsigned status = ser.PopUnsignedInt();
if(addon == AddonId::INEXHAUSTIBLE_MINES)
{
migrateLegacyInexhaustibleMines = status != 0;
continue;
}

if(const auto mineType = GetMineBuildingTypeForAddonId(addon))
hasMineBehaviorSetting[*mineType] = true;

setSelection(addon, status);
}
if(migrateLegacyInexhaustibleMines)
MigrateLegacyInexhaustibleMines(*this, hasMineBehaviorSetting);
}

void GlobalGameSettings::setSelection(AddonId id, unsigned selection)
Expand Down
13 changes: 8 additions & 5 deletions libs/s25main/addons/AddonInexhaustibleGraniteMines.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@

#pragma once

#include "AddonBool.h"
#include "AddonMineResourceBehavior.h"
#include "mygettext/mygettext.h"

/**
* Addon for allowing to have unlimited resources.
* Granite mine resource behavior list.
*
* Reuses the old boolean INEXHAUSTIBLE_GRANITEMINES id: saved value 0 still means default behavior and saved value 1
* now selects the inexhaustible behavior.
*/
class AddonInexhaustibleGraniteMines : public AddonBool
class AddonInexhaustibleGraniteMines : public AddonMineResourceBehaviorBase

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.

Please move this class to the other ones to avoid missing it. And you can rename the addon id to match the new ones if you keep the value.

{
public:
AddonInexhaustibleGraniteMines()
: AddonBool(AddonId::INEXHAUSTIBLE_GRANITEMINES, AddonGroup::Economy, _("Inexhaustible Granite Mines"),
_("Granite mines will never be depleted."))
: AddonMineResourceBehaviorBase(AddonId::INEXHAUSTIBLE_GRANITEMINES, _("Granite Mine Resource Behavior"),
_("Configures how granite mines consume and exhaust stone deposits."))
{}
};
6 changes: 5 additions & 1 deletion libs/s25main/addons/AddonInexhaustibleMines.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
#include "mygettext/mygettext.h"

/**
* Addon for allowing to have unlimited resources.
* Deprecated global mine setting.

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.

Add TODO(Replay) TODO(Savegame) so it can be found when updating

*
* Not registered anymore. The ID is still decoded when loading old
* settings/savegames and migrated to the per-mine
* resource behavior settings.
*/
class AddonInexhaustibleMines : public AddonBool
{
Expand Down
22 changes: 22 additions & 0 deletions libs/s25main/addons/AddonMineNoOutputFallback.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 "AddonList.h"
#include "const_addons.h"
#include "mygettext/mygettext.h"
#include "gameTypes/MineNoOutputFallback.h"

class AddonMineNoOutputFallback : public AddonList
{
public:
AddonMineNoOutputFallback()
: AddonList(AddonId::MINE_NO_OUTPUT_FALLBACK, AddonGroup::Economy, _("Mine No-Output Fallback"),
_("Configures what mines produce when S4-like exhaustion would produce nothing."),
{_("Produce nothing"), _("Produce granite 25%"), _("Produce granite 50%"),
_("Produce granite 100%"), _("Produce lower grade resource")},
static_cast<unsigned>(MineNoOutputFallback::ProduceNothing))
{}
};
48 changes: 48 additions & 0 deletions libs/s25main/addons/AddonMineResourceBehavior.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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 "const_addons.h"
#include "mygettext/mygettext.h"
#include "gameTypes/MineResourceBehavior.h"
#include <string>

class AddonMineResourceBehaviorBase : public AddonList
{
protected:
AddonMineResourceBehaviorBase(AddonId id, const std::string& name, const std::string& description)
: AddonList(id, AddonGroup::Economy, name, description,
{_("Default"), _("Inexhaustible"), _("S4-like exhaustion"), _("Work everywhere")},
static_cast<unsigned>(MineResourceBehavior::Default))
{}
};

class AddonCoalMineResourceBehavior : public AddonMineResourceBehaviorBase
{
public:
AddonCoalMineResourceBehavior()
: AddonMineResourceBehaviorBase(AddonId::COALMINE_RESOURCE_BEHAVIOR, _("Coal Mine Resource Behavior"),
_("Configures how coal mines consume and exhaust coal deposits."))
{}
};

class AddonIronMineResourceBehavior : public AddonMineResourceBehaviorBase
{
public:
AddonIronMineResourceBehavior()
: AddonMineResourceBehaviorBase(AddonId::IRONMINE_RESOURCE_BEHAVIOR, _("Iron Mine Resource Behavior"),
_("Configures how iron mines consume and exhaust iron deposits."))
{}
};

class AddonGoldMineResourceBehavior : public AddonMineResourceBehaviorBase
{
public:
AddonGoldMineResourceBehavior()
: AddonMineResourceBehaviorBase(AddonId::GOLDMINE_RESOURCE_BEHAVIOR, _("Gold Mine Resource Behavior"),
_("Configures how gold mines consume and exhaust gold deposits."))
{}
};
2 changes: 2 additions & 0 deletions libs/s25main/addons/Addons.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include "addons/AddonInexhaustibleGraniteMines.h"
#include "addons/AddonMaxRank.h"
#include "addons/AddonMilitaryAid.h"
#include "addons/AddonMineNoOutputFallback.h"
#include "addons/AddonMineResourceBehavior.h"
#include "addons/AddonSeaAttack.h"

#include "addons/AddonBattlefieldPromotion.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 @@ -38,6 +38,7 @@
//
// Add the #include for your AddonXXX.h in Addons.h!
//
// TODO: INEXHAUSTIBLE_MINES is kept only for loading old settings/savegames until the gamedata version can be raised.

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
// TODO: INEXHAUSTIBLE_MINES is kept only for loading old settings/savegames until the gamedata version can be raised.
// TODO(Replay) TODO(Savegame): INEXHAUSTIBLE_MINES is kept only for loading old settings/savegames until the gamedata version can be raised.

ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x00000001, REFUND_MATERIALS = 0x00000002,
EXHAUSTIBLE_WATER = 0x00000003, REFUND_ON_EMERGENCY = 0x00000004, MANUAL_ROAD_ENLARGEMENT = 0x00000005,
CATAPULT_GRAPHICS = 0x00000006, METALWORKSBEHAVIORONZERO = 0x00000007,
Expand All @@ -57,7 +58,9 @@ ENUM_WITH_STRING(AddonId, LIMIT_CATAPULTS = 0x00000000, INEXHAUSTIBLE_MINES = 0x

MILITARY_AID = 0x00700000,

INEXHAUSTIBLE_GRANITEMINES = 0x00800000,
INEXHAUSTIBLE_GRANITEMINES = 0x00800000, COALMINE_RESOURCE_BEHAVIOR = 0x00800001,
IRONMINE_RESOURCE_BEHAVIOR = 0x00800002, GOLDMINE_RESOURCE_BEHAVIOR = 0x00800003,
MINE_NO_OUTPUT_FALLBACK = 0x00800004,

MAX_RANK = 0x00900000, SEA_ATTACK = 0x00900001, INEXHAUSTIBLE_FISH = 0x00900002,
MORE_ANIMALS = 0x00900003, BURN_DURATION = 0x00900004, NO_ALLIED_PUSH = 0x00900005,
Expand Down
37 changes: 37 additions & 0 deletions libs/s25main/ai/AIInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "pathfinding/RoadPathFinder.h"
#include "nodeObjs/noFlag.h"
#include "nodeObjs/noTree.h"
#include "gameTypes/MineResourceBehavior.h"
#include "gameData/TerrainDesc.h"
#include <limits>
#include <numeric>
Expand Down Expand Up @@ -45,6 +46,14 @@ bool IsPointOK_RoadPathEvenStep(const GameWorldBase& gwb, const MapPoint pt, con
const auto* prp = static_cast<const Param_RoadPath*>(param);
return prp->boat_road || gwb.GetBQ(pt, gwb.GetNode(pt).owner - 1) != BuildingQuality::Nothing;
}

int GetS4LikeMineResourceRating(const Resource resource, const unsigned defaultRating)
{
if(resource.getAmount() == 0u)
return 0;

return std::max(1u, std::min(static_cast<unsigned>(resource.getAmount()), defaultRating));
}
} // namespace

AIInterface::AIInterface(const GameWorldBase& gwb, std::vector<gc::GameCommandPtr>& gcs, unsigned char playerID)
Expand Down Expand Up @@ -158,6 +167,23 @@ int AIInterface::GetResourceRating(const MapPoint pt, AIResource res) const
case AIResource::Ironore:
case AIResource::Coal:
case AIResource::Granite:
{
const Resource subres = gwb.GetNode(pt).resources;
if(convertToNodeResource(GetSubsurfaceResource(pt)) == res)
{
const auto mineBuildingType = GetMineBuildingType(subres.getType());
if(mineBuildingType
&& GetMineResourceBehavior(gwb.GetGGS(), *mineBuildingType)
== MineResourceBehavior::S4LikeExhaustion)
return GetS4LikeMineResourceRating(subres, RES_RADIUS[res]);

return RES_RADIUS[res];
}
if(IsMineResourceWorkEverywhere(res)
&& gwb.IsOfTerrain(pt, [](const TerrainDesc& desc) { return desc.Is(ETerrain::Mineable); }))
return RES_RADIUS[res];
break;
}
case AIResource::Fish:
if(convertToNodeResource(GetSubsurfaceResource(pt)) == res)
return RES_RADIUS[res];
Expand All @@ -166,6 +192,17 @@ int AIInterface::GetResourceRating(const MapPoint pt, AIResource res) const
return 0;
}

bool AIInterface::IsMineResourceWorkEverywhere(const AIResource res) const
{
const auto resourceType = convertToResourceType(res);
if(!resourceType)
return false;

const auto mineBuildingType = GetMineBuildingType(*resourceType);
return mineBuildingType
&& GetMineResourceBehavior(gwb.GetGGS(), *mineBuildingType) == MineResourceBehavior::WorkEverywhere;
}

int AIInterface::CalcResourceValue(const MapPoint pt, AIResource res, helpers::OptionalEnum<Direction> direction,
int lastval) const
{
Expand Down
2 changes: 2 additions & 0 deletions libs/s25main/ai/AIInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class AIInterface : public GameCommandFactory
int lastval = 0xffff) const;
/// Calculate the resource value for a given point
int GetResourceRating(MapPoint pt, AIResource res) const;
/// Check whether the given mine resource can be produced on otherwise empty mineable mountain.
bool IsMineResourceWorkEverywhere(AIResource res) const;
/// Test whether a given point is part of the border or not
bool IsBorder(const MapPoint pt) const
{
Expand Down
Loading