Basic unit boost command support
This commit is contained in:
@@ -339,6 +339,7 @@ command.repair = Repair
|
|||||||
command.rebuild = Rebuild
|
command.rebuild = Rebuild
|
||||||
command.assist = Assist Player
|
command.assist = Assist Player
|
||||||
command.move = Move
|
command.move = Move
|
||||||
|
command.boost = Boost
|
||||||
openlink = Open Link
|
openlink = Open Link
|
||||||
copylink = Copy Link
|
copylink = Copy Link
|
||||||
back = Back
|
back = Back
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ public class UnitCommand{
|
|||||||
|
|
||||||
public static final UnitCommand
|
public static final UnitCommand
|
||||||
|
|
||||||
moveCommand = new UnitCommand("move", "right", u -> null),
|
moveCommand = new UnitCommand("move", "right", u -> null){{
|
||||||
|
drawTarget = true;
|
||||||
|
resetTarget = false;
|
||||||
|
}},
|
||||||
repairCommand = new UnitCommand("repair", "modeSurvival", u -> new RepairAI()),
|
repairCommand = new UnitCommand("repair", "modeSurvival", u -> new RepairAI()),
|
||||||
rebuildCommand = new UnitCommand("rebuild", "hammer", u -> new BuilderAI()),
|
rebuildCommand = new UnitCommand("rebuild", "hammer", u -> new BuilderAI()),
|
||||||
assistCommand = new UnitCommand("assist", "players", u -> {
|
assistCommand = new UnitCommand("assist", "players", u -> {
|
||||||
@@ -22,7 +25,12 @@ public class UnitCommand{
|
|||||||
ai.onlyAssist = true;
|
ai.onlyAssist = true;
|
||||||
return ai;
|
return ai;
|
||||||
}),
|
}),
|
||||||
mineCommand = new UnitCommand("mine", "production", u -> new MinerAI());
|
mineCommand = new UnitCommand("mine", "production", u -> new MinerAI()),
|
||||||
|
boostCommand = new UnitCommand("boost", "up", u -> new BoostAI()){{
|
||||||
|
switchToMove = false;
|
||||||
|
drawTarget = true;
|
||||||
|
resetTarget = false;
|
||||||
|
}};
|
||||||
|
|
||||||
/** Unique ID number. */
|
/** Unique ID number. */
|
||||||
public final int id;
|
public final int id;
|
||||||
@@ -32,6 +40,12 @@ public class UnitCommand{
|
|||||||
public final String icon;
|
public final String icon;
|
||||||
/** Controller that this unit will use when this command is used. Return null for "default" behavior. */
|
/** Controller that this unit will use when this command is used. Return null for "default" behavior. */
|
||||||
public final Func<Unit, AIController> controller;
|
public final Func<Unit, AIController> controller;
|
||||||
|
/** If true, this unit will automatically switch away to the move command when given a position. */
|
||||||
|
public boolean switchToMove = true;
|
||||||
|
/** Whether to draw the movement/attack target. */
|
||||||
|
public boolean drawTarget = false;
|
||||||
|
/** Whether to reset targets when switching to or from this command. */
|
||||||
|
public boolean resetTarget = true;
|
||||||
|
|
||||||
public UnitCommand(String name, String icon, Func<Unit, AIController> controller){
|
public UnitCommand(String name, String icon, Func<Unit, AIController> controller){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|||||||
21
core/src/mindustry/ai/types/BoostAI.java
Normal file
21
core/src/mindustry/ai/types/BoostAI.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package mindustry.ai.types;
|
||||||
|
|
||||||
|
import mindustry.ai.*;
|
||||||
|
import mindustry.entities.units.*;
|
||||||
|
|
||||||
|
//not meant to be used outside RTS-AI-controlled units
|
||||||
|
public class BoostAI extends AIController{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateUnit(){
|
||||||
|
if(unit.controller() instanceof CommandAI ai){
|
||||||
|
ai.defaultBehavior();
|
||||||
|
unit.updateBoosting(true);
|
||||||
|
|
||||||
|
//auto land when near target
|
||||||
|
if(ai.attackTarget != null && unit.within(ai.attackTarget, unit.range())){
|
||||||
|
unit.command().command(UnitCommand.moveCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,8 +35,8 @@ public class CommandAI extends AIController{
|
|||||||
/** Last command type assigned. Used for detecting command changes. */
|
/** Last command type assigned. Used for detecting command changes. */
|
||||||
protected @Nullable UnitCommand lastCommand;
|
protected @Nullable UnitCommand lastCommand;
|
||||||
|
|
||||||
public @Nullable UnitCommand currentCommand(){
|
public UnitCommand currentCommand(){
|
||||||
return command;
|
return command == null ? UnitCommand.moveCommand : command;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Attempts to assign a command to this unit. If not supported by the unit type, does nothing. */
|
/** Attempts to assign a command to this unit. If not supported by the unit type, does nothing. */
|
||||||
@@ -62,7 +62,7 @@ public class CommandAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//update command controller based on index.
|
//update command controller based on index.
|
||||||
var curCommand = currentCommand();
|
var curCommand = command;
|
||||||
if(lastCommand != curCommand){
|
if(lastCommand != curCommand){
|
||||||
lastCommand = curCommand;
|
lastCommand = curCommand;
|
||||||
commandController = (curCommand == null ? null : curCommand.controller.get(unit));
|
commandController = (curCommand == null ? null : curCommand.controller.get(unit));
|
||||||
@@ -72,8 +72,14 @@ public class CommandAI extends AIController{
|
|||||||
if(commandController != null){
|
if(commandController != null){
|
||||||
if(commandController.unit() != unit) commandController.unit(unit);
|
if(commandController.unit() != unit) commandController.unit(unit);
|
||||||
commandController.updateUnit();
|
commandController.updateUnit();
|
||||||
return;
|
}else{
|
||||||
|
defaultBehavior();
|
||||||
|
//boosting control is not supported, so just don't.
|
||||||
|
unit.updateBoosting(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaultBehavior(){
|
||||||
|
|
||||||
//acquiring naval targets isn't supported yet, so use the fallback dumb AI
|
//acquiring naval targets isn't supported yet, so use the fallback dumb AI
|
||||||
if(unit.team.isAI() && unit.team.rules().rtsAi && unit.type.naval){
|
if(unit.team.isAI() && unit.team.rules().rtsAi && unit.type.naval){
|
||||||
@@ -162,10 +168,10 @@ public class CommandAI extends AIController{
|
|||||||
circleAttack(80f);
|
circleAttack(80f);
|
||||||
}else{
|
}else{
|
||||||
moveTo(vecOut,
|
moveTo(vecOut,
|
||||||
attackTarget != null && unit.within(attackTarget, engageRange) ? engageRange :
|
attackTarget != null && unit.within(attackTarget, engageRange) ? engageRange :
|
||||||
unit.isGrounded() ? 0f :
|
unit.isGrounded() ? 0f :
|
||||||
attackTarget != null ? engageRange :
|
attackTarget != null ? engageRange :
|
||||||
0f, unit.isFlying() ? 40f : 100f, false, null, targetPos.epsilonEquals(vecOut, 4.1f));
|
0f, unit.isFlying() ? 40f : 100f, false, null, targetPos.epsilonEquals(vecOut, 4.1f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,9 +213,6 @@ public class CommandAI extends AIController{
|
|||||||
}else if(target != null){
|
}else if(target != null){
|
||||||
faceTarget();
|
faceTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
//boosting control is not supported, so just don't.
|
|
||||||
unit.updateBoosting(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -249,8 +252,12 @@ public class CommandAI extends AIController{
|
|||||||
lastTargetPos = targetPos;
|
lastTargetPos = targetPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void commandPosition(Vec2 pos){
|
public void commandPosition(Vec2 pos){
|
||||||
commandPosition(pos, false);
|
commandPosition(pos, false);
|
||||||
|
if(commandController != null){
|
||||||
|
commandController.commandPosition(pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void commandPosition(Vec2 pos, boolean stopWhenInRange){
|
public void commandPosition(Vec2 pos, boolean stopWhenInRange){
|
||||||
@@ -261,8 +268,12 @@ public class CommandAI extends AIController{
|
|||||||
this.stopWhenInRange = stopWhenInRange;
|
this.stopWhenInRange = stopWhenInRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void commandTarget(Teamc moveTo){
|
public void commandTarget(Teamc moveTo){
|
||||||
commandTarget(moveTo, false);
|
commandTarget(moveTo, false);
|
||||||
|
if(commandController != null){
|
||||||
|
commandController.commandTarget(moveTo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void commandTarget(Teamc moveTo, boolean stopAtTarget){
|
public void commandTarget(Teamc moveTo, boolean stopAtTarget){
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateBoosting(boolean boost){
|
public void updateBoosting(boolean boost){
|
||||||
if(!type.canBoost) return;
|
if(!type.canBoost || dead) return;
|
||||||
|
|
||||||
elevation = Mathf.approachDelta(elevation, type.canBoost ? Mathf.num(boost || onSolid() || (isFlying() && !canLand())) : 0f, type.riseSpeed);
|
elevation = Mathf.approachDelta(elevation, type.canBoost ? Mathf.num(boost || onSolid() || (isFlying() && !canLand())) : 0f, type.riseSpeed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,10 @@ public class AIController implements UnitController{
|
|||||||
return target(x, y, range, air, ground);
|
return target(x, y, range, air, ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void commandTarget(Teamc moveTo){}
|
||||||
|
|
||||||
|
public void commandPosition(Vec2 pos){}
|
||||||
|
|
||||||
/** Called after this controller is assigned a unit. */
|
/** Called after this controller is assigned a unit. */
|
||||||
public void init(){
|
public void init(){
|
||||||
|
|
||||||
|
|||||||
@@ -231,10 +231,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
if(unit != null && unit.team == player.team() && unit.controller() instanceof CommandAI ai){
|
if(unit != null && unit.team == player.team() && unit.controller() instanceof CommandAI ai){
|
||||||
|
|
||||||
//implicitly order it to move
|
//implicitly order it to move
|
||||||
ai.command(UnitCommand.moveCommand);
|
if(ai.command == null || ai.command.switchToMove){
|
||||||
|
ai.command(UnitCommand.moveCommand);
|
||||||
|
}
|
||||||
|
|
||||||
if(teamTarget != null && teamTarget.team() != player.team()){
|
if(teamTarget != null && teamTarget.team() != player.team()){
|
||||||
ai.commandTarget(teamTarget);
|
ai.commandTarget(teamTarget);
|
||||||
|
|
||||||
}else if(posTarget != null){
|
}else if(posTarget != null){
|
||||||
ai.commandPosition(posTarget);
|
ai.commandPosition(posTarget);
|
||||||
}
|
}
|
||||||
@@ -269,10 +272,12 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
for(int id : unitIds){
|
for(int id : unitIds){
|
||||||
Unit unit = Groups.unit.getByID(id);
|
Unit unit = Groups.unit.getByID(id);
|
||||||
if(unit != null && unit.team == player.team() && unit.controller() instanceof CommandAI ai){
|
if(unit != null && unit.team == player.team() && unit.controller() instanceof CommandAI ai){
|
||||||
|
boolean reset = command.resetTarget || ai.currentCommand().resetTarget;
|
||||||
ai.command(command);
|
ai.command(command);
|
||||||
//reset targeting
|
if(reset){
|
||||||
ai.targetPos = null;
|
ai.targetPos = null;
|
||||||
ai.attackTarget = null;
|
ai.attackTarget = null;
|
||||||
|
}
|
||||||
unit.lastCommanded = player.coloredName();
|
unit.lastCommanded = player.coloredName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -868,7 +873,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
for(Unit unit : selectedUnits){
|
for(Unit unit : selectedUnits){
|
||||||
CommandAI ai = unit.command();
|
CommandAI ai = unit.command();
|
||||||
//draw target line
|
//draw target line
|
||||||
if(ai.targetPos != null && ai.command == UnitCommand.moveCommand){
|
if(ai.targetPos != null && ai.currentCommand().drawTarget){
|
||||||
Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos;
|
Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos;
|
||||||
Drawf.limitLine(unit, lineDest, unit.hitSize / 2f, 3.5f);
|
Drawf.limitLine(unit, lineDest, unit.hitSize / 2f, 3.5f);
|
||||||
|
|
||||||
@@ -880,7 +885,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
Drawf.square(unit.x, unit.y, unit.hitSize / 1.4f + 1f);
|
Drawf.square(unit.x, unit.y, unit.hitSize / 1.4f + 1f);
|
||||||
|
|
||||||
//TODO when to draw, when to not?
|
//TODO when to draw, when to not?
|
||||||
if(ai.attackTarget != null && ai.command == UnitCommand.moveCommand){
|
if(ai.attackTarget != null && ai.currentCommand().drawTarget){
|
||||||
Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove);
|
Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -805,6 +805,10 @@ public class UnitType extends UnlockableContent{
|
|||||||
|
|
||||||
cmds.add(UnitCommand.moveCommand);
|
cmds.add(UnitCommand.moveCommand);
|
||||||
|
|
||||||
|
if(canBoost){
|
||||||
|
cmds.add(UnitCommand.boostCommand);
|
||||||
|
}
|
||||||
|
|
||||||
//healing, mining and building is only supported for flying units; pathfinding to ambiguously reachable locations is hard.
|
//healing, mining and building is only supported for flying units; pathfinding to ambiguously reachable locations is hard.
|
||||||
if(flying){
|
if(flying){
|
||||||
if(canHeal){
|
if(canHeal){
|
||||||
|
|||||||
@@ -514,7 +514,7 @@ public class PlacementFragment{
|
|||||||
//find the command that all units have, or null if they do not share one
|
//find the command that all units have, or null if they do not share one
|
||||||
for(var unit : control.input.selectedUnits){
|
for(var unit : control.input.selectedUnits){
|
||||||
if(unit.isCommandable()){
|
if(unit.isCommandable()){
|
||||||
var nextCommand = unit.command().currentCommand();
|
var nextCommand = unit.command().command;
|
||||||
|
|
||||||
if(hadCommand){
|
if(hadCommand){
|
||||||
if(shareCommand != nextCommand){
|
if(shareCommand != nextCommand){
|
||||||
|
|||||||
Reference in New Issue
Block a user