SteamWar/FightSystem
Archiviert
13
1

Emergency Movement
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Signed-off-by: Lixfel <agga-games@gmx.de>
Dieser Commit ist enthalten in:
Lixfel 2024-01-06 15:22:55 +01:00
Ursprung f104ccc49a
Commit 6fa873204b
5 geänderte Dateien mit 129 neuen und 82 gelöschten Zeilen

Datei anzeigen

@ -22,7 +22,7 @@ package de.steamwar.fightsystem;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.Core;
import de.steamwar.fightsystem.ai.DummyAI;
import de.steamwar.fightsystem.ai.LixfelAI;
import de.steamwar.fightsystem.ai.lixfel.LixfelAI;
import de.steamwar.fightsystem.commands.*;
import de.steamwar.fightsystem.countdown.*;
import de.steamwar.fightsystem.event.HellsBells;

Datei anzeigen

@ -296,8 +296,14 @@ public abstract class AI {
}
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();

Datei anzeigen

@ -22,7 +22,7 @@ package de.steamwar.fightsystem.ai.chaos;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.ai.AI;
import de.steamwar.fightsystem.ai.LixfelPathplanner;
import de.steamwar.fightsystem.ai.lixfel.LixfelPathplanner;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.sql.SchematicData;
@ -78,7 +78,7 @@ public class ChaosAI extends AI {
throw new IllegalStateException(e);
}
pathplanner = new LixfelPathplanner(clipboard);
pathplanner = new LixfelPathplanner(this, clipboard);
return schem;
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
* 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
@ -17,20 +17,19 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.ai;
package de.steamwar.fightsystem.ai.lixfel;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockState;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.ai.lixfel.PlanResult;
import de.steamwar.fightsystem.ai.AI;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.sql.SchematicData;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.util.Vector;
@ -42,10 +41,10 @@ import java.util.function.Supplier;
public class LixfelAI extends AI {
private final Random random = new Random();
private final List<Supplier<PlanResult>> plans = new ArrayList<>();
private List<Supplier<PlanResult>> plans;
private LixfelPathplanner pathplanner;
private Vector plannedPosition;
private MovementEmergency movementEmergency;
public LixfelAI(FightTeam team, String user) {
super(team, SteamwarUser.get(user));
@ -62,13 +61,16 @@ public class LixfelAI extends AI {
throw new IllegalStateException(e);
}
pathplanner = new LixfelPathplanner(clipboard);
pathplanner = new LixfelPathplanner(this, clipboard);
movementEmergency = new MovementEmergency();
plans.clear();
plans.add(new MovementEmergency());
//TODO low priority plan to reconnect split walkable grid
plans = new ArrayList<>();
plans.add(movementEmergency);
plans.add(new ReadyPlan());
plans.add(new TimedInteraction(200.0, -1, new Vector(25, 15, 25)));
plans.add(new TimedInteraction(210.0, 420, new Vector(21, 15, 24)));
List<Cannon> 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),
@ -86,7 +88,7 @@ public class LixfelAI extends AI {
@Override
public void move(Vector pos) {
plannedPosition = pos;
movementEmergency.setPlannedPosition(pos);
super.move(pos);
}
@ -109,26 +111,6 @@ public class LixfelAI extends AI {
}
}
private PlanResult inRange(double rating, Vector target, Supplier<PlanResult> plan) {
return inRangeAndTime(rating, 0, target, plan);
}
private PlanResult inRangeAndTime(double rating, int time, Vector target, Supplier<PlanResult> plan) {
Vector position = getPosition();
boolean inRange = new Vector(0, getEntity().getEyeHeight(), 0).add(position).distance(target) <= AI.INTERACTION_RANGE;
if(inRange)
return time <= 0 ? plan.get() : PlanResult.EMPTY;
List<Vector> path = new ArrayList<>(pathplanner.planToRange(position, new Vector(0, -getEntity().getEyeHeight(), 0).add(target), 5.0));
for(Vector v : path)
Config.world.spawnParticle(Particle.DRIP_LAVA, translate(v, false), 1);
if(path.isEmpty() || time > path.size()*AI.MOVEMENT_DELAY)
return PlanResult.EMPTY;
return new PlanResult(rating, () -> move(path.get(0)));
}
private class ReadyPlan implements Supplier<PlanResult> {
@Override
public PlanResult get() {
@ -140,23 +122,43 @@ public class LixfelAI extends AI {
}
private class MovementEmergency implements Supplier<PlanResult> {
private Vector plannedPosition;
public void setPlannedPosition(Vector plannedPosition) {
this.plannedPosition = plannedPosition;
}
@Override
public PlanResult get() {
//TODO defunct ladder
if(plannedPosition == null || getPosition().equals(plannedPosition) || pathplanner.getLadders().contains(plannedPosition))
if(plannedPosition == null)
return PlanResult.EMPTY;
pathplanner.getWalkable().remove(plannedPosition); //TODO neighbour-table update
if(getEntity().getVelocity().getY() < 0) {
return new PlanResult(1000.0, () -> setTNT(getPosition().subtract(new Vector(0, 1.0, 0))));
} else {
pathplanner.getWalkable().add(getPosition()); //TODO idealized position, neighbour-table update
//TODO handle offgrid location
//TODO update grid
return new PlanResult(10.0, () -> {
Vector position = getPosition();
if(position.getY() == plannedPosition.getY())
return PlanResult.EMPTY;
});
boolean falling = getEntity().getVelocity().getY() < 0;
if(position.getY() > plannedPosition.getY()) {
if(!falling) { //Update grid accordingly
pathplanner.removePosition(plannedPosition);
position.setX(position.getBlockX() + 0.5);
position.setZ(position.getBlockZ() + 0.5);
pathplanner.addPosition(position);
}
return PlanResult.EMPTY;
}
if(pathplanner.isLadder(plannedPosition) && getEntity().getVelocity().getY() >= -0.23) //alternative measure: falling speed
return PlanResult.EMPTY;
pathplanner.removePosition(plannedPosition); //obviously not walkable anymore
if(falling) {
return new PlanResult(1000.0, () -> setTNT(position.subtract(new Vector(0, 1.0, 0))));
} else {
position.setX(position.getBlockX() + 0.5);
position.setZ(position.getBlockZ() + 0.5);
pathplanner.addPosition(position);
return PlanResult.EMPTY;
}
}
}
@ -180,7 +182,7 @@ public class LixfelAI extends AI {
timeReference = getEntity().getTicksLived();
int timeRemaining = time - (getEntity().getTicksLived() - timeReference);
return inRangeAndTime(rating, timeRemaining, vector, () -> new PlanResult(rating, () -> {
return pathplanner.inRangeAndTime(rating, timeRemaining, vector, () -> new PlanResult(rating, () -> {
interact(vector);
plans.remove(this);
}));
@ -234,7 +236,7 @@ public class LixfelAI extends AI {
});
}
return tnt.entrySet().stream().filter(entry -> !entry.getValue()).map(entry -> inRangeAndTime(90.0, FightState.infight() ? 0 : 1, entry.getKey(), () -> new PlanResult(100.0, () -> {
return tnt.entrySet().stream().filter(entry -> !entry.getValue()).map(entry -> pathplanner.inRangeAndTime(90.0, FightState.infight() ? 0 : 20*Config.PreFightDuration/AI.MOVEMENT_DELAY, entry.getKey(), () -> new PlanResult(100.0, () -> {
setTNT(entry.getKey());
entry.setValue(true);
@ -244,7 +246,7 @@ public class LixfelAI extends AI {
if(activator == null)
return PlanResult.EMPTY;
return inRange(110.0, activator, () -> new PlanResult(120.0, () -> {
return pathplanner.inRange(110.0, activator, () -> new PlanResult(120.0, () -> {
interact(activator);
fired();
}));

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
* 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
@ -17,13 +17,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.ai;
package de.steamwar.fightsystem.ai.lixfel;
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 de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.ai.AI;
import de.steamwar.fightsystem.utils.WorldeditWrapper;
import org.bukkit.Material;
import org.bukkit.block.Banner;
@ -32,6 +33,7 @@ import org.bukkit.block.data.type.*;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class LixfelPathplanner {
@ -76,14 +78,15 @@ public class LixfelPathplanner {
return new Vector(vector.getX() + offset, vector.getY() + height, vector.getZ() + offset);
}
private final Vector clipboardToSchem;
private final AI ai;
private final BlockVector3 diff;
private final List<Vector> ladders = new ArrayList<>();
private final List<Vector> walkable = new ArrayList<>();
private final Set<Vector> ladders = new HashSet<>();
private final Map<Vector, Vector[]> neighbours = new HashMap<>();
public LixfelPathplanner(Clipboard clipboard) {
clipboardToSchem = new Vector(Config.BluePasteRegion.getSizeX(), Config.BluePasteRegion.getSizeY(), Config.BluePasteRegion.getSizeZ())
public LixfelPathplanner(AI ai, Clipboard clipboard) {
this.ai = ai;
Vector clipboardToSchem = new Vector(Config.BluePasteRegion.getSizeX(), Config.BluePasteRegion.getSizeY(), Config.BluePasteRegion.getSizeZ())
.subtract(WorldeditWrapper.impl.getDimensions(clipboard))
.multiply(0.5)
.add(new Vector(Config.PreperationArea, 0, Config.PreperationArea));
@ -92,18 +95,8 @@ public class LixfelPathplanner {
fillWalkable(clipboard);
}
public Vector clipboardToSchem(BlockVector3 vector) {
return toBukkit(vector.subtract(diff), 0.0, 0.0);
}
public List<Vector> getLadders() {
return ladders;
}
public List<Vector> getWalkable() {
return walkable;
}
private void fillWalkable(Clipboard clipboard) {
List<Vector> walkable = new ArrayList<>();
Region region = clipboard.getRegion();
clipboard.getRegion().forEach(vector -> {
BlockVector3 below = vector.subtract(0, 1, 0);
@ -132,36 +125,82 @@ public class LixfelPathplanner {
}
}
@Deprecated
public Vector walkableNearby(double eyeHeight, double distance, List<Vector> nearby) {
List<Vector> moddedNearby = nearby.stream().map(n -> n.clone().subtract(new Vector(0, eyeHeight, 0))).collect(Collectors.toList());
return walkable.stream()
.filter(vector -> moddedNearby.stream()
.allMatch(n -> n.distance(vector) <= distance && !neighbouring(n, vector)))
.findAny().orElse(null);
public Vector clipboardToSchem(BlockVector3 vector) {
return toBukkit(vector.subtract(diff), 0.0, 0.0);
}
public boolean isLadder(Vector vector) {
return ladders.contains(vector);
}
public void addPosition(Vector vector) {
Vector[] n = neighbours.keySet().stream().filter(neighbour -> neighbouring(neighbour, vector)).toArray(Vector[]::new);
neighbours.put(vector, n);
for(Vector neighbour : n) {
neighbours.computeIfPresent(neighbour, (neighBour, array) -> {
array = Arrays.copyOf(array, array.length+1);
array[array.length-1] = vector;
return array;
});
}
}
public void removePosition(Vector vector) {
ladders.remove(vector);
Vector[] n = neighbours.remove(vector);
if(n == null)
return;
for(Vector neighbour : n) {
neighbours.computeIfPresent(neighbour, (neighBour, array) -> {
for(int i = 0; i < array.length; i++) {
if(array[i] == vector) {
Vector[] newArray = Arrays.copyOf(array, array.length-1);
System.arraycopy(array, i+1, newArray, i, newArray.length-i);
return newArray;
}
}
throw new IllegalStateException();
});
}
}
public PlanResult inRange(double rating, Vector target, Supplier<PlanResult> plan) {
return inRangeAndTime(rating, 0, target, plan);
}
public PlanResult inRangeAndTime(double rating, int time, Vector target, Supplier<PlanResult> plan) {
Vector position = ai.getPosition();
double eyeHeight = ai.getEntity().getEyeHeight();
boolean inRange = new Vector(0, eyeHeight, 0).add(position).distance(target) <= AI.INTERACTION_RANGE;
if(inRange)
return time <= 0 ? plan.get() : PlanResult.EMPTY;
List<Vector> path = new ArrayList<>(planToRange(position, new Vector(0, -eyeHeight, 0).add(target), 5.0));
if(path.isEmpty() || time > path.size()*AI.MOVEMENT_DELAY)
return PlanResult.EMPTY;
return new PlanResult(rating, () -> ai.move(path.get(0)));
}
public List<Vector> planToAnywhere(Vector start, Vector destination) {
Vector intermediate = walkable.stream().filter(vector -> neighbouring(vector, destination)).findAny().orElse(null);
Vector intermediate = neighbours.keySet().stream().filter(vector -> neighbouring(vector, destination)).findAny().orElse(null);
if(intermediate == null)
return Collections.emptyList();
List<Vector> plan = new ArrayList<>(plan(start, intermediate));
List<Vector> plan = new ArrayList<>(plan(start, Collections.singletonList(intermediate)));
plan.add(destination);
return plan;
}
public List<Vector> planToRange(Vector start, Vector destination, double range) {
return plan(start, walkable.stream().filter(vector -> vector.distance(destination) <= range).collect(Collectors.toList()));
return plan(start, neighbours.keySet().stream().filter(vector -> vector.distance(destination) <= range).collect(Collectors.toList()));
}
public List<Vector> plan(Vector start, Vector destination) {
return plan(start, Collections.singletonList(destination));
}
public List<Vector> plan(Vector start, List<Vector> destinations) {
private List<Vector> plan(Vector start, List<Vector> destinations) {
for(Vector destination : destinations)
if(neighbouring(start, destination))
return Collections.singletonList(destination);