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