diff options
-rw-r--r-- | crawl-ref/source/cloud.cc | 6 | ||||
-rw-r--r-- | crawl-ref/source/cloud.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/fight.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/mon-data.h | 8 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 60 | ||||
-rw-r--r-- | crawl-ref/source/monplace.cc | 82 | ||||
-rw-r--r-- | crawl-ref/source/monplace.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 56 | ||||
-rw-r--r-- | crawl-ref/source/travel.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/travel.h | 2 |
11 files changed, 202 insertions, 25 deletions
diff --git a/crawl-ref/source/cloud.cc b/crawl-ref/source/cloud.cc index 351f1a3773..3aa74613df 100644 --- a/crawl-ref/source/cloud.cc +++ b/crawl-ref/source/cloud.cc @@ -297,6 +297,12 @@ bool is_opaque_cloud(unsigned char cloud_idx) (ctype >= CLOUD_GREY_SMOKE && ctype <= CLOUD_STEAM) ); } +cloud_type cloud_type_at(const coord_def &c) +{ + const int cloudno = env.cgrid(c); + return (cloudno == EMPTY_CLOUD? CLOUD_NONE : env.cloud[cloudno].type); +} + cloud_type random_smoke_type() { // excludes black (reproducing existing behaviour) diff --git a/crawl-ref/source/cloud.h b/crawl-ref/source/cloud.h index 8ca697cf9f..3df9df85ca 100644 --- a/crawl-ref/source/cloud.h +++ b/crawl-ref/source/cloud.h @@ -35,6 +35,8 @@ struct fog_machine_data cloud_type random_smoke_type(); +cloud_type cloud_type_at(const coord_def &pos); + void delete_cloud( int cloud ); void move_cloud( int cloud, int new_x, int new_y ); diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 4f2d73a671..0fdb94ad99 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -1066,6 +1066,8 @@ public: void scale_hp(int num, int den); bool gain_exp(int exp); + void react_to_damage(int damage); + void add_enchantment_effect(const mon_enchant &me, bool quiet = false); void remove_enchantment_effect(const mon_enchant &me, bool quiet = false); void apply_enchantments(); diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 62a4c9f76b..f6d5ae9471 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -728,8 +728,6 @@ bool melee_attack::player_attack() bleed_onto_floor(where.x, where.y, defender->id(), blood, true); } - player_hurt_monster(); - if (damage_done > 0 || !defender_visible) player_announce_hit(); else if (!shield_blocked && damage_done <= 0) @@ -738,6 +736,8 @@ bool melee_attack::player_attack() make_stringf("You %s %s.", attack_verb.c_str(), defender->name(DESC_NOCAP_THE).c_str()); } + + player_hurt_monster(); if (damage_done) player_exercise_combat_skills(); diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index 8657178a59..9bd80cc10d 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -2166,13 +2166,13 @@ { MONS_ROYAL_JELLY, 'J', YELLOW, "royal jelly", - M_NO_SKELETON | M_SENSE_INVIS | M_ACID_SPLASH, + M_NO_SKELETON | M_SENSE_INVIS | M_SPECIAL_ABILITY | M_ACID_SPLASH, MR_RES_POISON | MR_RES_ASPHYX | MR_RES_ACID, 0, 20, MONS_JELLY, MONS_ROYAL_JELLY, MH_NATURAL, -7, - { {AT_HIT, AF_ACID, 50}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, - { 21, 0, 0, 111 }, + { {AT_HIT, AF_ACID, 50}, {AT_HIT, AF_ACID, 30}, AT_NO_ATK, AT_NO_ATK }, + { 21, 0, 0, 230 }, 8, 4, MST_NO_SPELLS, CE_CLEAN, Z_NOZOMBIE, S_SILENT, I_PLANT, - HT_LAND, 12, DEFAULT_ENERGY, MONUSE_EATS_ITEMS, SIZE_SMALL + HT_LAND, 16, DEFAULT_ENERGY, MONUSE_EATS_ITEMS, SIZE_SMALL }, // kobolds ('K') diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index bc381f03dc..24225112de 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -5201,6 +5201,66 @@ void monsters::lose_energy(energy_use_type et) speed_increment -= action_energy(et); } +static inline monster_type _royal_jelly_ejectable_monster() +{ + return static_cast<monster_type>( + random_choose( MONS_ACID_BLOB, + MONS_AZURE_JELLY, + MONS_DEATH_OOZE, + -1 ) ); +} + +void monsters::react_to_damage(int damage) +{ + // The royal jelly objects to taking damage and will SULK. :-) + if (type == MONS_ROYAL_JELLY && alive() + && damage > 8 && random2(50) < damage) + { + const int tospawn = + 1 + random2avg(1 + std::min((damage - 8) / 8, 5), 2); + mprf(MSGCH_DIAGNOSTICS, "Trying to spawn %d jellies.", tospawn); + const beh_type sbehaviour = SAME_ATTITUDE(this); + int spawned = 0; + for (int i = 0; i < tospawn; ++i) + { + const monster_type jelly = _royal_jelly_ejectable_monster(); + coord_def jpos = find_newmons_square_contiguous(jelly, pos()); + if (!in_bounds(jpos)) + continue; + + const int nmons = + mons_place( jelly, sbehaviour, foe, true, jpos.x, jpos.y, + you.level_type, PROX_ANYWHERE, MONS_PROGRAM_BUG, + 0, false ); + + if (nmons != -1 && nmons != NON_MONSTER) + { + // Don't allow milking the royal jelly. + menv[nmons].flags |= MF_CREATED_FRIENDLY; + spawned++; + } + } + + const bool needs_message = + spawned && mons_near(this) && player_monster_visible(this); + + if (needs_message) + { + const std::string mname = name(DESC_CAP_THE); + mprf("%s shudders%s.", mname.c_str(), + spawned >= 5 ? " alarmingly" : + spawned >= 3 ? " violently" : + spawned > 1 ? " vigorously" : ""); + if (spawned == 1) + mprf("%s spits out another jelly.", mname.c_str()); + else + mprf("%s spits out %s more jellies.", + mname.c_str(), + number_in_words(spawned).c_str()); + } + } +} + ///////////////////////////////////////////////////////////////////////// // mon_enchant diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index efea2a6e94..e0aa3b0d3d 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -1579,6 +1579,84 @@ int mons_place( int mon_type, beh_type behaviour, int target, bool summoned, return (mid); } // end mons_place() +static dungeon_feature_type _monster_habitat_feature(int mtype) +{ + return ((mtype == RANDOM_MONSTER) ? DNGN_FLOOR + : habitat2grid( mons_habitat_by_type(mtype) )); +} + +class newmons_square_find : public travel_pathfind +{ +private: + dungeon_feature_type grid_wanted; + coord_def start; + int maxdistance; + + int best_distance; + int nfound; +public: + // Terrain that we can't spawn on, but that we can skip through. + std::set<dungeon_feature_type> passable; +public: + newmons_square_find(dungeon_feature_type grdw, + const coord_def &pos, + int maxdist = 0) + : grid_wanted(grdw), start(pos), maxdistance(maxdist), + best_distance(0), nfound(0) + { + } + + coord_def pathfind() + { + set_floodseed(start); + return travel_pathfind::pathfind(RMODE_EXPLORE); + } + + bool path_flood(const coord_def &c, const coord_def &dc) + { + if (best_distance && traveled_distance > best_distance) + return (true); + + if (!in_bounds(dc) + || (maxdistance > 0 && traveled_distance > maxdistance)) + { + return (false); + } + if (!grid_compatible(grid_wanted, grd(dc), true)) + { + if (passable.find(grd(dc)) != passable.end()) + good_square(dc); + return (false); + } + if (mgrd(dc) == NON_MONSTER && dc != you.pos() + && one_chance_in(++nfound)) + { + greedy_dist = traveled_distance; + greedy_place = dc; + best_distance = traveled_distance; + } + else + { + good_square(dc); + } + return (false); + } +}; + +/* + * Finds a square for a monster of the given class, pathfinding + * through only contiguous squares of habitable terrain. + */ +coord_def find_newmons_square_contiguous(monster_type mons_class, + const coord_def &start, + int distance) +{ + newmons_square_find nmfind(_monster_habitat_feature(mons_class), + start, distance); + const coord_def p = nmfind.pathfind(); + return (in_bounds(p)? p : coord_def(-1, -1)); +} + coord_def find_newmons_square(int mons_class, int x, int y) { FixedVector < char, 2 > empty; @@ -1590,9 +1668,7 @@ coord_def find_newmons_square(int mons_class, int x, int y) if (mons_class == WANDERING_MONSTER) mons_class = RANDOM_MONSTER; - dungeon_feature_type spcw = - ((mons_class == RANDOM_MONSTER) ? DNGN_FLOOR - : habitat2grid( mons_habitat_by_type(mons_class) )); + const dungeon_feature_type spcw = _monster_habitat_feature(mons_class); // Might be better if we chose a space and tried to match the monster // to it in the case of RANDOM_MONSTER, that way if the target square diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h index 953f303151..b7139abde6 100644 --- a/crawl-ref/source/monplace.h +++ b/crawl-ref/source/monplace.h @@ -185,6 +185,9 @@ bool monster_habitable_grid(int monster_class, bool paralysed = false); bool monster_can_submerge(const monsters *mons, dungeon_feature_type grid); coord_def find_newmons_square(int mons_class, int x, int y); +coord_def find_newmons_square_contiguous(monster_type mons_class, + const coord_def &start, + int maxdistance = 3); void spawn_random_monsters(); diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index 63e285dbc0..107951b5b2 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -4443,6 +4443,35 @@ static void _monster_add_energy(monsters *monster) monster->speed_increment += energy_gained; } +static int _monster_natural_regen_roll(monsters *monster) +{ + // A HD divider ranging from 3 (at 1 HD) to 1 (at 8 HD). + int divider = + std::max(div_rand_round(15 - monster->hit_dice, 4), 1); + + // The undead have a harder time regenerating. + switch (monster->holiness()) + { + // The undead don't regenerate easily. + case MH_UNDEAD: + divider *= 4; + break; + + // And golems have it worse. + case MH_NONLIVING: + divider *= 5; + break; + + default: + break; + } + + const int regen_rate = + std::max(div_rand_round(monster->hit_dice, divider), 1); + + return (random2(25) < regen_rate); +} + // Do natural regeneration for monster. static void _monster_regenerate(monsters *monster) { @@ -4455,25 +4484,20 @@ static void _monster_regenerate(monsters *monster) { return; } - - // regenerate: + if (monster_descriptor(monster->type, MDSC_REGENERATES) - || (monster->type == MONS_FIRE_ELEMENTAL - && (grd[monster->x][monster->y] == DNGN_LAVA - || (env.cgrid(monster->pos()) != EMPTY_CLOUD - && env.cloud[env.cgrid(monster->pos())].type - == CLOUD_FIRE))) - + && (grd(monster->pos()) == DNGN_LAVA + || cloud_type_at(monster->pos()) == CLOUD_FIRE)) + || (monster->type == MONS_WATER_ELEMENTAL - && (grd[monster->x][monster->y] == DNGN_SHALLOW_WATER - || grd[monster->x][monster->y] == DNGN_DEEP_WATER)) - + && grid_is_watery(grd(monster->pos()))) + || (monster->type == MONS_AIR_ELEMENTAL - && env.cgrid[monster->x][monster->y] == EMPTY_CLOUD + && env.cgrid(monster->pos()) == EMPTY_CLOUD && one_chance_in(3)) - - || one_chance_in(25)) + + || _monster_natural_regen_roll(monster)) { heal_monster(monster, 1, false); } @@ -6463,6 +6487,10 @@ int hurt_monster(monsters * victim, int damage_dealt) { damage_dealt = std::max(std::min(damage_dealt, victim->hit_points), 0); victim->hit_points -= damage_dealt; + + // Allow the victim to exhibit passive damage behaviour (royal jelly). + victim->react_to_damage(damage_dealt); + return (damage_dealt); } diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index d3315077c2..f48abe8b81 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -1452,7 +1452,7 @@ const coord_def travel_pathfind::unexplored_square() const * The travel algorithm is based on the NetHack travel code written by Warwick * Allison - used with his permission. */ -const coord_def travel_pathfind::pathfind(run_mode_type rmode) +coord_def travel_pathfind::pathfind(run_mode_type rmode) { if (rmode == RMODE_INTERLEVEL) rmode = RMODE_TRAVEL; diff --git a/crawl-ref/source/travel.h b/crawl-ref/source/travel.h index f9f4b616d1..78b924ad66 100644 --- a/crawl-ref/source/travel.h +++ b/crawl-ref/source/travel.h @@ -576,7 +576,7 @@ public: virtual ~travel_pathfind(); // Finds travel direction or explore target. - const coord_def pathfind(run_mode_type rt); + coord_def pathfind(run_mode_type rt); // For flood-fills (explore), sets starting (seed) square. void set_floodseed(const coord_def &seed, bool double_flood = false); |