diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties index 3c220d64..9f80ab7e 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties @@ -83,6 +83,8 @@ SCHEM_PRIVATE=§ePrivate {0} SCHEM_NO_PRIVATE=§7No private {0} present SCHEM_PRIVATE_FORBIDDEN=§7No private {0} allowed +ADD_AI_TITLE=Add AI + # Countdowns COUNTDOWN_MINUTES=§e{0} §7Minutes {1} @@ -114,6 +116,7 @@ RESPAWN=§eRespawn REMOVE_PLAYERS=§cKick player CHOOSE_SCHEMATIC=§eChoose {0} SCHEMATIC_REQUIRED=§cChoose a schematic first +ADD_AI=§eAdd AI KIT_PREVIEW_EDIT=§7Edit kit KIT_PREVIEW_CHOOSE=§aSelect kit diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties index 06b07d18..6723bcfb 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties @@ -77,6 +77,8 @@ SCHEM_PRIVATE=§ePrivates {0} SCHEM_NO_PRIVATE=§7Kein privates {0} vorhanden SCHEM_PRIVATE_FORBIDDEN=§7Kein privates {0} erlaubt +ADD_AI_TITLE=KI hinzufügen + # Countdowns COUNTDOWN_MINUTES=§e{0} §7Minuten {1} @@ -107,6 +109,7 @@ RESPAWN=§eRespawn REMOVE_PLAYERS=§cSpieler rauswerfen CHOOSE_SCHEMATIC=§e{0} wählen SCHEMATIC_REQUIRED=§cZuerst muss eine Schematic gewählt sein +ADD_AI=§eKI hinzufügen KIT_PREVIEW_EDIT=§7Kit bearbeiten KIT_PREVIEW_CHOOSE=§aKit wählen diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java index 6f5cb005..0b681fc2 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java @@ -19,32 +19,38 @@ package de.steamwar.fightsystem.ai; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.FightTeam; +import de.steamwar.fightsystem.fight.JoinRequest; import de.steamwar.fightsystem.listener.Chat; +import de.steamwar.fightsystem.record.GlobalRecorder; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.OneShotStateDependent; import de.steamwar.fightsystem.utils.Region; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; +import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Note; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.Lectern; import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.FaceAttachable; import org.bukkit.block.data.Openable; import org.bukkit.block.data.Powerable; import org.bukkit.block.data.type.Comparator; import org.bukkit.block.data.type.NoteBlock; import org.bukkit.block.data.type.Repeater; +import org.bukkit.block.data.type.Switch; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; import org.bukkit.entity.Villager; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.scheduler.BukkitTask; @@ -55,10 +61,16 @@ import java.util.logging.Level; public abstract class AI { + public static final int MOVEMENT_DELAY = 4; + public static final double INTERACTION_RANGE = 5.0; + private static final Map ais = new HashMap<>(); + public static void printPos() { + ais.values().forEach(ai -> ai.chat(ai.entity.isValid() + " " + ai.entity.isDead() + " " + ai.entity.getLocation())); + } static { - new OneShotStateDependent(ArenaMode.All, FightState.Spectate, () -> { + new OneShotStateDependent(ArenaMode.AntiReplay, FightState.Spectate, () -> { ais.values().forEach(AI::stop); ais.clear(); }); @@ -68,7 +80,9 @@ public abstract class AI { return ais.get(uuid); } - private final FightTeam team; + @Getter + protected final FightTeam team; + @Getter private final LivingEntity entity; private final BukkitTask task; private final Queue queue = new ArrayDeque<>(); @@ -83,11 +97,15 @@ public abstract class AI { task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), this::run, 1, 1); ais.put(entity.getUniqueId(), this); team.addMember(entity, user); + + if(FightState.Schem.contains(FightState.getFightState())) + Bukkit.getScheduler().runTask(FightSystem.getPlugin(), () -> schematic(team.getClipboard())); } public abstract SchematicNode chooseSchematic(); + public abstract void schematic(Clipboard clipboard); - public boolean acceptJoinRequest(Player player, FightTeam team) { + public boolean acceptJoinRequest(JoinRequest.Enquirer enquirer, FightTeam team) { return true; } @@ -101,11 +119,7 @@ public abstract class AI { task.cancel(); } - public LivingEntity getEntity() { - return entity; - } - - protected void setReady() { + public void setReady() { if(FightState.getFightState() != FightState.POST_SCHEM_SETUP) return; @@ -115,12 +129,12 @@ public abstract class AI { team.setReady(true); } - protected void chat(String message) { + public void chat(String message) { FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + "» " + message); Chat.broadcastChat("PARTICIPANT_CHAT", team.getColoredName(), entity.getName(), message); } - protected Vector getPosition() { + public Vector getPosition() { Location location = entity.getLocation(); Region extend = team.getExtendRegion(); if(Fight.getUnrotated() == team) @@ -137,26 +151,28 @@ public abstract class AI { ); } - protected Material getBlock(Vector pos) { + public Material getBlock(Vector pos) { queue.add(new Action(1)); - return translate(pos, true).getBlock().getType(); + return translate(pos).getBlock().getType(); } - protected boolean isPowered(Vector pos) { + public BlockData getBlockData(Vector pos) { queue.add(new Action(1)); - return translate(pos, true).getBlock().isBlockPowered(); + return translate(pos).getBlock().getBlockData(); } - protected void setTNT(Vector pos) { + public void setTNT(Vector pos) { queue.add(new Action(1) { @Override public void run() { if(FightState.getFightState() != FightState.RUNNING) return; - Location location = translate(pos, true); - if(interactionDistanceViolation(location)) + Location location = translate(pos); + if(interactionDistanceViolation(location)) { + chat("InteractionDistanceViolation: setTNT"); return; + } Block block = location.getBlock(); if(block.getType() == Material.AIR) @@ -165,24 +181,26 @@ public abstract class AI { }); } - protected void interact(Vector pos) { + public void interact(Vector pos) { queue.add(new Action(1) { @Override public void run() { - Location location = translate(pos, true); - if(interactionDistanceViolation(location)) + Location location = translate(pos); + if(interactionDistanceViolation(location)) { + chat("InteractionDistanceViolation: interact"); return; + } interact(location.getBlock()); } }); } - protected void interact(Vector pos, int n) { + public void interact(Vector pos, int n) { queue.add(new Action(1) { @Override public void run() { - Location location = translate(pos, true); + Location location = translate(pos); if (interactionDistanceViolation(location)) return; Block block = location.getBlock(); @@ -199,13 +217,18 @@ public abstract class AI { }); } - protected void move(Vector pos) { - queue.add(new Action(2) { + public void move(Vector pos) { + queue.add(new Action(MOVEMENT_DELAY) { @Override public void run() { Location location = entity.getLocation(); - Location target = translate(pos, false); - if(Math.abs(location.getX() - target.getX()) > 1 || Math.abs(location.getY() - target.getY()) > 1.2 || Math.abs(location.getZ() - target.getZ()) > 1) { + if(!entity.isOnGround() && location.getBlock().getType() != Material.LADDER) { + FightSystem.getPlugin().getLogger().log(Level.INFO, "Entity falling"); + return; + } + + Location target = translate(pos); + 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; } @@ -213,13 +236,16 @@ public abstract class AI { if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target)) return; - entity.teleport(target, PlayerTeleportEvent.TeleportCause.COMMAND); + if(!entity.teleport(target, PlayerTeleportEvent.TeleportCause.PLUGIN)) + FightSystem.getPlugin().getLogger().log(Level.INFO, "Entity not teleported: " + entity.isValid()); + + GlobalRecorder.getInstance().entityMoves(entity); } }); } private boolean interactionDistanceViolation(Location location) { - return location.distance(entity.getEyeLocation()) > 5; + return location.distance(entity.getEyeLocation()) > INTERACTION_RANGE; } private void interact(Block block) { @@ -249,23 +275,51 @@ public abstract class AI { powerable.setPowered(false); block.setBlockData(powerable); + updateButton(block); }, type.name().endsWith("STONE_BUTTON") ? 20 : 30); } powerable.setPowered(!isPowered); } block.setBlockData(data); + if(data instanceof Switch) { + updateButton(block); + } + } + + private void updateButton(Block block) { + Switch sw = (Switch) block.getBlockData(); + FaceAttachable.AttachedFace face = sw.getAttachedFace(); + if (face == FaceAttachable.AttachedFace.FLOOR) { + update(block.getRelative(BlockFace.DOWN)); + } else if (face == FaceAttachable.AttachedFace.CEILING) { + update(block.getRelative(BlockFace.UP)); + } else { + update(block.getRelative(sw.getFacing().getOppositeFace())); + } + } + + private void update(Block block) { + BlockData data = block.getBlockData(); + block.setType(Material.BARRIER); + block.setBlockData(data); } private void run() { - if(queue.isEmpty()) - plan(); + if(queue.isEmpty()) { + try { + plan(); + } catch (Throwable t) { + stop(); + throw t; + } + } if(!queue.isEmpty() && --queue.peek().delay == 0) queue.poll().run(); } - private Location translate(Vector pos, boolean blockPos) { + public Location translate(Vector pos) { Region extend = team.getExtendRegion(); if(Fight.getUnrotated() == team) return new Location( @@ -277,9 +331,9 @@ public abstract class AI { else return new Location( Config.world, - extend.getMaxX() - pos.getX() - (blockPos ? 1 : 0), + extend.getMaxX() - pos.getX(), pos.getY() + team.getSchemRegion().getMinY(), - extend.getMaxZ() - pos.getZ() - (blockPos ? 1 : 0) + extend.getMaxZ() - pos.getZ() ); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AIManager.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AIManager.java new file mode 100644 index 00000000..5b9abf4b --- /dev/null +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AIManager.java @@ -0,0 +1,55 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.fightsystem.ai; + +import com.comphenix.tinyprotocol.Reflection; +import de.steamwar.fightsystem.fight.FightTeam; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.bukkit.Material; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class AIManager { + private static final List AIs = Arrays.asList( + new AIManager(DummyAI.class, Material.STONE, () -> true) + ); + + public static List availableAIs() { + return AIs.stream().filter(manager -> manager.available.getAsBoolean()).collect(Collectors.toList()); + } + + private final Class aiClass; + @Getter + private final Material icon; + private final BooleanSupplier available; + + public String name() { + return aiClass.getSimpleName(); + } + + public void join(FightTeam team) { + Reflection.getConstructor(aiClass, FightTeam.class).invoke(team); + } +} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/DummyAI.java similarity index 63% rename from FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java rename to FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/DummyAI.java index 2a89353c..90fcd3d0 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/DummyAI.java @@ -19,42 +19,42 @@ package de.steamwar.fightsystem.ai; +import com.sk89q.worldedit.extent.clipboard.Clipboard; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.fight.FightTeam; +import de.steamwar.fightsystem.states.FightState; +import de.steamwar.fightsystem.utils.FightStatistics; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; -import org.bukkit.util.Vector; import java.util.List; import java.util.Random; -public class LixfelAI extends AI { +public class DummyAI extends AI { - private final Random random = new Random(); - private LixfelPathplanner pathplanner; + private static final Random random = new Random(); - public LixfelAI(FightTeam team, String user) { - super(team, SteamwarUser.get(user)); + public DummyAI(FightTeam team) { + super(team, SteamwarUser.get("public")); + + FightStatistics.unrank(); + getEntity().setInvulnerable(true); } - @Override public SchematicNode chooseSchematic() { List publics = SchematicNode.getAllSchematicsOfType(0, Config.SchematicType.toDB()); - SchematicNode schem = publics.get(new Random().nextInt(publics.size())); - pathplanner = new LixfelPathplanner(schem); - return schem; + return publics.get(random.nextInt(publics.size())); + } + + @Override + public void schematic(Clipboard clipboard) { + //does nothing } @Override protected void plan() { - setReady(); - Vector destination = pathplanner.getWalkable().get(random.nextInt(pathplanner.getWalkable().size())); - List path = pathplanner.plan(getPosition(), destination); - if(!path.isEmpty()) - chat("Path size: " + path.size()); - for(Vector p : path) { - move(p); - } + if(FightState.getFightState() == FightState.POST_SCHEM_SETUP) + setReady(); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java deleted file mode 100644 index b2e593e8..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2023 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.fightsystem.ai; - -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.BlockType; -import de.steamwar.fightsystem.Config; -import de.steamwar.sql.SchematicData; -import de.steamwar.sql.SchematicNode; -import org.bukkit.util.Vector; - -import java.io.IOException; -import java.util.*; - -public class LixfelPathplanner { - - private static BlockType getBlockType(Clipboard clipboard, BlockVector3 vector) { - return clipboard.getBlock(vector).getBlockType(); - } - - private static boolean nonsolid(Clipboard clipboard, BlockVector3 vector) { - return !getBlockType(clipboard, vector).getMaterial().isSolid(); - } - - private static Vector toBukkit(BlockVector3 vector) { - return new Vector(vector.getX() + 0.5, vector.getY(), vector.getZ() + 0.5); - } - - private final List walkable = new ArrayList<>(); - private final Map neighbours = new HashMap<>(); - - public LixfelPathplanner(SchematicNode schem) { - try { - fillWalkable(new SchematicData(schem).load()); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - public List getWalkable() { - return walkable; - } - - private void fillWalkable(Clipboard clipboard) { - BlockVector3 min = clipboard.getRegion().getMinimumPoint().subtract(Config.PreperationArea, 0, Config.PreperationArea); //TODO assumes nonextended Schematic with maximal size - Region region = clipboard.getRegion(); - clipboard.getRegion().forEach(vector -> { - BlockVector3 below = vector.subtract(0, 1, 0); - if(!region.contains(below)) - return; - - BlockType belowMaterial = getBlockType(clipboard, below); - BlockVector3 above = vector.add(0, 1, 0); - if(nonsolid(clipboard, vector)) { - if( - (belowMaterial.getMaterial().isSolid() || belowMaterial.getId().equals("minecraft:ladder")) && - (!region.contains(above) || nonsolid(clipboard, above)) - ) - walkable.add(toBukkit(vector.subtract(min))); - } else { - if(!region.contains(above)) - walkable.add(toBukkit(above.subtract(min))); - } - }); - - for(Vector vector : walkable) { - neighbours.put(vector, walkable.stream().filter(neighbour -> neighbouring(neighbour, vector)).filter(neighbour -> neighbour != vector).toArray(Vector[]::new)); - } - } - - public List planToAnywhere(Vector start, Vector destination) { - Vector intermediate = walkable.stream().filter(vector -> neighbouring(vector, destination)).findAny().orElse(null); - - if(intermediate == null) - return Collections.emptyList(); - - List plan = plan(start, intermediate); - plan.add(destination); - - return plan; - } - - public List plan(Vector start, Vector destination) { - if(neighbouring(start, destination)) - return Collections.singletonList(destination); - - Map approach = new HashMap<>(); - Set checking = Collections.singleton(destination); - - while(!checking.isEmpty()) { - Set toCheck = new HashSet<>(); - for(Vector current : checking) { - Vector firstStep = Arrays.stream(neighbours.get(current)) - .filter(vector -> !approach.containsKey(vector)) - .filter(next -> { - approach.put(next, current); - toCheck.add(next); - return neighbouring(next, start); - }) - .findAny().orElse(null); - - if(firstStep != null) { - List path = new ArrayList<>(); - path.add(firstStep); - - while(path.get(path.size()-1) != destination) { - path.add(approach.get(path.get(path.size()-1))); - } - - return path; - } - } - checking = toCheck; - } - - return Collections.emptyList(); - } - - private boolean neighbouring(Vector a, Vector b) { - return Math.abs(a.getX() - b.getX()) <= 1 && Math.abs(a.getY() - b.getY()) <= 1 && Math.abs(a.getZ() - b.getZ()) <= 1; - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java index 0fba5388..4834bb0a 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java @@ -22,6 +22,7 @@ package de.steamwar.fightsystem.commands; import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; +import de.steamwar.fightsystem.ai.AIManager; import de.steamwar.fightsystem.fight.*; import de.steamwar.fightsystem.listener.PersonalKitCreator; import de.steamwar.fightsystem.states.FightState; @@ -38,9 +39,8 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; public class GUI { private GUI(){} @@ -53,7 +53,7 @@ public class GUI { String name = team.getLeader() != null ? team.getLeader().getEntity().getName() : team.getName(); inv.setItem(pos, SWItem.getDye(colorCode), colorCode, msg.parse("JOIN_REQUEST_TEAM", p, team.getColor() + name), click -> { p.closeInventory(); - new JoinRequest(p, team); + JoinRequest.forPlayer(p, team); }); } @@ -89,11 +89,29 @@ public class GUI { inv.open(); } + public static void addAI(Player p) { + SWListInv inv = new SWListInv<>( + p, msg.parse("ADD_AI_TITLE", p), + AIManager.availableAIs().stream().map(manager -> new SWListInv.SWListEntry<>(new SWItem(manager.getIcon(), manager.name()), manager)).collect(Collectors.toList()), + (click, manager) -> { + FightTeam team = Fight.getPlayerTeam(p); + if(FightState.PreLeaderSetup.contains(FightState.getFightState())) { + manager.join(Fight.getOpposite(team)); + } else { + JoinRequest.forAI(manager, team); + } + p.closeInventory(); + } + ); + inv.setCallback(-999, (ClickType click) -> p.closeInventory()); + inv.open(); + } + public static void chooseJoinRequests(Player p){ - List> players = JoinRequest.openRequests(p, Fight.getPlayerTeam(p)); - SWListInv inv = new SWListInv<>(p, msg.parse("REQUESTS_TITLE", p), players, (ClickType click, Player player) -> { + List> players = JoinRequest.openRequests(p, Fight.getPlayerTeam(p)); + SWListInv inv = new SWListInv<>(p, msg.parse("REQUESTS_TITLE", p), players, (ClickType click, JoinRequest request) -> { p.closeInventory(); - RequestsCommand.onJoinRequest(p, player, click.isLeftClick() ? JoinRequest::accept : JoinRequest::decline); + RequestsCommand.onJoinRequest(p, request, click.isLeftClick() ? JoinRequest::accept : JoinRequest::decline); }); inv.setCallback(-999, (ClickType click) -> p.closeInventory()); inv.open(); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java index 36c2a1e3..131cc898 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java @@ -59,13 +59,7 @@ public class RequestsCommand implements CommandExecutor { return false; } - public static void onJoinRequest(Player player, Player target, BiConsumer handleJoinRequest) { - JoinRequest request = JoinRequest.get(target); - if(request == null) { - FightSystem.getMessage().send("NO_JOIN_REQUEST", player); - return; - } - + public static void onJoinRequest(Player player, JoinRequest request, BiConsumer handleJoinRequest) { FightTeam team = Fight.getPlayerTeam(player); if(!request.required(team)) { FightSystem.getMessage().send("NO_CONFIRMATION", player); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java index 20c304d7..5e84ccdc 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java @@ -34,6 +34,7 @@ import de.steamwar.fightsystem.utils.WorldeditWrapper; import de.steamwar.sql.SchematicData; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicType; +import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.DyeColor; import org.bukkit.Location; @@ -52,6 +53,7 @@ public class FightSchematic extends StateDependent { private final Region region; private final boolean rotate; + @Getter private Clipboard clipboard = null; private int schematic = 0; @@ -137,6 +139,7 @@ public class FightSchematic extends StateDependent { private void paste(){ FreezeWorld freezer = new FreezeWorld(); + team.teleportToSpawn(); Vector dims = WorldeditWrapper.impl.getDimensions(clipboard); WorldeditWrapper.impl.pasteClipboard( clipboard, @@ -149,11 +152,11 @@ public class FightSchematic extends StateDependent { new AffineTransform().rotateY(rotate ? 180 : 0) ); FightSystem.getHullHider().initialize(team); + team.getPlayers().forEach(fightPlayer -> fightPlayer.ifAI(ai -> ai.schematic(clipboard))); if(ArenaMode.Check.contains(Config.mode) && !team.isBlue()) - replaceSync(Material.TNT, Material.OBSIDIAN); + replaceSync(Material.TNT, Material.RED_WOOL); Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), freezer::disable, 3); - Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), team::teleportToSpawn, 40); } @Override diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java index 853646fc..334f3f83 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard; import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; +import de.steamwar.fightsystem.ai.AIManager; import de.steamwar.fightsystem.commands.GUI; import de.steamwar.fightsystem.countdown.Countdown; import de.steamwar.fightsystem.events.TeamLeaveEvent; @@ -70,9 +71,11 @@ public class FightTeam { static { setKitButton(notReadyKit, true); - if(!ArenaMode.RankedEvent.contains(Config.mode)){ + if(ArenaMode.VariableTeams.contains(Config.mode)){ notReadyKit.setItem(2, "REQUESTS", new ItemBuilder(Material.PAPER).build(), GUI::chooseJoinRequests); notReadyKit.setItem(3, "REMOVE_PLAYERS", new ItemBuilder(SWItem.getMaterial("FIREWORK_CHARGE")).build(), GUI::chooseRemove); + if(!AIManager.availableAIs().isEmpty()) + notReadyKit.setItem(6, "ADD_AI", new ItemBuilder(Material.REDSTONE).build(), GUI::addAI); } if(Config.test()) @@ -452,6 +455,10 @@ public class FightTeam { return schematic.getId(); } + public Clipboard getClipboard() { + return schematic.getClipboard(); + } + public double getCurrentHearts() { return players.values().stream().filter(FightPlayer::isLiving).mapToDouble(fp -> fp.getEntity().getHealth()).sum(); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java index a7756e4c..e4e349c7 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java @@ -20,6 +20,7 @@ package de.steamwar.fightsystem.fight; import de.steamwar.fightsystem.FightSystem; +import de.steamwar.fightsystem.ai.AIManager; import de.steamwar.fightsystem.states.FightState; import de.steamwar.inventory.SWItem; import de.steamwar.inventory.SWListInv; @@ -28,44 +29,66 @@ import net.md_5.bungee.api.chat.ClickEvent; import org.bukkit.entity.Player; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.stream.Collectors; public class JoinRequest { - private static final Map activeRequests = new HashMap<>(); + private static final Map playerRequests = new HashMap<>(); + private static final List activeRequests = new ArrayList<>(); + public static JoinRequest get(Player player) { + return playerRequests.get(player); + } - public static List> openRequests(Player p, FightTeam team) { - return activeRequests.values().stream().filter( + public static List> openRequests(Player p, FightTeam team) { + return activeRequests.stream().filter( request -> request.waitOnApproval.contains(team) ).map(request -> { - SWItem item = SWItem.getPlayerSkull(request.player); - item.setLore(Arrays.asList( + AtomicReference item = new AtomicReference<>(); + request.enquirer.ifPlayer(player -> item.set(SWItem.getPlayerSkull(player))); + request.enquirer.ifAI(manager -> item.set(new SWItem(manager.getIcon(), manager.name()))); + item.get().setLore(Arrays.asList( FightSystem.getMessage().parse("REQUESTS_LEFT_CLICK", p), FightSystem.getMessage().parse("REQUESTS_RIGHT_CLICK", p) )); - return new SWListInv.SWListEntry<>(item, request.player); + return new SWListInv.SWListEntry<>(item.get(), request); }).collect(Collectors.toList()); } public static void clearRequests() { + playerRequests.clear(); activeRequests.clear(); } - public static JoinRequest get(Player player) { - return activeRequests.get(player); + public static void forPlayer(Player player, FightTeam team) { + new JoinRequest(new Enquirer() { + @Override public String name() { return player.getName(); } + @Override public void ifPlayer(Consumer function) { function.accept(player); } + @Override public void ifAI(Consumer function) {} + }, team, FightState.ingame() ? Fight.teams() : Collections.singleton(team)); } - private final Player player; + public static void forAI(AIManager manager, FightTeam team) { + new JoinRequest(new Enquirer() { + @Override public String name() { return manager.name(); } + @Override public void ifPlayer(Consumer function) {} + @Override public void ifAI(Consumer function) { function.accept(manager); } + }, team, Collections.singleton(Fight.getOpposite(team))); + } + + private final Enquirer enquirer; private final FightTeam team; private final Set waitOnApproval; - public JoinRequest(Player player, FightTeam team) { - this.player = player; + private JoinRequest(Enquirer enquirer, FightTeam team, Collection waitOnApproval) { + this.enquirer = enquirer; this.team = team; - this.waitOnApproval = new HashSet<>(FightState.ingame() ? Fight.teams() : Collections.singleton(team)); + this.waitOnApproval = new HashSet<>(waitOnApproval); Set alreadyAccepted = new HashSet<>(); - activeRequests.put(player, this); + enquirer.ifPlayer(player -> playerRequests.put(player, this)); + activeRequests.add(this); for(FightTeam t : waitOnApproval) { FightPlayer leader = t.getLeader(); if(leader == null) @@ -74,14 +97,14 @@ public class JoinRequest { if(leader.getEntity() == null) continue; - leader.ifPlayer(leaderPlayer -> FightSystem.getMessage().sendPrefixless("JOIN_REQUEST_NOTIFICATION", leaderPlayer, "REQUESTS", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/requests"), player.getName(), team.getColoredName())); + leader.ifPlayer(leaderPlayer -> FightSystem.getMessage().sendPrefixless("JOIN_REQUEST_NOTIFICATION", leaderPlayer, "REQUESTS", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/requests"), enquirer.name(), team.getColoredName())); leader.ifAI(ai -> { - if(ai.acceptJoinRequest(player, team)) + if(ai.acceptJoinRequest(enquirer, team)) alreadyAccepted.add(t); }); } - FightSystem.getMessage().sendPrefixless("JOIN_REQUEST_CONFIRMATION", player, ChatMessageType.ACTION_BAR); + enquirer.ifPlayer(player -> FightSystem.getMessage().sendPrefixless("JOIN_REQUEST_CONFIRMATION", player, ChatMessageType.ACTION_BAR)); alreadyAccepted.forEach(this::accept); } @@ -93,18 +116,26 @@ public class JoinRequest { waitOnApproval.remove(acceptor); if(waitOnApproval.isEmpty()) { - team.addMember(player); - activeRequests.remove(player); + enquirer.ifPlayer(team::addMember); + enquirer.ifAI(manager -> manager.join(team)); + close(); } } public void decline(FightTeam declinor) { - FightSystem.getMessage().sendPrefixless("REQUEST_YOUR_DECLINED", player, ChatMessageType.ACTION_BAR); - waitOnApproval.forEach(t -> t.broadcast("REQUEST_DECLINED", player.getName())); - silentDecline(); + enquirer.ifPlayer(player -> FightSystem.getMessage().sendPrefixless("REQUEST_YOUR_DECLINED", player, ChatMessageType.ACTION_BAR)); + waitOnApproval.forEach(t -> t.broadcast("REQUEST_DECLINED", enquirer.name())); + close(); } - public void silentDecline() { - activeRequests.remove(player); + public void close() { + enquirer.ifPlayer(playerRequests::remove); + activeRequests.remove(this); + } + + public interface Enquirer { + String name(); + void ifPlayer(Consumer function); + void ifAI(Consumer function); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java index 1ed3fe8b..45121cf7 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java @@ -69,6 +69,6 @@ public class JoinRequestListener implements Listener { public void onLeave(PlayerQuitEvent event) { JoinRequest request = JoinRequest.get(event.getPlayer()); if(request != null) - request.silentDecline(); + request.close(); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index f77c1904..889c3c1c 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -43,6 +43,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -70,7 +71,7 @@ public class Recording implements Listener { return stack; } - public static boolean isNotSent(Player p){ + public static boolean isNotSent(LivingEntity p){ FightPlayer fp = Fight.getFightPlayer(p); return fp == null || !fp.isLiving() || FightState.getFightState() == FightState.SPECTATE; } @@ -193,10 +194,10 @@ public class Recording implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onEntityDamage(EntityDamageEvent e) { - if(e.getEntityType() != EntityType.PLAYER) + if(!e.getEntityType().isAlive()) return; - Player p = (Player) e.getEntity(); + LivingEntity p = (LivingEntity) e.getEntity(); if(isNotSent(p)) return; @@ -208,10 +209,10 @@ public class Recording implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onEntityCombust(EntityCombustEvent e) { - if(e.getEntityType() != EntityType.PLAYER) + if(!e.getEntityType().isAlive()) return; - Player p = (Player) e.getEntity(); + LivingEntity p = (LivingEntity) e.getEntity(); if(isNotSent(p)) return; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java index d57a29b9..73821e6f 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java @@ -204,7 +204,7 @@ public interface Recorder { write(0x0a, e.getEntityId(), start, offHand); } - default void damageAnimation(Player p) { + default void damageAnimation(LivingEntity p) { write(0x0b, p.getEntityId()); }