]> granicus.if.org Git - nethack/commitdiff
fix #H3033 - Crash upon teleport onto a sink while equipping levitation boots
authornethack.rankin <nethack.rankin>
Sat, 26 Oct 2013 21:33:47 +0000 (21:33 +0000)
committernethack.rankin <nethack.rankin>
Sat, 26 Oct 2013 21:33:47 +0000 (21:33 +0000)
     From a bug report, being teleported onto
a sink while busy putting on levitation boots triggered a crash when
Boots_on() was called (as '(*aftermv)()' on the next turn) because
'uarmf' would be null by then.  Not mentioned, but the same problem was
encountered by Boots_off() if the teleport happened while you were busy
taking boots off.  It could be fixed by having having dosinkfall() call
cancel_don() if donning(uarmf) yields true, but this patch does a little
more than that:  cancel donning/doffing of any multi-turn armor if you
fall onto a sink.  It also prevents you from falling if you end up
flying (which will have been blocked while levitating).

     The situation when putting on levitation boots has a sequencing
issue:  setworn() causes you to be flagged as levitating immediately,
but the float_up() feedback doesn't occur until Boots_on() gets called
a turn later.  Teleporting to the sink will tell you that you crash
onto the sink and and that you stop putting on boots, without having
been told that you've floated up into the air.  It's suboptimal but it
doesn't seem to actually be incorrect.

doc/fixes35.0
src/hack.c

index 118ba60c8abee72885daf1ab3724e4ef3f5da61e..8aef6e1264bceea9708380eb3a8d3f966a3c2a83 100644 (file)
@@ -848,6 +848,8 @@ entering an untended shop while blind gave an inappropriate message
 engraving feedback about partial text when weapon became too dull to finish
        was lacking sentence-ending period
 impossible() might display inaccurate feedback after updating paniclog
+fix crash which occurred if hero was teleported onto a sink while busy putting
+       on or taking off levitation boots
 
 
 Platform- and/or Interface-Specific Fixes
index 055930d9f901aac45b5b2e79344748721304e54a..728e757748d4340dd50ba1e27a8d0f945c83b098 100644 (file)
@@ -497,9 +497,13 @@ dosinkfall()
 {
        register struct obj *obj;
        int dmg;
+       boolean lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS),
+               innate_lev = ((HLevitation & (FROMOUTSIDE|FROMFORM)) != 0L),
+               ufall = (!innate_lev && !(HFlying || EFlying)); /* BFlying */
 
-       if (HLevitation & (FROMOUTSIDE|FROMFORM)) {
-           You("wobble unsteadily for a moment.");
+       if (!ufall) {
+           You(innate_lev ? "wobble unsteadily for a moment." :
+                            "gain control of your flight.");
        } else {
            long save_ELev = ELevitation, save_HLev = HLevitation;
 
@@ -524,6 +528,25 @@ dosinkfall()
            HLevitation = save_HLev;
        }
 
+       /*
+        * Interrupt multi-turn putting on/taking off of armor (in which
+        * case we reached the sink due to being teleported while busy;
+        * in 3.4.3, Boots_on()/Boots_off() [called via (*aftermv)() when
+        * 'multi' reaches 0] triggered a crash if we were donning/doffing
+        * levitation boots [because the Boots_off() below causes 'uarmf'
+        * to be null by the time 'aftermv' gets called]).
+        *
+        * Interrupt donning/doffing if we fall onto the sink, or if the
+        * code below is going to remove levitation boots even when we
+        * haven't fallen (innate floating or flying becoming unblocked).
+        */
+       if (ufall || lev_boots) {
+           (void) stop_donning(lev_boots ? uarmf :  (struct obj *)0);
+           /* recalculate in case uarmf just got set to null */
+           lev_boots = (uarmf && uarmf->otyp == LEVITATION_BOOTS);
+       }
+
+       /* remove worn levitation items */
        ELevitation &= ~W_ARTI;
        HLevitation &= ~(I_SPECIAL|TIMEOUT);
        HLevitation++;
@@ -537,7 +560,7 @@ dosinkfall()
            Ring_off(obj);
            off_msg(obj);
        }
-       if(uarmf && uarmf->otyp == LEVITATION_BOOTS) {
+       if (lev_boots) {
            obj = uarmf;
            (void)Boots_off();
            off_msg(obj);