Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_c_object_c = 00003 * "$Id: c_object.c 12045 2009-07-09 23:07:04Z akirschbaum $"; 00004 */ 00005 /* 00006 CrossFire, A Multiplayer game for X-windows 00007 00008 Copyright (C) 2002-2007 Mark Wedel & Crossfire Development Team 00009 Copyright (C) 1992 Frank Tore Johansen 00010 00011 This program is free software; you can redistribute it and/or modify 00012 it under the terms of the GNU General Public License as published by 00013 the Free Software Foundation; either version 2 of the License, or 00014 (at your option) any later version. 00015 00016 This program is distributed in the hope that it will be useful, 00017 but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 GNU General Public License for more details. 00020 00021 You should have received a copy of the GNU General Public License 00022 along with this program; if not, write to the Free Software 00023 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00024 00025 The author can be reached via e-mail to crossfire-devel@real-time.com 00026 */ 00027 00034 #include <global.h> 00035 #include <loader.h> 00036 #include <skills.h> 00037 #ifndef __CEXTRACT__ 00038 #include <sproto.h> 00039 #endif 00040 #include <living.h> 00041 #include <math.h> 00042 00043 static void set_pickup_mode(const object *op, int i); 00044 00045 /* 00046 * Object id parsing functions 00047 */ 00048 00050 #define OBLINKMALLOC(p) if (!((p) = (objectlink *)malloc(sizeof(objectlink)))) \ 00051 fatal(OUT_OF_MEMORY); 00052 00070 static object *find_best_apply_object_match(object *start, object *pl, const char *params, int aflag) { 00071 object *tmp, *best = NULL; 00072 int match_val = 0, tmpmatch; 00073 00074 for (tmp = start; tmp; tmp = tmp->below) { 00075 if (tmp->invisible) 00076 continue; 00077 if ((aflag == AP_APPLY) && (QUERY_FLAG(tmp, FLAG_APPLIED))) 00078 continue; 00079 if ((aflag == AP_UNAPPLY) && (!QUERY_FLAG(tmp, FLAG_APPLIED))) 00080 continue; 00081 if ((tmpmatch = item_matched_string(pl, tmp, params)) > match_val) { 00082 match_val = tmpmatch; 00083 best = tmp; 00084 } 00085 } 00086 return best; 00087 } 00088 00099 static object *find_best_object_match(object *pl, const char *params) { 00100 return find_best_apply_object_match(pl->inv, pl, params, AP_NULL); 00101 } 00102 00113 int command_uskill(object *pl, char *params) { 00114 if (!params) { 00115 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00116 "Usage: use_skill <skill name>", NULL); 00117 return 0; 00118 } 00119 return use_skill(pl, params); 00120 } 00121 00132 int command_rskill(object *pl, char *params) { 00133 object *skill; 00134 00135 if (!params) { 00136 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00137 "Usage: ready_skill <skill name>", NULL); 00138 return 0; 00139 } 00140 skill = find_skill_by_name(pl, params); 00141 00142 if (!skill) { 00143 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING, 00144 "You have no knowledge of the skill %s", 00145 "You have no knowledge of the skill %s", 00146 params); 00147 return 0; 00148 } 00149 return change_skill(pl, skill, 0); 00150 } 00151 00152 00153 /* These functions (command_search, command_disarm) are really juse wrappers for 00154 * things like 'use_skill ...'). In fact, they should really be obsoleted 00155 * and replaced with those. 00156 */ 00167 int command_search(object *op, char *params) { 00168 return use_skill(op, skill_names[SK_FIND_TRAPS]); 00169 } 00170 00181 int command_disarm(object *op, char *params) { 00182 return use_skill(op, skill_names[SK_DISARM_TRAPS]); 00183 } 00184 00198 int command_throw(object *op, char *params) { 00199 object *skop; 00200 00201 skop = find_skill_by_name(op, skill_names[SK_THROWING]); 00202 if (skop) 00203 return do_skill(op, op, skop, op->facing, params); 00204 else { 00205 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING, 00206 "You have no knowledge of the skill throwing.", NULL); 00207 } 00208 return 0; 00209 } 00210 00221 int command_apply(object *op, char *params) { 00222 if (!params) { 00223 player_apply_below(op); 00224 return 0; 00225 } else { 00226 int aflag = 0; 00227 object *inv = op->inv; 00228 00229 while (*params == ' ') 00230 params++; 00231 if (!strncmp(params, "-a ", 3)) { 00232 aflag = AP_APPLY; 00233 params += 3; 00234 } 00235 if (!strncmp(params, "-u ", 3)) { 00236 aflag = AP_UNAPPLY; 00237 params += 3; 00238 } 00239 if (!strncmp(params, "-b ", 3)) { 00240 params += 3; 00241 if (op->container) 00242 inv = op->container->inv; 00243 else { 00244 inv = op; 00245 while (inv->above) 00246 inv = inv->above; 00247 } 00248 } 00249 while (*params == ' ') 00250 params++; 00251 00252 inv = find_best_apply_object_match(inv, op, params, aflag); 00253 if (inv) { 00254 player_apply(op, inv, aflag, 0); 00255 } else 00256 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00257 "Could not find any match to the %s.", 00258 "Could not find any match to the %s.", 00259 params); 00260 } 00261 return 0; 00262 } 00263 00282 int sack_can_hold(const object *pl, const object *sack, const object *op, uint32 nrof) { 00283 char name[MAX_BUF]; 00284 query_name(sack, name, MAX_BUF); 00285 00286 if (!QUERY_FLAG(sack, FLAG_APPLIED)) { 00287 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00288 "The %s is not active.", 00289 "The %s is not active.", 00290 name); 00291 return 0; 00292 } 00293 if (sack == op) { 00294 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00295 "You can't put the %s into itself.", 00296 "You can't put the %s into itself.", 00297 name); 00298 return 0; 00299 } 00300 if (sack->race 00301 && (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type))) { 00302 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00303 "You can put only %s into the %s.", 00304 "You can put only %s into the %s.", 00305 sack->race, name); 00306 return 0; 00307 } 00308 if (op->type == SPECIAL_KEY && sack->slaying && op->slaying) { 00309 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00310 "You can't want put the key into %s.", 00311 "You can't want put the key into %s.", 00312 name); 00313 return 0; 00314 } 00315 if (sack->weight_limit && sack->carrying+(nrof ? nrof : 1) 00316 *(op->weight+(op->type == CONTAINER ? (op->carrying*op->stats.Str) : 0)) 00317 *(100-sack->stats.Str)/100 > sack->weight_limit) { 00318 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00319 "That won't fit in the %s!", 00320 "That won't fit in the %s!", 00321 name); 00322 return 0; 00323 } 00324 /* All other checks pass, must be OK */ 00325 return 1; 00326 } 00327 00340 static void pick_up_object(object *pl, object *op, object *tmp, int nrof) { 00341 /* buf needs to be big (more than 256 chars) because you can get 00342 * very long item names. 00343 */ 00344 char buf[HUGE_BUF], name[MAX_BUF]; 00345 object *env = tmp->env; 00346 uint32 weight, effective_weight_limit; 00347 int tmp_nrof = tmp->nrof ? tmp->nrof : 1; 00348 00349 /* IF the player is flying & trying to take the item out of a container 00350 * that is in his inventory, let him. tmp->env points to the container 00351 * (sack, luggage, etc), tmp->env->env then points to the player (nested 00352 * containers not allowed as of now) 00353 */ 00354 if ((pl->move_type&MOVE_FLYING) 00355 && !QUERY_FLAG(pl, FLAG_WIZ) 00356 && get_player_container(tmp) != pl) { 00357 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00358 "You are levitating, you can't reach the ground!", NULL); 00359 return; 00360 } 00361 if (QUERY_FLAG(tmp, FLAG_NO_DROP)) 00362 return; 00363 00364 if (QUERY_FLAG(tmp, FLAG_WAS_WIZ) && !QUERY_FLAG(pl, FLAG_WAS_WIZ)) { 00365 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00366 "The object disappears in a puff of smoke! It must have been an illusion.", 00367 NULL); 00368 if (!QUERY_FLAG(tmp, FLAG_REMOVED)) 00369 remove_ob(tmp); 00370 free_object(tmp); 00371 return; 00372 } 00373 00374 if (nrof > tmp_nrof || nrof == 0) 00375 nrof = tmp_nrof; 00376 00377 /* Figure out how much weight this object will add to the player */ 00378 weight = tmp->weight*nrof; 00379 if (tmp->inv) 00380 weight += tmp->carrying*(100-tmp->stats.Str)/100; 00381 00382 if (pl->stats.Str <= MAX_STAT) 00383 effective_weight_limit = weight_limit[pl->stats.Str]; 00384 else 00385 effective_weight_limit = weight_limit[MAX_STAT]; 00386 00387 if ((pl->weight+pl->carrying+weight) > effective_weight_limit) { 00388 draw_ext_info(0, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00389 "That item is too heavy for you to pick up.", NULL); 00390 return; 00391 } 00392 00393 if (settings.real_wiz == FALSE && QUERY_FLAG(pl, FLAG_WAS_WIZ)) 00394 SET_FLAG(tmp, FLAG_WAS_WIZ); 00395 00396 if (nrof != tmp_nrof) { 00397 char failure[MAX_BUF]; 00398 00399 tmp = get_split_ob(tmp, nrof, failure, sizeof(failure)); 00400 if (!tmp) { 00401 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00402 failure, NULL); 00403 return; 00404 } 00405 } else { 00406 /* If the object is in a container, send a delete to the client. 00407 * - we are moving all the items from the container to elsewhere, 00408 * so it needs to be deleted. 00409 */ 00410 if (!QUERY_FLAG(tmp, FLAG_REMOVED)) { 00411 remove_ob(tmp); /* Unlink it */ 00412 } 00413 } 00414 query_name(tmp, name, MAX_BUF); 00415 00416 if (QUERY_FLAG(tmp, FLAG_UNPAID)) { 00417 char *value = stringbuffer_finish(query_cost_string(tmp, pl, F_BUY|F_SHOP, NULL)); 00418 snprintf(buf, sizeof(buf), "%s will cost you %s.", name, value); 00419 free(value); 00420 } else 00421 snprintf(buf, sizeof(buf), "You pick up the %s.", name); 00422 00423 /* Now item is about to be picked. */ 00424 if (execute_event(tmp, EVENT_PICKUP, pl, op, NULL, SCRIPT_FIX_ALL) != 0) 00425 return; 00426 00427 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 00428 buf, NULL); 00429 00430 tmp = insert_ob_in_ob(tmp, op); 00431 00432 /* All the stuff below deals with client/server code, and is only 00433 * usable by players 00434 */ 00435 if (pl->type != PLAYER) 00436 return; 00437 00438 /* Additional weight changes speed, etc */ 00439 fix_object(pl); 00440 00441 /* These are needed to update the weight for the container we 00442 * are putting the object in. 00443 */ 00444 if (op != pl) { 00445 esrv_update_item(UPD_WEIGHT, pl, op); 00446 esrv_update_item(UPD_WEIGHT, pl, pl); 00447 } 00448 00449 /* Update the container the object was in */ 00450 if (env && env != pl && env != op) 00451 esrv_update_item(UPD_WEIGHT, pl, env); 00452 } 00453 00462 void pick_up(object *op, object *alt) { 00463 /* modified slightly to allow monsters use this -b.t. 5-31-95 */ 00464 object *tmp = NULL, *tmp1; 00465 mapstruct *tmp_map = NULL; 00466 int count; 00467 tag_t tag; 00468 00469 /* Decide which object to pick. */ 00470 if (alt) { 00471 if (!can_pick(op, alt)) { 00472 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00473 "You can't pick up the %s.", 00474 "You can't pick up the %s.", 00475 alt->name); 00476 return; 00477 } 00478 tmp = alt; 00479 } else { 00480 if (op->below == NULL || !can_pick(op, op->below)) { 00481 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00482 "There is nothing to pick up here.", NULL); 00483 return; 00484 } 00485 tmp = op->below; 00486 } 00487 00488 /* it is possible that the object is a thrown object and is flying about. 00489 * in that case, what we want to pick up is the payload. Objects 00490 * that are thrown are encapsulated into a thrown object. 00491 * stop_item() returns the payload (unlinked from map) and gets rid of the 00492 * container object. If this object isn't picked up, we need to insert 00493 * it back on the map. 00494 * A bug here is that even attempting to pick up one of these objects will 00495 * result in this logic being called even if player is unable to pick it 00496 * up. 00497 */ 00498 00499 tmp_map = tmp->map; 00500 tmp1 = stop_item(tmp); 00501 if (tmp1 == NULL) 00502 return; 00503 00504 /* If it is a thrown object, insert it back into the map here. 00505 * makes life easier further along. Do no merge so pick up code 00506 * behaves more sanely. 00507 */ 00508 if (tmp1 != tmp) { 00509 tmp = insert_ob_in_map(tmp1, tmp_map, op, INS_NO_MERGE); 00510 } 00511 00512 if (tmp == NULL) return; 00513 00514 if (!can_pick(op, tmp)) return; 00515 00516 /* Establish how many of the object we are picking up */ 00517 if (op->type == PLAYER) { 00518 count = op->contr->count; 00519 if (count == 0) 00520 count = tmp->nrof; 00521 } else 00522 count = tmp->nrof; 00523 00524 /* container is open, so use it */ 00525 if (op->container) { 00526 alt = op->container; 00527 if (alt != tmp->env && !sack_can_hold(op, alt, tmp, count)) return; 00528 } else { 00529 /* non container pickup. See if player has any 00530 * active containers. 00531 */ 00532 object *container=NULL; 00533 00534 /* Look for any active containers that can hold this item. 00535 * we cover two cases here - the perfect match case, where we 00536 * break out of the loop, and the general case (have a container), 00537 * Moved this into a single loop - reduces redundant code, is 00538 * more efficient and easier to follow. MSW 2009-04-06 00539 */ 00540 for (alt = op->inv; alt; alt = alt->below) { 00541 if (alt->type == CONTAINER 00542 && QUERY_FLAG(alt, FLAG_APPLIED) 00543 && sack_can_hold(NULL, alt, tmp, count)) { 00544 if (alt->race && alt->race == tmp->race) { 00545 break; /* perfect match */ 00546 } 00547 else if (!container) { 00548 container = alt; 00549 } 00550 } 00551 } 00552 /* Note container could be null, but no reason to check for it */ 00553 if (!alt) alt=container; 00554 00555 if (!alt) 00556 alt = op; /* No free containers */ 00557 } 00558 /* see if this object is already in this container. If so, 00559 * move it to player inventory from this container. 00560 */ 00561 if (tmp->env == alt) { 00562 alt = op; 00563 } 00564 00565 /* Don't allow players to be put into containers. Instead, 00566 * just put them in the players inventory. 00567 */ 00568 if (tmp->type == CONTAINER && alt->type==CONTAINER) { 00569 alt = op; 00570 } 00571 #ifdef PICKUP_DEBUG 00572 LOG(llevDebug, "Pick_up(): %s picks %s (%d) and inserts it %s.\n", op->name, tmp->name, op->contr->count, alt->name); 00573 #endif 00574 00575 /* startequip items are not allowed to be put into containers 00576 * Not sure why we have this limitation 00577 */ 00578 if (op->type == PLAYER 00579 && alt->type == CONTAINER 00580 && QUERY_FLAG(tmp, FLAG_STARTEQUIP)) { 00581 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00582 "This object cannot be put into containers!", NULL); 00583 return; 00584 } 00585 00586 tag = tmp->count; 00587 pick_up_object(op, alt, tmp, count); 00588 if (op->type == PLAYER) 00589 op->contr->count = 0; 00590 } 00591 00602 int command_take(object *op, char *params) { 00603 object *tmp, *next; 00604 int ival; 00605 int missed = 0; 00606 00607 if (op->container) 00608 tmp = op->container->inv; 00609 else { 00610 tmp = op->above; 00611 if (tmp) 00612 while (tmp->above) { 00613 tmp = tmp->above; 00614 } 00615 if (!tmp) 00616 tmp = op->below; 00617 } 00618 00619 if (tmp == NULL) { 00620 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00621 "Nothing to take!", NULL); 00622 return 0; 00623 } 00624 00625 /* Makes processing easier */ 00626 if (params && *params == '\0') 00627 params = NULL; 00628 00629 while (tmp) { 00630 next = tmp->below; 00631 00632 if (tmp->invisible) { 00633 tmp = next; 00634 continue; 00635 } 00636 /* This following two if and else if could be merged into line 00637 * but that probably will make it more difficult to read, and 00638 * not make it any more efficient 00639 */ 00640 if (params && (ival = item_matched_string(op, tmp, params)) > 0) { 00641 if ((ival <= 2) && (!can_pick(op, tmp))) { 00642 if (!QUERY_FLAG(tmp, FLAG_IS_FLOOR))/* don't count floor tiles */ 00643 missed++; 00644 } else 00645 pick_up(op, tmp); 00646 } else if (can_pick(op, tmp) && !params) { 00647 pick_up(op, tmp); 00648 break; 00649 } 00650 tmp = next; 00651 /* Might as well just skip over the player immediately - 00652 * we know it can't be picked up 00653 */ 00654 if (tmp == op) 00655 tmp = tmp->below; 00656 } 00657 if (!params && !tmp) { 00658 for (tmp = op->below; tmp != NULL; tmp = tmp->next) 00659 if (!tmp->invisible) { 00660 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00661 "You can't pick up a %s.", 00662 "You can't pick up a %s.", 00663 tmp->name ? tmp->name : "null"); 00664 00665 break; 00666 } 00667 if (!tmp) 00668 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00669 "There is nothing to pick up.", NULL); 00670 } 00671 if (missed == 1) 00672 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00673 "You were unable to take one of the items.", NULL); 00674 else if (missed > 1) 00675 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 00676 "You were unable to take %d of the items.", 00677 "You were unable to take %d of the items.", 00678 missed); 00679 return 0; 00680 } 00681 00700 void put_object_in_sack(object *op, object *sack, object *tmp, uint32 nrof) { 00701 tag_t tmp_tag, tmp2_tag; 00702 object *tmp2, *sack2, *orig = sack; 00703 char name_sack[MAX_BUF], name_tmp[MAX_BUF]; 00704 00705 if (sack == tmp) 00706 return; /* Can't put an object in itself */ 00707 query_name(sack, name_sack, MAX_BUF); 00708 if (sack->type != CONTAINER && sack->type != TRANSPORT) { 00709 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00710 "The %s is not a container.", 00711 "The %s is not a container.", 00712 name_sack); 00713 return; 00714 } 00715 if (QUERY_FLAG(tmp, FLAG_STARTEQUIP)) { 00716 query_name(tmp, name_tmp, MAX_BUF); 00717 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00718 "You cannot put the %s in the %s.", 00719 "You cannot put the %s in the %s.", 00720 name_tmp, name_sack); 00721 return; 00722 } 00723 if (tmp->type == CONTAINER) { 00724 if (tmp->inv) { 00725 if (tmp->slaying) 00726 return; 00727 /* Eneq(@csd.uu.se): If the object to be dropped is a container 00728 * and does not require a key to be opened, 00729 * we instead move the contents of that container into the active 00730 * container, this is only done if the object has something in it. 00731 * If object is container but need a key, just don't do anything 00732 */ 00733 sack2 = tmp; 00734 query_name(tmp, name_tmp, MAX_BUF); 00735 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 00736 "You move the items from %s into %s.", 00737 "You move the items from %s into %s.", 00738 name_tmp, name_sack); 00739 00740 for (tmp2 = tmp->inv; tmp2; tmp2 = tmp) { 00741 tmp = tmp2->below; 00742 if ((sack->type == CONTAINER && sack_can_hold(op, op->container, tmp2, tmp2->nrof)) 00743 || (sack->type == TRANSPORT && transport_can_hold(sack, tmp2, tmp2->nrof))) { 00744 put_object_in_sack(op, sack, tmp2, 0); 00745 } else { 00746 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, 00747 MSG_TYPE_COMMAND_FAILURE, 00748 "Your %s fills up.", 00749 "Your %s fills up.", 00750 name_sack); 00751 break; 00752 } 00753 } 00754 esrv_update_item(UPD_WEIGHT, op, sack2); 00755 return; 00756 } else { 00757 query_name(tmp, name_tmp, MAX_BUF); 00758 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00759 "You can not put a %s into a %s", 00760 "You can not put a %s into a %s", 00761 name_tmp, 00762 name_sack); 00763 return; 00764 } 00765 00766 } 00767 00768 /* Don't worry about this for containers - our caller should have 00769 * already checked this. 00770 */ 00771 if ((sack->type == CONTAINER) && !sack_can_hold(op, sack, tmp, (nrof ? nrof : tmp->nrof))) 00772 return; 00773 00774 if (QUERY_FLAG(tmp, FLAG_APPLIED)) { 00775 if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) 00776 return; 00777 } 00778 00779 /* we want to put some portion of the item into the container */ 00780 if (nrof && tmp->nrof != nrof) { 00781 char failure[MAX_BUF]; 00782 object *tmp2 = tmp; 00783 00784 tmp2_tag = tmp2->count; 00785 tmp = get_split_ob(tmp, nrof, failure, sizeof(failure)); 00786 00787 if (!tmp) { 00788 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00789 failure, NULL); 00790 return; 00791 } 00792 } else 00793 remove_ob(tmp); 00794 00795 if (sack->nrof > 1) { 00796 orig = get_split_ob(sack, sack->nrof-1, NULL, 0); 00797 set_object_face_main(orig); 00798 CLEAR_FLAG(orig, FLAG_APPLIED); 00799 if (sack->env) { 00800 insert_ob_in_ob(orig, sack->env); 00801 } else { 00802 insert_ob_in_map_at(orig, sack->map, NULL, 0, sack->x, sack->y); 00803 orig->move_off = 0; 00804 } 00805 } 00806 00807 query_name(tmp, name_tmp, MAX_BUF); 00808 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 00809 "You put the %s in %s.", 00810 "You put the %s in %s.", 00811 name_tmp, name_sack); 00812 tmp_tag = tmp->count; 00813 tmp2 = insert_ob_in_ob(tmp, sack); 00814 if (!QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) 00815 fix_object(op); /* This is overkill, fix_player() is called somewhere */ 00816 /* in object.c */ 00817 00818 /* If a transport, need to update all the players in the transport 00819 * the view of what is in it. 00820 */ 00821 if (sack->type == TRANSPORT) { 00822 for (tmp = sack->inv; tmp; tmp = tmp->below) { 00823 if (tmp->type == PLAYER) 00824 tmp->contr->socket.update_look = 1; 00825 } 00826 } else { 00827 /* update the sacks weight */ 00828 esrv_update_item(UPD_WEIGHT, op, sack); 00829 } 00830 } 00831 00847 object *drop_object(object *op, object *tmp, uint32 nrof) { 00848 tag_t tmp_tag; 00849 00850 if (QUERY_FLAG(tmp, FLAG_NO_DROP)) { 00851 return NULL; 00852 } 00853 00854 if (QUERY_FLAG(tmp, FLAG_APPLIED)) { 00855 if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) 00856 return NULL; /* can't unapply it */ 00857 } 00858 00859 /* Lauwenmark: Handle for plugin drop event */ 00860 if (execute_event(tmp, EVENT_DROP, op, NULL, NULL, SCRIPT_FIX_ALL) != 0) 00861 return NULL; 00862 00863 /* We are only dropping some of the items. We split the current objec 00864 * off 00865 */ 00866 if (nrof && tmp->nrof != nrof) { 00867 char failure[MAX_BUF]; 00868 00869 tmp = get_split_ob(tmp, nrof, failure, sizeof(failure)); 00870 if (!tmp) { 00871 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00872 failure, NULL); 00873 return NULL; 00874 } 00875 } else 00876 remove_ob(tmp); 00877 00878 if (QUERY_FLAG(tmp, FLAG_STARTEQUIP)) { 00879 char name[MAX_BUF]; 00880 00881 query_name(tmp, name, MAX_BUF); 00882 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 00883 "You drop the %s. The gods who lent it to you retrieves it.", 00884 "You drop the %s. The gods who lent it to you retrieves it.", 00885 name); 00886 free_object(tmp); 00887 00888 if (!QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) 00889 fix_object(op); 00890 00891 return NULL; 00892 } 00893 00894 /* If SAVE_INTERVAL is commented out, we never want to save 00895 * the player here. 00896 */ 00897 #ifdef SAVE_INTERVAL 00898 /* I'm not sure why there is a value check - since the save 00899 * is done every SAVE_INTERVAL seconds, why care the value 00900 * of what he is dropping? 00901 */ 00902 if (op->type == PLAYER 00903 && !QUERY_FLAG(tmp, FLAG_UNPAID) 00904 && (tmp->nrof ? tmp->value*tmp->nrof : tmp->value > 2000) 00905 && (op->contr->last_save_time+SAVE_INTERVAL) <= time(NULL)) { 00906 save_player(op, 1); 00907 op->contr->last_save_time = time(NULL); 00908 } 00909 #endif /* SAVE_INTERVAL */ 00910 00911 00912 tmp->x = op->x; 00913 tmp->y = op->y; 00914 00915 tmp_tag = tmp->count; 00916 insert_ob_in_map(tmp, op->map, op, 0); 00917 if (!was_destroyed(tmp, tmp_tag) && !QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY && is_in_shop(op)) { 00918 sell_item(tmp, op); 00919 } 00920 00921 /* Call this before we update the various windows/players. At least 00922 * that we, we know the weight is correct. 00923 */ 00924 if (!QUERY_FLAG(op, FLAG_NO_FIX_PLAYER)) { 00925 fix_object(op); /* This is overkill, fix_player() is called somewhere */ 00926 /* in object.c */ 00927 00928 /* Need to update weight of player */ 00929 if (op->type == PLAYER) 00930 esrv_update_item(UPD_WEIGHT, op, op); 00931 } 00932 return tmp; 00933 } 00934 00943 void drop(object *op, object *tmp) { 00944 /* Hopeful fix for disappearing objects when dropping from a container - 00945 * somehow, players get an invisible object in the container, and the 00946 * old logic would skip over invisible objects - works fine for the 00947 * playes inventory, but drop inventory wants to use the next value. 00948 */ 00949 if (tmp->invisible) { 00950 /* if the following is the case, it must be in an container. */ 00951 if (tmp->env && tmp->env->type != PLAYER) { 00952 /* Just toss the object - probably shouldn't be hanging 00953 * around anyways 00954 */ 00955 remove_ob(tmp); 00956 free_object(tmp); 00957 return; 00958 } else { 00959 while (tmp != NULL && tmp->invisible) 00960 tmp = tmp->below; 00961 } 00962 } 00963 00964 if (tmp == NULL) { 00965 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00966 "You don't have anything to drop.", NULL); 00967 return; 00968 } 00969 if (QUERY_FLAG(tmp, FLAG_INV_LOCKED)) { 00970 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00971 "This item is locked", NULL); 00972 return; 00973 } 00974 if (QUERY_FLAG(tmp, FLAG_NO_DROP)) { 00975 return; 00976 } 00977 00978 if (op->type == PLAYER) { 00979 if (op->contr->last_used == tmp && op->contr->last_used_id == tmp->count) { 00980 object *n = NULL; 00981 00982 if (tmp->below != NULL) 00983 n = tmp->below; 00984 else if (tmp->above != NULL) 00985 n = tmp->above; 00986 op->contr->last_used = n; 00987 if (n != NULL) 00988 op->contr->last_used_id = n->count; 00989 else 00990 op->contr->last_used_id = 0; 00991 } 00992 }; 00993 00994 if (op->container) { 00995 if (op->type == PLAYER) { 00996 put_object_in_sack(op, op->container, tmp, op->contr->count); 00997 } else { 00998 put_object_in_sack(op, op->container, tmp, 0); 00999 }; 01000 } else { 01001 if (op->type == PLAYER) { 01002 drop_object(op, tmp, op->contr->count); 01003 } else { 01004 drop_object(op, tmp, 0); 01005 }; 01006 } 01007 if (op->type == PLAYER) 01008 op->contr->count = 0; 01009 } 01010 01021 int command_dropall(object *op, char *params) { 01022 object *curinv, *nextinv; 01023 int count = 0; 01024 01025 if (op->inv == NULL) { 01026 draw_ext_info(NDI_UNIQUE, 0, op, 01027 MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01028 "Nothing to drop!", NULL); 01029 return 0; 01030 } 01031 01032 curinv = op->inv; 01033 01034 if (op->contr) 01035 count = op->contr->count; 01036 01037 /* Set this so we don't call it for _every_ object that 01038 * is dropped. 01039 */ 01040 SET_FLAG(op, FLAG_NO_FIX_PLAYER); 01041 01042 /* 01043 * This is the default. Drops everything not locked or considered 01044 * not something that should be dropped. 01045 * Care must be taken that the next item pointer is not to money as 01046 * the drop() routine will do unknown things to it when dropping 01047 * in a shop. --Tero.Pelander@utu.fi 01048 */ 01049 if (params == NULL) { 01050 while (curinv != NULL) { 01051 nextinv = curinv->below; 01052 while (nextinv && nextinv->type == MONEY) 01053 nextinv = nextinv->below; 01054 if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) 01055 && curinv->type != MONEY 01056 && curinv->type != FOOD 01057 && curinv->type != KEY 01058 && curinv->type != SPECIAL_KEY 01059 && curinv->type != GEM 01060 && !curinv->invisible 01061 && (curinv->type != CONTAINER || op->container != curinv)) { 01062 drop(op, curinv); 01063 if (op->contr) 01064 op->contr->count = count; 01065 } 01066 curinv = nextinv; 01067 } 01068 } else if (strcmp(params, "weapons") == 0) { 01069 while (curinv != NULL) { 01070 nextinv = curinv->below; 01071 while (nextinv && nextinv->type == MONEY) 01072 nextinv = nextinv->below; 01073 if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) 01074 && ((curinv->type == WEAPON) || (curinv->type == BOW) || (curinv->type == ARROW))) { 01075 drop(op, curinv); 01076 if (op->contr) 01077 op->contr->count = count; 01078 } 01079 curinv = nextinv; 01080 } 01081 } else if (strcmp(params, "armor") == 0 || strcmp(params, "armour") == 0) { 01082 while (curinv != NULL) { 01083 nextinv = curinv->below; 01084 while (nextinv && nextinv->type == MONEY) 01085 nextinv = nextinv->below; 01086 if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) 01087 && ((curinv->type == ARMOUR) || curinv->type == SHIELD || curinv->type == HELMET)) { 01088 drop(op, curinv); 01089 if (op->contr) 01090 op->contr->count = count; 01091 } 01092 curinv = nextinv; 01093 } 01094 } else if (strcmp(params, "food") == 0) { 01095 while (curinv != NULL) { 01096 nextinv = curinv->below; 01097 if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) && (curinv->type == FOOD || curinv->type == DRINK)) { 01098 drop(op, curinv); 01099 if (op->contr) 01100 op->contr->count = count; 01101 } 01102 curinv = nextinv; 01103 } 01104 } else if (strcmp(params, "flesh") == 0) { 01105 while (curinv != NULL) { 01106 nextinv = curinv->below; 01107 if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) && (curinv->type == FLESH)) { 01108 drop(op, curinv); 01109 if (op->contr) 01110 op->contr->count = count; 01111 } 01112 curinv = nextinv; 01113 } 01114 } else if (strcmp(params, "misc") == 0) { 01115 while (curinv != NULL) { 01116 nextinv = curinv->below; 01117 while (nextinv && nextinv->type == MONEY) 01118 nextinv = nextinv->below; 01119 if (!QUERY_FLAG(curinv, FLAG_INV_LOCKED) 01120 && !QUERY_FLAG(curinv, FLAG_APPLIED)) { 01121 switch (curinv->type) { 01122 case HORN: 01123 case BOOK: 01124 case SPELLBOOK: 01125 case GIRDLE: 01126 case AMULET: 01127 case RING: 01128 case CLOAK: 01129 case BOOTS: 01130 case GLOVES: 01131 case BRACERS: 01132 case SCROLL: 01133 case ARMOUR_IMPROVER: 01134 case WEAPON_IMPROVER: 01135 case WAND: 01136 case ROD: 01137 case POTION: 01138 drop(op, curinv); 01139 curinv = nextinv; 01140 if (op->contr) 01141 op->contr->count = count; 01142 break; 01143 01144 default: 01145 curinv = nextinv; 01146 break; 01147 } 01148 } 01149 curinv = nextinv; 01150 } 01151 } 01152 op->contr->socket.update_look = 1; 01153 CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER); 01154 /* call it now, once */ 01155 fix_object(op); 01156 /* Need to update weight of player. Likewise, only do it once */ 01157 if (op->type == PLAYER) 01158 esrv_update_item(UPD_WEIGHT, op, op); 01159 01160 return 0; 01161 } 01162 01173 int command_drop(object *op, char *params) { 01174 object *tmp, *next; 01175 int did_one = 0; 01176 int ival = 0; 01177 int missed = 0; 01178 01179 if (!params) { 01180 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01181 "Drop what?", NULL); 01182 return 0; 01183 } else { 01184 for (tmp = op->inv; tmp; tmp = next) { 01185 next = tmp->below; 01186 if (QUERY_FLAG(tmp, FLAG_NO_DROP) || tmp->invisible) 01187 continue; 01188 if ((ival = item_matched_string(op, tmp, params)) > 0) { 01189 if ((QUERY_FLAG(tmp, FLAG_INV_LOCKED)) && ((ival == 1) || (ival == 2))) 01190 missed++; 01191 else 01192 drop(op, tmp); 01193 did_one = 1; 01194 } 01195 } 01196 if (!did_one) 01197 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01198 "Nothing to drop.", NULL); 01199 if (missed == 1) 01200 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01201 "One item couldn't be dropped because it was locked.", NULL); 01202 else if (missed > 1) 01203 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01204 "%d items couldn't be dropped because they were locked.", 01205 "%d items couldn't be dropped because they were locked.", 01206 missed); 01207 } 01208 if (op->type == PLAYER) { 01209 op->contr->count = 0; 01210 op->contr->socket.update_look = 1; 01211 } 01212 return 0; 01213 } 01214 01223 static void empty_container(object *container, object *pl) { 01224 object *inv; 01225 object *next; 01226 int left = 0; 01227 char name[MAX_BUF]; 01228 01229 if (!container->inv) 01230 return; 01231 01232 for (inv = container->inv; inv; inv = next) { 01233 next = inv->below; 01234 if (QUERY_FLAG(inv, FLAG_INV_LOCKED)) { 01235 /* you can have locked items in container. */ 01236 left++; 01237 continue; 01238 } 01239 drop(pl, inv); 01240 if (inv->below == next) 01241 /* item couldn't be dropped for some reason. */ 01242 left++; 01243 } 01244 esrv_update_item(UPD_WEIGHT, pl, container); 01245 01246 query_name(container, name, sizeof(name)); 01247 if (left) 01248 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, "You empty the %s except %d items.", NULL, name, left); 01249 else 01250 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, "You empty the %s.", NULL, name); 01251 } 01252 01263 int command_empty(object *op, char *params) { 01264 object *inv; 01265 object *container; 01266 01267 if (!params) { 01268 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01269 "Empty what?", NULL); 01270 return 0; 01271 } 01272 01273 if (strcmp(params, "all") == 0) { 01274 for (inv = op->inv; inv; inv = inv->below) 01275 if (inv->type == CONTAINER) 01276 empty_container(inv, op); 01277 return 0; 01278 } 01279 01280 container = find_best_object_match(op, params); 01281 if (!container) { 01282 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01283 "No such item.", NULL); 01284 return 0; 01285 } 01286 if (container->type != CONTAINER) { 01287 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01288 "This is not a container!", NULL); 01289 return 0; 01290 } 01291 empty_container(container, op); 01292 01293 return 0; 01294 } 01295 01306 int command_examine(object *op, char *params) { 01307 if (!params) { 01308 object *tmp = op->below; 01309 01310 while (tmp && !LOOK_OBJ(tmp)) 01311 tmp = tmp->below; 01312 if (tmp) 01313 examine(op, tmp); 01314 } else { 01315 object *tmp = find_best_object_match(op, params); 01316 01317 if (tmp) 01318 examine(op, tmp); 01319 else 01320 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01321 "Could not find an object that matches %s", 01322 "Could not find an object that matches %s", 01323 params); 01324 } 01325 return 0; 01326 } 01327 01339 object *find_marked_object(object *op) { 01340 object *tmp; 01341 01342 if (!op || !op->contr || !op->contr->mark) 01343 return NULL; 01344 01345 /* This may seem like overkill, but we need to make sure that they 01346 * player hasn't dropped the item. We use count on the off chance that 01347 * an item got reincarnated at some point. 01348 */ 01349 for (tmp = op->inv; tmp; tmp = tmp->below) { 01350 if (tmp->invisible) 01351 continue; 01352 if (tmp == op->contr->mark) { 01353 if (tmp->count == op->contr->mark_count) 01354 return tmp; 01355 else { 01356 op->contr->mark = NULL; 01357 op->contr->mark_count = 0; 01358 return NULL; 01359 } 01360 } 01361 } 01362 return NULL; 01363 } 01364 01376 int command_mark(object *op, char *params) { 01377 char name[MAX_BUF]; 01378 01379 if (!op->contr) 01380 return 1; 01381 if (!params) { 01382 object *mark = find_marked_object(op); 01383 if (!mark) 01384 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01385 "You have no marked object.", NULL); 01386 else { 01387 query_name(mark, name, MAX_BUF); 01388 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01389 "%s is marked.", 01390 "%s is marked.", 01391 name); 01392 } 01393 } else { 01394 object *mark1 = find_best_object_match(op, params); 01395 01396 if (!mark1) { 01397 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01398 "Could not find an object that matches %s", 01399 "Could not find an object that matches %s", 01400 params); 01401 return 1; 01402 } else { 01403 op->contr->mark = mark1; 01404 op->contr->mark_count = mark1->count; 01405 query_name(mark1, name, MAX_BUF); 01406 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01407 "Marked item %s", 01408 "Marked item %s", 01409 name); 01410 return 0; 01411 } 01412 } 01413 return 0; /*shouldnt get here */ 01414 } 01415 01424 void examine_monster(object *op, object *tmp) { 01425 object *mon = tmp->head ? tmp->head : tmp; 01426 01427 if (QUERY_FLAG(mon, FLAG_UNDEAD)) 01428 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01429 "It is an undead force.", NULL); 01430 if (mon->level > op->level) 01431 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01432 "It is likely more powerful than you.", NULL); 01433 else if (mon->level < op->level) 01434 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01435 "It is likely less powerful than you.", NULL); 01436 else 01437 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01438 "It is probably as powerful as you.", NULL); 01439 01440 if (mon->attacktype&AT_ACID) 01441 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01442 "You smell an acrid odor.", NULL); 01443 01444 /* Anyone know why this used to use the clone value instead of the 01445 * maxhp field? This seems that it should give more accurate results. 01446 */ 01447 switch ((mon->stats.hp+1)*4/(mon->stats.maxhp+1)) { /* From 1-4 */ 01448 case 1: 01449 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01450 "It is in a bad shape.", NULL); 01451 break; 01452 01453 case 2: 01454 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01455 "It is hurt.", NULL); 01456 break; 01457 01458 case 3: 01459 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01460 "It is somewhat hurt.", NULL); 01461 break; 01462 01463 case 4: 01464 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01465 "It is in excellent shape.", NULL); 01466 break; 01467 } 01468 if (present_in_ob(POISONING, mon) != NULL) 01469 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01470 "It looks very ill.", NULL); 01471 } 01472 01481 void examine(object *op, object *tmp) { 01482 char buf[VERY_BIG_BUF]; 01483 int in_shop; 01484 int i; 01485 01486 buf[0] = '\0'; 01487 01488 if (tmp == NULL || tmp->type == CLOSE_CON) 01489 return; 01490 01491 /* Put the description in buf. */ 01492 ob_describe(tmp, op, buf, sizeof(buf)); 01493 01494 /* Send the player the description, prepending "That is" if singular 01495 * and "Those are" if plural. 01496 */ 01497 if (tmp->nrof <= 1) 01498 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01499 "That is %s", 01500 "That is %s", 01501 buf); 01502 else 01503 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01504 "Those are %s", 01505 "Those are %s", 01506 buf); 01507 buf[0] = '\0'; 01508 01509 if (tmp->custom_name) { 01510 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01511 "You name it %s", 01512 "You name it %s", 01513 tmp->custom_name); 01514 } 01515 01516 switch (tmp->type) { 01517 case SPELLBOOK: 01518 if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->inv) { 01519 char level[100]; 01520 01521 get_levelnumber(tmp->inv->level, level, 100); 01522 snprintf(buf, sizeof(buf), "%s is a %s level %s spell", tmp->inv->name, level, tmp->inv->skill); 01523 } 01524 break; 01525 01526 case BOOK: 01527 if (tmp->msg != NULL) 01528 snprintf(buf, sizeof(buf), "Something is written in it."); 01529 break; 01530 01531 case CONTAINER: 01532 if (tmp->race != NULL) { 01533 if (tmp->weight_limit && tmp->stats.Str < 100) 01534 snprintf(buf, sizeof(buf), "It can hold only %s and its weight limit is %.1f kg.", tmp->race, tmp->weight_limit/(10.0*(100-tmp->stats.Str))); 01535 else 01536 snprintf(buf, sizeof(buf), "It can hold only %s.", tmp->race); 01537 } else 01538 if (tmp->weight_limit && tmp->stats.Str < 100) 01539 snprintf(buf, sizeof(buf), "Its weight limit is %.1f kg.", tmp->weight_limit/(10.0*(100-tmp->stats.Str))); 01540 break; 01541 01542 case WAND: 01543 if (QUERY_FLAG(tmp, FLAG_IDENTIFIED)) 01544 snprintf(buf, sizeof(buf), "It has %d charges left.", tmp->stats.food); 01545 break; 01546 } 01547 01548 if (buf[0] != '\0') 01549 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01550 buf, NULL); 01551 01552 if (tmp->materialname != NULL && !tmp->msg) { 01553 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01554 "It is made of: %s.", 01555 "It is made of: %s.", 01556 tmp->materialname); 01557 } 01558 /* Where to wear this item */ 01559 for (i = 0; i < NUM_BODY_LOCATIONS; i++) { 01560 if (tmp->body_info[i] < -1) { 01561 if (op->body_info[i]) 01562 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01563 "It goes %s (%d)", 01564 "It goes %s (%d)", 01565 body_locations[i].use_name, -tmp->body_info[i]); 01566 else 01567 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01568 "It goes %s", 01569 "It goes %s", 01570 body_locations[i].nonuse_name); 01571 } else if (tmp->body_info[i]) { 01572 if (op->body_info[i]) 01573 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01574 "It goes %s", 01575 "It goes %s", 01576 body_locations[i].use_name); 01577 else 01578 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01579 "It goes %s", 01580 "It goes %s", 01581 body_locations[i].nonuse_name); 01582 } 01583 } 01584 01585 if (tmp->weight) { 01586 snprintf(buf, sizeof(buf), tmp->nrof > 1 ? "They weigh %3.3f kg." : "It weighs %3.3f kg.", tmp->weight*((float)(tmp->nrof ? tmp->nrof : 1)/1000.0)); 01587 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01588 buf, NULL); 01589 } 01590 01591 in_shop = is_in_shop(op); 01592 01593 if (tmp->value && !QUERY_FLAG(tmp, FLAG_STARTEQUIP) && !QUERY_FLAG(tmp, FLAG_NO_PICK)) { 01594 char *value = stringbuffer_finish(query_cost_string(tmp, op, F_SELL|F_APPROX, NULL)); 01595 snprintf(buf, sizeof(buf), "You reckon %s worth %s.", tmp->nrof > 1 ? "they are" : "it is", value); 01596 free(value); 01597 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01598 buf, NULL); 01599 if (in_shop) { 01600 if (QUERY_FLAG(tmp, FLAG_UNPAID)) { 01601 value = stringbuffer_finish(query_cost_string(tmp, op, F_BUY|F_SHOP, NULL)); 01602 snprintf(buf, sizeof(buf), "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", value); 01603 free(value); 01604 } else { 01605 value = stringbuffer_finish(query_cost_string(tmp, op, F_SELL+F_SHOP, NULL)); 01606 snprintf(buf, sizeof(buf), "You are offered %s for %s.", value, tmp->nrof > 1 ? "them" : "it"); 01607 free(value); 01608 } 01609 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01610 buf, NULL); 01611 } 01612 } 01613 01614 if (QUERY_FLAG(tmp, FLAG_MONSTER)) 01615 examine_monster(op, tmp); 01616 01617 /* Is this item buildable? */ 01618 if (QUERY_FLAG(tmp, FLAG_IS_BUILDABLE)) 01619 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01620 "This is a buildable item.", NULL); 01621 01622 /* Does the object have a message? Don't show message for all object 01623 * types - especially if the first entry is a match 01624 */ 01625 if (tmp->msg 01626 && tmp->type != EXIT 01627 && tmp->type != BOOK 01628 && tmp->type != CORPSE 01629 && !tmp->move_on 01630 && strncasecmp(tmp->msg, "@match", 6)) { 01631 /* This is just a hack so when identifying hte items, we print 01632 * out the extra message 01633 */ 01634 if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED)) 01635 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01636 "The object has a story:", NULL); 01637 01638 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01639 tmp->msg, NULL); 01640 } 01641 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_EXAMINE, 01642 " ", " "); /* Blank line */ 01643 } 01644 01653 void inventory(object *op, object *inv) { 01654 object *tmp; 01655 const char *in; 01656 int items = 0, length; 01657 char weight[MAX_BUF], name[MAX_BUF]; 01658 01659 if (inv == NULL && op == NULL) { 01660 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01661 "Inventory of what object?", NULL); 01662 return; 01663 } 01664 tmp = inv ? inv->inv : op->inv; 01665 01666 while (tmp) { 01667 if ((!tmp->invisible && (inv == NULL || inv->type == CONTAINER || QUERY_FLAG(tmp, FLAG_APPLIED))) 01668 || (!op || QUERY_FLAG(op, FLAG_WIZ))) 01669 items++; 01670 tmp = tmp->below; 01671 } 01672 if (inv == NULL) { /* player's inventory */ 01673 if (items == 0) { 01674 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01675 "You carry nothing.", NULL); 01676 return; 01677 } else { 01678 length = 28; 01679 in = ""; 01680 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY, 01681 "Inventory:", NULL); 01682 } 01683 } else { 01684 if (items == 0) 01685 return; 01686 else { 01687 length = 28; 01688 in = " "; 01689 } 01690 } 01691 for (tmp = inv ? inv->inv : op->inv; tmp; tmp = tmp->below) { 01692 if ((!op || !QUERY_FLAG(op, FLAG_WIZ)) 01693 && (tmp->invisible || (inv && inv->type != CONTAINER && !QUERY_FLAG(tmp, FLAG_APPLIED)))) 01694 continue; 01695 query_weight(tmp, weight, MAX_BUF); 01696 query_name(tmp, name, MAX_BUF); 01697 if ((!op || QUERY_FLAG(op, FLAG_WIZ))) 01698 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY, 01699 "[fixed]%s- %-*.*s (%5d) %-8s", 01700 "%s- %-*.*s (%5d) %-8s", 01701 in, length, length, name, tmp->count, weight); 01702 else 01703 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY, 01704 "[fixed]%s- %-*.*s %-8s", 01705 "%s- %-*.*s %-8s", 01706 in, length+8, length+8, name, weight); 01707 } 01708 if (!inv && op) { 01709 query_weight(op, weight, MAX_BUF); 01710 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INVENTORY, 01711 "[fixed]%-*s %-8s", 01712 "%-*s %-8s", 01713 41, "Total weight :", weight); 01714 } 01715 } 01716 01723 static void display_new_pickup(const object *op) { 01724 int i = op->contr->mode; 01725 01726 if (!(i&PU_NEWMODE)) 01727 return; 01728 01729 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01730 "%d NEWMODE", 01731 "%d NEWMODE", 01732 i&PU_NEWMODE ? 1 : 0); 01733 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01734 "%d DEBUG", 01735 "%d DEBUG", 01736 i&PU_DEBUG ? 1 : 0); 01737 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01738 "%d INHIBIT", 01739 "%d INHIBIT", 01740 i&PU_INHIBIT ? 1 : 0); 01741 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01742 "%d STOP", 01743 "%d STOP", 01744 i&PU_STOP ? 1 : 0); 01745 01746 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01747 "%d <= x pickup weight/value RATIO (0==off)", 01748 "%d <= x pickup weight/value RATIO (0==off)", 01749 (i&PU_RATIO)*5); 01750 01751 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01752 "%d FOOD", 01753 "%d FOOD", 01754 i&PU_FOOD ? 1 : 0); 01755 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01756 "%d DRINK", 01757 "%d DRINK", 01758 i&PU_DRINK ? 1 : 0); 01759 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01760 "%d VALUABLES", 01761 "%d VALUABLES", 01762 i&PU_VALUABLES ? 1 : 0); 01763 01764 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01765 "%d BOW", 01766 "%d BOW", 01767 i&PU_BOW ? 1 : 0); 01768 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01769 "%d ARROW", 01770 "%d ARROW", 01771 i&PU_ARROW ? 1 : 0); 01772 01773 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01774 "%d HELMET", 01775 "%d HELMET", 01776 i&PU_HELMET ? 1 : 0); 01777 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01778 "%d SHIELD", 01779 "%d SHIELD", 01780 i&PU_SHIELD ? 1 : 0); 01781 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01782 "%d ARMOUR", 01783 "%d ARMOUR", 01784 i&PU_ARMOUR ? 1 : 0); 01785 01786 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01787 "%d BOOTS", 01788 "%d BOOTS", 01789 i&PU_BOOTS ? 1 : 0); 01790 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01791 "%d GLOVES", 01792 "%d GLOVES", 01793 i&PU_GLOVES ? 1 : 0); 01794 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01795 "%d CLOAK", 01796 "%d CLOAK", 01797 i&PU_CLOAK ? 1 : 0); 01798 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01799 "%d KEY", 01800 "%d KEY", 01801 i&PU_KEY ? 1 : 0); 01802 01803 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01804 "%d MISSILEWEAPON", 01805 "%d MISSILEWEAPON", 01806 i&PU_MISSILEWEAPON ? 1 : 0); 01807 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01808 "%d ALLWEAPON", 01809 "%d ALLWEAPON", 01810 i&PU_ALLWEAPON ? 1 : 0); 01811 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01812 "%d MAGICAL", 01813 "%d MAGICAL", 01814 i&PU_MAGICAL ? 1 : 0); 01815 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01816 "%d POTION", 01817 "%d POTION", 01818 i&PU_POTION ? 1 : 0); 01819 01820 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01821 "%d SPELLBOOK", 01822 "%d SPELLBOOK", 01823 i&PU_SPELLBOOK ? 1 : 0); 01824 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01825 "%d SKILLSCROLL", 01826 "%d SKILLSCROLL", 01827 i&PU_SKILLSCROLL ? 1 : 0); 01828 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01829 "%d READABLES", 01830 "%d READABLES", 01831 i&PU_READABLES ? 1 : 0); 01832 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01833 "%d MAGICDEVICE", 01834 "%d MAGICDEVICE", 01835 i&PU_MAGIC_DEVICE ? 1 : 0); 01836 01837 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01838 "%d NOT CURSED", 01839 "%d NOT CURSED", 01840 i&PU_NOT_CURSED ? 1 : 0); 01841 01842 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01843 "%d JEWELS", 01844 "%d JEWELS", 01845 i&PU_JEWELS ? 1 : 0); 01846 01847 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01848 "%d FLESH", 01849 "%d FLESH", 01850 i&PU_FLESH ? 1 : 0); 01851 01852 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_INFO, 01853 "", ""); 01854 } 01855 01867 int command_pickup(object *op, char *params) { 01868 uint32 i; 01869 static const char *names[] = { 01870 "debug", "inhibit", "stop", "food", "drink", 01871 "valuables", "bow", "arrow", "helmet", "shield", 01872 "armour", "boots", "gloves", "cloak", "key", 01873 "missile", "allweapon", "magical", "potion", "spellbook", 01874 "skillscroll", "readables", "magicdevice", "notcursed", "jewels", 01875 "flesh", NULL 01876 }; 01877 static const uint32 modes[] = { 01878 PU_DEBUG, PU_INHIBIT, PU_STOP, PU_FOOD, PU_DRINK, PU_VALUABLES, PU_BOW, PU_ARROW, PU_HELMET, 01879 PU_SHIELD, PU_ARMOUR, PU_BOOTS, PU_GLOVES, PU_CLOAK, PU_KEY, PU_MISSILEWEAPON, PU_ALLWEAPON, 01880 PU_MAGICAL, PU_POTION, PU_SPELLBOOK, PU_SKILLSCROLL, PU_READABLES, PU_MAGIC_DEVICE, 01881 PU_NOT_CURSED, PU_JEWELS, PU_FLESH, 0 01882 }; 01883 01884 if (!params) { 01885 /* if the new mode is used, just print the settings */ 01886 if (op->contr->mode&PU_NEWMODE) { 01887 display_new_pickup(op); 01888 return 1; 01889 } 01890 if (1) 01891 LOG(llevDebug, "command_pickup: !params\n"); 01892 set_pickup_mode(op, (op->contr->mode > 6) ? 0 : op->contr->mode+1); 01893 return 0; 01894 } 01895 01896 while (*params == ' ') 01897 params++; 01898 01899 if (*params == '+' || *params == '-') { 01900 int mode; 01901 01902 for (mode = 0; names[mode]; mode++) { 01903 if (!strcmp(names[mode], params+1)) { 01904 i = op->contr->mode; 01905 if (!(i&PU_NEWMODE)) 01906 i = PU_NEWMODE; 01907 if (*params == '+') 01908 i = i|modes[mode]; 01909 else 01910 i = i&~modes[mode]; 01911 op->contr->mode = i; 01912 display_new_pickup(op); 01913 return 1; 01914 } 01915 } 01916 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01917 "Pickup: invalid item %s\n", 01918 "Pickup: invalid item %s\n", 01919 params); 01920 return 1; 01921 } 01922 01923 if (sscanf(params, "%u", &i) != 1) { 01924 if (1) 01925 LOG(llevDebug, "command_pickup: params==NULL\n"); 01926 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 01927 "Usage: pickup <0-7> or <value_density> .", NULL); 01928 return 1; 01929 } 01930 set_pickup_mode(op, i); 01931 display_new_pickup(op); 01932 01933 return 1; 01934 } 01935 01944 static void set_pickup_mode(const object *op, int i) { 01945 switch (op->contr->mode = i) { 01946 case 0: 01947 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01948 "Mode: Don't pick up.", NULL); 01949 break; 01950 01951 case 1: 01952 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01953 "Mode: Pick up one item.", NULL); 01954 break; 01955 01956 case 2: 01957 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01958 "Mode: Pick up one item and stop.", NULL); 01959 break; 01960 01961 case 3: 01962 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01963 "Mode: Stop before picking up.", NULL); 01964 break; 01965 01966 case 4: 01967 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01968 "Mode: Pick up all items.", NULL); 01969 break; 01970 01971 case 5: 01972 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01973 "Mode: Pick up all items and stop.", NULL); 01974 break; 01975 01976 case 6: 01977 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01978 "Mode: Pick up all magic items.", NULL); 01979 break; 01980 01981 case 7: 01982 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 01983 "Mode: Pick up all coins and gems", NULL); 01984 break; 01985 } 01986 } 01987 01998 int command_search_items(object *op, char *params) { 01999 if (settings.search_items == FALSE) 02000 return 1; 02001 02002 if (params == NULL) { 02003 if (op->contr->search_str[0] == '\0') { 02004 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 02005 "Example: search magic+1 " 02006 "Would automatically pick up all " 02007 "items containing the word 'magic+1'.", 02008 NULL); 02009 return 1; 02010 } 02011 op->contr->search_str[0] = '\0'; 02012 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 02013 "Search mode turned off.", NULL); 02014 fix_object(op); 02015 return 1; 02016 } 02017 if ((int)strlen(params) >= MAX_BUF) { 02018 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02019 "Search string too long.", NULL); 02020 return 1; 02021 } 02022 strcpy(op->contr->search_str, params); 02023 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 02024 "Searching for '%s'.", 02025 "Searching for '%s'.", 02026 op->contr->search_str); 02027 fix_object(op); 02028 return 1; 02029 } 02030 02047 int command_rename_item(object *op, char *params) { 02048 char buf[VERY_BIG_BUF], name[MAX_BUF]; 02049 int itemnumber; 02050 object *item = NULL; 02051 object *tmp; 02052 char *closebrace; 02053 size_t counter; 02054 tag_t tag; 02055 02056 if (params) { 02057 /* Let's skip white spaces */ 02058 while (' ' == *params) 02059 params++; 02060 02061 /* Checking the first part */ 02062 if ((itemnumber = atoi(params)) != 0) { 02063 for (item = op->inv; item && ((item->count != itemnumber) || item->invisible); item = item->below) 02064 ; 02065 if (!item) { 02066 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02067 "Tried to rename an invalid item.", NULL); 02068 return 1; 02069 } 02070 while (isdigit(*params) || ' ' == *params) 02071 params++; 02072 } else if ('<' == *params) { 02073 /* Got old name, let's get it & find appropriate matching item */ 02074 closebrace = strchr(params, '>'); 02075 if (!closebrace) { 02076 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02077 "Syntax error!", NULL); 02078 return 1; 02079 } 02080 /* Sanity check for buffer overruns */ 02081 if ((closebrace-params) > 127) { 02082 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02083 "Old name too long (up to 127 characters allowed)!", NULL); 02084 return 1; 02085 } 02086 /* Copy the old name */ 02087 snprintf(buf, sizeof(buf), "%.*s", (int)(closebrace-(params+1)), params+1); 02088 02089 /* Find best matching item */ 02090 item = find_best_object_match(op, buf); 02091 if (!item) { 02092 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02093 "Could not find a matching item to rename.", NULL); 02094 return 1; 02095 } 02096 02097 /* Now need to move pointer to just after > */ 02098 params = closebrace+1; 02099 while (' ' == *params) 02100 params++; 02101 } else { 02102 /* Use marked item */ 02103 item = find_marked_object(op); 02104 if (!item) { 02105 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02106 "No marked item to rename.", NULL); 02107 return 1; 02108 } 02109 } 02110 02111 /* Now let's find the new name */ 02112 if (!strncmp(params, "to ", 3)) { 02113 params += 3; 02114 while (' ' == *params) 02115 params++; 02116 if ('<' != *params) { 02117 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02118 "Syntax error, expecting < at start of new name!", NULL); 02119 return 1; 02120 } 02121 closebrace = strchr(params+1, '>'); 02122 if (!closebrace) { 02123 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02124 "Syntax error, expecting > at end of new name!", NULL); 02125 return 1; 02126 } 02127 02128 /* Sanity check for buffer overruns */ 02129 if ((closebrace-params) > 127) { 02130 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02131 "New name too long (up to 127 characters allowed)!", NULL); 02132 return 1; 02133 } 02134 02135 /* Copy the new name */ 02136 snprintf(buf, sizeof(buf), "%.*s", (int)(closebrace-(params+1)), params+1); 02137 02138 /* Let's check it for weird characters */ 02139 for (counter = 0; counter < strlen(buf); counter++) { 02140 if (isalnum(buf[counter])) 02141 continue; 02142 if (' ' == buf[counter]) 02143 continue; 02144 if ('\'' == buf[counter]) 02145 continue; 02146 if ('+' == buf[counter]) 02147 continue; 02148 if ('_' == buf[counter]) 02149 continue; 02150 if ('-' == buf[counter]) 02151 continue; 02152 02153 /* If we come here, then the name contains an invalid character... 02154 * tell the player & exit 02155 */ 02156 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02157 "Invalid new name!", NULL); 02158 return 1; 02159 } 02160 } else { 02161 /* If param contains something, then syntax error... */ 02162 if (strlen(params)) { 02163 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02164 "Syntax error, expected 'to <' after old name!", NULL); 02165 return 1; 02166 } 02167 /* New name is empty */ 02168 buf[0] = '\0'; 02169 } 02170 } else { 02171 /* Last case: params==NULL */ 02172 item = find_marked_object(op); 02173 if (!item) { 02174 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02175 "No marked item to rename.", NULL); 02176 return 1; 02177 } 02178 buf[0] = '\0'; 02179 } 02180 02181 /* Coming here, everything is fine... */ 02182 if (!strlen(buf)) { 02183 /* Clear custom name */ 02184 if (item->custom_name == NULL) { 02185 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 02186 "This item has no custom name.", NULL); 02187 return 1; 02188 } 02189 02190 FREE_AND_CLEAR_STR(item->custom_name); 02191 query_base_name(item, item->nrof > 1 ? 1 : 0, name, MAX_BUF); 02192 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 02193 "You stop calling your %s with weird names.", 02194 "You stop calling your %s with weird names.", 02195 name); 02196 } else { 02197 if (item->custom_name != NULL && strcmp(item->custom_name, buf) == 0) { 02198 query_base_name(item, item->nrof > 1 ? 1 : 0, name, MAX_BUF); 02199 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 02200 "You keep calling your %s %s.", 02201 "You keep calling your %s %s.", 02202 name, buf); 02203 return 1; 02204 } 02205 02206 /* Set custom name */ 02207 FREE_AND_COPY(item->custom_name, buf); 02208 02209 query_base_name(item, item->nrof > 1 ? 1 : 0, name, MAX_BUF); 02210 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS, 02211 "Your %s will now be called %s.", 02212 "Your %s will now be called %s.", 02213 name, buf); 02214 } 02215 02216 tag = item->count; 02217 tmp = merge_ob(item, NULL); 02218 if (tmp == NULL) { 02219 /* object was not merged - if it was, merge_ob handles updating for us. */ 02220 esrv_update_item(UPD_NAME, op, item); 02221 } 02222 02223 return 1; 02224 } 02225 02234 int command_lock_item(object *op, char *params) { 02235 object *item; 02236 object *tmp; 02237 tag_t tag; 02238 char name[HUGE_BUF]; 02239 02240 if (!params || strlen(params) == 0) { 02241 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 02242 "Lock what item?", "Lock what item?"); 02243 return 1; 02244 } 02245 02246 item = find_best_object_match(op, params); 02247 if (!item) { 02248 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 02249 "Can't find any matching item.", "Can't find any matching item."); 02250 return 1; 02251 } 02252 02253 query_short_name(item, name, HUGE_BUF); 02254 if (QUERY_FLAG(item, FLAG_INV_LOCKED)) { 02255 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 02256 "Unlocked %s.", "Unlocked %s.", name); 02257 CLEAR_FLAG(item, FLAG_INV_LOCKED); 02258 } else { 02259 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, 02260 "Locked %s.", "Locked %s.", name); 02261 SET_FLAG(item, FLAG_INV_LOCKED); 02262 } 02263 02264 tag = item->count; 02265 tmp = merge_ob(item, NULL); 02266 if (tmp == NULL) { 02267 /* object was not merged, if it was merge_ob handles updates for us */ 02268 esrv_update_item(UPD_FLAGS, op, item); 02269 } 02270 return 1; 02271 } 02272 02282 int command_use(object *op, char *params) { 02283 char *with, copy[MAX_BUF]; 02284 object *first, *second, *add; 02285 archetype *arch; 02286 int count; 02287 sstring data; 02288 02289 if (!op->type == PLAYER) 02290 return 1; 02291 02292 snprintf(copy, sizeof(copy), "%s", params); 02293 with = strstr(copy, " with "); 02294 if (!with) { 02295 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "Syntax is use <item> with <item>.", NULL); 02296 return 1; 02297 } 02298 02299 with[0] = '\0'; 02300 with = with+strlen(" with "); 02301 02302 first = find_best_object_match(op, copy); 02303 if (!first) { 02304 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "No match for %s.", NULL, copy); 02305 return 1; 02306 } 02307 second = find_best_object_match(op, with); 02308 if (!second) { 02309 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "No match for %s.", NULL, with); 02310 return 1; 02311 } 02312 02313 snprintf(copy, sizeof(copy), "on_use_with_%s", first->arch->name); 02314 data = get_ob_key_value(second, copy); 02315 if (!data) { 02316 snprintf(copy, sizeof(copy), "on_use_with_%d_%d", first->type, first->subtype); 02317 data = get_ob_key_value(second, copy); 02318 if (!data) { 02319 snprintf(copy, sizeof(copy), "on_use_with_%d", first->type); 02320 data = get_ob_key_value(second, copy); 02321 if (!data) { 02322 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE, "Nothing happens.", NULL); 02323 return 1; 02324 } 02325 } 02326 } 02327 02328 while (data != NULL) { 02329 if (strncmp(data, "add ", 4) == 0) { 02330 data += 4; 02331 if (isdigit(*data)) { 02332 count = atol(data); 02333 data = strchr(data, ' ')+1; 02334 } else 02335 count = 1; 02336 with = strchr(data, ' '); 02337 if (!with) { 02338 strncpy(copy, data, sizeof(copy)); 02339 data = NULL; 02340 } else { 02341 *with = '\0'; 02342 strncpy(copy, data, sizeof(copy)); 02343 data += strlen(copy)+1; 02344 } 02345 arch = find_archetype(copy); 02346 if (!arch) { 02347 LOG(llevError, "Use: invalid archetype %s in %s.\n", copy, second->name); 02348 return 1; 02349 } 02350 add = object_create_arch(arch); 02351 add->nrof = count; 02352 insert_ob_in_ob(add, op); 02353 } else if (strncmp(data, "remove $", 8) == 0) { 02354 data += 8; 02355 if (*data == '1') { 02356 if (first) 02357 first = decrease_ob(first); 02358 data += 2; 02359 } else if (*data == '2') { 02360 if (second) 02361 second = decrease_ob(second); 02362 data += 2; 02363 } else { 02364 LOG(llevError, "Use: invalid use string %s in %s\n", data, second->name); 02365 return 1; 02366 } 02367 } else { 02368 LOG(llevError, "Use: invalid use string %s in %s\n", data, second->name); 02369 return 1; 02370 } 02371 } 02372 02373 return 1; 02374 }