1 /* Copyright (C) 2013-2014 Michal Brzozowski (rusolis@poczta.fm)
2
3 This file is part of KeeperRL.
4
5 KeeperRL is free software; you can redistribute it and/or modify it under the terms of the
6 GNU General Public License as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 KeeperRL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along with this program.
14 If not, see http://www.gnu.org/licenses/ . */
15
16 #include "stdafx.h"
17
18 #include "level_maker.h"
19 #include "item_factory.h"
20 #include "square.h"
21 #include "collective_builder.h"
22 #include "collective.h"
23 #include "shortest_path.h"
24 #include "creature.h"
25 #include "level_builder.h"
26 #include "model.h"
27 #include "monster_ai.h"
28 #include "item.h"
29 #include "view_id.h"
30 #include "furniture_type.h"
31 #include "furniture_factory.h"
32 #include "furniture.h"
33 #include "progress.h"
34 #include "file_path.h"
35 #include "movement_set.h"
36 #include "container_range.h"
37 #include "settlement_info.h"
38 #include "task.h"
39
40 namespace {
41
failGen()42 void failGen() {
43 throw LevelGenException();
44 }
45
checkGen(bool b)46 void checkGen(bool b) {
47 if (!b)
48 failGen();
49 }
50
51 class Predicate {
52 public:
apply(LevelBuilder * builder,Vec2 pos) const53 bool apply(LevelBuilder* builder, Vec2 pos) const {
54 return predFun(builder, pos);
55 }
56
getRandomPosition(LevelBuilder * builder,Rectangle area)57 Vec2 getRandomPosition(LevelBuilder* builder, Rectangle area) {
58 vector<Vec2> good;
59 for (Vec2 v : area)
60 if (apply(builder, v))
61 good.push_back(v);
62 if (good.empty())
63 failGen();
64 return builder->getRandom().choose(good);
65 }
66
attrib(SquareAttrib attr)67 static Predicate attrib(SquareAttrib attr) {
68 return Predicate([=] (LevelBuilder* builder, Vec2 pos) { return builder->hasAttrib(pos, attr);});
69 }
70
operator !() const71 Predicate operator !() const {
72 PredFun self(predFun);
73 return Predicate([self] (LevelBuilder* builder, Vec2 pos) { return !self(builder, pos);});
74 }
75
operator &&(const Predicate & p1) const76 Predicate operator && (const Predicate& p1) const {
77 PredFun self(predFun);
78 return Predicate([self, p1] (LevelBuilder* builder, Vec2 pos) {
79 return p1.apply(builder, pos) && self(builder, pos);});
80 }
81
operator ||(const Predicate & p1) const82 Predicate operator || (const Predicate& p1) const {
83 PredFun self(predFun);
84 return Predicate([=] (LevelBuilder* builder, Vec2 pos) {
85 return p1.apply(builder, pos) || self(builder, pos);});
86 }
87
type(FurnitureType t)88 static Predicate type(FurnitureType t) {
89 return Predicate([=] (LevelBuilder* builder, Vec2 pos) {
90 return builder->isFurnitureType(pos, t);});
91 }
92
inRectangle(Rectangle r)93 static Predicate inRectangle(Rectangle r) {
94 return Predicate([=] (LevelBuilder* builder, Vec2 pos) {
95 return pos.inRectangle(r);});
96 }
97
alwaysTrue()98 static Predicate alwaysTrue() {
99 return Predicate([=] (LevelBuilder* builder, Vec2 pos) { return true;});
100 }
101
alwaysFalse()102 static Predicate alwaysFalse() {
103 return Predicate([=] (LevelBuilder* builder, Vec2 pos) { return false;});
104 }
105
canEnter(MovementType m)106 static Predicate canEnter(MovementType m) {
107 return Predicate([=] (LevelBuilder* builder, Vec2 pos) { return builder->canNavigate(pos, m);});
108 }
109
110 private:
111 typedef function<bool(LevelBuilder*, Vec2)> PredFun;
Predicate(PredFun fun)112 Predicate(PredFun fun) : predFun(fun) {}
113 PredFun predFun;
114 };
115
116 class SquareChange {
117 public:
none()118 static SquareChange none() {
119 return SquareChange([](LevelBuilder*, Vec2) {});
120 }
121
add(SquareChange added)122 SquareChange& add(SquareChange added) {
123 auto funCopy = changeFun; // copy just the function because storing this leads to a crash
124 changeFun = [added, funCopy] (LevelBuilder* builder, Vec2 pos) {
125 funCopy(builder, pos); added.changeFun(builder, pos); };
126 return *this;
127 }
128
SquareChange(FurnitureType type,optional<SquareAttrib> attrib=::none)129 SquareChange(FurnitureType type, optional<SquareAttrib> attrib = ::none)
130 : changeFun([=](LevelBuilder* builder, Vec2 pos) {
131 builder->putFurniture(pos, type);
132 if (attrib)
133 builder->addAttrib(pos, *attrib);
134 }) {}
135
SquareChange(SquareAttrib attrib)136 SquareChange(SquareAttrib attrib)
137 : changeFun([=](LevelBuilder* builder, Vec2 pos) {
138 builder->addAttrib(pos, attrib);
139 }) {}
140
SquareChange(FurnitureType f1,FurnitureType f2)141 SquareChange(FurnitureType f1, FurnitureType f2)
142 : changeFun([=](LevelBuilder* builder, Vec2 pos) {
143 builder->putFurniture(pos, f1);
144 builder->putFurniture(pos, f2); }) {}
145
reset(FurnitureType f1)146 static SquareChange reset(FurnitureType f1) {
147 return SquareChange([=](LevelBuilder* builder, Vec2 pos) {
148 builder->resetFurniture(pos, f1);
149 });
150 }
151
apply(LevelBuilder * builder,Vec2 pos)152 void apply(LevelBuilder* builder, Vec2 pos) {
153 changeFun(builder, pos);
154 }
155
156 private:
157 typedef function<void(LevelBuilder*, Vec2)> ChangeFun;
SquareChange(ChangeFun fun)158 SquareChange(ChangeFun fun) : changeFun(fun) {}
159 ChangeFun changeFun;
160
161 };
162
163 class Empty : public LevelMaker {
164 public:
Empty(SquareChange s)165 Empty(SquareChange s) : square(s) {}
166
make(LevelBuilder * builder,Rectangle area)167 virtual void make(LevelBuilder* builder, Rectangle area) override {
168 for (Vec2 v : area)
169 square.apply(builder, v);
170 }
171
172 private:
173 SquareChange square;
174 };
175
176 class RoomMaker : public LevelMaker {
177 public:
RoomMaker(int _numRooms,int _minSize,int _maxSize,SquareChange wall=SquareChange::none (),optional<FurnitureType> _onType=none,PLevelMaker _roomContents=unique<Empty> (FurnitureType::FLOOR),vector<PLevelMaker> _insideMakers={},bool _diggableCorners=false)178 RoomMaker(int _numRooms,
179 int _minSize, int _maxSize,
180 SquareChange wall = SquareChange::none(),
181 optional<FurnitureType> _onType = none,
182 PLevelMaker _roomContents = unique<Empty>(FurnitureType::FLOOR),
183 vector<PLevelMaker> _insideMakers = {},
184 bool _diggableCorners = false) :
185 numRooms(_numRooms),
186 minSize(_minSize),
187 maxSize(_maxSize),
188 wallChange(wall.add(SquareChange(SquareAttrib::ROOM_WALL))),
189 onType(_onType),
190 roomContents(std::move(_roomContents)),
191 insideMakers(std::move(_insideMakers)),
192 diggableCorners(_diggableCorners) {}
193
make(LevelBuilder * builder,Rectangle area)194 virtual void make(LevelBuilder* builder, Rectangle area) override {
195 int spaceBetween = 0;
196 Table<int> taken(area.right(), area.bottom());
197 for (Vec2 v : area)
198 taken[v] = onType && !builder->isFurnitureType(v, *onType);
199 for (int i : Range(numRooms)) {
200 Vec2 p, k;
201 bool good;
202 int cnt = 100;
203 do {
204 k = Vec2(builder->getRandom().get(minSize, maxSize), builder->getRandom().get(minSize, maxSize));
205 p = Vec2(area.left() + spaceBetween + builder->getRandom().get(area.width() - k.x - 2 * spaceBetween),
206 area.top() + spaceBetween + builder->getRandom().get(area.height() - k.y - 2 * spaceBetween));
207 good = true;
208 for (Vec2 v : Rectangle(k.x + 2 * spaceBetween, k.y + 2 * spaceBetween))
209 if (taken[p + v - Vec2(spaceBetween,spaceBetween)]) {
210 good = false;
211 break;
212 }
213 } while (!good && --cnt > 0);
214 if (cnt == 0) {
215 INFO << "Placed only " << i << " rooms out of " << numRooms;
216 break;
217 }
218 for (Vec2 v : Rectangle(k))
219 taken[v + p] = 1;
220 for (Vec2 v : Rectangle(k - Vec2(2, 2)))
221 builder->resetFurniture(p + v + Vec2(1, 1), FurnitureType::FLOOR, SquareAttrib::ROOM);
222 for (int i : Range(p.x, p.x + k.x)) {
223 wallChange.apply(builder, Vec2(i, p.y));
224 wallChange.apply(builder, Vec2(i, p.y + k.y - 1));
225 if ((i == p.x || i == p.x + k.x - 1) && !diggableCorners) {
226 builder->addAttrib(Vec2(i, p.y), SquareAttrib::NO_DIG);
227 builder->addAttrib(Vec2(i, p.y + k.y - 1), SquareAttrib::NO_DIG);
228 }
229 }
230 for (int i : Range(p.y + 1, p.y + k.y - 1)) {
231 wallChange.apply(builder, Vec2(p.x, i));
232 wallChange.apply(builder, Vec2(p.x + k.x - 1, i));
233 }
234 Rectangle inside(p.x + 1, p.y + 1, p.x + k.x - 1, p.y + k.y - 1);
235 roomContents->make(builder, inside);
236 if (i < insideMakers.size())
237 insideMakers[i]->make(builder, inside);
238 else
239 for (Vec2 v : inside)
240 builder->addAttrib(v, SquareAttrib::EMPTY_ROOM);
241 }
242 }
243
244 private:
245 int numRooms;
246 int minSize;
247 int maxSize;
248 SquareChange wallChange;
249 optional<FurnitureType> onType;
250 PLevelMaker roomContents;
251 vector<PLevelMaker> insideMakers;
252 bool diggableCorners;
253 };
254
255 class Connector : public LevelMaker {
256 public:
Connector(optional<FurnitureFactory> _door,double _doorProb,double _diggingCost=3,Predicate pred=Predicate::canEnter ({MovementTrait::WALK}),optional<SquareAttrib> setAttr=none)257 Connector(optional<FurnitureFactory> _door, double _doorProb, double _diggingCost = 3,
258 Predicate pred = Predicate::canEnter({MovementTrait::WALK}), optional<SquareAttrib> setAttr = none)
259 : door(_door), doorProb(_doorProb), diggingCost(_diggingCost), connectPred(pred), setAttrib(setAttr) {
260 }
getValue(LevelBuilder * builder,Vec2 pos,Rectangle area)261 double getValue(LevelBuilder* builder, Vec2 pos, Rectangle area) {
262 if (builder->canNavigate(pos, {MovementTrait::WALK}))
263 return 1;
264 if (builder->hasAttrib(pos, SquareAttrib::NO_DIG))
265 return ShortestPath::infinity;
266 if (builder->hasAttrib(pos, SquareAttrib::LAKE))
267 return 15;
268 if (builder->hasAttrib(pos, SquareAttrib::RIVER))
269 return 15;
270 int numCorners = 0;
271 int numTotal = 0;
272 for (Vec2 v : Vec2::directions8())
273 if ((pos + v).inRectangle(area) && builder->canNavigate(pos + v, MovementTrait::WALK)) {
274 if (abs(v.x) == abs(v.y))
275 ++numCorners;
276 ++numTotal;
277 }
278 if (numCorners == 1)
279 return 1000;
280 if (numTotal - numCorners > 1)
281 return diggingCost + 5;
282 return diggingCost;
283 }
284
connect(LevelBuilder * builder,Vec2 p1,Vec2 p2,Rectangle area)285 void connect(LevelBuilder* builder, Vec2 p1, Vec2 p2, Rectangle area) {
286 ShortestPath path(area,
287 [builder, this, &area](Vec2 pos) { return getValue(builder, pos, area); },
288 [] (Vec2 v) { return v.length4(); },
289 Vec2::directions4(builder->getRandom()), p1 ,p2);
290 for (Vec2 v = p2; v != p1; v = path.getNextMove(v)) {
291 if (!builder->canNavigate(v, {MovementTrait::WALK})) {
292 if (auto furniture = builder->getFurniture(v, FurnitureLayer::MIDDLE)) {
293 bool placeDoor = furniture->isWall() && builder->hasAttrib(v, SquareAttrib::ROOM_WALL);
294 if (!furniture->getMovementSet().canEnter({MovementTrait::WALK}))
295 builder->removeFurniture(v, FurnitureLayer::MIDDLE);
296 if (placeDoor && door && builder->getRandom().chance(doorProb))
297 builder->putFurniture(v, *door);
298 }
299 if (builder->canNavigate(v, {MovementTrait::WALK}))
300 continue;
301 if (builder->getFurniture(v, FurnitureLayer::GROUND)->canBuildBridgeOver())
302 builder->putFurniture(v, FurnitureType::BRIDGE);
303 }
304 if (!path.isReachable(v))
305 failGen();
306 }
307 }
308
make(LevelBuilder * builder,Rectangle area)309 virtual void make(LevelBuilder* builder, Rectangle area) override {
310 Vec2 p1, p2;
311 vector<Vec2> points = area.getAllSquares().filter([&] (Vec2 v) { return connectPred.apply(builder, v);});
312 if (points.size() < 2)
313 return;
314 for (int i : Range(30)) {
315 p1 = builder->getRandom().choose(points);
316 p2 = builder->getRandom().choose(points);
317 if (p1 != p2)
318 connect(builder, p1, p2, area);
319 }
320 auto dijkstraFun = [&] (Vec2 pos) {
321 if (builder->canNavigate(pos, MovementTrait::WALK))
322 return 1.;
323 else
324 return ShortestPath::infinity;};
325 Table<bool> connected(area, false);
326 while (1) {
327 Dijkstra dijkstra(area, p1, 10000, dijkstraFun);
328 for (Vec2 v : area)
329 if (dijkstra.isReachable(v))
330 connected[v] = true;
331 bool found = false;
332 for (Vec2 v : area)
333 if (connectPred.apply(builder, v) && !connected[v]) {
334 connect(builder, p1, v, area);
335 p1 = v;
336 found = true;
337 break;
338 }
339 if (!found)
340 break;
341 }
342 }
343
344 private:
345 optional<FurnitureFactory> door;
346 double doorProb;
347 double diggingCost;
348 Predicate connectPred;
349 optional<SquareAttrib> setAttrib;
350 };
351
352 namespace {
353 class Furnitures : public LevelMaker {
354 public:
Furnitures(Predicate pred,double _density,FurnitureFactory _factory,optional<SquareAttrib> setAttr=none)355 Furnitures(Predicate pred, double _density, FurnitureFactory _factory, optional<SquareAttrib> setAttr = none):
356 factory(_factory), density(_density), predicate(pred), attr(setAttr) {
357 }
358
make(LevelBuilder * builder,Rectangle area)359 virtual void make(LevelBuilder* builder, Rectangle area) override {
360 vector<Vec2> available;
361 for (Vec2 v : area)
362 if (predicate.apply(builder, v) && builder->canPutFurniture(v, FurnitureLayer::MIDDLE))
363 available.push_back(v);
364 for (int i : Range(available.size() * density)) {
365 Vec2 pos = builder->getRandom().choose(available);
366 builder->putFurniture(pos, factory);
367 if (attr)
368 builder->addAttrib(pos, *attr);
369 available.removeElement(pos);
370 }
371 }
372
373 private:
374 FurnitureFactory factory;
375 double density;
376 Predicate predicate;
377 optional<SquareAttrib> attr;
378 };
379 }
380
381 class Inhabitants : public LevelMaker {
382 public:
383
Inhabitants(InhabitantsInfo inhab,CollectiveBuilder * col,Predicate pred=Predicate::alwaysTrue ())384 Inhabitants(InhabitantsInfo inhab, CollectiveBuilder* col, Predicate pred = Predicate::alwaysTrue()) :
385 inhabitants(inhab), actorFactory(MonsterAIFactory::monster()), onPred(pred), collective(col) {}
386
make(LevelBuilder * builder,Rectangle area)387 virtual void make(LevelBuilder* builder, Rectangle area) override {
388 if (!actorFactory)
389 actorFactory = MonsterAIFactory::stayInLocation(builder->toGlobalCoordinates(area));
390 Table<char> taken(area.right(), area.bottom());
391 for (auto& minion : inhabitants.generateCreatures(builder->getRandom(), collective->getTribe(), *actorFactory)) {
392 PCreature& creature = minion.first;
393 Vec2 pos;
394 int numTries = 100;
395 do {
396 pos = Vec2(builder->getRandom().get(area.left(), area.right()),
397 builder->getRandom().get(area.top(), area.bottom()));
398 } while (--numTries > 0 && (!builder->canPutCreature(pos, creature.get()) || (!onPred.apply(builder, pos))));
399 checkGen(numTries > 0);
400 if (collective) {
401 collective->addCreature(creature.get(), minion.second);
402 builder->addCollective(collective);
403 }
404 builder->putCreature(pos, std::move(creature));
405 taken[pos] = 1;
406 }
407 }
408
409 private:
410 InhabitantsInfo inhabitants;
411 optional<MonsterAIFactory> actorFactory;
412 Predicate onPred;
413 CollectiveBuilder* collective = nullptr;
414 };
415
416 class Creatures : public LevelMaker {
417 public:
Creatures(CreatureFactory f,int num,MonsterAIFactory actorF,Predicate pred=Predicate::alwaysTrue ())418 Creatures(CreatureFactory f, int num, MonsterAIFactory actorF, Predicate pred = Predicate::alwaysTrue()) :
419 creatures(f), numCreatures(num), actorFactory(actorF), onPred(pred) {}
420
make(LevelBuilder * builder,Rectangle area)421 virtual void make(LevelBuilder* builder, Rectangle area) override {
422 if (!actorFactory)
423 actorFactory = MonsterAIFactory::stayInLocation(builder->toGlobalCoordinates(area));
424 Table<char> taken(area.right(), area.bottom());
425 for (int i : Range(numCreatures)) {
426 PCreature creature = creatures.random(*actorFactory);
427 Vec2 pos;
428 int numTries = 100;
429 do {
430 pos = Vec2(builder->getRandom().get(area.left(), area.right()),
431 builder->getRandom().get(area.top(), area.bottom()));
432 } while (--numTries > 0 && (!builder->canPutCreature(pos, creature.get()) || (!onPred.apply(builder, pos))));
433 checkGen(numTries > 0);
434 builder->putCreature(pos, std::move(creature));
435 taken[pos] = 1;
436 }
437 }
438
439 private:
440 CreatureFactory creatures;
441 int numCreatures;
442 optional<MonsterAIFactory> actorFactory;
443 Predicate onPred;
444 };
445
446 class Items : public LevelMaker {
447 public:
Items(ItemFactory _factory,int minc,int maxc,Predicate pred=Predicate::alwaysTrue (),bool _placeOnFurniture=false)448 Items(ItemFactory _factory, int minc, int maxc, Predicate pred = Predicate::alwaysTrue(),
449 bool _placeOnFurniture = false) :
450 factory(_factory), minItem(minc), maxItem(maxc), predicate(pred), placeOnFurniture(_placeOnFurniture) {}
451
make(LevelBuilder * builder,Rectangle area)452 virtual void make(LevelBuilder* builder, Rectangle area) override {
453 int numItem = builder->getRandom().get(minItem, maxItem);
454 for (int i : Range(numItem)) {
455 Vec2 pos;
456 do {
457 pos = Vec2(builder->getRandom().get(area.left(), area.right()), builder->getRandom().get(area.top(),
458 area.bottom()));
459 } while (!predicate.apply(builder, pos) ||
460 !builder->canNavigate(pos, MovementTrait::WALK) ||
461 (!placeOnFurniture && builder->getFurniture(pos, FurnitureLayer::MIDDLE)));
462 builder->putItems(pos, factory.random());
463 }
464 }
465
466 private:
467 ItemFactory factory;
468 int minItem;
469 int maxItem;
470 Predicate predicate;
471 bool placeOnFurniture;
472 };
473
474 class River : public LevelMaker {
475 public:
River(int _width,FurnitureType type)476 River(int _width, FurnitureType type) : width(_width), furnitureType(type){}
477
make(LevelBuilder * builder,Rectangle area)478 virtual void make(LevelBuilder* builder, Rectangle area) override {
479 int wind = 5;
480 int middle = (area.left() + area.right()) / 2;
481 int px = builder->getRandom().get(middle - wind, middle + width);
482 int kx = px + builder->getRandom().get(-wind, wind); // builder->getRandom().get(area.left(), area.right()) - width;
483 if (kx < 0)
484 kx = 0;
485 if (kx >= area.right() - width)
486 kx = area.right() - width - 1;
487 int tot = 5;
488 for (int h : Range(tot)) {
489 int height = area.top() * (tot - h) / tot + area.bottom() * h / tot;
490 int height2 = area.top() * (tot - h - 1) / tot + area.bottom() * (h + 1) / tot;
491 vector<Vec2> line = straightLine(px, height, kx, (h == tot - 1) ? area.bottom() : height2);
492 for (Vec2 v : line)
493 for (int i : Range(width)) {
494 Vec2 pos = v + Vec2(i, 0);
495 builder->resetFurniture(pos, furnitureType, SquareAttrib::RIVER);
496 }
497 px = kx;
498 kx = px + builder->getRandom().get(-wind, wind);
499 if (kx < 0)
500 kx = 0;
501 if (kx >= area.right() - width)
502 kx = area.right() - width - 1;
503 }
504 }
505
506 private:
507
straightLine(int x0,int y0,int x1,int y1)508 vector<Vec2> straightLine(int x0, int y0, int x1, int y1){
509 INFO << "Line " << x1 << " " << y0 << " " << x1 << " " << y1;
510 int dx = x1 - x0;
511 int dy = y1 - y0;
512 vector<Vec2> ret{ Vec2(x0, y0)};
513 if (abs(dx) > abs(dy)) { // slope < 1
514 double m = (double) dy / (double) dx; // compute slope
515 double b = y0 - m*x0;
516 dx = (dx < 0) ? -1 : 1;
517 while (x0+dx != x1) {
518 x0 += dx;
519 ret.push_back(Vec2(x0,(int)roundf(m*x0+b)));
520 }
521 } else
522 if (dy != 0) { // slope >= 1
523 double m = (double) dx / (double) dy; // compute slope
524 double b = x0 - m*y0;
525 dy = (dy < 0) ? -1 : 1;
526 while (y0+dy != y1) {
527 y0 += dy;
528 ret.push_back(Vec2((int)round(m*y0+b),y0));
529 }
530 }
531 return ret;
532 }
533
534 int width;
535 FurnitureType furnitureType;
536 };
537
538 class MountainRiver : public LevelMaker {
539 public:
MountainRiver(int num,Predicate startPred)540 MountainRiver(int num, Predicate startPred)
541 : number(num), startPredicate(startPred) {}
542
fillLake(LevelBuilder * builder,set<Vec2> & waterTiles,Rectangle area,Vec2 pos)543 optional<Vec2> fillLake(LevelBuilder* builder, set<Vec2>& waterTiles, Rectangle area, Vec2 pos) {
544 vector<Vec2> ret;
545 double height = builder->getHeightMap(pos);
546 queue<Vec2> q;
547 set<Vec2> visited {pos};
548 map<Vec2, Vec2> predecessor {{ pos, pos}};
549 q.push(pos);
550 while (!q.empty()) {
551 pos = q.front();
552 q.pop();
553 for (Vec2 v : pos.neighbors8(builder->getRandom()))
554 if (v.inRectangle(area) && !visited.count(v)) {
555 visited.insert(v);
556 predecessor[v] = pos;
557 if (fabs(builder->getHeightMap(v) - height) < 0.000001)
558 q.push(v);
559 else
560 if (builder->getHeightMap(v) < height)
561 ret.push_back(v);
562 }
563 }
564 if (builder->getRandom().roll(predecessor.size()) || ret.empty()) {
565 for (auto& elem : predecessor)
566 if (!ret.contains(elem.first))
567 waterTiles.insert(elem.first);
568 if (ret.empty())
569 return none;
570 else
571 return builder->getRandom().choose(ret);
572 } else {
573 Vec2 end = builder->getRandom().choose(ret);
574 for (Vec2 v = predecessor.at(end);; v = predecessor.at(v)) {
575 waterTiles.insert(v);
576 if (v == predecessor.at(v))
577 break;
578 }
579 return end;
580 }
581 }
582
make(LevelBuilder * builder,Rectangle area)583 virtual void make(LevelBuilder* builder, Rectangle area) override {
584 set<Vec2> allWaterTiles;
585 for (int i : Range(number)) {
586 set<Vec2> waterTiles;
587 Vec2 pos = startPredicate.getRandomPosition(builder, area);
588 int width = builder->getRandom().get(3, 6);
589 while (1) {
590 if (builder->hasAttrib(pos, SquareAttrib::RIVER))
591 break;
592 if (auto next = fillLake(builder, waterTiles, area, pos))
593 pos = *next;
594 else
595 break;
596 }
597 for (Vec2 v : waterTiles)
598 for (Vec2 v2 : Rectangle(v, v + Vec2(width, width)))
599 allWaterTiles.insert(v2);
600 }
601 for (auto layer : Iter(Vec2::calculateLayers(allWaterTiles))) {
602 for (Vec2 v : *layer)
603 if (v.inRectangle(area) && !builder->hasAttrib(v, SquareAttrib::RIVER)) {
604 builder->addAttrib(v, SquareAttrib::RIVER);
605 builder->resetFurniture(v, getWaterType(builder, v, layer.index()));
606 }
607 }
608 }
609
getWaterType(LevelBuilder * builder,Vec2 pos,int numLayer)610 FurnitureType getWaterType(LevelBuilder* builder, Vec2 pos, int numLayer) {
611 if (builder->hasAttrib(pos, SquareAttrib::MOUNTAIN))
612 return FurnitureFactory::getWaterType(100);
613 else if (numLayer == 0)
614 return FurnitureType::SAND;
615 else
616 return FurnitureFactory::getWaterType(1.1 * (numLayer - 1));
617 }
618
619 private:
620 int number;
621 Predicate startPredicate;
622 };
623
624 class Blob : public LevelMaker {
625 public:
Blob(double _insideRatio=0.333)626 Blob(double _insideRatio = 0.333) : insideRatio(_insideRatio) {}
627
628 virtual void addSquare(LevelBuilder* builder, Vec2 pos, int edgeDist) = 0;
629
make(LevelBuilder * builder,Rectangle area)630 virtual void make(LevelBuilder* builder, Rectangle area) override {
631 vector<Vec2> squares;
632 Table<char> isInside(area, 0);
633 Vec2 center = area.middle();
634 squares.push_back(center);
635 isInside[center] = 1;
636 for (int i : Range(area.width() * area.height() * insideRatio)) {
637 vector<Vec2> nextPos;
638 for (auto pos : squares)
639 for (Vec2 next : pos.neighbors4())
640 if (next.inRectangle(area) && !squares.contains(next))
641 nextPos.push_back(next);
642 vector<double> probs = nextPos.transform([&](Vec2 v) {
643 double px = std::abs(v.x - center.x);
644 double py = std::abs(v.y - center.y);
645 py *= area.width();
646 py /= area.height();
647 double coeff = -1.0 + 1.0 / (sqrt(px * px + py * py) / sqrt(2 * area.width() * area.width()));
648 CHECK(coeff > 0.0);
649 return coeff;
650 });
651 Vec2 chosen = builder->getRandom().choose(nextPos, probs);
652 isInside[chosen] = 1;
653 squares.push_back(chosen);
654 }
655 queue<Vec2> q;
656 int inf = 10000;
657 Table<int> distance(area, inf);
658 for (Vec2 v : isInside.getBounds())
659 if (!isInside[v]) {
660 distance[v] = 0;
661 q.push(v);
662 }
663 while (!q.empty()) {
664 Vec2 pos = q.front();
665 q.pop();
666 for (Vec2 v : pos.neighbors8())
667 if (v.inRectangle(area) && distance[v] == inf) {
668 distance[v] = distance[pos] + 1;
669 q.push(v);
670 addSquare(builder, v, distance[v]);
671 }
672 }
673 }
674
675 private:
676 double insideRatio;
677 };
678
679 class UniformBlob : public Blob {
680 public:
UniformBlob(FurnitureType insideSquare,optional<FurnitureType> borderSquare=none,optional<SquareAttrib> _attrib=none,double insideRatio=0.3333)681 UniformBlob(FurnitureType insideSquare, optional<FurnitureType> borderSquare = none,
682 optional<SquareAttrib> _attrib = none, double insideRatio = 0.3333) : Blob(insideRatio),
683 inside(insideSquare), border(borderSquare), attrib(_attrib) {}
684
addSquare(LevelBuilder * builder,Vec2 pos,int edgeDist)685 virtual void addSquare(LevelBuilder* builder, Vec2 pos, int edgeDist) override {
686 if (edgeDist == 1 && border)
687 builder->resetFurniture(pos, *border, attrib);
688 else
689 builder->resetFurniture(pos, inside, attrib);
690 }
691
692 private:
693 FurnitureType inside;
694 optional<FurnitureType> border;
695 optional<SquareAttrib> attrib;
696 };
697
698 class FurnitureBlob : public Blob {
699 public:
FurnitureBlob(FurnitureFactory in,double insideRatio=0.3333)700 FurnitureBlob(FurnitureFactory in, double insideRatio = 0.3333) : Blob(insideRatio), inside(in) {}
701
addSquare(LevelBuilder * builder,Vec2 pos,int edgeDist)702 virtual void addSquare(LevelBuilder* builder, Vec2 pos, int edgeDist) override {
703 builder->putFurniture(pos, inside);
704 }
705
706 private:
707 FurnitureFactory inside;
708 };
709
710 class Lake : public Blob {
711 public:
Lake(bool s=true)712 Lake(bool s = true) : sand(s) {}
addSquare(LevelBuilder * builder,Vec2 pos,int edgeDist)713 virtual void addSquare(LevelBuilder* builder, Vec2 pos, int edgeDist) override {
714 builder->addAttrib(pos, SquareAttrib::LAKE);
715 if (sand && edgeDist == 1 && !builder->isFurnitureType(pos, FurnitureType::WATER))
716 builder->resetFurniture(pos, FurnitureType::SAND);
717 else
718 builder->resetFurniture(pos, FurnitureFactory::getWaterType(double(edgeDist) / 2));
719 }
720
721 private:
722 bool sand;
723 };
724
725 class RemoveFurniture : public LevelMaker {
726 public:
RemoveFurniture(FurnitureLayer l)727 RemoveFurniture(FurnitureLayer l) : layer(l) {
728 CHECK(layer != FurnitureLayer::GROUND);
729 }
730
make(LevelBuilder * builder,Rectangle area)731 virtual void make(LevelBuilder* builder, Rectangle area) override {
732 for (Vec2 v : area)
733 builder->removeFurniture(v, layer);
734 }
735
736 private:
737 FurnitureLayer layer;
738 };
739
740 struct BuildingType {
741 FurnitureType wall;
742 FurnitureType floorInside;
743 FurnitureType floorOutside;
744 optional<FurnitureFactory> door;
745 };
746
getBuildingInfo(SettlementInfo info)747 static BuildingType getBuildingInfo(SettlementInfo info) {
748 switch (info.buildingId) {
749 case BuildingId::WOOD:
750 return CONSTRUCT(BuildingType,
751 c.wall = FurnitureType::WOOD_WALL;
752 c.floorInside = FurnitureType::FLOOR;
753 c.floorOutside = FurnitureType::GRASS;
754 c.door = FurnitureFactory(info.tribe, FurnitureType::DOOR);
755 );
756 case BuildingId::WOOD_CASTLE:
757 return CONSTRUCT(BuildingType,
758 c.wall = FurnitureType::WOOD_WALL;
759 c.floorInside = FurnitureType::FLOOR;
760 c.floorOutside = FurnitureType::MUD;
761 c.door = FurnitureFactory(info.tribe, FurnitureType::DOOR);
762 );
763 case BuildingId::MUD:
764 return CONSTRUCT(BuildingType,
765 c.wall = FurnitureType::MUD_WALL;
766 c.floorInside = FurnitureType::MUD;
767 c.floorOutside = FurnitureType::MUD;);
768 case BuildingId::BRICK:
769 return CONSTRUCT(BuildingType,
770 c.wall = FurnitureType::CASTLE_WALL;
771 c.floorInside = FurnitureType::FLOOR;
772 c.floorOutside = FurnitureType::MUD;
773 c.door = FurnitureFactory(info.tribe, FurnitureType::DOOR);
774 );
775 case BuildingId::DUNGEON:
776 return CONSTRUCT(BuildingType,
777 c.wall = FurnitureType::MOUNTAIN;
778 c.floorInside = FurnitureType::FLOOR;
779 c.floorOutside = FurnitureType::FLOOR;
780 c.door = FurnitureFactory(info.tribe, FurnitureType::DOOR);
781 );
782 case BuildingId::DUNGEON_SURFACE:
783 return CONSTRUCT(BuildingType,
784 c.wall = FurnitureType::MOUNTAIN;
785 c.floorInside = FurnitureType::FLOOR;
786 c.floorOutside = FurnitureType::HILL;
787 c.door = FurnitureFactory(info.tribe, FurnitureType::DOOR);
788 );
789 }
790 }
791
792 class Buildings : public LevelMaker {
793 public:
Buildings(int _minBuildings,int _maxBuildings,int _minSize,int _maxSize,BuildingType _building,bool _align,vector<PLevelMaker> _insideMakers,bool _roadConnection=true)794 Buildings(int _minBuildings, int _maxBuildings,
795 int _minSize, int _maxSize,
796 BuildingType _building,
797 bool _align,
798 vector<PLevelMaker> _insideMakers,
799 bool _roadConnection = true) :
800 minBuildings(_minBuildings),
801 maxBuildings(_maxBuildings),
802 minSize(_minSize), maxSize(_maxSize),
803 align(_align),
804 building(_building),
805 insideMakers(std::move(_insideMakers)),
806 roadConnection(_roadConnection) {
807 CHECK(insideMakers.size() <= minBuildings);
808 }
809
Buildings(int _minBuildings,int _maxBuildings,int _minSize,int _maxSize,BuildingType _building,bool _align,PLevelMaker _insideMaker,bool _roadConnection=true)810 Buildings(int _minBuildings, int _maxBuildings,
811 int _minSize, int _maxSize,
812 BuildingType _building,
813 bool _align,
814 PLevelMaker _insideMaker,
815 bool _roadConnection = true) : Buildings(_minBuildings, _maxBuildings, _minSize, _maxSize, _building, _align,
816 _insideMaker ? makeVec<PLevelMaker>(std::move(_insideMaker)) : vector<PLevelMaker>(), _roadConnection) {}
817
make(LevelBuilder * builder,Rectangle area)818 virtual void make(LevelBuilder* builder, Rectangle area) override {
819 Table<bool> filled(area);
820 int width = area.width();
821 int height = area.height();
822 for (Vec2 v : area)
823 filled[v] = 0;
824 int sizeVar = 1;
825 int spaceBetween = 1;
826 int alignHeight = 0;
827 if (align) {
828 alignHeight = height / 2 - 2 + builder->getRandom().get(5);
829 }
830 int nextw = -1;
831 int numBuildings = builder->getRandom().get(minBuildings, maxBuildings);
832 for (int i = 0; i < numBuildings; ++i) {
833 bool spaceOk = true;
834 int w, h, px, py;
835 int cnt = 10000;
836 bool buildingRow;
837 do {
838 buildingRow = builder->getRandom().get(2);
839 spaceOk = true;
840 w = builder->getRandom().get(minSize, maxSize);
841 h = builder->getRandom().get(minSize, maxSize);
842 if (nextw > -1 && nextw + w < area.right()) {
843 px = nextw;
844 nextw = -1;
845 } else
846 px = area.left() + builder->getRandom().get(width - w - 2 * spaceBetween + 1) + spaceBetween;
847 if (!align)
848 py = area.top() + builder->getRandom().get(height - h - 2 * spaceBetween + 1) + spaceBetween;
849 else {
850 py = area.top() + (buildingRow == 1 ? alignHeight - h - 1 : alignHeight + 2);
851 if (py + h >= area.bottom() || py < area.top()) {
852 spaceOk = false;
853 continue;
854 }
855 }
856 Vec2 tmp(px - spaceBetween, py - spaceBetween);
857 for (Vec2 v : Rectangle(w + spaceBetween * 2 + 1, h + spaceBetween * 2 + 1))
858 if (!(tmp + v).inRectangle(area) || filled[px + v.x - spaceBetween][py + v.y - spaceBetween]) {
859 spaceOk = false;
860 break;
861 }
862 } while (!spaceOk && --cnt > 0);
863 if (cnt == 0) {
864 if (i < minBuildings)
865 failGen(); // "Failed to add " << minBuildings - i << " buildings out of " << minBuildings;
866 else
867 break;
868 }
869 if (builder->getRandom().roll(1))
870 nextw = px + w;
871 for (Vec2 v : Rectangle(w + 1, h + 1)) {
872 filled[Vec2(px, py) + v] = true;
873 builder->putFurniture(Vec2(px, py) + v, building.wall);
874 builder->setCovered(Vec2(px, py) + v, true);
875 }
876 for (Vec2 v : Rectangle(w - 1, h - 1)) {
877 auto pos = Vec2(px + 1, py + 1) + v;
878 builder->resetFurniture(pos, building.floorInside, SquareAttrib::ROOM);
879 }
880 Vec2 doorLoc = align ?
881 Vec2(px + builder->getRandom().get(1, w),
882 py + (buildingRow * h)) :
883 getRandomExit(Random, Rectangle(px, py, px + w + 1, py + h + 1));
884 builder->resetFurniture(doorLoc, building.floorInside);
885 if (building.door)
886 builder->putFurniture(doorLoc, *building.door);
887 Rectangle inside(px + 1, py + 1, px + w, py + h);
888 if (i < insideMakers.size())
889 insideMakers[i]->make(builder, inside);
890 else
891 for (Vec2 v : inside)
892 builder->addAttrib(v, SquareAttrib::EMPTY_ROOM);
893 }
894 if (align)
895 for (Vec2 v : Rectangle(area.left() + area.width() / 3, area.top() + alignHeight,
896 area.right() - area.width() / 3, area.top() + alignHeight + 2))
897 builder->addAttrib(v, SquareAttrib::BUILDINGS_CENTER);
898 if (roadConnection) {
899 Vec2 pos = Vec2((area.left() + area.right()) / 2, area.top() + alignHeight);
900 builder->removeFurniture(pos, FurnitureLayer::MIDDLE);
901 builder->putFurniture(pos, FurnitureParams{FurnitureType::ROAD, TribeId::getMonster()});
902 builder->addAttrib(pos, SquareAttrib::CONNECT_ROAD);
903 }
904 }
905
906 private:
907 int minBuildings;
908 int maxBuildings;
909 int minSize;
910 int maxSize;
911 bool align;
912 BuildingType building;
913 vector<PLevelMaker> insideMakers;
914 bool roadConnection;
915 };
916
917 DEF_UNIQUE_PTR(MakerQueue);
918
919 class MakerQueue : public LevelMaker {
920 public:
921 MakerQueue() = default;
MakerQueue(vector<PLevelMaker> _makers)922 MakerQueue(vector<PLevelMaker> _makers) : makers(std::move(_makers)) {}
923
924 template <typename T1, typename T2, typename... Args>
MakerQueue(T1 && t1,T2 && t2,Args &&...args)925 MakerQueue(T1&& t1, T2&& t2, Args&&... args) : MakerQueue(makeVec<PLevelMaker>(std::move(t1), std::move(t2), std::move(args)...)) {}
926
addMaker(PLevelMaker maker)927 void addMaker(PLevelMaker maker) {
928 makers.push_back(std::move(maker));
929 }
930
make(LevelBuilder * builder,Rectangle area)931 virtual void make(LevelBuilder* builder, Rectangle area) override {
932 for (auto& maker : makers)
933 maker->make(builder, area);
934 }
935
936 private:
937 vector<PLevelMaker> makers;
938 };
939
940 class PredicatePrecalc {
941 public:
PredicatePrecalc(const Predicate & predicate,LevelBuilder * builder,Rectangle area)942 PredicatePrecalc(const Predicate& predicate, LevelBuilder* builder, Rectangle area)
943 : counts(Rectangle(area.topLeft(), area.bottomRight() + Vec2(1, 1))) {
944 int px = counts.getBounds().left();
945 int py = counts.getBounds().top();
946 for (int x : Range(px, counts.getBounds().right()))
947 counts[x][py] = 0;
948 for (int y : Range(py, counts.getBounds().bottom()))
949 counts[px][y] = 0;
950 for (Vec2 v : Rectangle(area.topLeft() + Vec2(1, 1), counts.getBounds().bottomRight()))
951 counts[v] = (predicate.apply(builder, v - Vec2(1, 1)) ? 1 : 0) +
952 counts[v.x - 1][v.y] + counts[v.x][v.y - 1] -counts[v.x - 1][v.y - 1];
953 }
954
getCount(Rectangle area) const955 int getCount(Rectangle area) const {
956 return counts[area.bottomRight()] + counts[area.topLeft()]
957 -counts[area.bottomLeft()] - counts[area.topRight()];
958 }
959
960 private:
961 Table<int> counts;
962 };
963
964
965 class RandomLocations : public LevelMaker {
966 public:
RandomLocations(vector<PLevelMaker> _insideMakers,const vector<pair<int,int>> & _sizes,Predicate pred,bool _separate=true)967 RandomLocations(vector<PLevelMaker> _insideMakers, const vector<pair<int, int>>& _sizes,
968 Predicate pred, bool _separate = true)
969 : insideMakers(std::move(_insideMakers)), sizes(_sizes), predicate(sizes.size(), pred),
970 separate(_separate) {
971 CHECK(insideMakers.size() == sizes.size());
972 CHECK(predicate.size() == sizes.size());
973 }
974
RandomLocations(vector<PLevelMaker> _insideMakers,const vector<pair<int,int>> & _sizes,const vector<Predicate> & pred,bool _separate=true)975 RandomLocations(vector<PLevelMaker> _insideMakers, const vector<pair<int, int>>& _sizes,
976 const vector<Predicate>& pred, bool _separate = true)
977 : insideMakers(std::move(_insideMakers)), sizes(_sizes), predicate(pred.begin(), pred.end()),
978 separate(_separate) {
979 CHECK(insideMakers.size() == sizes.size());
980 CHECK(pred.size() == sizes.size());
981 }
982
983 class LocationPredicate {
984 public:
LocationPredicate(Predicate main,Predicate sec,int minSec,int maxSec)985 LocationPredicate(Predicate main, Predicate sec, int minSec, int maxSec)
986 // main and sec must be mutually exclusive!!!
987 : predicate(main), second(sec), minSecond(minSec), maxSecond(maxSec) {
988 }
989
LocationPredicate(Predicate p)990 LocationPredicate(Predicate p) : predicate(p) {}
991
992 class Precomputed {
993 public:
Precomputed(LevelBuilder * builder,Rectangle area,Predicate p1,Predicate p2,int minSec,int maxSec)994 Precomputed(LevelBuilder* builder, Rectangle area, Predicate p1, Predicate p2, int minSec, int maxSec)
995 : pred1(p1, builder, area), pred2(p2, builder, area), minSecond(minSec), maxSecond(maxSec) {
996 }
997
apply(Rectangle rect) const998 bool apply(Rectangle rect) const {
999 int numFirst = pred1.getCount(rect);
1000 int numSecond = pred2.getCount(rect);
1001 return numSecond >= minSecond && numSecond < maxSecond && numSecond + numFirst == rect.width() * rect.height();
1002 }
1003
1004 private:
1005 PredicatePrecalc pred1;
1006 PredicatePrecalc pred2;
1007 int minSecond;
1008 int maxSecond;
1009 };
1010
precompute(LevelBuilder * builder,Rectangle area)1011 Precomputed precompute(LevelBuilder* builder, Rectangle area) {
1012 return Precomputed(builder, area, predicate, second, minSecond, maxSecond);
1013 }
1014
1015 private:
1016 Predicate predicate;
1017 Predicate second = Predicate::alwaysFalse();
1018 int minSecond = 0;
1019 int maxSecond = 1;
1020 };
1021
RandomLocations(bool _separate=true)1022 RandomLocations(bool _separate = true) : separate(_separate) {}
1023
add(PLevelMaker maker,Vec2 size,LocationPredicate pred)1024 void add(PLevelMaker maker, Vec2 size, LocationPredicate pred) {
1025 insideMakers.push_back(std::move(maker));
1026 sizes.push_back({size.x, size.y});
1027 predicate.push_back(pred);
1028 }
1029
setMinDistance(LevelMaker * m1,LevelMaker * m2,double dist)1030 void setMinDistance(LevelMaker* m1, LevelMaker* m2, double dist) {
1031 minDistance[{m1, m2}] = dist;
1032 minDistance[{m2, m1}] = dist;
1033 }
1034
setMaxDistance(LevelMaker * m1,LevelMaker * m2,double dist)1035 void setMaxDistance(LevelMaker* m1, LevelMaker* m2, double dist) {
1036 maxDistance[{m1, m2}] = dist;
1037 maxDistance[{m2, m1}] = dist;
1038 }
1039
setMinMargin(LevelMaker * m1,int margin)1040 void setMinMargin(LevelMaker *m1, int margin) {
1041 minMargin[m1] = margin;
1042 }
1043
setMinDistanceLast(LevelMaker * m,double dist)1044 void setMinDistanceLast(LevelMaker* m, double dist) {
1045 minDistance[{m, insideMakers.back().get()}] = dist;
1046 minDistance[{insideMakers.back().get(), m}] = dist;
1047 }
1048
setMaxDistanceLast(LevelMaker * m,double dist)1049 void setMaxDistanceLast(LevelMaker* m, double dist) {
1050 maxDistance[{m, insideMakers.back().get()}] = dist;
1051 maxDistance[{insideMakers.back().get(), m}] = dist;
1052 }
1053
make(LevelBuilder * builder,Rectangle area)1054 virtual void make(LevelBuilder* builder, Rectangle area) override {
1055 vector<LocationPredicate::Precomputed> precomputed;
1056 for (int i : All(insideMakers))
1057 precomputed.push_back(predicate[i].precompute(builder, area));
1058 for (int i : Range(3000))
1059 if (tryMake(builder, precomputed, area))
1060 return;
1061 failGen(); // "Failed to find free space for " << (int)sizes.size() << " areas";
1062 }
1063
tryMake(LevelBuilder * builder,const vector<LocationPredicate::Precomputed> & precomputed,Rectangle area)1064 bool tryMake(LevelBuilder* builder, const vector<LocationPredicate::Precomputed>& precomputed, Rectangle area) {
1065 vector<Rectangle> occupied;
1066 vector<Rectangle> makerBounds;
1067 vector<LevelBuilder::Rot> maps;
1068 for (int i : All(insideMakers))
1069 maps.push_back(builder->getRandom().choose(
1070 LevelBuilder::CW0, LevelBuilder::CW1, LevelBuilder::CW2, LevelBuilder::CW3));
1071 for (int i : All(insideMakers)) {
1072 auto maker = insideMakers[i].get();
1073 int width = sizes[i].first;
1074 int height = sizes[i].second;
1075 if (contains({LevelBuilder::CW1, LevelBuilder::CW3}, maps[i]))
1076 std::swap(width, height);
1077 CHECK(width <= area.width() && height <= area.height());
1078 int px;
1079 int py;
1080 int cnt = 1000;
1081 bool ok;
1082 do {
1083 Progress::checkIfInterrupted();
1084 ok = true;
1085 int margin = minMargin.count(maker) ? minMargin.at(maker) : 0;
1086 CHECK(width + 2 * margin < area.width()) << "Couldn't fit maker width inside area.";
1087 CHECK(height + 2 * margin < area.height()) << "Couldn't fit maker height inside area.";
1088 px = area.left() + margin + builder->getRandom().get(area.width() - width - 2 * margin);
1089 py = area.top() + margin + builder->getRandom().get(area.height() - height - 2 * margin);
1090 Rectangle area(px, py, px + width, py + height);
1091 for (int j : Range(i))
1092 if ((maxDistance.count({insideMakers[j].get(), maker}) &&
1093 maxDistance[{insideMakers[j].get(), maker}] < area.middle().dist8(occupied[j].middle())) ||
1094 minDistance[{insideMakers[j].get(), maker}] > area.middle().dist8(occupied[j].middle())) {
1095 ok = false;
1096 break;
1097 }
1098 if (!precomputed[i].apply(area))
1099 ok = false;
1100 else
1101 if (separate)
1102 for (Rectangle r : occupied)
1103 if (r.intersects(area)) {
1104 ok = false;
1105 break;
1106 }
1107 } while (!ok && --cnt > 0);
1108 if (cnt == 0)
1109 return false;
1110 occupied.push_back(Rectangle(px, py, px + width, py + height));
1111 makerBounds.push_back(Rectangle(px, py, px + sizes[i].first, py + sizes[i].second));
1112 }
1113 CHECK(insideMakers.size() == occupied.size());
1114 for (int i : All(insideMakers)) {
1115 builder->pushMap(makerBounds[i], maps[i]);
1116 insideMakers[i]->make(builder, makerBounds[i]);
1117 builder->popMap();
1118 }
1119 return true;
1120 }
1121
1122 private:
1123 vector<PLevelMaker> insideMakers;
1124 vector<pair<int, int>> sizes;
1125 vector<LocationPredicate> predicate;
1126 bool separate;
1127 map<pair<LevelMaker*, LevelMaker*>, double> minDistance;
1128 map<pair<LevelMaker*, LevelMaker*>, double> maxDistance;
1129 map<LevelMaker*, int> minMargin;
1130 };
1131
1132 class Margin : public LevelMaker {
1133 public:
Margin(int s,PLevelMaker in)1134 Margin(int s, PLevelMaker in) : left(s), top(s), right(s), bottom(s), inside(std::move(in)) {}
Margin(int _left,int _top,int _right,int _bottom,PLevelMaker in)1135 Margin(int _left, int _top, int _right, int _bottom, PLevelMaker in)
1136 :left(_left) ,top(_top), right(_right), bottom(_bottom), inside(std::move(in)) {}
1137
make(LevelBuilder * builder,Rectangle area)1138 virtual void make(LevelBuilder* builder, Rectangle area) override {
1139 CHECK(area.width() > left + right && area.height() > top + bottom);
1140 inside->make(builder, Rectangle(
1141 area.left() + left,
1142 area.top() + top,
1143 area.right() - right,
1144 area.bottom() - bottom));
1145 }
1146
1147 private:
1148 int left, top, right, bottom;
1149 PLevelMaker inside;
1150 };
1151
addAvg(int x,int y,const Table<double> & wys,double & avg,int & num)1152 void addAvg(int x, int y, const Table<double>& wys, double& avg, int& num) {
1153 Vec2 pos(x, y);
1154 if (pos.inRectangle(wys.getBounds())) {
1155 avg += wys[pos];
1156 ++num;
1157 }
1158 }
1159
1160 struct NoiseInit {
1161 int topLeft;
1162 int topRight;
1163 int bottomRight;
1164 int bottomLeft;
1165 int middle;
1166 };
1167
genNoiseMap(RandomGen & random,Rectangle area,NoiseInit init,double varianceMult)1168 Table<double> genNoiseMap(RandomGen& random, Rectangle area, NoiseInit init, double varianceMult) {
1169 int width = 1;
1170 while (width < area.width() - 1 || width < area.height() - 1)
1171 width *= 2;
1172 width /= 2;
1173 ++width;
1174 Table<double> wys(width, width);
1175 wys[0][0] = init.topLeft;
1176 wys[width - 1][0] = init.topRight;
1177 wys[width - 1][width - 1] = init.bottomRight;
1178 wys[0][width - 1] = init.bottomLeft;
1179 wys[(width - 1) / 2][(width - 1) / 2] = init.middle;
1180
1181 double variance = 0.5;
1182 double heightDiff = 0.1;
1183 for (int a = width - 1; a >= 2; a /= 2) {
1184 if (a < width - 1)
1185 for (Vec2 pos1 : Rectangle((width - 1) / a, (width - 1) / a)) {
1186 Vec2 pos = pos1 * a;
1187 double avg = (wys[pos] + wys[pos.x + a][pos.y] + wys[pos.x][pos.y + a] + wys[pos.x + a][pos.y + a]) / 4;
1188 wys[pos.x + a / 2][pos.y + a / 2] =
1189 avg + variance * (random.getDouble() * 2 - 1);
1190 }
1191 for (Vec2 pos1 : Rectangle((width - 1) / a, (width - 1) / a + 1)) {
1192 Vec2 pos = pos1 * a;
1193 double avg = 0;
1194 int num = 0;
1195 addAvg(pos.x + a / 2, pos.y - a / 2, wys, avg, num);
1196 addAvg(pos.x, pos.y, wys, avg, num);
1197 addAvg(pos.x + a, pos.y, wys, avg, num);
1198 addAvg(pos.x + a / 2, pos.y + a / 2, wys, avg, num);
1199 wys[pos.x + a / 2][pos.y] =
1200 avg / num + variance * (random.getDouble() * 2 - 1);
1201 }
1202 for (Vec2 pos1 : Rectangle((width - 1) / a + 1, (width - 1) / a)) {
1203 Vec2 pos = pos1 * a;
1204 double avg = 0;
1205 int num = 0;
1206 addAvg(pos.x - a / 2, pos.y + a / 2, wys, avg, num);
1207 addAvg(pos.x, pos.y, wys, avg, num);
1208 addAvg(pos.x, pos.y + a , wys, avg, num);
1209 addAvg(pos.x + a / 2, pos.y + a / 2, wys, avg, num);
1210 wys[pos.x][pos.y + a / 2] =
1211 avg / num + variance * (random.getDouble() * 2 - 1);
1212 }
1213 variance *= varianceMult;
1214 }
1215 Table<double> ret(area);
1216 Vec2 offset(area.left(), area.top());
1217 for (Vec2 v : area) {
1218 Vec2 lv((v.x - offset.x) * width / area.width(), (v.y - offset.y) * width / area.height());
1219 ret[v] = wys[lv];
1220 }
1221 return ret;
1222 }
1223
raiseLocalMinima(Table<double> & t)1224 void raiseLocalMinima(Table<double>& t) {
1225 Vec2 minPos = t.getBounds().topLeft();
1226 for (Vec2 v : t.getBounds())
1227 if (t[v] < t[minPos])
1228 minPos = v;
1229 Table<bool> visited(t.getBounds(), false);
1230 auto comparator = [&](const Vec2& v1, const Vec2& v2) { return t[v1] > t[v2];};
1231 priority_queue<Vec2, vector<Vec2>, decltype(comparator)> q(comparator);
1232 q.push(minPos);
1233 visited[minPos] = true;
1234 while (!q.empty()) {
1235 Vec2 pos = q.top();
1236 q.pop();
1237 for (Vec2 v : pos.neighbors4())
1238 if (v.inRectangle(t.getBounds()) && !visited[v]) {
1239 if (t[v] < t[pos])
1240 t[v] = t[pos];
1241 q.push(v);
1242 visited[v] = true;
1243 }
1244 }
1245 }
1246
sortedValues(const Table<double> & t)1247 vector<double> sortedValues(const Table<double>& t) {
1248 vector<double> values;
1249 for (Vec2 v : t.getBounds()) {
1250 values.push_back(t[v]);
1251 }
1252 std::sort(values.begin(), values.end());
1253 return values;
1254 }
1255
1256 class SetSunlight : public LevelMaker {
1257 public:
SetSunlight(double a,Predicate p)1258 SetSunlight(double a, Predicate p) : amount(a), pred(p) {}
1259
make(LevelBuilder * builder,Rectangle area)1260 virtual void make(LevelBuilder* builder, Rectangle area) override {
1261 for (Vec2 v : area)
1262 if (pred.apply(builder, v))
1263 builder->setSunlight(v, amount);
1264 }
1265
1266 private:
1267 double amount;
1268 Predicate pred;
1269 };
1270
1271 class Mountains : public LevelMaker {
1272 public:
1273 static constexpr double varianceM = 0.45;
Mountains(double lowland,double hill,NoiseInit init)1274 Mountains(double lowland, double hill, NoiseInit init)
1275 : ratioLowland(lowland), ratioHill(hill), noiseInit(init), varianceMult(varianceM) {
1276 }
1277
make(LevelBuilder * builder,Rectangle area)1278 virtual void make(LevelBuilder* builder, Rectangle area) override {
1279 Table<double> wys = genNoiseMap(builder->getRandom(), area, noiseInit, varianceMult);
1280 raiseLocalMinima(wys);
1281 vector<double> values = sortedValues(wys);
1282 double cutOffLowland = values[(int)(ratioLowland * double(values.size() - 1))];
1283 double cutOffHill = values[(int)((ratioHill + ratioLowland) * double(values.size() - 1))];
1284 double cutOffDarkness = values[(int)((ratioHill + ratioLowland + 1.0) * 0.5 * double(values.size() - 1))];
1285 int dCnt = 0, mCnt = 0, hCnt = 0, lCnt = 0;
1286 for (Vec2 v : area) {
1287 builder->setHeightMap(v, wys[v]);
1288 if (wys[v] >= cutOffHill) {
1289 builder->putFurniture(v, FurnitureType::FLOOR);
1290 builder->putFurniture(v, {FurnitureType::MOUNTAIN, TribeId::getKeeper()}, SquareAttrib::MOUNTAIN);
1291 builder->setSunlight(v, max(0.0, 1. - (wys[v] - cutOffHill) / (cutOffDarkness - cutOffHill)));
1292 builder->setCovered(v, true);
1293 ++mCnt;
1294 }
1295 else if (wys[v] >= cutOffLowland) {
1296 builder->putFurniture(v, FurnitureType::HILL, SquareAttrib::HILL);
1297 ++hCnt;
1298 }
1299 else {
1300 builder->putFurniture(v, FurnitureType::GRASS, SquareAttrib::LOWLAND);
1301 ++lCnt;
1302 }
1303 }
1304 INFO << "Terrain distribution " << dCnt << " darkness, " << mCnt << " mountain, " << hCnt << " hill, " << lCnt << " lowland";
1305 }
1306
1307 private:
1308 double ratioLowland;
1309 double ratioHill;
1310 NoiseInit noiseInit;
1311 double varianceMult;
1312 };
1313
1314 class Roads : public LevelMaker {
1315 public:
Roads()1316 Roads() {}
1317
makeBridge(LevelBuilder * builder,Vec2 pos)1318 bool makeBridge(LevelBuilder* builder, Vec2 pos) {
1319 return !builder->canNavigate(pos, {MovementTrait::WALK}) && builder->canNavigate(pos, {MovementTrait::SWIM});
1320 }
1321
getValue(LevelBuilder * builder,Vec2 pos)1322 double getValue(LevelBuilder* builder, Vec2 pos) {
1323 if ((!builder->canNavigate(pos, MovementType({MovementTrait::WALK, MovementTrait::SWIM})) &&
1324 !builder->hasAttrib(pos, SquareAttrib::ROAD_CUT_THRU)) ||
1325 builder->hasAttrib(pos, SquareAttrib::NO_ROAD))
1326 return ShortestPath::infinity;
1327 if (makeBridge(builder, pos))
1328 return 10;
1329 if (builder->isFurnitureType(pos, FurnitureType::ROAD) || builder->isFurnitureType(pos, FurnitureType::BRIDGE))
1330 return 1;
1331 return 1 + pow(1 + builder->getHeightMap(pos), 2);
1332 }
1333
getRoadType(LevelBuilder * builder,Vec2 pos)1334 FurnitureType getRoadType(LevelBuilder* builder, Vec2 pos) {
1335 if (makeBridge(builder, pos))
1336 return FurnitureType::BRIDGE;
1337 else
1338 return FurnitureType::ROAD;
1339 }
1340
make(LevelBuilder * builder,Rectangle area)1341 virtual void make(LevelBuilder* builder, Rectangle area) override {
1342 vector<Vec2> points;
1343 for (Vec2 v : area)
1344 if (builder->hasAttrib(v, SquareAttrib::CONNECT_ROAD)) {
1345 points.push_back(v);
1346 INFO << "Connecting point " << v;
1347 }
1348 for (int ind : Range(1, points.size())) {
1349 Vec2 p1 = points[ind];
1350 Vec2 p2 = points[ind - 1];
1351 ShortestPath path(area,
1352 [=](Vec2 pos) { return (pos == p1 || pos == p2) ? 1 : getValue(builder, pos); },
1353 [] (Vec2 v) { return v.length4(); },
1354 Vec2::directions4(builder->getRandom()), p1, p2);
1355 for (Vec2 v = p2; v != p1; v = path.getNextMove(v)) {
1356 if (!path.isReachable(v))
1357 failGen();
1358 auto roadType = getRoadType(builder, v);
1359 if (v != p2 && v != p1 && !builder->isFurnitureType(v, roadType))
1360 builder->putFurniture(v, roadType);
1361 }
1362 }
1363 }
1364 };
1365
1366 class StartingPos : public LevelMaker {
1367 public:
1368
StartingPos(Predicate pred,StairKey key)1369 StartingPos(Predicate pred, StairKey key) : predicate(pred), stairKey(key) {}
1370
make(LevelBuilder * builder,Rectangle area)1371 virtual void make(LevelBuilder* builder, Rectangle area) override {
1372 for (Vec2 pos : area)
1373 if (predicate.apply(builder, pos))
1374 builder->modSquare(pos)->setLandingLink(stairKey);
1375 }
1376
1377 private:
1378 Predicate predicate;
1379 StairKey stairKey;
1380 };
1381
1382 class TransferPos : public LevelMaker {
1383 public:
1384
TransferPos(Predicate pred,StairKey key,int w)1385 TransferPos(Predicate pred, StairKey key, int w) : predicate(pred), stairKey(key), width(w) {}
1386
make(LevelBuilder * builder,Rectangle area)1387 virtual void make(LevelBuilder* builder, Rectangle area) override {
1388 bool found = false;
1389 for (Vec2 pos : area)
1390 if (((pos.x - area.left() < width) || (pos.y - area.top() < width) ||
1391 (area.right() - pos.x <= width) || (area.bottom() - pos.y <= width)) &&
1392 predicate.apply(builder, pos)) {
1393 builder->modSquare(pos)->setLandingLink(stairKey);
1394 found = true;
1395 }
1396 checkGen(found);
1397 }
1398
1399 private:
1400 Predicate predicate;
1401 StairKey stairKey;
1402 int width;
1403 };
1404
1405 class Forrest : public LevelMaker {
1406 public:
Forrest(double _ratio,double _density,FurnitureType _onType,FurnitureFactory _factory)1407 Forrest(double _ratio, double _density, FurnitureType _onType, FurnitureFactory _factory)
1408 : ratio(_ratio), density(_density), factory(_factory), onType(_onType) {}
1409
make(LevelBuilder * builder,Rectangle area)1410 virtual void make(LevelBuilder* builder, Rectangle area) override {
1411 Table<double> wys = genNoiseMap(builder->getRandom(), area, {0, 0, 0, 0, 0}, 0.65);
1412 vector<double> values = sortedValues(wys);
1413 double cutoff = values[values.size() * ratio];
1414 for (Vec2 v : area)
1415 if (builder->isFurnitureType(v, onType) && builder->canNavigate(v, {MovementTrait::WALK}) && wys[v] < cutoff) {
1416 if (builder->getRandom().getDouble() <= density)
1417 builder->putFurniture(v, factory);
1418 builder->addAttrib(v, SquareAttrib::FORREST);
1419 }
1420 }
1421
1422 private:
1423 double ratio;
1424 double density;
1425 FurnitureFactory factory;
1426 FurnitureType onType;
1427 };
1428
1429 class PlaceCollective : public LevelMaker {
1430 public:
PlaceCollective(CollectiveBuilder * c,Predicate pred=Predicate::alwaysTrue ())1431 PlaceCollective(CollectiveBuilder* c, Predicate pred = Predicate::alwaysTrue())
1432 : collective(NOTNULL(c)), predicate(pred) {}
1433
make(LevelBuilder * builder,Rectangle area)1434 virtual void make(LevelBuilder* builder, Rectangle area) override {
1435 if (!collective->hasCentralPoint())
1436 collective->setCentralPoint(builder->toGlobalCoordinates(area).middle());
1437 collective->addArea(builder->toGlobalCoordinates(area.getAllSquares()
1438 .filter([&](Vec2 pos) { return predicate.apply(builder, pos); })));
1439 builder->addCollective(collective);
1440 }
1441
1442 private:
1443 CollectiveBuilder* collective;
1444 Predicate predicate;
1445 };
1446
1447 class ForEachSquare : public LevelMaker {
1448 public:
ForEachSquare(function<void (LevelBuilder *,Vec2 pos)> f,Predicate _onPred=Predicate::alwaysTrue ())1449 ForEachSquare(function<void(LevelBuilder*, Vec2 pos)> f, Predicate _onPred = Predicate::alwaysTrue())
1450 : fun(f), onPred(_onPred) {}
1451
make(LevelBuilder * builder,Rectangle area)1452 virtual void make(LevelBuilder* builder, Rectangle area) override {
1453 for (Vec2 v : area)
1454 if (onPred.apply(builder, v))
1455 fun(builder, v);
1456 }
1457
1458 protected:
1459 function<void(LevelBuilder*, Vec2 pos)> fun;
1460 Predicate onPred;
1461 };
1462
1463 class AddAttrib : public ForEachSquare {
1464 public:
AddAttrib(SquareAttrib attr,Predicate onPred=Predicate::alwaysTrue ())1465 AddAttrib(SquareAttrib attr, Predicate onPred = Predicate::alwaysTrue())
1466 : ForEachSquare([attr](LevelBuilder* b, Vec2 pos) { b->addAttrib(pos, attr); }, onPred) {}
1467 };
1468
1469 class RemoveAttrib : public ForEachSquare {
1470 public:
RemoveAttrib(SquareAttrib attr,Predicate onPred=Predicate::alwaysTrue ())1471 RemoveAttrib(SquareAttrib attr, Predicate onPred = Predicate::alwaysTrue())
1472 : ForEachSquare([attr](LevelBuilder* b, Vec2 pos) { b->removeAttrib(pos, attr); }, onPred) {}
1473 };
1474
1475 enum class StairDirection {
1476 UP, DOWN
1477 };
1478
1479 class Stairs : public LevelMaker {
1480 public:
Stairs(StairDirection dir,StairKey k,Predicate onPred,optional<SquareAttrib> _setAttr=none)1481 Stairs(StairDirection dir, StairKey k, Predicate onPred, optional<SquareAttrib> _setAttr = none)
1482 : direction(dir), key(k), onPredicate(onPred), setAttr(_setAttr) {}
1483
make(LevelBuilder * builder,Rectangle area)1484 virtual void make(LevelBuilder* builder, Rectangle area) override {
1485 auto type = direction == StairDirection::DOWN ? FurnitureType::DOWN_STAIRS : FurnitureType::UP_STAIRS;
1486 vector<Vec2> allPos;
1487 for (Vec2 v : area)
1488 if (onPredicate.apply(builder, v) && builder->canPutFurniture(v, Furniture::getLayer(type)))
1489 allPos.push_back(v);
1490 checkGen(allPos.size() > 0);
1491 Vec2 pos = allPos[builder->getRandom().get(allPos.size())];
1492 builder->putFurniture(pos, FurnitureParams{type, TribeId::getHostile()});
1493 builder->setLandingLink(pos, key);
1494 }
1495
1496 private:
1497 StairDirection direction;
1498 StairKey key;
1499 Predicate onPredicate;
1500 optional<SquareAttrib> setAttr;
1501 };
1502
1503 class ShopMaker : public LevelMaker {
1504 public:
ShopMaker(ItemFactory _factory,TribeId _tribe,int _numItems,BuildingType _building)1505 ShopMaker(ItemFactory _factory, TribeId _tribe, int _numItems, BuildingType _building)
1506 : factory(_factory), tribe(_tribe), numItems(_numItems), building(_building) {}
1507
make(LevelBuilder * builder,Rectangle area)1508 virtual void make(LevelBuilder* builder, Rectangle area) override {
1509 PCreature shopkeeper = CreatureFactory::getShopkeeper(builder->toGlobalCoordinates(area), tribe);
1510 vector<Vec2> pos;
1511 for (Vec2 v : area)
1512 if (builder->canNavigate(v, MovementTrait::WALK) && builder->isFurnitureType(v, building.floorInside))
1513 pos.push_back(v);
1514 builder->putCreature(pos[builder->getRandom().get(pos.size())], std::move(shopkeeper));
1515 builder->putFurniture(pos[builder->getRandom().get(pos.size())], FurnitureParams{FurnitureType::GROUND_TORCH, tribe});
1516 for (int i : Range(numItems)) {
1517 Vec2 v = pos[builder->getRandom().get(pos.size())];
1518 builder->putItems(v, factory.random());
1519 }
1520 }
1521
1522 private:
1523 ItemFactory factory;
1524 TribeId tribe;
1525 int numItems;
1526 BuildingType building;
1527 };
1528
1529 class LevelExit : public LevelMaker {
1530 public:
LevelExit(FurnitureFactory e,int _minCornerDist=1)1531 LevelExit(FurnitureFactory e, int _minCornerDist = 1)
1532 : exit(e), minCornerDist(_minCornerDist) {}
1533
make(LevelBuilder * builder,Rectangle area)1534 virtual void make(LevelBuilder* builder, Rectangle area) override {
1535 Vec2 pos = getRandomExit(builder->getRandom(), area, minCornerDist);
1536 builder->putFurniture(pos, exit);
1537 }
1538
1539 private:
1540 FurnitureFactory exit;
1541 optional<SquareAttrib> attrib;
1542 int minCornerDist;
1543 };
1544
1545 class Division : public LevelMaker {
1546 public:
Division(double _vRatio,double _hRatio,PLevelMaker _upperLeft,PLevelMaker _upperRight,PLevelMaker _lowerLeft,PLevelMaker _lowerRight,optional<SquareChange> _wall=none)1547 Division(double _vRatio, double _hRatio,
1548 PLevelMaker _upperLeft, PLevelMaker _upperRight, PLevelMaker _lowerLeft, PLevelMaker _lowerRight,
1549 optional<SquareChange> _wall = none) : vRatio(_vRatio), hRatio(_hRatio),
1550 upperLeft(std::move(_upperLeft)), upperRight(std::move(_upperRight)), lowerLeft(std::move(_lowerLeft)),
1551 lowerRight(std::move(_lowerRight)), wall(_wall) {}
1552
Division(double _hRatio,PLevelMaker _left,PLevelMaker _right,optional<SquareChange> _wall=none)1553 Division(double _hRatio, PLevelMaker _left, PLevelMaker _right, optional<SquareChange> _wall = none)
1554 : vRatio(-1), hRatio(_hRatio), upperLeft(std::move(_left)), upperRight(std::move(_right)), wall(_wall) {}
1555
Division(bool,double _vRatio,PLevelMaker _top,PLevelMaker _bottom,optional<SquareChange> _wall=none)1556 Division(bool, double _vRatio, PLevelMaker _top, PLevelMaker _bottom, optional<SquareChange> _wall = none)
1557 : vRatio(_vRatio), hRatio(-1), upperLeft(std::move(_top)), lowerLeft(std::move(_bottom)), wall(_wall) {}
1558
makeHorizDiv(LevelBuilder * builder,Rectangle area)1559 void makeHorizDiv(LevelBuilder* builder, Rectangle area) {
1560 int hDiv = area.left() + min(area.width() - 1, max(1, (int) (hRatio * area.width())));
1561 if (upperLeft)
1562 upperLeft->make(builder, Rectangle(area.left(), area.top(), hDiv, area.bottom()));
1563 if (upperRight)
1564 upperRight->make(builder, Rectangle(hDiv + (wall ? 1 : 0), area.top(), area.right(), area.bottom()));
1565 if (wall)
1566 for (int i : Range(area.top(), area.bottom()))
1567 wall->apply(builder, Vec2(hDiv, i));
1568 }
1569
makeVertDiv(LevelBuilder * builder,Rectangle area)1570 void makeVertDiv(LevelBuilder* builder, Rectangle area) {
1571 int vDiv = area.top() + min(area.height() - 1, max(1, (int) (vRatio * area.height())));
1572 if (upperLeft)
1573 upperLeft->make(builder, Rectangle(area.left(), area.top(), area.right(), vDiv));
1574 if (lowerLeft)
1575 lowerLeft->make(builder, Rectangle(area.left(), vDiv + (wall ? 1 : 0), area.right(), area.bottom()));
1576 if (wall)
1577 for (int i : Range(area.left(), area.right()))
1578 wall->apply(builder, Vec2(i, vDiv));
1579 }
1580
makeDiv(LevelBuilder * builder,Rectangle area)1581 void makeDiv(LevelBuilder* builder, Rectangle area) {
1582 int vDiv = area.top() + min(area.height() - 1, max(1, (int) (vRatio * area.height())));
1583 int hDiv = area.left() + min(area.width() - 1, max(1, (int) (hRatio * area.width())));
1584 int wallSpace = wall ? 1 : 0;
1585 if (upperLeft)
1586 upperLeft->make(builder, Rectangle(area.left(), area.top(), hDiv, vDiv));
1587 if (upperRight)
1588 upperRight->make(builder, Rectangle(hDiv + wallSpace, area.top(), area.right(), vDiv));
1589 if (lowerLeft)
1590 lowerLeft->make(builder, Rectangle(area.left(), vDiv + wallSpace, hDiv, area.bottom()));
1591 if (lowerRight)
1592 lowerRight->make(builder, Rectangle(hDiv + wallSpace, vDiv + wallSpace, area.right(), area.bottom()));
1593 if (wall) {
1594 for (int i : Range(area.top(), area.bottom()))
1595 wall->apply(builder, Vec2(hDiv, i));
1596 for (int i : Range(area.left(), area.right()))
1597 wall->apply(builder, Vec2(i, vDiv));
1598 }
1599 }
1600
make(LevelBuilder * builder,Rectangle area)1601 virtual void make(LevelBuilder* builder, Rectangle area) override {
1602 if (vRatio < 0)
1603 makeHorizDiv(builder, area);
1604 else if (hRatio < 0)
1605 makeVertDiv(builder, area);
1606 else
1607 makeDiv(builder, area);
1608 }
1609
1610 private:
1611 double vRatio, hRatio;
1612 PLevelMaker upperLeft;
1613 PLevelMaker upperRight;
1614 PLevelMaker lowerLeft;
1615 PLevelMaker lowerRight;
1616 optional<SquareChange> wall;
1617 };
1618
1619 class AreaCorners : public LevelMaker {
1620 public:
AreaCorners(PLevelMaker _maker,Vec2 _size,vector<PLevelMaker> _insideMakers)1621 AreaCorners(PLevelMaker _maker, Vec2 _size, vector<PLevelMaker> _insideMakers)
1622 : maker(std::move(_maker)), size(_size), insideMakers(std::move(_insideMakers)) {}
1623
getCorners(Rectangle area)1624 vector<Rectangle> getCorners(Rectangle area) {
1625 return {
1626 Rectangle(area.topLeft(), area.topLeft() + size),
1627 Rectangle(area.topRight() - Vec2(size.x, 0), area.topRight() + Vec2(0, size.y)),
1628 Rectangle(area.bottomLeft() - Vec2(0, size.y), area.bottomLeft() + Vec2(size.x, 0)),
1629 Rectangle(area.bottomRight() - size, area.bottomRight())};
1630 }
1631
make(LevelBuilder * builder,Rectangle area)1632 virtual void make(LevelBuilder* builder, Rectangle area) override {
1633 vector<Rectangle> corners = builder->getRandom().permutation(getCorners(area));
1634 for (int i : All(corners)) {
1635 maker->make(builder, corners[i]);
1636 if (i < insideMakers.size())
1637 insideMakers[i]->make(builder, corners[i]);
1638 }
1639 }
1640
1641 private:
1642 PLevelMaker maker;
1643 Vec2 size;
1644 vector<PLevelMaker> insideMakers;
1645 };
1646
1647 class CastleExit : public LevelMaker {
1648 public:
CastleExit(TribeId _guardTribe,BuildingType _building,CreatureId _guardId)1649 CastleExit(TribeId _guardTribe, BuildingType _building, CreatureId _guardId)
1650 : guardTribe(_guardTribe), building(_building), guardId(_guardId) {}
1651
make(LevelBuilder * builder,Rectangle area)1652 virtual void make(LevelBuilder* builder, Rectangle area) override {
1653 Vec2 loc(area.right() - 1, area.middle().y);
1654 builder->resetFurniture(loc + Vec2(2, 0), building.floorInside);
1655 if (building.door)
1656 builder->putFurniture(loc + Vec2(2, 0), *building.door);
1657 builder->addAttrib(loc + Vec2(2, 0), SquareAttrib::CONNECT_ROAD);
1658 vector<Vec2> walls { Vec2(1, -2), Vec2(2, -2), Vec2(2, -1), Vec2(2, 1), Vec2(2, 2), Vec2(1, 2)};
1659 for (Vec2 v : walls)
1660 builder->putFurniture(loc + v, building.wall);
1661 vector<Vec2> floor { Vec2(1, -1), Vec2(1, 0), Vec2(1, 1), Vec2(0, -1), Vec2(0, 0), Vec2(0, 1) };
1662 for (Vec2 v : floor)
1663 builder->resetFurniture(loc + v, building.floorInside);
1664 vector<Vec2> guardPos { Vec2(1, 1), Vec2(1, -1) };
1665 for (Vec2 pos : guardPos) {
1666 builder->putCreature(loc + pos, CreatureFactory::fromId(guardId, guardTribe,
1667 MonsterAIFactory::stayInLocation(
1668 builder->toGlobalCoordinates(Rectangle(loc + pos, loc + pos + Vec2(1, 1))), false)));
1669 }
1670 }
1671
1672 private:
1673 TribeId guardTribe;
1674 BuildingType building;
1675 CreatureId guardId;
1676 };
1677
1678 class AddMapBorder : public LevelMaker {
1679 public:
AddMapBorder(int w)1680 AddMapBorder(int w) : width(w) {}
1681
make(LevelBuilder * builder,Rectangle area)1682 virtual void make(LevelBuilder* builder, Rectangle area) override {
1683 for (Vec2 v : area)
1684 if (!v.inRectangle(area.minusMargin(width)))
1685 builder->setUnavailable(v);
1686 }
1687
1688 private:
1689 int width;
1690 };
1691
1692 class BorderGuard : public LevelMaker {
1693 public:
1694
BorderGuard(PLevelMaker inside,SquareChange c)1695 BorderGuard(PLevelMaker inside, SquareChange c)
1696 : change(c), insideMaker(std::move(inside)) {}
1697
make(LevelBuilder * builder,Rectangle area)1698 virtual void make(LevelBuilder* builder, Rectangle area) override {
1699 for (int i : Range(area.left(), area.right())) {
1700 change.apply(builder, Vec2(i, area.top()));
1701 change.apply(builder, Vec2(i, area.bottom() - 1));
1702 }
1703 for (int i : Range(area.top(), area.bottom())) {
1704 change.apply(builder, Vec2(area.left(), i));
1705 change.apply(builder, Vec2(area.right() - 1, i));
1706 }
1707 insideMaker->make(builder, Rectangle(area.left() + 1, area.top() + 1, area.right() - 1, area.bottom() - 1));
1708 }
1709
1710 private:
1711 SquareChange change;
1712 PLevelMaker insideMaker;
1713
1714 };
1715 }
1716
1717
1718
stockpileMaker(StockpileInfo info)1719 static PMakerQueue stockpileMaker(StockpileInfo info) {
1720 auto floor = FurnitureType::FLOOR_STONE1;
1721 ItemFactory items;
1722 optional<FurnitureType> furniture;
1723 switch (info.type) {
1724 case StockpileInfo::GOLD:
1725 furniture = FurnitureType::TREASURE_CHEST;
1726 items = ItemFactory::singleType(ItemType::GoldPiece{});
1727 break;
1728 case StockpileInfo::MINERALS:
1729 items = ItemFactory::minerals();
1730 break;
1731 }
1732 auto queue = unique<MakerQueue>();
1733 queue->addMaker(unique<Empty>(floor));
1734 if (furniture)
1735 queue->addMaker(unique<Empty>(SquareChange(*furniture)));
1736 queue->addMaker(unique<Items>(items, info.number, info.number + 1, Predicate::alwaysTrue(), !!furniture));
1737 return queue;
1738 }
1739
cryptLevel(RandomGen & random,SettlementInfo info)1740 PLevelMaker LevelMaker::cryptLevel(RandomGen& random, SettlementInfo info) {
1741 auto queue = unique<MakerQueue>();
1742 BuildingType building = getBuildingInfo(info);
1743 queue->addMaker(unique<Empty>(SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN)));
1744 queue->addMaker(unique<PlaceCollective>(info.collective));
1745 queue->addMaker(unique<RoomMaker>(random.get(8, 15), 3, 5));
1746 queue->addMaker(unique<Connector>(building.door, 0));
1747 if (info.furniture)
1748 queue->addMaker(unique<Furnitures>(Predicate::attrib(SquareAttrib::EMPTY_ROOM), 0.3, *info.furniture));
1749 for (StairKey key : info.downStairs)
1750 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::type(FurnitureType::FLOOR)));
1751 for (StairKey key : info.upStairs)
1752 queue->addMaker(unique<Stairs>(StairDirection::UP, key, Predicate::type(FurnitureType::FLOOR)));
1753 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
1754 queue->addMaker(unique<Items>(ItemFactory::dungeon(), 5, 10));
1755 return unique<BorderGuard>(std::move(queue), SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN));
1756 }
1757
mazeLevel(RandomGen & random,SettlementInfo info)1758 PLevelMaker LevelMaker::mazeLevel(RandomGen& random, SettlementInfo info) {
1759 auto queue = unique<MakerQueue>();
1760 BuildingType building = getBuildingInfo(info);
1761 queue->addMaker(unique<Empty>(SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN)));
1762 queue->addMaker(unique<RoomMaker>(random.get(8, 15), 3, 5));
1763 queue->addMaker(unique<Connector>(building.door, 0.75));
1764 if (info.furniture)
1765 queue->addMaker(unique<Furnitures>(Predicate::attrib(SquareAttrib::EMPTY_ROOM), 0.3, *info.furniture));
1766 for (StairKey key : info.downStairs)
1767 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::type(FurnitureType::FLOOR)));
1768 for (StairKey key : info.upStairs)
1769 queue->addMaker(unique<Stairs>(StairDirection::UP, key, Predicate::type(FurnitureType::FLOOR)));
1770 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
1771 queue->addMaker(unique<Items>(ItemFactory::dungeon(), 5, 10));
1772 return unique<BorderGuard>(std::move(queue), SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN));
1773 }
1774
getElderRoom(SettlementInfo info)1775 static PMakerQueue getElderRoom(SettlementInfo info) {
1776 BuildingType building = getBuildingInfo(info);
1777 PMakerQueue elderRoom = unique<MakerQueue>();
1778 if (info.elderLoot)
1779 elderRoom->addMaker(unique<Items>(ItemFactory::singleType(*info.elderLoot), 1, 2));
1780 return elderRoom;
1781 }
1782
village2(RandomGen & random,SettlementInfo info)1783 static PMakerQueue village2(RandomGen& random, SettlementInfo info) {
1784 BuildingType building = getBuildingInfo(info);
1785 auto queue = unique<MakerQueue>();
1786 queue->addMaker(unique<PlaceCollective>(info.collective));
1787 vector<PLevelMaker> insideMakers = makeVec<PLevelMaker>(getElderRoom(info));
1788 for (auto& elem : info.stockpiles)
1789 insideMakers.push_back(stockpileMaker(elem));
1790 if (info.shopFactory)
1791 insideMakers.push_back(unique<ShopMaker>(*info.shopFactory, info.tribe, random.get(8, 16), building));
1792 queue->addMaker(unique<Buildings>(6, 10, 3, 4, building, false, std::move(insideMakers)));
1793 if (info.furniture)
1794 queue->addMaker(unique<Furnitures>(Predicate::attrib(SquareAttrib::EMPTY_ROOM), 0.3, *info.furniture));
1795 if (info.outsideFeatures)
1796 queue->addMaker(unique<Furnitures>(Predicate::type(building.floorOutside), 0.01, *info.outsideFeatures));
1797 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorOutside)));
1798 return queue;
1799 }
1800
village(RandomGen & random,SettlementInfo info,int minRooms,int maxRooms)1801 static PMakerQueue village(RandomGen& random, SettlementInfo info, int minRooms, int maxRooms) {
1802 BuildingType building = getBuildingInfo(info);
1803 auto queue = unique<MakerQueue>();
1804 queue->addMaker(unique<PlaceCollective>(info.collective));
1805 queue->addMaker(unique<UniformBlob>(building.floorOutside, none, none, 0.6));
1806 vector<PLevelMaker> insideMakers = makeVec<PLevelMaker>(
1807 // hatchery(CreatureFactory::singleType(info.tribe, CreatureId::PIG), random.get(2, 5)),
1808 getElderRoom(info));
1809 if (info.shopFactory)
1810 insideMakers.push_back(unique<ShopMaker>(*info.shopFactory, info.tribe, random.get(8, 16), building));
1811 for (auto& elem : info.stockpiles)
1812 insideMakers.push_back(stockpileMaker(elem));
1813 queue->addMaker(unique<Buildings>(minRooms, maxRooms, 3, 7, building, true, std::move(insideMakers)));
1814 if (info.furniture)
1815 queue->addMaker(unique<Furnitures>(Predicate::attrib(SquareAttrib::EMPTY_ROOM), 0.3, *info.furniture));
1816 if (info.outsideFeatures)
1817 queue->addMaker(unique<Furnitures>(
1818 Predicate::type(building.floorOutside) &&
1819 Predicate::attrib(SquareAttrib::BUILDINGS_CENTER), 0.2, *info.outsideFeatures, SquareAttrib::NO_ROAD));
1820 for (StairKey key : info.downStairs)
1821 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::attrib(SquareAttrib::EMPTY_ROOM)));
1822 for (StairKey key : info.upStairs)
1823 queue->addMaker(unique<Stairs>(StairDirection::UP, key, Predicate::attrib(SquareAttrib::EMPTY_ROOM)));
1824 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorOutside)));
1825 return queue;
1826 }
1827
cottage(SettlementInfo info)1828 static PMakerQueue cottage(SettlementInfo info) {
1829 BuildingType building = getBuildingInfo(info);
1830 auto queue = unique<MakerQueue>();
1831 queue->addMaker(unique<Empty>(building.floorOutside));
1832 auto room = getElderRoom(info);
1833 for (StairKey key : info.upStairs)
1834 room->addMaker(unique<Stairs>(StairDirection::UP, key, Predicate::type(building.floorInside), none));
1835 for (StairKey key : info.downStairs)
1836 room->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::type(building.floorInside), none));
1837 if (info.furniture)
1838 room->addMaker(unique<Furnitures>(Predicate::type(building.floorInside), 0.3, *info.furniture));
1839 if (info.outsideFeatures)
1840 room->addMaker(unique<Furnitures>(Predicate::type(building.floorOutside), 0.1, *info.outsideFeatures));
1841 queue->addMaker(unique<Buildings>(1, 2, 5, 7, building, false, std::move(room), false));
1842 queue->addMaker(unique<PlaceCollective>(info.collective));
1843 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorOutside)));
1844 return queue;
1845 }
1846
forrestCottage(SettlementInfo info)1847 static PMakerQueue forrestCottage(SettlementInfo info) {
1848 BuildingType building = getBuildingInfo(info);
1849 auto queue = unique<MakerQueue>();
1850 auto room = getElderRoom(info);
1851 for (StairKey key : info.upStairs)
1852 room->addMaker(unique<Stairs>(StairDirection::UP, key, Predicate::type(building.floorInside), none));
1853 for (StairKey key : info.downStairs)
1854 room->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::type(building.floorInside), none));
1855 if (info.furniture)
1856 room->addMaker(unique<Furnitures>(Predicate::type(building.floorInside), 0.3, *info.furniture));
1857 if (info.outsideFeatures)
1858 room->addMaker(unique<Furnitures>(Predicate::type(building.floorOutside), 0.1, *info.outsideFeatures));
1859 queue->addMaker(unique<Buildings>(1, 3, 3, 4, building, false, std::move(room), false));
1860 queue->addMaker(unique<PlaceCollective>(info.collective));
1861 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorOutside)));
1862 return queue;
1863 }
1864
castle(RandomGen & random,SettlementInfo info)1865 static PMakerQueue castle(RandomGen& random, SettlementInfo info) {
1866 BuildingType building = getBuildingInfo(info);
1867 auto castleRoom = [&] { return unique<BorderGuard>(unique<Empty>(SquareChange::reset(building.floorInside).add(SquareAttrib::EMPTY_ROOM)),
1868 SquareChange(building.wall, SquareAttrib::ROOM_WALL)); };
1869 auto leftSide = unique<MakerQueue>();
1870 leftSide->addMaker(unique<Division>(true, random.getDouble(0.5, 0.5),
1871 unique<Margin>(1, -1, -1, 1, castleRoom()), unique<Margin>(1, 1, -1, -1, castleRoom())));
1872 leftSide->addMaker(getElderRoom(info));
1873 auto inside = unique<MakerQueue>();
1874 vector<PLevelMaker> insideMakers;
1875 if (info.shopFactory)
1876 insideMakers.push_back(unique<ShopMaker>(*info.shopFactory, info.tribe, random.get(8, 16), building));
1877 inside->addMaker(unique<Division>(random.getDouble(0.25, 0.4), std::move(leftSide),
1878 unique<Buildings>(1, 3, 3, 6, building, false, std::move(insideMakers), false),
1879 SquareChange(building.wall, SquareAttrib::ROOM_WALL)));
1880 auto insidePlusWall = unique<MakerQueue>();
1881 if (info.outsideFeatures)
1882 inside->addMaker(unique<Furnitures>(Predicate::type(building.floorOutside), 0.03, *info.outsideFeatures));
1883 if (info.furniture)
1884 inside->addMaker(unique<Furnitures>(Predicate::attrib(SquareAttrib::EMPTY_ROOM), 0.35, *info.furniture));
1885 insidePlusWall->addMaker(unique<Empty>(SquareChange::reset(building.floorOutside)));
1886 insidePlusWall->addMaker(unique<BorderGuard>(std::move(inside), building.wall));
1887 auto queue = unique<MakerQueue>();
1888 int insideMargin = 2;
1889 queue->addMaker(unique<Margin>(insideMargin, unique<PlaceCollective>(info.collective)));
1890 queue->addMaker(unique<Margin>(insideMargin, std::move(insidePlusWall)));
1891 vector<PLevelMaker> cornerMakers;
1892 for (auto& elem : info.stockpiles)
1893 cornerMakers.push_back(unique<Margin>(1, stockpileMaker(elem)));
1894 queue->addMaker(unique<AreaCorners>(
1895 unique<BorderGuard>(unique<Empty>(SquareChange::reset(building.floorInside).add(SquareAttrib::CASTLE_CORNER)),
1896 SquareChange(building.wall, SquareAttrib::ROOM_WALL)),
1897 Vec2(5, 5),
1898 std::move(cornerMakers)));
1899 queue->addMaker(unique<Margin>(insideMargin, unique<Connector>(building.door, 1, 18)));
1900 queue->addMaker(unique<Margin>(insideMargin, unique<CastleExit>(info.tribe, building, *info.guardId)));
1901 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorOutside)));
1902 for (StairKey key : info.downStairs)
1903 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key,
1904 Predicate::attrib(SquareAttrib::CASTLE_CORNER) &&
1905 Predicate::type(building.floorInside), none));
1906 queue->addMaker(unique<StartingPos>(Predicate::type(FurnitureType::MUD), StairKey::heroSpawn()));
1907 queue->addMaker(unique<AddAttrib>(SquareAttrib::NO_DIG, Predicate::type(building.wall)));
1908 return queue;
1909 }
1910
castle2(RandomGen & random,SettlementInfo info)1911 static PMakerQueue castle2(RandomGen& random, SettlementInfo info) {
1912 BuildingType building = getBuildingInfo(info);
1913 auto inside = unique<MakerQueue>();
1914 auto insideMaker = unique<MakerQueue>(
1915 getElderRoom(info),
1916 stockpileMaker(info.stockpiles.getOnlyElement()));
1917 inside->addMaker(unique<Buildings>(1, 2, 3, 4, building, false, std::move(insideMaker), false));
1918 auto insidePlusWall = unique<MakerQueue>();
1919 insidePlusWall->addMaker(unique<Empty>(building.floorOutside));
1920 insidePlusWall->addMaker(unique<BorderGuard>(std::move(inside), building.wall));
1921 auto queue = unique<MakerQueue>();
1922 queue->addMaker(unique<PlaceCollective>(info.collective));
1923 queue->addMaker(std::move(insidePlusWall));
1924 queue->addMaker(unique<Connector>(building.door, 1, 18));
1925 queue->addMaker(unique<CastleExit>(info.tribe, building, *info.guardId));
1926 if (info.outsideFeatures)
1927 queue->addMaker(unique<Furnitures>(Predicate::type(building.floorOutside), 0.05, *info.outsideFeatures));
1928 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorOutside)));
1929 queue->addMaker(unique<AddAttrib>(SquareAttrib::NO_DIG, Predicate::type(building.wall)));
1930 return queue;
1931 }
1932
tower(RandomGen & random,SettlementInfo info,bool withExit)1933 static PLevelMaker tower(RandomGen& random, SettlementInfo info, bool withExit) {
1934 BuildingType building = getBuildingInfo(info);
1935 auto queue = unique<MakerQueue>();
1936 queue->addMaker(unique<Empty>(SquareChange(FurnitureType::FLOOR, building.wall)));
1937 if (withExit)
1938 queue->addMaker(unique<LevelExit>(*building.door, 2));
1939 queue->addMaker(unique<Margin>(1, unique<Empty>(SquareChange::reset(building.floorInside))));
1940 queue->addMaker(unique<Margin>(1, unique<AddAttrib>(SquareAttrib::ROOM)));
1941 queue->addMaker(unique<RemoveAttrib>(SquareAttrib::ROAD_CUT_THRU));
1942 if (info.collective)
1943 queue->addMaker(unique<PlaceCollective>(info.collective));
1944 PLevelMaker downStairs;
1945 for (StairKey key : info.downStairs)
1946 downStairs = unique<Stairs>(StairDirection::DOWN, key, Predicate::type(building.floorInside));
1947 PLevelMaker upStairs;
1948 for (StairKey key : info.upStairs)
1949 upStairs = unique<Stairs>(StairDirection::UP, key, Predicate::type(building.floorInside));
1950 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::type(building.floorInside)));
1951 queue->addMaker(unique<Division>(0.5, 0.5, std::move(upStairs), nullptr, nullptr, std::move(downStairs)));
1952 if (info.furniture)
1953 queue->addMaker(unique<Furnitures>(Predicate::type(building.floorInside), 0.5, *info.furniture));
1954 return std::move(queue);
1955 }
1956
towerLevel(RandomGen & random,SettlementInfo info)1957 PLevelMaker LevelMaker::towerLevel(RandomGen& random, SettlementInfo info) {
1958 return PLevelMaker(tower(random, info, false));
1959 }
1960
getSize(RandomGen & random,SettlementType type)1961 Vec2 getSize(RandomGen& random, SettlementType type) {
1962 switch (type) {
1963 case SettlementType::WITCH_HOUSE:
1964 case SettlementType::CEMETERY:
1965 case SettlementType::MOUNTAIN_LAKE:
1966 case SettlementType::SMALL_VILLAGE:
1967 case SettlementType::SWAMP: return {random.get(12, 16), random.get(12, 16)};
1968 case SettlementType::COTTAGE: return {random.get(8, 10), random.get(8, 10)};
1969 case SettlementType::FORREST_COTTAGE: return {15, 15};
1970 case SettlementType::FOREST: return {18, 13};
1971 case SettlementType::FORREST_VILLAGE: return {20, 20};
1972 case SettlementType::VILLAGE:
1973 case SettlementType::ANT_NEST:
1974 case SettlementType::CASTLE: return {30, 20};
1975 case SettlementType::CASTLE2: return {13, 13};
1976 case SettlementType::MINETOWN: return {30, 20};
1977 case SettlementType::SMALL_MINETOWN: return {15, 15};
1978 case SettlementType::CAVE: return {12, 12};
1979 case SettlementType::SPIDER_CAVE: return {12, 12};
1980 case SettlementType::VAULT: return {10, 10};
1981 case SettlementType::TOWER: return {5, 5};
1982 case SettlementType::ISLAND_VAULT_DOOR:
1983 case SettlementType::ISLAND_VAULT: return {6, 6};
1984 }
1985 }
1986
getSettlementPredicate(SettlementType type)1987 RandomLocations::LocationPredicate getSettlementPredicate(SettlementType type) {
1988 switch (type) {
1989 case SettlementType::FOREST:
1990 case SettlementType::FORREST_COTTAGE:
1991 case SettlementType::FORREST_VILLAGE:
1992 return !Predicate::attrib(SquareAttrib::RIVER) && Predicate::attrib(SquareAttrib::FORREST);
1993 case SettlementType::CAVE:
1994 return RandomLocations::LocationPredicate(
1995 Predicate::type(FurnitureType::MOUNTAIN), Predicate::attrib(SquareAttrib::HILL), 5, 15);
1996 case SettlementType::VAULT:
1997 case SettlementType::ANT_NEST:
1998 case SettlementType::SMALL_MINETOWN:
1999 case SettlementType::MINETOWN:
2000 return Predicate::type(FurnitureType::MOUNTAIN);
2001 case SettlementType::SPIDER_CAVE:
2002 return RandomLocations::LocationPredicate(
2003 Predicate::type(FurnitureType::MOUNTAIN),
2004 Predicate::attrib(SquareAttrib::CONNECTOR), 1, 2);
2005 case SettlementType::MOUNTAIN_LAKE:
2006 case SettlementType::ISLAND_VAULT:
2007 return Predicate::attrib(SquareAttrib::MOUNTAIN);
2008 case SettlementType::ISLAND_VAULT_DOOR:
2009 return RandomLocations::LocationPredicate(
2010 Predicate::attrib(SquareAttrib::MOUNTAIN) && !Predicate::attrib(SquareAttrib::RIVER),
2011 Predicate::attrib(SquareAttrib::RIVER), 10, 30);
2012 default:
2013 return Predicate::attrib(SquareAttrib::LOWLAND) &&
2014 !Predicate::attrib(SquareAttrib::RIVER);
2015 }
2016 }
2017
genericMineTownMaker(RandomGen & random,SettlementInfo info,int numCavern,int maxCavernSize,int numRooms,int minRoomSize,int maxRoomSize,bool connect)2018 static PMakerQueue genericMineTownMaker(RandomGen& random, SettlementInfo info, int numCavern, int maxCavernSize,
2019 int numRooms, int minRoomSize, int maxRoomSize, bool connect) {
2020 BuildingType building = getBuildingInfo(info);
2021 auto queue = unique<MakerQueue>();
2022 vector<PLevelMaker> vCavern;
2023 vector<pair<int, int>> sizes;
2024 for (int i : Range(numCavern)) {
2025 sizes.push_back(make_pair(random.get(5, maxCavernSize), random.get(5, maxCavernSize)));
2026 vCavern.push_back(unique<UniformBlob>(building.floorInside));
2027 }
2028 queue->addMaker(unique<RandomLocations>(std::move(vCavern), sizes, Predicate::alwaysTrue(), false));
2029 vector<PLevelMaker> roomInsides;
2030 if (info.shopFactory)
2031 roomInsides.push_back(unique<ShopMaker>(*info.shopFactory, info.tribe, random.get(8, 16), building));
2032 for (auto& elem : info.stockpiles)
2033 roomInsides.push_back(stockpileMaker(elem));
2034 queue->addMaker(unique<RoomMaker>(numRooms, minRoomSize, maxRoomSize, building.wall, none,
2035 unique<Empty>(SquareChange(building.floorInside, ifTrue(connect, SquareAttrib::CONNECT_CORRIDOR))),
2036 std::move(roomInsides), true));
2037 queue->addMaker(unique<Connector>(none, 0));
2038 Predicate featurePred = Predicate::attrib(SquareAttrib::EMPTY_ROOM) && Predicate::type(building.floorInside);
2039 for (StairKey key : info.downStairs)
2040 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key, featurePred));
2041 for (StairKey key : info.upStairs)
2042 queue->addMaker(unique<Stairs>(StairDirection::UP, key, featurePred));
2043 if (info.furniture)
2044 queue->addMaker(unique<Furnitures>(featurePred, 0.3, *info.furniture));
2045 if (info.outsideFeatures)
2046 queue->addMaker(unique<Furnitures>(Predicate::type(building.floorInside), 0.09, *info.outsideFeatures));
2047 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
2048 queue->addMaker(unique<PlaceCollective>(info.collective));
2049 return queue;
2050 }
2051
mineTownMaker(RandomGen & random,SettlementInfo info)2052 static PMakerQueue mineTownMaker(RandomGen& random, SettlementInfo info) {
2053 return genericMineTownMaker(random, info, 10, 12, random.get(5, 7), 6, 8, true);
2054 }
2055
antNestMaker(RandomGen & random,SettlementInfo info)2056 static PMakerQueue antNestMaker(RandomGen& random, SettlementInfo info) {
2057 auto ret = genericMineTownMaker(random, info, 4, 6, random.get(5, 7), 3, 4, false);
2058 ret->addMaker(unique<AddAttrib>(SquareAttrib::NO_DIG));
2059 return ret;
2060 }
2061
smallMineTownMaker(RandomGen & random,SettlementInfo info)2062 static PMakerQueue smallMineTownMaker(RandomGen& random, SettlementInfo info) {
2063 return genericMineTownMaker(random, info, 2, 7, random.get(3, 5), 5, 7, true);
2064 }
2065
vaultMaker(SettlementInfo info,bool connection)2066 static PMakerQueue vaultMaker(SettlementInfo info, bool connection) {
2067 auto queue = unique<MakerQueue>();
2068 BuildingType building = getBuildingInfo(info);
2069 if (connection)
2070 queue->addMaker(unique<UniformBlob>(building.floorOutside, none, SquareAttrib::CONNECT_CORRIDOR));
2071 else
2072 queue->addMaker(unique<UniformBlob>(building.floorOutside));
2073 auto insidePredicate = Predicate::type(building.floorOutside) && Predicate::canEnter(MovementTrait::WALK);
2074 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, insidePredicate));
2075 if (info.shopFactory)
2076 queue->addMaker(unique<Items>(*info.shopFactory, 16, 20, insidePredicate));
2077 queue->addMaker(unique<PlaceCollective>(info.collective, insidePredicate));
2078 return queue;
2079 }
2080
spiderCaveMaker(SettlementInfo info)2081 static PMakerQueue spiderCaveMaker(SettlementInfo info) {
2082 auto queue = unique<MakerQueue>();
2083 BuildingType building = getBuildingInfo(info);
2084 auto inside = unique<MakerQueue>();
2085 inside->addMaker(unique<UniformBlob>(building.floorOutside, none, SquareAttrib::CONNECT_CORRIDOR));
2086 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
2087 if (info.shopFactory)
2088 inside->addMaker(unique<Items>(*info.shopFactory, 5, 10));
2089 queue->addMaker(unique<Margin>(3, std::move(inside)));
2090 queue->addMaker(unique<PlaceCollective>(info.collective));
2091 queue->addMaker(unique<Connector>(none, 0));
2092 return queue;
2093 }
2094
islandVaultMaker(RandomGen & random,SettlementInfo info,bool door)2095 static PLevelMaker islandVaultMaker(RandomGen& random, SettlementInfo info, bool door) {
2096 BuildingType building = getBuildingInfo(info);
2097 auto inside = unique<MakerQueue>();
2098 inside->addMaker(unique<PlaceCollective>(info.collective));
2099 Predicate featurePred = Predicate::type(building.floorInside);
2100 if (!info.stockpiles.empty())
2101 inside->addMaker(stockpileMaker(info.stockpiles.getOnlyElement()));
2102 else
2103 inside->addMaker(unique<Empty>(SquareChange::reset(building.floorInside)));
2104 for (StairKey key : info.downStairs)
2105 inside->addMaker(unique<Stairs>(StairDirection::DOWN, key, featurePred));
2106 for (StairKey key : info.upStairs)
2107 inside->addMaker(unique<Stairs>(StairDirection::UP, key, featurePred));
2108 auto buildingMaker = unique<MakerQueue>(
2109 unique<Empty>(SquareChange(building.wall)),
2110 unique<AddAttrib>(SquareAttrib::NO_DIG),
2111 unique<RemoveAttrib>(SquareAttrib::CONNECT_CORRIDOR),
2112 unique<Margin>(1, std::move(inside))
2113 );
2114 if (door)
2115 buildingMaker->addMaker(unique<LevelExit>(FurnitureFactory(TribeId::getMonster(), FurnitureType::DOOR)));
2116 return unique<MakerQueue>(
2117 unique<Empty>(SquareChange::reset(FurnitureType::WATER)),
2118 unique<Margin>(1, std::move(buildingMaker)));
2119 }
2120
dragonCaveMaker(SettlementInfo info)2121 static PMakerQueue dragonCaveMaker(SettlementInfo info) {
2122 auto queue = vaultMaker(info, true);
2123 /* queue->addMaker(unique<RandomLocations>({unique<CreatureAltarMaker>(info.collective)}, {{1, 1}},
2124 {Predicate::type(FurnitureType::HILL)}));*/
2125 return queue;
2126 }
2127
mineTownLevel(RandomGen & random,SettlementInfo info)2128 PLevelMaker LevelMaker::mineTownLevel(RandomGen& random, SettlementInfo info) {
2129 auto queue = unique<MakerQueue>();
2130 queue->addMaker(unique<Empty>(SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN)));
2131 queue->addMaker(mineTownMaker(random, info));
2132 return unique<BorderGuard>(std::move(queue), SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN));
2133 }
2134
cemetery(SettlementInfo info)2135 static PMakerQueue cemetery(SettlementInfo info) {
2136 BuildingType building = getBuildingInfo(info);
2137 auto queue = unique<MakerQueue>(
2138 unique<PlaceCollective>(info.collective),
2139 unique<Margin>(1, unique<Buildings>(1, 2, 2, 3, building, false, nullptr, false)),
2140 unique<Furnitures>(Predicate::type(FurnitureType::GRASS), 0.15,
2141 FurnitureFactory(info.tribe, FurnitureType::GRAVE)));
2142 for (StairKey key : info.downStairs)
2143 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::type(building.floorInside)));
2144 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
2145 return queue;
2146 }
2147
emptyCollective(SettlementInfo info)2148 static PLevelMaker emptyCollective(SettlementInfo info) {
2149 return unique<MakerQueue>(
2150 unique<PlaceCollective>(info.collective),
2151 unique<Inhabitants>(info.inhabitants, info.collective));
2152 }
2153
swamp(SettlementInfo info)2154 static PMakerQueue swamp(SettlementInfo info) {
2155 auto queue = unique<MakerQueue>(
2156 unique<Lake>(false),
2157 unique<PlaceCollective>(info.collective)
2158 );
2159 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
2160 return queue;
2161 }
2162
mountainLake(SettlementInfo info)2163 static PMakerQueue mountainLake(SettlementInfo info) {
2164 auto queue = unique<MakerQueue>(
2165 unique<UniformBlob>(FurnitureType::WATER, none, SquareAttrib::LAKE),
2166 unique<PlaceCollective>(info.collective)
2167 );
2168 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective));
2169 return queue;
2170 }
2171
getMountains(BiomeId id)2172 static PLevelMaker getMountains(BiomeId id) {
2173 switch (id) {
2174 case BiomeId::GRASSLAND:
2175 case BiomeId::FORREST:
2176 return unique<Mountains>(0.68, 0.06, NoiseInit{0, 1, 0, 0, 0});
2177 case BiomeId::MOUNTAIN:
2178 return unique<Mountains>(0.25, 0.1, NoiseInit{0, 1, 0, 0, 0});
2179 }
2180 }
2181
getForrest(BiomeId id)2182 static PLevelMaker getForrest(BiomeId id) {
2183 FurnitureFactory vegetationLow(TribeId::getHostile(),
2184 {{FurnitureType::CANIF_TREE, 2}, {FurnitureType::BUSH, 1 }});
2185 FurnitureFactory vegetationHigh(TribeId::getHostile(),
2186 {{FurnitureType::DECID_TREE, 2}, {FurnitureType::BUSH, 1 }});
2187 switch (id) {
2188 case BiomeId::MOUNTAIN:
2189 return unique<MakerQueue>(
2190 unique<Forrest>(0.2, 0.5, FurnitureType::GRASS, vegetationLow),
2191 unique<Forrest>(0.8, 0.5, FurnitureType::HILL, vegetationHigh));
2192 case BiomeId::GRASSLAND:
2193 return unique<MakerQueue>(
2194 unique<Forrest>(0.3, 0.25, FurnitureType::GRASS, vegetationLow),
2195 unique<Forrest>(0.8, 0.25, FurnitureType::HILL, vegetationHigh));
2196 case BiomeId::FORREST:
2197 return unique<MakerQueue>(
2198 unique<Forrest>(0.8, 0.5, FurnitureType::GRASS, vegetationLow),
2199 unique<Forrest>(0.8, 0.5, FurnitureType::HILL, vegetationHigh));
2200 }
2201 }
2202
getForrestCreatures(CreatureFactory factory,int levelWidth,BiomeId biome)2203 static PLevelMaker getForrestCreatures(CreatureFactory factory, int levelWidth, BiomeId biome) {
2204 int div;
2205 switch (biome) {
2206 case BiomeId::FORREST: div = 2000; break;
2207 case BiomeId::GRASSLAND:
2208 case BiomeId::MOUNTAIN: div = 7000; break;
2209 }
2210 return unique<Creatures>(factory, levelWidth * levelWidth / div, MonsterAIFactory::wildlifeNonPredator());
2211 }
2212
topLevel(RandomGen & random,optional<CreatureFactory> forrestCreatures,vector<SettlementInfo> settlements,int width,bool keeperSpawn,BiomeId biomeId)2213 PLevelMaker LevelMaker::topLevel(RandomGen& random, optional<CreatureFactory> forrestCreatures,
2214 vector<SettlementInfo> settlements, int width, bool keeperSpawn, BiomeId biomeId) {
2215 auto queue = unique<MakerQueue>();
2216 auto locations = unique<RandomLocations>();
2217 auto locations2 = unique<RandomLocations>();
2218 LevelMaker* startingPos = nullptr;
2219 int locationMargin = 10;
2220 if (keeperSpawn) {
2221 auto startingPosMaker = unique<StartingPos>(Predicate::alwaysTrue(), StairKey::keeperSpawn());
2222 startingPos = startingPosMaker.get();
2223 locations->add(std::move(startingPosMaker), Vec2(4, 4), RandomLocations::LocationPredicate(
2224 Predicate::attrib(SquareAttrib::HILL) && Predicate::canEnter({MovementTrait::WALK}),
2225 Predicate::attrib(SquareAttrib::MOUNTAIN), 1, 8));
2226 int minMargin = 50;
2227 locations->setMinMargin(startingPos, minMargin - locationMargin);
2228 }
2229 struct CottageInfo {
2230 LevelMaker* maker;
2231 CollectiveBuilder* collective;
2232 TribeId tribe;
2233 int maxDistance;
2234 };
2235 vector<CottageInfo> cottages;
2236 for (SettlementInfo settlement : settlements) {
2237 PLevelMaker queue;
2238 switch (settlement.type) {
2239 case SettlementType::SMALL_VILLAGE:
2240 queue = village(random, settlement, 3, 4);
2241 cottages.push_back({queue.get(), settlement.collective, settlement.tribe, 16});
2242 break;
2243 case SettlementType::VILLAGE:
2244 queue = village(random, settlement, 4, 8);
2245 break;
2246 case SettlementType::FORREST_VILLAGE:
2247 queue = village2(random, settlement);
2248 break;
2249 case SettlementType::CASTLE:
2250 queue = castle(random, settlement);
2251 break;
2252 case SettlementType::CASTLE2:
2253 queue = castle2(random, settlement);
2254 break;
2255 case SettlementType::COTTAGE:
2256 queue = cottage(settlement);
2257 cottages.push_back({queue.get(), settlement.collective, settlement.tribe, 13});
2258 break;
2259 case SettlementType::FORREST_COTTAGE:
2260 queue = forrestCottage(settlement);
2261 break;
2262 case SettlementType::TOWER:
2263 queue = tower(random, settlement, true);
2264 break;
2265 case SettlementType::WITCH_HOUSE:
2266 queue = cottage(settlement);
2267 break;
2268 case SettlementType::FOREST:
2269 queue = emptyCollective(settlement);
2270 break;
2271 case SettlementType::MINETOWN:
2272 queue = mineTownMaker(random, settlement);
2273 break;
2274 case SettlementType::ANT_NEST:
2275 queue = antNestMaker(random, settlement);
2276 break;
2277 case SettlementType::SMALL_MINETOWN:
2278 queue = smallMineTownMaker(random, settlement);
2279 break;
2280 case SettlementType::VAULT:
2281 queue = vaultMaker(settlement, false);
2282 if (keeperSpawn)
2283 locations->setMaxDistance(startingPos, queue.get(), width / 3);
2284 break;
2285 case SettlementType::ISLAND_VAULT:
2286 queue = islandVaultMaker(random, settlement, false);
2287 break;
2288 case SettlementType::ISLAND_VAULT_DOOR:
2289 queue = islandVaultMaker(random, settlement, true);
2290 break;
2291 case SettlementType::CAVE:
2292 queue = dragonCaveMaker(settlement);
2293 break;
2294 case SettlementType::SPIDER_CAVE:
2295 queue = spiderCaveMaker(settlement);
2296 break;
2297 case SettlementType::CEMETERY:
2298 queue = cemetery(settlement);
2299 break;
2300 case SettlementType::MOUNTAIN_LAKE:
2301 queue = mountainLake(settlement);
2302 break;
2303 case SettlementType::SWAMP:
2304 queue = swamp(settlement);
2305 break;
2306 }
2307 if (settlement.type == SettlementType::SPIDER_CAVE)
2308 locations2->add(std::move(queue), getSize(random, settlement.type), getSettlementPredicate(settlement.type));
2309 else {
2310 if (keeperSpawn) {
2311 if (settlement.closeToPlayer) {
2312 locations->setMinDistance(startingPos, queue.get(), 25);
2313 locations->setMaxDistance(startingPos, queue.get(), 60);
2314 } else
2315 locations->setMinDistance(startingPos, queue.get(), 70);
2316 }
2317 locations->add(std::move(queue), getSize(random, settlement.type), getSettlementPredicate(settlement.type));
2318 }
2319 }
2320 Predicate lowlandPred = Predicate::attrib(SquareAttrib::LOWLAND) && !Predicate::attrib(SquareAttrib::RIVER);
2321 for (auto& cottage : cottages)
2322 for (int i : Range(random.get(1, 3))) {
2323 locations->add(unique<MakerQueue>(
2324 unique<RemoveFurniture>(FurnitureLayer::MIDDLE),
2325 unique<FurnitureBlob>(FurnitureFactory(cottage.tribe, FurnitureType::CROPS)),
2326 unique<PlaceCollective>(cottage.collective)),
2327 {random.get(7, 12), random.get(7, 12)},
2328 lowlandPred);
2329 locations->setMaxDistanceLast(cottage.maker, cottage.maxDistance);
2330 }
2331 if (biomeId == BiomeId::GRASSLAND || biomeId == BiomeId::FORREST)
2332 for (int i : Range(random.get(0, 3)))
2333 locations->add(unique<Lake>(), {random.get(20, 30), random.get(20, 30)}, Predicate::attrib(SquareAttrib::LOWLAND));
2334 if (biomeId == BiomeId::MOUNTAIN)
2335 for (int i : Range(random.get(3, 6))) {
2336 locations->add(unique<UniformBlob>(FurnitureType::WATER, none, SquareAttrib::LAKE),
2337 {random.get(10, 30), random.get(10, 30)}, Predicate::type(FurnitureType::MOUNTAIN));
2338 // locations->setMaxDistanceLast(startingPos, i == 0 ? 25 : 60);
2339 }
2340 /* for (int i : Range(random.get(3, 5))) {
2341 locations->add(unique<UniformBlob>(FurnitureType::FLOOR, none),
2342 {random.get(5, 12), random.get(5, 12)}, Predicate::type(SquareId::MOUNTAIN));
2343 // locations->setMaxDistanceLast(startingPos, i == 0 ? 25 : 40);
2344 }*/
2345 int mapBorder = 30;
2346 queue->addMaker(unique<Empty>(FurnitureType::WATER));
2347 queue->addMaker(getMountains(biomeId));
2348 queue->addMaker(unique<MountainRiver>(1, Predicate::type(FurnitureType::MOUNTAIN)));
2349 queue->addMaker(unique<AddAttrib>(SquareAttrib::CONNECT_CORRIDOR, Predicate::attrib(SquareAttrib::LOWLAND)));
2350 queue->addMaker(unique<AddAttrib>(SquareAttrib::CONNECT_CORRIDOR, Predicate::attrib(SquareAttrib::HILL)));
2351 queue->addMaker(getForrest(biomeId));
2352 queue->addMaker(unique<Margin>(mapBorder + locationMargin, std::move(locations)));
2353 queue->addMaker(unique<Margin>(mapBorder, unique<Roads>()));
2354 queue->addMaker(unique<Margin>(mapBorder,
2355 unique<TransferPos>(Predicate::canEnter(MovementTrait::WALK), StairKey::transferLanding(), 2)));
2356 queue->addMaker(unique<Margin>(mapBorder, unique<Connector>(none, 0, 5,
2357 Predicate::canEnter({MovementTrait::WALK}) &&
2358 Predicate::attrib(SquareAttrib::CONNECT_CORRIDOR),
2359 SquareAttrib::CONNECTOR)));
2360 queue->addMaker(unique<Margin>(mapBorder + locationMargin, std::move(locations2)));
2361 queue->addMaker(unique<Items>(ItemFactory::mushrooms(), width / 10, width / 5));
2362 queue->addMaker(unique<AddMapBorder>(mapBorder));
2363 if (forrestCreatures)
2364 queue->addMaker(unique<Margin>(mapBorder, getForrestCreatures(*forrestCreatures, width - 2 * mapBorder, biomeId)));
2365 return std::move(queue);
2366 }
2367
getRandomExit(RandomGen & random,Rectangle rect,int minCornerDist)2368 Vec2 LevelMaker::getRandomExit(RandomGen& random, Rectangle rect, int minCornerDist) {
2369 CHECK(rect.width() > 2 * minCornerDist && rect.height() > 2 * minCornerDist);
2370 int w1 = random.get(2);
2371 int w2 = random.get(2);
2372 int d1 = random.get(minCornerDist, rect.width() - minCornerDist);
2373 int d2 = random.get(minCornerDist, rect.height() - minCornerDist);
2374 return Vec2(
2375 rect.left() + d1 * w1 + (1 - w1) * w2 * (rect.width() - 1),
2376 rect.top() + d2 * (1 - w1) + w1 * w2 * (rect.height() - 1));
2377 }
2378
2379 class SpecificArea : public LevelMaker {
2380 public:
SpecificArea(Rectangle a,PLevelMaker m)2381 SpecificArea(Rectangle a, PLevelMaker m) : area(a), maker(std::move(m)) {}
2382
make(LevelBuilder * builder,Rectangle)2383 virtual void make(LevelBuilder* builder, Rectangle) override {
2384 maker->make(builder, area);
2385 }
2386
2387 private:
2388 Rectangle area;
2389 PLevelMaker maker;
2390 };
2391
splashLevel(CreatureFactory heroLeader,CreatureFactory heroes,CreatureFactory monsters,CreatureFactory imps,const FilePath & splashPath)2392 PLevelMaker LevelMaker::splashLevel(CreatureFactory heroLeader, CreatureFactory heroes, CreatureFactory monsters,
2393 CreatureFactory imps, const FilePath& splashPath) {
2394 auto queue = unique<MakerQueue>();
2395 queue->addMaker(unique<Empty>(FurnitureType::BLACK_FLOOR));
2396 Rectangle leaderSpawn(
2397 Level::getSplashVisibleBounds().right() + 1, Level::getSplashVisibleBounds().middle().y,
2398 Level::getSplashVisibleBounds().right() + 2, Level::getSplashVisibleBounds().middle().y + 1);
2399 Rectangle heroSpawn(
2400 Level::getSplashVisibleBounds().right() + 2, Level::getSplashVisibleBounds().middle().y - 1,
2401 Level::getSplashBounds().right(), Level::getSplashVisibleBounds().middle().y + 2);
2402 Rectangle monsterSpawn1(
2403 Level::getSplashVisibleBounds().left(), 0,
2404 Level::getSplashVisibleBounds().right(), Level::getSplashVisibleBounds().top() - 1);
2405 Rectangle monsterSpawn2(
2406 Level::getSplashVisibleBounds().left(), Level::getSplashVisibleBounds().bottom() + 2,
2407 Level::getSplashVisibleBounds().right(), Level::getSplashBounds().bottom());
2408 queue->addMaker(unique<SpecificArea>(leaderSpawn, unique<Creatures>(heroLeader, 1,MonsterAIFactory::splashHeroes(true))));
2409 queue->addMaker(unique<SpecificArea>(heroSpawn, unique<Creatures>(heroes, 22, MonsterAIFactory::splashHeroes(false))));
2410 queue->addMaker(unique<SpecificArea>(monsterSpawn1, unique<Creatures>(monsters, 17, MonsterAIFactory::splashMonsters())));
2411 queue->addMaker(unique<SpecificArea>(monsterSpawn2, unique<Creatures>(monsters, 17, MonsterAIFactory::splashMonsters())));
2412 queue->addMaker(unique<SpecificArea>(monsterSpawn1, unique<Creatures>(imps, 15,
2413 MonsterAIFactory::splashImps(splashPath))));
2414 queue->addMaker(unique<SpecificArea>(monsterSpawn2, unique<Creatures>(imps, 15,
2415 MonsterAIFactory::splashImps(splashPath))));
2416 queue->addMaker(unique<SetSunlight>(0.0, !Predicate::inRectangle(Level::getSplashVisibleBounds())));
2417 return std::move(queue);
2418 }
2419
2420
underground(RandomGen & random,CreatureFactory waterFactory,CreatureFactory lavaFactory)2421 static PLevelMaker underground(RandomGen& random, CreatureFactory waterFactory, CreatureFactory lavaFactory) {
2422 auto queue = unique<MakerQueue>();
2423 if (random.roll(1)) {
2424 vector<PLevelMaker> vCavern;
2425 vector<pair<int, int>> sizes;
2426 int minSize = random.get(5, 15);
2427 int maxSize = minSize + random.get(3, 10);
2428 for (int i : Range(sqrt(random.get(4, 100)))) {
2429 int size = random.get(minSize, maxSize);
2430 sizes.push_back(make_pair(size, size));
2431 /* if (random.roll(4))
2432 queue->addMaker(unique<Items>(ItemFactory::mushrooms(), SquareId::PATH, 2, 5));*/
2433 vCavern.push_back(unique<UniformBlob>(FurnitureType::FLOOR));
2434 }
2435 queue->addMaker(unique<RandomLocations>(std::move(vCavern), sizes, Predicate::alwaysTrue(), false));
2436 }
2437 switch (random.get(1, 3)) {
2438 case 1: queue->addMaker(unique<River>(3, random.choose(FurnitureType::WATER, FurnitureType::MAGMA)));
2439 break;
2440 case 2:{
2441 int numLakes = sqrt(random.get(1, 100));
2442 auto lakeType = random.choose(FurnitureType::WATER, FurnitureType::MAGMA);
2443 vector<pair<int, int>> sizes;
2444 vector<PLevelMaker> makers;
2445 for (int i : Range(numLakes)) {
2446 int size = random.get(6, 20);
2447 sizes.emplace_back(size, size);
2448 makers.push_back(unique<UniformBlob>(lakeType, none, SquareAttrib::LAKE));
2449 }
2450 queue->addMaker(unique<RandomLocations>(std::move(makers), sizes, Predicate::alwaysTrue(), false));
2451 if (lakeType == FurnitureType::WATER) {
2452 queue->addMaker(unique<Creatures>(waterFactory, 1, MonsterAIFactory::monster(),
2453 Predicate::type(FurnitureType::WATER)));
2454 }
2455 if (lakeType == FurnitureType::MAGMA) {
2456 queue->addMaker(unique<Creatures>(lavaFactory, random.get(1, 4),
2457 MonsterAIFactory::monster(), Predicate::type(FurnitureType::MAGMA)));
2458 }
2459 break;
2460 }
2461 default: break;
2462 }
2463 return std::move(queue);
2464 }
2465
roomLevel(RandomGen & random,CreatureFactory roomFactory,CreatureFactory waterFactory,CreatureFactory lavaFactory,vector<StairKey> up,vector<StairKey> down,FurnitureFactory furniture)2466 PLevelMaker LevelMaker::roomLevel(RandomGen& random, CreatureFactory roomFactory, CreatureFactory waterFactory,
2467 CreatureFactory lavaFactory, vector<StairKey> up, vector<StairKey> down, FurnitureFactory furniture) {
2468 auto queue = unique<MakerQueue>();
2469 queue->addMaker(unique<Empty>(SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN)));
2470 queue->addMaker(underground(random, waterFactory, lavaFactory));
2471 queue->addMaker(unique<RoomMaker>(random.get(8, 15), 4, 7, SquareChange::none(),
2472 FurnitureType::MOUNTAIN, unique<Empty>(FurnitureType::FLOOR)));
2473 queue->addMaker(unique<Connector>(FurnitureFactory(TribeId::getHostile(), FurnitureType::DOOR), 0.5));
2474 queue->addMaker(unique<Furnitures>(Predicate::attrib(SquareAttrib::EMPTY_ROOM), 0.05, furniture));
2475 for (StairKey key : down)
2476 queue->addMaker(unique<Stairs>(StairDirection::DOWN, key, Predicate::type(FurnitureType::FLOOR)));
2477 for (StairKey key : up)
2478 queue->addMaker(unique<Stairs>(StairDirection::UP, key, Predicate::type(FurnitureType::FLOOR)));
2479 queue->addMaker(unique<Creatures>(roomFactory, random.get(10, 15), MonsterAIFactory::monster()));
2480 queue->addMaker(unique<Items>(ItemFactory::dungeon(), 5, 10));
2481 return unique<BorderGuard>(std::move(queue), SquareChange(FurnitureType::FLOOR, FurnitureType::MOUNTAIN));
2482 }
2483
2484 namespace {
2485
2486 class SokobanFromFile : public LevelMaker {
2487 public:
SokobanFromFile(Table<char> f,StairKey hole)2488 SokobanFromFile(Table<char> f, StairKey hole) : file(f), holeKey(hole) {}
2489
make(LevelBuilder * builder,Rectangle area)2490 virtual void make(LevelBuilder* builder, Rectangle area) override {
2491 CHECK(area == file.getBounds()) << "Bad size of sokoban input.";
2492 builder->setNoDiagonalPassing();
2493 for (Vec2 v : area) {
2494 builder->resetFurniture(v, FurnitureType::FLOOR);
2495 switch (file[v]) {
2496 case '.':
2497 break;
2498 case '#':
2499 builder->putFurniture(v, FurnitureType::DUNGEON_WALL);
2500 break;
2501 case '^':
2502 builder->putFurniture(v, FurnitureType::SOKOBAN_HOLE);
2503 break;
2504 case '$':
2505 builder->addAttrib(v, SquareAttrib::SOKOBAN_PRIZE);
2506 break;
2507 case '@':
2508 builder->addAttrib(v, SquareAttrib::SOKOBAN_ENTRY);
2509 break;
2510 case '+':
2511 builder->putFurniture(v, FurnitureParams{FurnitureType::DOOR, TribeId::getHostile()});
2512 break;
2513 case '0':
2514 builder->putCreature(v, CreatureFactory::fromId(CreatureId::SOKOBAN_BOULDER, TribeId::getPeaceful()));
2515 break;
2516 default: FATAL << "Unknown symbol in sokoban data: " << file[v];
2517 }
2518 }
2519 }
2520
2521 Table<char> file;
2522 StairKey holeKey;
2523 };
2524
2525 }
2526
sokobanFromFile(RandomGen & random,SettlementInfo info,Table<char> file)2527 PLevelMaker LevelMaker::sokobanFromFile(RandomGen& random, SettlementInfo info, Table<char> file) {
2528 auto queue = unique<MakerQueue>();
2529 queue->addMaker(unique<SokobanFromFile>(file, info.downStairs.getOnlyElement()));
2530 queue->addMaker(unique<Stairs>(StairDirection::DOWN, info.downStairs.getOnlyElement(),
2531 Predicate::attrib(SquareAttrib::SOKOBAN_ENTRY)));
2532 //queue->addMaker(unique<PlaceCollective>(info.collective));
2533 queue->addMaker(unique<Inhabitants>(info.inhabitants, info.collective, Predicate::attrib(SquareAttrib::SOKOBAN_PRIZE)));
2534 return std::move(queue);
2535 }
2536
2537 namespace {
2538
2539 class BattleFromFile : public LevelMaker {
2540 public:
BattleFromFile(Table<char> f,CreatureList a,CreatureList e)2541 BattleFromFile(Table<char> f, CreatureList a, CreatureList e)
2542 : level(f), allies(a), enemies(e) {}
2543
make(LevelBuilder * builder,Rectangle area)2544 virtual void make(LevelBuilder* builder, Rectangle area) override {
2545 CHECK(area == level.getBounds()) << "Bad size of battle level input.";
2546 auto alliesList = allies.generate(builder->getRandom(), TribeId::getKeeper(), MonsterAIFactory::guard());
2547 int allyIndex = 0;
2548 auto enemyList = enemies.generate(builder->getRandom(), TribeId::getHuman(),
2549 MonsterAIFactory::singleTask(Task::attackCreatures(getWeakPointers(alliesList))));
2550 int enemyIndex = 0;
2551 for (Vec2 v : area) {
2552 builder->resetFurniture(v, FurnitureType::FLOOR);
2553 switch (level[v]) {
2554 case '.':
2555 break;
2556 case '#':
2557 builder->putFurniture(v, FurnitureType::MOUNTAIN);
2558 break;
2559 case 'a':
2560 if (allyIndex < alliesList.size()) {
2561 builder->putCreature(v, std::move(alliesList[allyIndex]));
2562 ++allyIndex;
2563 }
2564 break;
2565 case 'e':
2566 if (enemyIndex < enemyList.size()) {
2567 builder->putCreature(v, std::move(enemyList[enemyIndex]));
2568 ++enemyIndex;
2569 }
2570 break;
2571 default: FATAL << "Unknown symbol in battle test data: " << level[v];
2572 }
2573 }
2574 }
2575
2576 Table<char> level;
2577 CreatureList allies;
2578 CreatureList enemies;
2579 };
2580
2581 }
2582
battleLevel(Table<char> level,CreatureList allies,CreatureList enemies)2583 PLevelMaker LevelMaker::battleLevel(Table<char> level, CreatureList allies, CreatureList enemies) {
2584 return unique<BattleFromFile>(level, allies, enemies);
2585 }
2586
emptyLevel(RandomGen &)2587 PLevelMaker LevelMaker::emptyLevel(RandomGen&) {
2588 auto queue = unique<MakerQueue>();
2589 queue->addMaker(unique<Empty>(FurnitureType::GRASS));
2590 return std::move(queue);
2591 }
2592