ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.129
Committed: Mon Oct 29 23:55:55 2012 UTC (11 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.128: +5 -5 lines
Log Message:
trailing space removal

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992 Frank Tore Johansen
7 *
8 * Deliantra is free software: you can redistribute it and/or modify it under
9 * the terms of the Affero GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * 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 Affero GNU General Public License
19 * and the GNU General Public License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 *
22 * The authors can be reached via e-mail to <support@deliantra.net>
23 */
24
25 /*
26 * Object (handling) commands
27 */
28
29 #include <global.h>
30 #include <skills.h>
31 #include <sproto.h>
32 #include <living.h>
33
34 /*
35 * Object id parsing functions
36 */
37
38 #define ADD_ITEM(NEW,COUNT)\
39 if(!first) {\
40 first = new objectlink;\
41 last=first;\
42 } else {\
43 last->next = new objectlink;\
44 last=last->next;\
45 }\
46 last->next=0;\
47 last->ob=(NEW);\
48 last->id=(COUNT);
49
50 /**
51 * Search the inventory of 'pl' for what matches best with params.
52 * we use item_matched_string above - this gives us consistent behaviour
53 * between many commands. Return the best match, or NULL if no match.
54 * aflag is used with apply -u , and apply -a to
55 * only unapply applied, or apply unapplied objects
56 **/
57 static object *
58 find_best_apply_object_match (object *pl, const char *params, enum apply_flag aflag)
59 {
60 object *best = 0;
61 int match_val = 0;
62
63 for (object *tmp = pl->inv; tmp; tmp = tmp->below)
64 {
65 if (tmp->invisible)
66 continue;
67
68 int tmpmatch = item_matched_string (pl, tmp, params);
69
70 if (tmpmatch > match_val)
71 {
72 if ((aflag == AP_APPLY) && (tmp->flag [FLAG_APPLIED]))
73 continue;
74
75 if ((aflag == AP_UNAPPLY) && (!tmp->flag [FLAG_APPLIED]))
76 continue;
77
78 match_val = tmpmatch;
79 best = tmp;
80 }
81 }
82
83 return best;
84 }
85
86 /**
87 * Shortcut to find_best_apply_object_match(pl, params, AF_NULL);
88 **/
89 object *
90 find_best_object_match (object *pl, const char *params)
91 {
92 return find_best_apply_object_match (pl, params, AP_TOGGLE);
93 }
94
95 static bool
96 can_split (object *pl, object *&op, sint32 nrof)
97 {
98 if (object *tmp = op->split (nrof ? nrof : op->number_of ()))
99 {
100 op = tmp;
101 return true;
102 }
103 else
104 {
105 if (op->nrof > 1)
106 new_draw_info_format (NDI_UNIQUE, 0, pl, "There are only %d %s.", op->nrof, &op->name_pl);
107 else
108 new_draw_info_format (NDI_UNIQUE, 0, pl, "There is only one %s.", &op->name);
109
110 return false;
111 }
112 }
113
114 int
115 command_uskill (object *pl, char *params)
116 {
117 if (!params)
118 {
119 new_draw_info (NDI_UNIQUE, 0, pl, "Usage: use_skill <skill name>");
120 return 0;
121 }
122
123 return use_skill (pl, params);
124 }
125
126 int
127 command_rskill (object *pl, char *params)
128 {
129 object *skill;
130
131 if (!params)
132 {
133 new_draw_info (NDI_UNIQUE, 0, pl, "Usage: ready_skill <skill name>");
134 return 0;
135 }
136
137 skill = find_skill_by_name_fuzzy (pl, params);
138
139 if (!skill)
140 {
141 new_draw_info_format (NDI_UNIQUE, 0, pl, "You have no knowledge of the %s skill.", params);
142 return 0;
143 }
144
145 pl->apply (skill);
146 return 1;
147 }
148
149 /* A little special because we do want to pass the full params along
150 * as it includes the object to throw.
151 */
152 int
153 command_throw (object *op, char *params)
154 {
155 if (object *skop = find_skill_by_name (op, shstr_throwing))
156 return do_skill (op, op, skop, op->facing, params);
157 else
158 new_draw_info (NDI_UNIQUE, 0, op, "You have no knowledge of the throwing skill.");
159
160 return 0;
161 }
162
163 int
164 command_apply (object *op, char *params)
165 {
166 if (!params)
167 {
168 player_apply_below (op);
169 return 0;
170 }
171 else
172 {
173 apply_flag aflag = (apply_flag)0;
174
175 while (*params == ' ')
176 params++;
177
178 if (!strncmp (params, "-a ", 3))
179 {
180 aflag = AP_APPLY;
181 params += 3;
182 }
183
184 if (!strncmp (params, "-u ", 3))
185 {
186 aflag = AP_UNAPPLY;
187 params += 3;
188 }
189
190 while (*params == ' ')
191 params++;
192
193 if (object *inv = find_best_apply_object_match (op, params, aflag))
194 op->apply (inv, aflag);
195 else
196 op->failmsgf ("Could not find any match to the %s.", params);
197 }
198
199 return 0;
200 }
201
202 /*
203 * Check if an item op can be put into a sack. If pl exists then tell
204 * a player the reason of failure.
205 * returns 1 if it will fit, 0 if it will not. nrof is the number of
206 * objects (op) we want to put in. We specify it separately instead of
207 * using op->nrof because often times, a player may have specified a
208 * certain number of objects to drop, so we can pass that number, and
209 * not need to use split_ob and stuff.
210 */
211 int
212 sack_can_hold (object *pl, object *sack, object *op, uint32 nrof)
213 {
214 if (!sack->flag [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
220 if (sack == op)
221 {
222 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the %s into itself.", query_name (sack));
223 return 0;
224 }
225
226 if (sack->race && (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type)))
227 {
228 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can only put objects of type '%s' into the %s.", &sack->race, query_name (sack));
229 return 0;
230 }
231
232 if (op->type == SPECIAL_KEY && sack->slaying && op->slaying)
233 {
234 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't put the key into %s.", query_name (sack));
235 return 0;
236 }
237
238 if (sack->weight_limit && (sint32) (sack->carrying + (nrof ? nrof : 1) *
239 (op->weight + (op->type == CONTAINER ? (op->carrying * op->stats.Str) : 0))
240 * (100 - sack->stats.Str) / 100) > sack->weight_limit)
241 {
242 new_draw_info_format (NDI_UNIQUE, 0, pl, "That won't fit in the %s!", query_name (sack));
243 return 0;
244 }
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 uint32 weight, effective_weight_limit;
261 int tmp_nrof = tmp->number_of ();
262
263 /* IF the player is flying & trying to take the item out of a container
264 * that is in his inventory, let him. tmp->env points to the container
265 * (sack, luggage, etc), tmp->env->env then points to the player (nested
266 * containers not allowed as of now)
267 */
268 if ((pl->move_type & MOVE_FLYING) && !pl->flag [FLAG_WIZ] && tmp->in_player () != pl)
269 {
270 pl->failmsg ("You are levitating, you can't reach the ground! "
271 "H<You have to stop levitating first, if you can, either by using your levitation skill, "
272 "or waiting till the levitation effect wears off.>");
273 return;
274 }
275
276 if (tmp->flag [FLAG_NO_DROP])
277 return;
278
279 if (nrof > tmp_nrof || nrof <= 0)
280 nrof = tmp_nrof;
281
282 /* Figure out how much weight this object will add to the player */
283 weight = tmp->weight * nrof;
284 if (tmp->inv)
285 weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
286
287 effective_weight_limit = weight_limit [min (MAX_STAT, pl->stats.Str)];
288
289 if ((pl->weight + pl->carrying + weight) > effective_weight_limit)
290 {
291 new_draw_info (0, 0, pl, "That item is too heavy for you to pick up.");
292 return;
293 }
294
295 if (!can_split (pl, tmp, nrof))
296 return;
297
298 if (tmp->flag [FLAG_UNPAID])
299 {
300 tmp->flag.reset (FLAG_UNPAID);
301 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));
302 tmp->flag.set (FLAG_UNPAID);
303 }
304 else
305 new_draw_info_format (NDI_UNIQUE, 0, pl, "You pick up the %s.", query_name (tmp));
306
307 op->insert (tmp);
308 }
309
310 /* modified slightly to allow monsters use this -b.t. 5-31-95 */
311 void
312 pick_up (object *op, object *alt)
313 {
314 int need_fix_tmp = 0;
315 object *tmp = NULL;
316 maptile *tmp_map = NULL;
317 int count;
318
319 /* Decide which object to pick. */
320 if (alt)
321 {
322 if (!can_pick (op, alt))
323 {
324 new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up the %s.", &alt->name);
325 goto leave;
326 }
327
328 tmp = alt;
329 }
330 else
331 {
332 if (op->below == NULL || !can_pick (op, op->below))
333 {
334 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up here.");
335 goto leave;
336 }
337
338 tmp = op->below;
339 }
340
341 /* Try to catch it. */
342 tmp_map = tmp->map;
343 tmp = stop_item (tmp);
344 if (tmp == NULL)
345 goto leave;
346
347 need_fix_tmp = 1;
348 if (!can_pick (op, tmp))
349 goto leave;
350
351 if (op->type == PLAYER)
352 {
353 count = op->contr->count;
354 if (count == 0)
355 count = tmp->nrof;
356 }
357 else
358 count = tmp->nrof;
359
360 /* container is open, so use it */
361 if (tmp->flag [FLAG_STARTEQUIP])
362 alt = op;
363 else if ((alt = op->container_ ()))
364 {
365 if (alt != tmp->env && !sack_can_hold (op, alt, tmp, count))
366 goto leave;
367 }
368 else
369 { /* non container pickup */
370 for (alt = op->inv; alt; alt = alt->below)
371 if (alt->type == CONTAINER && alt->flag [FLAG_APPLIED] &&
372 alt->race && alt->race == tmp->race && sack_can_hold (NULL, alt, tmp, count))
373 break; /* perfect match */
374
375 if (!alt)
376 for (alt = op->inv; alt; alt = alt->below)
377 if (alt->type == CONTAINER && alt->flag [FLAG_APPLIED] && sack_can_hold (NULL, alt, tmp, count))
378 break; /* General container comes next */
379
380 if (!alt)
381 alt = op; /* No free containers */
382 }
383
384 if (tmp->env == alt)
385 {
386 /* here it could be possible to check rent,
387 * if someone wants to implement it
388 */
389 alt = op;
390 }
391
392 #ifdef PICKUP_DEBUG
393 LOG (llevDebug, "Pick_up(): %s picks %s (%d) and inserts it %s.\n", op->name, tmp->name, op->contr->count, alt->name);
394 #endif
395
396 /* startequip items are not allowed to be put into containers: */
397 if (op->type == PLAYER && alt->type == CONTAINER && tmp->flag [FLAG_STARTEQUIP])
398 {
399 new_draw_info (NDI_UNIQUE, 0, op, "This object cannot be put into containers!");
400 goto leave;
401 }
402
403 pick_up_object (op, alt, tmp, count);
404
405 if (tmp->destroyed () || tmp->env)
406 need_fix_tmp = 0;
407
408 if (op->type == PLAYER)
409 op->contr->count = 0;
410
411 goto leave;
412
413 leave:
414 if (need_fix_tmp)
415 fix_stopped_item (tmp, tmp_map, op);
416 }
417
418 /* This takes (picks up) and item. op is the player
419 * who issued the command. params is a string to
420 * match against the item name. Basically, always
421 * returns zero, but that should be improved.
422 */
423 int
424 command_take (object *op, char *params)
425 {
426 object *tmp, *next;
427
428 if (op->container_ ())
429 tmp = op->container_ ()->inv;
430 else
431 {
432 tmp = op->above;
433 if (tmp)
434 while (tmp->above)
435 tmp = tmp->above;
436
437 if (!tmp)
438 tmp = op->below;
439 }
440
441 if (!tmp)
442 {
443 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to take!");
444 return 0;
445 }
446
447 /* Makes processing easier */
448 if (params && *params == '\0')
449 params = 0;
450
451 int cnt = MAX_ITEM_PER_ACTION;
452
453 while (tmp)
454 {
455 next = tmp->below;
456
457 if (tmp->invisible)
458 {
459 tmp = next;
460 continue;
461 }
462
463 /* This following two if and else if could be merged into line
464 * but that probably will make it more difficult to read, and
465 * not make it any more efficient
466 */
467 if (params && item_matched_string (op, tmp, params))
468 {
469 if (--cnt < 0) break;
470 pick_up (op, tmp);
471 }
472 else if (can_pick (op, tmp) && !params)
473 {
474 if (--cnt < 0) break;
475 pick_up (op, tmp);
476 break;
477 }
478
479 tmp = next;
480 }
481
482 if (cnt < 0)
483 {
484 op->failmsg ("Couldn't pick up so many items at once.");
485 return 0;
486 }
487
488 if (!params && !tmp)
489 {
490 for (tmp = op->below; tmp; tmp = tmp->below)
491 if (!tmp->invisible)
492 {
493 new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up a %s.", &tmp->name);
494 break;
495 }
496
497 if (!tmp)
498 new_draw_info (NDI_UNIQUE, 0, op, "There is nothing to pick up.");
499 }
500
501 return 0;
502 }
503
504 /*
505 * This function was part of drop, now is own function.
506 * Player 'op' tries to put object 'tmp' into sack 'sack',
507 * if nrof is non zero, then nrof objects is tried to put into sack.
508 *
509 * Note that the 'sack' in question can now be a transport,
510 * so this function isn't named very good anymore.
511 */
512 void
513 put_object_in_sack (object *op, object *sack, object *tmp, uint32 nrof)
514 {
515 object *tmp2;
516 char buf[MAX_BUF];
517
518 if (sack == tmp)
519 return; /* Can't put an object in itself */
520
521 if (tmp->flag [FLAG_STARTEQUIP] || tmp->flag [FLAG_NO_DROP])
522 {
523 new_draw_info_format (NDI_UNIQUE, 0, op, "You cannot put the %s in the %s.", query_name (tmp), query_name (sack));
524 return;
525 }
526
527 if (tmp->type == CONTAINER && tmp->inv)
528 {
529 /* Eneq(@csd.uu.se): If the object to be dropped is a container
530 * we instead move the contents of that container into the active
531 * container, this is only done if the object has something in it.
532 */
533 new_draw_info_format (NDI_UNIQUE, 0, op, "You move the items from %s into %s.", query_name (tmp), query_name (sack));
534
535 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp)
536 {
537 tmp = tmp2->below;
538
539 if ((sack->type == CONTAINER && sack_can_hold (op, op->container_ (), tmp2, tmp2->nrof)))
540 put_object_in_sack (op, sack, tmp2, 0);
541 else
542 {
543 sprintf (buf, "Your %s fills up.", query_name (sack));
544 new_draw_info (NDI_UNIQUE, 0, op, buf);
545 break;
546 }
547 }
548
549 return;
550 }
551
552 /* Don't worry about this for containers - our caller should have
553 * already checked this.
554 */
555 if ((sack->type == CONTAINER) && !sack_can_hold (op, sack, tmp, (nrof ? nrof : tmp->nrof)))
556 return;
557
558 if (tmp->flag [FLAG_APPLIED])
559 if (!op->apply (tmp, AP_UNAPPLY | AP_NO_MERGE))
560 return;
561
562 /* we want to put some portion of the item into the container */
563 if (!can_split (op, tmp, nrof))
564 return;
565
566 new_draw_info_format (NDI_UNIQUE, 0, op, "You put the %s in %s.", query_name (tmp), query_name (sack));
567 sack->insert (tmp);
568 }
569
570 /* In contrast to drop_object (op, tmp, nrof) above this function takes the
571 * already split off object, and feeds it to the event handlers and does
572 * other magic with it.
573 *
574 * <droppper> is the object that dropped this object and <obj> is the
575 * object that was dropped.
576 *
577 * Make sure to check what happened with <obj> after this function returns!
578 * Otherwise you may leak this object.
579 */
580 void
581 drop_object (object *dropper, object *obj)
582 {
583 if (INVOKE_OBJECT (DROP, obj, ARG_OBJECT (dropper)))
584 return;
585
586 if (obj->destroyed () || obj->is_inserted ())
587 return;
588
589 if (obj->flag [FLAG_STARTEQUIP])
590 {
591 dropper->statusmsg (format ("You drop the %s.", query_name (obj)));
592 dropper->statusmsg ("The god who lent it to you retrieves it.");
593
594 obj->destroy ();
595 dropper->update_stats ();
596 return;
597 }
598
599 for (object *floor = GET_MAP_OB (dropper->map, dropper->x, dropper->y);
600 floor;
601 floor = floor->above)
602 if (INVOKE_OBJECT (DROP_ON, floor, ARG_OBJECT (obj), ARG_OBJECT (dropper)))
603 return;
604
605 if (obj->destroyed () || obj->is_inserted ())
606 return;
607
608 if (dropper->is_in_shop () && !obj->flag [FLAG_UNPAID] && obj->type != MONEY)
609 if (!sell_item (obj, dropper))
610 return;
611
612 if (!obj->can_drop_at (dropper->map, dropper->x, dropper->y, dropper))
613 return;
614
615 /* If nothing special happened with this object, the default action is to
616 * insert it below the dropper:
617 */
618
619 obj->x = dropper->x;
620 obj->y = dropper->y;
621
622 insert_ob_in_map (obj, dropper->map, dropper, INS_BELOW_ORIGINATOR);
623 }
624
625 /*
626 * This function was part of drop, now is own function.
627 * Player 'op' tries to drop object 'tmp', if tmp is non zero, then
628 * nrof objects are tried to drop.
629 * This is used when dropping objects onto the floor.
630 */
631 void
632 drop_object (object *op, object *tmp, uint32 nrof)
633 {
634 if (tmp->flag [FLAG_NO_DROP])
635 return;
636
637 if (tmp->flag [FLAG_APPLIED])
638 if (!op->apply (tmp, AP_UNAPPLY | AP_NO_MERGE))
639 return; /* can't unapply it */
640
641 /* We are only dropping some of the items. We split the current object
642 * off
643 */
644 if (!can_split (op, tmp, nrof))
645 return;
646
647 drop_object (op, tmp);
648
649 if (!tmp->destroyed () && !tmp->is_inserted ())
650 {
651 // if nothing happened with the object we give it back
652 op->insert (tmp);
653 }
654 }
655
656 void
657 drop (object *op, object *tmp)
658 {
659 /* Hopeful fix for disappearing objects when dropping from a container -
660 * somehow, players get an invisible object in the container, and the
661 * old logic would skip over invisible objects - works fine for the
662 * playes inventory, but drop inventory wants to use the next value.
663 */
664 if (tmp->invisible)
665 {
666 /* if the following is the case, it must be in an container. */
667 if (tmp->env && tmp->env->type != PLAYER)
668 {
669 /* Just toss the object - probably shouldn't be hanging
670 * around anyways
671 */
672 tmp->destroy ();
673 return;
674 }
675 else
676 while (tmp && tmp->invisible)
677 tmp = tmp->below;
678 }
679
680 if (!tmp)
681 {
682 new_draw_info (NDI_UNIQUE, 0, op, "You don't have anything to drop.");
683 return;
684 }
685
686 if (tmp->flag [FLAG_INV_LOCKED])
687 {
688 new_draw_info (NDI_UNIQUE, 0, op, "This item is locked");
689 return;
690 }
691
692 if (tmp->flag [FLAG_NO_DROP])
693 {
694 #if 0
695 /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
696 new_draw_info (NDI_UNIQUE, 0, op, "This item can't be dropped.");
697 #endif
698 return;
699 }
700
701 if (op->type == PLAYER && op->contr->last_used == tmp)
702 op->contr->last_used = tmp->below ? tmp->below
703 : tmp->above ? tmp->above
704 : (object *)0;
705
706 if (op->container_ ())
707 {
708 if (op->type == PLAYER)
709 put_object_in_sack (op, op->container_ (), tmp, op->contr->count);
710 else
711 put_object_in_sack (op, op->container_ (), tmp, 0);
712 }
713 else
714 {
715 if (op->type == PLAYER)
716 drop_object (op, tmp, op->contr->count);
717 else
718 drop_object (op, tmp, 0);
719 }
720
721 if (op->type == PLAYER)
722 op->contr->count = 0;
723 }
724
725 /* Command will drop all items that have not been locked */
726 int
727 command_dropall (object *op, char *params)
728 {
729
730 if (op->inv == NULL)
731 {
732 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop!");
733 return 0;
734 }
735
736 object *curinv = op->inv;
737 object *nextinv;
738
739 /*
740 This is the default. Drops everything not locked or considered
741 not something that should be dropped.
742 */
743 /*
744 Care must be taken that the next item pointer is not to money as
745 the drop() routine will do unknown things to it when dropping
746 in a shop. --Tero.Pelander@utu.fi
747 */
748 int cnt = MAX_ITEM_PER_ACTION;
749
750 if (!params)
751 {
752 while (curinv)
753 {
754 nextinv = curinv->below;
755
756 while (nextinv && nextinv->type == MONEY)
757 nextinv = nextinv->below;
758
759 if (!curinv->flag [FLAG_INV_LOCKED]
760 && !curinv->invisible
761 && curinv->type != MONEY
762 && curinv->type != FOOD
763 && curinv->type != KEY
764 && curinv->type != SPECIAL_KEY
765 && curinv->type != GEM
766 && curinv->type != CONTAINER)
767 {
768 drop (op, curinv);
769 if (--cnt <= 0) break;
770 }
771
772 curinv = nextinv;
773 }
774 }
775 else if (strcmp (params, "weapons") == 0)
776 {
777 while (curinv)
778 {
779 nextinv = curinv->below;
780
781 while (nextinv && nextinv->type == MONEY)
782 nextinv = nextinv->below;
783
784 if (!curinv->flag [FLAG_INV_LOCKED] && ((curinv->type == WEAPON) || (curinv->type == BOW) || (curinv->type == ARROW)))
785 {
786 drop (op, curinv);
787 if (--cnt <= 0) break;
788 }
789
790 curinv = nextinv;
791 }
792 }
793 else if (strcmp (params, "armor") == 0 || strcmp (params, "armour") == 0)
794 {
795 while (curinv)
796 {
797 nextinv = curinv->below;
798
799 while (nextinv && nextinv->type == MONEY)
800 nextinv = nextinv->below;
801
802 if (!curinv->flag [FLAG_INV_LOCKED] && ((curinv->type == ARMOUR) || curinv->type == SHIELD || curinv->type == HELMET))
803 {
804 drop (op, curinv);
805 if (--cnt <= 0) break;
806 }
807
808 curinv = nextinv;
809 }
810 }
811 else if (strcmp (params, "misc") == 0)
812 {
813 while (curinv)
814 {
815 nextinv = curinv->below;
816
817 while (nextinv && nextinv->type == MONEY)
818 nextinv = nextinv->below;
819
820 if (!curinv->flag [FLAG_INV_LOCKED] && !curinv->flag [FLAG_APPLIED])
821 {
822 switch (curinv->type)
823 {
824 case HORN:
825 case BOOK:
826 case SPELLBOOK:
827 case GIRDLE:
828 case AMULET:
829 case RING:
830 case CLOAK:
831 case BOOTS:
832 case GLOVES:
833 case BRACERS:
834 case SCROLL:
835 case ARMOUR_IMPROVER:
836 case WEAPON_IMPROVER:
837 case WAND:
838 case ROD:
839 case POTION:
840 drop (op, curinv);
841 curinv = nextinv;
842 break;
843 default:
844 curinv = nextinv;
845 break;
846 }
847
848 if (--cnt <= 0) break;
849 }
850
851 curinv = nextinv;
852 }
853 }
854
855 if (cnt <= 0)
856 op->failmsg ("Only dropped some items, can't drop that many items at once.");
857
858 /* draw_look(op);*/
859 return 0;
860 }
861
862
863 /* This function tries to drop all objects in the <objs> vector.
864 * <dropper> is the object that wants to drop them.
865 * <cnt> can be a 0 pointer or a pointer to the maximum number of
866 * drop operations to perform.
867 *
868 * Returns true if at least one item was dropped.
869 */
870 static bool
871 drop_vector (object *dropper, vector<object *> &objs, int *cnt)
872 {
873 vector<object *>::iterator i;
874
875 bool did_one = false;
876
877 for (i = objs.begin (); i != objs.end (); i++)
878 {
879 drop (dropper, *i);
880 if (cnt && --*cnt <= 0) break;
881 did_one = true;
882 }
883
884 return did_one;
885 }
886
887 /* Object op wants to drop object(s) params. params can be a
888 * comma seperated list.
889 */
890 int
891 command_drop (object *op, char *params)
892 {
893 object *tmp, *next;
894
895 if (!params)
896 {
897 new_draw_info (NDI_UNIQUE, 0, op, "Drop what?");
898 return 0;
899 }
900 else
901 {
902 vector<object *> matched_objs;
903
904 for (tmp = op->inv; tmp; tmp = next)
905 {
906 next = tmp->below;
907 if (tmp->flag [FLAG_NO_DROP] || tmp->invisible)
908 continue;
909
910 if (item_matched_string (op, tmp, params))
911 matched_objs.push_back (tmp);
912 }
913
914 int cnt = MAX_ITEM_PER_ACTION;
915
916 if (!drop_vector (op, matched_objs, &cnt))
917 new_draw_info (NDI_UNIQUE, 0, op, "Nothing to drop.");
918
919 if (cnt <= 0)
920 op->failmsg ("Only dropped some items, can't drop that many items at once.");
921 }
922
923 return 0;
924 }
925
926 int
927 command_examine (object *op, char *params)
928 {
929 if (!params)
930 {
931 object *tmp = op->below;
932
933 while (tmp && !tmp->client_visible ())
934 tmp = tmp->below;
935
936 if (tmp)
937 examine (op, tmp);
938 }
939 else
940 {
941 object *tmp = find_best_object_match (op, params);
942
943 if (tmp)
944 examine (op, tmp);
945 else
946 op->contr->infobox (MSG_CHANNEL ("examine"), format ("Could not find an object that matches %s", params));
947 }
948
949 return 0;
950 }
951
952 std::string
953 object::describe_monster (object *who)
954 {
955 dynbuf_text buf (512, 512);
956
957 object *mon = head ? head : this;
958
959 if (mon->flag [FLAG_UNDEAD])
960 buf << "It is an undead force.\r";
961
962 if (mon->level > who->level)
963 buf << "It is likely more powerful than you.\r";
964 else if (mon->level < who->level)
965 buf << "It is likely less powerful than you.\r";
966 else
967 buf << "It is probably as powerful as you.\r";
968
969 if (mon->attacktype & AT_ACID)
970 buf << "You seem to smell an acrid odor.\r";
971
972 /* Anyone know why this used to use the clone value instead of the
973 * maxhp field? This seems that it should give more accurate results.
974 */
975 switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
976 { /* From 1-4 */
977 case 1:
978 buf << "It is in a bad shape.\r";
979 break;
980 case 2:
981 buf << "It is hurt.\r";
982 break;
983 case 3:
984 buf << "It is somewhat hurt.\r";
985 break;
986 case 4:
987 buf << "It is in excellent shape.\r";
988 break;
989 }
990
991 if (present_in_ob (POISONING, mon))
992 buf << "It looks very ill.\r";
993
994 buf << '\n';
995
996 return buf;
997 }
998
999 /* tmp is the object being described, pl is who is examing it. */
1000 const char *
1001 long_desc (object *tmp, object *pl)
1002 {
1003 static std::string s;
1004
1005 return (s = tmp->long_desc (pl)).c_str ();
1006 }
1007
1008 std::string
1009 object::long_desc (object *who)
1010 {
1011 std::string buf (query_name ());
1012
1013 switch (type)
1014 {
1015 case RING:
1016 case SKILL:
1017 case WEAPON:
1018 case ARMOUR:
1019 case BRACERS:
1020 case HELMET:
1021 case SHIELD:
1022 case BOOTS:
1023 case GLOVES:
1024 case AMULET:
1025 case GIRDLE:
1026 case RANGED:
1027 case BOW:
1028 case ARROW:
1029 case CLOAK:
1030 case FOOD:
1031 case DRINK:
1032 case FLESH:
1033 case SKILL_TOOL:
1034 case LAMP:
1035 case POWER_CRYSTAL:
1036 {
1037 const char *cp = ::describe_item (this, who);
1038
1039 if (*cp)
1040 {
1041 buf.append (" ");
1042 buf.append (cp);
1043 }
1044 }
1045 }
1046
1047 return buf;
1048 }
1049
1050 /* op is the player
1051 * tmp is the monster being examined.
1052 */
1053 void
1054 examine_monster (object *op, object *tmp)
1055 {
1056 new_draw_info (NDI_UNIQUE, 0, op, tmp->describe_monster (op).c_str ());
1057 }
1058
1059 static void
1060 display_new_pickup (object *op)
1061 {
1062 int i = op->contr->mode;
1063
1064 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DEBUG", i & PU_DEBUG ? 1 : 0);
1065 new_draw_info_format (NDI_UNIQUE, 0, op, "%d INHIBIT", i & PU_INHIBIT ? 1 : 0);
1066 new_draw_info_format (NDI_UNIQUE, 0, op, "%d STOP", i & PU_STOP ? 1 : 0);
1067
1068 new_draw_info_format (NDI_UNIQUE, 0, op, "%d <= x pickup weight/value RATIO (0==off)", (i & PU_RATIO) * 5);
1069
1070 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FOOD", i & PU_FOOD ? 1 : 0);
1071 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DRINK", i & PU_DRINK ? 1 : 0);
1072 new_draw_info_format (NDI_UNIQUE, 0, op, "%d VALUABLES", i & PU_VALUABLES ? 1 : 0);
1073
1074 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOW", i & PU_BOW ? 1 : 0);
1075 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARROW", i & PU_ARROW ? 1 : 0);
1076
1077 new_draw_info_format (NDI_UNIQUE, 0, op, "%d HELMET", i & PU_HELMET ? 1 : 0);
1078 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SHIELD", i & PU_SHIELD ? 1 : 0);
1079 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARMOUR", i & PU_ARMOUR ? 1 : 0);
1080
1081 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOOTS", i & PU_BOOTS ? 1 : 0);
1082 new_draw_info_format (NDI_UNIQUE, 0, op, "%d GLOVES", i & PU_GLOVES ? 1 : 0);
1083 new_draw_info_format (NDI_UNIQUE, 0, op, "%d CLOAK", i & PU_CLOAK ? 1 : 0);
1084 new_draw_info_format (NDI_UNIQUE, 0, op, "%d KEY", i & PU_KEY ? 1 : 0);
1085
1086 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MISSILEWEAPON", i & PU_MISSILEWEAPON ? 1 : 0);
1087 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ALLWEAPON", i & PU_ALLWEAPON ? 1 : 0);
1088 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICAL", i & PU_MAGICAL ? 1 : 0);
1089 new_draw_info_format (NDI_UNIQUE, 0, op, "%d POTION", i & PU_POTION ? 1 : 0);
1090
1091 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SPELLBOOK", i & PU_SPELLBOOK ? 1 : 0);
1092 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SKILLSCROLL", i & PU_SKILLSCROLL ? 1 : 0);
1093 new_draw_info_format (NDI_UNIQUE, 0, op, "%d READABLES", i & PU_READABLES ? 1 : 0);
1094 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICDEVICE", i & PU_MAGIC_DEVICE ? 1 : 0);
1095
1096 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NOT CURSED", i & PU_NOT_CURSED ? 1 : 0);
1097
1098 new_draw_info_format (NDI_UNIQUE, 0, op, "%d JEWELS", i & PU_JEWELS ? 1 : 0);
1099 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FLESH", i & PU_FLESH ? 1 : 0);
1100
1101 new_draw_info_format (NDI_UNIQUE, 0, op, "");
1102 }
1103
1104 int
1105 command_pickup (object *op, char *params)
1106 {
1107 uint32 i;
1108 static const char *names[] = {
1109 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1110 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1111 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed",
1112 "jewels", "flesh", NULL
1113 };
1114 static uint32 modes[] = {
1115 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1116 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1117 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED,
1118 PU_JEWELS, PU_FLESH, 0
1119 };
1120
1121 if (!params)
1122 {
1123 /* if the new mode is used, just print the settings */
1124 display_new_pickup (op);
1125 return 0;
1126 }
1127
1128 while (*params == ' ' && *params)
1129 params++;
1130
1131 if (*params == '+' || *params == '-')
1132 {
1133 int mode;
1134
1135 for (mode = 0; names[mode]; mode++)
1136 {
1137 if (!strcmp (names[mode], params + 1))
1138 {
1139 i = op->contr->mode;
1140
1141 if (*params == '+')
1142 i = i | modes[mode];
1143 else
1144 i = i & ~modes[mode];
1145
1146 op->contr->mode = i;
1147 display_new_pickup (op);
1148 return 1;
1149 }
1150 }
1151 new_draw_info_format (NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params);
1152 return 1;
1153 }
1154
1155 if (sscanf (params, "%u", &i) != 1)
1156 {
1157 new_draw_info (NDI_UNIQUE, 0, op, "Usage: pickup <value_density> or [+-]type.");
1158 return 1;
1159 }
1160
1161 if (i <= PU_RATIO)
1162 i |= op->contr->mode & ~PU_RATIO;
1163
1164 op->contr->mode = i;
1165
1166 return 1;
1167 }
1168
1169 int
1170 command_search_items (object *op, char *params)
1171 {
1172 if (params == NULL)
1173 {
1174 if (op->contr->search_str[0] == '\0')
1175 {
1176 new_draw_info (NDI_UNIQUE, 0, op, "Example: search-items magic+1");
1177 new_draw_info (NDI_UNIQUE, 0, op, "Would automatically pick up all");
1178 new_draw_info (NDI_UNIQUE, 0, op, "items containing the word 'magic+1'.");
1179 return 1;
1180 }
1181
1182 op->contr->search_str[0] = '\0';
1183 new_draw_info (NDI_UNIQUE, 0, op, "Search mode turned off.");
1184 op->contr->queue_stats_update ();
1185 return 1;
1186 }
1187
1188 if (strlen (params) >= sizeof (op->contr->search_str))
1189 {
1190 new_draw_info (NDI_UNIQUE, 0, op, "Search string too long.");
1191 return 1;
1192 }
1193
1194 strcpy (op->contr->search_str, params);
1195 new_draw_info (NDI_UNIQUE, 0, op, format ("Now searching for '%s'.", op->contr->search_str));
1196 op->contr->queue_stats_update ();
1197
1198 return 1;
1199 }
1200
1201 int
1202 command_unlock (object *op, char *params)
1203 {
1204 /* if the unlock command typed with nothing, unlock everything,
1205 * this might be bad
1206 */
1207 if (params == NULL)
1208 {
1209 for (object *item = op->inv; item; item = item->below)
1210 {
1211 item->clr_flag (FLAG_INV_LOCKED);
1212 //d// new_draw_info (NDI_UNIQUE, 0, op, "unlocked items with null param.");
1213 esrv_update_item (UPD_FLAGS, op, item);
1214 }
1215 return 0;
1216 }
1217
1218 /* if the unlock command is used with a param,
1219 * unlock what matches. i.e. unlock material, should unlock all the materials
1220 */
1221 for (object *item = op->inv; item; item = item->below)
1222 if (item->name.contains (params))
1223 {
1224 item->clr_flag (FLAG_INV_LOCKED);
1225 //d// new_draw_info (NDI_UNIQUE, 0, op, "unlocked items with a param.");
1226 esrv_update_item (UPD_FLAGS, op, item);
1227 }
1228
1229 return 0;
1230 }
1231
1232 int
1233 command_lock (object *op, char *params)
1234 {
1235 /* if the lock command is typed by itself, lock everything
1236 */
1237 if (params == NULL)
1238 {
1239 for (object *item = op->inv; item; item = item->below)
1240 {
1241 item->set_flag (FLAG_INV_LOCKED);
1242 //d// new_draw_info (NDI_UNIQUE, 0, op, "locked items with null param.");
1243 esrv_update_item (UPD_FLAGS, op, item);
1244 }
1245 return 0;
1246 }
1247
1248 /* if the lock command is used with a param, lock what matches.
1249 * i.e. lock material, should lock all the materials
1250 */
1251 for (object *item = op->inv; item; item = item->below)
1252 if (item->name.contains (params))
1253 {
1254 item->set_flag (FLAG_INV_LOCKED);
1255 //d// new_draw_info (NDI_UNIQUE, 0, op, "locked items with param.");
1256 esrv_update_item (UPD_FLAGS, op, item);
1257 }
1258
1259 return 0;
1260 }
1261
1262 /* op should be a player, params is any params.
1263 * If no params given, we print out the currently marked object.
1264 * otherwise, try to find a matching object - try best match first.
1265 */
1266 int
1267 command_mark (object *op, char *params)
1268 {
1269 if (!params)
1270 {
1271 if (object *mark = op->mark ())
1272 op->statusmsg (format ("%s is marked.", query_name (mark)));
1273 else
1274 op->failmsg ("You have no marked object.");
1275 }
1276 else
1277 {
1278 if (object *mark = find_best_object_match (op, params))
1279 {
1280 op->contr->mark = mark;
1281 op->statusmsg (format ("Marked item %s", query_name (mark)));
1282 }
1283 else
1284 op->failmsgf ("Could not find an object that matches %s", params);
1285 }
1286
1287 return 0; /*shouldnt get here */
1288 }
1289