• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 24041761791

06 Apr 2026 05:10PM UTC coverage: 50.37% (+0.03%) from 50.337%
24041761791

Pull #1910

github

web-flow
Merge 8d86aacd8 into e4146df45
Pull Request #1910: Fix wrongly shown soldiers & Refactor HQ start wares and inventory handling

197 of 409 new or added lines in 22 files covered. (48.17%)

96 existing lines in 25 files now uncovered.

23067 of 45795 relevant lines covered (50.37%)

43239.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.0
/libs/s25main/EconomyModeHandler.cpp
1
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "EconomyModeHandler.h"
6
#include "EventManager.h"
7
#include "GameInterface.h"
8
#include "GamePlayer.h"
9
#include "GlobalGameSettings.h"
10
#include "SerializedGameData.h"
11
#include "helpers/containerUtils.h"
12
#include "helpers/make_array.h"
13
#include "helpers/serializeContainers.h"
14
#include "random/Random.h"
15
#include "world/GameWorld.h"
16
#include "gameTypes/JobTypes.h"
17
#include "gameData/GoodConsts.h"
18
#include "gameData/JobConsts.h"
19
#include <boost/optional.hpp>
20
#include <array>
21

22
EconomyModeHandler::EconomyModeHandler(unsigned endFrame) : endFrame(endFrame), gfLastUpdated(0)
3✔
23
{
24
    constexpr auto specialGoodPool =
3✔
25
      helpers::make_array(GoodType::Tongs, GoodType::Hammer, GoodType::Axe, GoodType::Saw, GoodType::PickAxe,
26
                          GoodType::Shovel, GoodType::Crucible, GoodType::RodAndLine, GoodType::Scythe,
27
                          GoodType::Cleaver, GoodType::Rollingpin, GoodType::Bow);
28

29
    constexpr auto commonGoodPool = helpers::make_array(
3✔
30
      GoodType::Beer, GoodType::Water, GoodType::Boat, GoodType::Sword, GoodType::Iron, GoodType::Flour, GoodType::Fish,
31
      GoodType::Bread, GoodType::Wood, GoodType::Boards, GoodType::Stones, GoodType::Grain, GoodType::Coins,
32
      GoodType::Gold, GoodType::IronOre, GoodType::Coal, GoodType::Meat, GoodType::Ham);
33

34
    constexpr unsigned numGoodTypesToCollect = 7;
3✔
35

36
    // Randomly determine *numGoodTypesToCollect* many good types, one of which is a special good (=tool)
37

38
    static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected");
39
    static_assert(commonGoodPool.size() >= numGoodTypesToCollect - 1, "There have to be enough commond goods");
40
    static_assert(!specialGoodPool.empty(), "There have to be enough special goods");
41
    goodsToCollect.reserve(numGoodTypesToCollect);
3✔
42

43
    while(goodsToCollect.size() < numGoodTypesToCollect - 1)
24✔
44
    {
45
        GoodType nextGoodType = RANDOM_ELEMENT(commonGoodPool);
21✔
46
        // No duplicates should be in goodsToCollect, so only add a good if it isn't one of the already found goods
47
        if(!helpers::contains(goodsToCollect, nextGoodType))
21✔
48
        {
49
            goodsToCollect.push_back(nextGoodType);
18✔
50
        }
51
    }
52
    goodsToCollect.push_back(RANDOM_ELEMENT(specialGoodPool));
3✔
53

54
    // Schedule end game event and trust the event manager to keep track of it
55
    if(!isInfinite())
3✔
56
        GetEvMgr().AddEvent(this, endFrame);
3✔
57

58
    // Find and set up Teams and trackers
59
    DetermineTeams();
3✔
60
    amountsThePlayersCollected.resize(goodsToCollect.size());
3✔
61
    maxAmountsATeamCollected.resize(goodsToCollect.size());
3✔
62

63
    // Send Mission Goal
64
    for(unsigned p = 0; p < world->GetNumPlayers(); ++p)
12✔
65
    {
66
        std::string goalText = _("Economy Mode: Collect as much as you can of the following good types: ");
18✔
67
        for(unsigned i = 0; i < numGoodTypesToCollect; i++)
72✔
68
        {
69
            if(i > 0)
63✔
70
                goalText += ", ";
54✔
71
            goalText += _(WARE_NAMES[goodsToCollect[i]]);
63✔
72
        }
73
        goalText += ". ";
9✔
74
        goalText += _("Tools in the hands of workers are also counted. So are weapons, and beer, that soldiers have in "
75
                      "use. For an updating tally of the collected goods see the economic progress window.");
9✔
76

77
        world->GetPostMgr().SetMissionGoal(p, goalText);
9✔
78
    }
79
}
3✔
80

81
EconomyModeHandler::EconomyModeHandler(SerializedGameData& sgd, unsigned objId)
1✔
82
    : GameObject(sgd, objId), endFrame(sgd.PopUnsignedInt()), gfLastUpdated(0)
1✔
83
{
84
    helpers::popContainer(sgd, goodsToCollect);
1✔
85

86
    std::vector<unsigned> teamBitMasks;
2✔
87
    helpers::popContainer(sgd, teamBitMasks);
1✔
88
    for(const unsigned teamMask : teamBitMasks)
3✔
89
    {
90
        economyModeTeams.emplace_back(teamMask, goodsToCollect.size());
2✔
91
    }
92

93
    amountsThePlayersCollected.resize(goodsToCollect.size());
1✔
94
    maxAmountsATeamCollected.resize(goodsToCollect.size());
1✔
95
}
1✔
96

97
void EconomyModeHandler::Destroy() {}
×
98

99
/// Serialisierungsfunktion
100
void EconomyModeHandler::Serialize(SerializedGameData& sgd) const
1✔
101
{
102
    sgd.PushUnsignedInt(endFrame);
1✔
103
    helpers::pushContainer(sgd, goodsToCollect);
1✔
104
    std::vector<unsigned> teamBitMasks;
2✔
105
    teamBitMasks.reserve(economyModeTeams.size());
1✔
106
    for(const EconomyModeHandler::EconTeam& curTeam : economyModeTeams)
3✔
107
    {
108
        teamBitMasks.push_back(curTeam.playersInTeam.to_ulong());
2✔
109
    }
110
    helpers::pushContainer(sgd, teamBitMasks);
1✔
111
}
1✔
112

113
void EconomyModeHandler::DetermineTeams()
3✔
114
{
115
    RTTR_Assert(economyModeTeams.empty());
3✔
116
    for(unsigned i = 0; i < world->GetNumPlayers(); ++i)
12✔
117
    {
118
        if(world->GetPlayer(i).isUsed())
9✔
119
        {
120
            bool foundTeam = false;
9✔
121
            for(const auto& team : economyModeTeams)
12✔
122
            {
123
                if(team.containsPlayer(i))
6✔
124
                {
125
                    foundTeam = true;
3✔
126
                    break;
3✔
127
                }
128
            }
129
            if(!foundTeam)
9✔
130
            {
131
                const GamePlayer& player = world->GetPlayer(i);
6✔
132
                std::bitset<MAX_PLAYERS> newTeam;
6✔
133
                newTeam.set(i);
6✔
134
                for(unsigned j = i + 1; j < world->GetNumPlayers(); ++j)
15✔
135
                {
136
                    if(world->GetPlayer(j).isUsed() && player.IsAlly(j))
9✔
137
                    {
138
                        newTeam.set(j);
3✔
139
                    }
140
                }
141
                economyModeTeams.emplace_back(newTeam, goodsToCollect.size());
6✔
142
            }
143
        }
144
    }
145
}
3✔
146

147
unsigned EconomyModeHandler::SumUpGood(GoodType good, const Inventory& Inventory)
84✔
148
{
149
    unsigned retVal = Inventory.goods[good];
84✔
150

151
    // Add the tools used by workers to the good totals
152
    for(const auto j : helpers::enumRange<Job>())
6,720✔
153
    {
154
        boost::optional<GoodType> tool = JOB_CONSTS[j].tool;
3,192✔
155
        if(tool == good)
3,192✔
156
        {
157
            retVal += Inventory.people[j];
24✔
158
        }
159
    }
160
    // Add the weapons and beer used by soldiers to the good totals
161
    if(good == GoodType::Beer || good == GoodType::Sword || good == GoodType::ShieldRomans)
84✔
162
    {
UNCOV
163
        for(const auto& it : SOLDIER_JOBS)
×
164
        {
UNCOV
165
            retVal += Inventory.people[it];
×
166
        }
167
    }
168

169
    return retVal;
84✔
170
}
171

172
void EconomyModeHandler::UpdateAmounts()
4✔
173
{
174
    // Return if the game is over or we already updated the amounts this game frame
175
    if(isOver() || gfLastUpdated == GetEvMgr().GetCurrentGF())
4✔
176
    {
177
        return;
×
178
    }
179

180
    // Sum up goods
181
    for(unsigned i = 0; i < world->GetNumPlayers(); ++i)
16✔
182
    {
183
        const GamePlayer& player = world->GetPlayer(i);
12✔
184
        Inventory playerInventory = player.GetInventory();
12✔
185
        for(unsigned g = 0; g < goodsToCollect.size(); g++)
96✔
186
        {
187
            amountsThePlayersCollected[g][i] = SumUpGood(goodsToCollect[g], playerInventory);
84✔
188
        }
189
    }
190

191
    // Compute the amounts for the teams
192
    std::fill(maxAmountsATeamCollected.begin(), maxAmountsATeamCollected.end(), 0);
4✔
193
    for(auto& team : economyModeTeams)
12✔
194
    {
195
        std::fill(team.amountsTheTeamCollected.begin(), team.amountsTheTeamCollected.end(), 0);
8✔
196
        for(unsigned i = 0; i < world->GetNumPlayers(); ++i)
32✔
197
        {
198
            if(team.containsPlayer(i))
24✔
199
            {
200
                for(unsigned g = 0; g < goodsToCollect.size(); g++)
96✔
201
                {
202
                    team.amountsTheTeamCollected[g] += GetAmount(g, i);
84✔
203
                    if(team.amountsTheTeamCollected[g] > maxAmountsATeamCollected[g])
84✔
204
                    {
205
                        maxAmountsATeamCollected[g] = team.amountsTheTeamCollected[g];
6✔
206
                    }
207
                }
208
            }
209
        }
210
    }
211
    // Determine the leading teams for each good type and determine how many good type wins is the maximum.
212
    mostGoodTypeWins = 0;
4✔
213
    for(auto& team : economyModeTeams)
12✔
214
    {
215
        team.goodTypeWins = 0;
8✔
216
        for(unsigned g = 0; g < goodsToCollect.size(); g++)
64✔
217
        {
218
            if(team.amountsTheTeamCollected[g] >= maxAmountsATeamCollected[g])
56✔
219
            {
220
                team.goodTypeWins++;
53✔
221
                if(team.goodTypeWins > mostGoodTypeWins)
53✔
222
                {
223
                    mostGoodTypeWins = team.goodTypeWins;
28✔
224
                }
225
            }
226
        }
227
    }
228

229
    gfLastUpdated = GetEvMgr().GetCurrentGF();
4✔
230
}
231

232
void EconomyModeHandler::HandleEvent(const unsigned)
×
233
{
234
    if(isOver())
×
235
    {
236
        return;
×
237
    }
238

239
    // Handle game end event
240

241
    // Update one last time
242
    UpdateAmounts();
×
243

244
    // Determine bitmask of all players in teams with the most good type wins
245
    std::bitset<MAX_PLAYERS> bestMask;
×
246
    for(auto& team : economyModeTeams)
×
247
    {
248
        if(team.goodTypeWins == mostGoodTypeWins)
×
249
        {
250
            bestMask |= team.playersInTeam;
×
251
        }
252
    }
253

254
    // Let players know who won
255
    if(bestMask.count() > 1)
×
256
        world->GetGameInterface()->GI_TeamWinner(bestMask.to_ulong());
×
257
    else
258
        for(unsigned i = 0; i < world->GetNumPlayers(); ++i)
×
259
        {
260
            if(bestMask[i])
×
261
            {
262
                world->GetGameInterface()->GI_Winner(i);
×
263
            }
264
        }
265

266
    world->MakeWholeMapVisibleForAllPlayers();
×
267
    world->GetGameInterface()->GI_UpdateMapVisibility();
×
268
}
269

270
bool EconomyModeHandler::isOver() const
5✔
271
{
272
    return world->GetGGS().objective == GameObjective::EconomyMode && !isInfinite()
10✔
273
           && endFrame < GetEvMgr().GetCurrentGF();
10✔
274
}
275

276
EconomyModeHandler::EconTeam::EconTeam(SerializedGameData& sgd, unsigned numGoodTypesToCollect)
×
277
    : playersInTeam(sgd.PopSignedInt()), amountsTheTeamCollected(numGoodTypesToCollect, 0), goodTypeWins(0)
×
278
{}
×
279

280
void EconomyModeHandler::EconTeam::Serialize(SerializedGameData& sgd) const
×
281
{
282
    sgd.PushUnsignedInt(playersInTeam.to_ulong());
×
283
}
×
284

285
bool EconomyModeHandler::EconTeam::containsPlayer(unsigned playerId) const
33✔
286
{
287
    return playersInTeam[playerId];
33✔
288
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc