Crossfire Server, Branch 1.12
R12190
|
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 }