diff options
author | zelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-01-12 09:32:55 +0000 |
---|---|---|
committer | zelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-01-12 09:32:55 +0000 |
commit | af82384c240a924e921b96d81b8dd295aa1f2a80 (patch) | |
tree | 247955562bf7669c24fce930959fe206942784ec /crawl-ref/source | |
parent | 53dd513378a230964bd09f3de2a1bcfaeabc37be (diff) | |
download | crawl-ref-af82384c240a924e921b96d81b8dd295aa1f2a80.tar.gz crawl-ref-af82384c240a924e921b96d81b8dd295aa1f2a80.zip |
Fix bug #2089989: only place friendly god gifts after the player's turn is
over, so that any in-progress effects won't hurt them, thus turning them
hostile and angering the player's god. Also try to avoid placing god gifts
(friendly or hostile) in a damaging cloud, and if it's unavoidable and placing
a friendly god gift there would anger the god then don't place them at all.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8426 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source')
-rw-r--r-- | crawl-ref/source/acr.cc | 7 | ||||
-rw-r--r-- | crawl-ref/source/monplace.cc | 26 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 113 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.h | 9 | ||||
-rw-r--r-- | crawl-ref/source/religion.cc | 293 | ||||
-rw-r--r-- | crawl-ref/source/religion.h | 2 |
6 files changed, 355 insertions, 95 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index fb73e3728b..ca1e0e33d3 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -1197,6 +1197,10 @@ static void _do_wizard_command(int wiz_command, bool silent_fail) formatted_mpr(formatted_string::parse_string("Not a <magenta>Wizard</magenta> Command.")); break; } + // Force the placement of any delayed monster gifts. + you.turn_is_over = true; + religion_turn_end(); + you.turn_is_over = false; } @@ -3108,6 +3112,9 @@ static void _check_sanctuary() void world_reacts() { + if (!crawl_state.arena) + religion_turn_end(); + crawl_state.clear_god_acting(); #ifdef USE_TILE diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index 8032d753c9..4fd5585a5c 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -2427,6 +2427,32 @@ int create_monster(mgen_data mg, bool fail_msg) || !mons_class_can_pass(montype, grd(mg.pos))) { mg.pos = find_newmons_square(montype, mg.pos); + // Gods other than Xom will try to avoid placing their monsters + // directly in harm's way. + if (mg.god != GOD_NO_GOD && mg.god != GOD_XOM) + { + monsters dummy; + dummy.type = mg.cls; + dummy.base_monster = mg.base_type; + dummy.god = mg.god; + + int tries = 0; + while (tries++ < 50 + && mons_avoids_cloud(&dummy, env.cgrid(mg.pos), NULL, true)) + { + mg.pos = find_newmons_square(montype, mg.pos); + } + const int cloud_num = env.cgrid(mg.pos); + // Don't place friendly god gift in a damaging cloud created by + // you if that would anger the god. + if (mons_avoids_cloud(&dummy, cloud_num, NULL, true) + && mg.behaviour == BEH_FRIENDLY + && god_hates_attacking_friend(you.religion, &dummy) + && YOU_KILL(env.cloud[cloud_num].killer)) + { + return (-1); + } + } } if (in_bounds(mg.pos)) diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index 4f5133e899..ad2ab21330 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -4734,8 +4734,12 @@ static bool _is_player_or_mon_sanct(const monsters* monster) || is_sanctuary(monster->pos())); } -static bool _mons_avoids_cloud(const monsters *monster, cloud_type cl_type) +bool mons_avoids_cloud(const monsters *monster, cloud_type cl_type, + bool placement, bool extra_careful) { + if (placement) + extra_careful = true; + switch (cl_type) { case CLOUD_MIASMA: @@ -4746,6 +4750,9 @@ static bool _mons_avoids_cloud(const monsters *monster, cloud_type cl_type) if (mons_res_fire(monster) > 1) return (false); + if (extra_careful) + return (true); + if (monster->hit_points >= 15 + random2avg(46, 5)) return (false); break; @@ -4753,6 +4760,8 @@ static bool _mons_avoids_cloud(const monsters *monster, cloud_type cl_type) case CLOUD_STINK: if (mons_res_poison(monster) > 0) return (false); + if (extra_careful) + return (true); if (x_chance_in_y(monster->hit_dice - 1, 5)) return (false); if (monster->hit_points >= random2avg(19, 2)) @@ -4763,6 +4772,9 @@ static bool _mons_avoids_cloud(const monsters *monster, cloud_type cl_type) if (mons_res_cold(monster) > 1) return (false); + if (extra_careful) + return (true); + if (monster->hit_points >= 15 + random2avg(46, 5)) return (false); break; @@ -4771,11 +4783,17 @@ static bool _mons_avoids_cloud(const monsters *monster, cloud_type cl_type) if (mons_res_poison(monster) > 0) return (false); + if (extra_careful) + return (true); + if (monster->hit_points >= random2avg(37, 4)) return (false); break; case CLOUD_GREY_SMOKE: + if (placement) + return (false); + // This isn't harmful, but dumb critters might think so. if (mons_intel(monster) > I_ANIMAL || coinflip()) return (false); @@ -4792,13 +4810,64 @@ static bool _mons_avoids_cloud(const monsters *monster, cloud_type cl_type) } // Exceedingly dumb creatures will wander into harmful clouds. - if (is_harmless_cloud(cl_type) || mons_intel(monster) == I_PLANT) + if (is_harmless_cloud(cl_type) + || mons_intel(monster) == I_PLANT && !extra_careful) + { return (false); + } // If we get here, the cloud is potentially harmful. return (true); } +// Like the above, but prevents monsters from moving into cloud if it +// would anger the player's god, and also allows a monster to move from +// one damaging cloud to another, even if they're of different types. +bool mons_avoids_cloud(const monsters *monster, int cloud_num, + cloud_type *cl_type, bool placement) +{ + if (cloud_num == EMPTY_CLOUD) + { + if (cl_type != NULL) + *cl_type = CLOUD_NONE; + + return (false); + } + + const cloud_struct &cloud = env.cloud[cloud_num]; + + if (cl_type != NULL) + *cl_type = cloud.type; + + const bool careful_friendly + = YOU_KILL(cloud.killer) && mons_friendly(monster) + && god_hates_attacking_friend(you.religion, monster); + + // Is the target cloud okay? + if (!mons_avoids_cloud(monster, cloud.type, placement, careful_friendly)) + return (false); + + // If we're already in a cloud that we'd want to avoid then moving + // from one to the other is okay. + if (!in_bounds(monster->pos()) || monster->pos() == cloud.pos) + return (true); + + const int our_cloud_num = env.cgrid(monster->pos()); + + if (our_cloud_num == EMPTY_CLOUD) + return (true); + + const cloud_struct &our_cloud = env.cloud[our_cloud_num]; + + // Don't move monster from a cloud that won't anger their god to one + // that will. + if (!YOU_KILL(our_cloud.killer) && careful_friendly) + return (true); + + return (!mons_avoids_cloud(monster, our_cloud.type, true, + careful_friendly)); +} + //--------------------------------------------------------------- // // handle_nearby_ability @@ -6751,13 +6820,13 @@ static void _handle_monster_move(int i, monsters *monster) monster->shield_blocks = 0; - const int cloud_num = env.cgrid(monster->pos()); - const cloud_type cl_type = cloud_num == EMPTY_CLOUD ? CLOUD_NONE - : env.cloud[cloud_num].type; - - if (cloud_num != EMPTY_CLOUD) + cloud_type cl_type; + const int cloud_num = env.cgrid(monster->pos()); + const bool avoid_cloud = mons_avoids_cloud(monster, cloud_num, + &cl_type); + if (cl_type != CLOUD_NONE) { - if (_mons_avoids_cloud(monster, cl_type)) + if (avoid_cloud) { if (mons_is_submerged(monster)) { @@ -6801,8 +6870,7 @@ static void _handle_monster_move(int i, monsters *monster) ASSERT(in_bounds(monster->target) || monster->target.origin()); // Submerging monsters will hide from clouds. - if (cloud_num != EMPTY_CLOUD && _mons_avoids_cloud(monster, cl_type) - && monster_can_submerge(monster, grd(monster->pos())) + if (avoid_cloud && monster_can_submerge(monster, grd(monster->pos())) && !monster->submerged()) { monster->add_ench(ENCH_SUBMERGED); @@ -6865,7 +6933,7 @@ static void _handle_monster_move(int i, monsters *monster) // cloud on top of the water. if (monster->hit_points <= monster->max_hit_points / 2 || monster->has_ench(ENCH_FEAR) - || _mons_avoids_cloud(monster, cl_type)) + || avoid_cloud) { monster->speed_increment -= non_move_energy; continue; @@ -7778,15 +7846,11 @@ static bool _mon_can_move_to_pos(const monsters *monster, if (monster->type == MONS_FIRE_ELEMENTAL || one_chance_in(5)) no_water = true; - const int targ_cloud_num = env.cgrid(targ); - const cloud_type targ_cloud_type = - (targ_cloud_num == EMPTY_CLOUD) ? CLOUD_NONE - : env.cloud[targ_cloud_num].type; + cloud_type targ_cloud_type; + const int targ_cloud_num = env.cgrid(targ); - const int curr_cloud_num = env.cgrid(monster->pos()); - const cloud_type curr_cloud_type = - (curr_cloud_num == EMPTY_CLOUD) ? CLOUD_NONE - : env.cloud[curr_cloud_num].type; + if (mons_avoids_cloud(monster, targ_cloud_num, &targ_cloud_type)) + return (false); if (mons_class_flag(monster->type, M_BURROWS) && (target_grid == DNGN_ROCK_WALL @@ -7898,17 +7962,6 @@ static bool _mon_can_move_to_pos(const monsters *monster, if (!_is_trap_safe(monster, targ, just_check)) return (false); - if (targ_cloud_num != EMPTY_CLOUD) - { - if (curr_cloud_num != EMPTY_CLOUD - && targ_cloud_type == curr_cloud_type) - { - return (true); - } - - return !_mons_avoids_cloud(monster, targ_cloud_type); - } - // If we end up here the monster can safely move. return (true); } diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h index 63bd62f371..3dea7bb035 100644 --- a/crawl-ref/source/monstuff.h +++ b/crawl-ref/source/monstuff.h @@ -213,4 +213,13 @@ int mons_thrown_weapon_damage(const item_def *weap); int mons_natural_regen_rate(monsters *monster); +bool mons_avoids_cloud(const monsters *monster, cloud_type cl_type, + bool placement = false, + bool extra_careful = false); + +// Like the above, but prevents monsters from moving into cloud if it +// would anger the player's god, and also allows a monster to move from +// one damaging cloud to another. +bool mons_avoids_cloud(const monsters *monster, int cloud_num, + cloud_type *cl_type = NULL, bool placement = false); #endif diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index 15198e1095..3221708ea3 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -382,6 +382,14 @@ static bool _make_god_gifts_hostile(bool level_only = true); static void _print_sacrifice_message(god_type, const item_def &, piety_gain_t, bool = false); +typedef void (*delayed_callback)(const mgen_data &mg, int &midx, int placed); + +static void _delayed_monster(const mgen_data &mg, + delayed_callback callback = NULL); +static void _delayed_monster_done(std::string success, std::string failure, + delayed_callback callback = NULL); +static void _place_delayed_monsters(); + ///////////////////////////////////////////////////////////////////// // god_conduct_trigger @@ -935,22 +943,26 @@ static int _yred_random_servants(int threshold, bool force_hostile = false) if (mon == MONS_FLYING_SKULL) how_many = 2 + random2(4); - int count = 0; + mgen_data mg(mon, !force_hostile ? BEH_FRIENDLY : BEH_HOSTILE, + 0, 0, you.pos(), !force_hostile ? you.pet_target : MHITYOU, + 0, GOD_YREDELEMNUL); - for (int i = 0; i < how_many; ++i) + int created = 0; + if (force_hostile) { - if (create_monster( - mgen_data(mon, - !force_hostile ? BEH_FRIENDLY : BEH_HOSTILE, - 0, 0, you.pos(), - !force_hostile ? you.pet_target : MHITYOU, - 0, GOD_YREDELEMNUL)) != -1) + for (int i = 0; i < how_many; ++i) { - count++; + if (create_monster(mg) != -1) + created++; } } + else + { + for (int i = 0; i < how_many; ++i) + _delayed_monster(mg); + } - return (count); + return (created); } static bool _okawaru_random_servant() @@ -971,16 +983,9 @@ static bool _okawaru_random_servant() (temp_rand < 95) ? MONS_HILL_GIANT // 5% : MONS_TITAN); // 5% - bool success = false; - - if (create_monster( - mgen_data::hostile_at(mon, - you.pos(), 0, 0, true, GOD_OKAWARU)) != -1) - { - success = true; - } - - return (success); + return (create_monster( + mgen_data::hostile_at(mon, + you.pos(), 0, 0, true, GOD_OKAWARU)) != -1); } static const item_def* _find_missile_launcher(int skill) @@ -1502,12 +1507,34 @@ static bool _tso_blessing_friendliness(monsters* mon) base_increase + random2(base_increase)); } +static void _beogh_reinf_callback(const mgen_data &mg, int &midx, int placed) +{ + ASSERT(mg.god == GOD_BEOGH); + + // Beogh tries a second time to place reinforcements. + if (midx == -1) + midx = create_monster(mg); + + if (midx == -1) + return; + + monsters* mon = &menv[midx]; + + mon->flags |= MF_ATT_CHANGE_ATTEMPT; + + bool high_level = (mon->type == MONS_ORC_PRIEST + || mon->type == MONS_ORC_WARRIOR + || mon->type == MONS_ORC_KNIGHT); + + // For high level orcs, there's a chance of being named. + if (high_level && one_chance_in(5)) + give_monster_proper_name(mon); +} + // If you don't currently have any followers, send a small band to help // you out. -static bool _beogh_blessing_reinforcement() +static void _beogh_blessing_reinforcement() { - bool success = false; - // Possible reinforcement. const monster_type followers[] = { MONS_ORC, MONS_ORC, MONS_ORC_WIZARD, MONS_ORC_PRIEST @@ -1533,25 +1560,11 @@ static bool _beogh_blessing_reinforcement() else follower_type = RANDOM_ELEMENT(followers); - int monster = - create_monster( - mgen_data(follower_type, BEH_FRIENDLY, 0, 0, - you.pos(), you.pet_target, 0, GOD_BEOGH)); - - if (monster != -1) - { - monsters *mon = &menv[monster]; - mon->flags |= MF_ATT_CHANGE_ATTEMPT; - - // For high level orcs, there's a chance of being named. - if (high_level && one_chance_in(5)) - give_monster_proper_name(mon); - - success = true; - } + _delayed_monster( + mgen_data(follower_type, BEH_FRIENDLY, 0, 0, + you.pos(), you.pet_target, 0, GOD_BEOGH), + _beogh_reinf_callback); } - - return (success); } static bool _beogh_blessing_priesthood(monsters* mon) @@ -1625,20 +1638,18 @@ bool bless_follower(monsters *follower, { // If no follower was found, attempt to send // reinforcement. - bool reinforced = _beogh_blessing_reinforcement(); + _beogh_blessing_reinforcement(); - if (!reinforced || coinflip()) - { - // Try again, or possibly send more reinforcement. - if (_beogh_blessing_reinforcement()) - reinforced = true; - } + // Possibly send more reinforcement. + if (coinflip()) + _beogh_blessing_reinforcement(); - if (!reinforced) - return (false); + _delayed_monster_done("Beogh blesses you with " + "reinforcements.", ""); - result = "reinforcement"; - goto blessing_done; + // Return true, even though the reinforcements might + // not be placed. + return (true); } } } @@ -1832,6 +1843,18 @@ blessing_done: return (true); } +static void _delayed_gift_callback(const mgen_data &mg, int &midx, + int placed) +{ + if (placed <= 0) + return; + + more(); + _inc_gift_timeout(4 + random2avg(7, 2)); + you.num_gifts[you.religion]++; + take_note(Note(NOTE_GOD_GIFT, you.religion)); +} + static void _do_god_gift(bool prayed_for) { ASSERT(you.religion != GOD_NO_GOD); @@ -1927,18 +1950,10 @@ static void _do_god_gift(bool prayed_for) { // The maximum threshold occurs at piety_breakpoint(5). int threshold = (you.piety - piety_breakpoint(2)) * 20 / 9; - int how_many = _yred_random_servants(threshold); + _yred_random_servants(threshold); - if (how_many > 0) - { - simple_god_message( - how_many > 1 ? " grants you several undead servants!" - : " grants you an undead servant!"); - more(); - _inc_gift_timeout(4 + random2avg(7, 2)); - you.num_gifts[you.religion]++; - take_note(Note(NOTE_GOD_GIFT, you.religion)); - } + _delayed_monster_done("grants you @an@ undead servant@s@!", + "", _delayed_gift_callback); } break; @@ -3112,12 +3127,23 @@ static FixedVector<bool, NUM_MONSTERS> _first_attack_was_friendly; void religion_turn_start() { + if (you.turn_is_over) + religion_turn_end(); + _first_attack_conduct.init(true); _first_attack_was_unchivalric.init(false); _first_attack_was_friendly.init(false); crawl_state.clear_god_acting(); } +void religion_turn_end() +{ + ASSERT(you.turn_is_over); + _place_delayed_monsters(); +} + +#define NEW_GIFT_FLAGS (MF_JUST_SUMMONED | MF_GOD_GIFT) + void set_attack_conducts(god_conduct_trigger conduct[3], const monsters *mon, bool known) { @@ -3125,7 +3151,14 @@ void set_attack_conducts(god_conduct_trigger conduct[3], const monsters *mon, if (mons_friendly(mon)) { - if(_first_attack_conduct[midx] + if ((mon->flags & NEW_GIFT_FLAGS) == NEW_GIFT_FLAGS) + { + mprf(MSGCH_ERROR, "Newly created friendly god gift '%s' was hurt " + "by you, shouldn't be possible; please file a bug report.", + mon->name(DESC_PLAIN, true).c_str()); + _first_attack_was_friendly[midx] = true; + } + else if(_first_attack_conduct[midx] || _first_attack_was_friendly[midx]) { conduct[0].set(DID_ATTACK_FRIEND, 5, known, mon); @@ -4769,6 +4802,9 @@ bool divine_retribution( god_type god ) return (false); } + if (you.turn_is_over) + religion_turn_end(); + god_acting gdact(god, true); bool do_more = true; @@ -4825,6 +4861,9 @@ bool divine_retribution( god_type god ) // point...the punishment might have reduced penance further. dec_penance(god, 1 + random2(3)); + if (you.turn_is_over) + religion_turn_end(); + return (did_retrib); } @@ -6024,6 +6063,11 @@ bool god_hates_attacking_friend(god_type god, const actor *fr) if (!fr || fr->kill_alignment() != KC_FRIENDLY) return (false); + return god_hates_attacking_friend(god, fr->mons_species()); +} + +bool god_hates_attacking_friend(god_type god, int species) +{ switch (god) { case GOD_ZIN: @@ -6032,7 +6076,7 @@ bool god_hates_attacking_friend(god_type god, const actor *fr) case GOD_OKAWARU: return (true); case GOD_BEOGH: // added penance to avoid killings for loot - return (mons_species(fr->id()) == MONS_ORC); + return (species == MONS_ORC); default: return (false); @@ -6680,6 +6724,9 @@ static bool _need_free_piety() //jmf: moved stuff from effects::handle_time() void handle_god_time() { + if (you.turn_is_over) + religion_turn_end(); + if (one_chance_in(100)) { // Choose a god randomly from those to whom we owe penance. @@ -6804,6 +6851,9 @@ void handle_god_time() DEBUGSTR("Bad god, no bishop!"); } } + + if (you.turn_is_over) + religion_turn_end(); } // yet another wrapper for mpr() {dlb}: @@ -7031,3 +7081,116 @@ int get_tension(god_type god) return std::max(0, tension); } + +///////////////////////////////////////////////////////////////////////////// +// Stuff for placing god gift monsters after the player's turn has ended. +///////////////////////////////////////////////////////////////////////////// + +static std::vector<mgen_data> _delayed_data; +static std::deque<delayed_callback> _delayed_callbacks; +static std::deque<unsigned int> _delayed_done_trigger_pos; +static std::deque<delayed_callback> _delayed_done_callbacks; +static std::deque<std::string> _delayed_success; +static std::deque<std::string> _delayed_failure; + +static void _delayed_monster(const mgen_data &mg, delayed_callback callback) +{ + _delayed_data.push_back(mg); + _delayed_callbacks.push_back(callback); +} + +static void _delayed_monster_done(std::string success, std::string failure, + delayed_callback callback) +{ + const unsigned int size = _delayed_data.size(); + ASSERT(size > 0); + + _delayed_done_trigger_pos.push_back(size - 1); + _delayed_success.push_back(success); + _delayed_failure.push_back(failure); + _delayed_done_callbacks.push_back(callback); +} + +static void _place_delayed_monsters() +{ + int placed = 0; + god_type prev_god = GOD_NO_GOD; + for (unsigned int i = 0; i < _delayed_data.size(); i++) + { + mgen_data &mg = _delayed_data[i]; + delayed_callback cback = _delayed_callbacks[i]; + + if (prev_god != mg.god) + { + placed = 0; + prev_god = mg.god; + } + + int midx = create_monster(mg); + + if (cback) + (*cback)(mg, midx, placed); + + if (midx != -1) + placed++; + + if (_delayed_done_trigger_pos.size() > 0 + && _delayed_done_trigger_pos[0] == i) + { + cback = _delayed_done_callbacks[0]; + + std::string msg; + if (placed > 0) + msg = _delayed_success[0]; + else + msg = _delayed_failure[0]; + + if (placed == 1) + { + msg = replace_all(msg, "@a@", "a"); + msg = replace_all(msg, "@an@", "an"); + } + else + { + msg = replace_all(msg, "@a@", ""); + msg = replace_all(msg, "@an@", ""); + } + + if (placed > 1) + msg = replace_all(msg, "@s@", "s"); + else + msg = replace_all(msg, "@s@", ""); + + prev_god = GOD_NO_GOD; + _delayed_done_trigger_pos.pop_front(); + _delayed_success.pop_front(); + _delayed_failure.pop_front(); + _delayed_done_callbacks.pop_front(); + + if (msg == "") + { + if (cback) + (*cback)(mg, midx, placed); + continue; + } + + // Fake it coming from simple_god_message(). + if (msg[0] == ' ' || msg[0] == '\'') + msg = god_name(mg.god) + msg; + + msg = apostrophise_fixup(msg); + trim_string(msg); + + god_speaks(mg.god, msg.c_str()); + + if (cback) + (*cback)(mg, midx, placed); + } + } + + _delayed_data.clear(); + _delayed_callbacks.clear(); + _delayed_done_trigger_pos.clear(); + _delayed_success.clear(); + _delayed_failure.clear(); +} // _place_delayed_monsters() diff --git a/crawl-ref/source/religion.h b/crawl-ref/source/religion.h index 16f56447f6..d9767c282f 100644 --- a/crawl-ref/source/religion.h +++ b/crawl-ref/source/religion.h @@ -153,9 +153,11 @@ bool bless_follower(monsters *follower = NULL, bool force = false); bool god_hates_attacking_friend(god_type god, const actor *fr); +bool god_hates_attacking_friend(god_type god, int species); bool god_likes_items(god_type god); void religion_turn_start(); +void religion_turn_end(); int get_tension(god_type god = you.religion); #endif |