1 #include "stdafx.h"
2 #include "position.h"
3 #include "level.h"
4 #include "square.h"
5 #include "creature.h"
6 #include "item.h"
7 #include "view_id.h"
8 #include "model.h"
9 #include "view_index.h"
10 #include "sound.h"
11 #include "game.h"
12 #include "view.h"
13 #include "view_object.h"
14 #include "furniture.h"
15 #include "furniture_factory.h"
16 #include "creature_name.h"
17 #include "player_message.h"
18 #include "creature_attributes.h"
19 #include "fire.h"
20 #include "movement_set.h"
21 #include "furniture_array.h"
22 #include "inventory.h"
23 
24 SERIALIZE_DEF(Position, coord, level)
25 SERIALIZATION_CONSTRUCTOR_IMPL(Position);
26 
getHash() const27 int Position::getHash() const {
28   return combineHash(coord, level->getUniqueId());
29 }
30 
getCoord() const31 Vec2 Position::getCoord() const {
32   return coord;
33 }
34 
getLevel() const35 WLevel Position::getLevel() const {
36   return level;
37 }
38 
getModel() const39 WModel Position::getModel() const {
40   if (isValid())
41     return level->getModel();
42   else
43     return nullptr;
44 }
45 
getGame() const46 WGame Position::getGame() const {
47   if (level)
48     return level->getModel()->getGame();
49   else
50     return nullptr;
51 }
52 
Position(Vec2 v,WLevel l)53 Position::Position(Vec2 v, WLevel l) : coord(v), level(l) {
54 }
55 
56 const static int otherLevel = 1000000;
57 
dist8(const Position & pos) const58 int Position::dist8(const Position& pos) const {
59   if (pos.level != level || !isValid() || !pos.isValid())
60     return otherLevel;
61   return pos.getCoord().dist8(coord);
62 }
63 
isSameLevel(const Position & p) const64 bool Position::isSameLevel(const Position& p) const {
65   return isValid() && level == p.level;
66 }
67 
isSameModel(const Position & p) const68 bool Position::isSameModel(const Position& p) const {
69   return isValid() && p.isValid() && getModel() == p.getModel();
70 }
71 
isSameLevel(WConstLevel l) const72 bool Position::isSameLevel(WConstLevel l) const {
73   return isValid() && level == l;
74 }
75 
isValid() const76 bool Position::isValid() const {
77   return level && level->inBounds(coord);
78 }
79 
getDir(const Position & p) const80 Vec2 Position::getDir(const Position& p) const {
81   CHECK(isSameLevel(p));
82   return p.coord - coord;
83 }
84 
getLandingLink() const85 optional<StairKey> Position::getLandingLink() const {
86   if (isValid())
87     return getSquare()->getLandingLink();
88   else
89     return none;
90 }
91 
modSquare() const92 WSquare Position::modSquare() const {
93   CHECK(isValid());
94   return level->modSafeSquare(coord);
95 }
96 
getSquare() const97 WConstSquare Position::getSquare() const {
98   CHECK(isValid());
99   return level->getSafeSquare(coord);
100 }
101 
modFurniture(FurnitureLayer layer) const102 WFurniture Position::modFurniture(FurnitureLayer layer) const {
103   if (isValid())
104     return level->furniture->getBuilt(layer).getWritable(coord);
105   else
106     return nullptr;
107 }
108 
modFurniture(FurnitureType type) const109 WFurniture Position::modFurniture(FurnitureType type) const {
110   if (auto furniture = modFurniture(Furniture::getLayer(type)))
111     if (furniture->getType() == type)
112       return furniture;
113   return nullptr;
114 }
115 
getFurniture(FurnitureLayer layer) const116 WConstFurniture Position::getFurniture(FurnitureLayer layer) const {
117   if (isValid())
118     return level->furniture->getBuilt(layer).getReadonly(coord);
119   else
120     return nullptr;
121 }
122 
getFurniture(FurnitureType type) const123 WConstFurniture Position::getFurniture(FurnitureType type) const {
124   if (auto furniture = getFurniture(Furniture::getLayer(type)))
125     if (furniture->getType() == type)
126       return furniture;
127   return nullptr;
128 }
129 
getFurniture() const130 vector<WConstFurniture> Position::getFurniture() const {
131   vector<WConstFurniture> ret;
132   for (auto layer : ENUM_ALL(FurnitureLayer))
133     if (auto f = getFurniture(layer))
134       ret.push_back(f);
135   return ret;
136 }
137 
modFurniture() const138 vector<WFurniture> Position::modFurniture() const {
139   vector<WFurniture> ret;
140   for (auto layer : ENUM_ALL(FurnitureLayer))
141     if (auto f = modFurniture(layer))
142       ret.push_back(f);
143   return ret;
144 }
145 
getCreature() const146 WCreature Position::getCreature() const {
147   if (isValid())
148     return getSquare()->getCreature();
149   else
150     return nullptr;
151 }
152 
removeCreature()153 void Position::removeCreature() {
154   CHECK(isValid());
155   modSquare()->removeCreature(*this);
156 }
157 
operator ==(const Position & o) const158 bool Position::operator == (const Position& o) const {
159   return coord == o.coord && level == o.level;
160 }
161 
operator !=(const Position & o) const162 bool Position::operator != (const Position& o) const {
163   return !(o == *this);
164 }
165 
operator =(const Position & o)166 Position& Position::operator = (const Position& o) {
167   coord = o.coord;
168   level = o.level;
169   return *this;
170 }
171 
operator <(const Position & p) const172 bool Position::operator < (const Position& p) const {
173   if (!level)
174     return !!p.level;
175   if (!p.level)
176     return false;
177   if (level->getUniqueId() == p.level->getUniqueId())
178     return coord < p.coord;
179   else
180     return level->getUniqueId() < p.level->getUniqueId();
181 }
182 
183 const static int hearingRange = 30;
184 
unseenMessage(const PlayerMessage & msg) const185 void Position::unseenMessage(const PlayerMessage& msg) const {
186   if (isValid()) {
187     for (auto player : level->getPlayers())
188       if (player->canSee(*this))
189         return;
190     for (auto player : level->getPlayers())
191       if (dist8(player->getPosition()) < hearingRange) {
192         player->privateMessage(msg);
193         return;
194       }
195   }
196 }
197 
globalMessage(const PlayerMessage & msg) const198 void Position::globalMessage(const PlayerMessage& msg) const {
199   if (isValid())
200     for (auto player : level->getPlayers())
201       if (player->canSee(*this)) {
202         player->privateMessage(msg);
203         break;
204       }
205 }
206 
neighbors8() const207 vector<Position> Position::neighbors8() const {
208   vector<Position> ret;
209   for (Vec2 v : coord.neighbors8())
210     ret.push_back(Position(v, level));
211   return ret;
212 }
213 
neighbors4() const214 vector<Position> Position::neighbors4() const {
215   vector<Position> ret;
216   for (Vec2 v : coord.neighbors4())
217     ret.push_back(Position(v, level));
218   return ret;
219 }
220 
neighbors8(RandomGen & random) const221 vector<Position> Position::neighbors8(RandomGen& random) const {
222   vector<Position> ret;
223   for (Vec2 v : coord.neighbors8(random))
224     ret.push_back(Position(v, level));
225   return ret;
226 }
227 
neighbors4(RandomGen & random) const228 vector<Position> Position::neighbors4(RandomGen& random) const {
229   vector<Position> ret;
230   for (Vec2 v : coord.neighbors4(random))
231     ret.push_back(Position(v, level));
232   return ret;
233 }
234 
getRectangle(Rectangle rect) const235 vector<Position> Position::getRectangle(Rectangle rect) const {
236   vector<Position> ret;
237   for (Vec2 v : rect.translate(coord))
238     ret.emplace_back(v, level);
239   return ret;
240 }
241 
addCreature(PCreature c)242 void Position::addCreature(PCreature c) {
243   if (isValid()) {
244     WCreature ref = c.get();
245     getModel()->addCreature(std::move(c));
246     level->putCreature(coord, ref);
247   }
248 }
249 
addCreature(PCreature c,double delay)250 void Position::addCreature(PCreature c, double delay) {
251   if (isValid()) {
252     WCreature ref = c.get();
253     getModel()->addCreature(std::move(c), delay);
254     level->putCreature(coord, ref);
255   }
256 }
257 
plus(Vec2 v) const258 Position Position::plus(Vec2 v) const {
259   return Position(coord + v, level);
260 }
261 
minus(Vec2 v) const262 Position Position::minus(Vec2 v) const {
263   return Position(coord - v, level);
264 }
265 
getClickType() const266 optional<FurnitureClickType> Position::getClickType() const {
267   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
268     return furniture->getClickType();
269   else
270     return none;
271 }
272 
addSound(const Sound & sound1) const273 void Position::addSound(const Sound& sound1) const {
274   Sound sound(sound1);
275   sound.setPosition(*this);
276   getGame()->getView()->addSound(sound);
277 }
278 
getName() const279 string Position::getName() const {
280   for (auto layer : ENUM_ALL_REVERSE(FurnitureLayer))
281     if (auto furniture = getFurniture(layer))
282       return furniture->getName();
283   return "";
284 }
285 
getViewIndex(ViewIndex & index,WConstCreature viewer) const286 void Position::getViewIndex(ViewIndex& index, WConstCreature viewer) const {
287   if (isValid()) {
288     getSquare()->getViewIndex(index, viewer);
289     if (isUnavailable())
290       index.setHighlight(HighlightType::UNAVAILABLE);
291     for (auto furniture : getFurniture())
292       if (furniture->isVisibleTo(viewer) && furniture->getViewObject())
293         index.insert(*furniture->getViewObject());
294     if (index.noObjects())
295       index.insert(ViewObject(ViewId::EMPTY, ViewLayer::FLOOR_BACKGROUND));
296   }
297 }
298 
getItems() const299 const vector<WItem>& Position::getItems() const {
300   if (isValid())
301     return getSquare()->getInventory().getItems();
302   else {
303     static vector<WItem> empty;
304     return empty;
305   }
306 }
307 
getItems(function<bool (WConstItem)> predicate) const308 vector<WItem> Position::getItems(function<bool (WConstItem)> predicate) const {
309   if (isValid())
310     return getSquare()->getInventory().getItems(predicate);
311   else
312     return {};
313 }
314 
getItems(ItemIndex index) const315 const vector<WItem>& Position::getItems(ItemIndex index) const {
316   if (isValid())
317     return getSquare()->getInventory().getItems(index);
318   else {
319     static vector<WItem> empty;
320     return empty;
321   }
322 }
323 
removeItem(WItem it)324 PItem Position::removeItem(WItem it) {
325   CHECK(isValid());
326   return modSquare()->removeItem(*this, it);
327 }
328 
modInventory() const329 Inventory& Position::modInventory() const {
330   if (!isValid()) {
331     static Inventory empty;
332     return empty;
333   } else
334     return modSquare()->getInventory();
335 }
336 
getInventory() const337 const Inventory& Position::getInventory() const {
338   if (!isValid()) {
339     static Inventory empty;
340     return empty;
341   } else
342     return getSquare()->getInventory();
343 }
344 
removeItems(vector<WItem> it)345 vector<PItem> Position::removeItems(vector<WItem> it) {
346   CHECK(isValid());
347   return modSquare()->removeItems(*this, it);
348 }
349 
isUnavailable() const350 bool Position::isUnavailable() const {
351   return !isValid() || level->isUnavailable(coord);
352 }
353 
canEnter(WConstCreature c) const354 bool Position::canEnter(WConstCreature c) const {
355   return !isUnavailable() && !getCreature() && canEnterEmpty(c->getMovementType());
356 }
357 
canEnter(const MovementType & t) const358 bool Position::canEnter(const MovementType& t) const {
359   return !isUnavailable() && !getCreature() && canEnterEmpty(t);
360 }
361 
canEnterEmpty(WConstCreature c) const362 bool Position::canEnterEmpty(WConstCreature c) const {
363   return canEnterEmpty(c->getMovementType());
364 }
365 
canEnterEmpty(const MovementType & t,optional<FurnitureLayer> ignore) const366 bool Position::canEnterEmpty(const MovementType& t, optional<FurnitureLayer> ignore) const {
367   if (isUnavailable())
368     return false;
369   auto square = getSquare();
370   bool result = true;
371   for (auto furniture : getFurniture()) {
372     if (ignore == furniture->getLayer())
373       continue;
374     bool canEnter =
375         furniture->getMovementSet().canEnter(t, level->covered[coord], square->isOnFire(), square->getForbiddenTribe());
376     if (furniture->overridesMovement())
377       return canEnter;
378     else
379       result &= canEnter;
380   }
381   return result;
382 }
383 
onEnter(WCreature c)384 void Position::onEnter(WCreature c) {
385   for (auto layer : ENUM_ALL_REVERSE(FurnitureLayer))
386     if (auto f = getFurniture(layer)) {
387       bool overrides = f->overridesMovement();
388       // f can be removed in onEnter so don't use it after
389       f->onEnter(c);
390       if (overrides)
391         break;
392     }
393 }
394 
dropItem(PItem item)395 void Position::dropItem(PItem item) {
396   dropItems(makeVec(std::move(item)));
397 }
398 
dropItems(vector<PItem> v)399 void Position::dropItems(vector<PItem> v) {
400   if (isValid()) {
401     for (auto layer : ENUM_ALL_REVERSE(FurnitureLayer))
402       if (auto f = getFurniture(layer)) {
403         v = f->dropItems(*this, std::move(v));
404         if (v.empty())
405           return;
406         if (f->overridesMovement())
407           break;
408       }
409     modSquare()->dropItems(*this, std::move(v));
410   }
411 }
412 
removeFurniture(WConstFurniture f) const413 void Position::removeFurniture(WConstFurniture f) const {
414   level->removeLightSource(coord, f->getLightEmission());
415   auto layer = f->getLayer();
416   CHECK(layer != FurnitureLayer::GROUND);
417   CHECK(getFurniture(layer) == f);
418   level->furniture->getBuilt(layer).clearElem(coord);
419   level->furniture->getConstruction(coord, layer).reset();
420   updateConnectivity();
421   updateVisibility();
422   updateSupport();
423   setNeedsRenderUpdate(true);
424 }
425 
addFurniture(PFurniture f) const426 void Position::addFurniture(PFurniture f) const {
427   auto furniture = f.get();
428   level->setFurniture(coord, std::move(f));
429   updateConnectivity();
430   updateVisibility();
431   level->addLightSource(coord, furniture->getLightEmission());
432   setNeedsRenderUpdate(true);
433 }
434 
replaceFurniture(WConstFurniture prev,PFurniture next) const435 void Position::replaceFurniture(WConstFurniture prev, PFurniture next) const {
436   level->removeLightSource(coord, prev->getLightEmission());
437   auto furniture = next.get();
438   auto layer = next->getLayer();
439   CHECK(prev->getLayer() == layer);
440   CHECK(getFurniture(layer) == prev);
441   level->setFurniture(coord, std::move(next));
442   updateConnectivity();
443   updateVisibility();
444   updateSupport();
445   level->addLightSource(coord, furniture->getLightEmission());
446   setNeedsRenderUpdate(true);
447 }
448 
canConstruct(FurnitureType type) const449 bool Position::canConstruct(FurnitureType type) const {
450   return !isUnavailable() && FurnitureFactory::canBuild(type, *this) && FurnitureFactory::hasSupport(type, *this);
451 }
452 
isWall() const453 bool Position::isWall() const {
454   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
455     return furniture->isWall();
456   else
457     return false;
458 }
459 
construct(FurnitureType type,WCreature c)460 void Position::construct(FurnitureType type, WCreature c) {
461   if (construct(type, c->getTribeId()))
462     modFurniture(Furniture::getLayer(type))->onConstructedBy(c);
463 }
464 
construct(FurnitureType type,TribeId tribe)465 bool Position::construct(FurnitureType type, TribeId tribe) {
466   CHECK(!isUnavailable());
467   CHECK(canConstruct(type));
468   auto& construction = level->furniture->getConstruction(coord, Furniture::getLayer(type));
469   if (!construction || construction->type != type)
470     construction = FurnitureArray::Construction{type, 10};
471   if (--construction->time == 0) {
472     addFurniture(FurnitureFactory::get(type, tribe));
473     return true;
474   } else
475     return false;
476 }
477 
isActiveConstruction(FurnitureLayer layer) const478 bool Position::isActiveConstruction(FurnitureLayer layer) const {
479   return !isUnavailable() && !!level->furniture->getConstruction(coord, layer);
480 }
481 
isBurning() const482 bool Position::isBurning() const {
483   for (auto furniture : getFurniture())
484     if (furniture->getFire() && furniture->getFire()->isBurning())
485       return true;
486   return false;
487 }
488 
updateMovement()489 void Position::updateMovement() {
490   if (isValid()) {
491     if (isBurning()) {
492       if (!getSquare()->isOnFire()) {
493         modSquare()->setOnFire(true);
494         updateConnectivity();
495       }
496     } else
497       if (getSquare()->isOnFire()) {
498         modSquare()->setOnFire(false);
499         updateConnectivity();
500       }
501   }
502 }
503 
fireDamage(double amount)504 void Position::fireDamage(double amount) {
505   for (auto furniture : modFurniture())
506     furniture->fireDamage(*this, amount);
507   if (WCreature creature = getCreature())
508     creature->affectByFire(amount);
509   for (WItem it : getItems())
510     it->fireDamage(amount, *this);
511 }
512 
needsMemoryUpdate() const513 bool Position::needsMemoryUpdate() const {
514   return isValid() && level->needsMemoryUpdate(getCoord());
515 }
516 
setNeedsMemoryUpdate(bool s) const517 void Position::setNeedsMemoryUpdate(bool s) const {
518   if (isValid())
519     level->setNeedsMemoryUpdate(getCoord(), s);
520 }
521 
needsRenderUpdate() const522 bool Position::needsRenderUpdate() const {
523   return isValid() && level->needsRenderUpdate(getCoord());
524 }
525 
setNeedsRenderUpdate(bool s) const526 void Position::setNeedsRenderUpdate(bool s) const {
527   if (isValid())
528     level->setNeedsRenderUpdate(getCoord(), s);
529 }
530 
getViewObject() const531 const ViewObject& Position::getViewObject() const {
532   for (auto layer : ENUM_ALL_REVERSE(FurnitureLayer))
533     if (auto furniture = getFurniture(layer))
534       if (auto& obj = furniture->getViewObject())
535         return *obj;
536   return ViewObject::empty();
537 }
538 
forbidMovementForTribe(TribeId t)539 void Position::forbidMovementForTribe(TribeId t) {
540   if (!isUnavailable())
541     modSquare()->forbidMovementForTribe(*this, t);
542 }
543 
allowMovementForTribe(TribeId t)544 void Position::allowMovementForTribe(TribeId t) {
545   if (!isUnavailable())
546     modSquare()->allowMovementForTribe(*this, t);
547 }
548 
isTribeForbidden(TribeId t) const549 bool Position::isTribeForbidden(TribeId t) const {
550   return isValid() && getSquare()->isTribeForbidden(t);
551 }
552 
getForbiddenTribe() const553 optional<TribeId> Position::getForbiddenTribe() const {
554   if (isValid())
555     return getSquare()->getForbiddenTribe();
556   else
557     return none;
558 }
559 
getVisibleTiles(const Vision & vision)560 vector<Position> Position::getVisibleTiles(const Vision& vision) {
561   if (isValid())
562     return getLevel()->getVisibleTiles(coord, vision).transform([this] (Vec2 v) { return Position(v, getLevel()); });
563   else
564     return {};
565 }
566 
addPoisonGas(double amount)567 void Position::addPoisonGas(double amount) {
568   if (isValid())
569     modSquare()->addPoisonGas(*this, amount);
570 }
571 
getPoisonGasAmount() const572 double Position::getPoisonGasAmount() const {
573   if (isValid())
574     return getSquare()->getPoisonGasAmount();
575   else
576     return 0;
577 }
578 
isCovered() const579 bool Position::isCovered() const {
580   if (isValid())
581     return level->covered[coord];
582   else
583     return false;
584 }
585 
sunlightBurns() const586 bool Position::sunlightBurns() const {
587   return isValid() && level->isInSunlight(coord);
588 }
589 
getLightEmission() const590 double Position::getLightEmission() const {
591   if (!isValid())
592     return 0;
593   double ret = 0;
594   for (auto f : getFurniture())
595     ret += f->getLightEmission();
596   return ret;
597 }
598 
throwItem(PItem item,const Attack & attack,int maxDist,Vec2 direction,VisionId vision)599 void Position::throwItem(PItem item, const Attack& attack, int maxDist, Vec2 direction, VisionId vision) {
600   if (isValid())
601     level->throwItem(std::move(item), attack, maxDist, coord, direction, vision);
602 }
603 
throwItem(vector<PItem> item,const Attack & attack,int maxDist,Vec2 direction,VisionId vision)604 void Position::throwItem(vector<PItem> item, const Attack& attack, int maxDist, Vec2 direction, VisionId vision) {
605   if (isValid())
606     level->throwItem(std::move(item), attack, maxDist, coord, direction, vision);
607 }
608 
updateConnectivity() const609 void Position::updateConnectivity() const {
610   if (isValid()) {
611     for (auto& elem : level->sectors)
612       if (canNavigate(elem.first))
613         elem.second.add(coord);
614       else
615         elem.second.remove(coord);
616   }
617 }
618 
updateVisibility() const619 void Position::updateVisibility() const {
620   if (isValid())
621     level->updateVisibility(coord);
622 }
623 
updateSupport() const624 void Position::updateSupport() const {
625   for (auto pos : neighbors8())
626     for (auto f : pos.modFurniture())
627       if (!FurnitureFactory::hasSupport(f->getType(), pos))
628         f->destroy(pos, DestroyAction::Type::BASH);
629 }
630 
canNavigate(const MovementType & type) const631 bool Position::canNavigate(const MovementType& type) const {
632   optional<FurnitureLayer> ignore;
633   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
634     for (DestroyAction action : type.getDestroyActions())
635       if (furniture->canDestroy(type, action))
636         ignore = FurnitureLayer::MIDDLE;
637   return canEnterEmpty(type, ignore);
638 }
639 
getBestDestroyAction(const MovementType & movement) const640 optional<DestroyAction> Position::getBestDestroyAction(const MovementType& movement) const {
641   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
642     if (canEnterEmpty(movement, FurnitureLayer::MIDDLE)) {
643       optional<double> strength;
644       optional<DestroyAction> bestAction;
645       for (DestroyAction action : movement.getDestroyActions()) {
646         if (furniture->canDestroy(movement, action)) {
647           double thisStrength = *furniture->getStrength(action);
648           if (!strength || thisStrength < *strength) {
649             strength = thisStrength;
650             bestAction = action;
651           }
652         }
653       }
654       return bestAction;
655     }
656   return none;
657 }
658 
getNavigationCost(const MovementType & movement) const659 optional<double> Position::getNavigationCost(const MovementType& movement) const {
660   if (canEnterEmpty(movement)) {
661     if (getCreature())
662       return 5.0;
663     else
664       return 1.0;
665   }
666   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
667     if (auto destroyAction = getBestDestroyAction(movement))
668       return *furniture->getStrength(*destroyAction) / 10;
669   return none;
670 }
671 
canSeeThru(VisionId id) const672 bool Position::canSeeThru(VisionId id) const {
673   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
674     return furniture->canSeeThru(id);
675   else
676     return isValid();
677 }
678 
stopsProjectiles(VisionId id) const679 bool Position::stopsProjectiles(VisionId id) const {
680   if (auto furniture = getFurniture(FurnitureLayer::MIDDLE))
681     return furniture->stopsProjectiles(id);
682   else
683     return !isValid();
684 }
685 
isVisibleBy(WConstCreature c) const686 bool Position::isVisibleBy(WConstCreature c) const {
687   return isValid() && level->canSee(c, coord);
688 }
689 
clearItemIndex(ItemIndex index) const690 void Position::clearItemIndex(ItemIndex index) const {
691   if (isValid())
692     modSquare()->clearItemIndex(index);
693 }
694 
isChokePoint(const MovementType & movement) const695 bool Position::isChokePoint(const MovementType& movement) const {
696   return isValid() && level->isChokePoint(coord, movement);
697 }
698 
isConnectedTo(Position pos,const MovementType & movement) const699 bool Position::isConnectedTo(Position pos, const MovementType& movement) const {
700   return isValid() && pos.isValid() && level == pos.level &&
701       level->areConnected(pos.coord, coord, movement);
702 }
703 
getAllCreatures(int range) const704 vector<WCreature> Position::getAllCreatures(int range) const {
705   if (isValid())
706     return level->getAllCreatures(Rectangle::centered(coord, range));
707   else
708     return {};
709 }
710 
moveCreature(Position pos)711 void Position::moveCreature(Position pos) {
712   CHECK(isValid());
713   if (isSameLevel(pos))
714     level->moveCreature(getCreature(), getDir(pos));
715   else if (isSameModel(pos))
716     level->changeLevel(pos, getCreature());
717   else pos.getLevel()->landCreature({pos}, getModel()->extractCreature(getCreature()));
718 }
719 
moveCreature(Vec2 direction)720 void Position::moveCreature(Vec2 direction) {
721   CHECK(isValid());
722   level->moveCreature(getCreature(), direction);
723 }
724 
canPass(Position position,WConstCreature c)725 static bool canPass(Position position, WConstCreature c) {
726   return position.canEnterEmpty(c) && (!position.getCreature() ||
727       !position.getCreature()->getAttributes().isBoulder());
728 }
729 
canMoveCreature(Vec2 direction) const730 bool Position::canMoveCreature(Vec2 direction) const {
731   if (!isUnavailable()) {
732     WCreature creature = getCreature();
733     Position destination = plus(direction);
734     if (level->noDiagonalPassing && direction.isCardinal8() && !direction.isCardinal4() &&
735         !canPass(plus(Vec2(direction.x, 0)), creature) &&
736         !canPass(plus(Vec2(0, direction.y)), creature))
737       return false;
738     return destination.canEnter(getCreature());
739   } else
740     return false;
741 }
742 
canMoveCreature(Position pos) const743 bool Position::canMoveCreature(Position pos) const {
744   return !isSameLevel(pos) || canMoveCreature(getDir(pos));
745 }
746 
getLight() const747 double Position::getLight() const {
748   if (isValid())
749     return level->getLight(coord);
750   else
751     return 0;
752 }
753 
getStairsTo(Position pos) const754 optional<Position> Position::getStairsTo(Position pos) const {
755   CHECK(isValid() && pos.isValid());
756   CHECK(!isSameLevel(pos));
757   return level->getStairsTo(pos.level);
758 }
759 
swapCreatures(WCreature c)760 void Position::swapCreatures(WCreature c) {
761   CHECK(isValid() && getCreature());
762   level->swapCreatures(getCreature(), c);
763 }
764 
withCoord(Vec2 newCoord) const765 Position Position::withCoord(Vec2 newCoord) const {
766   CHECK(isValid());
767   return Position(newCoord, level);
768 }
769 
putCreature(WCreature c)770 void Position::putCreature(WCreature c) {
771   CHECK(isValid());
772   level->putCreature(coord, c);
773 }
774 
775