package com.zombie.game.model; import lombok.Getter; import java.util.*; import static com.zombie.game.model.Constants.*; /** * 子弹/投掷物类 * * 管理玩家和僵尸发射的子弹、手榴弹等投掷物。 * 支持普通子弹的直线飞行和手榴弹的抛物线轨迹。 */ @Getter public class Bullet { private int id; private float x, y; private float z; private float vx, vy, vz; private float speed; private int damage; private String ownerId; private int weaponIndex; private float range; private float distanceTraveled; private boolean explosive; private float explosionRadius; private float flightTime; private float maxFlightTime; private float targetX, targetY; private boolean isGrenade; public Bullet(int id, float x, float y, float angle, float speed, int damage, String ownerId, int weaponIndex, float range) { this.id = id; this.x = x; this.y = y; this.z = 0.5f; this.speed = speed; this.vx = (float) Math.sin(angle) * speed; this.vy = (float) Math.cos(angle) * speed; this.vz = 0; this.damage = damage; this.ownerId = ownerId; this.weaponIndex = weaponIndex; this.range = range; this.distanceTraveled = 0; this.explosive = false; this.explosionRadius = 0; this.flightTime = 0; this.maxFlightTime = 0; this.isGrenade = false; this.targetX = x + (float) Math.sin(angle) * range; this.targetY = y + (float) Math.cos(angle) * range; } /** * 构造函数 - 手榴弹 * * @param id 子弹ID * @param startX 起始X坐标 * @param startY 起始Y坐标 * @param targetX 目标X坐标 * @param targetY 目标Y坐标 * @param flightDuration 飞行时长 * @param damage 伤害值 * @param ownerId 发射者ID * @param explosionRadius 爆炸半径 */ public Bullet(int id, float startX, float startY, float targetX, float targetY, float flightDuration, int damage, String ownerId, float explosionRadius) { this.id = id; this.x = startX; this.y = startY; this.z = 0.5f; this.targetX = targetX; this.targetY = targetY; this.damage = damage; this.ownerId = ownerId; this.weaponIndex = -1; this.range = 0; this.distanceTraveled = 0; this.explosive = true; this.explosionRadius = explosionRadius; this.flightTime = 0; this.maxFlightTime = flightDuration; this.isGrenade = true; this.speed = 0; float dx = targetX - startX; float dy = targetY - startY; this.vx = dx / flightDuration; this.vy = dy / flightDuration; this.vz = 3.0f; } public boolean update(float dt, GameMap map) { if (isGrenade) { flightTime += dt; x += vx * dt; y += vy * dt; float progress = flightTime / maxFlightTime; z = 0.5f + 4.0f * (float) Math.sin(progress * Math.PI); if (flightTime >= maxFlightTime || z <= 0.5f && progress > 0.5f) { return false; } if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) { return false; } return true; } else { float moveX = vx * dt; float moveY = vy * dt; x += moveX; y += moveY; distanceTraveled += (float) Math.sqrt(moveX * moveX + moveY * moveY); if (distanceTraveled >= range) return false; if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE) return false; int gx = (int) Math.floor(x); int gy = (int) Math.floor(y); // 只有碰到静态墙壁才销毁子弹,碰到坚果墙体让碰撞检测处理 Wall wall = map.getWall(gx, gy); if (wall instanceof StaticWall) return false; return true; } } /** * 检测子弹是否命中实体 * * @param ex 实体X坐标 * @param ey 实体Y坐标 * @param size 实体碰撞体大小 * @return true 表示命中 */ public boolean hitsEntity(float ex, float ey, float size) { float dx = x - ex; float dy = y - ey; float dist = (float) Math.sqrt(dx * dx + dy * dy); return dist < size / 2 + 0.1f; } /** * 将子弹状态转换为Map格式,用于网络传输 * * @return 包含子弹状态的Map */ public Map toStateMap() { Map map = new LinkedHashMap<>(); map.put("id", id); map.put("x", x); map.put("y", y); map.put("z", z); if (isGrenade) { map.put("angle", (float) Math.atan2(vx, vy)); map.put("targetX", targetX); map.put("targetY", targetY); } else { map.put("angle", (float) Math.atan2(vx, vy)); } map.put("weaponIndex", weaponIndex); map.put("ownerId", ownerId); return map; } }