ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.c
Revision: 1.1.1.2 (vendor branch)
Committed: Wed Feb 22 18:03:20 2006 UTC (18 years, 3 months ago) by elmex
Content type: text/plain
Branch: UPSTREAM
CVS Tags: UPSTREAM_2006_02_22
Changes since 1.1.1.1: +56 -41 lines
Log Message:
cvs -z7 -d:ext:elmex@cvs.schmorp.de:/schmorpforge import cf.schmorp.de UPSTREAM UPSTREAM_2006_02_22

File Contents

# User Rev Content
1 root 1.1 /*
2     * static char *rcsid_c_object_c =
3 elmex 1.1.1.2 * "$Id: c_object.c,v 1.77 2006/02/07 07:54:46 mwedel Exp $";
4 root 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     /* Search the inventory of 'pl' for what matches best with params.
58     * we use item_matched_string above - this gives us consistent behaviour
59     * between many commands. Return the best match, or NULL if no match.
60     */
61     object *find_best_object_match(object *pl, const char *params)
62     {
63     object *tmp, *best=NULL;
64     int match_val=0,tmpmatch;
65    
66     for (tmp=pl->inv; tmp; tmp=tmp->below) {
67     if (tmp->invisible) continue;
68     if ((tmpmatch=item_matched_string(pl, tmp, params))>match_val) {
69     match_val=tmpmatch;
70     best=tmp;
71     }
72     }
73     return best;
74     }
75     /* Simlilar to find_best_object_match , but accepts an
76     * additional parameter for apply -u , and apply -a to
77     * only unapply applied , or apply unapplied objects
78     */
79     static object *find_best_apply_object_match(object *pl, char *params, enum apply_flag aflag)
80     {
81     object *tmp, *best=NULL;
82     int match_val=0,tmpmatch;
83    
84     for (tmp=pl->inv; tmp; tmp=tmp->below) {
85     if (tmp->invisible) continue;
86     if ((tmpmatch=item_matched_string(pl, tmp, params))>match_val) {
87     if ((aflag==AP_APPLY) && (QUERY_FLAG(tmp,FLAG_APPLIED))) continue;
88     if ((aflag==AP_UNAPPLY) && (!QUERY_FLAG(tmp,FLAG_APPLIED))) continue;
89     match_val=tmpmatch;
90     best=tmp;
91     }
92     }
93     return best;
94     }
95    
96     /*
97     * Notes about item creation:
98     * 1) It is similar in syntax to the dm create command.
99     * 2) It requires a player to have a buildfacility below him, a tool in his
100     * possesion, and materials to build with.
101     * 3) The random roll is done in a loop, so if the player tries to make 100,
102     * he makes 100 checks.
103     * 4) Exp is given only on succ. creations, but materials are used regardless.
104     * 5) The properties of the tool are stored in tooltype and weapontype.
105     * 6) The properties of the buildfacilities are stored in tooltype.
106     * 7) For now, all ingredients must be type 73 INORGANIC.
107     * 8) The player can attempt to create any arch, but item_power and value
108     * will prevent most artifacts from being built.
109     * 9) The code allows magic bonuses up to +5. It is not trivial to make a +5
110     * item.
111     *10) If you ever extend it beyond +5, add more gemtypes. Currently the code
112     * looks for gemcost gems per item, per plus. So a +5 item requires
113     * gemcost pearls,rubies,emeralds,sapphires and diamonds. Not cheap.
114     *11) There are a zillion if statements in this code. Alot of checking takes
115     * place here. All of them are needed.
116     */
117    
118     int command_build (object *pl, char *params) {
119     return 0;
120     #if 0
121     object *marked, *facility, *tool, *newobj, *tmp;
122     archetype *at;
123     int skillnr, obpow, number, bonus, mneed, nrof, magic, i, nummade, found;
124     int gemcost;
125     char *bp;
126     materialtype_t *mt;
127    
128     /* NOTE THIS FUNCTION IS CURRENTLY DISABLED */
129    
130     /* Given this is currently disabled, I'm not going to bother updating
131     * it with the new skill system. IT really needs to get the skill object
132     * pointer in a better fashion than it is currently doing.
133     */
134     if (!params) {
135     new_draw_info(NDI_UNIQUE, 0, pl, "Usage:build [nr] [+magic] <object>");
136     return 0;
137     }
138     marked = find_marked_object(pl);
139     if (marked == NULL || !marked->material || marked->materialname == NULL ||
140     marked->type != INORGANIC) {
141     new_draw_info(NDI_UNIQUE, 0, pl, "You must mark some ingredients.");
142     return 0;
143     }
144     while (*params==' ')
145     params++;
146     bp = params;
147     nrof = 1;
148     magic = 0;
149    
150     if (sscanf(bp, "%d ", &nrof)) {
151     if ((bp = strchr(params, ' ')) == NULL) {
152     new_draw_info(NDI_UNIQUE, 0, pl,
153     "Usage: build [nr] [+magic] <object>");
154     return 0;
155     }
156     bp++;
157     }
158     if (sscanf(bp, "+%d ", &magic)) {
159     if ((bp = strchr(bp, ' ')) == NULL) {
160     new_draw_info(NDI_UNIQUE, 0, pl,
161     "Usage: build [nr] [+magic] <object>");
162     return 0;
163     }
164     bp++;
165     }
166     while (*bp==' ')
167     bp++;
168     at=find_archetype_by_object_name(bp);
169     if (at == NULL) {
170     new_draw_info_format(NDI_UNIQUE, 0, pl,
171     "You don't know how to make a %s.", bp);
172     return 0;
173     }
174     newobj = get_object();
175     copy_object(&at->clone, newobj);
176    
177     skillnr = -1;
178    
179     if ((IS_ARMOR(newobj) && newobj->material != M_LEATHER) ||
180     newobj->type == WEAPON)
181     skillnr = SK_SMITH;
182    
183     if (IS_ARMOR(newobj) && newobj->material == M_LEATHER)
184     skillnr = SK_WOODSMAN;
185    
186     if (newobj->type == BOW || newobj->type == ARROW)
187     skillnr = SK_BOWYER;
188    
189     if (skillnr == -1) {
190     new_draw_info(NDI_UNIQUE, 0, pl, "You don't know how to create that.");
191     return 0;
192     }
193    
194     if (!change_skill(pl, skillnr, 0)) {
195     new_draw_info(NDI_UNIQUE, 0, pl,
196     "You lack the needed skill to make that item.");
197     return 0;
198     }
199     facility = NULL;
200     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
201     if (tmp->type == BUILDFAC && tmp->tooltype == newobj->type)
202     facility=tmp;
203     if (facility == NULL) {
204     new_draw_info(NDI_UNIQUE, 0, pl, "You lack a suitable workspace.");
205     return 0;
206     }
207     if (magic && !(IS_ARMOR(newobj) || IS_WEAPON(newobj))) {
208     new_draw_info(NDI_UNIQUE, 0, pl, "A magical bonus is only valid with "
209     "armour and weapons.");
210     return 0;
211     }
212    
213     /* use newobj->weapontype == tool->weapontype for building weapons */
214     /* use newobj->material == tool->weapontype for building armour */
215     /* newobj->type == tool->tooltype */
216     tool = NULL;
217     for (tmp=pl->inv; tmp; tmp=tmp->below) {
218     if (tmp->type != TOOL)
219     continue;
220     if (IS_ARMOR(newobj) && (newobj->material & tmp->weapontype) &&
221     newobj->type == tmp->tooltype) {
222     if (tool == NULL ||
223     (tool->level + tool->magic) < (tmp->level + tmp->magic))
224     tool = tmp;
225     } else if (IS_WEAPON(newobj) && (newobj->weapontype&tmp->weapontype) &&
226     newobj->type == tmp->tooltype) {
227     if (tool == NULL ||
228     (tool->level + tool->magic) < (tmp->level + tmp->magic))
229     tool = tmp;
230     }
231     /* should split off bows arrows and probably bolts around here */
232     }
233     if (tool == NULL) {
234     new_draw_info(NDI_UNIQUE, 0, pl, "You lack the required tools.");
235     return 0;
236     }
237    
238     mt = name_to_material(marked->materialname);
239     if (mt == NULL) {
240     new_draw_info(NDI_UNIQUE, 0, pl, "Your raw materials are garbage.");
241     return 0;
242     }
243     if (magic < 0) {
244     new_draw_info(NDI_UNIQUE, 0, pl, "You cannot create cursed objects.");
245     return 0;
246     }
247     if (magic > 0 && SK_level(pl)/20 < magic) {
248     new_draw_info(NDI_UNIQUE, 0, pl, "You are not powerful enough to "
249     "create such a magical item.");
250     return 0;
251     }
252    
253     gemcost = 100;
254     if (newobj->type == ARROW)
255     gemcost = 1;
256     if (magic > 0) {
257     found = 0;
258     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
259     if (tmp->type == GEM && !strcmp(tmp->arch->name, "pearl") &&
260     tmp->nrof >= gemcost*nrof*mt->value/100)
261     found++;
262     if (magic > 1)
263     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
264     if (tmp->type == GEM && !strcmp(tmp->arch->name, "emerald") &&
265     tmp->nrof >= gemcost*nrof*mt->value/100)
266     found++;
267     if (magic > 2)
268     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
269     if (tmp->type == GEM && !strcmp(tmp->arch->name, "sapphire") &&
270     tmp->nrof >= gemcost*nrof*mt->value/100)
271     found++;
272     if (magic > 3)
273     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
274     if (tmp->type == GEM && !strcmp(tmp->arch->name, "ruby") &&
275     tmp->nrof >= gemcost*nrof*mt->value/100)
276     found++;
277     if (magic > 4)
278     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
279     if (tmp->type == GEM && !strcmp(tmp->arch->name, "diamond") &&
280     tmp->nrof >= gemcost*nrof*mt->value/100)
281     found++;
282     if (found < magic) {
283     new_draw_info(NDI_UNIQUE, 0, pl, "You did not provide a suitable "
284     "sacrifice of gems on the ground to add this much magic.");
285     return 0;
286     }
287     if (25*pow(3, magic)*mt->value/100 > pl->stats.sp) {
288     new_draw_info(NDI_UNIQUE, 0, pl, "You do not have enough mana "
289     "to create this object.");
290     return 0;
291     }
292     }
293    
294     /* good lord. Now we have a tool, facilites, materials (marked) and an
295     object we want to create. Thats alot of if's */
296    
297     obpow = (newobj->item_power + newobj->value/1000 + 1)*mt->value/100;
298     mneed = nrof*((newobj->weight * mt->weight)/80);
299     /* cost can be balanced out by cost to disassemble items for materials */
300     if ((marked->weight * MAX(1, marked->nrof)) < mneed) {
301     new_draw_info_format(NDI_UNIQUE, 0, pl, "You do not have enough %s.",
302     marked->name);
303     return 0;
304     }
305     if (obpow > (tool->level+tool->magic)) {
306     new_draw_info_format(NDI_UNIQUE, 0, pl, "Your %s is not capable of "
307     "crafting such a complex item.", tool->name);
308     return 0;
309     }
310     set_abs_magic(newobj, magic);
311     set_materialname(newobj, 1, mt);
312     for (i=0, nummade=0; i< nrof; i++) {
313     bonus = tool->level+tool->magic - obpow;
314     number = rndm(1, 3*obpow*magic);
315     LOG(llevDebug, "command_build: skill:%d obpow:%d rndm:%d tool:%s "
316     "newobj:%s marked:%s magic:%d\n", SK_level(pl)+bonus, obpow,
317     number, tool->name, newobj->name, marked->name, magic);
318     if (SK_level(pl)+bonus > number) {
319     /* wow, we actually created something */
320     newobj->x = pl->x;
321     newobj->y = pl->y;
322     newobj->map = pl->map;
323     SET_FLAG(newobj, FLAG_IDENTIFIED);
324     if (i == 0)
325     newobj = insert_ob_in_ob(newobj, pl);
326     else
327     newobj->nrof++;
328     esrv_send_item(pl, newobj);
329     nummade++;
330     } else {
331     free_object(newobj);
332     if (bonus < rndm(1, number-SK_level(pl)+bonus)) {
333     new_draw_info_format(NDI_UNIQUE, 0, pl,
334     "You broke your %s!\n", tool->name);
335     esrv_del_item(pl->contr, tool->count);
336     remove_ob(tool);
337     free_object(tool);
338     break;
339     }
340     }
341     /* take away materials too */
342     tmp = get_split_ob(marked, MAX(1, mneed/marked->weight));
343     if (tmp)
344     free_object(tmp);
345     if (marked->nrof < 1)
346     esrv_del_item(pl->contr, marked->count);
347     else
348     esrv_send_item(pl, marked);
349     }
350     if (magic)
351     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
352     if (tmp->type == GEM && !strcmp(tmp->arch->name, "pearl") &&
353     tmp->nrof >= gemcost*nrof*mt->value/100)
354     tmp->nrof -= gemcost*nrof*mt->value/100;
355     if (magic > 1)
356     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
357     if (tmp->type == GEM && !strcmp(tmp->arch->name, "emerald") &&
358     tmp->nrof >= gemcost*nrof*mt->value/100)
359     tmp->nrof -= gemcost*nrof*mt->value/100;
360     if (magic > 2)
361     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
362     if (tmp->type == GEM && !strcmp(tmp->arch->name, "sapphire") &&
363     tmp->nrof >= gemcost*nrof*mt->value/100)
364     tmp->nrof -= gemcost*nrof*mt->value/100;
365     if (magic > 3)
366     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
367     if (tmp->type == GEM && !strcmp(tmp->arch->name, "ruby") &&
368     tmp->nrof >= gemcost*nrof*mt->value/100)
369     tmp->nrof -= gemcost*nrof*mt->value/100;
370     if (magic > 4)
371     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
372     if (tmp->type == GEM && !strcmp(tmp->arch->name, "diamond") &&
373     tmp->nrof >= gemcost*nrof*mt->value/100)
374     tmp->nrof -= gemcost*nrof*mt->value/100;
375     if (magic)
376     for (tmp=GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp=tmp->above)
377     if (tmp->type == GEM &&
378     (!strcmp(tmp->arch->name, "diamond") ||
379     !strcmp(tmp->arch->name, "ruby") ||
380     !strcmp(tmp->arch->name, "sapphire") ||
381     !strcmp(tmp->arch->name, "emerald") ||
382     !strcmp(tmp->arch->name, "pearl"))) {
383     if (tmp->nrof == 0) {
384     remove_ob(tmp);
385     free_object(tmp);
386     }
387     if (pl->contr)
388     pl->contr->socket.update_look=1;
389     }
390     pl->stats.sp -= 25*pow(3, magic)*mt->value/100;
391     fix_player(pl);
392     if (nummade > 1) {
393     new_draw_info_format(NDI_UNIQUE, 0, pl, "You have created %d %s.",
394     nummade, query_base_name(newobj, 1));
395     } else if (nummade == 1) {
396     new_draw_info_format(NDI_UNIQUE, 0, pl, "You have created a %s.",
397     query_base_name(newobj, 0));
398     } else {
399     new_draw_info_format(NDI_UNIQUE, 0, pl,
400     "You have failed to craft a %s.", query_base_name(newobj, 0));
401     return 0;
402     }
403     if (skills[skillnr].category != EXP_NONE)
404     add_exp(pl, obpow*nummade);
405     return 1;
406     #endif
407     }
408    
409    
410    
411     int command_uskill ( object *pl, char *params) {
412     if (!params) {
413     new_draw_info(NDI_UNIQUE, 0, pl, "Usage: use_skill <skill name>");
414     return 0;
415     }
416     return use_skill(pl,params);
417     }
418    
419     int command_rskill ( object *pl, char *params) {
420     object *skill;
421    
422     if (!params) {
423     new_draw_info(NDI_UNIQUE, 0, pl, "Usage: ready_skill <skill name>");
424     return 0;
425     }
426     skill = find_skill_by_name(pl, params);
427    
428     if (!skill) {
429     new_draw_info_format(NDI_UNIQUE, 0, pl, "You have no knowledge of the skill %s", params);
430     return 0;
431     }
432     return change_skill(pl,skill, 0);
433     }
434    
435    
436     /* These functions (command_search, command_disarm) are really juse wrappers for
437     * things like 'use_skill ...'). In fact, they should really be obsoleted
438     * and replaced with those.
439     */
440     int command_search (object *op, char *params) {
441     return use_skill(op, skill_names[SK_FIND_TRAPS]);
442     }
443    
444     int command_disarm (object *op, char *params) {
445     return use_skill(op, skill_names[SK_DISARM_TRAPS]);
446     }
447    
448    
449     /* A little special because we do want to pass the full params along
450     * as it includes the object to throw.
451     */
452     int command_throw (object *op, char *params)
453     {
454     object *skop;
455    
456     skop = find_skill_by_name(op, skill_names[SK_THROWING]);
457     if (skop) return do_skill(op, op, skop, op->facing,params);
458     else {
459     new_draw_info(NDI_UNIQUE, 0, op, "You have no knowledge of the skill throwing.");
460     }
461     return 0;
462     }
463    
464    
465     int command_apply (object *op, char *params)
466     {
467     if (!params) {
468     player_apply_below(op);
469     return 0;
470     }
471     else {
472     enum apply_flag aflag = 0;
473     object *inv;
474    
475     while (*params==' ') params++;
476     if (!strncmp(params,"-a ",3)) {
477     aflag=AP_APPLY;
478     params+=3;
479     }
480     if (!strncmp(params,"-u ",3)) {
481     aflag=AP_UNAPPLY;
482     params+=3;
483     }
484     while (*params==' ') params++;
485    
486     inv=find_best_apply_object_match(op, params, aflag);
487     if (inv) {
488     player_apply(op,inv,aflag,0);
489     } else
490     new_draw_info_format(NDI_UNIQUE, 0, op,
491     "Could not find any match to the %s.",params);
492     }
493     return 0;
494     }
495    
496     /*
497     * Check if an item op can be put into a sack. If pl exists then tell
498     * a player the reason of failure.
499     * returns 1 if it will fit, 0 if it will not. nrof is the number of
500     * objects (op) we want to put in. We specify it separately instead of
501     * using op->nrof because often times, a player may have specified a
502     * certain number of objects to drop, so we can pass that number, and
503     * not need to use split_ob and stuff.
504     */
505     int sack_can_hold (object *pl, object *sack, object *op, uint32 nrof) {
506    
507     if (! QUERY_FLAG (sack, FLAG_APPLIED)) {
508     new_draw_info_format(NDI_UNIQUE, 0, pl,
509     "The %s is not active.", query_name(sack));
510     return 0;
511     }
512     if (sack == op) {
513     new_draw_info_format(NDI_UNIQUE, 0, pl,
514     "You can't put the %s into itself.", query_name(sack));
515     return 0;
516     }
517     if (sack->race && (sack->race != op->race || op->type == CONTAINER
518     || (sack->stats.food && sack->stats.food != op->type))) {
519     new_draw_info_format(NDI_UNIQUE, 0, pl,
520     "You can put only %s into the %s.", sack->race, query_name(sack));
521     return 0;
522     }
523     if (op->type == SPECIAL_KEY && sack->slaying && op->slaying) {
524     new_draw_info_format(NDI_UNIQUE, 0, pl,
525     "You can't want put the key into %s.", query_name(sack));
526     return 0;
527     }
528     if (sack->weight_limit && sack->carrying + (nrof ? nrof : 1) *
529     (op->weight + (op->type==CONTAINER?(op->carrying*op->stats.Str):0))
530     * (100 - sack->stats.Str) / 100 > sack->weight_limit) {
531     new_draw_info_format(NDI_UNIQUE, 0, pl,
532     "That won't fit in the %s!", query_name(sack));
533     return 0;
534     }
535     /* All other checks pass, must be OK */
536     return 1;
537     }
538    
539     /* Pick up commands follow */
540     /* pl = player (not always - monsters can use this now)
541     * op is the object to put tmp into,
542     * tmp is the object to pick up, nrof is the number to
543     * pick up (0 means all of them)
544     */
545     static void pick_up_object (object *pl, object *op, object *tmp, int nrof)
546     {
547     /* buf needs to be big (more than 256 chars) because you can get
548     * very long item names.
549     */
550     char buf[HUGE_BUF];
551     object *env=tmp->env;
552     uint32 weight, effective_weight_limit;
553     int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
554    
555     /* IF the player is flying & trying to take the item out of a container
556     * that is in his inventory, let him. tmp->env points to the container
557     * (sack, luggage, etc), tmp->env->env then points to the player (nested
558     * containers not allowed as of now)
559     */
560     if((pl->move_type & MOVE_FLYING) && !QUERY_FLAG(pl, FLAG_WIZ) &&
561     is_player_inv(tmp)!=pl) {
562     new_draw_info(NDI_UNIQUE, 0,pl, "You are levitating, you can't reach the ground!");
563     return;
564     }
565     if (QUERY_FLAG (tmp, FLAG_NO_DROP))
566     return;
567     if(QUERY_FLAG(tmp,FLAG_WAS_WIZ) && !QUERY_FLAG(pl, FLAG_WAS_WIZ)) {
568     new_draw_info(NDI_UNIQUE, 0,pl, "The object disappears in a puff of smoke!");
569     new_draw_info(NDI_UNIQUE, 0,pl, "It must have been an illusion.");
570     if (pl->type==PLAYER) esrv_del_item (pl->contr, tmp->count);
571     if ( ! QUERY_FLAG (tmp, FLAG_REMOVED))
572     remove_ob (tmp);
573     free_object(tmp);
574     return;
575     }
576    
577     if (nrof > tmp_nrof || nrof == 0)
578     nrof = tmp_nrof;
579     /* Figure out how much weight this object will add to the player */
580     weight = tmp->weight * nrof;
581     if (tmp->inv) weight += tmp->carrying * (100 - tmp->stats.Str) / 100;
582     if (pl->stats.Str <= MAX_STAT)
583     effective_weight_limit = weight_limit[pl->stats.Str];
584     else
585     effective_weight_limit = weight_limit[MAX_STAT];
586     if ((pl->weight + pl->carrying + weight) > effective_weight_limit) {
587     new_draw_info(0, 0,pl,"That item is too heavy for you to pick up.");
588     return;
589     }
590     if (settings.real_wiz == FALSE && QUERY_FLAG(pl, FLAG_WAS_WIZ))
591     SET_FLAG(tmp, FLAG_WAS_WIZ);
592     if (nrof != tmp_nrof) {
593     object *tmp2 = tmp;
594     tag_t tmp2_tag = tmp2->count;
595     tmp = get_split_ob (tmp, nrof);
596     if(!tmp) {
597     new_draw_info(NDI_UNIQUE, 0,pl, errmsg);
598     return;
599     }
600     /* Tell a client what happened rest of objects */
601     if (pl->type == PLAYER) {
602     if (was_destroyed (tmp2, tmp2_tag))
603     esrv_del_item (pl->contr, tmp2_tag);
604     else
605     esrv_send_item (pl, tmp2);
606     }
607     } else {
608     /* If the object is in a container, send a delete to the client.
609     * - we are moving all the items from the container to elsewhere,
610     * so it needs to be deleted.
611     */
612     if ( ! QUERY_FLAG (tmp, FLAG_REMOVED)) {
613     if (tmp->env && pl->type==PLAYER)
614     esrv_del_item (pl->contr, tmp->count);
615     remove_ob(tmp); /* Unlink it */
616     }
617     }
618     if(QUERY_FLAG(tmp, FLAG_UNPAID))
619     (void) sprintf(buf,"%s will cost you %s.", query_name(tmp),
620     query_cost_string(tmp,pl,F_BUY | F_SHOP));
621     else
622     (void) sprintf(buf,"You pick up the %s.", query_name(tmp));
623     new_draw_info(NDI_UNIQUE, 0,pl,buf);
624    
625     tmp = insert_ob_in_ob(tmp, op);
626    
627     /* All the stuff below deals with client/server code, and is only
628     * usable by players
629     */
630     if(pl->type!=PLAYER) return;
631    
632     esrv_send_item (pl, tmp);
633     /* These are needed to update the weight for the container we
634     * are putting the object in.
635     */
636     if (op!=pl) {
637     esrv_update_item (UPD_WEIGHT, pl, op);
638     esrv_send_item (pl, pl);
639     }
640    
641     /* Update the container the object was in */
642     if (env && env!=pl && env!=op) esrv_update_item (UPD_WEIGHT, pl, env);
643     }
644    
645    
646     void pick_up(object *op,object *alt)
647     /* modified slightly to allow monsters use this -b.t. 5-31-95 */
648     {
649     int need_fix_tmp = 0;
650     object *tmp=NULL;
651     mapstruct *tmp_map=NULL;
652     int count;
653     tag_t tag;
654    
655     /* Decide which object to pick. */
656     if (alt)
657     {
658     if ( ! can_pick (op, alt)) {
659     new_draw_info_format (NDI_UNIQUE, 0, op, "You can't pick up the %s.",
660     alt->name);
661     goto leave;
662     }
663     tmp = alt;
664     }
665     else
666     {
667     if (op->below == NULL || ! can_pick (op, op->below)) {
668     new_draw_info (NDI_UNIQUE, 0, op,
669     "There is nothing to pick up here.");
670     goto leave;
671     }
672     tmp = op->below;
673     }
674    
675     /* Try to catch it. */
676     tmp_map = tmp->map;
677     tmp = stop_item (tmp);
678     if (tmp == NULL)
679     goto leave;
680     need_fix_tmp = 1;
681     if ( ! can_pick (op, tmp))
682     goto leave;
683    
684     if (op->type==PLAYER) {
685     count=op->contr->count;
686     if (count==0) count = tmp->nrof;
687     }
688     else
689     count=tmp->nrof;
690    
691     /* container is open, so use it */
692     if (op->container) {
693     alt = op->container;
694     if (alt != tmp->env && !sack_can_hold (op, alt, tmp,count))
695     goto leave;
696     } else { /* non container pickup */
697     for (alt=op->inv; alt; alt=alt->below)
698     if (alt->type==CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) &&
699     alt->race && alt->race==tmp->race &&
700     sack_can_hold (NULL, alt, tmp,count))
701     break; /* perfect match */
702    
703     if (!alt)
704     for (alt=op->inv; alt; alt=alt->below)
705     if (alt->type==CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) &&
706     sack_can_hold (NULL, alt, tmp,count))
707     break; /* General container comes next */
708     if (!alt)
709     alt = op; /* No free containers */
710     }
711     if(tmp->env == alt) {
712     /* here it could be possible to check rent,
713     * if someone wants to implement it
714     */
715     alt = op;
716     }
717     #ifdef PICKUP_DEBUG
718     LOG(llevDebug, "Pick_up(): %s picks %s (%d) and inserts it %s.\n", op->name, tmp->name, op->contr->count, alt->name);
719     #endif
720    
721     /* startequip items are not allowed to be put into containers: */
722     if (op->type == PLAYER && alt->type == CONTAINER
723     && QUERY_FLAG (tmp, FLAG_STARTEQUIP))
724     {
725     new_draw_info (NDI_UNIQUE, 0, op,
726     "This object cannot be put into containers!");
727     goto leave;
728     }
729    
730     tag = tmp->count;
731     pick_up_object (op, alt, tmp, count);
732     if (was_destroyed (tmp, tag) || tmp->env)
733     need_fix_tmp = 0;
734     if (op->type == PLAYER)
735     op->contr->count=0;
736     goto leave;
737    
738     leave:
739     if (need_fix_tmp)
740     fix_stopped_item (tmp, tmp_map, op);
741     }
742    
743    
744     /* This takes (picks up) and item. op is the player
745     * who issued the command. params is a string to
746     * match against the item name. Basically, always
747     * returns zero, but that should be improved.
748     */
749     int command_take (object *op, char *params)
750     {
751     object *tmp, *next;
752    
753     if (op->container)
754     tmp=op->container->inv;
755     else {
756     tmp=op->above;
757     if (tmp) while (tmp->above) {
758     tmp=tmp->above;
759     }
760     if (!tmp)
761     tmp=op->below;
762     }
763    
764     if (tmp==NULL) {
765     new_draw_info(NDI_UNIQUE, 0,op,"Nothing to take!");
766     return 0;
767     }
768    
769     /* Makes processing easier */
770     if (params && *params=='\0') params=NULL;
771    
772     while (tmp) {
773     next=tmp->below;
774    
775     if (tmp->invisible) {
776     tmp=next;
777     continue;
778     }
779     /* This following two if and else if could be merged into line
780     * but that probably will make it more difficult to read, and
781     * not make it any more efficient
782     */
783     if (params && item_matched_string(op, tmp, params)) {
784     pick_up(op, tmp);
785     }
786     else if (can_pick(op, tmp) && !params) {
787     pick_up(op,tmp);
788     break;
789     }
790     tmp=next;
791     /* Might as well just skip over the player immediately -
792     * we know it can't be picked up
793     */
794     if (tmp == op) tmp=tmp->below;
795     }
796     if (!params && !tmp) {
797     for (tmp=op->below; tmp!=NULL; tmp=tmp->next)
798     if (!tmp->invisible) {
799     char buf[MAX_BUF];
800     sprintf(buf,"You can't pick up a %s.",
801     tmp->name? tmp->name:"null");
802     new_draw_info(NDI_UNIQUE, 0,op, buf);
803     break;
804     }
805     if (!tmp) new_draw_info(NDI_UNIQUE, 0,op, "There is nothing to pick up.");
806     }
807     return 0;
808     }
809    
810    
811     /*
812     * This function was part of drop, now is own function.
813     * Player 'op' tries to put object 'tmp' into sack 'sack',
814     * if nrof is non zero, then nrof objects is tried to put into sack.
815 elmex 1.1.1.2 * Note that the 'sack' in question can now be a transport,
816     * so this function isn't named very good anymore.
817 root 1.1 */
818     void put_object_in_sack (object *op, object *sack, object *tmp, uint32 nrof)
819     {
820     tag_t tmp_tag, tmp2_tag;
821     object *tmp2, *sack2;
822     char buf[MAX_BUF];
823    
824     if (sack==tmp) return; /* Can't put an object in itself */
825 elmex 1.1.1.2 if (sack->type != CONTAINER && sack->type != TRANSPORT) {
826     new_draw_info_format(NDI_UNIQUE, 0,op,
827     "The %s is not a container.", query_name(sack));
828     return;
829 root 1.1 }
830     if (QUERY_FLAG(tmp,FLAG_STARTEQUIP)) {
831 elmex 1.1.1.2 new_draw_info_format(NDI_UNIQUE, 0,op,
832     "You cannot put the %s in the %s.", query_name(tmp),
833     query_name(sack));
834     return;
835 root 1.1 }
836     if (tmp->type == CONTAINER && tmp->inv) {
837    
838 elmex 1.1.1.2 /* Eneq(@csd.uu.se): If the object to be dropped is a container
839     * we instead move the contents of that container into the active
840     * container, this is only done if the object has something in it.
841     */
842     sack2 = tmp;
843     new_draw_info_format(NDI_UNIQUE, 0,op, "You move the items from %s into %s.",
844     query_name(tmp), query_name(sack));
845     for (tmp2 = tmp->inv; tmp2; tmp2 = tmp) {
846     tmp = tmp2->below;
847     if ((sack->type == CONTAINER && sack_can_hold(op, op->container, tmp2,tmp2->nrof)) ||
848     (sack->type == TRANSPORT && transport_can_hold(sack, tmp2, tmp2->nrof))) {
849     put_object_in_sack (op, sack, tmp2, 0);
850     } else {
851     sprintf(buf,"Your %s fills up.", query_name(sack));
852     new_draw_info(NDI_UNIQUE, 0,op, buf);
853     break;
854     }
855 root 1.1 }
856 elmex 1.1.1.2 esrv_update_item (UPD_WEIGHT, op, sack2);
857     return;
858 root 1.1 }
859    
860 elmex 1.1.1.2 /* Don't worry about this for containers - our caller should have
861     * already checked this.
862     */
863     if ((sack->type == CONTAINER) && !sack_can_hold (op, sack, tmp,(nrof?nrof:tmp->nrof)))
864     return;
865 root 1.1
866     if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
867 elmex 1.1.1.2 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
868     return;
869 root 1.1 }
870    
871     /* we want to put some portion of the item into the container */
872     if (nrof && tmp->nrof != nrof) {
873     object *tmp2 = tmp;
874     tmp2_tag = tmp2->count;
875     tmp = get_split_ob (tmp, nrof);
876    
877     if(!tmp) {
878     new_draw_info(NDI_UNIQUE, 0,op, errmsg);
879     return;
880     }
881     /* Tell a client what happened other objects */
882     if (was_destroyed (tmp2, tmp2_tag))
883     esrv_del_item (op->contr, tmp2_tag);
884     else /* this can proably be replaced with an update */
885     esrv_send_item (op, tmp2);
886     } else
887     remove_ob(tmp);
888    
889 elmex 1.1.1.2 new_draw_info_format(NDI_UNIQUE, 0,op, "You put the %s in %s.",
890     query_name(tmp), query_name(sack));
891 root 1.1 tmp_tag = tmp->count;
892     tmp2 = insert_ob_in_ob(tmp, sack);
893     fix_player(op); /* This is overkill, fix_player() is called somewhere */
894     /* in object.c */
895    
896     /* If an object merged (and thus, different object), we need to
897     * delete the original.
898     */
899     if (tmp2 != tmp)
900     esrv_del_item (op->contr, tmp_tag);
901    
902     esrv_send_item (op, tmp2);
903 elmex 1.1.1.2
904     /* If a transport, need to update all the players in the transport
905     * the view of what is in it.
906     */
907     if (sack->type == TRANSPORT) {
908     for (tmp=sack->inv; tmp; tmp=tmp->below) {
909     if (tmp->type == PLAYER) tmp->contr->socket.update_look=1;
910     }
911     } else {
912     /* update the sacks weight */
913     esrv_update_item (UPD_WEIGHT, op, sack);
914     }
915 root 1.1 }
916    
917     /*
918     * This function was part of drop, now is own function.
919     * Player 'op' tries to drop object 'tmp', if tmp is non zero, then
920     * nrof objects is tried to dropped.
921     * This is used when dropping objects onto the floor.
922     */
923     void drop_object (object *op, object *tmp, uint32 nrof)
924     {
925     char buf[MAX_BUF];
926     object *floor;
927    
928     if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
929     #if 0
930     /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
931     new_draw_info(NDI_UNIQUE, 0,op, "This item can't be dropped.");
932     #endif
933     return;
934     }
935    
936     if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
937     if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
938     return; /* can't unapply it */
939     }
940    
941     /* We are only dropping some of the items. We split the current objec
942     * off
943     */
944     if(nrof && tmp->nrof != nrof) {
945     object *tmp2 = tmp;
946     tag_t tmp2_tag = tmp2->count;
947     tmp = get_split_ob (tmp, nrof);
948     if(!tmp) {
949     new_draw_info(NDI_UNIQUE, 0,op, errmsg);
950     return;
951     }
952     /* Tell a client what happened rest of objects. tmp2 is now the
953     * original object
954     */
955     if (op->type == PLAYER)
956     {
957     if (was_destroyed (tmp2, tmp2_tag))
958     esrv_del_item (op->contr, tmp2_tag);
959     else
960     esrv_send_item (op, tmp2);
961     }
962     } else
963     remove_ob (tmp);
964 elmex 1.1.1.2
965 root 1.1 /* Lauwenmark: Handle for plugin drop event */
966     if (execute_event(tmp, EVENT_DROP,op,NULL,NULL,SCRIPT_FIX_ALL)!= 0)
967     return;
968    
969     if (QUERY_FLAG (tmp, FLAG_STARTEQUIP)) {
970     sprintf(buf,"You drop the %s.", query_name(tmp));
971     new_draw_info(NDI_UNIQUE, 0,op,buf);
972     new_draw_info(NDI_UNIQUE, 0,op,"The gods who lent it to you retrieves it.");
973     if (op->type==PLAYER)
974     esrv_del_item (op->contr, tmp->count);
975     free_object(tmp);
976     fix_player(op);
977     return;
978     }
979    
980     /* If SAVE_INTERVAL is commented out, we never want to save
981     * the player here.
982     */
983     #ifdef SAVE_INTERVAL
984     /* I'm not sure why there is a value check - since the save
985     * is done every SAVE_INTERVAL seconds, why care the value
986     * of what he is dropping?
987     */
988     if (op->type == PLAYER && !QUERY_FLAG(tmp, FLAG_UNPAID) &&
989     (tmp->nrof ? tmp->value * tmp->nrof : tmp->value > 2000) &&
990     (op->contr->last_save_time + SAVE_INTERVAL) <= time(NULL)) {
991     save_player(op, 1);
992     op->contr->last_save_time = time(NULL);
993     }
994     #endif /* SAVE_INTERVAL */
995    
996    
997     floor = get_map_ob (op->map, op->x, op->y);
998     if( floor && floor->type == SHOP_FLOOR &&
999     !QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY)
1000     sell_item(tmp,op);
1001    
1002     tmp->x = op->x;
1003     tmp->y = op->y;
1004    
1005     if (op->type == PLAYER)
1006     esrv_del_item (op->contr, tmp->count);
1007     insert_ob_in_map(tmp, op->map, op,0);
1008    
1009    
1010     SET_FLAG (op, FLAG_NO_APPLY);
1011     remove_ob(op);
1012     insert_ob_in_map(op, op->map, op, INS_NO_MERGE | INS_NO_WALK_ON);
1013     CLEAR_FLAG (op, FLAG_NO_APPLY);
1014    
1015     /* Call this before we update the various windows/players. At least
1016     * that we, we know the weight is correct.
1017     */
1018     fix_player(op); /* This is overkill, fix_player() is called somewhere */
1019     /* in object.c */
1020    
1021     if (op->type == PLAYER)
1022     {
1023 elmex 1.1.1.2 op->contr->socket.update_look = 1;
1024     /* Need to update the weight for the player */
1025     esrv_send_item (op, op);
1026 root 1.1 }
1027     }
1028    
1029     void drop(object *op, object *tmp)
1030     {
1031     /* Hopeful fix for disappearing objects when dropping from a container -
1032     * somehow, players get an invisible object in the container, and the
1033     * old logic would skip over invisible objects - works fine for the
1034     * playes inventory, but drop inventory wants to use the next value.
1035     */
1036     if (tmp->invisible) {
1037     /* if the following is the case, it must be in an container. */
1038     if (tmp->env && tmp->env->type != PLAYER) {
1039     /* Just toss the object - probably shouldn't be hanging
1040     * around anyways
1041     */
1042     remove_ob(tmp);
1043     free_object(tmp);
1044     return;
1045     } else {
1046     while(tmp!=NULL && tmp->invisible)
1047     tmp=tmp->below;
1048     }
1049     }
1050    
1051     if (tmp==NULL) {
1052     new_draw_info(NDI_UNIQUE, 0,op,"You don't have anything to drop.");
1053     return;
1054     }
1055     if (QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1056     new_draw_info(NDI_UNIQUE, 0,op,"This item is locked");
1057     return;
1058     }
1059     if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
1060     #if 0
1061     /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
1062     new_draw_info(NDI_UNIQUE, 0,op, "This item can't be dropped.");
1063     #endif
1064     return;
1065     }
1066    
1067     if (op->type == PLAYER)
1068     {
1069     if (op->contr->last_used==tmp && op->contr->last_used_id == tmp->count) {
1070     object *n=NULL;
1071     if(tmp->below != NULL)
1072     n = tmp->below;
1073     else if(tmp->above != NULL)
1074     n = tmp->above;
1075     op->contr->last_used = n;
1076     if (n != NULL)
1077     op->contr->last_used_id = n->count;
1078     else
1079     op->contr->last_used_id = 0;
1080     }
1081     };
1082    
1083     if (op->container) {
1084     if (op->type == PLAYER)
1085     {
1086     put_object_in_sack (op, op->container, tmp, op->contr->count);
1087     } else {
1088     put_object_in_sack(op, op->container, tmp, 0);
1089     };
1090     } else {
1091     if (op->type == PLAYER)
1092     {
1093     drop_object (op, tmp, op->contr->count);
1094     } else {
1095     drop_object(op,tmp,0);
1096     };
1097     }
1098     if (op->type == PLAYER)
1099     op->contr->count = 0;
1100     }
1101    
1102    
1103    
1104     /* Command will drop all items that have not been locked */
1105     int command_dropall (object *op, char *params) {
1106    
1107     object * curinv, *nextinv;
1108    
1109     if(op->inv == NULL) {
1110     new_draw_info(NDI_UNIQUE, 0,op,"Nothing to drop!");
1111     return 0;
1112     }
1113    
1114     curinv = op->inv;
1115    
1116     /*
1117     This is the default. Drops everything not locked or considered
1118     not something that should be dropped.
1119     */
1120     /*
1121     Care must be taken that the next item pointer is not to money as
1122     the drop() routine will do unknown things to it when dropping
1123     in a shop. --Tero.Pelander@utu.fi
1124     */
1125    
1126     if(params==NULL) {
1127     while(curinv != NULL) {
1128     nextinv = curinv->below;
1129     while (nextinv && nextinv->type==MONEY)
1130     nextinv = nextinv->below;
1131     if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && curinv->type != MONEY &&
1132     curinv->type != FOOD && curinv->type != KEY &&
1133     curinv->type != SPECIAL_KEY && curinv->type != GEM &&
1134     !curinv->invisible &&
1135     (curinv->type!=CONTAINER || op->container!=curinv))
1136     {
1137     drop(op,curinv);
1138     }
1139     curinv = nextinv;
1140     }
1141     }
1142    
1143     else if(strcmp(params, "weapons") == 0) {
1144     while(curinv != NULL) {
1145     nextinv = curinv->below;
1146     while (nextinv && nextinv->type==MONEY)
1147     nextinv = nextinv->below;
1148     if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ((curinv->type == WEAPON) ||
1149     (curinv->type == BOW) || (curinv->type == ARROW)))
1150     {
1151     drop(op,curinv);
1152     }
1153     curinv = nextinv;
1154     }
1155     }
1156    
1157     else if(strcmp(params, "armor") == 0 || strcmp(params, "armour") == 0) {
1158     while(curinv != NULL) {
1159     nextinv = curinv->below;
1160     while (nextinv && nextinv->type==MONEY)
1161     nextinv = nextinv->below;
1162     if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ((curinv->type == ARMOUR) ||
1163     curinv->type == SHIELD || curinv->type==HELMET))
1164     {
1165     drop(op,curinv);
1166     }
1167     curinv = nextinv;
1168     }
1169     }
1170    
1171     else if(strcmp(params, "misc") == 0) {
1172     while(curinv != NULL) {
1173     nextinv = curinv->below;
1174     while (nextinv && nextinv->type==MONEY)
1175     nextinv = nextinv->below;
1176     if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ! QUERY_FLAG(curinv,FLAG_APPLIED)) {
1177     switch(curinv->type) {
1178     case HORN:
1179     case BOOK:
1180     case SPELLBOOK:
1181     case GIRDLE:
1182     case AMULET:
1183     case RING:
1184     case CLOAK:
1185     case BOOTS:
1186     case GLOVES:
1187     case BRACERS:
1188     case SCROLL:
1189     case ARMOUR_IMPROVER:
1190     case WEAPON_IMPROVER:
1191     case WAND:
1192     case ROD:
1193     case POTION:
1194     drop(op,curinv);
1195     curinv = nextinv;
1196     break;
1197     default:
1198     curinv = nextinv;
1199     break;
1200     }
1201     }
1202     curinv = nextinv;
1203     }
1204     }
1205     op->contr->socket.update_look=1;
1206     /* draw_look(op);*/
1207     return 0;
1208     }
1209    
1210     /* Object op wants to drop object(s) params. params can be a
1211     * comma seperated list.
1212     */
1213    
1214     int command_drop (object *op, char *params)
1215     {
1216     object *tmp, *next;
1217     int did_one=0;
1218    
1219     if (!params) {
1220     new_draw_info(NDI_UNIQUE,0, op, "Drop what?");
1221     return 0;
1222     } else {
1223     for (tmp=op->inv; tmp; tmp=next) {
1224     next=tmp->below;
1225     if (QUERY_FLAG(tmp,FLAG_NO_DROP) ||
1226     tmp->invisible) continue;
1227     if (item_matched_string(op,tmp,params)) {
1228     drop(op, tmp);
1229     did_one=1;
1230     }
1231     }
1232     if (!did_one) new_draw_info(NDI_UNIQUE, 0,op,"Nothing to drop.");
1233     }
1234     if (op->type==PLAYER)
1235     {
1236     op->contr->count=0;
1237     op->contr->socket.update_look=1;
1238     };
1239     /* draw_look(op);*/
1240     return 0;
1241     }
1242    
1243     int command_examine (object *op, char *params)
1244     {
1245     if (!params) {
1246     object *tmp=op->below;
1247     while (tmp && !LOOK_OBJ(tmp)) tmp=tmp->below;
1248     if (tmp) examine(op,tmp);
1249     }
1250     else {
1251     object *tmp=find_best_object_match(op,params);
1252     if (tmp)
1253     examine(op,tmp);
1254     else
1255     new_draw_info_format(NDI_UNIQUE,0,op,"Could not find an object that matches %s",params);
1256     }
1257     return 0;
1258     }
1259    
1260     /* op should be a player.
1261     * we return the object the player has marked with the 'mark' command
1262     * below. If no match is found (or object has changed), we return
1263     * NULL. We leave it up to the calling function to print messages if
1264     * nothing is found.
1265     */
1266     object *find_marked_object(object *op)
1267     {
1268     object *tmp;
1269    
1270     if (!op || !op->contr) return NULL;
1271     if (!op->contr->mark) {
1272     /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1273     return NULL;
1274     }
1275     /* This may seem like overkill, but we need to make sure that they
1276     * player hasn't dropped the item. We use count on the off chance that
1277     * an item got reincarnated at some point.
1278     */
1279     for (tmp=op->inv; tmp; tmp=tmp->below) {
1280     if (tmp->invisible) continue;
1281     if (tmp == op->contr->mark) {
1282     if (tmp->count == op->contr->mark_count)
1283     return tmp;
1284     else {
1285     op->contr->mark=NULL;
1286     op->contr->mark_count=0;
1287     /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1288     return NULL;
1289     }
1290     }
1291     }
1292     return NULL;
1293     }
1294    
1295    
1296     /* op should be a player, params is any params.
1297     * If no params given, we print out the currently marked object.
1298     * otherwise, try to find a matching object - try best match first.
1299     */
1300     int command_mark(object *op, char *params)
1301     {
1302     if (!op->contr) return 1;
1303     if (!params) {
1304     object *mark=find_marked_object(op);
1305     if (!mark) new_draw_info(NDI_UNIQUE,0,op,"You have no marked object.");
1306     else new_draw_info_format(NDI_UNIQUE,0,op,"%s is marked.", query_name(mark));
1307     }
1308     else {
1309     object *mark1=find_best_object_match(op, params);
1310     if (!mark1) {
1311     new_draw_info_format(NDI_UNIQUE,0,op,"Could not find an object that matches %s",params);
1312     return 1;
1313     }
1314     else {
1315     op->contr->mark=mark1;
1316     op->contr->mark_count=mark1->count;
1317     new_draw_info_format(NDI_UNIQUE,0,op,"Marked item %s", query_name(mark1));
1318     return 0;
1319     }
1320     }
1321     return 0; /*shouldnt get here */
1322     }
1323    
1324    
1325     /* op is the player
1326     * tmp is the monster being examined.
1327     */
1328     void examine_monster(object *op,object *tmp) {
1329     object *mon=tmp->head?tmp->head:tmp;
1330    
1331     if(QUERY_FLAG(mon,FLAG_UNDEAD))
1332     new_draw_info(NDI_UNIQUE, 0,op,"It is an undead force.");
1333     if(mon->level>op->level)
1334     new_draw_info(NDI_UNIQUE, 0,op,"It is likely more powerful than you.");
1335     else if(mon->level<op->level)
1336     new_draw_info(NDI_UNIQUE, 0,op,"It is likely less powerful than you.");
1337     else
1338     new_draw_info(NDI_UNIQUE, 0,op,"It is probably as powerful as you.");
1339     if(mon->attacktype&AT_ACID)
1340     new_draw_info(NDI_UNIQUE, 0,op,"You seem to smell an acrid odor.");
1341    
1342     /* Anyone know why this used to use the clone value instead of the
1343     * maxhp field? This seems that it should give more accurate results.
1344     */
1345     switch((mon->stats.hp+1)*4/(mon->stats.maxhp+1)) { /* From 1-4 */
1346     case 1:
1347     new_draw_info(NDI_UNIQUE, 0,op,"It is in a bad shape.");
1348     break;
1349     case 2:
1350     new_draw_info(NDI_UNIQUE, 0,op,"It is hurt.");
1351     break;
1352     case 3:
1353     new_draw_info(NDI_UNIQUE, 0,op,"It is somewhat hurt.");
1354     break;
1355     case 4:
1356     new_draw_info(NDI_UNIQUE, 0,op,"It is in excellent shape.");
1357     break;
1358     }
1359     if(present_in_ob(POISONING,mon)!=NULL)
1360     new_draw_info(NDI_UNIQUE, 0,op,"It looks very ill.");
1361     }
1362    
1363    
1364     /* tmp is the object being described, pl is who is examing it. */
1365     char *long_desc(object *tmp, object *pl) {
1366     static char buf[VERY_BIG_BUF];
1367     char *cp;
1368    
1369     if(tmp==NULL)
1370     return "";
1371    
1372     buf[0]='\0';
1373     switch(tmp->type) {
1374     case RING:
1375     case SKILL:
1376     case WEAPON:
1377     case ARMOUR:
1378     case BRACERS:
1379     case HELMET:
1380     case SHIELD:
1381     case BOOTS:
1382     case GLOVES:
1383     case AMULET:
1384     case GIRDLE:
1385     case BOW:
1386     case ARROW:
1387     case CLOAK:
1388     case FOOD:
1389     case DRINK:
1390     case FLESH:
1391     case SKILL_TOOL:
1392     case POWER_CRYSTAL:
1393     if(*(cp=describe_item(tmp, pl))!='\0') {
1394     int len;
1395    
1396     strncpy(buf,query_name(tmp), VERY_BIG_BUF-1);
1397     buf[VERY_BIG_BUF-1]=0;
1398     len=strlen(buf);
1399     if (len<VERY_BIG_BUF-5) {
1400     /* Since we know the length, we save a few cpu cycles by using
1401     * it instead of calling strcat */
1402     strcpy(buf+len," ");
1403     len++;
1404     strncpy(buf+len, cp, VERY_BIG_BUF-len-1);
1405     buf[VERY_BIG_BUF-1]=0;
1406     }
1407     }
1408     }
1409     if(buf[0]=='\0') {
1410     strncpy(buf,query_name(tmp), VERY_BIG_BUF-1);
1411     buf[VERY_BIG_BUF-1]=0;
1412     }
1413    
1414     return buf;
1415     }
1416    
1417     void examine(object *op, object *tmp) {
1418     char buf[VERY_BIG_BUF];
1419     int i;
1420    
1421     if (tmp == NULL || tmp->type == CLOSE_CON)
1422     return;
1423    
1424     strcpy(buf,"That is ");
1425     strncat(buf, long_desc(tmp, op), VERY_BIG_BUF-strlen(buf)-1);
1426     buf[VERY_BIG_BUF-1]=0;
1427    
1428     new_draw_info(NDI_UNIQUE, 0,op,buf);
1429     buf[0]='\0';
1430    
1431     if(tmp->custom_name) {
1432     strcpy(buf,"You name it ");
1433     strncat(buf, tmp->custom_name, VERY_BIG_BUF-strlen(buf)-1);
1434     buf[VERY_BIG_BUF-1]=0;
1435     new_draw_info(NDI_UNIQUE, 0,op,buf);
1436     buf[0]='\0';
1437     }
1438    
1439     switch(tmp->type) {
1440     case SPELLBOOK:
1441     if(QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->inv ) {
1442     sprintf(buf,"%s is a %s level %s spell",
1443     tmp->inv->name, get_levelnumber(tmp->inv->level),
1444     tmp->inv->skill);
1445     }
1446     break;
1447    
1448     case BOOK:
1449     if(tmp->msg!=NULL)
1450     strcpy(buf,"Something is written in it.");
1451     break;
1452    
1453     case CONTAINER:
1454     if(tmp->race!=NULL) {
1455     if(tmp->weight_limit && tmp->stats.Str<100)
1456     sprintf (buf,"It can hold only %s and its weight limit is %.1f kg.",
1457     tmp->race, tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
1458     else
1459     sprintf (buf,"It can hold only %s.", tmp->race);
1460     } else
1461     if(tmp->weight_limit && tmp->stats.Str<100)
1462     sprintf (buf,"Its weight limit is %.1f kg.",
1463     tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
1464     break;
1465    
1466     case WAND:
1467     if(QUERY_FLAG(tmp, FLAG_IDENTIFIED))
1468     sprintf(buf,"It has %d charges left.",tmp->stats.food);
1469     break;
1470     }
1471    
1472     if(buf[0]!='\0')
1473     new_draw_info(NDI_UNIQUE, 0,op,buf);
1474    
1475     if(tmp->materialname != NULL && !tmp->msg) {
1476     sprintf(buf, "It is made of: %s.", tmp->materialname);
1477     new_draw_info(NDI_UNIQUE, 0, op, buf);
1478     }
1479     /* Where to wear this item */
1480     for (i=0; i < NUM_BODY_LOCATIONS; i++) {
1481     if (tmp->body_info[i]<-1) {
1482     if (op->body_info[i])
1483     new_draw_info_format(NDI_UNIQUE, 0,op,
1484     "It goes %s (%d)", body_locations[i].use_name, -tmp->body_info[i]);
1485     else
1486     new_draw_info_format(NDI_UNIQUE, 0,op,
1487     "It goes %s", body_locations[i].nonuse_name);
1488     } else if (tmp->body_info[i]) {
1489     if (op->body_info[i])
1490     new_draw_info_format(NDI_UNIQUE, 0,op,
1491     "It goes %s", body_locations[i].use_name);
1492     else
1493     new_draw_info_format(NDI_UNIQUE, 0,op,
1494     "It goes %s", body_locations[i].nonuse_name);
1495     }
1496     }
1497    
1498     if(tmp->weight) {
1499     sprintf(buf,tmp->nrof>1?"They weigh %3.3f kg.":"It weighs %3.3f kg.",
1500     tmp->weight*(tmp->nrof?tmp->nrof:1)/1000.0);
1501     new_draw_info(NDI_UNIQUE, 0,op,buf);
1502     }
1503    
1504     if (tmp->value && !QUERY_FLAG(tmp, FLAG_STARTEQUIP) && !QUERY_FLAG(tmp, FLAG_NO_PICK)) {
1505     object *floor;
1506     sprintf(buf,"You reckon %s worth %s.",
1507     tmp->nrof>1?"they are":"it is",query_cost_string(tmp,op,F_SELL | F_APPROX));
1508     new_draw_info(NDI_UNIQUE, 0,op,buf);
1509     floor = get_map_ob (op->map, op->x, op->y);
1510     if (floor && floor->type == SHOP_FLOOR) {
1511     if(QUERY_FLAG(tmp, FLAG_UNPAID))
1512     sprintf(buf,"%s would cost you %s.",
1513     tmp->nrof>1?"They":"It",query_cost_string(tmp,op,F_BUY | F_SHOP));
1514     else
1515     sprintf(buf,"You are offered %s for %s.",
1516     query_cost_string(tmp,op,F_SELL+F_SHOP), tmp->nrof>1?"them":"it");
1517     new_draw_info(NDI_UNIQUE, 0,op,buf);
1518     }
1519     }
1520    
1521     if(QUERY_FLAG(tmp, FLAG_MONSTER))
1522     examine_monster(op,tmp);
1523    
1524     /* Is this item buildable? */
1525     if ( QUERY_FLAG( tmp, FLAG_IS_BUILDABLE ) )
1526     new_draw_info( NDI_UNIQUE, 0, op, "This is a buildable item." );
1527    
1528     /* Does the object have a message? Don't show message for all object
1529     * types - especially if the first entry is a match
1530     */
1531     if(tmp->msg && tmp->type != EXIT && tmp->type != BOOK &&
1532     tmp->type != CORPSE && !tmp->move_on &&
1533     strncasecmp(tmp->msg, "@match",7)) {
1534    
1535     /* This is just a hack so when identifying hte items, we print
1536     * out the extra message
1537     */
1538     if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))
1539     new_draw_info(NDI_UNIQUE, 0,op, "The object has a story:");
1540    
1541     new_draw_info(NDI_UNIQUE, 0,op,tmp->msg);
1542     }
1543     new_draw_info(NDI_UNIQUE, 0,op," "); /* Blank line */
1544     }
1545    
1546     /*
1547     * inventory prints object's inventory. If inv==NULL then print player's
1548     * inventory.
1549     * [ Only items which are applied are showed. Tero.Haatanen@lut.fi ]
1550     */
1551     void inventory(object *op,object *inv) {
1552     object *tmp;
1553     char *in;
1554     int items = 0, length;
1555    
1556     if (inv==NULL && op==NULL) {
1557     new_draw_info(NDI_UNIQUE, 0,op,"Inventory of what object?");
1558     return;
1559     }
1560     tmp = inv ? inv->inv : op->inv;
1561    
1562     while (tmp) {
1563     if ((!tmp->invisible &&
1564     (inv==NULL || inv->type == CONTAINER || QUERY_FLAG(tmp, FLAG_APPLIED)))
1565     || (!op || QUERY_FLAG(op, FLAG_WIZ)))
1566     items++;
1567     tmp=tmp->below;
1568     }
1569     if (inv==NULL) { /* player's inventory */
1570     if (items==0) {
1571     new_draw_info(NDI_UNIQUE, 0,op,"You carry nothing.");
1572     return;
1573     } else {
1574     length = 28;
1575     in = "";
1576     if (op)
1577     clear_win_info(op);
1578     new_draw_info(NDI_UNIQUE, 0,op,"Inventory:");
1579     }
1580     } else {
1581     if (items==0)
1582     return;
1583     else {
1584     length = 28;
1585     in = " ";
1586     }
1587     }
1588     for (tmp=inv?inv->inv:op->inv; tmp; tmp=tmp->below) {
1589     if((!op||!QUERY_FLAG(op, FLAG_WIZ)) && (tmp->invisible ||
1590     (inv && inv->type != CONTAINER && !QUERY_FLAG(tmp, FLAG_APPLIED))))
1591     continue;
1592     if((!op || QUERY_FLAG(op, FLAG_WIZ)))
1593     new_draw_info_format(NDI_UNIQUE, 0,op ,"%s- %-*.*s (%5d) %-8s", in, length, length,
1594     query_name(tmp), tmp->count,query_weight(tmp));
1595     else
1596     new_draw_info_format(NDI_UNIQUE,0, op, "%s- %-*.*s %-8s", in, length+8,
1597     length+8, query_name(tmp),
1598     query_weight(tmp));
1599     }
1600     if(!inv && op) {
1601     new_draw_info_format(NDI_UNIQUE,0, op ,"%-*s %-8s",
1602     41,"Total weight :",query_weight(op));
1603     }
1604     }
1605    
1606     static void display_new_pickup( object* op )
1607     {
1608     int i = op->contr->mode;
1609    
1610     if(!(i & PU_NEWMODE)) return;
1611    
1612     new_draw_info_format(NDI_UNIQUE, 0,op,"%d NEWMODE",i & PU_NEWMODE?1:0);
1613     new_draw_info_format(NDI_UNIQUE, 0,op,"%d DEBUG",i & PU_DEBUG?1:0);
1614     new_draw_info_format(NDI_UNIQUE, 0,op,"%d INHIBIT",i & PU_INHIBIT?1:0);
1615     new_draw_info_format(NDI_UNIQUE, 0,op,"%d STOP",i & PU_STOP?1:0);
1616    
1617     new_draw_info_format(NDI_UNIQUE, 0,op,"%d <= x pickup weight/value RATIO (0==off)",(i & PU_RATIO)*5);
1618    
1619     new_draw_info_format(NDI_UNIQUE, 0,op,"%d FOOD",i & PU_FOOD?1:0);
1620     new_draw_info_format(NDI_UNIQUE, 0,op,"%d DRINK",i & PU_DRINK?1:0);
1621     new_draw_info_format(NDI_UNIQUE, 0,op,"%d VALUABLES",i & PU_VALUABLES?1:0);
1622    
1623     new_draw_info_format(NDI_UNIQUE, 0,op,"%d BOW",i & PU_BOW?1:0);
1624     new_draw_info_format(NDI_UNIQUE, 0,op,"%d ARROW",i & PU_ARROW?1:0);
1625    
1626     new_draw_info_format(NDI_UNIQUE, 0,op,"%d HELMET",i & PU_HELMET?1:0);
1627     new_draw_info_format(NDI_UNIQUE, 0,op,"%d SHIELD",i & PU_SHIELD?1:0);
1628     new_draw_info_format(NDI_UNIQUE, 0,op,"%d ARMOUR",i & PU_ARMOUR?1:0);
1629    
1630     new_draw_info_format(NDI_UNIQUE, 0,op,"%d BOOTS",i & PU_BOOTS?1:0);
1631     new_draw_info_format(NDI_UNIQUE, 0,op,"%d GLOVES",i & PU_GLOVES?1:0);
1632     new_draw_info_format(NDI_UNIQUE, 0,op,"%d CLOAK",i & PU_CLOAK?1:0);
1633     new_draw_info_format(NDI_UNIQUE, 0,op,"%d KEY",i & PU_KEY?1:0);
1634    
1635     new_draw_info_format(NDI_UNIQUE, 0,op,"%d MISSILEWEAPON",i & PU_MISSILEWEAPON?1:0);
1636     new_draw_info_format(NDI_UNIQUE, 0,op,"%d ALLWEAPON",i & PU_ALLWEAPON?1:0);
1637     new_draw_info_format(NDI_UNIQUE, 0,op,"%d MAGICAL",i & PU_MAGICAL?1:0);
1638     new_draw_info_format(NDI_UNIQUE, 0,op,"%d POTION",i & PU_POTION?1:0);
1639    
1640     new_draw_info_format(NDI_UNIQUE, 0,op,"%d SPELLBOOK",i & PU_SPELLBOOK?1:0);
1641     new_draw_info_format(NDI_UNIQUE, 0,op,"%d SKILLSCROLL",i & PU_SKILLSCROLL?1:0);
1642     new_draw_info_format(NDI_UNIQUE, 0,op,"%d READABLES",i & PU_READABLES?1:0);
1643     new_draw_info_format(NDI_UNIQUE, 0,op,"%d MAGICDEVICE", i & PU_MAGIC_DEVICE?1:0);
1644    
1645     new_draw_info_format(NDI_UNIQUE, 0,op,"%d NOT CURSED", i & PU_NOT_CURSED?1:0);
1646    
1647     new_draw_info_format(NDI_UNIQUE, 0,op,"%d JEWELS", i & PU_JEWELS?1:0);
1648    
1649     new_draw_info_format(NDI_UNIQUE, 0,op,"");
1650     }
1651    
1652     int command_pickup (object *op, char *params)
1653     {
1654     uint32 i;
1655     static const char* names[ ] = {
1656     "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1657     "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1658     "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed", "jewels", NULL };
1659     static uint32 modes[ ] = {
1660     PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1661     PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1662     PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED, PU_JEWELS, 0 };
1663    
1664     if(!params) {
1665     /* if the new mode is used, just print the settings */
1666     if(op->contr->mode & PU_NEWMODE)
1667     {
1668     display_new_pickup( op );
1669     return 1;
1670     }
1671     if(1) LOG(llevDebug, "command_pickup: !params\n");
1672     set_pickup_mode(op, (op->contr->mode > 6)? 0: op->contr->mode+1);
1673     return 0;
1674     }
1675    
1676     while ( *params == ' ' && *params )
1677     params++;
1678    
1679     if ( *params == '+' || *params == '-' )
1680     {
1681     int mode;
1682     for ( mode = 0; names[ mode ]; mode++ )
1683     {
1684     if ( !strcmp( names[ mode ], params + 1 ) )
1685     {
1686     i = op->contr->mode;
1687     if ( !( i & PU_NEWMODE ) )
1688     i = PU_NEWMODE;
1689     if ( *params == '+' )
1690     i = i | modes[ mode ];
1691     else
1692     i = i & ~modes[ mode ];
1693     op->contr->mode = i;
1694     display_new_pickup( op );
1695     return 1;
1696     }
1697     }
1698     new_draw_info_format( NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params );
1699     return 1;
1700     }
1701    
1702     if(sscanf(params, "%u", &i) != 1) {
1703     if(1) LOG(llevDebug, "command_pickup: params==NULL\n");
1704     new_draw_info(NDI_UNIQUE, 0,op,"Usage: pickup <0-7> or <value_density> .");
1705     return 1;
1706     }
1707     set_pickup_mode(op,i);
1708     display_new_pickup( op );
1709    
1710     return 1;
1711     }
1712    
1713     void set_pickup_mode(object *op,int i) {
1714     switch(op->contr->mode=i) {
1715     case 0:
1716     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Don't pick up.");
1717     break;
1718     case 1:
1719     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item.");
1720     break;
1721     case 2:
1722     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item and stop.");
1723     break;
1724     case 3:
1725     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Stop before picking up.");
1726     break;
1727     case 4:
1728     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items.");
1729     break;
1730     case 5:
1731     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items and stop.");
1732     break;
1733     case 6:
1734     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all magic items.");
1735     break;
1736     case 7:
1737     new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all coins and gems");
1738     break;
1739     }
1740     }
1741    
1742     int command_search_items (object *op, char *params)
1743     {
1744     char buf[MAX_BUF];
1745    
1746     if (settings.search_items == FALSE)
1747     return 1;
1748    
1749     if(params == NULL) {
1750     if(op->contr->search_str[0]=='\0') {
1751     new_draw_info(NDI_UNIQUE, 0,op,"Example: search magic+1");
1752     new_draw_info(NDI_UNIQUE, 0,op,"Would automatically pick up all");
1753     new_draw_info(NDI_UNIQUE, 0,op,"items containing the word 'magic+1'.");
1754     return 1;
1755     }
1756     op->contr->search_str[0]='\0';
1757     new_draw_info(NDI_UNIQUE, 0,op,"Search mode turned off.");
1758     fix_player(op);
1759     return 1;
1760     }
1761     if((int)strlen(params) >= MAX_BUF) {
1762     new_draw_info(NDI_UNIQUE, 0,op,"Search string too long.");
1763     return 1;
1764     }
1765     strcpy(op->contr->search_str, params);
1766     sprintf(buf,"Searching for '%s'.",op->contr->search_str);
1767     new_draw_info(NDI_UNIQUE, 0,op,buf);
1768     fix_player(op);
1769     return 1;
1770     }
1771    
1772     /*
1773     * Changing the custom name of an item
1774     *
1775     * Syntax is: rename <what object> to <new name>
1776     * if '<what object>' is omitted, marked object is used
1777     * if 'to <new name>' is omitted, custom name is cleared
1778     *
1779     * Names are considered for all purpose having a length <=127 (max length sent to client
1780     * by server) */
1781    
1782     int command_rename_item(object *op, char *params)
1783     {
1784     char buf[VERY_BIG_BUF];
1785     int itemnumber;
1786     object *item=NULL;
1787     char *closebrace;
1788     size_t counter;
1789    
1790     if (params) {
1791     /* Let's skip white spaces */
1792     while(' '==*params) params++;
1793    
1794     /* Checking the first part */
1795 elmex 1.1.1.2 if ((itemnumber = atoi(params))!=0) {
1796 root 1.1 for (item=op->inv; item && ((item->count != itemnumber) || item->invisible); item=item->below);
1797     if (!item) {
1798     new_draw_info(NDI_UNIQUE,0,op,"Tried to rename an invalid item.");
1799     return 1;
1800     }
1801     while(isdigit(*params) || ' '==*params) params++;
1802     }
1803     else if ('<'==*params) {
1804     /* Got old name, let's get it & find appropriate matching item */
1805     closebrace=strchr(params,'>');
1806     if(!closebrace) {
1807     new_draw_info(NDI_UNIQUE,0,op,"Syntax error!");
1808     return 1;
1809     }
1810     /* Sanity check for buffer overruns */
1811     if((closebrace-params)>127) {
1812     new_draw_info(NDI_UNIQUE,0,op,"Old name too long (up to 127 characters allowed)!");
1813     return 1;
1814     }
1815     /* Copy the old name */
1816     strncpy(buf,params+1,closebrace-params-1);
1817     buf[closebrace-params-1]='\0';
1818    
1819     /* Find best matching item */
1820     item=find_best_object_match(op,buf);
1821     if(!item) {
1822     new_draw_info(NDI_UNIQUE,0,op,"Could not find a matching item to rename.");
1823     return 1;
1824     }
1825    
1826     /* Now need to move pointer to just after > */
1827     params=closebrace+1;
1828     while(' '==*params) params++;
1829    
1830     } else {
1831     /* Use marked item */
1832     item=find_marked_object(op);
1833     if(!item) {
1834     new_draw_info(NDI_UNIQUE,0,op,"No marked item to rename.");
1835     return 1;
1836     }
1837     }
1838    
1839     /* Now let's find the new name */
1840     if(!strncmp(params,"to ",3)) {
1841     params+=3;
1842     while(' '==*params) params++;
1843     if('<'!=*params) {
1844     new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expecting < at start of new name!");
1845     return 1;
1846     }
1847     closebrace=strchr(params+1,'>');
1848     if(!closebrace) {
1849     new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expecting > at end of new name!");
1850     return 1;
1851     }
1852    
1853     /* Sanity check for buffer overruns */
1854     if((closebrace-params)>127) {
1855     new_draw_info(NDI_UNIQUE,0,op,"New name too long (up to 127 characters allowed)!");
1856     return 1;
1857     }
1858    
1859     /* Copy the new name */
1860     strncpy(buf,params+1,closebrace-params-1);
1861     buf[closebrace-params-1]='\0';
1862    
1863     /* Let's check it for weird characters */
1864     for(counter=0;counter<strlen(buf);counter++) {
1865     if(isalnum(buf[counter])) continue;
1866     if(' '==buf[counter]) continue;
1867     if('\''==buf[counter]) continue;
1868     if('+'==buf[counter]) continue;
1869     if('_'==buf[counter]) continue;
1870     if('-'==buf[counter]) continue;
1871    
1872     /* If we come here, then the name contains an invalid character...
1873     tell the player & exit */
1874     new_draw_info(NDI_UNIQUE,0,op,"Invalid new name!");
1875     return 1;
1876     }
1877    
1878     } else {
1879     /* If param contains something, then syntax error... */
1880     if(strlen(params)) {
1881     new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expected 'to <' after old name!");
1882     return 1;
1883     }
1884     /* New name is empty */
1885     buf[0]='\0';
1886     }
1887     } else {
1888     /* Last case: params==NULL */
1889     item=find_marked_object(op);
1890     if(!item) {
1891     new_draw_info(NDI_UNIQUE,0,op,"No marked item to rename.");
1892     return 1;
1893     }
1894     buf[0]='\0';
1895     }
1896    
1897     /* Coming here, everything is fine... */
1898     if(!strlen(buf)) {
1899     /* Clear custom name */
1900     if(item->custom_name) {
1901     FREE_AND_CLEAR_STR(item->custom_name);
1902    
1903     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));
1904     esrv_update_item(UPD_NAME,op,item);
1905     } else {
1906     new_draw_info(NDI_UNIQUE,0,op,"This item has no custom name.");
1907     }
1908     } else {
1909     /* Set custom name */
1910     FREE_AND_COPY(item->custom_name,buf);
1911    
1912     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);
1913     esrv_update_item(UPD_NAME,op,item);
1914     }
1915    
1916     return 1;
1917     }