ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.21
Committed: Sun Oct 15 02:16:35 2006 UTC (17 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.20: +6 -9 lines
Log Message:
more now invalid tag_t uses replaced by refcounting. reduced codesize quite a bit.

File Contents

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