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