package dev.llan.model.pieces.enemies;
import dev.llan.model.board.GameMap;
import dev.llan.model.board.Grid;
import dev.llan.model.board.Point;
import dev.llan.model.board.pathfinding.Path;
import dev.llan.model.cards.Card;
import dev.llan.model.pieces.BaseEntity;
import dev.llan.model.pieces.attributes.stats.BaseStatsBuilder;
import dev.llan.model.pieces.attributes.stats.Stats;
import dev.llan.model.pieces.player.Player;
import dev.llan.model.state.combat.CombatManager;
import dev.llan.model.state.combat.actions.Action;
import dev.llan.model.state.combat.actions.CardUse;
import dev.llan.model.state.combat.actions.Move;
import dev.llan.model.state.combat.actions.Wait;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public abstract class BaseEnemy extends BaseEntity implements Enemy {
private boolean inCombat;
protected BaseEnemy(
String name, String resourcePath, Point startingPos, Grid grid, BaseStatsBuilder builder) {
super(name, resourcePath, startingPos, grid, builder);
this.inCombat = false;
}
protected BaseEnemy(
String name, String resourcePath, Point startingPos, Grid grid, Stats clonedStats) {
super(name, resourcePath, startingPos, grid, clonedStats);
this.inCombat = false;
}
@Override
public void setInCombat() {
inCombat = true;
}
@Override
public boolean inCombat() {
return inCombat;
}
@Override
public void notifyNearbyEnemies(List<Enemy> others, Set<Enemy> notified) {
List<Enemy> withinRadius = this.getNearbyEnemies(others);
for (Enemy other : withinRadius) {
if (!notified.contains(other) && !other.inCombat()) {
notified.add(other);
other.notifyNearbyEnemies(others, notified);
}
}
}
private List<Enemy> getNearbyEnemies(List<Enemy> others) {
List<Enemy> within = new ArrayList<>();
for (Enemy other : others) {
double distance = Point.getDistance(this.getPosition(), other.getPosition());
if (distance < this.getStats().getNotificationRadius()) {
within.add(other);
}
}
return within;
}
@Override
public boolean withinRadius(Point playerPos) {
double distance = Point.getDistance(playerPos, this.getPosition());
return distance < this.getStats().getScoutingRadius();
}
@Override
public List<Action> generateActions(GameMap map, Player player, CombatManager.CombatPhase phase) {
if (phase != CombatManager.CombatPhase.WAIT) {
return List.of(new Wait(this));
}
List<Action> actions = new ArrayList<>();
activePath = generateActivePath(player.getPosition());
if (activePath.isPresent()) {
actions.add(new Move(this, activePath.get()));
}
if(!hasAttacked){
actions.add(new CardUse(this, player, this.selectCard()));
}
return actions;
}
@Override
public Optional<Path> generateActivePath(Point playerPos) {
List<Point> points = playerPos.findClosestVacant(grid);
if (points.size() > 0) {
Point minPoint = points.getFirst();
double minDistance = Point.getDistance(getPosition(), minPoint);
for (int i = 1; i < points.size(); i++) {
Point currentPoint = points.get(i);
double currentDistance = Point.getDistance(getPosition(), currentPoint);
if (currentDistance < minDistance) {
minDistance = currentDistance;
minPoint = currentPoint;
}
}
return pathFinder.findPath(getPosition(), minPoint);
}
return Optional.empty();
}
protected abstract Card selectCard();
protected void cloneAttributes(BaseEnemy original) {
this.activePath = original.cloneActivePath();
this.clonedEffects = original.cloneEffects();
// NOT DEEP COPIES
this.collection = original.collection;
this.modifiers = original.modifiers;
this.effects = original.effects;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("BaseEnemy{");
sb.append("name=").append(this.getName());
sb.append(", position=").append(this.getPosition());
sb.append(", inCombat=").append(inCombat);
sb.append(", stats=").append(stats);
sb.append('}');
return sb.toString();
}
}