ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.97
Committed: Mon Oct 12 14:00:59 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_81
Changes since 1.96: +7 -6 lines
Log Message:
clarify license

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 (op->container)
368 {
369 alt = op->container;
370 if (alt != tmp->env && !sack_can_hold (op, alt, tmp, count))
371 goto leave;
372 }
373 else
374 { /* non container pickup */
375 for (alt = op->inv; alt; alt = alt->below)
376 if (alt->type == CONTAINER && QUERY_FLAG (alt, FLAG_APPLIED) &&
377 alt->race && alt->race == tmp->race && sack_can_hold (NULL, alt, tmp, count))
378 break; /* perfect match */
379
380 if (!alt)
381 for (alt = op->inv; alt; alt = alt->below)
382 if (alt->type == CONTAINER && QUERY_FLAG (alt, FLAG_APPLIED) && sack_can_hold (NULL, alt, tmp, count))
383 break; /* General container comes next */
384
385 if (!alt)
386 alt = op; /* No free containers */
387 }
388
389 if (tmp->env == alt)
390 {
391 /* here it could be possible to check rent,
392 * if someone wants to implement it
393 */
394 alt = op;
395 }
396
397 #ifdef PICKUP_DEBUG
398 LOG (llevDebug, "Pick_up(): %s picks %s (%d) and inserts it %s.\n", op->name, tmp->name, op->contr->count, alt->name);
399 #endif
400
401 /* startequip items are not allowed to be put into containers: */
402 if (op->type == PLAYER && alt->type == CONTAINER && QUERY_FLAG (tmp, FLAG_STARTEQUIP))
403 {
404 new_draw_info (NDI_UNIQUE, 0, op, "This object cannot be put into containers!");
405 goto leave;
406 }
407
408 pick_up_object (op, alt, tmp, count);
409
410 if (tmp->destroyed () || tmp->env)
411 need_fix_tmp = 0;
412
413 if (op->type == PLAYER)
414 op->contr->count = 0;
415
416 goto leave;
417
418 leave:
419 if (need_fix_tmp)
420 fix_stopped_item (tmp, tmp_map, op);
421 }
422
423 /* This takes (picks up) and item. op is the player
424 * who issued the command. params is a string to
425 * match against the item name. Basically, always
426 * returns zero, but that should be improved.
427 */
428 int
429 command_take (object *op, char *params)
430 {
431 object *tmp, *next;
432
433 if (op->container)
434 tmp = op->container->inv;
435 else
436 {
437 tmp = op->above;
438 if (tmp)
439 while (tmp->above)
440 tmp = tmp->above;
441
442 if (!tmp)
443 tmp = op->below;
444 }
445
446 if (!tmp)
447 {
448 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to take!");
449 return 0;
450 }
451
452 /* Makes processing easier */
453 if (params && *params == '\0')
454 params = 0;
455
456 int cnt = MAX_ITEM_PER_DROP;
457
458 while (tmp)
459 {
460 next = tmp->below;
461
462 if (tmp->invisible)
463 {
464 tmp = next;
465 continue;
466 }
467
468 /* This following two if and else if could be merged into line
469 * but that probably will make it more difficult to read, and
470 * not make it any more efficient
471 */
472 if (params && item_matched_string (op, tmp, params))
473 {
474 if (--cnt < 0) break;
475 pick_up (op, tmp);
476 }
477 else if (can_pick (op, tmp) && !params)
478 {
479 if (--cnt < 0) break;
480 pick_up (op, tmp);
481 break;
482 }
483
484 tmp = next;
485 }
486
487 if (cnt < 0)
488 {
489 op->failmsg ("Couldn't pick up so many items at once.");
490 return 0;
491 }
492
493 if (!params && !tmp)
494 {
495 for (tmp = op->below; tmp; tmp = tmp->below)
496 if (!tmp->invisible)
497 {
498 new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up a %s.", &tmp->name);
499 break;
500 }
501
502 if (!tmp)
503 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up.");
504 }
505
506 return 0;
507 }
508
509 /*
510 * This function was part of drop, now is own function.
511 * Player 'op' tries to put object 'tmp' into sack 'sack',
512 * if nrof is non zero, then nrof objects is tried to put into sack.
513 *
514 * Note that the 'sack' in question can now be a transport,
515 * so this function isn't named very good anymore.
516 */
517 void
518 put_object_in_sack (object *op, object *sack, object *tmp, uint32 nrof)
519 {
520 object *tmp2, *sack2;
521 char buf[MAX_BUF];
522
523 if (sack == tmp)
524 return; /* Can't put an object in itself */
525
526 if (QUERY_FLAG (tmp, FLAG_STARTEQUIP))
527 {
528 new_draw_info_format (NDI_UNIQUE, 0, op, "You cannot put the %s in the %s.", query_name (tmp), query_name (sack));
529 return;
530 }
531
532 if (tmp->type == CONTAINER && tmp->inv)
533 {
534 /* Eneq(@csd.uu.se): If the object to be dropped is a container
535 * we instead move the contents of that container into the active
536 * container, this is only done if the object has something in it.
537 */
538 sack2 = tmp;
539 new_draw_info_format (NDI_UNIQUE, 0, op, "You move the items from %s into %s.", query_name (tmp), query_name (sack));
540
541 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp)
542 {
543 tmp = tmp2->below;
544
545 if ((sack->type == CONTAINER && sack_can_hold (op, op->container, tmp2, tmp2->nrof)))
546 put_object_in_sack (op, sack, tmp2, 0);
547 else
548 {
549 sprintf (buf, "Your %s fills up.", query_name (sack));
550 new_draw_info (NDI_UNIQUE, 0, op, buf);
551 break;
552 }
553 }
554
555 return;
556 }
557
558 /* Don't worry about this for containers - our caller should have
559 * already checked this.
560 */
561 if ((sack->type == CONTAINER) && !sack_can_hold (op, sack, tmp, (nrof ? nrof : tmp->nrof)))
562 return;
563
564 if (QUERY_FLAG (tmp, FLAG_APPLIED))
565 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
566 return;
567
568 /* we want to put some portion of the item into the container */
569 if (!can_split (op, tmp, nrof))
570 return;
571
572 new_draw_info_format (NDI_UNIQUE, 0, op, "You put the %s in %s.", query_name (tmp), query_name (sack));
573 sack->insert (tmp);
574 }
575
576 /*
577 * This function was part of drop, now is own function.
578 * Player 'op' tries to drop object 'tmp', if tmp is non zero, then
579 * nrof objects is tried to dropped.
580 * This is used when dropping objects onto the floor.
581 */
582 void
583 drop_object (object *op, object *tmp, uint32 nrof)
584 {
585 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
586 return;
587
588 if (QUERY_FLAG (tmp, FLAG_APPLIED))
589 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
590 return; /* can't unapply it */
591
592 /* We are only dropping some of the items. We split the current object
593 * off
594 */
595 if (!can_split (op, tmp, nrof))
596 return;
597
598 drop_object (op, tmp);
599
600 if (!tmp->destroyed () && !tmp->is_inserted ())
601 {
602 // if nothing happened with the object we give it back
603 op->insert (tmp);
604 }
605 }
606
607 /* In contrast to drop_object (op, tmp, nrof) above this function takes the
608 * already split off object, and feeds it to the event handlers and does
609 * other magic with it.
610 *
611 * <droppper> is the object that dropped this object and <obj> is the
612 * object that was dropped.
613 *
614 * Make sure to check what happened with <obj> after this function returns!
615 * Otherwise you may leak this object.
616 */
617 void
618 drop_object (object *dropper, object *obj)
619 {
620 if (INVOKE_OBJECT (DROP, obj, ARG_OBJECT (dropper)))
621 return;
622
623 if (obj->destroyed () || obj->is_inserted ())
624 return;
625
626 if (QUERY_FLAG (obj, FLAG_STARTEQUIP))
627 {
628 dropper->statusmsg (format ("You drop the %s.", query_name (obj)));
629 dropper->statusmsg ("The god who lent it to you retrieves it.");
630
631 obj->destroy ();
632 dropper->update_stats ();
633 return;
634 }
635
636 for (object *floor = GET_MAP_OB (dropper->map, dropper->x, dropper->y);
637 floor;
638 floor = floor->above)
639 if (INVOKE_OBJECT (DROP_ON, floor, ARG_OBJECT (obj), ARG_OBJECT (dropper)))
640 return;
641
642 if (obj->destroyed () || obj->is_inserted ())
643 return;
644
645 if (is_in_shop (dropper) && !QUERY_FLAG (obj, FLAG_UNPAID) && obj->type != MONEY)
646 if (!sell_item (obj, dropper))
647 return;
648
649 /* If nothing special happened with this object, the default action is to
650 * insert it below the dropper:
651 */
652
653 obj->x = dropper->x;
654 obj->y = dropper->y;
655
656 insert_ob_in_map (obj, dropper->map, dropper, INS_BELOW_ORIGINATOR);
657 }
658
659 void
660 drop (object *op, object *tmp)
661 {
662 /* Hopeful fix for disappearing objects when dropping from a container -
663 * somehow, players get an invisible object in the container, and the
664 * old logic would skip over invisible objects - works fine for the
665 * playes inventory, but drop inventory wants to use the next value.
666 */
667 if (tmp->invisible)
668 {
669 /* if the following is the case, it must be in an container. */
670 if (tmp->env && tmp->env->type != PLAYER)
671 {
672 /* Just toss the object - probably shouldn't be hanging
673 * around anyways
674 */
675 tmp->destroy ();
676 return;
677 }
678 else
679 {
680 while (tmp && tmp->invisible)
681 tmp = tmp->below;
682 }
683 }
684
685 if (tmp == NULL)
686 {
687 new_draw_info (NDI_UNIQUE, 0, op, "You don't have anything to drop.");
688 return;
689 }
690
691 if (QUERY_FLAG (tmp, FLAG_INV_LOCKED))
692 {
693 new_draw_info (NDI_UNIQUE, 0, op, "This item is locked");
694 return;
695 }
696
697 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
698 {
699 #if 0
700 /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
701 new_draw_info (NDI_UNIQUE, 0, op, "This item can't be dropped.");
702 #endif
703 return;
704 }
705
706 if (op->type == PLAYER && op->contr->last_used == tmp)
707 op->contr->last_used = tmp->below ? tmp->below
708 : tmp->above ? tmp->above
709 : (object *)0;
710
711 if (op->container)
712 {
713 if (op->type == PLAYER)
714 put_object_in_sack (op, op->container, tmp, op->contr->count);
715 else
716 put_object_in_sack (op, op->container, tmp, 0);
717 }
718 else
719 {
720 if (op->type == PLAYER)
721 drop_object (op, tmp, op->contr->count);
722 else
723 drop_object (op, tmp, 0);
724 }
725
726 if (op->type == PLAYER)
727 op->contr->count = 0;
728 }
729
730 /* Command will drop all items that have not been locked */
731 int
732 command_dropall (object *op, char *params)
733 {
734
735 if (op->inv == NULL)
736 {
737 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop!");
738 return 0;
739 }
740
741 object *curinv = op->inv;
742 object *nextinv;
743
744 /*
745 This is the default. Drops everything not locked or considered
746 not something that should be dropped.
747 */
748 /*
749 Care must be taken that the next item pointer is not to money as
750 the drop() routine will do unknown things to it when dropping
751 in a shop. --Tero.Pelander@utu.fi
752 */
753
754 int cnt = MAX_ITEM_PER_DROP;
755
756 if (!params)
757 {
758 while (curinv)
759 {
760 nextinv = curinv->below;
761
762 while (nextinv && nextinv->type == MONEY)
763 nextinv = nextinv->below;
764
765 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && curinv->type != MONEY &&
766 curinv->type != FOOD && curinv->type != KEY &&
767 curinv->type != SPECIAL_KEY && curinv->type != GEM &&
768 !curinv->invisible && (curinv->type != CONTAINER || op->container != curinv))
769 {
770 drop (op, curinv);
771 if (--cnt <= 0) break;
772 }
773
774 curinv = nextinv;
775 }
776 }
777 else if (strcmp (params, "weapons") == 0)
778 {
779 while (curinv)
780 {
781 nextinv = curinv->below;
782
783 while (nextinv && nextinv->type == MONEY)
784 nextinv = nextinv->below;
785
786 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && ((curinv->type == WEAPON) || (curinv->type == BOW) || (curinv->type == ARROW)))
787 {
788 drop (op, curinv);
789 if (--cnt <= 0) break;
790 }
791
792 curinv = nextinv;
793 }
794 }
795 else if (strcmp (params, "armor") == 0 || strcmp (params, "armour") == 0)
796 {
797 while (curinv)
798 {
799 nextinv = curinv->below;
800
801 while (nextinv && nextinv->type == MONEY)
802 nextinv = nextinv->below;
803
804 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && ((curinv->type == ARMOUR) || curinv->type == SHIELD || curinv->type == HELMET))
805 {
806 drop (op, curinv);
807 if (--cnt <= 0) break;
808 }
809
810 curinv = nextinv;
811 }
812 }
813 else if (strcmp (params, "misc") == 0)
814 {
815 while (curinv)
816 {
817 nextinv = curinv->below;
818
819 while (nextinv && nextinv->type == MONEY)
820 nextinv = nextinv->below;
821
822 if (!QUERY_FLAG (curinv, FLAG_INV_LOCKED) && !QUERY_FLAG (curinv, FLAG_APPLIED))
823 {
824 switch (curinv->type)
825 {
826 case HORN:
827 case BOOK:
828 case SPELLBOOK:
829 case GIRDLE:
830 case AMULET:
831 case RING:
832 case CLOAK:
833 case BOOTS:
834 case GLOVES:
835 case BRACERS:
836 case SCROLL:
837 case ARMOUR_IMPROVER:
838 case WEAPON_IMPROVER:
839 case WAND:
840 case ROD:
841 case POTION:
842 drop (op, curinv);
843 curinv = nextinv;
844 break;
845 default:
846 curinv = nextinv;
847 break;
848 }
849
850 if (--cnt <= 0) break;
851 }
852
853 curinv = nextinv;
854 }
855 }
856
857 if (cnt <= 0)
858 op->failmsg ("Only dropped some items, can't drop that many items at once.");
859
860 /* draw_look(op);*/
861 return 0;
862 }
863
864
865 /* This function tries to drop all objects in the <objs> vector.
866 * <dropper> is the object that wants to drop them.
867 * <cnt> can be a 0 pointer or a pointer to the maximum number of
868 * drop operations to perform.
869 *
870 * Returns true if at least one item was dropped.
871 */
872 bool
873 drop_vector (object *dropper, vector<object *> &objs, int *cnt)
874 {
875 vector<object *>::iterator i;
876
877 bool did_one = false;
878
879 for (i = objs.begin (); i != objs.end (); i++)
880 {
881 drop (dropper, *i);
882 if (cnt && --*cnt <= 0) break;
883 did_one = true;
884 }
885
886 return did_one;
887 }
888
889 /* Object op wants to drop object(s) params. params can be a
890 * comma seperated list.
891 */
892 int
893 command_drop (object *op, char *params)
894 {
895 object *tmp, *next;
896 int did_one = 0;
897
898 if (!params)
899 {
900 new_draw_info (NDI_UNIQUE, 0, op, "Drop what?");
901 return 0;
902 }
903 else
904 {
905 vector<object *> matched_objs;
906
907 for (tmp = op->inv; tmp; tmp = next)
908 {
909 next = tmp->below;
910 if (QUERY_FLAG (tmp, FLAG_NO_DROP) || tmp->invisible)
911 continue;
912
913 if (item_matched_string (op, tmp, params))
914 matched_objs.push_back (tmp);
915 }
916
917 int cnt = MAX_ITEM_PER_DROP;
918
919 if (!drop_vector (op, matched_objs, &cnt))
920 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop.");
921
922 if (cnt <= 0)
923 op->failmsg ("Only dropped some items, can't drop that many items at once.");
924 }
925
926 return 0;
927 }
928
929 int
930 command_examine (object *op, char *params)
931 {
932 if (!params)
933 {
934 object *tmp = op->below;
935
936 while (tmp && !tmp->client_visible ())
937 tmp = tmp->below;
938
939 if (tmp)
940 examine (op, tmp);
941 }
942 else
943 {
944 object *tmp = find_best_object_match (op, params);
945
946 if (tmp)
947 examine (op, tmp);
948 else
949 op->contr->infobox (MSG_CHANNEL ("examine"), format ("Could not find an object that matches %s", params));
950 }
951
952 return 0;
953 }
954
955 /* op should be a player.
956 * we return the object the player has marked with the 'mark' command
957 * below. If no match is found (or object has changed), we return
958 * NULL. We leave it up to the calling function to print messages if
959 * nothing is found.
960 */
961 object *
962 find_marked_object (object *op)
963 {
964 object *tmp;
965
966 if (!op || !op->contr)
967 return NULL;
968
969 if (!op->contr->mark)
970 {
971 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
972 return 0;
973 }
974
975 /* This may seem like overkill, but we need to make sure that they
976 * player hasn't dropped the item. We use count on the off chance that
977 * an item got reincarnated at some point.
978 */
979 for (tmp = op->inv; tmp; tmp = tmp->below)
980 {
981 if (tmp->invisible)
982 continue;
983
984 if (tmp == op->contr->mark)
985 {
986 if (!tmp->destroyed ())
987 return tmp;
988 else
989 {
990 op->contr->mark = 0;
991 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
992 return 0;
993 }
994 }
995 }
996
997 return 0;
998 }
999
1000 std::string
1001 object::describe_monster (object *who)
1002 {
1003 dynbuf_text buf (512, 512);
1004
1005 object *mon = head ? head : this;
1006
1007 if (QUERY_FLAG (mon, FLAG_UNDEAD))
1008 buf << "It is an undead force.\r";
1009
1010 if (mon->level > who->level)
1011 buf << "It is likely more powerful than you.\r";
1012 else if (mon->level < who->level)
1013 buf << "It is likely less powerful than you.\r";
1014 else
1015 buf << "It is probably as powerful as you.\r";
1016
1017 if (mon->attacktype & AT_ACID)
1018 buf << "You seem to smell an acrid odor.\r";
1019
1020 /* Anyone know why this used to use the clone value instead of the
1021 * maxhp field? This seems that it should give more accurate results.
1022 */
1023 switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
1024 { /* From 1-4 */
1025 case 1:
1026 buf << "It is in a bad shape.\r";
1027 break;
1028 case 2:
1029 buf << "It is hurt.\r";
1030 break;
1031 case 3:
1032 buf << "It is somewhat hurt.\r";
1033 break;
1034 case 4:
1035 buf << "It is in excellent shape.\r";
1036 break;
1037 }
1038
1039 if (present_in_ob (POISONING, mon))
1040 buf << "It looks very ill.\r";
1041
1042 buf << '\n';
1043
1044 return buf;
1045 }
1046
1047 /* tmp is the object being described, pl is who is examing it. */
1048 const char *
1049 long_desc (object *tmp, object *pl)
1050 {
1051 static std::string s;
1052
1053 return (s = tmp->long_desc (pl)).c_str ();
1054 }
1055
1056 std::string
1057 object::long_desc (object *who)
1058 {
1059 std::string buf (query_name ());
1060
1061 switch (type)
1062 {
1063 case RING:
1064 case SKILL:
1065 case WEAPON:
1066 case ARMOUR:
1067 case BRACERS:
1068 case HELMET:
1069 case SHIELD:
1070 case BOOTS:
1071 case GLOVES:
1072 case AMULET:
1073 case GIRDLE:
1074 case BOW:
1075 case ARROW:
1076 case CLOAK:
1077 case FOOD:
1078 case DRINK:
1079 case FLESH:
1080 case SKILL_TOOL:
1081 case LAMP:
1082 case POWER_CRYSTAL:
1083 {
1084 const char *cp = ::describe_item (this, who);
1085
1086 if (*cp)
1087 {
1088 buf.append (" ");
1089 buf.append (cp);
1090 }
1091 }
1092 }
1093
1094 return buf;
1095 }
1096
1097 /* op is the player
1098 * tmp is the monster being examined.
1099 */
1100 void
1101 examine_monster (object *op, object *tmp)
1102 {
1103 new_draw_info (NDI_UNIQUE, 0, op, tmp->describe_monster (op).c_str ());
1104 }
1105
1106 static void
1107 describe_dump_object (dynbuf &buf, object *ob)
1108 {
1109 char *txt = dump_object (ob);
1110 for (char *p = txt; *p; ++p) if (*p == '\n') *p = '\r';
1111 buf << "\n" << txt << "\n";
1112
1113 if (!ob->is_arch ())
1114 describe_dump_object (buf, ob->arch);
1115 }
1116
1117 std::string
1118 object::describe (object *who)
1119 {
1120 dynbuf_text buf (1024, 1024);
1121
1122 buf.printf ("That is: %s.\r", long_desc (who).c_str ());
1123
1124 if (custom_name)
1125 buf.printf ("You call it %s.\r", &custom_name);
1126
1127 switch (type)
1128 {
1129 case SPELLBOOK:
1130 if (flag [FLAG_IDENTIFIED] && inv)
1131 buf.printf ("%s is a level %s %s spell.\r", &inv->name, get_levelnumber (inv->level), &inv->skill);
1132 break;
1133
1134 case BOOK:
1135 if (msg)
1136 buf << "Something is written in it.\r";
1137 break;
1138
1139 case CONTAINER:
1140 if (race)
1141 {
1142 if (weight_limit && stats.Str < 100)
1143 buf.printf ("It can hold only %s and its weight limit is %.1f kg.\r",
1144 &race, weight_limit / (10.0 * (100 - stats.Str)));
1145 else
1146 buf.printf ("It can hold only %s.\r", &race);
1147 }
1148 else if (weight_limit && stats.Str < 100)
1149 buf.printf ("Its weight limit is %.1f kg.\r", weight_limit / (10.0 * (100 - stats.Str)));
1150 break;
1151
1152 case WAND:
1153 if (flag [FLAG_IDENTIFIED])
1154 buf.printf ("It has %d charges left.\r", stats.food);
1155 break;
1156 }
1157
1158 if (materialname && !msg)
1159 buf << (nrof > 1 ? "They are made of " : "It is made of ")
1160 << materialname
1161 << '\r';
1162
1163 if (who)
1164 /* Where to wear this item */
1165 for (int i = 0; i < NUM_BODY_LOCATIONS; i++)
1166 if (slot[i].info)
1167 {
1168 buf << (who->slot[i].info ? body_locations[i].use_name : body_locations[i].nonuse_name);
1169
1170 if (slot[i].info < -1 && who->slot[i].info)
1171 buf.printf ("(%d)", -slot[i].info);
1172
1173 buf << ".\r";
1174 }
1175
1176 if (weight)
1177 buf.printf ("%s %3.3f kg.\r", nrof > 1 ? "They weigh" : "It weighs", weight * (nrof ? nrof : 1) / 1000.0);
1178
1179 if (value && !flag [FLAG_STARTEQUIP] && !flag [FLAG_NO_PICK] && who)
1180 {
1181 buf.printf ("You reckon %s worth %s.\r", nrof > 1 ? "they are" : "it is", query_cost_string (this, who, F_TRUE | F_APPROX));
1182
1183 if (is_in_shop (who))
1184 {
1185 if (flag [FLAG_UNPAID])
1186 buf.printf ("%s would cost you %s.\r", nrof > 1 ? "They" : "It", query_cost_string (this, who, F_BUY | F_SHOP));
1187 else
1188 buf.printf ("You are offered %s for %s.\r", query_cost_string (this, who, F_SELL + F_SHOP), nrof > 1 ? "them" : "it");
1189 }
1190 }
1191
1192 if (flag [FLAG_MONSTER])
1193 buf << describe_monster (who);
1194
1195 /* Is this item buildable? */
1196 if (flag [FLAG_IS_BUILDABLE])
1197 buf << "This is a buildable item.\r";
1198
1199 /* Does the object have a message? Don't show message for all object
1200 * types - especially if the first entry is a match
1201 */
1202 if (msg)
1203 {
1204 if (type != EXIT && type != BOOK && type != CORPSE && !move_on && !has_dialogue ())
1205 {
1206 buf << '\r';
1207
1208 /* This is just a hack so when identifying the items, we print
1209 * out the extra message
1210 */
1211 if (need_identify (this) && flag [FLAG_IDENTIFIED])
1212 buf << "The object has a story:\r";
1213
1214 buf << msg << '\n';
1215 }
1216 }
1217 else if (inv && inv->type == SPELL && flag [FLAG_IDENTIFIED]
1218 && (type == SPELLBOOK || type == ROD || type == WAND
1219 || type == ROD || type == POTION || type == SCROLL))
1220 // for spellbooks and other stuff that contains spells, print the spell message,
1221 // unless the object has a custom message handled above.
1222 buf << '\r' << inv->msg << '\n';
1223
1224 // try to display the duration for some potions and scrolls
1225 // this includes change ability potions and group spells,
1226 // but does not handle protection potions
1227 if (inv && inv->type == SPELL && flag [FLAG_IDENTIFIED]
1228 && (type == POTION || type == SCROLL))
1229 {
1230 object *spell = inv;
1231
1232 if (spell->subtype == SP_PARTY_SPELL)
1233 spell = spell->other_arch;
1234
1235 if (spell->subtype == SP_CHANGE_ABILITY)
1236 buf.printf ("\nH<The effect will last about %.10g seconds.>",
1237 TICK2TIME (change_ability_duration (spell, this)));
1238 }
1239
1240 buf << '\n';
1241
1242 // the dungeon master additionally gets a complete dump
1243 if (who && who->flag [FLAG_WIZLOOK])
1244 {
1245 buf << "\nT<Object>\n";
1246 describe_dump_object (buf, this);
1247
1248 if (inv)
1249 {
1250 buf << "\nT<Top Inventory>\n";
1251 describe_dump_object (buf, inv);
1252 }
1253 }
1254
1255 return std::string (buf.linearise (), buf.size ());
1256 }
1257
1258 static void
1259 display_new_pickup (object *op)
1260 {
1261 int i = op->contr->mode;
1262
1263 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DEBUG", i & PU_DEBUG ? 1 : 0);
1264 new_draw_info_format (NDI_UNIQUE, 0, op, "%d INHIBIT", i & PU_INHIBIT ? 1 : 0);
1265 new_draw_info_format (NDI_UNIQUE, 0, op, "%d STOP", i & PU_STOP ? 1 : 0);
1266
1267 new_draw_info_format (NDI_UNIQUE, 0, op, "%d <= x pickup weight/value RATIO (0==off)", (i & PU_RATIO) * 5);
1268
1269 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FOOD", i & PU_FOOD ? 1 : 0);
1270 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DRINK", i & PU_DRINK ? 1 : 0);
1271 new_draw_info_format (NDI_UNIQUE, 0, op, "%d VALUABLES", i & PU_VALUABLES ? 1 : 0);
1272
1273 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOW", i & PU_BOW ? 1 : 0);
1274 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARROW", i & PU_ARROW ? 1 : 0);
1275
1276 new_draw_info_format (NDI_UNIQUE, 0, op, "%d HELMET", i & PU_HELMET ? 1 : 0);
1277 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SHIELD", i & PU_SHIELD ? 1 : 0);
1278 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARMOUR", i & PU_ARMOUR ? 1 : 0);
1279
1280 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOOTS", i & PU_BOOTS ? 1 : 0);
1281 new_draw_info_format (NDI_UNIQUE, 0, op, "%d GLOVES", i & PU_GLOVES ? 1 : 0);
1282 new_draw_info_format (NDI_UNIQUE, 0, op, "%d CLOAK", i & PU_CLOAK ? 1 : 0);
1283 new_draw_info_format (NDI_UNIQUE, 0, op, "%d KEY", i & PU_KEY ? 1 : 0);
1284
1285 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MISSILEWEAPON", i & PU_MISSILEWEAPON ? 1 : 0);
1286 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ALLWEAPON", i & PU_ALLWEAPON ? 1 : 0);
1287 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICAL", i & PU_MAGICAL ? 1 : 0);
1288 new_draw_info_format (NDI_UNIQUE, 0, op, "%d POTION", i & PU_POTION ? 1 : 0);
1289
1290 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SPELLBOOK", i & PU_SPELLBOOK ? 1 : 0);
1291 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SKILLSCROLL", i & PU_SKILLSCROLL ? 1 : 0);
1292 new_draw_info_format (NDI_UNIQUE, 0, op, "%d READABLES", i & PU_READABLES ? 1 : 0);
1293 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICDEVICE", i & PU_MAGIC_DEVICE ? 1 : 0);
1294
1295 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NOT CURSED", i & PU_NOT_CURSED ? 1 : 0);
1296
1297 new_draw_info_format (NDI_UNIQUE, 0, op, "%d JEWELS", i & PU_JEWELS ? 1 : 0);
1298 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FLESH", i & PU_FLESH ? 1 : 0);
1299
1300 new_draw_info_format (NDI_UNIQUE, 0, op, "");
1301 }
1302
1303 int
1304 command_pickup (object *op, char *params)
1305 {
1306 uint32 i;
1307 static const char *names[] = {
1308 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1309 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1310 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed",
1311 "jewels", "flesh", NULL
1312 };
1313 static uint32 modes[] = {
1314 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1315 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1316 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED,
1317 PU_JEWELS, PU_FLESH, 0
1318 };
1319
1320 if (!params)
1321 {
1322 /* if the new mode is used, just print the settings */
1323 display_new_pickup (op);
1324 return 0;
1325 }
1326
1327 while (*params == ' ' && *params)
1328 params++;
1329
1330 if (*params == '+' || *params == '-')
1331 {
1332 int mode;
1333
1334 for (mode = 0; names[mode]; mode++)
1335 {
1336 if (!strcmp (names[mode], params + 1))
1337 {
1338 i = op->contr->mode;
1339
1340 if (*params == '+')
1341 i = i | modes[mode];
1342 else
1343 i = i & ~modes[mode];
1344
1345 op->contr->mode = i;
1346 display_new_pickup (op);
1347 return 1;
1348 }
1349 }
1350 new_draw_info_format (NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params);
1351 return 1;
1352 }
1353
1354 if (sscanf (params, "%u", &i) != 1)
1355 {
1356 new_draw_info (NDI_UNIQUE, 0, op, "Usage: pickup <value_density> or [+-]type.");
1357 return 1;
1358 }
1359
1360 if (i <= PU_RATIO)
1361 i |= op->contr->mode & ~PU_RATIO;
1362
1363 op->contr->mode = i;
1364
1365 return 1;
1366 }
1367
1368 int
1369 command_search_items (object *op, char *params)
1370 {
1371 char buf[MAX_BUF];
1372
1373 if (params == NULL)
1374 {
1375 if (op->contr->search_str[0] == '\0')
1376 {
1377 new_draw_info (NDI_UNIQUE, 0, op, "Example: search-items magic+1");
1378 new_draw_info (NDI_UNIQUE, 0, op, "Would automatically pick up all");
1379 new_draw_info (NDI_UNIQUE, 0, op, "items containing the word 'magic+1'.");
1380 return 1;
1381 }
1382
1383 op->contr->search_str[0] = '\0';
1384 new_draw_info (NDI_UNIQUE, 0, op, "Search mode turned off.");
1385 op->update_stats ();
1386 return 1;
1387 }
1388
1389 if (strlen (params) >= MAX_BUF)
1390 {
1391 new_draw_info (NDI_UNIQUE, 0, op, "Search string too long.");
1392 return 1;
1393 }
1394
1395 strcpy (op->contr->search_str, params);
1396 sprintf (buf, "Now searching for '%s'.", op->contr->search_str);
1397 new_draw_info (NDI_UNIQUE, 0, op, buf);
1398 op->update_stats ();
1399
1400 return 1;
1401 }
1402