ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.66
Committed: Mon Apr 21 07:01:40 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.65: +27 -68 lines
Log Message:
- fix weight update on insert-merge
- refactor get_split_ob into ->split, taking advantage
  (partially only) of split managing client updates.

File Contents

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