Can now open secret walls and pick up machine gun

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-14 15:34:27 +01:00
parent 9f5f29100b
commit 001c7c3131
13 changed files with 545 additions and 120 deletions

View File

@@ -14,21 +14,22 @@ abstract class Enemy extends Entity {
super.lastActionTime,
});
// Standard guard health
int health = 25;
int damage = 10;
bool isDying = false;
bool hasDroppedItem = false;
// Replaces ob->temp2 for reaction delays
int reactionTimeMs = 0;
void takeDamage(int amount, int currentTime) {
if (state == EntityState.dead) return;
health -= amount;
// Mark the start of the death/pain
lastActionTime = currentTime;
if (health <= 0) {
state = EntityState.dead;
// This triggers the dying animation
isDying = true;
} else if (math.Random().nextDouble() < 0.5) {
state = EntityState.pain;
@@ -37,12 +38,9 @@ abstract class Enemy extends Entity {
}
}
// Decodes the Map ID to figure out which way the enemy is facing
static double getInitialAngle(int objId) {
int normalizedId = (objId - 108) % 36;
// 0=East, 1=North, 2=West, 3=South
int direction = normalizedId % 4;
switch (direction) {
case 0:
return 0.0;
@@ -57,14 +55,19 @@ abstract class Enemy extends Entity {
}
}
// The enemy can now check its own line of sight!
// Matches WL_STATE.C's 'CheckLine' using canonical Integer DDA traversal
bool hasLineOfSight(
Coordinate2D playerPosition,
bool Function(int x, int y) isWalkable,
) {
double distance = position.distanceTo(playerPosition);
// 1. Proximity Check (Matches WL_STATE.C 'MINSIGHT')
// If the player is very close, sight is automatic regardless of facing angle.
// This compensates for our lack of a noise/gunshot alert system!
if (position.distanceTo(playerPosition) < 2.0) {
return true;
}
// 1. FOV Check
// 2. FOV Check (Matches original sight angles)
double angleToPlayer = position.angleTo(playerPosition);
double diff = angle - angleToPlayer;
@@ -77,40 +80,92 @@ abstract class Enemy extends Entity {
if (diff.abs() > math.pi / 2) return false;
// 2. Map Check
Coordinate2D dir = (playerPosition - position).normalized;
double stepSize = 0.2;
// 3. Map Check (Corrected Integer Bresenham)
int currentX = position.x.toInt();
int currentY = position.y.toInt();
int targetX = playerPosition.x.toInt();
int targetY = playerPosition.y.toInt();
for (double i = 0; i < distance; i += stepSize) {
Coordinate2D checkPos = position + (dir * i);
if (!isWalkable(checkPos.x.toInt(), checkPos.y.toInt())) return false;
int dx = (targetX - currentX).abs();
int dy = -(targetY - currentY).abs();
int sx = currentX < targetX ? 1 : -1;
int sy = currentY < targetY ? 1 : -1;
int err = dx + dy;
while (true) {
if (!isWalkable(currentX, currentY)) return false;
if (currentX == targetX && currentY == targetY) break;
int e2 = 2 * err;
if (e2 >= dy) {
err += dy;
currentX += sx;
}
if (e2 <= dx) {
err += dx;
currentY += sy;
}
}
return true;
}
// The weapon asks the enemy if it is unobstructed from the shooter
bool hasLineOfSightFrom(
Coordinate2D source,
double sourceAngle,
double distance,
Coordinate2D getValidMovement(
Coordinate2D intendedMovement,
bool Function(int x, int y) isWalkable,
void Function(int x, int y) tryOpenDoor,
) {
double dirX = math.cos(sourceAngle);
double dirY = math.sin(sourceAngle);
double newX = position.x + intendedMovement.x;
double newY = position.y + intendedMovement.y;
for (double i = 0.5; i < distance; i += 0.2) {
int checkX = (source.x + dirX * i).toInt();
int checkY = (source.y + dirY * i).toInt();
if (!isWalkable(checkX, checkY)) return false;
int currentTileX = position.x.toInt();
int currentTileY = position.y.toInt();
int targetTileX = newX.toInt();
int targetTileY = newY.toInt();
bool movedX = currentTileX != targetTileX;
bool movedY = currentTileY != targetTileY;
// 1. Check Diagonal Movement
if (movedX && movedY) {
bool canMoveX = isWalkable(targetTileX, currentTileY);
bool canMoveY = isWalkable(currentTileX, targetTileY);
bool canMoveDiag = isWalkable(targetTileX, targetTileY);
if (!canMoveX || !canMoveY || !canMoveDiag) {
// Trigger doors if they are blocking the path
if (!canMoveX) tryOpenDoor(targetTileX, currentTileY);
if (!canMoveY) tryOpenDoor(currentTileX, targetTileY);
if (!canMoveDiag) tryOpenDoor(targetTileX, targetTileY);
if (canMoveX) return Coordinate2D(intendedMovement.x, 0);
if (canMoveY) return Coordinate2D(0, intendedMovement.y);
return const Coordinate2D(0, 0);
}
}
return true;
// 2. Check Cardinal Movement
if (movedX && !movedY) {
if (!isWalkable(targetTileX, currentTileY)) {
tryOpenDoor(targetTileX, currentTileY); // Try to open!
return Coordinate2D(0, intendedMovement.y);
}
}
if (movedY && !movedX) {
if (!isWalkable(currentTileX, targetTileY)) {
tryOpenDoor(currentTileX, targetTileY); // Try to open!
return Coordinate2D(intendedMovement.x, 0);
}
}
return intendedMovement;
}
// Updated Signature
({Coordinate2D movement, double newAngle}) update({
required int elapsedMs,
required Coordinate2D playerPosition,
required bool Function(int x, int y) isWalkable,
required void Function(int x, int y) tryOpenDoor, // NEW
required void Function(int damage) onDamagePlayer,
});
}