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

Return-To-The-Roots / s25client / 29855900885

21 Jul 2026 06:07PM UTC coverage: 50.461% (-0.003%) from 50.464%
29855900885

Pull #1959

github

web-flow
Merge 2b8574dd8 into 4b3146470
Pull Request #1959: Update submodules and resize save/load window

0 of 2 new or added lines in 1 file covered. (0.0%)

28 existing lines in 3 files now uncovered.

23281 of 46137 relevant lines covered (50.46%)

47545.56 hits per line

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

79.23
/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 <LeatherLoader.h>
20
#include <WineLoader.h>
21
#include <array>
22
#include <optional>
23
#include <vector>
24

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

32
    std::vector<GoodType> commonGoodPool = {
33
      GoodType::Beer,  GoodType::Water, GoodType::Boat,    GoodType::Sword,  GoodType::Iron,   GoodType::Flour,
34
      GoodType::Fish,  GoodType::Bread, GoodType::Wood,    GoodType::Boards, GoodType::Stones, GoodType::Grain,
35
      GoodType::Coins, GoodType::Gold,  GoodType::IronOre, GoodType::Coal,   GoodType::Meat,   GoodType::Ham};
6✔
36

37
    if(leatheraddon::isAddonActive(*world))
3✔
UNCOV
38
        commonGoodPool.insert(commonGoodPool.end(), {GoodType::Armor, GoodType::Skins, GoodType::Leather});
×
39

40
    if(wineaddon::isAddonActive(*world))
3✔
UNCOV
41
        commonGoodPool.insert(commonGoodPool.end(), {GoodType::Grapes, GoodType::Wine});
×
42

43
    constexpr unsigned numGoodTypesToCollect = 7;
3✔
44

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

47
    static_assert(numGoodTypesToCollect > 0, "There have to be goods to be collected");
48
    RTTR_Assert(commonGoodPool.size() >= numGoodTypesToCollect - 1);
3✔
49
    static_assert(!specialGoodPool.empty(), "There have to be enough special goods");
50
    goodsToCollect.reserve(numGoodTypesToCollect);
3✔
51

52
    while(goodsToCollect.size() < numGoodTypesToCollect - 1)
24✔
53
    {
54
        GoodType nextGoodType = RANDOM_ELEMENT(commonGoodPool);
21✔
55
        // No duplicates should be in goodsToCollect, so only add a good if it isn't one of the already found goods
56
        if(!helpers::contains(goodsToCollect, nextGoodType))
21✔
57
        {
58
            goodsToCollect.push_back(nextGoodType);
18✔
59
        }
60
    }
61
    goodsToCollect.push_back(RANDOM_ELEMENT(specialGoodPool));
3✔
62

63
    // Schedule end game event and trust the event manager to keep track of it
64
    if(!isInfinite())
3✔
65
        GetEvMgr().AddEvent(this, endFrame);
3✔
66

67
    // Find and set up Teams and trackers
68
    DetermineTeams();
3✔
69
    amountsThePlayersCollected.resize(goodsToCollect.size());
3✔
70
    maxAmountsATeamCollected.resize(goodsToCollect.size());
3✔
71

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

86
        world->GetPostMgr().SetMissionGoal(p, goalText);
9✔
87
    }
88
}
3✔
89

90
EconomyModeHandler::EconomyModeHandler(SerializedGameData& sgd, unsigned objId)
1✔
91
    : GameObject(sgd, objId), endFrame(sgd.PopUnsignedInt()), gfLastUpdated(0)
1✔
92
{
93
    helpers::popContainer(sgd, goodsToCollect);
1✔
94

95
    std::vector<unsigned> teamBitMasks;
2✔
96
    helpers::popContainer(sgd, teamBitMasks);
1✔
97
    for(const unsigned teamMask : teamBitMasks)
3✔
98
    {
99
        economyModeTeams.emplace_back(teamMask, goodsToCollect.size());
2✔
100
    }
101

102
    amountsThePlayersCollected.resize(goodsToCollect.size());
1✔
103
    maxAmountsATeamCollected.resize(goodsToCollect.size());
1✔
104
}
1✔
105

UNCOV
106
void EconomyModeHandler::Destroy() {}
×
107

108
/// Serialisierungsfunktion
109
void EconomyModeHandler::Serialize(SerializedGameData& sgd) const
1✔
110
{
111
    sgd.PushUnsignedInt(endFrame);
1✔
112
    helpers::pushContainer(sgd, goodsToCollect);
1✔
113
    std::vector<unsigned> teamBitMasks;
2✔
114
    teamBitMasks.reserve(economyModeTeams.size());
1✔
115
    for(const EconomyModeHandler::EconTeam& curTeam : economyModeTeams)
3✔
116
    {
117
        teamBitMasks.push_back(curTeam.playersInTeam.to_ulong());
2✔
118
    }
119
    helpers::pushContainer(sgd, teamBitMasks);
1✔
120
}
1✔
121

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

156
unsigned EconomyModeHandler::SumUpGood(GoodType good, const Inventory& Inventory)
84✔
157
{
158
    unsigned retVal = Inventory.goods[good];
84✔
159

160
    // Add the tools used by workers to the good totals
161
    for(const auto j : helpers::enumRange<Job>())
6,720✔
162
    {
163
        std::optional<GoodType> tool = JOB_CONSTS[j].tool;
3,192✔
164
        if(tool == good)
3,192✔
165
        {
166
            retVal += Inventory.people[j];
24✔
167
        }
168
    }
169
    // Add the weapons and beer used by soldiers to the good totals
170
    if(good == GoodType::Beer || good == GoodType::Sword || good == GoodType::ShieldRomans)
84✔
171
    {
UNCOV
172
        for(const auto& it : SOLDIER_JOBS)
×
173
        {
UNCOV
174
            retVal += Inventory.people[it];
×
175
        }
176
    }
177

178
    return retVal;
84✔
179
}
180

181
void EconomyModeHandler::UpdateAmounts()
4✔
182
{
183
    // Return if the game is over or we already updated the amounts this game frame
184
    if(isOver() || gfLastUpdated == GetEvMgr().GetCurrentGF())
4✔
185
    {
UNCOV
186
        return;
×
187
    }
188

189
    // Sum up goods
190
    for(unsigned i = 0; i < world->GetNumPlayers(); ++i)
16✔
191
    {
192
        const GamePlayer& player = world->GetPlayer(i);
12✔
193
        Inventory playerInventory = player.GetInventory();
12✔
194
        for(unsigned g = 0; g < goodsToCollect.size(); g++)
96✔
195
        {
196
            amountsThePlayersCollected[g][i] = SumUpGood(goodsToCollect[g], playerInventory);
84✔
197
        }
198
    }
199

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

238
    gfLastUpdated = GetEvMgr().GetCurrentGF();
4✔
239
}
240

UNCOV
241
void EconomyModeHandler::HandleEvent(const unsigned)
×
242
{
UNCOV
243
    if(isOver())
×
244
    {
245
        return;
×
246
    }
247

248
    // Handle game end event
249

250
    // Update one last time
UNCOV
251
    UpdateAmounts();
×
252

253
    // Determine bitmask of all players in teams with the most good type wins
UNCOV
254
    std::bitset<MAX_PLAYERS> bestMask;
×
255
    for(auto& team : economyModeTeams)
×
256
    {
UNCOV
257
        if(team.goodTypeWins == mostGoodTypeWins)
×
258
        {
UNCOV
259
            bestMask |= team.playersInTeam;
×
260
        }
261
    }
262

263
    // Let players know who won
UNCOV
264
    if(bestMask.count() > 1)
×
UNCOV
265
        world->GetGameInterface()->GI_TeamWinner(bestMask.to_ulong());
×
266
    else
267
        for(unsigned i = 0; i < world->GetNumPlayers(); ++i)
×
268
        {
UNCOV
269
            if(bestMask[i])
×
270
            {
UNCOV
271
                world->GetGameInterface()->GI_Winner(i);
×
272
            }
273
        }
274

UNCOV
275
    world->MakeWholeMapVisibleForAllPlayers();
×
276
    world->GetGameInterface()->GI_UpdateMapVisibility();
×
277
}
278

279
bool EconomyModeHandler::isOver() const
5✔
280
{
281
    return world->GetGGS().objective == GameObjective::EconomyMode && !isInfinite()
10✔
282
           && endFrame < GetEvMgr().GetCurrentGF();
10✔
283
}
284

UNCOV
285
EconomyModeHandler::EconTeam::EconTeam(SerializedGameData& sgd, unsigned numGoodTypesToCollect)
×
UNCOV
286
    : playersInTeam(sgd.PopSignedInt()), amountsTheTeamCollected(numGoodTypesToCollect, 0), goodTypeWins(0)
×
UNCOV
287
{}
×
288

UNCOV
289
void EconomyModeHandler::EconTeam::Serialize(SerializedGameData& sgd) const
×
290
{
UNCOV
291
    sgd.PushUnsignedInt(playersInTeam.to_ulong());
×
UNCOV
292
}
×
293

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

© 2026 Coveralls, Inc