From 21ee580c0fb9ad12332a69c4c32d20fd2bfbab11 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 4 Sep 2023 16:21:25 +0200 Subject: [PATCH] New StateMachine, improved Height Classification Signed-off-by: Lixfel --- .../src/de/steamwar/fightsystem/ai/AI.java | 8 +- .../de/steamwar/fightsystem/ai/LixfelAI.java | 306 +++++++----------- .../fightsystem/ai/LixfelPathplanner.java | 18 +- 3 files changed, 137 insertions(+), 195 deletions(-) diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java index dfbde18..ad89881 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java @@ -55,6 +55,8 @@ import java.util.logging.Level; public abstract class AI { + public static final double INTERACTION_RANGE = 5.0; + private static final Map ais = new HashMap<>(); static { @@ -204,12 +206,12 @@ public abstract class AI { } public void move(Vector pos) { - queue.add(new Action(2) { + queue.add(new Action(4) { @Override public void run() { Location location = entity.getLocation(); Location target = translate(pos, false); - if(Math.abs(location.getX() - target.getX()) > 1.9 || Math.abs(location.getY() - target.getY()) > 1.9 || Math.abs(location.getZ() - target.getZ()) > 1.9) { + if(Math.abs(location.getX() - target.getX()) > 1.0 || Math.abs(location.getY() - target.getY()) > 1.5 || Math.abs(location.getZ() - target.getZ()) > 1.0) { FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + ": Overdistance movement " + location.toVector() + " " + target.toVector()); return; } @@ -223,7 +225,7 @@ public abstract class AI { } private boolean interactionDistanceViolation(Location location) { - return location.distance(entity.getEyeLocation()) > 5; + return location.distance(entity.getEyeLocation()) > INTERACTION_RANGE; } private void interact(Block block) { diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java index 463aa42..a49bfec 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java @@ -19,8 +19,6 @@ package de.steamwar.fightsystem.ai; -import de.steamwar.entity.RArmorStand; -import de.steamwar.entity.REntityServer; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.states.FightState; @@ -29,15 +27,18 @@ import de.steamwar.sql.SteamwarUser; import org.bukkit.util.Vector; import java.util.*; -import java.util.function.Function; +import java.util.function.Consumer; public class LixfelAI extends AI { private Random random; + private LixfelPathplanner pathplanner; - private Action action; - private REntityServer entityServer; - private RArmorStand entityMarker; + private List setup; + private long preRunningStart; + private List timedStart; + private List cannons; + private Cannon currentCannon; public LixfelAI(FightTeam team, String user) { super(team, SteamwarUser.get(user)); @@ -52,220 +53,155 @@ public class LixfelAI extends AI { schem = publics.stream().filter(s -> s.getName().equals("TheUnderground")).findAny().orElse(schem); pathplanner = new LixfelPathplanner(schem); - - 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) - ) - ) - ) - ) - ) - ) - ); - - /*entityServer = new REntityServer(); - Bukkit.getPluginManager().registerEvents(new Listener() { - @EventHandler - public void onJoin(PlayerJoinEvent e) { - entityServer.addPlayer(e.getPlayer()); - } - }, FightSystem.getPlugin()); - - System.out.println(pathplanner.getWalkable().size()); - for(Vector v : pathplanner.getWalkable()) { - RArmorStand armorStand = new RArmorStand(entityServer, translate(v, false), RArmorStand.Size.MARKER); - armorStand.setInvisible(true); - armorStand.setDisplayName("walkable"); - } - entityMarker = new RArmorStand(entityServer, getEntity().getLocation(), RArmorStand.Size.MARKER); - entityMarker.setInvisible(true); - entityMarker.setDisplayName("position");*/ + setup = new ArrayList<>(Collections.singletonList(new Vector(25, 15, 25))); + timedStart = new ArrayList<>(Collections.singletonList(new TimedVector(420L, new Vector(21, 15, 24)))); + cannons = new ArrayList<>(Arrays.asList( + 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(9,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) + )); + chooseCannon(); return schem; } - @Override - public void move(Vector pos) { - super.move(pos); - //entityMarker.move(getEntity().getLocation()); - } - - LixfelPathplanner getPathplanner() { - return pathplanner; - } - - void setAction(Action action) { - this.action = action; - } - @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 next) { - this.next = next; - } - - protected void next(LixfelAI ai) { - ai.setAction(next); - next.plan(ai); + switch (FightState.getFightState()) { + case POST_SCHEM_SETUP: + if(prepareSchem() && scanEnemy()) + setReady(); + break; + case PRE_RUNNING: + if(start() && scanEnemy()) + ensureInRange(currentCannon.tnt.keySet().iterator().next()); + break; + case RUNNING: + while(currentCannon.shoot()) + chooseCannon(); //TODO prevent endless loop + break; + default: + break; } } - 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 boolean prepareSchem() { + return doWith(setup, this::interact); } - 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 boolean scanEnemy() { + //TODO + return true; } - private static class MoveToRangeAction extends Action { + private boolean start() { + if(preRunningStart == 0) + preRunningStart = Config.world.getFullTime(); - private final Vector target; - public MoveToRangeAction(Vector target, Action next) { - super(next); - this.target = target; - } - - public boolean inRange(LixfelAI ai) { - return new Vector(0, ai.getEntity().getEyeHeight(), 0).add(ai.getPosition()).distance(target) <= 5; - } - - @Override - public void plan(LixfelAI ai) { - if(inRange(ai)) { - next(ai); - return; - } - - Vector position = ai.getPosition(); - List path = new ArrayList<>(ai.getPathplanner().planToRange(position, new Vector(0, -ai.getEntity().getEyeHeight(), 0).add(target), 5.0)); - if(path.isEmpty()) - path.add(new Vector(0, 1, 0).add(position)); - - ai.move(path.get(0)); + if(timedStart.isEmpty()) + return true; + + long time = Config.world.getFullTime() - preRunningStart; + TimedVector vector = timedStart.get(0); + if(!ensureInRange(vector.vector)) + return false; + + if(time >= vector.getTime()) { + interact(timedStart.remove(0).vector); + } else { + scanEnemy(); } + return false; } - 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()) { - MoveToRangeAction move = new MoveToRangeAction(tntToPlace.get(0), this); - if(move.inRange(ai)) - ai.setTNT(tntToPlace.remove(0)); - else - move.plan(ai); - - return; - } - - if(!interactables.isEmpty()) { - MoveToRangeAction move = new MoveToRangeAction(interactables.get(0), this); - if(move.inRange(ai)) - ai.interact(interactables.remove(0)); - else - move.plan(ai); - - return; - } - - next(ai); - } + private void chooseCannon() { + currentCannon = cannons.get(random.nextInt(cannons.size())); } - private static class ChooseCannonAction extends Action { + private boolean doWith(List targets, Consumer method) { + if(targets.isEmpty()) + return true; - private final List cannons; - private final List shootingList = new ArrayList<>(); + if(ensureInRange(targets.get(0))) + method.accept(targets.remove(0)); + return false; + } - public ChooseCannonAction(Cannon... cannons) { - super(null); - this.cannons = Arrays.asList(cannons); - Collections.shuffle(this.cannons); - } + private boolean ensureInRange(Vector target) { + Vector position = getPosition(); + boolean inRange = new Vector(0, getEntity().getEyeHeight(), 0).add(position).distance(target) <= 5; + if(inRange) + return true; - @Override - public void plan(LixfelAI ai) { - if(shootingList.isEmpty()) - shootingList.addAll(cannons); + List path = new ArrayList<>(pathplanner.planToRange(position, new Vector(0, -getEntity().getEyeHeight(), 0).add(target), 5.0)); + if(path.isEmpty()) + path.add(new Vector(0, 1, 0).add(position)); - setNext(shootingList.remove(0).toAction(this)); - next(ai); - } + move(path.get(0)); + return false; } private class Cannon { private final Vector activator; - private final List tnt = new ArrayList<>(); + private final Map tnt = new HashMap<>(); + private long freeAt; public Cannon(Vector activator, int... tntpos) { this.activator = activator; for(int i = 0; i < tntpos.length; i+=3) { - tnt.add(new Vector(tntpos[i], tntpos[i+1], tntpos[i+2])); + tnt.put(new Vector(tntpos[i], tntpos[i+1], tntpos[i+2]), false); } } - public BlockAction toAction(Action next) { - return new BlockAction(tnt, activator == null ? Collections.emptyList() : Collections.singletonList(activator), next); + public boolean shoot() { + if(Config.world.getFullTime() < freeAt) + return true; + + for(Map.Entry entry : tnt.entrySet()) { + if(!entry.getValue()) { + if(ensureInRange(entry.getKey())) { + setTNT(entry.getKey()); + entry.setValue(true); + } + return false; + } + } + + if(activator != null) { + if(!ensureInRange(activator)) + return false; + + interact(activator); + } + + freeAt = Config.world.getFullTime() + 80; + for(Map.Entry entry : tnt.entrySet()) + entry.setValue(false); + + return false; + } + } + + public static class TimedVector { + private final long time; + private final Vector vector; + + public TimedVector(long time, Vector vector) { + this.time = time; + this.vector = vector; + } + + public long getTime() { + return time; + } + + public Vector getVector() { + return vector; } } } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java index b7fd14c..93fb1ac 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java @@ -23,12 +23,12 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.world.block.BaseBlock; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.utils.WorldeditWrapper; import de.steamwar.sql.SchematicData; import de.steamwar.sql.SchematicNode; import org.bukkit.Material; +import org.bukkit.block.Banner; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.*; import org.bukkit.util.Vector; @@ -40,11 +40,11 @@ import java.util.stream.Collectors; public class LixfelPathplanner { private static double blockHeight(Clipboard clipboard, BlockVector3 vector) { - BaseBlock block = clipboard.getFullBlock(vector); - if(block.getBlockType().getMaterial().isFullCube()) - return 1.0; - BlockData data = BukkitAdapter.adapt(block); + BlockData data = BukkitAdapter.adapt(clipboard.getFullBlock(vector)); Material material = data.getMaterial(); + if(material.isOccluding()) + return 1.0; + if(material.isSolid()) { if(material.isInteractable()) { if(data instanceof Stairs) @@ -53,13 +53,17 @@ public class LixfelPathplanner { return 1.5; else if(data instanceof Bed) return 0.5625; + else if(data instanceof TrapDoor) + return -1.0; } else { if(data instanceof Slab) return ((Slab)data).getType() == Slab.Type.BOTTOM ? 0.5 : 1.0; else if(data instanceof Wall) return 1.5; - else if(data instanceof GlassPane || material == Material.IRON_BARS) - return 1.0; + else if(data instanceof Banner || material.name().endsWith("PRESSURE_PLATE")) + return 0.0; + + return 1.0; } } else { if(material == Material.LADDER || material == Material.SCAFFOLDING)