1
This commit is contained in:
61
backend/mvnw.cmd
vendored
61
backend/mvnw.cmd
vendored
@@ -1,61 +0,0 @@
|
|||||||
@REM ----------------------------------------------------------------------------
|
|
||||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
@REM or more contributor license agreements. See the NOTICE file
|
|
||||||
@REM distributed with this work for additional information
|
|
||||||
@REM regarding copyright ownership. The ASF licenses this file
|
|
||||||
@REM to you under the Apache License, Version 2.0 (the
|
|
||||||
@REM "License"); you may not use this file except in compliance
|
|
||||||
@REM with the License. You may obtain a copy of the License at
|
|
||||||
@REM
|
|
||||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
@REM
|
|
||||||
@REM Unless required by applicable law or agreed to in writing,
|
|
||||||
@REM software distributed under the License is distributed on an
|
|
||||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
@REM KIND, either express or implied. See the License for the
|
|
||||||
@REM specific language governing permissions and limitations
|
|
||||||
@REM under the License.
|
|
||||||
@REM ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@REM ----------------------------------------------------------------------------
|
|
||||||
@REM Apache Maven Wrapper startup batch script, version 3.2.0
|
|
||||||
@REM ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
|
||||||
@SET __MVNW_CMD__=
|
|
||||||
@SET __MVNW_ERROR__=
|
|
||||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
|
||||||
@SET PSModulePath=
|
|
||||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $proxy=''; [Net.WebRequest]::DefaultWebProxy.Credentials=[Net.CredentialCache]::DefaultCredentials; if('%MVNW_USERNAME%' -ne '' -and '%MVNW_PASSWORD%' -ne ''){$proxy=[Net.WebRequest]::GetSystemWebProxy(); $proxy.Credentials=[Net.NetworkCredential]::new('%MVNW_USERNAME%','%MVNW_PASSWORD%');} try{Invoke-WebRequest -Uri ('https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar') -Proxy $proxy -ProxyUseDefaultCredentials -OutFile ('%__MVNW_JAR__%')}catch{Write-Error $_}}" 2>&1`) DO @(
|
|
||||||
IF "%%A"=="MVNW_ERROR" (
|
|
||||||
SET __MVNW_ERROR__=%%B
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
|
||||||
|
|
||||||
@SET MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
|
||||||
@IF NOT "%MAVEN_PROJECTBASEDIR%"=="" GOTO endDetectBaseDir
|
|
||||||
@SET MAVEN_PROJECTBASEDIR=%~dp0..
|
|
||||||
:endDetectBaseDir
|
|
||||||
|
|
||||||
@IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" (
|
|
||||||
powershell -noprofile "& {Invoke-WebRequest -Uri 'https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar' -OutFile '%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@SET MAVEN_CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
@REM Find Java
|
|
||||||
@SET JAVA_EXE=java.exe
|
|
||||||
@IF DEFINED JAVA_HOME (
|
|
||||||
@SET JAVA_HOME=%JAVA_HOME:"=%
|
|
||||||
@SET JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|
||||||
)
|
|
||||||
|
|
||||||
@REM Execute Maven wrapper
|
|
||||||
"%JAVA_EXE%" ^
|
|
||||||
%JVM_CONFIG_MAVEN_PROPS% ^
|
|
||||||
%MAVEN_OPTS% ^
|
|
||||||
%MAVEN_DEBUG_OPTS% ^
|
|
||||||
-classpath "%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" ^
|
|
||||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
|
|
||||||
org.apache.maven.wrapper.MavenWrapperMain %MAVEN_CMD_LINE_ARGS%
|
|
||||||
@@ -6,11 +6,19 @@ public class GameMap {
|
|||||||
private final int[][] cells;
|
private final int[][] cells;
|
||||||
private final int width;
|
private final int width;
|
||||||
private final int height;
|
private final int height;
|
||||||
|
private float[][] distanceField;
|
||||||
|
private float[][] flowFieldX;
|
||||||
|
private float[][] flowFieldY;
|
||||||
|
private boolean flowFieldValid;
|
||||||
|
|
||||||
public GameMap() {
|
public GameMap() {
|
||||||
this.width = Constants.GRID_SIZE;
|
this.width = Constants.GRID_SIZE;
|
||||||
this.height = Constants.GRID_SIZE;
|
this.height = Constants.GRID_SIZE;
|
||||||
this.cells = new int[height][width];
|
this.cells = new int[height][width];
|
||||||
|
this.distanceField = new float[height][width];
|
||||||
|
this.flowFieldX = new float[height][width];
|
||||||
|
this.flowFieldY = new float[height][width];
|
||||||
|
this.flowFieldValid = false;
|
||||||
generateDefaultMap();
|
generateDefaultMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +140,121 @@ public class GameMap {
|
|||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateFlowField(List<float[]> playerPositions) {
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
distanceField[y][x] = Float.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Queue<int[]> queue = new LinkedList<>();
|
||||||
|
for (float[] pos : playerPositions) {
|
||||||
|
int px = (int) Math.floor(pos[0]);
|
||||||
|
int py = (int) Math.floor(pos[1]);
|
||||||
|
if (px >= 0 && px < width && py >= 0 && py < height && !isWall(px, py)) {
|
||||||
|
distanceField[py][px] = 0;
|
||||||
|
queue.add(new int[]{px, py});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int[][] dirs = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
int[] current = queue.poll();
|
||||||
|
int cx = current[0];
|
||||||
|
int cy = current[1];
|
||||||
|
float currentDist = distanceField[cy][cx];
|
||||||
|
|
||||||
|
for (int[] dir : dirs) {
|
||||||
|
int nx = cx + dir[0];
|
||||||
|
int ny = cy + dir[1];
|
||||||
|
|
||||||
|
if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue;
|
||||||
|
if (isWall(nx, ny)) continue;
|
||||||
|
|
||||||
|
if (dir[0] != 0 && dir[1] != 0) {
|
||||||
|
if (isWall(cx + dir[0], cy) || isWall(cx, cy + dir[1])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float moveCost = (dir[0] != 0 && dir[1] != 0) ? 1.414f : 1.0f;
|
||||||
|
float newDist = currentDist + moveCost;
|
||||||
|
|
||||||
|
if (newDist < distanceField[ny][nx]) {
|
||||||
|
distanceField[ny][nx] = newDist;
|
||||||
|
queue.add(new int[]{nx, ny});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
if (isWall(x, y) || distanceField[y][x] == Float.MAX_VALUE) {
|
||||||
|
flowFieldX[y][x] = 0;
|
||||||
|
flowFieldY[y][x] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float bestDist = distanceField[y][x];
|
||||||
|
float bestDirX = 0;
|
||||||
|
float bestDirY = 0;
|
||||||
|
|
||||||
|
for (int[] dir : dirs) {
|
||||||
|
int nx = x + dir[0];
|
||||||
|
int ny = y + dir[1];
|
||||||
|
|
||||||
|
if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue;
|
||||||
|
if (isWall(nx, ny)) continue;
|
||||||
|
|
||||||
|
if (dir[0] != 0 && dir[1] != 0) {
|
||||||
|
if (isWall(x + dir[0], y) || isWall(x, y + dir[1])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceField[ny][nx] < bestDist) {
|
||||||
|
bestDist = distanceField[ny][nx];
|
||||||
|
float len = (float) Math.sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
|
||||||
|
bestDirX = dir[0] / len;
|
||||||
|
bestDirY = dir[1] / len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flowFieldX[y][x] = bestDirX;
|
||||||
|
flowFieldY[y][x] = bestDirY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flowFieldValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getFlowDirection(float wx, float wy) {
|
||||||
|
int gx = (int) Math.floor(wx);
|
||||||
|
int gy = (int) Math.floor(wy);
|
||||||
|
|
||||||
|
if (gx < 0 || gx >= width || gy < 0 || gy >= height) {
|
||||||
|
return new float[]{0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new float[]{flowFieldX[gy][gx], flowFieldY[gy][gx]};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFlowFieldValid() {
|
||||||
|
return flowFieldValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDistance(float wx, float wy) {
|
||||||
|
int gx = (int) Math.floor(wx);
|
||||||
|
int gy = (int) Math.floor(wy);
|
||||||
|
|
||||||
|
if (gx < 0 || gx >= width || gy < 0 || gy >= height) {
|
||||||
|
return Float.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return distanceField[gy][gx];
|
||||||
|
}
|
||||||
|
|
||||||
public List<float[]> findPath(float startX, float startY, float endX, float endY) {
|
public List<float[]> findPath(float startX, float startY, float endX, float endY) {
|
||||||
int sgx = (int) Math.floor(startX);
|
int sgx = (int) Math.floor(startX);
|
||||||
int sgy = (int) Math.floor(startY);
|
int sgy = (int) Math.floor(startY);
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ public class GameWorld {
|
|||||||
spawnTimer += dt;
|
spawnTimer += dt;
|
||||||
difficultyTimer += dt;
|
difficultyTimer += dt;
|
||||||
|
|
||||||
|
updateFlowField();
|
||||||
|
|
||||||
if (difficultyTimer >= Constants.DIFFICULTY_INCREASE_INTERVAL) {
|
if (difficultyTimer >= Constants.DIFFICULTY_INCREASE_INTERVAL) {
|
||||||
difficultyTimer -= Constants.DIFFICULTY_INCREASE_INTERVAL;
|
difficultyTimer -= Constants.DIFFICULTY_INCREASE_INTERVAL;
|
||||||
waveNumber++;
|
waveNumber++;
|
||||||
@@ -103,15 +105,24 @@ public class GameWorld {
|
|||||||
checkLootCollection();
|
checkLootCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFlowField() {
|
||||||
|
List<float[]> playerPositions = new ArrayList<>();
|
||||||
|
for (Player p : players.values()) {
|
||||||
|
if (p.isAlive()) {
|
||||||
|
playerPositions.add(new float[]{p.getX(), p.getY()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!playerPositions.isEmpty()) {
|
||||||
|
map.updateFlowField(playerPositions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void spawnZombie() {
|
private void spawnZombie() {
|
||||||
List<int[]> spawnPoints = map.getZombieSpawnPoints();
|
List<int[]> spawnPoints = map.getZombieSpawnPoints();
|
||||||
int[] sp = spawnPoints.get(random.nextInt(spawnPoints.size()));
|
int[] sp = spawnPoints.get(random.nextInt(spawnPoints.size()));
|
||||||
float wx = sp[0] + 0.5f;
|
float wx = sp[0] + 0.5f;
|
||||||
float wy = sp[1] + 0.5f;
|
float wy = sp[1] + 0.5f;
|
||||||
|
|
||||||
Player nearest = findNearestPlayer(wx, wy);
|
|
||||||
if (nearest == null) return;
|
|
||||||
|
|
||||||
boolean isElite = random.nextFloat() < Constants.ELITE_ZOMBIE_SPAWN_CHANCE;
|
boolean isElite = random.nextFloat() < Constants.ELITE_ZOMBIE_SPAWN_CHANCE;
|
||||||
Zombie zombie;
|
Zombie zombie;
|
||||||
if (isElite) {
|
if (isElite) {
|
||||||
@@ -119,7 +130,6 @@ public class GameWorld {
|
|||||||
} else {
|
} else {
|
||||||
zombie = new Zombie(nextZombieId++, wx, wy, zombieHealth, zombieSpeed, false);
|
zombie = new Zombie(nextZombieId++, wx, wy, zombieHealth, zombieSpeed, false);
|
||||||
}
|
}
|
||||||
zombie.updatePath(map, nearest.getX(), nearest.getY());
|
|
||||||
zombies.put(zombie.getId(), zombie);
|
zombies.put(zombie.getId(), zombie);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,21 +149,20 @@ public class GameWorld {
|
|||||||
|
|
||||||
private void updateZombies(float dt) {
|
private void updateZombies(float dt) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
for (Zombie z : new ArrayList<>(zombies.values())) {
|
|
||||||
|
List<Zombie> sortedZombies = new ArrayList<>(zombies.values());
|
||||||
|
sortedZombies.sort((a, b) -> Integer.compare(a.getId(), b.getId()));
|
||||||
|
|
||||||
|
for (Zombie z : sortedZombies) {
|
||||||
if (!z.isAlive()) {
|
if (!z.isAlive()) {
|
||||||
onZombieKilled(z);
|
onZombieKilled(z);
|
||||||
zombies.remove(z.getId());
|
zombies.remove(z.getId());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player nearest = findNearestPlayer(z.getX(), z.getY());
|
if (z.isElite()) {
|
||||||
if (nearest != null) {
|
Player nearest = findNearestPlayer(z.getX(), z.getY());
|
||||||
if (z.getPath() == null || z.getPathIndex() >= (z.getPath() != null ? z.getPath().size() : 0)
|
if (nearest != null) {
|
||||||
|| random.nextFloat() < 0.02f) {
|
|
||||||
z.updatePath(map, nearest.getX(), nearest.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (z.isElite()) {
|
|
||||||
float dist = z.distanceTo(nearest.getX(), nearest.getY());
|
float dist = z.distanceTo(nearest.getX(), nearest.getY());
|
||||||
if (dist <= Constants.ELITE_ZOMBIE_ATTACK_RANGE && z.canRangedAttack(now)) {
|
if (dist <= Constants.ELITE_ZOMBIE_ATTACK_RANGE && z.canRangedAttack(now)) {
|
||||||
fireZombieBullet(z, nearest);
|
fireZombieBullet(z, nearest);
|
||||||
@@ -162,7 +171,7 @@ public class GameWorld {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
z.move(map, dt);
|
z.move(map, dt, zombies.values());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ public class Zombie {
|
|||||||
private float maxHealth;
|
private float maxHealth;
|
||||||
private float speed;
|
private float speed;
|
||||||
private long lastAttackTime;
|
private long lastAttackTime;
|
||||||
private List<float[]> path;
|
|
||||||
private int pathIndex;
|
|
||||||
private float targetX, targetY;
|
|
||||||
private boolean isElite;
|
private boolean isElite;
|
||||||
private long lastRangedAttackTime;
|
private long lastRangedAttackTime;
|
||||||
|
private float targetX, targetY;
|
||||||
|
private boolean hasTarget;
|
||||||
|
private int reservedGridX, reservedGridY;
|
||||||
|
private boolean hasReservation;
|
||||||
|
|
||||||
public Zombie(int id, float x, float y, float health, float speed) {
|
public Zombie(int id, float x, float y, float health, float speed) {
|
||||||
this(id, x, y, health, speed, false);
|
this(id, x, y, health, speed, false);
|
||||||
@@ -29,10 +30,14 @@ public class Zombie {
|
|||||||
this.maxHealth = health;
|
this.maxHealth = health;
|
||||||
this.speed = speed;
|
this.speed = speed;
|
||||||
this.lastAttackTime = 0;
|
this.lastAttackTime = 0;
|
||||||
this.path = null;
|
|
||||||
this.pathIndex = 0;
|
|
||||||
this.isElite = isElite;
|
this.isElite = isElite;
|
||||||
this.lastRangedAttackTime = 0;
|
this.lastRangedAttackTime = 0;
|
||||||
|
this.targetX = 0;
|
||||||
|
this.targetY = 0;
|
||||||
|
this.hasTarget = false;
|
||||||
|
this.reservedGridX = -1;
|
||||||
|
this.reservedGridY = -1;
|
||||||
|
this.hasReservation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() { return id; }
|
public int getId() { return id; }
|
||||||
@@ -41,9 +46,10 @@ public class Zombie {
|
|||||||
public float getAngle() { return angle; }
|
public float getAngle() { return angle; }
|
||||||
public float getHealth() { return health; }
|
public float getHealth() { return health; }
|
||||||
public float getMaxHealth() { return maxHealth; }
|
public float getMaxHealth() { return maxHealth; }
|
||||||
public List<float[]> getPath() { return path; }
|
|
||||||
public int getPathIndex() { return pathIndex; }
|
|
||||||
public boolean isElite() { return isElite; }
|
public boolean isElite() { return isElite; }
|
||||||
|
public int getReservedGridX() { return reservedGridX; }
|
||||||
|
public int getReservedGridY() { return reservedGridY; }
|
||||||
|
public boolean hasReservation() { return hasReservation; }
|
||||||
|
|
||||||
public void takeDamage(float damage) {
|
public void takeDamage(float damage) {
|
||||||
this.health -= damage;
|
this.health -= damage;
|
||||||
@@ -54,43 +60,218 @@ public class Zombie {
|
|||||||
return health > 0;
|
return health > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePath(GameMap map, float targetX, float targetY) {
|
public void move(GameMap map, float dt, Collection<Zombie> otherZombies) {
|
||||||
this.targetX = targetX;
|
if (!map.isFlowFieldValid()) return;
|
||||||
this.targetY = targetY;
|
|
||||||
this.path = map.findPath(x, y, targetX, targetY);
|
int currentGridX = (int) Math.floor(x);
|
||||||
this.pathIndex = 0;
|
int currentGridY = (int) Math.floor(y);
|
||||||
|
|
||||||
|
float centerDist = Float.MAX_VALUE;
|
||||||
|
if (hasTarget) {
|
||||||
|
float dx = targetX - x;
|
||||||
|
float dy = targetY - y;
|
||||||
|
centerDist = (float) Math.sqrt(dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTarget || centerDist < 0.15f) {
|
||||||
|
float[] flowDir = map.getFlowDirection(x, y);
|
||||||
|
float dirX = flowDir[0];
|
||||||
|
float dirY = flowDir[1];
|
||||||
|
|
||||||
|
if (dirX == 0 && dirY == 0) return;
|
||||||
|
|
||||||
|
float len = (float) Math.sqrt(dirX * dirX + dirY * dirY);
|
||||||
|
if (len > 0) {
|
||||||
|
dirX /= len;
|
||||||
|
dirY /= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nextGridX = currentGridX + (int) Math.round(dirX);
|
||||||
|
int nextGridY = currentGridY + (int) Math.round(dirY);
|
||||||
|
|
||||||
|
if (map.isWall(nextGridX, nextGridY)) {
|
||||||
|
nextGridX = currentGridX + (int) Math.signum(dirX);
|
||||||
|
nextGridY = currentGridY + (int) Math.signum(dirY);
|
||||||
|
|
||||||
|
if (map.isWall(nextGridX, nextGridY)) {
|
||||||
|
if (!map.isWall(currentGridX + (int) Math.signum(dirX), currentGridY)) {
|
||||||
|
nextGridX = currentGridX + (int) Math.signum(dirX);
|
||||||
|
nextGridY = currentGridY;
|
||||||
|
} else if (!map.isWall(currentGridX, currentGridY + (int) Math.signum(dirY))) {
|
||||||
|
nextGridX = currentGridX;
|
||||||
|
nextGridY = currentGridY + (int) Math.signum(dirY);
|
||||||
|
} else {
|
||||||
|
hasReservation = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGridOccupiedOrReserved(nextGridX, nextGridY, otherZombies)) {
|
||||||
|
int[] altDirs = findAlternativeDirection(currentGridX, currentGridY, dirX, dirY, map, otherZombies);
|
||||||
|
if (altDirs != null) {
|
||||||
|
nextGridX = altDirs[0];
|
||||||
|
nextGridY = altDirs[1];
|
||||||
|
} else {
|
||||||
|
hasReservation = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reservedGridX = nextGridX;
|
||||||
|
reservedGridY = nextGridY;
|
||||||
|
hasReservation = true;
|
||||||
|
|
||||||
|
targetX = nextGridX + 0.5f;
|
||||||
|
targetY = nextGridY + 0.5f;
|
||||||
|
hasTarget = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dx = targetX - x;
|
||||||
|
float dy = targetY - y;
|
||||||
|
float dist = (float) Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
if (dist < 0.01f) {
|
||||||
|
hasTarget = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float dirX = dx / dist;
|
||||||
|
float dirY = dy / dist;
|
||||||
|
|
||||||
|
float moveX = dirX * speed * dt;
|
||||||
|
float moveY = dirY * speed * dt;
|
||||||
|
|
||||||
|
float newX = x + moveX;
|
||||||
|
float newY = y + moveY;
|
||||||
|
|
||||||
|
boolean canMoveX = map.isWalkable(newX, y, Constants.ZOMBIE_SIZE);
|
||||||
|
boolean canMoveY = map.isWalkable(x, newY, Constants.ZOMBIE_SIZE);
|
||||||
|
boolean canMoveDiagonal = map.isWalkable(newX, newY, Constants.ZOMBIE_SIZE);
|
||||||
|
|
||||||
|
if (moveX != 0 && moveY != 0) {
|
||||||
|
int checkX = (int) Math.floor(newX);
|
||||||
|
int checkY = (int) Math.floor(newY);
|
||||||
|
int checkCurrentX = (int) Math.floor(x);
|
||||||
|
int checkCurrentY = (int) Math.floor(y);
|
||||||
|
|
||||||
|
boolean blockedByCorner = false;
|
||||||
|
if (checkX != checkCurrentX && checkY != checkCurrentY) {
|
||||||
|
boolean wallInX = map.isWall(checkX, checkCurrentY);
|
||||||
|
boolean wallInY = map.isWall(checkCurrentX, checkY);
|
||||||
|
|
||||||
|
if (wallInX || wallInY) {
|
||||||
|
blockedByCorner = true;
|
||||||
|
|
||||||
|
if (!wallInX && canMoveX) {
|
||||||
|
x = newX;
|
||||||
|
canMoveY = map.isWalkable(x, newY, Constants.ZOMBIE_SIZE);
|
||||||
|
} else if (!wallInY && canMoveY) {
|
||||||
|
y = newY;
|
||||||
|
canMoveX = map.isWalkable(newX, y, Constants.ZOMBIE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blockedByCorner && canMoveDiagonal) {
|
||||||
|
x = newX;
|
||||||
|
y = newY;
|
||||||
|
} else if (!blockedByCorner) {
|
||||||
|
if (canMoveX) x = newX;
|
||||||
|
if (canMoveY) y = newY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (canMoveX) x = newX;
|
||||||
|
if (canMoveY) y = newY;
|
||||||
|
}
|
||||||
|
|
||||||
|
float minSeparationDist = Constants.ZOMBIE_SIZE;
|
||||||
|
for (Zombie other : otherZombies) {
|
||||||
|
if (other.getId() == this.id) continue;
|
||||||
|
if (!other.isAlive()) continue;
|
||||||
|
|
||||||
|
float ox = other.getX();
|
||||||
|
float oy = other.getY();
|
||||||
|
float sepDx = x - ox;
|
||||||
|
float sepDy = y - oy;
|
||||||
|
float sepDist = (float) Math.sqrt(sepDx * sepDx + sepDy * sepDy);
|
||||||
|
|
||||||
|
if (sepDist < minSeparationDist && sepDist > 0.01f) {
|
||||||
|
float overlap = minSeparationDist - sepDist;
|
||||||
|
float pushX = (sepDx / sepDist) * overlap * 0.5f;
|
||||||
|
float pushY = (sepDy / sepDist) * overlap * 0.5f;
|
||||||
|
|
||||||
|
float pushedX = x + pushX;
|
||||||
|
float pushedY = y + pushY;
|
||||||
|
|
||||||
|
if (map.isWalkable(pushedX, pushedY, Constants.ZOMBIE_SIZE)) {
|
||||||
|
x = pushedX;
|
||||||
|
y = pushedY;
|
||||||
|
} else {
|
||||||
|
if (map.isWalkable(x + pushX, y, Constants.ZOMBIE_SIZE)) {
|
||||||
|
x = x + pushX;
|
||||||
|
}
|
||||||
|
if (map.isWalkable(x, y + pushY, Constants.ZOMBIE_SIZE)) {
|
||||||
|
y = y + pushY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirX != 0 || dirY != 0) {
|
||||||
|
angle = (float) Math.atan2(dirX, dirY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(GameMap map, float dt) {
|
private boolean isGridOccupiedOrReserved(int gridX, int gridY, Collection<Zombie> otherZombies) {
|
||||||
if (path != null && pathIndex < path.size()) {
|
for (Zombie other : otherZombies) {
|
||||||
float[] target = path.get(pathIndex);
|
if (other.getId() == this.id) continue;
|
||||||
float dx = target[0] - x;
|
if (!other.isAlive()) continue;
|
||||||
float dy = target[1] - y;
|
|
||||||
float dist = (float) Math.sqrt(dx * dx + dy * dy);
|
|
||||||
|
|
||||||
if (dist < 0.2f) {
|
int otherGridX = (int) Math.floor(other.getX());
|
||||||
pathIndex++;
|
int otherGridY = (int) Math.floor(other.getY());
|
||||||
if (pathIndex >= path.size()) {
|
|
||||||
path = null;
|
if (otherGridX == gridX && otherGridY == gridY) {
|
||||||
}
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float moveX = (dx / dist) * speed * dt;
|
if (other.hasReservation() && other.getReservedGridX() == gridX && other.getReservedGridY() == gridY) {
|
||||||
float moveY = (dy / dist) * speed * dt;
|
return true;
|
||||||
|
|
||||||
float newX = x + moveX;
|
|
||||||
float newY = y + moveY;
|
|
||||||
|
|
||||||
if (map.isWalkable(newX, y, Constants.ZOMBIE_SIZE)) {
|
|
||||||
x = newX;
|
|
||||||
}
|
}
|
||||||
if (map.isWalkable(x, newY, Constants.ZOMBIE_SIZE)) {
|
|
||||||
y = newY;
|
|
||||||
}
|
|
||||||
|
|
||||||
angle = (float) Math.atan2(dx, dy);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] findAlternativeDirection(int currentGridX, int currentGridY, float dirX, float dirY,
|
||||||
|
GameMap map, Collection<Zombie> otherZombies) {
|
||||||
|
int[][] allDirs = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {1, -1}, {-1, 1}, {1, 1}};
|
||||||
|
|
||||||
|
java.util.List<int[]> candidates = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
for (int[] dir : allDirs) {
|
||||||
|
int nx = currentGridX + dir[0];
|
||||||
|
int ny = currentGridY + dir[1];
|
||||||
|
|
||||||
|
if (map.isWall(nx, ny)) continue;
|
||||||
|
|
||||||
|
if (dir[0] != 0 && dir[1] != 0) {
|
||||||
|
if (map.isWall(currentGridX + dir[0], currentGridY) ||
|
||||||
|
map.isWall(currentGridX, currentGridY + dir[1])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGridOccupiedOrReserved(nx, ny, otherZombies)) continue;
|
||||||
|
|
||||||
|
float dotProduct = dir[0] * dirX + dir[1] * dirY;
|
||||||
|
candidates.add(new int[]{nx, ny, (int) (dotProduct * 1000)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.isEmpty()) return null;
|
||||||
|
|
||||||
|
candidates.sort((a, b) -> b[2] - a[2]);
|
||||||
|
|
||||||
|
return new int[]{candidates.get(0)[0], candidates.get(0)[1]};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canAttack(long now) {
|
public boolean canAttack(long now) {
|
||||||
|
|||||||
Reference in New Issue
Block a user