still fairly tangled, but they're better than the old monster. The idea is to also put monster-vs-player and monster-vs-monster into the melee_attack framework so that refactoring combat code with elements of 4.1 becomes easier.
This is a big refactoring, so it's likely to be buggy. Some of the combat diagnostics - notably the damage rolls - are also AWOL. Will fix going forward.
Note: The combat code is still classic b26.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@950 c06c8d41-db1a-0410-9941-cceddc491573
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC KEYK3CH5J46U6TTOKTWRNMYTZXMQXFVEAZUC4ZQ4QCOSJHVIBDRQC AU3E5FTABBHFZAZHLGA3UUR5TCTRLDOEMAKTGGEWKCQVH77I3SYAC I5N4EIR6SCLLRGKRBUKW5FKUVYK62EA5DOWIAS5XFIHZQKMCXWBAC ITDUEUO7XAZANPC4GRB3SEDFBOV7GLFPNPTYE5LYNC3CS6BSVZTQC ZMYT53SCUSEG26QOTSRBQ3TWMSQTGBXZNEDPLPHM5U3VHALVOOIQC RI5TNMRQUYPOFGT3LBGNSIYVU2IAQ2F6BT2AMUMM3EFJXOZRWPAQC QVVC7AYGVA6U64PTNA7L27422NLMO327P22BQKXEVIMPZHIHO7MQC QUXTRAANLFJWXMRAANUIXUU2AELFIZUBNAKGZZIVTSKX6NUMWGTQC 77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC 33ZMPQC6OXTESW7SRW765GRNJUEJRSYONRVZVIEUDAUEJ2PPMB4AC AOAJ6D3OKSELEYKAT55XCVU5LYJ7SMCZKC6DIEGLLB3TF2LEENWQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC ZP2KE7A2LE7Z2S7AC45WE4CXDSEVDTWIMV2EM4IBUKXYJIDU6R7QC ABPRQBBNMOXUJJNZXAJBIIUULMCB23MKAM5T3M5YFXDORJ3IY3JAC LXLUKS5CKXBUSVV3QTZ4SM7NWSY6JFQEBHUBQW2VUEU5DOL3RRLAC RGWUYJO74FDGTH22CYSHBKFDGJ4S76WTLIULKPXA4QT5ZCNOB4LQC VIFRP3HZEONFR6PQRYZYM3YUEJOQ7T4F5CZY4MN4YJMB23FMU7XAC if (is_feature('>',gx,gy))learned_something_new(TUT_SEEN_STAIRS);else if (is_feature('_',gx,gy))learned_something_new(TUT_SEEN_ALTAR);else if (grd[gx][gy] == DNGN_CLOSED_DOOR)learned_something_new(TUT_SEEN_DOOR,gx,gy);else if (grd[gx][gy] == DNGN_ENTER_SHOP)learned_something_new(TUT_SEEN_SHOP,gx,gy);
const int ex = gx - you.x_pos + 9;const int ey = gy - you.y_pos + 9;int object = env.show[ex][ey];if (object){if (is_feature('>',gx,gy))learned_something_new(TUT_SEEN_STAIRS);else if (is_feature('_',gx,gy))learned_something_new(TUT_SEEN_ALTAR);else if (grd[gx][gy] == DNGN_CLOSED_DOOR)learned_something_new(TUT_SEEN_DOOR,gx,gy);else if (grd[gx][gy] == DNGN_ENTER_SHOP)learned_something_new(TUT_SEEN_SHOP,gx,gy);}
}std::string actor_name(const monsters *actor, description_level_type desc){return (actor? ptr_monam(actor, desc) : pronoun_you(desc));}// actor_verb(NULL, "chop") == chop, as in "You chop"// actor_verb(monster, "chop") == chops, as in "The skeletal warrior chops".std::string actor_verb(const monsters *actor, const std::string &verb){// Simplistic - we really should be conjugating the verb// appropriately. We'll special-case as necessary.return (actor? verb + "s" : verb);}int actor_damage_type(const monsters *actor){return (actor? mons_damage_type(actor) : player_damage_type());
int actor_damage_brand(const monsters *actor){return (actor? mons_damage_brand(actor) : player_damage_brand());}
//////////////////////////////////////////////////////////////////////////
const int wpn = you.equip[ EQ_WEAPON ];if (wpn != -1){return (get_vorpal_type(you.inv[wpn]));}else if (you.equip[EQ_GLOVES] == -1 &&you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS){return (DVORP_SLICING);}else if (you.equip[EQ_GLOVES] == -1 &&(you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON|| you.mutation[MUT_CLAWS]|| you.species == SP_TROLL|| you.species == SP_GHOUL)){return (DVORP_CLAWING);}return (DVORP_CRUSHING);
return you.damage_type();
int ret = SPWPN_NORMAL;const int wpn = you.equip[ EQ_WEAPON ];if (wpn != -1){if ( !is_range_weapon(you.inv[wpn]) )ret = get_weapon_brand( you.inv[wpn] );}else if (you.confusing_touch)ret = SPWPN_CONFUSE;else if (you.mutation[MUT_DRAIN_LIFE])ret = SPWPN_DRAINING;else{switch (you.attribute[ATTR_TRANSFORMATION]){case TRAN_SPIDER:ret = SPWPN_VENOM;break;case TRAN_ICE_BEAST:ret = SPWPN_FREEZING;break;case TRAN_LICH:ret = SPWPN_DRAINING;break;default:break;}}return (ret);
return you.damage_brand();
size_type ret = (base) ? SIZE_CHARACTER : transform_size( psize );if (ret == SIZE_CHARACTER){// transformation has size of character's species:switch (you.species){case SP_OGRE:case SP_OGRE_MAGE:case SP_TROLL:ret = SIZE_LARGE;break;case SP_NAGA:// Most of their body is on the ground giving them a low profile.if (psize == PSIZE_TORSO || psize == PSIZE_PROFILE)ret = SIZE_MEDIUM;elseret = SIZE_BIG;break;case SP_CENTAUR:ret = (psize == PSIZE_TORSO) ? SIZE_MEDIUM : SIZE_BIG;break;case SP_SPRIGGAN:ret = SIZE_LITTLE;break;case SP_HALFLING:case SP_GNOME:case SP_KOBOLD:ret = SIZE_SMALL;break;default:ret = SIZE_MEDIUM;break;}}return (ret);
return you.body_size(psize, base);
bool player::in_water() const{return !is_levitating()&& grid_is_water(grd[you.x_pos][you.y_pos]);}bool player::can_swim() const{return (species == SP_MERFOLK);}bool player::swimming() const{return in_water() && can_swim();}bool player::has_spell(int spell) const{for (int i = 0; i < 25; i++){if (spells[i] == spell)return (true);}return (false);}bool player::floundering() const{return in_water() && !can_swim();}size_type player::body_size(int psize, bool base) const{size_type ret = (base) ? SIZE_CHARACTER : transform_size( psize );if (ret == SIZE_CHARACTER){// transformation has size of character's species:switch (species){case SP_OGRE:case SP_OGRE_MAGE:case SP_TROLL:ret = SIZE_LARGE;break;case SP_NAGA:// Most of their body is on the ground giving them a low profile.if (psize == PSIZE_TORSO || psize == PSIZE_PROFILE)ret = SIZE_MEDIUM;elseret = SIZE_BIG;break;case SP_CENTAUR:ret = (psize == PSIZE_TORSO) ? SIZE_MEDIUM : SIZE_BIG;break;case SP_SPRIGGAN:ret = SIZE_LITTLE;break;case SP_HALFLING:case SP_GNOME:case SP_KOBOLD:ret = SIZE_SMALL;break;default:ret = SIZE_MEDIUM;break;}}return (ret);}int player::damage_type(int){const int wpn = equip[ EQ_WEAPON ];if (wpn != -1){return (get_vorpal_type(inv[wpn]));}else if (equip[EQ_GLOVES] == -1 &&attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS){return (DVORP_SLICING);}else if (equip[EQ_GLOVES] == -1 &&(attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON|| mutation[MUT_CLAWS]|| species == SP_TROLL|| species == SP_GHOUL)){return (DVORP_CLAWING);}return (DVORP_CRUSHING);}int player::damage_brand(int){int ret = SPWPN_NORMAL;const int wpn = equip[ EQ_WEAPON ];if (wpn != -1){if ( !is_range_weapon(inv[wpn]) )ret = get_weapon_brand( inv[wpn] );}else if (confusing_touch)ret = SPWPN_CONFUSE;else if (mutation[MUT_DRAIN_LIFE])ret = SPWPN_DRAINING;else{switch (attribute[ATTR_TRANSFORMATION]){case TRAN_SPIDER:ret = SPWPN_VENOM;break;case TRAN_ICE_BEAST:ret = SPWPN_FREEZING;break;case TRAN_LICH:ret = SPWPN_DRAINING;break;default:break;}}return (ret);}item_def *player::slot_item(equipment_type eq){ASSERT(eq >= EQ_WEAPON && eq <= EQ_AMULET);const int item = equip[eq];return (item == -1? NULL : &inv[item]);}// Returns the item in the player's weapon slot.item_def *player::weapon(int /* which_attack */){return slot_item(EQ_WEAPON);}item_def *player::shield(){return slot_item(EQ_SHIELD);}std::string player::name(description_level_type type) const{return (pronoun_you(type));}std::string player::conj_verb(const std::string &verb) const{return (verb);}int player::id() const{return (-1);}bool player::fumbles_attack(bool verbose){// fumbling in shallow water <early return>:if (floundering()){if (random2(dex) < 4 || one_chance_in(5)){if (verbose)mpr("Unstable footing causes you to fumble your attack.");return (true);}}return (false);}bool player::cannot_fight() const{return (false);}void player::attacking(actor *other){if (other && other->atype() == ACT_MONSTER){const monsters *mons = dynamic_cast<monsters*>(other);if (mons_friendly(mons))did_god_conduct(DID_ATTACK_FRIEND, 5);elsepet_target = monster_index(mons);}if (mutation[MUT_BERSERK] &&(random2(100) < (mutation[MUT_BERSERK] * 10) - 5)){go_berserk(false);}}void player::go_berserk(bool intentional){::go_berserk(intentional);}void player::make_hungry(int hunger_increase, bool silent){::make_hungry(hunger_increase, silent);}
const int grid = grd[m->x][m->y];return ((grid == DNGN_DEEP_WATER || grid == DNGN_SHALLOW_WATER)// Can't use monster_habitable_grid because that'll return true// for non-water monsters in shallow water.&& monster_habitat(m->type) != DNGN_DEEP_WATER&& !mons_class_flag(m->type, M_AMPHIBIOUS)&& !mons_flies(m));
return (m->floundering());
int mons_weapon_index(const monsters *m){// This randomly picks one of the wielded weapons for monsters that can use// two weapons. Not ideal, but better than nothing. fight.cc does it right,// for various values of right.int weap = m->inv[MSLOT_WEAPON];if (mons_wields_two_weapons(m)){const int offhand = mons_offhand_weapon_index(m);if (offhand != NON_ITEM && (weap == NON_ITEM || coinflip()))weap = offhand;}return (weap);}
int mons_damage_type(const monsters *m){const int mweap = mons_weapon_index(m);if (mweap == NON_ITEM)return (mons_base_damage_type(m));return (get_vorpal_type(mitm[mweap]));}int mons_damage_brand(const monsters *m)
int mons_size(const monsters *m)
const int mweap = mons_weapon_index(m);if (mweap == NON_ITEM)return (SPWPN_NORMAL);const item_def &weap = mitm[mweap];return (!is_range_weapon(weap)? get_weapon_brand(weap) : SPWPN_NORMAL);
return m->body_size();
}///////////////////////////////////////////////////////////////////////////////// monsters methodscoord_def monsters::pos() const{return coord_def(x, y);}bool monsters::swimming() const{const int grid = grd[x][y];return (grid_is_watery(grid) && monster_habitat(type) == DNGN_DEEP_WATER);}bool monsters::floundering() const{const int grid = grd[x][y];return (grid_is_water(grid)// Can't use monster_habitable_grid because that'll return true// for non-water monsters in shallow water.&& monster_habitat(type) != DNGN_DEEP_WATER&& !mons_class_flag(type, M_AMPHIBIOUS)&& !mons_flies(this));}size_type monsters::body_size(int /* psize */, bool /* base */) const{const monsterentry *e = seekmonster(type);return (e? e->size : SIZE_MEDIUM);}int monsters::damage_type(int which_attack){const item_def *mweap = weapon(which_attack);if (!mweap)return (mons_base_damage_type(this));return (get_vorpal_type(*mweap));
int monsters::damage_brand(int which_attack){const item_def *mweap = weapon(which_attack);if (!mweap)return (SPWPN_NORMAL);return (!is_range_weapon(*mweap)? get_weapon_brand(*mweap) : SPWPN_NORMAL);}item_def *monsters::weapon(int which_attack){if (which_attack > 1)which_attack &= 1;// This randomly picks one of the wielded weapons for monsters that can use// two weapons. Not ideal, but better than nothing. fight.cc does it right,// for various values of right.int weap = inv[MSLOT_WEAPON];if (which_attack && mons_wields_two_weapons(this)){const int offhand = mons_offhand_weapon_index(this);if (offhand != NON_ITEM&& (weap == NON_ITEM || which_attack == 1 || coinflip())){weap = offhand;}}return (weap == NON_ITEM? NULL : &mitm[weap]);}item_def *monsters::shield(){return (NULL);}std::string monsters::name(description_level_type desc) const{return (ptr_monam(this, desc));}std::string monsters::conj_verb(const std::string &verb) const{return (verb + "s");}int monsters::id() const{return (type);}bool monsters::fumbles_attack(bool verbose){if (floundering() && one_chance_in(4)){if (verbose && !silenced(you.x_pos, you.y_pos)&& !silenced(x, y)){mprf(MSGCH_SOUND, "You hear a splashing noise.");}}return (false);}bool monsters::cannot_fight() const{return mons_class_flag(type, M_NO_EXP_GAIN)|| mons_is_statue(type);}void monsters::attacking(actor * /* other */){}void monsters::go_berserk(bool /* intentional */){}
- row 3: mass, experience modifier, charclass, holiness, resist magic- row 4: damage for each of four attacks- row 5: hit dice, described by four parameters- row 6: AC, evasion, speed, speed_inc, sec(spell), corpse_thingy,
- row 3: monster resistance flags- row 4: mass, experience modifier, charclass, holiness, resist magic- row 5: damage for each of four attacks- row 6: hit dice, described by four parameters- row 7: AC, evasion, speed, speed_inc, sec(spell), corpse_thingy,
/* ************************************************************************Josh added the following, but they just won't work in the game just yet ...besides, four bear types !?!?! isn't that a *bit* excessive given thelimited diversity of existing monster types?I'm still far from happy about the inclusion of "Shuggoths" -- I just donot think it fits into Crawl ... {dlb}************************************************************************ *///jmf: it's never created anywhere yet, so you can save the punctuation.// as to bears & wolves: the lair needs more variety.
MONS_SHUGGOTH, 'A', LIGHTGREEN, "shuggoth",M_NO_SKELETON | M_SEE_INVIS,MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD,1000, 10, MONS_SHUGGOTH, MONS_SHUGGOTH, MH_DEMONIC, 300,{ 5, 5, 5, 0 },{ 10, 4, 4, 0 },10, 10, 20, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, -1, I_NORMAL,MONUSE_NOTHING},#endif{
}int item_special_wield_effect(const item_def &item){if (item.base_type != OBJ_WEAPONS || !is_artefact(item))return (SPWLD_NONE);int i_eff = SPWPN_NORMAL;if (is_random_artefact( item ))i_eff = randart_wpn_property(item, RAP_BRAND);elsei_eff = item.special;switch (i_eff){case SPWPN_SINGING_SWORD:return (SPWLD_SING);case SPWPN_WRATH_OF_TROG:return (SPWLD_TROG);case SPWPN_SCYTHE_OF_CURSES:return (SPWLD_CURSE);case SPWPN_MACE_OF_VARIABILITY:return (SPWLD_VARIABLE);case SPWPN_GLAIVE_OF_PRUNE:return (SPWLD_NONE);case SPWPN_SCEPTRE_OF_TORMENT:return (SPWLD_TORMENT);case SPWPN_SWORD_OF_ZONGULDROK:return (SPWLD_ZONGULDROK);case SPWPN_SWORD_OF_POWER:return (SPWLD_POWER);case SPWPN_STAFF_OF_OLGREB:return (SPWLD_OLGREB);case SPWPN_STAFF_OF_WUCAD_MU:return (SPWLD_WUCAD_MU);default:return (SPWLD_NONE);}
struct melee_attack{public:// At the moment this only covers players fighting monstersactor *attacker, *defender;// If attacker and/or defender are monsters, these are set.monsters *atk, *def;bool did_hit;bool unarmed_ok;int attack_number;int to_hit;int base_damage;int potential_damage;int damage_done;int special_damage;int aux_damage;bool stab_attempt;int stab_bonus;int min_delay;int final_attack_delay;// Attacker's damage output potential:item_def *weapon;int damage_brand; // Can be special even if unarmed (transforms)int wpn_skill, hands;int spwld; // Special wield effects?bool hand_half_bonus;// If weapon is a randart, its properties.randart_properties_t art_props;// Attack messagesstd::string attack_verb, verb_degree;std::string no_damage_message;std::string special_damage_message;std::string unarmed_attack;item_def *shield;// Armour penalties?int heavy_armour_penalty;bool can_do_unarmed;// Attacker uses watery terrain to advantage vs defender. Implies that// both attacker and defender are in water.bool water_attack;public:melee_attack(actor *attacker, actor *defender,bool allow_unarmed = true, int attack_num = -1);
// Applies attack damage and other effects.bool attack();int calc_to_hit(bool random = true);private:void init_attack();bool is_water_attack(const actor *, const actor *) const;void check_hand_half_bonus_eligible();void check_autoberserk();void check_special_wield_effects();void emit_nodmg_hit_message();std::string debug_damage_number();std::string special_attack_punctuation();std::string attack_strength_punctuation();private:// Monster-attack specific stuffbool mons_attack_you();bool mons_attack_mons();int mons_to_hit();private:// Player-attack specific stuffbool player_attack();bool player_aux_unarmed();bool player_apply_aux_unarmed();int player_stat_modify_damage(int damage);int player_aux_stat_modify_damage(int damage);int player_to_hit(bool random_factor);void player_apply_attack_delay();int player_apply_water_attack_bonus(int damage);int player_apply_weapon_bonuses(int damage);int player_apply_weapon_skill(int damage);int player_apply_fighting_skill(int damage, bool aux);int player_apply_misc_modifiers(int damage);int player_apply_monster_ac(int damage);void player_weapon_auto_id();int player_stab_weapon_bonus(int damage);int player_stab(int damage);int player_weapon_type_modify(int damage);bool player_hits_monster();int player_calc_base_weapon_damage();int player_calc_base_unarmed_damage();bool player_hurt_monster();void player_exercise_combat_skills();bool player_monattk_hit_effects(bool mondied);void player_calc_brand_damage(int res, const char *message_format);bool player_apply_damage_brand();void player_sustain_passive_damage();int player_staff_damage(int skill);void player_apply_staff_damage();bool player_check_monster_died();void player_calc_hit_damage();void player_stab_check();int player_weapon_speed();int player_unarmed_speed();int player_apply_shield_delay(int attack_delay);void player_announce_hit();std::string player_why_missed();void player_warn_miss();};
const int weapon = you.equip[EQ_WEAPON];const bool ur_armed = (weapon != -1); // compacts code a bit {dlb}int wpn_skill = SK_UNARMED_COMBAT;if (weapon != -1){wpn_skill = weapon_skill( you.inv[weapon].base_type,you.inv[weapon].sub_type );}int your_to_hit;// preliminary to_hit modifications:your_to_hit = 15 + (calc_stat_to_hit_base() / 2);if (water_attack)your_to_hit += 5;if (wearing_amulet(AMU_INACCURACY))your_to_hit -= 5;// if you can't see yourself, you're a little less acurate.if (you.invis && !player_see_invis())your_to_hit -= 5;// fighting contributionyour_to_hit += maybe_random2(1 + you.skills[SK_FIGHTING], random_factor);// weapon skill contributionif (ur_armed){if (wpn_skill != SK_FIGHTING){your_to_hit += maybe_random2(you.skills[wpn_skill] + 1,random_factor);}}else{ // ...you must be unarmedyour_to_hit +=(you.species == SP_TROLL || you.species == SP_GHOUL) ? 4 : 2;your_to_hit += maybe_random2(1 + you.skills[SK_UNARMED_COMBAT],random_factor);}// weapon bonus contributionif (ur_armed){if (you.inv[ weapon ].base_type == OBJ_WEAPONS){your_to_hit += you.inv[ weapon ].plus;your_to_hit += property( you.inv[ weapon ], PWPN_HIT );if (get_equip_race(you.inv[ weapon ]) == ISFLAG_ELVEN&& player_genus(GENPC_ELVEN)){your_to_hit += (random_factor && coinflip() ? 2 : 1);}}else if (item_is_staff( you.inv[ weapon ] )){// magical staffyour_to_hit += property( you.inv[ weapon ], PWPN_HIT );}}// slaying bonusyour_to_hit += slaying_bonus(PWPN_HIT);// hunger penaltyif (you.hunger_state == HS_STARVING)your_to_hit -= 3;// armour penaltyyour_to_hit -= heavy_armour;#if DEBUG_DIAGNOSTICSint roll_hit = your_to_hit;#endif// hit rollyour_to_hit = maybe_random2(your_to_hit, random_factor);#if DEBUG_DIAGNOSTICSsnprintf( info, INFO_SIZE, "to hit die: %d; rolled value: %d",roll_hit, your_to_hit );mpr( info, MSGCH_DIAGNOSTICS );#endifif (hand_and_a_half_bonus)your_to_hit += maybe_random2(3, random_factor);if (ur_armed && wpn_skill == SK_SHORT_BLADES && you.sure_blade)your_to_hit += 5 +(random_factor ? random2limit( you.sure_blade, 10 ) :you.sure_blade / 2);// other stuffif (!ur_armed){if ( you.confusing_touch )// just trying to touch is easier that trying to damageyour_to_hit += maybe_random2(you.dex, random_factor);switch ( you.attribute[ATTR_TRANSFORMATION] ){case TRAN_NONE:break;case TRAN_SPIDER:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_ICE_BEAST:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_BLADE_HANDS:your_to_hit += maybe_random2(12, random_factor);break;case TRAN_STATUE:your_to_hit += maybe_random2(9, random_factor);break;case TRAN_SERPENT_OF_HELL:case TRAN_DRAGON:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_LICH:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_AIR:your_to_hit = 0;break;default:break;}}return your_to_hit;
melee_attack attk(&you, NULL);return attk.calc_to_hit(random_factor);
// fumbling in shallow water <early return>:if (player_in_water() && !player_is_swimming()){if (random2(you.dex) < 4 || one_chance_in(5)){mpr("Unstable footing causes you to fumble your attack.");return (true);}}return (false);
return (you.burden_state == BS_UNENCUMBERED&& random2(20) < you.skills[SK_UNARMED_COMBAT]&& random2(1 + heavy_armour_penalty) < 2);
// Returns true if you hit the monster.bool you_attack(int monster_attacked, bool unarmed_attacks)
//////////////////////////////////////////////////////////////////////////// Melee attackmelee_attack::melee_attack(actor *attk, actor *defn,bool allow_unarmed, int which_attack): attacker(attk), defender(defn),atk(NULL), def(NULL),unarmed_ok(allow_unarmed),attack_number(which_attack),to_hit(0), base_damage(0), potential_damage(0), damage_done(0),special_damage(0), aux_damage(0), stab_attempt(false), stab_bonus(0),weapon(NULL), damage_brand(SPWPN_NORMAL),wpn_skill(SK_UNARMED_COMBAT), hands(HANDS_ONE),spwld(SPWLD_NONE), hand_half_bonus(false),art_props(0), attack_verb(), verb_degree(),no_damage_message(), special_damage_message(), unarmed_attack(),heavy_armour_penalty(0), can_do_unarmed(false),water_attack(false)
monsters *defender = &menv[monster_attacked];int your_to_hit;int damage_done = 0;bool hit = false;bool base_nodamage = false;unsigned char stab_bonus = 0; // this is never negative {dlb}int temp_rand; // for probability determination {dlb}
init_attack();}
const int weapon = you.equip[EQ_WEAPON];const bool ur_armed = (weapon != -1); // compacts code a bit {dlb}const bool bearing_shield = (you.equip[EQ_SHIELD] != -1);
void melee_attack::check_hand_half_bonus_eligible(){hand_half_bonus =unarmed_ok&& !can_do_unarmed&& !shield&& weapon&& !item_cursed( *weapon )&& hands == HANDS_HALF;}
char str_pass[ ITEMNAME_SIZE ];char base_damage_message[ITEMNAME_SIZE];char special_damage_message[ITEMNAME_SIZE];
wpn_skill = weapon? weapon_skill( *weapon ) : SK_UNARMED_COMBAT;if (weapon){hands = hands_reqd( *weapon, attacker->body_size() );spwld = item_special_wield_effect( *weapon );}
// We're trying to hit a monster, break out of travel/explore now.interrupt_activity(AI_HIT_MONSTER, defender);if (ur_armed && (you.inv[weapon].base_type != OBJ_WEAPONS|| you.inv[weapon].sub_type == WPN_BOW || you.inv[weapon].sub_type == WPN_SLING))learned_something_new(TUT_WIELD_WEAPON);if (ur_armed && you.inv[weapon].base_type == OBJ_WEAPONS&& is_random_artefact( you.inv[weapon] ))
void melee_attack::check_autoberserk(){if (weapon&& art_props[RAP_ANGRY] >= 1&& !one_chance_in(1 + art_props[RAP_ANGRY]))
// Probably over protective, but in this code we're going// to play it safe. -- bwrfor (int i = 0; i < RA_PROPERTIES; i++)art_proprt[i] = 0;
case SPWLD_TROG:if (coinflip())attacker->go_berserk(false);break;case SPWLD_WUCAD_MU:// XXX At some distant point in the future, allow// miscast_effect to operate on any actor.if (one_chance_in(9) && attacker->atype() == ACT_PLAYER){miscast_effect( SPTYP_DIVINATION, random2(9), random2(70), 100,"the Staff of Wucad Mu" );}break;default:break;
// if we're not getting potential unarmed attacks, and not wearing a// shield, and have a suitable uncursed weapon we get the bonus.bool use_hand_and_a_half_bonus = false;
// The attacker loses nutrition.attacker->make_hungry(3, true);
if (weapon != -1){wpn_skill = weapon_skill( you.inv[weapon].base_type,you.inv[weapon].sub_type );
// Trying to stay general beyond this point is a recipe for insanity.// Maybe when Stone Soup hits 1.0... :-)return (attacker->atype() == ACT_PLAYER? player_attack() :defender->atype() == ACT_PLAYER? mons_attack_you() :mons_attack_mons() );}bool melee_attack::player_attack(){potential_damage =!weapon? player_calc_base_unarmed_damage(): player_calc_base_weapon_damage();
/**************************************************************************** ** IMPORTANT: When altering damage routines, must also change in ouch.cc ** for saving of player ghosts. ** ****************************************************************************/bool helpless = mons_class_flag(defender->type, M_NO_EXP_GAIN)|| mons_is_statue(defender->type);
// This actually does more than calculate damage - it also sets up// messages, etc.player_calc_hit_damage();
if (mons_friendly(defender))did_god_conduct(DID_ATTACK_FRIEND, 5);
// always upset monster regardless of damagebehaviour_event(def, ME_WHACK, MHITYOU);if (player_hurt_monster())player_exercise_combat_skills();if (player_check_monster_died())return (true);
//jmf: check for backlight enchantmentif (mons_has_ench(defender, ENCH_BACKLIGHT_I, ENCH_BACKLIGHT_IV))your_to_hit += 2 + random2(8);
if ((did_primary_hit || did_hit) && def->type != -1)print_wounds(def);
if (ur_armed &&you.inv[ weapon ].base_type == OBJ_WEAPONS &&is_random_artefact( you.inv[ weapon ] ) &&art_proprt[RAP_ANGRY] >= 1 &&random2(1 + art_proprt[RAP_ANGRY])){go_berserk(false);}
// Returns true to end the attack round.bool melee_attack::player_aux_unarmed(){damage_brand = SPWPN_NORMAL;int uattack = UNAT_NO_ATTACK;
case SPWLD_WUCAD_MU:if (one_chance_in(9))
if ((you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON|| player_genus(GENPC_DRACONIAN)|| (you.species == SP_MERFOLK && player_is_swimming())|| you.mutation[ MUT_STINGER ])&& one_chance_in(3))
// if (you.mutation[MUT_DRAIN_LIFE])// special_brand = SPWPN_DRAINING;
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SERPENT_OF_HELL|| you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON|| you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER){continue;}
if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE){switch (you.attribute[ATTR_TRANSFORMATION])
unarmed_attack = "kick";aux_damage = ((you.mutation[MUT_HOOVES]|| you.species == SP_CENTAUR) ? 10 : 5);break;case 1:if (uattack != UNAT_HEADBUTT)
case TRAN_SPIDER:damage = 5;// special_brand = SPWPN_VENOM;break;case TRAN_ICE_BEAST:damage = 12;// special_brand = SPWPN_FREEZING;break;case TRAN_BLADE_HANDS:damage = 12 + (you.strength / 4) + (you.dex / 4);break;case TRAN_STATUE:damage = 12 + you.strength;break;case TRAN_SERPENT_OF_HELL:case TRAN_DRAGON:damage = 20 + you.strength;break;case TRAN_LICH:damage = 5;// special_brand = SPWPN_DRAINING;break;case TRAN_AIR:damage = 0;break;
if ((!you.mutation[MUT_HORNS] && you.species != SP_KENKU)|| !one_chance_in(3)){continue;}
damage += (you.mutation[ MUT_CLAWS ] * 2);}
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SERPENT_OF_HELL|| you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER|| you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON){continue;}
damage += you.skills[SK_UNARMED_COMBAT];}else{if (you.inv[ weapon ].base_type == OBJ_WEAPONS|| item_is_staff( you.inv[ weapon ] )){damage = property( you.inv[ weapon ], PWPN_DAMAGE );}}
unarmed_attack =(you.species == SP_KENKU) ? "peck" : "headbutt";
#if DEBUG_DIAGNOSTICSconst int base_damage = damage;#endif
aux_damage = 5 + you.mutation[MUT_HORNS] * 3;// minotaurs used to get +5 damage here, now they get// +6 because of the horns.if (you.equip[EQ_HELMET] != -1&& (get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_HELMET|| get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_HELM)){aux_damage += 2;if (get_helmet_desc(you.inv[you.equip[EQ_HELMET]]) == THELM_DESC_SPIKED|| get_helmet_desc(you.inv[you.equip[EQ_HELMET]]) == THELM_DESC_HORNED){aux_damage += 3;}}break;case 2: /* draconians */if (uattack != UNAT_TAILSLAP){// not draconian, and not wet merfolkif ((!player_genus(GENPC_DRACONIAN)&& (!(you.species == SP_MERFOLK && player_is_swimming()))&& !you.mutation[ MUT_STINGER ])|| (!one_chance_in(4)))
if (ur_armed){if (you.inv[ weapon ].base_type == OBJ_WEAPONS|| item_is_staff( you.inv[ weapon ] )){weapon_speed2 = property( you.inv[ weapon ], PWPN_SPEED );weapon_speed2 -= you.skills[ wpn_skill ] / 2;
if (you.mutation[ MUT_STINGER ] > 0){aux_damage += (you.mutation[ MUT_STINGER ] * 2 - 1);damage_brand = SPWPN_VENOM;}
min_speed = property( you.inv[ weapon ], PWPN_SPEED ) / 2;
/* grey dracs have spiny tails, or something */// maybe add this to player messaging {dlb}//// STINGER mutation doesn't give extra damage here... that// would probably be a bit much, we'll still get the// poison bonus so it's still somewhat good.if (you.species == SP_GREY_DRACONIAN && you.experience_level >= 7)aux_damage = 12;break;
// Using both hands can get a weapon up to speed 7if ((hands == HANDS_TWO || use_hand_and_a_half_bonus)&& min_speed > 7)
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SERPENT_OF_HELL|| you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER|| you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON)
// never go faster than speed 3 (ie 3 attacks per round)if (min_speed < 3)min_speed = 3;
/* no punching with a shield or 2-handed wpn, except staves */if (shield || coinflip()|| (weapon&& hands == HANDS_TWO&& weapon->base_type != OBJ_STAVES&& weapon->sub_type != WPN_QUARTERSTAFF) ){continue;}
}else{// Unarmed speedif (you.burden_state == BS_UNENCUMBERED&& one_chance_in(heavy_armour + 1))
// unified to-hit calculationto_hit = random2( calc_your_to_hit_unarmed() );make_hungry(2, true);alert_nearby_monsters();// XXX We're clobbering did_hitdid_hit = false;if (to_hit >= def->evasion || one_chance_in(30))
case ARM_LARGE_SHIELD:if (you.skills[SK_SHIELDS] <= 10 + random2(17))weapon_speed2++;// [dshaligram] Fall-throughcase ARM_SHIELD:if (you.skills[SK_SHIELDS] <= 3 + random2(17))weapon_speed2++;break;
mprf("Your %s misses %s.",unarmed_attack.c_str(),defender->name(DESC_NOCAP_THE).c_str());
#if DEBUG_DIAGNOSTICSsnprintf( info, INFO_SIZE, "Weapon speed: %d; min: %d; speed: %d; attack time: %d",weapon_speed2, min_speed, weapon_speed2, you.time_taken );
aux_damage = player_aux_stat_modify_damage(aux_damage);aux_damage += slaying_bonus(PWPN_DAMAGE);
bool stabAttempt = false;bool rollNeeded = true;
// Clear stab bonus which will be set for the primary weapon attack.stab_bonus = 0;aux_damage = player_apply_monster_ac(aux_damage);if (aux_damage < 1)aux_damage = 0;elsehurt_monster(def, damage_done);
// confused (but not perma-confused)if (mons_has_ench(defender, ENCH_CONFUSION)&& !mons_class_flag(defender->type, M_CONFUSED)){stabAttempt = true;stab_bonus = 2;
mprf("You %s %s%s%s",unarmed_attack.c_str(),defender->name(DESC_NOCAP_THE).c_str(),debug_damage_number().c_str(),attack_strength_punctuation().c_str());if (damage_brand == SPWPN_VENOM && coinflip())poison_monster( def, true );if (mons_holiness(def) == MH_HOLY)did_god_conduct(DID_KILL_ANGEL, 1);
// see if we need to roll against dexterity / stabbingif (stabAttempt && rollNeeded)stabAttempt = (random2(200) <= you.skills[SK_STABBING] + you.dex);
std::string melee_attack::debug_damage_number(){#ifdef DEBUG_DIAGNOSTICSreturn make_stringf(" for %d", damage_done);#elsereturn ("");#endif}
// check for invisibility - no stabs on invisible monsters.if (!player_monster_visible( defender )){stabAttempt = false;stab_bonus = 0;}
std::string melee_attack::special_attack_punctuation(){if (special_damage < 3)return ".";else if (special_damage < 7)return "!";elsereturn "!!";}
#if DEBUG_DIAGNOSTICSsnprintf( info, INFO_SIZE, "your to-hit: %d; defender EV: %d",your_to_hit, defender->evasion );
std::string melee_attack::attack_strength_punctuation(){if (damage_done < HIT_WEAK)return ".";else if (damage_done < HIT_MED)return "!";else if (damage_done < HIT_STRONG)return "!!";elsereturn "!!!";}
mpr( info, MSGCH_DIAGNOSTICS );#endif
void melee_attack::player_announce_hit(){if (!verb_degree.empty() && verb_degree[0] != ' ')verb_degree = " " + verb_degree;mprf("You %s %s%s%s%s",attack_verb.c_str(),ptr_monam(def, DESC_NOCAP_THE),verb_degree.c_str(),debug_damage_number().c_str(),attack_strength_punctuation().c_str());}
if ((your_to_hit >= defender->evasion || one_chance_in(30)) ||((mons_is_paralysed(defender) || defender->behaviour == BEH_SLEEP)&& !one_chance_in(10 + you.skills[SK_STABBING]))){hit = true;int dammod = 78;
std::string melee_attack::player_why_missed(){if ((to_hit + heavy_armour_penalty / 2) >= def->evasion)return "Your armour prevents you from hitting ";elsereturn "You miss ";}
if (dam_stat_val > 11)dammod += (random2(dam_stat_val - 11) * 2);else if (dam_stat_val < 9)dammod -= (random2(9 - dam_stat_val) * 3);
// upset only non-sleeping monsters if we missedif (def->behaviour != BEH_SLEEP)behaviour_event( def, ME_WHACK, MHITYOU );
// Yes, this isn't the *2 damage that monsters get, but since// monster hps and player hps are different (as are the sizes// of the attacks) it just wouldn't be fair to give merfolk// that gross a potential... still they do get something for// making use of the water. -- bwrif (water_attack)damage += random2avg(10,2);
return (to_hit >= def->evasion|| one_chance_in(3)|| ((mons_is_paralysed(def) || def->behaviour == BEH_SLEEP)&& !one_chance_in(10 + you.skills[SK_STABBING])));}
#if DEBUG_DIAGNOSTICSconst int water_damage = damage;#endif
int melee_attack::player_stat_modify_damage(int damage){int dammod = 78;const int dam_stat_val = calc_stat_to_dam_base();if (dam_stat_val > 11)dammod += (random2(dam_stat_val - 11) * 2);else if (dam_stat_val < 9)dammod -= (random2(9 - dam_stat_val) * 3);damage *= dammod;damage /= 78;
#if DEBUG_DIAGNOSTICSconst int slay_damage = damage;
int melee_attack::player_aux_stat_modify_damage(int damage){int dammod = 10;const int dam_stat_val = calc_stat_to_dam_base();if (dam_stat_val > 11)dammod += random2(dam_stat_val - 11) / 3;if (dam_stat_val < 9)dammod -= random2(9 - dam_stat_val) / 2;damage *= dammod;damage /= 10;
mpr( info, MSGCH_DIAGNOSTICS );
int melee_attack::player_apply_water_attack_bonus(int damage){#if 0// Yes, this isn't the *2 damage that monsters get, but since// monster hps and player hps are different (as are the sizes// of the attacks) it just wouldn't be fair to give merfolk// that gross a potential... still they do get something for// making use of the water. -- bwr// return (damage + random2avg(10,2));
#if DEBUG_DIAGNOSTICSconst int roll_damage = damage_done;#endif
int melee_attack::player_apply_weapon_skill(int damage){if (weapon && (weapon->base_type == OBJ_WEAPONS|| item_is_staff( *weapon ))){damage *= 25 + (random2( you.skills[ wpn_skill ] + 1 ));damage /= 25;}
#if DEBUG_DIAGNOSTICSconst int skill_damage = damage_done;#endif
int melee_attack::player_apply_fighting_skill(int damage, bool aux){const int base = aux? 40 : 30;damage *= base + (random2(you.skills[SK_FIGHTING] + 1));damage /= base;
if (you.hunger_state == HS_STARVING)damage_done -= random2(5);
int melee_attack::player_apply_weapon_bonuses(int damage){if (weapon && weapon->base_type == OBJ_WEAPONS){int wpn_damage_plus = weapon->plus2;damage += (wpn_damage_plus > -1) ? (random2(1 + wpn_damage_plus)): -(1 + random2(-wpn_damage_plus));
#if DEBUG_DIAGNOSTICSconst int preplus_damage = damage_done;int plus_damage = damage_done;int bonus_damage = damage_done;#endifif (ur_armed && you.inv[ weapon ].base_type == OBJ_WEAPONS)
// removed 2-handed weapons from here... their "bonus" is// already included in the damage stat for the weapon -- bwrif (hand_half_bonus)damage += random2(3);if (get_equip_race(*weapon) == ISFLAG_DWARVEN&& player_genus(GENPC_DWARVEN))
#if DEBUG_DIAGNOSTICSplus_damage = damage_done;#endif
void melee_attack::player_weapon_auto_id(){if (weapon&& !is_range_weapon( *weapon )&& !item_ident( *weapon, ISFLAG_KNOW_PLUSES )&& random2(100) < you.skills[ wpn_skill ]){set_ident_flags( *weapon, ISFLAG_KNOW_PLUSES );mprf("You are wielding %s.",item_name(*weapon, DESC_NOCAP_A));more();you.wield_change = true;}}
// removed 2-handed weapons from here... their "bonus" is// already included in the damage stat for the weapon -- bwrif (use_hand_and_a_half_bonus)damage_done += random2(3);
int melee_attack::player_stab_weapon_bonus(int damage){switch (wpn_skill){case SK_SHORT_BLADES:{int bonus = (you.dex * (you.skills[SK_STABBING] + 1)) / 5;if (weapon->sub_type != WPN_DAGGER)bonus /= 2;bonus = stepdown_value( bonus, 10, 10, 30, 30 );damage += bonus;}// fall throughcase SK_LONG_SWORDS:damage *= 10 + you.skills[SK_STABBING] /(stab_bonus + (wpn_skill == SK_SHORT_BLADES ? 0 : 1));damage /= 10;// fall throughdefault:damage *= 12 + you.skills[SK_STABBING] / stab_bonus;damage /= 12;}
if (get_equip_race(you.inv[ weapon ]) == ISFLAG_ORCISH&& you.species == SP_HILL_ORC && coinflip()){damage_done++;}
int melee_attack::player_stab(int damage){// The stabbing message looks better here:if (stab_attempt){// construct reasonable messagestab_message( def, stab_bonus );
#if DEBUG_DIAGNOSTICSbonus_damage = damage_done;#endif
exercise(SK_STABBING, 1 + random2avg(5, 4));if (mons_holiness(def) == MH_NATURAL|| mons_holiness(def) == MH_HOLY){did_god_conduct(DID_STABBING, 4);}}else{stab_bonus = 0;// ok.. if you didn't backstab, you wake up the neighborhood.// I can live with that.alert_nearby_monsters();}
if (!is_range_weapon( you.inv[weapon] )&& !item_ident( you.inv[ weapon ], ISFLAG_KNOW_PLUSES )&& random2(100) < you.skills[ wpn_skill ])
if (stab_bonus){// lets make sure we have some damage to work with...if (damage < 1)damage = 1;if (def->behaviour == BEH_SLEEP){// Sleeping moster wakes up when stabbed but may be groggyif (random2(200) <= you.skills[SK_STABBING] + you.dex)
set_ident_flags( you.inv[ weapon ], ISFLAG_KNOW_PLUSES );strcpy(info, "You are wielding ");in_name( weapon , DESC_NOCAP_A, str_pass );strcat(info, str_pass);strcat(info, ".");mpr(info);more();you.wield_change = true;
unsigned int stun = random2( you.dex + 1 );if (def->speed_increment > stun)def->speed_increment -= stun;elsedef->speed_increment = 0;
if (mons_holiness(defender) == MH_NATURAL|| mons_holiness(defender) == MH_HOLY){did_god_conduct(DID_STABBING, 4);}}else
int melee_attack::player_apply_monster_ac(int damage){if (stab_bonus){// when stabbing we can get by some of the armourif (def->armour_class > 0)
stab_bonus = 0;// ok.. if you didn't backstab, you wake up the neighborhood.// I can live with that.alert_nearby_monsters();
int ac = def->armour_class- random2( you.skills[SK_STABBING] / stab_bonus );if (ac > 0)damage -= random2(1 + ac);
if (stab_bonus){// lets make sure we have some damage to work with...if (damage_done < 1)damage_done = 1;
if (!weapon)weap_type = WPN_UNARMED;else if (item_is_staff( *weapon ))weap_type = WPN_QUARTERSTAFF;else if (weapon->base_type == OBJ_WEAPONS)weap_type = weapon->sub_type;
if (defender->behaviour == BEH_SLEEP){// Sleeping moster wakes up when stabbed but may be groggyif (random2(200) <= you.skills[SK_STABBING] + you.dex){unsigned int stun = random2( you.dex + 1 );
// All weak hits look the same, except for when the player// has a non-weapon in hand. -- bwrif (damage < HIT_WEAK){if (weap_type != WPN_UNKNOWN)attack_verb = "hit";elseattack_verb = "clumsily bash";
switch (wpn_skill)
// take transformations into account, if no weapon is wieldedif (weap_type == WPN_UNARMED&& you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE){switch (you.attribute[ATTR_TRANSFORMATION]){case TRAN_SPIDER:if (damage < HIT_STRONG)attack_verb = "bite";elseattack_verb = "maul";break;case TRAN_BLADE_HANDS:if (damage < HIT_MED)attack_verb = "slash";else if (damage < HIT_STRONG)attack_verb = "slice";elseattack_verb = "shred";break;case TRAN_ICE_BEAST:case TRAN_STATUE:case TRAN_LICH:if (damage < HIT_MED)attack_verb = "punch";elseattack_verb = "pummel";break;case TRAN_DRAGON:case TRAN_SERPENT_OF_HELL:if (damage < HIT_MED)attack_verb = "claw";else if (damage < HIT_STRONG)attack_verb = "bite";else
case SK_SHORT_BLADES:{int bonus = (you.dex * (you.skills[SK_STABBING] + 1)) / 5;
attack_verb = "maul";if (defender->body_size() <= SIZE_MEDIUM && coinflip())attack_verb = "trample on";}break;case TRAN_AIR:attack_verb = "buffet";break;} // transformations
bonus = stepdown_value( bonus, 10, 10, 30, 30 );
switch (weapon? get_damage_type(*weapon) : -1){case DAM_PIERCE:if (damage < HIT_MED)attack_verb = "puncture";else if (damage < HIT_STRONG)attack_verb = "impale";else{attack_verb = "spit";verb_degree = " like a pig";}break;
damage_done += bonus;}// fall throughcase SK_LONG_SWORDS:damage_done *= 10 + you.skills[SK_STABBING] /(stab_bonus + (wpn_skill == SK_SHORT_BLADES ? 0 : 1));damage_done /= 10;// fall throughdefault:damage_done *= 12 + you.skills[SK_STABBING] / stab_bonus;damage_done /= 12;}
case DAM_SLICE:if (damage < HIT_MED)attack_verb = "slash";else if (damage < HIT_STRONG)attack_verb = "slice";else{attack_verb = "open";verb_degree = " like a pillowcase";}break;
#if DEBUG_DIAGNOSTICSstab_damage = damage_done;#endif
case DAM_BLUDGEON:if (damage < HIT_MED)attack_verb = one_chance_in(4)? "thump" : "sock";else if (damage < HIT_STRONG)attack_verb = "bludgeon";else{attack_verb = "crush";verb_degree = " like a grape";}break;
// when stabbing we can get by some of the armourif (defender->armour_class > 0){int ac = defender->armour_class- random2( you.skills[SK_STABBING] / stab_bonus );
case DAM_WHIP:if (damage < HIT_MED)attack_verb = "whack";elseattack_verb = "thrash";break;
if (ac > 0)damage_done -= random2(1 + ac);}
case -1: // unarmedif (you.species == SP_TROLL || you.mutation[MUT_CLAWS]){if (damage < HIT_MED)attack_verb = "claw";else if (damage < HIT_STRONG)attack_verb = "mangle";elseattack_verb = "eviscerate";
mpr( info, MSGCH_DIAGNOSTICS );#endif
void melee_attack::player_exercise_combat_skills(){const bool helpless = defender->cannot_fight();if (!helpless || you.skills[ wpn_skill ] < 2)exercise( wpn_skill, 1 );if ((!helpless || you.skills[SK_FIGHTING] < 2)&& one_chance_in(3)){exercise(SK_FIGHTING, 1);}}
// This doesn't actually modify damage -- bwrdamage_done = weapon_type_modify( weapon, damage_noise, damage_noise2,damage_done );
// Returns true if the combat round should end here.bool melee_attack::player_monattk_hit_effects(bool mondied){if (mons_holiness(def) == MH_HOLY)did_god_conduct(mondied? DID_KILL_ANGEL : DID_ATTACK_HOLY, 1);
if (hurt_monster(defender, damage_done))
if (weapon&& weapon->base_type == OBJ_WEAPONS&& is_demonic( *weapon )){did_god_conduct(DID_UNHOLY, 1);}if (mondied && damage_brand == SPWPN_VAMPIRICISM){if (mons_holiness(def) == MH_NATURAL&& damage_done > 0 && you.hp < you.hp_max&& !one_chance_in(5))
if (ur_armed && wpn_skill){if (!helpless || you.skills[ wpn_skill ] < 2)exercise( wpn_skill, 1 );}else{if (!helpless || you.skills[SK_UNARMED_COMBAT] < 2)exercise(SK_UNARMED_COMBAT, 1);}if ((!helpless || you.skills[SK_FIGHTING] < 2)&& one_chance_in(3)){exercise(SK_FIGHTING, 1);}
mpr("You feel better.");// more than if not killedinc_hp(1 + random2(damage_done), false);if (you.hunger_state != HS_ENGORGED)lessen_hunger(30 + random2avg(59, 2), true);did_god_conduct(DID_NECROMANCY, 2);
if (defender->hit_points < 1){#if DEBUG_DIAGNOSTICS/* note: doesn't take account of special weapons etc */snprintf( info, INFO_SIZE, "Hit for %d.", damage_done );mpr( info, MSGCH_DIAGNOSTICS );#endifif (ur_armed && melee_brand == SPWPN_VAMPIRICISM){if (mons_holiness(defender) == MH_NATURAL&& damage_done > 0 && you.hp < you.hp_max&& !one_chance_in(5)){mpr("You feel better.");
}
if (defender->type == MONS_GIANT_SPORE)mprf("You %s the giant spore.", damage_noise);else if (defender->type == MONS_BALL_LIGHTNING)mprf("You %s the ball lightning.", damage_noise);
if (!no_damage_message.empty()){if (special_damage > 0)emit_nodmg_hit_message();elsemprf("You %s %s, but do no damage.",attack_verb.c_str(),defender->name(DESC_NOCAP_THE).c_str());}if (!special_damage_message.empty())mprf("%s", special_damage_message.c_str());
if (damage_done < 1 && player_monster_visible( defender )){hit = true;base_nodamage = true;snprintf( base_damage_message, INFO_SIZE, "You %s %s.",damage_noise, ptr_monam(defender, DESC_NOCAP_THE));}
if (def->hit_points < 1){monster_die(def, KILL_YOU, 0);return (true);
if ((your_to_hit + heavy_armour / 2) >= defender->evasion)strcpy(info, "Your armour prevents you from hitting ");elsestrcpy(info, "You miss ");
void melee_attack::player_calc_brand_damage(int res,const char *message_format){if (res == 0)special_damage = random2(damage_done) / 2 + 1;else if (res < 0)special_damage = random2(damage_done) + 1;
strcat(info, ptr_monam(defender, DESC_NOCAP_THE));strcat(info, ".");mpr(info);
if (special_damage){special_damage_message = make_stringf(message_format,defender->name(DESC_NOCAP_THE).c_str(),special_attack_punctuation().c_str());
snprintf(info, INFO_SIZE, "You %s %s%s", damage_noise,ptr_monam(defender, DESC_NOCAP_THE), damage_noise2);#if DEBUG_DIAGNOSTICSstrcat( info, " for " );/* note: doesn't take account of special weapons etc */itoa( damage_done, st_prn, 10 );strcat( info, st_prn );#endifif (damage_done < HIT_WEAK)strcat(info, ".");else if (damage_done < HIT_MED)strcat(info, "!");else if (damage_done < HIT_STRONG)strcat(info, "!!");elsestrcat(info, "!!!");
case SPWPN_FLAMING:res = mons_res_fire(def);if (weapon && weapon->special == SPWPN_SWORD_OF_CEREBOV && res >= 0)res = (res > 0) - 1;
torment(TORMENT_SPWLD, you.x_pos, you.y_pos);did_god_conduct(DID_UNHOLY, 5);
case MH_UNDEAD:special_damage = 1 + random2(damage_done);break;case MH_DEMONIC:special_damage = 1 + (random2(damage_done * 15) / 10);break;default:break;
/* remember, damage_done is still useful! */if (hit){if (defender->type == MONS_JELLY|| defender->type == MONS_BROWN_OOZE|| defender->type == MONS_ACID_BLOB|| defender->type == MONS_ROYAL_JELLY)
case SPWPN_ORC_SLAYING:if (mons_species(def->type) == MONS_ORC)
int specdam = 0;if (ur_armed&& you.inv[ weapon ].base_type == OBJ_WEAPONS&& is_demonic( you.inv[ weapon ] ))
break;case SPWPN_VENOM:if (!one_chance_in(4))
// If decapitation, extra damage is bypassed.if (actor_decapitates_hydra(NULL, defender, damage_done))goto mons_dies;
special_damage_message =make_stringf("You drain %s!",defender->name(DESC_NOCAP_THE).c_str());if (one_chance_in(5))def->hit_dice--;def->max_hit_points -= 2 + random2(3);def->hit_points -= 2 + random2(3);if (def->hit_points >= def->max_hit_points)def->hit_points = def->max_hit_points;if (def->hit_dice < 1)def->hit_points = 0;special_damage = 1 + (random2(damage_done) / 2);did_god_conduct( DID_NECROMANCY, 2 );break;
// jmf: BEGIN STAFF HACK// How much bonus damage will a staff of <foo> do?// FIXME: make these not macros. inline functions?// actually, it will all be pulled out and replaced by functions -- {dlb}//// This is similar to the previous, in both value and distribution, except// that instead of just SKILL, its now averaged with Evocations. -- bwr#define STAFF_DAMAGE(SKILL) (roll_dice( 3, 1 + (you.skills[(SKILL)] + you.skills[SK_EVOCATIONS]) / 12 ))#define STAFF_COST 2
/* 9 = speed - done before */case SPWPN_VORPAL:special_damage = 1 + random2(damage_done) / 2;// note: leaving special_damage_message empty because there// isn't one.break;
// magic staves have their own special damageif (ur_armed && item_is_staff( you.inv[weapon] ))
case SPWPN_VAMPIRICISM:if (mons_holiness(def) != MH_NATURAL ||mons_res_negative_energy(def) > 0 ||damage_done < 1 || you.hp == you.hp_max ||one_chance_in(5))
if (you.magic_points >= STAFF_COST&& random2(20) <= you.skills[SK_EVOCATIONS]){switch (you.inv[weapon].sub_type){case STAFF_AIR:if (damage_done + you.skills[SK_AIR_MAGIC] > random2(30)){if (mons_res_elec(defender))break;
// thus is probably more valuable on larger weapons?if (weapon&& is_fixed_artefact( *weapon )&& weapon->special == SPWPN_VAMPIRES_TOOTH){inc_hp(damage_done, false);}else{inc_hp(1 + random2(damage_done), false);}if (you.hunger_state != HS_ENGORGED)lessen_hunger(random2avg(59, 2), true);did_god_conduct( DID_NECROMANCY, 2 );break;
specdam = STAFF_DAMAGE(SK_AIR_MAGIC);
case SPWPN_DISRUPTION:if (mons_holiness(def) == MH_UNDEAD && !one_chance_in(3)){special_damage_message =str_simple_monster_message(def, " shudders.");special_damage += random2avg((1 + (damage_done * 3)), 3);}break;case SPWPN_PAIN:if (mons_res_negative_energy(def) <= 0&& random2(8) <= you.skills[SK_NECROMANCY]){special_damage_message =str_simple_monster_message(def, " convulses in agony.");special_damage += random2( 1 + you.skills[SK_NECROMANCY] );}did_god_conduct(DID_NECROMANCY, 4);break;
if (specdam){snprintf( special_damage_message, INFO_SIZE,"%s is jolted!",ptr_monam(defender, DESC_CAP_THE) );}}break;
case SPWPN_DISTORTION://jmf: blink frogs *like* distortion// I think could be amended to let blink frogs "grow" like// jellies do {dlb}if (defender->id() == MONS_BLINK_FROG){if (one_chance_in(5)){emit_nodmg_hit_message();simple_monster_message(def," basks in the translocular energy." );heal_monster(def, 1 + random2avg(7, 2), true); // heh heh}break;}if (one_chance_in(3)){special_damage_message =make_stringf("Space bends around %s.",defender->name(DESC_NOCAP_THE).c_str());special_damage += 1 + random2avg(7, 2);break;}
case STAFF_COLD: // FIXME: I don't think I used these right ...if (mons_res_cold(defender) > 0)break;
if (one_chance_in(3)){special_damage_message =make_stringf("Space warps horribly around %s!",ptr_monam(def, DESC_NOCAP_THE));special_damage += 3 + random2avg(24, 2);break;}
if (specdam){snprintf( special_damage_message, INFO_SIZE,"You freeze %s!",ptr_monam(defender, DESC_NOCAP_THE) );}break;
if (coinflip()){emit_nodmg_hit_message();monster_die(def, KILL_RESET, 0);return (true);}break;
case STAFF_EARTH:if (mons_flies(defender))break; //jmf: lame, but someone ought to resist
case SPWPN_CONFUSE:{emit_nodmg_hit_message();// declaring these just to pass to the enchant functionbolt beam_temp;beam_temp.flavour = BEAM_CONFUSION;mons_ench_f2( def, beam_temp );you.confusing_touch -= random2(20);if (you.confusing_touch < 1)you.confusing_touch = 1;break;}}
if (specdam){snprintf( special_damage_message, INFO_SIZE,"You crush %s!",ptr_monam(defender, DESC_NOCAP_THE) );}break;
void melee_attack::player_sustain_passive_damage(){if (mons_class_flag(defender->id(), M_ACID_SPLASH))weapon_acid(5);}
if (specdam){snprintf( special_damage_message, INFO_SIZE,"You burn %s!",ptr_monam(defender, DESC_NOCAP_THE) );}break;
const int staff_cost = 2;if (you.magic_points < staff_cost|| random2(15) > you.skills[SK_EVOCATIONS]){return;}switch (weapon->sub_type){case STAFF_AIR:if (damage_done + you.skills[SK_AIR_MAGIC] <= random2(20))break;
if (random2(100) < temp_rand){// HACK: poison_monster emits a message, so// we need to get ours out first.if ( base_nodamage ){mpr(base_damage_message);base_nodamage = false; // don't double-message}poison_monster(defender, true);}break;case STAFF_DEATH:if (mons_res_negative_energy(defender) > 0)break;if (random2(8) <= you.skills[SK_NECROMANCY]){specdam = STAFF_DAMAGE(SK_NECROMANCY);if (specdam){snprintf( special_damage_message, INFO_SIZE,"%s convulses in agony!",ptr_monam(defender, DESC_CAP_THE));did_god_conduct(DID_NECROMANCY, 4);}}break;case STAFF_POWER:case STAFF_SUMMONING:case STAFF_CHANNELING:case STAFF_CONJURATION:case STAFF_ENCHANTMENT:case STAFF_ENERGY:case STAFF_WIZARDRY:break;
case STAFF_COLD:if (mons_res_cold(def) > 0)break;
if (!item_type_known(you.inv[weapon])){set_ident_flags( you.inv[weapon], ISFLAG_KNOW_TYPE );strcpy(info, "You are wielding ");in_name( weapon, DESC_NOCAP_A, str_pass);strcat(info, str_pass);strcat(info, ".");mpr(info);more();you.wield_change = true;}}#undef STAFF_DAMAGE#undef STAFF_COST// END STAFF HACK}else
if (special_damage)
// handle special brand damage (unarmed or armed non-staff ego):int res = 0;switch (melee_brand){case SPWPN_NORMAL:break;case SPWPN_FLAMING:specdam = 0;res = mons_res_fire(defender);if (ur_armed && you.inv[weapon].special == SPWPN_SWORD_OF_CEREBOV){if (res < 3 && res > 0)res = 0;else if (res == 0)res = -1;}if (res == 0)specdam = random2(damage_done) / 2 + 1;else if (res < 0)specdam = random2(damage_done) + 1;if (specdam){snprintf(special_damage_message, INFO_SIZE,"You burn %s",ptr_monam(defender, DESC_NOCAP_THE));if (specdam < 3)strcat(special_damage_message, ".");else if (specdam < 7)strcat(special_damage_message, "!");elsestrcat(special_damage_message, "!!");}break;case SPWPN_FREEZING:specdam = 0;
special_damage_message =make_stringf("You freeze %s!",defender->name(DESC_NOCAP_THE).c_str());}break;
if (specdam){snprintf(special_damage_message, INFO_SIZE,"You freeze %s",ptr_monam(defender, DESC_NOCAP_THE));
if (special_damage){special_damage_message =make_stringf("You crush %s!",defender->name(DESC_NOCAP_THE).c_str());}break;
if (specdam < 3)strcat(special_damage_message, ".");else if (specdam < 7)strcat(special_damage_message, "!");elsestrcat(special_damage_message, "!!");
case STAFF_FIRE:if (mons_res_fire(def) > 0)break;
case SPWPN_HOLY_WRATH:// there should be a case in here for holy monsters,// see elsewhere in fight.cc {dlb}specdam = 0;switch (mons_holiness(defender)){case MH_UNDEAD:specdam = 1 + random2(damage_done);break;
if (mons_res_fire(def) < 0)special_damage += player_staff_damage(SK_FIRE_MAGIC);
if (mons_flies(defender))break;else if (mons_res_elec(defender) > 0)break;else if (one_chance_in(3)){strcpy(special_damage_message,"There is a sudden explosion of sparks!");specdam = random2avg(28, 3);}break;
case STAFF_DEATH:if (mons_res_negative_energy(def) > 0)break;
case SPWPN_ORC_SLAYING:if (mons_species(defender->type) == MONS_ORC)hurt_monster(defender, 1 + random2(damage_done));break;
if (random2(8) <= you.skills[SK_NECROMANCY]){special_damage = player_staff_damage(SK_NECROMANCY);
case SPWPN_VENOM:if (!one_chance_in(4)){// HACK - poison_monster bites us againif ( base_nodamage ){mpr(base_damage_message);base_nodamage = false;}poison_monster(defender, true);}break;
if (special_damage){special_damage_message =make_stringf("%s convulses in agony!",defender->name(DESC_CAP_THE).c_str());
snprintf(special_damage_message, INFO_SIZE, "You drain %s!",ptr_monam(defender, DESC_NOCAP_THE));
case STAFF_POWER:case STAFF_SUMMONING:case STAFF_CHANNELING:case STAFF_CONJURATION:case STAFF_ENCHANTMENT:case STAFF_ENERGY:case STAFF_WIZARDRY:break;
specdam = 1 + (random2(damage_done) / 2);did_god_conduct( DID_NECROMANCY, 2 );break;
bool melee_attack::player_check_monster_died(){if (def->hit_points < 1){#if DEBUG_DIAGNOSTICS/* note: doesn't take account of special weapons etc */mprf( MSGCH_DIAGNOSTICS, "Hit for %d.", damage_done );#endif
case SPWPN_VORPAL:specdam = 1 + random2(damage_done) / 2;// note: leaving special_damage_message empty because there// isn't one.break;
if (def->type == MONS_GIANT_SPORE || def->type == MONS_BALL_LIGHTNING)mprf("You %s %s.", attack_verb.c_str(),ptr_monam(def, DESC_NOCAP_THE));
if (mons_holiness(defender) != MH_NATURAL ||mons_res_negative_energy(defender) > 0 ||damage_done < 1 || you.hp == you.hp_max ||one_chance_in(5))break;
return (true);}
// thus is probably more valuable on larger weapons?if (ur_armed&& is_fixed_artefact( you.inv[weapon] )&& you.inv[weapon].special == SPWPN_VAMPIRES_TOOTH){inc_hp(damage_done, false);}elseinc_hp(1 + random2(damage_done), false);
void melee_attack::player_calc_hit_damage(){potential_damage = player_stat_modify_damage(potential_damage);
if (you.hunger_state != HS_ENGORGED)lessen_hunger(random2avg(59, 2), true);
if (water_attack)potential_damage = player_apply_water_attack_bonus(potential_damage);// apply damage bonus from ring of slaying// (before randomization -- some of these rings// are stupidly powerful) -- GDLpotential_damage += slaying_bonus(PWPN_DAMAGE);damage_done = potential_damage > 0? 1 + random2(potential_damage) : 0;damage_done = player_apply_weapon_skill(damage_done);damage_done = player_apply_fighting_skill(damage_done, false);damage_done = player_apply_misc_modifiers(damage_done);damage_done = player_apply_weapon_bonuses(damage_done);
case SPWPN_DISRUPTION:specdam = 0;if (mons_holiness(defender) == MH_UNDEAD && !one_chance_in(3)){// see previous HACKsif ( base_nodamage ){mpr(base_damage_message);base_nodamage = false;}simple_monster_message(defender, " shudders.");specdam += random2avg((1 + (damage_done * 3)), 3);}break;
damage_done = player_stab(damage_done);damage_done = player_apply_monster_ac(damage_done);
case SPWPN_PAIN:specdam = 0;if (mons_res_negative_energy(defender) <= 0&& random2(8) <= you.skills[SK_NECROMANCY]){// see previous HACKsif ( base_nodamage ){mpr(base_damage_message);base_nodamage = false;}simple_monster_message(defender, " convulses in agony.");specdam += random2( 1 + you.skills[SK_NECROMANCY] );}did_god_conduct(DID_NECROMANCY, 4);break;
// This doesn't actually modify damage -- bwrdamage_done = player_weapon_type_modify( damage_done );
case SPWPN_DISTORTION://jmf: blink frogs *like* distortion// I think could be amended to let blink frogs "grow" like// jellies do {dlb}if (defender->type == MONS_BLINK_FROG){if (one_chance_in(5)){if ( base_nodamage ){mpr(base_damage_message);base_nodamage = false;}simple_monster_message( defender," basks in the translocular energy." );heal_monster(defender, 1 + random2avg(7, 2), true); // heh heh}break;}
if (damage_done < 0)damage_done = 0;}
if (one_chance_in(3)){snprintf(special_damage_message, INFO_SIZE,"Space bends around %s.",ptr_monam(defender, DESC_NOCAP_THE));specdam += 1 + random2avg(7, 2);break;}
int melee_attack::calc_to_hit(bool random){return (attacker->atype() == ACT_PLAYER? player_to_hit(random): mons_to_hit());}
if (one_chance_in(3)){snprintf( special_damage_message, INFO_SIZE,"Space warps horribly around %s!",ptr_monam(defender, DESC_NOCAP_THE));specdam += 3 + random2avg(24, 2);break;}
int melee_attack::player_to_hit(bool random_factor){heavy_armour_penalty = calc_heavy_armour_penalty(random_factor);can_do_unarmed = player_fights_well_unarmed(heavy_armour_penalty);
if (coinflip()){if ( base_nodamage ){mpr(base_damage_message);base_nodamage = false;}monster_teleport(defender, coinflip());break;}
int your_to_hit = 15 + (calc_stat_to_hit_base() / 2);
if (coinflip()){if ( base_nodamage )mpr(base_damage_message);monster_die(defender, KILL_RESET, 0);return (true);}break;
if (water_attack)your_to_hit += 5;
// declaring these just to pass to the enchant functionstruct bolt beam_temp;beam_temp.flavour = BEAM_CONFUSION;
// if you can't see yourself, you're a little less acurate.if (you.invis && !player_see_invis())your_to_hit -= 5;
you.confusing_touch -= random2(20);if (you.confusing_touch < 1)you.confusing_touch = 1;}break;} /* end switch */}/* remember, the hydra function sometimes skips straight to mons_dies */#if DEBUG_DIAGNOSTICSsnprintf( info, INFO_SIZE, "brand: %d; melee special damage: %d",melee_brand, specdam );mpr( info, MSGCH_DIAGNOSTICS );#endifif ( base_nodamage )
// weapon skill contributionif (weapon){if (wpn_skill != SK_FIGHTING)
hurt_monster( defender, specdam );} // end if (hit)mons_dies:if (defender->hit_points < 1){monster_die(defender, KILL_YOU, 0);return (hit);
your_to_hit += maybe_random2(1 + you.skills[SK_UNARMED_COMBAT],random_factor);
if ((you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON|| player_genus(GENPC_DRACONIAN)|| (you.species == SP_MERFOLK && player_is_swimming())|| you.mutation[ MUT_STINGER ])&& one_chance_in(3))
if (get_equip_race(*weapon) == ISFLAG_ELVEN&& player_genus(GENPC_ELVEN))
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SERPENT_OF_HELL|| you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON|| you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER){continue;}
// hunger penaltyif (you.hunger_state == HS_STARVING)your_to_hit -= 3;
case 1:if (unarmed_attack != UNAT_HEADBUTT){if ((!you.mutation[MUT_HORNS] && you.species != SP_KENKU)|| !one_chance_in(3)){continue;}}
#if DEBUG_DIAGNOSTICSint roll_hit = your_to_hit;#endif
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SERPENT_OF_HELL|| you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER|| you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON){continue;}
// hit rollyour_to_hit = maybe_random2(your_to_hit, random_factor);
if (you.equip[EQ_HELMET] != -1&& (get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_HELMET|| get_helmet_type(you.inv[you.equip[EQ_HELMET]]) == THELM_HELM)){sc_dam += 2;
if (weapon && wpn_skill == SK_SHORT_BLADES && you.sure_blade)your_to_hit += 5 +(random_factor ? random2limit( you.sure_blade, 10 ) :you.sure_blade / 2);
if (get_helmet_desc(you.inv[you.equip[EQ_HELMET]]) == THELM_DESC_SPIKED|| get_helmet_desc(you.inv[you.equip[EQ_HELMET]]) == THELM_DESC_HORNED){sc_dam += 3;}}break;
// other stuffif (!weapon){if ( you.confusing_touch )// just trying to touch is easier that trying to damageyour_to_hit += maybe_random2(you.dex, random_factor);
case 2: /* draconians */if (unarmed_attack != UNAT_TAILSLAP){// not draconian and not wet merfolkif ((!player_genus(GENPC_DRACONIAN)&& (!(you.species == SP_MERFOLK && player_is_swimming()))&& !you.mutation[ MUT_STINGER ])|| (!one_chance_in(4)))
switch ( you.attribute[ATTR_TRANSFORMATION] ){case TRAN_SPIDER:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_ICE_BEAST:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_BLADE_HANDS:your_to_hit += maybe_random2(12, random_factor);break;case TRAN_STATUE:your_to_hit += maybe_random2(9, random_factor);break;case TRAN_SERPENT_OF_HELL:case TRAN_DRAGON:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_LICH:your_to_hit += maybe_random2(10, random_factor);break;case TRAN_AIR:your_to_hit = 0;break;case TRAN_NONE:default:break;}}
if (you.mutation[ MUT_STINGER ] > 0){sc_dam += (you.mutation[ MUT_STINGER ] * 2 - 1);brand = SPWPN_VENOM;}
// not paying attention (but not batty)if (def->foe != MHITYOU && !testbits(def->flags, MF_BATTY)){stab_attempt = true;stab_bonus = 3;}
/* grey dracs have spiny tails, or something */// maybe add this to player messaging {dlb}//// STINGER mutation doesn't give extra damage here... that// would probably be a bit much, we'll still get the// poison bonus so it's still somewhat good.if (you.species == SP_GREY_DRACONIAN && you.experience_level >= 7){sc_dam = 12;}break;
// confused (but not perma-confused)if (mons_has_ench(def, ENCH_CONFUSION)&& !mons_class_flag(def->type, M_CONFUSED)){stab_attempt = true;stab_bonus = 2;}
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SERPENT_OF_HELL|| you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER|| you.attribute[ATTR_TRANSFORMATION] == TRAN_ICE_BEAST|| you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON){continue;}
// sleepingif (def->behaviour == BEH_SLEEP){stab_attempt = true;roll_needed = false;stab_bonus = 1;}
/* no punching with a shield or 2-handed wpn, except staves */if (bearing_shield || coinflip()|| (ur_armed && hands == HANDS_TWO&& you.inv[weapon].base_type != OBJ_STAVES&& you.inv[weapon].sub_type != WPN_QUARTERSTAFF) ){continue;}
// helpless (plants, etc)if (defender->cannot_fight())stab_attempt = false;
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS){strcpy(attack_name, "slash");sc_dam += 6;}break;
void melee_attack::player_apply_attack_delay(){int attack_delay = weapon? player_weapon_speed() : player_unarmed_speed();attack_delay = player_apply_shield_delay(attack_delay);
damage = sc_dam; //4 + you.experience_level / 3;
int melee_attack::player_weapon_speed(){int attack_delay = 0;if (weapon && (weapon->base_type == OBJ_WEAPONS|| item_is_staff( *weapon ))){attack_delay = property( *weapon, PWPN_SPEED );attack_delay -= you.skills[ wpn_skill ] / 2;min_delay = property( *weapon, PWPN_SPEED ) / 2;
alert_nearby_monsters();
// Short blades can get up to at least unarmed speed.if (wpn_skill == SK_SHORT_BLADES && min_delay > 5)min_delay = 5;// Using both hands can get a weapon up to speed 7if ((hands == HANDS_TWO || hand_half_bonus)&& min_delay > 7){min_delay = 7;}// never go faster than speed 3 (ie 3 attacks per round)if (min_delay < 3)min_delay = 3;// Hand and a half bonus only helps speed up to a point, any more// than speed 10 must come from skill and the weaponif (hand_half_bonus && attack_delay > 10)attack_delay--;// apply minimum to weapon skill modificationif (attack_delay < min_delay)attack_delay = min_delay;if (weapon->base_type == OBJ_WEAPONS&& damage_brand == SPWPN_SPEED){attack_delay = (attack_delay + 1) / 2;}}return (attack_delay);}
if (your_to_hit >= defender->evasion || one_chance_in(30)){hit = true;int dammod = 10;
int melee_attack::player_unarmed_speed(){int unarmed_delay = 10;
const int dam_stat_val = calc_stat_to_dam_base();
min_delay = 5;// Unarmed speedif (you.burden_state == BS_UNENCUMBERED&& one_chance_in(heavy_armour_penalty + 1)){unarmed_delay = 10 - you.skills[SK_UNARMED_COMBAT] / 5;/* this shouldn't happen anyway...sanity */if (unarmed_delay < min_delay)unarmed_delay = min_delay;}
damage *= dammod;damage /= 10;
int melee_attack::player_apply_shield_delay(int attack_delay){if (shield){switch (shield->sub_type){case ARM_LARGE_SHIELD:if (you.skills[SK_SHIELDS] <= 10 + random2(17))attack_delay++;// [dshaligram] Fall-throughcase ARM_SHIELD:if (you.skills[SK_SHIELDS] <= 3 + random2(17))attack_delay++;break;}}
damage_done *= 25 + (random2(you.skills[SK_UNARMED_COMBAT]+1));damage_done /= 25;
if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE){switch (you.attribute[ATTR_TRANSFORMATION]){case TRAN_SPIDER:damage = 5;break;case TRAN_ICE_BEAST:damage = 12;break;case TRAN_BLADE_HANDS:damage = 12 + (you.strength / 4) + (you.dex / 4);break;case TRAN_STATUE:damage = 12 + you.strength;break;case TRAN_SERPENT_OF_HELL:case TRAN_DRAGON:damage = 20 + you.strength;break;case TRAN_LICH:damage = 5;break;case TRAN_AIR:damage = 0;break;}}else if (you.equip[ EQ_GLOVES ] == -1){// claw damage only applies for bare handsif (you.species == SP_TROLL)damage += 5;else if (you.species == SP_GHOUL)damage += 2;
if (damage_done < 1)damage_done = 0;elsehurt_monster(defender, damage_done);
int melee_attack::player_calc_base_weapon_damage(){int damage = 0;if (weapon->base_type == OBJ_WEAPONS|| item_is_staff( *weapon )){damage = property( *weapon, PWPN_DAMAGE );}
if (brand == SPWPN_VENOM && coinflip())poison_monster( defender, true );
static void tutorial_weapon_check(const item_def *weapon){if (weapon &&(weapon->base_type != OBJ_WEAPONS|| is_range_weapon(*weapon))){learned_something_new(TUT_WIELD_WEAPON);}}
if (mons_holiness(defender) == MH_HOLY)did_god_conduct(DID_KILL_ANGEL, 1);
// Returns true if you hit the monster.bool you_attack(int monster_attacked, bool unarmed_attacks){monsters *defender = &menv[monster_attacked];melee_attack attk(&you, defender, unarmed_attacks);
hit = true;}else // no damage was done{strcpy(info, "You ");strcat(info, attack_name);strcat(info, " ");strcat(info, ptr_monam(defender, DESC_NOCAP_THE));
// We're trying to hit a monster, break out of travel/explore now.interrupt_activity(AI_HIT_MONSTER, defender);
mpr(info);hit = true;}if (defender->hit_points < 1){monster_die(defender, KILL_YOU, 0);if (defender->type == MONS_GIANT_SPORE){strcpy(info, "You ");strcat(info, attack_name);strcat(info, "the giant spore.");mpr(info);}else if (defender->type == MONS_BALL_LIGHTNING){strcpy(info, "You ");strcat(info, attack_name);strcat(info, "the ball lightning.");mpr(info);}return (true);}}else{strcpy(info, "Your ");strcat(info, attack_name);strcat(info, " misses ");strcat(info, ptr_monam(defender, DESC_NOCAP_THE));strcat(info, ".");mpr(info);}}}if (hit)print_wounds(defender);
return attk.attack();}
// Added by DML 6/10/99.// For now, always returns damage: that is, it never modifies values,// just adds 'color'.static int weapon_type_modify( int weapnum, char noise[80], char noise2[80],int damage ){int weap_type = WPN_UNKNOWN;if (weapnum == -1)weap_type = WPN_UNARMED;else if (item_is_staff( you.inv[weapnum] ))weap_type = WPN_QUARTERSTAFF;else if (you.inv[weapnum].base_type == OBJ_WEAPONS)weap_type = you.inv[weapnum].sub_type;noise2[0] = 0;// All weak hits look the same, except for when the player// has a non-weapon in hand. -- bwrif (damage < HIT_WEAK){if (weap_type != WPN_UNKNOWN)strcpy( noise, "hit" );elsestrcpy( noise, "clumsily bash" );return (damage);}// take transformations into account, if no weapon is wieldedif (weap_type == WPN_UNARMED&& you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE){switch (you.attribute[ATTR_TRANSFORMATION]){case TRAN_SPIDER:if (damage < HIT_STRONG)strcpy( noise, "bite" );elsestrcpy( noise, "maul" );break;case TRAN_BLADE_HANDS:if (damage < HIT_MED)strcpy( noise, "slash" );else if (damage < HIT_STRONG)strcpy( noise, "slice" );elsestrcpy( noise, "shred" );break;case TRAN_ICE_BEAST:case TRAN_STATUE:case TRAN_LICH:if (damage < HIT_MED)strcpy( noise, "punch" );elsestrcpy( noise, "pummel" );break;case TRAN_DRAGON:case TRAN_SERPENT_OF_HELL:if (damage < HIT_MED)strcpy( noise, "claw" );else if (damage < HIT_STRONG)strcpy( noise, "bite" );elsestrcpy( noise, "maul" );break;case TRAN_AIR:strcpy( noise, "buffet" );break;} // transformationsreturn (damage);}switch (weap_type){case WPN_KNIFE:case WPN_DAGGER:case WPN_SHORT_SWORD:case WPN_TRIDENT:case WPN_DEMON_TRIDENT:case WPN_SPEAR:if (damage < HIT_MED)strcpy( noise, "puncture" );else if (damage < HIT_STRONG)strcpy( noise, "impale" );else{strcpy( noise, "spit" );strcpy( noise2, " like a pig" );}return (damage);case WPN_BOW:case WPN_LONGBOW:case WPN_CROSSBOW:case WPN_HAND_CROSSBOW:if (damage < HIT_STRONG)strcpy( noise, "puncture" );elsestrcpy( noise, "skewer" );return (damage);case WPN_LONG_SWORD:case WPN_GREAT_SWORD:case WPN_SCIMITAR:case WPN_FALCHION:case WPN_HALBERD:case WPN_GLAIVE:case WPN_HAND_AXE:case WPN_WAR_AXE:case WPN_BROAD_AXE:case WPN_BATTLEAXE:case WPN_SCYTHE:case WPN_QUICK_BLADE:case WPN_KATANA:case WPN_LAJATANG:case WPN_LOCHABER_AXE:case WPN_EXECUTIONERS_AXE:case WPN_DOUBLE_SWORD:case WPN_TRIPLE_SWORD:case WPN_SABRE:case WPN_DEMON_BLADE:case WPN_BLESSED_BLADE:if (damage < HIT_MED)strcpy( noise, "slash" );else if (damage < HIT_STRONG)strcpy( noise, "slice" );else{strcpy( noise, "open" );strcpy( noise2, " like a pillowcase" );}return (damage);
case WPN_SLING:case WPN_CLUB:case WPN_MACE:case WPN_FLAIL:case WPN_GREAT_MACE:case WPN_DIRE_FLAIL:case WPN_QUARTERSTAFF:case WPN_GIANT_CLUB:case WPN_HAMMER:case WPN_ANCUS:case WPN_MORNINGSTAR: /*for now, just a bludgeoning weapon */case WPN_SPIKED_FLAIL: /*for now, just a bludgeoning weapon */case WPN_EVENINGSTAR:case WPN_GIANT_SPIKED_CLUB:if (damage < HIT_MED)strcpy( noise, "sock" );else if (damage < HIT_STRONG)strcpy( noise, "bludgeon" );else{strcpy( noise, "crush" );strcpy( noise2, " like a grape" );}return (damage);case WPN_WHIP:case WPN_DEMON_WHIP:if (damage < HIT_MED)strcpy( noise, "whack" );elsestrcpy( noise, "thrash" );return (damage);case WPN_UNARMED:if (you.species == SP_TROLL || you.mutation[MUT_CLAWS]){if (damage < HIT_MED)strcpy( noise, "claw" );else if (damage < HIT_STRONG)strcpy( noise, "mangle" );elsestrcpy( noise, "eviscerate" );}else{if (damage < HIT_MED)strcpy( noise, "punch" );elsestrcpy( noise, "pummel" );}return (damage);case WPN_UNKNOWN:default:strcpy( noise, "hit" );return (damage);}} // end weapon_type_modify()
case 3: // big melee, monster surrounded/not paying attentionif (r<3){snprintf( info, INFO_SIZE, "You strike %s from a blind spot!",ptr_monam(defender, DESC_NOCAP_THE) );}else{snprintf( info, INFO_SIZE, "You catch %s momentarily off-guard.",ptr_monam(defender, DESC_NOCAP_THE) );}break;case 2: // confused/fleeingif (r<4){snprintf( info, INFO_SIZE, "You catch %s completely off-guard!",ptr_monam(defender, DESC_NOCAP_THE) );}else{snprintf( info, INFO_SIZE, "You strike %s from behind!",ptr_monam(defender, DESC_NOCAP_THE) );}break;case 1:snprintf( info, INFO_SIZE, "%s fails to defend %s.",ptr_monam(defender, DESC_CAP_THE),mons_pronoun( defender->type, PRONOUN_REFLEXIVE ) );break;
case 3: // big melee, monster surrounded/not paying attentionif (r<3){mprf( "You strike %s from a blind spot!",ptr_monam(defender, DESC_NOCAP_THE) );}else{mprf( "You catch %s momentarily off-guard.",ptr_monam(defender, DESC_NOCAP_THE) );}break;case 2: // confused/fleeingif (r<4){mprf( "You catch %s completely off-guard!",ptr_monam(defender, DESC_NOCAP_THE) );}else{mprf( "You strike %s from behind!",ptr_monam(defender, DESC_NOCAP_THE) );}break;case 1:mprf( "%s fails to defend %s.",ptr_monam(defender, DESC_CAP_THE),mons_pronoun( defender->type, PRONOUN_REFLEXIVE ) );break;
{}};class item_def;class melee_attack;class coord_def;class actor{public:virtual ~actor() { }virtual int id() const = 0;virtual actor_type atype() const = 0;virtual coord_def pos() const = 0;virtual bool swimming() const = 0;virtual bool floundering() const = 0;virtual size_type body_size(int psize = PSIZE_TORSO,bool base = false) const = 0;virtual int damage_type(int which_attack = -1) = 0;virtual int damage_brand(int which_attack = -1) = 0;virtual item_def *weapon(int which_attack = -1) = 0;virtual item_def *shield() = 0;virtual void make_hungry(int nutrition, bool silent)
virtual std::string name(description_level_type type) const = 0;virtual std::string conj_verb(const std::string &verb) const = 0;virtual bool fumbles_attack(bool verbose = true) = 0;// Returns true if the actor has no way to attack (plants, statues).// (statues have only indirect attacks).virtual bool cannot_fight() const = 0;virtual void attacking(actor *other) = 0;virtual void go_berserk(bool intentional) = 0;
// For sortingbool operator < (const player &p) const;
bool has_spell(int spell) const;size_type transform_size(int psize = PSIZE_TORSO) const;item_def *slot_item(equipment_type eq);// actorint id() const;coord_def pos() const;bool swimming() const;bool floundering() const;size_type body_size(int psize = PSIZE_TORSO, bool base = false) const;int damage_type(int attk = -1);int damage_brand(int attk = -1);item_def *weapon(int which_attack = -1);item_def *shield();std::string name(description_level_type type) const;std::string conj_verb(const std::string &verb) const;bool fumbles_attack(bool verbose = true);bool cannot_fight() const;void attacking(actor *other);void go_berserk(bool intentional);void make_hungry(int nutrition, bool silent);actor_type atype() const { return ACT_PLAYER; }
coord_def pos() const{return coord_def(x, y);}
public:// actor interfaceint id() const;coord_def pos() const;bool swimming() const;bool floundering() const;size_type body_size(int psize = PSIZE_TORSO, bool base = false) const;int damage_type(int attk = -1);int damage_brand(int attk = -1);item_def *weapon(int which_attack = -1);item_def *shield();std::string name(description_level_type type) const;std::string conj_verb(const std::string &verb) const;bool fumbles_attack(bool verbose = true);bool cannot_fight() const;void attacking(actor *other);void go_berserk(bool intentional);actor_type atype() const { return ACT_MONSTER; }