#include "wizard1.hpp" #include "artifact_type.hpp" #include "cmd7.hpp" #include "ego_item_type.hpp" #include "monster_race.hpp" #include "object1.hpp" #include "object2.hpp" #include "object_kind.hpp" #include "object_type.hpp" #include "skill_type.hpp" #include "tables.hpp" #include "util.hpp" #include "util.h" #include "variable.h" #include "variable.hpp" #include /* * The spoiler file being created */ static FILE *fff = NULL; /* * Write out `n' of the character `c' to the spoiler file */ static void spoiler_out_n_chars(int n, char c) { while (--n >= 0) fputc(c, fff); } /* * Write out `n' blank lines to the spoiler file */ static void spoiler_blanklines(int n) { spoiler_out_n_chars(n, '\n'); } /* * Write a line to the spoiler file and then "underline" it with hyphens */ static void spoiler_underline(cptr str) { fprintf(fff, "%s\n", str); spoiler_out_n_chars(strlen(str), '-'); fprintf(fff, "\n"); } /* * Buffer text to the given file. (-SHAWN-) * This is basically c_roff() from mon-desc.c with a few changes. */ static void spoil_out(cptr str) { cptr r; /* Line buffer */ static char roff_buf[256]; /* Current pointer into line roff_buf */ static char *roff_p = roff_buf; /* Last space saved into roff_buf */ static char *roff_s = NULL; /* Special handling for "new sequence" */ if (!str) { if (roff_p != roff_buf) roff_p--; while (*roff_p == ' ' && roff_p != roff_buf) roff_p--; if (roff_p == roff_buf) fprintf(fff, "\n"); else { *(roff_p + 1) = '\0'; fprintf(fff, "%s\n\n", roff_buf); } roff_p = roff_buf; roff_s = NULL; roff_buf[0] = '\0'; return; } /* Scan the given string, character at a time */ for (; *str; str++) { char ch = *str; int wrap = (ch == '\n'); if (!isprint(ch)) ch = ' '; if (roff_p >= roff_buf + 75) wrap = 1; if ((ch == ' ') && (roff_p + 2 >= roff_buf + 75)) wrap = 1; /* Handle line-wrap */ if (wrap) { *roff_p = '\0'; r = roff_p; if (roff_s && (ch != ' ')) { *roff_s = '\0'; r = roff_s + 1; } fprintf(fff, "%s\n", roff_buf); roff_s = NULL; roff_p = roff_buf; while (*r) *roff_p++ = *r++; } /* Save the char */ if ((roff_p > roff_buf) || (ch != ' ')) { if (ch == ' ') roff_s = roff_p; *roff_p++ = ch; } } } /* * Extract a textual representation of an attribute */ static cptr attr_to_text(byte a) { switch (a) { case TERM_DARK: return ("xxx"); case TERM_WHITE: return ("White"); case TERM_SLATE: return ("Slate"); case TERM_ORANGE: return ("Orange"); case TERM_RED: return ("Red"); case TERM_GREEN: return ("Green"); case TERM_BLUE: return ("Blue"); case TERM_UMBER: return ("Umber"); case TERM_L_DARK: return ("L.Dark"); case TERM_L_WHITE: return ("L.Slate"); case TERM_VIOLET: return ("Violet"); case TERM_YELLOW: return ("Yellow"); case TERM_L_RED: return ("L.Red"); case TERM_L_GREEN: return ("L.Green"); case TERM_L_BLUE: return ("L.Blue"); case TERM_L_UMBER: return ("L.Umber"); } /* Oops */ return ("Icky"); } /* * A tval grouper */ typedef struct { byte tval; cptr name; } grouper; /* * Item Spoilers by: benh@phial.com (Ben Harrison) */ /* * The basic items categorized by type */ static grouper group_item[] = { { TV_SWORD, "Melee Weapons" }, { TV_POLEARM, NULL }, { TV_HAFTED, NULL }, { TV_AXE, NULL }, { TV_MSTAFF, NULL }, { TV_BOW, "Bows and Slings" }, { TV_SHOT, "Ammo" }, { TV_ARROW, NULL }, { TV_BOLT, NULL }, { TV_BOOMERANG, "Boomerangs" }, { TV_INSTRUMENT, "Instruments" }, { TV_SOFT_ARMOR, "Armour (Body)" }, { TV_HARD_ARMOR, NULL }, { TV_DRAG_ARMOR, NULL }, { TV_SHIELD, "Armour (Misc)" }, { TV_HELM, NULL }, { TV_CROWN, NULL }, { TV_GLOVES, NULL }, { TV_BOOTS, NULL }, { TV_CLOAK, "Cloaks" }, { TV_AMULET, "Amulets" }, { TV_RING, "Rings" }, { TV_SCROLL, "Scrolls" }, { TV_POTION, "Potions" }, { TV_POTION2, NULL }, { TV_FOOD, "Food" }, { TV_ROD_MAIN, "Rods" }, { TV_ROD, "Rod Tips" }, { TV_WAND, "Wands" }, { TV_STAFF, "Staves" }, { TV_BOOK, "Books (Magic, Gods, Music)" }, { TV_DAEMON_BOOK, "Demonic Equipment" }, { TV_RUNE1, "Runes" }, { TV_RUNE2, NULL }, { TV_PARCHMENT, "Parchments" }, { TV_DIGGING, "Tools" }, { TV_TOOL, NULL }, { TV_TRAPKIT, "Trapping Kits" }, { TV_CHEST, "Chests" }, { TV_SPIKE, "Various" }, { TV_LITE, NULL }, { TV_FLASK, NULL }, { TV_BOTTLE, NULL }, { TV_JUNK, NULL }, { TV_SKELETON, "Corpses and Eggs" }, { TV_CORPSE, NULL }, { TV_EGG, NULL }, { 0, "" } }; /* * Describe the kind */ static void kind_info(char *buf, char *dam, char *wgt, int *lev, s32b *val, int k) { object_type forge; object_type *q_ptr; object_kind *k_ptr; /* Get local object */ q_ptr = &forge; /* Prepare a fake item */ object_prep(q_ptr, k); /* Obtain the "kind" info */ k_ptr = &k_info[q_ptr->k_idx]; /* It is known */ q_ptr->ident |= (IDENT_KNOWN); /* Cancel bonuses */ q_ptr->to_a = 0; q_ptr->to_h = 0; q_ptr->to_d = 0; if ((k_ptr->tval == TV_WAND) || (k_ptr->tval == TV_STAFF)) { apply_magic(q_ptr, 0, FALSE, FALSE, FALSE, boost::make_optional(0)); } /* Level */ (*lev) = k_ptr->level; /* Value */ (*val) = object_value(q_ptr); /* Hack */ if (!buf || !dam || !wgt) return; /* Description (too brief) */ object_desc_store(buf, q_ptr, FALSE, 0); /* Misc info */ strcpy(dam, ""); /* Damage */ switch (q_ptr->tval) { /* Bows */ case TV_BOW: { break; } /* Ammo */ case TV_SHOT: case TV_BOLT: case TV_ARROW: /* Boomerangs */ case TV_BOOMERANG: /* Weapons */ case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_AXE: case TV_MSTAFF: /* Tools */ case TV_DIGGING: { sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds); break; } /* Armour */ case TV_BOOTS: case TV_GLOVES: case TV_CLOAK: case TV_CROWN: case TV_HELM: case TV_SHIELD: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: { sprintf(dam, "%d", q_ptr->ac); break; } } /* Weight */ sprintf(wgt, "%3ld.%ld", (long int) (q_ptr->weight / 10), (long int) (q_ptr->weight % 10)); } /* * Create a spoiler file for items */ static void spoil_obj_desc(cptr fname) { int i, k, s, t, n = 0; u16b who[200]; char buf[1024]; char wgt[80]; char dam[80]; /* Build the filename */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msg_print("Cannot create spoiler file."); return; } /* Header */ sprintf(buf, "Basic Items Spoilers for %s", get_version_string()); spoiler_underline(buf); spoiler_blanklines(2); /* More Header */ fprintf(fff, "%-45s %8s%7s%5s%9s\n", "Description", "Dam/AC", "Wgt", "Lev", "Cost"); fprintf(fff, "%-45s %8s%7s%5s%9s\n", "----------------------------------------", "------", "---", "---", "----"); /* List the groups */ for (i = 0; TRUE; i++) { /* Write out the group title */ if (group_item[i].name) { /* Hack -- bubble-sort by cost and then level */ for (s = 0; s < n - 1; s++) { for (t = 0; t < n - 1; t++) { int i1 = t; int i2 = t + 1; int e1; int e2; s32b t1; s32b t2; kind_info(NULL, NULL, NULL, &e1, &t1, who[i1]); kind_info(NULL, NULL, NULL, &e2, &t2, who[i2]); if ((t1 > t2) || ((t1 == t2) && (e1 > e2))) { int tmp = who[i1]; who[i1] = who[i2]; who[i2] = tmp; } } } /* Spoil each item */ for (s = 0; s < n; s++) { int e; s32b v; /* Describe the kind */ kind_info(buf, dam, wgt, &e, &v, who[s]); /* Dump it */ fprintf(fff, " %-45s%8s%7s%5d%9ld\n", buf, dam, wgt, e, (long)(v)); } /* Start a new set */ n = 0; /* Notice the end */ if (!group_item[i].tval) break; /* Start a new set */ fprintf(fff, "\n\n%s\n\n", group_item[i].name); } /* Acquire legal item types */ for (k = 1; k < max_k_idx; k++) { object_kind *k_ptr = &k_info[k]; /* Skip wrong tval's */ if (k_ptr->tval != group_item[i].tval) continue; /* Hack -- Skip artifacts */ if (k_ptr->flags3 & (TR3_INSTA_ART | TR3_NORM_ART)) continue; /* Hack -- Skip Ring of Powers */ if (k == 785) continue; /* Save the index */ who[n++] = k; } } /* Check for errors */ if (ferror(fff) || my_fclose(fff)) { msg_print("Cannot close spoiler file."); return; } /* Message */ msg_print("Successfully created a spoiler file."); } /* * Artifact Spoilers by: randy@PICARD.tamu.edu (Randy Hutson) */ /* * Returns a "+" string if a number is non-negative and an empty * string if negative */ #define POSITIZE(v) (((v) >= 0) ? "+" : "") /* * These are used to format the artifact spoiler file. INDENT1 is used * to indent all but the first line of an artifact spoiler. INDENT2 is * used when a line "wraps". (Bladeturner's resistances cause this.) */ #define INDENT1 " " #define INDENT2 " " /* * MAX_LINE_LEN specifies when a line should wrap. */ #define MAX_LINE_LEN 75 /* * Given an array, determine how many elements are in the array */ #define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0])) /* * The artifacts categorized by type */ static grouper group_artifact[] = { { TV_SWORD, "Edged Weapons" }, { TV_POLEARM, "Polearms" }, { TV_HAFTED, "Hafted Weapons" }, { TV_AXE, "Axes" }, { TV_MSTAFF, "Mage Staffs" }, { TV_BOW, "Bows" }, { TV_SHOT, "Ammo" }, { TV_ARROW, NULL }, { TV_BOLT, NULL }, { TV_BOOMERANG, "Boomerangs" }, { TV_INSTRUMENT, "Instruments" }, { TV_SOFT_ARMOR, "Body Armor" }, { TV_HARD_ARMOR, NULL }, { TV_DRAG_ARMOR, NULL }, { TV_CLOAK, "Cloaks" }, { TV_SHIELD, "Shields" }, { TV_HELM, "Helms/Crowns" }, { TV_CROWN, NULL }, { TV_GLOVES, "Gloves" }, { TV_BOOTS, "Boots" }, { TV_DAEMON_BOOK, "Demonic Equipment" }, { TV_LITE, "Light Sources" }, { TV_AMULET, "Amulets" }, { TV_RING, "Rings" }, { TV_TOOL, "Tools" }, { TV_DIGGING, NULL }, { TV_TRAPKIT, "Trapping Kits" }, { 0, NULL } }; /* * Pair together a constant flag with a textual description. * * Used by both "init.c" and "wiz-spo.c". * * Note that it sometimes more efficient to actually make an array * of textual names, where entry 'N' is assumed to be paired with * the flag whose value is "1L << N", but that requires hard-coding. */ typedef struct flag_desc flag_desc; struct flag_desc { const u32b flag; const char *const desc; }; /* * These are used for "+3 to STR, DEX", etc. These are separate from * the other pval affected traits to simplify the case where an object * affects all stats. In this case, "All stats" is used instead of * listing each stat individually. */ static flag_desc stat_flags_desc[] = { { TR1_STR, "STR" }, { TR1_INT, "INT" }, { TR1_WIS, "WIS" }, { TR1_DEX, "DEX" }, { TR1_CON, "CON" }, { TR1_CHR, "CHR" } }; /* * Besides stats, these are the other player traits * which may be affected by an object's pval */ static flag_desc pval_flags1_desc[] = { { TR1_STEALTH, "Stealth" }, { TR1_SEARCH, "Searching" }, { TR1_INFRA, "Infravision" }, { TR1_TUNNEL, "Tunnelling" }, { TR1_BLOWS, "Attacks" }, { TR1_SPEED, "Speed" } }; /* * Slaying preferences for weapons */ static flag_desc slay_flags_desc[] = { { TR1_SLAY_ANIMAL, "Animal" }, { TR1_SLAY_EVIL, "Evil" }, { TR1_SLAY_UNDEAD, "Undead" }, { TR1_SLAY_DEMON, "Demon" }, { TR1_SLAY_ORC, "Orc" }, { TR1_SLAY_TROLL, "Troll" }, { TR1_SLAY_GIANT, "Giant" }, { TR1_SLAY_DRAGON, "Dragon" }, { TR1_KILL_DRAGON, "Xdragon" } }; /* * Elemental brands for weapons * * Clearly, TR1_IMPACT is a bit out of place here. To simplify * coding, it has been included here along with the elemental * brands. It does seem to fit in with the brands and slaying * more than the miscellaneous section. */ static flag_desc brand_flags_desc[] = { { TR1_BRAND_ACID, "Acid Brand" }, { TR1_BRAND_ELEC, "Lightning Brand" }, { TR1_BRAND_FIRE, "Flame Tongue" }, { TR1_BRAND_COLD, "Frost Brand" }, { TR1_BRAND_POIS, "Poisoned" }, { TR1_CHAOTIC, "Mark of Chaos" }, { TR1_VAMPIRIC, "Vampiric" }, { TR1_IMPACT, "Earthquake impact on hit" }, { TR1_VORPAL, "Very sharp" }, }; /* * The 15 resistables */ static const flag_desc resist_flags_desc[] = { { TR2_RES_ACID, "Acid" }, { TR2_RES_ELEC, "Lightning" }, { TR2_RES_FIRE, "Fire" }, { TR2_RES_COLD, "Cold" }, { TR2_RES_POIS, "Poison" }, { TR2_RES_FEAR, "Fear"}, { TR2_RES_LITE, "Light" }, { TR2_RES_DARK, "Dark" }, { TR2_RES_BLIND, "Blindness" }, { TR2_RES_CONF, "Confusion" }, { TR2_RES_SOUND, "Sound" }, { TR2_RES_SHARDS, "Shards" }, { TR2_RES_NETHER, "Nether" }, { TR2_RES_NEXUS, "Nexus" }, { TR2_RES_CHAOS, "Chaos" }, { TR2_RES_DISEN, "Disenchantment" }, }; /* * Elemental immunities (along with poison) */ static const flag_desc immune_flags_desc[] = { { TR2_IM_ACID, "Acid" }, { TR2_IM_ELEC, "Lightning" }, { TR2_IM_FIRE, "Fire" }, { TR2_IM_COLD, "Cold" }, }; /* * Sustain stats - these are given their "own" line in the * spoiler file, mainly for simplicity */ static const flag_desc sustain_flags_desc[] = { { TR2_SUST_STR, "STR" }, { TR2_SUST_INT, "INT" }, { TR2_SUST_WIS, "WIS" }, { TR2_SUST_DEX, "DEX" }, { TR2_SUST_CON, "CON" }, { TR2_SUST_CHR, "CHR" }, }; /* * Miscellaneous magic given by an object's "flags2" field */ static const flag_desc misc_flags2_desc[] = { { TR2_REFLECT, "Reflection" }, { TR2_FREE_ACT, "Free Action" }, { TR2_HOLD_LIFE, "Hold Life" }, }; /* * Miscellaneous magic given by an object's "flags3" field * * Note that cursed artifacts and objects with permanent light * are handled "directly" -- see analyze_misc_magic() */ static const flag_desc misc_flags3_desc[] = { { TR3_SH_FIRE, "Fiery Aura" }, { TR3_SH_ELEC, "Electric Aura" }, { TR3_NO_TELE, "Prevent Teleportation" }, { TR3_NO_MAGIC, "Anti-Magic" }, { TR3_WRAITH, "Wraith Form" }, { TR3_FEATHER, "Levitation" }, { TR3_SEE_INVIS, "See Invisible" }, { TR3_SLOW_DIGEST, "Slow Digestion" }, { TR3_REGEN, "Regeneration" }, { TR3_XTRA_SHOTS, "+1 Extra Shot" }, /* always +1? */ { TR3_DRAIN_EXP, "Drains Experience" }, { TR3_AGGRAVATE, "Aggravates" }, { TR3_BLESSED, "Blessed Blade" }, }; /* * A special type used just for dealing with pvals */ typedef struct { /* * This will contain a string such as "+2", "-10", etc. */ char pval_desc[12]; /* * A list of various player traits affected by an object's pval such * as stats, speed, stealth, etc. "Extra attacks" is NOT included in * this list since it will probably be desirable to format its * description differently. * * Note that room need only be reserved for the number of stats - 1 * since the description "All stats" is used if an object affects all * all stats. Also, room must be reserved for a sentinel NULL pointer. * * This will be a list such as ["STR", "DEX", "Stealth", NULL] etc. * * This list includes extra attacks, for simplicity. */ cptr pval_affects[N_ELEMENTS(stat_flags_desc) - 1 + N_ELEMENTS(pval_flags1_desc) + 1]; } pval_info_type; /* * An "object analysis structure" * * It will be filled with descriptive strings detailing an object's * various magical powers. The "ignore X" traits are not noted since * all artifacts ignore "normal" destruction. */ typedef struct { /* "The Longsword Dragonsmiter (6d4) (+20, +25)" */ char description[160]; /* Description of what is affected by an object's pval */ pval_info_type pval_info; /* A list of an object's slaying preferences */ cptr slays[N_ELEMENTS(slay_flags_desc) + 1]; /* A list if an object's elemental brands */ cptr brands[N_ELEMENTS(brand_flags_desc) + 1]; /* A list of immunities granted by an object */ cptr immunities[N_ELEMENTS(immune_flags_desc) + 1]; /* A list of resistances granted by an object */ cptr resistances[N_ELEMENTS(resist_flags_desc) + 1]; /* A list of stats sustained by an object */ cptr sustains[N_ELEMENTS(sustain_flags_desc) - 1 + 1]; /* A list of various magical qualities an object may have */ cptr misc_magic[N_ELEMENTS(misc_flags2_desc) + N_ELEMENTS(misc_flags3_desc) + 1 /* Permanent Light */ + 1 /* type of curse */ + 1]; /* sentinel NULL */ /* A string describing an artifact's activation */ cptr activation; /* "Level 20, Rarity 30, 3.0 lbs, 20000 Gold" */ char misc_desc[80]; } obj_desc_list; /* * This function does most of the actual "analysis". Given a set of bit flags * (which will be from one of the flags fields from the object in question), * a "flag description structure", a "description list", and the number of * elements in the "flag description structure", this function sets the * "description list" members to the appropriate descriptions contained in * the "flag description structure". * * The possibly updated description pointer is returned. */ static cptr *spoiler_flag_aux(const u32b art_flags, const flag_desc *flag_ptr, cptr *desc_ptr, const int n_elmnts) { int i; for (i = 0; i < n_elmnts; ++i) { if (art_flags & flag_ptr[i].flag) { *desc_ptr++ = flag_ptr[i].desc; } } return desc_ptr; } /* * Acquire a "basic" description "The Cloak of Death [1,+10]" */ static void analyze_general (object_type *o_ptr, char *desc_ptr) { /* Get a "useful" description of the object */ object_desc_store(desc_ptr, o_ptr, TRUE, 1); } /* * List "player traits" altered by an artifact's pval. These include stats, * speed, infravision, tunnelling, stealth, searching, and extra attacks. */ static void analyze_pval (object_type *o_ptr, pval_info_type *p_ptr) { const u32b all_stats = (TR1_STR | TR1_INT | TR1_WIS | TR1_DEX | TR1_CON | TR1_CHR); u32b f1, f2, f3, f4, f5, esp; cptr *affects_list; /* If pval == 0, there is nothing to do. */ if (!o_ptr->pval) { /* An "empty" pval description indicates that pval == 0 */ p_ptr->pval_desc[0] = '\0'; return; } /* Extract the flags */ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); affects_list = p_ptr->pval_affects; /* Create the "+N" string */ sprintf(p_ptr->pval_desc, "%s%ld", POSITIZE(o_ptr->pval), (long int) o_ptr->pval); /* First, check to see if the pval affects all stats */ if ((f1 & all_stats) == all_stats) { *affects_list++ = "All stats"; } /* Are any stats affected? */ else if (f1 & all_stats) { affects_list = spoiler_flag_aux(f1, stat_flags_desc, affects_list, N_ELEMENTS(stat_flags_desc)); } /* And now the "rest" */ affects_list = spoiler_flag_aux(f1, pval_flags1_desc, affects_list, N_ELEMENTS(pval_flags1_desc)); /* Terminate the description list */ *affects_list = NULL; } /* Note the slaying specialties of a weapon */ static void analyze_slay (object_type *o_ptr, cptr *slay_list) { u32b f1, f2, f3, f4, f5, esp; object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); slay_list = spoiler_flag_aux(f1, slay_flags_desc, slay_list, N_ELEMENTS(slay_flags_desc)); /* Terminate the description list */ *slay_list = NULL; } /* Note an object's elemental brands */ static void analyze_brand (object_type *o_ptr, cptr *brand_list) { u32b f1, f2, f3, f4, f5, esp; object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); brand_list = spoiler_flag_aux(f1, brand_flags_desc, brand_list, N_ELEMENTS(brand_flags_desc)); /* Terminate the description list */ *brand_list = NULL; } /* Note the resistances granted by an object */ static void analyze_resist (object_type *o_ptr, cptr *resist_list) { u32b f1, f2, f3, f4, f5, esp; object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); resist_list = spoiler_flag_aux(f2, resist_flags_desc, resist_list, N_ELEMENTS(resist_flags_desc)); /* Terminate the description list */ *resist_list = NULL; } /* Note the immunities granted by an object */ static void analyze_immune (object_type *o_ptr, cptr *immune_list) { u32b f1, f2, f3, f4, f5, esp; object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); immune_list = spoiler_flag_aux(f2, immune_flags_desc, immune_list, N_ELEMENTS(immune_flags_desc)); /* Terminate the description list */ *immune_list = NULL; } /* Note which stats an object sustains */ static void analyze_sustains (object_type *o_ptr, cptr *sustain_list) { const u32b all_sustains = (TR2_SUST_STR | TR2_SUST_INT | TR2_SUST_WIS | TR2_SUST_DEX | TR2_SUST_CON | TR2_SUST_CHR); u32b f1, f2, f3, f4, f5, esp; object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); /* Simplify things if an item sustains all stats */ if ((f2 & all_sustains) == all_sustains) { *sustain_list++ = "All stats"; } /* Should we bother? */ else if ((f2 & all_sustains)) { sustain_list = spoiler_flag_aux(f2, sustain_flags_desc, sustain_list, N_ELEMENTS(sustain_flags_desc)); } /* Terminate the description list */ *sustain_list = NULL; } /* * Note miscellaneous powers bestowed by an artifact such as see invisible, * free action, permanent light, etc. */ static void analyze_misc_magic (object_type *o_ptr, cptr *misc_list) { u32b f1, f2, f3, f4, f5, esp; int radius = 0; object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); misc_list = spoiler_flag_aux(f2, misc_flags2_desc, misc_list, N_ELEMENTS(misc_flags2_desc)); misc_list = spoiler_flag_aux(f3, misc_flags3_desc, misc_list, N_ELEMENTS(misc_flags3_desc)); /* * Glowing artifacts -- small radius light. */ if (f3 & TR3_LITE1) radius++; if (f4 & TR4_LITE2) radius += 2; if (f4 & TR4_LITE3) radius += 3; if (f4 & TR4_FUEL_LITE) { *misc_list++ = format("It provides light (radius %d) forever.", radius); } else { *misc_list++ = format("It provides light (radius %d) when fueled.", radius); } /* * Handle cursed objects here to avoid redundancies such as noting * that a permanently cursed object is heavily cursed as well as * being "lightly cursed". */ if (cursed_p(o_ptr)) { if (f3 & (TR3_TY_CURSE)) { *misc_list++ = "Ancient Curse"; } if (f3 & (TR3_PERMA_CURSE)) { *misc_list++ = "Permanently Cursed"; } else if (f3 & (TR3_HEAVY_CURSE)) { *misc_list++ = "Heavily Cursed"; } else { *misc_list++ = "Cursed"; } } /* Terminate the description list */ *misc_list = NULL; } /* * Determine the minimum depth an artifact can appear, its rarity, its weight, * and its value in gold pieces */ static void analyze_misc (object_type *o_ptr, char *misc_desc) { artifact_type *a_ptr = &a_info[o_ptr->name1]; sprintf(misc_desc, "Level %u, Rarity %u, %d.%d lbs, %ld Gold", a_ptr->level, a_ptr->rarity, a_ptr->weight / 10, a_ptr->weight % 10, (long int) a_ptr->cost); } /* * Fill in an object description structure for a given object */ static void object_analyze(object_type *o_ptr, obj_desc_list *desc_ptr) { analyze_general(o_ptr, desc_ptr->description); analyze_pval(o_ptr, &desc_ptr->pval_info); analyze_brand(o_ptr, desc_ptr->brands); analyze_slay(o_ptr, desc_ptr->slays); analyze_immune(o_ptr, desc_ptr->immunities); analyze_resist(o_ptr, desc_ptr->resistances); analyze_sustains(o_ptr, desc_ptr->sustains); analyze_misc_magic(o_ptr, desc_ptr->misc_magic); analyze_misc(o_ptr, desc_ptr->misc_desc); desc_ptr->activation = item_activation(o_ptr, 0); } static void print_header(void) { char buf[80]; sprintf(buf, "Artifact Spoilers for %s", get_version_string()); spoiler_underline(buf); } /* * This is somewhat ugly. * * Given a header ("Resist", e.g.), a list ("Fire", "Cold", Acid", e.g.), * and a separator character (',', e.g.), write the list to the spoiler file * in a "nice" format, such as: * * Resist Fire, Cold, Acid * * That was a simple example, but when the list is long, a line wrap * should occur, and this should induce a new level of indention if * a list is being spread across lines. So for example, Bladeturner's * list of resistances should look something like this * * Resist Acid, Lightning, Fire, Cold, Poison, Light, Dark, Blindness, * Confusion, Sound, Shards, Nether, Nexus, Chaos, Disenchantment * * However, the code distinguishes between a single list of many items vs. * many lists. (The separator is used to make this determination.) A single * list of many items will not cause line wrapping (since there is no * apparent reason to do so). So the lists of Ulmo's miscellaneous traits * might look like this: * * Free Action; Hold Life; See Invisible; Slow Digestion; Regeneration * Blessed Blade * * So comparing the two, "Regeneration" has no trailing separator and * "Blessed Blade" was not indented. (Also, Ulmo's lists have no headers, * but that's not relevant to line wrapping and indention.) */ /* ITEM_SEP separates items within a list */ #define ITEM_SEP ',' /* LIST_SEP separates lists */ #define LIST_SEP ';' /* Create a spoiler file entry for an artifact */ static void spoiler_print_art(obj_desc_list *art_ptr, int name1, int set, object_type *o_ptr) { /* Don't indent the first line */ fprintf(fff, "%s\n ", art_ptr->description); text_out_indent = 4; object_out_desc(o_ptr, fff, FALSE, TRUE); text_out_indent = 0; /* End with the miscellaneous facts */ fprintf(fff, "%s%s\n\n", INDENT1, art_ptr->misc_desc); } /* * Hack -- Create a "forged" artifact */ static bool_ make_fake_artifact(object_type *o_ptr, int name1) { int i; int cur; artifact_type *a_ptr = &a_info[name1]; /* Ignore "empty" artifacts */ if (!a_ptr->name) return FALSE; /* Acquire the "kind" index */ i = lookup_kind(a_ptr->tval, a_ptr->sval); /* Oops */ if (!i) return (FALSE); /* Create the artifact */ object_prep(o_ptr, i); /* Save the name */ o_ptr->name1 = name1; /* Keep the One Ring untouched by apply_magic */ if (name1 != ART_POWER) { cur = a_ptr->cur_num; apply_magic(o_ptr, -1, TRUE, TRUE, TRUE); a_ptr->cur_num = cur; } else { o_ptr->pval = a_ptr->pval; } /* Success */ return (TRUE); } /* * Create a spoiler file for artifacts */ static void spoil_artifact(cptr fname) { int i, j; object_type forge; object_type *q_ptr; obj_desc_list artifact; char buf[1024]; /* Build the filename */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msg_print("Cannot create spoiler file."); return; } /* Dump the header */ print_header(); /* List the artifacts by tval */ for (i = 0; group_artifact[i].tval; i++) { /* Write out the group title */ if (group_artifact[i].name) { spoiler_blanklines(2); spoiler_underline(group_artifact[i].name); spoiler_blanklines(1); } /* Now search through all of the artifacts */ for (j = 1; j < max_a_idx; ++j) { artifact_type *a_ptr = &a_info[j]; /* We only want objects in the current group */ if (a_ptr->tval != group_artifact[i].tval) continue; /* Get local object */ q_ptr = &forge; /* Wipe the object */ object_wipe(q_ptr); /* Attempt to "forge" the artifact */ if (!make_fake_artifact(q_ptr, j)) continue; /* Aware and Known */ object_known(q_ptr); /* Mark the item as fully known */ q_ptr->ident |= (IDENT_MENTAL); /* Analyze the artifact */ object_analyze(q_ptr, &artifact); /* Write out the artifact description to the spoiler file */ spoiler_print_art(&artifact, j, a_ptr->set, q_ptr); } } /* Check for errors */ if (ferror(fff) || my_fclose(fff)) { msg_print("Cannot close spoiler file."); return; } /* Message */ msg_print("Successfully created a spoiler file."); } /* * Create a spoiler file for monsters -BEN- */ static void spoil_mon_desc(cptr fname) { char buf[1024]; char nam[80]; char lev[80]; char rar[80]; char spd[80]; char ac[80]; char hp[80]; char exp[80]; /* Build the filename */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msg_print("Cannot create spoiler file."); return; } /* Allocate the "who" array */ std::vector who; /* Dump the header */ sprintf(buf, "Monster Spoilers for %s", get_version_string()); spoiler_underline(buf); spoiler_blanklines(2); /* Dump the header */ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n", "Name", "Lev", "Rar", "Spd", "Hp", "Ac", "Visual Info"); fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n", "----", "---", "---", "---", "--", "--", "-----------"); /* Scan the monsters */ for (size_t i = 1; i < max_r_idx; i++) { monster_race *r_ptr = &r_info[i]; /* Use that monster */ if (r_ptr->name) { who.push_back(i); } } /* Scan again */ for (auto const who_i : who) { monster_race *r_ptr = &r_info[who_i]; /* Get the "name" */ if (r_ptr->flags1 & (RF1_UNIQUE)) { sprintf(nam, "[U] %s", r_ptr->name); } else { sprintf(nam, "The %s", r_ptr->name); } /* Level */ sprintf(lev, "%d", r_ptr->level); /* Rarity */ sprintf(rar, "%d", r_ptr->rarity); /* Speed */ if (r_ptr->speed >= 110) { sprintf(spd, "+%d", (r_ptr->speed - 110)); } else { sprintf(spd, "-%d", (110 - r_ptr->speed)); } /* Armor Class */ sprintf(ac, "%d", r_ptr->ac); /* Hitpoints */ if ((r_ptr->flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1)) { sprintf(hp, "%d", r_ptr->hdice * r_ptr->hside); } else { sprintf(hp, "%dd%d", r_ptr->hdice, r_ptr->hside); } /* Experience */ sprintf(exp, "%ld", (long)(r_ptr->mexp)); /* Hack -- use visual instead */ sprintf(exp, "%s '%c'", attr_to_text(r_ptr->d_attr), r_ptr->d_char); /* Dump the info */ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n", nam, lev, rar, spd, hp, ac, exp); } /* End it */ fprintf(fff, "\n"); /* Check for errors */ if (ferror(fff) || my_fclose(fff)) { msg_print("Cannot close spoiler file."); return; } /* Worked */ msg_print("Successfully created a spoiler file."); } /* * Monster spoilers by: smchorse@ringer.cs.utsa.edu (Shawn McHorse) * * Primarily based on code already in mon-desc.c, mostly by -BEN- */ /* * Pronoun arrays */ static cptr wd_che[3] = { "It", "He", "She" }; static cptr wd_lhe[3] = { "it", "he", "she" }; /* * Create a spoiler file for monsters (-SHAWN-) */ static void spoil_mon_info(cptr fname) { char buf[1024]; int msex, vn, i, j, k, n; bool_ breath, magic, sin; cptr p, q; cptr vp[64]; u32b flags1, flags2, flags3, flags4, flags5, flags6, flags9; /* Build the filename */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname); /* Open the file */ fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msg_print("Cannot create spoiler file."); return; } /* Dump the header */ sprintf(buf, "Monster Spoilers for %s", get_version_string()); spoiler_underline(buf); spoiler_blanklines(2); /* * List all monsters in order. */ for (n = 1; n < max_r_idx; n++) { monster_race *r_ptr = &r_info[n]; /* Extract the flags */ flags1 = r_ptr->flags1; flags2 = r_ptr->flags2; flags3 = r_ptr->flags3; flags4 = r_ptr->flags4; flags5 = r_ptr->flags5; flags6 = r_ptr->flags6; flags9 = r_ptr->flags9; breath = FALSE; magic = FALSE; /* Extract a gender (if applicable) */ if (flags1 & (RF1_FEMALE)) msex = 2; else if (flags1 & (RF1_MALE)) msex = 1; else msex = 0; /* Prefix */ if (flags1 & (RF1_UNIQUE)) { spoil_out("[U] "); } else { spoil_out("The "); } /* Name */ sprintf(buf, "%s (", r_ptr->name); /* ---)--- */ spoil_out(buf); /* Color */ spoil_out(attr_to_text(r_ptr->d_attr)); /* Symbol --(-- */ sprintf(buf, " '%c')\n", r_ptr->d_char); spoil_out(buf); /* Indent */ sprintf(buf, "=== "); spoil_out(buf); /* Number */ sprintf(buf, "Num:%d ", n); spoil_out(buf); /* Level */ sprintf(buf, "Lev:%d ", r_ptr->level); spoil_out(buf); /* Rarity */ sprintf(buf, "Rar:%d ", r_ptr->rarity); spoil_out(buf); /* Speed */ if (r_ptr->speed >= 110) { sprintf(buf, "Spd:+%d ", (r_ptr->speed - 110)); } else { sprintf(buf, "Spd:-%d ", (110 - r_ptr->speed)); } spoil_out(buf); /* Hitpoints */ if ((flags1 & (RF1_FORCE_MAXHP)) || (r_ptr->hside == 1)) { sprintf(buf, "Hp:%d ", r_ptr->hdice * r_ptr->hside); } else { sprintf(buf, "Hp:%dd%d ", r_ptr->hdice, r_ptr->hside); } spoil_out(buf); /* Armor Class */ sprintf(buf, "Ac:%d ", r_ptr->ac); spoil_out(buf); /* Experience */ sprintf(buf, "Exp:%ld\n", (long)(r_ptr->mexp)); spoil_out(buf); /* Describe */ spoil_out(r_ptr->text); spoil_out(" "); spoil_out("This"); if (flags2 & (RF2_ELDRITCH_HORROR)) spoil_out (" sanity-blasting"); if (flags3 & (RF3_ANIMAL)) spoil_out(" natural"); if (flags3 & (RF3_EVIL)) spoil_out(" evil"); if (flags3 & (RF3_GOOD)) spoil_out(" good"); if (flags3 & (RF3_UNDEAD)) spoil_out(" undead"); if (flags3 & (RF3_DRAGON)) spoil_out(" dragon"); else if (flags3 & (RF3_DEMON)) spoil_out(" demon"); else if (flags3 & (RF3_GIANT)) spoil_out(" giant"); else if (flags3 & (RF3_TROLL)) spoil_out(" troll"); else if (flags3 & (RF3_ORC)) spoil_out(" orc"); else if (flags3 & (RF3_THUNDERLORD)) spoil_out (" Thunderlord"); else spoil_out(" creature"); spoil_out(" moves"); if ((flags1 & (RF1_RAND_50)) && (flags1 & (RF1_RAND_25))) { spoil_out(" extremely erratically"); } else if (flags1 & (RF1_RAND_50)) { spoil_out(" somewhat erratically"); } else if (flags1 & (RF1_RAND_25)) { spoil_out(" a bit erratically"); } else { spoil_out(" normally"); } if (flags1 & (RF1_NEVER_MOVE)) { spoil_out(", but does not deign to chase intruders"); } spoil_out(". "); if (!r_ptr->level || (flags1 & (RF1_FORCE_DEPTH))) { sprintf(buf, "%s is never found out of depth. ", wd_che[msex]); spoil_out(buf); } if (flags1 & (RF1_FORCE_SLEEP)) { sprintf(buf, "%s is always created sluggish. ", wd_che[msex]); spoil_out(buf); } if (flags2 & (RF2_AURA_FIRE)) { sprintf(buf, "%s is surrounded by flames. ", wd_che[msex]); spoil_out(buf); } if (flags2 & (RF2_AURA_ELEC)) { sprintf(buf, "%s is surrounded by electricity. ", wd_che[msex]); spoil_out(buf); } if (flags2 & (RF2_REFLECTING)) { sprintf(buf, "%s reflects bolt spells. ", wd_che[msex]); spoil_out(buf); } if (flags1 & (RF1_ESCORT)) { sprintf(buf, "%s usually appears with ", wd_che[msex]); spoil_out(buf); if (flags1 & (RF1_ESCORTS)) spoil_out("escorts. "); else spoil_out("an escort. "); } if ((flags1 & (RF1_FRIEND)) || (flags1 & (RF1_FRIENDS))) { sprintf(buf, "%s usually appears in groups. ", wd_che[msex]); spoil_out(buf); } /* Collect innate attacks */ vn = 0; if (flags4 & (RF4_SHRIEK)) vp[vn++] = "shriek for help"; if (flags4 & (RF4_ROCKET)) vp[vn++] = "shoot a rocket"; if (flags4 & (RF4_ARROW_1)) vp[vn++] = "fire arrows"; if (flags4 & (RF4_ARROW_2)) vp[vn++] = "fire arrows"; if (flags4 & (RF4_ARROW_3)) vp[vn++] = "fire missiles"; if (flags4 & (RF4_ARROW_4)) vp[vn++] = "fire missiles"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" may "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect breaths */ vn = 0; if (flags4 & (RF4_BR_ACID)) vp[vn++] = "acid"; if (flags4 & (RF4_BR_ELEC)) vp[vn++] = "lightning"; if (flags4 & (RF4_BR_FIRE)) vp[vn++] = "fire"; if (flags4 & (RF4_BR_COLD)) vp[vn++] = "frost"; if (flags4 & (RF4_BR_POIS)) vp[vn++] = "poison"; if (flags4 & (RF4_BR_NETH)) vp[vn++] = "nether"; if (flags4 & (RF4_BR_LITE)) vp[vn++] = "light"; if (flags4 & (RF4_BR_DARK)) vp[vn++] = "darkness"; if (flags4 & (RF4_BR_CONF)) vp[vn++] = "confusion"; if (flags4 & (RF4_BR_SOUN)) vp[vn++] = "sound"; if (flags4 & (RF4_BR_CHAO)) vp[vn++] = "chaos"; if (flags4 & (RF4_BR_DISE)) vp[vn++] = "disenchantment"; if (flags4 & (RF4_BR_NEXU)) vp[vn++] = "nexus"; if (flags4 & (RF4_BR_TIME)) vp[vn++] = "time"; if (flags4 & (RF4_BR_INER)) vp[vn++] = "inertia"; if (flags4 & (RF4_BR_GRAV)) vp[vn++] = "gravity"; if (flags4 & (RF4_BR_SHAR)) vp[vn++] = "shards"; if (flags4 & (RF4_BR_PLAS)) vp[vn++] = "plasma"; if (flags4 & (RF4_BR_WALL)) vp[vn++] = "force"; if (flags4 & (RF4_BR_MANA)) vp[vn++] = "mana"; if (flags4 & (RF4_BR_NUKE)) vp[vn++] = "toxic waste"; if (flags4 & (RF4_BR_DISI)) vp[vn++] = "disintegration"; if (vn) { breath = TRUE; spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" may breathe "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } if (flags2 & (RF2_POWERFUL)) spoil_out(" powerfully"); } /* Collect spells */ vn = 0; if (flags5 & (RF5_BA_ACID)) vp[vn++] = "produce acid balls"; if (flags5 & (RF5_BA_ELEC)) vp[vn++] = "produce lightning balls"; if (flags5 & (RF5_BA_FIRE)) vp[vn++] = "produce fire balls"; if (flags5 & (RF5_BA_COLD)) vp[vn++] = "produce frost balls"; if (flags5 & (RF5_BA_POIS)) vp[vn++] = "produce poison balls"; if (flags5 & (RF5_BA_NETH)) vp[vn++] = "produce nether balls"; if (flags5 & (RF5_BA_WATE)) vp[vn++] = "produce water balls"; if (flags4 & (RF4_BA_NUKE)) vp[vn++] = "produce balls of radiation"; if (flags5 & (RF5_BA_MANA)) vp[vn++] = "produce mana storms"; if (flags5 & (RF5_BA_DARK)) vp[vn++] = "produce darkness storms"; if (flags4 & (RF4_BA_CHAO)) vp[vn++] = "invoke raw Chaos"; if (flags6 & (RF6_HAND_DOOM)) vp[vn++] = "invoke the Hand of Doom"; if (flags5 & (RF5_DRAIN_MANA)) vp[vn++] = "drain mana"; if (flags5 & (RF5_MIND_BLAST)) vp[vn++] = "cause mind blasting"; if (flags5 & (RF5_BRAIN_SMASH)) vp[vn++] = "cause brain smashing"; if (flags5 & (RF5_CAUSE_1)) vp[vn++] = "cause light wounds and cursing"; if (flags5 & (RF5_CAUSE_2)) vp[vn++] = "cause serious wounds and cursing"; if (flags5 & (RF5_CAUSE_3)) vp[vn++] = "cause critical wounds and cursing"; if (flags5 & (RF5_CAUSE_4)) vp[vn++] = "cause mortal wounds"; if (flags5 & (RF5_BO_ACID)) vp[vn++] = "produce acid bolts"; if (flags5 & (RF5_BO_ELEC)) vp[vn++] = "produce lightning bolts"; if (flags5 & (RF5_BO_FIRE)) vp[vn++] = "produce fire bolts"; if (flags5 & (RF5_BO_COLD)) vp[vn++] = "produce frost bolts"; if (flags5 & (RF5_BO_POIS)) vp[vn++] = "produce poison bolts"; if (flags5 & (RF5_BO_NETH)) vp[vn++] = "produce nether bolts"; if (flags5 & (RF5_BO_WATE)) vp[vn++] = "produce water bolts"; if (flags5 & (RF5_BO_MANA)) vp[vn++] = "produce mana bolts"; if (flags5 & (RF5_BO_PLAS)) vp[vn++] = "produce plasma bolts"; if (flags5 & (RF5_BO_ICEE)) vp[vn++] = "produce ice bolts"; if (flags5 & (RF5_MISSILE)) vp[vn++] = "produce magic missiles"; if (flags5 & (RF5_SCARE)) vp[vn++] = "terrify"; if (flags5 & (RF5_BLIND)) vp[vn++] = "blind"; if (flags5 & (RF5_CONF)) vp[vn++] = "confuse"; if (flags5 & (RF5_SLOW)) vp[vn++] = "slow"; if (flags5 & (RF5_HOLD)) vp[vn++] = "paralyse"; if (flags6 & (RF6_HASTE)) vp[vn++] = "haste-self"; if (flags6 & (RF6_HEAL)) vp[vn++] = "heal-self"; if (flags6 & (RF6_BLINK)) vp[vn++] = "blink-self"; if (flags6 & (RF6_TPORT)) vp[vn++] = "teleport-self"; if (flags6 & (RF6_S_BUG)) vp[vn++] = "summon software bugs"; if (flags6 & (RF6_S_RNG)) vp[vn++] = "summon RNGs"; if (flags6 & (RF6_TELE_TO)) vp[vn++] = "teleport to"; if (flags6 & (RF6_TELE_AWAY)) vp[vn++] = "teleport away"; if (flags6 & (RF6_TELE_LEVEL)) vp[vn++] = "teleport level"; if (flags6 & (RF6_DARKNESS)) vp[vn++] = "create darkness"; if (flags6 & (RF6_TRAPS)) vp[vn++] = "create traps"; if (flags6 & (RF6_FORGET)) vp[vn++] = "cause amnesia"; if (flags6 & (RF6_RAISE_DEAD)) vp[vn++] = "raise dead"; if (flags6 & (RF6_S_THUNDERLORD)) vp[vn++] = "summon a thunderlord"; if (flags6 & (RF6_S_MONSTER)) vp[vn++] = "summon a monster"; if (flags6 & (RF6_S_MONSTERS)) vp[vn++] = "summon monsters"; if (flags6 & (RF6_S_KIN)) vp[vn++] = "summon aid"; if (flags6 & (RF6_S_ANT)) vp[vn++] = "summon ants"; if (flags6 & (RF6_S_SPIDER)) vp[vn++] = "summon spiders"; if (flags6 & (RF6_S_HOUND)) vp[vn++] = "summon hounds"; if (flags6 & (RF6_S_HYDRA)) vp[vn++] = "summon hydras"; if (flags6 & (RF6_S_ANGEL)) vp[vn++] = "summon an angel"; if (flags6 & (RF6_S_DEMON)) vp[vn++] = "summon a demon"; if (flags6 & (RF6_S_UNDEAD)) vp[vn++] = "summon an undead"; if (flags6 & (RF6_S_DRAGON)) vp[vn++] = "summon a dragon"; if (flags4 & (RF4_S_ANIMAL)) vp[vn++] = "summon animal"; if (flags6 & (RF6_S_ANIMALS)) vp[vn++] = "summon animals"; if (flags6 & (RF6_S_HI_UNDEAD)) vp[vn++] = "summon greater undead"; if (flags6 & (RF6_S_HI_DRAGON)) vp[vn++] = "summon ancient dragons"; if (flags6 & (RF6_S_HI_DEMON)) vp[vn++] = "summon greater demons"; if (flags6 & (RF6_S_WRAITH)) vp[vn++] = "summon Ringwraith"; if (flags6 & (RF6_S_UNIQUE)) vp[vn++] = "summon unique monsters"; if (vn) { magic = TRUE; if (breath) { spoil_out(", and is also"); } else { spoil_out(wd_che[msex]); spoil_out(" is"); } spoil_out(" magical, casting spells"); if (flags2 & (RF2_SMART)) spoil_out(" intelligently"); for (i = 0; i < vn; i++) { if (!i) spoil_out(" which "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } } if (breath || magic) { int times = r_ptr->freq_inate + r_ptr->freq_spell; sprintf(buf, "; 1 time in %d. ", 200 / ((times) ? times : 1)); spoil_out(buf); } /* Collect special abilities. */ vn = 0; if (flags2 & (RF2_OPEN_DOOR)) vp[vn++] = "open doors"; if (flags2 & (RF2_BASH_DOOR)) vp[vn++] = "bash down doors"; if (flags2 & (RF2_PASS_WALL)) vp[vn++] = "pass through walls"; if (flags2 & (RF2_KILL_WALL)) vp[vn++] = "bore through walls"; if (flags2 & (RF2_MOVE_BODY)) vp[vn++] = "push past weaker monsters"; if (flags2 & (RF2_KILL_BODY)) vp[vn++] = "destroy weaker monsters"; if (flags2 & (RF2_TAKE_ITEM)) vp[vn++] = "pick up objects"; if (flags2 & (RF2_KILL_ITEM)) vp[vn++] = "destroy objects"; if (flags9 & (RF9_HAS_LITE)) vp[vn++] = "illuminate the dungeon"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" can "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } if (flags2 & (RF2_INVISIBLE)) { spoil_out(wd_che[msex]); spoil_out(" is invisible. "); } if (flags2 & (RF2_COLD_BLOOD)) { spoil_out(wd_che[msex]); spoil_out(" is cold blooded. "); } if (flags2 & (RF2_EMPTY_MIND)) { spoil_out(wd_che[msex]); spoil_out(" is not detected by telepathy. "); } if (flags2 & (RF2_WEIRD_MIND)) { spoil_out(wd_che[msex]); spoil_out(" is rarely detected by telepathy. "); } if (flags4 & (RF4_MULTIPLY)) { spoil_out(wd_che[msex]); spoil_out(" breeds explosively. "); } if (flags2 & (RF2_REGENERATE)) { spoil_out(wd_che[msex]); spoil_out(" regenerates quickly. "); } /* Collect susceptibilities */ vn = 0; if (flags3 & (RF3_HURT_ROCK)) vp[vn++] = "rock remover"; if (flags3 & (RF3_HURT_LITE)) vp[vn++] = "bright light"; if (flags3 & (RF3_SUSCEP_FIRE)) vp[vn++] = "fire"; if (flags3 & (RF3_SUSCEP_COLD)) vp[vn++] = "cold"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" is hurt by "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect immunities */ vn = 0; if (flags3 & (RF3_IM_ACID)) vp[vn++] = "acid"; if (flags3 & (RF3_IM_ELEC)) vp[vn++] = "lightning"; if (flags3 & (RF3_IM_FIRE)) vp[vn++] = "fire"; if (flags3 & (RF3_IM_COLD)) vp[vn++] = "cold"; if (flags3 & (RF3_IM_POIS)) vp[vn++] = "poison"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" resists "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect resistances */ vn = 0; if (flags3 & (RF3_RES_NETH)) vp[vn++] = "nether"; if (flags3 & (RF3_RES_WATE)) vp[vn++] = "water"; if (flags3 & (RF3_RES_PLAS)) vp[vn++] = "plasma"; if (flags3 & (RF3_RES_NEXU)) vp[vn++] = "nexus"; if (flags3 & (RF3_RES_DISE)) vp[vn++] = "disenchantment"; if (flags3 & (RF3_RES_TELE)) vp[vn++] = "teleportation"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" resists "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" and "); spoil_out(vp[i]); } spoil_out(". "); } /* Collect non-effects */ vn = 0; if (flags3 & (RF3_NO_STUN)) vp[vn++] = "stunned"; if (flags3 & (RF3_NO_FEAR)) vp[vn++] = "frightened"; if (flags3 & (RF3_NO_CONF)) vp[vn++] = "confused"; if (flags3 & (RF3_NO_SLEEP)) vp[vn++] = "slept"; if (vn) { spoil_out(wd_che[msex]); for (i = 0; i < vn; i++) { if (!i) spoil_out(" cannot be "); else if (i < vn - 1) spoil_out(", "); else spoil_out(" or "); spoil_out(vp[i]); } spoil_out(". "); } spoil_out(wd_che[msex]); if (r_ptr->sleep > 200) spoil_out(" prefers to ignore"); else if (r_ptr->sleep > 95) spoil_out(" pays very little attention to"); else if (r_ptr->sleep > 75) spoil_out(" pays little attention to"); else if (r_ptr->sleep > 45) spoil_out(" tends to overlook"); else if (r_ptr->sleep > 25) spoil_out(" takes quite a while to see"); else if (r_ptr->sleep > 10) spoil_out(" takes a while to see"); else if (r_ptr->sleep > 5) spoil_out(" is fairly observant of"); else if (r_ptr->sleep > 3) spoil_out(" is observant of"); else if (r_ptr->sleep > 1) spoil_out(" is very observant of"); else if (r_ptr->sleep > 0) spoil_out(" is vigilant for"); else spoil_out(" is ever vigilant for"); sprintf(buf, " intruders, which %s may notice from %d feet. ", wd_lhe[msex], 10 * r_ptr->aaf); spoil_out(buf); i = 0; if (flags1 & (RF1_DROP_60)) i += 1; if (flags1 & (RF1_DROP_90)) i += 2; if (flags1 & (RF1_DROP_1D2)) i += 2; if (flags1 & (RF1_DROP_2D2)) i += 4; if (flags1 & (RF1_DROP_3D2)) i += 6; if (flags1 & (RF1_DROP_4D2)) i += 8; /* Drops gold and/or items */ if (i) { sin = FALSE; spoil_out(wd_che[msex]); spoil_out(" will carry"); if (i == 1) { spoil_out(" a"); sin = TRUE; } else if (i == 2) { spoil_out(" one or two"); } else { sprintf(buf, " up to %u", i); spoil_out(buf); } if (flags1 & (RF1_DROP_GREAT)) { if (sin) spoil_out("n"); spoil_out(" exceptional object"); } else if (flags1 & (RF1_DROP_GOOD)) { spoil_out(" good object"); } else if (flags1 & (RF1_DROP_USEFUL)) { spoil_out(" useful object"); } else if (flags1 & (RF1_ONLY_ITEM)) { spoil_out(" object"); } else if (flags1 & (RF1_ONLY_GOLD)) { spoil_out(" treasure"); } else { if (sin) spoil_out("n"); spoil_out(" object"); if (i > 1) spoil_out("s"); spoil_out(" or treasure"); } if (i > 1) spoil_out("s"); if (flags1 & (RF1_DROP_CHOSEN)) { spoil_out(", in addition to chosen objects"); } spoil_out(". "); } /* Count the actual attacks */ for (i = 0, j = 0; j < 4; j++) { if (r_ptr->blow[j].method) i++; } /* Examine the actual attacks */ for (k = 0, j = 0; j < 4; j++) { if (!r_ptr->blow[j].method) continue; /* No method yet */ p = "???"; /* Acquire the method */ switch (r_ptr->blow[j].method) { case RBM_HIT: p = "hit"; break; case RBM_TOUCH: p = "touch"; break; case RBM_PUNCH: p = "punch"; break; case RBM_KICK: p = "kick"; break; case RBM_CLAW: p = "claw"; break; case RBM_BITE: p = "bite"; break; case RBM_STING: p = "sting"; break; case RBM_XXX1: break; case RBM_BUTT: p = "butt"; break; case RBM_CRUSH: p = "crush"; break; case RBM_ENGULF: p = "engulf"; break; case RBM_CHARGE: p = "charge"; break; case RBM_CRAWL: p = "crawl on you"; break; case RBM_DROOL: p = "drool on you"; break; case RBM_SPIT: p = "spit"; break; case RBM_EXPLODE: p = "explode"; break; case RBM_GAZE: p = "gaze"; break; case RBM_WAIL: p = "wail"; break; case RBM_SPORE: p = "release spores"; break; case RBM_XXX4: break; case RBM_BEG: p = "beg"; break; case RBM_INSULT: p = "insult"; break; case RBM_MOAN: p = "moan"; break; case RBM_SHOW: p = "sing"; break; } /* Default effect */ q = "???"; /* Acquire the effect */ switch (r_ptr->blow[j].effect) { case RBE_HURT: q = "attack"; break; case RBE_POISON: q = "poison"; break; case RBE_UN_BONUS: q = "disenchant"; break; case RBE_UN_POWER: q = "drain charges"; break; case RBE_EAT_GOLD: q = "steal gold"; break; case RBE_EAT_ITEM: q = "steal items"; break; case RBE_EAT_FOOD: q = "eat your food"; break; case RBE_EAT_LITE: q = "absorb light"; break; case RBE_ACID: q = "shoot acid"; break; case RBE_ELEC: q = "electrocute"; break; case RBE_FIRE: q = "burn"; break; case RBE_COLD: q = "freeze"; break; case RBE_BLIND: q = "blind"; break; case RBE_CONFUSE: q = "confuse"; break; case RBE_TERRIFY: q = "terrify"; break; case RBE_PARALYZE: q = "paralyze"; break; case RBE_LOSE_STR: q = "reduce strength"; break; case RBE_LOSE_INT: q = "reduce intelligence"; break; case RBE_LOSE_WIS: q = "reduce wisdom"; break; case RBE_LOSE_DEX: q = "reduce dexterity"; break; case RBE_LOSE_CON: q = "reduce constitution"; break; case RBE_LOSE_CHR: q = "reduce charisma"; break; case RBE_LOSE_ALL: q = "reduce all stats"; break; case RBE_SHATTER: q = "shatter"; break; case RBE_EXP_10: q = "lower experience (by 10d6+)"; break; case RBE_EXP_20: q = "lower experience (by 20d6+)"; break; case RBE_EXP_40: q = "lower experience (by 40d6+)"; break; case RBE_EXP_80: q = "lower experience (by 80d6+)"; break; case RBE_DISEASE: q = "disease"; break; case RBE_TIME: q = "time"; break; case RBE_SANITY: q = "make insane"; break; case RBE_HALLU: q = "cause hallucinations"; break; case RBE_PARASITE: q = "parasite"; break; } if (!k) { spoil_out(wd_che[msex]); spoil_out(" can "); } else if (k < i - 1) { spoil_out(", "); } else { spoil_out(", and "); } /* Describe the method */ spoil_out(p); /* Describe the effect, if any */ if (r_ptr->blow[j].effect) { spoil_out(" to "); spoil_out(q); if (r_ptr->blow[j].d_dice && r_ptr->blow[j].d_side) { spoil_out(" with damage"); if (r_ptr->blow[j].d_side == 1) sprintf(buf, " %d", r_ptr->blow[j].d_dice); else sprintf(buf, " %dd%d", r_ptr->blow[j].d_dice, r_ptr->blow[j].d_side); spoil_out(buf); } } k++; } if (k) { spoil_out(". "); } else if (flags1 & (RF1_NEVER_BLOW)) { sprintf(buf, "%s has no physical attacks. ", wd_che[msex]); spoil_out(buf); } spoil_out(NULL); } /* Check for errors */ if (ferror(fff) || my_fclose(fff)) { msg_print("Cannot close spoiler file."); return; } msg_print("Successfully created a spoiler file."); } /* * Print a bookless spell list */ void print_magic_powers( magic_power *powers, int max_powers, void(*power_info)(char *p, int power), int skill_num ) { int i, save_skill; char buf[80]; magic_power spell; /* Use a maximal skill */ save_skill = s_info[skill_num].value; s_info[skill_num].value = SKILL_MAX; /* Dump the header line */ spoiler_blanklines(2); sprintf(buf, "%s", s_info[skill_num].name); spoiler_underline(buf); spoiler_blanklines(1); fprintf(fff, " Name Lvl Mana Fail Info\n"); /* Dump the spells */ for (i = 0; i < max_powers; i++) { /* Access the spell */ spell = powers[i]; /* Get the additional info */ power_info(buf, i); /* Dump the spell */ spoil_out(format("%c) %-30s%2d %4d %3d%%%s\n", I2A(i), spell.name, spell.min_lev, spell.mana_cost, spell.fail, buf)); spoil_out(format("%s\n", spell.desc)); } /* Restore skill */ s_info[skill_num].value = save_skill; } /* * Create a spoiler file for spells */ static void spoil_spells(cptr fname) { char buf[1024]; /* Build the filename */ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname); fff = my_fopen(buf, "w"); /* Oops */ if (!fff) { msg_print("Cannot create spoiler file."); return; } /* Dump the header */ sprintf(buf, "Spell Spoiler (Skill Level 50) for %s", get_version_string()); spoiler_underline(buf); /* Dump the bookless magic powers in alphabetical order */ /* Mimicry */ print_magic_powers(mimic_powers, MAX_MIMIC_POWERS, mimic_info, SKILL_MIMICRY); /* Mindcraft */ print_magic_powers(mindcraft_powers, MAX_MINDCRAFT_POWERS, mindcraft_info, SKILL_MINDCRAFT); /* Necromancy */ print_magic_powers(necro_powers, MAX_NECRO_POWERS, necro_info, SKILL_NECROMANCY); /* Symbiosis */ print_magic_powers(symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info, SKILL_SYMBIOTIC); /* Check for errors */ if (ferror(fff) || my_fclose(fff)) { msg_print("Cannot close spoiler file."); return; } /* Message */ msg_print("Successfully created a spoiler file."); } /* * Create Spoiler files -BEN- */ void do_cmd_spoilers() { int i; /* Enter "icky" mode */ character_icky = TRUE; /* Save the screen */ Term_save(); /* Interact */ while (1) { /* Clear screen */ Term_clear(); /* Info */ prt("Create a spoiler file.", 2, 0); /* Prompt for a file */ prt("(1) Brief Object Info (obj-desc.spo)", 5, 5); prt("(2) Full Artifact Info (artifact.spo)", 6, 5); prt("(3) Brief Monster Info (mon-desc.spo)", 7, 5); prt("(4) Full Monster Info (mon-info.spo)", 8, 5); prt("(5) Spell Info (spell.spo)", 10, 5); /* Prompt */ prt("Command: ", 12, 0); /* Get a choice */ i = inkey(); /* Escape */ if (i == ESCAPE) { break; } /* Option (1) */ else if (i == '1') { spoil_obj_desc("obj-desc.spo"); } /* Option (2) */ else if (i == '2') { spoil_artifact("artifact.spo"); } /* Option (3) */ else if (i == '3') { spoil_mon_desc("mon-desc.spo"); } /* Option (4) */ else if (i == '4') { spoil_mon_info("mon-info.spo"); } /* Option (5) */ else if (i == '5') { spoil_spells("spell.spo"); } /* Oops */ else { bell(); } /* Flush messages */ msg_print(NULL); } /* Restore the screen */ Term_load(); /* Leave "icky" mode */ character_icky = FALSE; }