1 #include "stdafx.h"
2 #include "level_builder.h"
3 #include "progress_meter.h"
4 #include "square.h"
5 #include "creature.h"
6 #include "level_maker.h"
7 #include "collective_builder.h"
8 #include "view_object.h"
9 #include "item.h"
10 #include "furniture.h"
11 #include "furniture_factory.h"
12 #include "position.h"
13 #include "movement_set.h"
14 
LevelBuilder(ProgressMeter * meter,RandomGen & r,int width,int height,const string & n,bool allCovered,optional<double> defaultLight)15 LevelBuilder::LevelBuilder(ProgressMeter* meter, RandomGen& r, int width, int height, const string& n, bool allCovered,
16     optional<double> defaultLight)
17   : squares(Rectangle(width, height)), unavailable(width, height, false),
18     heightMap(width, height, 0), covered(width, height, allCovered),
19     sunlight(width, height, defaultLight ? *defaultLight : (allCovered ? 0.0 : 1.0)),
20     attrib(width, height), items(width, height), furniture(Rectangle(width, height)),
21     name(n), progressMeter(meter), random(r) {
22   for (Vec2 v : squares.getBounds())
23     squares.putElem(v, {});
24 }
25 
LevelBuilder(RandomGen & r,int width,int height,const string & n,bool covered)26 LevelBuilder::LevelBuilder(RandomGen& r, int width, int height, const string& n, bool covered)
27   : LevelBuilder(nullptr, r, width, height, n, covered) {
28 }
29 
~LevelBuilder()30 LevelBuilder::~LevelBuilder() {}
31 
32 LevelBuilder::LevelBuilder(LevelBuilder&&) = default;
33 
getRandom()34 RandomGen& LevelBuilder::getRandom() {
35   return random;
36 }
37 
hasAttrib(Vec2 posT,SquareAttrib attr)38 bool LevelBuilder::hasAttrib(Vec2 posT, SquareAttrib attr) {
39   Vec2 pos = transform(posT);
40   return attrib[pos].contains(attr);
41 }
42 
addAttrib(Vec2 pos,SquareAttrib attr)43 void LevelBuilder::addAttrib(Vec2 pos, SquareAttrib attr) {
44   attrib[transform(pos)].insert(attr);
45 }
46 
removeAttrib(Vec2 pos,SquareAttrib attr)47 void LevelBuilder::removeAttrib(Vec2 pos, SquareAttrib attr) {
48   attrib[transform(pos)].erase(attr);
49 }
50 
modSquare(Vec2 pos)51 WSquare LevelBuilder::modSquare(Vec2 pos) {
52   return squares.getWritable(transform(pos));
53 }
54 
toGlobalCoordinates(Rectangle area)55 Rectangle LevelBuilder::toGlobalCoordinates(Rectangle area) {
56   return area.apply([this](Vec2 v) { return transform(v); });
57 }
58 
toGlobalCoordinates(vector<Vec2> v)59 vector<Vec2> LevelBuilder::toGlobalCoordinates(vector<Vec2> v) {
60   return v.transform([this](Vec2 v) { return transform(v); });
61 }
62 
addCollective(CollectiveBuilder * col)63 void LevelBuilder::addCollective(CollectiveBuilder* col) {
64   if (!collectives.contains(col))
65     collectives.push_back(col);
66 }
67 
setHeightMap(Vec2 pos,double h)68 void LevelBuilder::setHeightMap(Vec2 pos, double h) {
69   heightMap[transform(pos)] = h;
70 }
71 
getHeightMap(Vec2 pos)72 double LevelBuilder::getHeightMap(Vec2 pos) {
73   return heightMap[transform(pos)];
74 }
75 
putCreature(Vec2 pos,PCreature creature)76 void LevelBuilder::putCreature(Vec2 pos, PCreature creature) {
77   creatures.emplace_back(std::move(creature), transform(pos));
78 }
79 
putItems(Vec2 posT,vector<PItem> it)80 void LevelBuilder::putItems(Vec2 posT, vector<PItem> it) {
81   CHECK(canNavigate(posT, {MovementTrait::WALK}));
82   Vec2 pos = transform(posT);
83   append(items[pos], std::move(it));
84 }
85 
putFurniture(Vec2 posT,FurnitureFactory & f,optional<SquareAttrib> attrib)86 void LevelBuilder::putFurniture(Vec2 posT, FurnitureFactory& f, optional<SquareAttrib> attrib) {
87   putFurniture(posT, f.getRandom(getRandom()), attrib);
88 }
89 
putFurniture(Vec2 posT,FurnitureParams f,optional<SquareAttrib> attrib)90 void LevelBuilder::putFurniture(Vec2 posT, FurnitureParams f, optional<SquareAttrib> attrib) {
91   auto layer = Furniture::getLayer(f.type);
92   if (getFurniture(posT, layer))
93     removeFurniture(posT, layer);
94   furniture.getBuilt(layer).putElem(transform(posT), f);
95   if (attrib)
96     addAttrib(posT, *attrib);
97 }
98 
putFurniture(Vec2 pos,FurnitureType type,optional<SquareAttrib> attrib)99 void LevelBuilder::putFurniture(Vec2 pos, FurnitureType type, optional<SquareAttrib> attrib) {
100   putFurniture(pos, {type, TribeId::getHostile()}, attrib);
101 }
102 
resetFurniture(Vec2 posT,FurnitureType type,optional<SquareAttrib> attrib)103 void LevelBuilder::resetFurniture(Vec2 posT, FurnitureType type, optional<SquareAttrib> attrib) {
104   CHECK(Furniture::getLayer(type) == FurnitureLayer::GROUND);
105   removeAllFurniture(posT);
106   putFurniture(posT, type, attrib);
107 }
108 
canPutFurniture(Vec2 posT,FurnitureLayer layer)109 bool LevelBuilder::canPutFurniture(Vec2 posT, FurnitureLayer layer) {
110   return !getFurniture(posT, layer);
111 }
112 
removeFurniture(Vec2 pos,FurnitureLayer layer)113 void LevelBuilder::removeFurniture(Vec2 pos, FurnitureLayer layer) {
114   furniture.getBuilt(layer).clearElem(transform(pos));
115 }
116 
removeAllFurniture(Vec2 pos)117 void LevelBuilder::removeAllFurniture(Vec2 pos) {
118   for (auto layer : ENUM_ALL(FurnitureLayer))
119     removeFurniture(pos, layer);
120 }
121 
getFurnitureType(Vec2 posT,FurnitureLayer layer)122 optional<FurnitureType> LevelBuilder::getFurnitureType(Vec2 posT, FurnitureLayer layer) {
123   if (auto f = getFurniture(posT, layer))
124     return f->getType();
125   else
126     return none;
127 }
128 
isFurnitureType(Vec2 pos,FurnitureType type)129 bool LevelBuilder::isFurnitureType(Vec2 pos, FurnitureType type) {
130   return getFurnitureType(pos, Furniture::getLayer(type)) == type;
131 }
132 
getFurniture(Vec2 posT,FurnitureLayer layer)133 WConstFurniture LevelBuilder::getFurniture(Vec2 posT, FurnitureLayer layer) {
134   return furniture.getBuilt(layer).getReadonly(transform(posT));
135 }
136 
setLandingLink(Vec2 posT,StairKey key)137 void LevelBuilder::setLandingLink(Vec2 posT, StairKey key) {
138   Vec2 pos = transform(posT);
139   squares.getWritable(pos)->setLandingLink(key);
140 }
141 
canPutCreature(Vec2 posT,WCreature c)142 bool LevelBuilder::canPutCreature(Vec2 posT, WCreature c) {
143   Vec2 pos = transform(posT);
144   if (!canNavigate(posT, c->getMovementType()))
145     return false;
146   for (pair<PCreature, Vec2>& c : creatures) {
147     if (c.second == pos)
148       return false;
149   }
150   return true;
151 }
152 
setNoDiagonalPassing()153 void LevelBuilder::setNoDiagonalPassing() {
154   noDiagonalPassing = true;
155 }
156 
build(WModel m,LevelMaker * maker,LevelId levelId)157 PLevel LevelBuilder::build(WModel m, LevelMaker* maker, LevelId levelId) {
158   CHECK(!!m);
159   CHECK(mapStack.empty());
160   maker->make(this, squares.getBounds());
161   for (Vec2 v : squares.getBounds())
162     if (!items[v].empty())
163       squares.getWritable(v)->dropItemsLevelGen(std::move(items[v]));
164   auto l = Level::create(std::move(squares), std::move(furniture), m, name, sunlight, levelId, covered);
165   l->unavailable = unavailable;
166   for (pair<PCreature, Vec2>& c : creatures)
167     Position(c.second, l.get()).addCreature(std::move(c.first));
168   for (CollectiveBuilder* c : collectives)
169     c->setLevel(l.get());
170   l->noDiagonalPassing = noDiagonalPassing;
171   return l;
172 }
173 
identity()174 static Vec2::LinearMap identity() {
175   return [](Vec2 v) { return v; };
176 }
177 
deg90(Rectangle bounds)178 static Vec2::LinearMap deg90(Rectangle bounds) {
179   return [bounds](Vec2 v) {
180     v -= bounds.topLeft();
181     return bounds.topLeft() + Vec2(v.y, v.x);
182   };
183 }
184 
deg180(Rectangle bounds)185 static Vec2::LinearMap deg180(Rectangle bounds) {
186   return [bounds](Vec2 v) {
187     return bounds.topLeft() - v + bounds.bottomRight() - Vec2(1, 1);
188   };
189 }
190 
deg270(Rectangle bounds)191 static Vec2::LinearMap deg270(Rectangle bounds) {
192   return [bounds](Vec2 v) {
193     v -= bounds.topRight() - Vec2(1, 0);
194     return bounds.topLeft() + Vec2(v.y, -v.x);
195   };
196 }
197 
pushMap(Rectangle bounds,Rot rot)198 void LevelBuilder::pushMap(Rectangle bounds, Rot rot) {
199   switch (rot) {
200     case CW0: mapStack.push_back(identity()); break;
201     case CW1: mapStack.push_back(deg90(bounds)); break;
202     case CW2: mapStack.push_back(deg180(bounds)); break;
203     case CW3: mapStack.push_back(deg270(bounds)); break;
204   }
205 }
206 
popMap()207 void LevelBuilder::popMap() {
208   mapStack.pop_back();
209 }
210 
transform(Vec2 v)211 Vec2 LevelBuilder::transform(Vec2 v) {
212   for (auto m : mapStack.reverse()) {
213     v = m(v);
214   }
215   return v;
216 }
217 
setCovered(Vec2 posT,bool state)218 void LevelBuilder::setCovered(Vec2 posT, bool state) {
219   covered[transform(posT)] = state;
220 }
221 
setSunlight(Vec2 pos,double s)222 void LevelBuilder::setSunlight(Vec2 pos, double s) {
223   sunlight[pos] = s;
224 }
225 
setUnavailable(Vec2 pos)226 void LevelBuilder::setUnavailable(Vec2 pos) {
227   unavailable[transform(pos)] = true;
228 }
229 
canNavigate(Vec2 posT,const MovementType & movement)230 bool LevelBuilder::canNavigate(Vec2 posT, const MovementType& movement) {
231   Vec2 pos = transform(posT);
232   if (unavailable[pos])
233     return false;
234   bool result = true;
235   for (auto layer : ENUM_ALL(FurnitureLayer))
236     if (auto f = furniture.getBuilt(layer).getReadonly(pos)) {
237       bool canEnter = f->getMovementSet().canEnter(movement, covered[pos], false, none);
238       if (f->overridesMovement())
239         return canEnter;
240       else
241         result &= canEnter;
242     }
243   return result;
244 
245 }
246