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

Return-To-The-Roots / s25client / 22044569181

15 Feb 2026 10:50PM UTC coverage: 50.34% (-0.5%) from 50.826%
22044569181

Pull #1720

github

web-flow
Merge 4dbe54b70 into 6db06730b
Pull Request #1720: Add leather addon

274 of 1055 new or added lines in 65 files covered. (25.97%)

286 existing lines in 28 files now uncovered.

23017 of 45723 relevant lines covered (50.34%)

43559.46 hits per line

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

60.47
/libs/s25main/buildings/noBaseBuilding.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 "noBaseBuilding.h"
6
#include "GameInterface.h"
7
#include "GamePlayer.h"
8
#include "GlobalGameSettings.h"
9
#include "Loader.h"
10
#include "SerializedGameData.h"
11
#include "Ware.h"
12
#include "addons/const_addons.h"
13
#include "nobBaseWarehouse.h"
14
#include "notifications/BuildingNote.h"
15
#include "world/GameWorld.h"
16
#include "nodeObjs/noExtension.h"
17
#include "nodeObjs/noFlag.h"
18
#include "gameData/BuildingConsts.h"
19
#include "gameData/DoorConsts.h"
20
#include "gameData/MapConsts.h"
21
#include "s25util/Log.h"
22

23
noBaseBuilding::noBaseBuilding(const NodalObjectType nop, const BuildingType type, const MapPoint pos,
650✔
24
                               const unsigned char player)
650✔
25
    : noRoadNode(nop, pos, player), bldType_(type), nation(world->GetPlayer(player).nation), door_point_x(1000000),
1,300✔
26
      door_point_y(DOOR_CONSTS[world->GetPlayer(player).nation][type])
650✔
27
{
28
    MapPoint flagPt = GetFlagPos();
650✔
29
    // Evtl Flagge setzen, wenn noch keine da ist
30
    if(world->GetNO(flagPt)->GetType() != NodalObjectType::Flag)
650✔
31
    {
32
        world->DestroyNO(flagPt, false);
594✔
33
        world->SetNO(flagPt, new noFlag(flagPt, player));
594✔
34
    }
35

36
    // Straßeneingang setzen (wenn nicht schon vorhanden z.b. durch vorherige Baustelle!)
37
    if(world->GetPointRoad(pos, Direction::SouthEast) == PointRoad::None)
650✔
38
    {
39
        world->SetPointRoad(pos, Direction::SouthEast, PointRoad::Normal);
644✔
40

41
        // Straßenverbindung erstellen zwischen Flagge und Haus
42
        // immer von Flagge ZU Gebäude (!)
43
        std::vector<Direction> route(1, Direction::NorthWest);
1,288✔
44
        // Straße zuweisen
45
        auto* rs = new RoadSegment(RoadType::Normal, world->GetSpecObj<noRoadNode>(flagPt), this, route);
644✔
46
        world->GetSpecObj<noRoadNode>(flagPt)->SetRoute(Direction::NorthWest, rs); // der Flagge
644✔
47
        SetRoute(Direction::SouthEast, rs);                                        // dem Gebäude
644✔
48
    } else
49
    {
50
        // vorhandene Straße der Flagge nutzen
51
        auto* flag = world->GetSpecObj<noFlag>(flagPt);
6✔
52

53
        RTTR_Assert(flag->GetRoute(Direction::NorthWest));
6✔
54
        SetRoute(Direction::SouthEast, flag->GetRoute(Direction::NorthWest));
6✔
55
        GetRoute(Direction::SouthEast)->SetF2(this);
6✔
56
    }
57

58
    // Werde/Bin ich (mal) ein großes Schloss? Dann müssen die Anbauten gesetzt werden
59
    if(GetSize() == BuildingQuality::Castle || GetSize() == BuildingQuality::Harbor)
650✔
60
    {
61
        for(const Direction i : {Direction::West, Direction::NorthWest, Direction::NorthEast})
1,676✔
62
        {
63
            const MapPoint neighbor = world->GetNeighbour(pos, i);
1,257✔
64

65
            if(type == BuildingType::Headquarters)
1,257✔
66
            {
67
                const NodalObjectType neighborNoType = world->GetNO(neighbor)->GetType();
1,062✔
68
                // Don't replace nearby static objects or trees. Needed for "build headquarters" cheat to work like in
69
                // the original. This situation shouldn't happen any other way (can't normally build big buildings right
70
                // next to static objects or trees). Trees which be remain because of this will be replaced by
71
                // extensions instead of stumps if they are cut while still right next to the HQ.
72
                if(neighborNoType == NodalObjectType::Object || neighborNoType == NodalObjectType::Tree)
1,062✔
73
                    continue;
×
74
            }
75

76
            world->DestroyNO(neighbor, false);
1,257✔
77
            world->SetNO(neighbor, new noExtension(this));
1,257✔
78
        }
79
    }
80
}
650✔
81

82
noBaseBuilding::~noBaseBuilding() = default;
661✔
83

84
void noBaseBuilding::Destroy()
114✔
85
{
86
    DestroyAllRoads();
114✔
87
    world->GetNotifications().publish(BuildingNote(BuildingNote::Destroyed, player, pos, bldType_));
114✔
88

89
    if(world->GetGameInterface())
114✔
90
        world->GetGameInterface()->GI_UpdateMinimap(pos);
×
91

92
    // evtl Anbauten wieder abreißen
93
    DestroyBuildingExtensions();
114✔
94

95
    // Baukosten zurückerstatten (nicht bei Baustellen)
96
    const GlobalGameSettings& settings = world->GetGGS();
114✔
97
    if((GetGOT() != GO_Type::Buildingsite)
114✔
98
       && (settings.isEnabled(AddonId::REFUND_MATERIALS) || settings.isEnabled(AddonId::REFUND_ON_EMERGENCY)))
114✔
99
    {
100
        // lebt unsere Flagge noch?
101
        noFlag* flag = GetFlag();
×
102
        if(flag)
×
103
        {
104
            unsigned percent_index = 0;
×
105

106
            // wenn Rückerstattung aktiv ist, entsprechende Prozentzahl wählen
107
            if(settings.isEnabled(AddonId::REFUND_MATERIALS))
×
108
                percent_index = settings.getSelection(AddonId::REFUND_MATERIALS);
×
109
            // wenn Rückerstattung bei Notprogramm aktiv ist, 50% zurückerstatten
110
            else if(world->GetPlayer(player).hasEmergency() && settings.isEnabled(AddonId::REFUND_ON_EMERGENCY))
×
111
                percent_index = 2;
×
112

113
            // wieviel kriegt man von jeder Ware wieder?
114
            const std::array<unsigned, 5> percents = {0, 25, 50, 75, 100};
×
115
            const unsigned percent = 10 * percents[percent_index];
×
116

117
            // zurückgaben berechnen (abgerundet)
118
            unsigned boards = (percent * BUILDING_COSTS[bldType_].boards) / 1000;
×
119
            unsigned stones = (percent * BUILDING_COSTS[bldType_].stones) / 1000;
×
120

121
            std::array<GoodType, 2> goods = {GoodType::Boards, GoodType::Stones};
×
122
            bool which = false;
×
123
            while(flag->HasSpaceForWare() && (boards > 0 || stones > 0))
×
124
            {
125
                if((!which && boards > 0) || (which && stones > 0))
×
126
                {
127
                    // Ware erzeugen
128
                    auto ware = std::make_unique<Ware>(goods[which], nullptr, flag);
×
129
                    ware->WaitAtFlag(flag);
×
130
                    // Inventur anpassen
131
                    world->GetPlayer(player).IncreaseInventoryWare(goods[which], 1);
×
132
                    // Abnehmer für Ware finden
133
                    ware->SetGoal(world->GetPlayer(player).FindClientForWare(*ware));
×
134
                    // Ware soll ihren weiteren Weg berechnen
135
                    ware->RecalcRoute();
×
136
                    // Ware ablegen
137
                    flag->AddWare(std::move(ware));
×
138

139
                    if(!which)
×
140
                        --boards;
×
141
                    else
142
                        --stones;
×
143
                }
144

145
                which = !which;
×
146
            }
147
        }
148
    }
149

150
    noRoadNode::Destroy();
114✔
151
}
114✔
152

153
void noBaseBuilding::Serialize(SerializedGameData& sgd) const
23✔
154
{
155
    noRoadNode::Serialize(sgd);
23✔
156

157
    sgd.PushEnum<uint8_t>(bldType_);
23✔
158
    sgd.PushEnum<uint8_t>(nation);
23✔
159
    sgd.PushSignedInt(door_point_x);
23✔
160
    sgd.PushSignedInt(door_point_y);
23✔
161
}
23✔
162

163
noBaseBuilding::noBaseBuilding(SerializedGameData& sgd, const unsigned obj_id)
11✔
164
    : noRoadNode(sgd, obj_id), bldType_(sgd.Pop<BuildingType>()), nation(sgd.Pop<Nation>()),
33✔
165
      door_point_x(sgd.PopSignedInt()), door_point_y(sgd.PopSignedInt())
11✔
166
{}
11✔
167

168
int noBaseBuilding::GetDoorPointX()
×
169
{
170
    // Did we calculate the door point yet?
171
    if(door_point_x == 1000000)
×
172
    {
173
        // The door is on the line between the building and flag point. The position of the line is set by the y-offset
174
        // this is why we need the x-offset here according to the equation x = m*y + n
175
        // with n=0 (as door point is relative to building pos) and m = dx/dy
176
        const Position bldPos = world->GetNodePos(pos);
×
177
        const Position flagPos = world->GetNodePos(GetFlagPos());
×
178
        Position diff = flagPos - bldPos;
×
179

180
        // We could have crossed the map border which results in unreasonable diffs
181
        // clamp the diff to [-w/2,w/2],[-h/2, h/2] (maximum diffs)
182
        const int mapWidth = world->GetWidth() * TR_W;
×
183
        const int mapHeight = world->GetHeight() * TR_H;
×
184

185
        if(diff.x < -mapWidth / 2)
×
186
            diff.x += mapWidth;
×
187
        else if(diff.x > mapWidth / 2)
×
188
            diff.x -= mapWidth;
×
189
        if(diff.y < -mapHeight / 2)
×
190
            diff.y += mapHeight;
×
191
        else if(diff.y > mapHeight / 2)
×
192
            diff.y -= mapHeight;
×
193

194
        door_point_x = (door_point_y * diff.x) / diff.y;
×
195
    }
196

197
    return door_point_x;
×
198
}
199

200
noFlag* noBaseBuilding::GetFlag() const
2,319✔
201
{
202
    return world->GetSpecObj<noFlag>(GetFlagPos());
2,319✔
203
}
204

205
MapPoint noBaseBuilding::GetFlagPos() const
3,720✔
206
{
207
    return world->GetNeighbour(pos, Direction::SouthEast);
3,720✔
208
}
209

210
bool noBaseBuilding::IsConnected() const
216✔
211
{
212
    // Harbors are always considered connected via sea
213
    if(GetBuildingType() == BuildingType::HarborBuilding)
216✔
214
        return true;
9✔
215

216
    const helpers::EnumArray<RoadSegment*, Direction>& routes = GetFlag()->getRoutes();
207✔
217
    // Check paths in all directions except back to building
218
    for(const auto dir : helpers::EnumRange<Direction>{})
2,884✔
219
    {
220
        if(dir != Direction::NorthWest && routes[dir])
1,074✔
221
            return true;
46✔
222
    }
223

224
    return false;
161✔
225
}
226

227
void noBaseBuilding::WareNotNeeded(Ware* ware)
21✔
228
{
229
    if(!ware)
21✔
230
    {
UNCOV
231
        RTTR_Assert(false);
×
232
        LOG.write("Warning: Trying to remove non-existing ware. Please report this replay!\n");
233
        return;
234
    }
235

236
    if(ware->IsWaitingInWarehouse())
21✔
237
    {
238
        // Bestellung im Lagerhaus stornieren
239
        world->GetPlayer(player).RemoveWare(*ware);
10✔
240
        static_cast<nobBaseWarehouse*>(ware->GetLocation())->CancelWare(ware);
10✔
241
    } else
242
        ware->GoalDestroyed();
11✔
243
}
244

245
void noBaseBuilding::DestroyBuildingExtensions()
114✔
246
{
247
    // Nur bei großen Gebäuden gibts diese Anbauten
248
    if(GetSize() == BuildingQuality::Castle || GetSize() == BuildingQuality::Harbor)
114✔
249
    {
250
        for(const Direction i : {Direction::West, Direction::NorthWest, Direction::NorthEast})
224✔
251
        {
252
            const MapPoint neighbor = world->GetNeighbour(pos, i);
168✔
253
            if(world->GetNO(neighbor)->GetType() == NodalObjectType::Extension)
168✔
254
                world->DestroyNO(neighbor);
168✔
255
        }
256
    }
257
}
114✔
258

259
BuildingQuality noBaseBuilding::GetSize() const
1,363✔
260
{
261
    return BUILDING_SIZE[bldType_];
1,363✔
262
}
263

264
BlockingManner noBaseBuilding::GetBM() const
14,519✔
265
{
266
    return BlockingManner::Building;
14,519✔
267
}
268

269
/// Gibt ein Bild zurück für das normale Gebäude
UNCOV
270
ITexture& noBaseBuilding::GetBuildingImage() const
×
271
{
UNCOV
272
    return GetBuildingImage(bldType_, nation);
×
273
}
274

UNCOV
275
ITexture& noBaseBuilding::GetBuildingImage(BuildingType type, Nation nation) //-V688
×
276
{
UNCOV
277
    return LOADER.building_cache[nation][type].building;
×
278
}
279

280
/// Gibt ein Bild zurück für die Tür des Gebäudes
UNCOV
281
ITexture& noBaseBuilding::GetDoorImage() const
×
282
{
UNCOV
283
    return LOADER.building_cache[nation][bldType_].door;
×
284
}
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