Crossfire Server, Branch 1.12  R12190
c_object.c
Go to the documentation of this file.
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 }