ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.65
Committed: Sun Apr 20 18:48:51 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.64: +3 -1 lines
Log Message:
*** empty log message ***

File Contents

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