ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.C
Revision: 1.8
Committed: Mon Aug 28 07:07:42 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.7: +3 -1 lines
Log Message:
better but more wasteful serialisation support, perl can invoke events, fixes

File Contents

# Content
1 /*
2 * static char *rcsid_c_object_c =
3 * "$Id: c_object.C,v 1.7 2006-08-27 15:24:22 root Exp $";
4 */
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 void
602 drop_object (object * op, object * tmp, uint32 nrof)
603 {
604 char buf[MAX_BUF];
605 object *floor;
606
607 if (QUERY_FLAG (tmp, FLAG_NO_DROP))
608 return;
609
610 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 }
638 else
639 remove_ob (tmp);
640
641 if (INVOKE_OBJECT (DROP, tmp, ARG_OBJECT (op)))
642 return;
643
644 if (QUERY_FLAG (tmp, FLAG_STARTEQUIP))
645 {
646 sprintf (buf, "You drop the %s.", query_name (tmp));
647 new_draw_info (NDI_UNIQUE, 0, op, buf);
648 new_draw_info (NDI_UNIQUE, 0, op,
649 "The gods who lent it to you retrieves it.");
650 if (op->type == PLAYER)
651 esrv_del_item (op->contr, tmp->count);
652 free_object (tmp);
653 fix_player (op);
654 return;
655 }
656
657 /* If SAVE_INTERVAL is commented out, we never want to save
658 * the player here.
659 */
660 #ifdef SAVE_INTERVAL
661 /* I'm not sure why there is a value check - since the save
662 * is done every SAVE_INTERVAL seconds, why care the value
663 * of what he is dropping?
664 */
665 if (op->type == PLAYER && !QUERY_FLAG (tmp, FLAG_UNPAID) &&
666 (tmp->nrof ? tmp->value * tmp->nrof : tmp->value > 2000) &&
667 (op->contr->last_save_time + SAVE_INTERVAL) <= time (NULL))
668 {
669 save_player (op, 1);
670 op->contr->last_save_time = time (NULL);
671 }
672 #endif /* SAVE_INTERVAL */
673
674 if (op->type == PLAYER)
675 esrv_del_item (op->contr, tmp->count);
676
677 /* Call this before we update the various windows/players. At least
678 * that we, we know the weight is correct.
679 */
680 fix_player (op); /* This is overkill, fix_player() is called somewhere */
681 /* in object.c */
682
683 if (op->type == PLAYER)
684 {
685 op->contr->socket.update_look = 1;
686 /* Need to update the weight for the player */
687 esrv_send_item (op, op);
688 }
689
690 for (floor = get_map_ob (op->map, op->x, op->y); floor; floor = floor->above)
691 if (INVOKE_OBJECT (DROP_ON, floor, ARG_OBJECT (tmp), ARG_OBJECT (op)))
692 return;
693
694 floor = get_map_ob (op->map, op->x, op->y);
695
696 if (floor
697 && floor->type == SHOP_FLOOR
698 && !QUERY_FLAG (tmp, FLAG_UNPAID)
699 && tmp->type != MONEY)
700 sell_item (tmp, op);
701
702 tmp->x = op->x;
703 tmp->y = op->y;
704
705 insert_ob_in_map (tmp, op->map, op, INS_BELOW_ORIGINATOR);
706 }
707
708 void drop(object *op, object *tmp)
709 {
710 /* Hopeful fix for disappearing objects when dropping from a container -
711 * somehow, players get an invisible object in the container, and the
712 * old logic would skip over invisible objects - works fine for the
713 * playes inventory, but drop inventory wants to use the next value.
714 */
715 if (tmp->invisible) {
716 /* if the following is the case, it must be in an container. */
717 if (tmp->env && tmp->env->type != PLAYER) {
718 /* Just toss the object - probably shouldn't be hanging
719 * around anyways
720 */
721 remove_ob(tmp);
722 free_object(tmp);
723 return;
724 } else {
725 while(tmp!=NULL && tmp->invisible)
726 tmp=tmp->below;
727 }
728 }
729
730 if (tmp==NULL) {
731 new_draw_info(NDI_UNIQUE, 0,op,"You don't have anything to drop.");
732 return;
733 }
734 if (QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
735 new_draw_info(NDI_UNIQUE, 0,op,"This item is locked");
736 return;
737 }
738 if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
739 #if 0
740 /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
741 new_draw_info(NDI_UNIQUE, 0,op, "This item can't be dropped.");
742 #endif
743 return;
744 }
745
746 if (op->type == PLAYER)
747 {
748 if (op->contr->last_used==tmp && op->contr->last_used_id == tmp->count) {
749 object *n=NULL;
750 if(tmp->below != NULL)
751 n = tmp->below;
752 else if(tmp->above != NULL)
753 n = tmp->above;
754 op->contr->last_used = n;
755 if (n != NULL)
756 op->contr->last_used_id = n->count;
757 else
758 op->contr->last_used_id = 0;
759 }
760 };
761
762 if (op->container) {
763 if (op->type == PLAYER)
764 {
765 put_object_in_sack (op, op->container, tmp, op->contr->count);
766 } else {
767 put_object_in_sack(op, op->container, tmp, 0);
768 };
769 } else {
770 if (op->type == PLAYER)
771 {
772 drop_object (op, tmp, op->contr->count);
773 } else {
774 drop_object(op,tmp,0);
775 };
776 }
777 if (op->type == PLAYER)
778 op->contr->count = 0;
779 }
780
781
782
783 /* Command will drop all items that have not been locked */
784 int command_dropall (object *op, char *params) {
785
786 object * curinv, *nextinv;
787
788 if(op->inv == NULL) {
789 new_draw_info(NDI_UNIQUE, 0,op,"Nothing to drop!");
790 return 0;
791 }
792
793 curinv = op->inv;
794
795 /*
796 This is the default. Drops everything not locked or considered
797 not something that should be dropped.
798 */
799 /*
800 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 if(params==NULL) {
806 while(curinv != NULL) {
807 nextinv = curinv->below;
808 while (nextinv && nextinv->type==MONEY)
809 nextinv = nextinv->below;
810 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && curinv->type != MONEY &&
811 curinv->type != FOOD && curinv->type != KEY &&
812 curinv->type != SPECIAL_KEY && curinv->type != GEM &&
813 !curinv->invisible &&
814 (curinv->type!=CONTAINER || op->container!=curinv))
815 {
816 drop(op,curinv);
817 }
818 curinv = nextinv;
819 }
820 }
821
822 else if(strcmp(params, "weapons") == 0) {
823 while(curinv != NULL) {
824 nextinv = curinv->below;
825 while (nextinv && nextinv->type==MONEY)
826 nextinv = nextinv->below;
827 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ((curinv->type == WEAPON) ||
828 (curinv->type == BOW) || (curinv->type == ARROW)))
829 {
830 drop(op,curinv);
831 }
832 curinv = nextinv;
833 }
834 }
835
836 else if(strcmp(params, "armor") == 0 || strcmp(params, "armour") == 0) {
837 while(curinv != NULL) {
838 nextinv = curinv->below;
839 while (nextinv && nextinv->type==MONEY)
840 nextinv = nextinv->below;
841 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ((curinv->type == ARMOUR) ||
842 curinv->type == SHIELD || curinv->type==HELMET))
843 {
844 drop(op,curinv);
845 }
846 curinv = nextinv;
847 }
848 }
849
850 else if(strcmp(params, "misc") == 0) {
851 while(curinv != NULL) {
852 nextinv = curinv->below;
853 while (nextinv && nextinv->type==MONEY)
854 nextinv = nextinv->below;
855 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ! QUERY_FLAG(curinv,FLAG_APPLIED)) {
856 switch(curinv->type) {
857 case HORN:
858 case BOOK:
859 case SPELLBOOK:
860 case GIRDLE:
861 case AMULET:
862 case RING:
863 case CLOAK:
864 case BOOTS:
865 case GLOVES:
866 case BRACERS:
867 case SCROLL:
868 case ARMOUR_IMPROVER:
869 case WEAPON_IMPROVER:
870 case WAND:
871 case ROD:
872 case POTION:
873 drop(op,curinv);
874 curinv = nextinv;
875 break;
876 default:
877 curinv = nextinv;
878 break;
879 }
880 }
881 curinv = nextinv;
882 }
883 }
884 op->contr->socket.update_look=1;
885 /* draw_look(op);*/
886 return 0;
887 }
888
889 /* Object op wants to drop object(s) params. params can be a
890 * comma seperated list.
891 */
892
893 int command_drop (object *op, char *params)
894 {
895 object *tmp, *next;
896 int did_one=0;
897
898 if (!params) {
899 new_draw_info(NDI_UNIQUE,0, op, "Drop what?");
900 return 0;
901 } else {
902 for (tmp=op->inv; tmp; tmp=next) {
903 next=tmp->below;
904 if (QUERY_FLAG(tmp,FLAG_NO_DROP) ||
905 tmp->invisible) continue;
906 if (item_matched_string(op,tmp,params)) {
907 drop(op, tmp);
908 did_one=1;
909 }
910 }
911 if (!did_one) new_draw_info(NDI_UNIQUE, 0,op,"Nothing to drop.");
912 }
913 if (op->type==PLAYER)
914 {
915 op->contr->count=0;
916 op->contr->socket.update_look=1;
917 };
918 /* draw_look(op);*/
919 return 0;
920 }
921
922 int command_examine (object *op, char *params)
923 {
924 if (!params) {
925 object *tmp=op->below;
926 while (tmp && !LOOK_OBJ(tmp)) tmp=tmp->below;
927 if (tmp) examine(op,tmp);
928 }
929 else {
930 object *tmp=find_best_object_match(op,params);
931 if (tmp)
932 examine(op,tmp);
933 else
934 new_draw_info_format(NDI_UNIQUE,0,op,"Could not find an object that matches %s",params);
935 }
936 return 0;
937 }
938
939 /* op should be a player.
940 * we return the object the player has marked with the 'mark' command
941 * below. If no match is found (or object has changed), we return
942 * NULL. We leave it up to the calling function to print messages if
943 * nothing is found.
944 */
945 object *find_marked_object(object *op)
946 {
947 object *tmp;
948
949 if (!op || !op->contr) return NULL;
950 if (!op->contr->mark) {
951 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
952 return NULL;
953 }
954 /* This may seem like overkill, but we need to make sure that they
955 * player hasn't dropped the item. We use count on the off chance that
956 * an item got reincarnated at some point.
957 */
958 for (tmp=op->inv; tmp; tmp=tmp->below) {
959 if (tmp->invisible) continue;
960 if (tmp == op->contr->mark) {
961 if (tmp->count == op->contr->mark_count)
962 return tmp;
963 else {
964 op->contr->mark=NULL;
965 op->contr->mark_count=0;
966 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
967 return NULL;
968 }
969 }
970 }
971 return NULL;
972 }
973
974
975 /* op should be a player, params is any params.
976 * If no params given, we print out the currently marked object.
977 * otherwise, try to find a matching object - try best match first.
978 */
979 int command_mark(object *op, char *params)
980 {
981 if (!op->contr) return 1;
982 if (!params) {
983 object *mark=find_marked_object(op);
984 if (!mark) new_draw_info(NDI_UNIQUE,0,op,"You have no marked object.");
985 else new_draw_info_format(NDI_UNIQUE,0,op,"%s is marked.", query_name(mark));
986 }
987 else {
988 object *mark1=find_best_object_match(op, params);
989 if (!mark1) {
990 new_draw_info_format(NDI_UNIQUE,0,op,"Could not find an object that matches %s",params);
991 return 1;
992 }
993 else {
994 op->contr->mark=mark1;
995 op->contr->mark_count=mark1->count;
996 new_draw_info_format(NDI_UNIQUE,0,op,"Marked item %s", query_name(mark1));
997 return 0;
998 }
999 }
1000 return 0; /*shouldnt get here */
1001 }
1002
1003
1004 /* op is the player
1005 * tmp is the monster being examined.
1006 */
1007 void examine_monster(object *op,object *tmp) {
1008 object *mon=tmp->head?tmp->head:tmp;
1009
1010 if(QUERY_FLAG(mon,FLAG_UNDEAD))
1011 new_draw_info(NDI_UNIQUE, 0,op,"It is an undead force.");
1012 if(mon->level>op->level)
1013 new_draw_info(NDI_UNIQUE, 0,op,"It is likely more powerful than you.");
1014 else if(mon->level<op->level)
1015 new_draw_info(NDI_UNIQUE, 0,op,"It is likely less powerful than you.");
1016 else
1017 new_draw_info(NDI_UNIQUE, 0,op,"It is probably as powerful as you.");
1018 if(mon->attacktype&AT_ACID)
1019 new_draw_info(NDI_UNIQUE, 0,op,"You seem to smell an acrid odor.");
1020
1021 /* Anyone know why this used to use the clone value instead of the
1022 * maxhp field? This seems that it should give more accurate results.
1023 */
1024 switch((mon->stats.hp+1)*4/(mon->stats.maxhp+1)) { /* From 1-4 */
1025 case 1:
1026 new_draw_info(NDI_UNIQUE, 0,op,"It is in a bad shape.");
1027 break;
1028 case 2:
1029 new_draw_info(NDI_UNIQUE, 0,op,"It is hurt.");
1030 break;
1031 case 3:
1032 new_draw_info(NDI_UNIQUE, 0,op,"It is somewhat hurt.");
1033 break;
1034 case 4:
1035 new_draw_info(NDI_UNIQUE, 0,op,"It is in excellent shape.");
1036 break;
1037 }
1038 if(present_in_ob(POISONING,mon)!=NULL)
1039 new_draw_info(NDI_UNIQUE, 0,op,"It looks very ill.");
1040 }
1041
1042
1043 /* tmp is the object being described, pl is who is examing it. */
1044 char *long_desc(object *tmp, object *pl) {
1045 static char buf[VERY_BIG_BUF];
1046 char *cp;
1047
1048 if(tmp==NULL)
1049 return "";
1050
1051 buf[0]='\0';
1052 switch(tmp->type) {
1053 case RING:
1054 case SKILL:
1055 case WEAPON:
1056 case ARMOUR:
1057 case BRACERS:
1058 case HELMET:
1059 case SHIELD:
1060 case BOOTS:
1061 case GLOVES:
1062 case AMULET:
1063 case GIRDLE:
1064 case BOW:
1065 case ARROW:
1066 case CLOAK:
1067 case FOOD:
1068 case DRINK:
1069 case FLESH:
1070 case SKILL_TOOL:
1071 case POWER_CRYSTAL:
1072 if(*(cp=describe_item(tmp, pl))!='\0') {
1073 int len;
1074
1075 strncpy(buf,query_name(tmp), VERY_BIG_BUF-1);
1076 buf[VERY_BIG_BUF-1]=0;
1077 len=strlen(buf);
1078 if (len<VERY_BIG_BUF-5) {
1079 /* Since we know the length, we save a few cpu cycles by using
1080 * it instead of calling strcat */
1081 strcpy(buf+len," ");
1082 len++;
1083 strncpy(buf+len, cp, VERY_BIG_BUF-len-1);
1084 buf[VERY_BIG_BUF-1]=0;
1085 }
1086 }
1087 }
1088 if(buf[0]=='\0') {
1089 strncpy(buf,query_name(tmp), VERY_BIG_BUF-1);
1090 buf[VERY_BIG_BUF-1]=0;
1091 }
1092
1093 return buf;
1094 }
1095
1096 void examine(object *op, object *tmp) {
1097 char buf[VERY_BIG_BUF];
1098 int i;
1099
1100 if (tmp == NULL || tmp->type == CLOSE_CON)
1101 return;
1102
1103 strcpy(buf,"That is ");
1104 strncat(buf, long_desc(tmp, op), VERY_BIG_BUF-strlen(buf)-1);
1105 buf[VERY_BIG_BUF-1]=0;
1106
1107 new_draw_info(NDI_UNIQUE, 0,op,buf);
1108 buf[0]='\0';
1109
1110 if(tmp->custom_name) {
1111 strcpy(buf,"You call it ");
1112 strncat(buf, tmp->custom_name, VERY_BIG_BUF-strlen(buf)-1);
1113 buf[VERY_BIG_BUF-1]=0;
1114 new_draw_info(NDI_UNIQUE, 0,op,buf);
1115 buf[0]='\0';
1116 }
1117
1118 switch(tmp->type) {
1119 case SPELLBOOK:
1120 if(QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->inv ) {
1121 sprintf(buf,"%s is a %s level %s spell",
1122 tmp->inv->name, get_levelnumber(tmp->inv->level),
1123 tmp->inv->skill);
1124 }
1125 break;
1126
1127 case BOOK:
1128 if(tmp->msg!=NULL)
1129 strcpy(buf,"Something is written in it.");
1130 break;
1131
1132 case CONTAINER:
1133 if(tmp->race!=NULL) {
1134 if(tmp->weight_limit && tmp->stats.Str<100)
1135 sprintf (buf,"It can hold only %s and its weight limit is %.1f kg.",
1136 tmp->race, tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
1137 else
1138 sprintf (buf,"It can hold only %s.", tmp->race);
1139 } else
1140 if(tmp->weight_limit && tmp->stats.Str<100)
1141 sprintf (buf,"Its weight limit is %.1f kg.",
1142 tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
1143 break;
1144
1145 case WAND:
1146 if(QUERY_FLAG(tmp, FLAG_IDENTIFIED))
1147 sprintf(buf,"It has %d charges left.",tmp->stats.food);
1148 break;
1149 }
1150
1151 if(buf[0]!='\0')
1152 new_draw_info(NDI_UNIQUE, 0,op,buf);
1153
1154 if(tmp->materialname != NULL && !tmp->msg) {
1155 sprintf(buf, "It is made of: %s.", tmp->materialname);
1156 new_draw_info(NDI_UNIQUE, 0, op, buf);
1157 }
1158 /* Where to wear this item */
1159 for (i=0; i < NUM_BODY_LOCATIONS; i++) {
1160 if (tmp->body_info[i]<-1) {
1161 if (op->body_info[i])
1162 new_draw_info_format(NDI_UNIQUE, 0,op,
1163 "It goes %s (%d)", body_locations[i].use_name, -tmp->body_info[i]);
1164 else
1165 new_draw_info_format(NDI_UNIQUE, 0,op,
1166 "It goes %s", body_locations[i].nonuse_name);
1167 } else if (tmp->body_info[i]) {
1168 if (op->body_info[i])
1169 new_draw_info_format(NDI_UNIQUE, 0,op,
1170 "It goes %s", body_locations[i].use_name);
1171 else
1172 new_draw_info_format(NDI_UNIQUE, 0,op,
1173 "It goes %s", body_locations[i].nonuse_name);
1174 }
1175 }
1176
1177 if(tmp->weight) {
1178 sprintf(buf,tmp->nrof>1?"They weigh %3.3f kg.":"It weighs %3.3f kg.",
1179 tmp->weight*(tmp->nrof?tmp->nrof:1)/1000.0);
1180 new_draw_info(NDI_UNIQUE, 0,op,buf);
1181 }
1182
1183 if (tmp->value && !QUERY_FLAG(tmp, FLAG_STARTEQUIP) && !QUERY_FLAG(tmp, FLAG_NO_PICK)) {
1184 object *floor;
1185 sprintf(buf,"You reckon %s worth %s.",
1186 tmp->nrof>1?"they are":"it is",query_cost_string(tmp,op, F_TRUE | F_APPROX));
1187 new_draw_info(NDI_UNIQUE, 0,op,buf);
1188 floor = get_map_ob (op->map, op->x, op->y);
1189 if (floor && floor->type == SHOP_FLOOR) {
1190 if(QUERY_FLAG(tmp, FLAG_UNPAID))
1191 sprintf(buf,"%s would cost you %s.",
1192 tmp->nrof>1?"They":"It",query_cost_string(tmp,op,F_BUY | F_SHOP));
1193 else
1194 sprintf(buf,"You are offered %s for %s.",
1195 query_cost_string(tmp,op,F_SELL+F_SHOP), tmp->nrof>1?"them":"it");
1196 new_draw_info(NDI_UNIQUE, 0,op,buf);
1197 }
1198 }
1199
1200 if(QUERY_FLAG(tmp, FLAG_MONSTER))
1201 examine_monster(op,tmp);
1202
1203 /* Is this item buildable? */
1204 if ( QUERY_FLAG( tmp, FLAG_IS_BUILDABLE ) )
1205 new_draw_info( NDI_UNIQUE, 0, op, "This is a buildable item." );
1206
1207 /* Does the object have a message? Don't show message for all object
1208 * types - especially if the first entry is a match
1209 */
1210 if(tmp->msg && tmp->type != EXIT && tmp->type != BOOK &&
1211 tmp->type != CORPSE && !tmp->move_on &&
1212 strncasecmp(tmp->msg, "@match",7)) {
1213
1214 /* This is just a hack so when identifying the items, we print
1215 * out the extra message
1216 */
1217 if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))
1218 new_draw_info(NDI_UNIQUE, 0,op, "The object has a story:");
1219
1220 new_draw_info(NDI_UNIQUE, 0,op,tmp->msg);
1221 }
1222 new_draw_info(NDI_UNIQUE, 0,op," "); /* Blank line */
1223 }
1224
1225 /*
1226 * inventory prints object's inventory. If inv==NULL then print player's
1227 * inventory.
1228 * [ Only items which are applied are showed. Tero.Haatanen@lut.fi ]
1229 */
1230 void inventory(object *op,object *inv) {
1231 object *tmp;
1232 char *in;
1233 int items = 0, length;
1234
1235 if (inv==NULL && op==NULL) {
1236 new_draw_info(NDI_UNIQUE, 0,op,"Inventory of what object?");
1237 return;
1238 }
1239 tmp = inv ? inv->inv : op->inv;
1240
1241 while (tmp) {
1242 if ((!tmp->invisible &&
1243 (inv==NULL || inv->type == CONTAINER || QUERY_FLAG(tmp, FLAG_APPLIED)))
1244 || (!op || QUERY_FLAG(op, FLAG_WIZ)))
1245 items++;
1246 tmp=tmp->below;
1247 }
1248 if (inv==NULL) { /* player's inventory */
1249 if (items==0) {
1250 new_draw_info(NDI_UNIQUE, 0,op,"You carry nothing.");
1251 return;
1252 } else {
1253 length = 28;
1254 in = "";
1255 if (op)
1256 clear_win_info(op);
1257 new_draw_info(NDI_UNIQUE, 0,op,"Inventory:");
1258 }
1259 } else {
1260 if (items==0)
1261 return;
1262 else {
1263 length = 28;
1264 in = " ";
1265 }
1266 }
1267 for (tmp=inv?inv->inv:op->inv; tmp; tmp=tmp->below) {
1268 if((!op||!QUERY_FLAG(op, FLAG_WIZ)) && (tmp->invisible ||
1269 (inv && inv->type != CONTAINER && !QUERY_FLAG(tmp, FLAG_APPLIED))))
1270 continue;
1271 if((!op || QUERY_FLAG(op, FLAG_WIZ)))
1272 new_draw_info_format(NDI_UNIQUE, 0,op ,"%s- %-*.*s (%5d) %-8s", in, length, length,
1273 query_name(tmp), tmp->count,query_weight(tmp));
1274 else
1275 new_draw_info_format(NDI_UNIQUE,0, op, "%s- %-*.*s %-8s", in, length+8,
1276 length+8, query_name(tmp),
1277 query_weight(tmp));
1278 }
1279 if(!inv && op) {
1280 new_draw_info_format(NDI_UNIQUE,0, op ,"%-*s %-8s",
1281 41,"Total weight :",query_weight(op));
1282 }
1283 }
1284
1285 static void display_new_pickup( object* op )
1286 {
1287 int i = op->contr->mode;
1288
1289 if(!(i & PU_NEWMODE)) return;
1290
1291 new_draw_info_format(NDI_UNIQUE, 0,op,"%d NEWMODE",i & PU_NEWMODE?1:0);
1292 new_draw_info_format(NDI_UNIQUE, 0,op,"%d DEBUG",i & PU_DEBUG?1:0);
1293 new_draw_info_format(NDI_UNIQUE, 0,op,"%d INHIBIT",i & PU_INHIBIT?1:0);
1294 new_draw_info_format(NDI_UNIQUE, 0,op,"%d STOP",i & PU_STOP?1:0);
1295
1296 new_draw_info_format(NDI_UNIQUE, 0,op,"%d <= x pickup weight/value RATIO (0==off)",(i & PU_RATIO)*5);
1297
1298 new_draw_info_format(NDI_UNIQUE, 0,op,"%d FOOD",i & PU_FOOD?1:0);
1299 new_draw_info_format(NDI_UNIQUE, 0,op,"%d DRINK",i & PU_DRINK?1:0);
1300 new_draw_info_format(NDI_UNIQUE, 0,op,"%d VALUABLES",i & PU_VALUABLES?1:0);
1301
1302 new_draw_info_format(NDI_UNIQUE, 0,op,"%d BOW",i & PU_BOW?1:0);
1303 new_draw_info_format(NDI_UNIQUE, 0,op,"%d ARROW",i & PU_ARROW?1:0);
1304
1305 new_draw_info_format(NDI_UNIQUE, 0,op,"%d HELMET",i & PU_HELMET?1:0);
1306 new_draw_info_format(NDI_UNIQUE, 0,op,"%d SHIELD",i & PU_SHIELD?1:0);
1307 new_draw_info_format(NDI_UNIQUE, 0,op,"%d ARMOUR",i & PU_ARMOUR?1:0);
1308
1309 new_draw_info_format(NDI_UNIQUE, 0,op,"%d BOOTS",i & PU_BOOTS?1:0);
1310 new_draw_info_format(NDI_UNIQUE, 0,op,"%d GLOVES",i & PU_GLOVES?1:0);
1311 new_draw_info_format(NDI_UNIQUE, 0,op,"%d CLOAK",i & PU_CLOAK?1:0);
1312 new_draw_info_format(NDI_UNIQUE, 0,op,"%d KEY",i & PU_KEY?1:0);
1313
1314 new_draw_info_format(NDI_UNIQUE, 0,op,"%d MISSILEWEAPON",i & PU_MISSILEWEAPON?1:0);
1315 new_draw_info_format(NDI_UNIQUE, 0,op,"%d ALLWEAPON",i & PU_ALLWEAPON?1:0);
1316 new_draw_info_format(NDI_UNIQUE, 0,op,"%d MAGICAL",i & PU_MAGICAL?1:0);
1317 new_draw_info_format(NDI_UNIQUE, 0,op,"%d POTION",i & PU_POTION?1:0);
1318
1319 new_draw_info_format(NDI_UNIQUE, 0,op,"%d SPELLBOOK",i & PU_SPELLBOOK?1:0);
1320 new_draw_info_format(NDI_UNIQUE, 0,op,"%d SKILLSCROLL",i & PU_SKILLSCROLL?1:0);
1321 new_draw_info_format(NDI_UNIQUE, 0,op,"%d READABLES",i & PU_READABLES?1:0);
1322 new_draw_info_format(NDI_UNIQUE, 0,op,"%d MAGICDEVICE", i & PU_MAGIC_DEVICE?1:0);
1323
1324 new_draw_info_format(NDI_UNIQUE, 0,op,"%d NOT CURSED", i & PU_NOT_CURSED?1:0);
1325
1326 new_draw_info_format(NDI_UNIQUE, 0,op,"%d JEWELS", i & PU_JEWELS?1:0);
1327
1328 new_draw_info_format(NDI_UNIQUE, 0,op,"");
1329 }
1330
1331 int command_pickup (object *op, char *params)
1332 {
1333 uint32 i;
1334 static const char* names[ ] = {
1335 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1336 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1337 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed", "jewels", NULL };
1338 static uint32 modes[ ] = {
1339 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1340 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1341 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED, PU_JEWELS, 0 };
1342
1343 if(!params) {
1344 /* if the new mode is used, just print the settings */
1345 if(op->contr->mode & PU_NEWMODE)
1346 {
1347 display_new_pickup( op );
1348 return 1;
1349 }
1350 if(1) LOG(llevDebug, "command_pickup: !params\n");
1351 set_pickup_mode(op, (op->contr->mode > 6)? 0: op->contr->mode+1);
1352 return 0;
1353 }
1354
1355 while ( *params == ' ' && *params )
1356 params++;
1357
1358 if ( *params == '+' || *params == '-' )
1359 {
1360 int mode;
1361 for ( mode = 0; names[ mode ]; mode++ )
1362 {
1363 if ( !strcmp( names[ mode ], params + 1 ) )
1364 {
1365 i = op->contr->mode;
1366 if ( !( i & PU_NEWMODE ) )
1367 i = PU_NEWMODE;
1368 if ( *params == '+' )
1369 i = i | modes[ mode ];
1370 else
1371 i = i & ~modes[ mode ];
1372 op->contr->mode = i;
1373 display_new_pickup( op );
1374 return 1;
1375 }
1376 }
1377 new_draw_info_format( NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params );
1378 return 1;
1379 }
1380
1381 if(sscanf(params, "%u", &i) != 1) {
1382 if(1) LOG(llevDebug, "command_pickup: params==NULL\n");
1383 new_draw_info(NDI_UNIQUE, 0,op,"Usage: pickup <0-7> or <value_density> .");
1384 return 1;
1385 }
1386 set_pickup_mode(op,i);
1387
1388 return 1;
1389 }
1390
1391 void set_pickup_mode(object *op,int i) {
1392 switch(op->contr->mode=i) {
1393 case 0:
1394 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Don't pick up.");
1395 break;
1396 case 1:
1397 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item.");
1398 break;
1399 case 2:
1400 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item and stop.");
1401 break;
1402 case 3:
1403 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Stop before picking up.");
1404 break;
1405 case 4:
1406 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items.");
1407 break;
1408 case 5:
1409 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items and stop.");
1410 break;
1411 case 6:
1412 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all magic items.");
1413 break;
1414 case 7:
1415 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all coins and gems");
1416 break;
1417 }
1418 }
1419
1420 int command_search_items (object *op, char *params)
1421 {
1422 char buf[MAX_BUF];
1423
1424 if (settings.search_items == FALSE)
1425 return 1;
1426
1427 if(params == NULL) {
1428 if(op->contr->search_str[0]=='\0') {
1429 new_draw_info(NDI_UNIQUE, 0,op,"Example: search magic+1");
1430 new_draw_info(NDI_UNIQUE, 0,op,"Would automatically pick up all");
1431 new_draw_info(NDI_UNIQUE, 0,op,"items containing the word 'magic+1'.");
1432 return 1;
1433 }
1434 op->contr->search_str[0]='\0';
1435 new_draw_info(NDI_UNIQUE, 0,op,"Search mode turned off.");
1436 fix_player(op);
1437 return 1;
1438 }
1439 if((int)strlen(params) >= MAX_BUF) {
1440 new_draw_info(NDI_UNIQUE, 0,op,"Search string too long.");
1441 return 1;
1442 }
1443 strcpy(op->contr->search_str, params);
1444 sprintf(buf,"Searching for '%s'.",op->contr->search_str);
1445 new_draw_info(NDI_UNIQUE, 0,op,buf);
1446 fix_player(op);
1447 return 1;
1448 }
1449
1450 /*
1451 * Changing the custom name of an item
1452 *
1453 * Syntax is: rename <what object> to <new name>
1454 * if '<what object>' is omitted, marked object is used
1455 * if 'to <new name>' is omitted, custom name is cleared
1456 *
1457 * Names are considered for all purpose having a length <=127 (max length sent to client
1458 * by server) */
1459
1460 int command_rename_item(object *op, char *params)
1461 {
1462 char buf[VERY_BIG_BUF];
1463 int itemnumber;
1464 object *item=NULL;
1465 char *closebrace;
1466 size_t counter;
1467
1468 if (params) {
1469 /* Let's skip white spaces */
1470 while(' '==*params) params++;
1471
1472 /* Checking the first part */
1473 if ((itemnumber = atoi(params))!=0) {
1474 for (item=op->inv; item && ((item->count != itemnumber) || item->invisible); item=item->below);
1475 if (!item) {
1476 new_draw_info(NDI_UNIQUE,0,op,"Tried to rename an invalid item.");
1477 return 1;
1478 }
1479 while(isdigit(*params) || ' '==*params) params++;
1480 }
1481 else if ('<'==*params) {
1482 /* Got old name, let's get it & find appropriate matching item */
1483 closebrace=strchr(params,'>');
1484 if(!closebrace) {
1485 new_draw_info(NDI_UNIQUE,0,op,"Syntax error!");
1486 return 1;
1487 }
1488 /* Sanity check for buffer overruns */
1489 if((closebrace-params)>127) {
1490 new_draw_info(NDI_UNIQUE,0,op,"Old name too long (up to 127 characters allowed)!");
1491 return 1;
1492 }
1493 /* Copy the old name */
1494 strncpy(buf,params+1,closebrace-params-1);
1495 buf[closebrace-params-1]='\0';
1496
1497 /* Find best matching item */
1498 item=find_best_object_match(op,buf);
1499 if(!item) {
1500 new_draw_info(NDI_UNIQUE,0,op,"Could not find a matching item to rename.");
1501 return 1;
1502 }
1503
1504 /* Now need to move pointer to just after > */
1505 params=closebrace+1;
1506 while(' '==*params) params++;
1507
1508 } else {
1509 /* Use marked item */
1510 item=find_marked_object(op);
1511 if(!item) {
1512 new_draw_info(NDI_UNIQUE,0,op,"No marked item to rename.");
1513 return 1;
1514 }
1515 }
1516
1517 /* Now let's find the new name */
1518 if(!strncmp(params,"to ",3)) {
1519 params+=3;
1520 while(' '==*params) params++;
1521 if('<'!=*params) {
1522 new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expecting < at start of new name!");
1523 return 1;
1524 }
1525 closebrace=strchr(params+1,'>');
1526 if(!closebrace) {
1527 new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expecting > at end of new name!");
1528 return 1;
1529 }
1530
1531 /* Sanity check for buffer overruns */
1532 if((closebrace-params)>127) {
1533 new_draw_info(NDI_UNIQUE,0,op,"New name too long (up to 127 characters allowed)!");
1534 return 1;
1535 }
1536
1537 /* Copy the new name */
1538 strncpy(buf,params+1,closebrace-params-1);
1539 buf[closebrace-params-1]='\0';
1540
1541 /* Let's check it for weird characters */
1542 for(counter=0;counter<strlen(buf);counter++) {
1543 if(isalnum(buf[counter])) continue;
1544 if(' '==buf[counter]) continue;
1545 if('\''==buf[counter]) continue;
1546 if('+'==buf[counter]) continue;
1547 if('_'==buf[counter]) continue;
1548 if('-'==buf[counter]) continue;
1549
1550 /* If we come here, then the name contains an invalid character...
1551 tell the player & exit */
1552 new_draw_info(NDI_UNIQUE,0,op,"Invalid new name!");
1553 return 1;
1554 }
1555
1556 } else {
1557 /* If param contains something, then syntax error... */
1558 if(strlen(params)) {
1559 new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expected 'to <' after old name!");
1560 return 1;
1561 }
1562 /* New name is empty */
1563 buf[0]='\0';
1564 }
1565 } else {
1566 /* Last case: params==NULL */
1567 item=find_marked_object(op);
1568 if(!item) {
1569 new_draw_info(NDI_UNIQUE,0,op,"No marked item to rename.");
1570 return 1;
1571 }
1572 buf[0]='\0';
1573 }
1574
1575 if (QUERY_FLAG(item, FLAG_UNPAID)) {
1576 new_draw_info(NDI_UNIQUE,0,op,"You can't rename an unpaid item! You should pay for it first.");
1577 return 1;
1578 }
1579
1580 /* Coming here, everything is fine... */
1581 if(!strlen(buf)) {
1582 /* Clear custom name */
1583 if(item->custom_name) {
1584 FREE_AND_CLEAR_STR(item->custom_name);
1585
1586 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));
1587 esrv_update_item(UPD_NAME,op,item);
1588 } else {
1589 new_draw_info(NDI_UNIQUE,0,op,"This item has no custom name.");
1590 }
1591 } else {
1592 /* Set custom name */
1593 FREE_AND_COPY(item->custom_name,buf);
1594
1595 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);
1596 esrv_update_item(UPD_NAME,op,item);
1597 }
1598
1599 return 1;
1600 }