From 0cca010ff1a511133c04c24e65142022bed5aec3 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 16 Mar 2021 11:01:43 -0700 Subject: [PATCH] fix pull request #470 - two riding fixes Post-3.6 change to monster inventory handling could result in hero remaining mounted on an unsaddled steed (if saddle was removed via opening magic). Hero falling out of saddle would fall to the ground and take damage even if levitating or flying without steed's help after dismount. Fixes #470 --- doc/fixes37.0 | 3 +++ src/steal.c | 2 +- src/steed.c | 24 ++++++++++++++++-------- src/worn.c | 24 ++++++++++++++---------- 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 91c500a12..5ffc200a9 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -410,6 +410,8 @@ remove superfluous "All" from "All foos are already nonexistent." when blessed genocide tries to remove something which has already been genocided "#dip into -" produced a scrambled message: You mime dip intoing something. +mounted hero falling out of saddle shouldn't hit ground and take damage when + levitating or flying (if done without steed's help) Fixes to 3.7.0-x Problems that Were Exposed Via git Repository @@ -540,6 +542,7 @@ change wizard mode command #wizmgender to wizard mode option 'wizmgender' engraving with non-blade dulled the weapon anyway (pr #464) 'sortdiscoveries:s' had a spurious generic header shown at the start of the last class if there were any artifacts or unique items discovered +loss of saddle by opening magic left hero mounted on unsaddled steed curses: 'msg_window' option wasn't functional for curses unless the binary also included tty support diff --git a/src/steal.c b/src/steal.c index b5e40329d..b4427ee78 100644 --- a/src/steal.c +++ b/src/steal.c @@ -671,7 +671,7 @@ mdrop_obj( boolean verbosely) { int omx = mon->mx, omy = mon->my; - boolean unwornmask = obj->owornmask; + long unwornmask = obj->owornmask; extract_from_minvent(mon, obj, FALSE, TRUE); /* don't charge for an owned saddle on dead steed (provided diff --git a/src/steed.c b/src/steed.c index e374e6fab..93b12d58e 100644 --- a/src/steed.c +++ b/src/steed.c @@ -482,30 +482,38 @@ dismount_steed( { struct monst *mtmp; struct obj *otmp; + const char *verb; coord cc, steedcc; - const char *verb = "fall"; - boolean repair_leg_damage = (Wounded_legs != 0L); unsigned save_utrap = u.utrap; - boolean have_spot = landing_spot(&cc, reason, 0); + boolean ulev, ufly, + repair_leg_damage = (Wounded_legs != 0L), + have_spot = landing_spot(&cc, reason, 0); mtmp = u.usteed; /* make a copy of steed pointer */ /* Sanity check */ if (!mtmp) /* Just return silently */ return; + u.usteed = 0; /* affects Fly test; could hypothetically affect Lev */ + ufly = Flying ? TRUE : FALSE; + ulev = Levitation ? TRUE : FALSE; + u.usteed = mtmp; /* Check the reason for dismounting */ otmp = which_armor(mtmp, W_SADDLE); switch (reason) { case DISMOUNT_THROWN: - verb = "are thrown"; - /*FALLTHRU*/ case DISMOUNT_FELL: + verb = (reason == DISMOUNT_THROWN) ? "are thrown" + : ulev ? "float" : ufly ? "fly" : "fall"; You("%s off of %s!", verb, mon_nam(mtmp)); if (!have_spot) have_spot = landing_spot(&cc, reason, 1); - losehp(Maybe_Half_Phys(rn1(10, 10)), "riding accident", KILLED_BY_AN); - set_wounded_legs(BOTH_SIDES, (int) HWounded_legs + rn1(5, 5)); - repair_leg_damage = FALSE; + if (!ulev && !ufly) { + losehp(Maybe_Half_Phys(rn1(10, 10)), "riding accident", + KILLED_BY_AN); + set_wounded_legs(BOTH_SIDES, (int) HWounded_legs + rn1(5, 5)); + repair_leg_damage = FALSE; + } break; case DISMOUNT_POLY: You("can no longer ride %s.", mon_nam(u.usteed)); diff --git a/src/worn.c b/src/worn.c index 53c9b951c..b1817eeed 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1035,20 +1035,24 @@ racial_exception(struct monst *mon, struct obj *obj) return 0; } -/* Remove an object from a monster's inventory. - * At its core this is just obj_extract_self(), but it also handles any updates - * that needs to happen if the gear is equipped or in some other sort of state - * that needs handling. - * Note that like obj_extract_self(), this leaves obj free. */ +/* Remove an object from a monster's inventory. */ void -extract_from_minvent(struct monst *mon, struct obj *obj, - boolean do_intrinsics, /* whether to call - update_mon_intrinsics */ - boolean silently) /* doesn't affect all possible messages, - just update_mon_intrinsics's */ +extract_from_minvent( + struct monst *mon, + struct obj *obj, + boolean do_intrinsics, /* whether to call update_mon_intrinsics */ + boolean silently) /* doesn't affect all possible messages, + * just update_mon_intrinsics's */ { long unwornmask = obj->owornmask; + /* + * At its core this is just obj_extract_self(), but it also handles + * any updates that need to happen if the gear is equipped or in + * some other sort of state that needs handling. + * Note that like obj_extract_self(), this leaves obj free. + */ + if (obj->where != OBJ_MINVENT) { impossible("extract_from_minvent called on object not in minvent"); return;