From 9c96524cedd38e9fc5ca23130729db62113c844c Mon Sep 17 00:00:00 2001 From: Andrio Celos <6897624+AndrioCelos@users.noreply.github.com> Date: Tue, 10 Nov 2020 20:25:32 +1100 Subject: [PATCH 1/2] Allow cancelling spells at the direction/target prompt --- include/extern.h | 2 +- outdated/sys/msdos/SCHEMA35.MSC | 9 +- outdated/sys/msdos/schema3.MSC | 9 +- src/apply.c | 2 +- src/pray.c | 2 +- src/spell.c | 438 +++++++++++++++++++------------- src/teleport.c | 5 +- 7 files changed, 279 insertions(+), 188 deletions(-) diff --git a/include/extern.h b/include/extern.h index e24e605b6e..1e114b5959 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2523,8 +2523,8 @@ E void FDECL(book_disappears, (struct obj *)); E void FDECL(book_substitution, (struct obj *, struct obj *)); E void NDECL(age_spells); E int NDECL(docast); +E int FDECL(docastspecial, (int, BOOLEAN_P)); E int FDECL(spell_skilltype, (int)); -E int FDECL(spelleffects, (int, BOOLEAN_P)); E int FDECL(tport_spell, (int)); E void NDECL(losespells); E int NDECL(dovspell); diff --git a/outdated/sys/msdos/SCHEMA35.MSC b/outdated/sys/msdos/SCHEMA35.MSC index 9f9c37d319..5077f08448 100644 --- a/outdated/sys/msdos/SCHEMA35.MSC +++ b/outdated/sys/msdos/SCHEMA35.MSC @@ -20,7 +20,7 @@ functions:0 _tty_dismiss_nhwindow _gender _genl_outrip _get_cost _get_free_room_ functions:0 _get_location _get_map _get_mleash ; functions:1 _move_update _movebubbles _movecmd _movemon _moverock _movobj _mpickgold -functions:1 _doaltarobj _doapply _dobreathe _docall _docast +functions:1 _doaltarobj _doapply _dobreathe _docall _docast _docastspecial ; functions:2 _do_vicinity_map functions:3 _pcmain @@ -240,19 +240,19 @@ functions:215 _shuffle_all _shuffle_tiles _simple_look _singular _sitoa _skill_a functions:216 _skinback _sleep_monst _slept_monst _slip_or_trip _sliparm _slots_required _snuff_candle _snuff_light_source functions:217 _snuff_lit _sobj_at _some_armor _somegold _somex _somexy _somey _sort_rooms functions:218 _sort_valuables _sp_lev_shuffle _spec_ability _spec_abon _spec_applies _spec_dbon _spell_damage_bonus _spell_hit_bonus -functions:219 _spell_skilltype _spelleffects _spitmu _splatter_burning_oil _split_mon _split_rects _splitbill _splitobj +functions:219 _spell_skilltype _spelleffects _spellhunger _spitmu _splatter_burning_oil _split_mon _split_rects _splitbill _splitobj functions:220 _spoteffects _squadmon _srandom _stackobj _standoutbeg _standoutend _start_corpse_timeout _start_eating functions:221 _start_engulf _start_timer _start_tin _steal _steal_it _stealamulet _stealarm _stealgold functions:223 _stop_timer _store_version _strange_feeling _strategy _string_for_env_opt _string_for_opt _strncmpi _strprepend functions:224 _strstri _study_book _stumble_onto_mimic _sub_one_frombill _subfrombill _substitute_tiles _summon_minion _surface functions:225 _swallow_to_glyph _swapin_file _swapout_oldest _switchar _t_warn -functions:226 _tabexpand _tactics _take_gold _take_off _tamedog _target_on _tele _tele_jump_ok +functions:226 _tabexpand _tactics _take_gold _take_off _tamedog _target_on _targetspell _tele _tele_jump_ok functions:227 _tele_restrict _tele_trap _teleds _teleok _teleport_pet _temple_occupied _tended_shop _term_end_attr functions:228 _term_end_color functions:229 _The _this_type_only _thitm _thitmonst _thitu _throw_gold _throwing_weapon _throwit functions:230 _thrwmu _tileview _timed_occupation _timer_is_local _timer_sanity_check _tinnable _title_to_mon _tmp_at functions:231 _topl_putsym _topologize _topten _topten_print _topten_print_bold _toss_up _toss_wsegs _touch_artifact -functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tt_oname _tty_add_menu +functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tryspell _tt_oname _tty_add_menu functions:233 _tty_askname _tty_clear_nhwindow _tty_cliparound functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _tty_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event functions:235 _tty_getlin _tty_init_nhwindows _tty_mark_synch _tty_message_menu _tty_nh_poskey _tty_nhbell _tty_nhgetch _tty_number_pad @@ -517,4 +517,3 @@ functions:478 _zapsetup _zapwrapup _comp_times _directionname _fname_decode _fna ;functions:479 _free_edog _free_egd _free_emin _free_eshk _free_olong _free_saved_games ;functions:480 _genl_can_suspend_no _genl_getmsghistory _genl_message_menu _genl_putmsghistory ;functions:481 _get_saved_games _initstate _set_savepref _set_wc_option_mod_status _set_wc2_option_mod_status _setstate _uint_to_any - diff --git a/outdated/sys/msdos/schema3.MSC b/outdated/sys/msdos/schema3.MSC index 33ad97bff7..0a9ab8b344 100644 --- a/outdated/sys/msdos/schema3.MSC +++ b/outdated/sys/msdos/schema3.MSC @@ -20,7 +20,7 @@ functions:0 _tty_dismiss_nhwindow _gender _genl_outrip _get_cost _get_free_room_ functions:0 _get_location _get_map _get_mleash ; functions:1 _move_update _movebubbles _movecmd _movemon _moverock _movobj _mpickgold -functions:1 _doaltarobj _doapply _dobreathe _docall _docast +functions:1 _doaltarobj _doapply _dobreathe _docall _docast _docastspecial ; functions:2 _do_vicinity_map functions:3 _pcmain @@ -240,19 +240,19 @@ functions:215 _shuffle_all _shuffle_tiles _simple_look _singular _sitoa _skill_a functions:216 _skinback _sleep_monst _slept_monst _slip_or_trip _sliparm _slots_required _snuff_candle _snuff_light_source functions:217 _snuff_lit _sobj_at _some_armor _somegold _somex _somexy _somey _sort_rooms functions:218 _sort_valuables _sp_lev_shuffle _spec_ability _spec_abon _spec_applies _spec_dbon _spell_damage_bonus _spell_hit_bonus -functions:219 _spell_skilltype _spelleffects _spitmu _splatter_burning_oil _split_mon _split_rects _splitbill _splitobj +functions:219 _spell_skilltype _spelleffects _spellhunger _spitmu _splatter_burning_oil _split_mon _split_rects _splitbill _splitobj functions:220 _spoteffects _squadmon _srandom _stackobj _standoutbeg _standoutend _start_corpse_timeout _start_eating functions:221 _start_engulf _start_timer _start_tin _steal _steal_it _stealamulet _stealarm _stealgold functions:223 _stop_timer _store_version _strange_feeling _strategy _string_for_env_opt _string_for_opt _strncmpi _strprepend functions:224 _strstri _study_book _stumble_onto_mimic _sub_one_frombill _subfrombill _substitute_tiles _summon_minion _surface functions:225 _swallow_to_glyph _swapin_file _swapout_oldest _switch_symbols _switchar _t_warn -functions:226 _tabexpand _tactics _take_gold _take_off _tamedog _target_on _tele _tele_jump_ok +functions:226 _tabexpand _tactics _take_gold _take_off _tamedog _target_on _targetspell _tele _tele_jump_ok functions:227 _tele_restrict _tele_trap _teleds _teleok _teleport_pet _temple_occupied _tended_shop _term_end_attr functions:228 _term_end_color functions:229 _The _this_type_only _thitm _thitmonst _thitu _throw_gold _throwing_weapon _throwit functions:230 _thrwmu _tileview _timed_occupation _timer_is_local _timer_sanity_check _tinnable _title_to_mon _tmp_at functions:231 _topl_putsym _topologize _topten _topten_print _topten_print_bold _toss_up _toss_wsegs _touch_artifact -functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tt_oname _tty_add_menu +functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tryspell _tt_oname _tty_add_menu functions:233 _tty_askname _tty_clear_nhwindow _tty_cliparound functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _tty_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event functions:235 _tty_getlin _tty_init_nhwindows _tty_mark_synch _tty_message_menu _tty_nh_poskey _tty_nhbell _tty_nhgetch _tty_number_pad @@ -664,4 +664,3 @@ functions:626 _wiz_level_change _wiz_map_terrain _wiz_mon_polycontrol _wiz_panic functions:627 _wiz_polyself _wiz_rumor_check _wiz_smell _worst_cursed_item functions:628 _yn_function _yobjnam _Yobjnam2 _You_see functions:629 _ysimple_name - diff --git a/src/apply.c b/src/apply.c index 9c742959c3..b07c00f877 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1774,7 +1774,7 @@ int magic; /* 0=Physical, otherwise skill level */ if (g.spl_book[sp_no].sp_id == NO_SPELL) break; else if (g.spl_book[sp_no].sp_id == SPE_JUMPING) - return spelleffects(sp_no, FALSE); + return docastspecial(sp_no, FALSE); } if (!magic && (nolimbs(g.youmonst.data) || slithy(g.youmonst.data))) { diff --git a/src/pray.c b/src/pray.c index f6ce19b1a4..e4d75b38a2 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1993,7 +1993,7 @@ doturn() if (g.spl_book[sp_no].sp_id == NO_SPELL) break; else if (g.spl_book[sp_no].sp_id == SPE_TURN_UNDEAD) - return spelleffects(sp_no, FALSE); + return docastspecial(sp_no, TRUE); } You("don't know how to turn undead!"); return 0; diff --git a/src/spell.c b/src/spell.c index 160a8ed5d6..fb6c9d069d 100644 --- a/src/spell.c +++ b/src/spell.c @@ -32,6 +32,9 @@ static void FDECL(deadbook, (struct obj *)); static int NDECL(learn); static boolean NDECL(rejectcasting); static boolean FDECL(getspell, (int *)); +static void FDECL(spellhunger, (int)); +static int FDECL(tryspell, (int)); +static int FDECL(targetspell, (int, boolean)); static int FDECL(CFDECLSPEC spell_cmp, (const genericptr, const genericptr)); static void NDECL(sortspells); @@ -39,9 +42,10 @@ static boolean NDECL(spellsortmenu); static boolean FDECL(dospellmenu, (const char *, int, int *)); static int FDECL(percent_success, (int)); static char *FDECL(spellretention, (int, char *)); -static int NDECL(throwspell); +static int FDECL(throwspell, (boolean)); static void NDECL(cast_protection); static void FDECL(spell_backfire, (int)); +static int FDECL(spelleffects, (int)); static const char *FDECL(spelltypemnemonic, (int)); static boolean FDECL(spell_aim_step, (genericptr_t, int, int)); @@ -730,15 +734,234 @@ int *spell_no; spell_no); } +static void +spellhunger(spell_no) +int spell_no; +{ + if (spellid(spell_no) != SPE_DETECT_FOOD) { + int hungr = spellev(spell_no) * 10; + + /* If hero is a wizard, their current intelligence + * (bonuses + temporary + current) + * affects hunger reduction in casting a spell. + * 1. int = 17-25 no cost + * 2. int = 16 1/4 cost + * 3. int = 15 1/2 cost + * 4. int = 1-14 normal cost + * The reason for this is: + * a) Intelligence affects the amount of exertion + * in thinking. + * b) Wizards have spent their life at magic and + * understand quite well how to cast spells. + */ + if (Role_if(PM_WIZARD)) { + switch (acurr(A_INT)) { + case 25: + case 24: + case 23: + case 22: + case 21: + case 20: + case 19: + case 18: + case 17: + hungr = 0; + break; + case 16: + hungr /= 4; + break; + case 15: + hungr /= 2; + break; + } + } + /* don't put player (quite) into fainting from + * casting a spell, particularly since they might + * not even be hungry at the beginning; however, + * this is low enough that they must eat before + * casting anything else except detect food + */ + if (hungr > u.uhunger - 3) + hungr = u.uhunger - 3; + morehungry(hungr); + } +} + +/* + * Return 1 if we successfully cast the spell, 0 if we failed without using a move, + * 2 if we failed but used a move anyway, or 3 if we succeeded but force using a move + * even if cancelled (due to Amulet energy drain). + * Applies failure energy cost and Amulet drain but not other costs. + */ +static int +tryspell(spell_no) +int spell_no; +{ + int energy, hunger, chance, res = 0; + boolean confused = (Confusion != 0); + boolean had_energy; + + /* + * Reject attempting to cast while stunned or with no free hands. + * Already done in getspell() to stop casting before choosing + * which spell, but duplicated here for cases where tryspell() + * gets called directly for ^T without intrinsic teleport capability, + * #turn for non-priest/non-knight or j without intrinsic jumping. + * (There's no duplication of messages; when the rejection takes + * place in getspell(), we don't get called.) + */ + if (rejectcasting()) { + return 0; + } + + /* + * Spell casting no longer affects knowledge of the spell. A + * decrement of spell knowledge is done every turn. + */ + if (spellknow(spell_no) <= 0) { + Your("knowledge of this spell is twisted."); + pline("It invokes nightmarish images in your mind..."); + spell_backfire(spell_no); + return 2; + } else if (spellknow(spell_no) <= KEEN / 200) { /* 100 turns left */ + You("strain to recall the spell."); + } else if (spellknow(spell_no) <= KEEN / 40) { /* 500 turns left */ + You("have difficulty remembering the spell."); + } else if (spellknow(spell_no) <= KEEN / 20) { /* 1000 turns left */ + Your("knowledge of this spell is growing faint."); + } else if (spellknow(spell_no) <= KEEN / 10) { /* 2000 turns left */ + Your("recall of this spell is gradually fading."); + } + + /* + * Note: dotele() also calculates energy use and checks nutrition + * and strength requirements; it any of these change, update it too. + */ + energy = (spellev(spell_no) * 5); /* 5 <= energy <= 35 */ + + if (u.uhunger <= 10 && spellid(spell_no) != SPE_DETECT_FOOD) { + You("are too hungry to cast that spell."); + return 0; + } else if (ACURR(A_STR) < 4 && spellid(spell_no) != SPE_RESTORE_ABILITY) { + You("lack the strength to cast spells."); + return 0; + } else if (check_capacity( + "Your concentration falters while carrying so much stuff.")) { + return 2; + } + + /* if the cast attempt is already going to fail due to insufficient + energy (ie, u.uen < energy), the Amulet's drain effect won't kick + in and no turn will be consumed; however, when it does kick in, + the attempt may fail due to lack of energy after the draining, in + which case a turn will be used up in addition to the energy loss */ + had_energy = u.uen >= energy; + if (u.uhave.amulet && had_energy) { + You_feel("the amulet draining your energy away."); + /* this used to be 'energy += rnd(2 * energy)' (without 'res'), + so if amulet-induced cost was more than u.uen, nothing + (except the "don't have enough energy" message) happened + and player could just try again (and again and again...); + now we drain some energy immediately, which has a + side-effect of not increasing the hunger aspect of casting */ + u.uen -= rnd(2 * energy); + if (u.uen < 0) + u.uen = 0; + g.context.botl = 1; + res = 2; /* time is going to elapse even if spell doesn't get cast */ + } + + if (energy > u.uen) { + You("%s have enough energy to cast that spell.", had_energy ? "no longer" : "don't"); + return res; + } + + chance = percent_success(spell_no); + if (confused || (rnd(100) > chance)) { + spellhunger(spell_no); /* Failing to cast a spell uses energy, but cancelling it doesn't. */ + You("fail to cast the spell correctly."); + u.uen -= energy / 2; + g.context.botl = 1; + return 2; + } + + return 1 + res; +} + +/* + * Return 1 if a target was chosen or the spell does not require a target, + * 0 if the player cancelled out of the prompt or chose an invalid target + * (fireball, cone of cold) + */ +static int +targetspell(spell_no, force_move) +int spell_no; +boolean force_move; +{ + switch (spellid(spell_no)) + { + case SPE_FIREBALL: + case SPE_CONE_OF_COLD: + if (P_SKILL(spell_skilltype(spellid(spell_no))) >= P_SKILLED) { + return throwspell(force_move); + } + /*FALLTHRU*/ + case SPE_FORCE_BOLT: + case SPE_SLEEP: + case SPE_MAGIC_MISSILE: + case SPE_KNOCK: + case SPE_SLOW_MONSTER: + case SPE_WIZARD_LOCK: + case SPE_DIG: + case SPE_TURN_UNDEAD: + case SPE_POLYMORPH: + case SPE_TELEPORT_AWAY: + case SPE_CANCELLATION: + case SPE_FINGER_OF_DEATH: + case SPE_LIGHT: + case SPE_DETECT_UNSEEN: + case SPE_HEALING: + case SPE_EXTRA_HEALING: + case SPE_DRAIN_LIFE: + case SPE_STONE_TO_FLESH: + if (!getdir((char *) 0)) { + pline1(force_move ? nothing_happens : Never_mind); + return FALSE; + } + break; + } + return TRUE; +} + + /* the 'Z' command -- cast a spell */ int docast() { - int spell_no; + int spell_no, res; + if (!getspell(&spell_no)) return 0; + res = tryspell(spell_no); + if (!(res & 1)) return res != 0; /* spell failed; may have used a move */ + if (!targetspell(spell_no, res == 3)) return res == 3; + return spelleffects(spell_no); +} - if (getspell(&spell_no)) - return spelleffects(spell_no, FALSE); - return 0; +/* + * Cast a specific spell, possibly skipping the target prompt. + * If target is FALSE, we assume the caller has already set a target (^T). + */ +int +docastspecial(spell_no, target) +int spell_no; +boolean target; +{ + int res; + res = tryspell(spell_no); + if (!(res & 1)) return res != 0; /* spell failed; may have used a move */ + if (target) { + if (!targetspell(spell_no, res == 3)) return res == 3; + } + return spelleffects(spell_no); } static const char * @@ -895,9 +1118,8 @@ int spell; } int -spelleffects(spell, atme) +spelleffects(spell) int spell; -boolean atme; { int energy, damage, chance, n, intell; int otyp, skill, role_skill, res = 0; @@ -906,137 +1128,20 @@ boolean atme; struct obj *pseudo; coord cc; - /* - * Reject attempting to cast while stunned or with no free hands. - * Already done in getspell() to stop casting before choosing - * which spell, but duplicated here for cases where spelleffects() - * gets called directly for ^T without intrinsic teleport capability - * or #turn for non-priest/non-knight. - * (There's no duplication of messages; when the rejection takes - * place in getspell(), we don't get called.) - */ - if (rejectcasting()) { - return 0; /* no time elapses */ - } + spellhunger(spell); - /* - * Spell casting no longer affects knowledge of the spell. A - * decrement of spell knowledge is done every turn. - */ - if (spellknow(spell) <= 0) { - Your("knowledge of this spell is twisted."); - pline("It invokes nightmarish images in your mind..."); - spell_backfire(spell); - return 1; - } else if (spellknow(spell) <= KEEN / 200) { /* 100 turns left */ - You("strain to recall the spell."); - } else if (spellknow(spell) <= KEEN / 40) { /* 500 turns left */ - You("have difficulty remembering the spell."); - } else if (spellknow(spell) <= KEEN / 20) { /* 1000 turns left */ - Your("knowledge of this spell is growing faint."); - } else if (spellknow(spell) <= KEEN / 10) { /* 2000 turns left */ - Your("recall of this spell is gradually fading."); - } /* * Note: dotele() also calculates energy use and checks nutrition * and strength requirements; it any of these change, update it too. */ energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */ - if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) { - You("are too hungry to cast that spell."); - return 0; - } else if (ACURR(A_STR) < 4 && spellid(spell) != SPE_RESTORE_ABILITY) { - You("lack the strength to cast spells."); - return 0; - } else if (check_capacity( - "Your concentration falters while carrying so much stuff.")) { - return 1; - } - - /* if the cast attempt is already going to fail due to insufficient - energy (ie, u.uen < energy), the Amulet's drain effect won't kick - in and no turn will be consumed; however, when it does kick in, - the attempt may fail due to lack of energy after the draining, in - which case a turn will be used up in addition to the energy loss */ - if (u.uhave.amulet && u.uen >= energy) { - You_feel("the amulet draining your energy away."); - /* this used to be 'energy += rnd(2 * energy)' (without 'res'), - so if amulet-induced cost was more than u.uen, nothing - (except the "don't have enough energy" message) happened - and player could just try again (and again and again...); - now we drain some energy immediately, which has a - side-effect of not increasing the hunger aspect of casting */ - u.uen -= rnd(2 * energy); - if (u.uen < 0) - u.uen = 0; - g.context.botl = 1; - res = 1; /* time is going to elapse even if spell doesn't get cast */ - } - - if (energy > u.uen) { - You("don't have enough energy to cast that spell."); - return res; - } else { - if (spellid(spell) != SPE_DETECT_FOOD) { - int hungr = energy * 2; - - /* If hero is a wizard, their current intelligence - * (bonuses + temporary + current) - * affects hunger reduction in casting a spell. - * 1. int = 17-18 no reduction - * 2. int = 16 1/4 hungr - * 3. int = 15 1/2 hungr - * 4. int = 1-14 normal reduction - * The reason for this is: - * a) Intelligence affects the amount of exertion - * in thinking. - * b) Wizards have spent their life at magic and - * understand quite well how to cast spells. - */ - intell = acurr(A_INT); - if (!Role_if(PM_WIZARD)) - intell = 10; - switch (intell) { - case 25: - case 24: - case 23: - case 22: - case 21: - case 20: - case 19: - case 18: - case 17: - hungr = 0; - break; - case 16: - hungr /= 4; - break; - case 15: - hungr /= 2; - break; - } - /* don't put player (quite) into fainting from - * casting a spell, particularly since they might - * not even be hungry at the beginning; however, - * this is low enough that they must eat before - * casting anything else except detect food - */ - if (hungr > u.uhunger - 3) - hungr = u.uhunger - 3; - morehungry(hungr); - } - } - - chance = percent_success(spell); - if (confused || (rnd(100) > chance)) { - You("fail to cast the spell correctly."); - u.uen -= energy / 2; - g.context.botl = 1; - return 1; - } - u.uen -= energy; + if (u.uen < 0) + { + impossible("Not enough energy but spell was successful?"); + u.uen = 0; + } g.context.botl = 1; exercise(A_WIS, TRUE); /* pseudo is a temporary "false" object containing the spell stats */ @@ -1061,34 +1166,32 @@ boolean atme; case SPE_FIREBALL: case SPE_CONE_OF_COLD: if (role_skill >= P_SKILLED) { - if (throwspell()) { - cc.x = u.dx; - cc.y = u.dy; - n = rnd(8) + 1; - while (n--) { - if (!u.dx && !u.dy && !u.dz) { - if ((damage = zapyourself(pseudo, TRUE)) != 0) { - char buf[BUFSZ]; - Sprintf(buf, "zapped %sself with a spell", - uhim()); - losehp(damage, buf, NO_KILLER_PREFIX); - } - } else { - explode(u.dx, u.dy, - otyp - SPE_MAGIC_MISSILE + 10, - spell_damage_bonus(u.ulevel / 2 + 1), 0, - (otyp == SPE_CONE_OF_COLD) - ? EXPL_FROSTY - : EXPL_FIERY); - } - u.dx = cc.x + rnd(3) - 2; - u.dy = cc.y + rnd(3) - 2; - if (!isok(u.dx, u.dy) || !cansee(u.dx, u.dy) - || IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) { - /* Spell is reflected back to center */ - u.dx = cc.x; - u.dy = cc.y; + cc.x = u.dx; + cc.y = u.dy; + n = rnd(8) + 1; + while (n--) { + if (!u.dx && !u.dy && !u.dz) { + if ((damage = zapyourself(pseudo, TRUE)) != 0) { + char buf[BUFSZ]; + Sprintf(buf, "zapped %sself with a spell", + uhim()); + losehp(damage, buf, NO_KILLER_PREFIX); } + } else { + explode(u.dx, u.dy, + otyp - SPE_MAGIC_MISSILE + 10, + spell_damage_bonus(u.ulevel / 2 + 1), 0, + (otyp == SPE_CONE_OF_COLD) + ? EXPL_FROSTY + : EXPL_FIERY); + } + u.dx = cc.x + rnd(3) - 2; + u.dy = cc.y + rnd(3) - 2; + if (!isok(u.dx, u.dy) || !cansee(u.dx, u.dy) + || IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) { + /* Spell is reflected back to center */ + u.dx = cc.x; + u.dy = cc.y; } } break; @@ -1123,20 +1226,6 @@ boolean atme; if (role_skill >= P_SKILLED) pseudo->blessed = 1; } - if (atme) { - u.dx = u.dy = u.dz = 0; - } else if (!getdir((char *) 0)) { - /* getdir cancelled, re-use previous direction */ - /* - * FIXME: reusing previous direction only makes sense - * if there is an actual previous direction. When there - * isn't one, the spell gets cast at self which is rarely - * what the player intended. Unfortunately, the way - * spelleffects() is organized means that aborting with - * "nevermind" is not an option. - */ - pline_The("magical energy is released!"); - } if (!u.dx && !u.dy && !u.dz) { if ((damage = zapyourself(pseudo, TRUE)) != 0) { char buf[BUFSZ]; @@ -1242,7 +1331,8 @@ int x, y; /* Choose location where spell takes effect. */ static int -throwspell() +throwspell(force_move) +boolean force_move; { coord cc, uc; struct monst *mtmp; @@ -1258,8 +1348,10 @@ throwspell() pline("Where do you want to cast the spell?"); cc.x = u.ux; cc.y = u.uy; - if (getpos(&cc, TRUE, "the desired position") < 0) + if (getpos(&cc, TRUE, "the desired position") < 0) { + pline1(force_move ? nothing_happens : Never_mind); return 0; /* user pressed ESC */ + } /* The number of moves from hero to where the spell drops.*/ if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) { pline_The("spell dissipates over the distance!"); diff --git a/src/teleport.c b/src/teleport.c index cb8bb4f40e..e325facd21 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -731,7 +731,7 @@ boolean break_the_rules; /* True: wizard mode ^T */ cantdoit = 0; /* 3.6.2: the magic numbers for hunger, strength, and energy - have been changed to match the ones used in spelleffects(). + have been changed to match those of the teleport away spell. Also, failing these tests used to return 1 and use a move even though casting failure due to these reasons doesn't. [Note: this spellev() is different from the one in spell.c @@ -767,7 +767,8 @@ boolean break_the_rules; /* True: wizard mode ^T */ if (castit) { /* energy cost is deducted in spelleffects() */ exercise(A_WIS, TRUE); - if (spelleffects(sp_no, TRUE)) + u.dx = u.dy = u.dz = 0; /* cast at self */ + if (docastspecial(sp_no, FALSE)) return 1; else if (!break_the_rules) return 0; From a8d1f46ff82907deb40ab209deb5b0971639c008 Mon Sep 17 00:00:00 2001 From: Andrio Celos <6897624+AndrioCelos@users.noreply.github.com> Date: Tue, 10 Nov 2020 20:57:32 +1100 Subject: [PATCH 2/2] Unskilled fireball and cone of cold don't deal physical damage --- src/spell.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/spell.c b/src/spell.c index fb6c9d069d..a3b72eda99 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1124,7 +1124,7 @@ int spell; int energy, damage, chance, n, intell; int otyp, skill, role_skill, res = 0; boolean confused = (Confusion != 0); - boolean physical_damage = FALSE; + boolean physical_damage; struct obj *pseudo; coord cc; @@ -1153,6 +1153,7 @@ int spell; * See spell_skilltype for categories. */ otyp = pseudo->otyp; + physical_damage = otyp == SPE_FORCE_BOLT; skill = spell_skilltype(otyp); role_skill = P_SKILL(skill); @@ -1200,8 +1201,6 @@ int spell; /* these spells are all duplicates of wand effects */ case SPE_FORCE_BOLT: - physical_damage = TRUE; - /*FALLTHRU*/ case SPE_SLEEP: case SPE_MAGIC_MISSILE: case SPE_KNOCK: