ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_object.c
Revision: 1.1.1.1 (vendor branch)
Committed: Fri Feb 3 07:14:29 2006 UTC (18 years, 4 months ago) by root
Content type: text/plain
Branch: UPSTREAM
CVS Tags: UPSTREAM_2006_02_03
Changes since 1.1: +0 -0 lines
Log Message:
initial import

File Contents

# Content
1 /*
2 * static char *rcsid_c_object_c =
3 * "$Id: c_object.c,v 1.76 2006/01/11 16:28:00 cavesomething Exp $";
4 */
5 /*
6 CrossFire, A Multiplayer game for X-windows
7
8 Copyright (C) 2002 Mark Wedel & Crossfire Development Team
9 Copyright (C) 1992 Frank Tore Johansen
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 The author can be reached via e-mail to crossfire-devel@real-time.com
26
27 Object (handling) commands
28 */
29
30 #include <global.h>
31 #include <loader.h>
32 #include <skills.h>
33 #ifndef __CEXTRACT__
34 #include <sproto.h>
35 #endif
36 #include <living.h>
37 #include <math.h>
38 /*
39 * Object id parsing functions
40 */
41
42 #define OBLINKMALLOC(p) if(!((p)=(objectlink *)malloc(sizeof(objectlink))))\
43 fatal(OUT_OF_MEMORY);
44
45 #define ADD_ITEM(NEW,COUNT)\
46 if(!first) {\
47 OBLINKMALLOC(first);\
48 last=first;\
49 } else {\
50 OBLINKMALLOC(last->next);\
51 last=last->next;\
52 }\
53 last->next=NULL;\
54 last->ob=(NEW);\
55 last->id=(COUNT);
56
57 /* 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 */
816 void put_object_in_sack (object *op, object *sack, object *tmp, uint32 nrof)
817 {
818 tag_t tmp_tag, tmp2_tag;
819 object *tmp2, *sack2;
820 char buf[MAX_BUF];
821
822 if (sack==tmp) return; /* Can't put an object in itself */
823 if (sack->type != CONTAINER) {
824 new_draw_info_format(NDI_UNIQUE, 0,op,
825 "The %s is not a container.", query_name(sack));
826 return;
827 }
828 if (QUERY_FLAG(tmp,FLAG_STARTEQUIP)) {
829 new_draw_info_format(NDI_UNIQUE, 0,op,
830 "You cannot put the %s in the container.", query_name(tmp));
831 return;
832 }
833 if (tmp->type == CONTAINER && tmp->inv) {
834
835 /* Eneq(@csd.uu.se): If the object to be dropped is a container
836 * we instead move the contents of that container into the active
837 * container, this is only done if the object has something in it.
838 */
839 sack2 = tmp;
840 new_draw_info_format(NDI_UNIQUE, 0,op, "You move the items from %s into %s.",
841 query_name(tmp), query_name(op->container));
842 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp) {
843 tmp = tmp2->below;
844 if (sack_can_hold(op, op->container, tmp2,tmp2->nrof))
845 put_object_in_sack (op, sack, tmp2, 0);
846 else {
847 sprintf(buf,"Your %s fills up.", query_name(op->container));
848 new_draw_info(NDI_UNIQUE, 0,op, buf);
849 break;
850 }
851 }
852 esrv_update_item (UPD_WEIGHT, op, sack2);
853 return;
854 }
855
856 if (! sack_can_hold (op, sack, tmp,(nrof?nrof:tmp->nrof)))
857 return;
858
859 if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
860 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
861 return;
862 }
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 sprintf(buf, "You put the %s in ", query_name(tmp));
883 strcat (buf, query_name(sack));
884 strcat (buf, ".");
885 tmp_tag = tmp->count;
886 tmp2 = insert_ob_in_ob(tmp, sack);
887 new_draw_info(NDI_UNIQUE, 0,op,buf);
888 fix_player(op); /* This is overkill, fix_player() is called somewhere */
889 /* in object.c */
890
891 /* If an object merged (and thus, different object), we need to
892 * delete the original.
893 */
894 if (tmp2 != tmp)
895 esrv_del_item (op->contr, tmp_tag);
896
897 esrv_send_item (op, tmp2);
898 /* update the sacks weight */
899 esrv_update_item (UPD_WEIGHT, op, sack);
900 }
901
902 /*
903 * This function was part of drop, now is own function.
904 * Player 'op' tries to drop object 'tmp', if tmp is non zero, then
905 * nrof objects is tried to dropped.
906 * This is used when dropping objects onto the floor.
907 */
908 void drop_object (object *op, object *tmp, uint32 nrof)
909 {
910 char buf[MAX_BUF];
911 object *floor;
912
913 if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
914 #if 0
915 /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
916 new_draw_info(NDI_UNIQUE, 0,op, "This item can't be dropped.");
917 #endif
918 return;
919 }
920
921 if(QUERY_FLAG(tmp, FLAG_APPLIED)) {
922 if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
923 return; /* can't unapply it */
924 }
925
926 /* We are only dropping some of the items. We split the current objec
927 * off
928 */
929 if(nrof && tmp->nrof != nrof) {
930 object *tmp2 = tmp;
931 tag_t tmp2_tag = tmp2->count;
932 tmp = get_split_ob (tmp, nrof);
933 if(!tmp) {
934 new_draw_info(NDI_UNIQUE, 0,op, errmsg);
935 return;
936 }
937 /* Tell a client what happened rest of objects. tmp2 is now the
938 * original object
939 */
940 if (op->type == PLAYER)
941 {
942 if (was_destroyed (tmp2, tmp2_tag))
943 esrv_del_item (op->contr, tmp2_tag);
944 else
945 esrv_send_item (op, tmp2);
946 }
947 } else
948 remove_ob (tmp);
949 /* Lauwenmark: Handle for plugin drop event */
950 if (execute_event(tmp, EVENT_DROP,op,NULL,NULL,SCRIPT_FIX_ALL)!= 0)
951 return;
952
953 if (QUERY_FLAG (tmp, FLAG_STARTEQUIP)) {
954 sprintf(buf,"You drop the %s.", query_name(tmp));
955 new_draw_info(NDI_UNIQUE, 0,op,buf);
956 new_draw_info(NDI_UNIQUE, 0,op,"The gods who lent it to you retrieves it.");
957 if (op->type==PLAYER)
958 esrv_del_item (op->contr, tmp->count);
959 free_object(tmp);
960 fix_player(op);
961 return;
962 }
963
964 /* If SAVE_INTERVAL is commented out, we never want to save
965 * the player here.
966 */
967 #ifdef SAVE_INTERVAL
968 /* I'm not sure why there is a value check - since the save
969 * is done every SAVE_INTERVAL seconds, why care the value
970 * of what he is dropping?
971 */
972 if (op->type == PLAYER && !QUERY_FLAG(tmp, FLAG_UNPAID) &&
973 (tmp->nrof ? tmp->value * tmp->nrof : tmp->value > 2000) &&
974 (op->contr->last_save_time + SAVE_INTERVAL) <= time(NULL)) {
975 save_player(op, 1);
976 op->contr->last_save_time = time(NULL);
977 }
978 #endif /* SAVE_INTERVAL */
979
980
981 floor = get_map_ob (op->map, op->x, op->y);
982 if( floor && floor->type == SHOP_FLOOR &&
983 !QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY)
984 sell_item(tmp,op);
985
986 tmp->x = op->x;
987 tmp->y = op->y;
988
989 if (op->type == PLAYER)
990 esrv_del_item (op->contr, tmp->count);
991 insert_ob_in_map(tmp, op->map, op,0);
992
993
994 SET_FLAG (op, FLAG_NO_APPLY);
995 remove_ob(op);
996 insert_ob_in_map(op, op->map, op, INS_NO_MERGE | INS_NO_WALK_ON);
997 CLEAR_FLAG (op, FLAG_NO_APPLY);
998
999 /* Call this before we update the various windows/players. At least
1000 * that we, we know the weight is correct.
1001 */
1002 fix_player(op); /* This is overkill, fix_player() is called somewhere */
1003 /* in object.c */
1004
1005 if (op->type == PLAYER)
1006 {
1007 op->contr->socket.update_look = 1;
1008 /* esrv_send_item (op, tmp);*/
1009 /* Need to update the weight for the player */
1010 esrv_send_item (op, op);
1011 }
1012 }
1013
1014 void drop(object *op, object *tmp)
1015 {
1016 /* Hopeful fix for disappearing objects when dropping from a container -
1017 * somehow, players get an invisible object in the container, and the
1018 * old logic would skip over invisible objects - works fine for the
1019 * playes inventory, but drop inventory wants to use the next value.
1020 */
1021 if (tmp->invisible) {
1022 /* if the following is the case, it must be in an container. */
1023 if (tmp->env && tmp->env->type != PLAYER) {
1024 /* Just toss the object - probably shouldn't be hanging
1025 * around anyways
1026 */
1027 remove_ob(tmp);
1028 free_object(tmp);
1029 return;
1030 } else {
1031 while(tmp!=NULL && tmp->invisible)
1032 tmp=tmp->below;
1033 }
1034 }
1035
1036 if (tmp==NULL) {
1037 new_draw_info(NDI_UNIQUE, 0,op,"You don't have anything to drop.");
1038 return;
1039 }
1040 if (QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1041 new_draw_info(NDI_UNIQUE, 0,op,"This item is locked");
1042 return;
1043 }
1044 if (QUERY_FLAG(tmp, FLAG_NO_DROP)) {
1045 #if 0
1046 /* Eneq(@csd.uu.se): Objects with NO_DROP defined can't be dropped. */
1047 new_draw_info(NDI_UNIQUE, 0,op, "This item can't be dropped.");
1048 #endif
1049 return;
1050 }
1051
1052 if (op->type == PLAYER)
1053 {
1054 if (op->contr->last_used==tmp && op->contr->last_used_id == tmp->count) {
1055 object *n=NULL;
1056 if(tmp->below != NULL)
1057 n = tmp->below;
1058 else if(tmp->above != NULL)
1059 n = tmp->above;
1060 op->contr->last_used = n;
1061 if (n != NULL)
1062 op->contr->last_used_id = n->count;
1063 else
1064 op->contr->last_used_id = 0;
1065 }
1066 };
1067
1068 if (op->container) {
1069 if (op->type == PLAYER)
1070 {
1071 put_object_in_sack (op, op->container, tmp, op->contr->count);
1072 } else {
1073 put_object_in_sack(op, op->container, tmp, 0);
1074 };
1075 } else {
1076 if (op->type == PLAYER)
1077 {
1078 drop_object (op, tmp, op->contr->count);
1079 } else {
1080 drop_object(op,tmp,0);
1081 };
1082 }
1083 if (op->type == PLAYER)
1084 op->contr->count = 0;
1085 }
1086
1087
1088
1089 /* Command will drop all items that have not been locked */
1090 int command_dropall (object *op, char *params) {
1091
1092 object * curinv, *nextinv;
1093
1094 if(op->inv == NULL) {
1095 new_draw_info(NDI_UNIQUE, 0,op,"Nothing to drop!");
1096 return 0;
1097 }
1098
1099 curinv = op->inv;
1100
1101 /*
1102 This is the default. Drops everything not locked or considered
1103 not something that should be dropped.
1104 */
1105 /*
1106 Care must be taken that the next item pointer is not to money as
1107 the drop() routine will do unknown things to it when dropping
1108 in a shop. --Tero.Pelander@utu.fi
1109 */
1110
1111 if(params==NULL) {
1112 while(curinv != NULL) {
1113 nextinv = curinv->below;
1114 while (nextinv && nextinv->type==MONEY)
1115 nextinv = nextinv->below;
1116 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && curinv->type != MONEY &&
1117 curinv->type != FOOD && curinv->type != KEY &&
1118 curinv->type != SPECIAL_KEY && curinv->type != GEM &&
1119 !curinv->invisible &&
1120 (curinv->type!=CONTAINER || op->container!=curinv))
1121 {
1122 drop(op,curinv);
1123 }
1124 curinv = nextinv;
1125 }
1126 }
1127
1128 else if(strcmp(params, "weapons") == 0) {
1129 while(curinv != NULL) {
1130 nextinv = curinv->below;
1131 while (nextinv && nextinv->type==MONEY)
1132 nextinv = nextinv->below;
1133 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ((curinv->type == WEAPON) ||
1134 (curinv->type == BOW) || (curinv->type == ARROW)))
1135 {
1136 drop(op,curinv);
1137 }
1138 curinv = nextinv;
1139 }
1140 }
1141
1142 else if(strcmp(params, "armor") == 0 || strcmp(params, "armour") == 0) {
1143 while(curinv != NULL) {
1144 nextinv = curinv->below;
1145 while (nextinv && nextinv->type==MONEY)
1146 nextinv = nextinv->below;
1147 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ((curinv->type == ARMOUR) ||
1148 curinv->type == SHIELD || curinv->type==HELMET))
1149 {
1150 drop(op,curinv);
1151 }
1152 curinv = nextinv;
1153 }
1154 }
1155
1156 else if(strcmp(params, "misc") == 0) {
1157 while(curinv != NULL) {
1158 nextinv = curinv->below;
1159 while (nextinv && nextinv->type==MONEY)
1160 nextinv = nextinv->below;
1161 if(! QUERY_FLAG(curinv,FLAG_INV_LOCKED) && ! QUERY_FLAG(curinv,FLAG_APPLIED)) {
1162 switch(curinv->type) {
1163 case HORN:
1164 case BOOK:
1165 case SPELLBOOK:
1166 case GIRDLE:
1167 case AMULET:
1168 case RING:
1169 case CLOAK:
1170 case BOOTS:
1171 case GLOVES:
1172 case BRACERS:
1173 case SCROLL:
1174 case ARMOUR_IMPROVER:
1175 case WEAPON_IMPROVER:
1176 case WAND:
1177 case ROD:
1178 case POTION:
1179 drop(op,curinv);
1180 curinv = nextinv;
1181 break;
1182 default:
1183 curinv = nextinv;
1184 break;
1185 }
1186 }
1187 curinv = nextinv;
1188 }
1189 }
1190 op->contr->socket.update_look=1;
1191 /* draw_look(op);*/
1192 return 0;
1193 }
1194
1195 /* Object op wants to drop object(s) params. params can be a
1196 * comma seperated list.
1197 */
1198
1199 int command_drop (object *op, char *params)
1200 {
1201 object *tmp, *next;
1202 int did_one=0;
1203
1204 if (!params) {
1205 new_draw_info(NDI_UNIQUE,0, op, "Drop what?");
1206 return 0;
1207 } else {
1208 for (tmp=op->inv; tmp; tmp=next) {
1209 next=tmp->below;
1210 if (QUERY_FLAG(tmp,FLAG_NO_DROP) ||
1211 tmp->invisible) continue;
1212 if (item_matched_string(op,tmp,params)) {
1213 drop(op, tmp);
1214 did_one=1;
1215 }
1216 }
1217 if (!did_one) new_draw_info(NDI_UNIQUE, 0,op,"Nothing to drop.");
1218 }
1219 if (op->type==PLAYER)
1220 {
1221 op->contr->count=0;
1222 op->contr->socket.update_look=1;
1223 };
1224 /* draw_look(op);*/
1225 return 0;
1226 }
1227
1228 int command_examine (object *op, char *params)
1229 {
1230 if (!params) {
1231 object *tmp=op->below;
1232 while (tmp && !LOOK_OBJ(tmp)) tmp=tmp->below;
1233 if (tmp) examine(op,tmp);
1234 }
1235 else {
1236 object *tmp=find_best_object_match(op,params);
1237 if (tmp)
1238 examine(op,tmp);
1239 else
1240 new_draw_info_format(NDI_UNIQUE,0,op,"Could not find an object that matches %s",params);
1241 }
1242 return 0;
1243 }
1244
1245 /* op should be a player.
1246 * we return the object the player has marked with the 'mark' command
1247 * below. If no match is found (or object has changed), we return
1248 * NULL. We leave it up to the calling function to print messages if
1249 * nothing is found.
1250 */
1251 object *find_marked_object(object *op)
1252 {
1253 object *tmp;
1254
1255 if (!op || !op->contr) return NULL;
1256 if (!op->contr->mark) {
1257 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1258 return NULL;
1259 }
1260 /* This may seem like overkill, but we need to make sure that they
1261 * player hasn't dropped the item. We use count on the off chance that
1262 * an item got reincarnated at some point.
1263 */
1264 for (tmp=op->inv; tmp; tmp=tmp->below) {
1265 if (tmp->invisible) continue;
1266 if (tmp == op->contr->mark) {
1267 if (tmp->count == op->contr->mark_count)
1268 return tmp;
1269 else {
1270 op->contr->mark=NULL;
1271 op->contr->mark_count=0;
1272 /* new_draw_info(NDI_UNIQUE,0,op,"You have no marked object");*/
1273 return NULL;
1274 }
1275 }
1276 }
1277 return NULL;
1278 }
1279
1280
1281 /* op should be a player, params is any params.
1282 * If no params given, we print out the currently marked object.
1283 * otherwise, try to find a matching object - try best match first.
1284 */
1285 int command_mark(object *op, char *params)
1286 {
1287 if (!op->contr) return 1;
1288 if (!params) {
1289 object *mark=find_marked_object(op);
1290 if (!mark) new_draw_info(NDI_UNIQUE,0,op,"You have no marked object.");
1291 else new_draw_info_format(NDI_UNIQUE,0,op,"%s is marked.", query_name(mark));
1292 }
1293 else {
1294 object *mark1=find_best_object_match(op, params);
1295 if (!mark1) {
1296 new_draw_info_format(NDI_UNIQUE,0,op,"Could not find an object that matches %s",params);
1297 return 1;
1298 }
1299 else {
1300 op->contr->mark=mark1;
1301 op->contr->mark_count=mark1->count;
1302 new_draw_info_format(NDI_UNIQUE,0,op,"Marked item %s", query_name(mark1));
1303 return 0;
1304 }
1305 }
1306 return 0; /*shouldnt get here */
1307 }
1308
1309
1310 /* op is the player
1311 * tmp is the monster being examined.
1312 */
1313 void examine_monster(object *op,object *tmp) {
1314 object *mon=tmp->head?tmp->head:tmp;
1315
1316 if(QUERY_FLAG(mon,FLAG_UNDEAD))
1317 new_draw_info(NDI_UNIQUE, 0,op,"It is an undead force.");
1318 if(mon->level>op->level)
1319 new_draw_info(NDI_UNIQUE, 0,op,"It is likely more powerful than you.");
1320 else if(mon->level<op->level)
1321 new_draw_info(NDI_UNIQUE, 0,op,"It is likely less powerful than you.");
1322 else
1323 new_draw_info(NDI_UNIQUE, 0,op,"It is probably as powerful as you.");
1324 if(mon->attacktype&AT_ACID)
1325 new_draw_info(NDI_UNIQUE, 0,op,"You seem to smell an acrid odor.");
1326
1327 /* Anyone know why this used to use the clone value instead of the
1328 * maxhp field? This seems that it should give more accurate results.
1329 */
1330 switch((mon->stats.hp+1)*4/(mon->stats.maxhp+1)) { /* From 1-4 */
1331 case 1:
1332 new_draw_info(NDI_UNIQUE, 0,op,"It is in a bad shape.");
1333 break;
1334 case 2:
1335 new_draw_info(NDI_UNIQUE, 0,op,"It is hurt.");
1336 break;
1337 case 3:
1338 new_draw_info(NDI_UNIQUE, 0,op,"It is somewhat hurt.");
1339 break;
1340 case 4:
1341 new_draw_info(NDI_UNIQUE, 0,op,"It is in excellent shape.");
1342 break;
1343 }
1344 if(present_in_ob(POISONING,mon)!=NULL)
1345 new_draw_info(NDI_UNIQUE, 0,op,"It looks very ill.");
1346 }
1347
1348
1349 /* tmp is the object being described, pl is who is examing it. */
1350 char *long_desc(object *tmp, object *pl) {
1351 static char buf[VERY_BIG_BUF];
1352 char *cp;
1353
1354 if(tmp==NULL)
1355 return "";
1356
1357 buf[0]='\0';
1358 switch(tmp->type) {
1359 case RING:
1360 case SKILL:
1361 case WEAPON:
1362 case ARMOUR:
1363 case BRACERS:
1364 case HELMET:
1365 case SHIELD:
1366 case BOOTS:
1367 case GLOVES:
1368 case AMULET:
1369 case GIRDLE:
1370 case BOW:
1371 case ARROW:
1372 case CLOAK:
1373 case FOOD:
1374 case DRINK:
1375 case FLESH:
1376 case SKILL_TOOL:
1377 case POWER_CRYSTAL:
1378 if(*(cp=describe_item(tmp, pl))!='\0') {
1379 int len;
1380
1381 strncpy(buf,query_name(tmp), VERY_BIG_BUF-1);
1382 buf[VERY_BIG_BUF-1]=0;
1383 len=strlen(buf);
1384 if (len<VERY_BIG_BUF-5) {
1385 /* Since we know the length, we save a few cpu cycles by using
1386 * it instead of calling strcat */
1387 strcpy(buf+len," ");
1388 len++;
1389 strncpy(buf+len, cp, VERY_BIG_BUF-len-1);
1390 buf[VERY_BIG_BUF-1]=0;
1391 }
1392 }
1393 }
1394 if(buf[0]=='\0') {
1395 strncpy(buf,query_name(tmp), VERY_BIG_BUF-1);
1396 buf[VERY_BIG_BUF-1]=0;
1397 }
1398
1399 return buf;
1400 }
1401
1402 void examine(object *op, object *tmp) {
1403 char buf[VERY_BIG_BUF];
1404 int i;
1405
1406 if (tmp == NULL || tmp->type == CLOSE_CON)
1407 return;
1408
1409 strcpy(buf,"That is ");
1410 strncat(buf, long_desc(tmp, op), VERY_BIG_BUF-strlen(buf)-1);
1411 buf[VERY_BIG_BUF-1]=0;
1412
1413 new_draw_info(NDI_UNIQUE, 0,op,buf);
1414 buf[0]='\0';
1415
1416 if(tmp->custom_name) {
1417 strcpy(buf,"You name it ");
1418 strncat(buf, tmp->custom_name, VERY_BIG_BUF-strlen(buf)-1);
1419 buf[VERY_BIG_BUF-1]=0;
1420 new_draw_info(NDI_UNIQUE, 0,op,buf);
1421 buf[0]='\0';
1422 }
1423
1424 switch(tmp->type) {
1425 case SPELLBOOK:
1426 if(QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->inv ) {
1427 sprintf(buf,"%s is a %s level %s spell",
1428 tmp->inv->name, get_levelnumber(tmp->inv->level),
1429 tmp->inv->skill);
1430 }
1431 break;
1432
1433 case BOOK:
1434 if(tmp->msg!=NULL)
1435 strcpy(buf,"Something is written in it.");
1436 break;
1437
1438 case CONTAINER:
1439 if(tmp->race!=NULL) {
1440 if(tmp->weight_limit && tmp->stats.Str<100)
1441 sprintf (buf,"It can hold only %s and its weight limit is %.1f kg.",
1442 tmp->race, tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
1443 else
1444 sprintf (buf,"It can hold only %s.", tmp->race);
1445 } else
1446 if(tmp->weight_limit && tmp->stats.Str<100)
1447 sprintf (buf,"Its weight limit is %.1f kg.",
1448 tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
1449 break;
1450
1451 case WAND:
1452 if(QUERY_FLAG(tmp, FLAG_IDENTIFIED))
1453 sprintf(buf,"It has %d charges left.",tmp->stats.food);
1454 break;
1455 }
1456
1457 if(buf[0]!='\0')
1458 new_draw_info(NDI_UNIQUE, 0,op,buf);
1459
1460 if(tmp->materialname != NULL && !tmp->msg) {
1461 sprintf(buf, "It is made of: %s.", tmp->materialname);
1462 new_draw_info(NDI_UNIQUE, 0, op, buf);
1463 }
1464 /* Where to wear this item */
1465 for (i=0; i < NUM_BODY_LOCATIONS; i++) {
1466 if (tmp->body_info[i]<-1) {
1467 if (op->body_info[i])
1468 new_draw_info_format(NDI_UNIQUE, 0,op,
1469 "It goes %s (%d)", body_locations[i].use_name, -tmp->body_info[i]);
1470 else
1471 new_draw_info_format(NDI_UNIQUE, 0,op,
1472 "It goes %s", body_locations[i].nonuse_name);
1473 } else if (tmp->body_info[i]) {
1474 if (op->body_info[i])
1475 new_draw_info_format(NDI_UNIQUE, 0,op,
1476 "It goes %s", body_locations[i].use_name);
1477 else
1478 new_draw_info_format(NDI_UNIQUE, 0,op,
1479 "It goes %s", body_locations[i].nonuse_name);
1480 }
1481 }
1482
1483 if(tmp->weight) {
1484 sprintf(buf,tmp->nrof>1?"They weigh %3.3f kg.":"It weighs %3.3f kg.",
1485 tmp->weight*(tmp->nrof?tmp->nrof:1)/1000.0);
1486 new_draw_info(NDI_UNIQUE, 0,op,buf);
1487 }
1488
1489 if (tmp->value && !QUERY_FLAG(tmp, FLAG_STARTEQUIP) && !QUERY_FLAG(tmp, FLAG_NO_PICK)) {
1490 object *floor;
1491 sprintf(buf,"You reckon %s worth %s.",
1492 tmp->nrof>1?"they are":"it is",query_cost_string(tmp,op,F_SELL | F_APPROX));
1493 new_draw_info(NDI_UNIQUE, 0,op,buf);
1494 floor = get_map_ob (op->map, op->x, op->y);
1495 if (floor && floor->type == SHOP_FLOOR) {
1496 if(QUERY_FLAG(tmp, FLAG_UNPAID))
1497 sprintf(buf,"%s would cost you %s.",
1498 tmp->nrof>1?"They":"It",query_cost_string(tmp,op,F_BUY | F_SHOP));
1499 else
1500 sprintf(buf,"You are offered %s for %s.",
1501 query_cost_string(tmp,op,F_SELL+F_SHOP), tmp->nrof>1?"them":"it");
1502 new_draw_info(NDI_UNIQUE, 0,op,buf);
1503 }
1504 }
1505
1506 if(QUERY_FLAG(tmp, FLAG_MONSTER))
1507 examine_monster(op,tmp);
1508
1509 /* Is this item buildable? */
1510 if ( QUERY_FLAG( tmp, FLAG_IS_BUILDABLE ) )
1511 new_draw_info( NDI_UNIQUE, 0, op, "This is a buildable item." );
1512
1513 /* Does the object have a message? Don't show message for all object
1514 * types - especially if the first entry is a match
1515 */
1516 if(tmp->msg && tmp->type != EXIT && tmp->type != BOOK &&
1517 tmp->type != CORPSE && !tmp->move_on &&
1518 strncasecmp(tmp->msg, "@match",7)) {
1519
1520 /* This is just a hack so when identifying hte items, we print
1521 * out the extra message
1522 */
1523 if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))
1524 new_draw_info(NDI_UNIQUE, 0,op, "The object has a story:");
1525
1526 new_draw_info(NDI_UNIQUE, 0,op,tmp->msg);
1527 }
1528 new_draw_info(NDI_UNIQUE, 0,op," "); /* Blank line */
1529 }
1530
1531 /*
1532 * inventory prints object's inventory. If inv==NULL then print player's
1533 * inventory.
1534 * [ Only items which are applied are showed. Tero.Haatanen@lut.fi ]
1535 */
1536 void inventory(object *op,object *inv) {
1537 object *tmp;
1538 char *in;
1539 int items = 0, length;
1540
1541 if (inv==NULL && op==NULL) {
1542 new_draw_info(NDI_UNIQUE, 0,op,"Inventory of what object?");
1543 return;
1544 }
1545 tmp = inv ? inv->inv : op->inv;
1546
1547 while (tmp) {
1548 if ((!tmp->invisible &&
1549 (inv==NULL || inv->type == CONTAINER || QUERY_FLAG(tmp, FLAG_APPLIED)))
1550 || (!op || QUERY_FLAG(op, FLAG_WIZ)))
1551 items++;
1552 tmp=tmp->below;
1553 }
1554 if (inv==NULL) { /* player's inventory */
1555 if (items==0) {
1556 new_draw_info(NDI_UNIQUE, 0,op,"You carry nothing.");
1557 return;
1558 } else {
1559 length = 28;
1560 in = "";
1561 if (op)
1562 clear_win_info(op);
1563 new_draw_info(NDI_UNIQUE, 0,op,"Inventory:");
1564 }
1565 } else {
1566 if (items==0)
1567 return;
1568 else {
1569 length = 28;
1570 in = " ";
1571 }
1572 }
1573 for (tmp=inv?inv->inv:op->inv; tmp; tmp=tmp->below) {
1574 if((!op||!QUERY_FLAG(op, FLAG_WIZ)) && (tmp->invisible ||
1575 (inv && inv->type != CONTAINER && !QUERY_FLAG(tmp, FLAG_APPLIED))))
1576 continue;
1577 if((!op || QUERY_FLAG(op, FLAG_WIZ)))
1578 new_draw_info_format(NDI_UNIQUE, 0,op ,"%s- %-*.*s (%5d) %-8s", in, length, length,
1579 query_name(tmp), tmp->count,query_weight(tmp));
1580 else
1581 new_draw_info_format(NDI_UNIQUE,0, op, "%s- %-*.*s %-8s", in, length+8,
1582 length+8, query_name(tmp),
1583 query_weight(tmp));
1584 }
1585 if(!inv && op) {
1586 new_draw_info_format(NDI_UNIQUE,0, op ,"%-*s %-8s",
1587 41,"Total weight :",query_weight(op));
1588 }
1589 }
1590
1591 static void display_new_pickup( object* op )
1592 {
1593 int i = op->contr->mode;
1594
1595 if(!(i & PU_NEWMODE)) return;
1596
1597 new_draw_info_format(NDI_UNIQUE, 0,op,"%d NEWMODE",i & PU_NEWMODE?1:0);
1598 new_draw_info_format(NDI_UNIQUE, 0,op,"%d DEBUG",i & PU_DEBUG?1:0);
1599 new_draw_info_format(NDI_UNIQUE, 0,op,"%d INHIBIT",i & PU_INHIBIT?1:0);
1600 new_draw_info_format(NDI_UNIQUE, 0,op,"%d STOP",i & PU_STOP?1:0);
1601
1602 new_draw_info_format(NDI_UNIQUE, 0,op,"%d <= x pickup weight/value RATIO (0==off)",(i & PU_RATIO)*5);
1603
1604 new_draw_info_format(NDI_UNIQUE, 0,op,"%d FOOD",i & PU_FOOD?1:0);
1605 new_draw_info_format(NDI_UNIQUE, 0,op,"%d DRINK",i & PU_DRINK?1:0);
1606 new_draw_info_format(NDI_UNIQUE, 0,op,"%d VALUABLES",i & PU_VALUABLES?1:0);
1607
1608 new_draw_info_format(NDI_UNIQUE, 0,op,"%d BOW",i & PU_BOW?1:0);
1609 new_draw_info_format(NDI_UNIQUE, 0,op,"%d ARROW",i & PU_ARROW?1:0);
1610
1611 new_draw_info_format(NDI_UNIQUE, 0,op,"%d HELMET",i & PU_HELMET?1:0);
1612 new_draw_info_format(NDI_UNIQUE, 0,op,"%d SHIELD",i & PU_SHIELD?1:0);
1613 new_draw_info_format(NDI_UNIQUE, 0,op,"%d ARMOUR",i & PU_ARMOUR?1:0);
1614
1615 new_draw_info_format(NDI_UNIQUE, 0,op,"%d BOOTS",i & PU_BOOTS?1:0);
1616 new_draw_info_format(NDI_UNIQUE, 0,op,"%d GLOVES",i & PU_GLOVES?1:0);
1617 new_draw_info_format(NDI_UNIQUE, 0,op,"%d CLOAK",i & PU_CLOAK?1:0);
1618 new_draw_info_format(NDI_UNIQUE, 0,op,"%d KEY",i & PU_KEY?1:0);
1619
1620 new_draw_info_format(NDI_UNIQUE, 0,op,"%d MISSILEWEAPON",i & PU_MISSILEWEAPON?1:0);
1621 new_draw_info_format(NDI_UNIQUE, 0,op,"%d ALLWEAPON",i & PU_ALLWEAPON?1:0);
1622 new_draw_info_format(NDI_UNIQUE, 0,op,"%d MAGICAL",i & PU_MAGICAL?1:0);
1623 new_draw_info_format(NDI_UNIQUE, 0,op,"%d POTION",i & PU_POTION?1:0);
1624
1625 new_draw_info_format(NDI_UNIQUE, 0,op,"%d SPELLBOOK",i & PU_SPELLBOOK?1:0);
1626 new_draw_info_format(NDI_UNIQUE, 0,op,"%d SKILLSCROLL",i & PU_SKILLSCROLL?1:0);
1627 new_draw_info_format(NDI_UNIQUE, 0,op,"%d READABLES",i & PU_READABLES?1:0);
1628 new_draw_info_format(NDI_UNIQUE, 0,op,"%d MAGICDEVICE", i & PU_MAGIC_DEVICE?1:0);
1629
1630 new_draw_info_format(NDI_UNIQUE, 0,op,"%d NOT CURSED", i & PU_NOT_CURSED?1:0);
1631
1632 new_draw_info_format(NDI_UNIQUE, 0,op,"%d JEWELS", i & PU_JEWELS?1:0);
1633
1634 new_draw_info_format(NDI_UNIQUE, 0,op,"");
1635 }
1636
1637 int command_pickup (object *op, char *params)
1638 {
1639 uint32 i;
1640 static const char* names[ ] = {
1641 "debug", "inhibit", "stop", "food", "drink", "valuables", "bow", "arrow", "helmet",
1642 "shield", "armour", "boots", "gloves", "cloak", "key", "missile", "allweapon",
1643 "magical", "potion", "spellbook", "skillscroll", "readables", "magicdevice", "notcursed", "jewels", NULL };
1644 static uint32 modes[ ] = {
1645 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET,
1646 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON,
1647 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, PU_NOT_CURSED, PU_JEWELS, 0 };
1648
1649 if(!params) {
1650 /* if the new mode is used, just print the settings */
1651 if(op->contr->mode & PU_NEWMODE)
1652 {
1653 display_new_pickup( op );
1654 return 1;
1655 }
1656 if(1) LOG(llevDebug, "command_pickup: !params\n");
1657 set_pickup_mode(op, (op->contr->mode > 6)? 0: op->contr->mode+1);
1658 return 0;
1659 }
1660
1661 while ( *params == ' ' && *params )
1662 params++;
1663
1664 if ( *params == '+' || *params == '-' )
1665 {
1666 int mode;
1667 for ( mode = 0; names[ mode ]; mode++ )
1668 {
1669 if ( !strcmp( names[ mode ], params + 1 ) )
1670 {
1671 i = op->contr->mode;
1672 if ( !( i & PU_NEWMODE ) )
1673 i = PU_NEWMODE;
1674 if ( *params == '+' )
1675 i = i | modes[ mode ];
1676 else
1677 i = i & ~modes[ mode ];
1678 op->contr->mode = i;
1679 display_new_pickup( op );
1680 return 1;
1681 }
1682 }
1683 new_draw_info_format( NDI_UNIQUE, 0, op, "Pickup: invalid item %s\n", params );
1684 return 1;
1685 }
1686
1687 if(sscanf(params, "%u", &i) != 1) {
1688 if(1) LOG(llevDebug, "command_pickup: params==NULL\n");
1689 new_draw_info(NDI_UNIQUE, 0,op,"Usage: pickup <0-7> or <value_density> .");
1690 return 1;
1691 }
1692 set_pickup_mode(op,i);
1693 display_new_pickup( op );
1694
1695 return 1;
1696 }
1697
1698 void set_pickup_mode(object *op,int i) {
1699 switch(op->contr->mode=i) {
1700 case 0:
1701 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Don't pick up.");
1702 break;
1703 case 1:
1704 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item.");
1705 break;
1706 case 2:
1707 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item and stop.");
1708 break;
1709 case 3:
1710 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Stop before picking up.");
1711 break;
1712 case 4:
1713 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items.");
1714 break;
1715 case 5:
1716 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items and stop.");
1717 break;
1718 case 6:
1719 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all magic items.");
1720 break;
1721 case 7:
1722 new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all coins and gems");
1723 break;
1724 }
1725 }
1726
1727 int command_search_items (object *op, char *params)
1728 {
1729 char buf[MAX_BUF];
1730
1731 if (settings.search_items == FALSE)
1732 return 1;
1733
1734 if(params == NULL) {
1735 if(op->contr->search_str[0]=='\0') {
1736 new_draw_info(NDI_UNIQUE, 0,op,"Example: search magic+1");
1737 new_draw_info(NDI_UNIQUE, 0,op,"Would automatically pick up all");
1738 new_draw_info(NDI_UNIQUE, 0,op,"items containing the word 'magic+1'.");
1739 return 1;
1740 }
1741 op->contr->search_str[0]='\0';
1742 new_draw_info(NDI_UNIQUE, 0,op,"Search mode turned off.");
1743 fix_player(op);
1744 return 1;
1745 }
1746 if((int)strlen(params) >= MAX_BUF) {
1747 new_draw_info(NDI_UNIQUE, 0,op,"Search string too long.");
1748 return 1;
1749 }
1750 strcpy(op->contr->search_str, params);
1751 sprintf(buf,"Searching for '%s'.",op->contr->search_str);
1752 new_draw_info(NDI_UNIQUE, 0,op,buf);
1753 fix_player(op);
1754 return 1;
1755 }
1756
1757 /*
1758 * Changing the custom name of an item
1759 *
1760 * Syntax is: rename <what object> to <new name>
1761 * if '<what object>' is omitted, marked object is used
1762 * if 'to <new name>' is omitted, custom name is cleared
1763 *
1764 * Names are considered for all purpose having a length <=127 (max length sent to client
1765 * by server) */
1766
1767 int command_rename_item(object *op, char *params)
1768 {
1769 char buf[VERY_BIG_BUF];
1770 int itemnumber;
1771 object *item=NULL;
1772 char *closebrace;
1773 size_t counter;
1774
1775 if (params) {
1776 /* Let's skip white spaces */
1777 while(' '==*params) params++;
1778
1779 /* Checking the first part */
1780 if (itemnumber = atoi(params)) {
1781 for (item=op->inv; item && ((item->count != itemnumber) || item->invisible); item=item->below);
1782 if (!item) {
1783 new_draw_info(NDI_UNIQUE,0,op,"Tried to rename an invalid item.");
1784 return 1;
1785 }
1786 while(isdigit(*params) || ' '==*params) params++;
1787 }
1788 else if ('<'==*params) {
1789 /* Got old name, let's get it & find appropriate matching item */
1790 closebrace=strchr(params,'>');
1791 if(!closebrace) {
1792 new_draw_info(NDI_UNIQUE,0,op,"Syntax error!");
1793 return 1;
1794 }
1795 /* Sanity check for buffer overruns */
1796 if((closebrace-params)>127) {
1797 new_draw_info(NDI_UNIQUE,0,op,"Old name too long (up to 127 characters allowed)!");
1798 return 1;
1799 }
1800 /* Copy the old name */
1801 strncpy(buf,params+1,closebrace-params-1);
1802 buf[closebrace-params-1]='\0';
1803
1804 /* Find best matching item */
1805 item=find_best_object_match(op,buf);
1806 if(!item) {
1807 new_draw_info(NDI_UNIQUE,0,op,"Could not find a matching item to rename.");
1808 return 1;
1809 }
1810
1811 /* Now need to move pointer to just after > */
1812 params=closebrace+1;
1813 while(' '==*params) params++;
1814
1815 } else {
1816 /* Use marked item */
1817 item=find_marked_object(op);
1818 if(!item) {
1819 new_draw_info(NDI_UNIQUE,0,op,"No marked item to rename.");
1820 return 1;
1821 }
1822 }
1823
1824 /* Now let's find the new name */
1825 if(!strncmp(params,"to ",3)) {
1826 params+=3;
1827 while(' '==*params) params++;
1828 if('<'!=*params) {
1829 new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expecting < at start of new name!");
1830 return 1;
1831 }
1832 closebrace=strchr(params+1,'>');
1833 if(!closebrace) {
1834 new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expecting > at end of new name!");
1835 return 1;
1836 }
1837
1838 /* Sanity check for buffer overruns */
1839 if((closebrace-params)>127) {
1840 new_draw_info(NDI_UNIQUE,0,op,"New name too long (up to 127 characters allowed)!");
1841 return 1;
1842 }
1843
1844 /* Copy the new name */
1845 strncpy(buf,params+1,closebrace-params-1);
1846 buf[closebrace-params-1]='\0';
1847
1848 /* Let's check it for weird characters */
1849 for(counter=0;counter<strlen(buf);counter++) {
1850 if(isalnum(buf[counter])) continue;
1851 if(' '==buf[counter]) continue;
1852 if('\''==buf[counter]) continue;
1853 if('+'==buf[counter]) continue;
1854 if('_'==buf[counter]) continue;
1855 if('-'==buf[counter]) continue;
1856
1857 /* If we come here, then the name contains an invalid character...
1858 tell the player & exit */
1859 new_draw_info(NDI_UNIQUE,0,op,"Invalid new name!");
1860 return 1;
1861 }
1862
1863 } else {
1864 /* If param contains something, then syntax error... */
1865 if(strlen(params)) {
1866 new_draw_info(NDI_UNIQUE,0,op,"Syntax error, expected 'to <' after old name!");
1867 return 1;
1868 }
1869 /* New name is empty */
1870 buf[0]='\0';
1871 }
1872 } else {
1873 /* Last case: params==NULL */
1874 item=find_marked_object(op);
1875 if(!item) {
1876 new_draw_info(NDI_UNIQUE,0,op,"No marked item to rename.");
1877 return 1;
1878 }
1879 buf[0]='\0';
1880 }
1881
1882 /* Coming here, everything is fine... */
1883 if(!strlen(buf)) {
1884 /* Clear custom name */
1885 if(item->custom_name) {
1886 FREE_AND_CLEAR_STR(item->custom_name);
1887
1888 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));
1889 esrv_update_item(UPD_NAME,op,item);
1890 } else {
1891 new_draw_info(NDI_UNIQUE,0,op,"This item has no custom name.");
1892 }
1893 } else {
1894 /* Set custom name */
1895 FREE_AND_COPY(item->custom_name,buf);
1896
1897 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);
1898 esrv_update_item(UPD_NAME,op,item);
1899 }
1900
1901 return 1;
1902 }