Allow controlling/selecting units with LogicAI
This commit is contained in:
@@ -473,6 +473,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
return controller instanceof AIController;
|
||||
}
|
||||
|
||||
/** @return whether the unit *can* be commanded, even if its controller is not currently CommandAI. */
|
||||
public boolean allowCommand(){
|
||||
return controller instanceof CommandAI || (controller instanceof LogicAI && type.allowChangeCommands);
|
||||
}
|
||||
|
||||
/** @return whether the unit has a CommandAI controller */
|
||||
public boolean isCommandable(){
|
||||
return controller instanceof CommandAI;
|
||||
}
|
||||
|
||||
@@ -288,14 +288,14 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
//validate commanding units
|
||||
selectedUnits.removeAll(u -> !u.isCommandable() || !u.isValid() || u.team != player.team());
|
||||
selectedUnits.removeAll(u -> !u.allowCommand() || !u.isValid() || u.team != player.team());
|
||||
|
||||
if(commandMode && !scene.hasField() && !scene.hasDialog()){
|
||||
if(input.keyTap(Binding.select_all_units)){
|
||||
selectedUnits.clear();
|
||||
commandBuildings.clear();
|
||||
for(var unit : player.team().data().units){
|
||||
if(unit.isCommandable()){
|
||||
if(unit.allowCommand()){
|
||||
selectedUnits.add(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,43 +261,50 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
for(int id : unitIds){
|
||||
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()){
|
||||
|
||||
//implicitly order it to move
|
||||
if(ai.command == null || ai.command.switchToMove){
|
||||
ai.command(UnitCommand.moveCommand);
|
||||
if(unit.controller() instanceof LogicAI){
|
||||
//reset to commandAI if applicable
|
||||
unit.resetController();
|
||||
}
|
||||
|
||||
if(teamTarget != null && teamTarget.team() != player.team() &&
|
||||
!(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){
|
||||
|
||||
anyCommandedTarget = true;
|
||||
if(queueCommand){
|
||||
ai.commandQueue(teamTarget);
|
||||
}else{
|
||||
ai.commandQueue.clear();
|
||||
ai.commandTarget(teamTarget);
|
||||
if(unit.controller() instanceof CommandAI ai){
|
||||
//implicitly order it to move
|
||||
if(ai.command == null || ai.command.switchToMove){
|
||||
ai.command(UnitCommand.moveCommand);
|
||||
}
|
||||
}else if(posTarget != null){
|
||||
if(queueCommand){
|
||||
ai.commandQueue(posTarget);
|
||||
}else{
|
||||
ai.commandQueue.clear();
|
||||
ai.commandPosition(posTarget);
|
||||
|
||||
if(teamTarget != null && teamTarget.team() != player.team() &&
|
||||
!(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){
|
||||
|
||||
anyCommandedTarget = true;
|
||||
if(queueCommand){
|
||||
ai.commandQueue(teamTarget);
|
||||
}else{
|
||||
ai.commandQueue.clear();
|
||||
ai.commandTarget(teamTarget);
|
||||
}
|
||||
}else if(posTarget != null){
|
||||
if(queueCommand){
|
||||
ai.commandQueue(posTarget);
|
||||
}else{
|
||||
ai.commandQueue.clear();
|
||||
ai.commandPosition(posTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unit.lastCommanded = player.coloredName();
|
||||
if(ai.commandQueue.size <= 0){
|
||||
ai.group = null;
|
||||
}
|
||||
unit.lastCommanded = player.coloredName();
|
||||
if(ai.commandQueue.size <= 0){
|
||||
ai.group = null;
|
||||
}
|
||||
|
||||
//remove when other player command
|
||||
if(!headless && player != Vars.player){
|
||||
control.input.selectedUnits.remove(unit);
|
||||
}
|
||||
//remove when other player command
|
||||
if(!headless && player != Vars.player){
|
||||
control.input.selectedUnits.remove(unit);
|
||||
}
|
||||
|
||||
toAdd.add(unit);
|
||||
toAdd.add(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1093,38 +1100,42 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
public void drawCommanded(boolean flying){
|
||||
float lineLimit = 6.5f;
|
||||
Color color = Pal.accent;
|
||||
int sides = 6;
|
||||
float alpha = 0.5f;
|
||||
|
||||
if(commandMode){
|
||||
//happens sometimes
|
||||
selectedUnits.removeAll(u -> !u.isCommandable());
|
||||
selectedUnits.removeAll(u -> !u.allowCommand());
|
||||
|
||||
//draw command overlay UI
|
||||
for(Unit unit : selectedUnits){
|
||||
|
||||
CommandAI ai = unit.command();
|
||||
Position lastPos = ai.attackTarget != null ? ai.attackTarget : ai.targetPos;
|
||||
Color color = unit.controller() instanceof LogicAI ? Team.malis.color : Pal.accent;
|
||||
|
||||
if(flying && ai.attackTarget != null && ai.currentCommand().drawTarget){
|
||||
Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove);
|
||||
}
|
||||
Position lastPos = null;
|
||||
|
||||
if(unit.isFlying() != flying) continue;
|
||||
if(unit.controller() instanceof CommandAI ai){
|
||||
lastPos = ai.attackTarget != null ? ai.attackTarget : ai.targetPos;
|
||||
|
||||
//draw target line
|
||||
if(ai.targetPos != null && ai.currentCommand().drawTarget){
|
||||
Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos;
|
||||
Drawf.limitLine(unit, lineDest, unit.hitSize / unitSelectRadScl + 1f, lineLimit, color.write(Tmp.c1).a(alpha));
|
||||
if(flying && ai.attackTarget != null && ai.currentCommand().drawTarget){
|
||||
Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove);
|
||||
}
|
||||
|
||||
if(ai.attackTarget == null){
|
||||
Drawf.square(lineDest.getX(), lineDest.getY(), 3.5f, color.write(Tmp.c1).a(alpha));
|
||||
if(unit.isFlying() != flying) continue;
|
||||
|
||||
if(ai.currentCommand() == UnitCommand.enterPayloadCommand){
|
||||
var build = world.buildWorld(lineDest.getX(), lineDest.getY());
|
||||
if(build != null && build.block.acceptsUnitPayloads && build.team == unit.team){
|
||||
Drawf.selected(build, color);
|
||||
//draw target line
|
||||
if(ai.targetPos != null && ai.currentCommand().drawTarget){
|
||||
Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos;
|
||||
Drawf.limitLine(unit, lineDest, unit.hitSize / unitSelectRadScl + 1f, lineLimit, color.write(Tmp.c1).a(alpha));
|
||||
|
||||
if(ai.attackTarget == null){
|
||||
Drawf.square(lineDest.getX(), lineDest.getY(), 3.5f, color.write(Tmp.c1).a(alpha));
|
||||
|
||||
if(ai.currentCommand() == UnitCommand.enterPayloadCommand){
|
||||
var build = world.buildWorld(lineDest.getX(), lineDest.getY());
|
||||
if(build != null && build.block.acceptsUnitPayloads && build.team == unit.team){
|
||||
Drawf.selected(build, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1148,42 +1159,43 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
//Lines.poly(unit.x, unit.y, sides, rad + 1.5f);
|
||||
Draw.reset();
|
||||
|
||||
|
||||
|
||||
if(lastPos == null){
|
||||
lastPos = unit;
|
||||
}
|
||||
|
||||
//draw command queue
|
||||
if(ai.currentCommand().drawTarget && ai.commandQueue.size > 0){
|
||||
for(var next : ai.commandQueue){
|
||||
Drawf.limitLine(lastPos, next, lineLimit, lineLimit, color.write(Tmp.c1).a(alpha));
|
||||
lastPos = next;
|
||||
if(unit.controller() instanceof CommandAI ai){
|
||||
//draw command queue
|
||||
if(ai.currentCommand().drawTarget && ai.commandQueue.size > 0){
|
||||
for(var next : ai.commandQueue){
|
||||
Drawf.limitLine(lastPos, next, lineLimit, lineLimit, color.write(Tmp.c1).a(alpha));
|
||||
lastPos = next;
|
||||
|
||||
if(next instanceof Vec2 vec){
|
||||
Drawf.square(vec.x, vec.y, 3.5f, color.write(Tmp.c1).a(alpha));
|
||||
}else{
|
||||
Drawf.target(next.getX(), next.getY(), 6f, Pal.remove);
|
||||
if(next instanceof Vec2 vec){
|
||||
Drawf.square(vec.x, vec.y, 3.5f, color.write(Tmp.c1).a(alpha));
|
||||
}else{
|
||||
Drawf.target(next.getX(), next.getY(), 6f, Pal.remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ai.targetPos != null && ai.currentCommand() == UnitCommand.loopPayloadCommand && unit instanceof Payloadc pay){
|
||||
Draw.color(color, 0.4f + Mathf.absin(5f, 0.5f));
|
||||
TextureRegion region = pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion();
|
||||
float offset = 11f;
|
||||
float size = 8f;
|
||||
Draw.rect(region, ai.targetPos.x, ai.targetPos.y + offset, size, size / region.ratio());
|
||||
if(ai.targetPos != null && ai.currentCommand() == UnitCommand.loopPayloadCommand && unit instanceof Payloadc pay){
|
||||
Draw.color(color, 0.4f + Mathf.absin(5f, 0.5f));
|
||||
TextureRegion region = pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion();
|
||||
float offset = 11f;
|
||||
float size = 8f;
|
||||
Draw.rect(region, ai.targetPos.x, ai.targetPos.y + offset, size, size / region.ratio());
|
||||
|
||||
if(ai.commandQueue.size > 0){
|
||||
region = !pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion();
|
||||
Draw.rect(region, ai.commandQueue.first().getX(), ai.commandQueue.first().getY() + offset, size, size / region.ratio());
|
||||
if(ai.commandQueue.size > 0){
|
||||
region = !pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion();
|
||||
Draw.rect(region, ai.commandQueue.first().getX(), ai.commandQueue.first().getY() + offset, size, size / region.ratio());
|
||||
}
|
||||
Draw.color();
|
||||
}
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
|
||||
if(flying){
|
||||
Color color = Pal.accent;
|
||||
for(var commandBuild : commandBuildings){
|
||||
if(commandBuild != null){
|
||||
Drawf.square(commandBuild.x, commandBuild.y, commandBuild.hitSize() / 1.4f + 1f);
|
||||
@@ -1897,7 +1909,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
tmpUnits.clear();
|
||||
float rad = 4f;
|
||||
tree.intersect(x - rad/2f, y - rad/2f, rad, rad, tmpUnits);
|
||||
return tmpUnits.min(u -> u.isCommandable(), u -> u.dst(x, y) - u.hitSize/2f);
|
||||
return tmpUnits.min(u -> u.allowCommand(), u -> u.dst(x, y) - u.hitSize/2f);
|
||||
}
|
||||
|
||||
public @Nullable Unit selectedEnemyUnit(float x, float y){
|
||||
@@ -1919,7 +1931,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
tmpUnits.clear();
|
||||
float rad = 4f;
|
||||
tree.intersect(Tmp.r1.set(x - rad/2f, y - rad/2f, rad*2f + w, rad*2f + h).normalize(), tmpUnits);
|
||||
tmpUnits.removeAll(u -> !u.isCommandable() || !predicate.get(u));
|
||||
tmpUnits.removeAll(u -> !u.allowCommand() || !predicate.get(u));
|
||||
return tmpUnits;
|
||||
}
|
||||
|
||||
|
||||
@@ -770,7 +770,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
|
||||
//validate commanding units
|
||||
selectedUnits.removeAll(u -> !u.isCommandable() || !u.isValid() || u.team != player.team());
|
||||
selectedUnits.removeAll(u -> !u.allowCommand() || !u.isValid() || u.team != player.team());
|
||||
|
||||
if(!commandMode){
|
||||
commandBuildings.clear();
|
||||
|
||||
@@ -109,6 +109,13 @@ public class LandingPad extends Block{
|
||||
addBar("cooldown", (LandingPadBuild entity) -> new Bar("bar.cooldown", Pal.lightOrange, () -> entity.cooldown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.cooldownTime, (cooldownTime+arrivalDuration)/60f, StatUnit.seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outputsItems(){
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user