1 /*
2  *  contain.cc - Container objects.
3  *
4  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
5  *  Copyright (C) 2000-2013  The Exult Team
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25 
26 #include "contain.h"
27 #include "gamewin.h"
28 #include "gamemap.h"
29 #include "objiter.h"
30 #include "game.h"
31 #include "ucmachine.h"
32 #include "keyring.h"
33 #include "utils.h"
34 #include "Gump_manager.h"
35 #include "databuf.h"
36 #include "ucsched.h"
37 #include "cheat.h"
38 #include "ready.h"
39 #include "weaponinf.h"
40 #include "frflags.h"
41 #include "exult.h"
42 #include "ShortcutBar_gump.h"
43 #include "ignore_unused_variable_warning.h"
44 
45 #ifdef USE_EXULTSTUDIO
46 #include "server.h"
47 #include "objserial.h"
48 #include "servemsg.h"
49 #endif
50 
51 using std::cout;
52 using std::endl;
53 using std::ostream;
54 
55 /*
56  *  Determines if a shape can be added/found inside a container.
57  */
Can_be_added(const Container_game_object * cont,int shapenum,bool allow_locked=false)58 static inline bool Can_be_added(
59     const Container_game_object *cont,
60     int shapenum,
61     bool allow_locked = false
62 ) {
63 	const Shape_info &continfo = cont->get_info();
64 	const Shape_info &add_info = ShapeID::get_info(shapenum);
65 	return !(cont->get_shapenum() == shapenum   // Shape can't be inside itself.
66 	         || (!allow_locked && continfo.is_container_locked())   // Locked container.
67 	         || !continfo.is_shape_accepted(shapenum)  // Shape can't be inside.
68 	         || (!add_info.is_spell() && add_info.is_on_fire()));	// Object is on fire, and can't be inside others
69 }
70 
71 // Max. we'll hold.  (Guessing).
get_max_volume() const72 int Container_game_object::get_max_volume() const {
73 	return get_info().has_extradimensional_storage() ? 0 : get_volume();
74 }
75 
76 /*
77  *  Remove an object.  The object's (cx, cy) fields are set to invalid
78  *  #'s (255, 255).
79  */
80 
remove(Game_object * obj)81 void Container_game_object::remove(
82     Game_object *obj
83 ) {
84 	if (objects.is_empty())
85 		return;
86 	int shapenum = obj->get_shapenum();
87 	volume_used -= obj->get_volume();
88 	obj->set_owner(nullptr);
89 	obj->set_invalid();     // No longer part of world.
90 	objects.remove(obj);
91 	if(g_shortcutBar)
92 		g_shortcutBar->check_for_updates(shapenum);
93 }
94 
95 /*
96  *  Add an object.
97  *
98  *  Output: 1, meaning object is completely contained in this.  Obj may
99  *          be deleted in this case if combine==true.
100  *      0 if not enough space, although obj's quantity may be
101  *          reduced if combine==true.
102  */
103 
add(Game_object * obj,bool dont_check,bool combine,bool noset)104 bool Container_game_object::add(
105     Game_object *obj,
106     bool dont_check,        // True to skip volume/recursion check.
107     bool combine,           // True to try to combine obj.  MAY
108     //   cause obj to be deleted.
109     bool noset      // True to prevent actors from setting sched. weapon.
110 ) {
111 	ignore_unused_variable_warning(noset);
112 	// Prevent dragging the avatar into a container.
113 	// Casting to void* to avoid including actors.h.
114 	if (obj == static_cast<void *>(gwin->get_main_actor()))
115 		return false;
116 	const Shape_info &info = get_info();
117 	if (!dont_check && (get_shapenum() == obj->get_shapenum()   // Shape can't be inside itself (not sure originals check this).
118 	        || info.is_container_locked())) // Locked container.
119 		return false;
120 	if (!info.is_shape_accepted(obj->get_shapenum()))   // Shape can't be inside.
121 		return false;
122 	const Shape_info &add_info = obj->get_info();
123 	if (!dont_check && !cheat.in_map_editor() && !cheat.in_hack_mover() && (!add_info.is_spell() && add_info.is_on_fire()))
124 		return false;
125 
126 	// Always check this. ALWAYS!
127 	Game_object *parent = this;
128 	do              // Watch for snake eating itself.
129 		if (obj == parent)
130 			return false;
131 	while ((parent = parent->get_owner()) != nullptr);
132 
133 	if (combine) {          // Should we try to combine?
134 		const Shape_info &info = obj->get_info();
135 		int quant = obj->get_quantity();
136 		// Combine, but don't add.
137 		int newquant = add_quantity(quant, obj->get_shapenum(),
138 		                            info.has_quality() ? obj->get_quality() : c_any_qual,
139 		                            obj->get_framenum(), true);
140 		if (newquant == 0) { // All added?
141 			int shape_num = obj->get_shapenum();
142 			obj->remove_this();
143 			if(g_shortcutBar)
144 				g_shortcutBar->check_for_updates(shape_num);
145 			return true;
146 		} else if (newquant < quant) // Partly successful.
147 			obj->modify_quantity(newquant - quant);
148 	}
149 	int objvol = obj->get_volume();
150 	if (!cheat.in_hack_mover() && !dont_check) {
151 		int maxvol = get_max_volume();
152 		// maxvol = 0 means infinite (ship's hold, hollow tree, etc...)
153 		if (maxvol > 0 && objvol + volume_used > maxvol)
154 			return false;   // Doesn't fit.
155 	}
156 	volume_used += objvol;
157 	obj->set_owner(this);       // Set us as the owner.
158 	objects.append(obj->shared_from_this());        // Append to chain.
159 	// Guessing:
160 	if (get_flag(Obj_flags::okay_to_take))
161 		obj->set_flag(Obj_flags::okay_to_take);
162 	if(g_shortcutBar)
163 		g_shortcutBar->check_for_updates(obj->get_shapenum());
164 	return true;
165 }
166 
167 /*
168  *  Change shape of a member.
169  */
170 
change_member_shape(Game_object * obj,int newshape)171 void Container_game_object::change_member_shape(
172     Game_object *obj,
173     int newshape
174 ) {
175 	int oldvol = obj->get_volume();
176 	obj->set_shape(newshape);
177 	// Update total volume.
178 	volume_used += obj->get_volume() - oldvol;
179 }
180 
181 /*
182  *  Add a key (by quality) to the SI keyring.
183  *
184  *  Output: 1 if successful, else 0 (and this does need to be an int).
185  */
186 
Add2keyring(int qual,int framenum)187 static int Add2keyring(
188     int qual,
189     int framenum
190 ) {
191 	if (framenum >= 21 && framenum <= 23)
192 		return 0;       // Fire, ice, blackrock in SI.
193 	Keyring *ring =
194 	    Game_window::get_instance()->get_usecode()->getKeyring();
195 	// Valid quality & not already there?
196 	if (qual != c_any_qual && !ring->checkkey(qual)) {
197 		ring->addkey(qual);
198 		return 1;
199 	}
200 	return 0;
201 }
202 
203 /*
204  *  Recursively add a quantity of an item to those existing in
205  *  this container, and create new objects if necessary.
206  *
207  *  Output: Delta decremented # added.
208  */
209 
add_quantity(int delta,int shapenum,int qual,int framenum,bool dontcreate,bool temporary)210 int Container_game_object::add_quantity(
211     int delta,          // Quantity to add.
212     int shapenum,           // Shape #.
213     int qual,           // Quality, or c_any_qual for any.
214     int framenum,           // Frame, or c_any_framenum for any.
215     bool dontcreate,        // If true, don't create new objs.
216     bool temporary      // If objects should be temporary
217 ) {
218 	if (delta <= 0 || !Can_be_added(this, shapenum))
219 		return delta;
220 
221 	int cant_add = 0;       // # we can't add due to weight.
222 	int maxweight = get_max_weight();// Check weight.
223 	if (maxweight) {
224 		maxweight *= 10;    // Work in .1 stones.
225 		int avail = maxweight - get_outermost()->get_weight();
226 		int objweight = Ireg_game_object::get_weight(shapenum, delta);
227 		if (objweight && objweight > avail) {
228 			// Limit what we can add.
229 			// Work in 1/100ths.
230 			int weight1 = (10 * objweight) / delta;
231 			cant_add = delta - (10 * avail) / (weight1 ? weight1 : 1);
232 			if (cant_add >= delta)
233 				return delta;   // Can't add any.
234 			delta -= cant_add;
235 		}
236 	}
237 	const Shape_info &info = ShapeID::get_info(shapenum);
238 	bool has_quantity = info.has_quantity();    // Quantity-type shape?
239 	bool has_quantity_frame = has_quantity ? info.has_quantity_frames() : false;
240 	// Note:  quantity is ignored for
241 	//   figuring volume.
242 	Game_object *obj;
243 	if (!objects.is_empty()) {
244 		// First try existing items.
245 		Object_iterator next(objects);
246 		while (delta && (obj = next.get_next()) != nullptr) {
247 			if (has_quantity && obj->get_shapenum() == shapenum &&
248 			        (framenum == c_any_framenum || has_quantity_frame ||
249 			         obj->get_framenum() == framenum))
250 				delta = obj->modify_quantity(delta);
251 			// Adding key to SI keyring?
252 			else if (GAME_SI && shapenum == 641 &&
253 			         obj->get_shapenum() == 485 && delta == 1)
254 				delta -= Add2keyring(qual, framenum);
255 		}
256 		next.reset();           // Now try recursively.
257 		while ((obj = next.get_next()) != nullptr)
258 			delta = obj->add_quantity(
259 			            delta, shapenum, qual, framenum, true, temporary);
260 	}
261 	if (!delta || dontcreate)   // All added?
262 		return delta + cant_add;
263 	else
264 		return cant_add + create_quantity(delta, shapenum, qual,
265 		                                  framenum == c_any_framenum ? 0 : framenum, temporary);
266 }
267 
268 /*
269  *  Recursively create a quantity of an item.  Assumes weight check has
270  *  already been done.
271  *
272  *  Output: Delta decremented # added.
273  */
274 
create_quantity(int delta,int shnum,int qual,int frnum,bool temporary)275 int Container_game_object::create_quantity(
276     int delta,          // Quantity to add.
277     int shnum,          // Shape #.
278     int qual,           // Quality, or c_any_qual for any.
279     int frnum,          // Frame.
280     bool temporary          // Create temporary quantity
281 ) {
282 	if (!Can_be_added(this, shnum))
283 		return delta;
284 	// Usecode container?
285 	const Shape_info &info = ShapeID::get_info(get_shapenum());
286 	if (info.get_ready_type() == ucont)
287 		return delta;
288 	const Shape_info &shp_info = ShapeID::get_info(shnum);
289 	if (!shp_info.has_quality())    // Not a quality object?
290 		qual = c_any_qual;  // Then don't set it.
291 	while (delta) {         // Create them here first.
292 		Game_object_shared newobj = gmap->create_ireg_object(
293 		                          shp_info, shnum, frnum, 0, 0, 0);
294 		if (!add(newobj.get())) {
295 			newobj.reset();
296 			break;
297 		}
298 
299 		// Set temporary
300 		if (temporary) newobj->set_flag(Obj_flags::is_temporary);
301 
302 		if (qual != c_any_qual) // Set desired quality.
303 			newobj->set_quality(qual);
304 		delta--;
305 		if (delta > 0)
306 			delta =  newobj->modify_quantity(delta);
307 	}
308 	if (!delta)         // All done?
309 		return 0;
310 	// Now try those below.
311 	Game_object *obj;
312 	if (objects.is_empty())
313 		return delta;
314 	Object_iterator next(objects);
315 	while ((obj = next.get_next()) != nullptr)
316 		delta = obj->create_quantity(delta, shnum, qual, frnum);
317 	return delta;
318 }
319 
320 /*
321  *  Recursively remove a quantity of an item from those existing in
322  *  this container.
323  *
324  *  Output: Delta decremented by # removed.
325  */
326 
remove_quantity(int delta,int shapenum,int qual,int framenum)327 int Container_game_object::remove_quantity(
328     int delta,          // Quantity to remove.
329     int shapenum,           // Shape #.
330     int qual,           // Quality, or c_any_qual for any.
331     int framenum            // Frame, or c_any_framenum for any.
332 ) {
333 	if (objects.is_empty() || !Can_be_added(this, shapenum))
334 		return delta;       // Empty.
335 	Game_object *obj = objects.get_first();
336 	Game_object *last = obj->get_prev();    // Save last.
337 	Game_object *next;
338 	while (obj && delta) {
339 		// Might be deleting obj.
340 		next = obj == last ? nullptr : obj->get_next();
341 		bool del = false;   // Gets 'deleted' flag.
342 		if (obj->get_shapenum() == shapenum &&
343 		        (qual == c_any_qual || obj->get_quality() == qual) &&
344 		        (framenum == c_any_framenum ||
345 		         (obj->get_framenum() & 31) == framenum))
346 			delta = -obj->modify_quantity(-delta, &del);
347 
348 		if (!del)       // Still there?
349 			// Do it recursively.
350 			delta = obj->remove_quantity(delta, shapenum,
351 			                             qual, framenum);
352 		obj = next;
353 	}
354 	return delta;
355 }
356 
357 /*
358  *  Find and return a desired item.
359  *
360  *  Output: ->object if found, else nullptr.
361  */
362 
find_item(int shapenum,int qual,int framenum)363 Game_object *Container_game_object::find_item(
364     int shapenum,           // Shape #.
365     int qual,           // Quality, or c_any_qual for any.
366     int framenum            // Frame, or c_any_framenum for any.
367 ) {
368 	if (objects.is_empty() || !Can_be_added(this, shapenum, true))
369 		return nullptr;       // Empty.
370 	Game_object *obj;
371 	Object_iterator next(objects);
372 	while ((obj = next.get_next()) != nullptr) {
373 		if (obj->get_shapenum() == shapenum &&
374 		        (framenum == c_any_framenum ||
375 		         (obj->get_framenum() & 31) == framenum) &&
376 		        (qual == c_any_qual || obj->get_quality() == qual))
377 			return obj;
378 
379 		// Do it recursively.
380 		Game_object *found = obj->find_item(shapenum, qual, framenum);
381 		if (found)
382 			return found;
383 	}
384 	return nullptr;
385 }
386 
387 /*
388  *  Displays the object's gump.
389  *  Returns true if the gump has been handled.
390  */
391 
show_gump(int event)392 bool Container_game_object::show_gump(
393     int event
394 ) {
395 	ignore_unused_variable_warning(event);
396 	const Shape_info &inf = get_info();
397 	int gump;
398 	if (cheat.in_map_editor())
399 		return true;    // Do nothing.
400 	else if (inf.has_object_flag(get_framenum(),
401 	                             inf.has_quality() ? get_quality() : -1, Frame_flags::fp_force_usecode))
402 		// Run normal usecode fun.
403 		return false;
404 	else if ((gump = inf.get_gump_shape()) >= 0) {
405 		Gump_manager *gump_man = gumpman;
406 		gump_man->add_gump(this, gump);
407 		return true;
408 	}
409 	return false;
410 }
411 
412 /*
413  *  Run usecode when double-clicked.
414  */
415 
activate(int event)416 void Container_game_object::activate(
417     int event
418 ) {
419 	if (edit())
420 		return;         // Map-editing.
421 	if (!show_gump(event))
422 		// Try to run normal usecode fun.
423 		ucmachine->call_usecode(get_usecode(), this,
424 		                        static_cast<Usecode_machine::Usecode_events>(event));
425 }
426 
427 /*
428  *  Edit in ExultStudio.
429  */
430 
edit()431 bool Container_game_object::edit(
432 ) {
433 #ifdef USE_EXULTSTUDIO
434 	if (client_socket >= 0 &&   // Talking to ExultStudio?
435 	        cheat.in_map_editor()) {
436 		editing.reset();
437 		Tile_coord t = get_tile();
438 		std::string name = get_name();
439 		if (Container_out(client_socket, this, t.tx, t.ty, t.tz,
440 		                  get_shapenum(), get_framenum(), get_quality(), name,
441 		                  get_obj_hp(),
442 		                  get_flag(Obj_flags::invisible),
443 		                  get_flag(Obj_flags::okay_to_take)) != -1) {
444 			cout << "Sent object data to ExultStudio" << endl;
445 			editing = shared_from_this();
446 		} else
447 			cout << "Error sending object to ExultStudio" << endl;
448 		return true;
449 	}
450 #endif
451 	return false;
452 }
453 
454 /*
455  *  Message to update from ExultStudio.
456  */
457 
update_from_studio(unsigned char * data,int datalen)458 void Container_game_object::update_from_studio(
459     unsigned char *data,
460     int datalen
461 ) {
462 #ifdef USE_EXULTSTUDIO
463 	Container_game_object *obj;
464 	int tx;
465 	int ty;
466 	int tz;
467 	int shape;
468 	int frame;
469 	int quality;
470 	unsigned char res;
471 	bool invis;
472 	bool can_take;
473 	std::string name;
474 	if (!Container_in(data, datalen, obj, tx, ty, tz, shape, frame,
475 	                  quality, name, res, invis, can_take)) {
476 		cout << "Error decoding object" << endl;
477 		return;
478 	}
479 	if (!editing || obj != editing.get()) {
480 		cout << "Obj from ExultStudio is not being edited" << endl;
481 		return;
482 	}
483 	// Keeps NPC alive until end of function
484 	//Game_object_shared keep = std::move(editing); // He may have chosen 'Apply', so still editing.
485 	if (invis)
486 		obj->set_flag(Obj_flags::invisible);
487 	else
488 		obj->clear_flag(Obj_flags::invisible);
489 	if (can_take)
490 		obj->set_flag(Obj_flags::okay_to_take);
491 	else
492 		obj->clear_flag(Obj_flags::okay_to_take);
493 	gwin->add_dirty(obj);
494 	obj->set_shape(shape, frame);
495 	gwin->add_dirty(obj);
496 	obj->set_quality(quality);
497 	obj->set_obj_hp(res);
498 	Container_game_object *owner = obj->get_owner();
499 	if (!owner) {
500 		// See if it moved -- but only if not inside something!
501 		Tile_coord oldt = obj->get_tile();
502 		if (oldt.tx != tx || oldt.ty != ty || oldt.tz != tz)
503 			obj->move(tx, ty, tz);
504 	}
505 	cout << "Object updated" << endl;
506 #else
507 	ignore_unused_variable_warning(data, datalen);
508 #endif
509 }
510 
511 /*
512  *  Get (total) weight.
513  */
514 
get_weight()515 int Container_game_object::get_weight(
516 ) {
517 	int wt = Ireg_game_object::get_weight();
518 	const Shape_info &info = get_info();
519 	if (info.has_extradimensional_storage()) {
520 		return wt;
521 	}
522 	Game_object *obj;
523 	Object_iterator next(objects);
524 	while ((obj = next.get_next()) != nullptr)
525 		wt += obj->get_weight();
526 	return wt;
527 }
528 
529 /*
530  *  Drop another onto this.
531  *
532  *  Output: false to reject, true to accept.
533  */
534 
drop(Game_object * obj)535 bool Container_game_object::drop(
536     Game_object *obj        // May be deleted if combined.
537 ) {
538 	if (!get_owner())       // Only accept if inside another.
539 		return false;
540 	return add(obj, false, true); // We'll take it, and try to combine.
541 }
542 
543 /*
544  *  Recursively count all objects of a given shape.
545  */
546 
count_objects(int shapenum,int qual,int framenum)547 int Container_game_object::count_objects(
548     int shapenum,           // Shape#, or c_any_shapenum for any.
549     int qual,           // Quality, or c_any_qual for any.
550     int framenum            // Frame#, or c_any_framenum for any.
551 ) {
552 	if (!Can_be_added(this, shapenum, true))
553 		return 0;
554 	int total = 0;
555 	Game_object *obj;
556 	Object_iterator next(objects);
557 	while ((obj = next.get_next()) != nullptr) {
558 		if ((shapenum == c_any_shapenum || obj->get_shapenum() == shapenum) &&
559 		        // Watch for reflection.
560 		        (framenum == c_any_framenum || (obj->get_framenum() & 31) == framenum) &&
561 		        (qual == c_any_qual || obj->get_quality() == qual)) {
562 			// Check quantity.
563 			int quant = obj->get_quantity();
564 			total += quant;
565 		}
566 		// Count recursively.
567 		total += obj->count_objects(shapenum, qual, framenum);
568 	}
569 	return total;
570 }
571 
572 /*
573  *  Recursively get all objects of a given shape.
574  */
575 
get_objects(Game_object_vector & vec,int shapenum,int qual,int framenum)576 int Container_game_object::get_objects(
577     Game_object_vector &vec,    // Objects returned here.
578     int shapenum,           // Shape#, or c_any_shapenum for any.
579     int qual,           // Quality, or c_any_qual for any.
580     int framenum            // Frame#, or c_any_framenum for any.
581 ) {
582 	int vecsize = vec.size();
583 	Game_object *obj;
584 	Object_iterator next(objects);
585 	while ((obj = next.get_next()) != nullptr) {
586 		if ((shapenum == c_any_shapenum || obj->get_shapenum() == shapenum) &&
587 		        (qual == c_any_qual || obj->get_quality() == qual) &&
588 		        // Watch for reflection.
589 		        (framenum == c_any_framenum || (obj->get_framenum() & 31) == framenum))
590 			vec.push_back(obj);
591 		// Search recursively.
592 		obj->get_objects(vec, shapenum, qual, framenum);
593 	}
594 	return vec.size() - vecsize;
595 }
596 
597 
598 /*
599  *  Set a flag on this and all contents.
600  */
601 
set_flag_recursively(int flag)602 void Container_game_object::set_flag_recursively(
603     int flag
604 ) {
605 	set_flag(flag);
606 	Game_object *obj;
607 	Object_iterator next(objects);
608 	while ((obj = next.get_next()) != nullptr)
609 		obj->set_flag_recursively(flag);
610 }
611 
612 /*
613  *  Write out container and its members.
614  */
615 
write_ireg(ODataSource * out)616 void Container_game_object::write_ireg(
617     ODataSource *out
618 ) {
619 	unsigned char buf[20];      // 12-byte entry.
620 	uint8 *ptr = write_common_ireg(12, buf);
621 	Game_object *first = objects.get_first(); // Guessing: +++++
622 	unsigned short tword = first ? first->get_prev()->get_shapenum()
623 	                       : 0;
624 	Write2(ptr, tword);
625 	*ptr++ = 0;         // Unknown.
626 	*ptr++ = get_quality();
627 	*ptr++ = 0;     // "Quantity".
628 	*ptr++ = nibble_swap(get_lift()); // Lift
629 	*ptr++ = static_cast<unsigned char>(resistance);        // Resistance.
630 	// Flags:  B0=invis. B3=okay_to_take.
631 	*ptr++ = (get_flag(Obj_flags::invisible) ? 1 : 0) +
632 	         (get_flag(Obj_flags::okay_to_take) ? (1 << 3) : 0);
633 	out->write(reinterpret_cast<char *>(buf), ptr - buf);
634 	write_contents(out);        // Write what's contained within.
635 	// Write scheduled usecode.
636 	Game_map::write_scheduled(out, this);
637 }
638 
639 // Get size of IREG. Returns -1 if can't write to buffer
get_ireg_size()640 int Container_game_object::get_ireg_size() {
641 	// These shouldn't ever happen, but you never know
642 	if (gumpman->find_gump(this) || Usecode_script::find(this))
643 		return -1;
644 
645 	int total_size = 8 + get_common_ireg_size();
646 
647 	// Now what's inside.
648 	if (!objects.is_empty()) {
649 		Game_object *obj;
650 		Object_iterator next(objects);
651 		while ((obj = next.get_next()) != nullptr) {
652 			int size = obj->get_ireg_size();
653 
654 			if (size < 0) return -1;
655 
656 			total_size += size;
657 		}
658 		total_size += 1;
659 	}
660 
661 	return total_size;
662 }
663 
664 /*
665  *  Write contents (if there is any).
666  */
667 
write_contents(ODataSource * out)668 void Container_game_object::write_contents(
669     ODataSource *out
670 ) {
671 	if (!objects.is_empty()) {  // Now write out what's inside.
672 		Game_object *obj;
673 		Object_iterator next(objects);
674 		while ((obj = next.get_next()) != nullptr)
675 			obj->write_ireg(out);
676 		out->write1(0x01);      // A 01 terminates the list.
677 	}
678 }
679 
680 
extract_contents(Container_game_object * targ)681 bool Container_game_object::extract_contents(Container_game_object *targ) {
682 	if (objects.is_empty())
683 		return true;
684 
685 	bool status = true;
686 
687 	Game_object *obj;
688 
689 	while ((obj = objects.get_first())) {
690 	    Game_object_shared keep = obj->shared_from_this();
691 		remove(obj);
692 
693 		if (targ) {
694 			targ->add(obj, true); // add without checking volume
695 		} else {
696 			obj->set_invalid(); // set to invalid chunk so move() doesn't fail
697 			if ((get_cx() == 255) && (get_cy() == 255)) {
698 				obj->remove_this(nullptr);
699 				status = false;
700 			} else {
701 				obj->move(get_tile());
702 			}
703 		}
704 	}
705 
706 	return status;
707 }
708 
delete_contents()709 void Container_game_object::delete_contents() {
710 	if (objects.is_empty())
711 		return;
712 
713 	Game_object *obj;
714 	while ((obj = objects.get_first())) {
715 	    Game_object_shared keep = obj->shared_from_this();
716 	    remove(obj);
717 		obj->delete_contents(); // recurse into contained containers
718 		obj->remove_this(nullptr);
719 	}
720 }
721 
remove_this(Game_object_shared * keep)722 void Container_game_object::remove_this(
723     Game_object_shared *keep     // Non-null to not delete.
724 	) {
725 	// Needs to be saved, as it is invalidated below but needed
726 	// shortly after.
727 	Game_object_shared tmp_keep;
728 	Container_game_object *safe_owner = Container_game_object::get_owner();
729 	// Special case to avoid recursion.
730 	if (safe_owner) {
731 		// First remove from owner.
732 		Ireg_game_object::remove_this(&tmp_keep);
733 		if (keep) {      // Not deleting?  Then done.
734 		    *keep = std::move(tmp_keep);
735 			return;
736 		}
737 	}
738 	if (!keep)
739 		extract_contents(safe_owner);
740 
741 	Ireg_game_object::remove_this(keep);
742 }
743 
744 /*
745  *  Find ammo used by weapon.
746  *
747  *  Output: ->object if found. Additionally, is_readied is set to
748  *  true if the ammo is readied.
749  */
750 
find_weapon_ammo(int weapon,int needed,bool recursive)751 Game_object *Container_game_object::find_weapon_ammo(
752     int weapon,         // Weapon shape.
753     int needed,
754     bool recursive
755 ) {
756 	ignore_unused_variable_warning(recursive);
757 	if (weapon < 0 || !Can_be_added(this, weapon))
758 		return nullptr;
759 	const Weapon_info *winf = ShapeID::get_info(weapon).get_weapon_info();
760 	if (!winf)
761 		return nullptr;
762 	int family = winf->get_ammo_consumed();
763 	if (family >= 0)
764 		return nullptr;
765 
766 	Game_object_vector vec;     // Get list of all possessions.
767 	vec.reserve(50);
768 	get_objects(vec, c_any_shapenum, c_any_qual, c_any_framenum);
769 	for (auto *obj : vec) {
770 		if (obj->get_shapenum() != weapon)
771 			continue;
772 		const Shape_info &inf = obj->get_info();
773 		if (family == -2) {
774 			if (!inf.has_quality() || obj->get_quality() >= needed)
775 				return obj;
776 		}
777 		// Family -1 and family -3.
778 		else if (obj->get_quantity() >= needed)
779 			return obj;
780 	}
781 	return nullptr;
782 }
783