package lect78_impl.model; import lect78_impl.view.Sprite; /** * @overview A projectile that can be fired by tank unit in the game * * @attributes * damage Integer Amount of damage dealt * angle Integer Firing angle * source Tank Tank that fired this projectile * position Position Current position * active Boolean Whether the projectile is active */ public abstract class Projectile { protected int damage; protected int angle; protected Tank source; protected Position position; protected boolean active; // Movement properties protected double exactX, exactY; // Exact position for smooth movement // Constants protected static final double MOVEMENT_SPEED = 0.25; // Grid cells per frame protected static final int GRID_SIZE = 8; // Match Board.GRID_SIZE /** * @effects Initialize this as a Projectile with given parameters */ public Projectile(int damage, int angle, Tank source) { this.damage = damage; this.angle = angle; this.source = source; this.active = true; // Initialize position from source tank if (source != null && source.getPosition() != null) { this.position = new Position(source.getPosition().getX(), source.getPosition().getY()); this.exactX = this.position.getX(); this.exactY = this.position.getY(); } else { this.position = new Position(0, 0); this.exactX = 0; this.exactY = 0; } } /** * @effects Updates the projectile position */ public void update() { if (!active) return; // Calculate new position based on angle double rad = Math.toRadians(angle); double dx = Math.cos(rad); double dy = Math.sin(rad); // Update exact position exactX += dx * MOVEMENT_SPEED; exactY += dy * MOVEMENT_SPEED; // Update position object with rounded coordinates position.setPosition((int)Math.round(exactX), (int)Math.round(exactY)); // Check if projectile has traveled beyond the source tank's range if (source != null) { double distanceTraveled = position.getDistance(source.getPosition()); if (distanceTraveled > source.getRange()) { active = false; } } // Check if projectile is off the board (assuming 8x8 board) if (exactX < 0 || exactY < 0 || exactX >= GRID_SIZE || exactY >= GRID_SIZE) { active = false; } } /** * @effects Checks if this projectile collides with a tank */ public boolean checkCollision(Tank target) { if (!active || target == source || target.isDestroyed()) return false; // Simple position-based collision return position.equals(target.getPosition()); } /** * @effects Apply damage to the target tank based on projectile type */ public abstract void applyDamage(Tank target); /** * @effects Set the projectile's position */ public void setPosition(Position position) { this.position = position; if (position != null) { this.exactX = position.getX(); this.exactY = position.getY(); } } /** * @effects Get the projectile's position */ public Position getPosition() { return position; } /** * @effects Deactivate this projectile */ public void deactivate() { active = false; } /** * @effects Checks if this projectile is active */ public boolean isActive() { return active; } /** * @effects Gets the source tank of this projectile */ public Tank getSource() { return source; } /** * @effects Gets the damage of this projectile */ public int getDamage() { return damage; } /** * @effects Gets the angle of this projectile */ public int getAngle() { return angle; } }