Implement ordering your friends to stay where they are. To do this, I've added a new variable to the monster struct: patrol_point, that is set by the new t sub-command "Wait here!" Once this is set, monsters will spend their time wandering around within the LOS radius centred on the patrol point. If they are attacked, or the player or other friends are attacked, they'll stop wandering to fight, but once the foe is gone, they continue doing so. Currently, the only way to make them stop again is to issue another command, "Follow me!" that is basically the already existing "Come here!" command. I've also added a "Stop fighting!" command that for non-patrolling monsters has the same effect as "Follow me!" - patrolling monsters are supposed to take up their wanderings again.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@5247 c06c8d41-db1a-0410-9941-cceddc491573
P5TRGRH7XMQSPCZKM5IEEO34TY6WMLGHHX7BU6Y453JFRXLUR2VQC GMCNF7YBSN7WQJNHSF3RV2NTHTVP4TABCNP7S26UEIHPAKNCPWGAC 2Z55MZLCQ4LQVNZSEDQSCDJMUEAHEP7RFUC72ZVOAGNJFABSGO3AC 47RJZCYIM3B7IXJT7FFT6NBZZREY6REK5DZWKZ5E7G66BXEXFN6QC SZI3RQJBWG24RIA2HC4VKGKNHHLHVVONTSCFLB4BLS6YMXUKJYOQC 6EXYHS7E76E7RARFIB5RKUAUBCN6RKOBKWMEGCUADQRRSSZBEUFQC 32N3KXEP3OVYI7EE3IULLTPBZABDZS7FCTXHXZ2X76QGY7E6IFSAC PFDWNTN3NEZCP6NKPIM2FK3KRVDBQUCGIAWWOU2DJVS4KWWJLYYAC GVW4OBPGXY2Q75HB7QHADZIOHKL22FI2BSJ2TM4K5SBJENBFTQKAC WQIEW3O4MANA2KKYRUWEZP44KHVJ4RRHEZTDXSF4EDELX66LO26QC NM6UK4SZ3JWD3WWDY355C5NW2MBIFR4N73QHF43AE4CKR5LOLRCQC 2WRXQTGYDBLV46WRNVIUKGNA5QS563XZNNW3N2L6PVOCHIP2YGHQC O4DT3BQQ3XYPL6PQ72G6VPBAVHXZMEOLONFXNHXFMBXBVOYMB6VQC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC F5PJ6H7W2SS5LTUJ7N6JW4SSWTGTDJRXX7L4JBM2NSD2QC26FFLQC JGKYRZ34S3I23PMJX6IUBR7EHEFD6I4XXEGXNT7GKT2M2VIRBSMQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC AM7QPHDAWNXHLUEVUHVRHG2RO2DOIEFFU4GV3DCIROW6O5HW7H4AC 7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC PSCYVKJ7DGXAL3V5U4O6AJTRV6Q3N3SHQWAZ73VIPRTE4W64F2XAC LJK4ZQATLSB4MKZG3ARZX5V6RFGTN3NLCN6GTCUGJQKU26SOXMUAC 3BJ2OOF4F524G6UKVGOZVT6W3FSTSHHTKRJADUBZCHDXZWV3KANQC 5UC5TI7B6NWGIGN74QRBZKDBYUMHLM2ZO5ATXEZ6TZOGOLF3JX5QC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC C5U3HSOOQ7BKXKXIDS7MLVXUKDTHAWJ5NXNX6YDXTM3GWY5UWX4QC TTCA2KKE56DAAKEVUGAP7ZRNF5KDLOWRVO37E3D6KDNMLDBVL7MAC KAOE5HB3THUKVGFZRO5EZESHEB3Q34WUO5DFMLWIKOBF47LZTIYAC N5XD5IAOMEDF37AXEBALHFINB4H527T6YNTCHN5KKO6YHXAP5PNQC UZ6N6HOUPGVSPC5NQROEEDWMEGJA5XUWUY2AKH5QG65AZ25PVXDAC DH3YTI6VVI727SQXO4CXSDCSBG2UN3UAWLFULBGRLBVH22ACRXIAC ZGUJWUFJ4NFFJ6PGXLFGQWCWBCZHPWGWI44NJHJEVPRG5L36PADQC AS2IQQJNNCEQNXXKTGYHLB7RO3ZKCF4F7GK6FJH66BOOKDDRGNIQC IO5CHPT4QBYSFAPSZGGJAYMN33RDAQKGLVW6OKK25HVIEEQEI4IQC NUYXKJP5YXHRDUQW5QW7UC3D5U3VPANIOZAOHFCPWMSRYGMA3GCAC 627CM2ZOKVBMPVPBYGWBWWPT2FBMVRRH2VDGPT6Z5XCVJ5R4YQWQC QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC 4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC SFWCESFCUEVKJ6ZQQX3Y5YTIQD5BC6MCVSLVZFRGRTU46BFLKKWAC NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC 3V52MSSK7QX7FWLLUW63DTWCBAJEK674EFZLKP45FLZ5KZKVARHAC CGYTZT5QWIEGYKUOLOK7MFXSLJKLYRZONER5ZCDZO5XYWSLG475QC ASH5CK6CPBKMLGGIRJ5GKTWMS5W3OBVHTL66RTYZIPFM6KFBYA3QC OSGS3PH2L5CBTDVZCZS6OCFQNA4A7RMEXBYJQB7DDZBYYJW7QSSAC NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC WMHFDQKUDCUGM3R245LLVZ5NNEZSCXFDSTNMVS2O5EFUHHO7HU3AC PM65H4V4GNPVIJFUQW57DC3VDB7TRUUNXKVZONQKEFZSK3AXX5GQC GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC OWERGKLVPNPGIIS23FZ7ZDOBWUIXCKYAFG3URXU75JAUDX3N5ENAC UW4XQAAAV3S2ZVBLMSK6VQG6AMYR6DRKXFP64HHBC6Z3QIUWPVXQC FCZSQBKDNMJZRJS2LWQQWLUFGOXSKXDJZQIHC7L5S7HXCXQPOMMAC 6GT5JAWOIIL4SQ5MWIID6ZVO3KKQFWDQDZNVFHZ6DNK5QCBXJ4UAC 7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC JQFDRS5YPQ5KKTCNXW2XNUZU2XK2CW4PXRD6BSQFKMC5QFG3Y23AC X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC E335DPK7M5WBYKN5C3KG75KY75CVXLN3XTLK55MADVZMHW74AHGQC WQLOHSNCA3VOMDJF6IINJYKSYVYZEBPJJWBB33QSNE4RP5HEXPMAC CRUW4EVU3UDWNKXBCPWWHWXXGE7EMEHKK3PLLUD7NWPYY4K2R3YAC KEANRIMF5CGFVZ2XJYNFPOAKLXOSOJUOVA73IWBWOG576265ERHAC OP6CTAKWCAU64JXQ3USQYR5E5IFHQHNCACII5UMVRXUTZXJQOAZAC FU7EQZLXD7YNGUUDHXCBI3VUKL6M2G3EPDY6FB5UA6B6RD4S5UOQC R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC PKHOZG6TIUP2NZZIP6CW5OIPZ3O6PCGWXXW5MH4I6P2WVM24HZEQC JN4GPMQCXOY5ICTLPLWP6DXBFULN4GMAEK7T4GXTZVIJAUUKBBYAC IHLGRQOXBGZE3COMNKBKMIDQPJ7HRY4PT74ZUN7HD4ADDPDOX2NQC QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC OSRZEPPGBIMSZBWIVBTZTTIMV6TEUGVZRZ5AI2ZJW7CVZZQBUIMQC ZI7643NG3LOKVSDXHFNV2YU3ZIQUVEY25AOXZCPBXJB5SQAUCE6QC MJWFTUS66PTCNEYXEJA3CUJFXNWXIKDD6H3V24PW7HK64NSVOFSAC SBTVKHKZRMVDBYLGQNMZMJXPAYJG43UWBBD7HQJWIPN3BMMHUBJAC 7X3KS2DYACYTMNRE47MZSXC3RYRYUYDC5SOPRVWOIH6MQCHMEU7QC 2HWIWULS64JQ6QEVUUM5FAYNBPHU2FIGCI2HDYFYY447UMOL2AIQC UTGQ25S6K4R2POPYLVF6A5ZU4PRTN3SIR4DL672HERNAE3RZP7AAC EWBHI7SZ66QCI2JXIXPDDCVHZ2K45ANOGLR2APG74D63WH4N4UVAC J43G4UGXBYWSEJRLOP3V3NMJIFUXZHSR7MSZ2MWPCNDVI3QBMVDAC 7VRY3OH6WA7QLMNNOX76RR7C6HA7CLNYLZLWPXJWAP75QRF5B27AC KNW37MRIU72X4LPXSA4AUPW3VJMOXKWY2XFTV67KW2ZXTCSYMMNAC ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC RBAGQ2PB7V5YAM5KSHSZR2E3MLKDSRVM5XYGI2TIXP5QMVBOQHDQC U3KGUJJQWQORJIIFH3ADVNIEEX5HOX6KEOXO7DJSL7L3Z6GG3PAQC TOPYNQFDXFXNMNOW2LACAGZQIIBAZFS7NK4CBIA2IF6UUB6Y6ZJAC UFKLHUYL7WAQ3CI3D42T4C6KBGAUR63DSQAUQTTZG7GJMXSCVJWAC WW6THKR7JN447YC23YYHYYNH7ABMCFFSECNUFTIJBZX6JHX6W7TAC 3C2VE43SHCSBY4LTRTFYFLIPRWFUN6DXU6D34QVWDQTSNRBUFG7AC ILN2K6ASDZSMEHOPJ22IZLZJUO6DDGZTKAKXM3YXG6JZZHJNLX4AC 45EMD3KLQPMERNMIKU5G76H6556XOMIW352TSBP7VLWJX2YYGS7AC B7MSPF6X2RLGWN4M6ZZF3WSOPKGYPTTD7LIJVST7DXN27DG6JHNAC }static bool _choose_random_target_grid_in_los(monsters *mon){int pos_x, pos_y;int count_grids = 0;for (int j = -LOS_RADIUS; j < LOS_RADIUS; j++)for (int k = -LOS_RADIUS; k < LOS_RADIUS; k++){if (j == 0 && k == 0)continue;pos_x = mon->x + j;pos_y = mon->y + k;if (!in_bounds(pos_x, pos_y))continue;if (!mon->mon_see_grid(pos_x, pos_y))continue;if (!mon->can_pass_through_feat(grd[pos_x][pos_y]))continue;if (one_chance_in(++count_grids)){mon->target_x = pos_x;mon->target_y = pos_y;}}return (count_grids);
bool isHurt = (mon->hit_points <= mon->max_hit_points / 4 - 1);bool isHealthy = (mon->hit_points > mon->max_hit_points / 2);bool isSmart = (mons_intel(mon->type) > I_ANIMAL);bool isScared = mon->has_ench(ENCH_FEAR);bool isMobile = !mons_is_stationary(mon);
bool isHurt = (mon->hit_points <= mon->max_hit_points / 4 - 1);bool isHealthy = (mon->hit_points > mon->max_hit_points / 2);bool isSmart = (mons_intel(mon->type) > I_ANIMAL);bool isScared = mon->has_ench(ENCH_FEAR);bool isMobile = !mons_is_stationary(mon);bool patrolling = mon->is_patrolling();
// now, the corollary to that is that sometimes, if a// player is right next to a monster, they will 'see'
// Now, the corollary to that is that sometimes, if a// player is right next to a monster, they will 'see'.
mon->target_x = 10 + random2(GXM - 10);mon->target_y = 10 + random2(GYM - 10);
if (patrolling){if (mon->patrol_point != coord_def(mon->x, mon->y)|| !_choose_random_target_grid_in_los(mon)){mon->target_x = mon->patrol_point.x;mon->target_y = mon->patrol_point.y;}}else{mon->target_x = 10 + random2(GXM - 10);mon->target_y = 10 + random2(GYM - 10);}
// during their wanderings, monsters will// eventually relax their guard (stupid// ones will do so faster, smart monsters// have longer memories
// During their wanderings, monsters will eventually relax their// guard (stupid ones will do so faster, smart monsters have// longer memories.
// Smart monsters flee until they can// flee no more... possible to get a// 'CORNERED' event, at which point// we can jump back to WANDER if the foe// isn't present.
// Smart monsters flee until they can flee no more...// possible to get a 'CORNERED' event, at which point// we can jump back to WANDER if the foe isn't present.
if (mons_is_confused(monster)|| monster->type == MONS_AIR_ELEMENTAL&& monster->has_ench(ENCH_SUBMERGED)){std::vector<coord_def> moves;
if (mons_is_confused(monster)|| monster->type == MONS_AIR_ELEMENTAL&& monster->has_ench(ENCH_SUBMERGED)){std::vector<coord_def> moves;
int pfound = 0;for (int yi = -1; yi <= 1; ++yi)for (int xi = -1; xi <= 1; ++xi){coord_def c = monster->pos() + coord_def(xi, yi);if (in_bounds(c) && monster->can_pass_through(c)&& one_chance_in(++pfound)){mmov_x = xi;mmov_y = yi;}}
int pfound = 0;for (int yi = -1; yi <= 1; ++yi)for (int xi = -1; xi <= 1; ++xi){coord_def c = monster->pos() + coord_def(xi, yi);if (in_bounds(c) && monster->can_pass_through(c)&& one_chance_in(++pfound)){mmov_x = xi;mmov_y = yi;}}
// Bounds check: don't let confused monsters try to run// off the map.if (monster->x + mmov_x < 0 || monster->x + mmov_x >= GXM)mmov_x = 0;
// Bounds check: don't let confused monsters try to run// off the map.if (monster->x + mmov_x < 0 || monster->x + mmov_x >= GXM)mmov_x = 0;
if (!monster->can_pass_through(monster->x + mmov_x,monster->y + mmov_y)){mmov_x = mmov_y = 0;}
if (!monster->can_pass_through(monster->x + mmov_x,monster->y + mmov_y)){mmov_x = mmov_y = 0;}
if (mgrd[monster->x + mmov_x][monster->y + mmov_y] != NON_MONSTER&& !is_sanctuary(monster->x, monster->y)&& (mmov_x != 0 || mmov_y != 0)){monsters_fight(i,mgrd[monster->x + mmov_x][monster->y + mmov_y]);
int enemy = mgrd[monster->x + mmov_x][monster->y + mmov_y];if (enemy != NON_MONSTER&& !is_sanctuary(monster->x, monster->y)&& (mmov_x != 0 || mmov_y != 0)){if (monsters_fight(i, enemy)){brkk = true;mmov_x = 0;mmov_y = 0;DEBUG_ENERGY_USE("monsters_fight()");}else{// FIXME: None of these work!// Instead run away!if (monster->add_ench(mon_enchant(ENCH_FEAR))){behaviour_event(monster, ME_SCARE, MHITNOT,monster->x + mmov_x,monster->y + mmov_y);}break;/*if (monster->foe == enemy || mons_friendly(monster)&& monster->foe == MHITYOU){monster->foe = MHITNOT;monster->behaviour = BEH_WANDER;}
brkk = true;mmov_x = 0;mmov_y = 0;DEBUG_ENERGY_USE("monsters_fight()");}}
monster->target_x = 10 + random2(GXM - 10);monster->target_y = 10 + random2(GYM - 10);*/}}}
// smacking the player is always a good move if we're// hostile (even if we're heading somewhere else)// also friendlies want to keep close to the player// so it's okay as well
// Smacking the player is always a good move if we're// hostile (even if we're heading somewhere else).// Also friendlies want to keep close to the player// so it's okay as well.
target_x(0), target_y(0), inv(NON_ITEM), spells(), attitude(ATT_HOSTILE),behaviour(BEH_WANDER), foe(MHITYOU), enchantments(), flags(0L),experience(0), number(0), colour(BLACK), foe_memory(0), shield_blocks(0),god(GOD_NO_GOD), ghost(), seen_context("")
target_x(0), target_y(0), patrol_point(0, 0), inv(NON_ITEM), spells(),attitude(ATT_HOSTILE), behaviour(BEH_WANDER), foe(MHITYOU),enchantments(), flags(0L), experience(0), number(0), colour(BLACK),foe_memory(0), shield_blocks(0), god(GOD_NO_GOD), ghost(),seen_context("")
|| (flags & MF_BANISHED)|| type == MONS_ROYAL_JELLY|| (you.level_type == LEVEL_DUNGEON && hit_dice > 8 + random2(25)))
|| (flags & MF_BANISHED)|| type == MONS_ROYAL_JELLY|| you.level_type == LEVEL_DUNGEON && hit_dice > 8 + random2(25))
hand_half_bonus =unarmed_ok&& !can_do_unarmed&& !shield&& weapon&& !item_cursed( *weapon )&& hands == HANDS_HALF;
hand_half_bonus = (unarmed_ok&& !can_do_unarmed&& !shield&& weapon&& !item_cursed( *weapon )&& hands == HANDS_HALF);
attacker_invisible = !attacker_visible && see_grid(attacker->pos());defender_visible = defender && defender->visible();defender_invisible = !defender_visible && defender&& see_grid(defender->pos());needs_message = attacker_visible || defender_visible;
attacker_invisible = (!attacker_visible && see_grid(attacker->pos()));defender_visible = (defender && defender->visible());defender_invisible = (!defender_visible && defender&& see_grid(defender->pos()));needs_message = (attacker_visible || defender_visible);
if (attack && (is_sanctuary(you.x_pos, you.y_pos)|| is_sanctuary(defender->x, defender->y)))
if (!attack){// Attack was cancelled or unsuccessful...if (attk.cancel_attack)you.turn_is_over = false;return (false);}if (is_sanctuary(you.x_pos, you.y_pos)|| is_sanctuary(defender->x, defender->y))
mon->foe = you.pet_target;
mon->foe = (allow_patrol && mon->is_patrolling() ? MHITNOT: you.pet_target);}}static void _set_allies_patrol_point(bool clear = false){for (int i = 0; i < MAX_MONSTERS; ++i){monsters *mon(&menv[i]);if (!mon->alive() || !mons_near(mon) || !mons_friendly(mon))continue;mon->patrol_point = (clear ? coord_def(0, 0): coord_def(mon->x, mon->y));if (!clear)mon->behaviour = BEH_WANDER;
case 'f':case 's':mons_targd = MHITYOU;if (keyn == 'f'){// Don't reset patrol points for 'Stop fighting!'_set_allies_patrol_point(true);mpr("Follow me!");}elsempr("Stop fighting!");break;case 'w':mpr("Wait here!");mons_targd = MHITNOT;_set_allies_patrol_point();break;case 'p':if (you.duration[DUR_BERSERKER]){canned_msg(MSG_TOO_BERSERK);return;}if (targ_prev){mons_targd = you.prev_targ;mpr("Attack!");break;}
if (mons_targd != MHITNOT){you.pet_target = mons_targd;set_friendly_foes();}
you.pet_target = mons_targd;// Allow patrolling for "Stop fighting!" and "Wait here!"_set_friendly_foes(keyn == 's' || keyn == 'w');if (mons_targd != MHITNOT && mons_targd != MHITYOU)mpr("Attack!");