ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.100
Committed: Mon Oct 19 21:48:49 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_82
Changes since 1.99: +8 -9 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Deliantra is free software: you can redistribute it and/or modify it under
9 * the terms of the Affero GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the Affero GNU General Public License
19 * and the GNU General Public License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 *
22 * The authors can be reached via e-mail to <support@deliantra.net>
23 */
24
25 /*
26 * Object (handling) commands
27 */
28
29 #include <global.h>
30 #include <loader.h>
31 #include <skills.h>
32 #include <sproto.h>
33 #include <living.h>
34 #include <math.h>
35
36 /*
37 * Object id parsing functions
38 */
39
40 #define ADD_ITEM(NEW,COUNT)\
41 if(!first) {\
42 first = new objectlink;\
43 last=first;\
44 } else {\
45 last->next = new objectlink;\
46 last=last->next;\
47 }\
48 last->next=0;\
49 last->ob=(NEW);\
50 last->id=(COUNT);
51
52 /**
53 * Search the inventory of 'pl' for what matches best with params.
54 * we use item_matched_string above - this gives us consistent behaviour
55 * between many commands. Return the best match, or NULL if no match.
56 * aflag is used with apply -u , and apply -a to
57 * only unapply applied, or apply unapplied objects
58 **/
59 static object *
60 find_best_apply_object_match (object *pl, const char *params, enum apply_flag aflag)
61 {
62 object *best = 0;
63 int match_val = 0;
64
65 for (object *tmp = pl->inv; tmp; tmp = tmp->below)
66 {
67 if (tmp->invisible)
68 continue;
69
70 int tmpmatch = item_matched_string (pl, tmp, params);
71
72 if (tmpmatch > match_val)
73 {
74 if ((aflag == AP_APPLY) && (QUERY_FLAG (tmp, FLAG_APPLIED)))
75 continue;
76
77 if ((aflag == AP_UNAPPLY) && (!QUERY_FLAG (tmp, FLAG_APPLIED)))
78 continue;
79
80 match_val = tmpmatch;
81 best = tmp;
82 }
83 }
84
85 return best;
86 }
87
88 /**
89 * Shortcut to find_best_apply_object_match(pl, params, AF_NULL);
90 **/
91 object *
92 find_best_object_match (object *pl, const char *params)
93 {
94 return find_best_apply_object_match (pl, params, AP_TOGGLE);
95 }
96
97 static bool
98 can_split (object *pl, object *&op, sint32 nrof)
99 {
100 if (object *tmp = op->split (nrof ? nrof : op->number_of ()))
101 {
102 op = tmp;
103 return true;
104 }
105 else
106 {
107 if (op->nrof > 1)
108 new_draw_info_format (NDI_UNIQUE, 0, pl, "There are only %d %s.", op->nrof, &op->name_pl);
109 else
110 new_draw_info_format (NDI_UNIQUE, 0, pl, "There is only one %s.", &op->name);
111
112 return false;
113 }
114 }
115
116 int
117 command_uskill (object *pl, char *params)
118 {
119 if (!params)
120 {
121 new_draw_info (NDI_UNIQUE, 0, pl, "Usage: use_skill <skill name>");
122 return 0;
123 }
124
125 return use_skill (pl, params);
126 }
127
128 int
129 command_rskill (object *pl, char *params)
130 {
131 object *skill;
132
133 if (!params)
134 {
135 new_draw_info (NDI_UNIQUE, 0, pl, "Usage: ready_skill <skill name>");
136 return 0;
137 }
138
139 skill = find_skill_by_name_fuzzy (pl, params);
140
141 if (!skill)
142 {
143 new_draw_info_format (NDI_UNIQUE, 0, pl, "You have no knowledge of the skill %s", params);
144 return 0;
145 }
146
147 pl->change_skill (0);
148 apply_special (pl, skill, AP_APPLY);
149 return 1;
150 }
151
152 /* A little special because we do want to pass the full params along
153 * as it includes the object to throw.
154 */
155 int
156 command_throw (object *op, char *params)
157 {
158 if (object *skop = find_skill_by_name (op, skill_names[SK_THROWING]))
159 return do_skill (op, op, skop, op->facing, params);
160 else
161 new_draw_info (NDI_UNIQUE, 0, op, "You have no knowledge of the skill throwing.");
162
163 return 0;
164 }
165
166 int
167 command_apply (object *op, char *params)
168 {
169 if (!params)
170 {
171 player_apply_below (op);
172 return 0;
173 }
174 else
175 {
176 apply_flag aflag = (apply_flag)0;
177
178 while (*params == ' ')
179 params++;
180
181 if (!strncmp (params, "-a ", 3))
182 {
183 aflag = AP_APPLY;
184 params += 3;
185 }
186
187 if (!strncmp (params, "-u ", 3))
188 {
189 aflag = AP_UNAPPLY;
190 params += 3;
191 }
192
193 while (*params == ' ')
194 params++;
195
196 if (object *inv = find_best_apply_object_match (op, params, aflag))
197 player_apply (op, inv, aflag, 0);
198 else
199 new_draw_info_format (NDI_UNIQUE, 0, op, "Could not find any match to the %s.", params);
200 }
201
202 return 0;
203 }
204
205 /*
206 * Check if an item op can be put into a sack. If pl exists then tell
207 * a player the reason of failure.
208 * returns 1 if it will fit, 0 if it will not. nrof is the number of
209 * objects (op) we want to put in. We specify it separately instead of
210 * using op->nrof because often times, a player may have specified a
211 * certain number of objects to drop, so we can pass that number, and
212 * not need to use split_ob and stuff.
213 */
214 int
215 sack_can_hold (object *pl, object *sack, object *op, uint32 nrof)
216 {
217 if (!QUERY_FLAG (sack, FLAG_APPLIED))
218 {
219 new_draw_info_format (NDI_UNIQUE, 0, pl, "The %s is not active.", query_name (sack));
220 return 0;
221 }
222
223 if (sack == op)
224 {
225 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the %s into itself.", query_name (sack));
226 return 0;
227 }
228
229 if (sack->race && (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type)))
230 {
231 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can put only %s into the %s.", &sack->race, query_name (sack));
232 return 0;
233 }
234
235 if (op->type == SPECIAL_KEY && sack->slaying && op->slaying)
236 {
237 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the key into %s.", query_name (sack));
238 return 0;
239 }
240
241 if (sack->weight_limit && (sint32) (sack->carrying + (nrof ? nrof : 1) *
242 (op->weight + (op->type == CONTAINER ? (op->carrying * op->stats.Str) : 0))
243 * (100 - sack->stats.Str) / 100) > sack->weight_limit)
244 {
245 new_draw_info_format (NDI_UNIQUE, 0, pl, "That won't fit in the %s!", query_name (sack));
246 return 0;
247 }
248
249 /* All other checks pass, must be OK */
250 return 1;
251 }
252
253 /* Pick up commands follow */
254
255 /* pl = player (not always - monsters can use this now)
256 * op is the object to put tmp into,
257 * tmp is the object to pick up, nrof is the number to
258 * pick up (0 means all of them)
259 */
260 static void
261 pick_up_object (object *pl, object *op, object *tmp, int nrof)
262 {
263 object *env = tmp->env;
264 uint32 weight, effective_weight_limit;
265 int tmp_nrof = tmp->number_of ();
266
267 /* IF the player is flying & trying to take the item out of a container
268 * that is in his inventory, let him. tmp->env points to the container
269 * (sack, luggage, etc), tmp->env->env then points to the player (nested
270 * containers not allowed as of now)
271 */
272 if ((pl->move_type & MOVE_FLYING) && !QUERY_FLAG (pl, FLAG_WIZ) && tmp->in_player () != pl)
273 {
274 pl->failmsg ("You are levitating, you can't reach the ground! "
275 "H<You have to stop levitating first, if you can, either by using your levitation skill, "
276 "or waiting till the levitation effect wears off.>");
277 return;
278 }
279
280 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
281 return;
282
283 if (nrof > tmp_nrof || nrof <= 0)
284 nrof = tmp_nrof;
285
286 /* Figure out how much weight this object will add to the player */
287 weight = tmp->weight * nrof;
288 if (tmp->inv)
289 weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
290
291 effective_weight_limit = weight_limit [min (MAX_STAT, pl->stats.Str)];
292
293 if ((pl->weight + pl->carrying + weight) > effective_weight_limit)
294 {
295 new_draw_info (0, 0, pl, "That item is too heavy for you to pick up.");
296 return;
297 }
298
299 if (!can_split (pl, tmp, nrof))
300 return;
301
302 if (QUERY_FLAG (tmp, FLAG_UNPAID))
303 {
304 tmp->flag.reset (FLAG_UNPAID);
305 new_draw_info_format (NDI_UNIQUE, 0, pl, "%s will cost you %s.", query_name (tmp), query_cost_string (tmp, pl, F_BUY | F_SHOP));
306 tmp->flag.set (FLAG_UNPAID);
307 }
308 else
309 new_draw_info_format (NDI_UNIQUE, 0, pl, "You pick up the %s.", query_name (tmp));
310
311 op->insert (tmp);
312 }
313
314 /* modified slightly to allow monsters use this -b.t. 5-31-95 */
315 void
316 pick_up (object *op, object *alt)
317 {
318 int need_fix_tmp = 0;
319 object *tmp = NULL;
320 maptile *tmp_map = NULL;
321 int count;
322
323 /* Decide which object to pick. */
324 if (alt)
325 {
326 if (!can_pick (op, alt))
327 {
328 new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up the %s.", &alt->name);
329 goto leave;
330 }
331
332 tmp = alt;
333 }
334 else
335 {
336 if (op->below == NULL || !can_pick (op, op->below))
337 {
338 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up here.");
339 goto leave;
340 }
341
342 tmp = op->below;
343 }
344
345 /* Try to catch it. */
346 tmp_map = tmp->map;
347 tmp = stop_item (tmp);
348 if (tmp == NULL)
349 goto leave;
350
351 need_fix_tmp = 1;
352 if (!can_pick (op, tmp))
353 goto leave;
354
355 if (op->type == PLAYER)
356 {
357 count = op->contr->count;
358 if (count == 0)
359 count = tmp->nrof;
360 }
361 else
362 count = tmp->nrof;
363
364 /* container is open, so use it */
365 if (tmp->flag [FLAG_STARTEQUIP])
366 alt = op;
367 else if ((alt = op->container_ ()))
368 {
369 if (alt != tmp->env && !sack_can_hold (op, alt, tmp, count))
370 goto leave;
371 }
372 else
373 { /* non container pickup */
374 for (alt = op->inv; alt; alt = alt->below)
375 if (alt->type == CONTAINER && QUERY_FLAG (alt, FLAG_APPLIED) &&
376 alt->race && alt->race == tmp->race && sack_can_hold (NULL, alt, tmp, count))
377 break; /* perfect match */
378
379 if (!alt)
380 for (alt = op->inv; alt; alt = alt->below)
381 if (alt->type == CONTAINER && QUERY_FLAG (alt, FLAG_APPLIED) && sack_can_hold (NULL, alt, tmp, count))
382 break; /* General container comes next */
383
384 if (!alt)
385 alt = op; /* No free containers */
386 }
387
388 if (tmp->env == alt)
389 {
390 /* here it could be possible to check rent,
391 * if someone wants to implement it
392 */
393 alt = op;
394 }
395
396 #ifdef PICKUP_DEBUG
397 LOG (llevDebug, "Pick_up(): %s picks %s (%d) and inserts it %s.\n", op->name, tmp->name, op->contr->count, alt->name);
398 #endif
399
400 /* startequip items are not allowed to be put into containers: */
401 if (op->type == PLAYER && alt->type == CONTAINER && QUERY_FLAG (tmp, FLAG_STARTEQUIP))
402 {
403 new_draw_info (NDI_UNIQUE, 0, op, "This object cannot be put into containers!");
404 goto leave;
405 }
406
407 pick_up_object (op, alt, tmp, count);
408
409 if (tmp->destroyed () || tmp->env)
410 need_fix_tmp = 0;
411
412 if (op->type == PLAYER)
413 op->contr->count = 0;
414
415 goto leave;
416
417 leave:
418 if (need_fix_tmp)
419 fix_stopped_item (tmp, tmp_map, op);
420 }
421
422 /* This takes (picks up) and item. op is the player
423 * who issued the command. params is a string to
424 * match against the item name. Basically, always
425 * returns zero, but that should be improved.
426 */
427 int
428 command_take (object *op, char *params)
429 {
430 object *tmp, *next;
431
432 if (op->container_ ())
433 tmp = op->container_ ()->inv;
434 else
435 {
436 tmp = op->above;
437 if (tmp)
438 while (tmp->above)
439 tmp = tmp->above;
440
441 if (!tmp)
442 tmp = op->below;
443 }
444
445 if (!tmp)
446 {
447 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to take!");
448 return 0;
449 }
450
451 /* Makes processing easier */
452 if (params && *params == '\0')
453 params = 0;
454
455 int cnt = MAX_ITEM_PER_DROP;
456
457 while (tmp)
458 {
459 next = tmp->below;
460
461 if (tmp->invisible)
462 {
463 tmp = next;
464 continue;
465 }
466
467 /* This following two if and else if could be merged into line
468 * but that probably will make it more difficult to read, and
469 * not make it any more efficient
470 */
471 if (params && item_matched_string (op, tmp, params))
472 {
473 if (--cnt < 0) break;
474 pick_up (op, tmp);
475 }
476 else if (can_pick (op, tmp) && !params)
477 {
478 if (--cnt < 0) break;
479 pick_up (op, tmp);
480 break;
481 }
482
483 tmp = next;
484 }
485
486 if (cnt < 0)
487 {
488 op->failmsg ("Couldn't pick up so many items at once.");
489 return 0;
490 }
491
492 if (!params && !tmp)
493 {
494 for (tmp = op->below; tmp; tmp = tmp->below)
495 if (!tmp->invisible)
496 {
497 new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up a %s.", &tmp->name);
498 break;
499 }
500
501 if (!tmp)
502 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up.");
503 }
504
505 return 0;
506 }
507
508 /*
509 * This function was part of drop, now is own function.
510 * Player 'op' tries to put object 'tmp' into sack 'sack',
511 * if nrof is non zero, then nrof objects is tried to put into sack.
512 *
513 * Note that the 'sack' in question can now be a transport,
514 * so this function isn't named very good anymore.
515 */
516 void
517 put_object_in_sack (object *op, object *sack, object *tmp, uint32 nrof)
518 {
519 object *tmp2, *sack2;
520 char buf[MAX_BUF];
521
522 if (sack == tmp)
523 return; /* Can't put an object in itself */
524
525 if (QUERY_FLAG (tmp, FLAG_STARTEQUIP) || QUERY_FLAG (tmp, FLAG_NO_DROP))
526 {
527 new_draw_info_format (NDI_UNIQUE, 0, op, "You cannot put the %s in the %s.", query_name (tmp), query_name (sack));
528 return;
529 }
530
531 if (tmp->type == CONTAINER && tmp->inv)
532 {
533 /* Eneq(@csd.uu.se): If the object to be dropped is a container
534 * we instead move the contents of that container into the active
535 * container, this is only done if the object has something in it.
536 */
537 sack2 = tmp;
538 new_draw_info_format (NDI_UNIQUE, 0, op, "You move the items from %s into %s.", query_name (tmp), query_name (sack));
539
540 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp)
541 {
542 tmp = tmp2->below;
543
544 if ((sack->type == CONTAINER && sack_can_hold (op, op->container_ (), tmp2, tmp2->nrof)))
545 put_object_in_sack (op, sack, tmp2, 0);
546 else
547 {
548 sprintf (buf, "Your %s fills up.", query_name (sack));
549 new_draw_info (NDI_UNIQUE, 0, op, buf);
550 break;
551 }
552 }
553
554 return;
555 }
556
557 /* Don't worry about this for containers - our caller should have
558 * already checked this.
559 */
560 if ((sack->type == CONTAINER) && !sack_can_hold (op, sack, tmp, (nrof ? nrof : tmp->nrof)))
561 return;
562
563 if (QUERY_FLAG (tmp, FLAG_APPLIED))
564 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
565 return;
566
567 /* we want to put some portion of the item into the container */
568 if (!can_split (op, tmp, nrof))
569 return;
570
571 new_draw_info_format (NDI_UNIQUE, 0, op, "You put the %s in %s.", query_name (tmp), query_name (sack));
572 sack->insert (tmp);
573 }
574
575 /*
576 * This function was part of drop, now is own function.
577 * Player 'op' tries to drop object 'tmp', if tmp is non zero, then
578 * nrof objects is tried to dropped.
579 * This is used when dropping objects onto the floor.
580 */
581 void
582 drop_object (object *op, object *tmp, uint32 nrof)
583 {
584 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
585 return;
586
587 if (QUERY_FLAG (tmp, FLAG_APPLIED))
588 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
589 return; /* can't unapply it */
590
591 /* We are only dropping some of the items. We split the current object
592 * off
593 */
594 if (!can_split (op, tmp, nrof))
595 return;
596
597 drop_object (op, tmp);
598
599 if (!tmp->destroyed () && !tmp->is_inserted ())
600 {
601 // if nothing happened with the object we give it back
602 op->insert (tmp);
603 }
604 }
605
606 /* In contrast to drop_object (op, tmp, nrof) above this function takes the
607 * already split off object, and feeds it to the event handlers and does
608 * other magic with it.
609 *
610 * <droppper> is the object that dropped this object and <obj> is the
611 * object that was dropped.
612 *
613 * Make sure to check what happened with <obj> after this function returns!
614 * Otherwise you may leak this object.
615 */
616 void
617 drop_object (object *dropper, object *obj)
618 {
619 if (INVOKE_OBJECT (DROP, obj, ARG_OBJECT (dropper)))
620 return;
621
622 if (obj->destroyed () || obj->is_inserted ())
623 return;
624
625 if (QUERY_FLAG (obj, FLAG_STARTEQUIP))
626 {
627 dropper->statusmsg (format ("You drop the %s.", query_name (obj)));
628 dropper->statusmsg ("The god who lent it to you retrieves it.");
629
630 obj->destroy ();
631 dropper->update_stats ();
632 return;
633 }
634
635 for (object *floor = GET_MAP_OB (dropper->map, dropper->x, dropper->y);
636 floor;
637 floor = floor->above)
638 if (INVOKE_OBJECT (DROP_ON, floor, ARG_OBJECT (obj), ARG_OBJECT (dropper)))
639 return;
640
641 if (obj->destroyed () || obj->is_inserted ())
642 return;
643
644 if (is_in_shop (dropper) && !QUERY_FLAG (obj, FLAG_UNPAID) && obj->type != MONEY)
645 if (!sell_item (obj, dropper))
646 return;
647
648 /* If nothing special happened with this object, the default action is to
649 * insert it below the dropper:
650 */
651
652 obj->x = dropper->x;
653 obj->y = dropper->y;
654
655 insert_ob_in_map (obj, dropper->map, dropper, INS_BELOW_ORIGINATOR);
656 }
657
658 void
659 drop (object *op, object *tmp)
660 {
661 /* Hopeful fix for disappearing objects when dropping from a container -
662 * somehow, players get an invisible object in the container, and the
663 * old logic would skip over invisible objects - works fine for the
664 * playes inventory, but drop inventory wants to use the next value.
665 */
666 if (tmp->invisible)
667 {
668 /* if the following is the case, it must be in an container. */
669 if (tmp->env && tmp->env->type != PLAYER)
670 {
671 /* Just toss the object - probably shouldn't be hanging
672 * around anyways
673 */
674 tmp->destroy ();
675 return;
676 }
677 else
678 {
679 while (tmp && tmp->invisible)
680 tmp = tmp->below;
681 }
682 }
683
684 if (tmp == NULL)
685 {
686 new_draw_info (NDI_UNIQUE, 0, op, "You don't have anything to drop.");
687 return;
688 }
689
690 if (QUERY_FLAG (tmp, FLAG_INV_LOCKED))
691 {
692 new_draw_info (NDI_UNIQUE, 0, op, "This item is locked");
693 return;
694 }
695
696 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
697 {
698 #if 0
699 /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
700 new_draw_info (NDI_UNIQUE, 0, op, "This item can't be dropped.");
701 #endif
702 return;
703 }
704
705 if (op->type == PLAYER && op->contr->last_used == tmp)
706 op->contr->last_used = tmp->below ? tmp->below
707 : tmp->above ? tmp->above
708 : (object *)0;
709
710 if (op->container_ ())
711 {
712 if (op->type == PLAYER)
713 put_object_in_sack (op, op->container_ (), tmp, op->contr->count);
714 else
715 put_object_in_sack (op, op->container_ (), tmp, 0);
716 }
717 else
718 {
719 if (op->type == PLAYER)
720 drop_object (op, tmp, op->contr->count);
721 else
722 drop_object (op, tmp, 0);
723 }
724
725 if (op->type == PLAYER)
726 op->contr->count = 0;
727 }
728
729 /* Command will drop all items that have not been locked */
730 int
731 command_dropall (object *op, char *params)
732 {
733
734 if (op->inv == NULL)
735 {
736 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop!");
737 return 0;
738 }
739
740 object *curinv = op->inv;
741 object *nextinv;
742
743 /*
744 This is the default. Drops everything not locked or considered
745 not something that should be dropped.
746 */
747 /*
748 Care must be taken that the next item pointer is not to money as
749 the drop() routine will do unknown things to it when dropping
750 in a shop. --Tero.Pelander@utu.fi
751 */
752
753 int cnt = MAX_ITEM_PER_DROP;
754
755 if (!params)
756 {
757 while (curinv)
758 {
759 nextinv = curinv->below;
760
761 while (nextinv && nextinv->type == MONEY)
762 nextinv = nextinv->below;
763
764 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && curinv->type != MONEY &&
765 curinv->type != FOOD && curinv->type != KEY &&
766 curinv->type != SPECIAL_KEY && curinv->type != GEM &&
767 !curinv->invisible && (curinv->type != CONTAINER || op->container_ () != curinv))
768 {
769 drop (op, curinv);
770 if (--cnt <= 0) break;
771 }
772
773 curinv = nextinv;
774 }
775 }
776 else if (strcmp (params, "weapons") == 0)
777 {
778 while (curinv)
779 {
780 nextinv = curinv->below;
781
782 while (nextinv && nextinv->type == MONEY)
783 nextinv = nextinv->below;
784
785 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && ((curinv->type == WEAPON) || (curinv->type == BOW) || (curinv->type == ARROW)))
786 {
787 drop (op, curinv);
788 if (--cnt <= 0) break;
789 }
790
791 curinv = nextinv;
792 }
793 }
794 else if (strcmp (params, "armor") == 0 || strcmp (params, "armour") == 0)
795 {
796 while (curinv)
797 {
798 nextinv = curinv->below;
799
800 while (nextinv && nextinv->type == MONEY)
801 nextinv = nextinv->below;
802
803 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && ((curinv->type == ARMOUR) || curinv->type == SHIELD || curinv->type == HELMET))
804 {
805 drop (op, curinv);
806 if (--cnt <= 0) break;
807 }
808
809 curinv = nextinv;
810 }
811 }
812 else if (strcmp (params, "misc") == 0)
813 {
814 while (curinv)
815 {
816 nextinv = curinv->below;
817
818 while (nextinv && nextinv->type == MONEY)
819 nextinv = nextinv->below;
820
821 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && !QUERY_FLAG (curinv, FLAG_APPLIED))
822 {
823 switch (curinv->type)
824 {
825 case HORN:
826 case BOOK:
827 case SPELLBOOK:
828 case GIRDLE:
829 case AMULET:
830 case RING:
831 case CLOAK:
832 case BOOTS:
833 case GLOVES:
834 case BRACERS:
835 case SCROLL:
836 case ARMOUR_IMPROVER:
837 case WEAPON_IMPROVER:
838 case WAND:
839 case ROD:
840 case POTION:
841 drop (op, curinv);
842 curinv = nextinv;
843 break;
844 default:
845 curinv = nextinv;
846 break;
847 }
848
849 if (--cnt <= 0) break;
850 }
851
852 curinv = nextinv;
853 }
854 }
855
856 if (cnt <= 0)
857 op->failmsg ("Only dropped some items, can't drop that many items at once.");
858
859 /* draw_look(op);*/
860 return 0;
861 }
862
863
864 /* This function tries to drop all objects in the <objs> vector.
865 * <dropper> is the object that wants to drop them.
866 * <cnt> can be a 0 pointer or a pointer to the maximum number of
867 * drop operations to perform.
868 *
869 * Returns true if at least one item was dropped.
870 */
871 bool
872 drop_vector (object *dropper, vector<object *> &objs, int *cnt)
873 {
874 vector<object *>::iterator i;
875
876 bool did_one = false;
877
878 for (i = objs.begin (); i != objs.end (); i++)
879 {
880 drop (dropper, *i);
881 if (cnt && --*cnt <= 0) break;
882 did_one = true;
883 }
884
885 return did_one;
886 }
887
888 /* Object op wants to drop object(s) params. params can be a
889 * comma seperated list.
890 */
891 int
892 command_drop (object *op, char *params)
893 {
894 object *tmp, *next;
895 int did_one = 0;
896
897 if (!params)
898 {
899 new_draw_info (NDI_UNIQUE, 0, op, "Drop what?");
900 return 0;
901 }
902 else
903 {
904 vector<object *> matched_objs;
905
906 for (tmp = op->inv; tmp; tmp = next)
907 {
908 next = tmp->below;
909 if (QUERY_FLAG (tmp, FLAG_NO_DROP) || tmp->invisible)
910 continue;
911
912 if (item_matched_string (op, tmp, params))
913 matched_objs.push_back (tmp);
914 }
915
916 int cnt = MAX_ITEM_PER_DROP;
917
918 if (!drop_vector (op, matched_objs, &cnt))
919 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop.");
920
921 if (cnt <= 0)
922 op->failmsg ("Only dropped some items, can't drop that many items at once.");
923 }
924
925 return 0;
926 }
927
928 int
929 command_examine (object *op, char *params)
930 {
931 if (!params)
932 {
933 object *tmp = op->below;
934
935 while (tmp && !tmp->client_visible ())
936 tmp = tmp->below;
937
938 if (tmp)
939 examine (op, tmp);
940 }
941 else
942 {
943 object *tmp = find_best_object_match (op, params);
944
945 if (tmp)
946 examine (op, tmp);
947 else
948 op->contr->infobox (MSG_CHANNEL ("examine"), format ("Could not find an object that matches %s", params));
949 }
950
951 return 0;
952 }
953
954 /* op should be a player.
955 * we return the object the player has marked with the 'mark' command
956 * below. If no match is found (or object has changed), we return
957 * NULL. We leave it up to the calling function to print messages if
958 * nothing is found.
959 */
960 object *
961 find_marked_object (object *op)
962 {
963 object *tmp;
964
965 if (!op || !op->contr)
966 return NULL;
967
968 if (!op->contr->mark)
969 {
970 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
971 return 0;
972 }
973
974 /* This may seem like overkill, but we need to make sure that they
975 * player hasn't dropped the item. We use count on the off chance that
976 * an item got reincarnated at some point.
977 */
978 for (tmp = op->inv; tmp; tmp = tmp->below)
979 {
980 if (tmp->invisible)
981 continue;
982
983 if (tmp == op->contr->mark)
984 {
985 if (!tmp->destroyed ())
986 return tmp;
987 else
988 {
989 op->contr->mark = 0;
990 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
991 return 0;
992 }
993 }
994 }
995
996 return 0;
997 }
998
999 std::string
1000 object::describe_monster (object *who)
1001 {
1002 dynbuf_text buf (512, 512);
1003
1004 object *mon = head ? head : this;
1005
1006 if (QUERY_FLAG (mon, FLAG_UNDEAD))
1007 buf << "It is an undead force.\r";
1008
1009 if (mon->level > who->level)
1010 buf << "It is likely more powerful than you.\r";
1011 else if (mon->level < who->level)
1012 buf << "It is likely less powerful than you.\r";
1013 else
1014 buf << "It is probably as powerful as you.\r";
1015
1016 if (mon->attacktype & AT_ACID)
1017 buf << "You seem to smell an acrid odor.\r";
1018
1019 /* Anyone know why this used to use the clone value instead of the
1020 * maxhp field? This seems that it should give more accurate results.
1021 */
1022 switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
1023 { /* From 1-4 */
1024 case 1:
1025 buf << "It is in a bad shape.\r";
1026 break;
1027 case 2:
1028 buf << "It is hurt.\r";
1029 break;
1030 case 3:
1031 buf << "It is somewhat hurt.\r";
1032 break;
1033 case 4:
1034 buf << "It is in excellent shape.\r";
1035 break;
1036 }
1037
1038 if (present_in_ob (POISONING, mon))
1039 buf << "It looks very ill.\r";
1040
1041 buf << '\n';
1042
1043 return buf;
1044 }
1045
1046 /* tmp is the object being described, pl is who is examing it. */
1047 const char *
1048 long_desc (object *tmp, object *pl)
1049 {
1050 static std::string s;
1051
1052 return (s = tmp->long_desc (pl)).c_str ();
1053 }
1054
1055 std::string
1056 object::long_desc (object *who)
1057 {
1058 std::string buf (query_name ());
1059
1060 switch (type)
1061 {
1062 case RING:
1063 case SKILL:
1064 case WEAPON:
1065 case ARMOUR:
1066 case BRACERS:
1067 case HELMET:
1068 case SHIELD:
1069 case BOOTS:
1070 case GLOVES:
1071 case AMULET:
1072 case GIRDLE:
1073 case BOW:
1074 case ARROW:
1075 case CLOAK:
1076 case FOOD:
1077 case DRINK:
1078 case FLESH:
1079 case SKILL_TOOL:
1080 case LAMP:
1081 case POWER_CRYSTAL:
1082 {
1083 const char *cp = ::describe_item (this, who);
1084
1085 if (*cp)
1086 {
1087 buf.append (" ");
1088 buf.append (cp);
1089 }
1090 }
1091 }
1092
1093 return buf;
1094 }
1095
1096 /* op is the player
1097 * tmp is the monster being examined.
1098 */
1099 void
1100 examine_monster (object *op, object *tmp)
1101 {
1102 new_draw_info (NDI_UNIQUE, 0, op, tmp->describe_monster (op).c_str ());
1103 }
1104
1105 static void
1106 describe_dump_object (dynbuf &buf, object *ob)
1107 {
1108 char *txt = dump_object (ob);
1109 for (char *p = txt; *p; ++p) if (*p == '\n') *p = '\r';
1110 buf << "\n" << txt << "\n";
1111
1112 if (!ob->is_arch ())
1113 describe_dump_object (buf, ob->arch);
1114 }
1115
1116 std::string
1117 object::describe (object *who)
1118 {
1119 dynbuf_text buf (1024, 1024);
1120
1121 buf.printf ("That is: %s.\r", long_desc (who).c_str ());
1122
1123 if (custom_name)
1124 buf.printf ("You call it %s.\r", &custom_name);
1125
1126 switch (type)
1127 {
1128 case SPELLBOOK:
1129 if (flag [FLAG_IDENTIFIED] && inv)
1130 buf.printf ("%s is a level %s %s spell.\r", &inv->name, get_levelnumber (inv->level), &inv->skill);
1131 break;
1132
1133 case BOOK:
1134 if (msg)
1135 buf << "Something is written in it.\r";
1136 break;
1137
1138 case CONTAINER:
1139 if (race)
1140 {
1141 if (weight_limit && stats.Str < 100)
1142 buf.printf ("It can hold only %s and its weight limit is %.1f kg.\r",
1143 &race, weight_limit / (10.0 * (100 - stats.Str)));
1144 else
1145 buf.printf ("It can hold only %s.\r", &race);
1146 }
1147 else if (weight_limit && stats.Str < 100)
1148 buf.printf ("Its weight limit is %.1f kg.\r", weight_limit / (10.0 * (100 - stats.Str)));
1149 break;
1150
1151 case WAND:
1152 if (flag [FLAG_IDENTIFIED])
1153 buf.printf ("It has %d charges left.\r", stats.food);
1154 break;
1155 }
1156
1157 if (materialname && !msg)
1158 buf << (nrof > 1 ? "They are made of " : "It is made of ")
1159 << materialname
1160 << '\r';
1161
1162 if (who)
1163 /* Where to wear this item */
1164 for (int i = 0; i < NUM_BODY_LOCATIONS; i++)
1165 if (slot[i].info)
1166 {
1167 buf << (who->slot[i].info ? body_locations[i].use_name : body_locations[i].nonuse_name);
1168
1169 if (slot[i].info < -1 && who->slot[i].info)
1170 buf.printf ("(%d)", -slot[i].info);
1171
1172 buf << ".\r";
1173 }
1174
1175 if (weight)
1176 buf.printf ("%s %3.3f kg.\r", nrof > 1 ? "They weigh" : "It weighs", weight * (nrof ? nrof : 1) / 1000.0);
1177
1178 if (value && !flag [FLAG_STARTEQUIP] && !flag [FLAG_NO_PICK] && who)
1179 {
1180 buf.printf ("You reckon %s worth %s.\r", nrof > 1 ? "they are" : "it is", query_cost_string (this, who, F_TRUE | F_APPROX));
1181
1182 if (is_in_shop (who))
1183 {
1184 if (flag [FLAG_UNPAID])
1185 buf.printf ("%s would cost you %s.\r", nrof > 1 ? "They" : "It", query_cost_string (this, who, F_BUY | F_SHOP));
1186 else
1187 buf.printf ("You are offered %s for %s.\r", query_cost_string (this, who, F_SELL + F_SHOP), nrof > 1 ? "them" : "it");
1188 }
1189 }
1190
1191 if (flag [FLAG_MONSTER])
1192 buf << describe_monster (who);
1193
1194 /* Is this item buildable? */
1195 if (flag [FLAG_IS_BUILDABLE])
1196 buf << "This is a buildable item.\r";
1197
1198 /* Does the object have a message? Don't show message for all object
1199 * types - especially if the first entry is a match
1200 */
1201 if (msg)
1202 {
1203 if (type != EXIT && type != BOOK && type != CORPSE && !move_on && !has_dialogue ())
1204 {
1205 buf << '\r';
1206
1207 /* This is just a hack so when identifying the items, we print
1208 * out the extra message
1209 */
1210 if (need_identify (this) && flag [FLAG_IDENTIFIED])
1211 buf << "The object has a story:\r";
1212
1213 buf << msg << '\n';
1214 }
1215 }
1216 else if (inv && inv->type == SPELL && flag [FLAG_IDENTIFIED]
1217 && (type == SPELLBOOK || type == ROD || type == WAND
1218 || type == ROD || type == POTION || type == SCROLL))
1219 // for spellbooks and other stuff that contains spells, print the spell message,
1220 // unless the object has a custom message handled above.
1221 buf << '\r' << inv->msg << '\n';
1222
1223 // try to display the duration for some potions and scrolls
1224 // this includes change ability potions and group spells,
1225 // but does not handle protection potions
1226 if (inv && inv->type == SPELL && flag [FLAG_IDENTIFIED]
1227 && (type == POTION || type == SCROLL))
1228 {
1229 object *spell = inv;
1230
1231 if (spell->subtype == SP_PARTY_SPELL)
1232 spell = spell->other_arch;
1233
1234 if (spell->subtype == SP_CHANGE_ABILITY)
1235 buf.printf ("\nH<The effect will last about %.10g seconds.>",
1236 TICK2TIME (change_ability_duration (spell, this)));
1237 }
1238
1239 // Display a hint about inscribable items [empty books]
1240 // This includes the amount of text they can hold.
1241 if (type == INSCRIBABLE)
1242 {
1243 if (other_arch && other_arch->type == SCROLL)
1244 buf.printf ("\nH<You can use the inscription skill to inscribe a spell into it.>");
1245 else
1246 buf.printf ("\nH<You can use the inscription skill to inscribe text into it. It has room for up to %d characters.>",
1247 weight_limit);
1248 }
1249
1250 buf << '\n';
1251
1252 // the dungeon master additionally gets a complete dump
1253 if (who && who->flag [FLAG_WIZLOOK])
1254 {
1255 buf << "\nT<Object>\n";
1256 describe_dump_object (buf, this);
1257
1258 if (inv)
1259 {
1260 buf << "\nT<Top Inventory>\n";
1261 describe_dump_object (buf, inv);
1262 }
1263 }
1264
1265 return std::string (buf.linearise (), buf.size ());
1266 }
1267
1268 static void
1269 display_new_pickup (object *op)
1270 {
1271 int i = op->contr->mode;
1272
1273 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DEBUG", i & PU_DEBUG ? 1 : 0);
1274 new_draw_info_format (NDI_UNIQUE, 0, op, "%d INHIBIT", i & PU_INHIBIT ? 1 : 0);
1275 new_draw_info_format (NDI_UNIQUE, 0, op, "%d STOP", i & PU_STOP ? 1 : 0);
1276
1277 new_draw_info_format (NDI_UNIQUE, 0, op, "%d <= x pickup weight/value RATIO (0==off)", (i & PU_RATIO) * 5);
1278
1279 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FOOD", i & PU_FOOD ? 1 : 0);
1280 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DRINK", i & PU_DRINK ? 1 : 0);
1281 new_draw_info_format (NDI_UNIQUE, 0, op, "%d VALUABLES", i & PU_VALUABLES ? 1 : 0);
1282
1283 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOW", i & PU_BOW ? 1 : 0);
1284 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARROW", i & PU_ARROW ? 1 : 0);
1285
1286 new_draw_info_format (NDI_UNIQUE, 0, op, "%d HELMET", i & PU_HELMET ? 1 : 0);
1287 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SHIELD", i & PU_SHIELD ? 1 : 0);
1288 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARMOUR", i & PU_ARMOUR ? 1 : 0);
1289
1290 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOOTS", i & PU_BOOTS ? 1 : 0);
1291 new_draw_info_format (NDI_UNIQUE, 0, op, "%d GLOVES", i & PU_GLOVES ? 1 : 0);
1292 new_draw_info_format (NDI_UNIQUE, 0, op, "%d CLOAK", i & PU_CLOAK ? 1 : 0);
1293 new_draw_info_format (NDI_UNIQUE, 0, op, "%d KEY", i & PU_KEY ? 1 : 0);
1294
1295 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MISSILEWEAPON", i & PU_MISSILEWEAPON ? 1 : 0);
1296 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ALLWEAPON", i & PU_ALLWEAPON ? 1 : 0);
1297 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICAL", i & PU_MAGICAL ? 1 : 0);
1298 new_draw_info_format (NDI_UNIQUE, 0, op, "%d POTION", i & PU_POTION ? 1 : 0);
1299
1300 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SPELLBOOK", i & PU_SPELLBOOK ? 1 : 0);
1301 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SKILLSCROLL", i & PU_SKILLSCROLL ? 1 : 0);
1302 new_draw_info_format (NDI_UNIQUE, 0, op, "%d READABLES", i & PU_READABLES ? 1 : 0);
1303 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICDEVICE", i & PU_MAGIC_DEVICE ? 1 : 0);
1304
1305 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NOT CURSED", i & PU_NOT_CURSED ? 1 : 0);
1306
1307 new_draw_info_format (NDI_UNIQUE, 0, op, "%d JEWELS", i & PU_JEWELS ? 1 : 0);
1308 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FLESH", i & PU_FLESH ? 1 : 0);
1309
1310 new_draw_info_format (NDI_UNIQUE, 0, op, "");
1311 }
1312
1313 int
1314 command_pickup (object *op, char *params)
1315 {
1316 uint32 i;
1317 static const char *names[] = {
1318 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1319 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1320 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed",
1321 "jewels", "flesh", NULL
1322 };
1323 static uint32 modes[] = {
1324 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1325 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1326 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED,
1327 PU_JEWELS, PU_FLESH, 0
1328 };
1329
1330 if (!params)
1331 {
1332 /* if the new mode is used, just print the settings */
1333 display_new_pickup (op);
1334 return 0;
1335 }
1336
1337 while (*params == ' ' && *params)
1338 params++;
1339
1340 if (*params == '+' || *params == '-')
1341 {
1342 int mode;
1343
1344 for (mode = 0; names[mode]; mode++)
1345 {
1346 if (!strcmp (names[mode], params + 1))
1347 {
1348 i = op->contr->mode;
1349
1350 if (*params == '+')
1351 i = i | modes[mode];
1352 else
1353 i = i & ~modes[mode];
1354
1355 op->contr->mode = i;
1356 display_new_pickup (op);
1357 return 1;
1358 }
1359 }
1360 new_draw_info_format (NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params);
1361 return 1;
1362 }
1363
1364 if (sscanf (params, "%u", &i) != 1)
1365 {
1366 new_draw_info (NDI_UNIQUE, 0, op, "Usage: pickup <value_density> or [+-]type.");
1367 return 1;
1368 }
1369
1370 if (i <= PU_RATIO)
1371 i |= op->contr->mode & ~PU_RATIO;
1372
1373 op->contr->mode = i;
1374
1375 return 1;
1376 }
1377
1378 int
1379 command_search_items (object *op, char *params)
1380 {
1381 char buf[MAX_BUF];
1382
1383 if (params == NULL)
1384 {
1385 if (op->contr->search_str[0] == '\0')
1386 {
1387 new_draw_info (NDI_UNIQUE, 0, op, "Example: search-items magic+1");
1388 new_draw_info (NDI_UNIQUE, 0, op, "Would automatically pick up all");
1389 new_draw_info (NDI_UNIQUE, 0, op, "items containing the word 'magic+1'.");
1390 return 1;
1391 }
1392
1393 op->contr->search_str[0] = '\0';
1394 new_draw_info (NDI_UNIQUE, 0, op, "Search mode turned off.");
1395 op->update_stats ();
1396 return 1;
1397 }
1398
1399 if (strlen (params) >= MAX_BUF)
1400 {
1401 new_draw_info (NDI_UNIQUE, 0, op, "Search string too long.");
1402 return 1;
1403 }
1404
1405 strcpy (op->contr->search_str, params);
1406 sprintf (buf, "Now searching for '%s'.", op->contr->search_str);
1407 new_draw_info (NDI_UNIQUE, 0, op, buf);
1408 op->update_stats ();
1409
1410 return 1;
1411 }
1412