From d8537ce97fcc261408188af698b908948a0a2b84 Mon Sep 17 00:00:00 2001 From: dshaligram Date: Fri, 2 Nov 2007 15:05:33 +0000 Subject: Experimental support for monster shields, needs more work for rationalising shield class for monsters with player and beam to-hit. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2727 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/beam.cc | 26 ++++++++++++-- crawl-ref/source/debug.cc | 4 +-- crawl-ref/source/enum.h | 1 + crawl-ref/source/fight.cc | 25 +++++++++---- crawl-ref/source/makeitem.cc | 85 ++++++++++++++++++++++++++++++++++++++++---- crawl-ref/source/mon-util.cc | 72 +++++++++++++++++++++---------------- 6 files changed, 166 insertions(+), 47 deletions(-) diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 4e532cb96d..ba3c94cacc 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -3129,14 +3129,14 @@ static int affect_player( bolt &beam ) #endif if (hit < block) { - you.shield_blocks++; mprf( "You block the %s.", beam.name.c_str() ); - exercise( SK_SHIELDS, exer + 1 ); + you.shield_block_succeeded(); return (BEAM_STOP); } // some training just for the "attempt" - exercise( SK_SHIELDS, exer ); + if (coinflip()) + exercise( SK_SHIELDS, exer ); } if (player_light_armour(true) && !beam.aimed_at_feet @@ -3878,6 +3878,26 @@ static int affect_monster(bolt &beam, monsters *mon) return (0); } + // The monster may block the beam. + if (!engulfs && beam_is_blockable(beam)) + { + const int shield_block = mon->shield_bonus(); + if (shield_block > 0) + { + const int hit = random2( beam.hit * 130 / 100 + + mon->shield_block_penalty() ); + if (hit < shield_block && mons_near(mon) + && player_monster_visible(mon)) + { + mprf("%s blocks the %s.", + mon->name(DESC_CAP_THE).c_str(), + beam.name.c_str()); + mon->shield_block_succeeded(); + return (BEAM_STOP); + } + } + } + update_hurt_or_helped(beam, mon); // the beam hit. diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index d2cbb2510e..d3d020b1ab 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -1448,8 +1448,8 @@ void stethoscope(int mwh) (menv[i].foe == MHITNOT) ? "none" : (menv[menv[i].foe].type == -1) ? "unassigned monster" : menv[menv[i].foe].name(DESC_PLAIN, true).c_str()), - menv[i].foe, - menv[i].foe_memory, + menv[i].foe, + menv[i].foe_memory, menv[i].target_x, menv[i].target_y ); // print resistances diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index eb4859627b..a7cecf10c4 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1825,6 +1825,7 @@ enum mon_inv_type // (int) menv[].inv[] // for monsters that can use two weapons. MSLOT_MISSILE, MSLOT_ARMOUR, + MSLOT_SHIELD, MSLOT_MISCELLANY, MSLOT_POTION, MSLOT_WAND, diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 4a5a9c7f5d..e132835e88 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -545,9 +545,16 @@ bool melee_attack::player_attack() if (Options.tutorial_left) Options.tut_melee_counter++; - // This actually does more than calculate damage - it also sets up - // messages, etc. - player_calc_hit_damage(); + const bool shield_blocked = attack_shield_blocked(true); + + if (shield_blocked) + damage_done = 0; + else + { + // This actually does more than calculate damage - it also sets up + // messages, etc. + player_calc_hit_damage(); + } bool hit_woke_orc = false; if (you.religion == GOD_BEOGH && mons_species(def->type) == MONS_ORC @@ -564,7 +571,7 @@ bool melee_attack::player_attack() if (damage_done > 0 || !defender_visible) player_announce_hit(); - else if (damage_done <= 0) + else if (!shield_blocked && damage_done <= 0) no_damage_message = make_stringf("You %s %s.", attack_verb.c_str(), @@ -584,6 +591,10 @@ bool melee_attack::player_attack() } player_sustain_passive_damage(); + + // At this point, pretend we didn't hit at all. + if (shield_blocked) + did_hit = false; } else player_warn_miss(); @@ -854,6 +865,8 @@ bool melee_attack::player_aux_unarmed() did_hit = false; if (to_hit >= def->ev || one_chance_in(30)) { + if (attack_shield_blocked(true)) + continue; if (player_apply_aux_unarmed()) return (true); } @@ -2865,8 +2878,8 @@ bool melee_attack::attack_shield_blocked(bool verbose) pro_block /= 3; #ifdef DEBUG_DIAGNOSTICS - mprf(MSGCH_DIAGNOSTICS, "Pro-block: %d, Con-block: %d", - pro_block, con_block); + mprf(MSGCH_DIAGNOSTICS, "Defender: %s, Pro-block: %d, Con-block: %d", + def_name(DESC_PLAIN).c_str(), pro_block, con_block); #endif if (pro_block >= con_block) diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc index 2fef1653bf..e0a5e70030 100644 --- a/crawl-ref/source/makeitem.cc +++ b/crawl-ref/source/makeitem.cc @@ -3222,12 +3222,12 @@ static item_make_species_type give_weapon(monsters *mon, int level, const int temp_rand = random2(100); item.sub_type = ((temp_rand > 79) ? WPN_LONG_SWORD : // 20% - (temp_rand > 59) ? WPN_SHORT_SWORD : // 20% - (temp_rand > 45) ? WPN_SCIMITAR : // 14% - (temp_rand > 31) ? WPN_MACE : // 14% - (temp_rand > 18) ? WPN_BOW : // 13% - (temp_rand > 5) ? WPN_HAND_CROSSBOW // 13% - : WPN_LONGBOW); // 6% + (temp_rand > 59) ? WPN_SHORT_SWORD : // 20% + (temp_rand > 45) ? WPN_SCIMITAR : // 14% + (temp_rand > 31) ? WPN_MACE : // 14% + (temp_rand > 18) ? WPN_BOW : // 13% + (temp_rand > 5) ? WPN_HAND_CROSSBOW // 13% + : WPN_LONGBOW); // 6% break; } @@ -3830,6 +3830,78 @@ static void give_ammo(monsters *mon, int level, } } +static bool make_item_for_monster( + monsters *mons, + object_class_type base, + int subtype, + int level, + item_make_species_type race = MAKE_ITEM_NO_RACE, + int allow_uniques = 0) +{ + const int bp = get_item_slot(); + if (bp == NON_ITEM) + return (false); + + const int thing_created = + items( allow_uniques, base, subtype, true, level, race ); + + if (thing_created == NON_ITEM) + return (false); + + give_monster_item(mons, thing_created); + return (true); +} + +void give_shield(monsters *mon, int level) +{ + switch (mon->type) + { + case MONS_DAEVA: + make_item_for_monster(mon, OBJ_ARMOUR, ARM_LARGE_SHIELD, + level * 2 + 1, MAKE_ITEM_RANDOM_RACE, 1); + break; + + case MONS_DEEP_ELF_SOLDIER: + case MONS_DEEP_ELF_FIGHTER: + if (one_chance_in(5)) + make_item_for_monster(mon, OBJ_ARMOUR, ARM_BUCKLER, + level, MAKE_ITEM_ELVEN); + break; + case MONS_NAGA_WARRIOR: + case MONS_VAULT_GUARD: + if (one_chance_in(3)) + make_item_for_monster(mon, OBJ_ARMOUR, + one_chance_in(3)? ARM_LARGE_SHIELD + : ARM_SHIELD, + level, MAKE_ITEM_NO_RACE); + break; + case MONS_DRACONIAN_KNIGHT: + if (coinflip()) + make_item_for_monster(mon, OBJ_ARMOUR, + coinflip()? ARM_LARGE_SHIELD : ARM_SHIELD, + level, MAKE_ITEM_NO_RACE); + break; + case MONS_DEEP_ELF_KNIGHT: + if (coinflip()) + make_item_for_monster(mon, OBJ_ARMOUR, ARM_BUCKLER, + level, MAKE_ITEM_ELVEN); + break; + case MONS_NORRIS: + make_item_for_monster(mon, OBJ_ARMOUR, ARM_BUCKLER, + level * 2 + 1, MAKE_ITEM_RANDOM_RACE, 1); + break; + case MONS_NORBERT: + case MONS_LOUISE: + make_item_for_monster(mon, OBJ_ARMOUR, ARM_LARGE_SHIELD, + level * 2 + 1, MAKE_ITEM_RANDOM_RACE, 1); + break; + case MONS_DONALD: + make_item_for_monster(mon, OBJ_ARMOUR, ARM_SHIELD, + level * 2 + 1, MAKE_ITEM_RANDOM_RACE, 1); + break; + } +} + void give_armour(monsters *mon, int level) { const int bp = get_item_slot(); @@ -4071,6 +4143,7 @@ void give_item(int mid, int level_number) //mv: cleanup+minor changes give_ammo(mons, level_number, item_race); give_armour(mons, 1 + level_number / 2); + give_shield(mons, 1 + level_number / 2); } // end give_item() jewellery_type get_random_amulet_type() diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 5cf5aa1168..84e27d4843 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -2765,14 +2765,19 @@ void monsters::equip_armour(item_def &item, int near) mprf("%s wears %s.", name(DESC_CAP_THE).c_str(), item.name(DESC_NOCAP_A).c_str()); - ac += property( item, PARM_AC ); + const equipment_type eq = get_armour_slot(item); + if (eq != EQ_SHIELD) + { + ac += property( item, PARM_AC ); + + const int armour_plus = item.plus; + ASSERT(abs(armour_plus) < 20); + if (abs(armour_plus) < 20) + ac += armour_plus; + } - const int armour_plus = item.plus; - ASSERT(abs(armour_plus) < 20); - if (abs(armour_plus) < 20) - ac += armour_plus; + // Shields can affect evasion. ev += property( item, PARM_EVASION ) / 2; - if (ev < 1) ev = 1; // This *shouldn't* happen. } @@ -2842,14 +2847,18 @@ void monsters::unequip_armour(item_def &item, int near) mprf("%s takes off %s.", name(DESC_CAP_THE).c_str(), item.name(DESC_NOCAP_A).c_str()); - ac -= property( item, PARM_AC ); + const equipment_type eq = get_armour_slot(item); + if (eq != EQ_SHIELD) + { + ac -= property( item, PARM_AC ); - const int armour_plus = item.plus; - ASSERT(abs(armour_plus) < 20); - if (abs(armour_plus) < 20) - ac -= armour_plus; + const int armour_plus = item.plus; + ASSERT(abs(armour_plus) < 20); + if (abs(armour_plus) < 20) + ac -= armour_plus; + } + ev -= property( item, PARM_EVASION ) / 2; - if (ev < 1) ev = 1; // This *shouldn't* happen. } @@ -3107,6 +3116,17 @@ bool monsters::wants_armour(const item_def &item) const return (!mslot_item(MSLOT_ARMOUR)); } +static mon_inv_type equip_slot_to_mslot(equipment_type eq) +{ + switch (eq) + { + case EQ_WEAPON: return MSLOT_WEAPON; + case EQ_BODY_ARMOUR: return MSLOT_ARMOUR; + case EQ_SHIELD: return MSLOT_SHIELD; + default: return (NUM_MONSTER_SLOTS); + } +} + bool monsters::pickup_armour(item_def &item, int near, bool force) { ASSERT(item.base_type == OBJ_ARMOUR); @@ -3114,21 +3134,24 @@ bool monsters::pickup_armour(item_def &item, int near, bool force) if (!force && !wants_armour(item)) return (false); - // XXX: Monsters can only equip body armour (as of 0.3). - if (get_armour_slot(item) != EQ_BODY_ARMOUR) + const equipment_type eq = get_armour_slot(item); + // XXX: Monsters can only equip body armour and shields (as of 0.4). + // They can still be forced to wear stuff - this is needed for bardings. + if (!force && eq != EQ_BODY_ARMOUR && eq != EQ_SHIELD) return (false); + const mon_inv_type mslot = equip_slot_to_mslot(eq); // XXX: Very simplistic armour evaluation for the moment. - if (const item_def *existing_armour = slot_item(EQ_BODY_ARMOUR)) + if (const item_def *existing_armour = slot_item(eq)) { if (!force && existing_armour->armour_rating() >= item.armour_rating()) return (false); - if (!drop_item(MSLOT_ARMOUR, near)) + if (!drop_item(mslot, near)) return (false); } - return pickup(item, MSLOT_ARMOUR, near); + return pickup(item, mslot, near); } bool monsters::pickup_weapon(item_def &item, int near, bool force) @@ -3302,16 +3325,6 @@ void monsters::wield_melee_weapon(int near) } } -static mon_inv_type equip_slot_to_mslot(equipment_type eq) -{ - switch (eq) - { - case EQ_WEAPON: return MSLOT_WEAPON; - case EQ_BODY_ARMOUR: return MSLOT_ARMOUR; - default: return (NUM_MONSTER_SLOTS); - } -} - item_def *monsters::slot_item(equipment_type eq) { return mslot_item(equip_slot_to_mslot(eq)); @@ -3325,7 +3338,7 @@ item_def *monsters::mslot_item(mon_inv_type mslot) const item_def *monsters::shield() { - return (NULL); + return (mslot_item(MSLOT_SHIELD)); } std::string monsters::name(description_level_type desc) const @@ -3465,12 +3478,11 @@ bool monsters::caught() const int monsters::shield_bonus() const { - // XXX: Monsters don't actually get shields yet. const item_def *shld = const_cast(this)->shield(); if (shld) { const int shld_c = property(*shld, PARM_AC); - return (random2(shld_c + random2(hit_dice / 2)) / 2); + return (random2(1 + shld_c) + random2(hit_dice / 2)); } return (-100); } -- cgit v1.2.3-54-g00ecf