Unit stances for specific item mining

This commit is contained in:
Anuken
2025-04-25 15:44:49 -04:00
parent ca19d525c4
commit 847a201597
11 changed files with 113 additions and 26 deletions

View File

@@ -378,6 +378,8 @@ 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.ram = Stance: Ram\n[lightgray]Straight line movement, no pathfinding stance.ram = Stance: Ram\n[lightgray]Straight line movement, no pathfinding
stance.mineauto = Automatic Mining
stance.mine = Mine Item: {0}
openlink = Open Link openlink = Open Link
copylink = Copy Link copylink = Copy Link
back = Back back = Back

View File

@@ -0,0 +1,33 @@
package mindustry.ai;
import arc.*;
import arc.scene.style.*;
import arc.struct.*;
import arc.util.*;
import mindustry.type.*;
public class ItemUnitStance extends UnitStance{
private static ObjectMap<Item, ItemUnitStance> itemToStance = new ObjectMap<>();
public final Item item;
public ItemUnitStance(Item item){
super("item-" + item.name, "item-" + item.name, null);
this.item = item;
itemToStance.put(item, this);
}
public static @Nullable ItemUnitStance getByItem(Item item){
return itemToStance.get(item);
}
@Override
public String localized(){
return Core.bundle.format("stance.mine", item.localizedName);
}
@Override
public TextureRegionDrawable getIcon(){
return new TextureRegionDrawable(item.uiIcon);
}
}

View File

@@ -26,6 +26,8 @@ public class UnitCommand extends MappableContent{
public boolean resetTarget = true; public boolean resetTarget = true;
/** */ /** */
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!*/
public boolean refreshOnSelect = false;
/** Key to press for this command. */ /** Key to press for this command. */
public @Nullable Binding keybind = null; public @Nullable Binding keybind = null;
@@ -76,7 +78,9 @@ public class UnitCommand extends MappableContent{
ai.onlyAssist = true; ai.onlyAssist = true;
return ai; return ai;
}); });
mineCommand = new UnitCommand("mine", "production", Binding.unit_command_mine, u -> new MinerAI()); mineCommand = new UnitCommand("mine", "production", Binding.unit_command_mine, u -> new MinerAI()){{
refreshOnSelect = true;
}};
boostCommand = new UnitCommand("boost", "up", Binding.unit_command_boost, u -> new BoostAI()){{ boostCommand = new UnitCommand("boost", "up", Binding.unit_command_boost, u -> new BoostAI()){{
switchToMove = false; switchToMove = false;
drawTarget = true; drawTarget = true;

View File

@@ -3,12 +3,14 @@ package mindustry.ai;
import arc.*; import arc.*;
import arc.scene.style.*; import arc.scene.style.*;
import arc.util.*; import arc.util.*;
import mindustry.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.input.*; import mindustry.input.*;
import mindustry.type.*;
public class UnitStance extends MappableContent{ public class UnitStance extends MappableContent{
public static UnitStance stop, shoot, holdFire, pursueTarget, patrol, ram; public static UnitStance stop, shoot, holdFire, pursueTarget, patrol, ram, mineAuto;
/** Name of UI icon (from Icon class). */ /** Name of UI icon (from Icon class). */
public String icon; public String icon;
@@ -30,7 +32,7 @@ public class UnitStance extends MappableContent{
} }
public char getEmoji() { public char getEmoji() {
return (char) Iconc.codes.get(icon, Iconc.cancel); return (char)Iconc.codes.get(icon, Iconc.cancel);
} }
@Override @Override
@@ -50,5 +52,11 @@ public class UnitStance extends MappableContent{
pursueTarget = new UnitStance("pursuetarget", "right", Binding.unit_stance_pursue_target); pursueTarget = new UnitStance("pursuetarget", "right", Binding.unit_stance_pursue_target);
patrol = new UnitStance("patrol", "refresh", Binding.unit_stance_patrol); patrol = new UnitStance("patrol", "refresh", Binding.unit_stance_patrol);
ram = new UnitStance("ram", "rightOpen", Binding.unit_stance_ram); ram = new UnitStance("ram", "rightOpen", Binding.unit_stance_ram);
mineAuto = new UnitStance("mineauto", "settings", null);
//Only vanilla items are supported for now
for(Item item : Vars.content.items()){
new ItemUnitStance(item);
}
} }
} }

View File

@@ -77,6 +77,11 @@ public class CommandAI extends AIController{
//this should not be possible //this should not be possible
if(stance == UnitStance.stop) stance = UnitStance.shoot; if(stance == UnitStance.stop) stance = UnitStance.shoot;
//fix incorrect stance when mining
if(command == UnitCommand.mineCommand && stance != UnitStance.mineAuto && !(stance instanceof ItemUnitStance)){
stance = UnitStance.mineAuto;
}
//pursue the target if relevant //pursue the target if relevant
if(stance == UnitStance.pursueTarget && target != null && attackTarget == null && targetPos == null){ if(stance == UnitStance.pursueTarget && target != null && attackTarget == null && targetPos == null){
commandTarget(target, false); commandTarget(target, false);

View File

@@ -1,5 +1,6 @@
package mindustry.ai.types; package mindustry.ai.types;
import mindustry.ai.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.type.*; import mindustry.type.*;
@@ -22,8 +23,12 @@ public class MinerAI extends AIController{
unit.mineTile(null); unit.mineTile(null);
} }
Item autoItem = unit.controller() instanceof CommandAI ai && ai.stance instanceof ItemUnitStance stance ? stance.item : null;
if(mining){ if(mining){
if(timer.get(timerTarget2, 60 * 4) || targetItem == null){ if(autoItem != null){
targetItem = autoItem;
}else if(timer.get(timerTarget2, 60 * 4) || targetItem == null){
targetItem = unit.type.mineItems.min(i -> indexer.hasOre(i) && unit.canMine(i), i -> core.items.get(i)); targetItem = unit.type.mineItems.min(i -> indexer.hasOre(i) && unit.canMine(i), i -> core.items.get(i));
} }

View File

@@ -1256,11 +1256,7 @@ public class UnitTypes{
//region air support //region air support
mono = new UnitType("mono"){{ mono = new UnitType("mono"){{
//there's no reason to command monos anywhere. it's just annoying.
controller = u -> new MinerAI();
defaultCommand = UnitCommand.mineCommand; defaultCommand = UnitCommand.mineCommand;
allowChangeCommands = false;
flying = true; flying = true;
drag = 0.06f; drag = 0.06f;

View File

@@ -43,9 +43,9 @@ public class ContentLoader{
/** Creates all base types. */ /** Creates all base types. */
public void createBaseContent(){ public void createBaseContent(){
UnitCommand.loadAll(); UnitCommand.loadAll();
UnitStance.loadAll();
TeamEntries.load(); TeamEntries.load();
Items.load(); Items.load();
UnitStance.loadAll(); //needs to access items
StatusEffects.load(); StatusEffects.load();
Liquids.load(); Liquids.load();
Bullets.load(); Bullets.load();

View File

@@ -54,6 +54,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
/** Used for dropping items. */ /** Used for dropping items. */
final static float playerSelectRange = mobile ? 17f : 11f; final static float playerSelectRange = mobile ? 17f : 11f;
final static float unitSelectRadScl = 1f; final static float unitSelectRadScl = 1f;
final static Seq<UnitStance> stancesOut = new Seq<>();
final static IntSeq removed = new IntSeq(); final static IntSeq removed = new IntSeq();
final static IntSet intSet = new IntSet(); final static IntSet intSet = new IntSet();
/** Maximum line length. */ /** Maximum line length. */
@@ -369,6 +370,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
ai.attackTarget = null; ai.attackTarget = null;
} }
unit.lastCommanded = player.coloredName(); unit.lastCommanded = player.coloredName();
//make sure its stance is valid
stancesOut.clear();
unit.type.getUnitStances(unit, stancesOut);
if(stancesOut.size > 0 && !stancesOut.contains(ai.stance)){
ai.stance = stancesOut.first();
}
} }
} }
} }

View File

@@ -592,6 +592,24 @@ public class UnitType extends UnlockableContent implements Senseable{
return hittable || (vulnerableWithPayloads && unit instanceof Payloadc p && p.hasPayload()); return hittable || (vulnerableWithPayloads && unit instanceof Payloadc p && p.hasPayload());
} }
/** 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){
//return mining stances based on present items
if(unit.controller() instanceof CommandAI ai && ai.currentCommand() == UnitCommand.mineCommand){
out.add(UnitStance.mineAuto);
for(Item item : indexer.getAllPresentOres()){
if(unit.canMine(item)){
var itemStance = ItemUnitStance.getByItem(item);
if(itemStance != null){
out.add(itemStance);
}
}
}
}else{
out.addAll(stances);
}
}
public void update(Unit unit){ public void update(Unit unit){
} }

View File

@@ -468,17 +468,33 @@ public class PlacementFragment{
UnitStance[] currentStance = {null}; UnitStance[] currentStance = {null};
var stances = new Seq<UnitStance>(); var stances = new Seq<UnitStance>();
var stancesOut = new Seq<UnitStance>();
rebuildCommand = () -> { rebuildCommand = () -> {
u.clearChildren(); u.clearChildren();
var units = control.input.selectedUnits; var units = control.input.selectedUnits;
if(units.size > 0){ if(units.size > 0){
int[] counts = new int[content.units().size];
for(var unit : units){
counts[unit.type.id] ++;
}
commands.clear(); commands.clear();
stances.clear(); stances.clear();
boolean firstCommand = false, firstStance = false; boolean firstCommand = false, firstStance = false;
int[] counts = new int[content.units().size];
for(var unit : units){
counts[unit.type.id] ++;
stancesOut.clear();
unit.type.getUnitStances(unit, stancesOut);
if(!firstStance){
stances.add(stancesOut);
firstStance = true;
}else{
//remove commands that this next unit type doesn't have
stances.removeAll(st -> !stancesOut.contains(st));
}
}
Table unitlist = u.table().growX().left().get(); Table unitlist = u.table().growX().left().get();
unitlist.left(); unitlist.left();
@@ -520,14 +536,6 @@ public class PlacementFragment{
//remove commands that this next unit type doesn't have //remove commands that this next unit type doesn't have
commands.removeAll(com -> !type.commands.contains(com)); commands.removeAll(com -> !type.commands.contains(com));
} }
if(!firstStance){
stances.add(type.stances);
firstStance = true;
}else{
//remove commands that this next unit type doesn't have
stances.removeAll(st -> !type.stances.contains(st));
}
} }
} }
@@ -562,7 +570,7 @@ public class PlacementFragment{
int scol = 0; int scol = 0;
for(var stance : stances){ for(var stance : stances){
coms.button(Icon.icons.get(stance.icon, Icon.cancel), Styles.clearNoneTogglei, () -> { coms.button(stance.getIcon(), Styles.clearNoneTogglei, () -> {
Call.setUnitStance(player, units.mapInt(un -> un.id).toArray(), stance); Call.setUnitStance(player, units.mapInt(un -> un.id).toArray(), stance);
}).checked(i -> currentStance[0] == stance).size(50f).tooltip(stance.localized(), true); }).checked(i -> currentStance[0] == stance).size(50f).tooltip(stance.localized(), true);
@@ -608,15 +616,15 @@ public class PlacementFragment{
} }
} }
currentCommand[0] = shareCommand;
currentStance[0] = shareStance;
int size = control.input.selectedUnits.size; int size = control.input.selectedUnits.size;
if(curCount[0] != size){ if(curCount[0] != size || (currentCommand[0] != shareCommand && currentCommand[0] != null && (currentCommand[0].refreshOnSelect || shareCommand.refreshOnSelect))){
curCount[0] = size; curCount[0] = size;
rebuildCommand.run(); rebuildCommand.run();
} }
currentCommand[0] = shareCommand;
currentStance[0] = shareStance;
//not a huge fan of running input logic here, but it's convenient as the stance arrays are all here... //not a huge fan of running input logic here, but it's convenient as the stance arrays are all here...
for(UnitStance stance : stances){ for(UnitStance stance : stances){
//first stance must always be the stop stance //first stance must always be the stop stance