ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.14
Committed: Sun Sep 10 15:59:57 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.13: +1274 -1062 lines
Log Message:
indent

File Contents

# Content
1
2 /*
3 * static char *rcsid_c_object_c =
4 * "$Id: c_object.C,v 1.13 2006-09-07 10:01:57 pippijn Exp $";
5 */
6
7 /*
8 CrossFire, A Multiplayer game for X-windows
9
10 Copyright (C) 2002 Mark Wedel & Crossfire Development Team
11 Copyright (C) 1992 Frank Tore Johansen
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27 The author can be reached via e-mail to crossfire-devel@real-time.com
28
29 Object (handling) commands
30 */
31
32 #include <global.h>
33 #include <loader.h>
34 #include <skills.h>
35 #ifndef __CEXTRACT__
36 # include <sproto.h>
37 #endif
38 #include <living.h>
39 #include <math.h>
40
41 /*
42 * Object id parsing functions
43 */
44
45 #define OBLINKMALLOC(p) if(!((p)=(objectlink *)malloc(sizeof(objectlink))))\
46 fatal(OUT_OF_MEMORY);
47
48 #define ADD_ITEM(NEW,COUNT)\
49 if(!first) {\
50 OBLINKMALLOC(first);\
51 last=first;\
52 } else {\
53 OBLINKMALLOC(last->next);\
54 last=last->next;\
55 }\
56 last->next=NULL;\
57 last->ob=(NEW);\
58 last->id=(COUNT);
59
60 /**
61 * Search the inventory of 'pl' for what matches best with params.
62 * we use item_matched_string above - this gives us consistent behaviour
63 * between many commands. Return the best match, or NULL if no match.
64 * aflag is used with apply -u , and apply -a to
65 * only unapply applied, or apply unapplied objects
66 **/
67 static object *
68 find_best_apply_object_match (object *pl, const char *params, enum apply_flag aflag)
69 {
70 object *tmp, *best = NULL;
71 int match_val = 0, tmpmatch;
72
73 for (tmp = pl->inv; tmp; tmp = tmp->below)
74 {
75 if (tmp->invisible)
76 continue;
77 if ((tmpmatch = item_matched_string (pl, tmp, params)) > match_val)
78 {
79 if ((aflag == AP_APPLY) && (QUERY_FLAG (tmp, FLAG_APPLIED)))
80 continue;
81 if ((aflag == AP_UNAPPLY) && (!QUERY_FLAG (tmp, FLAG_APPLIED)))
82 continue;
83 match_val = tmpmatch;
84 best = tmp;
85 }
86 }
87 return best;
88 }
89
90 /**
91 * Shortcut to find_best_apply_object_match(pl, params, AF_NULL);
92 **/
93 object *
94 find_best_object_match (object *pl, const char *params)
95 {
96 return find_best_apply_object_match (pl, params, AP_NULL);
97 }
98
99 int
100 command_uskill (object *pl, char *params)
101 {
102 if (!params)
103 {
104 new_draw_info (NDI_UNIQUE, 0, pl, "Usage: use_skill <skill name>");
105 return 0;
106 }
107 return use_skill (pl, params);
108 }
109
110 int
111 command_rskill (object *pl, char *params)
112 {
113 object *skill;
114
115 if (!params)
116 {
117 new_draw_info (NDI_UNIQUE, 0, pl, "Usage: ready_skill <skill name>");
118 return 0;
119 }
120 skill = find_skill_by_name (pl, params);
121
122 if (!skill)
123 {
124 new_draw_info_format (NDI_UNIQUE, 0, pl, "You have no knowledge of the skill %s", params);
125 return 0;
126 }
127 return change_skill (pl, skill, 0);
128 }
129
130
131 /* These functions (command_search, command_disarm) are really just wrappers for
132 * things like 'use_skill ...'). In fact, they should really be obsoleted
133 * and replaced with those.
134 */
135 int
136 command_search (object *op, char *params)
137 {
138 return use_skill (op, skill_names[SK_FIND_TRAPS]);
139 }
140
141 int
142 command_disarm (object *op, char *params)
143 {
144 return use_skill (op, skill_names[SK_DISARM_TRAPS]);
145 }
146
147
148 /* A little special because we do want to pass the full params along
149 * as it includes the object to throw.
150 */
151 int
152 command_throw (object *op, char *params)
153 {
154 object *skop;
155
156 skop = find_skill_by_name (op, skill_names[SK_THROWING]);
157 if (skop)
158 return do_skill (op, op, skop, op->facing, params);
159 else
160 {
161 new_draw_info (NDI_UNIQUE, 0, op, "You have no knowledge of the skill throwing.");
162 }
163 return 0;
164 }
165
166
167 int
168 command_apply (object *op, char *params)
169 {
170 if (!params)
171 {
172 player_apply_below (op);
173 return 0;
174 }
175 else
176 {
177 apply_flag aflag = (apply_flag) 0;
178 object *inv;
179
180 while (*params == ' ')
181 params++;
182 if (!strncmp (params, "-a ", 3))
183 {
184 aflag = AP_APPLY;
185 params += 3;
186 }
187 if (!strncmp (params, "-u ", 3))
188 {
189 aflag = AP_UNAPPLY;
190 params += 3;
191 }
192 while (*params == ' ')
193 params++;
194
195 inv = find_best_apply_object_match (op, params, aflag);
196 if (inv)
197 {
198 player_apply (op, inv, aflag, 0);
199 }
200 else
201 new_draw_info_format (NDI_UNIQUE, 0, op, "Could not find any match to the %s.", params);
202 }
203 return 0;
204 }
205
206 /*
207 * Check if an item op can be put into a sack. If pl exists then tell
208 * a player the reason of failure.
209 * returns 1 if it will fit, 0 if it will not. nrof is the number of
210 * objects (op) we want to put in. We specify it separately instead of
211 * using op->nrof because often times, a player may have specified a
212 * certain number of objects to drop, so we can pass that number, and
213 * not need to use split_ob and stuff.
214 */
215 int
216 sack_can_hold (object *pl, object *sack, object *op, uint32 nrof)
217 {
218
219 if (!QUERY_FLAG (sack, FLAG_APPLIED))
220 {
221 new_draw_info_format (NDI_UNIQUE, 0, pl, "The %s is not active.", query_name (sack));
222 return 0;
223 }
224 if (sack == op)
225 {
226 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the %s into itself.", query_name (sack));
227 return 0;
228 }
229 if (sack->race && (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type)))
230 {
231 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can put only %s into the %s.", &sack->race, query_name (sack));
232 return 0;
233 }
234 if (op->type == SPECIAL_KEY && sack->slaying && op->slaying)
235 {
236 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the key into %s.", query_name (sack));
237 return 0;
238 }
239 if (sack->weight_limit && (sint32) (sack->carrying + (nrof ? nrof : 1) *
240 (op->weight + (op->type == CONTAINER ? (op->carrying * op->stats.Str) : 0))
241 * (100 - sack->stats.Str) / 100) > sack->weight_limit)
242 {
243 new_draw_info_format (NDI_UNIQUE, 0, pl, "That won't fit in the %s!", query_name (sack));
244 return 0;
245 }
246 /* All other checks pass, must be OK */
247 return 1;
248 }
249
250 /* Pick up commands follow */
251
252 /* pl = player (not always - monsters can use this now)
253 * op is the object to put tmp into,
254 * tmp is the object to pick up, nrof is the number to
255 * pick up (0 means all of them)
256 */
257 static void
258 pick_up_object (object *pl, object *op, object *tmp, int nrof)
259 {
260 /* buf needs to be big (more than 256 chars) because you can get
261 * very long item names.
262 */
263 char buf[HUGE_BUF];
264 object *env = tmp->env;
265 uint32 weight, effective_weight_limit;
266 int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
267
268 /* IF the player is flying & trying to take the item out of a container
269 * that is in his inventory, let him. tmp->env points to the container
270 * (sack, luggage, etc), tmp->env->env then points to the player (nested
271 * containers not allowed as of now)
272 */
273 if ((pl->move_type & MOVE_FLYING) && !QUERY_FLAG (pl, FLAG_WIZ) && is_player_inv (tmp) != pl)
274 {
275 new_draw_info (NDI_UNIQUE, 0, pl, "You are levitating, you can't reach the ground!");
276 return;
277 }
278 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
279 return;
280 if (QUERY_FLAG (tmp, FLAG_WAS_WIZ) && !QUERY_FLAG (pl, FLAG_WAS_WIZ))
281 {
282 new_draw_info (NDI_UNIQUE, 0, pl, "The object disappears in a puff of smoke!");
283 new_draw_info (NDI_UNIQUE, 0, pl, "It must have been an illusion.");
284 if (pl->type == PLAYER)
285 esrv_del_item (pl->contr, tmp->count);
286 if (!QUERY_FLAG (tmp, FLAG_REMOVED))
287 remove_ob (tmp);
288 free_object (tmp);
289 return;
290 }
291
292 if (nrof > tmp_nrof || nrof == 0)
293 nrof = tmp_nrof;
294 /* Figure out how much weight this object will add to the player */
295 weight = tmp->weight * nrof;
296 if (tmp->inv)
297 weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
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 if ((pl->weight + pl->carrying + weight) > effective_weight_limit)
303 {
304 new_draw_info (0, 0, pl, "That item is too heavy for you to pick up.");
305 return;
306 }
307 if (settings.real_wiz == FALSE && QUERY_FLAG (pl, FLAG_WAS_WIZ))
308 SET_FLAG (tmp, FLAG_WAS_WIZ);
309 if (nrof != tmp_nrof)
310 {
311 object *tmp2 = tmp;
312 tag_t tmp2_tag = tmp2->count;
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 /* Tell a client what happened rest of objects */
321 if (pl->type == PLAYER)
322 {
323 if (was_destroyed (tmp2, tmp2_tag))
324 esrv_del_item (pl->contr, tmp2_tag);
325 else
326 esrv_send_item (pl, tmp2);
327 }
328 }
329 else
330 {
331 /* If the object is in a container, send a delete to the client.
332 * - we are moving all the items from the container to elsewhere,
333 * so it needs to be deleted.
334 */
335 if (!QUERY_FLAG (tmp, FLAG_REMOVED))
336 {
337 if (tmp->env && pl->type == PLAYER)
338 esrv_del_item (pl->contr, tmp->count);
339 remove_ob (tmp); /* Unlink it */
340 }
341 }
342 if (QUERY_FLAG (tmp, FLAG_UNPAID))
343 (void) sprintf (buf, "%s will cost you %s.", query_name (tmp), query_cost_string (tmp, pl, F_BUY | F_SHOP));
344 else
345 (void) sprintf (buf, "You pick up the %s.", query_name (tmp));
346 new_draw_info (NDI_UNIQUE, 0, pl, buf);
347
348 tmp = insert_ob_in_ob (tmp, op);
349
350 /* All the stuff below deals with client/server code, and is only
351 * usable by players
352 */
353 if (pl->type != PLAYER)
354 return;
355
356 esrv_send_item (pl, tmp);
357 /* These are needed to update the weight for the container we
358 * are putting the object in.
359 */
360 if (op != pl)
361 {
362 esrv_update_item (UPD_WEIGHT, pl, op);
363 esrv_send_item (pl, pl);
364 }
365
366 /* Update the container the object was in */
367 if (env && env != pl && env != op)
368 esrv_update_item (UPD_WEIGHT, pl, env);
369 }
370
371
372 void
373 pick_up (object *op, object *alt)
374
375 /* modified slightly to allow monsters use this -b.t. 5-31-95 */
376 {
377 int need_fix_tmp = 0;
378 object *tmp = NULL;
379 mapstruct *tmp_map = NULL;
380 int count;
381 tag_t tag;
382
383 /* Decide which object to pick. */
384 if (alt)
385 {
386 if (!can_pick (op, alt))
387 {
388 new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up the %s.", &alt->name);
389 goto leave;
390 }
391 tmp = alt;
392 }
393 else
394 {
395 if (op->below == NULL || !can_pick (op, op->below))
396 {
397 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up here.");
398 goto leave;
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 if (!alt)
440 alt = op; /* No free containers */
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 tag = tmp->count;
461 pick_up_object (op, alt, tmp, count);
462 if (was_destroyed (tmp, tag) || tmp->env)
463 need_fix_tmp = 0;
464 if (op->type == PLAYER)
465 op->contr->count = 0;
466 goto leave;
467
468 leave:
469 if (need_fix_tmp)
470 fix_stopped_item (tmp, tmp_map, op);
471 }
472
473
474 /* This takes (picks up) and item. op is the player
475 * who issued the command. params is a string to
476 * match against the item name. Basically, always
477 * returns zero, but that should be improved.
478 */
479 int
480 command_take (object *op, char *params)
481 {
482 object *tmp, *next;
483
484 if (op->container)
485 tmp = op->container->inv;
486 else
487 {
488 tmp = op->above;
489 if (tmp)
490 while (tmp->above)
491 {
492 tmp = tmp->above;
493 }
494 if (!tmp)
495 tmp = op->below;
496 }
497
498 if (tmp == NULL)
499 {
500 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to take!");
501 return 0;
502 }
503
504 /* Makes processing easier */
505 if (params && *params == '\0')
506 params = NULL;
507
508 while (tmp)
509 {
510 next = tmp->below;
511
512 if (tmp->invisible)
513 {
514 tmp = next;
515 continue;
516 }
517 /* This following two if and else if could be merged into line
518 * but that probably will make it more difficult to read, and
519 * not make it any more efficient
520 */
521 if (params && item_matched_string (op, tmp, params))
522 {
523 pick_up (op, tmp);
524 }
525 else if (can_pick (op, tmp) && !params)
526 {
527 pick_up (op, tmp);
528 break;
529 }
530 tmp = next;
531 /* Might as well just skip over the player immediately -
532 * we know it can't be picked up
533 */
534 if (tmp == op)
535 tmp = tmp->below;
536 }
537 if (!params && !tmp)
538 {
539 for (tmp = op->below; tmp != NULL; tmp = tmp->next)
540 if (!tmp->invisible)
541 {
542 char buf[MAX_BUF];
543
544 sprintf (buf, "You can't pick up a %s.", &tmp->name);
545 new_draw_info (NDI_UNIQUE, 0, op, buf);
546 break;
547 }
548 if (!tmp)
549 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up.");
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 tag_t tmp_tag, tmp2_tag;
566 object *tmp2, *sack2;
567 char buf[MAX_BUF];
568
569 if (sack == tmp)
570 return; /* Can't put an object in itself */
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 if (tmp->type == CONTAINER && tmp->inv)
577 {
578
579 /* Eneq(@csd.uu.se): If the object to be dropped is a container
580 * we instead move the contents of that container into the active
581 * container, this is only done if the object has something in it.
582 */
583 sack2 = tmp;
584 new_draw_info_format (NDI_UNIQUE, 0, op, "You move the items from %s into %s.", query_name (tmp), query_name (sack));
585 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp)
586 {
587 tmp = tmp2->below;
588 if ((sack->type == CONTAINER && sack_can_hold (op, op->container, tmp2, tmp2->nrof)))
589 {
590 put_object_in_sack (op, sack, tmp2, 0);
591 }
592 else
593 {
594 sprintf (buf, "Your %s fills up.", query_name (sack));
595 new_draw_info (NDI_UNIQUE, 0, op, buf);
596 break;
597 }
598 }
599 esrv_update_item (UPD_WEIGHT, op, sack2);
600 return;
601 }
602
603 /* Don't worry about this for containers - our caller should have
604 * already checked this.
605 */
606 if ((sack->type == CONTAINER) && !sack_can_hold (op, sack, tmp, (nrof ? nrof : tmp->nrof)))
607 return;
608
609 if (QUERY_FLAG (tmp, FLAG_APPLIED))
610 {
611 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
612 return;
613 }
614
615 /* we want to put some portion of the item into the container */
616 if (nrof && tmp->nrof != nrof)
617 {
618 object *tmp2 = tmp;
619
620 tmp2_tag = tmp2->count;
621 tmp = get_split_ob (tmp, nrof);
622
623 if (!tmp)
624 {
625 new_draw_info (NDI_UNIQUE, 0, op, errmsg);
626 return;
627 }
628 /* Tell a client what happened other objects */
629 if (was_destroyed (tmp2, tmp2_tag))
630 esrv_del_item (op->contr, tmp2_tag);
631 else /* this can proably be replaced with an update */
632 esrv_send_item (op, tmp2);
633 }
634 else
635 remove_ob (tmp);
636
637 new_draw_info_format (NDI_UNIQUE, 0, op, "You put the %s in %s.", query_name (tmp), query_name (sack));
638 tmp_tag = tmp->count;
639 tmp2 = insert_ob_in_ob (tmp, sack);
640 fix_player (op); /* 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_tag);
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 /* We are only dropping some of the items. We split the current objec
675 * off
676 */
677 if (nrof && tmp->nrof != nrof)
678 {
679 object *tmp2 = tmp;
680 tag_t tmp2_tag = tmp2->count;
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 (was_destroyed (tmp2, tmp2_tag))
694 esrv_del_item (op->contr, tmp2_tag);
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 if (!op->contr->mark)
1050 {
1051
1052 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1053 return NULL;
1054 }
1055 /* This may seem like overkill, but we need to make sure that they
1056 * player hasn't dropped the item. We use count on the off chance that
1057 * an item got reincarnated at some point.
1058 */
1059 for (tmp = op->inv; tmp; tmp = tmp->below)
1060 {
1061 if (tmp->invisible)
1062 continue;
1063 if (tmp == op->contr->mark)
1064 {
1065 if (tmp->count == op->contr->mark_count)
1066 return tmp;
1067 else
1068 {
1069 op->contr->mark = NULL;
1070 op->contr->mark_count = 0;
1071
1072 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1073 return NULL;
1074 }
1075 }
1076 }
1077 return NULL;
1078 }
1079
1080
1081 /* op should be a player, params is any params.
1082 * If no params given, we print out the currently marked object.
1083 * otherwise, try to find a matching object - try best match first.
1084 */
1085 int
1086 command_mark (object *op, char *params)
1087 {
1088 if (!op->contr)
1089 return 1;
1090 if (!params)
1091 {
1092 object *mark = find_marked_object (op);
1093
1094 if (!mark)
1095 new_draw_info (NDI_UNIQUE, 0, op, "You have no marked object.");
1096 else
1097 new_draw_info_format (NDI_UNIQUE, 0, op, "%s is marked.", query_name (mark));
1098 }
1099 else
1100 {
1101 object *mark1 = find_best_object_match (op, params);
1102
1103 if (!mark1)
1104 {
1105 new_draw_info_format (NDI_UNIQUE, 0, op, "Could not find an object that matches %s", params);
1106 return 1;
1107 }
1108 else
1109 {
1110 op->contr->mark = mark1;
1111 op->contr->mark_count = mark1->count;
1112 new_draw_info_format (NDI_UNIQUE, 0, op, "Marked item %s", query_name (mark1));
1113 return 0;
1114 }
1115 }
1116 return 0; /*shouldnt get here */
1117 }
1118
1119
1120 /* op is the player
1121 * tmp is the monster being examined.
1122 */
1123 void
1124 examine_monster (object *op, object *tmp)
1125 {
1126 object *mon = tmp->head ? tmp->head : tmp;
1127
1128 if (QUERY_FLAG (mon, FLAG_UNDEAD))
1129 new_draw_info (NDI_UNIQUE, 0, op, "It is an undead force.");
1130 if (mon->level > op->level)
1131 new_draw_info (NDI_UNIQUE, 0, op, "It is likely more powerful than you.");
1132 else if (mon->level < op->level)
1133 new_draw_info (NDI_UNIQUE, 0, op, "It is likely less powerful than you.");
1134 else
1135 new_draw_info (NDI_UNIQUE, 0, op, "It is probably as powerful as you.");
1136 if (mon->attacktype & AT_ACID)
1137 new_draw_info (NDI_UNIQUE, 0, op, "You seem to smell an acrid odor.");
1138
1139 /* Anyone know why this used to use the clone value instead of the
1140 * maxhp field? This seems that it should give more accurate results.
1141 */
1142 switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
1143 { /* From 1-4 */
1144 case 1:
1145 new_draw_info (NDI_UNIQUE, 0, op, "It is in a bad shape.");
1146 break;
1147 case 2:
1148 new_draw_info (NDI_UNIQUE, 0, op, "It is hurt.");
1149 break;
1150 case 3:
1151 new_draw_info (NDI_UNIQUE, 0, op, "It is somewhat hurt.");
1152 break;
1153 case 4:
1154 new_draw_info (NDI_UNIQUE, 0, op, "It is in excellent shape.");
1155 break;
1156 }
1157 if (present_in_ob (POISONING, mon) != NULL)
1158 new_draw_info (NDI_UNIQUE, 0, op, "It looks very ill.");
1159 }
1160
1161
1162 /* tmp is the object being described, pl is who is examing it. */
1163 char *
1164 long_desc (object *tmp, object *pl)
1165 {
1166 static char buf[VERY_BIG_BUF];
1167 char *cp;
1168
1169 if (tmp == NULL)
1170 return "";
1171
1172 buf[0] = '\0';
1173 switch (tmp->type)
1174 {
1175 case RING:
1176 case SKILL:
1177 case WEAPON:
1178 case ARMOUR:
1179 case BRACERS:
1180 case HELMET:
1181 case SHIELD:
1182 case BOOTS:
1183 case GLOVES:
1184 case AMULET:
1185 case GIRDLE:
1186 case BOW:
1187 case ARROW:
1188 case CLOAK:
1189 case FOOD:
1190 case DRINK:
1191 case FLESH:
1192 case SKILL_TOOL:
1193 case POWER_CRYSTAL:
1194 if (*(cp = describe_item (tmp, pl)) != '\0')
1195 {
1196 int len;
1197
1198 strncpy (buf, query_name (tmp), VERY_BIG_BUF - 1);
1199 buf[VERY_BIG_BUF - 1] = 0;
1200 len = strlen (buf);
1201 if (len < VERY_BIG_BUF - 5)
1202 {
1203 /* Since we know the length, we save a few cpu cycles by using
1204 * it instead of calling strcat */
1205 strcpy (buf + len, " ");
1206 len++;
1207 strncpy (buf + len, cp, VERY_BIG_BUF - len - 1);
1208 buf[VERY_BIG_BUF - 1] = 0;
1209 }
1210 }
1211 }
1212 if (buf[0] == '\0')
1213 {
1214 strncpy (buf, query_name (tmp), VERY_BIG_BUF - 1);
1215 buf[VERY_BIG_BUF - 1] = 0;
1216 }
1217
1218 return buf;
1219 }
1220
1221 void
1222 examine (object *op, object *tmp)
1223 {
1224 char buf[VERY_BIG_BUF];
1225 int i;
1226
1227 if (tmp == NULL || tmp->type == CLOSE_CON)
1228 return;
1229
1230 strcpy (buf, "That is ");
1231 strncat (buf, long_desc (tmp, op), VERY_BIG_BUF - strlen (buf) - 1);
1232 buf[VERY_BIG_BUF - 1] = 0;
1233
1234 new_draw_info (NDI_UNIQUE, 0, op, buf);
1235 buf[0] = '\0';
1236
1237 if (tmp->custom_name)
1238 {
1239 strcpy (buf, "You call it ");
1240 strncat (buf, tmp->custom_name, VERY_BIG_BUF - strlen (buf) - 1);
1241 buf[VERY_BIG_BUF - 1] = 0;
1242 new_draw_info (NDI_UNIQUE, 0, op, buf);
1243 buf[0] = '\0';
1244 }
1245
1246 switch (tmp->type)
1247 {
1248 case SPELLBOOK:
1249 if (QUERY_FLAG (tmp, FLAG_IDENTIFIED) && tmp->inv)
1250 {
1251 sprintf (buf, "%s is a %s level %s spell", &tmp->inv->name, get_levelnumber (tmp->inv->level), &tmp->inv->skill);
1252 }
1253 break;
1254
1255 case BOOK:
1256 if (tmp->msg != NULL)
1257 strcpy (buf, "Something is written in it.");
1258 break;
1259
1260 case CONTAINER:
1261 if (tmp->race != NULL)
1262 {
1263 if (tmp->weight_limit && tmp->stats.Str < 100)
1264 sprintf (buf, "It can hold only %s and its weight limit is %.1f kg.",
1265 &tmp->race, tmp->weight_limit / (10.0 * (100 - tmp->stats.Str)));
1266 else
1267 sprintf (buf, "It can hold only %s.", &tmp->race);
1268 }
1269 else if (tmp->weight_limit && tmp->stats.Str < 100)
1270 sprintf (buf, "Its weight limit is %.1f kg.", tmp->weight_limit / (10.0 * (100 - tmp->stats.Str)));
1271 break;
1272
1273 case WAND:
1274 if (QUERY_FLAG (tmp, FLAG_IDENTIFIED))
1275 sprintf (buf, "It has %d charges left.", tmp->stats.food);
1276 break;
1277 }
1278
1279 if (buf[0] != '\0')
1280 new_draw_info (NDI_UNIQUE, 0, op, buf);
1281
1282 if (tmp->materialname != NULL && !tmp->msg)
1283 {
1284 sprintf (buf, "It is made of: %s.", &tmp->materialname);
1285 new_draw_info (NDI_UNIQUE, 0, op, buf);
1286 }
1287 /* Where to wear this item */
1288 for (i = 0; i < NUM_BODY_LOCATIONS; i++)
1289 {
1290 if (tmp->body_info[i] < -1)
1291 {
1292 if (op->body_info[i])
1293 new_draw_info_format (NDI_UNIQUE, 0, op, "It goes %s (%d)", body_locations[i].use_name, -tmp->body_info[i]);
1294 else
1295 new_draw_info_format (NDI_UNIQUE, 0, op, "It goes %s", body_locations[i].nonuse_name);
1296 }
1297 else if (tmp->body_info[i])
1298 {
1299 if (op->body_info[i])
1300 new_draw_info_format (NDI_UNIQUE, 0, op, "It goes %s", body_locations[i].use_name);
1301 else
1302 new_draw_info_format (NDI_UNIQUE, 0, op, "It goes %s", body_locations[i].nonuse_name);
1303 }
1304 }
1305
1306 if (tmp->weight)
1307 {
1308 sprintf (buf, tmp->nrof > 1 ? "They weigh %3.3f kg." : "It weighs %3.3f kg.", tmp->weight * (tmp->nrof ? tmp->nrof : 1) / 1000.0);
1309 new_draw_info (NDI_UNIQUE, 0, op, buf);
1310 }
1311
1312 if (tmp->value && !QUERY_FLAG (tmp, FLAG_STARTEQUIP) && !QUERY_FLAG (tmp, FLAG_NO_PICK))
1313 {
1314 sprintf (buf, "You reckon %s worth %s.", tmp->nrof > 1 ? "they are" : "it is", query_cost_string (tmp, op, F_TRUE | F_APPROX));
1315 new_draw_info (NDI_UNIQUE, 0, op, buf);
1316 if (is_in_shop (op))
1317 {
1318 if (QUERY_FLAG (tmp, FLAG_UNPAID))
1319 sprintf (buf, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", query_cost_string (tmp, op, F_BUY | F_SHOP));
1320 else
1321 sprintf (buf, "You are offered %s for %s.", query_cost_string (tmp, op, F_SELL + F_SHOP), tmp->nrof > 1 ? "them" : "it");
1322 new_draw_info (NDI_UNIQUE, 0, op, buf);
1323 }
1324 }
1325
1326 if (QUERY_FLAG (tmp, FLAG_MONSTER))
1327 examine_monster (op, tmp);
1328
1329 /* Is this item buildable? */
1330 if (QUERY_FLAG (tmp, FLAG_IS_BUILDABLE))
1331 new_draw_info (NDI_UNIQUE, 0, op, "This is a buildable item.");
1332
1333 /* Does the object have a message? Don't show message for all object
1334 * types - especially if the first entry is a match
1335 */
1336 if (tmp->msg && tmp->type != EXIT && tmp->type != BOOK && tmp->type != CORPSE && !tmp->move_on && strncasecmp (tmp->msg, "@match", 7))
1337 {
1338
1339 /* This is just a hack so when identifying the items, we print
1340 * out the extra message
1341 */
1342 if (need_identify (tmp) && QUERY_FLAG (tmp, FLAG_IDENTIFIED))
1343 new_draw_info (NDI_UNIQUE, 0, op, "The object has a story:");
1344
1345 new_draw_info (NDI_UNIQUE, 0, op, tmp->msg);
1346 }
1347 new_draw_info (NDI_UNIQUE, 0, op, " "); /* Blank line */
1348 }
1349
1350 /*
1351 * inventory prints object's inventory. If inv==NULL then print player's
1352 * inventory.
1353 * [ Only items which are applied are showed. Tero.Haatanen@lut.fi ]
1354 */
1355 void
1356 inventory (object *op, object *inv)
1357 {
1358 object *tmp;
1359 char *in;
1360 int items = 0, length;
1361
1362 if (inv == NULL && op == NULL)
1363 {
1364 new_draw_info (NDI_UNIQUE, 0, op, "Inventory of what object?");
1365 return;
1366 }
1367 tmp = inv ? inv->inv : op->inv;
1368
1369 while (tmp)
1370 {
1371 if ((!tmp->invisible &&
1372 (inv == NULL || inv->type == CONTAINER || QUERY_FLAG (tmp, FLAG_APPLIED))) || (!op || QUERY_FLAG (op, FLAG_WIZ)))
1373 items++;
1374 tmp = tmp->below;
1375 }
1376 if (inv == NULL)
1377 { /* player's inventory */
1378 if (items == 0)
1379 {
1380 new_draw_info (NDI_UNIQUE, 0, op, "You carry nothing.");
1381 return;
1382 }
1383 else
1384 {
1385 length = 28;
1386 in = "";
1387 if (op)
1388 clear_win_info (op);
1389 new_draw_info (NDI_UNIQUE, 0, op, "Inventory:");
1390 }
1391 }
1392 else
1393 {
1394 if (items == 0)
1395 return;
1396 else
1397 {
1398 length = 28;
1399 in = " ";
1400 }
1401 }
1402 for (tmp = inv ? inv->inv : op->inv; tmp; tmp = tmp->below)
1403 {
1404 if ((!op || !QUERY_FLAG (op, FLAG_WIZ)) && (tmp->invisible || (inv && inv->type != CONTAINER && !QUERY_FLAG (tmp, FLAG_APPLIED))))
1405 continue;
1406 if ((!op || QUERY_FLAG (op, FLAG_WIZ)))
1407 new_draw_info_format (NDI_UNIQUE, 0, op, "%s- %-*.*s (%5d) %-8s", in, length, length,
1408 query_name (tmp), tmp->count, query_weight (tmp));
1409 else
1410 new_draw_info_format (NDI_UNIQUE, 0, op, "%s- %-*.*s %-8s", in, length + 8, length + 8, query_name (tmp), query_weight (tmp));
1411 }
1412 if (!inv && op)
1413 {
1414 new_draw_info_format (NDI_UNIQUE, 0, op, "%-*s %-8s", 41, "Total weight :", query_weight (op));
1415 }
1416 }
1417
1418 static void
1419 display_new_pickup (object *op)
1420 {
1421 int i = op->contr->mode;
1422
1423 if (!(i & PU_NEWMODE))
1424 return;
1425
1426 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NEWMODE", i & PU_NEWMODE ? 1 : 0);
1427 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DEBUG", i & PU_DEBUG ? 1 : 0);
1428 new_draw_info_format (NDI_UNIQUE, 0, op, "%d INHIBIT", i & PU_INHIBIT ? 1 : 0);
1429 new_draw_info_format (NDI_UNIQUE, 0, op, "%d STOP", i & PU_STOP ? 1 : 0);
1430
1431 new_draw_info_format (NDI_UNIQUE, 0, op, "%d <= x pickup weight/value RATIO (0==off)", (i & PU_RATIO) * 5);
1432
1433 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FOOD", i & PU_FOOD ? 1 : 0);
1434 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DRINK", i & PU_DRINK ? 1 : 0);
1435 new_draw_info_format (NDI_UNIQUE, 0, op, "%d VALUABLES", i & PU_VALUABLES ? 1 : 0);
1436
1437 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOW", i & PU_BOW ? 1 : 0);
1438 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARROW", i & PU_ARROW ? 1 : 0);
1439
1440 new_draw_info_format (NDI_UNIQUE, 0, op, "%d HELMET", i & PU_HELMET ? 1 : 0);
1441 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SHIELD", i & PU_SHIELD ? 1 : 0);
1442 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARMOUR", i & PU_ARMOUR ? 1 : 0);
1443
1444 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOOTS", i & PU_BOOTS ? 1 : 0);
1445 new_draw_info_format (NDI_UNIQUE, 0, op, "%d GLOVES", i & PU_GLOVES ? 1 : 0);
1446 new_draw_info_format (NDI_UNIQUE, 0, op, "%d CLOAK", i & PU_CLOAK ? 1 : 0);
1447 new_draw_info_format (NDI_UNIQUE, 0, op, "%d KEY", i & PU_KEY ? 1 : 0);
1448
1449 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MISSILEWEAPON", i & PU_MISSILEWEAPON ? 1 : 0);
1450 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ALLWEAPON", i & PU_ALLWEAPON ? 1 : 0);
1451 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICAL", i & PU_MAGICAL ? 1 : 0);
1452 new_draw_info_format (NDI_UNIQUE, 0, op, "%d POTION", i & PU_POTION ? 1 : 0);
1453
1454 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SPELLBOOK", i & PU_SPELLBOOK ? 1 : 0);
1455 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SKILLSCROLL", i & PU_SKILLSCROLL ? 1 : 0);
1456 new_draw_info_format (NDI_UNIQUE, 0, op, "%d READABLES", i & PU_READABLES ? 1 : 0);
1457 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICDEVICE", i & PU_MAGIC_DEVICE ? 1 : 0);
1458
1459 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NOT CURSED", i & PU_NOT_CURSED ? 1 : 0);
1460
1461 new_draw_info_format (NDI_UNIQUE, 0, op, "%d JEWELS", i & PU_JEWELS ? 1 : 0);
1462
1463 new_draw_info_format (NDI_UNIQUE, 0, op, "");
1464 }
1465
1466 int
1467 command_pickup (object *op, char *params)
1468 {
1469 uint32 i;
1470 static const char *names[] = {
1471 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1472 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1473 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed", "jewels", NULL
1474 };
1475 static uint32 modes[] = {
1476 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1477 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1478 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED, PU_JEWELS, 0
1479 };
1480
1481 if (!params)
1482 {
1483 /* if the new mode is used, just print the settings */
1484 if (op->contr->mode & PU_NEWMODE)
1485 {
1486 display_new_pickup (op);
1487 return 1;
1488 }
1489 if (1)
1490 LOG (llevDebug, "command_pickup: !params\n");
1491 set_pickup_mode (op, (op->contr->mode > 6) ? 0 : op->contr->mode + 1);
1492 return 0;
1493 }
1494
1495 while (*params == ' ' && *params)
1496 params++;
1497
1498 if (*params == '+' || *params == '-')
1499 {
1500 int mode;
1501
1502 for (mode = 0; names[mode]; mode++)
1503 {
1504 if (!strcmp (names[mode], params + 1))
1505 {
1506 i = op->contr->mode;
1507 if (!(i & PU_NEWMODE))
1508 i = PU_NEWMODE;
1509 if (*params == '+')
1510 i = i | modes[mode];
1511 else
1512 i = i & ~modes[mode];
1513 op->contr->mode = i;
1514 display_new_pickup (op);
1515 return 1;
1516 }
1517 }
1518 new_draw_info_format (NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params);
1519 return 1;
1520 }
1521
1522 if (sscanf (params, "%u", &i) != 1)
1523 {
1524 if (1)
1525 LOG (llevDebug, "command_pickup: params==NULL\n");
1526 new_draw_info (NDI_UNIQUE, 0, op, "Usage: pickup <0-7> or <value_density> .");
1527 return 1;
1528 }
1529 set_pickup_mode (op, i);
1530
1531 return 1;
1532 }
1533
1534 void
1535 set_pickup_mode (object *op, int i)
1536 {
1537 switch (op->contr->mode = i)
1538 {
1539 case 0:
1540 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Don't pick up.");
1541 break;
1542 case 1:
1543 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up one item.");
1544 break;
1545 case 2:
1546 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up one item and stop.");
1547 break;
1548 case 3:
1549 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Stop before picking up.");
1550 break;
1551 case 4:
1552 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all items.");
1553 break;
1554 case 5:
1555 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all items and stop.");
1556 break;
1557 case 6:
1558 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all magic items.");
1559 break;
1560 case 7:
1561 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all coins and gems");
1562 break;
1563 }
1564 }
1565
1566 int
1567 command_search_items (object *op, char *params)
1568 {
1569 char buf[MAX_BUF];
1570
1571 if (settings.search_items == FALSE)
1572 return 1;
1573
1574 if (params == NULL)
1575 {
1576 if (op->contr->search_str[0] == '\0')
1577 {
1578 new_draw_info (NDI_UNIQUE, 0, op, "Example: search magic+1");
1579 new_draw_info (NDI_UNIQUE, 0, op, "Would automatically pick up all");
1580 new_draw_info (NDI_UNIQUE, 0, op, "items containing the word 'magic+1'.");
1581 return 1;
1582 }
1583 op->contr->search_str[0] = '\0';
1584 new_draw_info (NDI_UNIQUE, 0, op, "Search mode turned off.");
1585 fix_player (op);
1586 return 1;
1587 }
1588 if ((int) strlen (params) >= MAX_BUF)
1589 {
1590 new_draw_info (NDI_UNIQUE, 0, op, "Search string too long.");
1591 return 1;
1592 }
1593 strcpy (op->contr->search_str, params);
1594 sprintf (buf, "Searching for '%s'.", op->contr->search_str);
1595 new_draw_info (NDI_UNIQUE, 0, op, buf);
1596 fix_player (op);
1597 return 1;
1598 }
1599
1600 /*
1601 * Changing the custom name of an item
1602 *
1603 * Syntax is: rename <what object> to <new name>
1604 * if '<what object>' is omitted, marked object is used
1605 * if 'to <new name>' is omitted, custom name is cleared
1606 *
1607 * Names are considered for all purpose having a length <=127 (max length sent to client
1608 * by server) */
1609
1610 int
1611 command_rename_item (object *op, char *params)
1612 {
1613 char buf[VERY_BIG_BUF];
1614 int itemnumber;
1615 object *item = NULL;
1616 char *closebrace;
1617 size_t counter;
1618
1619 if (params)
1620 {
1621 /* Let's skip white spaces */
1622 while (' ' == *params)
1623 params++;
1624
1625 /* Checking the first part */
1626 if ((itemnumber = atoi (params)) != 0)
1627 {
1628 for (item = op->inv; item && ((item->count != (tag_t) itemnumber) || item->invisible); item = item->below);
1629 if (!item)
1630 {
1631 new_draw_info (NDI_UNIQUE, 0, op, "Tried to rename an invalid item.");
1632 return 1;
1633 }
1634 while (isdigit (*params) || ' ' == *params)
1635 params++;
1636 }
1637 else if ('<' == *params)
1638 {
1639 /* Got old name, let's get it & find appropriate matching item */
1640 closebrace = strchr (params, '>');
1641 if (!closebrace)
1642 {
1643 new_draw_info (NDI_UNIQUE, 0, op, "Syntax error!");
1644 return 1;
1645 }
1646 /* Sanity check for buffer overruns */
1647 if ((closebrace - params) > 127)
1648 {
1649 new_draw_info (NDI_UNIQUE, 0, op, "Old name too long (up to 127 characters allowed)!");
1650 return 1;
1651 }
1652 /* Copy the old name */
1653 strncpy (buf, params + 1, closebrace - params - 1);
1654 buf[closebrace - params - 1] = '\0';
1655
1656 /* Find best matching item */
1657 item = find_best_object_match (op, buf);
1658 if (!item)
1659 {
1660 new_draw_info (NDI_UNIQUE, 0, op, "Could not find a matching item to rename.");
1661 return 1;
1662 }
1663
1664 /* Now need to move pointer to just after > */
1665 params = closebrace + 1;
1666 while (' ' == *params)
1667 params++;
1668
1669 }
1670 else
1671 {
1672 /* Use marked item */
1673 item = find_marked_object (op);
1674 if (!item)
1675 {
1676 new_draw_info (NDI_UNIQUE, 0, op, "No marked item to rename.");
1677 return 1;
1678 }
1679 }
1680
1681 /* Now let's find the new name */
1682 if (!strncmp (params, "to ", 3))
1683 {
1684 params += 3;
1685 while (' ' == *params)
1686 params++;
1687 if ('<' != *params)
1688 {
1689 new_draw_info (NDI_UNIQUE, 0, op, "Syntax error, expecting < at start of new name!");
1690 return 1;
1691 }
1692 closebrace = strchr (params + 1, '>');
1693 if (!closebrace)
1694 {
1695 new_draw_info (NDI_UNIQUE, 0, op, "Syntax error, expecting > at end of new name!");
1696 return 1;
1697 }
1698
1699 /* Sanity check for buffer overruns */
1700 if ((closebrace - params) > 127)
1701 {
1702 new_draw_info (NDI_UNIQUE, 0, op, "New name too long (up to 127 characters allowed)!");
1703 return 1;
1704 }
1705
1706 /* Copy the new name */
1707 strncpy (buf, params + 1, closebrace - params - 1);
1708 buf[closebrace - params - 1] = '\0';
1709
1710 /* Let's check it for weird characters */
1711 for (counter = 0; counter < strlen (buf); counter++)
1712 {
1713 if (isalnum (buf[counter]))
1714 continue;
1715 if (' ' == buf[counter])
1716 continue;
1717 if ('\'' == buf[counter])
1718 continue;
1719 if ('+' == buf[counter])
1720 continue;
1721 if ('_' == buf[counter])
1722 continue;
1723 if ('-' == buf[counter])
1724 continue;
1725
1726 /* If we come here, then the name contains an invalid character...
1727 tell the player & exit */
1728 new_draw_info (NDI_UNIQUE, 0, op, "Invalid new name!");
1729 return 1;
1730 }
1731
1732 }
1733 else
1734 {
1735 /* If param contains something, then syntax error... */
1736 if (strlen (params))
1737 {
1738 new_draw_info (NDI_UNIQUE, 0, op, "Syntax error, expected 'to <' after old name!");
1739 return 1;
1740 }
1741 /* New name is empty */
1742 buf[0] = '\0';
1743 }
1744 }
1745 else
1746 {
1747 /* Last case: params==NULL */
1748 item = find_marked_object (op);
1749 if (!item)
1750 {
1751 new_draw_info (NDI_UNIQUE, 0, op, "No marked item to rename.");
1752 return 1;
1753 }
1754 buf[0] = '\0';
1755 }
1756
1757 if (QUERY_FLAG (item, FLAG_UNPAID))
1758 {
1759 new_draw_info (NDI_UNIQUE, 0, op, "You can't rename an unpaid item! You should pay for it first.");
1760 return 1;
1761 }
1762
1763 /* Coming here, everything is fine... */
1764 if (!strlen (buf))
1765 {
1766 /* Clear custom name */
1767 if (item->custom_name)
1768 {
1769 item->custom_name = 0;
1770
1771 new_draw_info_format (NDI_UNIQUE, 0, op, "You stop calling your %s with weird names.",
1772 query_base_name (item, item->nrof > 1 ? 1 : 0));
1773 esrv_update_item (UPD_NAME, op, item);
1774 }
1775 else
1776 {
1777 new_draw_info (NDI_UNIQUE, 0, op, "This item has no custom name.");
1778 }
1779 }
1780 else
1781 {
1782 /* Set custom name */
1783 item->custom_name = buf;
1784
1785 new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s will now be called %s.", query_base_name (item, item->nrof > 1 ? 1 : 0), buf);
1786 esrv_update_item (UPD_NAME, op, item);
1787 }
1788
1789 return 1;
1790 }