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
IYMUEJG2VI3TV7HVAZIU2L4DVQY7VNSIFVV4I3QIVP7H66EVJISAC
NNMFGXB5BJXGHTLNP4IL3IZ6TRQWZGANE4CXRNSD2EGY47QW3I6QC
IFBGMR2E3AYGZBCMTEGVWPOKZR7MV4R5XHAPIEAL2J2FTWTQBEDQC
5NGWEQYBNPFMXWZDEEH6XPF6CNDEPR5ZHVRWK4GGEIR4YRASWEVAC
M5G5XXQQ3HR5UKGL234635NXOY5VK3CT7N5WBS6XFFAIXYVBINJAC
YWOHKYO7MHOFSUIKYMJAWP5SMYNLU57WZHOHHOLJJFWBQF6U43MQC
MIMW5CBZXQEGL6BFKK2LK322VRSBADFF7AXMC24F3OBCDUYKQTGQC
PG5XH3WD6YPDOIZQ7SGHCQVXSDP5KQIGZLG7QNGHQVU6ULAVJWFAC
XGPPA2X3G3266EVSTXGQ7ZWYOCJ32FEYPEUJV55AI5TGFYHTYTQQC
6PAG7GHXHIYXJPPTEK4KZQZT4CL2SJDAGTVIUDB4KK66PVSTWUMAC
NK2TBKWOQBD2ZRGAO4Z3HMZPIN7UX3HNUS2YHEIJUUINUTRMPODQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
L6RNMJ4AGWWA5FRQW5QRCFY5OD5NUB5QF7H36TNIKQYRTR2KPYPAC
W3F2XPGTHDOSFFKZOHB3555C2ZTIG2KYSXEHSNODVUKGHOLYKGRQC
X3CWUWK437W32BTAAQY7DQ3P4KI5CM3KQW5AW4E2VCHGEOP5WKCAC
PWY4VZVHDLYL7UVNCCOW7BM7LYK2BOGTL23P75HXUJ33MHJPEJPQC
CA6ZG6P2CE5EPAOQSB3P7NBDKTNFFHQP4CPOUFZL32ON6N32GSSQC
YGDO5RDP2QWDEPUFJGY223VQUWCKPKQRNIWXIRXSMYHHXWJACSNAC
MI7CWKRDXHAU7PIHLXXOSFBULRGT2OOMIMOSQLMVYOUVGABIVXGAC
POP6UTTHSJGDU5NU7ENQJOZVFUHYM63HJ6HLPXDDLK7XFIP4XEIQC
2EPIGP4UVXPQVMQTEUQ2ZEZW65AXBSJWWQ6AKDIYVPTEFHWMH3RQC
P2OYYNPHIBGOLT4CRLNTTIXDN34EU2QCMFQJNLAMUAHQXZTEDPXQC
2TFYJ7D72JY4DYQW3GSPEONA2WYIVHAJXTIQ2QRDIWF65XN2QFGAC
CRU7JBTVJWTTVQ5JTRA2B3X2FPKPJ2RRR33IK2OG536VMOEZJYJAC
GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC
4SWAT5KCKQV527NKELAXFQ5XA4Q5HONQXD4VBXMUZNPVPQKPCPNAC
3XL4DKV7PSWAU3ZBJSKZFJ2VUYABC47I7KKWGIRCJYXULQD453MQC
OJKUO2UJ2O2MUITGVQNF3BQITGVYE3XK6QTG7XW6OYPJ4YFWCFQQC
PNIUNDZ5EU2BP673QTWVFQFQJAAI2MAFIVEQAZMDBIMD4ZDWWXTQC
2DORUQ4B574MDOOMRYWGU5I72AKHMCSTZ6B3VSHQBUQOZYHRC7FAC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
6TEISZD7HYSSL24EOKIBNURU66KGSQX7B7SNAHBP4DQSAOTGH2MQC
J77VWSSEGMKS4S3HUAKROJYFL3H5FQSO4MCEPXBFJDMYBU36VRLAC
K27R6ZMYMKVNXIV7K3QU2NXRVOGQRLMR6TI6ZQQSVKXVKS76NLSQC
EL35XZPOY2BCQPPVP2XJBMZJQOFPUSBX4EVSFANUBGRV2BR5ATIAC
3MAPXTL5GAQ6373CUUVPBARIRHBRSISAF3BO2WBEROT2KUAXWLGAC
EI5XQIKW3OBVTDVT2A4Q535I5FOVEKARLFISM457IHGAK7TVOMVAC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
74LQ7JXVLAFSHLI7LCBKFX47CNTYSKGUQSXNX5FCIUIGCC2JTR3QC
XC2U46CC57VN7ZWKWSVTN2MNZ43RLMMDZKZT7MQJFXX247WQ3NIAC
B3HWU2BEQQ4E6WKVTW3JQQJFMWTVW3XWKY6BHFNBRHSZPRCF2OTQC
OWERGKLVPNPGIIS23FZ7ZDOBWUIXCKYAFG3URXU75JAUDX3N5ENAC
QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC
LG7XJWUXOV4ZBMOTZX7R5NUC5IONVNCF5O42M4QVYLHNZ62AV25AC
Y5YQ2AXS6XTWCHEQFDXRYU2IDQTBW7WXNJFES22GSKYQ2JGZ3U5AC
OSGS3PH2L5CBTDVZCZS6OCFQNA4A7RMEXBYJQB7DDZBYYJW7QSSAC
542UIZKI65UDRNEMGFFDBWYD5XC7AYLTZ3JZQRR2GHYJALD3YY6QC
3HGELZU7NELOQ635HZO6IJIYLBSNCJ5VPH46IE22KA3OSLEFK7AQC
PM65H4V4GNPVIJFUQW57DC3VDB7TRUUNXKVZONQKEFZSK3AXX5GQC
4C4D3H4E5MDQ3KT5CWXN5K6H4FVT567PN66GAAHHIWVROHZWG5CQC
EHP6PYCIPYQ3KF4JFGBTZXEUQHN3FVAH4NUWEOWDDNKGPYVOTOJQC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
MHI3OM6VJY6LZUD577O22XREHPOPMWDVR3UT2JWGFDAKD7SO6Y3QC
RYT42Z6CED4KV5CCJ45CHZ3DQGLFMDCVH6CSQZNXOILULDG4MXVQC
IQGGFC563RBS7GDOACKCLXK752EE5RC3T6G5L6H446SXTMSA7T2AC
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();
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)
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);
}
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);
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);
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]
/////////////////////////////////////////////////////////////////////////////
// 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()
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);
// 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));
}
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)
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);
// 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);
}
}