1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23
24 #include "ultima/ultima8/world/container.h"
25
26 #include "ultima/ultima8/kernel/object_manager.h"
27 #include "ultima/ultima8/usecode/uc_machine.h"
28 #include "ultima/ultima8/usecode/uc_list.h"
29 #include "ultima/ultima8/world/actors/main_actor.h"
30 #include "ultima/ultima8/world/get_object.h"
31 #include "ultima/ultima8/ultima8.h"
32
33
34 namespace Ultima {
35 namespace Ultima8 {
36
DEFINE_RUNTIME_CLASSTYPE_CODE(Container)37 DEFINE_RUNTIME_CLASSTYPE_CODE(Container)
38
39 Container::Container() {
40 }
41
42
~Container()43 Container::~Container() {
44 // TODO: handle container's _contents.
45 // Either destroy the _contents, or move them up to this container's parent?
46
47
48
49 // if we don't have an _objId, we _must_ delete children
50 if (_objId == 0xFFFF) {
51 Std::list<Item *>::iterator iter;
52 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
53 delete(*iter);
54 }
55 }
56 }
57
58
assignObjId()59 ObjId Container::assignObjId() {
60 ObjId id = Item::assignObjId();
61
62 Std::list<Item *>::iterator iter;
63 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
64 (*iter)->assignObjId();
65 (*iter)->setParent(id);
66 }
67
68 return id;
69 }
70
clearObjId()71 void Container::clearObjId() {
72 Item::clearObjId();
73
74 Std::list<Item *>::iterator iter;
75 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
76 // make sure we don't clear the ObjId of an Actor
77 assert((*iter)->getObjId() >= 256);
78
79 (*iter)->clearObjId();
80 }
81 }
82
83
CanAddItem(Item * item,bool checkwghtvol)84 bool Container::CanAddItem(Item *item, bool checkwghtvol) {
85 if (!item) return false;
86 if (item->getParent() == this->getObjId()) return true; // already in here
87
88 if (item->getObjId() < 256) return false; // actors don't fit in containers
89
90 Container *c = dynamic_cast<Container *>(item);
91 if (c) {
92 // To quote Exult: "Watch for snake eating itself."
93 Container *p = this;
94 do {
95 if (p == c)
96 return false;
97 } while ((p = p->getParentAsContainer()) != nullptr);
98 }
99
100 if (checkwghtvol) {
101
102 uint32 volume = getContentVolume();
103 uint32 capacity = getCapacity();
104
105 // Because Avatar should be able to remove backpack and haul a barrel
106 // or chest on his back instead, artificially increase backpack volume
107 // for chests or barrels.
108 uint32 shapeid = item->getShape();
109 if (GAME_IS_U8 && (shapeid == 115 /*Barrel*/
110 || shapeid == 78 || shapeid == 117 /*Chests*/)) {
111 // TODO: make this off by default, but can enable it through config
112 MainActor *avatar = getMainActor();
113 ObjId bp = avatar->getEquip(7); // !! constant
114 Container *avatarbackpack = getContainer(bp);
115 if (avatarbackpack == this) {
116 capacity = 500;
117 }
118 }
119
120 // FIXME: this check isn't entirely correct. (combining items,...?)
121 if (volume + item->getVolume() > capacity)
122 return false;
123
124 Item *p = getTopItem();
125 Item *current = item->getTopItem();
126
127 // From outside to inside Avatar's inventory?
128 if (p->getObjId() == 1 && current->getObjId() != 1) {
129 MainActor *av = getMainActor();
130 unsigned int str = av->getStr();
131 // FIXME: this check isn't entirely correct. (combining items,...?)
132 //CONSTANT!
133 if (p->getTotalWeight() + item->getTotalWeight() > 40 * str)
134 return false;
135 }
136 }
137
138 return true;
139 }
140
addItem(Item * item,bool checkwghtvol)141 bool Container::addItem(Item *item, bool checkwghtvol) {
142 if (!CanAddItem(item, checkwghtvol)) return false;
143 if (item->getParent() == _objId) return true; // already in here
144
145 _contents.push_back(item);
146 return true;
147 }
148
149
removeItem(Item * item)150 bool Container::removeItem(Item *item) {
151 Std::list<Item *>::iterator iter;
152
153 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
154 if (*iter == item) {
155 _contents.erase(iter);
156 return true;
157 }
158 }
159 return false;
160 }
161
moveItemToEnd(Item * item)162 bool Container::moveItemToEnd(Item *item) {
163 Std::list<Item *>::iterator iter;
164
165 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
166 if (*iter == item) {
167 // found; move to end
168 _contents.erase(iter);
169 _contents.push_back(item);
170 return true;
171 }
172 }
173
174 // not found
175
176 return false;
177 }
178
removeContents()179 void Container::removeContents() {
180 // CHECKME: ethereal items?
181
182 Container *parentCon = getParentAsContainer();
183 if (parentCon) {
184 // move _contents to parent
185 while (_contents.begin() != _contents.end()) {
186 Item *item = *(_contents.begin());
187 item->moveToContainer(parentCon);
188 }
189 } else {
190 // move _contents to our coordinates
191 while (_contents.begin() != _contents.end()) {
192 Item *item = *(_contents.begin());
193 item->move(_x, _y, _z);
194 }
195 }
196 }
197
198
destroyContents()199 void Container::destroyContents() {
200 while (_contents.begin() != _contents.end()) {
201 Item *item = *(_contents.begin());
202 Container *cont = dynamic_cast<Container *>(item);
203 if (cont) cont->destroyContents();
204 item->destroy(true); // we destroy the item immediately
205 }
206 }
207
setFlagRecursively(uint32 mask)208 void Container::setFlagRecursively(uint32 mask) {
209 setFlag(mask);
210
211 Std::list<Item *>::iterator iter;
212 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
213 (*iter)->setFlag(mask);
214 Container *cont = dynamic_cast<Container *>(*iter);
215 if (cont) cont->setFlagRecursively(mask);
216 }
217 }
218
destroy(bool delnow)219 void Container::destroy(bool delnow) {
220 //! What do we do with our _contents?
221 //! (in Exult we remove the _contents)
222
223 removeContents();
224
225 Item::destroy(delnow);
226 }
227
getTotalWeight() const228 uint32 Container::getTotalWeight() const {
229 uint32 weight = Item::getTotalWeight();
230
231 // CONSTANT!
232 if (GAME_IS_U8 && getShape() == 79) {
233 // _contents of keyring don't weigh anything
234 return weight;
235 }
236
237 // CONSTANT!
238 if (GAME_IS_U8 && getShape() == 115) {
239 // barrel weight is unreasonably heavy
240 weight = 300;
241 }
242
243 Std::list<Item *>::const_iterator iter;
244
245 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
246 weight += (*iter)->getTotalWeight();
247 }
248
249 return weight;
250 }
251
getCapacity() const252 uint32 Container::getCapacity() const {
253 uint32 volume = getShapeInfo()->_volume;
254
255 return (volume == 0) ? 32 : volume;
256 }
257
getContentVolume() const258 uint32 Container::getContentVolume() const {
259 uint32 volume = 0;
260
261 Std::list<Item *>::const_iterator iter;
262
263 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
264 volume += (*iter)->getVolume();
265 }
266
267 return volume;
268 }
269
containerSearch(UCList * itemlist,const uint8 * loopscript,uint32 scriptsize,bool recurse) const270 void Container::containerSearch(UCList *itemlist, const uint8 *loopscript,
271 uint32 scriptsize, bool recurse) const {
272 Std::list<Item *>::const_iterator iter;
273 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
274 // check item against loopscript
275 if ((*iter)->checkLoopScript(loopscript, scriptsize)) {
276 assert(itemlist->getElementSize() == 2);
277 uint16 oId = (*iter)->getObjId();
278 itemlist->appenduint16(oId);
279 }
280
281 if (recurse) {
282 // recurse into child-containers
283 Container *container = dynamic_cast<Container *>(*iter);
284 if (container)
285 container->containerSearch(itemlist, loopscript,
286 scriptsize, recurse);
287 }
288 }
289 }
290
getFirstItemWithShape(uint16 shapeno,bool recurse)291 Item *Container::getFirstItemWithShape(uint16 shapeno, bool recurse) {
292 Std::list<Item *>::iterator iter;
293 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
294 if ((*iter)->getShape() == shapeno)
295 return *iter;
296
297 if (recurse) {
298 // recurse into child-containers
299 Container *container = dynamic_cast<Container *>(*iter);
300 if (container) {
301 Item *result = container->getFirstItemWithShape(shapeno, recurse);
302 if (result)
303 return result;
304 }
305 }
306 }
307
308 return nullptr;
309 }
310
getItemsWithShapeFamily(Std::vector<Item * > & itemlist,uint16 family,bool recurse)311 void Container::getItemsWithShapeFamily(Std::vector<Item *> &itemlist, uint16 family, bool recurse) {
312 Std::list<Item *>::iterator iter;
313 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
314 if ((*iter)->getShapeInfo()->_family == family)
315 itemlist.push_back(*iter);
316
317 if (recurse) {
318 // recurse into child-containers
319 Container *container = dynamic_cast<Container *>(*iter);
320 if (container) {
321 container->getItemsWithShapeFamily(itemlist, family, recurse);
322 }
323 }
324 }
325
326 }
327
dumpInfo() const328 void Container::dumpInfo() const {
329 Item::dumpInfo();
330
331 pout << "Volume: " << getContentVolume() << "/" << getCapacity()
332 << ", total weight: " << getTotalWeight() << Std::endl;
333 }
334
saveData(Common::WriteStream * ws)335 void Container::saveData(Common::WriteStream *ws) {
336 Item::saveData(ws);
337 ws->writeUint32LE(static_cast<uint32>(_contents.size()));
338 Std::list<Item *>::iterator iter;
339 for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
340 ObjectManager::get_instance()->saveObject(ws, *iter);
341 }
342 }
343
loadData(Common::ReadStream * rs,uint32 version)344 bool Container::loadData(Common::ReadStream *rs, uint32 version) {
345 if (!Item::loadData(rs, version)) return false;
346
347 uint32 contentcount = rs->readUint32LE();
348
349 // read contents
350 for (unsigned int i = 0; i < contentcount; ++i) {
351 Object *obj = ObjectManager::get_instance()->loadObject(rs, version);
352 Item *item = dynamic_cast<Item *>(obj);
353 if (!item) return false;
354
355 addItem(item);
356 item->setParent(_objId);
357 }
358
359 return true;
360 }
361
362
I_removeContents(const uint8 * args,unsigned int)363 uint32 Container::I_removeContents(const uint8 *args, unsigned int /*argsize*/) {
364 ARG_CONTAINER_FROM_PTR(container);
365 if (!container) return 0;
366
367 container->removeContents();
368 return 0;
369 }
370
I_destroyContents(const uint8 * args,unsigned int)371 uint32 Container::I_destroyContents(const uint8 *args, unsigned int /*argsize*/) {
372 ARG_CONTAINER_FROM_PTR(container);
373 if (!container) return 0;
374
375 container->destroyContents();
376 return 0;
377 }
378
379 } // End of namespace Ultima8
380 } // End of namespace Ultima
381