From ba3d08acb430062418484ed3e778474cd3b9482b Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Wed, 3 Dec 2008 16:16:06 +0000 Subject: Implement harpyes. They ... * appear in bands of 2-5 * use bat like movement * may steal (= destroy) the player's food. Still needs a tile. Also, actually use the M_BATTY flag instead of hardcoding batty monsters. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7734 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/directn.cc | 2 +- crawl-ref/source/enum.h | 36 ++++++++++---------- crawl-ref/source/fight.cc | 26 ++++++++++++--- crawl-ref/source/mon-data.h | 35 ++++++++++++++++---- crawl-ref/source/mon-pick.cc | 4 +++ crawl-ref/source/mon-util.cc | 8 ++--- crawl-ref/source/mon-util.h | 11 +++--- crawl-ref/source/monplace.cc | 15 +++++---- crawl-ref/source/monplace.h | 3 +- crawl-ref/source/ouch.cc | 79 ++++++++++++++++++++++++-------------------- crawl-ref/source/ouch.h | 2 +- 11 files changed, 137 insertions(+), 84 deletions(-) (limited to 'crawl-ref/source') diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index ba3f2df750..16725bf3ed 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -2797,7 +2797,7 @@ static void _describe_monster(const monsters *mon) { // special case: batty monsters get set to BEH_WANDER as // part of their special behaviour. - if (!testbits(mon->flags, MF_BATTY)) + if (!mons_is_batty(mon)) { mprf(MSGCH_EXAMINE, "%s doesn't appear to have noticed you.", mon->pronoun(PRONOUN_CAP).c_str()); diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 9ab78972e6..b15edfa7da 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -248,6 +248,7 @@ enum beam_type // beam[].flavour BEAM_POTION_RANDOM, BEAM_TORMENT_DAMAGE, // Pseudo-beam for damage flavour. + BEAM_STEAL_FOOD, // Pseudo-beam for harpyes stealing food. BEAM_LINE_OF_SIGHT // only used for checking monster LOS }; @@ -1739,6 +1740,7 @@ enum monster_type // (int) menv[].type MONS_MERMAID, MONS_SIREN, // 195 MONS_FLAMING_CORPSE, + MONS_HARPY, // 197 //jmf: end new monsters MONS_WHITE_IMP = 220, // 220 MONS_LEMURE, @@ -2003,31 +2005,31 @@ enum mon_flight_type enum monster_flag_type { MF_CREATED_FRIENDLY = 0x01, // no benefit from killing - MF_BATTY = 0x02, // flutters like a bat - MF_JUST_SUMMONED = 0x04, // monster skips next available action - MF_TAKING_STAIRS = 0x08, // is following player through stairs + MF_JUST_SUMMONED = 0x02, // monster skips next available action + MF_TAKING_STAIRS = 0x04, // is following player through stairs + MF_INTERESTING = 0x08, // Player finds monster interesting - MF_INTERESTING = 0x10, // Player finds monster interesting - MF_SEEN = 0x20, // Player has already seen monster - MF_DIVINE_PROTECTION = 0x40, // Monster has divine protection. - - MF_KNOWN_MIMIC = 0x80, // Mimic that has taken a swing at the PC, + MF_SEEN = 0x10, // Player has already seen monster + MF_DIVINE_PROTECTION = 0x20, // Monster has divine protection. + MF_KNOWN_MIMIC = 0x40, // Mimic that has taken a swing at the PC, // or that the player has inspected with ? - MF_BANISHED = 0x100, // Monster that has been banished. - MF_HARD_RESET = 0x200, // Summoned, should not drop gear on reset - MF_WAS_NEUTRAL = 0x400, // mirror to CREATED_FRIENDLY for neutrals - MF_ATT_CHANGE_ATTEMPT = 0x800, // Saw player and attitude changed (or + MF_BANISHED = 0x80, // Monster that has been banished. + + MF_HARD_RESET = 0x100, // Summoned, should not drop gear on reset + MF_WAS_NEUTRAL = 0x200, // mirror to CREATED_FRIENDLY for neutrals + MF_ATT_CHANGE_ATTEMPT = 0x400, // Saw player and attitude changed (or // not); currently used for holy beings // (good god worshippers -> neutral) // and orcs (Beogh worshippers -> friendly) - MF_WAS_IN_VIEW = 0x1000, // Was in view during previous turn - MF_BAND_MEMBER = 0x2000, // Created as a member of a band - MF_GOT_HALF_XP = 0x4000, // Player already got half xp value earlier - MF_HONORARY_UNDEAD = 0x8000, // Consider this monster to have MH_UNDEAD + MF_WAS_IN_VIEW = 0x800, // Was in view during previous turn. + + MF_BAND_MEMBER = 0x1000, // Created as a member of a band + MF_GOT_HALF_XP = 0x2000, // Player already got half xp value earlier + MF_HONORARY_UNDEAD = 0x4000, // Consider this monster to have MH_UNDEAD // holiness, regardless of its actual type; // currently used for abominations created // via Twisted Resurrection - MF_ENSLAVED_SOUL = 0x10000 // An undead monster soul enslaved by + MF_ENSLAVED_SOUL = 0x8000 // An undead monster soul enslaved by // Yredelemnul's power, or the natural // monster from whom the soul is taken }; diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index a47588edb9..c3a7af0c45 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -2179,7 +2179,7 @@ static bool _can_clone(const actor *defender, coord_def *pos, int *midx) return (false); } - return (true); + return (true); } static bool _do_clone(monsters* orig, coord_def pos, int midx) @@ -2321,7 +2321,7 @@ void melee_attack::chaos_affects_defender() ASSERT(can_poly); beam.flavour = BEAM_POLYMORPH; break; - + case CHAOS_POLY_UP: ASSERT(can_poly); ASSERT(defender->atype() == ACT_MONSTER); @@ -2329,7 +2329,7 @@ void melee_attack::chaos_affects_defender() obvious_effect = you.can_see(defender); monster_polymorph(def, RANDOM_MONSTER, PPT_MORE, true); break; - + case CHAOS_MAKE_SHIFTER: ASSERT(can_poly); ASSERT(!is_shifter); @@ -2341,11 +2341,11 @@ void melee_attack::chaos_affects_defender() // Immediately polymorph monster, just to make the effect obvious. monster_polymorph(def, RANDOM_MONSTER, PPT_SAME, true); break; - + case CHAOS_HEAL: beam.flavour = BEAM_HEALING; break; - + case CHAOS_HASTE: beam.flavour = BEAM_HASTE; break; @@ -4194,6 +4194,22 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk) case AF_CHAOS: chaos_affects_defender(); break; + + case AF_STEAL_FOOD: + // Monsters don't carry food. + if (defender->atype() != ACT_PLAYER) + break; + + // Only use this attack sometimes. + if (!one_chance_in(3)) + break; + + if (expose_player_to_element(BEAM_STEAL_FOOD, 10) && needs_message) + { + mprf("%s steals some of your food!", + atk_name(DESC_CAP_THE).c_str()); + } + break; } } diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index 35a30cade6..2964d3971e 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -169,20 +169,29 @@ static inline mon_energy_usage MISSILE_ENERGY(int ae) static monsterentry mondata[] = { // monster 250: The Thing That Should Not Be(tm) -// do not remove, or seekmonster will crash on unknown mc request -// it is also a good prototype for new monsters +// NOTE: Do not remove, or seekmonster will crash on unknown mc request! +// It is also a good prototype for new monsters. { + // id, glyph, colour, name MONS_PROGRAM_BUG, 'B', LIGHTRED, "program bug", + // monster flags M_NO_EXP_GAIN, + // resistance flags MR_NO_FLAGS, + // mass, xp modifier, genus, species, holiness, magic resistance 0, 10, MONS_PROGRAM_BUG, MONS_PROGRAM_BUG, MH_NATURAL, -3, + // up to four attacks { AT_NO_ATK, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + // hit points { 0, 0, 0, 0 }, + // AC, EV, spells, corpse type, zombie size, shout type, intelligence 0, 0, MST_NO_SPELLS, CE_CONTAMINATED, Z_NOZOMBIE, S_SILENT, I_PLANT, + // habitat, speed, energy usage, use type, body size HT_LAND, 0, DEFAULT_ENERGY, MONUSE_NOTHING, SIZE_HUGE }, -// real monsters begin here {dlb}: +// Real monsters begin here {dlb}: + // insects ('a') { MONS_GIANT_COCKROACH, 'a', BROWN, "giant cockroach", @@ -220,7 +229,7 @@ static monsterentry mondata[] = { // batty monsters ('b') { MONS_GIANT_BAT, 'b', LIGHTGREY, "giant bat", - M_FLIES | M_SENSE_INVIS | M_WARM_BLOOD, + M_FLIES | M_SENSE_INVIS | M_WARM_BLOOD | M_BATTY, MR_NO_FLAGS, 150, 4, MONS_GIANT_BAT, MONS_GIANT_BAT, MH_NATURAL, -1, { {AT_HIT, AF_PLAIN, 1}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, @@ -985,7 +994,7 @@ static monsterentry mondata[] = { }, { - MONS_MERMAID, 'm', LIGHTCYAN, "cyan", + MONS_MERMAID, 'm', CYAN, "mermaid", M_SPELLCASTER | M_WARM_BLOOD | M_SPEAKS, MR_RES_POISON | MR_RES_COLD, 500, 10, MONS_MERMAID, MONS_MERMAID, MH_NATURAL, -5, @@ -1488,7 +1497,7 @@ static monsterentry mondata[] = { // small abominations ('x') { MONS_UNSEEN_HORROR, 'x', MAGENTA, "unseen horror", - M_SEE_INVIS | M_INVIS, + M_SEE_INVIS | M_INVIS | M_BATTY, MR_NO_FLAGS, 0, 12, MONS_UNSEEN_HORROR, MONS_UNSEEN_HORROR, MH_NATURAL, -3, { {AT_HIT, AF_PLAIN, 12}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, @@ -1533,7 +1542,7 @@ static monsterentry mondata[] = { { MONS_GIANT_BLOWFLY, 'y', LIGHTGREY, "giant blowfly", - M_FLIES, + M_FLIES | M_BATTY, MR_VUL_POISON, 200, 10, MONS_GIANT_BLOWFLY, MONS_GIANT_BLOWFLY, MH_NATURAL, -3, { {AT_BITE, AF_PLAIN, 13}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, @@ -2146,6 +2155,18 @@ static monsterentry mondata[] = { HT_LAND, 13, DEFAULT_ENERGY, MONUSE_OPEN_DOORS, SIZE_BIG }, +{ + MONS_HARPY, 'H', GREEN, "harpy", + M_FLIES | M_WARM_BLOOD | M_BATTY, + MR_NO_FLAGS, + 1000, 10, MONS_HARPY, MONS_HARPY, MH_NATURAL, -3, + { {AT_BITE, AF_PLAIN, 10}, {AT_CLAW, AF_PLAIN, 8}, + {AT_CLAW, AF_STEAL_FOOD, 5}, AT_NO_ATK }, + { 7, 3, 5, 0 }, + 2, 10, MST_NO_SPELLS, CE_CONTAMINATED, Z_BIG, S_SCREECH, I_ANIMAL, + HT_LAND, 30, DEFAULT_ENERGY, MONUSE_NOTHING, SIZE_MEDIUM +}, + // ice beast ('I') { MONS_ICE_BEAST, 'I', WHITE, "ice beast", diff --git a/crawl-ref/source/mon-pick.cc b/crawl-ref/source/mon-pick.cc index 3ba5edfdbe..940bd6f1f9 100644 --- a/crawl-ref/source/mon-pick.cc +++ b/crawl-ref/source/mon-pick.cc @@ -738,6 +738,7 @@ int mons_standard_level(int mcls) case MONS_SKELETON_SMALL: case MONS_SPINY_WORM: case MONS_VERY_UGLY_THING: + case MONS_HARPY: return 20; case MONS_BOULDER_BEETLE: @@ -1015,6 +1016,7 @@ int mons_standard_rare(int mcls) case MONS_STORM_DRAGON: case MONS_VERY_UGLY_THING: case MONS_WIZARD: + case MONS_HARPY: return 20; case MONS_BORING_BEETLE: @@ -1713,6 +1715,7 @@ int mons_shoals_level(int mcls) case MONS_CENTAUR_WARRIOR: case MONS_CYCLOPS: // will have a sheep band + case MONS_HARPY: mlev += 3; break; @@ -1762,6 +1765,7 @@ int mons_shoals_rare(int mcls) case MONS_STONE_GIANT: case MONS_YAKTAUR_CAPTAIN: + case MONS_HARPY: return 10; case MONS_OKLOB_PLANT: diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 0cb418ffc1..3e6cf8ceec 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -1024,7 +1024,7 @@ mon_attack_def mons_attack_spec(const monsters *mon, int attk_number) if (attk.type == AT_RANDOM) attk.type = static_cast(random_range(AT_HIT, - AT_BUTT)); + AT_BUTT)); if (attk.flavour == AF_KLOWN) { @@ -2470,7 +2470,7 @@ bool mons_is_lurking(const monsters *m) bool mons_is_batty(const monsters *m) { - return testbits(m->flags, MF_BATTY); + return mons_class_flag(m->type, M_BATTY); } bool mons_was_seen(const monsters *m) @@ -4803,7 +4803,7 @@ std::string monsters::hand_name(bool plural, bool *can_plural) const // Reduce the chance of a random-shaped monster having hands. if (rand && coinflip()) return (hand_name(plural, can_plural)); - + str = "hand"; } @@ -4918,7 +4918,7 @@ std::string monsters::foot_name(bool plural, bool *can_plural) const // Reduce the chance of a random-shaped monster having feet. if (rand && coinflip()) return (foot_name(plural, can_plural)); - + return (plural ? "feet" : "foot"); } diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h index add15efa74..b7536dee3f 100644 --- a/crawl-ref/source/mon-util.h +++ b/crawl-ref/source/mon-util.h @@ -81,7 +81,8 @@ enum mon_attack_flavour AF_DISTORT, AF_RAGE, AF_NAPALM, - AF_CHAOS + AF_CHAOS, + AF_STEAL_FOOD }; // properties of the monster class (other than resists/vulnerabilities) @@ -201,7 +202,7 @@ enum mon_resist_flags MR_VUL_FIRE = (1<< 9), MR_VUL_COLD = (1<<10), - // melee armour resists/vulnerabilities + // Melee armour resists/vulnerabilities. // XXX: how to do combos (bludgeon/slice, bludgeon/pierce) MR_RES_PIERCE = (1<<11), MR_RES_SLICE = (1<<12), @@ -211,9 +212,9 @@ enum mon_resist_flags MR_VUL_SLICE = (1<<15), MR_VUL_BLUDGEON = (1<<16), - // immune to stickiness of sticky flame. - MR_RES_STICKY_FLAME = (1 << 17), - MR_RES_STEAM = (1 << 18) + // Immune to stickiness of sticky flame. + MR_RES_STICKY_FLAME = (1<<17), + MR_RES_STEAM = (1<<18) }; enum shout_type diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index 754b4cb5e1..487bbd6704 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -892,12 +892,6 @@ static int _place_monster_aux( const mgen_data &mg, if (mg.cls == MONS_GLOWING_SHAPESHIFTER) menv[id].add_ench(ENCH_GLOWING_SHAPESHIFTER); - if (mg.cls == MONS_GIANT_BAT || mg.cls == MONS_UNSEEN_HORROR - || mg.cls == MONS_GIANT_BLOWFLY) - { - menv[id].flags |= MF_BATTY; - } - if (monster_can_submerge(&menv[id], grd(fpos)) && !one_chance_in(5)) menv[id].add_ench(ENCH_SUBMERGED); @@ -957,7 +951,7 @@ static int _place_monster_aux( const mgen_data &mg, menv[id].foe = mg.foe; - // Initialise pandemonium demons + // Initialise pandemonium demons. if (menv[id].type == MONS_PANDEMONIUM_DEMON) { ghost_demon ghost; @@ -1464,6 +1458,10 @@ static band_type _choose_band( int mon_type, int power, int &band_size ) band = BAND_DEATH_YAKS; band_size = 3 + random2(3); break; + case MONS_HARPY: + band = BAND_HARPYES; + band_size = 2 + random2(3); + break; // Journey -- Added Draconian Packs case MONS_WHITE_DRACONIAN: @@ -1570,6 +1568,9 @@ static monster_type _band_member(band_type band, int power) mon_type = MONS_YAK; break; + case BAND_HARPYES: + mon_type = MONS_HARPY; + case BAND_UGLY_THINGS: mon_type = ((power > 21 && one_chance_in(4)) ? MONS_VERY_UGLY_THING : MONS_UGLY_THING); diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h index bbb1c80ce4..1cc3422cd2 100644 --- a/crawl-ref/source/monplace.h +++ b/crawl-ref/source/monplace.h @@ -62,7 +62,8 @@ enum band_type BAND_BLINK_FROGS, BAND_SKELETAL_WARRIORS, BAND_DRACONIAN, // 45 - BAND_PANDEMONIUM_DEMON, // 46 + BAND_PANDEMONIUM_DEMON, + BAND_HARPYES, // 47 NUM_BANDS // always last }; diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc index 6cdfe1c2fb..436d24a82f 100644 --- a/crawl-ref/source/ouch.cc +++ b/crawl-ref/source/ouch.cc @@ -428,6 +428,7 @@ static int _get_target_class(beam_type flavour) break; case BEAM_SPORE: + case BEAM_STEAL_FOOD: target_class = OBJ_FOOD; break; @@ -441,13 +442,13 @@ static int _get_target_class(beam_type flavour) // XXX: These expose functions could use being reworked into a real system... // the usage and implementation is currently very hacky. // Handles the destruction of inventory items from the elements. -static void _expose_invent_to_element(beam_type flavour, int strength) +static bool _expose_invent_to_element(beam_type flavour, int strength) { int num_dest = 0; const int target_class = _get_target_class( flavour ); if (target_class == OBJ_UNASSIGNED) - return; + return (false); // Currently we test against each stack (and item in the stack) // independently at strength%... perhaps we don't want that either @@ -458,13 +459,15 @@ static void _expose_invent_to_element(beam_type flavour, int strength) if (!is_valid_item(you.inv[i])) continue; - if (is_valid_item(you.inv[i]) - && (you.inv[i].base_type == target_class - || target_class == OBJ_FOOD - && you.inv[i].base_type == OBJ_CORPSES)) + if (you.inv[i].base_type == target_class + || target_class == OBJ_FOOD + && you.inv[i].base_type == OBJ_CORPSES) { - if (player_item_conserve() && !one_chance_in(10)) + if (flavour != BEAM_STEAL_FOOD + && player_item_conserve() && !one_chance_in(10)) + { continue; + } for (int j = 0; j < you.inv[i].quantity; ++j) { @@ -484,36 +487,38 @@ static void _expose_invent_to_element(beam_type flavour, int strength) } } - if (num_dest > 0) + if (!num_dest) + return (false); + + switch (target_class) { - switch (target_class) - { - case OBJ_SCROLLS: - mprf("%s you are carrying %s fire!", - (num_dest > 1) ? "Some of the scrolls" : "A scroll", - (num_dest > 1) ? "catch" : "catches" ); - break; - - case OBJ_POTIONS: - mprf("%s you are carrying %s and %s!", - (num_dest > 1) ? "Some of the potions" : "A potion", - (num_dest > 1) ? "freeze" : "freezes", - (num_dest > 1) ? "shatter" : "shatters" ); - break; - - case OBJ_FOOD: - mpr("Some of your food is covered with spores!"); - break; + case OBJ_SCROLLS: + mprf("%s you are carrying %s fire!", + (num_dest > 1) ? "Some of the scrolls" : "A scroll", + (num_dest > 1) ? "catch" : "catches" ); + break; - default: - mprf("%s you are carrying %s destroyed!", - (num_dest > 1) ? "Some items" : "An item", - (num_dest > 1) ? "were" : "was" ); - break; - } + case OBJ_POTIONS: + mprf("%s you are carrying %s and %s!", + (num_dest > 1) ? "Some of the potions" : "A potion", + (num_dest > 1) ? "freeze" : "freezes", + (num_dest > 1) ? "shatter" : "shatters" ); + break; - xom_is_stimulated((num_dest > 1) ? 32 : 16); + case OBJ_FOOD: + if (flavour == BEAM_SPORE) + mpr("Some of your food is covered with spores!"); + break; + + default: + mprf("%s you are carrying %s destroyed!", + (num_dest > 1) ? "Some items" : "An item", + (num_dest > 1) ? "were" : "was" ); + break; } + + xom_is_stimulated((num_dest > 1) ? 32 : 16); + return (true); } void expose_items_to_element(beam_type flavour, const coord_def& where, @@ -588,7 +593,7 @@ void expose_items_to_element(beam_type flavour, const coord_def& where, // This function now calls _expose_invent_to_element() if strength > 0. // // XXX: This function is far from perfect and a work in progress. -void expose_player_to_element(beam_type flavour, int strength) +bool expose_player_to_element(beam_type flavour, int strength) { // Note that BEAM_TELEPORT is sent here when the player // blinks or teleports. @@ -601,8 +606,10 @@ void expose_player_to_element(beam_type flavour, int strength) remove_condensation_shield(); } - if (strength > 0) - _expose_invent_to_element( flavour, strength ); + if (strength <= 0) + return (false); + + return (_expose_invent_to_element( flavour, strength )); } void lose_level() diff --git a/crawl-ref/source/ouch.h b/crawl-ref/source/ouch.h index a61101902d..3bcb532387 100644 --- a/crawl-ref/source/ouch.h +++ b/crawl-ref/source/ouch.h @@ -75,6 +75,6 @@ void drain_exp(bool announce_full = true); void expose_items_to_element(beam_type flavour, const coord_def& where, int strength = 0); -void expose_player_to_element(beam_type flavour, int strength = 0); +bool expose_player_to_element(beam_type flavour, int strength = 0); #endif -- cgit v1.2.3-54-g00ecf