Crossfire Server, Branch 1.12  R12190
item.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_item_c =
00003  *   "$Id: item.c 13899 2010-09-26 16:25:57Z ryo_saeba $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00037 #include <stdio.h>
00038 #include <global.h>
00039 #include <living.h>
00040 #include <spells.h>
00041 
00064 body_locations_struct body_locations[NUM_BODY_LOCATIONS] = {
00065     { "body_range",    "in your range slot",     "in a human's range slot" },
00066     { "body_arm",      "on your arm",            "on a human's arm" },
00067     { "body_torso",    "on your body",           "on a human's torso" },
00068     { "body_head",     "on your head",           "on a human's head" },
00069     { "body_neck",     "around your neck",       "around a humans neck" },
00070     { "body_skill",    "in your skill slot",     "in a human's skill slot" },
00071     { "body_finger",   "on your finger",         "on a human's finger" },
00072     { "body_shoulder", "around your shoulders",  "around a human's shoulders" },
00073     { "body_foot",     "on your feet",           "on a human's feet" },
00074     { "body_hand",     "on your hands",          "on a human's hands" },
00075     { "body_wrist",    "around your wrists",     "around a human's wrist" },
00076     { "body_waist",    "around your waist",      "around a human's waist" },
00077     { "body_leg",      "around your legs",       "around a human's legs" },
00078 
00079     /*{"body_dragon_torso", "your body", "a dragon's body"} */
00080 };
00081 
00083 static const char numbers_10[10][20] = {
00084     "zero", "ten", "twenty", "thirty", "fourty", "fifty", "sixty", "seventy",
00085     "eighty", "ninety"
00086 };
00087 
00089 static const char levelnumbers[21][20] = {
00090     "zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh",
00091     "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth",
00092     "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteen",
00093     "nineteen", "twentieth"
00094 };
00095 
00097 static const char levelnumbers_10[11][20] = {
00098     "zeroth", "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth",
00099     "seventieth", "eightieth", "ninetieth"
00100 };
00101 
00109 static const typedata item_types[] = {
00110     { PLAYER, "player", "players", 0, 0 },
00111     { ROD, "rod", "rods", SK_THAUMATURGY, 0 },
00112     { TREASURE, "treasure", "treasure", 0, 0 },
00113     { POTION, "potion", "potions", SK_ALCHEMY, 0 },
00114     { FOOD, "food", "food", SK_WOODSMAN, 0 },
00115     { POISON, "poison", "poisons", SK_ALCHEMY, 0 },
00116     { BOOK, "book", "books", SK_LITERACY, 0 },
00117     { CLOCK, "clock", "clocks", 0, 0 },
00118     { ARROW, "arrow", "arrows", SK_BOWYER, 0 },
00119     { BOW, "bow", "bows", SK_BOWYER, 0 },
00120     { WEAPON, "weapon", "weapons", SK_SMITHERY, 0 },
00121     { ARMOUR, "armour", "armour", SK_SMITHERY, 0 },
00122     { PEDESTAL, "pedestal", "pedestals", 0, 0 },
00123     { ALTAR, "altar", "altars", 0, 0 },
00124     { LOCKED_DOOR, "locked door", "locked doors", 0, 0 },
00125     { SPECIAL_KEY, "special key", "special keys", 0, 0 },
00126     { MAP, "map", "maps", 0, 0 },
00127     { DOOR, "door", "doors", 0, 0 },
00128     { KEY, "key", "keys", 0, 0 },
00129     { TIMED_GATE, "timed_gate", "timed_gates", 0, 0 },
00130     { TRIGGER, "trigger", "triggers", 0, 0 },
00131     { GRIMREAPER, "grimreaper", "grimreapers", 0, 0 },
00132     { MAGIC_EAR, "magic ear", "magic ears", 0, 0 },
00133     { TRIGGER_BUTTON, "trigger button", "trigger buttons", 0, 0 },
00134     { TRIGGER_ALTAR, "trigger altar", "trigger altars", 0, 0 },
00135     { TRIGGER_PEDESTAL, "trigger pedestal", "trigger pedestals", 0, 0 },
00136     { SHIELD, "shield", "shields", SK_SMITHERY, 0 },
00137     { HELMET, "helmet", "helmets", SK_SMITHERY, 0 },
00138     { HORN, "horn", "horns", SK_THAUMATURGY, 0 },
00139     { MONEY, "money", "money", 0, 0 },
00140     { CLASS, "class", "classes", 0, 0 },
00141     { AMULET, "amulet", "amulets", SK_JEWELER, 0 },
00142     { PLAYERMOVER, "player mover", "player movers", 0, 0 },
00143     { TELEPORTER, "teleporter", "teleporters", 0, 0 },
00144     { CREATOR, "creator", "creators", 0, 0 },
00145     { SKILL, "skill", "skills", 0, 0 },
00146     { EXPERIENCE, "experience", "experience", 0, 0 },
00147     { EARTHWALL, "earthwall", "earthwalls", 0, 0 },
00148     { GOLEM, "golem", "golems", 0, 0 },
00149     { THROWN_OBJ, "projectile", "projectiles", 0, 0 },
00150     { BLINDNESS, "blindness", "blindness", 0, 0 },
00151     { GOD, "god", "gods", 0, 0 },
00152     { DETECTOR, "detector", "detectors", 0, 0 },
00153     { TRIGGER_MARKER, "trigger marker", "trigger markers", 0, 0 },
00154     { DEAD_OBJECT, "dead object", "dead objects", 0, 0 },
00155     { DRINK, "drink", "drinks", SK_WOODSMAN, SK_ALCHEMY },
00156     { MARKER, "marker", "markers", 0, 0 },
00157     { HOLY_ALTAR, "holy altar", "holy altars", 0, 0 },
00158     { PLAYER_CHANGER, "player changer", "player changers", 0, 0 },
00159     { BATTLEGROUND, "battleground", "battlegrounds", 0, 0 },
00160     { PEACEMAKER, "peacemaker", "peacemakers", 0, 0 },
00161     { GEM, "gem", "gems", SK_JEWELER, 0 },
00162     { FIREWALL, "firewall", "firewalls", 0, 0 },
00163     { CHECK_INV, "inventory checker", "inventory checkers", 0, 0 },
00164     { MOOD_FLOOR, "mood floor", "mood floors", 0, 0 },
00165     { EXIT, "exit", "exits", 0, 0 },
00166     { ENCOUNTER, "encounter", "encounters", 0, 0 },
00167     { SHOP_FLOOR, "shop floor", "shop floors", 0, 0 },
00168     { SHOP_MAT, "shop mat", "shop mats", 0, 0 },
00169     { RING, "ring", "rings", SK_JEWELER, 0 },
00170     { FLOOR, "floor", "floors", 0, 0 },
00171     { FLESH, "flesh", "flesh", SK_WOODSMAN, 0 },
00172     { INORGANIC, "inorganic", "inorganics", SK_ALCHEMY, 0 },
00173     { SKILL_TOOL, "skill tool", "skill tools", 0, 0 },
00174     { LIGHTER, "lighter", "lighters", 0, 0 },
00175     { WALL, "wall", "walls", 0, 0 },
00176     { MISC_OBJECT, "bric-a-brac", "bric-a-brac", 0, 0 },
00177     { MONSTER, "monster", "monsters", 0, 0 },
00178     { LAMP, "lamp", "lamps", 0, 0 },
00179     { DUPLICATOR, "duplicator", "duplicators", 0, 0 },
00180     { SPELLBOOK, "spellbook", "spellbooks", SK_LITERACY, 0 },
00181     { CLOAK, "cloak", "cloaks", SK_SMITHERY, 0 },
00182     { SPINNER, "spinner", "spinners", 0, 0 },
00183     { GATE, "gate", "gates", 0, 0 },
00184     { BUTTON, "button", "buttons", 0, 0 },
00185     { CF_HANDLE, "cf handle", "cf handles", 0, 0 },
00186     { HOLE, "hole", "holes", 0, 0 },
00187     { TRAPDOOR, "trapdoor", "trapdoors", 0, 0 },
00188     { SIGN, "sign", "signs", 0, 0 },
00189     { BOOTS, "boots", "boots", SK_SMITHERY, 0 },
00190     { GLOVES, "gloves", "gloves", SK_SMITHERY, 0 },
00191     { SPELL, "spell", "spells", 0, 0 },
00192     { SPELL_EFFECT, "spell effect", "spell effects", 0, 0 },
00193     { CONVERTER, "converter", "converters", 0, 0 },
00194     { BRACERS, "bracers", "bracers", SK_SMITHERY, 0 },
00195     { POISONING, "poisoning", "poisonings", 0, 0 },
00196     { SAVEBED, "savebed", "savebeds", 0, 0 },
00197     { WAND, "wand", "wands", SK_THAUMATURGY, 0 },
00198     { SCROLL, "scroll", "scrolls", SK_LITERACY, 0 },
00199     { DIRECTOR, "director", "directors", 0, 0 },
00200     { GIRDLE, "girdle", "girdles", SK_SMITHERY, 0 },
00201     { FORCE, "force", "forces", 0, 0 },
00202     { POTION_EFFECT, "potion effect", "potion effects", 0, 0 },
00203     { CLOSE_CON, "closed container", "closed container", 0, 0 },
00204     { CONTAINER, "container", "containers", SK_ALCHEMY, 0 },
00205     { ARMOUR_IMPROVER, "armour improver", "armour improvers", 0, 0 },
00206     { WEAPON_IMPROVER, "weapon improver", "weapon improvers", 0, 0 },
00207     { SKILLSCROLL, "skillscroll", "skillscrolls", 0, 0 },
00208     { DEEP_SWAMP, "deep swamp", "deep swamps", 0, 0 },
00209     { IDENTIFY_ALTAR, "identify altar", "identify altars", 0, 0 },
00210     { SHOP_INVENTORY, "inventory list", "inventory lists", 0, 0 },
00211     { RUNE, "rune", "runes", 0, 0 },
00212     { TRAP, "trap", "traps", 0, 0 },
00213     { POWER_CRYSTAL, "power_crystal", "power_crystals", 0, 0 },
00214     { CORPSE, "corpse", "corpses", 0, 0 },
00215     { DISEASE, "disease", "diseases", 0, 0 },
00216     { SYMPTOM, "symptom", "symptoms", 0, 0 },
00217     { BUILDER, "item builder", "item builders", 0, 0 },
00218     { MATERIAL, "building material", "building materials", 0, 0 },
00219 };
00220 
00222 static const int item_types_size = sizeof(item_types)/sizeof(*item_types);
00223 
00233 static const int enc_to_item_power[21] = {
00234     0, 0, 1, 2, 3, 4,    /* 5 */
00235     5, 7, 9, 11, 13,    /* 10 */
00236     15, 18, 21, 24, 27, /* 15 */
00237     30, 35, 40, 45, 50  /* 20 */
00238 };
00239 
00240 int get_power_from_ench(int ench) {
00241     if (ench < 0)
00242         ench = 0;
00243     if (ench > 20)
00244         ench = 20;
00245     return enc_to_item_power[ench];
00246 }
00247 
00264 int calc_item_power(const object *op, int flag) {
00265     int i, tmp, enc;
00266 
00267     enc = 0;
00268     for (i = 0; i < NUM_STATS; i++)
00269         enc += get_attr_value(&op->stats, i);
00270 
00271     /* This protection logic is pretty flawed.  20% fire resistance
00272      * is much more valuable than 20% confusion, or 20% slow, or
00273      * several others.  Start at 1 - ignore physical - all that normal
00274      * armour shouldn't be counted against
00275      */
00276     tmp = 0;
00277     for (i = 1; i < NROFATTACKS; i++)
00278         tmp += op->resist[i];
00279 
00280     /* Add/substract 10 so that the rounding works out right */
00281     if (tmp > 0)
00282         enc += (tmp+10)/20;
00283     else if (tmp < 0)
00284         enc += (tmp-10)/20;
00285 
00286     enc += op->magic;
00287 
00288     /* For each attacktype a weapon has, one more encantment.  Start at 1 -
00289      * physical doesn't count against total.
00290      */
00291     if (op->type == WEAPON) {
00292         for (i = 1; i < NROFATTACKS; i++)
00293             if (op->attacktype&(1<<i))
00294                 enc++;
00295         if (op->slaying)
00296             enc += 2;     /* What it slays is probably more relevent */
00297     }
00298     /* Items the player can equip */
00299     if ((op->type == WEAPON)
00300     || (op->type == ARMOUR)
00301     || (op->type == HELMET)
00302     || (op->type == SHIELD)
00303     || (op->type == RING)
00304     || (op->type == BOOTS)
00305     || (op->type == GLOVES)
00306     || (op->type == AMULET)
00307     || (op->type == GIRDLE)
00308     || (op->type == BRACERS)
00309     || (op->type == CLOAK)) {
00310         enc += op->stats.food;     /* sustenance */
00311         enc += op->stats.hp;       /* hp regen */
00312         enc += op->stats.sp;       /* mana regen */
00313         enc += op->stats.grace;    /* grace regen */
00314         enc += op->stats.exp;      /* speed bonus */
00315     }
00316     enc += op->stats.luck;
00317 
00318     /* Do spell paths now */
00319     for (i = 1; i < NRSPELLPATHS; i++) {
00320         if (op->path_attuned&(1<<i))
00321             enc++;
00322         else if (op->path_denied&(1<<i))
00323             enc -= 2;
00324         else if (op->path_repelled&(1<<i))
00325             enc--;
00326     }
00327 
00328     if (QUERY_FLAG(op, FLAG_LIFESAVE))
00329         enc += 5;
00330     if (QUERY_FLAG(op, FLAG_REFL_SPELL))
00331         enc += 3;
00332     if (QUERY_FLAG(op, FLAG_REFL_MISSILE))
00333         enc += 2;
00334     if (QUERY_FLAG(op, FLAG_STEALTH))
00335         enc += 1;
00336     if (QUERY_FLAG(op, FLAG_XRAYS))
00337         enc += 2;
00338     if (QUERY_FLAG(op, FLAG_SEE_IN_DARK))
00339         enc += 1;
00340     if (QUERY_FLAG(op, FLAG_MAKE_INVIS))
00341         enc += 1;
00342 
00343     return get_power_from_ench(enc);
00344 }
00345 
00352 const typedata *get_typedata(int itemtype) {
00353     int i;
00354 
00355     for (i = 0; i < item_types_size; i++)
00356         if (item_types[i].number == itemtype)
00357             return &item_types[i];
00358     return NULL;
00359 }
00360 
00372 const typedata *get_typedata_by_name(const char *name) {
00373     int i;
00374 
00375     for (i = 0; i < item_types_size; i++)
00376         if (!strcmp(item_types[i].name, name))
00377             return &item_types[i];
00378     for (i = 0; i < item_types_size; i++)
00379         if (!strcmp(item_types[i].name_pl, name)) {
00380             LOG(llevInfo, "get_typedata_by_name: I have been sent the plural %s, the singular form %s is preffered\n", name, item_types[i].name);
00381             return &item_types[i];
00382         }
00383     return NULL;
00384 }
00385 
00399 void describe_resistance(const object *op, int newline, char *buf, size_t size) {
00400     char *p;
00401     int tmpvar;
00402 
00403     p = buf;
00404     for (tmpvar = 0; tmpvar < NROFATTACKS; tmpvar++) {
00405         if (op->resist[tmpvar] && (op->type != FLESH || atnr_is_dragon_enabled(tmpvar) == 1)) {
00406             if (!newline)
00407                 snprintf(p, buf+size-p, "(%s %+d)", resist_plus[tmpvar], op->resist[tmpvar]);
00408             else
00409                 snprintf(p, buf+size-p, "%s %d\n", resist_plus[tmpvar], op->resist[tmpvar]);
00410             p = strchr(p, '\0');
00411         }
00412     }
00413 }
00414 
00424 void query_weight(const object *op, char *buf, size_t size) {
00425     sint32 i = (op->nrof ? op->nrof : 1)*op->weight+op->carrying;
00426 
00427     if (op->weight < 0)
00428         snprintf(buf, size, "      ");
00429     else if (i%1000)
00430         snprintf(buf, size, "%6.1f", i/1000.0);
00431     else
00432         snprintf(buf, size, "%4d  ", i/1000);
00433 }
00434 
00444 void get_levelnumber(int i, char *buf, size_t size) {
00445     if (i > 99 || i < 0) {
00446         snprintf(buf, size, "%d.", i);
00447         return;
00448     }
00449     if (i < 21) {
00450         snprintf(buf, size, "%s", levelnumbers[i]);
00451         return;
00452     }
00453     if (!(i%10)) {
00454         snprintf(buf, size, "%s", levelnumbers_10[i/10]);
00455         return;
00456     }
00457     snprintf(buf, size, "%s%s", numbers_10[i/10], levelnumbers[i%10]);
00458     return;
00459 }
00460 
00483 static void ring_desc(const object *op, char *buf, size_t size) {
00484     int attr, val;
00485     size_t len;
00486 
00487     buf[0] = 0;
00488 
00489     if (!QUERY_FLAG(op, FLAG_IDENTIFIED))
00490         return;
00491 
00492     for (attr = 0; attr < NUM_STATS; attr++) {
00493         if ((val = get_attr_value(&(op->stats), attr)) != 0) {
00494             snprintf(buf+strlen(buf), size-strlen(buf), "(%s%+d)", short_stat_name[attr], val);
00495         }
00496     }
00497     if (op->stats.exp)
00498         snprintf(buf+strlen(buf), size-strlen(buf), "(speed %+"FMT64")", op->stats.exp);
00499     if (op->stats.wc)
00500         snprintf(buf+strlen(buf), size-strlen(buf), "(wc%+d)", op->stats.wc);
00501     if (op->stats.dam)
00502         snprintf(buf+strlen(buf), size-strlen(buf), "(dam%+d)", op->stats.dam);
00503     if (op->stats.ac)
00504         snprintf(buf+strlen(buf), size-strlen(buf), "(ac%+d)", op->stats.ac);
00505 
00506     describe_resistance(op, 0, buf+strlen(buf), size-strlen(buf));
00507 
00508     if (op->stats.food != 0)
00509         snprintf(buf+strlen(buf), size-strlen(buf), "(sustenance%+d)", op->stats.food);
00510 /*  else if (op->stats.food < 0)
00511         snprintf(buf+strlen(buf), size-strlen(buf), "(hunger%+d)", op->stats.food); */
00512     if (op->stats.grace)
00513         snprintf(buf+strlen(buf), size-strlen(buf), "(grace%+d)", op->stats.grace);
00514     if (op->stats.sp && op->type != SKILL)
00515         snprintf(buf+strlen(buf), size-strlen(buf), "(magic%+d)", op->stats.sp);
00516     if (op->stats.hp)
00517         snprintf(buf+strlen(buf), size-strlen(buf), "(regeneration%+d)", op->stats.hp);
00518     if (op->stats.luck)
00519         snprintf(buf+strlen(buf), size-strlen(buf), "(luck%+d)", op->stats.luck);
00520     if (QUERY_FLAG(op, FLAG_LIFESAVE))
00521         snprintf(buf+strlen(buf), size-strlen(buf), "(lifesaving)");
00522     if (QUERY_FLAG(op, FLAG_REFL_SPELL))
00523         snprintf(buf+strlen(buf), size-strlen(buf), "(reflect spells)");
00524     if (QUERY_FLAG(op, FLAG_REFL_MISSILE))
00525         snprintf(buf+strlen(buf), size-strlen(buf), "(reflect missiles)");
00526     if (QUERY_FLAG(op, FLAG_STEALTH))
00527         snprintf(buf+strlen(buf), size-strlen(buf), "(stealth)");
00528     /* Shorten some of the names, so they appear better in the windows */
00529     len = strlen(buf);
00530     DESCRIBE_PATH_SAFE(buf, op->path_attuned, "Attuned", &len, size);
00531     DESCRIBE_PATH_SAFE(buf, op->path_repelled, "Repelled", &len, size);
00532     DESCRIBE_PATH_SAFE(buf, op->path_denied, "Denied", &len, size);
00533 
00534 /*  if (op->item_power)
00535         snprintf(buf+strlen(buf), size-strlen(buf), "(item_power %+d)", op->item_power);*/
00536     if (buf[0] == 0 && op->type != SKILL)
00537         snprintf(buf, size, "of adornment");
00538 }
00539 
00551 void query_short_name(const object *op, char *buf, size_t size) {
00552     size_t len = 0;
00553 
00554     if (op->name == NULL) {
00555         snprintf(buf, size, "(null)");
00556         return;
00557     }
00558     if (!op->nrof && !op->weight && !op->title && !is_magical(op)) {
00559         snprintf(buf, size, "%s", op->name); /* To speed things up (or make things slower?) */
00560         return;
00561     }
00562     buf[0] = '\0';
00563 
00564     if (op->nrof <= 1)
00565         safe_strcat(buf, op->name, &len, size);
00566     else
00567         safe_strcat(buf, op->name_pl, &len, size);
00568 
00569     if (op->title && QUERY_FLAG(op, FLAG_IDENTIFIED)) {
00570         safe_strcat(buf, " ", &len, size);
00571         safe_strcat(buf, op->title, &len, size);
00572     }
00573 
00574     switch (op->type) {
00575     case SPELLBOOK:
00576     case SCROLL:
00577     case WAND:
00578     case ROD:
00579         if (QUERY_FLAG(op, FLAG_IDENTIFIED)||QUERY_FLAG(op, FLAG_BEEN_APPLIED)) {
00580             if (!op->title) {
00581                 safe_strcat(buf, " of ", &len, size);
00582                 if (op->inv)
00583                     safe_strcat(buf, op->inv->name, &len, size);
00584                 else
00585                     LOG(llevError, "Spellbook %s lacks inventory\n", op->name);
00586             }
00587             if (op->type != SPELLBOOK) {
00588                 snprintf(buf+len, size-len, " (lvl %d)", op->level);
00589                 len += strlen(buf+len);
00590             }
00591         }
00592         break;
00593 
00594     case SKILL:
00595     case AMULET:
00596     case RING:
00597         if (!op->title) {
00598             /* If ring has a title, full description isn't so useful */
00599             char desc[VERY_BIG_BUF];
00600 
00601             ring_desc(op, desc, VERY_BIG_BUF);
00602             if (desc[0]) {
00603                 safe_strcat(buf, " ", &len, size);
00604                 safe_strcat(buf, desc, &len, size);
00605             }
00606         }
00607         break;
00608 
00609     default:
00610         if (op->magic
00611         && ((QUERY_FLAG(op, FLAG_BEEN_APPLIED) && need_identify(op)) || QUERY_FLAG(op, FLAG_IDENTIFIED))) {
00612             snprintf(buf+len, size-len, " %+d", op->magic);
00613             len += strlen(buf+len);
00614         }
00615     }
00616 }
00617 
00628 void query_name(const object *op, char *buf, size_t size) {
00629     size_t len = 0;
00630 #ifdef NEW_MATERIAL_CODE
00631     materialtype_t *mt;
00632 #endif
00633 
00634     buf[0] = '\0';
00635 
00636 #ifdef NEW_MATERIAL_CODE
00637     if ((IS_ARMOR(op) || IS_WEAPON(op) || IS_SHIELD(op)) && op->materialname) {
00638         mt = name_to_material(op->materialname);
00639         if (mt) {
00640             safe_strcat(buf, mt->description, &len, size);
00641             safe_strcat(buf, " ", &len, size);
00642         }
00643     }
00644 #endif
00645 
00646     query_short_name(op, buf+len, size-len);
00647     len += strlen(buf+len);
00648 
00649     if (QUERY_FLAG(op, FLAG_INV_LOCKED))
00650         safe_strcat(buf, " *", &len, size);
00651     if (op->type == CONTAINER
00652     && ((op->env && op->env->container == op) || (!op->env && QUERY_FLAG(op, FLAG_APPLIED))))
00653         safe_strcat(buf, " (open)", &len, size);
00654 
00655     if (QUERY_FLAG(op, FLAG_KNOWN_CURSED)) {
00656         if (QUERY_FLAG(op, FLAG_DAMNED))
00657             safe_strcat(buf, " (damned)", &len, size);
00658         else if (QUERY_FLAG(op, FLAG_CURSED))
00659             safe_strcat(buf, " (cursed)", &len, size);
00660     }
00661     if (QUERY_FLAG(op, FLAG_BLESSED) && QUERY_FLAG(op, FLAG_KNOWN_BLESSED))
00662         safe_strcat(buf, " (blessed)", &len, size);
00663 
00664     /* Basically, if the object is known magical (detect magic spell on it),
00665      * and it isn't identified,  print out the fact that
00666      * it is magical.  Assume that the detect magical spell will only set
00667      * KNOWN_MAGICAL if the item actually is magical.
00668      *
00669      * Changed in V 0.91.4 - still print that the object is magical even
00670      * if it has been applied.  Equipping an item does not tell full
00671      * abilities, especially for artifact items.
00672      */
00673     if (QUERY_FLAG(op, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(op, FLAG_IDENTIFIED))
00674         safe_strcat(buf, " (magic)", &len, size);
00675 
00676     if (QUERY_FLAG(op, FLAG_APPLIED)) {
00677         switch (op->type) {
00678         case BOW:
00679         case WAND:
00680         case ROD:
00681         case HORN:
00682             safe_strcat(buf, " (readied)", &len, size);
00683             break;
00684 
00685         case WEAPON:
00686             safe_strcat(buf, " (wielded)", &len, size);
00687             break;
00688 
00689         case ARMOUR:
00690         case HELMET:
00691         case SHIELD:
00692         case RING:
00693         case BOOTS:
00694         case GLOVES:
00695         case AMULET:
00696         case GIRDLE:
00697         case BRACERS:
00698         case CLOAK:
00699             safe_strcat(buf, " (worn)", &len, size);
00700             break;
00701 
00702         case CONTAINER:
00703             safe_strcat(buf, " (active)", &len, size);
00704             break;
00705 
00706         case SKILL:
00707         default:
00708             safe_strcat(buf, " (applied)", &len, size);
00709         }
00710     }
00711     if (QUERY_FLAG(op, FLAG_UNPAID))
00712         safe_strcat(buf, " (unpaid)", &len, size);
00713 }
00714 
00732 void query_base_name(const object *op, int plural, char *buf, size_t size) {
00733     size_t len;
00734 #ifdef NEW_MATERIAL_CODE
00735     materialtype_t *mt;
00736 #endif
00737 
00738     if ((!plural && !op->name)
00739     || (plural && !op->name_pl)) {
00740         strncpy(buf, "(null)", size);
00741         return;
00742     }
00743 
00744     if (!op->nrof && !op->weight && !op->title && !is_magical(op)) {
00745         strncpy(buf, op->name, size); /* To speed things up (or make things slower?) */
00746         return;
00747     }
00748 
00749     buf[0] = '\0';
00750 
00751 #ifdef NEW_MATERIAL_CODE
00752     if ((IS_ARMOR(op) || IS_WEAPON(op)) && op->materialname)
00753         mt = name_to_material(op->materialname);
00754     else
00755         mt = NULL;
00756 
00757     if (mt
00758     && op->arch->clone.materialname != mt->name
00759     && !(op->material&M_SPECIAL)) {
00760         snprintf(buf, size, "%s", mt->description);
00761         len = strlen(buf);
00762         safe_strcat(buf, " ", &len, size);
00763         safe_strcat(buf, plural ? op->name_pl : op->name, &len, size);
00764     } else {
00765 #endif
00766         snprintf(buf, size, "%s", plural ? op->name_pl : op->name);
00767         len = strlen(buf);
00768 #ifdef NEW_MATERIAL_CODE
00769     }
00770 #endif
00771 
00772     if (op->title && QUERY_FLAG(op, FLAG_IDENTIFIED)) {
00773         safe_strcat(buf, " ", &len, size);
00774         safe_strcat(buf, op->title, &len, size);
00775     }
00776 
00777     switch (op->type) {
00778     case SPELLBOOK:
00779     case SCROLL:
00780     case WAND:
00781     case ROD:
00782         if (QUERY_FLAG(op, FLAG_IDENTIFIED)||QUERY_FLAG(op, FLAG_BEEN_APPLIED)) {
00783             if (!op->title) {
00784                 safe_strcat(buf, " of ", &len, size);
00785                 if (op->inv)
00786                     safe_strcat(buf, op->inv->name, &len, size);
00787                 else
00788                     LOG(llevError, "Spellbook %s lacks inventory\n", op->name);
00789             }
00790             if (op->type != SPELLBOOK) {
00791                 snprintf(buf+len, size-len, " (lvl %d)", op->level);
00792                 len += strlen(buf+len);
00793             }
00794         }
00795         break;
00796 
00797     case SKILL:
00798     case AMULET:
00799     case RING:
00800         if (!op->title) {
00801             /* If ring has a title, full description isn't so useful */
00802             char s[MAX_BUF];
00803 
00804             ring_desc(op, s, MAX_BUF);
00805             if (s[0]) {
00806                 safe_strcat(buf, " ", &len, size);
00807                 safe_strcat(buf, s, &len, size);
00808             }
00809         }
00810         break;
00811 
00812     default:
00813         if (op->magic
00814         && ((QUERY_FLAG(op, FLAG_BEEN_APPLIED) && need_identify(op)) || QUERY_FLAG(op, FLAG_IDENTIFIED))) {
00815             snprintf(buf+strlen(buf), size-strlen(buf), " %+d", op->magic);
00816         }
00817     }
00818 }
00819 
00840 void describe_monster(const object *op, char *retbuf, size_t size) {
00841     int i;
00842     size_t len;
00843 
00844     retbuf[0] = '\0';
00845 
00846     /* Note that the resolution this provides for players really isn't
00847      * very good.  Any player with a speed greater than .67 will
00848      * fall into the 'lightning fast movement' category.
00849      */
00850     if (FABS(op->speed) > MIN_ACTIVE_SPEED) {
00851         switch ((int)((FABS(op->speed))*15)) {
00852         case 0:
00853             snprintf(retbuf, size, "(very slow movement)");
00854             break;
00855 
00856         case 1:
00857             snprintf(retbuf, size, "(slow movement)");
00858             break;
00859 
00860         case 2:
00861             snprintf(retbuf, size, "(normal movement)");
00862             break;
00863 
00864         case 3:
00865         case 4:
00866             snprintf(retbuf, size, "(fast movement)");
00867             break;
00868 
00869         case 5:
00870         case 6:
00871             snprintf(retbuf, size, "(very fast movement)");
00872             break;
00873 
00874         case 7:
00875         case 8:
00876         case 9:
00877         case 10:
00878             snprintf(retbuf, size, "(extremely fast movement)");
00879             break;
00880 
00881         default:
00882             snprintf(retbuf, size, "(lightning fast movement)");
00883             break;
00884         }
00885     }
00886     if (QUERY_FLAG(op, FLAG_UNDEAD))
00887         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(undead)");
00888     if (QUERY_FLAG(op, FLAG_SEE_INVISIBLE))
00889         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(see invisible)");
00890     if (QUERY_FLAG(op, FLAG_USE_WEAPON))
00891         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(wield weapon)");
00892     if (QUERY_FLAG(op, FLAG_USE_BOW))
00893         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(archer)");
00894     if (QUERY_FLAG(op, FLAG_USE_ARMOUR))
00895         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(wear armour)");
00896     if (QUERY_FLAG(op, FLAG_USE_RING))
00897         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(wear ring)");
00898     if (QUERY_FLAG(op, FLAG_USE_SCROLL))
00899         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(read scroll)");
00900     if (QUERY_FLAG(op, FLAG_USE_RANGE))
00901         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(fires wand/rod/horn)");
00902     if (QUERY_FLAG(op, FLAG_CAN_USE_SKILL))
00903         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(skill user)");
00904     if (QUERY_FLAG(op, FLAG_CAST_SPELL))
00905         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(spellcaster)");
00906     if (QUERY_FLAG(op, FLAG_FRIENDLY))
00907         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(friendly)");
00908     if (QUERY_FLAG(op, FLAG_UNAGGRESSIVE))
00909         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(unaggressive)");
00910     if (QUERY_FLAG(op, FLAG_HITBACK))
00911         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(hitback)");
00912     if (QUERY_FLAG(op, FLAG_STEALTH))
00913         snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(stealthy)");
00914     if (op->randomitems != NULL) {
00915         treasure *t;
00916         int first = 1;
00917 
00918         for (t = op->randomitems->items; t != NULL; t = t->next)
00919             if (t->item && (t->item->clone.type == SPELL)) {
00920                 if (first) {
00921                     first = 0;
00922                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(Spell abilities:)");
00923                 }
00924                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(%s)",  t->item->clone.name);
00925             }
00926     }
00927     if (op->type == PLAYER) {
00928         if (op->contr->digestion) {
00929             if (op->contr->digestion != 0)
00930                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(sustenance%+d)", op->contr->digestion);
00931         }
00932         if (op->contr->gen_grace) {
00933             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(grace%+d)", op->contr->gen_grace);
00934         }
00935         if (op->contr->gen_sp) {
00936             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(magic%+d)", op->contr->gen_sp);
00937         }
00938         if (op->contr->gen_hp) {
00939             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(regeneration%+d)", op->contr->gen_hp);
00940         }
00941         if (op->stats.luck) {
00942             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(luck%+d)", op->stats.luck);
00943         }
00944     }
00945 
00946     /* describe attacktypes */
00947     len = strlen(retbuf);
00948     if (is_dragon_pl(op)) {
00949         /* for dragon players display the attacktypes from clawing skill
00950          * Break apart the for loop - move the comparison checking down -
00951          * this makes it more readable.
00952          */
00953         object *tmp;
00954 
00955         for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
00956             if (tmp->type == SKILL && !strcmp(tmp->name, "clawing"))
00957                 break;
00958 
00959         if (tmp && tmp->attacktype != 0) {
00960             DESCRIBE_ABILITY_SAFE(retbuf, tmp->attacktype, "Claws", &len, size);
00961         } else {
00962             DESCRIBE_ABILITY_SAFE(retbuf, op->attacktype, "Attacks", &len, size);
00963         }
00964     } else {
00965         DESCRIBE_ABILITY_SAFE(retbuf, op->attacktype, "Attacks", &len, size);
00966     }
00967     DESCRIBE_PATH_SAFE(retbuf, op->path_attuned, "Attuned", &len, size);
00968     DESCRIBE_PATH_SAFE(retbuf, op->path_repelled, "Repelled", &len, size);
00969     DESCRIBE_PATH_SAFE(retbuf, op->path_denied, "Denied", &len, size);
00970     for (i = 0; i < NROFATTACKS; i++) {
00971         if (op->resist[i]) {
00972             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(%s %+d)", resist_plus[i], op->resist[i]);
00973         }
00974     }
00975 }
00976 
01018 void describe_item(const object *op, const object *owner, char *retbuf, size_t size) {
01019     int identified, i;
01020 
01021     retbuf[0] = '\0';
01022     if (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER) {
01023         describe_monster(op, retbuf, size);
01024         return;
01025     }
01026     /* figure this out once, instead of making multiple calls to need_identify.
01027      * also makes the code easier to read.
01028      */
01029     if (!need_identify(op) || QUERY_FLAG(op, FLAG_IDENTIFIED))
01030         identified = 1;
01031     else {
01032         snprintf(retbuf, size, "(unidentified)");
01033         identified = 0;
01034     }
01035     switch (op->type) {
01036     case BOW:
01037     case ARROW:
01038     case WAND:
01039     case ROD:
01040     case HORN:
01041     case WEAPON:
01042     case ARMOUR:
01043     case HELMET:
01044     case SHIELD:
01045     case BOOTS:
01046     case GLOVES:
01047     case GIRDLE:
01048     case BRACERS:
01049     case CLOAK:
01050     case SKILL_TOOL:
01051         break;  /* We have more information to do below this switch */
01052 
01053     case LAMP:
01054         break; /* just so we get the "glowing" part. */
01055 
01056     case POWER_CRYSTAL:
01057         /* Avoid division by zero... */
01058         if (op->stats.maxsp == 0) {
01059             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(capacity %d).", op->stats.maxsp);
01060         } else {
01061             if (op->stats.maxsp > 1000) { /*higher capacity crystals*/
01062                 i = (op->stats.maxsp%1000)/100;
01063                 if (i)
01064                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(capacity %d.%dk). It is ", op->stats.maxsp/1000, i);
01065                 else
01066                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(capacity %dk). It is ", op->stats.maxsp/1000);
01067             } else
01068                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(capacity %d). It is ", op->stats.maxsp);
01069             i = (op->stats.sp*10)/op->stats.maxsp;
01070             if (op->stats.sp == 0)
01071                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "empty.");
01072             else if (i == 0)
01073                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "almost empty.");
01074             else if (i < 3)
01075                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "partially filled.");
01076             else if (i < 6)
01077                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "half full.");
01078             else if (i < 9)
01079                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "well charged.");
01080             else if (op->stats.sp == op->stats.maxsp)
01081                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "fully charged.");
01082             else
01083                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "almost full.");
01084         }
01085         break;
01086 
01087     case FOOD:
01088     case FLESH:
01089     case DRINK:
01090         if (identified || QUERY_FLAG(op, FLAG_BEEN_APPLIED)) {
01091             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(food+%d)", op->stats.food);
01092 
01093             if (op->type == FLESH && op->last_eat > 0 && atnr_is_dragon_enabled(op->last_eat)) {
01094                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(%s metabolism)", change_resist_msg[op->last_eat]);
01095             }
01096 
01097             if (!QUERY_FLAG(op, FLAG_CURSED)) {
01098                 if (op->stats.hp)
01099                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(heals)");
01100                 if (op->stats.sp)
01101                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(spellpoint regen)");
01102             } else {
01103                 if (op->stats.hp)
01104                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(damages)");
01105                 if (op->stats.sp)
01106                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(spellpoint depletion)");
01107             }
01108         }
01109         break;
01110 
01111     case SKILL:
01112     case RING:
01113     case AMULET:
01114         if (op->item_power) {
01115             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(item_power %+d)", op->item_power);
01116         }
01117         if (op->title) {
01118             ring_desc(op, retbuf+strlen(retbuf), size-strlen(retbuf));
01119         }
01120         return;
01121 
01122     default:
01123         return;
01124     }
01125 
01126     /* Down here, we more further describe equipment type items.
01127      * only describe them if they have been identified or the like.
01128      */
01129     if (identified || QUERY_FLAG(op, FLAG_BEEN_APPLIED)) {
01130         int attr, val;
01131 
01132         for (attr = 0; attr < NUM_STATS; attr++) {
01133             if ((val = get_attr_value(&(op->stats), attr)) != 0) {
01134                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(%s%+d)", short_stat_name[attr], val);
01135             }
01136         }
01137         if (op->glow_radius)
01138             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(glowing)");
01139 
01140         switch (op->type) {
01141         case FLESH:
01142             break;
01143 
01144         default:
01145             if (op->stats.exp) {
01146                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(speed %+"FMT64")", op->stats.exp);
01147             }
01148             break;
01149         }
01150         switch (op->type) {
01151         case BOW:
01152         case ARROW:
01153         case GIRDLE:
01154         case HELMET:
01155         case SHIELD:
01156         case BOOTS:
01157         case GLOVES:
01158         case WEAPON:
01159         case SKILL:
01160         case RING:
01161         case AMULET:
01162         case ARMOUR:
01163         case BRACERS:
01164         case FORCE:
01165         case CLOAK:
01166             if (op->stats.wc) {
01167                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(wc%+d)", op->stats.wc);
01168             }
01169             if (op->stats.dam) {
01170                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(dam%+d)", op->stats.dam);
01171             }
01172             if (op->stats.ac) {
01173                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(ac%+d)", op->stats.ac);
01174             }
01175             if ((op->type == WEAPON || op->type == BOW) && op->level > 0) {
01176                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(improved %d/%d)", op->last_eat, op->level);
01177             }
01178             break;
01179 
01180         default:
01181             break;
01182         }
01183         if (QUERY_FLAG(op, FLAG_XRAYS))
01184             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(xray-vision)");
01185         if (QUERY_FLAG(op, FLAG_SEE_IN_DARK))
01186             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(infravision)");
01187 
01188         /* levitate was what is was before, so we'll keep it */
01189         if (op->move_type&MOVE_FLY_LOW)
01190             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(levitate)");
01191 
01192         if (op->move_type&MOVE_FLY_HIGH)
01193             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(fly)");
01194 
01195         if (op->move_type&MOVE_SWIM)
01196             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(swim)");
01197 
01198         /* walking is presumed as 'normal', so doesn't need mentioning */
01199 
01200         if (op->item_power) {
01201             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(item_power %+d)", op->item_power);
01202         }
01203     } /* End if identified or applied */
01204 
01205     /* This blocks only deals with fully identified object.
01206      * it is intentional that this is not an 'else' from a above -
01207      * in this way, information is added.
01208       */
01209     if (identified) {
01210         int more_info = 0;
01211         size_t len;
01212 
01213         switch (op->type) {
01214         case ROD:  /* These use stats.sp for spell selection and stats.food */
01215         case HORN: /* and stats.hp for spell-point regeneration... */
01216         case BOW:
01217         case ARROW:
01218         case WAND:
01219         case FOOD:
01220         case FLESH:
01221         case DRINK:
01222             more_info = 0;
01223             break;
01224 
01225             /* Armor type objects */
01226         case ARMOUR:
01227         case HELMET:
01228         case SHIELD:
01229         case BOOTS:
01230         case GLOVES:
01231         case GIRDLE:
01232         case BRACERS:
01233         case CLOAK:
01234             if (ARMOUR_SPEED(op)) {
01235                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(Max speed %1.2f)", ARMOUR_SPEED(op)/10.0);
01236             }
01237             if (ARMOUR_SPELLS(op)) {
01238                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(Spell regen penalty %d)", ARMOUR_SPELLS(op));
01239             }
01240             more_info = 1;
01241             break;
01242 
01243         case WEAPON:
01244             /* Calculate it the same way fix_object does so the results
01245              * make sense.
01246              */
01247             i = (WEAPON_SPEED(op)*2-op->magic)/2;
01248             if (i < 0)
01249                 i = 0;
01250 
01251             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(weapon speed %d)", i);
01252             more_info = 1;
01253             break;
01254         }
01255         if (more_info) {
01256             if (op->stats.food) {
01257                 if (op->stats.food != 0)
01258                     snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(sustenance%+d)", op->stats.food);
01259             }
01260             if (op->stats.grace) {
01261                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(grace%+d)", op->stats.grace);
01262             }
01263             if (op->stats.sp) {
01264                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(magic%+d)", op->stats.sp);
01265             }
01266             if (op->stats.hp) {
01267                 snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(regeneration%+d)", op->stats.hp);
01268             }
01269         }
01270 
01271         if (op->stats.luck) {
01272             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(luck%+d)", op->stats.luck);
01273         }
01274         if (QUERY_FLAG(op, FLAG_LIFESAVE))
01275             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(lifesaving)");
01276         if (QUERY_FLAG(op, FLAG_REFL_SPELL))
01277             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(reflect spells)");
01278         if (QUERY_FLAG(op, FLAG_REFL_MISSILE))
01279             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(reflect missiles)");
01280         if (QUERY_FLAG(op, FLAG_STEALTH))
01281             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(stealth)");
01282         if (op->slaying != NULL && op->type != FOOD) {
01283             snprintf(retbuf+strlen(retbuf), size-strlen(retbuf), "(slay %s)", op->slaying);
01284         }
01285         len = strlen(retbuf);
01286         DESCRIBE_ABILITY_SAFE(retbuf, op->attacktype, "Attacks", &len, size);
01287         /* resistance on flesh is only visible for dragons.  If
01288          * non flesh, everyone can see its resistances
01289          */
01290         if (op->type != FLESH || (owner && is_dragon_pl(owner))) {
01291             describe_resistance(op, 0, retbuf+strlen(retbuf), size-strlen(retbuf));
01292         }
01293         len = strlen(retbuf);
01294         DESCRIBE_PATH_SAFE(retbuf, op->path_attuned, "Attuned", &len, size);
01295         DESCRIBE_PATH_SAFE(retbuf, op->path_repelled, "Repelled", &len, size);
01296         DESCRIBE_PATH_SAFE(retbuf, op->path_denied, "Denied", &len, size);
01297     }
01298 }
01299 
01312 int is_magical(const object *op) {
01313     int i;
01314 
01315     /* living creatures are considered non magical */
01316     if (QUERY_FLAG(op, FLAG_ALIVE))
01317         return 0;
01318 
01319     /* This is a test for it being an artifact, as artifacts have titles */
01320     if (op->title != NULL)
01321         return 1;
01322 
01323     /* Handle rings and amulets specially.  If they change any of these
01324      * values, it means they are magical.
01325      */
01326     if ((op->type == AMULET || op->type == RING)
01327     && (op->stats.ac || op->stats.food || op->stats.exp || op->stats.dam || op->stats.wc || op->stats.sp || op->stats.hp || op->stats.luck))
01328         return 1;
01329 
01330     /* Check for stealty, speed, flying, or just plain magic in the boots */
01331     /* Presume any boots that have a move_type are special. */
01332     if (op->type == BOOTS
01333     && ((QUERY_FLAG(op, FLAG_STEALTH) || op->move_type || op->stats.exp)))
01334         return 1;
01335 
01336     /* Take care of amulet/shield that reflects spells/missiles */
01337     if ((op->type == AMULET || op->type == SHIELD)
01338     && (QUERY_FLAG(op, FLAG_REFL_SPELL) || QUERY_FLAG(op, FLAG_REFL_MISSILE)))
01339         return 1;
01340 
01341     /* Take care of helmet of xrays */
01342     if (op->type == HELMET
01343     && QUERY_FLAG(op, FLAG_XRAYS))
01344         return 1;
01345 
01346     /* Potions & rods are always magical.  Wands/staves are also magical,
01347      * assuming they still have any charges left.
01348      */
01349     if (op->type == POTION || op->type == ROD || (op->type == WAND && op->stats.food))
01350         return 1;
01351 
01352     /* if something gives a protection, either positive or negative, its magical */
01353     /* This is really a pretty bad hack - as of now, ATNR_PHYSICAL is 0,
01354      * so this always works out fine.
01355      */
01356     for (i = ATNR_PHYSICAL+1; i < NROFATTACKS; i++)
01357         if (op->resist[i])
01358             return 1;
01359 
01360     /* Physical protection is expected on some item types, so they should
01361      * not be considered magical.
01362      */
01363     if (op->resist[ATNR_PHYSICAL]
01364     && op->type != HELMET
01365     && op->type != SHIELD
01366     && op->type != BOOTS
01367     && op->type != GLOVES
01368     && op->type != ARMOUR)
01369         return 1;
01370 
01371     /* power crystal, spellbooks, and scrolls are always magical.  */
01372     if (op->magic
01373     || op->type == POWER_CRYSTAL
01374     || op->type == SPELLBOOK
01375     || op->type == SCROLL
01376     || op->type == GIRDLE)
01377         return 1;
01378 
01379     /* Check to see if it increases/decreases any stats */
01380     for (i = 0; i < NUM_STATS; i++)
01381         if (get_attr_value(&(op->stats), i) != 0)
01382             return 1;
01383 
01384     /* If it doesn't fall into any of the above categories, must
01385      * be non magical.
01386      */
01387     return 0;
01388 }
01389 
01401 int need_identify(const object *op) {
01402     switch (op->type) {
01403     case RING:
01404     case WAND:
01405     case ROD:
01406     case HORN:
01407     case SCROLL:
01408     case SKILL:
01409     case SKILLSCROLL:
01410     case SPELLBOOK:
01411     case FOOD:
01412     case POTION:
01413     case BOW:
01414     case ARROW:
01415     case WEAPON:
01416     case ARMOUR:
01417     case SHIELD:
01418     case HELMET:
01419     case AMULET:
01420     case BOOTS:
01421     case GLOVES:
01422     case BRACERS:
01423     case GIRDLE:
01424     case CONTAINER:
01425     case DRINK:
01426     case FLESH:
01427     case INORGANIC:
01428     case CLOSE_CON:
01429     case CLOAK:
01430     case GEM:
01431     case POWER_CRYSTAL:
01432     case POISON:
01433     case BOOK:
01434     case SKILL_TOOL:
01435         return 1;
01436     }
01437     return 0;
01438 }
01439 
01447 void identify(object *op) {
01448     object *pl;
01449 
01450     SET_FLAG(op, FLAG_IDENTIFIED);
01451     CLEAR_FLAG(op, FLAG_KNOWN_MAGICAL);
01452     CLEAR_FLAG(op, FLAG_NO_SKILL_IDENT);
01453 
01454     /*
01455      * We want autojoining of equal objects:
01456      */
01457     if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED))
01458         SET_FLAG(op, FLAG_KNOWN_CURSED);
01459 
01460     if (QUERY_FLAG(op, FLAG_BLESSED))
01461         SET_FLAG(op, FLAG_KNOWN_BLESSED);
01462 
01463     if (op->type == POTION) {
01464         if (op->inv && op->randomitems) {
01465             if (op->title)
01466                 free_string(op->title);
01467             op->title = add_refcount(op->inv->name);
01468         } else if (op->arch) {
01469             free_string(op->name);
01470             op->name = add_refcount(op->arch->clone.name);
01471             free_string(op->name_pl);
01472             op->name_pl = add_refcount(op->arch->clone.name_pl);
01473         }
01474     }
01475 
01476     if (op->map) {
01477         /* If the object is on a map, make sure we update its face.
01478          * Also send name and such information to a player standing on it.
01479          */
01480         object *player = present(PLAYER, op->map, op->x, op->y);
01481         if (player)
01482             esrv_update_item(UPD_FACE | UPD_NAME, player, op);
01483         update_object(op, UP_OBJ_FACE);
01484     } else {
01485         pl = get_player_container(op->env);
01486         if (pl)
01487             /* A lot of the values can change from an update - might as well send
01488              * it all.
01489              */
01490             esrv_update_item(UPD_ALL, pl, op);
01491     }
01492 }