diff --git a/changes/issue-516.md b/changes/issue-516.md new file mode 100644 index 00000000..ba8915fd --- /dev/null +++ b/changes/issue-516.md @@ -0,0 +1,2 @@ +- + The whip, spear, and rapier now work against invisible enemies when you are telepathic. diff --git a/src/brogue/Items.c b/src/brogue/Items.c index 07fc63a1..e657ebc9 100644 --- a/src/brogue/Items.c +++ b/src/brogue/Items.c @@ -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 && @@ -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; ibookkeepingFlags & MB_SUBMERGED)) { // Imaginary bolt hit the player or a monster. break; diff --git a/src/brogue/Monsters.c b/src/brogue/Monsters.c index ed9d35e6..fe6f6027 100644 --- a/src/brogue/Monsters.c +++ b/src/brogue/Monsters.c @@ -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 ) { + 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; diff --git a/src/brogue/Movement.c b/src/brogue/Movement.c index f0033568..e3e54db1 100644 --- a/src/brogue/Movement.c +++ b/src/brogue/Movement.c @@ -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)) { @@ -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; } @@ -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++; diff --git a/src/brogue/Rogue.h b/src/brogue/Rogue.h index de02c8fe..24f4cb9b 100644 --- a/src/brogue/Rogue.h +++ b/src/brogue/Rogue.h @@ -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,