ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.47
Committed: Thu May 3 04:50:27 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.46: +3 -0 lines
Log Message:
- skill system is looking as if it were going somewhere, slowly.
- support a regex argument to who to limit user reports.

File Contents

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