mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-12-06 02:40:23 -08:00
RTS Control groups
This commit is contained in:
parent
e6b2e27d2a
commit
aecd220574
8 changed files with 119 additions and 18 deletions
|
|
@ -1068,6 +1068,7 @@ setting.backgroundpause.name = Pause In Background
|
|||
setting.buildautopause.name = Auto-Pause Building
|
||||
setting.doubletapmine.name = Double-Tap to Mine
|
||||
setting.commandmodehold.name = Hold For Command Mode
|
||||
setting.distinctcontrolgroups.name = Limit One Control Group Per Unit
|
||||
setting.modcrashdisable.name = Disable Mods On Startup Crash
|
||||
setting.animatedwater.name = Animated Surfaces
|
||||
setting.animatedshields.name = Animated Shields
|
||||
|
|
@ -1158,7 +1159,8 @@ keybind.mouse_move.name = Follow Mouse
|
|||
keybind.pan.name = Pan View
|
||||
keybind.boost.name = Boost
|
||||
keybind.command_mode.name = Command Mode
|
||||
keybind.command_queue.name = Unit Command Queue
|
||||
keybind.command_queue.name = Queue Unit Command
|
||||
keybind.create_control_group.name = Create Control Group
|
||||
keybind.rebuild_select.name = Rebuild Region
|
||||
keybind.schematic_select.name = Select Region
|
||||
keybind.schematic_menu.name = Schematic Menu
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ public enum Binding implements KeyBind{
|
|||
boost(KeyCode.shiftLeft),
|
||||
command_mode(KeyCode.shiftLeft),
|
||||
command_queue(KeyCode.mouseMiddle),
|
||||
create_control_group(KeyCode.controlLeft),
|
||||
control(KeyCode.controlLeft),
|
||||
respawn(KeyCode.v),
|
||||
select(KeyCode.mouseLeft),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import arc.math.*;
|
|||
import arc.math.geom.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
|
|
@ -22,6 +23,7 @@ import mindustry.graphics.*;
|
|||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static arc.Core.camera;
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.input.PlaceMode.*;
|
||||
|
|
@ -49,6 +51,11 @@ public class DesktopInput extends InputHandler{
|
|||
/** Previously selected tile. */
|
||||
public Tile prevSelected;
|
||||
|
||||
/** Most recently selected control group by index */
|
||||
public int lastCtrlGroup;
|
||||
/** Time of most recent control group selection */
|
||||
public long lastCtrlGroupSelectMillis;
|
||||
|
||||
boolean showHint(){
|
||||
return ui.hudfrag.shown && Core.settings.getBool("hints") && selectPlans.isEmpty() &&
|
||||
(!isBuilding && !Core.settings.getBool("buildautopause") || player.unit().isBuilding() || !player.dead() && !player.unit().spawnedByCore());
|
||||
|
|
@ -262,22 +269,86 @@ public class DesktopInput extends InputHandler{
|
|||
//validate commanding units
|
||||
selectedUnits.removeAll(u -> !u.isCommandable() || !u.isValid());
|
||||
|
||||
if(commandMode && input.keyTap(Binding.select_all_units) && !scene.hasField() && !scene.hasDialog()){
|
||||
selectedUnits.clear();
|
||||
commandBuildings.clear();
|
||||
for(var unit : player.team().data().units){
|
||||
if(unit.isCommandable()){
|
||||
selectedUnits.add(unit);
|
||||
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()){
|
||||
selectedUnits.add(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(commandMode && input.keyTap(Binding.select_all_unit_factories) && !scene.hasField() && !scene.hasDialog()){
|
||||
selectedUnits.clear();
|
||||
commandBuildings.clear();
|
||||
for(var build : player.team().data().buildings){
|
||||
if(build.block.commandable){
|
||||
commandBuildings.add(build);
|
||||
if(input.keyTap(Binding.select_all_unit_factories)){
|
||||
selectedUnits.clear();
|
||||
commandBuildings.clear();
|
||||
for(var build : player.team().data().buildings){
|
||||
if(build.block.commandable){
|
||||
commandBuildings.add(build);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < controlGroupBindings.length; i++){
|
||||
if(input.keyTap(controlGroupBindings[i])){
|
||||
|
||||
//create control group if it doesn't exist yet
|
||||
if(controlGroups[i] == null) controlGroups[i] = new IntSeq();
|
||||
|
||||
IntSeq group = controlGroups[i];
|
||||
boolean creating = input.keyDown(Binding.create_control_group);
|
||||
|
||||
//clear existing if making a new control group
|
||||
//if any of the control group edit buttons are pressed take the current selection
|
||||
if(creating){
|
||||
group.clear();
|
||||
|
||||
IntSeq selectedUnitIds = selectedUnits.mapInt(u -> u.id);
|
||||
if(Core.settings.getBool("distinctcontrolgroups", true)){
|
||||
for(IntSeq cg : controlGroups){
|
||||
if(cg != null){
|
||||
cg.removeAll(selectedUnitIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
group.addAll(selectedUnitIds);
|
||||
}
|
||||
|
||||
//remove invalid units
|
||||
for(int j = 0; j < group.size; j++){
|
||||
Unit u = Groups.unit.getByID(group.get(j));
|
||||
if(u == null || !u.isCommandable() || !u.isValid()){
|
||||
group.removeIndex(j);
|
||||
j --;
|
||||
}
|
||||
}
|
||||
|
||||
//replace the selected units with the current control group
|
||||
if(!group.isEmpty() && !creating){
|
||||
selectedUnits.clear();
|
||||
commandBuildings.clear();
|
||||
|
||||
group.each(id -> {
|
||||
var unit = Groups.unit.getByID(id);
|
||||
if(unit != null){
|
||||
selectedUnits.addAll(unit);
|
||||
}
|
||||
});
|
||||
|
||||
//double tap to center camera
|
||||
if(lastCtrlGroup == i && Time.timeSinceMillis(lastCtrlGroupSelectMillis) < 400){
|
||||
float totalX = 0, totalY = 0;
|
||||
for(Unit unit : selectedUnits){
|
||||
totalX += unit.x;
|
||||
totalY += unit.y;
|
||||
}
|
||||
panning = true;
|
||||
Core.camera.position.set(totalX / selectedUnits.size, totalY / selectedUnits.size);
|
||||
}
|
||||
lastCtrlGroup = i;
|
||||
lastCtrlGroupSelectMillis = Time.millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,18 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
final static int maxLength = 100;
|
||||
final static Rect r1 = new Rect(), r2 = new Rect();
|
||||
final static Seq<Unit> tmpUnits = new Seq<>(false);
|
||||
final static Binding[] controlGroupBindings = {
|
||||
Binding.block_select_01,
|
||||
Binding.block_select_02,
|
||||
Binding.block_select_03,
|
||||
Binding.block_select_04,
|
||||
Binding.block_select_05,
|
||||
Binding.block_select_06,
|
||||
Binding.block_select_07,
|
||||
Binding.block_select_08,
|
||||
Binding.block_select_09,
|
||||
Binding.block_select_10
|
||||
};
|
||||
|
||||
/** If true, there is a cutscene currently occurring in logic. */
|
||||
public boolean logicCutscene;
|
||||
|
|
@ -87,6 +99,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
public boolean commandRect = false;
|
||||
public boolean tappedOne = false;
|
||||
public float commandRectX, commandRectY;
|
||||
/** Groups of units saved to different hotkeys */
|
||||
public IntSeq[] controlGroups = new IntSeq[controlGroupBindings.length];
|
||||
|
||||
private Seq<BuildPlan> plansOut = new Seq<>(BuildPlan.class);
|
||||
private QuadTree<BuildPlan> playerPlanTree = new QuadTree<>(new Rect());
|
||||
|
|
@ -124,6 +138,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
logicCutscene = false;
|
||||
Arrays.fill(controlGroups, null);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,10 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||
node.save();
|
||||
}
|
||||
|
||||
writeStringMap(stream, StringMap.of(
|
||||
StringMap result = new StringMap();
|
||||
result.putAll(tags);
|
||||
|
||||
writeStringMap(stream, result.merge(StringMap.of(
|
||||
"saved", Time.millis(),
|
||||
"playtime", headless ? 0 : control.saves.getTotalPlaytime(),
|
||||
"build", Version.build,
|
||||
|
|
@ -135,13 +138,14 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||
"stats", JsonIO.write(state.stats),
|
||||
"rules", JsonIO.write(state.rules),
|
||||
"mods", JsonIO.write(mods.getModStrings().toArray(String.class)),
|
||||
"controlGroups", headless || control == null ? "null" : JsonIO.write(control.input.controlGroups),
|
||||
"width", world.width(),
|
||||
"height", world.height(),
|
||||
"viewpos", Tmp.v1.set(player == null ? Vec2.ZERO : player).toString(),
|
||||
"controlledType", headless || control.input.controlledType == null ? "null" : control.input.controlledType.name,
|
||||
"nocores", state.rules.defaultTeam.cores().isEmpty(),
|
||||
"playerteam", player == null ? state.rules.defaultTeam.id : player.team().id
|
||||
).merge(tags));
|
||||
)));
|
||||
}
|
||||
|
||||
public void readMeta(DataInput stream, WorldContext context) throws IOException{
|
||||
|
|
@ -177,6 +181,11 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||
if(!net.client() && team != Team.derelict){
|
||||
player.team(team);
|
||||
}
|
||||
|
||||
var groups = JsonIO.read(IntSeq[].class, map.get("controlGroups", "null"));
|
||||
if(groups != null && groups.length == control.input.controlGroups.length){
|
||||
control.input.controlGroups = groups;
|
||||
}
|
||||
}
|
||||
|
||||
Map worldmap = maps.byName(map.get("mapname", "\\\\\\"));
|
||||
|
|
|
|||
|
|
@ -341,6 +341,7 @@ public class SettingsMenuDialog extends BaseDialog{
|
|||
if(!mobile){
|
||||
game.checkPref("backgroundpause", true);
|
||||
game.checkPref("buildautopause", false);
|
||||
game.checkPref("distinctcontrolgroups", true);
|
||||
}
|
||||
|
||||
game.checkPref("doubletapmine", false);
|
||||
|
|
|
|||
|
|
@ -611,7 +611,9 @@ public class PlacementFragment{
|
|||
|
||||
rebuildCategory.run();
|
||||
frame.update(() -> {
|
||||
if(gridUpdate(control.input)) rebuildCategory.run();
|
||||
if(!control.input.commandMode && gridUpdate(control.input)){
|
||||
rebuildCategory.run();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,4 +25,4 @@ org.gradle.caching=true
|
|||
#used for slow jitpack builds; TODO see if this actually works
|
||||
org.gradle.internal.http.socketTimeout=100000
|
||||
org.gradle.internal.http.connectionTimeout=100000
|
||||
archash=70f345cc75
|
||||
archash=38114a3118
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue