ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.c
Revision: 1.1.1.3 (vendor branch)
Committed: Wed Mar 15 14:05:35 2006 UTC (18 years, 2 months ago) by elmex
Content type: text/plain
Branch: UPSTREAM
CVS Tags: UPSTREAM_2006_03_15
Changes since 1.1.1.2: +20 -27 lines
Log Message:
cvs -z9 -d:ext:elmex@cvs.schmorp.de:/schmorpforge import cf.schmorp.de UPSTREAM UPSTREAM_2006_03_15

File Contents

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