ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.17
Committed: Thu Sep 14 22:34:03 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.16: +1 -1 lines
Log Message:
indent

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