Skip to content

Commit 9c35671

Browse files
authored
Player can attack discordant ally, not swap places (#657)
1 parent 3789ce2 commit 9c35671

File tree

3 files changed

+66
-19
lines changed

3 files changed

+66
-19
lines changed

changes/attack-discordant-allies.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A discordant ally will no longer swap places with the player and can be attacked. Attacks require player confirmation and will not make the ally an enemy.

src/brogue/Monsters.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -283,18 +283,20 @@ boolean attackWouldBeFutile(const creature *attacker, const creature *defender)
283283
return false;
284284
}
285285

286-
// This is a specific kind of willingness, bordering on ability.
287-
// Intuition: if it swung an axe from that position, should it
288-
// hit the defender? Or silently pass through it, as it does for
289-
// allies?
286+
/// @brief Determines if a creature is willing to attack another. Considers factors like discord,
287+
/// entrancement, confusion, and whether they are enemies. Terrain and location are not considered,
288+
/// except for krakens and eels that attack anything in deep water. Used for player and monster attacks.
289+
/// @param attacker the attacking creature
290+
/// @param defender the defending creature
291+
/// @return true if the attacker is willing to attack the defender
290292
boolean monsterWillAttackTarget(const creature *attacker, const creature *defender) {
291293
if (attacker == defender || (defender->bookkeepingFlags & MB_IS_DYING)) {
292294
return false;
293295
}
294296
if (attacker == &player
295297
&& defender->creatureState == MONSTER_ALLY) {
296298

297-
return false;
299+
return defender->status[STATUS_DISCORDANT];
298300
}
299301
if (attacker->status[STATUS_ENTRANCED]
300302
&& defender->creatureState != MONSTER_ALLY) {

src/brogue/Movement.c

+58-14
Original file line numberDiff line numberDiff line change
@@ -526,16 +526,15 @@ boolean freeCaptivesEmbeddedAt(short x, short y) {
526526
return false;
527527
}
528528

529-
// Do we need confirmation so we don't accidently hit an acid mound?
529+
/// @brief Ask the player for confirmation before attacking an acidic monster
530+
/// @param hitList the creature(s) getting attacked
531+
/// @return true to abort the attack
530532
boolean abortAttackAgainstAcidicTarget(creature *hitList[8]) {
531533
short i;
532534
char monstName[COLS], weaponName[COLS];
533535
char buf[COLS*3];
534536

535-
if (rogue.weapon
536-
&& !(rogue.weapon->flags & ITEM_PROTECTED)
537-
&& !player.status[STATUS_HALLUCINATING]
538-
&& !player.status[STATUS_CONFUSED]) {
537+
if (rogue.weapon && !(rogue.weapon->flags & ITEM_PROTECTED)) {
539538

540539
for (i=0; i<8; i++) {
541540
if (hitList[i]
@@ -560,9 +559,54 @@ boolean abortAttackAgainstAcidicTarget(creature *hitList[8]) {
560559
return false;
561560
}
562561

562+
/// @brief Ask the player for confirmation before attacking a discordant ally
563+
/// @param hitList the creature(s) getting attacked
564+
/// @return true to abort the attack
565+
static boolean abortAttackAgainstDiscordantAlly(const creature *hitList[8]) {
566+
567+
for (int i=0; i<8; i++) {
568+
if (hitList[i]
569+
&& hitList[i]->creatureState == MONSTER_ALLY
570+
&& hitList[i]->status[STATUS_DISCORDANT]
571+
&& canSeeMonster(hitList[i])) {
572+
573+
char monstName[COLS], buf[COLS*3];
574+
monsterName(monstName, hitList[i], true);
575+
sprintf(buf, "Are you sure you want to attack %s?", monstName);
576+
if (confirm(buf, false)) {
577+
return false; // Don't abort. Attack the ally.
578+
} else {
579+
return true; // Abort!
580+
}
581+
}
582+
}
583+
return false; // the confirmation dialog was not shown
584+
}
585+
586+
/// @brief Determines if a player attack against the given creature(s) should be aborted. A confirmation
587+
/// dialog is shown when attempting to attack an acidic monster or discordant ally, unless confused or
588+
/// hallucinating (but not telepathic).
589+
/// @param hitList the creature(s) getting attacked
590+
/// @return true to abort the attack
591+
static boolean abortAttack(const creature *hitList[8]) {
592+
593+
// too bad so sad if you're confused or hallucinating (but not telepathic)
594+
if (player.status[STATUS_CONFUSED]
595+
|| (player.status[STATUS_HALLUCINATING] && !player.status[STATUS_TELEPATHIC])) {
596+
return false;
597+
}
598+
599+
if (abortAttackAgainstAcidicTarget(hitList)
600+
|| abortAttackAgainstDiscordantAlly(hitList)) {
601+
return true;
602+
}
603+
604+
return false; // either the player confirmed the attack or the confirmation dialog was not shown
605+
}
606+
563607
// Returns true if a whip attack was launched.
564608
// If "aborted" pointer is provided, sets it to true if it was aborted because
565-
// the player opted not to attack an acid mound (in which case the whole turn
609+
// the player opted not to attack (in which case the whole turn
566610
// should be aborted), as opposed to there being no valid whip attack available
567611
// (in which case the player/monster should move instead).
568612
boolean handleWhipAttacks(creature *attacker, enum directions dir, boolean *aborted) {
@@ -600,7 +644,7 @@ boolean handleWhipAttacks(creature *attacker, enum directions dir, boolean *abor
600644

601645
if (attacker == &player) {
602646
hitList[0] = defender;
603-
if (abortAttackAgainstAcidicTarget(hitList)) {
647+
if (abortAttack(hitList)) {
604648
if (aborted) {
605649
*aborted = true;
606650
}
@@ -618,7 +662,7 @@ boolean handleWhipAttacks(creature *attacker, enum directions dir, boolean *abor
618662

619663
// Returns true if a spear attack was launched.
620664
// If "aborted" pointer is provided, sets it to true if it was aborted because
621-
// the player opted not to attack an acid mound (in which case the whole turn
665+
// the player opted not to attack (in which case the whole turn
622666
// should be aborted), as opposed to there being no valid spear attack available
623667
// (in which case the player/monster should move instead).
624668
boolean handleSpearAttacks(creature *attacker, enum directions dir, boolean *aborted) {
@@ -682,7 +726,7 @@ boolean handleSpearAttacks(creature *attacker, enum directions dir, boolean *abo
682726
range = i;
683727
if (proceed) {
684728
if (attacker == &player) {
685-
if (abortAttackAgainstAcidicTarget(hitList)) {
729+
if (abortAttack(hitList)) {
686730
if (aborted) {
687731
*aborted = true;
688732
}
@@ -869,7 +913,7 @@ boolean playerMoves(short direction) {
869913
moveEntrancedMonsters(direction);
870914
playerTurnEnded();
871915
return true;
872-
} else if (specialAttackAborted) { // Canceled an attack against an acid mound.
916+
} else if (specialAttackAborted) { // Canceled an attack against an acidic monster or discordant ally
873917
brogueAssert(!committed);
874918
cancelKeystroke();
875919
rogue.disturbed = true;
@@ -897,15 +941,15 @@ boolean playerMoves(short direction) {
897941
}
898942
}
899943

900-
if (defender->creatureState != MONSTER_ALLY) {
944+
if (defender->creatureState != MONSTER_ALLY || defender->status[STATUS_DISCORDANT]) {
901945
// Make a hit list of monsters the player is attacking this turn.
902946
// We separate this tallying phase from the actual attacking phase because sometimes the attacks themselves
903947
// create more monsters, and those shouldn't be attacked in the same turn.
904948

905949
buildHitList(hitList, &player, defender,
906950
rogue.weapon && (rogue.weapon->flags & ITEM_ATTACKS_ALL_ADJACENT));
907951

908-
if (abortAttackAgainstAcidicTarget(hitList)) { // Acid mound attack confirmation.
952+
if (abortAttack(hitList)) {
909953
brogueAssert(!committed);
910954
cancelKeystroke();
911955
rogue.disturbed = true;
@@ -1049,7 +1093,7 @@ boolean playerMoves(short direction) {
10491093
&& (!cellHasTerrainFlag(tempMonst->loc.x, tempMonst->loc.y, T_OBSTRUCTS_PASSABILITY) || (tempMonst->info.flags & MONST_ATTACKABLE_THRU_WALLS))) {
10501094

10511095
hitList[0] = tempMonst;
1052-
if (abortAttackAgainstAcidicTarget(hitList)) { // Acid mound attack confirmation.
1096+
if (abortAttack(hitList)) {
10531097
brogueAssert(!committed);
10541098
cancelKeystroke();
10551099
rogue.disturbed = true;
@@ -1060,7 +1104,7 @@ boolean playerMoves(short direction) {
10601104
}
10611105
if (rogue.weapon && (rogue.weapon->flags & ITEM_PASS_ATTACKS)) {
10621106
buildFlailHitList(x, y, newX, newY, hitList);
1063-
if (abortAttackAgainstAcidicTarget(hitList)) { // Acid mound attack confirmation.
1107+
if (abortAttack(hitList)) {
10641108
brogueAssert(!committed);
10651109
cancelKeystroke();
10661110
rogue.disturbed = true;

0 commit comments

Comments
 (0)