Atrinik Server 2.5
commands/object.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00039 object *find_best_object_match(object *pl, char *params)
00040 {
00041     object *tmp, *best = NULL;
00042     int match_val = 0, tmpmatch;
00043 
00044     for (tmp = pl->inv; tmp; tmp = tmp->below)
00045     {
00046         if (IS_SYS_INVISIBLE(tmp))
00047         {
00048             continue;
00049         }
00050 
00051         if ((tmpmatch = item_matched_string(pl, tmp, params)) > match_val)
00052         {
00053             match_val = tmpmatch;
00054             best = tmp;
00055         }
00056     }
00057 
00058     return best;
00059 }
00060 
00061 
00067 int command_uskill(object *pl, char *params)
00068 {
00069     if (!params)
00070     {
00071         new_draw_info(0, COLOR_WHITE, pl, "Usage: /use_skill <skill name>");
00072         return 0;
00073     }
00074 
00075     if (pl->type == PLAYER)
00076     {
00077         CONTR(pl)->praying = 0;
00078     }
00079 
00080     return use_skill(pl, params);
00081 }
00082 
00088 int command_rskill(object *pl, char *params)
00089 {
00090     int skillno;
00091 
00092     if (!params)
00093     {
00094         new_draw_info(0, COLOR_WHITE, pl, "Usage: /ready_skill <skill name>");
00095         return 0;
00096     }
00097 
00098     if (pl->type == PLAYER)
00099     {
00100         CONTR(pl)->praying = 0;
00101     }
00102 
00103     skillno = lookup_skill_by_name(params);
00104 
00105     if (skillno == -1)
00106     {
00107         new_draw_info_format(0, COLOR_WHITE, pl, "Couldn't find the skill %s", params);
00108         return 0;
00109     }
00110 
00111     return change_skill(pl, skillno);
00112 }
00113 
00120 int command_apply(object *op, char *params)
00121 {
00122     if (op->type == PLAYER)
00123         CONTR(op)->praying = 0;
00124 
00125     if (!params)
00126     {
00127         player_apply_below(op);
00128         return 0;
00129     }
00130     else
00131     {
00132         enum apply_flag aflag = 0;
00133         object *inv;
00134 
00135         while (*params == ' ')
00136         {
00137             params++;
00138         }
00139 
00140         if (!strncmp(params, "-a ", 3))
00141         {
00142             aflag = AP_APPLY;
00143             params += 3;
00144         }
00145 
00146         if (!strncmp(params, "-u ", 3))
00147         {
00148             aflag = AP_UNAPPLY;
00149             params += 3;
00150         }
00151 
00152         while (*params == ' ')
00153         {
00154             params++;
00155         }
00156 
00157         inv = find_best_object_match(op, params);
00158 
00159         if (inv)
00160         {
00161             player_apply(op, inv, aflag, 0);
00162         }
00163         else
00164         {
00165             new_draw_info_format(0, COLOR_WHITE, op, "Could not find any match to the %s.", params);
00166         }
00167     }
00168 
00169     return 0;
00170 }
00171 
00180 int sack_can_hold(object *pl, object *sack, object *op, int nrof)
00181 {
00182     char buf[MAX_BUF];
00183 
00184     buf[0] = '\0';
00185 
00186     if (!QUERY_FLAG(sack, FLAG_APPLIED))
00187     {
00188         snprintf(buf, sizeof(buf), "The %s is not active.", query_name(sack, NULL));
00189     }
00190 
00191     if (sack == op)
00192     {
00193         snprintf(buf, sizeof(buf), "You can't put the %s into itself.", query_name(sack, NULL));
00194     }
00195 
00196     if ((sack->race && (sack->sub_type & 1) != ST1_CONTAINER_CORPSE) && (sack->race != op->race || op->type == CONTAINER || (sack->stats.food && sack->stats.food != op->type)))
00197     {
00198         snprintf(buf, sizeof(buf), "You can put only %s into the %s.", sack->race, query_name(sack, NULL));
00199     }
00200 
00201     if (sack->weight_limit && sack->carrying + (sint32) ((float) (((nrof ? nrof : 1) * op->weight) + op->carrying) * sack->weapon_speed) > (sint32) sack->weight_limit)
00202     {
00203         snprintf(buf, sizeof(buf), "That won't fit in the %s!", query_name(sack, NULL));
00204     }
00205 
00206     if (buf[0])
00207     {
00208         if (pl)
00209         {
00210             new_draw_info(0, COLOR_WHITE, pl, buf);
00211         }
00212 
00213         return 0;
00214     }
00215 
00216     return 1;
00217 }
00218 
00226 static void pick_up_object(object *pl, object *op, object *tmp, int nrof, int no_mevent)
00227 {
00228     char buf[HUGE_BUF];
00229     object *env = tmp->env;
00230     int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
00231 
00232     if (pl->type == PLAYER)
00233     {
00234         CONTR(pl)->praying = 0;
00235     }
00236 
00237     /* IF the player is flying & trying to take the item out of a container
00238      * that is in his inventory, let him.  tmp->env points to the container
00239      * (sack, luggage, etc), tmp->env->env then points to the player (nested
00240      * containers not allowed as of now) */
00241     if (QUERY_FLAG(pl, FLAG_FLYING) && !QUERY_FLAG(pl, FLAG_WIZ) && is_player_inv(tmp) != pl)
00242     {
00243         new_draw_info(0, COLOR_WHITE, pl, "You are levitating, you can't reach the ground!");
00244         return;
00245     }
00246 
00247     if (QUERY_FLAG(tmp, FLAG_WAS_WIZ) && !QUERY_FLAG(pl, FLAG_WAS_WIZ))
00248     {
00249         new_draw_info(0, COLOR_WHITE, pl, "The object disappears in a puff of smoke!\nIt must have been an illusion.");
00250 
00251         if (pl->type == PLAYER)
00252         {
00253             esrv_del_item(CONTR(pl), tmp->count, tmp->env);
00254         }
00255 
00256         if (!QUERY_FLAG(tmp, FLAG_REMOVED))
00257         {
00258             remove_ob(tmp);
00259             check_walk_off(tmp, NULL, MOVE_APPLY_VANISHED);
00260         }
00261 
00262         return;
00263     }
00264 
00265     if (nrof > tmp_nrof || nrof == 0)
00266     {
00267         nrof = tmp_nrof;
00268     }
00269 
00270     if (!player_can_carry(pl, WEIGHT_NROF(tmp, nrof)))
00271     {
00272         new_draw_info(0, COLOR_WHITE, pl, "That item is too heavy for you to pick up.");
00273         return;
00274     }
00275 
00276     if (tmp->type == CONTAINER)
00277     {
00278         container_unlink(NULL, tmp);
00279     }
00280 
00281     /* Trigger the PICKUP event */
00282     if (trigger_event(EVENT_PICKUP, pl, tmp, op, NULL, tmp_nrof, 0, 0, SCRIPT_FIX_ACTIVATOR))
00283     {
00284         return;
00285     }
00286 
00287     /* Trigger the map-wide pick up event. */
00288     if (!no_mevent && pl->map && pl->map->events && trigger_map_event(MEVENT_PICK, pl->map, pl, tmp, op, NULL, nrof))
00289     {
00290         return;
00291     }
00292 
00293 #ifndef REAL_WIZ
00294     if (QUERY_FLAG(pl, FLAG_WAS_WIZ))
00295     {
00296         SET_FLAG(tmp, FLAG_WAS_WIZ);
00297     }
00298 #endif
00299 
00300     if (pl->type == PLAYER)
00301     {
00302         CONTR(pl)->stat_items_picked++;
00303     }
00304 
00305     if (QUERY_FLAG(tmp, FLAG_UNPAID))
00306     {
00307         /* This is a clone shop - clone an item for inventory */
00308         if (QUERY_FLAG(tmp, FLAG_NO_PICK))
00309         {
00310             tmp = object_create_clone(tmp);
00311             CLEAR_FLAG(tmp, FLAG_NO_PICK);
00312             SET_FLAG(tmp, FLAG_STARTEQUIP);
00313             tmp->nrof = nrof;
00314             tmp_nrof = nrof;
00315             snprintf(buf, sizeof(buf), "You pick up %s for %s from the storage.", query_name(tmp, NULL), query_cost_string(tmp, pl, COST_BUY));
00316         }
00317         /* This is an unique shop item */
00318         else
00319         {
00320             tmp->nrof = nrof;
00321             snprintf(buf, sizeof(buf), "%s will cost you %s.", query_name(tmp, NULL), query_cost_string(tmp, pl, COST_BUY));
00322             tmp->nrof = tmp_nrof;
00323         }
00324     }
00325     else
00326     {
00327         tmp->nrof = nrof;
00328         snprintf(buf, sizeof(buf), "You pick up the %s.", query_name(tmp, NULL));
00329         tmp->nrof = tmp_nrof;
00330     }
00331 
00332     if (nrof != tmp_nrof)
00333     {
00334         object *tmp2 = tmp, *tmp2_cont = tmp->env;
00335         tag_t tmp2_tag = tmp2->count;
00336         char err[MAX_BUF];
00337         tmp = get_split_ob(tmp, nrof, err, sizeof(err));
00338 
00339         if (!tmp)
00340         {
00341             new_draw_info(0, COLOR_WHITE, pl, err);
00342             return;
00343         }
00344 
00345         /* Tell the client what happened to rest of the objects */
00346         if (pl->type == PLAYER)
00347         {
00348             if (was_destroyed(tmp2, tmp2_tag))
00349             {
00350                 esrv_del_item(CONTR(pl), tmp2_tag, tmp2_cont);
00351             }
00352             else
00353             {
00354                 esrv_send_item(pl, tmp2);
00355             }
00356         }
00357     }
00358     else
00359     {
00360         /* If the object is in a container, send a delete to the client.
00361          * - we are moving all the items from the container to elsewhere,
00362          * so it needs to be deleted. */
00363         if (!QUERY_FLAG(tmp, FLAG_REMOVED))
00364         {
00365             if (tmp->env && pl->type == PLAYER)
00366             {
00367                 esrv_del_item (CONTR(pl), tmp->count, tmp->env);
00368             }
00369 
00370             /* Unlink it - no move off check */
00371             remove_ob(tmp);
00372         }
00373     }
00374 
00375     new_draw_info(0, COLOR_WHITE, pl, buf);
00376     tmp = insert_ob_in_ob(tmp, op);
00377 
00378     /* All the stuff below deals with client/server code, and is only
00379      * usable by players */
00380     if (pl->type != PLAYER)
00381     {
00382         return;
00383     }
00384 
00385     esrv_send_item(pl, tmp);
00386     /* These are needed to update the weight for the container we
00387      * are putting the object in, and the players weight, if different. */
00388     esrv_update_item(UPD_WEIGHT, pl, op);
00389 
00390     if (op != pl)
00391     {
00392         esrv_send_item(pl, pl);
00393     }
00394 
00395     /* Update the container the object was in */
00396     if (env && env != pl && env != op)
00397     {
00398         esrv_update_item(UPD_WEIGHT, pl, env);
00399     }
00400 }
00401 
00408 void pick_up(object *op, object *alt, int no_mevent)
00409 {
00410     int need_fix_tmp = 0, count;
00411     object *tmp = NULL;
00412     mapstruct *tmp_map = NULL;
00413     tag_t tag;
00414 
00415     /* Decide which object to pick. */
00416     if (alt)
00417     {
00418         if (!can_pick(op, alt))
00419         {
00420             new_draw_info_format(0, COLOR_WHITE, op, "You can't pick up %s.", alt->name);
00421             goto leave;
00422         }
00423 
00424         tmp = alt;
00425     }
00426     else
00427     {
00428         if (op->below == NULL || !can_pick(op, op->below))
00429         {
00430             new_draw_info(0, COLOR_WHITE, op, "There is nothing to pick up here.");
00431             goto leave;
00432         }
00433 
00434         tmp = op->below;
00435     }
00436 
00437     if (tmp->type == CONTAINER)
00438     {
00439         container_unlink(NULL, tmp);
00440     }
00441 
00442     /* Try to catch it. */
00443     tmp_map = tmp->map;
00444     tmp = stop_item(tmp);
00445 
00446     if (tmp == NULL)
00447     {
00448         goto leave;
00449     }
00450 
00451     need_fix_tmp = 1;
00452 
00453     if (!can_pick(op, tmp))
00454     {
00455         goto leave;
00456     }
00457 
00458     if (op->type == PLAYER)
00459     {
00460         count = CONTR(op)->count;
00461 
00462         if (count == 0)
00463         {
00464             count = tmp->nrof;
00465         }
00466     }
00467     else
00468     {
00469         count = tmp->nrof;
00470     }
00471 
00472     /* Container is open, so use it */
00473     if (op->type == PLAYER && CONTR(op)->container)
00474     {
00475         alt = CONTR(op)->container;
00476 
00477         if (alt != tmp->env && !sack_can_hold(op, alt, tmp, count) && !check_magical_container(tmp, alt))
00478         {
00479             goto leave;
00480         }
00481     }
00482     /* Con container pickup */
00483     else
00484     {
00485         for (alt = op->inv; alt; alt = alt->below)
00486         {
00487             if (alt->type == CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) && alt->race && alt->race == tmp->race && sack_can_hold(NULL, alt, tmp, count) && !check_magical_container(tmp, alt))
00488             {
00489                 /* Perfect match */
00490                 break;
00491             }
00492         }
00493 
00494         if (!alt)
00495         {
00496             for (alt = op->inv; alt; alt = alt->below)
00497             {
00498                 if (alt->type == CONTAINER && QUERY_FLAG(alt, FLAG_APPLIED) && sack_can_hold(NULL, alt, tmp, count) && !check_magical_container(tmp, alt))
00499                 {
00500                     /* General container comes next */
00501                     break;
00502                 }
00503             }
00504         }
00505 
00506         /* No free containers */
00507         if (!alt)
00508         {
00509             alt = op;
00510         }
00511     }
00512 
00513     if (tmp->env == alt)
00514     {
00515         alt = op;
00516     }
00517 
00518     /* Startequip items are not allowed to be put into containers. */
00519     if (op->type == PLAYER && alt->type == CONTAINER && QUERY_FLAG(tmp, FLAG_STARTEQUIP))
00520     {
00521         new_draw_info(0, COLOR_WHITE, op, "This object cannot be put into containers!");
00522         goto leave;
00523     }
00524 
00525     tag = tmp->count;
00526     pick_up_object(op, alt, tmp, count, no_mevent);
00527 
00528     if (was_destroyed(tmp, tag) || tmp->env)
00529     {
00530         need_fix_tmp = 0;
00531     }
00532 
00533     if (op->type == PLAYER)
00534     {
00535         CONTR(op)->count = 0;
00536     }
00537 
00538     goto leave;
00539 
00540 leave:
00541     if (need_fix_tmp)
00542     {
00543         fix_stopped_item(tmp, tmp_map, op);
00544     }
00545 }
00546 
00554 void put_object_in_sack(object *op, object *sack, object *tmp, long nrof)
00555 {
00556     tag_t tmp_tag, tmp2_tag;
00557     object *tmp2, *tmp_cont;
00558     char buf[MAX_BUF];
00559     int tmp_nrof = tmp->nrof ? tmp->nrof : 1;
00560 
00561     if (op->type != PLAYER)
00562     {
00563         LOG(llevDebug, "put_object_in_sack: op not a player.\n");
00564         return;
00565     }
00566 
00567     /* Can't put an object in itself */
00568     if (sack == tmp)
00569     {
00570         return;
00571     }
00572 
00573     if (sack->type != CONTAINER)
00574     {
00575         new_draw_info_format(0, COLOR_WHITE, op, "The %s is not a container.", query_name(sack, NULL));
00576         return;
00577     }
00578 
00579     if (check_magical_container(tmp, sack))
00580     {
00581         new_draw_info(0, COLOR_WHITE, op, "You can't put a magical container into another magical container.");
00582         return;
00583     }
00584 
00585     /* Trigger the map-wide put event. */
00586     if (op->map && op->map->events && trigger_map_event(MEVENT_PUT, op->map, op, tmp, sack, NULL, nrof))
00587     {
00588         return;
00589     }
00590 
00591     if (tmp->type == CONTAINER)
00592     {
00593         container_unlink(NULL, tmp);
00594     }
00595 
00596     if (nrof > tmp_nrof || nrof == 0)
00597     {
00598         nrof = tmp_nrof;
00599     }
00600 
00601     if (!sack_can_hold(op, sack, tmp, nrof))
00602     {
00603         return;
00604     }
00605 
00606     if (QUERY_FLAG(tmp, FLAG_APPLIED))
00607     {
00608         if (apply_special(op, tmp, AP_UNAPPLY | AP_NO_MERGE))
00609         {
00610             return;
00611         }
00612     }
00613 
00614     if (QUERY_FLAG(tmp, FLAG_UNPAID))
00615     {
00616         /* This is a clone shop - clone an item for inventory */
00617         if (QUERY_FLAG(tmp, FLAG_NO_PICK))
00618         {
00619             tmp = object_create_clone(tmp);
00620             CLEAR_FLAG(tmp, FLAG_NO_PICK);
00621             SET_FLAG(tmp, FLAG_STARTEQUIP);
00622             tmp->nrof = nrof;
00623             tmp_nrof = nrof;
00624             new_draw_info_format(0, COLOR_WHITE, op, "You pick up %s for %s from the storage.", query_name(tmp, NULL), query_cost_string(tmp, op, COST_BUY));
00625         }
00626         /* This is an unique shop item */
00627         else
00628         {
00629             tmp->nrof = nrof;
00630             new_draw_info_format(0, COLOR_WHITE, op, "%s will cost you %s.", query_name(tmp, NULL), query_cost_string(tmp, op, COST_BUY));
00631             tmp->nrof = tmp_nrof;
00632         }
00633     }
00634 
00635     /* We want to put some portion of the item into the container */
00636     if (nrof != tmp_nrof)
00637     {
00638         object *tmp2_cont = tmp->env;
00639         char err[MAX_BUF];
00640 
00641         tmp2 = tmp;
00642         tmp2_tag = tmp2->count;
00643         tmp = get_split_ob(tmp, nrof, err, sizeof(err));
00644 
00645         if (!tmp)
00646         {
00647             new_draw_info(0, COLOR_WHITE, op, err);
00648             return;
00649         }
00650 
00651         /* Tell the client what happened to the other objects */
00652         if (was_destroyed(tmp2, tmp2_tag))
00653         {
00654             esrv_del_item(CONTR(op), tmp2_tag, tmp2_cont);
00655         }
00656         /* This can probably be replaced with an update */
00657         else
00658         {
00659             esrv_send_item(op, tmp2);
00660         }
00661     }
00662     else
00663     {
00664         /* If the object is in a container, send a delete to the client.
00665          * - we are moving all the items from the container to elsewhere,
00666          * so it needs to be deleted. */
00667         if (!QUERY_FLAG(tmp, FLAG_REMOVED))
00668         {
00669             esrv_del_item(CONTR(op), tmp->count, tmp->env);
00670             /* Unlink it - no move off check */
00671             remove_ob(tmp);
00672         }
00673     }
00674 
00675     snprintf(buf, sizeof(buf), "You put the %s in %s.", query_name(tmp, NULL), query_name(sack, NULL));
00676     tmp_tag = tmp->count;
00677     tmp_cont = tmp->env;
00678     tmp2 = insert_ob_in_ob(tmp, sack);
00679     new_draw_info(0, COLOR_WHITE, op, buf);
00680     /* This is overkill, fix_player() is called somewhere in object.c */
00681     fix_player(op);
00682 
00683     /* If an object merged (and thus, different object), we need to
00684      * delete the original. */
00685     if (tmp2 != tmp)
00686     {
00687         esrv_del_item(CONTR(op), tmp_tag, tmp_cont);
00688     }
00689 
00690     esrv_send_item(op, tmp2);
00691     /* update the sack's and player's weight */
00692     esrv_update_item(UPD_WEIGHT, op, sack);
00693     esrv_update_item(UPD_WEIGHT, op, op);
00694 }
00695 
00702 void drop_object(object *op, object *tmp, long nrof, int no_mevent)
00703 {
00704     object *floor;
00705 
00706     if (QUERY_FLAG(tmp, FLAG_NO_DROP) && !QUERY_FLAG(op, FLAG_WIZ))
00707     {
00708         new_draw_info(0, COLOR_WHITE, op, "You can't drop that item.");
00709         return;
00710     }
00711 
00712     /* Trigger the map-wide drop event. */
00713     if (!no_mevent && op->map && op->map->events && trigger_map_event(MEVENT_DROP, op->map, op, tmp, NULL, NULL, nrof))
00714     {
00715         return;
00716     }
00717 
00718     /* Stop praying. */
00719     if (op->type == PLAYER)
00720     {
00721         CONTR(op)->praying = 0;
00722     }
00723 
00724     if (QUERY_FLAG(tmp, FLAG_APPLIED))
00725     {
00726         /* Can't unapply it */
00727         if (apply_special(op, tmp, AP_UNAPPLY | AP_NO_MERGE))
00728         {
00729             return;
00730         }
00731     }
00732 
00733     if (tmp->type == CONTAINER)
00734     {
00735         container_unlink(NULL, tmp);
00736     }
00737 
00738     /* Trigger the DROP event */
00739     if (trigger_event(EVENT_DROP, op, tmp, NULL, NULL, nrof, 0, 0, SCRIPT_FIX_ACTIVATOR))
00740     {
00741         return;
00742     }
00743 
00744     /* We are only dropping some of the items. We split the current
00745      * object off. */
00746     if (nrof && tmp->nrof != (uint32) nrof)
00747     {
00748         object *tmp2 = tmp, *tmp2_cont = tmp->env;
00749         tag_t tmp2_tag = tmp2->count;
00750         char err[MAX_BUF];
00751         tmp = get_split_ob(tmp, nrof, err, sizeof(err));
00752 
00753         if (!tmp)
00754         {
00755             new_draw_info(0, COLOR_WHITE, op, err);
00756             return;
00757         }
00758 
00759         /* Tell the client what happened to the rest of the objects. tmp2
00760          * is now the original object */
00761         if (op->type == PLAYER)
00762         {
00763             if (was_destroyed(tmp2, tmp2_tag))
00764             {
00765                 esrv_del_item(CONTR(op), tmp2_tag, tmp2_cont);
00766             }
00767             else
00768             {
00769                 esrv_send_item(op, tmp2);
00770             }
00771         }
00772     }
00773     else
00774     {
00775         remove_ob(tmp);
00776 
00777         if (check_walk_off(tmp, NULL, MOVE_APPLY_DEFAULT) != CHECK_WALK_OK)
00778         {
00779             return;
00780         }
00781     }
00782 
00783     if (op->type == PLAYER)
00784     {
00785         CONTR(op)->stat_items_dropped++;
00786     }
00787 
00788     if (QUERY_FLAG(tmp, FLAG_STARTEQUIP) || QUERY_FLAG(tmp, FLAG_UNPAID))
00789     {
00790         if (op->type == PLAYER)
00791         {
00792             new_draw_info_format(0, COLOR_WHITE, op, "You drop the %s.", query_name(tmp, NULL));
00793             esrv_del_item(CONTR(op), tmp->count, tmp->env);
00794 
00795             if (QUERY_FLAG(tmp, FLAG_UNPAID))
00796             {
00797                 new_draw_info(0, COLOR_WHITE, op, "The shop magic put it back to the storage.");
00798 
00799                 floor = GET_MAP_OB_LAYER(op->map, op->x, op->y, 0);
00800 
00801                 /* If the player is standing on a unique shop floor or unique randomitems shop floor, drop the object back to the floor */
00802                 if (floor && floor->type == SHOP_FLOOR && (QUERY_FLAG(floor, FLAG_IS_MAGICAL) || (floor->randomitems && QUERY_FLAG(floor, FLAG_CURSED))))
00803                 {
00804                     tmp->x = op->x;
00805                     tmp->y = op->y;
00806                     insert_ob_in_map(tmp, op->map, op, 0);
00807                 }
00808             }
00809             else
00810             {
00811                 new_draw_info(0, COLOR_WHITE, op, "The god-given item vanishes to nowhere as you drop it!");
00812             }
00813         }
00814 
00815         fix_player(op);
00816         return;
00817     }
00818 
00819     /* If SAVE_INTERVAL is commented out, we never want to save
00820      * the player here. */
00821 #ifdef SAVE_INTERVAL
00822     if (op->type == PLAYER && !QUERY_FLAG(tmp, FLAG_UNPAID) && (tmp->nrof ? tmp->value * tmp->nrof : tmp->value > 2000) && (CONTR(op)->last_save_time + SAVE_INTERVAL) <= time(NULL))
00823     {
00824         save_player(op, 1);
00825         CONTR(op)->last_save_time = time(NULL);
00826     }
00827 #endif
00828 
00829     floor = GET_MAP_OB_LAYER(op->map, op->x, op->y, 0);
00830 
00831     if (floor && floor->type == SHOP_FLOOR && !QUERY_FLAG(tmp, FLAG_UNPAID) && tmp->type != MONEY)
00832     {
00833         sell_item(tmp, op, -1);
00834 
00835         /* Ok, we have really sold it - not only dropped. Run this only
00836          * if the floor is not magical (i.e., unique shop) */
00837         if (QUERY_FLAG(tmp, FLAG_UNPAID) && !QUERY_FLAG(floor, FLAG_IS_MAGICAL))
00838         {
00839             if (op->type == PLAYER)
00840             {
00841                 new_draw_info(0, COLOR_WHITE, op, "The shop magic put it to the storage.");
00842                 esrv_del_item(CONTR(op), tmp->count, tmp->env);
00843             }
00844 
00845             fix_player(op);
00846 
00847             if (op->type == PLAYER)
00848             {
00849                 esrv_send_item(op, op);
00850             }
00851 
00852             return;
00853         }
00854     }
00855 
00856     tmp->x = op->x;
00857     tmp->y = op->y;
00858 
00859     if (op->type == PLAYER)
00860     {
00861         esrv_del_item(CONTR(op), tmp->count, tmp->env);
00862     }
00863 
00864     insert_ob_in_map(tmp, op->map, op, 0);
00865 
00866     SET_FLAG(op, FLAG_NO_APPLY);
00867     remove_ob(op);
00868     insert_ob_in_map(op, op->map, op, INS_NO_MERGE | INS_NO_WALK_ON);
00869     CLEAR_FLAG(op, FLAG_NO_APPLY);
00870 
00871     /* Need to update the weight for the player */
00872     if (op->type == PLAYER)
00873     {
00874         fix_player(op);
00875         esrv_send_item(op, op);
00876     }
00877 }
00878 
00884 void drop(object *op, object *tmp, int no_mevent)
00885 {
00886     if (tmp == NULL)
00887     {
00888         new_draw_info(0, COLOR_WHITE, op, "You don't have anything to drop.");
00889         return;
00890     }
00891 
00892     if (QUERY_FLAG(tmp, FLAG_INV_LOCKED))
00893     {
00894         new_draw_info(0, COLOR_WHITE, op, "This item is locked.");
00895         return;
00896     }
00897 
00898     if (QUERY_FLAG(tmp, FLAG_NO_DROP))
00899     {
00900         new_draw_info(0, COLOR_WHITE, op, "You can't drop that item.");
00901         return;
00902     }
00903 
00904     if (op->type == PLAYER)
00905     {
00906         if (CONTR(op)->container)
00907         {
00908             put_object_in_sack(op, CONTR(op)->container, tmp, CONTR(op)->count);
00909         }
00910         else
00911         {
00912             drop_object(op, tmp, CONTR(op)->count, no_mevent);
00913         }
00914 
00915         CONTR(op)->count = 0;
00916     }
00917     else
00918     {
00919         drop_object(op, tmp, 0, no_mevent);
00920     }
00921 }
00922 
00928 int command_take(object *op, char *params)
00929 {
00930     object *tmp, *next;
00931     int did_one = 0, missed = 0, ground_total = 0, ival;
00932 
00933     if (!params)
00934     {
00935         new_draw_info(0, COLOR_WHITE, op, "Take what?");
00936         return 0;
00937     }
00938 
00939     if (CONTR(op)->container)
00940     {
00941         tmp = CONTR(op)->container->inv;
00942     }
00943     else
00944     {
00945         tmp = GET_MAP_OB_LAST(op->map, op->x, op->y);
00946     }
00947 
00948     if (!tmp)
00949     {
00950         new_draw_info(0, COLOR_WHITE, op, "Nothing to take.");
00951         return 0;
00952     }
00953 
00954     if (op->map && op->map->events && trigger_map_event(MEVENT_CMD_TAKE, op->map, op, tmp, NULL, params, 0))
00955     {
00956         return 0;
00957     }
00958 
00959     SET_FLAG(op, FLAG_NO_FIX_PLAYER);
00960 
00961     for ( ; tmp; tmp = next)
00962     {
00963         next = tmp->below;
00964 
00965         if ((tmp->layer != LAYER_ITEM && tmp->layer != LAYER_ITEM2) || QUERY_FLAG(tmp, FLAG_NO_PICK) || IS_SYS_INVISIBLE(tmp))
00966         {
00967             continue;
00968         }
00969 
00970         ival = item_matched_string(op, tmp, params);
00971 
00972         if (ival > 0)
00973         {
00974             if (ival <= 2 && !can_pick(op, tmp))
00975             {
00976                 missed++;
00977             }
00978             else
00979             {
00980                 pick_up(op, tmp, 1);
00981                 did_one = 1;
00982             }
00983         }
00984 
00985         /* Keep track how many visible objects are left on the ground. */
00986         if (!CONTR(op)->container && !tmp->env)
00987         {
00988             if (!(tmp->layer <= LAYER_FMASK || IS_SYS_INVISIBLE(tmp) || (!QUERY_FLAG(op, FLAG_SEE_INVISIBLE) && QUERY_FLAG(tmp, FLAG_IS_INVISIBLE))) || QUERY_FLAG(op, FLAG_WIZ))
00989             {
00990                 ground_total++;
00991             }
00992         }
00993     }
00994 
00995     CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER);
00996 
00997     if (did_one)
00998     {
00999         int layer;
01000 
01001         fix_player(op);
01002 
01003         /* Update below inventory positions for all players on this tile. */
01004         for (layer = LAYER_LIVING; ; layer = LAYER_LIVING + NUM_LAYERS)
01005         {
01006             for (tmp = GET_MAP_OB_LAYER(op->map, op->x, op->y, layer - 1); tmp && tmp->layer == LAYER_LIVING; tmp = tmp->above)
01007             {
01008                 /* Ensures the below inventory position is not higher than
01009                  * the actual number of visible items on the tile. */
01010                 if (tmp->type == PLAYER && CONTR(tmp) && CONTR(tmp)->socket.look_position > ground_total)
01011                 {
01012                     /* Update the visible row of objects. */
01013                     CONTR(tmp)->socket.look_position = ((int) (((float) ground_total / NUM_LOOK_OBJECTS) - 0.5f)) * NUM_LOOK_OBJECTS;
01014                 }
01015             }
01016 
01017             if (layer != LAYER_LIVING)
01018             {
01019                 break;
01020             }
01021         }
01022     }
01023     else if (!missed)
01024     {
01025         new_draw_info(0, COLOR_WHITE, op, "Nothing to take.");
01026     }
01027 
01028     if (missed == 1)
01029     {
01030         new_draw_info(0, COLOR_WHITE, op, "You were unable to take one of the items.");
01031     }
01032     else if (missed > 1)
01033     {
01034         new_draw_info_format(0, COLOR_WHITE, op, "You were unable to take %d of the items.", missed);
01035     }
01036 
01037     if (op->type == PLAYER)
01038     {
01039         CONTR(op)->count = 0;
01040     }
01041 
01042     return 0;
01043 }
01044 
01050 int command_drop(object *op, char *params)
01051 {
01052     object *tmp, *next;
01053     int did_one = 0, missed = 0, ival;
01054 
01055     if (!params)
01056     {
01057         new_draw_info(0, COLOR_WHITE, op, "Drop what?");
01058         return 0;
01059     }
01060 
01061     if (op->map && op->map->events && trigger_map_event(MEVENT_CMD_DROP, op->map, op, NULL, NULL, params, 0))
01062     {
01063         return 0;
01064     }
01065 
01066     SET_FLAG(op, FLAG_NO_FIX_PLAYER);
01067 
01068     for (tmp = op->inv; tmp; tmp = next)
01069     {
01070         next = tmp->below;
01071 
01072         if (QUERY_FLAG(tmp, FLAG_NO_DROP) || QUERY_FLAG(tmp, FLAG_STARTEQUIP) || IS_SYS_INVISIBLE(tmp))
01073         {
01074             continue;
01075         }
01076 
01077         ival = item_matched_string(op, tmp, params);
01078 
01079         if (ival > 0)
01080         {
01081             if (ival <= 2 && QUERY_FLAG(tmp, FLAG_INV_LOCKED))
01082             {
01083                 missed++;
01084             }
01085             else
01086             {
01087                 drop(op, tmp, 1);
01088                 did_one = 1;
01089             }
01090         }
01091     }
01092 
01093     CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER);
01094 
01095     if (did_one)
01096     {
01097         fix_player(op);
01098     }
01099     else if (!missed)
01100     {
01101         new_draw_info(0, COLOR_WHITE, op, "Nothing to drop.");
01102     }
01103 
01104     if (missed == 1)
01105     {
01106         new_draw_info(0, COLOR_WHITE, op, "One item couldn't be dropped because it was locked.");
01107     }
01108     else if (missed > 1)
01109     {
01110         new_draw_info_format(0, COLOR_WHITE, op, "%d items couldn't be dropped because they were locked.", missed);
01111     }
01112 
01113     if (op->type == PLAYER)
01114     {
01115         CONTR(op)->count = 0;
01116     }
01117 
01118     return 0;
01119 }
01120 
01128 static object *find_marked_object_rec(object *op, object **marked, uint32 *marked_count)
01129 {
01130     object *tmp, *tmp2;
01131     int wiz = QUERY_FLAG(op, FLAG_WIZ);
01132 
01133     /* This may seem like overkill, but we need to make sure that they
01134      * player hasn't dropped the item. We use count on the off chance
01135      * that an item got reincarnated at some point. */
01136     for (tmp = op->inv; tmp; tmp = tmp->below)
01137     {
01138         if (IS_SYS_INVISIBLE(tmp) && !wiz)
01139         {
01140             continue;
01141         }
01142 
01143         if (tmp == *marked)
01144         {
01145             if (tmp->count == *marked_count)
01146             {
01147                 return tmp;
01148             }
01149             else
01150             {
01151                 *marked = NULL;
01152                 *marked_count = 0;
01153                 return NULL;
01154             }
01155         }
01156         else if (tmp->inv)
01157         {
01158             tmp2 = find_marked_object_rec(tmp, marked, marked_count);
01159 
01160             if (tmp2)
01161             {
01162                 return tmp2;
01163             }
01164 
01165             if (*marked == NULL)
01166             {
01167                 return NULL;
01168             }
01169         }
01170     }
01171 
01172     return NULL;
01173 }
01174 
01179 object *find_marked_object(object *op)
01180 {
01181     if (op->type != PLAYER || !op || !CONTR(op) || !CONTR(op)->mark)
01182     {
01183         return NULL;
01184     }
01185 
01186     return find_marked_object_rec(op, &CONTR(op)->mark, &CONTR(op)->mark_count);
01187 }
01188 
01193 void examine_living(object *op, object *tmp)
01194 {
01195     object *mon = tmp->head ? tmp->head : tmp;
01196     int val, val2, i, gender;
01197 
01198     CONTR(op)->praying = 0;
01199     gender = object_get_gender(mon);
01200 
01201     if (QUERY_FLAG(mon, FLAG_IS_GOOD))
01202     {
01203         new_draw_info_format(0, COLOR_WHITE, op, "%s is a good aligned %s %s.", gender_subjective_upper[gender], gender_noun[gender], mon->race);
01204     }
01205     else if (QUERY_FLAG(mon, FLAG_IS_EVIL))
01206     {
01207         new_draw_info_format(0, COLOR_WHITE, op, "%s is an evil aligned %s %s.", gender_subjective_upper[gender], gender_noun[gender], mon->race);
01208     }
01209     else if (QUERY_FLAG(mon, FLAG_IS_NEUTRAL))
01210     {
01211         new_draw_info_format(0, COLOR_WHITE, op, "%s is a neutral aligned %s %s.", gender_subjective_upper[gender], gender_noun[gender], mon->race);
01212     }
01213     else
01214     {
01215         new_draw_info_format(0, COLOR_WHITE, op, "%s is a %s %s.", gender_subjective_upper[gender], gender_noun[gender], mon->race);
01216     }
01217 
01218     new_draw_info_format(0, COLOR_WHITE, op, "%s is level %d.", gender_subjective_upper[gender], mon->level);
01219     new_draw_info_format(0, COLOR_WHITE, op, "%s has a base damage of %d and hp of %d.", gender_subjective_upper[gender], mon->stats.dam, mon->stats.maxhp);
01220     new_draw_info_format(0, COLOR_WHITE, op, "%s has a wc of %d and ac of %d.", gender_subjective_upper[gender], mon->stats.wc, mon->stats.ac);
01221 
01222     for (val = val2 = -1, i = 0; i < NROFATTACKS; i++)
01223     {
01224         if (mon->protection[i] > 0)
01225         {
01226             val = i;
01227         }
01228         else if (mon->protection[i] < 0)
01229         {
01230             val = i;
01231         }
01232     }
01233 
01234     if (val != -1)
01235     {
01236         new_draw_info_format(0, COLOR_WHITE, op, "%s can naturally resist some attacks.", gender_subjective_upper[gender]);
01237     }
01238 
01239     if (val2 != -1)
01240     {
01241         new_draw_info_format(0, COLOR_WHITE, op, "%s is naturally vulnerable to some attacks.", gender_subjective_upper[gender]);
01242     }
01243 
01244     for (val =- 1, val2 = i = 0; i < NROFATTACKS; i++)
01245     {
01246         if (mon->protection[i] > val2)
01247         {
01248             val = i;
01249             val2 = mon->protection[i];
01250         }
01251     }
01252 
01253     if (val != -1)
01254     {
01255         new_draw_info_format(0, COLOR_WHITE, op, "Best armour protection seems to be for %s.", attack_name[val]);
01256     }
01257 
01258     if (QUERY_FLAG(mon, FLAG_UNDEAD))
01259     {
01260         new_draw_info_format(0, COLOR_WHITE, op, "%s is an undead force.", gender_subjective_upper[gender]);
01261     }
01262 
01263     switch ((mon->stats.hp + 1) * 4 / (mon->stats.maxhp + 1))
01264     {
01265         case 1:
01266             new_draw_info_format(0, COLOR_WHITE, op, "%s is in a bad shape.", gender_subjective_upper[gender]);
01267             break;
01268 
01269         case 2:
01270             new_draw_info_format(0, COLOR_WHITE, op, "%s is hurt.", gender_subjective_upper[gender]);
01271             break;
01272 
01273         case 3:
01274             new_draw_info_format(0, COLOR_WHITE, op, "%s is somewhat hurt.", gender_subjective_upper[gender]);
01275             break;
01276 
01277         default:
01278             new_draw_info_format(0, COLOR_WHITE, op, "%s is in excellent shape.", gender_subjective_upper[gender]);
01279             break;
01280     }
01281 
01282     if (present_in_ob(POISONING, mon) != NULL)
01283     {
01284         new_draw_info_format(0, COLOR_WHITE, op, "%s looks very ill.", gender_subjective_upper[gender]);
01285     }
01286 }
01287 
01293 char *long_desc(object *tmp, object *caller)
01294 {
01295     static char buf[VERY_BIG_BUF];
01296     char *cp;
01297 
01298     if (tmp == NULL)
01299     {
01300         return "";
01301     }
01302 
01303     buf[0] = '\0';
01304 
01305     switch (tmp->type)
01306     {
01307         case RING:
01308         case SKILL:
01309         case WEAPON:
01310         case ARMOUR:
01311         case BRACERS:
01312         case HELMET:
01313         case SHIELD:
01314         case BOOTS:
01315         case GLOVES:
01316         case AMULET:
01317         case GIRDLE:
01318         case POTION:
01319         case BOW:
01320         case ARROW:
01321         case CLOAK:
01322         case FOOD:
01323         case DRINK:
01324         case HORN:
01325         case WAND:
01326         case ROD:
01327         case FLESH:
01328         case BOOK:
01329         case CONTAINER:
01330             if (*(cp = describe_item(tmp)) != '\0')
01331             {
01332                 size_t len;
01333 
01334                 strncat(buf, query_name(tmp, caller), VERY_BIG_BUF - 1);
01335 
01336                 buf[VERY_BIG_BUF - 1] = '\0';
01337                 len = strlen(buf);
01338 
01339                 if (len < VERY_BIG_BUF - 5 && ((tmp->type != AMULET && tmp->type != RING) || tmp->title))
01340                 {
01341                     /* Since we know the length, we save a few CPU cycles by using
01342                      * it instead of calling strcat */
01343                     strcpy(buf + len, " ");
01344                     len++;
01345                     strncpy(buf + len, cp, VERY_BIG_BUF - len - 1);
01346                     buf[VERY_BIG_BUF - 1] = '\0';
01347                 }
01348             }
01349 
01350             break;
01351     }
01352 
01353     if (buf[0] == '\0')
01354     {
01355         strncat(buf, query_name(tmp, caller), VERY_BIG_BUF - 1);
01356         buf[VERY_BIG_BUF - 1] = '\0';
01357     }
01358 
01359     return buf;
01360 }
01361 
01366 void examine(object *op, object *tmp)
01367 {
01368     char buf[VERY_BIG_BUF], tmp_buf[64];
01369     int i;
01370 
01371     if (tmp == NULL || tmp->type == CLOSE_CON)
01372     {
01373         return;
01374     }
01375 
01376     strcpy(buf, "That is ");
01377     strncat(buf, long_desc(tmp, op), VERY_BIG_BUF - strlen(buf) - 1);
01378     buf[VERY_BIG_BUF - 1] = '\0';
01379 
01380     /* Only add this for usable items, not for objects like walls or
01381      * floors for example. */
01382     if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && need_identify(tmp))
01383     {
01384         strncat(buf, " (unidentified)", VERY_BIG_BUF - strlen(buf) - 1);
01385     }
01386 
01387     buf[VERY_BIG_BUF - 1] = '\0';
01388     new_draw_info(0, COLOR_WHITE, op, buf);
01389     buf[0] = '\0';
01390 
01391     if (tmp->custom_name)
01392     {
01393         new_draw_info_format(0, COLOR_WHITE, op, "You name it %s.", tmp->custom_name);
01394     }
01395 
01396     if (QUERY_FLAG(tmp, FLAG_MONSTER) || tmp->type == PLAYER)
01397     {
01398         new_draw_info_format(0, COLOR_WHITE, op, "%s.", describe_item(tmp->head ? tmp->head : tmp));
01399         examine_living(op, tmp);
01400     }
01401     /* We don't double use the item_xxx arch commands, so they are always valid */
01402     else if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01403     {
01404         if (QUERY_FLAG(tmp, FLAG_IS_GOOD))
01405         {
01406             new_draw_info_format(0, COLOR_WHITE, op, "It is good aligned.");
01407         }
01408         else if (QUERY_FLAG(tmp, FLAG_IS_EVIL))
01409         {
01410             new_draw_info_format(0, COLOR_WHITE, op, "It is evil aligned.");
01411         }
01412         else if (QUERY_FLAG(tmp, FLAG_IS_NEUTRAL))
01413         {
01414             new_draw_info_format(0, COLOR_WHITE, op, "It is neutral aligned.");
01415         }
01416 
01417         if (tmp->item_level)
01418         {
01419             if (tmp->item_skill)
01420             {
01421                 new_draw_info_format(0, COLOR_WHITE, op, "It needs a level of %d in %s to use.", tmp->item_level, find_skill_exp_skillname(tmp->item_skill));
01422             }
01423             else
01424             {
01425                 new_draw_info_format(0, COLOR_WHITE, op, "It needs a level of %d to use.", tmp->item_level);
01426             }
01427         }
01428 
01429         if (tmp->item_quality)
01430         {
01431             if (QUERY_FLAG(tmp, FLAG_INDESTRUCTIBLE))
01432             {
01433                 new_draw_info_format(0, COLOR_WHITE, op, "Qua: %d Con: Indestructible.", tmp->item_quality);
01434             }
01435             else
01436             {
01437                 new_draw_info_format(0, COLOR_WHITE, op, "Qua: %d Con: %d.", tmp->item_quality, tmp->item_condition);
01438             }
01439         }
01440 
01441         buf[0] = '\0';
01442     }
01443 
01444     switch (tmp->type)
01445     {
01446         case SPELLBOOK:
01447             if (QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->stats.sp >= 0 && tmp->stats.sp <= NROFREALSPELLS)
01448             {
01449                 if (tmp->sub_type == ST1_SPELLBOOK_CLERIC)
01450                 {
01451                     snprintf(buf, sizeof(buf), "%s is a %d level prayer.", spells[tmp->stats.sp].name, spells[tmp->stats.sp].level);
01452                 }
01453                 else
01454                 {
01455                     snprintf(buf, sizeof(buf), "%s is a %d level spell.", spells[tmp->stats.sp].name, spells[tmp->stats.sp].level);
01456                 }
01457             }
01458 
01459             break;
01460 
01461         case BOOK:
01462             if (tmp->msg)
01463             {
01464                 strcpy(buf, "Something is written in it.");
01465 
01466                 if (op->type == PLAYER && !QUERY_FLAG(tmp, FLAG_NO_SKILL_IDENT))
01467                 {
01468                     int level = CONTR(op)->skill_ptr[SK_LITERACY]->level;
01469 
01470                     new_draw_info(0, COLOR_WHITE, op, buf);
01471 
01472                     /* Gray. */
01473                     if (tmp->level < level_color[level].green)
01474                     {
01475                         strcpy(buf, "It seems to contain no knowledge you could learn from.");
01476                     }
01477                     /* Green. */
01478                     else if (tmp->level < level_color[level].blue)
01479                     {
01480                         strcpy(buf, "It seems to contain tiny bits of knowledge you could learn from.");
01481                     }
01482                     /* Blue. */
01483                     else if (tmp->level < level_color[level].yellow)
01484                     {
01485                         strcpy(buf, "It seems to contain a small amount of knowledge you could learn from.");
01486                     }
01487                     /* Yellow. */
01488                     else if (tmp->level < level_color[level].orange)
01489                     {
01490                         strcpy(buf, "It seems to contain an average amount of knowledge you could learn from.");
01491                     }
01492                     /* Orange. */
01493                     else if (tmp->level < level_color[level].red)
01494                     {
01495                         strcpy(buf, "It seems to contain a moderate amount of knowledge you could learn from.");
01496                     }
01497                     /* Red. */
01498                     else if (tmp->level < level_color[level].purple)
01499                     {
01500                         strcpy(buf, "It seems to contain a fair amount of knowledge you could learn from.");
01501                     }
01502                     /* Purple. */
01503                     else
01504                     {
01505                         strcpy(buf, "It seems to contain a great amount of knowledge you could learn from.");
01506                     }
01507                 }
01508             }
01509 
01510             break;
01511 
01512         case CONTAINER:
01513             if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01514             {
01515                 if (tmp->race != NULL)
01516                 {
01517                     if (tmp->weight_limit)
01518                     {
01519                         snprintf(buf, sizeof(buf), "It can hold only %s and its weight limit is %.1f kg.", tmp->race, (float) tmp->weight_limit / 1000.0f);
01520                     }
01521                     else
01522                     {
01523                         snprintf(buf, sizeof(buf), "It can hold only %s.", tmp->race);
01524                     }
01525 
01526                     /* Has magic modifier? */
01527                     if (tmp->weapon_speed != 1.0f)
01528                     {
01529                         new_draw_info(0, COLOR_WHITE, op, buf);
01530 
01531                         /* Bad */
01532                         if (tmp->weapon_speed > 1.0f)
01533                         {
01534                             snprintf(buf, sizeof(buf), "It increases the weight of items inside by %.1f%%.", tmp->weapon_speed * 100.0f);
01535                         }
01536                         /* Good */
01537                         else
01538                         {
01539                             snprintf(buf, sizeof(buf), "It decreases the weight of items inside by %.1f%%.", 100.0f - (tmp->weapon_speed * 100.0f));
01540                         }
01541                     }
01542                 }
01543                 else
01544                 {
01545                     if (tmp->weight_limit)
01546                     {
01547                         snprintf(buf, sizeof(buf), "Its weight limit is %.1f kg.", (float)tmp->weight_limit / 1000.0f);
01548                     }
01549 
01550                     /* Has magic modifier? */
01551                     if (tmp->weapon_speed != 1.0f)
01552                     {
01553                         new_draw_info(0, COLOR_WHITE, op, buf);
01554 
01555                         /* Bad */
01556                         if (tmp->weapon_speed > 1.0f)
01557                         {
01558                             snprintf(buf, sizeof(buf), "It increases the weight of items inside by %.1f%%.", tmp->weapon_speed * 100.0f);
01559                         }
01560                         /* Good */
01561                         else
01562                         {
01563                             snprintf(buf, sizeof(buf), "It decreases the weight of items inside by %.1f%%.", 100.0f - (tmp->weapon_speed * 100.0f));
01564                         }
01565                     }
01566                 }
01567 
01568                 new_draw_info(0, COLOR_WHITE, op, buf);
01569 
01570                 if (tmp->weapon_speed == 1.0f)
01571                 {
01572                     snprintf(buf, sizeof(buf), "It contains %3.3f kg.", (float) tmp->carrying / 1000.0f);
01573                 }
01574                 else if (tmp->weapon_speed > 1.0f)
01575                 {
01576                     snprintf(buf, sizeof(buf), "It contains %3.3f kg, increased to %3.3f kg.", (float) tmp->damage_round_tag / 1000.0f, (float) tmp->carrying / 1000.0f);
01577                 }
01578                 else
01579                 {
01580                     snprintf(buf, sizeof(buf), "It contains %3.3f kg, decreased to %3.3f kg.", (float) tmp->damage_round_tag / 1000.0f, (float) tmp->carrying / 1000.0f);
01581                 }
01582             }
01583 
01584             break;
01585 
01586         case WAND:
01587             if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01588             {
01589                 snprintf(buf, sizeof(buf), "It has %d charges left.", tmp->stats.food);
01590             }
01591 
01592             break;
01593 
01594         case POWER_CRYSTAL:
01595             /* Avoid division by zero... */
01596             if (tmp->stats.maxsp == 0)
01597             {
01598                 snprintf(buf, sizeof(buf), "It has capacity of %d.", tmp->stats.maxsp);
01599             }
01600             else
01601             {
01602                 /* Higher capacity crystals */
01603                 if (tmp->stats.maxsp > 1000)
01604                 {
01605                     i = (tmp->stats.maxsp % 1000) / 100;
01606 
01607                     if (i)
01608                     {
01609                         snprintf(tmp_buf, sizeof(tmp_buf), "It has capacity of %d.%dk and is ", tmp->stats.maxsp / 1000, i);
01610                     }
01611                     else
01612                     {
01613                         snprintf(tmp_buf, sizeof(tmp_buf), "It has capacity of %dk and is ", tmp->stats.maxsp / 1000);
01614                     }
01615                 }
01616                 else
01617                 {
01618                     snprintf(tmp_buf, sizeof(tmp_buf), "It has capacity of %d and is ", tmp->stats.maxsp);
01619                 }
01620 
01621                 strcat(buf, tmp_buf);
01622                 i = (tmp->stats.sp * 10) / tmp->stats.maxsp;
01623 
01624                 if (tmp->stats.sp == 0)
01625                 {
01626                     strcat(buf, "empty.");
01627                 }
01628                 else if (i == 0)
01629                 {
01630                     strcat(buf, "almost empty.");
01631                 }
01632                 else if (i < 3)
01633                 {
01634                     strcat(buf, "partially filled.");
01635                 }
01636                 else if (i < 6)
01637                 {
01638                     strcat(buf, "half full.");
01639                 }
01640                 else if (i < 9)
01641                 {
01642                     strcat(buf, "well charged.");
01643                 }
01644                 else if (tmp->stats.sp == tmp->stats.maxsp)
01645                 {
01646                     strcat(buf, "fully charged.");
01647                 }
01648                 else
01649                 {
01650                     strcat(buf, "almost full.");
01651                 }
01652             }
01653 
01654             break;
01655     }
01656 
01657     if (buf[0] != '\0')
01658     {
01659         new_draw_info(0, COLOR_WHITE, op, buf);
01660     }
01661 
01662     if (tmp->material && (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED)))
01663     {
01664         strcpy(buf, "It is made of: ");
01665 
01666         for (i = 0; i < NROFMATERIALS; i++)
01667         {
01668             if (tmp->material & (1 << i))
01669             {
01670                 strcat(buf, materials[i].name);
01671                 strcat(buf, " ");
01672             }
01673         }
01674 
01675         new_draw_info(0, COLOR_WHITE, op, buf);
01676     }
01677 
01678     if (tmp->weight)
01679     {
01680         float weight = (float) (tmp->nrof ? tmp->weight * (int) tmp->nrof : tmp->weight) / 1000.0f;
01681 
01682         if (tmp->type == MONSTER)
01683         {
01684             new_draw_info_format(0, COLOR_WHITE, op, "%s weighs %3.3f kg.", gender_subjective_upper[object_get_gender(tmp)], weight);
01685         }
01686         else if (tmp->type == PLAYER)
01687         {
01688             new_draw_info_format(0, COLOR_WHITE, op, "%s weighs %3.3f kg and is carrying %3.3f kg.", gender_subjective_upper[object_get_gender(tmp)], weight, (float) tmp->carrying / 1000.0f);
01689         }
01690         else
01691         {
01692             new_draw_info_format(0, COLOR_WHITE, op, tmp->nrof > 1 ? "They weigh %3.3f kg." : "It weighs %3.3f kg.", weight);
01693         }
01694     }
01695 
01696     if (QUERY_FLAG(tmp, FLAG_STARTEQUIP))
01697     {
01698         /* Unpaid clone shop item */
01699         if (QUERY_FLAG(tmp, FLAG_UNPAID))
01700         {
01701             new_draw_info_format(0, COLOR_WHITE, op, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", query_cost_string(tmp, op, COST_BUY));
01702         }
01703         /* God-given item */
01704         else
01705         {
01706             new_draw_info_format(0, COLOR_WHITE, op, "%s god-given item%s.", tmp->nrof > 1 ? "They are" : "It is a", tmp->nrof > 1 ? "s" : "");
01707 
01708             if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01709             {
01710                 if (tmp->value)
01711                 {
01712                     new_draw_info_format(0, COLOR_WHITE, op, "But %s worth %s.", tmp->nrof > 1 ? "they are" : "it is", query_cost_string(tmp, op, COST_TRUE));
01713                 }
01714                 else
01715                 {
01716                     new_draw_info_format(0, COLOR_WHITE, op, "%s worthless.", tmp->nrof > 1 ? "They are" : "It is");
01717                 }
01718             }
01719         }
01720     }
01721     else if (tmp->value && !IS_LIVE(tmp))
01722     {
01723         if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01724         {
01725             if (QUERY_FLAG(tmp, FLAG_UNPAID))
01726             {
01727                 new_draw_info_format(0, COLOR_WHITE, op, "%s would cost you %s.", tmp->nrof > 1 ? "They" : "It", query_cost_string(tmp, op, COST_BUY));
01728             }
01729             else
01730             {
01731                 new_draw_info_format(0, COLOR_WHITE, op, "%s worth %s.", tmp->nrof > 1 ? "They are" : "It is", query_cost_string(tmp, op, COST_TRUE));
01732                 goto dirty_little_jump1;
01733             }
01734         }
01735         else
01736         {
01737             object *floor;
01738 dirty_little_jump1:
01739 
01740             floor = GET_MAP_OB_LAYER(op->map, op->x, op->y, 0);
01741 
01742             if (floor && floor->type == SHOP_FLOOR && tmp->type != MONEY)
01743             {
01744                 /* Used for SK_BARGAINING modification */
01745                 int charisma = op->stats.Cha;
01746 
01747                 /* This skill gives us a charisma boost */
01748                 if (find_skill(op, SK_BARGAINING))
01749                 {
01750                     charisma += 4;
01751 
01752                     if (charisma > MAX_STAT)
01753                     {
01754                         charisma = MAX_STAT;
01755                     }
01756                 }
01757 
01758                 new_draw_info_format(0, COLOR_WHITE, op, "This shop will pay you %s (%0.1f%%).", query_cost_string(tmp, op, COST_SELL), 20.0f + 100.0f * cha_bonus[charisma]);
01759             }
01760         }
01761     }
01762     else if (!IS_LIVE(tmp))
01763     {
01764         if (QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01765         {
01766             if (QUERY_FLAG(tmp, FLAG_UNPAID))
01767             {
01768                 new_draw_info_format(0, COLOR_WHITE, op, "%s would cost nothing.", tmp->nrof > 1 ? "They" : "It");
01769             }
01770             else
01771             {
01772                 new_draw_info_format(0, COLOR_WHITE, op, "%s worthless.", tmp->nrof > 1 ? "They are" : "It is");
01773             }
01774         }
01775     }
01776 
01777     /* Does the object have a message?  Don't show message for all object
01778      * types - especially if the first entry is a match */
01779     if (tmp->msg && tmp->type != EXIT && tmp->type != BOOK && tmp->type != CORPSE && !QUERY_FLAG(tmp, FLAG_WALK_ON) && strncasecmp(tmp->msg, "@match", 7))
01780     {
01781         /* This is just a hack so when identifying the items, we print
01782          * out the extra message */
01783         if ((need_identify(tmp) || QUERY_FLAG(tmp, FLAG_QUEST_ITEM)) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))
01784         {
01785             new_draw_info(0, COLOR_WHITE, op, "The object has a story:");
01786             new_draw_info(0, COLOR_WHITE, op, tmp->msg);
01787         }
01788     }
01789 
01790     trigger_map_event(MEVENT_EXAMINE, op->map, op, tmp, NULL, NULL, 0);
01791 
01792     /* Blank line */
01793     new_draw_info(0, COLOR_WHITE, op, " ");
01794 
01795     if (QUERY_FLAG(op, FLAG_WIZ))
01796     {
01797         StringBuffer *sb = stringbuffer_new();
01798         char *diff;
01799 
01800         stringbuffer_append_printf(sb, "count %d\n", tmp->count);
01801         dump_object(tmp, sb);
01802         diff = stringbuffer_finish(sb);
01803         new_draw_info(0, COLOR_WHITE, op, diff);
01804         free(diff);
01805     }
01806 }
01807 
01813 int command_rename_item(object *op, char *params)
01814 {
01815     object *tmp = find_marked_object(op), *merged, *cont;
01816     tag_t del_tag;
01817 
01818     if (!tmp)
01819     {
01820         new_draw_info(0, COLOR_WHITE, op, "No marked item to rename.");
01821         return 1;
01822     }
01823 
01824     /* Will also clear unprintable chars. */
01825     params = cleanup_chat_string(params);
01826 
01827     /* Clear custom name. */
01828     if (!params)
01829     {
01830         if (!tmp->custom_name)
01831         {
01832             new_draw_info(0, COLOR_WHITE, op, "This item has no custom name.");
01833             return 1;
01834         }
01835 
01836         FREE_AND_CLEAR_HASH(tmp->custom_name);
01837         new_draw_info_format(0, COLOR_WHITE, op, "You stop calling your %s with weird names.", query_base_name(tmp, NULL));
01838     }
01839     else
01840     {
01841         if (tmp->type == MONEY)
01842         {
01843             new_draw_info(0, COLOR_WHITE, op, "You cannot rename that item.");
01844             return 1;
01845         }
01846 
01847         if (strlen(params) > 127)
01848         {
01849             new_draw_info(0, COLOR_WHITE, op, "New name is too long, maximum is 127 characters.");
01850             return 1;
01851         }
01852 
01853         if (tmp->custom_name && !strcmp(tmp->custom_name, params))
01854         {
01855             new_draw_info_format(0, COLOR_WHITE, op, "You keep calling your %s %s.", query_base_name(tmp, NULL), tmp->custom_name);
01856             return 1;
01857         }
01858 
01859         string_remove_markup(params);
01860         /* Set custom name. */
01861         FREE_AND_COPY_HASH(tmp->custom_name, params);
01862         new_draw_info_format(0, COLOR_WHITE, op, "Your %s will now be called %s.", query_base_name(tmp, NULL), tmp->custom_name);
01863         CONTR(op)->stat_renamed_items++;
01864     }
01865 
01866     del_tag = tmp->count;
01867     cont = tmp->env;
01868     merged = merge_ob(tmp, NULL);
01869 
01870     /* It was merged. */
01871     if (merged)
01872     {
01873         esrv_del_item(CONTR(op), del_tag, cont);
01874         tmp = merged;
01875     }
01876 
01877     esrv_send_item(op, tmp);
01878     return 1;
01879 }