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