From 0ba059f749cb2eed859bb1a49fe422dd84ca3602 Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Tue, 29 May 2007 02:00:25 +0000 Subject: [PATCH] Hallucination vs gaze attacks (trunk only) Suggested by in March, 2005 based on newsgroup discussion at the time: hallucination protects against touch of death attack by disrupting how the hero's brain reacts, so why not against gaze attacks too? This gives hallucinating hero 75% chance of being unaffected by gazes. If unaffected or if the gazer has been cancelled, the gaze will fail with some feedback. Previously, all cancelled gazes failed but only Medusa's gave feedback. This will give players another way to defeat Medusa, but since it isn't foolproof and there are several sure fire ways already, I don't think it'll hurt play balance there. It may be useful to avoid getting repeatedly stunned by Archons though. --- doc/fixes35.0 | 1 + src/apply.c | 3 +- src/mhitu.c | 148 ++++++++++++++++++++++++++++++++++--------------- src/polyself.c | 29 +++++----- src/uhitm.c | 4 ++ 5 files changed, 124 insertions(+), 61 deletions(-) diff --git a/doc/fixes35.0 b/doc/fixes35.0 index ce95be87c..225180b9b 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -344,6 +344,7 @@ pile_limit option to control when to switch to "there are objects here" some monsters will use fire to prevent selves being turned into green slime add `#vanquished' debug mode command C and #name commands are now same and use menu to choose monster vs object +hallucination provides partial protection against gaze attacks Platform- and/or Interface-Specific New Features diff --git a/src/apply.c b/src/apply.c index f889fb265..1d3073181 100644 --- a/src/apply.c +++ b/src/apply.c @@ -751,7 +751,8 @@ struct obj *obj; pline("Yow! The %s stares back!", mirror); else pline("Yikes! You've frozen yourself!"); - nomul(-rnd((MAXULEV+6) - u.ulevel)); + if (!Hallucination || !rn2(4)) + nomul(-rnd(MAXULEV + 6 - u.ulevel)); nomovemsg = 0; /* default, "you can move again" */ } } else if (youmonst.data->mlet == S_VAMPIRE) diff --git a/src/mhitu.c b/src/mhitu.c index b181dbe6b..1e38b0c8d 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1921,17 +1921,38 @@ gazemu(mtmp, mattk) /* monster gazes at you */ register struct monst *mtmp; register struct attack *mattk; { + static const char * const reactions[] = { + "confused", /* [0] */ + "stunned", /* [1] */ + "puzzled", "dazzled", /* [2,3] */ + "irritated", "inflamed", /* [4,5] */ + "tired", /* [6] */ + "dulled", /* [7] */ + }; + int react = -1; + boolean cancelled = (mtmp->mcan != 0), already = FALSE; + + /* assumes that hero has to see monster's gaze in order to be + affected, rather than monster just having to look at hero; + when hallucinating, hero's brain doesn't register what + it's seeing correctly so the gaze is usually ineffective + [this could be taken a lot farther and select a gaze effect + appropriate to what's currently being displayed, giving + ordinary monsters a gaze attack when hero thinks he or she + is facing a gazing creature, but let's not go that far...] */ + if (Hallucination && rn2(4)) cancelled = TRUE; + switch(mattk->adtyp) { - case AD_STON: - if (mtmp->mcan || !mtmp->mcansee) { + case AD_STON: + if (cancelled || !mtmp->mcansee) { if (!canseemon(mtmp)) break; /* silently */ pline("%s %s.", Monnam(mtmp), (mtmp->data == &mons[PM_MEDUSA] && mtmp->mcan) ? "doesn't look all that ugly" : "gazes ineffectually"); break; - } - if (Reflecting && couldsee(mtmp->mx, mtmp->my) && + } + if (Reflecting && couldsee(mtmp->mx, mtmp->my) && mtmp->data == &mons[PM_MEDUSA]) { /* hero has line of sight to Medusa and she's not blind */ boolean useeit = canseemon(mtmp); @@ -1956,8 +1977,8 @@ gazemu(mtmp, mattk) /* monster gazes at you */ if (mtmp->mhp > 0) break; return 2; - } - if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && + } + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && !Stone_resistance) { You("meet %s gaze.", s_suffix(mon_nam(mtmp))); stop_occupation(); @@ -1967,12 +1988,15 @@ gazemu(mtmp, mattk) /* monster gazes at you */ killer.format = KILLED_BY; Strcpy(killer.name, mtmp->data->mname); done(STONING); - } - break; - case AD_CONF: - if(!mtmp->mcan && canseemon(mtmp) && - couldsee(mtmp->mx, mtmp->my) && + } + break; + case AD_CONF: + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && mtmp->mcansee && !mtmp->mspec_used && rn2(5)) { + if (cancelled) { + react = 0; /* "confused" */ + already = (mtmp->mconf != 0); + } else { int conf = d(3,4); mtmp->mspec_used = mtmp->mspec_used + (conf + rn2(6)); @@ -1984,11 +2008,15 @@ gazemu(mtmp, mattk) /* monster gazes at you */ make_confused(HConfusion + conf, FALSE); stop_occupation(); } - break; - case AD_STUN: - if(!mtmp->mcan && canseemon(mtmp) && - couldsee(mtmp->mx, mtmp->my) && + } + break; + case AD_STUN: + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && mtmp->mcansee && !mtmp->mspec_used && rn2(5)) { + if (cancelled) { + react = 1; /* "stunned" */ + already = (mtmp->mstun != 0); + } else { int stun = d(2,6); mtmp->mspec_used = mtmp->mspec_used + (stun + rn2(6)); @@ -1996,10 +2024,19 @@ gazemu(mtmp, mattk) /* monster gazes at you */ make_stunned(HStun + stun, TRUE); stop_occupation(); } - break; - case AD_BLND: - if (!mtmp->mcan && canseemon(mtmp) && !resists_blnd(&youmonst) - && distu(mtmp->mx,mtmp->my) <= BOLT_LIM*BOLT_LIM) { + } + break; + case AD_BLND: + if (canseemon(mtmp) && !resists_blnd(&youmonst) && + distu(mtmp->mx,mtmp->my) <= BOLT_LIM*BOLT_LIM) { + if (cancelled) { + react = rn1(2,2); /* "puzzled" || "dazzled" */ + already = (mtmp->mcansee == 0); + /* Archons gaze every round; we don't want cancelled ones + giving the "seems puzzled/dazzled" message that often */ + if (mtmp->mcan && mtmp->data == &mons[PM_ARCHON] && rn2(5)) + react = -1; + } else { int blnd = d((int)mattk->damn, (int)mattk->damd); You("are blinded by %s radiance!", @@ -2012,12 +2049,15 @@ gazemu(mtmp, mattk) /* monster gazes at you */ if (!Blind) Your(vision_clears); else make_stunned((long)d(1,3),TRUE); } - break; - case AD_FIRE: - if (!mtmp->mcan && canseemon(mtmp) && - couldsee(mtmp->mx, mtmp->my) && + } + break; + case AD_FIRE: + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && mtmp->mcansee && !mtmp->mspec_used && rn2(5)) { - int dmg = d(2,6); + if (cancelled) { + react = rn1(2,4); /* "irritated" || "inflamed" */ + } else { + int dmg = d(2,6), lev = (int)mtmp->m_lev; pline("%s attacks you with a fiery gaze!", Monnam(mtmp)); stop_occupation(); @@ -2026,37 +2066,53 @@ gazemu(mtmp, mattk) /* monster gazes at you */ dmg = 0; } burn_away_slime(); - if ((int) mtmp->m_lev > rn2(20)) - destroy_item(SCROLL_CLASS, AD_FIRE); - if ((int) mtmp->m_lev > rn2(20)) - destroy_item(POTION_CLASS, AD_FIRE); - if ((int) mtmp->m_lev > rn2(25)) - destroy_item(SPBOOK_CLASS, AD_FIRE); + if (lev > rn2(20)) destroy_item(SCROLL_CLASS, AD_FIRE); + if (lev > rn2(20)) destroy_item(POTION_CLASS, AD_FIRE); + if (lev > rn2(25)) destroy_item(SPBOOK_CLASS, AD_FIRE); if (dmg) mdamageu(mtmp, dmg); } - break; + } + break; #ifdef PM_BEHOLDER /* work in progress */ - case AD_SLEE: - if(!mtmp->mcan && canseemon(mtmp) && - couldsee(mtmp->mx, mtmp->my) && mtmp->mcansee && - multi >= 0 && !rn2(5) && !Sleep_resistance) { - + case AD_SLEE: + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && + mtmp->mcansee && multi >= 0 && !rn2(5) && !Sleep_resistance) { + if (cancelled) { + react = 6; /* "tired" */ + already = (mtmp->mfrozen != 0); /* can't happen... */ + } else { fall_asleep(-rnd(10), TRUE); pline("%s gaze makes you very sleepy...", s_suffix(Monnam(mtmp))); } - break; - case AD_SLOW: - if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee && - (HFast & (INTRINSIC|TIMEOUT)) && - !defends(AD_SLOW, uwep) && !rn2(4)) - + } + break; + case AD_SLOW: + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && + mtmp->mcansee && (HFast & (INTRINSIC|TIMEOUT)) && + !defends(AD_SLOW, uwep) && !rn2(4)) { + if (cancelled) { + react = 7; /* "dulled" */ + already = (mtmp->mspeed == MSLOW); + } else { u_slow_down(); stop_occupation(); - break; -#endif - default: impossible("Gaze attack %d?", mattk->adtyp); - break; + } + } + break; +#endif /* BEHOLDER */ + default: + impossible("Gaze attack %d?", mattk->adtyp); + break; + } + if (react >= 0) { + if (Hallucination && rn2(3)) react = rn2(SIZE(reactions)); + /* cancelled/hallucinatory feedback; monster might look "confused", + "stunned",&c but we don't actually set corresponding attribute */ + pline("%s looks %s%s.", Monnam(mtmp), + !rn2(3) ? "" : already ? "quite " : + (!rn2(2) ? "a bit " : "somewhat "), + reactions[react]); } return(0); } diff --git a/src/polyself.c b/src/polyself.c index b7af15e22..27ff57687 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1085,10 +1085,12 @@ dogaze() return 0; } - if (Blind) { You_cant("see anything to gaze at."); return 0; + } else if (Hallucination) { + You_cant("gaze at anything you can see."); + return 0; } if (u.uen < 15) { You("lack the energy to use your special gaze!"); @@ -1109,12 +1111,10 @@ dogaze() || mtmp->m_ap_type == M_AP_OBJECT) { looked--; continue; - } else if (flags.safe_dog && !Confusion && !Hallucination - && mtmp->mtame) { + } else if (flags.safe_dog && mtmp->mtame && !Confusion) { You("avoid gazing at %s.", y_monnam(mtmp)); } else { - if (flags.confirm && mtmp->mpeaceful && !Confusion - && !Hallucination) { + if (flags.confirm && mtmp->mpeaceful && !Confusion) { Sprintf(qbuf, "Really %s %s?", (adtyp == AD_CONF) ? "confuse" : "attack", mon_nam(mtmp)); @@ -1137,26 +1137,28 @@ dogaze() Monnam(mtmp)); mtmp->mconf = 1; } else if (adtyp == AD_FIRE) { - int dmg = d(2,6); + int dmg = d(2,6), lev = (int)u.ulevel; + You("attack %s with a fiery gaze!", mon_nam(mtmp)); if (resists_fire(mtmp)) { pline_The("fire doesn't burn %s!", mon_nam(mtmp)); dmg = 0; } - if((int) u.ulevel > rn2(20)) + if (lev > rn2(20)) (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); - if((int) u.ulevel > rn2(20)) + if (lev > rn2(20)) (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); - if((int) u.ulevel > rn2(25)) + if (lev > rn2(25)) (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); - if (dmg && !DEADMONSTER(mtmp)) mtmp->mhp -= dmg; + if (dmg) mtmp->mhp -= dmg; if (mtmp->mhp <= 0) killed(mtmp); } /* For consistency with passive() in uhitm.c, this only * affects you if the monster is still alive. */ - if (!DEADMONSTER(mtmp) && - (mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) { + if (DEADMONSTER(mtmp)) continue; + + if (mtmp->data == &mons[PM_FLOATING_EYE] && !mtmp->mcan) { if (!Free_action) { You("are frozen by %s gaze!", s_suffix(mon_nam(mtmp))); @@ -1175,8 +1177,7 @@ dogaze() * works on the monster's turn, but for it to *not* have an * effect would be too weird. */ - if (!DEADMONSTER(mtmp) && - (mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) { + if (mtmp->data == &mons[PM_MEDUSA] && !mtmp->mcan) { pline( "Gazing at the awake %s is not a very good idea.", l_monnam(mtmp)); diff --git a/src/uhitm.c b/src/uhitm.c index 1c6b55653..3373ba7b5 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -2352,6 +2352,10 @@ boolean wep_was_destroyed; else if (Free_action) You("momentarily stiffen under %s gaze!", s_suffix(mon_nam(mon))); + else if (Hallucination && rn2(4)) + pline("%s looks %s%s.", Monnam(mon), + !rn2(2) ? "" : "rather ", + !rn2(2) ? "numb" : "stupified"); else { You("are frozen by %s gaze!", s_suffix(mon_nam(mon))); -- 2.40.0