diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java index f4ca4a1..c79d837 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java @@ -26,17 +26,14 @@ import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; import org.bukkit.util.Vector; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Random; +import java.util.*; +import java.util.function.Function; public class LixfelAI extends AI { private Random random; private LixfelPathplanner pathplanner; - private List cannons; - private List shootingList; + private Action action; public LixfelAI(FightTeam team, String user) { super(team, SteamwarUser.get(user)); @@ -45,8 +42,6 @@ public class LixfelAI extends AI { @Override public SchematicNode chooseSchematic() { random = new Random(); - cannons = new ArrayList<>(); - shootingList = new ArrayList<>(); List publics = SchematicNode.getAllSchematicsOfType(0, Config.SchematicType.toDB()); SchematicNode schem = publics.get(random.nextInt(publics.size())); @@ -54,47 +49,194 @@ public class LixfelAI extends AI { pathplanner = new LixfelPathplanner(schem); - cannons.add(new Cannon(new Vector(11,25,11), 10,23,14, 12,23,14, 10,23,12, 12,23,12, 12,24,15, 10,24,14, 12,23,15, 12,24,14, 11,24,12, 10,24,15, 12,24,12, 10,23,15, 10,24,12)); - cannons.add(new Cannon(new Vector(39,25,11), 38,24,15, 38,23,12, 40,23,14, 40,24,12, 40,23,15, 40,24,14, 39,24,12, 38,23,14, 40,24,15, 40,23,12, 38,23,15, 38,24,12, 38,24,14)); - cannons.add(new Cannon(new Vector(12,18,11), 13,17,15, 13,17,16, 13,18,16, 13,18,14, 14,18,14, 14,17,15, 14,17,16, 14,18,16, 13,17,14, 14,17,14, 13,18,15, 14,18,15)); - cannons.add(new Cannon(new Vector(38,18,11), 37,18,14, 36,18,14, 36,17,15, 37,17,15, 37,18,15, 36,18,15, 36,17,14, 37,17,14, 36,17,16, 37,17,16, 36,18,16, 37,18,16)); - cannons.add(new Cannon(new Vector(8,9,16), 10,11,17, 10,8,19, 10,8,17, 11,8,19, 11,8,15, 10,8,15, 12,8,15, 10,10,15, 12,7,19, 11,10,15, 12,8,19, 12,7,15, 11,7,15, 10,9,17, 11,9,19, 12,9,19, 10,9,19, 10,7,17, 11,7,19, 10,7,15, 10,7,19, 10,10,17, 12,9,15, 10,9,15, 11,9,15, 12,10,19, 11,10,19, 12,10,15, 10,6,17, 10,10,19)); - //cannons.add(new Cannon(null, 23,5,9, 23,6,9, 23,7,9, 23,8,9)); - //cannons.add(new Cannon(null, 27,5,9, 27,6,9, 27,7,9, 27,8,9)); - cannons.add(new Cannon(null, 23,5,9, 23,6,9, 23,7,9, 23,8,9, 27,5,9, 27,6,9, 27,7,9, 27,8,9, 23,5,9, 23,6,9, 23,7,9, 23,8,9, 27,5,9, 27,6,9, 27,7,9, 27,8,9, 23,5,9, 23,6,9, 23,7,9, 23,8,9, 27,5,9, 27,6,9, 27,7,9, 27,8,9, 23,5,9, 23,6,9, 23,7,9, 23,8,9, 27,5,9, 27,6,9, 27,7,9, 27,8,9)); - Collections.shuffle(cannons); + action = new BlockAction(Collections.emptyList(), Collections.singletonList(new Vector(25, 15, 25)), + new MoveToRangeAction(new Vector(21, 15, 24), + new FunctionAction(ai -> { + setReady(); + return FightState.getFightState() == FightState.PRE_RUNNING; + }, + new WaitAction(420, + new BlockAction(Collections.emptyList(), Collections.singletonList(new Vector(21, 15, 24)), + new FunctionAction(ai -> FightState.getFightState() == FightState.RUNNING, + new ChooseCannonAction( + new Cannon(new Vector(11,25,11), 10,23,14, 12,23,14, 10,23,12, 12,23,12, 12,24,15, 10,24,14, 12,23,15, 12,24,14, 11,24,12, 10,24,15, 12,24,12, 10,23,15, 10,24,12), + new Cannon(new Vector(39,25,11), 38,24,15, 38,23,12, 40,23,14, 40,24,12, 40,23,15, 40,24,14, 39,24,12, 38,23,14, 40,24,15, 40,23,12, 38,23,15, 38,24,12, 38,24,14), + new Cannon(new Vector(12,18,11), 13,17,15, 13,17,16, 13,18,16, 13,18,14, 14,18,14, 14,17,15, 14,17,16, 14,18,16, 13,17,14, 14,17,14, 13,18,15, 14,18,15), + new Cannon(new Vector(38,18,11), 37,18,14, 36,18,14, 36,17,15, 37,17,15, 37,18,15, 36,18,15, 36,17,14, 37,17,14, 36,17,16, 37,17,16, 36,18,16, 37,18,16), + new Cannon(new Vector(8,9,16), 10,11,17, 10,8,19, 10,8,17, 11,8,19, 11,8,15, 10,8,15, 12,8,15, 10,10,15, 12,7,19, 11,10,15, 12,8,19, 12,7,15, 11,7,15, 10,9,17, 11,9,19, 12,9,19, 10,9,19, 10,7,17, 11,7,19, 10,7,15, 10,7,19, 10,10,17, 12,9,15, 10,9,15, 11,9,15, 12,10,19, 11,10,19, 12,10,15, 10,6,17, 10,10,19), + new Cannon(null, 23,5,9, 23,6,9, 23,7,9, 23,8,9), + new Cannon(null, 27,5,9, 27,6,9, 27,7,9, 27,8,9) + ) + ) + ) + ) + ) + ) + ); return schem; } - @Override - protected void plan() { - setReady(); - if(shootingList.isEmpty()) - shootingList.addAll(cannons); - - if(shootingList.isEmpty() || FightState.getFightState() != FightState.RUNNING) { - moveTo(pathplanner.getWalkable().get(random.nextInt(pathplanner.getWalkable().size()))); - return; - } - - Cannon cannon = shootingList.remove(0); - cannon.shoot(); + LixfelPathplanner getPathplanner() { + return pathplanner; } - private boolean moveTo(Vector destination) { - List path = pathplanner.plan(getPosition(), destination); - if(path.isEmpty()) - return false; + void setAction(Action action) { + this.action = action; + } - path.forEach(this::move); - return true; + @Override + protected void plan() { + action.plan(this); + } + + private abstract static class Action { + private Action next; + + public Action(Action next) { + this.next = next; + } + + public abstract void plan(LixfelAI ai); + + public void setNext(Action followup) { + this.next = followup; + } + + protected void next(LixfelAI ai) { + ai.setAction(next); + next.plan(ai); + } + } + + private static class FunctionAction extends Action { + + private final Function action; + public FunctionAction(Function action, Action followup) { + super(followup); + this.action = action; + } + + @Override + public void plan(LixfelAI ai) { + if(action.apply(ai)) + next(ai); + } + } + + private static class WaitAction extends Action { + + private int remaining; + public WaitAction(int ticks, Action followup) { + super(followup); + this.remaining = ticks; + } + + @Override + public void plan(LixfelAI ai) { + if(--remaining == 0) + next(ai); + } + } + + private static class MoveToRangeAction extends Action { + + private final Vector target; + public MoveToRangeAction(Vector target, Action next) { + super(next); + this.target = target; + } + + @Override + public void plan(LixfelAI ai) { + Vector position = ai.getPosition(); + Vector eyePosition = new Vector(0, ai.getEntity().getEyeHeight(), 0).add(position); + + if(eyePosition.distance(target) > 5) { + List path = new ArrayList<>(ai.getPathplanner().planToRange(position, target, 5.0)); + if(path.isEmpty()) + path.add(new Vector(0, 1, 0).add(position)); + + ai.move(path.get(0)); + } else { + next(ai); + } + } + } + + private static class BlockAction extends Action { + private final List tntToPlace; + private final List interactables; + + public BlockAction(List tntToPlace, List interactables, Action followup) { + super(followup); + this.tntToPlace = new ArrayList<>(tntToPlace); + this.interactables = new ArrayList<>(interactables); + } + + @Override + public void plan(LixfelAI ai) { + if(!tntToPlace.isEmpty()) { + if(outOfRange(ai, tntToPlace.get(0))) + return; + + ai.setTNT(tntToPlace.remove(0)); + return; + } + + if(!interactables.isEmpty()) { + if(outOfRange(ai, interactables.get(0))) + return; + + ai.interact(interactables.remove(0)); + return; + } + + next(ai); + } + + private boolean outOfRange(LixfelAI ai, Vector location) { + Vector position = ai.getPosition(); + Vector eyePosition = new Vector(0, ai.getEntity().getEyeHeight(), 0).add(position); + boolean outOfRange = eyePosition.distance(location) > 5; + + if(outOfRange) { + List path = new ArrayList<>(ai.getPathplanner().planToRange(position, location, 5.0)); + if(path.isEmpty()) + path.add(new Vector(0, 1, 0).add(position)); + + System.out.println(ai.getEntity().getName() + ": " + position + "->" + path.get(0)); + ai.move(path.get(0)); + } + + return outOfRange; + } + } + + private static class ChooseCannonAction extends Action { + + private final List cannons; + private final List shootingList = new ArrayList<>(); + + public ChooseCannonAction(Cannon... cannons) { + super(null); + this.cannons = Arrays.asList(cannons); + Collections.shuffle(this.cannons); + } + + @Override + public void plan(LixfelAI ai) { + if(shootingList.isEmpty()) + shootingList.addAll(cannons); + + setNext(shootingList.remove(0).toAction(this)); + next(ai); + } } private class Cannon { private final Vector activator; private final List tnt = new ArrayList<>(); - private final Vector entityLocation; public Cannon(Vector activator, int... tntpos) { this.activator = activator; @@ -102,25 +244,10 @@ public class LixfelAI extends AI { for(int i = 0; i < tntpos.length; i+=3) { tnt.add(new Vector(tntpos[i], tntpos[i+1], tntpos[i+2])); } - - List locations = new ArrayList<>(tnt); - if(activator != null) - locations.add(activator); - entityLocation = pathplanner.walkableNearby(getEntity().getEyeHeight(), 5, locations); } - public boolean shoot() { - if(entityLocation == null) - return false; - - if(!moveTo(entityLocation)) - return false; - - tnt.forEach(LixfelAI.this::setTNT); - if(activator != null) - interact(activator); - - return true; + public BlockAction toAction(Action next) { + return new BlockAction(new ArrayList<>(tnt), activator == null ? Collections.emptyList() : Collections.singletonList(activator), next); } } } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java index 0cbfe28..b7fd14c 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java @@ -125,6 +125,7 @@ public class LixfelPathplanner { } } + @Deprecated public Vector walkableNearby(double eyeHeight, double distance, List nearby) { List moddedNearby = nearby.stream().map(n -> n.clone().subtract(new Vector(0, eyeHeight, 0))).collect(Collectors.toList()); return walkable.stream() @@ -145,12 +146,21 @@ public class LixfelPathplanner { return plan; } + public List planToRange(Vector start, Vector destination, double range) { + return plan(start, walkable.stream().filter(vector -> vector.distance(destination) <= range).collect(Collectors.toList())); + } + public List plan(Vector start, Vector destination) { - if(neighbouring(start, destination)) - return Collections.singletonList(destination); + return plan(start, Collections.singletonList(destination)); + } + + public List plan(Vector start, List destinations) { + for(Vector destination : destinations) + if(neighbouring(start, destination)) + return Collections.singletonList(destination); Map approach = new HashMap<>(); - Set checking = Collections.singleton(destination); + Set checking = new HashSet<>(destinations); while(!checking.isEmpty()) { Set toCheck = new HashSet<>(); @@ -168,7 +178,7 @@ public class LixfelPathplanner { List path = new ArrayList<>(); path.add(firstStep); - while(path.get(path.size()-1) != destination) { + while(!destinations.contains(path.get(path.size()-1))) { path.add(approach.get(path.get(path.size()-1))); }