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 "square.h"
19 #include "level.h"
20 #include "creature.h"
21 #include "item.h"
22 #include "view_object.h"
23 #include "progress_meter.h"
24 #include "game.h"
25 #include "vision.h"
26 #include "view_index.h"
27 #include "inventory.h"
28 #include "poison_gas.h"
29 #include "tribe.h"
30 #include "view.h"
31 #include "event_listener.h"
32 
33 template <class Archive>
serialize(Archive & ar,const unsigned int version)34 void Square::serialize(Archive& ar, const unsigned int version) {
35   ar & SUBCLASS(OwnedObject<Square>);
36   ar(inventory, onFire);
37   ar(creature, landingLink, poisonGas);
38   ar(lastViewer, viewIndex);
39   ar(forbiddenTribe);
40   if (progressMeter)
41     progressMeter->addProgress();
42 }
43 
44 ProgressMeter* Square::progressMeter = nullptr;
45 
46 SERIALIZABLE(Square);
47 
Square()48 Square::Square() : viewIndex(new ViewIndex()) {
49 }
50 
~Square()51 Square::~Square() {
52 }
53 
putCreature(WCreature c)54 void Square::putCreature(WCreature c) {
55   //CHECK(canEnter(c)) << c->getName().bare() << " " << getName();
56   setCreature(c);
57   onEnter(c);
58   if (WGame game = c->getGame())
59     game->addEvent(EventInfo::CreatureMoved{c});
60 }
61 
setLandingLink(StairKey key)62 void Square::setLandingLink(StairKey key) {
63   landingLink = key;
64 }
65 
getLandingLink() const66 optional<StairKey> Square::getLandingLink() const {
67   return landingLink;
68 }
69 
setCreature(WCreature c)70 void Square::setCreature(WCreature c) {
71   creature = c;
72 }
73 
onAddedToLevel(Position pos) const74 void Square::onAddedToLevel(Position pos) const {
75   if (!inventory->isEmpty())
76     pos.getLevel()->addTickingSquare(pos.getCoord());
77 }
78 
tick(Position pos)79 void Square::tick(Position pos) {
80   setDirty(pos);
81   if (!inventory->isEmpty()) {
82     vector<WItem> discarded;
83     for (auto item : copyOf(inventory->getItems())) {
84       item->tick(pos);
85       if (item->isDiscarded())
86         discarded.push_back(item);
87     }
88     for (auto item : discarded)
89       inventory->removeItem(item);
90   }
91   poisonGas->tick(pos);
92   if (creature && poisonGas->getAmount() > 0.2) {
93     creature->poisonWithGas(min(1.0, poisonGas->getAmount()));
94   }
95 }
96 
itemLands(vector<WItem> item,const Attack & attack) const97 bool Square::itemLands(vector<WItem> item, const Attack& attack) const {
98   if (creature) {
99     if (item.size() > 1)
100       creature->you(MsgType::MISS_THROWN_ITEM_PLURAL, item[0]->getPluralTheName(item.size()));
101     else
102       creature->you(MsgType::MISS_THROWN_ITEM, item[0]->getTheName());
103   }
104   return false;
105 }
106 
onItemLands(Position pos,vector<PItem> item,const Attack & attack,int remainingDist,Vec2 dir,VisionId vision)107 void Square::onItemLands(Position pos, vector<PItem> item, const Attack& attack, int remainingDist, Vec2 dir,
108     VisionId vision) {
109   setDirty(pos);
110   if (creature) {
111     item[0]->onHitCreature(creature, attack, item.size());
112     if (!item[0]->isDiscarded())
113       dropItems(pos, std::move(item));
114     return;
115   }
116   item[0]->onHitSquareMessage(pos, item.size());
117   if (!item[0]->isDiscarded())
118     pos.dropItems(std::move(item));
119 }
120 
addPoisonGas(Position pos,double amount)121 void Square::addPoisonGas(Position pos, double amount) {
122   setDirty(pos);
123   if (pos.canSeeThru(VisionId::NORMAL)) {
124     poisonGas->addAmount(amount);
125     pos.getLevel()->addTickingSquare(pos.getCoord());
126   }
127 }
128 
getPoisonGasAmount() const129 double Square::getPoisonGasAmount() const {
130   return poisonGas->getAmount();
131 }
132 
getViewIndex(ViewIndex & ret,WConstCreature viewer) const133 void Square::getViewIndex(ViewIndex& ret, WConstCreature viewer) const {
134   if ((!viewer && lastViewer) || (viewer && lastViewer == viewer->getUniqueId())) {
135     ret = *viewIndex;
136     return;
137   }
138   // viewer is null only in Spectator mode, so setting a random id to lastViewer is ok
139   lastViewer = viewer ? viewer->getUniqueId() : Creature::Id();
140   double fireSize = 0;
141   if (!inventory->isEmpty())
142     for (WItem it : getInventory().getItems())
143       fireSize = max(fireSize, it->getFireSize());
144   if (WItem it = getTopItem())
145     ret.insert(copyOf(it->getViewObject()).setAttribute(ViewObject::Attribute::BURNING, fireSize));
146   if (poisonGas->getAmount() > 0)
147     ret.setHighlight(HighlightType::POISON_GAS, min(1.0, poisonGas->getAmount()));
148   *viewIndex = ret;
149 }
150 
onEnter(WCreature c)151 void Square::onEnter(WCreature c) {
152   setDirty(c->getPosition());
153 }
154 
dropItem(Position pos,PItem item)155 void Square::dropItem(Position pos, PItem item) {
156   dropItems(pos, makeVec(std::move(item)));
157 }
158 
dropItemsLevelGen(vector<PItem> items)159 void Square::dropItemsLevelGen(vector<PItem> items) {
160   getInventory().addItems(std::move(items));
161 }
162 
dropItems(Position pos,vector<PItem> items)163 void Square::dropItems(Position pos, vector<PItem> items) {
164   setDirty(pos);
165   pos.getLevel()->addTickingSquare(pos.getCoord());
166   dropItemsLevelGen(std::move(items));
167 }
168 
getCreature() const169 WCreature Square::getCreature() const {
170   return creature;
171 }
172 
isOnFire() const173 bool Square::isOnFire() const {
174   return onFire;
175 }
176 
setOnFire(bool state)177 void Square::setOnFire(bool state) {
178   onFire = state;
179 }
180 
removeCreature(Position pos)181 void Square::removeCreature(Position pos) {
182   setDirty(pos);
183   CHECK(creature);
184   WCreature tmp = creature;
185   creature = nullptr;
186 }
187 
getTopItem() const188 WItem Square::getTopItem() const {
189   if (inventory->isEmpty())
190     return nullptr;
191   else
192     return inventory->getItems().back();
193 }
194 
removeItem(Position pos,WItem it)195 PItem Square::removeItem(Position pos, WItem it) {
196   setDirty(pos);
197   return getInventory().removeItem(it);
198 }
199 
removeItems(Position pos,vector<WItem> it)200 vector<PItem> Square::removeItems(Position pos, vector<WItem> it) {
201   setDirty(pos);
202   return getInventory().removeItems(it);
203 }
204 
setDirty(Position pos)205 void Square::setDirty(Position pos) {
206   pos.getLevel()->setNeedsMemoryUpdate(pos.getCoord(), true);
207   pos.getLevel()->setNeedsRenderUpdate(pos.getCoord(), true);
208   lastViewer.reset();
209 }
210 
forbidMovementForTribe(Position pos,TribeId tribe)211 void Square::forbidMovementForTribe(Position pos, TribeId tribe) {
212   CHECK(!forbiddenTribe || forbiddenTribe == tribe);
213   forbiddenTribe = tribe;
214   pos.updateConnectivity();
215   setDirty(pos);
216 }
217 
allowMovementForTribe(Position pos,TribeId tribe)218 void Square::allowMovementForTribe(Position pos, TribeId tribe) {
219   CHECK(!forbiddenTribe || forbiddenTribe == tribe);
220   forbiddenTribe = none;
221   pos.updateConnectivity();
222   setDirty(pos);
223 }
224 
isTribeForbidden(TribeId tribe) const225 bool Square::isTribeForbidden(TribeId tribe) const {
226   return forbiddenTribe == tribe;
227 }
228 
getForbiddenTribe() const229 optional<TribeId> Square::getForbiddenTribe() const {
230   return forbiddenTribe;
231 }
232 
getInventory()233 Inventory& Square::getInventory() {
234   return *inventory;
235 }
236 
getInventory() const237 const Inventory& Square::getInventory() const {
238   return *inventory;
239 }
240 
clearItemIndex(ItemIndex index)241 void Square::clearItemIndex(ItemIndex index) {
242   inventory->clearIndex(index);
243 }
244