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

Return-To-The-Roots / s25client / 18080261965

28 Sep 2025 09:47PM UTC coverage: 50.484% (-0.01%) from 50.497%
18080261965

Pull #1803

github

web-flow
Merge 586ea43c1 into cbd82eb45
Pull Request #1803: Fixing crash in CampaignSelection

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

8 existing lines in 4 files now uncovered.

22511 of 44590 relevant lines covered (50.48%)

35310.79 hits per line

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

78.8
/libs/s25main/mapGenerator/RandomMap.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 "mapGenerator/RandomMap.h"
6
#include "mapGenerator/Harbors.h"
7
#include "mapGenerator/HeadQuarters.h"
8
#include "mapGenerator/Islands.h"
9
#include "mapGenerator/Resources.h"
10
#include "mapGenerator/Terrain.h"
11
#include "mapGenerator/TextureHelper.h"
12
#include "mapGenerator/Textures.h"
13

14
#include "lua/GameDataLoader.h"
15
#include "libsiedler2/libsiedler2.h"
16

17
#include <stdexcept>
18

19
namespace rttr::mapGenerator {
20

21
unsigned GetMaximumHeight(const MapExtent& size)
6✔
22
{
23
    const unsigned combinedSize = size.x * size.y;
6✔
24
    if(combinedSize <= 64 * 64)
6✔
25
        return 32;
3✔
26
    else if(combinedSize <= 128 * 128)
3✔
27
        return 64;
3✔
28
    else if(combinedSize <= 256 * 256)
×
29
        return 128;
×
30
    else if(combinedSize <= 512 * 512)
×
31
        return 150;
×
32
    else if(combinedSize <= 1024 * 1024)
×
33
        return 200;
×
34
    else
35
        return 60;
×
36
}
37

38
unsigned GetCoastline(const MapExtent& size)
6✔
39
{
40
    const unsigned combinedSize = size.x * size.y;
6✔
41
    if(combinedSize <= 128 * 128)
6✔
42
        return 1;
6✔
43
    else if(combinedSize <= 512 * 512)
×
44
        return 2;
×
45
    else if(combinedSize <= 1024 * 1024)
×
46
        return 3;
×
47
    else
48
        return 4;
×
49
}
50

51
unsigned GetIslandRadius(const MapExtent& size)
4✔
52
{
53
    const unsigned combinedSize = size.x * size.y;
4✔
54
    if(combinedSize <= 128 * 128)
4✔
55
        return 2;
4✔
56
    else if(combinedSize <= 256 * 256)
×
57
        return 3;
×
58
    else if(combinedSize <= 512 * 512)
×
59
        return 4;
×
60
    else if(combinedSize <= 1024 * 1024)
×
61
        return 5;
×
62
    else
63
        return 6;
×
64
}
65

66
unsigned GetIslandSize(const MapExtent& size)
4✔
67
{
68
    const unsigned combinedSize = size.x * size.y;
4✔
69
    if(combinedSize <= 64 * 64)
4✔
UNCOV
70
        return 200;
×
71
    else if(combinedSize <= 128 * 128)
4✔
72
        return 400;
4✔
73
    else if(combinedSize <= 256 * 256)
×
74
        return 600;
×
75
    else if(combinedSize <= 512 * 512)
×
76
        return 900;
×
77
    else
78
        return 1200;
×
79
}
80

81
unsigned GetSmoothRadius(const MapExtent& size)
6✔
82
{
83
    const unsigned combinedSize = size.x * size.y;
6✔
84
    if(combinedSize <= 128 * 128)
6✔
85
        return 2;
6✔
86
    else if(combinedSize <= 256 * 256)
×
87
        return 3;
×
88
    else if(combinedSize <= 512 * 512)
×
89
        return 4;
×
90
    else if(combinedSize <= 1024 * 1024)
×
91
        return 6;
×
92
    else
93
        return 7;
×
94
}
95

96
unsigned GetSmoothIterations(const MapExtent& size)
6✔
97
{
98
    const unsigned combinedSize = size.x * size.y;
6✔
99
    if(combinedSize <= 64 * 64)
6✔
100
        return 10;
3✔
101
    else if(combinedSize <= 128 * 128)
3✔
102
        return 11;
3✔
103
    else if(combinedSize <= 256 * 256)
×
104
        return 9;
×
105
    else if(combinedSize <= 512 * 512)
×
106
        return 12;
×
107
    else if(combinedSize <= 1024 * 1024)
×
108
        return 15;
×
109
    else
110
        return 13;
×
111
}
112

113
void SmoothHeightMap(NodeMapBase<uint8_t>& z, const ValueRange<uint8_t>& range)
6✔
114
{
115
    int radius = GetSmoothRadius(z.GetSize());
6✔
116
    int iterations = GetSmoothIterations(z.GetSize());
6✔
117

118
    Smooth(iterations, radius, z);
6✔
119
    Scale(z, range.minimum, range.maximum);
6✔
120
}
6✔
121

122
RandomMap::RandomMap(RandomUtility& rnd, Map& map)
6✔
123
    : rnd_(rnd), map_(map), texturizer_(map.z, map.getTextures(), map.textureMap)
6✔
124
{}
6✔
125

126
void RandomMap::Create(const MapSettings& settings)
6✔
127
{
128
    auto defaultHeight = map_.height.minimum + map_.height.GetDifference() / 2;
6✔
129

130
    settings_ = settings;
6✔
131
    map_.z.Resize(settings.size, defaultHeight);
6✔
132

133
    switch(settings.style)
6✔
134
    {
135
        case MapStyle::Water: CreateWaterMap(); break;
1✔
136

137
        case MapStyle::Mixed: CreateMixedMap(); break;
2✔
138

139
        case MapStyle::Land: CreateLandMap(); break;
3✔
140
    }
141

142
    AddObjects(map_, rnd_, settings_);
6✔
143
    AddResources(map_, rnd_, settings_);
6✔
144
    AddAnimals(map_, rnd_);
6✔
145
}
6✔
146

147
std::vector<River> RandomMap::CreateRivers(const MapPoint source)
6✔
148
{
149
    std::vector<River> rivers;
6✔
150

151
    const MapExtent size = settings_.size;
6✔
152
    const unsigned length = size.x + size.y;
6✔
153

154
    for(const auto dir : helpers::EnumRange<Direction>())
96✔
155
    {
156
        if(rnd_.ByChance(settings_.rivers))
36✔
157
        {
158
            const unsigned splitRate = rnd_.RandomValue(0u, 2u);
3✔
159
            rivers.push_back(
3✔
160
              CreateStream(rnd_, map_, source.isValid() ? source : rnd_.Point(map_.size), dir, length, splitRate));
6✔
161
        }
162
    }
163
    return rivers;
12✔
164
}
165

166
void RandomMap::CreateFreeIslands(unsigned waterNodes)
3✔
167
{
168
    const auto islandRadius = GetIslandRadius(map_.size);
3✔
169
    const auto islandAmount = static_cast<double>(settings_.islands) / 100;
3✔
170
    const auto maxIslandSize = GetIslandSize(map_.size);
3✔
171
    const auto minIslandSize = std::min(200u, maxIslandSize);
3✔
172
    auto islandNodes = static_cast<unsigned>(islandAmount * waterNodes);
3✔
173
    auto islandSize = rnd_.RandomValue(minIslandSize, maxIslandSize);
3✔
174
    while(islandNodes >= islandSize)
5✔
175
    {
176
        islandNodes -= islandSize;
2✔
177
        CreateIsland(map_, rnd_, islandSize, islandRadius, .2);
2✔
178
        islandSize = rnd_.RandomValue(minIslandSize, maxIslandSize);
2✔
179
    }
180
}
3✔
181

182
void RandomMap::CreateMixedMap()
2✔
183
{
184
    const auto center = rnd_.Point(map_.size);
2✔
185
    const unsigned maxDistance = map_.z.CalcMaxDistance();
2✔
186

187
    Restructure(map_, [this, &center, maxDistance](const MapPoint& pt) {
2✔
188
        auto weight = 1. - static_cast<float>(map_.z.CalcDistance(pt, center)) / maxDistance;
9,608✔
189
        auto percentage = static_cast<unsigned>(12 * weight);
9,608✔
190
        return rnd_.ByChance(percentage);
9,608✔
191
    });
192
    SmoothHeightMap(map_.z, map_.height);
2✔
193

194
    const double sea = 0.5;
2✔
195
    const double mountain = 0.1;
2✔
196
    const double land = 1. - sea - mountain;
2✔
197

198
    ResetSeaLevel(map_, rnd_, LimitFor(map_.z, sea, map_.height.minimum));
2✔
199

200
    const auto mountainLevel = LimitFor(map_.z, land, static_cast<uint8_t>(map_.height.minimum + 1)) + 1;
2✔
201
    const auto rivers = CreateRivers(center);
4✔
202
    const unsigned waterNodes = helpers::count(map_.z, map_.height.minimum);
2✔
203

204
    CreateFreeIslands(waterNodes);
2✔
205

206
    texturizer_.AddTextures(mountainLevel, GetCoastline(map_.size));
2✔
207

208
    PlaceHarbors(map_, rivers);
2✔
209
    PlaceHeadquarters(map_, rnd_, map_.players, settings_.mountainDistance);
2✔
210
}
2✔
211

212
void RandomMap::CreateWaterMap()
1✔
213
{
214
    const auto center = rnd_.Point(map_.size);
1✔
215
    const unsigned maxDistance = map_.z.CalcMaxDistance();
1✔
216

217
    Restructure(map_, [this, &center, maxDistance](const MapPoint& pt) {
1✔
218
        const auto weight = 1. - static_cast<float>(map_.z.CalcDistance(pt, center)) / maxDistance;
6,724✔
219
        const auto percentage = static_cast<unsigned>(15 * weight * weight);
6,724✔
220
        return rnd_.ByChance(percentage);
6,724✔
221
    });
222
    SmoothHeightMap(map_.z, map_.height);
1✔
223

224
    const double sea = 0.80;      // 20% of map is center island (100% - 80% water)
1✔
225
    const double mountain = 0.05; // 20% of center island is mountain (5% of 20% land)
1✔
226
    const auto seaLevel = LimitFor(map_.z, sea, map_.height.minimum);
1✔
227

228
    ResetSeaLevel(map_, rnd_, seaLevel);
1✔
229

230
    const unsigned waterNodes = helpers::count(map_.z, map_.height.minimum);
1✔
231
    const auto land = 1. - static_cast<double>(waterNodes) / (map_.size.x * map_.size.y) - mountain;
1✔
232
    const auto mountainLevel = LimitFor(map_.z, land, static_cast<uint8_t>(1)) + 1;
1✔
233
    const auto islandSize = GetIslandSize(map_.size);
1✔
234
    const auto islandRadius = GetIslandRadius(map_.size);
1✔
235

236
    std::vector<Island> islands(map_.players);
2✔
237

238
    for(unsigned i = 0; i < map_.players; i++)
4✔
239
    {
240
        islands[i] = CreateIsland(map_, rnd_, islandSize, islandRadius, .2);
3✔
241
    }
242

243
    if(waterNodes > map_.players * islandSize)
1✔
244
    {
245
        CreateFreeIslands(waterNodes - map_.players * islandSize);
1✔
246
    }
247

248
    const auto rivers = CreateRivers(center);
2✔
249

250
    texturizer_.AddTextures(mountainLevel, GetCoastline(map_.size));
1✔
251

252
    PlaceHarbors(map_, rivers);
1✔
253

254
    for(unsigned i = 0; i < map_.players; i++)
4✔
255
    {
256
        PlaceHeadquarter(map_, islands[i], settings_.mountainDistance);
3✔
257
    }
258
}
1✔
259

260
void RandomMap::CreateLandMap()
3✔
261
{
262
    Restructure(map_, [this](auto&&) { return rnd_.ByChance(5); });
8,243✔
263
    SmoothHeightMap(map_.z, map_.height);
3✔
264

265
    const double sea = rnd_.RandomDouble(0.1, 0.2);
3✔
266
    const double mountain = rnd_.RandomDouble(0.15, 0.4 - sea);
3✔
267
    const double land = 1. - sea - mountain;
3✔
268

269
    ResetSeaLevel(map_, rnd_, LimitFor(map_.z, sea, map_.height.minimum));
3✔
270

271
    const auto mountainLevel = LimitFor(map_.z, land, static_cast<uint8_t>(1)) + 1;
3✔
272
    CreateRivers();
3✔
273

274
    texturizer_.AddTextures(mountainLevel, GetCoastline(map_.size));
3✔
275

276
    PlaceHeadquarters(map_, rnd_, map_.players, settings_.mountainDistance);
3✔
277
}
3✔
278

279
Map GenerateRandomMap(RandomUtility& rnd, const WorldDescription& worldDesc, const MapSettings& settings)
6✔
280
{
281
    auto height = GetMaximumHeight(settings.size);
6✔
282
    Map map(settings.size, settings.numPlayers, worldDesc, settings.type, height);
6✔
283
    RandomMap randomMap(rnd, map);
12✔
284
    randomMap.Create(settings);
6✔
285
    return map;
12✔
286
}
287

288
void CreateRandomMap(const boost::filesystem::path& filePath, const MapSettings& settings)
1✔
289
{
290
    RandomUtility rnd;
1✔
291
    WorldDescription worldDesc;
2✔
292
    loadGameData(worldDesc);
1✔
293

294
    Map map = GenerateRandomMap(rnd, worldDesc, settings);
1✔
295
    libsiedler2::Write(filePath, map.CreateArchiv());
1✔
296
}
1✔
297

298
} // namespace rttr::mapGenerator
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