diff --git a/annotations/src/main/java/mindustry/annotations/misc/LogicStatementProcessor.java b/annotations/src/main/java/mindustry/annotations/misc/LogicStatementProcessor.java index 64adc37c89..6e597bd002 100644 --- a/annotations/src/main/java/mindustry/annotations/misc/LogicStatementProcessor.java +++ b/annotations/src/main/java/mindustry/annotations/misc/LogicStatementProcessor.java @@ -43,9 +43,9 @@ public class LogicStatementProcessor extends BaseProcessor{ String name = c.annotation(RegisterStatement.class).value(); if(beganWrite){ - writer.nextControlFlow("else if(obj instanceof $T)", c.mirror()); + writer.nextControlFlow("else if(obj.getClass() == $T.class)", c.mirror()); }else{ - writer.beginControlFlow("if(obj instanceof $T)", c.mirror()); + writer.beginControlFlow("if(obj.getClass() == $T.class)", c.mirror()); beganWrite = true; } @@ -53,6 +53,7 @@ public class LogicStatementProcessor extends BaseProcessor{ writer.addStatement("out.append($S)", name); Seq fields = c.fields(); + fields.addAll(c.superclass().fields()); String readSt = "if(tokens[0].equals($S))"; if(beganRead){ diff --git a/annotations/src/main/resources/classids.properties b/annotations/src/main/resources/classids.properties index 86ca440e1c..43a3517ff0 100644 --- a/annotations/src/main/resources/classids.properties +++ b/annotations/src/main/resources/classids.properties @@ -14,6 +14,8 @@ mindustry.entities.comp.EffectStateComp=9 mindustry.entities.comp.FireComp=10 mindustry.entities.comp.LaunchCoreComp=11 mindustry.entities.comp.PlayerComp=12 +mindustry.entities.comp.PosTeam=27 +mindustry.entities.comp.PosTeamDef=28 mindustry.entities.comp.PuddleComp=13 mindustry.type.Weather.WeatherStateComp=14 mindustry.world.blocks.campaign.LaunchPad.LaunchPayloadComp=15 diff --git a/annotations/src/main/resources/revisions/PosTeam/0.json b/annotations/src/main/resources/revisions/PosTeam/0.json new file mode 100644 index 0000000000..1c1e6c36ec --- /dev/null +++ b/annotations/src/main/resources/revisions/PosTeam/0.json @@ -0,0 +1 @@ +{fields:[{name:team,type:mindustry.game.Team},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 647901a572..7869dcf8e3 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -638,6 +638,8 @@ bar.progress = Build Progress bar.input = Input bar.output = Output +units.processorcontrol = [lightgray]Processor Controlled + bullet.damage = [stat]{0}[lightgray] damage bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles bullet.incendiary = [stat]incendiary diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 99d5567fda..80db8f354a 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -82,6 +82,8 @@ public class Vars implements Loadable{ public static final float buildingRange = 220f; /** range for moving items */ public static final float itemTransferRange = 220f; + /** range for moving items for logic units */ + public static final float logicItemTransferRange = 40f; /** duration of time between turns in ticks */ public static final float turnDuration = 20 * Time.toMinutes; /** turns needed to destroy a sector completely */ @@ -188,7 +190,6 @@ public class Vars implements Loadable{ public static Schematics schematics; public static BeControl becontrol; public static AsyncCore asyncCore; - public static TeamIndexProcess teamIndex; public static BaseRegistry bases; public static Universe universe; diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java new file mode 100644 index 0000000000..ab902b3e50 --- /dev/null +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -0,0 +1,101 @@ +package mindustry.ai.types; + +import arc.struct.*; +import arc.util.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.logic.LExecutor.*; +import mindustry.logic.*; + +public class LogicAI extends AIController{ + /** Minimum delay between item transfers. */ + public static final float transferDelay = 60f * 2f; + /** Time after which the unit resets its controlled and reverts to a normal unit. */ + public static final float logicControlTimeout = 15f * 60f; + + public LUnitControl control = LUnitControl.stop; + public float moveX, moveY, moveRad; + public float itemTimer, controlTimer = logicControlTimeout, targetTimer; + + //type of aiming to use + public LUnitControl aimControl = LUnitControl.stop; + + //main target set for shootP + public Teamc mainTarget; + //whether to shoot at all + public boolean shoot; + //target shoot positions for manual aiming + public PosTeam posTarget = PosTeam.create(); + + private ObjectSet radars = new ObjectSet<>(); + + @Override + protected void updateMovement(){ + if(itemTimer > 0){ + itemTimer -= Time.delta; + } + + if(targetTimer > 0f){ + targetTimer -= Time.delta; + }else{ + radars.clear(); + targetTimer = 30f; + } + + //timeout when not controlled by logic for a while + if(controlTimer > 0){ + controlTimer -= Time.delta; + }else{ + unit.resetController(); + return; + } + + switch(control){ + case move -> { + moveTo(Tmp.v1.set(moveX, moveY), 1f, 30f); + } + case approach -> { + moveTo(Tmp.v1.set(moveX, moveY), moveRad, 10f); + } + } + + //look where moving if there's nothing to aim at + if(!shoot){ + if(unit.moving()){ + unit.lookAt(unit.vel().angle()); + } + }else if(unit.hasWeapons()){ //if there is, look at the object + unit.lookAt(unit.mounts[0].aimX, unit.mounts[0].aimY); + } + } + + public boolean checkTargetTimer(RadarI radar){ + return radars.add(radar); + } + + //always retarget + @Override + protected boolean retarget(){ + return true; + } + + @Override + protected boolean invalid(Teamc target){ + return false; + } + + @Override + protected boolean shouldShoot(){ + return shoot; + } + + //always aim for the main target + @Override + protected Teamc target(float x, float y, float range, boolean air, boolean ground){ + return switch(aimControl){ + case target -> posTarget; + case targetp -> mainTarget; + default -> null; + }; + } +} diff --git a/core/src/mindustry/async/AsyncCore.java b/core/src/mindustry/async/AsyncCore.java index 764cd3dc4b..a9d4cdd90d 100644 --- a/core/src/mindustry/async/AsyncCore.java +++ b/core/src/mindustry/async/AsyncCore.java @@ -2,7 +2,6 @@ package mindustry.async; import arc.*; import arc.struct.*; -import mindustry.*; import mindustry.game.EventType.*; import java.util.concurrent.*; @@ -12,8 +11,7 @@ import static mindustry.Vars.*; public class AsyncCore{ //all processes to be executed each frame private final Seq processes = Seq.with( - new PhysicsProcess(), - Vars.teamIndex = new TeamIndexProcess() + new PhysicsProcess() ); //futures to be awaited diff --git a/core/src/mindustry/async/TeamIndexProcess.java b/core/src/mindustry/async/TeamIndexProcess.java deleted file mode 100644 index 86b5e16902..0000000000 --- a/core/src/mindustry/async/TeamIndexProcess.java +++ /dev/null @@ -1,82 +0,0 @@ -package mindustry.async; - -import arc.math.geom.*; -import mindustry.*; -import mindustry.game.*; -import mindustry.gen.*; -import mindustry.type.*; -import mindustry.world.blocks.payloads.*; - -import java.util.*; - -/** Creates quadtrees per unit team. */ -public class TeamIndexProcess implements AsyncProcess{ - private QuadTree[] trees = new QuadTree[Team.all.length]; - private int[] counts = new int[Team.all.length]; - private int[][] typeCounts = new int[Team.all.length][0]; - - public QuadTree tree(Team team){ - if(trees[team.id] == null) trees[team.id] = new QuadTree<>(Vars.world.getQuadBounds(new Rect())); - - return trees[team.id]; - } - - public int count(Team team){ - return counts[team.id]; - } - - public int countType(Team team, UnitType type){ - return typeCounts[team.id].length <= type.id ? 0 : typeCounts[team.id][type.id]; - } - - public void updateCount(Team team, UnitType type, int amount){ - counts[team.id] = Math.max(amount + counts[team.id], 0); - if(typeCounts[team.id].length <= type.id){ - typeCounts[team.id] = new int[Vars.content.units().size]; - } - typeCounts[team.id][type.id] = Math.max(amount + typeCounts[team.id][type.id], 0); - } - - private void count(Unit unit){ - updateCount(unit.team, unit.type(), 1); - - if(unit instanceof Payloadc){ - ((Payloadc)unit).payloads().each(p -> { - if(p instanceof UnitPayload){ - count(((UnitPayload)p).unit); - } - }); - } - } - - @Override - public void reset(){ - counts = new int[Team.all.length]; - trees = new QuadTree[Team.all.length]; - } - - @Override - public void begin(){ - - for(Team team : Team.all){ - if(trees[team.id] != null){ - trees[team.id].clear(); - } - - Arrays.fill(typeCounts[team.id], 0); - } - - Arrays.fill(counts, 0); - - for(Unit unit : Groups.unit){ - tree(unit.team).insert(unit); - - count(unit); - } - } - - @Override - public boolean shouldProcess(){ - return false; - } -} diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 5cae2c66bb..9e00bd65ce 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -266,7 +266,6 @@ public class UnitTypes implements ContentList{ //region ground support nova = new UnitType("nova"){{ - itemCapacity = 60; canBoost = true; boostMultiplier = 1.5f; speed = 0.55f; @@ -293,7 +292,6 @@ public class UnitTypes implements ContentList{ }}; pulsar = new UnitType("pulsar"){{ - itemCapacity = 60; canBoost = true; boostMultiplier = 1.5f; speed = 0.65f; @@ -340,7 +338,6 @@ public class UnitTypes implements ContentList{ mineTier = 1; hitSize = 12f; boostMultiplier = 2f; - itemCapacity = 80; health = 650f; buildSpeed = 1.7f; canBoost = true; @@ -445,7 +442,6 @@ public class UnitTypes implements ContentList{ corvus = new UnitType("corvus"){{ mineTier = 1; hitSize = 29f; - itemCapacity = 80; health = 18000f; buildSpeed = 1.7f; armor = 9f; @@ -545,7 +541,6 @@ public class UnitTypes implements ContentList{ }}; atrax = new UnitType("atrax"){{ - itemCapacity = 80; speed = 0.5f; drag = 0.4f; hitSize = 10f; @@ -1134,7 +1129,6 @@ public class UnitTypes implements ContentList{ health = 100; engineSize = 1.8f; engineOffset = 5.7f; - itemCapacity = 30; range = 50f; isCounted = false; @@ -1153,7 +1147,6 @@ public class UnitTypes implements ContentList{ rotateSpeed = 15f; accel = 0.1f; range = 70f; - itemCapacity = 70; health = 400; buildSpeed = 0.5f; engineOffset = 6.5f; diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index e03771843a..dd54bcc59d 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -283,6 +283,8 @@ public class Logic implements ApplicationListener{ } if(!state.isPaused()){ + state.teams.updateTeamStats(); + if(state.isCampaign()){ state.secinfo.update(); } diff --git a/core/src/mindustry/entities/Units.java b/core/src/mindustry/entities/Units.java index 85761795e5..c6f592f30f 100644 --- a/core/src/mindustry/entities/Units.java +++ b/core/src/mindustry/entities/Units.java @@ -65,7 +65,7 @@ public class Units{ /** @return whether a new instance of a unit of this team can be created. */ public static boolean canCreate(Team team, UnitType type){ - return teamIndex.countType(team, type) < getCap(team); + return team.data().countType(type) < getCap(team); } public static int getCap(Team team){ @@ -284,7 +284,7 @@ public class Units{ /** Iterates over all units in a rectangle. */ public static void nearby(Team team, float x, float y, float width, float height, Cons cons){ - teamIndex.tree(team).intersect(x, y, width, height, cons); + team.data().tree().intersect(x, y, width, height, cons); } /** Iterates over all units in a circle around this position. */ @@ -316,7 +316,7 @@ public class Units{ //inactive teams have no cache, check everything //TODO cache all teams with units OR blocks for(Team other : Team.all){ - if(other != team && teamIndex.count(other) > 0){ + if(other != team && other.data().unitCount > 0){ nearby(other, x, y, width, height, cons); } } diff --git a/core/src/mindustry/entities/comp/BulletComp.java b/core/src/mindustry/entities/comp/BulletComp.java index 5ef68ac2e1..cff7694c3e 100644 --- a/core/src/mindustry/entities/comp/BulletComp.java +++ b/core/src/mindustry/entities/comp/BulletComp.java @@ -31,12 +31,12 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw public void getCollisions(Cons consumer){ if(team.active()){ for(Team team : team.enemies()){ - consumer.get(teamIndex.tree(team)); + consumer.get(team.data().tree()); } }else{ for(Team other : Team.all){ - if(other != team && teamIndex.count(other) > 0){ - consumer.get(teamIndex.tree(other)); + if(other != team && team.data().unitCount > 0){ + consumer.get(team.data().tree()); } } } diff --git a/core/src/mindustry/entities/comp/PosTeamDef.java b/core/src/mindustry/entities/comp/PosTeamDef.java new file mode 100644 index 0000000000..0157200d66 --- /dev/null +++ b/core/src/mindustry/entities/comp/PosTeamDef.java @@ -0,0 +1,9 @@ +package mindustry.entities.comp; + +import mindustry.annotations.Annotations.*; +import mindustry.gen.*; + +//dummy target definition +@EntityDef(value = Teamc.class, genio = false, isFinal = false) +public class PosTeamDef{ +} diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 64cc23be20..98e134c2fb 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -27,7 +27,7 @@ import mindustry.world.blocks.environment.*; import static mindustry.Vars.*; @Component(base = true) -abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Displayable, Senseable{ +abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Displayable, Senseable, Ranged{ @Import boolean hovering, dead; @Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health, ammo; @@ -38,6 +38,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I private UnitType type; boolean spawnedByCore; + //TODO mark as non-transient when done + transient double flag; + transient Seq abilities = new Seq<>(0); private transient float resupplyTime = Mathf.random(10f); @@ -63,6 +66,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I return type.hasWeapons(); } + @Override public float range(){ return type.range; } @@ -76,6 +80,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I public double sense(LAccess sensor){ return switch(sensor){ case totalItems -> stack().amount; + case itemCapacity -> type.itemCapacity; case rotation -> rotation; case health -> health; case maxHealth -> maxHealth; @@ -85,6 +90,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I case shooting -> isShooting() ? 1 : 0; case shootX -> aimX(); case shootY -> aimY(); + case flag -> flag; default -> 0; }; } @@ -93,6 +99,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I public Object senseObject(LAccess sensor){ return switch(sensor){ case type -> type; + case name -> controller instanceof Player p ? p.name : null; default -> noSensed; }; @@ -182,7 +189,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I } public int count(){ - return teamIndex.countType(team, type); + return team.data().countType(type); } public int cap(){ @@ -224,13 +231,13 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I //check if over unit cap if(count() > cap() && !spawnedByCore && !dead){ Call.unitCapDeath(self()); - teamIndex.updateCount(team, type, -1); + team.data().updateCount(type, -1); } } @Override public void remove(){ - teamIndex.updateCount(team, type, -1); + team.data().updateCount(type, -1); controller.removed(self()); } diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index d61da2a1ba..52b7dc3b2a 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -63,6 +63,10 @@ public class AIController implements UnitController{ } } + protected boolean invalid(Teamc target){ + return Units.invalidateTarget(target, unit.team, unit.x, unit.y); + } + protected void updateWeapons(){ if(targets.length != unit.mounts.length) targets = new Teamc[unit.mounts.length]; @@ -73,7 +77,7 @@ public class AIController implements UnitController{ target = findTarget(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround); } - if(Units.invalidateTarget(target, unit.team, unit.x, unit.y)){ + if(invalid(target)){ target = null; } @@ -99,13 +103,11 @@ public class AIController implements UnitController{ boolean shoot = false; if(targets[i] != null){ - shoot = targets[i].within(mountX, mountY, weapon.bullet.range()); + shoot = targets[i].within(mountX, mountY, weapon.bullet.range()) && shouldShoot(); - if(shoot){ - Vec2 to = Predict.intercept(unit, targets[i], weapon.bullet.speed); - mount.aimX = to.x; - mount.aimY = to.y; - } + Vec2 to = Predict.intercept(unit, targets[i], weapon.bullet.speed); + mount.aimX = to.x; + mount.aimY = to.y; } mount.shoot = shoot; @@ -113,6 +115,10 @@ public class AIController implements UnitController{ } } + protected boolean shouldShoot(){ + return true; + } + protected Teamc targetFlag(float x, float y, BlockFlag flag, boolean enemy){ Tile target = Geometry.findClosest(x, y, enemy ? indexer.getEnemy(unit.team, flag) : indexer.getAllied(unit.team, flag)); return target == null ? null : target.build; @@ -157,11 +163,15 @@ public class AIController implements UnitController{ } protected void moveTo(Position target, float circleLength){ + moveTo(target, circleLength, 100f); + } + + protected void moveTo(Position target, float circleLength, float smooth){ if(target == null) return; vec.set(target).sub(unit); - float length = circleLength <= 0.001f ? 1f : Mathf.clamp((unit.dst(target) - circleLength) / 100f, -1f, 1f); + float length = circleLength <= 0.001f ? 1f : Mathf.clamp((unit.dst(target) - circleLength) / smooth, -1f, 1f); vec.setLength(unit.type().speed * length); if(length < -0.5f){ diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index 60d36ee74e..5e38b43eb2 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -2,15 +2,20 @@ package mindustry.game; import arc.func.*; import arc.math.geom.*; +import arc.struct.Queue; import arc.struct.*; import arc.util.*; +import mindustry.*; import mindustry.ai.*; import mindustry.content.*; import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.type.*; +import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.storage.CoreBlock.*; +import java.util.*; + import static mindustry.Vars.*; /** Class for various team-based utilities. */ @@ -67,9 +72,7 @@ public class Teams{ /** Returns team data by type. */ public TeamData get(Team team){ - if(map[team.id] == null){ - map[team.id] = new TeamData(team); - } + if(map[team.id] == null) map[team.id] = new TeamData(team); return map[team.id]; } @@ -129,6 +132,61 @@ public class Teams{ } } + private void count(Unit unit){ + unit.team.data().updateCount(unit.type(), 1); + + if(unit instanceof Payloadc){ + ((Payloadc)unit).payloads().each(p -> { + if(p instanceof UnitPayload){ + count(((UnitPayload)p).unit); + } + }); + } + } + + public void updateTeamStats(){ + for(Team team : Team.all){ + TeamData data = team.data(); + + data.unitCount = 0; + data.units.clear(); + if(data.tree != null){ + data.tree.clear(); + } + + if(data.typeCounts != null){ + Arrays.fill(data.typeCounts, 0); + } + + //clear old unit records + if(data.unitsByType != null){ + for(int i = 0; i < data.unitsByType.length; i++){ + if(data.unitsByType[i] != null){ + data.unitsByType[i].clear(); + } + } + } + } + + for(Unit unit : Groups.unit){ + TeamData data = unit.team.data(); + data.tree().insert(unit); + data.units.add(unit); + + if(data.unitsByType == null || data.unitsByType.length <= unit.type().id){ + data.unitsByType = new Seq[content.units().size]; + } + + if(data.unitsByType[unit.type().id] == null){ + data.unitsByType[unit.type().id] = new Seq<>(); + } + + data.unitsByType[unit.type().id].add(unit); + + count(unit); + } + } + private void updateEnemies(){ if(state.rules.waves && !active.contains(get(state.rules.waveTeam))){ active.add(get(state.rules.waveTeam)); @@ -147,7 +205,7 @@ public class Teams{ } } - public class TeamData{ + public static class TeamData{ public final Seq cores = new Seq<>(); public final Team team; public final BaseAI ai; @@ -160,11 +218,48 @@ public class Teams{ /** Target items to mine. */ public Seq mineItems = Seq.with(Items.copper, Items.lead, Items.titanium, Items.thorium); + /** Total unit count. */ + public int unitCount; + /** Counts for each type of unit. Do not access directly. */ + @Nullable + public int[] typeCounts; + /** Quadtree for units of this type. Do not access directly. */ + @Nullable + public QuadTree tree; + /** Units of this team. Updated each frame. */ + public Seq units = new Seq<>(); + /** Units of this team by type. Updated each frame. */ + @Nullable + public Seq[] unitsByType; + public TeamData(Team team){ this.team = team; this.ai = new BaseAI(this); } + @Nullable + public Seq unitCache(UnitType type){ + if(unitsByType == null || unitsByType.length <= type.id || unitsByType[type.id] == null) return null; + return unitsByType[type.id]; + } + + public void updateCount(UnitType type, int amount){ + unitCount = Math.max(amount + unitCount, 0); + if(typeCounts == null || typeCounts.length <= type.id){ + typeCounts = new int[Vars.content.units().size]; + } + typeCounts [type.id] = Math.max(amount + typeCounts [type.id], 0); + } + + public QuadTree tree(){ + if(tree == null) tree = new QuadTree<>(Vars.world.getQuadBounds(new Rect())); + return tree; + } + + public int countType(UnitType type){ + return typeCounts == null || typeCounts.length <= type.id ? 0 : typeCounts[type.id]; + } + public boolean active(){ return (team == state.rules.waveTeam && state.rules.waves) || cores.size > 0; } diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 576e644ce6..ae0f1cd888 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -48,7 +48,7 @@ public class DesktopInput extends InputHandler{ public void buildUI(Group group){ group.fill(t -> { - t.visible(() -> Core.settings.getBool("hints") && ui.hudfrag.shown() && !player.dead() && !player.unit().spawnedByCore() && !(Core.settings.getBool("hints") && lastSchematic != null && !selectRequests.isEmpty())); + t.visible(() -> Core.settings.getBool("hints") && ui.hudfrag.shown && !player.dead() && !player.unit().spawnedByCore() && !(Core.settings.getBool("hints") && lastSchematic != null && !selectRequests.isEmpty())); t.bottom(); t.table(Styles.black6, b -> { b.defaults().left(); diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index d6e15127fc..445df662ce 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -77,6 +77,19 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ createItemTransfer(item, 1, x, y, to, null); } + @Remote(called = Loc.server, unreliable = true) + public static void takeItems(Building build, Item item, int amount, Unit to){ + if(to == null || build == null) return; + + int removed = build.removeStack(item, Math.min(player.unit().maxAccepted(item), amount)); + if(removed == 0) return; + + to.addItem(item, removed); + for(int j = 0; j < Mathf.clamp(removed / 3, 1, 8); j++){ + Time.run(j * 3f, () -> Call.transferItemEffect(item, build.x, build.y, to)); + } + } + @Remote(called = Loc.server, unreliable = true) public static void transferItemToUnit(Item item, float x, float y, Itemsc to){ if(to == null) return; @@ -92,6 +105,16 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ build.items.add(item, amount); } + @Remote(called = Loc.server, unreliable = true) + public static void transferItemTo(Unit unit, Item item, int amount, float x, float y, Building build){ + if(build == null || build.items == null) return; + unit.stack.amount = Math.max(unit.stack.amount - amount, 0); + for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){ + Time.run(i * 3, () -> createItemTransfer(item, amount, x, y, build, () -> {})); + } + build.handleStack(item, amount, unit); + } + public static void createItemTransfer(Item item, int amount, float x, float y, Position to, Runnable done){ Fx.itemTransfer.at(x, y, amount, item.color, to); if(done != null){ @@ -99,6 +122,29 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } } + @Remote(called = Loc.server, targets = Loc.both, forward = true) + public static void requestItem(Player player, Building tile, Item item, int amount){ + if(player == null || tile == null || !tile.interactable(player.team()) || !player.within(tile, buildingRange)) return; + amount = Math.min(player.unit().maxAccepted(item), amount); + int fa = amount; + + if(amount == 0) return; + + if(net.server() && (!Units.canInteract(player, tile) || + !netServer.admins.allowAction(player, ActionType.withdrawItem, tile.tile(), action -> { + action.item = item; + action.itemAmount = fa; + }))) throw new ValidateException(player, "Player cannot request items."); + + int removed = tile.removeStack(item, amount); + + player.unit().addItem(item, removed); + Events.fire(new WithdrawEvent(tile, player, item, amount)); + for(int j = 0; j < Mathf.clamp(removed / 3, 1, 8); j++){ + Time.run(j * 3f, () -> Call.transferItemEffect(item, tile.x, tile.y, player.unit())); + } + } + @Remote(variants = Variant.one) public static void removeQueueBlock(int x, int y, boolean breaking){ player.builder().removeBuild(x, y, breaking); diff --git a/core/src/mindustry/logic/LAccess.java b/core/src/mindustry/logic/LAccess.java index f38745447c..cdb7d838c3 100644 --- a/core/src/mindustry/logic/LAccess.java +++ b/core/src/mindustry/logic/LAccess.java @@ -26,6 +26,8 @@ public enum LAccess{ shooting, team, type, + flag, + name, //values with parameters are considered controllable enabled("to"), //"to" is standard for single parameter access @@ -34,21 +36,21 @@ public enum LAccess{ ; - public final String[] parameters; + public final String[] params; public final boolean isObj; public static final LAccess[] all = values(), - senseable = Seq.select(all, t -> t.parameters.length <= 1).toArray(LAccess.class), - controls = Seq.select(all, t -> t.parameters.length > 0).toArray(LAccess.class); + senseable = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class), + controls = Seq.select(all, t -> t.params.length > 0).toArray(LAccess.class); - LAccess(String... parameters){ - this.parameters = parameters; + LAccess(String... params){ + this.params = params; isObj = false; } - LAccess(boolean obj, String... parameters){ - this.parameters = parameters; + LAccess(boolean obj, String... params){ + this.params = params; isObj = obj; } } diff --git a/core/src/mindustry/logic/LAssembler.java b/core/src/mindustry/logic/LAssembler.java index 10ccbfe1b9..016724f9ba 100644 --- a/core/src/mindustry/logic/LAssembler.java +++ b/core/src/mindustry/logic/LAssembler.java @@ -21,8 +21,12 @@ public class LAssembler{ LInstruction[] instructions; public LAssembler(){ + //instruction counter putVar("@counter").value = 0; + //unix timestamp putConst("@time", 0); + //currently controlled unit + putConst("@unit", null); //add default constants putConst("false", 0); @@ -45,6 +49,10 @@ public class LAssembler{ } } + for(UnitType type : Vars.content.units()){ + putConst("@" + type.name, type); + } + //store sensor constants for(LAccess sensor : LAccess.all){ diff --git a/core/src/mindustry/logic/LCategory.java b/core/src/mindustry/logic/LCategory.java index b576261f74..849e2f64bd 100644 --- a/core/src/mindustry/logic/LCategory.java +++ b/core/src/mindustry/logic/LCategory.java @@ -7,7 +7,8 @@ public enum LCategory{ blocks(Pal.accentBack), control(Color.cyan.cpy().shiftSaturation(-0.6f).mul(0.7f)), operations(Pal.place.cpy().shiftSaturation(-0.5f).mul(0.7f)), - io(Pal.remove.cpy().shiftSaturation(-0.5f).mul(0.7f)); + io(Pal.remove.cpy().shiftSaturation(-0.5f).mul(0.7f)), + units(Pal.bulletYellowBack.cpy().shiftSaturation(-0.3f).mul(0.8f)); public final Color color; diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 053d1e9c1a..9188adba2e 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -4,10 +4,13 @@ import arc.struct.*; import arc.util.*; import arc.util.noise.*; import mindustry.*; +import mindustry.ai.types.*; import mindustry.ctype.*; import mindustry.entities.*; import mindustry.game.*; import mindustry.gen.*; +import mindustry.type.*; +import mindustry.world.*; import mindustry.world.blocks.logic.LogicDisplay.*; import mindustry.world.blocks.logic.MemoryBlock.*; import mindustry.world.blocks.logic.MessageBlock.*; @@ -23,7 +26,8 @@ public class LExecutor{ //special variables public static final int varCounter = 0, - varTime = 1; + varTime = 1, + varUnit = 2; public static final int maxGraphicsBuffer = 256, @@ -36,6 +40,7 @@ public class LExecutor{ public LongSeq graphicsBuffer = new LongSeq(); public StringBuilder textBuffer = new StringBuilder(); public Building[] links = {}; + public Team team = Team.derelict; public boolean initialized(){ return instructions != null && vars != null && instructions.length > 0; @@ -102,6 +107,11 @@ public class LExecutor{ return v.isobj ? v.objval != null ? 1 : 0 : v.numval; } + public float numf(int index){ + Var v = vars[index]; + return v.isobj ? v.objval != null ? 1 : 0 : (float)v.numval; + } + public int numi(int index){ return (int)num(index); } @@ -121,6 +131,12 @@ public class LExecutor{ v.isobj = true; } + public void setconst(int index, Object value){ + Var v = vars[index]; + v.objval = value; + v.isobj = true; + } + //endregion public static class Var{ @@ -142,6 +158,153 @@ public class LExecutor{ void run(LExecutor exec); } + /** Binds the processor to a unit based on some filters. */ + public static class UnitBindI implements LInstruction{ + public int type; + + //iteration index + private int index; + + public UnitBindI(int type){ + this.type = type; + } + + public UnitBindI(){ + } + + @Override + public void run(LExecutor exec){ + Object typeObj = exec.obj(type); + UnitType type = typeObj instanceof UnitType t ? t : null; + + Seq seq = type == null ? exec.team.data().units : exec.team.data().unitCache(type); + + if(seq != null && seq.any()){ + index %= seq.size; + if(index < seq.size){ + //bind to the next unit + exec.setconst(varUnit, seq.get(index)); + } + index ++; + }else{ + //no units of this type found + exec.setconst(varUnit, null); + } + } + } + + /** Controls the unit based on some parameters. */ + public static class UnitControlI implements LInstruction{ + public LUnitControl type = LUnitControl.move; + public int p1, p2, p3, p4; + + public UnitControlI(LUnitControl type, int p1, int p2, int p3, int p4){ + this.type = type; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.p4 = p4; + } + + public UnitControlI(){ + } + + /** Checks is a unit is valid for logic AI control, and returns the controller. */ + @Nullable + public static LogicAI checkLogicAI(LExecutor exec, Object unitObj){ + if(unitObj instanceof Unit unit && exec.obj(varUnit) == unit && unit.team == exec.team && !unit.isPlayer() && !(unit.controller() instanceof FormationAI)){ + if(!(unit.controller() instanceof LogicAI)){ + unit.controller(new LogicAI()); + + //clear old state + if(unit instanceof Minerc miner){ + miner.mineTile(null); + } + + if(unit instanceof Builderc builder){ + builder.clearBuilding(); + } + + return (LogicAI)unit.controller(); + } + return (LogicAI)unit.controller(); + } + return null; + } + + @Override + public void run(LExecutor exec){ + Object unitObj = exec.obj(varUnit); + LogicAI ai = checkLogicAI(exec, unitObj); + + //only control standard AI units + if(unitObj instanceof Unit unit && ai != null){ + ai.controlTimer = LogicAI.logicControlTimeout; + + switch(type){ + case move, stop, approach -> { + ai.control = type; + ai.moveX = exec.numf(p1); + ai.moveY = exec.numf(p2); + if(type == LUnitControl.approach){ + ai.moveRad = exec.numf(p3); + } + } + case target -> { + ai.posTarget.set(exec.numf(p1), exec.numf(p2)); + ai.aimControl = type; + ai.mainTarget = null; + ai.shoot = exec.bool(p3); + } + case targetp -> { + ai.aimControl = type; + ai.mainTarget = exec.obj(p1) instanceof Teamc t ? t : null; + ai.shoot = exec.bool(p2); + } + case flag -> { + unit.flag = exec.num(p1); + } + case mine -> { + Tile tile = world.tileWorld(exec.numf(p1), exec.numf(p2)); + if(unit instanceof Minerc miner){ + miner.mineTile(tile); + } + } + case itemDrop -> { + if(ai.itemTimer > 0) return; + + Building build = exec.building(p1); + int amount = exec.numi(p2); + int dropped = Math.min(unit.stack.amount, amount); + if(build != null && dropped > 0 && unit.within(build, logicItemTransferRange)){ + int accepted = build.acceptStack(unit.item(), dropped, unit); + if(accepted > 0){ + Call.transferItemTo(unit, unit.item(), accepted, unit.x, unit.y, build); + ai.itemTimer = LogicAI.transferDelay; + } + } + } + case itemTake -> { + if(ai.itemTimer > 0) return; + + Building build = exec.building(p1); + int amount = exec.numi(p3); + + if(build != null && exec.obj(p2) instanceof Item item && unit.within(build, logicItemTransferRange)){ + int taken = Math.min(build.items.get(item), Math.min(amount, unit.maxAccepted(item))); + + if(taken > 0){ + Call.takeItems(build, item, taken, unit); + ai.itemTimer = LogicAI.transferDelay; + } + } + } + default -> {} + } + } + } + } + /** Controls a building's state. */ public static class ControlI implements LInstruction{ public int target; @@ -311,16 +474,20 @@ public class LExecutor{ @Override public void run(LExecutor exec){ - Building target = exec.building(radar); + Object base = exec.obj(radar); int sortDir = exec.bool(sortOrder) ? 1 : -1; + LogicAI ai = null; - if(target instanceof Ranged){ - float range = ((Ranged)target).range(); + if(base instanceof Ranged r && r.team() == exec.team && + (base instanceof Building || (ai = UnitControlI.checkLogicAI(exec, base)) != null)){ //must be a building or a controllable unit + float range = r.range(); Healthc targeted; - if(timer.get(30f)){ + //timers update on a fixed 30 tick interval + //units update on a special timer per controller instance + if((base instanceof Building && timer.get(30f)) || (ai != null && ai.checkTargetTimer(this))){ //if any of the targets involve enemies boolean enemies = target1 == RadarTarget.enemy || target2 == RadarTarget.enemy || target3 == RadarTarget.enemy; @@ -328,11 +495,11 @@ public class LExecutor{ bestValue = 0; if(enemies){ - for(Team enemy : state.teams.enemiesOf(target.team)){ - find(target, range, sortDir, enemy); + for(Team enemy : state.teams.enemiesOf(r.team())){ + find(r, range, sortDir, enemy); } }else{ - find(target, range, sortDir, target.team); + find(r, range, sortDir, r.team()); } lastTarget = targeted = best; @@ -346,14 +513,14 @@ public class LExecutor{ } } - void find(Building b, float range, int sortDir, Team team){ - Units.nearby(team, b.x, b.y, range, u -> { + void find(Ranged b, float range, int sortDir, Team team){ + Units.nearby(team, b.x(), b.y(), range, u -> { if(!u.within(b, range)) return; boolean valid = - target1.func.get(b.team, u) && - target2.func.get(b.team, u) && - target3.func.get(b.team, u); + target1.func.get(b.team(), u) && + target2.func.get(b.team(), u) && + target3.func.get(b.team(), u); if(!valid) return; diff --git a/core/src/mindustry/logic/LStatement.java b/core/src/mindustry/logic/LStatement.java index 3f2f6fd413..6292d8cfa4 100644 --- a/core/src/mindustry/logic/LStatement.java +++ b/core/src/mindustry/logic/LStatement.java @@ -31,6 +31,10 @@ public abstract class LStatement{ return read.size == 0 ? null : read.first(); } + public boolean hidden(){ + return false; + } + //protected methods are only for internal UI layout utilities protected Cell field(Table table, String value, Cons setter){ @@ -38,9 +42,9 @@ public abstract class LStatement{ .size(144f, 40f).pad(2f).color(table.color).addInputDialog(); } - protected void fields(Table table, String desc, String value, Cons setter){ + protected Cell fields(Table table, String desc, String value, Cons setter){ table.add(desc).padLeft(10).left(); - field(table, value, setter).width(85f).padRight(10).left(); + return field(table, value, setter).width(85f).padRight(10).left(); } protected void fields(Table table, String value, Cons setter){ diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index 2ce318f4a7..330a9b0b3b 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -347,9 +347,9 @@ public class LStatements{ //Q: why don't you just use arrays for this? //A: arrays aren't as easy to serialize so the code generator doesn't handle them int c = 0; - for(int i = 0; i < type.parameters.length; i++){ + for(int i = 0; i < type.params.length; i++){ - fields(table, type.parameters[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : p4, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : v -> p4 = v); + fields(table, type.params[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : p4, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : v -> p4 = v); if(++c % 2 == 0) row(table); } @@ -376,11 +376,13 @@ public class LStatements{ public void build(Table table){ table.defaults().left(); - table.add(" from "); + if(buildFrom()){ + table.add(" from "); - fields(table, radar, v -> radar = v); + fields(table, radar, v -> radar = v); - row(table); + row(table); + } for(int i = 0; i < 3; i++){ int fi = i; @@ -420,6 +422,10 @@ public class LStatements{ fields(table, output, v -> output = v); } + public boolean buildFrom(){ + return true; + } + @Override public LCategory category(){ return LCategory.blocks; @@ -694,4 +700,95 @@ public class LStatements{ return LCategory.control; } } + + @RegisterStatement("ubind") + public static class UnitBindStatement extends LStatement{ + public String type = "@mono"; + + @Override + public void build(Table table){ + table.add(" type "); + + field(table, type, str -> type = str); + } + + @Override + public LCategory category(){ + return LCategory.units; + } + + @Override + public LInstruction build(LAssembler builder){ + return new UnitBindI(builder.var(type)); + } + } + + @RegisterStatement("ucontrol") + public static class UnitControlStatement extends LStatement{ + public LUnitControl type = LUnitControl.move; + public String p1 = "0", p2 = "0", p3 = "0", p4 = "0"; + + @Override + public void build(Table table){ + rebuild(table); + } + + void rebuild(Table table){ + table.clearChildren(); + + table.left(); + + table.add(" "); + + table.button(b -> { + b.label(() -> type.name()); + b.clicked(() -> showSelect(b, LUnitControl.all, type, t -> { + type = t; + rebuild(table); + }, 2, cell -> cell.size(120, 50))); + }, Styles.logict, () -> {}).size(120, 40).color(table.color).left().padLeft(2); + + row(table); + + //Q: why don't you just use arrays for this? + //A: arrays aren't as easy to serialize so the code generator doesn't handle them + int c = 0; + for(int i = 0; i < type.params.length; i++){ + + fields(table, type.params[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : p4, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : v -> p4 = v).width(110f); + + if(++c % 2 == 0) row(table); + } + } + + @Override + public LCategory category(){ + return LCategory.units; + } + + @Override + public LInstruction build(LAssembler builder){ + return new UnitControlI(type, builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4)); + } + } + + @RegisterStatement("uradar") + public static class UnitRadarStatement extends RadarStatement{ + + @Override + public boolean buildFrom(){ + //do not build the "from" section + return false; + } + + @Override + public LCategory category(){ + return LCategory.units; + } + + @Override + public LInstruction build(LAssembler builder){ + return new RadarI(target1, target2, target3, sort, LExecutor.varUnit, builder.var(sortOrder), builder.var(output)); + } + } } diff --git a/core/src/mindustry/logic/LUnitControl.java b/core/src/mindustry/logic/LUnitControl.java new file mode 100644 index 0000000000..6c11b21d5e --- /dev/null +++ b/core/src/mindustry/logic/LUnitControl.java @@ -0,0 +1,20 @@ +package mindustry.logic; + +public enum LUnitControl{ + stop, + move("x", "y"), + approach("x", "y", "radius"), + target("x", "y", "shoot"), + targetp("unit", "shoot"), + itemDrop("to", "amount"), + itemTake("from", "item", "amount"), + mine("x", "y"), + flag("value"); + + public final String[] params; + public static final LUnitControl[] all = values(); + + LUnitControl(String... params){ + this.params = params; + } +} diff --git a/core/src/mindustry/logic/LogicDialog.java b/core/src/mindustry/logic/LogicDialog.java index fb97e5c4bc..44fcddd2c0 100644 --- a/core/src/mindustry/logic/LogicDialog.java +++ b/core/src/mindustry/logic/LogicDialog.java @@ -60,7 +60,7 @@ public class LogicDialog extends BaseDialog{ int i = 0; for(Prov prov : LogicIO.allStatements){ LStatement example = prov.get(); - if(example instanceof InvalidStatement) continue; + if(example instanceof InvalidStatement || example.hidden()) continue; TextButtonStyle style = new TextButtonStyle(Styles.cleart); style.fontColor = example.category().color; diff --git a/core/src/mindustry/logic/LogicOp.java b/core/src/mindustry/logic/LogicOp.java index b289efc1f2..40d02a8345 100644 --- a/core/src/mindustry/logic/LogicOp.java +++ b/core/src/mindustry/logic/LogicOp.java @@ -1,6 +1,7 @@ package mindustry.logic; import arc.math.*; +import arc.util.*; public enum LogicOp{ add("+", (a, b) -> a + b), @@ -9,8 +10,8 @@ public enum LogicOp{ div("/", (a, b) -> a / b), idiv("//", (a, b) -> Math.floor(a / b)), mod("%", (a, b) -> a % b), - equal("==", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0, (a, b) -> a == b ? 1 : 0), - notEqual("not", (a, b) -> Math.abs(a - b) < 0.000001 ? 0 : 1, (a, b) -> a != b ? 1 : 0), + equal("==", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0, (a, b) -> Structs.eq(a, b) ? 1 : 0), + notEqual("not", (a, b) -> Math.abs(a - b) < 0.000001 ? 0 : 1, (a, b) -> !Structs.eq(a, b) ? 1 : 0), lessThan("<", (a, b) -> a < b ? 1 : 0), lessThanEq("<=", (a, b) -> a <= b ? 1 : 0), greaterThan(">", (a, b) -> a > b ? 1 : 0), diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index f927bc83d5..0ea8b99c1b 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -163,6 +163,11 @@ public class UnitType extends UnlockableContent{ bars.row(); } }).growX(); + + if(unit.controller() instanceof LogicAI){ + table.row(); + table.add(Blocks.microProcessor.emoji() + " " + Core.bundle.get("units.processorcontrol")).growX().left(); + } table.row(); } @@ -206,7 +211,7 @@ public class UnitType extends UnlockableContent{ singleTarget = weapons.size <= 1; if(itemCapacity < 0){ - itemCapacity = Math.max(Mathf.round(hitSize * 7, 20), 20); + itemCapacity = Math.max(Mathf.round(hitSize * 4, 10), 10); } //set up default range diff --git a/core/src/mindustry/ui/fragments/BlockInventoryFragment.java b/core/src/mindustry/ui/fragments/BlockInventoryFragment.java index 3b60918356..a4a97553bd 100644 --- a/core/src/mindustry/ui/fragments/BlockInventoryFragment.java +++ b/core/src/mindustry/ui/fragments/BlockInventoryFragment.java @@ -14,12 +14,8 @@ import arc.scene.ui.layout.Stack; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; -import mindustry.annotations.Annotations.*; -import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.gen.*; -import mindustry.net.Administration.*; -import mindustry.net.*; import mindustry.type.*; import mindustry.ui.*; @@ -42,29 +38,6 @@ public class BlockInventoryFragment extends Fragment{ Events.on(WorldLoadEvent.class, e -> hide()); } - @Remote(called = Loc.server, targets = Loc.both, forward = true) - public static void requestItem(Player player, Building tile, Item item, int amount){ - if(player == null || tile == null || !tile.interactable(player.team()) || !player.within(tile, buildingRange)) return; - amount = Math.min(player.unit().maxAccepted(item), amount); - int fa = amount; - - if(amount == 0) return; - - if(net.server() && (!Units.canInteract(player, tile) || - !netServer.admins.allowAction(player, ActionType.withdrawItem, tile.tile(), action -> { - action.item = item; - action.itemAmount = fa; - }))) throw new ValidateException(player, "Player cannot request items."); - - int removed = tile.removeStack(item, amount); - - player.unit().addItem(item, removed); - Events.fire(new WithdrawEvent(tile, player, item, amount)); - for(int j = 0; j < Mathf.clamp(removed / 3, 1, 8); j++){ - Time.run(j * 3f, () -> Call.transferItemEffect(item, tile.x, tile.y, player.unit())); - } - } - @Override public void build(Group parent){ table.name = "inventory"; diff --git a/core/src/mindustry/ui/fragments/ChatFragment.java b/core/src/mindustry/ui/fragments/ChatFragment.java index 93654f048c..be9115293e 100644 --- a/core/src/mindustry/ui/fragments/ChatFragment.java +++ b/core/src/mindustry/ui/fragments/ChatFragment.java @@ -57,7 +57,7 @@ public class ChatFragment extends Table{ } } - return net.active() && ui.hudfrag.shown(); + return net.active() && ui.hudfrag.shown; }); update(() -> { diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index c16d2f23ec..8ee4447079 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -32,16 +32,16 @@ public class HudFragment extends Fragment{ private static final float dsize = 65f; public final PlacementFragment blockfrag = new PlacementFragment(); + public boolean shown = true; private ImageButton flip; - private Table lastUnlockTable; - private Table lastUnlockLayout; - private boolean shown = true; private CoreItemsDisplay coreItems = new CoreItemsDisplay(); private String hudText = ""; private boolean showHudText; + private Table lastUnlockTable; + private Table lastUnlockLayout; private long lastToast; @Override @@ -420,10 +420,6 @@ public class HudFragment extends Fragment{ }); } - public boolean shown(){ - return shown; - } - /** Show unlock notification for a new recipe. */ public void showUnlock(UnlockableContent content){ //some content may not have icons... yet diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index cd5279d0d8..5362769a49 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -192,7 +192,7 @@ public class PlacementFragment extends Fragment{ public void build(Group parent){ parent.fill(full -> { toggler = full; - full.bottom().right().visible(() -> ui.hudfrag.shown()); + full.bottom().right().visible(() -> ui.hudfrag.shown); full.table(frame -> { diff --git a/core/src/mindustry/world/blocks/distribution/Router.java b/core/src/mindustry/world/blocks/distribution/Router.java index cb0ae9807d..5b107d5a0e 100644 --- a/core/src/mindustry/world/blocks/distribution/Router.java +++ b/core/src/mindustry/world/blocks/distribution/Router.java @@ -28,7 +28,7 @@ public class Router extends Block{ @Override public void updateTile(){ if(lastItem == null && items.any()){ - items.clear(); + lastItem = items.first(); } if(lastItem != null){ diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index b5439b3e70..5f6fabc983 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -331,6 +331,7 @@ public class LogicBlock extends Block{ @Override public void updateTile(){ + executor.team = team; //check for previously invalid links to add after configuration boolean changed = false; diff --git a/core/src/mindustry/world/blocks/logic/LogicDisplay.java b/core/src/mindustry/world/blocks/logic/LogicDisplay.java index 9a6592ea36..cb062259b8 100644 --- a/core/src/mindustry/world/blocks/logic/LogicDisplay.java +++ b/core/src/mindustry/world/blocks/logic/LogicDisplay.java @@ -75,15 +75,15 @@ public class LogicDisplay extends Block{ p1 = DisplayCmd.p1(c), p2 = DisplayCmd.p2(c), p3 = DisplayCmd.p3(c), p4 = DisplayCmd.p4(c); switch(type){ - case commandClear: Core.graphics.clear(x/255f, y/255f, p1/255f, 1f); break; - case commandLine: Lines.line(x, y, p1, p2); break; - case commandRect: Fill.crect(x, y, p1, p2); break; - case commandLineRect: Lines.rect(x, y, p1, p2); break; - case commandPoly: Fill.poly(x, y, Math.min(p1, maxSides), p2, p3); break; - case commandLinePoly: Lines.poly(x, y, Math.min(p1, maxSides), p2, p3); break; - case commandTriangle: Fill.tri(x, y, p1, p2, p3, p4); break; - case commandColor: this.color = Color.toFloatBits(x, y, p1, p2); Draw.color(this.color); break; - case commandStroke: this.stroke = x; Lines.stroke(x); break; + case commandClear -> Core.graphics.clear(x / 255f, y / 255f, p1 / 255f, 1f); + case commandLine -> Lines.line(x, y, p1, p2); + case commandRect -> Fill.crect(x, y, p1, p2); + case commandLineRect -> Lines.rect(x, y, p1, p2); + case commandPoly -> Fill.poly(x, y, Math.min(p1, maxSides), p2, p3); + case commandLinePoly -> Lines.poly(x, y, Math.min(p1, maxSides), p2, p3); + case commandTriangle -> Fill.tri(x, y, p1, p2, p3, p4); + case commandColor -> Draw.color(this.color = Color.toFloatBits(x, y, p1, p2)); + case commandStroke -> Lines.stroke(this.stroke = x); } } diff --git a/core/src/mindustry/world/blocks/units/Reconstructor.java b/core/src/mindustry/world/blocks/units/Reconstructor.java index fa2783f30f..3cd453719e 100644 --- a/core/src/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/mindustry/world/blocks/units/Reconstructor.java @@ -50,11 +50,11 @@ public class Reconstructor extends UnitBlock{ () -> e.unit() == null ? "[lightgray]" + Iconc.cancel : Core.bundle.format("bar.unitcap", Fonts.getUnicodeStr(e.unit().name), - teamIndex.countType(e.team, e.unit()), + e.team.data().countType(e.unit()), Units.getCap(e.team) ), () -> Pal.power, - () -> e.unit() == null ? 0f : (float)teamIndex.countType(e.team, e.unit()) / Units.getCap(e.team) + () -> e.unit() == null ? 0f : (float)e.team.data().countType(e.unit()) / Units.getCap(e.team) )); } diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 9b96b6f084..d664653e46 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -21,8 +21,6 @@ import mindustry.world.blocks.payloads.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; -import static mindustry.Vars.*; - public class UnitFactory extends UnitBlock{ public int[] capacities; @@ -71,11 +69,11 @@ public class UnitFactory extends UnitBlock{ () -> e.unit() == null ? "[lightgray]" + Iconc.cancel : Core.bundle.format("bar.unitcap", Fonts.getUnicodeStr(e.unit().name), - teamIndex.countType(e.team, e.unit()), + e.team.data().countType(e.unit()), Units.getCap(e.team) ), () -> Pal.power, - () -> e.unit() == null ? 0f : (float)teamIndex.countType(e.team, e.unit()) / Units.getCap(e.team) + () -> e.unit() == null ? 0f : (float)e.team.data().countType(e.unit()) / Units.getCap(e.team) )); }