WIP command order system
This commit is contained in:
52
core/src/mindustry/ai/UnitCommand.java
Normal file
52
core/src/mindustry/ai/UnitCommand.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package mindustry.ai;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
/** Defines a pattern of behavior that an RTS-controlled unit should follow. Shows up in the command UI. */
|
||||
public class UnitCommand{
|
||||
/** List of all commands by ID. */
|
||||
public static final Seq<UnitCommand> all = new Seq<>();
|
||||
|
||||
public static final UnitCommand
|
||||
|
||||
//TODO they do not use the command "interface" or designation at all
|
||||
moveCommand = new UnitCommand("move", "right", u -> null),
|
||||
repairCommand = new UnitCommand("repair", "modeSurvival", u -> new RepairAI()),
|
||||
rebuildCommand = new UnitCommand("rebuild", "hammer", u -> new BuilderAI()),
|
||||
assistCommand = new UnitCommand("assist", "players", u -> {
|
||||
var ai = new BuilderAI();
|
||||
ai.onlyAssist = true;
|
||||
return ai;
|
||||
}),
|
||||
mineCommand = new UnitCommand("mine", "production", u -> new MinerAI());
|
||||
|
||||
/** Default set of specified commands. */
|
||||
public static final UnitCommand[] defaultCommands = {moveCommand};
|
||||
|
||||
/** Unique ID number. */
|
||||
public final int id;
|
||||
/** Named used for tooltip/description. */
|
||||
public final String name;
|
||||
/** Name of UI icon (from Icon class). */
|
||||
public final String icon;
|
||||
/** Controller that this unit will use when this command is used. Return null for "default" behavior. */
|
||||
public final Func<Unit, AIController> controller;
|
||||
|
||||
public UnitCommand(String name, String icon, Func<Unit, AIController> controller){
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
this.controller = controller;
|
||||
|
||||
id = all.size;
|
||||
all.add(this);
|
||||
}
|
||||
|
||||
public String localized(){
|
||||
return Core.bundle.get("command." + name);
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,14 @@ import static mindustry.Vars.*;
|
||||
public class BuilderAI extends AIController{
|
||||
public static float buildRadius = 1500, retreatDst = 110f, retreatDelay = Time.toSeconds * 2f;
|
||||
|
||||
public @Nullable Unit assistFollowing;
|
||||
public @Nullable Unit following;
|
||||
public @Nullable Teamc enemy;
|
||||
public @Nullable BlockPlan lastPlan;
|
||||
|
||||
public float fleeRange = 370f;
|
||||
public boolean alwaysFlee;
|
||||
public boolean onlyAssist;
|
||||
|
||||
boolean found = false;
|
||||
float retreatTimer;
|
||||
@@ -41,6 +43,10 @@ public class BuilderAI extends AIController{
|
||||
|
||||
unit.updateBuilding = true;
|
||||
|
||||
if(assistFollowing != null && assistFollowing.activelyBuilding()){
|
||||
following = assistFollowing;
|
||||
}
|
||||
|
||||
if(following != null){
|
||||
retreatTimer = 0f;
|
||||
//try to follow and mimic someone
|
||||
@@ -108,6 +114,10 @@ public class BuilderAI extends AIController{
|
||||
}
|
||||
}else{
|
||||
|
||||
if(assistFollowing != null){
|
||||
moveTo(assistFollowing, assistFollowing.type.hitSize * 1.5f + 60f);
|
||||
}
|
||||
|
||||
//follow someone and help them build
|
||||
if(timer.get(timerTarget2, 60f)){
|
||||
found = false;
|
||||
@@ -130,13 +140,29 @@ public class BuilderAI extends AIController{
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(onlyAssist){
|
||||
float minDst = Float.MAX_VALUE;
|
||||
Player closest = null;
|
||||
for(var player : Groups.player){
|
||||
if(player.unit().canBuild() && !player.dead()){
|
||||
float dst = player.dst2(unit);
|
||||
if(dst < minDst){
|
||||
closest = player;
|
||||
minDst = dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assistFollowing = closest == null ? null : closest.unit();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO this is bad, rebuild time should not depend on AI here
|
||||
float rebuildTime = (unit.team.rules().rtsAi ? 12f : 2f) * 60f;
|
||||
|
||||
//find new plan
|
||||
if(!unit.team.data().plans.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){
|
||||
if(!onlyAssist && !unit.team.data().plans.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){
|
||||
Queue<BlockPlan> blocks = unit.team.data().plans;
|
||||
BlockPlan block = blocks.first();
|
||||
|
||||
|
||||
@@ -12,20 +12,61 @@ import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class CommandAI extends AIController{
|
||||
private static final float localInterval = 40f;
|
||||
private static final Vec2 vecOut = new Vec2(), flockVec = new Vec2(), separation = new Vec2(), cohesion = new Vec2(), massCenter = new Vec2();
|
||||
protected static final float localInterval = 40f;
|
||||
protected static final Vec2 vecOut = new Vec2(), flockVec = new Vec2(), separation = new Vec2(), cohesion = new Vec2(), massCenter = new Vec2();
|
||||
|
||||
public @Nullable Vec2 targetPos;
|
||||
public @Nullable Teamc attackTarget;
|
||||
|
||||
private boolean stopAtTarget;
|
||||
private Vec2 lastTargetPos;
|
||||
private int pathId = -1;
|
||||
private Seq<Unit> local = new Seq<>(false);
|
||||
private boolean flocked;
|
||||
protected boolean stopAtTarget;
|
||||
protected Vec2 lastTargetPos;
|
||||
protected int pathId = -1;
|
||||
protected Seq<Unit> local = new Seq<>(false);
|
||||
protected boolean flocked;
|
||||
|
||||
/** Current command this unit is following. */
|
||||
public @Nullable UnitCommand command;
|
||||
/** Current controller instance based on command. */
|
||||
protected @Nullable AIController commandController;
|
||||
/** Last command type assigned. Used for detecting command changes. */
|
||||
protected @Nullable UnitCommand lastCommand;
|
||||
|
||||
public @Nullable UnitCommand currentCommand(){
|
||||
return command;
|
||||
}
|
||||
|
||||
/** Attempts to assign a command to this unit. If not supported by the unit type, does nothing. */
|
||||
public void command(UnitCommand command){
|
||||
if(Structs.contains(unit.type.commands, command)){
|
||||
//clear old state.
|
||||
unit.mineTile = null;
|
||||
unit.clearBuilding();
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUnit(){
|
||||
|
||||
//assign defaults
|
||||
if(command == null && unit.type.commands.length > 0){
|
||||
command = unit.type.defaultCommand == null ? unit.type.commands[0] : unit.type.defaultCommand;
|
||||
}
|
||||
|
||||
//update command controller based on index.
|
||||
var curCommand = currentCommand();
|
||||
if(lastCommand != curCommand){
|
||||
lastCommand = curCommand;
|
||||
commandController = (curCommand == null ? null : curCommand.controller.get(unit));
|
||||
}
|
||||
|
||||
//use the command controller if it is provided, and bail out.
|
||||
if(commandController != null){
|
||||
if(commandController.unit() != unit) commandController.unit(unit);
|
||||
commandController.updateUnit();
|
||||
return;
|
||||
}
|
||||
|
||||
updateVisuals();
|
||||
updateTargeting();
|
||||
|
||||
@@ -104,6 +145,7 @@ public class CommandAI extends AIController{
|
||||
}
|
||||
|
||||
if(attackTarget == null){
|
||||
//TODO overshoot.
|
||||
if(unit.within(targetPos, Math.max(5f, unit.hitSize / 2f))){
|
||||
targetPos = null;
|
||||
}else if(local.size > 1){
|
||||
@@ -134,10 +176,55 @@ public class CommandAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keepState(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
return attackTarget == null || !attackTarget.within(x, y, range + 3f + (attackTarget instanceof Sized s ? s.hitSize()/2f : 0f)) ? super.findTarget(x, y, range, air, ground) : attackTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retarget(){
|
||||
//retarget faster when there is an explicit target
|
||||
return attackTarget != null ? timer.get(timerTarget, 10) : timer.get(timerTarget, 20);
|
||||
}
|
||||
|
||||
public boolean hasCommand(){
|
||||
return targetPos != null;
|
||||
}
|
||||
|
||||
public void setupLastPos(){
|
||||
lastTargetPos = targetPos;
|
||||
}
|
||||
|
||||
public void commandPosition(Vec2 pos){
|
||||
targetPos = pos;
|
||||
lastTargetPos = pos;
|
||||
attackTarget = null;
|
||||
pathId = Vars.controlPath.nextTargetId();
|
||||
}
|
||||
|
||||
public void commandTarget(Teamc moveTo){
|
||||
commandTarget(moveTo, false);
|
||||
}
|
||||
|
||||
public void commandTarget(Teamc moveTo, boolean stopAtTarget){
|
||||
attackTarget = moveTo;
|
||||
this.stopAtTarget = stopAtTarget;
|
||||
pathId = Vars.controlPath.nextTargetId();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
//TODO ひどい
|
||||
(does not work)
|
||||
|
||||
public static float cohesionScl = 0.3f;
|
||||
public static float cohesionRad = 3f, separationRad = 1.1f, separationScl = 1f, flockMult = 0.5f;
|
||||
|
||||
//TODO ひどい
|
||||
Vec2 calculateFlock(){
|
||||
if(local.isEmpty()) return flockVec.setZero();
|
||||
|
||||
@@ -177,47 +264,5 @@ public class CommandAI extends AIController{
|
||||
}
|
||||
|
||||
return flockVec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keepState(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
return attackTarget == null || !attackTarget.within(x, y, range + 3f + (attackTarget instanceof Sized s ? s.hitSize()/2f : 0f)) ? super.findTarget(x, y, range, air, ground) : attackTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retarget(){
|
||||
//retarget faster when there is an explicit target
|
||||
return attackTarget != null ? timer.get(timerTarget, 10) : timer.get(timerTarget, 20);
|
||||
}
|
||||
|
||||
public boolean hasCommand(){
|
||||
return targetPos != null;
|
||||
}
|
||||
|
||||
public void setupLastPos(){
|
||||
lastTargetPos = targetPos;
|
||||
|
||||
}
|
||||
|
||||
public void commandPosition(Vec2 pos){
|
||||
targetPos = pos;
|
||||
lastTargetPos = pos;
|
||||
attackTarget = null;
|
||||
pathId = Vars.controlPath.nextTargetId();
|
||||
}
|
||||
|
||||
public void commandTarget(Teamc moveTo){
|
||||
commandTarget(moveTo, false);
|
||||
}
|
||||
|
||||
public void commandTarget(Teamc moveTo, boolean stopAtTarget){
|
||||
attackTarget = moveTo;
|
||||
this.stopAtTarget = stopAtTarget;
|
||||
pathId = Vars.controlPath.nextTargetId();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user