diff --git a/android/build.gradle b/android/build.gradle index a57048a4c6..dd5cd98c34 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -62,7 +62,7 @@ android{ targetSdkVersion 30 versionName versionNameResult - versionCode = (System.getenv("TRAVIS_BUILD_ID") != null ? System.getenv("TRAVIS_BUILD_ID").toInteger() : vcode) + versionCode = vcode if(project.hasProperty("release")){ props['androidBuildCode'] = (vcode + 1).toString() diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro index 373c4b9124..368b7f4e1e 100644 --- a/android/proguard-rules.pro +++ b/android/proguard-rules.pro @@ -6,4 +6,6 @@ -keep class arc.** { *; } -keep class net.jpountz.** { *; } -keep class rhino.** { *; } --keep class com.android.dex.** { *; } \ No newline at end of file +-keep class com.android.dex.** { *; } + +#-printusage out.txt \ No newline at end of file diff --git a/core/assets-raw/sprites/units/assembly-drone-cell.png b/core/assets-raw/sprites/units/assembly-drone-cell.png new file mode 100644 index 0000000000..8312d7a6e7 Binary files /dev/null and b/core/assets-raw/sprites/units/assembly-drone-cell.png differ diff --git a/core/assets-raw/sprites/units/assembly-drone.png b/core/assets-raw/sprites/units/assembly-drone.png new file mode 100644 index 0000000000..fa6c784b16 Binary files /dev/null and b/core/assets-raw/sprites/units/assembly-drone.png differ diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index 745f318e5b..078d883db9 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -487,3 +487,4 @@ 63219=tungsten-wall|block-tungsten-wall-ui 63218=tungsten-wall-large|block-tungsten-wall-large-ui 63217=tank-assembler|block-tank-assembler-ui +63216=assembly-drone|unit-assembly-drone-ui diff --git a/core/src/mindustry/ai/types/AssemblerAI.java b/core/src/mindustry/ai/types/AssemblerAI.java new file mode 100644 index 0000000000..9e3f9fb16b --- /dev/null +++ b/core/src/mindustry/ai/types/AssemblerAI.java @@ -0,0 +1,22 @@ +package mindustry.ai.types; + +import arc.math.geom.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.world.blocks.units.UnitAssembler.*; + +public class AssemblerAI extends AIController{ + public Vec2 targetPos = new Vec2(); + + @Override + public void updateMovement(){ + //TODO + if(!targetPos.isZero()){ + moveTo(targetPos, 8f, 11f); + } + + if(unit instanceof BuildingTetherc tether && tether.building() instanceof UnitAssemblerBuild assembler){ + unit.lookAt(assembler.getUnitSpawn()); + } + } +} diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 1fe1ae5388..bd5899f48e 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -1,7 +1,6 @@ package mindustry.ai.types; import arc.math.*; -import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.ai.*; @@ -113,33 +112,6 @@ public class LogicAI extends AIController{ return radars.add(radar); } - @Override - public 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) / smooth, -1f, 1f); - - vec.setLength(unit.speed() * length); - if(length < -0.5f){ - vec.rotate(180f); - }else if(length < 0){ - vec.setZero(); - } - - //do not move when infinite vectors are used. - if(vec.isNaN() || vec.isInfinite()) return; - - if(unit.type.omniMovement){ - unit.approach(vec); - }else{ - unit.rotateMove(vec); - } - - - } - @Override public boolean checkTarget(Teamc target, float x, float y, float range){ return false; diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 9170acb910..03dcddd5e5 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -3320,6 +3320,9 @@ public class Blocks{ output = UnitTypes.vanquish; droneType = UnitTypes.manifold; requirements = BlockStack.list(Blocks.thoriumWallLarge, 4, Blocks.duct, 2); + consumes.power(1f); + + droneType = UnitTypes.assemblyDrone; }}; //endregion diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 697e9830d8..4019983cd6 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -68,8 +68,8 @@ public class UnitTypes{ //special block unit type public static @EntityDef({Unitc.class, BlockUnitc.class}) UnitType block; - //transport - public static @EntityDef({Unitc.class, BuildingTetherc.class}) UnitType manifold; + //special tethered + public static @EntityDef({Unitc.class, BuildingTetherc.class}) UnitType manifold, assemblyDrone; //tank //TODO tank comp @@ -2714,8 +2714,6 @@ public class UnitTypes{ commandLimit = 0; engineSize = 2.3f; engineOffset = 6.5f; - - //should not appear anywhere, it's for internal use only and will despawn hidden = true; setEnginesMirror( @@ -2723,6 +2721,29 @@ public class UnitTypes{ ); }}; + assemblyDrone = new UnitType("assembly-drone"){{ + defaultController = AssemblerAI::new; + + flying = true; + drag = 0.06f; + accel = 0.11f; + speed = 1.3f; + health = 90; + engineSize = 2f; + engineOffset = 6.5f; + + outlineColor = Pal.darkOutline; + isCounted = false; + hidden = true; + useUnitCap = false; + logicControllable = false; + playerControllable = false; + allowedInPayloads = false; + createWreck = false; + envEnabled = Env.any; + envDisabled = Env.none; + }}; + //endregion //region neoplasm diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index d1736c658c..0cacd53026 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -5,7 +5,6 @@ import arc.func.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; -import arc.math.geom.*; import arc.struct.Queue; import arc.util.*; import mindustry.*; @@ -27,8 +26,6 @@ import static mindustry.Vars.*; @Component abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ - static final Vec2[] vecs = new Vec2[]{new Vec2(), new Vec2(), new Vec2(), new Vec2()}; - @Import float x, y, rotation, buildSpeedMultiplier; @Import UnitType type; @Import Team team; @@ -289,22 +286,6 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ float px = x + Angles.trnsx(rotation, focusLen); float py = y + Angles.trnsy(rotation, focusLen); - float sz = Vars.tilesize * size / 2f; - float ang = angleTo(tx, ty); - - vecs[0].set(tx - sz, ty - sz); - vecs[1].set(tx + sz, ty - sz); - vecs[2].set(tx - sz, ty + sz); - vecs[3].set(tx + sz, ty + sz); - - Arrays.sort(vecs, Structs.comparingFloat(vec -> -Angles.angleDist(angleTo(vec), ang))); - - Vec2 close = Geometry.findClosest(x, y, vecs); - - float x1 = vecs[0].x, y1 = vecs[0].y, - x2 = close.x, y2 = close.y, - x3 = vecs[1].x, y3 = vecs[1].y; - Draw.z(Layer.buildBeam); Draw.alpha(buildAlpha); @@ -313,17 +294,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ Fill.square(plan.drawx(), plan.drawy(), size * tilesize/2f); } - if(renderer.animateShields){ - if(close != vecs[0] && close != vecs[1]){ - Fill.tri(px, py, x1, y1, x2, y2); - Fill.tri(px, py, x3, y3, x2, y2); - }else{ - Fill.tri(px, py, x1, y1, x3, y3); - } - }else{ - Lines.line(px, py, x1, y1); - Lines.line(px, py, x3, y3); - } + Drawf.buildBeam(px, py, tx, ty, Vars.tilesize * size / 2f); Fill.square(px, py, 1.8f + Mathf.absin(Time.time, 2.2f, 1.1f), rotation + 45); diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index 88ab08e18e..64cff57fb1 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -244,7 +244,10 @@ public class AIController implements UnitController{ vec.setZero(); } - unit.moveAt(vec); + //do not move when infinite vectors are used. + if(vec.isNaN() || vec.isInfinite()) return; + + unit.movePref(vec); } @Override diff --git a/core/src/mindustry/graphics/Drawf.java b/core/src/mindustry/graphics/Drawf.java index 3d5f2f2b8e..3698e3f32d 100644 --- a/core/src/mindustry/graphics/Drawf.java +++ b/core/src/mindustry/graphics/Drawf.java @@ -13,10 +13,13 @@ import mindustry.game.*; import mindustry.gen.*; import mindustry.world.*; +import java.util.*; + import static mindustry.Vars.*; public class Drawf{ - private static FloatSeq points = new FloatSeq(); + private static final Vec2[] vecs = new Vec2[]{new Vec2(), new Vec2(), new Vec2(), new Vec2()}; + private static final FloatSeq points = new FloatSeq(); public static void flame(float x, float y, int divisions, float rotation, float length, float width, float pan){ flame(x, y, divisions, rotation, length, width, pan, 0f); @@ -62,6 +65,35 @@ public class Drawf{ points.add(Tmp.v1.x + baseX, Tmp.v1.y + baseY); } + public static void buildBeam(float x, float y, float tx, float ty, float radius){ + float ang = Angles.angle(x, y, tx, ty); + + vecs[0].set(tx - radius, ty - radius); + vecs[1].set(tx + radius, ty - radius); + vecs[2].set(tx - radius, ty + radius); + vecs[3].set(tx + radius, ty + radius); + + Arrays.sort(vecs, Structs.comparingFloat(vec -> -Angles.angleDist(Angles.angle(x, y, vec.x, vec.y), ang))); + + Vec2 close = Geometry.findClosest(x, y, vecs); + + float x1 = vecs[0].x, y1 = vecs[0].y, + x2 = close.x, y2 = close.y, + x3 = vecs[1].x, y3 = vecs[1].y; + + if(renderer.animateShields){ + if(close != vecs[0] && close != vecs[1]){ + Fill.tri(x, y, x1, y1, x2, y2); + Fill.tri(x, y, x3, y3, x2, y2); + }else{ + Fill.tri(x, y, x1, y1, x3, y3); + } + }else{ + Lines.line(x, y, x1, y1); + Lines.line(x, y, x3, y3); + } + } + public static void additive(TextureRegion region, Color color, float x, float y, float rotation, float layer){ float pz = Draw.z(); Draw.z(layer); @@ -82,11 +114,19 @@ public class Drawf{ Draw.reset(); } + public static void dashSquare(Color color, float x, float y, float size){ + dashRect(color, x - size/2f, y - size/2f, size, size); + } + public static void dashRect(Color color, Rect rect){ - dashLine(color, rect.x, rect.y, rect.x + rect.width, rect.y); - dashLine(color, rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height); - dashLine(color, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y + rect.height); - dashLine(color, rect.x, rect.y + rect.height, rect.x, rect.y); + dashRect(color, rect.x, rect.y, rect.width, rect.height); + } + + public static void dashRect(Color color, float x, float y, float width, float height){ + dashLine(color, x, y, x + width, y); + dashLine(color, x + width, y, x + width, y + height); + dashLine(color, x + width, y + height, x, y + height); + dashLine(color, x, y + height, x, y); } public static void target(float x, float y, float rad, Color color){ diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index bdf86ecb8e..666cd91795 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -1,13 +1,13 @@ package mindustry.world.blocks.units; import arc.*; -import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; import arc.util.io.*; +import mindustry.ai.types.*; import mindustry.content.*; import mindustry.entities.*; import mindustry.entities.units.*; @@ -17,6 +17,7 @@ import mindustry.type.*; import mindustry.ui.*; import mindustry.world.blocks.payloads.*; import mindustry.world.consumers.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -29,10 +30,11 @@ import static mindustry.Vars.*; * */ public class UnitAssembler extends PayloadBlock{ public int areaSize = 11; - public UnitType droneType; + public UnitType droneType = UnitTypes.assemblyDrone; public int dronesCreated = 4; //TODO should be different for every tier. + public float buildTime = 60f * 5f; public Seq requirements = new Seq<>(); public UnitType output; @@ -42,6 +44,7 @@ public class UnitAssembler extends PayloadBlock{ rotate = true; rotateDraw = false; acceptsPayload = true; + flags = EnumSet.of(BlockFlag.unitAssembler); } @Override @@ -66,7 +69,7 @@ public class UnitAssembler extends PayloadBlock{ super.setBars(); //TODO progress bar - //bars.add("progress", (UnitAssemblerBuild e) -> new Bar("bar.progress", Pal.ammo, e::fraction)); + bars.add("progress", (UnitAssemblerBuild e) -> new Bar("bar.progress", Pal.ammo, () -> e.progress)); bars.add("units", (UnitAssemblerBuild e) -> new Bar(() -> @@ -95,8 +98,12 @@ public class UnitAssembler extends PayloadBlock{ } public class UnitAssemblerBuild extends PayloadBlockBuild{ + protected IntSeq readUnits = new IntSeq(); + public Seq units = new Seq<>(); public BlockSeq blocks = new BlockSeq(); + public float progress, warmup; + public float invalidWarmup = 0f; public boolean wasOccupied = false; @@ -111,10 +118,20 @@ public class UnitAssembler extends PayloadBlock{ @Override public void updateTile(){ + if(!readUnits.isEmpty()){ + units.clear(); + readUnits.each(i -> { + var unit = Groups.unit.getByID(i); + if(unit != null){ + units.add(unit); + } + }); + readUnits.clear(); + } + units.removeAll(u -> !u.isAdded() || u.dead); - //units annoying, disabled for now - if(false && efficiency() > 0 && units.size < dronesCreated){ + if(efficiency() > 0 && units.size < dronesCreated){ //TODO build animation? distribute spawning? var unit = droneType.create(team); if(unit instanceof BuildingTetherc bt){ @@ -134,23 +151,40 @@ public class UnitAssembler extends PayloadBlock{ Vec2 spawn = getUnitSpawn(); + //arrange units around perimeter + for(int i = 0; i < units.size; i++){ + var unit = units.get(i); + if(unit.controller() instanceof AssemblerAI ai){ + ai.targetPos.trns(i * 90f + 45f, areaSize / 2f * Mathf.sqrt2 * tilesize).add(spawn); + } + } + wasOccupied = checkSolid(spawn); + float eff = (units.size / (float)dronesCreated); + + invalidWarmup = Mathf.lerpDelta(invalidWarmup, wasOccupied ? 1f : 0f, 0.1f); //check if all requirements are met - if(!wasOccupied && consValid() & Units.canCreate(team, output)){ + if(!wasOccupied && consValid() && Units.canCreate(team, output)){ + warmup = Mathf.lerpDelta(warmup, efficiency(), 0.1f); - //TODO ???? should this even be part of a trigger - consume(); + if((progress += edelta() * eff / buildTime) >= 1f){ + //TODO ???? should this even be part of a trigger + consume(); - //TODO actually just goes poof - var unit = output.create(team); - unit.set(spawn.x + Mathf.range(0.001f), spawn.y + Mathf.range(0.001f)); - unit.rotation = 90f; - unit.add(); + //TODO actually just goes poof + var unit = output.create(team); + unit.set(spawn.x + Mathf.range(0.001f), spawn.y + Mathf.range(0.001f)); + unit.rotation = 90f; + unit.add(); + progress = 0f; - Fx.spawn.at(unit); + Fx.spawn.at(unit); - blocks.clear(); + blocks.clear(); + } + }else{ + warmup = Mathf.lerpDelta(warmup, 0f, 0.1f); } //TODO drones need to indicate that they are in position and actually play an animation @@ -161,6 +195,12 @@ public class UnitAssembler extends PayloadBlock{ Draw.rect(region, x, y); //Draw.rect(outRegion, x, y, rotdeg()); + for(int i = 0; i < 4; i++){ + if(blends(i) && i != rotation){ + Draw.rect(inRegion, x, y, (i * 90) - 180); + } + } + Draw.z(Layer.blockOver); //payRotation = rotdeg(); @@ -172,16 +212,51 @@ public class UnitAssembler extends PayloadBlock{ Vec2 spawn = getUnitSpawn(); - //TODO which layer? - Draw.z(Layer.power - 1f); + Draw.alpha(progress); - Draw.mixcol(Pal.accent, 1f); - Draw.alpha(0.4f + Mathf.absin(10f, 0.2f)); Draw.rect(output.fullIcon, spawn.x, spawn.y); + //TODO which layer? + Draw.z(Layer.buildBeam); + + Draw.mixcol(Tmp.c1.set(Pal.accent).lerp(Pal.remove, invalidWarmup), 1f); + Draw.alpha(0.8f + Mathf.absin(10f, 0.2f)); + Draw.rect(output.fullIcon, spawn.x, spawn.y); + + Draw.alpha(warmup * Draw.getColor().a); + int c = 0; + for(var unit : units){ + if(!Angles.within(unit.rotation, c * 90f + 45f + 180f, 15f)) continue; + + float + px = unit.x + Angles.trnsx(unit.rotation, unit.type.buildBeamOffset), + py = unit.y + Angles.trnsy(unit.rotation, unit.type.buildBeamOffset); + + Drawf.buildBeam(px, py, spawn.x, spawn.y, output.hitSize/2f); + c ++; + } + + Draw.z(Layer.overlayUI - 1); + + var color = Tmp.c3.set(Pal.accent).lerp(Pal.remove, invalidWarmup).a(efficiency()); + + //TODO dashes bad + + float outSize = output.hitSize + 9f; + float hs = size * tilesize/2f, ha = outSize/2f; + + Draw.reset(); //TODO dash rect for output, fades in/out - Draw.color(!wasOccupied ? Color.green : Color.red); - Lines.square(spawn.x, spawn.y, output.hitSize/2f); + Drawf.dashSquare(color, spawn.x, spawn.y, outSize); + + for(int i : Mathf.signs){ + Tmp.v1.trns(rotation * 90, hs, hs * i).add(x, y); + + Tmp.v2.trns(rotation * 90, -ha, ha * i).add(spawn); + Draw.color(); + + Drawf.dashLine(color, Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y); + } Draw.reset(); } @@ -204,7 +279,6 @@ public class UnitAssembler extends PayloadBlock{ //payloads.add((BuildPayload)payload); } - //TODO doesn't accept from payload source @Override public boolean acceptPayload(Building source, Payload payload){ return payload instanceof BuildPayload bp && requirements.contains(b -> b.block == bp.block() && blocks.get(bp.block()) < b.amount); @@ -214,7 +288,13 @@ public class UnitAssembler extends PayloadBlock{ public void write(Writes write){ super.write(write); - //blocks.write(write); + write.f(progress); + write.b(units.size); + for(var unit : units){ + write.i(unit.id); + } + + blocks.write(write); //TODO save: //- unit IDs @@ -225,8 +305,14 @@ public class UnitAssembler extends PayloadBlock{ @Override public void read(Reads read, byte revision){ super.read(read, revision); + progress = read.f(); + int count = read.b(); + readUnits.clear(); + for(int i = 0; i < count; i++){ + readUnits.add(read.i()); + } - //blocks.read(read); + blocks.read(read); } } } diff --git a/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java b/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java new file mode 100644 index 0000000000..cf90914314 --- /dev/null +++ b/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java @@ -0,0 +1,22 @@ +package mindustry.world.blocks.units; + +import mindustry.world.blocks.payloads.*; +import mindustry.world.blocks.units.UnitAssembler.*; + +public class UnitAssemblerModule extends PayloadBlock{ + + public UnitAssemblerModule(String name){ + super(name); + } + + //TODO how does it link? + public class UnitAssemblerModuleBuild extends PayloadBlockBuild{ + public UnitAssemblerBuild link; + + @Override + public void updateTile(){ + + } + + } +} diff --git a/core/src/mindustry/world/meta/BlockFlag.java b/core/src/mindustry/world/meta/BlockFlag.java index 3e69cff29c..a19e57c824 100644 --- a/core/src/mindustry/world/meta/BlockFlag.java +++ b/core/src/mindustry/world/meta/BlockFlag.java @@ -22,10 +22,11 @@ public enum BlockFlag{ reactor, /** Blocks that extinguishes fires. */ extinguisher, - /** Just a launch pad. */ + + //single-block identifiers launchPad, - /** Destination for unit cargo. */ - unitCargoUnloadPoint; + unitCargoUnloadPoint, + unitAssembler; public final static BlockFlag[] all = values(); diff --git a/tools/src/mindustry/tools/Generators.java b/tools/src/mindustry/tools/Generators.java index 3941217b88..1523fc0173 100644 --- a/tools/src/mindustry/tools/Generators.java +++ b/tools/src/mindustry/tools/Generators.java @@ -561,7 +561,7 @@ public class Generators{ //draw treads if(sample instanceof Tankc){ - image.draw(get(type.treadRegion), true); + image.draw(outline.get(get(type.treadRegion)), true); image.draw(get(type.region), true); }