ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.18
Committed: Thu Sep 14 23:13:49 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.17: +31 -22 lines
Log Message:
replace was_destroyed by much simpler and less expensive ->destroyed,
which is valid, as objetc pointers are now reliable.

File Contents

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