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

Return-To-The-Roots / s25client / 20972065269

13 Jan 2026 08:48PM UTC coverage: 50.627% (+0.07%) from 50.558%
20972065269

push

github

Flow86
Fix clang-tidy warning

1 of 1 new or added line in 1 file covered. (100.0%)

238 existing lines in 6 files now uncovered.

22607 of 44654 relevant lines covered (50.63%)

36265.45 hits per line

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

66.05
/libs/s25main/Ware.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 "Ware.h"
6
#include "EventManager.h"
7
#include "GamePlayer.h"
8
#include "RoadSegment.h"
9
#include "SerializedGameData.h"
10
#include "buildings/noBaseBuilding.h"
11
#include "buildings/noBuilding.h"
12
#include "buildings/nobBaseWarehouse.h"
13
#include "buildings/nobHarborBuilding.h"
14
#include "world/GameWorld.h"
15
#include "nodeObjs/noFlag.h"
16
#include "nodeObjs/noRoadNode.h"
17
#include "gameData/BuildingProperties.h"
18
#include "gameData/GameConsts.h"
19
#include "gameData/GoodConsts.h"
20
#include "gameData/ShieldConsts.h"
21
#include "s25util/Log.h"
22
#include <sstream>
23

24
Ware::Ware(const GoodType type, noBaseBuilding* goal, noRoadNode* location)
145✔
25
    : next_dir(RoadPathDirection::None), state(State::WaitInWarehouse), location(location),
26
      type(convertShieldToNation(type,
145✔
27
                                 world->GetPlayer(location->GetPlayer()).nation)), // Use nation specific shield
145✔
28
      goal(goal), next_harbor(MapPoint::Invalid())
290✔
29
{
30
    RTTR_Assert(location);
145✔
31
    // Ware in den Index mit eintragen
32
    world->GetPlayer(location->GetPlayer()).RegisterWare(*this);
145✔
33
    if(goal)
145✔
34
        goal->TakeWare(this);
133✔
35
}
145✔
36

37
Ware::~Ware() = default;
290✔
38

39
void Ware::Destroy()
12✔
40
{
41
    RTTR_Assert(!goal);
12✔
42
    RTTR_Assert(!location);
12✔
43
#if RTTR_ENABLE_ASSERTS
44
    for(unsigned p = 0; p < world->GetNumPlayers(); p++)
26✔
45
    {
46
        RTTR_Assert(!world->GetPlayer(p).IsWareRegistred(*this));
14✔
47
        RTTR_Assert(!world->GetPlayer(p).IsWareDependent(*this));
14✔
48
    }
49
#endif
50
}
12✔
51

52
void Ware::Serialize(SerializedGameData& sgd) const
4✔
53
{
54
    sgd.PushEnum<uint8_t>(next_dir);
4✔
55
    sgd.PushEnum<uint8_t>(state);
4✔
56
    sgd.PushObject(location);
4✔
57
    sgd.PushEnum<uint8_t>(type);
4✔
58
    sgd.PushObject(goal);
4✔
59
    helpers::pushPoint(sgd, next_harbor);
4✔
60
}
4✔
61

62
static RoadPathDirection PopRoadPathDirection(SerializedGameData& sgd)
2✔
63
{
64
    if(sgd.GetGameDataVersion() < 5)
2✔
65
    {
66
        const auto iDir = sgd.PopUnsignedChar();
×
67
        if(iDir == 100)
×
68
            return RoadPathDirection::Ship;
×
69
        if(iDir == 0xFF)
×
70
            return RoadPathDirection::None;
×
71
        if(iDir > helpers::MaxEnumValue_v<Direction>)
×
72
            throw SerializedGameData::Error("Invalid RoadPathDirection");
×
73
        return RoadPathDirection(iDir);
×
74
    } else
75
        return sgd.Pop<RoadPathDirection>();
2✔
76
}
77

78
Ware::Ware(SerializedGameData& sgd, const unsigned obj_id)
2✔
79
    : GameObject(sgd, obj_id), next_dir(PopRoadPathDirection(sgd)), state(sgd.Pop<State>()),
6✔
80
      location(sgd.PopObject<noRoadNode>()), type(sgd.Pop<GoodType>()), goal(sgd.PopObject<noBaseBuilding>()),
4✔
81
      next_harbor(sgd.PopMapPoint())
6✔
82
{}
2✔
83

84
void Ware::SetGoal(noBaseBuilding* newGoal)
16✔
85
{
86
    goal = newGoal;
16✔
87
    if(goal)
16✔
88
        goal->TakeWare(this);
12✔
89
}
16✔
90

91
void Ware::RecalcRoute()
243✔
92
{
93
    // Nächste Richtung nehmen
94
    if(location && goal)
243✔
95
        next_dir = world->FindPathForWareOnRoads(*location, *goal, nullptr, &next_harbor);
243✔
96
    else
97
        next_dir = RoadPathDirection::None;
×
98

99
    // Evtl gibts keinen Weg mehr? Dann wieder zurück ins Lagerhaus (wenns vorher überhaupt zu nem Ziel ging)
100
    if(next_dir == RoadPathDirection::None && goal)
243✔
101
    {
102
        RTTR_Assert(location);
4✔
103
        // Tell goal about this
104
        NotifyGoalAboutLostWare();
4✔
105
        if(state == State::WaitForShip)
4✔
106
        {
107
            // Ware was waiting for a ship so send the ware into the harbor
108
            RTTR_Assert(location->GetGOT() == GO_Type::NobHarborbuilding);
1✔
109
            state = State::WaitInWarehouse;
1✔
110
            SetGoal(static_cast<nobHarborBuilding*>(location));
1✔
111
            // but not going by ship
112
            static_cast<nobHarborBuilding*>(goal)->WareDontWantToTravelByShip(this);
1✔
113
        } else
114
        {
115
            // TODO(Replay) This should calculate the next dir even when carried
116
            FindRouteToWarehouse();
3✔
117
        }
118
    } else
119
    {
120
        // If we waited in the harbor for the ship before and don't want to travel now
121
        // -> inform the harbor so that it can remove us from its list
122
        if(state == State::WaitForShip && next_dir != RoadPathDirection::Ship)
239✔
123
        {
124
            RTTR_Assert(location);
×
125
            RTTR_Assert(location->GetGOT() == GO_Type::NobHarborbuilding);
×
126
            state = State::WaitInWarehouse;
×
UNCOV
127
            static_cast<nobHarborBuilding*>(location)->WareDontWantToTravelByShip(this);
×
128
        }
129
    }
130
}
243✔
131

132
void Ware::GoalDestroyed()
11✔
133
{
134
    if(state == State::WaitInWarehouse)
11✔
135
    {
136
        // Ware ist noch im Lagerhaus auf der Warteliste
UNCOV
137
        RTTR_Assert(false); // Should not happen. noBaseBuilding::WareNotNeeded handles this case!
×
138
        goal = nullptr; // just in case: avoid corruption although the ware itself might be lost (won't ever be carried
139
                        // again)
140
    }
141
    // Ist sie evtl. gerade mit dem Schiff unterwegs?
142
    else if(state == State::OnShip)
11✔
143
    {
144
        // Ziel zunächst auf nullptr setzen, was dann vom Zielhafen erkannt wird,
145
        // woraufhin dieser die Ware gleich in sein Inventar mit übernimmt
146
        goal = nullptr;
4✔
147
    }
148
    // Oder wartet sie im Hafen noch auf ein Schiff
149
    else if(state == State::WaitForShip)
7✔
150
    {
151
        // Dann dem Hafen Bescheid sagen
152
        RTTR_Assert(location);
6✔
153
        RTTR_Assert(location->GetGOT() == GO_Type::NobHarborbuilding);
6✔
154
        // This also adds the ware to the harbors inventory
155
        auto ownedWare = static_cast<nobHarborBuilding*>(location)->CancelWareForShip(this);
6✔
156
        // Kill the ware
157
        world->GetPlayer(location->GetPlayer()).RemoveWare(*this);
6✔
158
        goal = nullptr;
6✔
159
        location = nullptr;
6✔
160
        GetEvMgr().AddToKillList(std::move(ownedWare));
6✔
161
    } else
162
    {
163
        // Ware ist unterwegs, Lagerhaus finden und Ware dort einliefern
164
        RTTR_Assert(location);
1✔
165
        RTTR_Assert(location->GetPlayer() < MAX_PLAYERS);
1✔
166

167
        // Wird sie gerade aus einem Lagerhaus rausgetragen?
168
        if(location->GetGOT() == GO_Type::NobStorehouse || location->GetGOT() == GO_Type::NobHarborbuilding
2✔
169
           || location->GetGOT() == GO_Type::NobHq)
2✔
170
        {
UNCOV
171
            if(location != goal)
×
172
            {
UNCOV
173
                SetGoal(static_cast<noBaseBuilding*>(location));
×
174
            } else // at the goal (which was just destroyed) and get carried out right now? -> we are about to get
175
                   // destroyed...
176
            {
177
                goal = nullptr;
×
UNCOV
178
                next_dir = RoadPathDirection::None;
×
179
            }
180
        }
181
        // Wenn sie an einer Flagge liegt, muss der Weg neu berechnet werden und dem Träger Bescheid gesagt werden
182
        else if(state == State::WaitAtFlag)
1✔
183
        {
184
            goal = nullptr;
1✔
185
            const auto oldNextDir = next_dir;
1✔
186
            FindRouteToWarehouse();
1✔
187
            if(oldNextDir != next_dir)
1✔
188
            {
189
                RemoveWareJobForDir(oldNextDir);
1✔
190
                if(next_dir != RoadPathDirection::None)
1✔
191
                {
192
                    RTTR_Assert(goal); // Having a nextDir implies having a goal
1✔
193
                    CallCarrier();
1✔
194
                } else
UNCOV
195
                    RTTR_Assert(!goal); // Can only have a goal with a valid path
×
196
            }
UNCOV
197
        } else if(state == State::Carried)
×
198
        {
UNCOV
199
            if(goal != location)
×
200
            {
201
                // find a warehouse for us (if we are entering a warehouse already set this as new goal (should only
202
                // happen if its a harbor for shipping as the building wasnt our goal))
203
                if(location->GetGOT() == GO_Type::NobStorehouse || location->GetGOT() == GO_Type::NobHarborbuilding
×
UNCOV
204
                   || location->GetGOT()
×
205
                        == GO_Type::NobHq) // currently carried into a warehouse? -> add ware (pathfinding
206
                                           // will not return this wh because of path lengths 0)
207
                {
208
                    if(location->GetGOT() != GO_Type::NobHarborbuilding)
×
UNCOV
209
                        LOG.write("WARNING: Ware::GoalDestroyed() -- ware is currently being carried into warehouse or "
×
210
                                  "hq that was not "
211
                                  "it's goal! ware id %i, type %s, player %i, wareloc %i,%i, goal loc %i,%i \n")
212
                          % GetObjId() % WARE_NAMES[type] % location->GetPlayer() % GetLocation()->GetX()
×
213
                          % GetLocation()->GetY() % goal->GetX() % goal->GetY();
×
UNCOV
214
                    SetGoal(static_cast<noBaseBuilding*>(location));
×
215
                } else
216
                {
217
                    goal = nullptr;
×
UNCOV
218
                    FindRouteToWarehouse();
×
219
                }
220
            } else
221
            {
222
                // too late to do anything our road will be removed and ware destroyed when the carrier starts walking
223
                // about
UNCOV
224
                goal = nullptr;
×
225
            }
226
        }
227
    }
228
}
11✔
229

230
void Ware::WaitAtFlag(noFlag* flag)
133✔
231
{
232
    RTTR_Assert(flag);
133✔
233
    state = State::WaitAtFlag;
133✔
234
    location = flag;
133✔
235
}
133✔
236

237
void Ware::WaitInWarehouse(nobBaseWarehouse* wh)
111✔
238
{
239
    RTTR_Assert(wh);
111✔
240
    state = State::WaitInWarehouse;
111✔
241
    location = wh;
111✔
242
}
111✔
243

244
void Ware::Carry(noRoadNode* nextGoal)
305✔
245
{
246
    RTTR_Assert(nextGoal);
305✔
247
    state = State::Carried;
305✔
248
    location = nextGoal;
305✔
249
}
305✔
250

251
/// Gibt dem Ziel der Ware bekannt, dass diese nicht mehr kommen kann
252
void Ware::NotifyGoalAboutLostWare()
12✔
253
{
254
    // Meinem Ziel Bescheid sagen, dass ich weg vom Fenster bin (falls ich ein Ziel habe!)
255
    if(goal)
12✔
256
    {
257
        goal->WareLost(*this);
10✔
258
        goal = nullptr;
10✔
259
        next_dir = RoadPathDirection::None;
10✔
260
    }
261
}
12✔
262

263
/// Wenn die Ware vernichtet werden muss
264
void Ware::WareLost(const unsigned char player)
6✔
265
{
266
    location = nullptr;
6✔
267
    // Inventur verringern
268
    world->GetPlayer(player).DecreaseInventoryWare(type, 1);
6✔
269
    // Ziel der Ware Bescheid sagen
270
    NotifyGoalAboutLostWare();
6✔
271
    // Zentrale Registrierung der Ware löschen
272
    world->GetPlayer(player).RemoveWare(*this);
6✔
273
}
6✔
274

275
void Ware::RemoveWareJobForDir(const RoadPathDirection last_next_dir)
1✔
276
{
277
    // last_next_dir war die letzte Richtung, in die die Ware eigentlich wollte,
278
    // aber nun nicht mehr will, deshalb muss dem Träger Bescheid gesagt werden
279

280
    // War's überhaupt ne richtige Richtung?
281
    if(last_next_dir == RoadPathDirection::None || last_next_dir == RoadPathDirection::Ship)
1✔
UNCOV
282
        return;
×
283
    Direction lastDir = toDirection(last_next_dir);
1✔
284
    // Existiert da noch ne Straße?
285
    if(!location->GetRoute(lastDir))
1✔
UNCOV
286
        return;
×
287
    // Den Trägern Bescheid sagen
288
    location->GetRoute(lastDir)->WareJobRemoved(nullptr);
1✔
289
    // Wenn nicht, könntes ja sein, dass die Straße in ein Lagerhaus führt, dann muss dort Bescheid gesagt werden
290
    if(location->GetRoute(lastDir)->GetF2()->GetType() == NodalObjectType::Building)
1✔
291
    {
292
        auto* bld = static_cast<noBuilding*>(location->GetRoute(Direction::NorthWest)->GetF2());
×
293
        if(BuildingProperties::IsWareHouse(bld->GetBuildingType()))
×
UNCOV
294
            static_cast<nobBaseWarehouse*>(bld)->DontFetchNextWare();
×
295
    }
296
}
297

298
void Ware::CallCarrier()
1✔
299
{
300
    RTTR_Assert(IsWaitingAtFlag());
1✔
301
    RTTR_Assert(next_dir != RoadPathDirection::None && next_dir != RoadPathDirection::Ship);
1✔
302
    RTTR_Assert(location);
1✔
303
    location->GetRoute(toDirection(next_dir))->AddWareJob(location);
1✔
304
}
1✔
305

306
bool Ware::FindRouteToWarehouse()
4✔
307
{
308
    RTTR_Assert(location);
4✔
309
    RTTR_Assert(!goal); // Goal should have been notified and therefore reset
4✔
310
    SetGoal(world->GetPlayer(location->GetPlayer()).FindWarehouseForWare(*this));
4✔
311

312
    if(goal)
4✔
313
    {
314
        // Find path if not already carried (will be called after arrival in that case)
315
        if(state != State::Carried)
3✔
316
        {
317
            if(location == goal)
1✔
UNCOV
318
                next_dir = RoadPathDirection::None; // Warehouse will detect this
×
319
            else
320
            {
321
                next_dir = world->FindPathForWareOnRoads(*location, *goal, nullptr, &next_harbor);
1✔
322
                RTTR_Assert(next_dir != RoadPathDirection::None);
1✔
323
            }
324
        }
325
    } else
326
        next_dir = RoadPathDirection::None; // Make sure we are not going anywhere
1✔
327
    return goal != nullptr;
4✔
328
}
329

330
/// a lost ware got ordered
UNCOV
331
unsigned Ware::CheckNewGoalForLostWare(const noBaseBuilding& newgoal) const
×
332
{
UNCOV
333
    if(!IsWaitingAtFlag()) // todo: check all special cases for wares being carried right now and where possible allow
×
334
                           // them to be ordered
335
        return 0xFFFFFFFF;
×
UNCOV
336
    return CalcPathToGoal(newgoal).length;
×
337
}
338

UNCOV
339
Ware::RouteParams Ware::CalcPathToGoal(const noBaseBuilding& newgoal) const
×
340
{
UNCOV
341
    RTTR_Assert(location);
×
342
    unsigned length;
343
    RoadPathDirection possibledir = world->FindPathForWareOnRoads(*location, newgoal, &length);
×
UNCOV
344
    if(possibledir != RoadPathDirection::None) // there is a valid path to the goal? -> ordered!
×
345
    {
346
        // in case the ware is right in front of the goal building the ware has to be moved away 1 flag and then back
347
        // because non-warehouses cannot just carry in new wares they need a helper to do this
UNCOV
348
        if(possibledir == RoadPathDirection::NorthWest && newgoal.GetFlagPos() == location->GetPos())
×
349
        {
350
            // Not executed for road from flag to the warehouse as that is handled directly by the warehouse
351
            RTTR_Assert(!BuildingProperties::IsWareHouse(newgoal.GetBuildingType()));
×
UNCOV
352
            for(const auto dir : helpers::EnumRange<Direction>{})
×
353
            {
354
                // Bounce of in this direction
355
                if(dir != Direction::NorthWest && location->GetRoute(dir))
×
UNCOV
356
                    return {1, toRoadPathDirection(dir)};
×
357
            }
358
            // got no other route from the flag -> impossible
UNCOV
359
            return {0xFFFFFFFF, RoadPathDirection::None};
×
360
        }
UNCOV
361
        return {length, possibledir};
×
362
    }
UNCOV
363
    return {0xFFFFFFFF, RoadPathDirection::None};
×
364
}
365

366
/// this assumes that the ware is at a flag (todo: handle carried wares) and that there is a valid path to the goal
UNCOV
367
void Ware::SetNewGoalForLostWare(noBaseBuilding* newgoal)
×
368
{
369
    RTTR_Assert(newgoal);
×
370
    const auto newDir = CalcPathToGoal(*newgoal).dir;
×
UNCOV
371
    if(newDir != RoadPathDirection::None) // there is a valid path to the goal? -> ordered!
×
372
    {
373
        next_dir = newDir;
×
374
        SetGoal(newgoal);
×
UNCOV
375
        CallCarrier();
×
376
    }
UNCOV
377
}
×
378

UNCOV
379
bool Ware::IsRouteToGoal()
×
380
{
381
    RTTR_Assert(location);
×
382
    if(!goal)
×
383
        return false;
×
384
    if(location == goal)
×
385
        return true; // We are at our goal. All ok
×
UNCOV
386
    return world->FindPathForWareOnRoads(*location, *goal) != RoadPathDirection::None;
×
387
}
388

389
/// Informiert Ware, dass eine Schiffsreise beginnt
390
void Ware::StartShipJourney()
5✔
391
{
392
    state = State::OnShip;
5✔
393
    location = nullptr;
5✔
394
}
5✔
395

396
/// Informiert Ware, dass Schiffsreise beendet ist und die Ware nun in einem Hafengebäude liegt
397
void Ware::ShipJorneyEnded(nobHarborBuilding* hb)
4✔
398
{
399
    RTTR_Assert(hb);
4✔
400
    state = State::WaitInWarehouse;
4✔
401
    location = hb;
4✔
402
}
4✔
403

404
/// Beginnt damit auf ein Schiff im Hafen zu warten
405
void Ware::WaitForShip(nobHarborBuilding* hb)
17✔
406
{
407
    RTTR_Assert(hb);
17✔
408
    state = State::WaitForShip;
17✔
409
    location = hb;
17✔
410
}
17✔
411

UNCOV
412
std::string Ware::ToString() const
×
413
{
414
    std::stringstream s;
×
415
    s << "Ware(" << GetObjId() << "): type=" << GoodType2String(type) << ", location=" << location->GetX() << ","
×
416
      << location->GetY();
×
UNCOV
417
    return s.str();
×
418
}
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