summaryrefslogtreecommitdiff
path: root/src/cmd7.c
diff options
context:
space:
mode:
authorBardur Arantsson <bardur@scientician.net>2010-01-08 20:28:34 +0100
committerBardur Arantsson <bardur@scientician.net>2010-01-08 23:46:06 +0100
commit6aa48afdd57d03314fdf4be6c9da911c32277c84 (patch)
tree2dc401f9aae2dc6736d2fc3811c8f8099d3eabe6 /src/cmd7.c
Import tome-2.3.5.
Diffstat (limited to 'src/cmd7.c')
-rw-r--r--src/cmd7.c7984
1 files changed, 7984 insertions, 0 deletions
diff --git a/src/cmd7.c b/src/cmd7.c
new file mode 100644
index 00000000..123822a6
--- /dev/null
+++ b/src/cmd7.c
@@ -0,0 +1,7984 @@
+/* File: cmd7.c */
+
+/* Purpose: More Class commands */
+
+/*
+ * Copyright (c) 1999 Dark God
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+
+#include "angband.h"
+
+
+/*
+ * Describe class powers of Mindcrafters
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mindcraft_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+
+ /* Clear buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " rad %d", DEFAULT_RADIUS);
+ break;
+ case 1:
+ strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15);
+ break;
+ case 2:
+ strnfmt(p, 80, " range %d", (plev < 25 ? 10 : plev + 2 + p_ptr->to_s * 3));
+ break;
+ case 3:
+ strnfmt(p, 80, " range %d", plev * 5);
+ break;
+ case 4:
+ strnfmt(p, 80, " power %d", plev * (plev < 30 ? 1 : 2));
+ break;
+ case 5:
+ if (plev > 20)
+ strnfmt(p, 80, " dam %dd8 rad %d", 8 + ((plev - 5) / 4), (plev - 20)/8 + 1);
+ else
+ strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4));
+ break;
+ case 6:
+ strnfmt(p, 80, " dur %d", plev);
+ break;
+ case 7:
+ break;
+ case 8:
+ if (plev < 25)
+ strnfmt(p, 80, " dam %d rad %d", (3 * plev) / 2, 2 + (plev / 10));
+ else
+ strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1));
+ break;
+ case 9:
+ strnfmt(p, 80, " dur 11-%d", 10 + plev + plev / 2);
+ break;
+ case 10:
+ strnfmt(p, 80, " dam %dd6 rad %d", plev / 2, 0 + (plev - 25) / 10);
+ break;
+ case 11:
+ strnfmt(p, 80, " dam %d rad %d", plev * (plev > 39 ? 4 : 3), 3 + plev / 10);
+ break;
+ }
+}
+
+
+/*
+ * Describe class powers of Mimics
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mimic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MIMICRY);
+ object_type *o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ /* Clear the buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " dur %d", k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 70));
+ break;
+ case 1:
+ strnfmt(p, 80, " dur %d+d20", 10 + plev);
+ break;
+ case 2:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 3:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 4:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ }
+}
+
+
+/*
+ * Allow user to choose a magic power.
+ *
+ * If a valid spell is chosen, saves it in '*sn' and returns TRUE
+ * If the user hits escape, returns FALSE, and set '*sn' to -1
+ * If there are no legal choices, returns FALSE, and sets '*sn' to -2
+ *
+ * The "prompt" should be "cast", "recite", or "study"
+ * The "known" should be TRUE for cast/pray, FALSE for study
+ *
+ * nb: This function has a (trivial) display bug which will be obvious
+ * when you run it. It's probably easy to fix but I haven't tried,
+ * sorry.
+ */
+bool get_magic_power(int *sn, magic_power *powers, int max_powers,
+ void (*power_info)(char *p, int power), int plev, int cast_stat)
+{
+ int i;
+
+ int num = 0;
+
+ int y = 2;
+
+ int x = 18;
+
+ int minfail = 0;
+
+ int chance = 0;
+
+ int info;
+
+ char choice;
+
+ char out_val[160];
+
+ char comment[80];
+
+ cptr p = "power";
+
+ magic_power spell;
+
+ bool flag, redraw;
+
+
+ /* Assume cancelled */
+ *sn = ( -1);
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ /* Get the spell, if available */
+ if (repeat_pull(sn))
+ {
+ /* Verify the spell */
+ if (powers[*sn].min_lev <= plev)
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Count number of powers that satisfies minimum plev requirement */
+ for (i = 0; i < max_powers; i++)
+ {
+ if (powers[i].min_lev <= plev)
+ {
+ num++;
+ }
+ }
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(%^ss %c-%c, *=List, ESC=exit, %c-%c=Info) Use which %s? ",
+ p, I2A(0), I2A(num - 1), toupper(I2A(0)), toupper(I2A(num - 1)), p);
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ char psi_desc[80];
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Display a list of spells */
+ prt("", 1, x);
+ prt("", y, x);
+ put_str("Name", y, x + 5);
+ put_str("Lv Mana Fail Info", y, x + 35);
+
+ /* Dump the spells */
+ for (i = 0; i < max_powers; i++)
+ {
+ /* Access the spell */
+ spell = powers[i];
+ if (spell.min_lev > plev) break;
+
+ chance = spell.fail;
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[cast_stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[cast_stat]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Get info */
+ power_info(comment, i);
+
+ /* Dump the spell --(-- */
+ strnfmt(psi_desc, 80, " %c) %-30s%2d %4d %3d%%%s",
+ I2A(i), spell.name,
+ spell.min_lev, spell.mana_cost, chance, comment);
+ prt(psi_desc, y + i + 1, x);
+ }
+
+ /* Clear the bottom line */
+ prt("", y + i + 1, x);
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ /* Note verify */
+ info = (isupper(choice));
+
+ /* Lowercase */
+ if (info) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ spell = powers[i];
+
+ /* Provides info */
+ if (info)
+ {
+ c_prt(TERM_L_BLUE, spell.desc, 1, 0);
+
+ /* Restore the screen */
+ inkey();
+ Term_load();
+ character_icky = FALSE;
+ continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ }
+ character_icky = FALSE;
+
+ /* Abort if needed */
+ if (!flag) return (FALSE);
+
+ /* Save the choice */
+ (*sn) = i;
+
+#ifdef ALLOW_REPEAT /* TNB */
+
+ repeat_push(*sn);
+
+#endif /* ALLOW_REPEAT -- TNB */
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mindcrafter'.
+ */
+void do_cmd_mindcraft(void)
+{
+ int n = 0, b = 0;
+
+ int chance;
+
+ int dir;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+ magic_power spell;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mindcraft_powers, MAX_MINDCRAFT_POWERS,
+ mindcraft_info, plev, A_WIS)) return;
+
+ spell = mindcraft_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_MINDCRAFT) - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_WIS]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_WIS]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else if (b < 90)
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ else
+ {
+ /* Mana storm */
+ msg_print("Your mind unleashes its power in an uncontrollable storm!");
+ project(1, 2 + plev / 10, p_ptr->py, p_ptr->px, plev * 2,
+ GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM);
+ p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Precog */
+ case 0:
+ {
+ /* Magic mapping */
+ if (plev > 44)
+ {
+ wiz_lite();
+ }
+ else if (plev > 19)
+ {
+ map_area();
+ }
+
+ /* Detection */
+ if (plev < 30)
+ {
+ b = detect_monsters_normal(DEFAULT_RADIUS);
+ if (plev > 14) b |= detect_monsters_invis(DEFAULT_RADIUS);
+ if (plev > 4) b |= detect_traps(DEFAULT_RADIUS);
+ }
+ else
+ {
+ b = detect_all(DEFAULT_RADIUS);
+ }
+
+ /* Telepathy */
+ if (plev > 24)
+ {
+ set_tim_esp(p_ptr->tim_esp + plev);
+
+ /* If plvl >= 40, we should have permanent ESP */
+ }
+
+ if (!b) msg_print("You feel safe.");
+
+ break;
+ }
+
+ /* Mindblast */
+ case 1:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ if (randint(100) < plev * 2)
+ {
+ fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)));
+ }
+ else
+ {
+ fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0);
+ }
+
+ break;
+ }
+
+ /* Minor displace */
+ case 2:
+ {
+ if (plev < 25)
+ {
+ teleport_player(10);
+ }
+ else
+ {
+ int ii, ij;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ break;
+ }
+
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2 + (p_ptr->to_s*3)) ||
+ (rand_int(plev * plev / 2) == 0))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10 + p_ptr->to_s / 2, &ij, &ii);
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+ }
+
+ break;
+ }
+
+ /* Major displace */
+ case 3:
+ {
+ if (plev > 29) banish_monsters(plev);
+ teleport_player(plev * 5);
+
+ break;
+ }
+
+ /* Domination */
+ case 4:
+ {
+ if (plev < 30)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_DOMINATION, dir, plev, 0);
+ }
+ else
+ {
+ charm_monsters(plev * 2);
+ }
+
+ break;
+ }
+
+ /* Fist of Force --- not 'true' TK */
+ case 5:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8),
+ (plev > 20 ? (plev - 20) / 8 + 1 : 0));
+
+ break;
+ }
+
+ /* Character Armour */
+ case 6:
+ {
+ set_shield(p_ptr->shield + plev, 50, 0, 0, 0);
+ if (plev > 14) set_oppose_acid(p_ptr->oppose_acid + plev);
+ if (plev > 19) set_oppose_fire(p_ptr->oppose_fire + plev);
+ if (plev > 24) set_oppose_cold(p_ptr->oppose_cold + plev);
+ if (plev > 29) set_oppose_elec(p_ptr->oppose_elec + plev);
+ if (plev > 34) set_oppose_pois(p_ptr->oppose_pois + plev);
+
+ break;
+ }
+
+ /* Psychometry */
+ case 7:
+ {
+ if (plev < 40)
+ {
+ psychometry();
+ }
+ else
+ {
+ ident_spell();
+ }
+
+ break;
+ }
+
+ /* Mindwave */
+ case 8:
+ {
+ msg_print("Mind-warping forces emanate from your brain!");
+ if (plev < 25)
+ {
+ project(0, 2 + plev / 10, p_ptr->py, p_ptr->px,
+ (plev*3) / 2, GF_PSI, PROJECT_KILL);
+ }
+ else
+ {
+ (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1));
+ }
+
+ break;
+ }
+
+ /* Adrenaline */
+ case 9:
+ {
+ set_afraid(0);
+ set_stun(0);
+ hp_player(plev);
+
+ b = 10 + randint((plev * 3) / 2);
+
+ if (plev < 35)
+ {
+ set_hero(p_ptr->hero + b);
+ }
+ else
+ {
+ set_shero(p_ptr->shero + b);
+ }
+
+ if (!p_ptr->fast)
+ {
+ /* Haste */
+ (void)set_fast(b, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + b, 10);
+ }
+
+ break;
+ }
+
+ /* Psychic Drain */
+ case 10:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ b = damroll(plev / 2, 6);
+
+ if (fire_ball(GF_PSI_DRAIN, dir, b, 0 + (plev - 25) / 10))
+ {
+ p_ptr->energy -= randint(150);
+ }
+
+ break;
+ }
+
+ /* Telekinesis */
+ case 11:
+ {
+ msg_print("A wave of pure physical force radiates out from your body!");
+ project(0, 3 + plev / 10, p_ptr->py, p_ptr->px,
+ plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage WIS (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your mind!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_WIS, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static int get_mimic_chance(int mimic)
+{
+ s32b chance;
+
+ call_lua("get_mimic_info", "(d,s)", "d", mimic, "level", &chance);
+ chance *= 3;
+
+ chance -= get_skill_scale(SKILL_MIMICRY, 150);
+ chance -= 3 * adj_mag_stat[p_ptr->stat_ind[A_DEX]];
+
+ if (chance < 2) chance = 2;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+
+void do_cmd_mimic_lore()
+{
+ int fail;
+
+ object_type *o_ptr;
+
+
+ /* Player has to be able to see */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* No transformations when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ /* Already in a mimic form -- Allow cancelling */
+ if (p_ptr->mimic_form)
+ {
+ msg_print("You morph back to your natural form!");
+
+ set_mimic(0, 0, 0);
+ }
+
+ /* Not in mimic forms -- Allow transformations */
+ else
+ {
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ if ((o_ptr->tval != TV_CLOAK) || (o_ptr->sval != SV_MIMIC_CLOAK))
+ {
+ msg_print("You are not wearing any cloaks of mimicry.");
+ return;
+ }
+
+ /* Calculate failure rate */
+ fail = get_mimic_chance(o_ptr->pval2);
+
+ if (fail > 75)
+ {
+ msg_print("You feel uneasy with this shape-change.");
+
+ if (!get_check("Try it anyway? ")) return;
+ }
+
+ /* Fumble */
+ if (randint(100) < fail)
+ {
+ msg_print("Your shape-change goes horribly wrong!");
+
+ if (randint(100) < p_ptr->skill_sav)
+ {
+ msg_print("You manage to wrest your body back under control.");
+ return;
+ }
+
+ set_mimic(30, resolve_mimic_name("Abomination"), get_skill(SKILL_MIMICRY));
+ }
+
+ /* Success */
+ else
+ {
+ set_mimic(k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000), o_ptr->pval2, get_skill(SKILL_MIMICRY));
+ }
+ }
+
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_TITLE);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+static bool mimic_forbid_travel(char *fmt)
+{
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ if(value > 0 && (att & CLASS_ARMS || att & CLASS_LEGS))
+ {
+ msg_print("You had best not travel with your extra limbs.");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mimic'.
+ */
+void do_cmd_mimic(void)
+{
+ int n = 0, b = 0;
+
+ int fail;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MIMICRY);
+
+ magic_power spell;
+
+ static bool added_hooks = FALSE;
+ if(!added_hooks)
+ {
+ add_hook(HOOK_FORBID_TRAVEL, mimic_forbid_travel, "mimic_forbid_travel");
+ added_hooks = TRUE;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mimic_powers, MAX_MIMIC_POWERS, mimic_info,
+ plev, A_DEX)) return;
+
+ spell = mimic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ fail = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ fail -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ fail -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ fail += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Minimum failure rate */
+ if (fail < minfail) fail = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) fail += 25;
+ else if (p_ptr->stun) fail += 15;
+
+ /* Always a 5 percent chance of working */
+ if (fail > 95) fail = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < fail)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (fail / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Mimic */
+ case 0:
+ {
+ do_cmd_mimic_lore();
+
+ break;
+ }
+
+ /* Invisibility */
+ case 1:
+ {
+ int ii = 10 + plev + randint(20) + p_ptr->to_s;
+
+ set_invis(p_ptr->tim_invisible + ii, 50);
+ set_tim_invis(p_ptr->tim_invisible + ii);
+
+ break;
+ }
+
+ /* Legs Mimicry */
+ case 2:
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_WALL);
+
+ if (att & CLASS_LEGS)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You mimic a new pair of legs.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_LEGS);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ /* Wall Mimicry */
+ case 3:
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_ARMS);
+ att &= ~(CLASS_LEGS);
+
+ if (att & CLASS_WALL)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You grow an affinity for walls.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_WALL);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ case 4: /* Arms Mimicry */
+ {
+ /* Extract the value and the flags */
+ u32b value = p_ptr->mimic_extra >> 16;
+ u32b att = p_ptr->mimic_extra & 0xFFFF;
+
+ /* Clear useless things */
+ att &= ~(CLASS_LEGS);
+ att &= ~(CLASS_WALL);
+
+ if (att & CLASS_ARMS)
+ {
+ value += 50 + randint(50 + (2 * plev));
+ }
+ else
+ {
+ msg_print("You mimic a new pair of arms.");
+
+ value = 50 + randint(50 + (2 * plev));
+ att |= (CLASS_ARMS);
+ }
+
+ if (value > 10000) value = 10000;
+
+ p_ptr->mimic_extra = att + (value << 16);
+ p_ptr->update |= (PU_BODY);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage WIS (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your mind!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_DEX, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'beastmaster'.
+ */
+void do_cmd_beastmaster(void)
+{
+ int plev = p_ptr->lev, i, num;
+
+ monster_type *m_ptr;
+
+
+ /* Process the monsters (backwards) */
+ num = 0;
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ num++;
+ }
+ }
+
+ if (num < plev * 2)
+ {
+ /* XXX XXX */
+ if (rand_int(80-(plev) - p_ptr->stat_use[5]-p_ptr->to_s) < 20)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, plev, rand_int(plev / 2), FALSE);
+ }
+ }
+ else msg_print("You can't summon more pets");
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+/*
+ * Set of variables and functions to create an artifact
+ */
+
+
+/* LOG2 is a constant (compile-time) method of converting a single
+ * set bit into a number. Works well, but for variable (runtime)
+ * expressions, use a loop instead.. much smaller code*/
+#define LOG2(x) ( (x) & 0xFFFF? BLOG16(x) : BLOG16((x)>>16) + 16 )
+#define BLOG16(x) ( (x) & 0xFF ? BLOG8(x) : BLOG8 ((x)>>8 ) + 8 )
+#define BLOG8(x) ( (x) & 0xF ? BLOG4(x) : BLOG4 ((x)>>4 ) + 4 )
+#define BLOG4(x) ( (x) & 0x3 ? BLOG2(x) : BLOG2 ((x)>>2 ) + 2 )
+#define BLOG2(x) ( (x) & 0x1 ? 0 : 1 )
+
+int flags_select[32*5];
+int activation_select;
+
+/* Return true if the player is wielding the philosopher's stone
+ */
+bool alchemist_has_stone(void)
+{
+ if (p_ptr->inventory[INVEN_LITE].name1 == 209)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ Display a group of flags from a_select flags, and return
+ the number of flags displayed (even invisible ones)
+ */
+int show_flags(byte group, int pval)
+{
+ int i, x, color = TERM_WHITE;
+ int items = 0;
+
+ char ttt[80];
+
+ Term_clear();
+
+ group++; /* Adjust - no zero group */
+
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ {
+ if (a_select_flags[i].group != group)
+ continue;
+
+ if (a_select_flags[i].xp == 0)
+ break;
+ else
+ {
+ sprintf(ttt, "%c) %s",
+ (items < 26) ? I2A(items) : ('0' + items - 26),
+ al_name + a_select_flags[i].desc);
+ if ( wizard || alchemist_has_stone())
+ sprintf(ttt, "%c) %s (exp %ld)",
+ (items < 26) ? I2A(items) : ('0' + items - 26),
+ al_name + a_select_flags[i].desc,
+ a_select_flags[i].xp);
+
+ /* Note: Somebody is VERY clever, and it wasn't me. Text printed as
+ * TERM_DARK is actually printed as TERM_BLUE *SPACES* to prevent the
+ * player from using a 'cut-and-paste' enabled terminal to see
+ * what he shouldn't. Thus, simply setting the color to TERM_DARK
+ * will entirely prevent the unspoiled player from knowing that it's
+ * even possible. */
+
+ switch (flags_select[i])
+ {
+ case 1:
+ color = TERM_YELLOW;
+ break; /* Flag was set by the player (just now)*/
+ case 0:
+ color = TERM_WHITE;
+ break; /* This flag can be set, player is 'aware' of it*/
+ case - 1:
+ color = TERM_L_GREEN;
+ break; /* Flag is already set*/
+ case - 2:
+ color = TERM_DARK;
+ break; /* Invisible option */
+ case - 3:
+ color = TERM_RED;
+ break; /* Flag is set, but player isn't 'aware' of it */
+ case - 4:
+ color = TERM_L_DARK;
+ break; /* Flag is not set, player is 'aware', but it's beyond thier skill */
+ default:
+ color = TERM_DARK;
+ break; /* Just in Case*/
+ }
+ }
+ /* For alchemists who have the stone, at least show all the flags... */
+ if ((alchemist_has_stone() || wizard) && color == TERM_DARK)
+ color = TERM_BLUE;
+
+ if (items < 16) x = 5;
+ else x = 45;
+ c_prt(color, ttt, ((items < 16) ? items : items - 16) + 5, x);
+ items++;
+
+ }
+ return items;
+}
+
+void show_levels(void)
+{
+ Term_clear();
+ c_prt(TERM_WHITE, "[a] Stats, sustains, luck, speed, vision, etc. ", 3, 10);
+ c_prt(TERM_WHITE, "[b] Misc. (Auras, light, see invis, etc) ", 4, 10);
+ c_prt(TERM_WHITE, "[c] Weapon Branding ", 5, 10);
+ c_prt(TERM_WHITE, "[d] Resistances and Immunities ", 6, 10);
+ c_prt(TERM_WHITE, "[e] ESP and Curses ", 7, 10);
+ c_prt(TERM_WHITE, "[f] Activation ", 8, 10);
+ c_prt(TERM_DARK , "[g] Abilities Gained ", 9, 10);
+ c_prt(TERM_WHITE, "[h] Display Required Essences and items ", 10, 10);
+ c_prt(TERM_WHITE, "[i] Done! Finalize and commit changes. ", 11, 10);
+ /*No need to return anything - if the valid selections change, it'll be a code level change.*/
+}
+
+s32b get_flags_exp(int pval, int oldpval)
+{
+ int i;
+ s32b exp = 0;
+
+ for (i = 0 ; a_select_flags[i].group ; i++ )
+ {
+ if (a_select_flags[i].xp == 0)
+ break;
+ else
+ {
+ if ( a_select_flags[i].group <= 5 && flags_select[i] )
+ {
+ s32b xp = a_select_flags[i].xp;
+ int factor = 1, oldfactor = 0;
+
+ /* don't even look at flags which the user can't set
+ * because they also can't change the pval when a pval-
+ * dependant flag is set, flags which they can't set
+ * cannot effect the exp in any way, whether their set or not
+ */
+ if ( flags_select[i] < -1 )
+ continue;
+ if ( flags_select[i] == -1 )
+ oldfactor = 1;
+
+ if (a_select_flags[i].pval)
+ {
+ /* (1/4)x^2 + x
+ * I wanted something smaller than x^2 or x^x
+ * this is because although a ring of speed +10 is
+ * more than 10 times better than a ring of speed +1,
+ * I don't think it's 100 times better. More like 30.
+ * this function yields:
+ * 1=1 * 2=3 * 3=5 * 4=8 * 5=11 * 6=15 * 7=21
+ * 8=24 * 9=29 * 10=35 * 11=41 * 12=48 * 13=55
+ * 14=63 * 15=71 * 20=120 * 25=181 * 30=255
+ * which I think is acceptable.
+ * briefly, to get a +30 speed ring, it would be:
+ * 255*50000 or over 12 million experience
+ * points. For reference, a level 50 human requires
+ * 5 million xp. I'm sure it's doable, but it'd be
+ * *HARD*
+ * a speed+10 artifact would require 1.75 million.
+ * much more doable, but not too easily.
+ */
+ factor = (pval * pval / 4 + pval);
+ if ( flags_select[i] == -1 )
+ {
+ oldfactor = oldpval * oldpval / 4 + oldpval;
+ }
+ }
+ exp += xp * factor - xp * oldfactor;
+ }
+ if ( a_select_flags[i].group == 88 && a_select_flags[i].flag == -activation_select )
+ {
+ exp += a_select_flags[i].xp;
+ }
+ }
+ }
+ if ( alchemist_has_stone() ) exp = exp / 4;
+ return exp;
+}
+
+/* returns the 'real quantity' of items needed to empower
+ * a particular flag to a particular pval.
+ * Note that this routine returns zero for any flag that
+ * doesn't require some sort of action.
+ */
+int calc_rqty(int i, int pval, int oldpval)
+{
+ /* return 0 if flag is greater than size of flags_select && ! activation */
+ if ( a_select_flags[i].group > 5 )
+ {
+ if ( activation_select == a_select_flags[i].flag)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* return 0 if the flag wasn't set */
+ if ( flags_select[i] < -1 || flags_select[i] == 0 )
+ return 0;
+
+ /* Return change in pval if the flag was already set */
+ if ( flags_select[i] == -1 && a_select_flags[i].pval)
+ return pval - oldpval;
+
+ /* Return pval if the flag will be set this time */
+ else if ( a_select_flags[i].pval )
+ return pval;
+
+ /* Return 0 if the flag is unknown */
+ else if ( flags_select[i] == -1 )
+ return 0;
+ return 1;
+}
+
+/* Handle the various items that creating artifacts requires.
+ * Mode = 0 to print a description,
+ * 1 to use up the items
+ * -1 to check to see if the items exist
+ * Note that this function is called ONLY from the
+ * other artifact item helper function.
+ */
+
+
+int check_artifact_items(int pval, int oldpval, int mode)
+{
+ int i, j, k, row = 1 , col = 15, rqty, orqty, trqty;
+ bool good = TRUE;
+ int temporary = -1;
+ char ch;
+
+ /* For temporary items, waive the item requirements,
+ * except for the corpse... */
+ for ( j = 0 ; a_select_flags[j].group ; j++)
+ if (a_select_flags[j].flag == 4*32 && flags_select[j] == 1 )
+ temporary = j;
+ /* Check for enough items */
+ for (i = 0; a_select_flags[i].group ; i++)
+ {
+ /* For temporary items, ignore
+ everything except the one item
+ */
+ if (temporary != -1 && i != temporary)
+ continue;
+
+ /* Calc quantity is done per flag, because
+ some have a pval, some don't, some where already
+ set at pval=2, etc
+ */
+ rqty = orqty = calc_rqty(i, pval, oldpval);
+
+ /* If no item is associated with this flag,
+ or this flag wasn't set or didn't change */
+ if ( !a_select_flags[i].rtval || !rqty)
+ continue;
+
+ for ( k = 0 ; k < INVEN_WIELD ; k++ )
+ {
+ object_type *o_ptr = &p_ptr->inventory[k];
+
+ /* Note here that an rsval of -1 (which is read is 0xff
+ for a byte..) matches anything. */
+ if (o_ptr->tval == a_select_flags[i].rtval
+ && (o_ptr->sval == a_select_flags[i].rsval
+ || a_select_flags[i].rsval == (byte) - 1 ) )
+ {
+ /* Corpse validation is COMPLICATED!
+ * But at least we don't have to do this twice.
+ */
+ if ( a_select_flags[i].rtval == TV_CORPSE )
+ {
+ bool itemgood = TRUE;
+
+ /*Specified race not this one */
+ if ( o_ptr->pval2 != a_select_flags[i].rpval && a_select_flags[i].rpval)
+ continue;
+
+ /* Race flag (any monster who...)*/
+ for ( j = 0 ; !a_select_flags[i].rpval && a_select_flags[i].rflag[j] && j < 6 && itemgood ; j++)
+ {
+ int flag = a_select_flags[i].rflag[j] / 32;
+ u32b mask = 1 << (a_select_flags[i].rflag[j] % 32);
+
+ switch (flag)
+ {
+ case 0:
+ if ( !(r_info[o_ptr->pval2].flags1 & mask) ) itemgood = FALSE;
+ break;
+ case 1:
+ if ( !(r_info[o_ptr->pval2].flags2 & mask) ) itemgood = FALSE;
+ break;
+ case 2:
+ if ( !(r_info[o_ptr->pval2].flags3 & mask) ) itemgood = FALSE;
+ break;
+ case 3:
+ if ( !(r_info[o_ptr->pval2].flags4 & mask) ) itemgood = FALSE;
+ break;
+ case 4:
+ if ( !(r_info[o_ptr->pval2].flags5 & mask) ) itemgood = FALSE;
+ break;
+ case 5:
+ if ( !(r_info[o_ptr->pval2].flags6 & mask) ) itemgood = FALSE;
+ break;
+ case 6:
+ if ( !(r_info[o_ptr->pval2].flags7 & mask) ) itemgood = FALSE;
+ break;
+ case 7:
+ if ( !(r_info[o_ptr->pval2].flags8 & mask) ) itemgood = FALSE;
+ break;
+ case 8:
+ if ( !(r_info[o_ptr->pval2].flags9 & mask) ) itemgood = FALSE;
+ break;
+ default:
+ msg_print("This code should never be hit!");
+ }
+ }
+ if ( ! itemgood )
+ continue;
+
+ }
+ /* Validate pval of good item */
+ else if ( a_select_flags[i].rpval)
+ {
+ /* Must have matching signs */
+ if ( (o_ptr->pval < 0) != (a_select_flags[i].rpval < 0))
+ continue;
+ /* Must be greater than */
+ if ( abs(o_ptr->pval) < abs(a_select_flags[i].rpval))
+ continue;
+ }
+
+ trqty = MIN(o_ptr->number, rqty);
+ rqty -= trqty;
+
+ if ( mode == 1 )
+ {
+ inven_item_increase(k, -trqty);
+ inven_item_describe(k);
+ /*
+ if we optimize this now, it moves everything after it
+ in the p_ptr->inventory up one, and the pointer to the item
+ being artifactized now points to something else.
+ DON'T DO IT!
+ inven_item_optimize(k);
+ */
+ }
+ }/* if p_ptr->inventory item is acceptable */
+
+ } /*end of looping through the p_ptr->inventory*/
+
+ if (rqty)
+ {
+ good = FALSE;
+ /* Oops, we didn't have enough of this object
+ when actually creating the artifact.
+ unset this flag
+ */
+ if ( mode == 1 )
+ {
+ flags_select[i] = -4;
+ }
+ /* we only return false for mode -1,
+ * for mode 0 we display stuff, and for
+ * mode 1 we want to continue destroying things
+ * even if the player is missing one small item,
+ * because there's no way to change things now.
+ * We may have already destroyed a unique corpse,
+ * or some other hard-to-find item.
+ */
+ if ( mode == -1 )
+ return FALSE;
+ }
+
+ /* Display a description of the required object, if needed */
+ /* Note that the tests for good items HAVE to be in a different
+ place, because otherwise we don't know how many the player
+ has, as opposed to how many they need.
+ */
+ if ( mode == 0 )
+ {
+ char *o_name = al_name + a_select_flags[i].item_desc;
+ if (orqty > 1 && a_select_flags[i].pval && a_select_flags[i].item_descp)
+ o_name = al_name + a_select_flags[i].item_descp;
+
+ if ( rqty )
+ {
+ if ( orqty > 1 )
+ c_prt(TERM_RED, format(" you are missing %d of the %d %s", rqty, orqty, o_name), row++, col);
+ else if ( is_a_vowel(o_name[0]))
+ c_prt(TERM_RED, format(" you are missing an %s", o_name), row++, col);
+ else
+ c_prt(TERM_RED, format(" you are missing a %s", o_name), row++, col);
+ }
+ else
+ {
+ if ( orqty > 1 )
+ c_prt(TERM_GREEN, format(" you have the %d %s", orqty, o_name), row++, col);
+ else if ( is_a_vowel(o_name[0]))
+ c_prt(TERM_GREEN, format(" you have an %s", o_name), row++, col);
+ else
+ c_prt(TERM_GREEN, format(" you have a %s", o_name), row++, col);
+ }
+
+ if ( row > 21 )
+ {
+ row = 1;
+ if (!good)
+ (void)get_com("You are missing some items:", &ch);
+ else
+ (void)get_com("You have these needed items on hand:", &ch);
+ }
+
+ }
+
+ } /* End of group associated with this a_select_flags entry */
+
+ if ( mode == 0 )
+ {
+ while ( row < 22 )
+ c_prt(TERM_GREEN, " ", row++, col);
+ if (!good)
+ (void)get_com("You are missing some items:", &ch);
+ else
+ (void)get_com("You have these needed items on hand:", &ch);
+ }
+ return good;
+}
+
+/* Display a list of required essences,
+ * and/or use up the essences. */
+bool artifact_display_or_use(int pval, int oldpval, bool use)
+{
+ int essence[MAX_BATERIE_SVAL];
+ int essenceh[MAX_BATERIE_SVAL];
+ int al_idx, i, j, k;
+ bool enough;
+
+ /* Temporary Items require only one item, and no essences. */
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ if ( a_select_flags[i].flag == 32*4)
+ {
+ if ( use )
+ return check_artifact_items(pval, oldpval, 1);
+ else
+ return check_artifact_items(pval, oldpval, 0);
+ }
+
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++ )
+ essence[i] = essenceh[i] = 0;
+
+ /* Accumulate a list of required essences */
+ for ( al_idx = 0; al_idx < max_al_idx ; al_idx++ )
+ if ( alchemist_recipes[al_idx].tval == 0 )
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ {
+ int rqty = calc_rqty(i, pval, oldpval);
+
+ /* If the flag isn't being set, rqty will be zero */
+ if ( !rqty)
+ continue;
+
+ if ( alchemist_recipes[al_idx].sval == a_select_flags[i].flag )
+ essence[alchemist_recipes[al_idx].sval_essence] +=
+ alchemist_recipes[al_idx].qty * rqty;
+ }
+
+ /* The essence array now contains a list of all essences
+ * that will be consumed in the creation of this artifact */
+
+ /* Check for existence of required quatities of essences. */
+ for ( i = 0 ; i < INVEN_WIELD ; i++ )
+ {
+ for ( j = 0 ; j < MAX_BATERIE_SVAL ; j++)
+ if ( p_ptr->inventory[i].tval == TV_BATERIE && p_ptr->inventory[i].sval == j + 1)
+ {
+ essenceh[j] += p_ptr->inventory[i].number;
+ }
+ }
+
+ /* Check for enough essences */
+ enough = TRUE;
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ if ( essenceh[i] < essence[i] )
+ {
+ enough = FALSE;
+ break;
+ }
+
+ /* Check for items */
+ if ( enough )
+ enough = check_artifact_items(pval, oldpval, -1);
+
+
+ /* Display recipe list if they don't have enough, or not enough exp */
+ if (!enough || !use )
+ {
+ int row = 1 , col = 15;
+ bool good = FALSE;
+ char ch;
+
+ /* display of list of required essences */
+ /* Note: there are only 12 or so essences, so this list
+ * will ALWAYS fit on the screen */
+ for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ if ( essence[i] )
+ {
+ int missing = -MIN(essenceh[i] - essence[i], 0);
+ good = TRUE;
+ if ( missing )
+ c_prt(TERM_RED, format("%d of the required %d essences of %s",
+ missing, essence[i],
+ k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ),
+ row++, col);
+ else
+ c_prt(TERM_GREEN, format("you have the needed %d essences of %s",
+ essence[i],
+ k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ),
+ row++, col);
+ }
+
+ if (good)
+ {
+ /* blank the bottom row */
+ c_prt(TERM_WHITE, " ", row++, col);
+
+ /* and wait for a key */
+ (void)get_com("You are currently missing:", &ch);
+ }
+
+ /* Display a list of needed items as well */
+ check_artifact_items(pval, oldpval, 0);
+
+ return FALSE;
+ }
+
+ /* If we get to this point in the code, then the player
+ * has the required essences and items in their p_ptr->inventory */
+
+ /* If they do have enough, and they have enough exp, consume them */
+ for (i = 0 ; i < MAX_BATERIE_SVAL ; i++)
+ for ( k = 0 ; k < INVEN_WIELD && essence[i] > 0 ; k++)
+ if (p_ptr->inventory[k].tval == TV_BATERIE
+ && p_ptr->inventory[k].sval == i + 1
+ && essence[i])
+ {
+ int num = p_ptr->inventory[k].number;
+
+ inven_item_increase(k, MAX( -essence[i], -num));
+ inven_item_describe(k);
+ /*
+ messy bug, don't optimize here because it
+ rearanges the p_ptr->inventory.
+ inven_item_optimize(k);
+ */
+ essence[i] -= MIN(num, essence[i]);
+ }
+
+ /* Destroy the items needed */
+ check_artifact_items(pval, oldpval, 1);
+
+ return TRUE;
+}
+
+
+void display_activation_info(int num)
+{
+ object_type forge;
+ int i;
+
+
+ /* find the a_select_flags number of this activation type... */
+ for ( i = 0 ; a_select_flags[i].group ; i++)
+ if (a_select_flags[i].group == 88 && a_select_flags[i].flag == -num )
+ break;
+
+ object_wipe(&forge);
+ forge.xtra2 = num;
+ /* Print out various information about this activation... */
+ /* min level, experience, required items (and essences)
+ full description (from activation_aux) */
+ if (wizard)
+ c_prt(TERM_WHITE, format(" number:%d ", num), 5, 5);
+ else
+ c_prt(TERM_WHITE, " ", 5, 5);
+ c_prt(TERM_WHITE, format(" Level:%d ", a_select_flags[i].level), 6, 5);
+ c_prt(TERM_WHITE, format(" Exp :%d ", a_select_flags[i].xp), 7, 5);
+ c_prt(TERM_WHITE, format(" Item :%s ", al_name + a_select_flags[i].item_desc), 8, 5);
+ c_prt(TERM_WHITE, " ", 9, 5);
+ c_prt(TERM_WHITE, format(" %s ", activation_aux(&forge, 0, 0)), 9, 5);
+ c_prt(TERM_WHITE, " ", 10, 5);
+ inkey();
+}
+
+void select_an_activation(void)
+{
+ int i, lev, wid, hgt, begin = 0, sel = 0;
+ u32b max;
+ cptr act_list[150]; /* currently, ~127 hardcoded activations */
+ int act_ref[150];
+ char c;
+ /* How do we want to do this? */
+ /* Ideally, we let them select from a list, which includes all the activations that they've ecountered in any form.
+ Problems with this idea include mainly the lack of any (current) place to store which activations they've seen, and
+ that they'll not get credit for any seen before we start tracking it.
+
+ So - list is everything. If they select one which they're to low-level for
+ or if the explicitly request it, we'll display info about this item.
+ We'll also get our descriptions from the activation_aux(ACT_CONSTANT)
+ function, because they are more complete, and include even lua-scripted ones.
+ msg_print("Since the code to actually let you select one isn't here");
+ msg_print("You will automatically get the activation 'Dawn'");
+ activation_select = ACT_DAWN;
+ */
+
+ /* Build a list of available activations at the player's level */
+ lev = get_skill(SKILL_ALCHEMY);
+ for ( i = max = 0 ; max < (sizeof(act_list) / sizeof(cptr)) && a_select_flags[i].group ; i++)
+ if (a_select_flags[i].group == 88 && a_select_flags[i].level <= lev )
+ {
+ act_ref[max] = -a_select_flags[i].flag; /* Activation number */
+ act_list[max++] = al_name + a_select_flags[i].desc; /* Description */
+ }
+
+ /* Select from that list, using the util.c function display_list to display the scrolled list */
+ /* Note: I think that there is only one other place that uses this function. Should be more! */
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, "Enter to select, ? for more information, 2 and 8 to scroll ", 0, 0);
+ display_list(1, 0, hgt - 2, wid - 2, "Select an Activation", act_list, max, begin, sel, TERM_L_GREEN);
+
+ c = inkey();
+
+ if (c == ESCAPE) break;
+ else if (c == '8')
+ {
+ sel--;
+ if (sel < 0)
+ {
+ sel = max - 1;
+ begin = max - hgt;
+ if (begin < 0) begin = 0;
+ }
+ if (sel < begin) begin = sel;
+ }
+ else if (c == '2')
+ {
+ sel++;
+ if (sel >= (s32b)max)
+ {
+ sel = 0;
+ begin = 0;
+ }
+ if (sel >= begin + hgt - 1) begin++;
+ }
+ else if (c == '?')
+ {
+ display_activation_info(act_ref[sel]);
+ }
+ else if (c == '\r')
+ {
+ display_activation_info(act_ref[sel]);
+ activation_select = act_ref[sel];
+ return;
+ }
+ }
+ activation_select = 0;
+}
+
+
+/* Consume 'num' magic essences and return true.
+ * If there aren't enough essences, return false */
+
+bool magic_essence(int num)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Count the magic essences */
+ if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) j += o_ptr->number;
+ }
+
+ /* Abort if not enough essences. */
+ if (j < num) return FALSE;
+
+ /* Consume them */
+ i = 0;
+ j = num;
+ while (i < INVEN_WIELD)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC))
+ {
+ /* This can lead to invalid object pointer for objects
+ * that come after the magic essences. Therefore, every
+ * artifactable object should come before the essences.
+ */
+ j -= o_ptr->number;
+ inven_item_increase(i, -num);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+ num = j;
+ if (num <= 0) break;
+ /* Stay on this slot; do not increment i. */
+ }
+ else
+ {
+ /* Move on to the next slot. */
+ i++;
+ }
+ }
+
+ /* Sanity check. */
+ if (num > 0)
+ {
+ msg_format("ERROR: Couldn't destroy %d essences!", num);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void do_cmd_create_artifact(object_type *q_ptr)
+{
+ int max, i = 0, j, cur_set = 0, abord = FALSE, done = FALSE;
+ int skill;
+ s32b exp = 0;
+
+ char out_val[160];
+ char choice = 0;
+ bool lockpval = FALSE;
+ int pval;
+ int oldpval;
+ energy_use = 100;
+
+ pval = q_ptr->pval;
+ oldpval = pval;
+ skill = get_skill(SKILL_ALCHEMY);
+
+ if ( !pval )
+ pval = 1;
+ /* No activation added on this round */
+ activation_select = 0;
+
+ /* Save the current flags */
+ for (i = 0 ; a_select_flags[i].group ; i++)
+ {
+ if ( a_select_flags[i].flag < 0 || a_select_flags[i].group > 5)
+ continue;
+
+ flags_select[i] = 0;
+
+ switch (a_select_flags[i].flag / 32)
+ {
+ case 0:
+ if (q_ptr->art_flags1 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 1:
+ if (q_ptr->art_flags2 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 2:
+ if (q_ptr->art_flags3 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 3:
+ if (q_ptr->art_flags4 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 4:
+ if (q_ptr->art_flags5 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ case 5:
+ if (q_ptr->art_esp & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1;
+ break;
+ default:
+ /*This will not be hit, inspite of activations, because of the <= 5 above...*/
+ break;
+ }
+ /*
+ this would learn about ALL flags....
+ if(wizard)
+ alchemist_known_artifacts[a_select_flags[i].flag/32] = 0xffffffffL;
+ */
+
+ /* Set various flags if they haven't *ID*'d an artifact with this flag set.*/
+ if ( !(alchemist_known_artifacts[a_select_flags[i].flag / 32] & (1 << (a_select_flags[i].flag % 32)) ))
+ {
+ /* If this item has an ability that depends on pval which the player
+ * cannot set, don't allow them to change the pval either. */
+ if ( a_select_flags[i].pval && flags_select[i])
+ lockpval = TRUE;
+
+ /* Set the color and set-ablitity of this flag */
+ if ( flags_select[i] )
+ flags_select[i] = -3;
+ else
+ flags_select[i] = -2;
+ continue;
+ }
+ else if ( skill < a_select_flags[i].level )
+ {
+ /* If the alchemist has not passed the skill level for this flag,
+ Set this flag as unsettable.
+ */
+ if ( flags_select[i])
+ lockpval = TRUE;
+ else
+ flags_select[i] = -4;
+ }
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+
+ /* Everlasting love ... ... nevermind :) */
+ while ( !done && !abord)
+ {
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ /* Display the menu, but don't display it if we just
+ * displayed a message (it erases the screen, creating a blink message */
+ if ( cur_set < 6 || cur_set == 7 )
+ show_levels();
+
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ prt("Enter to accept, Escape to abort", 1, 0);
+
+ abord = !get_com("Play around with which group of powers?[a-g]", &choice);
+
+ if ( choice == ESCAPE)
+ abord = TRUE;
+
+ if ( abord )
+ continue; /*or break, same diff */
+
+ if ( isalpha(choice))
+ {
+ if (isupper(choice))
+ choice = tolower(choice);
+ cur_set = A2I(choice);
+ }
+ else
+ {
+ bell();
+ continue;
+ }
+
+ if ( cur_set == 5 )
+ {
+ if (q_ptr->xtra2 && !activation_select
+ && !get_check("This item already activates! Choose a different activation?")) continue;
+ select_an_activation();
+ exp = get_flags_exp(pval, oldpval);
+ continue;
+ }
+ if ( cur_set == 6 )
+ {
+ msg_print("This option is not available");
+ continue;
+ }
+ if ( cur_set == 7 )
+ {
+ artifact_display_or_use(pval, oldpval, FALSE);
+ continue;
+ }
+ if ( cur_set == 8 )
+ {
+ if (q_ptr->exp - exp < 0)
+ msg_print("Not enough experience for the flags you've selected.");
+ else
+ done = TRUE;
+ continue;
+ }
+
+ if (cur_set < 0 || cur_set > 4 )
+ {
+ bell();
+ continue;
+ }
+
+
+ while (!done && !abord)
+ {
+ /* Chose the flags */
+ exp = 0;
+ max = show_flags(cur_set, pval);
+ exp = get_flags_exp(pval, oldpval);
+ c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0);
+
+ /* Build a prompt (accept all flags) */
+ if (max <= 26)
+ {
+ /* Build a prompt (accept all flags) */
+ strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ",
+ I2A(0), I2A(max - 1));
+ }
+ else
+ {
+ strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ",
+ I2A(0), '0' + max - 27);
+ }
+ c_prt(TERM_L_BLUE, format("Power(I/D to increase/decrease): %d", pval), 3, 0);
+
+ /* Get a spell from the user */
+ while (!(done = !get_com(out_val, &choice)))
+ {
+ if (choice == 'I')
+ {
+ if ( lockpval )
+ {
+ msg_print("You cannot do that - you don't know how!");
+ continue;
+ }
+ if (q_ptr->exp - exp < 0)
+ {
+ msg_print("Not enough experience. Decrease power or deselect flags.");
+ continue;
+ }
+ pval++;
+ break;
+ }
+ else if (choice == 'D')
+ {
+ if ( lockpval )
+ {
+ msg_print("You cannot do that - you don't know how!");
+ continue;
+ }
+ pval--;
+ if (pval < oldpval) pval = oldpval;
+ break;
+ }
+ else if (choice == '\r' || choice == ESCAPE || choice == ' ')
+ {
+ done = TRUE;
+ break;
+ }
+ else if (isalpha(choice))
+ {
+ /* Lowercase */
+ if (isupper(choice)) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ i = D2I(choice) + 26;
+
+ /* Illegal */
+ if (i < 26) i = -1;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= max))
+ {
+ bell();
+ continue;
+ }
+ else
+ {
+ /*Find the i'th flag in group cur_set...*/
+ for ( j = 0 ; a_select_flags[j].group ; j++)
+ if (a_select_flags[j].group == cur_set + 1)
+ if (!i--) break;
+
+ if ( flags_select[j] == -4 )
+ {
+ msg_format("You need at least %d skill in alchemy.",
+ a_select_flags[j].level);
+ continue;
+ }
+ if ( flags_select[j] != 0 && flags_select[j] != 1)
+ {
+ bell();
+ continue;
+ }
+ if (flags_select[j]) flags_select[j] = 0;
+ else if (!flags_select[j])
+ {
+ if (q_ptr->exp - exp < 0)
+ {
+ msg_print("Not enough experience. Decrease power or deselect flags.");
+ continue;
+ }
+ flags_select[j] = 1;
+ }
+ break;
+ }
+ }
+ }/*sub-screen select and redraw loop*/
+ done = FALSE;
+ Term_clear();
+ }/* main screen (flag select screen) select and redraw loop*/
+
+ /* Abort if not enough experience, or no flags added */
+ if ( q_ptr->exp - exp < 0 || exp == 0 )
+ abord = TRUE;
+
+ /* Display the recipe, or use up the essences.
+ * Note that this has to be done before the screen
+ * is restored. This is because it's also called from
+ * within the loop to display the required items. */
+ if ( !abord )
+ if (!artifact_display_or_use(pval, oldpval, TRUE))
+ abord = TRUE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Return if abort, or missing ingredients */
+ if ( abord )
+ return;
+
+ /* Actually create the artifact */
+ q_ptr->exp -= exp;
+ q_ptr->art_flags4 &= ~TR4_ART_EXP;
+ q_ptr->pval = pval;
+
+ /* Just to be sure */
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD );
+
+ {
+ int now = 0, before = 0;
+ char dummy_name[80];
+ char new_name[80];
+
+ /* Apply the flags */
+ for (i = 0; a_select_flags[i].group ; i++)
+ {
+ if (flags_select[i] < 0)
+ before++;
+ else if ( flags_select[i] == 1)
+ {
+ now++;
+ switch (a_select_flags[i].flag / 32)
+ {
+ case 0:
+ q_ptr->art_flags1 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 1:
+ q_ptr->art_flags2 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 2:
+ q_ptr->art_flags3 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 3:
+ q_ptr->art_flags4 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 4:
+ q_ptr->art_flags5 |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ case 5:
+ q_ptr->art_esp |= 1 << (a_select_flags[i].flag % 32);
+ break;
+ default:
+ msg_print("error: this code can't ever be hit!");
+ }
+ }
+ }
+
+ if ( activation_select )
+ {
+ q_ptr->art_flags3 |= TR3_ACTIVATE;
+ q_ptr->xtra2 = activation_select;
+ }
+
+
+ /* Set the 'show modifier' flag */
+ q_ptr->art_flags3 |= TR3_SHOW_MODS;
+
+ /* For temporary items, set a timeout.
+ * alchemist_skill^2 for now */
+ if ( q_ptr->art_flags5 & TR5_TEMPORARY )
+ {
+ int lev = get_skill(SKILL_ALCHEMY);
+ q_ptr->timeout = lev * lev * 3;
+ }
+
+ /* Describe the new artifact */
+ object_out_desc(q_ptr, NULL, FALSE, TRUE);
+
+
+ /* Name the new artifact */
+ strcpy(dummy_name, "of an Alchemist");
+ if (!(get_string("What do you want to call the artifact? ", dummy_name, 80)))
+ strcpy(new_name, "of an Alchemist");
+ else
+ {
+ if ((strncmp(dummy_name, "of ", 3) == 0) ||
+ (strncmp(dummy_name, "Of ", 3) == 0) ||
+ ((dummy_name[0] == '\'') &&
+ (dummy_name[strlen(dummy_name) - 1] == '\'')))
+ {
+ strcpy(new_name, dummy_name);
+ }
+ else
+ {
+ strcpy(new_name, "called '");
+ strcat(new_name, dummy_name);
+ strcat(new_name, "'");
+ }
+ }
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+ q_ptr->ident |= IDENT_STOREB; /* This will be used later on... */
+
+ /* Save the inscription */
+ q_ptr->art_name = quark_add(new_name);
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ done = FALSE;
+ while (!done && get_com("Do you want to let this item continue to gain experience?", &choice))
+ {
+ switch (choice)
+ {
+ case 'y':
+ case 'Y':
+ if (magic_essence(get_skill(SKILL_ALCHEMY)))
+ q_ptr->art_flags4 |= TR4_ART_EXP;
+ else
+ msg_format("Oh, NO! You don't have enough magic essences. You needed %d.", get_skill(SKILL_ALCHEMY));
+ done = TRUE;
+ break;
+ case 'n':
+ case 'N':
+ q_ptr->exp = 0;
+ done = TRUE;
+ break;
+ }
+ }
+
+ /* Cycle through the p_ptr->inventory, and optimize everything.
+ * This wasn't done earlier, because if we had, then
+ * things in the p_ptr->inventory would shift around, and q_ptr
+ * wouldn't point to the right thing. BUT, at this point
+ * we don't need q_ptr anymore, so optimizing the p_ptr->inventory
+ * becomes sane. Sticky bug to figure out, let me tell you.
+ * Note also that this is cycling backwards - this is so
+ * that the same effect doesn't cause us to skip items. */
+ for ( i = INVEN_WIELD - 1 ; i >= 0 ; i-- )
+ inven_item_optimize(i);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+}
+
+/*
+ * Test to see if this tval/sval combo is in the alchemists'
+ * recipes as a createable item. Used to determine if we
+ * should extract from it.
+ */
+bool alchemist_exists(int tval, int sval, int ego, int artifact)
+{
+ int al_idx;
+
+ /* To prevent conflicts with recipes for ego-items.
+ * artifact not used, simplifies the loop below. */
+ if ((tval == 1) || artifact)
+ return FALSE;
+
+ /*Search for recipes with this tval/sval combo as the final result*/
+ for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++)
+ {
+ int rtval = alchemist_recipes[al_idx].tval;
+ int rsval = alchemist_recipes[al_idx].sval;
+
+ /* Accept ego wands and staves since ego is extracted last */
+ if (((!ego || tval == TV_WAND || tval == TV_STAFF) && rtval == tval && rsval == sval) ||
+ ( ego && rtval == 1 && rsval == ego))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ * Hook to determine if an object can have things extracted from it.
+ */
+bool item_tester_hook_extractable(object_type *o_ptr)
+{
+
+ /* No artifacts */
+ if (artifact_p(o_ptr)) return (FALSE);
+
+ /* No cursed things */
+ if (cursed_p(o_ptr)) return (FALSE);
+
+ /* If we REALLY wanted to rebalance alchemists,
+ * we'd test for 'fully identified this object kind' here.
+ */
+
+ return ((o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != 0)
+ || alchemist_exists(o_ptr->tval, o_ptr->sval, o_ptr->name2, o_ptr->name1));
+}
+
+/*
+ * Hook to determine if an object is empowerable (NOT rechargeable)
+ */
+bool item_tester_hook_empower(object_type *o_ptr)
+{
+ int sval = -1;
+ int lev = get_skill(SKILL_ALCHEMY);
+ /* after level 25, can empower ego items to create artifacts
+ * and double ego items.
+ * after level 50, can empower artifacts to create powerful artifacts
+ */
+
+ /* Never Empower a cursed item */
+ if ( cursed_p(o_ptr))
+ {
+ return FALSE;
+ }
+
+ /* Allow finalizing a self created artifact */
+ if (artifact_p(o_ptr)
+ && (o_ptr->art_flags4 & TR4_ART_EXP)
+ && !(o_ptr->art_flags4 & TR4_ULTIMATE))
+ return TRUE;
+
+ switch ( o_ptr->tval)
+ {
+ /* Empowerable objects: Traditional alchemist stuff */
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ case TV_ROD_MAIN:
+ sval = -1;
+ break;
+ case TV_BOOK:
+ sval = -1;
+ break;
+
+ /* Ego item stuff */
+ /* Disallow ego dragon armour before you can create artifacts.*/
+ case TV_DRAG_ARMOR:
+ if ( lev < 25)
+ return FALSE;
+ /* FALL THROUGH! no break here. */
+
+ /* weapons */
+
+ case TV_DAEMON_BOOK:
+ case TV_SWORD:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_AXE:
+ case TV_MSTAFF:
+
+ /* misc other items */
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_INSTRUMENT:
+ case TV_DIGGING:
+ case TV_LITE:
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+
+ /* Armor of various sorts */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+
+ /* Disallow ANY creation of ego items below level 5*/
+ if ( lev < 5)
+ return FALSE;
+
+ /* empowering an ego item creates an artifact or a
+ * double ego item, disallow below level 25 */
+ if ( lev < 25 && o_ptr->name2)
+ return FALSE;
+
+ /* Disallow double-ego and artifact unless the character has
+ * the artifact creation ability. */
+ if (!has_ability(AB_CREATE_ART) &&
+ (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b)))
+ return FALSE;
+
+ /* Otherwise... */
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ /* Return to the traditional alchemist objects.
+ * All ego items and artifacts returning TRUE are accepted as artifactable
+ * at level 25. If we want double ego non wieldable items (Fireproof Staff
+ * of Plenty) the artifactable test in do_cmd_alchemist() must be changed,
+ * e.g. checking if the item is wearable.
+ * For now, we disallow non-wearable ego-items and artifacts here.
+ */
+
+ if ((o_ptr->name2 || artifact_p(o_ptr)) &&
+ o_ptr->tval != TV_RING && o_ptr->tval != TV_AMULET)
+ return FALSE;
+
+ /* return true if it's a 'of nothing' item;
+ * does nothing for TV_ROD_MAIN and TV_BOOK
+ */
+ return (sval == o_ptr->sval
+
+ /* or if it's artifactable */
+ || ((lev >= 50 || (lev >= 25 && !artifact_p(o_ptr))) &&
+ (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET))
+
+ /* or if it's egoable (note that normal egos start at level 5, wands and such start at 15) */
+ || (!o_ptr->name2 && lev >= 15));
+}
+
+/* Extract a rod tip from a rod */
+void rod_tip_extract(object_type *o_ptr)
+{
+ object_type *q_ptr;
+ object_type forge;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Paranoia, return if it's a rod of nothing */
+ if (o_ptr->pval == SV_ROD_NOTHING)
+ return;
+
+ /* Extract the rod tip */
+ object_prep(q_ptr, lookup_kind(TV_ROD, o_ptr->pval));
+
+ q_ptr->number = o_ptr->number;
+
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+
+ /* Remove it from the rod */
+ o_ptr->pval = SV_ROD_NOTHING;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+}
+
+
+/* Begin & finish an art */
+void do_cmd_toggle_artifact(object_type *o_ptr)
+{
+ char o_name[80];
+
+ if (!(o_ptr->art_flags4 & TR4_ART_EXP))
+ {
+ bool okay = TRUE;
+
+ if ( !alchemist_has_stone())
+ {
+ msg_print("Creating an artifact will result into a permanent loss of 10 hp.");
+ if (!get_check("Are you sure you want to do that?")) return;
+ }
+
+ if (!magic_essence(get_skill(SKILL_ALCHEMY)))
+ {
+ msg_format("You need %d magic essences.", get_skill(SKILL_ALCHEMY));
+ return;
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ if (o_ptr->number > 1)
+ {
+ msg_print("Not enough energy to enchant more than one object!");
+ msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was"));
+ o_ptr->number = 1;
+ }
+ okay = TRUE;
+
+ if (!okay) return;
+
+ /* he/she got warned */
+ p_ptr->hp_mod -= 10;
+
+ /* Ok toggle it */
+ o_ptr->art_flags4 |= TR4_ART_EXP;
+ o_ptr->name2 = 0;
+ o_ptr->name2b = 0;
+ o_ptr->art_name = quark_add("Becoming");
+
+ /* Copy the object_kind flags to the artifact flags.
+ * Note that this is only needed so that flags set in the
+ * 'kind' area are visible when finalizing the artifact.
+ */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ o_ptr->art_flags1 |= f1;
+ o_ptr->art_flags2 |= f2;
+ o_ptr->art_flags3 |= f3;
+ o_ptr->art_flags4 |= f4;
+ o_ptr->art_flags5 |= f5;
+ o_ptr->art_esp |= esp;
+ }
+
+ p_ptr->update |= (PU_HP);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ else
+ {
+ do_cmd_create_artifact(o_ptr);
+ }
+}
+
+/*
+ * Test to see if they have all the ingredients to create an item.
+ * (doesn't count base item)
+ * creates 'tocreate' items (may be -1, but no more than that!)
+ * if tocreate=0, will return true if the player has enough
+ * in their p_ptr->inventory to empower that item.
+ */
+bool alchemist_items_check(int tval, int sval, int ego, int tocreate, bool message)
+{
+ int al_idx, j;
+ bool exists = FALSE;
+
+
+ for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ )
+ if ((ego && alchemist_recipes[al_idx].sval == ego
+ && alchemist_recipes[al_idx].tval == 1 )
+ || (!ego && alchemist_recipes[al_idx].sval == sval
+ && alchemist_recipes[al_idx].tval == tval))
+ {
+ exists = TRUE;
+ /* Create the essences */
+ if (tocreate > 0)
+ {
+ object_type forge;
+ object_type *o_ptr = &forge;
+
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence));
+ o_ptr->number = alchemist_recipes[al_idx].qty * tocreate;
+ /* Don't bother with apply_magic */
+
+ /* Randomly decrease the number of essences created */
+ if ( randint(3) == 1
+ && randint(52) > get_skill(SKILL_ALCHEMY)
+ && !alchemist_has_stone())
+ o_ptr->number /= randint(2) + 1;
+ if ( o_ptr->number == 0)
+ continue;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ if (inven_carry_okay(o_ptr))
+ {
+ int i;
+ inven_carry(o_ptr, FALSE);
+ for (i = 0; i < INVEN_WIELD ; i++)
+ if (p_ptr->inventory[i].tval == o_ptr->tval && p_ptr->inventory[i].sval == o_ptr->sval)
+ {
+ if ( message )
+ inven_item_describe(i);
+ break;
+ }
+
+ }
+ else
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ o_ptr->ident |= IDENT_STOREB;
+ }
+ else if ( tocreate < -1)
+ {
+ /*It's not valid to create more than one
+ * thing at a time, so if it's less than -1,
+ * it must be time to display a recipe
+ */
+ msg_format("%d essences of %d",
+ alchemist_recipes[al_idx].qty,
+ al_idx);
+ }
+ else /* Destroy the essences (tocreate == -1)
+ * or check for existence(tocreate == 0)*/
+ {
+ int rqty = alchemist_recipes[al_idx].qty;
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[j];
+ if (o_ptr->k_idx
+ && (o_ptr->tval == TV_BATERIE )
+ && (o_ptr->sval == alchemist_recipes[al_idx].sval_essence )
+ && (o_ptr->number >= rqty ))
+ {
+ /* At this point, the item is required, destroy it. */
+ if ( tocreate )
+ {
+ inven_item_increase(j, 0 - rqty);
+ if ( message)
+ inven_item_describe(j);
+ inven_item_optimize(j);
+ }
+
+ /* When we find enough of the item, break out of the
+ * 'search through the p_ptr->inventory' loop */
+ break;
+ }
+ }
+ if ( j == INVEN_WIELD)
+ /* This ingredient was not found, cannot do recipe */
+ return FALSE;
+ }/*destroying items, or just checking for existence */
+ }
+ return exists;
+}
+
+/* This function lists all the ingredients
+ * needed to create something.
+ */
+void alchemist_display_recipe(int tval, int sval, int ego)
+{
+ int al_idx;
+ int row = 1, col = 15;
+ char o_name[80];
+ char ch;
+ object_type *o_ptr, forge;
+
+ /* Display the ingredients for a recipe */
+ for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ )
+ if ((ego && alchemist_recipes[al_idx].sval == ego
+ && alchemist_recipes[al_idx].tval == 1 )
+ || (!ego && alchemist_recipes[al_idx].sval == sval
+ && alchemist_recipes[al_idx].tval == tval))
+ {
+ int qty = alchemist_recipes[al_idx].qty;
+ c_prt(TERM_GREEN,
+ format(" %d essence%s %s ", qty,
+ qty > 1 ? "s" : "",
+ k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)].name ),
+ row++, col);
+ }
+
+ c_prt(TERM_WHITE, " ", row++, col);
+
+ if (!ego)
+ {
+ /* Find the name of that object */
+ o_ptr = &forge;
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ o_ptr->name2 = ego;
+ hack_apply_magic_power = -99;
+ apply_magic(o_ptr, get_skill(SKILL_ALCHEMY) * 2, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ /* the 0 mode means only the text, leaving off any numbers */
+ object_desc(o_name, o_ptr, FALSE, 0);
+ }
+ else
+ {
+ /* Display the ego item name */
+ strcpy(o_name, e_name + e_info[ego].name);
+ }
+
+ /* Display a short message about it, and wait for a key. */
+ (void)get_com(format("ingredients needed to create a %s", o_name), &ch);
+
+}
+
+/*
+ *
+ * The alchemist_recipe_select was copied from
+ * wiz_create_itemtype
+ * and then changed quite a bit.
+ *
+ */
+
+/*
+ The select array is a simple array of 'use this char to select item x'
+ It has 88 items (three columns of 20 each)
+ selectitem is initilized with the reverse mappings:
+ selectitem[selectchar[x]] == x is always true.
+ */
+char selectchar[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*():;,.<=>[]{}/=?+'~";
+byte selectitem[256];
+
+void strip_and_print(char *str, int color, int num)
+{
+ int row = 2 + (num % 20), col = 40 * (num / 20);
+ int ch, max_len = 0;
+ char buf[80];
+ char *string;
+
+ if (num > 60)
+ {
+ msg_print("Attempting to display too many items!");
+ return;
+ }
+ ch = selectchar[num];
+ if (selectitem[ch] != num)
+ {
+ int i;
+ for ( i = 0 ; i < 256 ; i++)
+ selectitem[i] = 0xff;
+ for ( i = 0 ; selectchar[i] ; i++)
+ selectitem[(byte)selectchar[i]] = i;
+ }
+
+ /* Skip past leading characters */
+ while ((*str == ' ') || (*str == '&')) str++;
+
+ /* Copy useful chars */
+ for (string = buf; *str; str++)
+ if (*str != '~') *string++ = *str;
+
+ /* Terminate the new name */
+ *string = '\0';
+
+ /* strip the name down to size
+ if (76-col < (signed)max_len)
+ max_len = 76-col;
+ else
+ max_len = 30-6;*/
+ max_len = 39;
+
+ string = buf;
+ if (strlen(string) > (unsigned)max_len)
+ string = string + (strlen(string) - max_len);
+
+ /* Print it */
+ c_prt(color, format("[%c] %s", ch, string), row, col);
+}
+
+/* Display a list of recipes that need a particular essence.
+ * Note that we display a list of essences first,
+ * so in effect, this is the alchemist's recipe book.
+ */
+void alchemist_recipe_book(void)
+{
+ int num, max_num, i, al_idx, bat, kidx;
+ int choice[61], choice2[61];
+ int mod40;
+ bool essence[MAX_BATERIE_SVAL + 1];
+ char ch;
+
+ /* Save and clear the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ while ( TRUE )
+ {
+ Term_clear();
+
+ num = 0;
+
+ /* Display bateries */
+
+ /* start with assumption that the alchemist knows about no recipes */
+ for (i = 0; i < MAX_BATERIE_SVAL + 1 ; i++)
+ essence[i] = FALSE;
+
+ /* cycle through all alchemist recipes */
+ for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++)
+ /* if we aren't already going to display this essence */
+ if (!essence[alchemist_recipes[al_idx].sval_essence])
+ {
+
+ /*Note that we don't display artifact recipes here...*/
+ /*This is partially because artifacts often require exotic
+ ingredients as well */
+
+ if (!alchemist_recipes[al_idx].tval)
+ continue;
+
+ if (alchemist_recipes[al_idx].tval == 1)
+ {
+ if (alchemist_known_egos[alchemist_recipes[al_idx].sval / 32]
+ & (1 << (alchemist_recipes[al_idx].sval % 32)) )
+ essence[alchemist_recipes[al_idx].sval_essence] = TRUE;
+ continue;
+ }
+
+ kidx = lookup_kind(alchemist_recipes[al_idx].tval, alchemist_recipes[al_idx].sval);
+ if (alchemist_recipes[al_idx].tval != 1 && k_info[kidx].know)
+ essence[alchemist_recipes[al_idx].sval_essence] = TRUE;
+
+ }
+ for (num = 0, i = 0; i < MAX_BATERIE_SVAL + 7 ; i++)
+ if (essence[i] || i > MAX_BATERIE_SVAL)
+ {
+ int kidx = lookup_kind(TV_BATERIE, i);
+ if (i > MAX_BATERIE_SVAL)
+ {
+ switch (i)
+ {
+ case (MAX_BATERIE_SVAL + 1): strip_and_print("Scrolls", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 2): strip_and_print("Potions", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 3): strip_and_print("Wands", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 4): strip_and_print("Rings", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 5): strip_and_print("Staves", TERM_WHITE, num);
+ break;
+ case (MAX_BATERIE_SVAL + 6): strip_and_print("Amulets", TERM_WHITE, num);
+ break;
+ default:
+ continue;
+ }
+ }
+ else
+ /* add this essence to the list*/
+ strip_and_print(k_name + k_info[kidx].name, TERM_WHITE, num);
+
+ choice[num++] = i;
+ }
+ max_num = num;
+ if ( max_num == 0)
+ {
+ /*Note that this should never actually happen, as any skill
+ at alchemy automatically gets you some recipes, and this
+ procedure shouldn't be called for players without alchemist skill
+ */
+ msg_print("You don't know any recipes!");
+ msg_print("You can't be an alchemist without recipes!");
+ break;
+ }
+
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 ||
+ !get_com(format("Which Type of Recipe?[a-%c]", selectchar[max_num - 1]), &ch))
+ break;
+
+ /* Analyze choice - note that the cast to byte prevents overflow*/
+ num = selectitem[(byte)ch];
+
+ }
+ /* This break, and the break for no recipes above,
+ are the only exits from this procedure.
+ */
+ if ( num == 0xff || num >= max_num)
+ break;
+
+ /* Save the baterie index */
+ bat = choice[num];
+ num = 0;
+
+ /*Display the 'type of object' recipe screen*/
+ if (bat > MAX_BATERIE_SVAL)
+ {
+ int tval;
+ switch (bat)
+ {
+ case MAX_BATERIE_SVAL + 1:
+ tval = TV_SCROLL;
+ break;
+ case MAX_BATERIE_SVAL + 2:
+ tval = TV_POTION;
+ break;
+ case MAX_BATERIE_SVAL + 3:
+ tval = TV_WAND;
+ break;
+ case MAX_BATERIE_SVAL + 4:
+ tval = TV_RING;
+ break;
+ case MAX_BATERIE_SVAL + 5:
+ tval = TV_STAFF;
+ break;
+ case MAX_BATERIE_SVAL + 6:
+ tval = TV_AMULET;
+ break;
+ }
+ Term_load();
+ alchemist_recipe_select(&tval, 0, FALSE, TRUE);
+ Term_save();
+ continue;
+ }
+ mod40 = 0;
+ while ( TRUE )
+ {
+ int skipped;
+
+ Term_clear();
+ num = 0;
+
+ if (mod40)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ choice[num] = -2;
+ choice2[num++] = 0;
+ }
+
+ /* Display all items made with this essence */
+ for ( al_idx = 0 , skipped = 0 ; al_idx < max_al_idx ; al_idx++)
+ if ( alchemist_recipes[al_idx].sval_essence == bat)
+ {
+ int sval = alchemist_recipes[al_idx].sval;
+ int tval = alchemist_recipes[al_idx].tval;
+ char names[200] = "";
+
+ if (alchemist_recipes[al_idx].tval == 1)
+ {
+ /* Ego items */
+ ego_item_type *e_ptr = &e_info[sval];
+ int j, k;
+
+ if ( !(alchemist_known_egos[sval / 32] & (1 << (sval % 32))))
+ continue;
+
+ for ( j = 0 ; j < 6 && e_ptr->tval[j] ; j ++ )
+ {
+ if ( j > 0 && e_ptr->tval[j] == e_ptr->tval[j - 1])
+ continue;
+ for ( k = 0; tvals[k].tval; k++)
+ if (tvals[k].tval == e_ptr->tval[j])
+ {
+ strcat(names, tvals[k].desc);
+ strcat(names, ", ");
+ break;
+ }
+ }
+ strcat(names, e_name + e_ptr->name);
+ }
+ else
+ {
+ /* Normal Items */
+ int kidx = lookup_kind(tval, sval);
+ int k;
+ if ( !k_info[kidx].know )
+ continue;
+
+ for ( k = 0; tvals[k].tval; k++)
+ if (tvals[k].tval == tval)
+ {
+ strcat(names, tvals[k].desc);
+ break;
+ }
+ strcat(names, " of ");
+ strcat(names, k_name + k_info[kidx].name);
+
+ }
+
+ /*Skip the first mod40 pages of recipes*/
+ if (skipped++ < mod40*38)
+ continue;
+
+ /* add this object kind to the list*/
+ strip_and_print(names, TERM_WHITE, num);
+ choice[num] = tval;
+ choice2[num++] = sval;
+ if (num > 38)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ choice[num] = -1;
+ choice2[num++] = 0;
+ break;
+ }
+
+ }/*Loop through tidx/sidx*/
+
+ max_num = num;
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 || !get_com(
+ format("Examine which recipe?[%c-%c]", selectchar[0], ch)
+ , &ch))
+ {
+ break;
+ }
+
+ /* Analyze choice */
+ num = selectitem[(byte)ch];
+ }
+
+ if ( choice[num] < 0)
+ {
+ if (choice[num] < -1)
+ mod40--;
+ else
+ mod40++;
+ continue;
+ }
+
+ if ( num == 0xff || num >= max_num)
+ break;
+
+ /* Display the recipe */
+ if (choice[num] == 1)
+ alchemist_display_recipe(0, 0, choice2[num]);
+ else
+ alchemist_display_recipe(choice[num], choice2[num], 0);
+ }
+ /*
+ break is at top of loop, after essence list
+ if( num < 0 || num >= max_num)
+ break;
+ */
+
+ }/*show recipes*/
+
+ /* Restore screen contents */
+ Term_load();
+ character_icky = FALSE;
+}
+
+/* Display a list of known recipies that can be made with
+ * materials on hand (including the passed tval). Also
+ * calls the recipe_display function, if requested by the
+ * player or there aren't enough essences to make the
+ * requested object.
+ *
+ * Note: sval is ignored if !ego, tval is the only determinant
+ * of what recipies are available otherwise.
+ *
+ * This function needs to be able to scroll a list, because
+ * there are SO MANY potions. :)
+ */
+int alchemist_recipe_select(int *tval, int sval, int ego, bool recipe)
+{
+ int i, mod40 = 0, num, max_num = 0;
+
+ cptr tval_desc2 = "";
+ char ch;
+ bool done = FALSE;
+
+ int choice[60];
+ int validc[60];
+
+ char *string;
+
+
+ /* Save and clear the screen */
+ character_icky = TRUE;
+ Term_save();
+ Term_clear();
+
+ /* Base object type chosen, fill in tval */
+ for ( num = 0 ; num < 40 ; num ++)
+ if (tvals[num].tval == *tval)
+ {
+ tval_desc2 = tvals[num].desc;
+ }
+
+ while (!done)
+ {
+ Term_clear();
+ if (ego)
+ {
+ /* Find matching ego items */
+ for (num = 0, i = 1; (num < 40) && (i < max_e_idx) ; i++)
+ {
+ int j;
+ ego_item_type *e_ptr = &e_info[i];
+
+ /* Skip if unknown ego type */
+ if ( !(alchemist_known_egos[i / 32] & (1 << (i % 32))))
+ continue;
+
+ /* search in permitted tvals/svals for allowed egos */
+ for ( j = 0 ; j < 6 ; j ++ )
+ if ( e_ptr->tval[j] == *tval
+ && sval >= e_ptr->min_sval[j]
+ && sval <= e_ptr->max_sval[j])
+ {
+ int color = TERM_GREEN;
+
+ /*Reject if not opposite end of name
+ prefixes only on postfix egos,
+ postfixes only on prefix egos.
+ */
+ if (ego != -1 && e_ptr->before == e_info[ego].before)
+ continue;
+
+ /*Color it red of the alchemist doesn't have the essences to create it*/
+ if (!alchemist_items_check(*tval, 0, i, 0, TRUE))
+ color = TERM_RED;
+
+ /* add this ego to the list*/
+ strip_and_print(e_name + e_info[i].name, color, num);
+ validc[num] = color;
+ choice[num++] = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ char skipped = 0;
+ num = 0;
+ if (mod40 != 0)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ validc[num] = TERM_WHITE;
+ choice[num++] = -1;
+ }
+
+ for (i = 1; (num < 39) && (i < max_k_idx); i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze matching items */
+ if (k_ptr->tval == *tval || (k_ptr->tval == TV_POTION2 && *tval == TV_POTION))
+ {
+ char color = TERM_GREEN;
+ /* Hack -- Skip instant artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /*Don't display recipes that the alchemist doesn't know about*/
+ if (!k_ptr->know && !wizard) continue;
+
+ /*Skip recipes that are somehow known, but don't exist*/
+ if (!alchemist_exists(k_ptr->tval, k_ptr->sval, 0, 0))
+ continue;
+
+ /* Skip the first 39 if they hit 'more' */
+ if (skipped++ < mod40*39)
+ continue;
+
+ /* Color 'unable to create' items different */
+ if (!alchemist_items_check(k_ptr->tval, k_ptr->sval, 0, 0, TRUE))
+ color = TERM_RED;
+
+ /* Acquire the "name" of object "i" */
+ /* and print it in it's place */
+ strip_and_print(k_name + k_ptr->name, color, num);
+
+ /* Remember the object index */
+ validc[num] = color;
+ choice[num++] = i;
+ }
+ }
+ if (num == 39)
+ {
+ strip_and_print("--MORE--", TERM_WHITE, num);
+ validc[num] = TERM_WHITE;
+ choice[num++] = -1;
+ }
+ }
+
+ /* We need to know the maximal possible remembered object_index */
+ max_num = num;
+ string = "What Kind of %s? (* to see recipe) [%c-%c,*]";
+ num = 0xff;
+
+ /* Pretend they're all undoable if we where called to display recipes */
+ if (recipe)
+ {
+ for ( num = 0 ; num < max_num ; num++)
+ if (validc[num] != TERM_WHITE) validc[num] = TERM_RED;
+ string = "show which %s recipe? [%c-%c]";
+ }
+
+ while (num == 0xff || num >= max_num)
+ {
+ ch = selectchar[max_num - 1];
+ /* Choose! */
+ if ( max_num == 0 || !get_com(format(string, tval_desc2, selectchar[0], ch), &ch))
+ {
+ break;
+ }
+
+ /* Extra breaks for recipe */
+ if (recipe && (ch == '\r' || ch == ' ' || ch == ESCAPE ))
+ break;
+
+ /* Analyze choice */
+ num = selectitem[(byte)ch];
+
+ /* Pretend that we don't have enough essences for anything */
+ if (ch == '*' )
+ {
+ for ( num = 0 ; num < max_num ; num++)
+ if (validc[num] != TERM_WHITE) validc[num] = TERM_RED;
+ string = "Show which %s recipe? [%c-%c]";
+ }
+ }
+ if ( num == 0xff || max_num == 0 || num >= max_num)
+ break;
+
+ if ( validc[num] == TERM_WHITE )
+ {
+ if (num == 0)
+ mod40--;
+ else
+ mod40++;
+ if ( mod40 < 0)
+ mod40 = 0;
+ continue;
+ }
+
+ /* If we don't have enough essences, or user asked for recipes */
+ if ( validc[num] != TERM_GREEN )
+ {
+ /* Display the recipe */
+ if (ego)
+ alchemist_display_recipe(*tval, sval, choice[num]);
+ else
+ alchemist_display_recipe(k_info[choice[num]].tval, k_info[choice[num]].sval, 0);
+ }
+ else
+ done = TRUE;
+
+ }/*while(!done)*/
+
+ /* Restore screen contents */
+ Term_load();
+ character_icky = FALSE;
+
+ /* User abort, or no choices */
+ if (max_num == 0 || num == 0xff || num >= max_num)
+ {
+ if (max_num == 0)
+ msg_print("You don't know of anything you can make using that.");
+ return ( -1);
+ }
+ if ( validc[num] != TERM_GREEN )
+ return ( -1);
+
+ /* And return successful */
+ if ( ego )
+ return choice[num];
+
+ /* Set the tval, should be the same unless they selected a potion2 */
+ if (*tval != k_info[choice[num]].tval && *tval != TV_POTION)
+ msg_print("Coding error: tval != TV_POTION");
+ *tval = k_info[choice[num]].tval;
+ return ( k_info[choice[num]].sval );
+}
+
+/* Set the 'known' flags for all objects with a level <= lev
+ * This lets the budding alchemist create basic items.
+ */
+void alchemist_learn_all(int lev)
+{
+ int i;
+
+ if ( !get_skill(SKILL_ALCHEMY) )
+ return;
+
+ /* msg_format("You learn about level %d items",lev); */
+
+ for ( i = 0 ; i < max_k_idx ; i++ )
+ if ( k_info[i].level <= lev )
+ if (alchemist_exists(k_info[i].tval, k_info[i].sval, 0, 0))
+ k_info[i].know = TRUE;
+}
+
+void alchemist_learn_ego(int ego)
+{
+ char *name;
+ int i;
+
+ /* some Paranoia*/
+ if ( !ego || ego >= max_e_idx )
+ return;
+
+ /* Get the ego items name */
+ name = e_name + e_info[ego].name;
+ while (strchr(name, ' '))
+ name = strchr(name, ' ') + 1;
+
+ /* Don't learn about egos without recipes, and
+ * always learn about the passed ego item. */
+ if (alchemist_exists(0, 0, ego, 0))
+ {
+ alchemist_known_egos[ego / 32] |= (1 << (ego % 32));
+ /* msg_format("You learn about '%s' ego items.",e_name+e_info[ego].name); */
+ }
+ else
+ {
+ return;
+ }
+
+ /* Don't mass learn about egos that have no name. */
+ if ( name[0] == 0 )
+ {
+ return;
+ }
+
+ /* Look through all ego's for matching name */
+ /* Note that the original ego is marked here too */
+ for ( i = 0 ; i < max_e_idx ; i++ )
+ if ( strstr(e_name + e_info[i].name, name) != NULL /*Last word of name exists in this ego's name*/
+ && alchemist_exists(0, 0, i, 0) /*There exists a recipe for this*/
+ && !(alchemist_known_egos[i / 32] & (1 << (i % 32)) ) ) /*Not already known*/
+ /*&& (e_name+e_info[i].name)[0])non-blank name*/
+ {
+ alchemist_known_egos[i / 32] |= (1 << (i % 32));
+ /* msg_format("You learn about '%s' ego items.",e_name+e_info[i].name); */
+ }
+
+ return;
+}
+
+/* Alchemist has learned about a new item.
+ * Learn about not only it, but ALL egos with the
+ * same name.
+ */
+int alchemist_learn_object(object_type *o_ptr)
+{
+
+ /* Allow alchemist to create this item,
+ and.. learn about it even if the player
+ doesn't currently have the alchemy skill
+ */
+ k_info[o_ptr->k_idx].know = TRUE;
+
+ /* Not Paranoia, identify_fully calls this always */
+ if ( !get_skill(SKILL_ALCHEMY) )
+ return FALSE;
+
+ if ( artifact_p(o_ptr) )
+ {
+ char o_name[80];
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Randarts and normal artifacts both*/
+ alchemist_known_artifacts[0] |= f1;
+ alchemist_known_artifacts[1] |= f2;
+ alchemist_known_artifacts[2] |= f3;
+ alchemist_known_artifacts[3] |= f4;
+ alchemist_known_artifacts[4] |= f5;
+ alchemist_known_artifacts[5] |= esp;
+
+ object_desc(o_name, o_ptr, 1, 0);
+ msg_format("You learn all about the abilities of %s!", o_name);
+ }
+ if (o_ptr->name2)
+ alchemist_learn_ego(o_ptr->name2);
+
+ if (o_ptr->name2b)
+ alchemist_learn_ego(o_ptr->name2b);
+
+ return (TRUE);
+}
+
+/* Alchemist has gained a level - set the ego flags
+ * for all egos <= lev/4.
+ */
+void alchemist_gain_level(int lev)
+{
+ object_type forge;
+ object_type *o_ptr = &forge;
+
+ if ( lev == 0)
+ {
+ /* Learn about potions of Detonation */
+ k_info[417].know = TRUE;
+ }
+ if ( lev == 5)
+ {
+ int ego;
+ int egos[] = {
+ 7/*armor of resist fire*/
+ , 18/*shield of resist fire*/
+ , 74/*shocking weapon*/
+ , 75/*fiery weapon*/
+ , 76/*frozen weapon*/
+ , 77/*Venomous weapon*/
+ , 78/*Chaotic weapon*/
+ , 115/*projectile of venom*/
+ , 116/*projectile of Acid*/
+ , 122/*projectile of flame*/
+ , 123/*projectile of frost*/
+ , 137/*Lite of fearlessness*/
+ , 0 /*terminator*/
+ };
+ object_wipe(o_ptr);
+ /* learn about some basic ego items */
+ /* Note that this is just to get you started. */
+ for ( ego = 0 ; egos[ego] ; ego++)
+ {
+ o_ptr->name2 = egos[ego];
+ alchemist_learn_object(o_ptr);
+ }
+ msg_print("You recall your old master teaching you about elemental item infusing.");
+ }
+ if ( lev == 10)
+ {
+ /*For 'hard rooms' Players only, learn about diggers.*/
+ if (ironman_rooms)
+ {
+ msg_print("There's gotta be an easier way to get into all these vaults!");
+ object_wipe(o_ptr);
+ o_ptr->name2 = 101; /* Ego item, 'of digging' */
+ alchemist_learn_object(o_ptr);
+ }
+ }
+ if ( lev == 25)
+ {
+ msg_print("You recall your old master reminiscing about legendary infusings");
+ msg_print("and the Philosophers' stone.");
+
+ /* No auto-learn on artifacts - by this level, you'll have *ID*'d several */
+ }
+ if ( lev == 25)
+ {
+ msg_print("You wonder about shocking daggers of slay evil.");
+ }
+ if ( lev == 50)
+ {
+ /* learn about Temporary item creation */
+ /* Note that this is the ONLY way to learn this,
+ because spells which create a temporary item
+ also fully ID it. */
+ alchemist_known_artifacts[4] |= TR5_TEMPORARY;
+ msg_print("It suddenly occurs to you that artifacts don't *HAVE* to be permanent...");
+ }
+
+ /* Every Four Levels, learn about items that are
+ * less than that.
+ * Note that this isn't a significant effect after the
+ * first few levels, as the level at which you are learning
+ * things here quickly drops behind the level at which you
+ * are finding items.
+ */
+ if ( (lev & 0x3) != 0 )
+ return;
+ lev = (lev >> 2) + 1;
+ alchemist_learn_all(lev);
+
+}
+
+/* This, in combination with some code in loadsave.c,
+ insures that alchemist_gain_level is called EXACTLY
+ once with each possible value during the characters
+ lifetime.
+ */
+void alchemist_check_level()
+{
+ u32b lev = get_skill(SKILL_ALCHEMY);
+ if ( alchemist_gained > lev )
+ return;
+ /*Paranoia*/
+ if ( !lev )
+ return;
+ while ( alchemist_gained <= lev )
+ alchemist_gain_level(alchemist_gained++);
+}
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'alchemist'.
+ */
+void do_cmd_alchemist(void)
+{
+ int item, ext = 0;
+ int value, basechance;
+ int askill;
+ bool repeat = 0;
+ char ch;
+
+ object_type *o_ptr, *q_ptr;
+ object_type forge, forge2;
+ byte carry_o_ptr = FALSE;
+
+ cptr q, s;
+
+ /* With the new skill system, we can no longer depend on
+ * check_exp to handle the changes and learning involved in
+ * gaining levels.
+ * So we'll have to check for it here.
+ */
+ alchemist_check_level();
+ askill = get_skill(SKILL_ALCHEMY);
+
+
+ q_ptr = &forge;
+
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+ if ((o_ptr->tval != TV_GLOVES) || (o_ptr->sval != SV_SET_OF_LEATHER_GLOVES))
+ {
+ msg_print("You must wear gloves in order to do alchemy.");
+ return;
+ }
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ while (TRUE)
+ {
+ if (!get_com("[P]ower, [R]echarge or [L]eech an item, [E]xtract essences, or recipe [B]ook?", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if (ch == ' ' )
+ {
+ ext = 0;
+ break;
+ }
+ if (ch == 'P' || ch == 'p')
+ {
+ ext = 1;
+ break;
+ }
+ if (ch == 'E' || ch == 'e')
+ {
+ ext = 2;
+ break;
+ }
+ if (ch == 'R' || ch == 'r')
+ {
+ ext = 3;
+ break;
+ }
+ if (ch == 'L' || ch == 'l')
+ {
+ ext = 2;
+ repeat = 1;
+ break;
+ }
+ if (ch == 'B' || ch == 'b')
+ {
+ ext = 4;
+ break;
+ }
+ }
+
+ /**********Add a power*********/
+ if (ext == 1)
+ {
+ int i, qty, tval, sval = 0, ego = 0;
+ char o_name[200];
+
+ /* Get an item */
+ q = "Empower which item? ";
+ s = "You have no empowerable items.";
+ item_tester_hook = item_tester_hook_empower;
+
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ /* Create an artifact from an ego or double ego item,
+ * from a previous artifact, or finish an artifact
+ */
+ if ((askill >= 25) && (artifact_p(o_ptr) || o_ptr->name2) && has_ability(AB_CREATE_ART))
+ {
+ if (get_check("Create an artifact?"))
+ {
+ do_cmd_toggle_artifact(o_ptr);
+ return;
+ }
+ /* Don't change artifacts or double ego items further */
+ else if (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b))
+ return;
+ }
+ /*Ok, now we have the item, so we can now pick recipes.
+ Note: No recipe is known unless we have 'extracted' from
+ that object type. I.E. the 'know' flag (also greater identify)
+ is set.
+ */
+
+ /* Here we're not setting what kind of ego item it IS,
+ * where' just deciding that it CAN be an ego item */
+ if ( o_ptr->name2 ) /* creating a DUAL ego */
+ ego = TRUE;
+ if ( o_ptr->tval < 40 && o_ptr->tval != TV_BOTTLE)
+ ego = TRUE;
+ if ( o_ptr->tval == TV_ROD_MAIN || o_ptr->tval == TV_DAEMON_BOOK || o_ptr->tval == TV_BOOK)
+ ego = TRUE;
+
+ sval = o_ptr->sval;
+ if (!ego)
+ {
+ switch ( o_ptr->tval)
+ {
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ }
+ }
+ if ( o_ptr->sval != sval )
+ ego = TRUE;
+
+ tval = o_ptr->tval;
+ sval = o_ptr->sval;
+
+ /*HACK - bottles don't have the same tval as potions*/
+ /*Everything else will have the same tval after empowering*/
+ if (tval == TV_BOTTLE) tval = TV_POTION;
+ if (ego)
+ if (o_ptr->name2)
+ ego = alchemist_recipe_select(&tval, sval, o_ptr->name2, FALSE);
+ else
+ ego = alchemist_recipe_select(&tval, sval, -1, FALSE);
+ else
+ sval = alchemist_recipe_select(&tval, 0, 0, FALSE);
+
+ if ( sval < 0 || ego < 0)
+ return;
+
+ /* Check to make sure we have enough essences */
+ /* theoretically this is taken care of by recipe_select*/
+ /* but we'll double check just for paranoia. */
+ if (!alchemist_items_check(tval, sval, ego, 0, TRUE))
+ {
+ msg_print("You do not have enough essences.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Use up the essences */
+ (void)alchemist_items_check(tval, sval, ego, -1, TRUE);
+
+ /* Enchant stacks of ammunition at a time */
+ if ( o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW || o_ptr->tval == TV_BOLT )
+ {
+ qty = 1;
+ while (qty < o_ptr->number && alchemist_items_check(tval, sval, ego, -1, FALSE))
+ qty++;
+ }
+ else
+ qty = 1;
+
+ /* Copy the object */
+ q_ptr = &forge;
+ object_copy(q_ptr, o_ptr);
+
+ if ( o_ptr->tval == TV_WAND)
+ {
+ /* distribute charges on wands */
+ q_ptr->pval = o_ptr->pval / o_ptr->number;
+ o_ptr->pval -= q_ptr->pval;
+ }
+
+ o_ptr = q_ptr;
+ o_ptr->number = qty;
+ carry_o_ptr = TRUE;
+
+ /* Destroy the initial object */
+ if (item >= 0)
+ {
+ /* Destroy an item in the pack */
+ inven_item_increase(item, -qty);
+ inven_item_describe(item);
+ }
+ else
+ {
+ /* Destroy an item on the floor */
+ floor_item_increase(0 - item, -qty);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+
+ if ( ego )
+ {
+ int pval, pval2;
+ s32b pval3;
+
+ pval = o_ptr->pval;
+ pval2 = o_ptr->pval2;
+ pval3 = o_ptr->pval3;
+
+ if (o_ptr->name2)
+ o_ptr->name2b = ego;
+ else
+ o_ptr->name2 = ego;
+ o_ptr->pval = randint(e_info[ego].max_pval - 1) + 1;
+ /* dilemma - how to prevent creation of cursed items,
+ * without allowing the creation of artifacts?
+ * We can't, unless we want to finalize the ego flags ourselves.
+ */
+ apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE);
+ /* Remember what the old pval was, so that we can re-apply it. */
+ if ( o_ptr->tval == TV_WAND
+ || o_ptr->tval == TV_RING
+ || o_ptr->tval == TV_AMULET
+ || o_ptr->tval == TV_STAFF)
+ {
+ o_ptr->pval = pval;
+ o_ptr->pval2 = pval2;
+ o_ptr->pval3 = pval3;
+ }
+ else if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ o_ptr->pval = pval;
+ }
+ else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255))
+ {
+ o_ptr->pval = pval;
+ }
+ else if (o_ptr->tval == TV_SHOT
+ || o_ptr->tval == TV_ARROW
+ || o_ptr->tval == TV_BOLT)
+ {
+ o_ptr->pval2 = pval2;
+ }
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ o_ptr->pval2 = pval2;
+ }
+
+ /* Calculate failure rate, lev=val/2500+5 */
+ value = MIN(e_info[o_ptr->name2].cost, 50000);
+ if (o_ptr->name2b) value += MIN(e_info[o_ptr->name2b].cost, 50000);
+ basechance = (value / 1000 + 5 - get_skill_scale(SKILL_ALCHEMY, 100) ) * 10;
+ if ( basechance < 0) basechance = 0;
+ if ( basechance > 100) basechance = 100;
+
+ value = object_value_real(o_ptr);
+
+ }
+ else /* not an ego item */
+ {
+ o_ptr = &forge;
+ object_wipe(o_ptr);
+ object_prep(o_ptr, lookup_kind(tval, sval));
+ hack_apply_magic_power = -99;
+ apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE);
+ if ( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF)
+ o_ptr->pval = 0;
+ value = object_value_real(o_ptr);
+
+ basechance = k_info[o_ptr->k_idx].level - askill * 2;
+ basechance *= 10;
+
+ /* Can't fail more that 100% of the time... */
+ if (basechance > 100)
+ basechance = 100;
+ /* Always success in creation of potion of detonations */
+ if (o_ptr->tval == TV_POTION && o_ptr->sval == SV_POTION_DETONATIONS)
+ {
+ basechance /= 10;
+#if 0 /* Let's see how it works */
+ o_ptr->discount = 100;
+#endif
+ }
+ }
+
+ /* Use up gold to create items */
+ /* this has the effect of making the alchemist
+ chronically short of funds, unless he finds the
+ philosopher's stone. It also means the easiest
+ things to make are 'bad', like a potion of
+ detonations...
+ */
+ /* Problem - to restrictive. We need something
+ which requires less money. But at the same time,
+ we don't want an 'easy cash' situation. Maybe something
+ like '10% * level difference', meaning at skill level 5,
+ level one items are free? But egos are frequently level
+ zero! Maybe egos are forced to level 25? with a cost ceiling?
+ I mean, Potions and scrolls are really the problem causing the
+ 'easy cash' situation, it's ego items. Ego items require
+ relatively few essences, and the rewards are HUGE. Most powerful
+ potions and scrolls require rare essences. Maybe force all egos
+ to require a magic essence? But then you'd get lots of magic
+ from distilling them. Maybe consumed in the creation? then when
+ you got a powerful item, you could make one ego item...
+ But if making things doesn't take gold, what about the cash
+ does the Philosopher's stone do?
+ Time*/
+
+ /* 0% failure if you have the stone */
+ if ( alchemist_has_stone())
+ basechance = 0;
+
+ if (basechance > 0 && value)
+ {
+ char string[80];
+ string[0] = '0';
+ string[1] = 0;
+
+ msg_format("The chance of success is only %d%%!", 100-basechance);
+ get_string("How much gold do you want to add?", string, 50);
+ i = atoi(string);
+ /* Note: don't trust the user to enter a positive number... */
+ if ( i < 0)
+ i = 0;
+ if ( i > p_ptr->au)
+ i = p_ptr->au;
+
+ if (i)
+ {
+ basechance = basechance - (i * 20) / value;
+ msg_format("The chance of success improved to %d%%.", 100-basechance);
+ }
+
+ if (randint(100) < basechance )
+ /*creation failed, even with the extra gold...*/
+ carry_o_ptr = FALSE;
+
+ /* Redraw gold */
+ p_ptr->au -= i;
+ p_ptr->redraw |= (PR_GOLD);
+ }
+
+ /* Set fully identified
+ * After all, the player just made it...
+ */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= IDENT_MENTAL;
+ o_ptr->found = OBJ_FOUND_SELFMADE;
+
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ if ( carry_o_ptr)
+ {
+ msg_format("You have successfully created %s %s",
+ (o_ptr->number > 1 ? "some" : (is_a_vowel(o_name[0]) ? "an" : "a")),
+ o_name);
+
+ if (inven_carry_okay(o_ptr))
+ inven_carry(o_ptr, FALSE);
+ else
+ {
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+ msg_format("You drop the %s", o_name);
+ }
+ carry_o_ptr = FALSE;
+ }
+ else /* don't carry, or in other words... */
+ {
+ int level = k_info[o_ptr->k_idx].level;
+ if (o_ptr->name1) /* created ego item */
+ level += e_info[o_ptr->name2].level;
+
+ msg_format("Your attempt backfires! Your %s explodes!", o_name);
+ take_hit(damroll(3, level - askill ) , "Alchemical Explosion");
+ p_ptr->redraw |= (PR_HP);
+ }
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Optimize the entire p_ptr->inventory - needed because we
+ don't know how many essences where used, and we may
+ have 'used up' a wielded item as well.
+ */
+ for ( item = 0 ; item < INVEN_TOTAL ; item++ )
+ inven_item_optimize(item);
+
+ /**********Extract a power*********/
+ }
+ else if (ext == 2)
+ {
+ int ego;
+ bool discharge_stick = FALSE;
+
+ /* s_ptr holds the empty items */
+ object_type *s_ptr = NULL;
+ bool carry_s_ptr = FALSE;
+
+ item_tester_hook = item_tester_hook_extractable;
+
+ /* Get an item */
+ q = "Extract from which item? ";
+ s = "You have no item to extract power from.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* This is to prevent creating magic essences by extracting
+ * from a recharged wand of dragon breath or something.
+ */
+ if (( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF )
+ && o_ptr->art_flags4 & TR4_RECHARGED)
+ {
+ msg_print("You cannot extract essences after it's been magically recharged.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Handle Rods before the loop, since they don't stack */
+ if (o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != SV_ROD_NOTHING)
+ {
+ rod_tip_extract(o_ptr);
+ return;
+ }
+
+ do
+ { /* Repeat (for leech command) */
+
+ /* Create the items.
+ * we don't care if they drop to the ground,
+ * and if no action was taken, return
+ */
+ ego = 0;
+ if ( o_ptr->name2)
+ ego = o_ptr->name2;
+
+ /* For ego staves and wands (not of nothing), discharge before extracting the ego */
+ discharge_stick = (o_ptr->pval > 0 &&
+ ((o_ptr->tval == TV_STAFF && o_ptr->sval != SV_STAFF_NOTHING) ||
+ (o_ptr->tval == TV_WAND && o_ptr->sval != SV_WAND_NOTHING)));
+ if (discharge_stick)
+ ego = 0;
+
+ if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, ego, 1, TRUE))
+ {
+ msg_print("You cannot extract anything from that item.");
+ return;
+ }
+
+ if (o_ptr->name2b && !alchemist_items_check(o_ptr->tval, o_ptr->sval, o_ptr->name2b, 1, TRUE))
+ {
+ /* do nothing - if the second ego can't be extracted
+ because there is no recipe for it, simply destroy it
+ */
+ }
+
+ /* Once in three times, learn how to make the item */
+ /* Sorry for the complicated if! Basically, if it's an
+ * unknown regular item or an unknown ego item, there's
+ * a one in 3 chance that it'll be id'd */
+ if (((!ego && !k_info[o_ptr->k_idx].know)
+ || (ego && !(alchemist_known_egos[ego / 32] & (1 << (ego % 32)))))
+ && randint(3) == 1)
+ {
+ msg_print("While destroying it, you gain insight into this item.");
+ /* If over level 10, the player has a chance of 'greater ID'
+ * on extracted items
+ */
+ if (askill > 9)
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+ alchemist_learn_object(o_ptr);
+ }
+
+ /* Always learn what kind of thing it is */
+ object_known(o_ptr);
+ object_aware(o_ptr);
+
+ /* If it's a wand or staff with charges (but not of nothing),
+ * decrease number of charges, unstacking if needed.
+ * Otherwise, create the 'of nothing' item and destroy the old one.
+ */
+ if (discharge_stick)
+ {
+ /* Unstack staves */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ /* Create one local copy of the staff */
+ q_ptr = &forge2;
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Unstack the copied staff */
+ o_ptr->number--;
+
+ /* Use the local copy of the staff */
+ o_ptr = q_ptr;
+ carry_o_ptr = TRUE;
+ }
+ /* remove one charge */
+ o_ptr->pval--;
+ }
+ else
+ {
+ /* Create the empty, plain item */
+ /* If the item was already created, increase the number */
+ if (carry_s_ptr)
+ {
+ s_ptr->number++;
+ }
+ else
+ {
+ /* Otherwise we must create a local copy of the empty item */
+ int tval, sval;
+ bool create_item = TRUE;
+
+ tval = o_ptr->tval;
+ if ( !ego && (tval == TV_POTION || tval == TV_POTION2))
+ tval = TV_BOTTLE;
+
+ sval = o_ptr->sval;
+
+ if (!ego)
+ {
+ switch ( tval)
+ {
+ case TV_WAND:
+ sval = SV_WAND_NOTHING;
+ break;
+ case TV_RING:
+ sval = SV_RING_NOTHING;
+ break;
+ case TV_STAFF:
+ sval = SV_STAFF_NOTHING;
+ break;
+ case TV_BOTTLE:
+ sval = 1;
+ break;
+ case TV_AMULET:
+ sval = SV_AMULET_NOTHING;
+ break;
+ case TV_SCROLL:
+ sval = SV_SCROLL_NOTHING;
+ break;
+ case TV_ROD:
+ sval = SV_ROD_NOTHING;
+ break;
+ default:
+ create_item = FALSE;
+ }
+ }
+
+ if (create_item)
+ {
+ /* Create the empty item */
+ s_ptr = &forge;
+ object_wipe(s_ptr);
+ object_prep(s_ptr, lookup_kind(tval, sval));
+ s_ptr->number = 1;
+
+ /* Force creation of non ego non cursed */
+ hack_apply_magic_power = -99;
+ apply_magic(s_ptr, 0, FALSE, FALSE, FALSE);
+
+ /* Hack -- remove possible curse */
+ if (cursed_p(s_ptr))
+ {
+ s_ptr->art_flags3 &= ~(TR3_CURSED | TR3_HEAVY_CURSE);
+ s_ptr->ident &= ~(IDENT_CURSED);
+ }
+
+ /* Restore pvals (e.g. charges ==0) of the item */
+ if (ego && ((tval == TV_WAND) || (tval == TV_STAFF) ||
+ (tval == TV_RING) || (tval == TV_AMULET)))
+ {
+ s_ptr->pval = o_ptr->pval;
+ s_ptr->pval2 = o_ptr->pval2;
+ s_ptr->pval3 = o_ptr->pval3;
+ }
+ /* Restore the spell stored in a random book */
+ else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255))
+ {
+ s_ptr->pval = o_ptr->pval;
+ }
+ /* Restore the type of explosive ammo */
+ else if (o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW
+ || o_ptr->tval == TV_BOLT)
+ {
+ s_ptr->pval2 = o_ptr->pval2;
+ }
+ /* Restore the music stored in an instrument */
+ else if (o_ptr->tval == TV_INSTRUMENT)
+ {
+ s_ptr->pval2 = o_ptr->pval2;
+ }
+
+ object_aware(s_ptr);
+ object_known(s_ptr);
+ s_ptr->ident |= IDENT_STOREB;
+
+ /* The empty item will be added to the p_ptr->inventory later */
+ carry_s_ptr = TRUE;
+ }
+ }
+
+ /* Now, we can delete the original (leeched) object.
+ * Is o_ptr an p_ptr->inventory / floor item or a local copy?
+ */
+ if (!carry_o_ptr)
+ {
+ /* Break the leech-loop if it was the last item */
+ if (o_ptr->number == 1)
+ repeat = 0;
+
+ if (item >= 0)
+ {
+ inven_item_increase(item, ( -1));
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase(0 - item, ( -1));
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+ else
+ {
+ /* Forget the local object */
+ carry_o_ptr = FALSE;
+
+ /* reset o_ptr to the original stack,
+ * which contains at least another item */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ }
+ }
+ }
+ while ( repeat == 1);
+
+ /* If we carry empty items, add them to the p_ptr->inventory */
+ if (carry_s_ptr)
+ inven_carry(s_ptr, TRUE);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /******* Recharge an item *******/
+ }
+ else if (ext == 3)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_recharge;
+
+ /* Get an item */
+ q = "Recharge which item? ";
+ s = "You have no rechargable items.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR ))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Make sure we have enough essences to recharge this */
+ if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, 0, TRUE))
+ {
+ msg_print("You don't have the essences to recharge this item.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Destroy the essences */
+ (void)alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, -1, TRUE);
+
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ /* Unstack staves */
+ /* Get local object */
+ q_ptr = &forge2;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = 1;
+
+ /* Unstack the used item */
+ o_ptr->number--;
+
+ o_ptr = q_ptr;
+ carry_o_ptr = TRUE;
+ }
+ o_ptr->pval++;
+ }
+ else if ( ext == 4)
+ {
+ alchemist_recipe_book();
+ }
+ /* Just in case - */
+ if (carry_o_ptr)
+ {
+ /* the o_ptr item was probably an unstacked staff
+ * Anyway, we need to add it to the p_ptr->inventory */
+ if (inven_carry_okay(o_ptr))
+ inven_carry(o_ptr, TRUE);
+ else
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+ }
+}
+
+
+/*
+ * Command to ask favors from your god.
+ */
+void do_cmd_pray(void)
+{
+ if (p_ptr->pgod == GOD_NONE)
+ {
+ msg_print("Pray hard enough and your prayers might be answered.");
+ return;
+ }
+ else
+ {
+ if (!p_ptr->praying)
+ msg_format("You start praying to %s.", deity_info[p_ptr->pgod].name);
+ else
+ msg_format("You stop praying to %s.", deity_info[p_ptr->pgod].name);
+ p_ptr->praying = !p_ptr->praying;
+
+ /* Update stuffs */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ p_ptr->redraw |= PR_PIETY | PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP;
+ energy_use = 100;
+ }
+}
+
+
+/*
+ * Return percentage chance of spell failure.
+ */
+int spell_chance_random(random_spell* rspell)
+{
+ int chance, minfail;
+
+
+ /* Extract the base spell failure rate */
+ chance = rspell->level + 25;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_THAUMATURGY) - rspell->level);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (rspell->mana > p_ptr->csp)
+ {
+ chance += 5 * (rspell->mana - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+
+
+
+/*
+ * Print a batch of spells.
+ */
+static void print_spell_batch(int batch, int max)
+{
+ char buff[80];
+
+ random_spell* rspell;
+
+ int i;
+
+
+ prt(format(" %-30s Lev Fail Mana Damage ", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ rspell = &random_spells[batch * 10 + i];
+
+ if (rspell->untried)
+ {
+ strnfmt(buff, 80, " %c) %-30s (Spell untried) ",
+ I2A(i), rspell->name);
+
+ }
+ else
+ {
+ strnfmt(buff, 80, " %c) %-30s %3d %4d%% %3d %3dd%d ",
+ I2A(i), rspell->name,
+ rspell->level, spell_chance_random(rspell), rspell->mana,
+ rspell->dam_dice, rspell->dam_sides);
+ }
+
+ prt(buff, 2 + i, 20);
+ }
+
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+static random_spell* select_spell_from_batch(int batch, bool quick)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ random_spell* ret;
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ if (spell_num < (batch + 1) * 10)
+ {
+ mut_max = spell_num - batch * 10;
+ }
+
+ strnfmt(tmp, 160, "(a-%c, * to list, A-%cto browse, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1), I2A(mut_max - 1) - 'a' + 'A');
+
+ prt(tmp, 0, 0);
+
+ if (quick)
+ {
+ print_spell_batch(batch, mut_max);
+ }
+
+ while (1)
+ {
+ /* Get a command */
+ which = inkey();
+
+ /* Abort */
+ if (which == ESCAPE)
+ {
+ /* No selection */
+ ret = NULL;
+
+ /* Leave the command loop */
+ break;
+
+ }
+
+ /* List */
+ if (which == '*' || which == '?' || which == ' ')
+ {
+ /* Print power list */
+ print_spell_batch(batch, mut_max);
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Accept default */
+ if (which == '\r')
+ {
+ /* There are no other choices */
+ if (mut_max == 1)
+ {
+ ret = &random_spells[batch * 10];
+
+ /* Leave the command loop */
+ break;
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Rename */
+ if (which == '/')
+ {
+ prt("Rename which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, random_spells[batch*10 + A2I(which)].name);
+ if (get_string("Name this power: ", out_val, 29))
+ {
+ strcpy(random_spells[batch*10 + A2I(which)].name, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ /* Comment */
+ if (which == '-')
+ {
+ prt("Comment which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, random_spells[batch*10 + A2I(which)].desc);
+ if (get_string("Comment this power: ", out_val, 29))
+ {
+ strcpy(random_spells[batch*10 + A2I(which)].desc, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+
+ /* Wait for next command */
+ continue;
+ }
+
+ if (isalpha(which) && isupper(which))
+ {
+ which = tolower(which);
+ c_prt(TERM_L_BLUE, format("%s : %s", random_spells[batch*10 + A2I(which)].name, random_spells[batch*10 + A2I(which)].desc), 0, 0);
+ inkey();
+ prt(tmp, 0, 0);
+ continue;
+ }
+ else if (isalpha(which) && (A2I(which) < mut_max))
+ {
+ /* Pick the power */
+ ret = &random_spells[batch * 10 + A2I(which)];
+
+ /* Leave the command loop */
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ /* Return selection */
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+random_spell* select_spell(bool quick)
+{
+ char tmp[160];
+
+ char which;
+
+ int batch_max = (spell_num - 1) / 10;
+
+ random_spell *ret;
+
+
+ /* Too confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You can't use your powers while confused!");
+ return NULL;
+ }
+
+ /* No spells available */
+ if (spell_num == 0)
+ {
+ msg_print("There are no spells you can cast.");
+ return NULL;
+ }
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max));
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = NULL;
+
+ break;
+ }
+
+ if (which == '\r')
+ {
+ if (batch_max == 0)
+ {
+ ret = select_spell_from_batch(0, quick);
+
+ break;
+ }
+
+ continue;
+ }
+
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ ret = select_spell_from_batch(A2I(which), quick);
+
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+void do_cmd_powermage(void)
+{
+ random_spell *s_ptr;
+
+ u32b proj_flags;
+
+ int dir, chance;
+
+ int ty = 0, tx = 0;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ s_ptr = select_spell(FALSE);
+
+ if (s_ptr == NULL) return;
+
+ if (p_ptr->csp < s_ptr->mana)
+ {
+ msg_print("You do not have enough mana.");
+ return;
+ }
+
+ /* Spell failure chance */
+ chance = spell_chance_random(s_ptr);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ /* Let time pass */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Mana is spent anyway */
+ p_ptr->csp -= s_ptr->mana;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+
+ return;
+ }
+
+
+ p_ptr->csp -= s_ptr->mana;
+
+ s_ptr->untried = FALSE;
+ proj_flags = s_ptr->proj_flags;
+
+ /* Hack -- Spell needs a target */
+ if ((s_ptr->proj_flags & PROJECT_BEAM) ||
+ (s_ptr->proj_flags & PROJECT_STOP))
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+
+ /* Mega-Hack -- Beam spells should continue through
+ * the target; bolt spells should stop at the
+ * target. --dsb */
+ if (s_ptr->proj_flags & PROJECT_BEAM)
+ proj_flags |= PROJECT_THRU;
+ }
+ else
+ {
+ /* Use the given direction */
+ ty = p_ptr->py + ddy[dir];
+ tx = p_ptr->px + ddx[dir];
+
+ /* Mega-Hack -- Both beam and bolt spells should
+ * continue through this fake target. --dsb */
+ proj_flags |= PROJECT_THRU;
+ }
+ }
+
+ if (s_ptr->proj_flags & PROJECT_BLAST)
+ {
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (s_ptr->proj_flags & PROJECT_VIEWABLE)
+ {
+ project_hack(s_ptr->GF, damroll(s_ptr->dam_dice, s_ptr->dam_sides));
+ }
+ else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER)
+ {
+ project_meteor(s_ptr->radius, s_ptr->GF,
+ damroll(s_ptr->dam_dice, s_ptr->dam_sides),
+ s_ptr->proj_flags);
+ }
+ else
+ {
+ project(0, s_ptr->radius, ty, tx,
+ damroll(s_ptr->dam_dice, s_ptr->dam_sides),
+ s_ptr->GF, proj_flags);
+ }
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+
+#if 0
+
+/*
+ * Incremental sleep spell -KMW-
+ */
+static void do_sleep_monster(void)
+{
+ int dir;
+
+ if (p_ptr->lev < 15)
+ {
+ if (!get_aim_dir(&dir)) return;
+ sleep_monster(dir);
+ }
+ else if (p_ptr->lev < 30)
+ {
+ sleep_monsters_touch();
+ }
+ else
+ {
+ sleep_monsters();
+ }
+}
+
+#endif /* 0 */
+
+
+#if 0
+
+/*
+ * Multiple Monster Fear -KMW-
+ */
+static bool fear_monsters(void)
+{
+ return (project_hack(GF_TURN_ALL, p_ptr->lev));
+}
+
+
+/*
+ * Close to Player Monster Fear -KMW-
+ */
+static bool fear_monsters_touch(void)
+{
+ int flg = PROJECT_KILL | PROJECT_HIDE;
+
+ return (project(0, 1, p_ptr->py, p_ptr->px, p_ptr->lev,
+ GF_TURN_ALL, flg));
+}
+
+
+/*
+ * Incremental fear spell -KMW-
+ */
+static void do_fear_monster(void)
+{
+ int dir;
+
+ if (p_ptr->lev < 15)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fear_monster(dir, p_ptr->lev);
+ }
+ else if (p_ptr->lev < 30)
+ {
+ fear_monsters_touch();
+ }
+ else
+ {
+ fear_monsters();
+ }
+}
+
+#endif /* 0 */
+
+
+/*
+ * Brand some ammunition. Used by Cubragol and a mage spell. The spell was
+ * moved here from cmd6.c where it used to be for Cubragol only. I've also
+ * expanded it to do either frost, fire or venom, at random. -GJW -KMW-
+ */
+void brand_ammo(int brand_type, int bolts_only)
+{
+ int a;
+
+ int allowable;
+
+
+ if (bolts_only)
+ {
+ allowable = TV_BOLT;
+ }
+ else
+ {
+ allowable = TV_BOLT | TV_ARROW | TV_SHOT;
+ }
+
+ for (a = 0; a < INVEN_PACK; a++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+
+ if (bolts_only && (o_ptr->tval != TV_BOLT)) continue;
+
+ if (!bolts_only && (o_ptr->tval != TV_BOLT) &&
+ (o_ptr->tval != TV_ARROW) && (o_ptr->tval != TV_SHOT))
+ continue;
+
+ if (!artifact_p(o_ptr) && !ego_item_p(o_ptr) &&
+ !cursed_p(o_ptr))
+ break;
+ }
+
+ /* Enchant the ammo (or fail) */
+ if ((a < INVEN_PACK) && (rand_int(100) < 50))
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+ char *ammo_name;
+ char *aura_name;
+ char msg[48];
+ int aura_type, r;
+
+ /* fire only */
+ if (brand_type == 1) r = 0;
+
+ /* cold only */
+ else if (brand_type == 2) r = 99;
+
+ /* No bias */
+ else r = rand_int(100);
+
+ if (r < 50)
+ {
+ aura_name = "fiery";
+ aura_type = EGO_FLAME;
+ }
+ else
+ {
+ aura_name = "frosty";
+ aura_type = EGO_FROST;
+ }
+
+ if (o_ptr->tval == TV_BOLT)
+ {
+ ammo_name = "bolts";
+ }
+ else if (o_ptr->tval == TV_ARROW)
+ {
+ ammo_name = "arrows";
+ }
+ else
+ {
+ ammo_name = "shots";
+ }
+
+ strnfmt(msg, 48, "Your %s are covered in a %s aura!",
+ ammo_name, aura_name);
+ msg_print(msg);
+
+ o_ptr->name2 = aura_type;
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ o_ptr->discount = 100;
+
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+ }
+ else
+ {
+ if (flush_failure) flush();
+ msg_print("The enchantment failed.");
+ }
+}
+
+
+/*
+ * From Kamband by Ivan Tkatchev
+ */
+void summon_monster(int sumtype)
+{
+ /* Take a turn */
+ energy_use = 100;
+
+ if (p_ptr->inside_arena)
+ {
+ msg_print("This place seems devoid of life.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level + randint(5), sumtype, TRUE))
+ {
+ msg_print("You summon some help.");
+ }
+ else
+ {
+ msg_print("You called, but no help came.");
+ }
+}
+
+
+
+/*
+ * Use a class power of Possessor
+ */
+void do_cmd_possessor()
+{
+ char ch, ext;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ while (TRUE)
+ {
+ if (!get_com("Use your [R]ace powers or your [I]ncarnating powers?", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'I') || (ch == 'i'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ if (ext == 1)
+ {
+ bool use_great = FALSE;
+
+ if (p_ptr->disembodied)
+ {
+ msg_print("You don't currently own a body to use.");
+ return;
+ }
+
+ /* Do we have access to all the powers ? */
+ if (get_skill_scale(SKILL_POSSESSION, 100) >= r_info[p_ptr->body_monster].level)
+ use_great = TRUE;
+
+ use_symbiotic_power(p_ptr->body_monster, use_great, FALSE, FALSE);
+
+ if (p_ptr->csp < 0)
+ {
+ msg_print("You lose control of your body!");
+ if (!do_cmd_leave_body(FALSE))
+ {
+ cmsg_print(TERM_VIOLET,
+ "You are forced back into your body by your cursed items, "
+ "you suffer a system shock!");
+
+ p_ptr->chp = 1;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+ }
+ }
+ }
+ else if (ext == 2)
+ {
+ if (p_ptr->disembodied)
+ {
+ do_cmd_integrate_body();
+ }
+ else
+ {
+ do_cmd_leave_body(TRUE);
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+}
+
+
+/*
+ * Hook to determine if an object is contertible in an arrow/bolt
+ */
+static bool item_tester_hook_convertible(object_type *o_ptr)
+{
+ if ((o_ptr->tval == TV_JUNK) || (o_ptr->tval == TV_SKELETON)) return TRUE;
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'archer'.
+ */
+void do_cmd_archer(void)
+{
+ int ext = 0;
+ char ch;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char com[80];
+
+
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ if (p_ptr->blind)
+ {
+ msg_print("You are blind!");
+ return;
+ }
+
+
+ if (get_skill(SKILL_ARCHERY) >= 20)
+ {
+ strnfmt(com, 80, "Create [S]hots, [A]rrows or [B]olts? ");
+ }
+ else if (get_skill(SKILL_ARCHERY) >= 10)
+ {
+ strnfmt(com, 80, "Create [S]hots or [A]rrows? ");
+ }
+ else
+ {
+ strnfmt(com, 80, "Create [S]hots? ");
+ }
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'S') || (ch == 's'))
+ {
+ ext = 1;
+ break;
+ }
+ if (((ch == 'A') || (ch == 'a')) && (get_skill(SKILL_ARCHERY) >= 10))
+ {
+ ext = 2;
+ break;
+ }
+ if (((ch == 'B') || (ch == 'b')) && (get_skill(SKILL_ARCHERY) >= 20))
+ {
+ ext = 3;
+ break;
+ }
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /**********Create shots*********/
+ if (ext == 1)
+ {
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_SHOT, m_bonus(2, dun_level)));
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some ammo.");
+
+ (void)wall_to_mud(dir);
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /**********Create arrows*********/
+ else if (ext == 2)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_convertible;
+
+ /* Get an item */
+ q = "Convert which item? ";
+ s = "You have no item to convert.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ q_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ q_ptr = &o_list[0 - item];
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some arrows */
+ object_prep(q_ptr, lookup_kind(TV_ARROW, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /**********Create bolts*********/
+ else if (ext == 3)
+ {
+ int item;
+
+ cptr q, s;
+
+ item_tester_hook = item_tester_hook_convertible;
+
+ /* Get an item */
+ q = "Convert which item? ";
+ s = "You have no item to convert.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ q_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ q_ptr = &o_list[0 - item];
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some bolts */
+ object_prep(q_ptr, lookup_kind(TV_BOLT, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+}
+
+/*
+ * Control whether shots are allowed to pierce
+ */
+void do_cmd_set_piercing(void)
+{
+ int ext = 0;
+ char ch;
+ char com[80];
+
+ strnfmt(com, 80, "Allow shots to pierce? ");
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'Y') || (ch == 'y'))
+ {
+ p_ptr->use_piercing_shots = 1;
+ break;
+ }
+ if ((ch == 'N') || (ch == 'n'))
+ {
+ p_ptr->use_piercing_shots = 0;
+ break;
+ }
+ }
+}
+/*
+ * Helper function to describe necro powers
+ */
+void necro_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_NECROMANCY);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 0:
+ {
+ if (p_ptr->to_s)
+ strnfmt(p, 80, " power %dd%d+%d", 2 + (plev * 2 / 3), 4, (p_ptr->to_s * 2));
+ else
+ strnfmt(p, 80, " power %dd%d", 2 + (plev * 2 / 3), 4);
+ break;
+ }
+ case 2:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 100 + (plev * 4), 200 + (plev * 3));
+ break;
+ }
+ case 3:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 30 + (plev * 2), 50 + plev);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a Necromancy spell
+ */
+void do_cmd_necromancer(void)
+{
+ int n = 0, b = 0;
+ int chance;
+ int dir;
+ int minfail = 0;
+ int plev = get_skill(SKILL_NECROMANCY);
+ magic_power spell;
+ int to_s2 = p_ptr->to_s / 2;
+ int mto_s2 = p_ptr->to_s / 2;
+
+
+ if (mto_s2 == 0) mto_s2 = 1;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, necro_powers, MAX_NECRO_POWERS, necro_info,
+ get_skill(SKILL_NECROMANCY), A_CON)) return;
+
+ spell = necro_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_CON]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_CON]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 10)
+ {
+ msg_print("Oh, no! You become undead!");
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = 2 * plev;
+ msg_format("You have to kill %d monster%s to be brought back to life.",
+ p_ptr->necro_extra2,
+ (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce maximum */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else if (b < 40)
+ {
+ msg_print("Suddenly you feel that you're in a bad situation...");
+ summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type],
+ (plev >= 30) ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD);
+ }
+ else
+ {
+ msg_print("Your body is damaged by the horrible forces of the spell!");
+ take_hit(damroll(5, plev), "using necromancy unwisely");
+ }
+ }
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Horrify */
+ case 0:
+ {
+ int dam = damroll(2 + (plev * 2 / 3), 4) + (p_ptr->to_s * 2);
+
+ if (plev > 45)
+ {
+ project_hack(GF_STUN, dam);
+ project_hack(GF_TURN_ALL, dam);
+ }
+ else if (plev > 35)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_STUN, dir, dam, 3 + (plev / 10));
+ fire_ball(GF_TURN_ALL, dir, dam, 3 + (plev / 10));
+ }
+ else if (plev > 20)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_beam(GF_STUN, dir, dam);
+ fire_beam(GF_TURN_ALL, dir, dam);
+ }
+ else
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_STUN, dir, dam);
+ fire_bolt(GF_TURN_ALL, dir, dam);
+ }
+
+ break;
+ }
+
+ /* Raise Death */
+ case 1:
+ {
+ fire_ball(GF_RAISE, 0, plev * 3, 1 + to_s2 + (plev / 10));
+
+ break;
+ }
+
+ /* Conjures temporary weapon */
+ case 2:
+ {
+ int dur = randint(100 + (plev * 4)) + 200 + (plev * 3);
+ object_type forge, *o_ptr = &forge;
+ int k_idx = test_item_name("& Necromantic Teeth~");
+
+ k_allow_special[k_idx] = TRUE;
+
+ object_prep(o_ptr, k_idx);
+ apply_magic(o_ptr, plev * 2, TRUE, TRUE, TRUE);
+
+ o_ptr->art_flags5 |= TR5_TEMPORARY;
+ o_ptr->timeout = dur;
+
+ /* These objects are "storebought" */
+ o_ptr->ident |= IDENT_MENTAL;
+ o_ptr->number = 1;
+
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ (void)inven_carry(o_ptr, FALSE);
+
+ k_allow_special[k_idx] = FALSE;
+
+ break;
+ }
+
+ /* Absorb souls */
+ case 3:
+ {
+ set_absorb_soul(randint(30 + (plev * 2)) + 50 + plev);
+ break;
+ }
+
+ /* Vampirism */
+ case 4:
+ {
+ int i;
+ if (!get_aim_dir(&dir)) return;
+ for (i = 0; i < 1 + to_s2 + (plev / 15); i++)
+ {
+ if (drain_life(dir, 100))
+ hp_player(100);
+ }
+
+ break;
+ }
+
+ /* Death */
+ case 5:
+ {
+ if (get_check("Using the Death word will leave you undead, with 1 DP. Do you *really* want to use it? "))
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_DEATH, dir, 1);
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = plev + (rand_int(plev / 2) - (plev / 4));
+ msg_format("You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce 1 DP */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage CON (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your body!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_CON, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/* Runecrafters -- Move this into variable.c XXX XXX XXX */
+static s32b rune_combine = 0;
+
+/*
+ * Hook to determine if an object is "runestone"
+ */
+static bool item_tester_hook_runestone(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval != RUNE_STONE) return (FALSE);
+
+ if (o_ptr->pval != 0) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+static bool item_tester_hook_runestone_full(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval != RUNE_STONE) return (FALSE);
+
+ if (o_ptr->pval == 0) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is "rune-able"
+ */
+static bool item_tester_hook_runeable1(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE1) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * Hook to determine if an object is "rune-able"
+ */
+static bool item_tester_hook_runeable2(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_RUNE2) return (FALSE);
+
+ if (o_ptr->sval == RUNE_STONE) return (FALSE);
+
+ if (rune_combine & BIT(o_ptr->sval)) return (FALSE);
+
+ /* Assume yes */
+ return (TRUE);
+}
+
+
+/*
+ * math.h(sqrt) is banned of angband so ... :)
+ */
+s32b sroot(s32b n)
+{
+ s32b i = n / 2;
+
+ if (n < 2) return (n);
+
+ while (1)
+ {
+ s32b err = (i - n / (i + 1)) / 2;
+
+ if (!err) break;
+
+ i -= err;
+ }
+
+ return ((n / i < i) ? (i - 1) : i);
+}
+
+
+/*
+ * Damage formula, for runes
+ */
+void rune_calc_power(s32b *power, s32b *powerdiv)
+{
+ /* Not too weak power(paranoia) */
+ *power = (*power < 1) ? 1 : *power;
+ *power += 3;
+
+ *power = 37 * sroot(*power) / 10;
+
+ /* To reduce the high level power, while increasing the low levels */
+ *powerdiv = *power / 3;
+ if (*powerdiv < 1) *powerdiv = 1;
+
+ /* Use the spell multiplicator */
+ *power *= (p_ptr->to_s / 2) ? (p_ptr->to_s / 2) : 1;
+}
+
+
+/*
+ * Return percentage chance of runespell failure.
+ */
+int spell_chance_rune(rune_spell* spell)
+{
+ int chance, minfail;
+
+ s32b power = spell->mana, power_rune = 0, powerdiv = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ chance = (5 * power_rune) + (power);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Return the chance */
+ return (chance);
+}
+
+
+/*
+ * Combine the Runes
+ */
+int rune_exec(rune_spell *spell, int cost)
+{
+ int dir, power_rune = 0, mana_used, plev = get_skill(SKILL_RUNECRAFT);
+
+ int chance;
+
+ s32b power, powerdiv;
+
+ int rad = 0, ty = -1, tx = -1, dam = 0, flg = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+
+ power = spell->mana;
+
+ if (cost && ((power * cost / 100) > p_ptr->csp - (power_rune * (plev / 5))))
+ {
+ power = p_ptr->csp - (power_rune * (plev / 5));
+ mana_used = power + (power_rune * (plev / 5));
+ }
+ else
+ {
+ mana_used = (power * cost / 100) + (power_rune * (plev / 5));
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ dam = damroll(powerdiv, power);
+
+ if (wizard) msg_format("Rune %dd%d = dam %d", powerdiv, power, dam);
+
+ /* Extract the base spell failure rate */
+ chance = spell_chance_rune(spell);
+
+ /* Failure ? */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+ return (mana_used);
+ }
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ flg |= (PROJECT_VIEWABLE);
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_ITEM);
+ flg |= (PROJECT_GRID);
+ flg |= (PROJECT_METEOR_SHOWER);
+ rad = (power / 8 == 0) ? 1 : power / 8;
+ rad = (rad > 10) ? 10 : rad;
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_ITEM);
+ flg |= (PROJECT_GRID);
+ rad = (power / 8 == 0) ? 1 : power / 8;
+ rad = (rad > 10) ? 10 : rad;
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ }
+
+ if (spell->rune2 & RUNE_RAY)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_KILL);
+ flg |= (PROJECT_BEAM);
+ ty = -1;
+ tx = -1;
+ }
+ if (spell->rune2 & RUNE_ARROW)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_STOP);
+ flg |= (PROJECT_KILL);
+ ty = -1;
+ tx = -1;
+ }
+ if (spell->rune2 & RUNE_SELF)
+ {
+ flg |= (PROJECT_THRU);
+ flg |= (PROJECT_STOP);
+ flg |= (PROJECT_KILL);
+ ty = p_ptr->py;
+ tx = p_ptr->px;
+ unsafe = TRUE;
+ }
+
+ if ((ty == -1) && (tx == -1))
+ {
+ if (!get_aim_dir(&dir)) return (mana_used);
+
+ /* Use the given direction */
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+
+ /* Hack -- Use an actual "target" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ }
+
+ if (flg & PROJECT_VIEWABLE)
+ {
+ project_hack(spell->type, dam);
+ }
+ else if (flg & PROJECT_METEOR_SHOWER)
+ {
+ project_meteor(rad, spell->type, dam, flg);
+ }
+ else project(0, rad, ty, tx, dam, spell->type, flg);
+
+ if (unsafe) unsafe = FALSE;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+
+ return (mana_used);
+}
+
+
+/*
+ * Test if all runes needed at in the player p_ptr->inventory
+ */
+bool test_runespell(rune_spell *spell)
+{
+ int i;
+
+ object_type *o_ptr;
+
+ bool typeok = FALSE;
+
+ int rune2 = 0;
+
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+
+ /* Does the rune1(type) match ? */
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == spell->type))
+ {
+ typeok = TRUE;
+ }
+
+ if ((o_ptr->tval == TV_RUNE2) && (o_ptr->sval != RUNE_STONE))
+ {
+ /* Add it to the list */
+ rune2 |= 1 << o_ptr->sval;
+ }
+ }
+
+ /* Need all runes to be present */
+ return (typeok && ((rune2 & spell->rune2) == spell->rune2));
+}
+
+
+/*
+ * Ask for rune, rune2 and mana
+ */
+bool get_runespell(rune_spell *spell)
+{
+ int item, power_rune = 0, rune2 = 0, plev = get_skill(SKILL_RUNECRAFT);
+
+ s32b power;
+
+ int type = 0;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ bool OK = FALSE;
+
+
+ rune_combine = 0;
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runeable1;
+
+ /* Get an item */
+ q = "Use which rune? ";
+ s = "You have no rune to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return FALSE;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ type = o_ptr->sval;
+
+ while (1)
+ {
+ /* Restrict choices to unused secondary runes */
+ item_tester_hook = item_tester_hook_runeable2;
+
+ OK = !get_item(&item, q, s, (USE_INVEN | USE_FLOOR));
+
+ if (OK) break;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+ rune_combine |= 1 << o_ptr->sval;
+ rune2 |= 1 << o_ptr->sval;
+ }
+
+ if (!rune2)
+ {
+ msg_print("You have not selected a second rune!");
+ return (FALSE);
+ }
+
+ power = get_quantity("Which amount of Mana?",
+ p_ptr->csp - (power_rune * (plev / 5)));
+ if (power < 1) power = 1;
+
+ spell->mana = power;
+ spell->type = type;
+ spell->rune2 = rune2;
+
+ return (TRUE);
+}
+
+
+void do_cmd_rune(void)
+{
+ rune_spell spell;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ if (!get_runespell(&spell)) return;
+
+ /* Execute at normal mana cost */
+ p_ptr->csp -= rune_exec(&spell, 100);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Print a batch of runespells.
+ */
+static void print_runespell_batch(int batch, int max)
+{
+ char buff[80];
+
+ rune_spell* spell;
+
+ int i;
+
+ s32b power, powerdiv;
+
+ int p, dp;
+
+
+ prt(format(" %-30s Fail Mana Power", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ spell = &rune_spells[batch * 10 + i];
+
+ power = spell->mana;
+ rune_calc_power(&power, &powerdiv);
+ p = power;
+ dp = powerdiv;
+
+ strnfmt(buff, 80, " %c) %-30s %4d%% %4d %dd%d ", I2A(i), spell->name,
+ spell_chance_rune(spell), spell->mana, dp, p);
+
+ prt(buff, 2 + i, 20);
+ }
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+
+static rune_spell* select_runespell_from_batch(int batch, bool quick,
+ int *s_idx)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ rune_spell* ret;
+
+
+ character_icky = TRUE;
+ Term_save();
+
+ if (rune_num < (batch + 1) * 10)
+ {
+ mut_max = rune_num - batch * 10;
+ }
+
+ strnfmt(tmp, 160, "(a-%c, * to list, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1));
+
+ prt(tmp, 0, 0);
+
+ if (quick)
+ {
+ print_runespell_batch(batch, mut_max);
+ }
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ *s_idx = -1;
+ ret = NULL;
+ break;
+ }
+ else if ((which == '*') || (which == '?') || (which == ' '))
+ {
+ print_runespell_batch(batch, mut_max);
+ }
+ else if ((which == '\r') && (mut_max == 1))
+ {
+ *s_idx = batch * 10;
+ ret = &rune_spells[batch * 10];
+ break;
+ }
+ else if (which == '/')
+ {
+ prt("Rename which power: ", 0, 0);
+ which = tolower(inkey());
+
+ if (isalpha(which) && (A2I(which) <= mut_max))
+ {
+ strcpy(out_val, rune_spells[batch*10 + A2I(which)].name);
+ if (get_string("Name this power: ", out_val, 29))
+ {
+ strcpy(rune_spells[batch*10 + A2I(which)].name, out_val);
+ }
+ prt(tmp, 0, 0);
+ }
+ else
+ {
+ bell();
+ prt(tmp, 0, 0);
+ }
+ }
+ else
+ {
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) < mut_max))
+ {
+ *s_idx = batch * 10 + A2I(which);
+ ret = &rune_spells[batch * 10 + A2I(which)];
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+ }
+
+ Term_load();
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+
+rune_spell* select_runespell(bool quick, int *s_idx)
+{
+ char tmp[160];
+
+ char which;
+
+ int batch_max = (rune_num - 1) / 10;
+
+ if (rune_num == 0)
+ {
+ msg_print("There are no runespells you can cast.");
+ return (NULL);
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max));
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (NULL);
+ }
+ else if ((which == '\r') && (batch_max == 0))
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (select_runespell_from_batch(0, quick, s_idx));
+
+ }
+ else
+ {
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ Term_load();
+ character_icky = FALSE;
+ return (select_runespell_from_batch(A2I(which), quick, s_idx));
+ }
+ else
+ {
+ bell();
+ }
+ }
+ }
+}
+
+
+/*
+ * Cast a memorized runespell
+ * Note that the only limits are antimagic & conf, NOT blind
+ */
+void do_cmd_rune_cast()
+{
+ rune_spell *s_ptr;
+
+ int s_idx;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ s_ptr = select_runespell(FALSE, &s_idx);
+
+ if (s_ptr == NULL) return;
+
+ /* Need the runes */
+ if (!test_runespell(s_ptr))
+ {
+ msg_print("You lack some essential rune(s) for this runespell!");
+ return;
+ }
+
+ /* Execute at normal mana cost */
+ p_ptr->csp -= rune_exec(s_ptr, 100);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Cast a runespell from a carved runestone
+ */
+void do_cmd_runestone()
+{
+ rune_spell s_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ int item;
+
+
+ /* Require some mana */
+ if (p_ptr->csp <= 0)
+ {
+ msg_print("You have no mana!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runestone_full;
+
+ /* Get an item */
+ q = "Cast from which runestone? ";
+ s = "You have no runestone to cast from.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ s_ptr.type = o_ptr->pval;
+ s_ptr.rune2 = o_ptr->pval2;
+ s_ptr.mana = o_ptr->pval3;
+
+ /* Execute less mana */
+ p_ptr->csp -= rune_exec(&s_ptr, 75);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Add a runespell to the list
+ */
+void do_cmd_rune_add_mem()
+{
+ rune_spell s_ptr;
+
+ rune_spell *ds_ptr = &rune_spells[rune_num];
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ if (rune_num >= MAX_RUNES)
+ {
+ msg_print("You have already learn the maximun number of runespells!");
+ return;
+ }
+
+ if (!get_runespell(&s_ptr)) return;
+
+ ds_ptr->type = s_ptr.type;
+ ds_ptr->rune2 = s_ptr.rune2;
+ ds_ptr->mana = s_ptr.mana;
+ strcpy(ds_ptr->name, "Unnamed Runespell");
+
+ get_string("Name this runespell: ", ds_ptr->name, 29);
+
+ rune_num++;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Carve a runespell onto a Runestone
+ */
+void do_cmd_rune_carve()
+{
+ rune_spell s_ptr;
+
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ int item, i;
+
+ char out_val[80];
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ if (!get_check("Beware, this will destroy the involved runes, continue?"))
+ {
+ return;
+ }
+
+ if (!get_runespell(&s_ptr)) return;
+
+ /* Restrict choices to unused runes */
+ item_tester_hook = item_tester_hook_runestone;
+
+ /* Get an item */
+ q = "Use which runestone? ";
+ s = "You have no runestone to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ o_ptr->pval = s_ptr.type;
+ o_ptr->pval2 = s_ptr.rune2;
+ o_ptr->pval3 = s_ptr.mana;
+
+ /* Start with nothing */
+ strcpy(out_val, "");
+
+ /* Use old inscription */
+ if (o_ptr->note)
+ {
+ /* Start with the old inscription */
+ strcpy(out_val, quark_str(o_ptr->note));
+ }
+
+ /* Get a new inscription (possibly empty) */
+ if (get_string("Name this runestone: ", out_val, 80))
+ {
+ /* Save the inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Delete the runes */
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx)
+ {
+ bool do_del = FALSE;
+
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == s_ptr.type)) do_del = TRUE;
+ if ((o_ptr->tval == TV_RUNE2) && (BIT(o_ptr->sval) & s_ptr.rune2)) do_del = TRUE;
+
+ if (do_del)
+ {
+ inven_item_increase(i, -1);
+ inven_item_optimize(i);
+ }
+ }
+ }
+
+ /* Take a turn -- Carving takes a LONG time */
+ energy_use = 400;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+/*
+ * Remove a runespell
+ */
+void do_cmd_rune_del()
+{
+ rune_spell *s_ptr;
+
+ int s_idx;
+
+ int i;
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ s_ptr = select_runespell(FALSE, &s_idx);
+
+ if (s_ptr == NULL) return;
+
+ /* Delete and move */
+ for (i = s_idx + 1; i < rune_num; i++)
+ {
+ rune_spells[i - 1].type = rune_spells[i].type;
+ rune_spells[i - 1].rune2 = rune_spells[i].rune2;
+ rune_spells[i - 1].mana = rune_spells[i].mana;
+ strcpy(rune_spells[i - 1].name, rune_spells[i].name);
+ }
+ rune_num--;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_MANA);
+}
+
+
+void do_cmd_rune_add()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Add to [M]emory(need runes to cast) or "
+ "Carve a [R]unestone(less mana to cast)", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'M') || (ch == 'm'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Create a Spell in memory */
+ case 1:
+ {
+ do_cmd_rune_add_mem();
+ break;
+ }
+
+ /* Carve a Runestone */
+ case 2:
+ {
+ do_cmd_rune_carve();
+ break;
+ }
+ }
+}
+
+
+void do_cmd_runecrafter()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Rune Spell:[C]reate, [D]elete, C[a]st, D[i]rectly Cast "
+ "or Use [R]unestone", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'C') || (ch == 'c'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'D') || (ch == 'd'))
+ {
+ ext = 2;
+ break;
+ }
+ if ((ch == 'A') || (ch == 'a'))
+ {
+ ext = 3;
+ break;
+ }
+ if ((ch == 'I') || (ch == 'i'))
+ {
+ ext = 4;
+ break;
+ }
+ if ((ch == 'R') || (ch == 'r'))
+ {
+ ext = 5;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Create a Spell */
+ case 1:
+ {
+ do_cmd_rune_add();
+ break;
+ }
+
+ /* Delete a Spell */
+ case 2:
+ {
+ do_cmd_rune_del();
+ break;
+ }
+
+ /* Cast a Spell */
+ case 3:
+ {
+ do_cmd_rune_cast();
+ break;
+ }
+
+ /* Directly Cast a Spell */
+ case 4:
+ {
+ do_cmd_rune();
+ break;
+ }
+
+ /* Cast a Runestone */
+ case 5:
+ {
+ do_cmd_runestone();
+ break;
+ }
+ }
+}
+
+
+void do_cmd_unbeliever_antimagic()
+{
+ if (get_skill(SKILL_ANTIMAGIC) < 20)
+ {
+ msg_print("You must have at least a level 20 antimagic skill "
+ "to be able to disrupt the magic continuum.");
+ return;
+ }
+
+ if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC)
+ {
+ p_ptr->antimagic_extra &= ~CLASS_ANTIMAGIC;
+ msg_print("You stop disrupting the magic continuum.");
+ }
+ else
+ {
+ p_ptr->antimagic_extra |= CLASS_ANTIMAGIC;
+ msg_print("You start disrupting the magic continuum.");
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+
+/*
+ * Detect traps + kill traps
+ */
+void do_cmd_unbeliever()
+{
+ int ext = 0;
+
+ char ch;
+
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("Disrupt [C]ontinuum or [D]etect Traps", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'C') || (ch == 'c'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 'D') || (ch == 'd'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ /* Disrupt Continuum */
+ case 1:
+ {
+ do_cmd_unbeliever_antimagic();
+ break;
+ }
+
+ /* Detect Traps */
+ case 2:
+ {
+ s16b skill = get_skill(SKILL_ANTIMAGIC);
+
+ if (skill < 25)
+ {
+ msg_print("You cannot use your detection abilities yet.");
+ break;
+ }
+
+ detect_traps(DEFAULT_RADIUS);
+
+ if (skill >= 35) destroy_doors_touch();
+
+ break;
+ }
+ }
+}
+
+/*
+ * Hook to determine if an object is totemable
+ */
+static bool item_tester_hook_totemable(object_type *o_ptr)
+{
+ /* Only full corpse */
+ if ((o_ptr->tval == TV_CORPSE) &&
+ ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON)))
+ {
+ return (TRUE);
+ }
+
+ /* Assume not */
+ return (FALSE);
+}
+
+
+/*
+ * Summoners
+ */
+void do_cmd_summoner_extract()
+{
+ object_type *o_ptr, forge, *q_ptr;
+
+ cptr q, s;
+
+ int item, r, e;
+
+ bool partial;
+
+
+ /* Not when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Require lite */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ item_tester_hook = item_tester_hook_totemable;
+
+ /* Get an item */
+ q = "Use which corpse? ";
+ s = "You have no corpse to use.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ /* Get the item (on the floor) */
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ if (r_info[o_ptr->pval2].flags1 & RF1_UNIQUE)
+ {
+ partial = FALSE;
+ }
+ else
+ {
+ partial = get_check("Do you want to create a partial totem?");
+ }
+
+ r = o_ptr->pval2;
+ e = o_ptr->pval3;
+
+ if (item > 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+ else
+ {
+ floor_item_increase( -item, -1);
+ floor_item_describe( -item);
+ floor_item_optimize( -item);
+ }
+
+ if (magik(r_info[o_ptr->pval2].level - get_skill(SKILL_SUMMON)))
+ {
+ msg_print("You failed to extract a totem.");
+ energy_use += 100;
+ return;
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /* Create the object */
+ object_prep(q_ptr, lookup_kind(TV_TOTEM, partial ? 1 : 2));
+ q_ptr->pval = r;
+ q_ptr->pval2 = 0;
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You extract a totem from the dead corpse.");
+ energy_use += 100;
+}
+
+
+void summon_true(int r_idx, int item)
+{
+ int i, status, x = 1, y = 1, rx, ry = 0, chance;
+
+ bool used;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+
+ /* Uniques are less likely to be nice */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Because it's unique, it will always be destroyed */
+ used = TRUE;
+
+ /* About twice as hard as non-uniques */
+ chance = (get_skill(SKILL_SUMMON) * 70 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Non-uniques are easier to handle */
+ else
+ {
+ if (get_skill(SKILL_SUMMON) == 0)
+ {
+ used = TRUE;
+ }
+ else
+ {
+ /* It can be used multiple times */
+ used = FALSE;
+
+ /* But it is not 100% sure (note: skill > 0) */
+ chance = (r_ptr->level * 25 / get_skill(SKILL_SUMMON));
+ if (magik(chance)) used = TRUE;
+ }
+
+ chance = (get_skill(SKILL_SUMMON) * 130 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ if (!(i = place_monster_one (y, x, r_idx, 0, 0, status)))
+ {
+ msg_print("The summoning fails.");
+ }
+ else
+ {
+ m_list[i].status = status;
+ m_list[i].mflag |= MFLAG_NO_DROP;
+ }
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Destroy the totem if the used flag is set */
+ if (used)
+ {
+ /* Eliminate the totem (from the pack) */
+ if (item >= 0)
+ {
+ inven_item_increase(item, -1);
+ inven_item_describe(item);
+ inven_item_optimize(item);
+ }
+
+ /* Eliminate the totem (from the floor) */
+ else
+ {
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ }
+
+ /* Done */
+ return;
+}
+
+
+void do_cmd_summoner_summon()
+{
+ int item, x = 1, y = 1, rx, ry, m_idx = 0, i;
+
+ cptr q, s;
+
+ object_type *o_ptr;
+
+ monster_type *m_ptr;
+
+
+ /* Which Totem? */
+ item_tester_tval = TV_TOTEM;
+
+ q = "Summon from which Totem?";
+ s = "There are no totems to summon from!";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return;
+
+ /* Access the item */
+ if (item >= 0)
+ {
+ o_ptr = &p_ptr->inventory[item];
+ }
+ else
+ {
+ o_ptr = &o_list[0 - item];
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* True Totems have their own function. */
+ if (o_ptr->sval == 2)
+ {
+ summon_true(o_ptr->pval, item);
+ return;
+ }
+
+ /* Handle partial totems */
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ place_monster_one_no_drop = TRUE;
+ m_idx = place_monster_one(y, x, o_ptr->pval, 0, 0, MSTATUS_PET);
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Failure. */
+ if (!m_idx)
+ {
+ msg_print("The summoning fails.");
+ }
+
+ /* Mark the monster as a "partial" ally */
+ m_ptr = &m_list[m_idx];
+ m_ptr->mflag |= MFLAG_PARTIAL | MFLAG_NO_DROP;
+}
+
+
+void do_cmd_summoner(void)
+{
+ int ext = 0;
+
+ char ch;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* not if blind */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("[E]xtract a totem, [S]ummon", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'E') || (ch == 'e'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 's') || (ch == 'S'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ case 1:
+ {
+ do_cmd_summoner_extract();
+ break;
+ }
+
+ case 2:
+ {
+ do_cmd_summoner_summon();
+ break;
+ }
+ }
+}
+
+
+/*
+ * Fighters may invoke The Rush.
+ */
+void do_cmd_blade(void)
+{
+ /* Are we already Rushed? */
+ if (p_ptr->rush)
+ {
+ msg_format("You have %d turns of The Rush remaining", p_ptr->rush);
+ return;
+ }
+
+ /* Are you sure? */
+ if (!get_check("Are you sure you want to invoke The Rush?")) return;
+
+ /* Let's Rush! */
+ set_rush(2 + p_ptr->lev / 2 + randint(p_ptr->lev / 2));
+}
+
+
+/*
+ * Dodge Chance Feedback.
+ */
+void use_ability_blade(void)
+{
+ int chance = p_ptr->dodge_chance - ((dun_level * 5) / 6);
+
+ if (chance < 0) chance = 0;
+ if (wizard)
+ {
+ msg_format("You have exactly %d chances of dodging a level %d monster.", chance, dun_level);
+ }
+
+ if (chance < 5)
+ {
+ msg_format("You have almost no chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 10)
+ {
+ msg_format("You have a slight chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 20)
+ {
+ msg_format("You have a significant chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 40)
+ {
+ msg_format("You have a large chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 70)
+ {
+ msg_format("You have a high chance of dodging a level %d monster.", dun_level);
+ }
+ else
+ {
+ msg_format("You will usually dodge successfully a level %d monster.", dun_level);
+ }
+
+ return;
+}
+
+/*
+ * Helper function to describe symbiotic powers
+ */
+void symbiotic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_SYMBIOTIC);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 2:
+ {
+ strnfmt(p, 80, " power %d", plev * 3);
+ break;
+ }
+ case 5:
+ {
+ strnfmt(p, 80, " heal %d%%", 15 + get_skill_scale(SKILL_SYMBIOTIC, 35));
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a symbiotic spell
+ */
+void do_cmd_symbiotic(void)
+{
+ int n = 0;
+ int chance;
+ int minfail = 0;
+ int plev = get_skill(SKILL_SYMBIOTIC);
+ magic_power spell;
+
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info,
+ get_skill(SKILL_SYMBIOTIC), A_INT)) return;
+
+ spell = symbiotic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ case 0:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ monster_race *r_ptr;
+ object_type *q_ptr;
+ object_type forge;
+
+ msg_print("Hypnotise which pet?");
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx)
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+ r_ptr = race_inf(m_ptr);
+
+ if (!(r_ptr->flags1 & RF1_NEVER_MOVE))
+ {
+ msg_print("You can only hypnotise monsters that cannot move.");
+ }
+ else if (m_ptr->status < MSTATUS_PET)
+ {
+ msg_print("You can only hypnotise pets and companions.");
+ }
+ else if (r_ptr->flags9 & RF9_SPECIAL_GENE)
+ {
+ msg_print("You cannot hypnotise this monster.");
+ }
+ else
+ {
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1));
+ q_ptr->number = 1;
+ q_ptr->pval = m_ptr->r_idx;
+ q_ptr->pval2 = m_ptr->hp;
+ q_ptr->pval3 = m_ptr->maxhp;
+ /* overflow alert */
+ q_ptr->exp = m_ptr->exp;
+ q_ptr->elevel = m_ptr->level;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ q_ptr->ident |= IDENT_STOREB;
+
+ drop_near(q_ptr, 0, y, x);
+
+ delete_monster(y, x);
+ health_who = 0;
+ }
+ }
+ else
+ {
+ msg_print("There is no pet here !");
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ monster_type *m_ptr;
+ int m_idx;
+ int item, x, y, d;
+ object_type *o_ptr;
+
+ cptr q, s;
+
+ /* Restrict choices to monsters */
+ item_tester_tval = TV_HYPNOS;
+
+ /* Get an item */
+ q = "Awaken which monster? ";
+ s = "You have no monster to awaken.";
+ if (!get_item(&item, q, s, (USE_FLOOR))) return;
+
+ o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d, 0);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ /* Have to be careful here; releasing the symbiote into a
+ * dungeon with leveled monsters will level the symbiote
+ * before we can get hold of it. We'll be nice and use the
+ * larger of the saved exp and the exp that the newly-generated
+ * monster starts with. */
+ m_ptr = &m_list[m_idx];
+ if (m_ptr->exp < o_ptr->exp)
+ {
+ m_ptr->exp = o_ptr->exp;
+ monster_check_experience(m_idx, TRUE);
+ if (m_ptr->level != o_ptr->elevel)
+ cmsg_format(TERM_VIOLET, "ERROR: level-%d HYPNOS becomes level-%d symbiote", o_ptr->elevel, m_ptr->level);
+ }
+ m_ptr->hp = o_ptr->pval2;
+ m_ptr->maxhp = o_ptr->pval3;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ break;
+ }
+
+ /* Charm */
+ case 2:
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return;
+
+ fire_bolt(GF_CHARM_UNMOVING, dir, plev * 3);
+
+ break;
+ }
+
+ /* Life Share */
+ case 3:
+ {
+ s32b percent1, percent2;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ r_ptr = &r_info[o_ptr->pval];
+
+ percent1 = p_ptr->chp;
+ percent1 = (percent1 * 100) / p_ptr->mhp;
+
+ percent2 = o_ptr->pval2;
+ percent2 = (percent2 * 100) / o_ptr->pval3;
+
+ /* Now get the average */
+ percent1 = (percent1 + percent2) / 2;
+
+ /* And set the hp of monster & player to it */
+ p_ptr->chp = (percent1 * p_ptr->mhp) / 100;
+ o_ptr->pval2 = (percent1 * o_ptr->pval3) / 100;
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+
+ break;
+ }
+
+ /* Minor Symbiotic Powers */
+ case 4:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if (0 > use_symbiotic_power(o_ptr->pval, FALSE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Heal Symbiote */
+ case 5:
+ {
+ int hp;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ r_ptr = &r_info[o_ptr->pval];
+ hp = o_ptr->pval3 * (15 + get_skill_scale(SKILL_SYMBIOTIC, 35)) / 100;
+ o_ptr->pval2 += hp;
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ msg_format("%s is healed.", symbiote_name(TRUE));
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_MH);
+
+ break;
+ }
+
+
+ /* Major Symbiotic Powers */
+ case 6:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if(0 > use_symbiotic_power(o_ptr->pval, TRUE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Summon never-moving pet */
+ case 7:
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE);
+
+ break;
+ }
+
+ /* Force Symbiosis */
+ case 8:
+ {
+ int y, x;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+
+ if (!tgt_pt(&x, &y)) return;
+
+ c_ptr = &cave[y][x];
+
+ if (!c_ptr->m_idx) break;
+
+ m_ptr = &m_list[c_ptr->m_idx];
+ use_symbiotic_power(m_ptr->r_idx, TRUE, FALSE, TRUE);
+
+ break;
+ }
+
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(p_ptr->paralyzed + randint(5 * oops + 1));
+
+ /* Damage CON (possibly permanently) */
+ if (rand_int(100) < 50)
+ {
+ bool perm = (rand_int(100) < 25);
+
+ /* Message */
+ msg_print("You have damaged your body!");
+
+ /* Reduce constitution */
+ (void)dec_stat(A_CHR, 15 + randint(10), perm);
+ }
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Boulder creation .. sorry :)
+ */
+void do_cmd_create_boulder()
+{
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Granite -- How about other wall types? */
+ if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) ||
+ ((c_ptr->feat >= FEAT_MAGMA_H) && (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ)))
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ (void)wall_to_mud(dir);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_JUNK, SV_BOULDER));
+ q_ptr->number = (byte)rand_range(2, 5);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some boulders.");
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+}