Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changes/issue-516.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-
The whip, spear, and rapier now work against invisible enemies when you are telepathic.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also mention that whips no longer work against submerged enemies?

6 changes: 3 additions & 3 deletions src/brogue/Items.c
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ void populateItems(pos upstairs) {
} else if (rogue.depthLevel > gameConst->amuletLevel) {
theCategory = GEM;
} else {

for (int j = 0; j < gameConst->numberMeteredItems; j++) {
// Create any metered items that reach generation thresholds
if (meteredItemsGenerationTable[j].levelScaling != 0 &&
Expand Down Expand Up @@ -3491,13 +3491,13 @@ void getImpactLoc(pos *returnLoc, const pos originLoc, const pos targetLoc,
pos coords[DCOLS + 1];
short i, n;
creature *monst;
creature *orig = monsterAtLoc(originLoc);

n = getLineCoordinates(coords, originLoc, targetLoc, theBolt);
n = min(n, maxDistance);
for (i=0; i<n; i++) {
monst = monsterAtLoc(coords[i]);
if (monst
&& !monsterIsHidden(monst, monsterAtLoc(originLoc))
if (canAttack(orig, monst)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong from the POV of getImpactLoc. Bolts do not pass through creatures if the caster is unable to attack them.

&& !(monst->bookkeepingFlags & MB_SUBMERGED)) {
// Imaginary bolt hit the player or a monster.
break;
Expand Down
24 changes: 24 additions & 0 deletions src/brogue/Monsters.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,30 @@ boolean canDirectlySeeMonster(creature *monst) {
return false;
}

// Centralize the logic of whether an attacker can/will attack the defender
boolean canAttack( creature *attacker, creature *defender ) {
Comment on lines +234 to +235
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should decide what this means precisely. For example, this check includes that the defender's location is known, but it's obviously possible to attack completely invisible enemies.

It probably means something more like "does the attack know it will attack defender if it moves into its cell?"

if (attacker == NULL || defender == NULL) return false;

// Must be enemies
boolean areEnemies = monsterWillAttackTarget(attacker, defender);

// Must be able to attack the cell where the defender is located
boolean canAttackCell = !cellHasTerrainFlag(defender->loc.x, defender->loc.y, T_OBSTRUCTS_PASSABILITY)
|| (defender->info.flags & MONST_ATTACKABLE_THRU_WALLS);

// Must be able to detect the defender
boolean locationKnown;
if (attacker == &player) {
locationKnown = canSeeMonster(defender) || monsterRevealed(defender);
} else {
// Monsters don't understand revealing.
locationKnown = !monsterIsHidden(defender, attacker);
}

// All three requirements must be met
return (areEnemies && canAttackCell && locationKnown);
}

void monsterName(char *buf, creature *monst, boolean includeArticle) {
short oldRNG;

Expand Down
15 changes: 5 additions & 10 deletions src/brogue/Movement.c
Original file line number Diff line number Diff line change
Expand Up @@ -586,11 +586,8 @@ boolean handleWhipAttacks(creature *attacker, enum directions dir, boolean *abor
getImpactLoc(&strikeLoc, originLoc, targetLoc, 5, false, &boltCatalog[BOLT_WHIP]);

defender = monsterAtLoc(strikeLoc);
if (defender
&& (attacker != &player || canSeeMonster(defender))
&& !monsterIsHidden(defender, attacker)
&& monsterWillAttackTarget(attacker, defender)) {

if (canAttack(attacker, defender)
&& !(defender->bookkeepingFlags & MB_SUBMERGED)) {
if (attacker == &player) {
hitList[0] = defender;
if (abortAttackAgainstAcidicTarget(hitList)) {
Expand Down Expand Up @@ -654,8 +651,7 @@ boolean handleSpearAttacks(creature *attacker, enum directions dir, boolean *abo
/* We check if i=0, i.e. the defender is right next to us, because
we have to do "normal" attacking here. We can't just return
false and leave to playerMoves/moveMonster due to the collateral hitlist. */
if (i == 0 || !monsterIsHidden(defender, attacker)
&& (attacker != &player || canSeeMonster(defender))) {
if (i == 0 || canAttack(attacker, defender)) {
// We'll attack.
proceed = true;
}
Expand Down Expand Up @@ -722,11 +718,10 @@ void buildFlailHitList(const short x, const short y, const short newX, const sho
my = monst->loc.y;
if (distanceBetween((pos){x, y}, (pos){mx, my}) == 1
&& distanceBetween((pos){newX, newY}, (pos){mx, my}) == 1
&& canSeeMonster(monst)
&& monstersAreEnemies(&player, monst)
&& canAttack(&player, monst)
&& monst->creatureState != MONSTER_ALLY
&& !(monst->bookkeepingFlags & MB_IS_DYING)
&& (!cellHasTerrainFlag(monst->loc.x, monst->loc.y, T_OBSTRUCTS_PASSABILITY) || (monst->info.flags & MONST_ATTACKABLE_THRU_WALLS))) {
&& canAttack(&player, monst)) {

while (hitList[i]) {
i++;
Expand Down
1 change: 1 addition & 0 deletions src/brogue/Rogue.h
Original file line number Diff line number Diff line change
Expand Up @@ -3170,6 +3170,7 @@ extern "C" {
boolean monsterIsHidden(const creature *monst, const creature *observer);
boolean canSeeMonster(creature *monst);
boolean canDirectlySeeMonster(creature *monst);
boolean canAttack( creature *attacker, creature *defender );
void monsterName(char *buf, creature *monst, boolean includeArticle);
boolean monsterIsInClass(const creature *monst, const short monsterClass);
boolean chooseTarget(pos *returnLoc, short maxDistance, boolean stopAtTarget, boolean autoTarget,
Expand Down