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

Return-To-The-Roots / s25client / 21285142390

23 Jan 2026 11:49AM UTC coverage: 50.761% (+0.1%) from 50.663%
21285142390

Pull #1679

github

web-flow
Merge b1ffe8192 into 12da8bf44
Pull Request #1679: Add all classic S2 cheats and a few more

94 of 155 new or added lines in 12 files covered. (60.65%)

8 existing lines in 6 files now uncovered.

22800 of 44916 relevant lines covered (50.76%)

41543.76 hits per line

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

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

36
    // Straßeneingang setzen (wenn nicht schon vorhanden z.b. durch vorherige Baustelle!)
37
    if(world->GetPointRoad(pos, Direction::SouthEast) == PointRoad::None)
604✔
38
    {
39
        world->SetPointRoad(pos, Direction::SouthEast, PointRoad::Normal);
598✔
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,196✔
44
        // Straße zuweisen
45
        auto* rs = new RoadSegment(RoadType::Normal, world->GetSpecObj<noRoadNode>(flagPt), this, route);
598✔
46
        world->GetSpecObj<noRoadNode>(flagPt)->SetRoute(Direction::NorthWest, rs); // der Flagge
598✔
47
        SetRoute(Direction::SouthEast, rs);                                        // dem Gebäude
598✔
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)
604✔
60
    {
61
        for(const Direction i : {Direction::West, Direction::NorthWest, Direction::NorthEast})
1,580✔
62
        {
63
            const MapPoint neighbor = world->GetNeighbour(pos, i);
1,185✔
64

65
            if(type == BuildingType::Headquarters)
1,185✔
66
            {
67
                const NodalObjectType neighborNoType = world->GetNO(neighbor)->GetType();
990✔
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)
990✔
NEW
73
                    continue;
×
74
            }
75

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

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

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

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

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

95
    // Baukosten zurückerstatten (nicht bei Baustellen)
96
    const GlobalGameSettings& settings = world->GetGGS();
111✔
97
    if((GetGOT() != GO_Type::Buildingsite)
111✔
98
       && (settings.isEnabled(AddonId::REFUND_MATERIALS) || settings.isEnabled(AddonId::REFUND_ON_EMERGENCY)))
111✔
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();
111✔
151
}
111✔
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
1,820✔
201
{
202
    return world->GetSpecObj<noFlag>(GetFlagPos());
1,820✔
203
}
204

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

210
void noBaseBuilding::WareNotNeeded(Ware* ware)
21✔
211
{
212
    if(!ware)
21✔
213
    {
214
        RTTR_Assert(false);
×
215
        LOG.write("Warning: Trying to remove non-existing ware. Please report this replay!\n");
216
        return;
217
    }
218

219
    if(ware->IsWaitingInWarehouse())
21✔
220
    {
221
        // Bestellung im Lagerhaus stornieren
222
        world->GetPlayer(player).RemoveWare(*ware);
10✔
223
        static_cast<nobBaseWarehouse*>(ware->GetLocation())->CancelWare(ware);
10✔
224
    } else
225
        ware->GoalDestroyed();
11✔
226
}
227

228
void noBaseBuilding::DestroyBuildingExtensions()
111✔
229
{
230
    // Nur bei großen Gebäuden gibts diese Anbauten
231
    if(GetSize() == BuildingQuality::Castle || GetSize() == BuildingQuality::Harbor)
111✔
232
    {
233
        for(const Direction i : {Direction::West, Direction::NorthWest, Direction::NorthEast})
224✔
234
        {
235
            const MapPoint neighbor = world->GetNeighbour(pos, i);
168✔
236
            if(world->GetNO(neighbor)->GetType() == NodalObjectType::Extension)
168✔
237
                world->DestroyNO(neighbor);
168✔
238
        }
239
    }
240
}
111✔
241

242
BuildingQuality noBaseBuilding::GetSize() const
1,284✔
243
{
244
    return BUILDING_SIZE[bldType_];
1,284✔
245
}
246

247
BlockingManner noBaseBuilding::GetBM() const
13,650✔
248
{
249
    return BlockingManner::Building;
13,650✔
250
}
251

252
/// Gibt ein Bild zurück für das normale Gebäude
253
ITexture& noBaseBuilding::GetBuildingImage() const
×
254
{
255
    return GetBuildingImage(bldType_, nation);
×
256
}
257

258
ITexture& noBaseBuilding::GetBuildingImage(BuildingType type, Nation nation) //-V688
×
259
{
260
    return LOADER.building_cache[nation][type].building;
×
261
}
262

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