ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.64
Committed: Sun Apr 20 00:44:13 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.63: +10 -17 lines
Log Message:
reloadable archetypes, maybe

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 if (tmp)
1003 examine (op, tmp);
1004 }
1005 else
1006 {
1007 object *tmp = find_best_object_match (op, params);
1008
1009 if (tmp)
1010 examine (op, tmp);
1011 else
1012 new_draw_info_format (NDI_UNIQUE, 0, op, "Could not find an object that matches %s", params);
1013 }
1014 return 0;
1015 }
1016
1017 /* op should be a player.
1018 * we return the object the player has marked with the 'mark' command
1019 * below. If no match is found (or object has changed), we return
1020 * NULL. We leave it up to the calling function to print messages if
1021 * nothing is found.
1022 */
1023 object *
1024 find_marked_object (object *op)
1025 {
1026 object *tmp;
1027
1028 if (!op || !op->contr)
1029 return NULL;
1030
1031 if (!op->contr->mark)
1032 {
1033 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1034 return 0;
1035 }
1036
1037 /* This may seem like overkill, but we need to make sure that they
1038 * player hasn't dropped the item. We use count on the off chance that
1039 * an item got reincarnated at some point.
1040 */
1041 for (tmp = op->inv; tmp; tmp = tmp->below)
1042 {
1043 if (tmp->invisible)
1044 continue;
1045
1046 if (tmp == op->contr->mark)
1047 {
1048 if (!tmp->destroyed ())
1049 return tmp;
1050 else
1051 {
1052 op->contr->mark = 0;
1053 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1054 return 0;
1055 }
1056 }
1057 }
1058
1059 return 0;
1060 }
1061
1062 std::string
1063 object::describe_monster (object *who)
1064 {
1065 dynbuf_text buf (512, 512);
1066
1067 object *mon = head ? head : this;
1068
1069 if (QUERY_FLAG (mon, FLAG_UNDEAD))
1070 buf << "It is an undead force.\n";
1071
1072 if (mon->level > who->level)
1073 buf << "It is likely more powerful than you.\n";
1074 else if (mon->level < who->level)
1075 buf << "It is likely less powerful than you.\n";
1076 else
1077 buf << "It is probably as powerful as you.\n";
1078
1079 if (mon->attacktype & AT_ACID)
1080 buf << "You seem to smell an acrid odor.\n";
1081
1082 /* Anyone know why this used to use the clone value instead of the
1083 * maxhp field? This seems that it should give more accurate results.
1084 */
1085 switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
1086 { /* From 1-4 */
1087 case 1:
1088 buf << "It is in a bad shape.\n";
1089 break;
1090 case 2:
1091 buf << "It is hurt.\n";
1092 break;
1093 case 3:
1094 buf << "It is somewhat hurt.\n";
1095 break;
1096 case 4:
1097 buf << "It is in excellent shape.\n";
1098 break;
1099 }
1100
1101 if (present_in_ob (POISONING, mon))
1102 buf << "It looks very ill.\n";
1103
1104 return buf;
1105 }
1106
1107 /* tmp is the object being described, pl is who is examing it. */
1108 const char *
1109 long_desc (object *tmp, object *pl)
1110 {
1111 static std::string s;
1112
1113 return (s = tmp->long_desc (pl)).c_str ();
1114 }
1115
1116 std::string
1117 object::long_desc (object *who)
1118 {
1119 std::string buf (query_name (this));
1120
1121 switch (type)
1122 {
1123 case RING:
1124 case SKILL:
1125 case WEAPON:
1126 case ARMOUR:
1127 case BRACERS:
1128 case HELMET:
1129 case SHIELD:
1130 case BOOTS:
1131 case GLOVES:
1132 case AMULET:
1133 case GIRDLE:
1134 case BOW:
1135 case ARROW:
1136 case CLOAK:
1137 case FOOD:
1138 case DRINK:
1139 case FLESH:
1140 case SKILL_TOOL:
1141 case POWER_CRYSTAL:
1142 {
1143 const char *cp = ::describe_item (this, who);
1144
1145 if (*cp)
1146 {
1147 buf.append (" ");
1148 buf.append (cp);
1149 }
1150 }
1151 }
1152
1153 return buf;
1154 }
1155
1156 /* op is the player
1157 * tmp is the monster being examined.
1158 */
1159 void
1160 examine_monster (object *op, object *tmp)
1161 {
1162 new_draw_info (NDI_UNIQUE, 0, op, tmp->describe_monster (op).c_str ());
1163 }
1164
1165 std::string
1166 object::describe (object *who)
1167 {
1168 dynbuf_text buf (1024, 1024);
1169
1170 buf.printf ("That is: %s.\n", long_desc (who).c_str ());
1171
1172 if (custom_name)
1173 buf.printf ("You call it %s\n", &custom_name);
1174
1175 switch (type)
1176 {
1177 case SPELLBOOK:
1178 if (flag [FLAG_IDENTIFIED] && inv)
1179 buf.printf ("%s is a %s %s spell\n", &inv->name, get_levelnumber (inv->level), &inv->skill);
1180 break;
1181
1182 case BOOK:
1183 if (msg)
1184 buf << "Something is written in it.\n";
1185 break;
1186
1187 case CONTAINER:
1188 if (race != NULL)
1189 {
1190 if (weight_limit && stats.Str < 100)
1191 buf.printf ("It can hold only %s and its weight limit is %.1f kg.\n",
1192 &race, weight_limit / (10.0 * (100 - stats.Str)));
1193 else
1194 buf.printf ("It can hold only %s.\n", &race);
1195 }
1196 else if (weight_limit && stats.Str < 100)
1197 buf.printf ("Its weight limit is %.1f kg.\n", weight_limit / (10.0 * (100 - stats.Str)));
1198 break;
1199
1200 case WAND:
1201 if (flag [FLAG_IDENTIFIED])
1202 buf.printf ("It has %d charges left.\n", stats.food);
1203 break;
1204 }
1205
1206 if (materialname && !msg)
1207 buf.printf ("It is made of: %s.\n", &materialname);
1208
1209 if (who)
1210 /* Where to wear this item */
1211 for (int i = 0; i < NUM_BODY_LOCATIONS; i++)
1212 if (slot[i].info)
1213 {
1214 buf << (who->slot[i].info ? body_locations[i].use_name : body_locations[i].nonuse_name);
1215
1216 if (slot[i].info < -1 && who->slot[i].info)
1217 buf.printf ("(%d)", -slot[i].info);
1218
1219 buf << ".\n";
1220 }
1221
1222 if (weight)
1223 buf.printf (nrof > 1 ? "They weigh %3.3f kg.\n" : "It weighs %3.3f kg.\n", weight * (nrof ? nrof : 1) / 1000.0);
1224
1225 if (value && !flag [FLAG_STARTEQUIP] && !flag [FLAG_NO_PICK] && who)
1226 {
1227 buf.printf ("You reckon %s worth %s.\n", nrof > 1 ? "they are" : "it is", query_cost_string (this, who, F_TRUE | F_APPROX));
1228
1229 if (is_in_shop (who))
1230 {
1231 if (flag [FLAG_UNPAID])
1232 buf.printf ("%s would cost you %s.\n", nrof > 1 ? "They" : "It", query_cost_string (this, who, F_BUY | F_SHOP));
1233 else
1234 buf.printf ("You are offered %s for %s.\n", query_cost_string (this, who, F_SELL + F_SHOP), nrof > 1 ? "them" : "it");
1235 }
1236 }
1237
1238 if (flag [FLAG_MONSTER])
1239 buf << describe_monster (who);
1240
1241 /* Is this item buildable? */
1242 if (flag [FLAG_IS_BUILDABLE])
1243 buf << "This is a buildable item.\n";
1244
1245 /* Does the object have a message? Don't show message for all object
1246 * types - especially if the first entry is a match
1247 */
1248 if (msg && type != EXIT && type != BOOK && type != CORPSE && !move_on && *msg != '@')
1249 {
1250 /* This is just a hack so when identifying the items, we print
1251 * out the extra message
1252 */
1253 if (need_identify (this) && flag [FLAG_IDENTIFIED])
1254 buf << "The object has a story:\n";
1255
1256 buf << msg << '\n';
1257 }
1258
1259 buf << '\n';
1260
1261 return std::string (buf.linearise (), buf.size ());
1262 }
1263
1264 static void
1265 display_new_pickup (object *op)
1266 {
1267 int i = op->contr->mode;
1268
1269 if (!(i & PU_NEWMODE))
1270 return;
1271
1272 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NEWMODE", i & PU_NEWMODE ? 1 : 0);
1273 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DEBUG", i & PU_DEBUG ? 1 : 0);
1274 new_draw_info_format (NDI_UNIQUE, 0, op, "%d INHIBIT", i & PU_INHIBIT ? 1 : 0);
1275 new_draw_info_format (NDI_UNIQUE, 0, op, "%d STOP", i & PU_STOP ? 1 : 0);
1276
1277 new_draw_info_format (NDI_UNIQUE, 0, op, "%d <= x pickup weight/value RATIO (0==off)", (i & PU_RATIO) * 5);
1278
1279 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FOOD", i & PU_FOOD ? 1 : 0);
1280 new_draw_info_format (NDI_UNIQUE, 0, op, "%d DRINK", i & PU_DRINK ? 1 : 0);
1281 new_draw_info_format (NDI_UNIQUE, 0, op, "%d VALUABLES", i & PU_VALUABLES ? 1 : 0);
1282
1283 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOW", i & PU_BOW ? 1 : 0);
1284 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARROW", i & PU_ARROW ? 1 : 0);
1285
1286 new_draw_info_format (NDI_UNIQUE, 0, op, "%d HELMET", i & PU_HELMET ? 1 : 0);
1287 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SHIELD", i & PU_SHIELD ? 1 : 0);
1288 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ARMOUR", i & PU_ARMOUR ? 1 : 0);
1289
1290 new_draw_info_format (NDI_UNIQUE, 0, op, "%d BOOTS", i & PU_BOOTS ? 1 : 0);
1291 new_draw_info_format (NDI_UNIQUE, 0, op, "%d GLOVES", i & PU_GLOVES ? 1 : 0);
1292 new_draw_info_format (NDI_UNIQUE, 0, op, "%d CLOAK", i & PU_CLOAK ? 1 : 0);
1293 new_draw_info_format (NDI_UNIQUE, 0, op, "%d KEY", i & PU_KEY ? 1 : 0);
1294
1295 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MISSILEWEAPON", i & PU_MISSILEWEAPON ? 1 : 0);
1296 new_draw_info_format (NDI_UNIQUE, 0, op, "%d ALLWEAPON", i & PU_ALLWEAPON ? 1 : 0);
1297 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICAL", i & PU_MAGICAL ? 1 : 0);
1298 new_draw_info_format (NDI_UNIQUE, 0, op, "%d POTION", i & PU_POTION ? 1 : 0);
1299
1300 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SPELLBOOK", i & PU_SPELLBOOK ? 1 : 0);
1301 new_draw_info_format (NDI_UNIQUE, 0, op, "%d SKILLSCROLL", i & PU_SKILLSCROLL ? 1 : 0);
1302 new_draw_info_format (NDI_UNIQUE, 0, op, "%d READABLES", i & PU_READABLES ? 1 : 0);
1303 new_draw_info_format (NDI_UNIQUE, 0, op, "%d MAGICDEVICE", i & PU_MAGIC_DEVICE ? 1 : 0);
1304
1305 new_draw_info_format (NDI_UNIQUE, 0, op, "%d NOT CURSED", i & PU_NOT_CURSED ? 1 : 0);
1306
1307 new_draw_info_format (NDI_UNIQUE, 0, op, "%d JEWELS", i & PU_JEWELS ? 1 : 0);
1308 new_draw_info_format (NDI_UNIQUE, 0, op, "%d FLESH", i & PU_FLESH ? 1 : 0);
1309
1310 new_draw_info_format (NDI_UNIQUE, 0, op, "");
1311 }
1312
1313 int
1314 command_pickup (object *op, char *params)
1315 {
1316 uint32 i;
1317 static const char *names[] = {
1318 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1319 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1320 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed",
1321 "jewels", "flesh", NULL
1322 };
1323 static uint32 modes[] = {
1324 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1325 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1326 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED,
1327 PU_JEWELS, PU_FLESH, 0
1328 };
1329
1330 if (!params)
1331 {
1332 /* if the new mode is used, just print the settings */
1333 if (op->contr->mode & PU_NEWMODE)
1334 {
1335 display_new_pickup (op);
1336 return 1;
1337 }
1338 if (1)
1339 LOG (llevDebug, "command_pickup: !params\n");
1340 set_pickup_mode (op, (op->contr->mode > 6) ? 0 : op->contr->mode + 1);
1341 return 0;
1342 }
1343
1344 while (*params == ' ' && *params)
1345 params++;
1346
1347 if (*params == '+' || *params == '-')
1348 {
1349 int mode;
1350
1351 for (mode = 0; names[mode]; mode++)
1352 {
1353 if (!strcmp (names[mode], params + 1))
1354 {
1355 i = op->contr->mode;
1356 if (!(i & PU_NEWMODE))
1357 i = PU_NEWMODE;
1358 if (*params == '+')
1359 i = i | modes[mode];
1360 else
1361 i = i & ~modes[mode];
1362 op->contr->mode = i;
1363 display_new_pickup (op);
1364 return 1;
1365 }
1366 }
1367 new_draw_info_format (NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params);
1368 return 1;
1369 }
1370
1371 if (sscanf (params, "%u", &i) != 1)
1372 {
1373 if (1)
1374 LOG (llevDebug, "command_pickup: params==NULL\n");
1375 new_draw_info (NDI_UNIQUE, 0, op, "Usage: pickup <0-7> or <value_density> .");
1376 return 1;
1377 }
1378 set_pickup_mode (op, i);
1379
1380 return 1;
1381 }
1382
1383 void
1384 set_pickup_mode (object *op, int i)
1385 {
1386 switch (op->contr->mode = i)
1387 {
1388 case 0:
1389 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Don't pick up.");
1390 break;
1391 case 1:
1392 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up one item.");
1393 break;
1394 case 2:
1395 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up one item and stop.");
1396 break;
1397 case 3:
1398 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Stop before picking up.");
1399 break;
1400 case 4:
1401 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all items.");
1402 break;
1403 case 5:
1404 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all items and stop.");
1405 break;
1406 case 6:
1407 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all magic items.");
1408 break;
1409 case 7:
1410 new_draw_info (NDI_UNIQUE, 0, op, "Mode: Pick up all coins and gems");
1411 break;
1412 }
1413 }
1414
1415 int
1416 command_search_items (object *op, char *params)
1417 {
1418 char buf[MAX_BUF];
1419
1420 if (settings.search_items == FALSE)
1421 return 1;
1422
1423 if (params == NULL)
1424 {
1425 if (op->contr->search_str[0] == '\0')
1426 {
1427 new_draw_info (NDI_UNIQUE, 0, op, "Example: search magic+1");
1428 new_draw_info (NDI_UNIQUE, 0, op, "Would automatically pick up all");
1429 new_draw_info (NDI_UNIQUE, 0, op, "items containing the word 'magic+1'.");
1430 return 1;
1431 }
1432
1433 op->contr->search_str[0] = '\0';
1434 new_draw_info (NDI_UNIQUE, 0, op, "Search mode turned off.");
1435 op->update_stats ();
1436 return 1;
1437 }
1438
1439 if ((int) strlen (params) >= MAX_BUF)
1440 {
1441 new_draw_info (NDI_UNIQUE, 0, op, "Search string too long.");
1442 return 1;
1443 }
1444
1445 strcpy (op->contr->search_str, params);
1446 sprintf (buf, "Searching for '%s'.", op->contr->search_str);
1447 new_draw_info (NDI_UNIQUE, 0, op, buf);
1448 op->update_stats ();
1449 return 1;
1450 }
1451