ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.6
Committed: Sat Aug 26 23:36:33 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +1 -316 lines
Log Message:
intermediate check-in, per-object events work

File Contents

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