Added 'hold position' stance (closes Anuken/Mindustry-Suggestions/issues/6157)
This commit is contained in:
@@ -380,6 +380,7 @@ stance.shoot = Stance: Shoot
|
|||||||
stance.holdfire = Stance: Hold Fire
|
stance.holdfire = Stance: Hold Fire
|
||||||
stance.pursuetarget = Stance: Pursue Target
|
stance.pursuetarget = Stance: Pursue Target
|
||||||
stance.patrol = Stance: Patrol Path
|
stance.patrol = Stance: Patrol Path
|
||||||
|
stance.holdposition = Stance: Hold Position
|
||||||
stance.ram = Stance: Ram\n[lightgray]Straight line movement, no pathfinding
|
stance.ram = Stance: Ram\n[lightgray]Straight line movement, no pathfinding
|
||||||
stance.boost = Boost
|
stance.boost = Boost
|
||||||
stance.mineauto = Automatic Mining
|
stance.mineauto = Automatic Mining
|
||||||
@@ -1364,6 +1365,7 @@ keybind.unit_stance_pursue_target.name = Unit Stance: Pursue Target
|
|||||||
keybind.unit_stance_patrol.name = Unit Stance: Patrol
|
keybind.unit_stance_patrol.name = Unit Stance: Patrol
|
||||||
keybind.unit_stance_ram.name = Unit Stance: Ram
|
keybind.unit_stance_ram.name = Unit Stance: Ram
|
||||||
keybind.unit_stance_boost.name = Unit Stance: Boost
|
keybind.unit_stance_boost.name = Unit Stance: Boost
|
||||||
|
keybind.unit_stance_hold_position.name = Unit Stance: Hold Position
|
||||||
|
|
||||||
keybind.unit_command_move.name = Unit Command: Move
|
keybind.unit_command_move.name = Unit Command: Move
|
||||||
keybind.unit_command_repair.name = Unit Command: Repair
|
keybind.unit_command_repair.name = Unit Command: Repair
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import arc.*;
|
|||||||
import arc.func.*;
|
import arc.func.*;
|
||||||
import arc.input.*;
|
import arc.input.*;
|
||||||
import arc.scene.style.*;
|
import arc.scene.style.*;
|
||||||
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import mindustry.ai.types.*;
|
import mindustry.ai.types.*;
|
||||||
import mindustry.ctype.*;
|
import mindustry.ctype.*;
|
||||||
@@ -27,12 +28,14 @@ public class UnitCommand extends MappableContent{
|
|||||||
public boolean resetTarget = true;
|
public boolean resetTarget = true;
|
||||||
/** Whether to snap the command destination to ally buildings. */
|
/** Whether to snap the command destination to ally buildings. */
|
||||||
public boolean snapToBuilding = false;
|
public boolean snapToBuilding = false;
|
||||||
/** */
|
/** If true, the unit will arrive at this command's exact endpoint. */
|
||||||
public boolean exactArrival = false;
|
public boolean exactArrival = false;
|
||||||
/** If true, this command refreshes the list of stances when selected TODO: do not use, this will likely be removed later!*/
|
/** If true, this command refreshes the list of stances when selected TODO: do not use, this will likely be removed later!*/
|
||||||
public boolean refreshOnSelect = false;
|
public boolean refreshOnSelect = false;
|
||||||
/** Key to press for this command. */
|
/** Key to press for this command. */
|
||||||
public @Nullable KeyBind keybind = null;
|
public @Nullable KeyBind keybind = null;
|
||||||
|
/** Extra stances that are available when this command is selected. These ignore incompatibleStances. */
|
||||||
|
public Seq<UnitStance> extraStances = new Seq<>();
|
||||||
|
|
||||||
public UnitCommand(String name, String icon, Func<Unit, AIController> controller){
|
public UnitCommand(String name, String icon, Func<Unit, AIController> controller){
|
||||||
super(name);
|
super(name);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import mindustry.input.*;
|
|||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
|
|
||||||
public class UnitStance extends MappableContent{
|
public class UnitStance extends MappableContent{
|
||||||
public static UnitStance stop, holdFire, pursueTarget, patrol, ram, boost, mineAuto;
|
public static UnitStance stop, holdFire, pursueTarget, patrol, ram, boost, holdPosition, mineAuto;
|
||||||
|
|
||||||
/** Name of UI icon (from Icon class). */
|
/** Name of UI icon (from Icon class). */
|
||||||
public String icon;
|
public String icon;
|
||||||
@@ -84,16 +84,22 @@ public class UnitStance extends MappableContent{
|
|||||||
stop = new UnitStance("stop", "cancel", Binding.cancelOrders, false);
|
stop = new UnitStance("stop", "cancel", Binding.cancelOrders, false);
|
||||||
holdFire = new UnitStance("holdfire", "none", Binding.unitStanceHoldFire);
|
holdFire = new UnitStance("holdfire", "none", Binding.unitStanceHoldFire);
|
||||||
pursueTarget = new UnitStance("pursuetarget", "right", Binding.unitStancePursueTarget);
|
pursueTarget = new UnitStance("pursuetarget", "right", Binding.unitStancePursueTarget);
|
||||||
patrol = new UnitStance("patrol", "refresh", Binding.unitStancePatrol);
|
patrol = new UnitStance("patrol", "refresh", Binding.unitStancePatrol){{
|
||||||
|
incompatibleCommands.addAll(UnitCommand.repairCommand, UnitCommand.assistCommand, UnitCommand.rebuildCommand);
|
||||||
|
}};
|
||||||
ram = new UnitStance("ram", "rightOpen", Binding.unitStanceRam);
|
ram = new UnitStance("ram", "rightOpen", Binding.unitStanceRam);
|
||||||
boost = new UnitStance("boost", "up", Binding.unitStanceBoost){{
|
boost = new UnitStance("boost", "up", Binding.unitStanceBoost){{
|
||||||
incompatibleCommands.addAll(UnitCommand.rebuildCommand, UnitCommand.repairCommand, UnitCommand.assistCommand);
|
incompatibleCommands.addAll(UnitCommand.rebuildCommand, UnitCommand.repairCommand, UnitCommand.assistCommand);
|
||||||
}};
|
}};
|
||||||
|
holdPosition = new UnitStance("holdposition", "effect", Binding.unitStanceHoldPosition);
|
||||||
mineAuto = new UnitStance("mineauto", "settings", null, false);
|
mineAuto = new UnitStance("mineauto", "settings", null, false);
|
||||||
|
|
||||||
//Only vanilla items are supported for now
|
//Only vanilla items are supported for now
|
||||||
for(Item item : Vars.content.items()){
|
for(Item item : Vars.content.items()){
|
||||||
new ItemUnitStance(item);
|
new ItemUnitStance(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Seq.with(UnitCommand.repairCommand, UnitCommand.assistCommand, UnitCommand.rebuildCommand)
|
||||||
|
.each(c -> c.extraStances.add(holdPosition));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mindustry.ai.types;
|
|||||||
|
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
|
import mindustry.ai.*;
|
||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.entities.units.*;
|
import mindustry.entities.units.*;
|
||||||
import mindustry.game.Teams.*;
|
import mindustry.game.Teams.*;
|
||||||
@@ -61,6 +62,7 @@ public class BuilderAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean moving = false;
|
boolean moving = false;
|
||||||
|
boolean hold = hasStance(UnitStance.holdPosition);
|
||||||
|
|
||||||
if(following != null){
|
if(following != null){
|
||||||
retreatTimer = 0f;
|
retreatTimer = 0f;
|
||||||
@@ -77,7 +79,7 @@ public class BuilderAI extends AIController{
|
|||||||
unit.plans.clear();
|
unit.plans.clear();
|
||||||
unit.plans.addFirst(following.buildPlan());
|
unit.plans.addFirst(following.buildPlan());
|
||||||
lastPlan = null;
|
lastPlan = null;
|
||||||
}else if(unit.buildPlan() == null || alwaysFlee){
|
}else if((unit.buildPlan() == null || alwaysFlee) && !hold){
|
||||||
//not following anyone or building
|
//not following anyone or building
|
||||||
if(timer.get(timerTarget4, 40)){
|
if(timer.get(timerTarget4, 40)){
|
||||||
enemy = target(unit.x, unit.y, fleeRange, true, true);
|
enemy = target(unit.x, unit.y, fleeRange, true, true);
|
||||||
@@ -121,10 +123,16 @@ public class BuilderAI extends AIController{
|
|||||||
Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation)));
|
Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation)));
|
||||||
|
|
||||||
if(valid){
|
if(valid){
|
||||||
float range = Math.min(unit.type.buildRange - unit.type.hitSize * 2f, buildRadius);
|
if(!hold){
|
||||||
//move toward the plan
|
float range = Math.min(unit.type.buildRange - unit.type.hitSize * 2f, buildRadius);
|
||||||
moveTo(req.tile(), range, 20f);
|
//move toward the plan
|
||||||
moving = !unit.within(req.tile(), range);
|
moveTo(req.tile(), range, 20f);
|
||||||
|
moving = !unit.within(req.tile(), range);
|
||||||
|
}else if(!unit.within(req, unit.type.buildRange - tilesize) && !state.rules.infiniteResources){
|
||||||
|
//discard the plan, it's too far away to reach while holding position. try the next one
|
||||||
|
unit.plans.removeFirst();
|
||||||
|
lastPlan = null;
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
//discard invalid plan
|
//discard invalid plan
|
||||||
unit.plans.removeFirst();
|
unit.plans.removeFirst();
|
||||||
@@ -132,7 +140,7 @@ public class BuilderAI extends AIController{
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
if(assistFollowing != null){
|
if(assistFollowing != null && !hold){
|
||||||
moveTo(assistFollowing, assistFollowing.type.hitSize + unit.type.hitSize/2f + 60f);
|
moveTo(assistFollowing, assistFollowing.type.hitSize + unit.type.hitSize/2f + 60f);
|
||||||
moving = !unit.within(assistFollowing, assistFollowing.type.hitSize + unit.type.hitSize/2f + 65f);
|
moving = !unit.within(assistFollowing, assistFollowing.type.hitSize + unit.type.hitSize/2f + 65f);
|
||||||
}
|
}
|
||||||
@@ -179,21 +187,43 @@ public class BuilderAI extends AIController{
|
|||||||
|
|
||||||
//find new plan
|
//find new plan
|
||||||
if(!onlyAssist && !unit.team.data().plans.isEmpty() && following == null && timer.get(timerTarget3, rebuildPeriod)){
|
if(!onlyAssist && !unit.team.data().plans.isEmpty() && following == null && timer.get(timerTarget3, rebuildPeriod)){
|
||||||
Queue<BlockPlan> blocks = unit.team.data().plans;
|
var blocks = unit.team.data().plans;
|
||||||
BlockPlan block = blocks.first();
|
|
||||||
|
|
||||||
//check if it's already been placed
|
|
||||||
if(world.tile(block.x, block.y) != null && world.tile(block.x, block.y).block() == block.block){
|
if(hold){
|
||||||
blocks.removeFirst();
|
//essentially build turret behavior (find first plan in range)
|
||||||
}else if(Build.validPlace(block.block, unit.team(), block.x, block.y, block.rotation) && (!alwaysFlee || !nearEnemy(block.x, block.y))){ //it's valid
|
for(int i = 0; i < blocks.size; i++){
|
||||||
lastPlan = block;
|
var block = blocks.get(i);
|
||||||
//add build plan
|
if(state.rules.infiniteResources || unit.within(block.x * tilesize, block.y * tilesize, unit.type.buildRange)){
|
||||||
unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, block.block, block.config));
|
var btype = block.block;
|
||||||
//shift build plan to tail so next unit builds something else
|
|
||||||
blocks.addLast(blocks.removeFirst());
|
if(Build.validPlace(btype, unit.team(), block.x, block.y, block.rotation)){
|
||||||
|
unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, block.block, block.config));
|
||||||
|
//shift build plan to tail so next unit builds something else
|
||||||
|
blocks.addLast(blocks.removeIndex(i));
|
||||||
|
lastPlan = block;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
//shift head of queue to tail, try something else next time
|
BlockPlan block = blocks.first();
|
||||||
blocks.addLast(blocks.removeFirst());
|
|
||||||
|
//check if it's already been placed
|
||||||
|
if(world.tile(block.x, block.y) != null && world.tile(block.x, block.y).block() == block.block){
|
||||||
|
blocks.removeFirst();
|
||||||
|
}else if(Build.validPlace(block.block, unit.team(), block.x, block.y, block.rotation)
|
||||||
|
&& (!alwaysFlee || !nearEnemy(block.x, block.y))){ //check if it's valid
|
||||||
|
|
||||||
|
lastPlan = block;
|
||||||
|
//add build plan
|
||||||
|
unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, block.block, block.config));
|
||||||
|
//shift build plan to tail so next unit builds something else
|
||||||
|
blocks.addLast(blocks.removeFirst());
|
||||||
|
}else{
|
||||||
|
//shift head of queue to tail, try something else next time
|
||||||
|
blocks.addLast(blocks.removeFirst());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ public class CommandAI extends AIController{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean hasStance(@Nullable UnitStance stance){
|
public boolean hasStance(@Nullable UnitStance stance){
|
||||||
return stance != null && stances.get(stance.id);
|
return stance != null && stances.get(stance.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.ai.types;
|
package mindustry.ai.types;
|
||||||
|
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
|
import mindustry.ai.*;
|
||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.entities.units.*;
|
import mindustry.entities.units.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
@@ -28,11 +29,15 @@ public class RepairAI extends AIController{
|
|||||||
unit.controlWeapons(false);
|
unit.controlWeapons(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hold = hasStance(UnitStance.holdPosition);
|
||||||
|
|
||||||
if(target != null && target instanceof Building b && b.team == unit.team){
|
if(target != null && target instanceof Building b && b.team == unit.team){
|
||||||
if(unit.type.circleTarget){
|
if(!hold){
|
||||||
circleAttack(unit.type.circleTargetRadius);
|
if(unit.type.circleTarget){
|
||||||
}else if(!target.within(unit, unit.type.range * 0.65f)){
|
circleAttack(unit.type.circleTargetRadius);
|
||||||
moveTo(target, unit.type.range * 0.65f);
|
}else if(!target.within(unit, unit.type.range * 0.65f)){
|
||||||
|
moveTo(target, unit.type.range * 0.65f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!unit.type.circleTarget){
|
if(!unit.type.circleTarget){
|
||||||
@@ -41,7 +46,7 @@ public class RepairAI extends AIController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//not repairing
|
//not repairing
|
||||||
if(!(target instanceof Building)){
|
if(!(target instanceof Building) && !hold){
|
||||||
if(timer.get(timerTarget4, 40)){
|
if(timer.get(timerTarget4, 40)){
|
||||||
avoid = target(unit.x, unit.y, fleeRange, true, true);
|
avoid = target(unit.x, unit.y, fleeRange, true, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import arc.math.*;
|
|||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
|
import mindustry.ai.*;
|
||||||
import mindustry.async.*;
|
import mindustry.async.*;
|
||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
@@ -53,6 +54,13 @@ public class AIController implements UnitController{
|
|||||||
updateMovement();
|
updateMovement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasStance(@Nullable UnitStance stance){
|
||||||
|
if(unit.controller() instanceof AIController ai){
|
||||||
|
return ai.hasStance(stance);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Called when the parent CommandAI changes its stance. */
|
/** Called when the parent CommandAI changes its stance. */
|
||||||
public void stanceChanged(){}
|
public void stanceChanged(){}
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ public class Binding{
|
|||||||
unitStancePatrol = KeyBind.add("unit_stance_patrol", KeyCode.unset),
|
unitStancePatrol = KeyBind.add("unit_stance_patrol", KeyCode.unset),
|
||||||
unitStanceRam = KeyBind.add("unit_stance_ram", KeyCode.unset),
|
unitStanceRam = KeyBind.add("unit_stance_ram", KeyCode.unset),
|
||||||
unitStanceBoost = KeyBind.add("unit_stance_boost", KeyCode.unset),
|
unitStanceBoost = KeyBind.add("unit_stance_boost", KeyCode.unset),
|
||||||
|
unitStanceHoldPosition = KeyBind.add("unit_stance_hold_position", KeyCode.unset),
|
||||||
|
|
||||||
unitCommandMove = KeyBind.add("unit_command_move", KeyCode.unset),
|
unitCommandMove = KeyBind.add("unit_command_move", KeyCode.unset),
|
||||||
unitCommandRepair = KeyBind.add("unit_command_repair", KeyCode.unset),
|
unitCommandRepair = KeyBind.add("unit_command_repair", KeyCode.unset),
|
||||||
|
|||||||
@@ -648,8 +648,12 @@ public class UnitType extends UnlockableContent implements Senseable{
|
|||||||
|
|
||||||
/** Adds all available unit stances based on the unit's current state. This can change based on the command of the unit. */
|
/** Adds all available unit stances based on the unit's current state. This can change based on the command of the unit. */
|
||||||
public void getUnitStances(Unit unit, Seq<UnitStance> out){
|
public void getUnitStances(Unit unit, Seq<UnitStance> out){
|
||||||
|
if(!(unit.controller() instanceof CommandAI ai)) return;
|
||||||
|
|
||||||
|
var current = ai.currentCommand();
|
||||||
|
|
||||||
//return mining stances based on present items
|
//return mining stances based on present items
|
||||||
if(unit.controller() instanceof CommandAI ai && ai.currentCommand() == UnitCommand.mineCommand){
|
if(current == UnitCommand.mineCommand){
|
||||||
out.add(UnitStance.mineAuto);
|
out.add(UnitStance.mineAuto);
|
||||||
for(Item item : indexer.getAllPresentOres()){
|
for(Item item : indexer.getAllPresentOres()){
|
||||||
if(unit.canMine(item) && ((mineFloor && indexer.hasOre(item)) || (mineWalls && indexer.hasWallOre(item)))){
|
if(unit.canMine(item) && ((mineFloor && indexer.hasOre(item)) || (mineWalls && indexer.hasWallOre(item)))){
|
||||||
@@ -660,13 +664,15 @@ public class UnitType extends UnlockableContent implements Senseable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
var command = unit.controller() instanceof CommandAI ai ? ai.command : null;
|
|
||||||
for(var stance : stances){
|
for(var stance : stances){
|
||||||
if(stance.isCompatible(command)){
|
if(stance.isCompatible(current)){
|
||||||
out.add(stance);
|
out.add(stance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//there might be duplicates, but that shouldn't cause issues
|
||||||
|
out.addAll(current.extraStances);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allowStance(Unit unit, UnitStance stance){
|
public boolean allowStance(Unit unit, UnitStance stance){
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ task incrementConfig{
|
|||||||
dependencies{
|
dependencies{
|
||||||
implementation project(":core")
|
implementation project(":core")
|
||||||
|
|
||||||
implementation arcModule("natives:natives-ios")
|
|
||||||
implementation arcModule("natives:natives-freetype-ios")
|
implementation arcModule("natives:natives-freetype-ios")
|
||||||
implementation arcModule("backends:backend-robovm")
|
implementation arcModule("backends:backend-robovm")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user