mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-01-28 07:22:21 -08:00
Merge branch 'master' into healbullets-bullets
This commit is contained in:
commit
cba2fd4351
43 changed files with 735 additions and 224 deletions
|
|
@ -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<Svar> fields = c.fields();
|
||||
fields.addAll(c.superclass().fields());
|
||||
|
||||
String readSt = "if(tokens[0].equals($S))";
|
||||
if(beganRead){
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
1
annotations/src/main/resources/revisions/PosTeam/0.json
Normal file
1
annotations/src/main/resources/revisions/PosTeam/0.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{fields:[{name:team,type:mindustry.game.Team},{name:x,type:float},{name:y,type:float}]}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = 45f;
|
||||
/** 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;
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ public class BlockIndexer{
|
|||
|
||||
if(other == null) continue;
|
||||
|
||||
if(other.team == team && pred.get(other) && intSet.add(other.pos())){
|
||||
if((team == null || other.team == team) && pred.get(other) && intSet.add(other.pos())){
|
||||
cons.get(other);
|
||||
any = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@ public class GroundAI extends AIController{
|
|||
if(spawner != null && unit.within(spawner, state.rules.dropZoneRadius + 120f)) move = false;
|
||||
}
|
||||
|
||||
if(move) moveTo(Pathfinder.fieldCore);
|
||||
if(move) pathfind(Pathfinder.fieldCore);
|
||||
}
|
||||
|
||||
if(command() == UnitCommand.rally){
|
||||
Teamc target = targetFlag(unit.x, unit.y, BlockFlag.rally, false);
|
||||
|
||||
if(target != null && !unit.within(target, 70f)){
|
||||
moveTo(Pathfinder.fieldRally);
|
||||
pathfind(Pathfinder.fieldRally);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,16 +72,4 @@ public class GroundAI extends AIController{
|
|||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
protected void moveTo(int pathTarget){
|
||||
int costType = unit.pathType();
|
||||
|
||||
Tile tile = unit.tileOn();
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathTarget));
|
||||
|
||||
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;
|
||||
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
129
core/src/mindustry/ai/types/LogicAI.java
Normal file
129
core/src/mindustry/ai/types/LogicAI.java
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package mindustry.ai.types;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
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 = 10f * 60f;
|
||||
|
||||
public LUnitControl control = LUnitControl.stop;
|
||||
public float moveX, moveY, moveRad;
|
||||
public float itemTimer, controlTimer = logicControlTimeout, targetTimer;
|
||||
public Building controller;
|
||||
|
||||
//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<RadarI> 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 && controller != null && controller.isValid()){
|
||||
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, 1f);
|
||||
}
|
||||
case pathfind -> {
|
||||
Building core = unit.closestEnemyCore();
|
||||
|
||||
if((core == null || !unit.within(core, unit.range() * 0.5f)) && command() == UnitCommand.attack){
|
||||
boolean move = true;
|
||||
|
||||
if(state.rules.waves && unit.team == state.rules.defaultTeam){
|
||||
Tile spawner = getClosestSpawner();
|
||||
if(spawner != null && unit.within(spawner, state.rules.dropZoneRadius + 120f)) move = false;
|
||||
}
|
||||
|
||||
if(move) pathfind(Pathfinder.fieldCore);
|
||||
}
|
||||
|
||||
if(command() == UnitCommand.rally){
|
||||
Teamc target = targetFlag(unit.x, unit.y, BlockFlag.rally, false);
|
||||
|
||||
if(target != null && !unit.within(target, 70f)){
|
||||
pathfind(Pathfinder.fieldRally);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -67,10 +67,10 @@ public class SuicideAI extends GroundAI{
|
|||
Teamc target = targetFlag(unit.x, unit.y, BlockFlag.rally, false);
|
||||
|
||||
if(target != null && !unit.within(target, 70f)){
|
||||
moveTo(Pathfinder.fieldRally);
|
||||
pathfind(Pathfinder.fieldRally);
|
||||
}
|
||||
}else if(command() == UnitCommand.attack && core != null){
|
||||
moveTo(Pathfinder.fieldCore);
|
||||
pathfind(Pathfinder.fieldCore);
|
||||
}
|
||||
|
||||
if(unit.moving()) unit.lookAt(unit.vel().angle());
|
||||
|
|
|
|||
|
|
@ -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<AsyncProcess> processes = Seq.with(
|
||||
new PhysicsProcess(),
|
||||
Vars.teamIndex = new TeamIndexProcess()
|
||||
new PhysicsProcess()
|
||||
);
|
||||
|
||||
//futures to be awaited
|
||||
|
|
|
|||
|
|
@ -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<Unit>[] 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<Unit> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -397,7 +394,7 @@ public class UnitTypes implements ContentList{
|
|||
engineSize = 6f;
|
||||
lowAltitude = true;
|
||||
|
||||
health = 6500f;
|
||||
health = 7000f;
|
||||
armor = 7f;
|
||||
canBoost = true;
|
||||
landShake = 4f;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -283,6 +283,8 @@ public class Logic implements ApplicationListener{
|
|||
}
|
||||
|
||||
if(!state.isPaused()){
|
||||
state.teams.updateTeamStats();
|
||||
|
||||
if(state.isCampaign()){
|
||||
state.secinfo.update();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ public class Renderer implements ApplicationListener{
|
|||
Color.white.set(1f, 1f, 1f, 1f);
|
||||
Gl.clear(Gl.stencilBufferBit);
|
||||
|
||||
camerascale = Mathf.lerpDelta(camerascale, targetscale, 0.1f);
|
||||
float dest = Mathf.round(targetscale, 0.5f);
|
||||
camerascale = Mathf.lerpDelta(camerascale, dest, 0.1f);
|
||||
if(Mathf.within(camerascale, dest, 0.001f)) camerascale = dest;
|
||||
laserOpacity = Core.settings.getInt("lasersopacity") / 100f;
|
||||
|
||||
if(landTime > 0){
|
||||
|
|
@ -303,6 +305,10 @@ public class Renderer implements ApplicationListener{
|
|||
targetscale = Mathf.clamp(targetscale, minScale(), Math.round(s * 6));
|
||||
}
|
||||
|
||||
public float getDisplayScale(){
|
||||
return camerascale;
|
||||
}
|
||||
|
||||
public float minScale(){
|
||||
return Scl.scl(1.5f);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Unit> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,6 +146,9 @@ public abstract class BulletType extends Content{
|
|||
}
|
||||
|
||||
public void hitTile(Bullet b, Building tile, float initialHealth){
|
||||
if(status == StatusEffects.burning) {
|
||||
Fires.create(tile.tile);
|
||||
}
|
||||
hit(b);
|
||||
|
||||
if(healPercent > 0f && tile.team == b.team && !(tile.block instanceof ConstructBlock)){
|
||||
|
|
@ -193,11 +196,16 @@ public abstract class BulletType extends Content{
|
|||
if(status != StatusEffects.none){
|
||||
Damage.status(b.team, x, y, splashDamageRadius, status, statusDuration, collidesAir, collidesGround);
|
||||
}
|
||||
|
||||
|
||||
if(healPercent > 0f) {
|
||||
indexer.eachBlock(b.team, x, y, splashDamageRadius, other -> other.damaged(), other -> {
|
||||
Fx.healBlockFull.at(other.x, other.y, other.block.size, Pal.heal);
|
||||
other.heal(healPercent / 100f * other.maxHealth());
|
||||
}
|
||||
|
||||
if(status == StatusEffects.burning) {
|
||||
indexer.eachBlock(null, x, y, splashDamageRadius, other -> other.team != b.team, other -> {
|
||||
Fires.create(other.tile);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
|||
public void getCollisions(Cons<QuadTree> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
core/src/mindustry/entities/comp/PosTeamDef.java
Normal file
9
core/src/mindustry/entities/comp/PosTeamDef.java
Normal file
|
|
@ -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{
|
||||
}
|
||||
|
|
@ -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<Ability> 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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import arc.math.*;
|
|||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
|
@ -63,6 +64,23 @@ public class AIController implements UnitController{
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean invalid(Teamc target){
|
||||
return Units.invalidateTarget(target, unit.team, unit.x, unit.y);
|
||||
}
|
||||
|
||||
|
||||
protected void pathfind(int pathTarget){
|
||||
int costType = unit.pathType();
|
||||
|
||||
Tile tile = unit.tileOn();
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathTarget));
|
||||
|
||||
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;
|
||||
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
||||
}
|
||||
|
||||
protected void updateWeapons(){
|
||||
if(targets.length != unit.mounts.length) targets = new Teamc[unit.mounts.length];
|
||||
|
||||
|
|
@ -73,7 +91,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 +117,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 +129,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 +177,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){
|
||||
|
|
|
|||
|
|
@ -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<CoreBuild> cores = new Seq<>();
|
||||
public final Team team;
|
||||
public final BaseAI ai;
|
||||
|
|
@ -160,11 +218,48 @@ public class Teams{
|
|||
/** Target items to mine. */
|
||||
public Seq<Item> 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<Unit> tree;
|
||||
/** Units of this team. Updated each frame. */
|
||||
public Seq<Unit> units = new Seq<>();
|
||||
/** Units of this team by type. Updated each frame. */
|
||||
@Nullable
|
||||
public Seq<Unit>[] unitsByType;
|
||||
|
||||
public TeamData(Team team){
|
||||
this.team = team;
|
||||
this.ai = new BaseAI(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Seq<Unit> 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<Unit> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,14 @@ public class LAssembler{
|
|||
LInstruction[] instructions;
|
||||
|
||||
public LAssembler(){
|
||||
//instruction counter
|
||||
putVar("@counter").value = 0;
|
||||
//unix timestamp
|
||||
putConst("@time", 0);
|
||||
//currently controlled unit
|
||||
putConst("@unit", null);
|
||||
//reference to self
|
||||
putConst("@this", null);
|
||||
|
||||
//add default constants
|
||||
putConst("false", 0);
|
||||
|
|
@ -45,6 +51,10 @@ public class LAssembler{
|
|||
}
|
||||
}
|
||||
|
||||
for(UnitType type : Vars.content.units()){
|
||||
putConst("@" + type.name, type);
|
||||
}
|
||||
|
||||
//store sensor constants
|
||||
|
||||
for(LAccess sensor : LAccess.all){
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,9 @@ public class LExecutor{
|
|||
//special variables
|
||||
public static final int
|
||||
varCounter = 0,
|
||||
varTime = 1;
|
||||
varTime = 1,
|
||||
varUnit = 2,
|
||||
varThis = 3;
|
||||
|
||||
public static final int
|
||||
maxGraphicsBuffer = 256,
|
||||
|
|
@ -36,6 +41,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 +108,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 +132,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 +159,157 @@ 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<Unit> 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());
|
||||
((LogicAI)unit.controller()).controller = exec.building(varThis);
|
||||
|
||||
//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 pathfind -> {
|
||||
ai.control = type;
|
||||
}
|
||||
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 +479,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 +500,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 +518,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TextField> field(Table table, String value, Cons<String> 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<String> setter){
|
||||
protected Cell<TextField> fields(Table table, String desc, String value, Cons<String> 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<String> setter){
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
core/src/mindustry/logic/LUnitControl.java
Normal file
21
core/src/mindustry/logic/LUnitControl.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package mindustry.logic;
|
||||
|
||||
public enum LUnitControl{
|
||||
stop,
|
||||
move("x", "y"),
|
||||
approach("x", "y", "radius"),
|
||||
pathfind(),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ public class LogicDialog extends BaseDialog{
|
|||
int i = 0;
|
||||
for(Prov<LStatement> 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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class ChatFragment extends Table{
|
|||
}
|
||||
}
|
||||
|
||||
return net.active() && ui.hudfrag.shown();
|
||||
return net.active() && ui.hudfrag.shown;
|
||||
});
|
||||
|
||||
update(() -> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 -> {
|
||||
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ public class LogicBlock extends Block{
|
|||
assemble.get(asm);
|
||||
}
|
||||
|
||||
asm.putConst("@this", this);
|
||||
asm.getVar("@this").value = this;
|
||||
asm.putConst("@thisx", x);
|
||||
asm.putConst("@thisy", y);
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
||||
archash=6fca97cecdf28a696b2c19097f70960485a62743
|
||||
archash=86e6ad07a2bfc727f8772c3e9c5f9d2761580879
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue