diff --git a/annotations/src/main/resources/revisions/manifold/0.json b/annotations/src/main/resources/revisions/manifold/0.json index 13a7219ebf..b478f74a95 100644 --- a/annotations/src/main/resources/revisions/manifold/0.json +++ b/annotations/src/main/resources/revisions/manifold/0.json @@ -1 +1 @@ -{fields:[{name:ammo,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file +{fields:[{name:ammo,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:payloads,type:arc.struct.Seq},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/core/assets-raw/sprites/blocks/payload/factory-in-5.png b/core/assets-raw/sprites/blocks/payload/factory-in-5.png index 785faba9cf..df9e5a45b3 100644 Binary files a/core/assets-raw/sprites/blocks/payload/factory-in-5.png and b/core/assets-raw/sprites/blocks/payload/factory-in-5.png differ diff --git a/core/assets-raw/sprites/blocks/units/tank-assembler.png b/core/assets-raw/sprites/blocks/units/tank-assembler.png index 5bf4b32783..82423bf477 100644 Binary files a/core/assets-raw/sprites/blocks/units/tank-assembler.png and b/core/assets-raw/sprites/blocks/units/tank-assembler.png differ diff --git a/core/assets-raw/sprites/units/vanquish.png b/core/assets-raw/sprites/units/vanquish.png index 3f6317a3d4..c867e2f81d 100644 Binary files a/core/assets-raw/sprites/units/vanquish.png and b/core/assets-raw/sprites/units/vanquish.png differ diff --git a/core/src/mindustry/ai/types/AssemblerAI.java b/core/src/mindustry/ai/types/AssemblerAI.java index 9e3f9fb16b..3a0a6f358e 100644 --- a/core/src/mindustry/ai/types/AssemblerAI.java +++ b/core/src/mindustry/ai/types/AssemblerAI.java @@ -1,22 +1,26 @@ package mindustry.ai.types; +import arc.math.*; 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(); + public float targetAngle; @Override public void updateMovement(){ //TODO if(!targetPos.isZero()){ - moveTo(targetPos, 8f, 11f); + moveTo(targetPos, 1f, 3f); } - if(unit instanceof BuildingTetherc tether && tether.building() instanceof UnitAssemblerBuild assembler){ - unit.lookAt(assembler.getUnitSpawn()); + if(unit.within(targetPos, 5f)){ + unit.lookAt(targetAngle); } } + + public boolean inPosition(){ + return unit.within(targetPos, 10f) && Angles.within(unit.rotation, targetAngle, 15f); + } } diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 1a6b1b627b..179359cf38 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -3319,7 +3319,7 @@ public class Blocks{ //TODO completely unfinished tankAssembler = new UnitAssembler("tank-assembler"){{ requirements(Category.units, with(Items.graphite, 10)); - size = 3; + size = 5; droneType = UnitTypes.manifold; plans.add(new AssemblerUnitPlan(UnitTypes.vanquish, 60f * 5f, BlockStack.list(Blocks.thoriumWallLarge, 4, Blocks.duct, 2))); consumes.power(1f); diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index ae84c353ee..c044c43915 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -13,6 +13,7 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.units.UnitAssembler.*; import static arc.graphics.g2d.Draw.rect; import static arc.graphics.g2d.Draw.*; @@ -226,6 +227,17 @@ public class Fx{ }); }), + payloadDeposit = new Effect(30f, e -> { + if(!(e.data instanceof YeetData data)) return; + Tmp.v1.set(e.x, e.y).lerp(data.target, e.finpow()); + float x = Tmp.v1.x, y = Tmp.v1.y; + + scl(e.fout(Interp.pow3Out) * 1.05f); + Drawf.squareShadow(x, y, data.block.size * tilesize * 1.85f, 1f); + mixcol(Pal.accent, e.fin()); + rect(data.block.fullIcon, x, y); + }).layer(Layer.flyingUnitLow - 5f), + select = new Effect(23, e -> { color(Pal.accent); stroke(e.fout() * 3f); diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 81509d59d0..dd2cbbbe43 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; - //special tethered - public static @EntityDef({Unitc.class, BuildingTetherc.class}) UnitType manifold, assemblyDrone; + //special tethered (has payload capability, because it's necessary sometimes) + public static @EntityDef({Unitc.class, BuildingTetherc.class, Payloadc.class}) UnitType manifold, assemblyDrone, payloadDrone; //tank //TODO tank comp @@ -2431,7 +2431,7 @@ public class UnitTypes{ hitSize = 28f; treadPullOffset = 4; speed = 0.6f; - health = 10000; + health = 9000; armor = 20f; treadRect = new Rect(22f, 16f, 28f, 130f); @@ -2448,7 +2448,7 @@ public class UnitTypes{ y = 0; shadow = 28f; - bullet = new BasicBulletType(7f, 50){{ + bullet = new BasicBulletType(7f, 90){{ sprite = "missile-large"; width = 9f; height = 15f; @@ -2744,6 +2744,10 @@ public class UnitTypes{ envDisabled = Env.none; }}; + //payloadDrone = new UnitType("payload-drone"){{ + + //}}; + //endregion //region neoplasm diff --git a/core/src/mindustry/graphics/Drawf.java b/core/src/mindustry/graphics/Drawf.java index ca2af39629..25dc008953 100644 --- a/core/src/mindustry/graphics/Drawf.java +++ b/core/src/mindustry/graphics/Drawf.java @@ -224,6 +224,12 @@ public class Drawf{ shadow(x, y, rad, 1f); } + public static void squareShadow(float x, float y, float rad, float alpha){ + Draw.color(0, 0, 0, 0.4f * alpha); + Draw.rect("square-shadow", x, y, rad * Draw.xscl, rad * Draw.yscl); + Draw.color(); + } + public static void shadow(float x, float y, float rad, float alpha){ Draw.color(0, 0, 0, 0.4f * alpha); Draw.rect("circle-shadow", x, y, rad * Draw.xscl, rad * Draw.yscl); diff --git a/core/src/mindustry/maps/planet/ErekirPlanetGenerator.java b/core/src/mindustry/maps/planet/ErekirPlanetGenerator.java index 9077e86cac..ec30788a27 100644 --- a/core/src/mindustry/maps/planet/ErekirPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/ErekirPlanetGenerator.java @@ -252,12 +252,12 @@ public class ErekirPlanetGenerator extends PlanetGenerator{ state.rules.defaultTeam.items().add(Seq.with(ItemStack.with(Items.beryllium, 300, Items.graphite, 300))); //TODO proper waves - state.rules.waves = false; + state.rules.waves = true; state.rules.showSpawns = true; state.rules.waveTimer = true; - state.rules.waveSpacing = 60f * 60f * 10f; + state.rules.waveSpacing = 60f * 60f * 15f; state.rules.spawns = Seq.with(new SpawnGroup(){{ - type = UnitTypes.fortress; + type = UnitTypes.vanquish; spacing = 1; shieldScaling = 60; unitScaling = 2f; diff --git a/core/src/mindustry/world/blocks/payloads/BuildPayload.java b/core/src/mindustry/world/blocks/payloads/BuildPayload.java index cf6370ccf7..15476fe888 100644 --- a/core/src/mindustry/world/blocks/payloads/BuildPayload.java +++ b/core/src/mindustry/world/blocks/payloads/BuildPayload.java @@ -85,13 +85,15 @@ public class BuildPayload implements Payload{ @Override public void drawShadow(float alpha){ - Drawf.shadow(build.x, build.y, build.block.size * tilesize * 2f, alpha); + Drawf.squareShadow(build.x, build.y, build.block.size * tilesize * 1.85f, alpha); } @Override public void draw(){ - drawShadow(1f); float prevZ = Draw.z(); + Draw.z(prevZ - 0.0001f); + drawShadow(1f); + Draw.z(prevZ); Draw.zTransform(z -> z >= Layer.flyingUnitLow ? z : 0.0011f + Mathf.clamp(z, prevZ - 0.001f, prevZ + 0.9f)); build.tile = emptyTile; build.payloadDraw(); diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index bb7b7280fa..9739c8becf 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -92,12 +92,12 @@ public class UnitAssembler extends PayloadBlock{ @Override public void init(){ + clipSize = Math.max(clipSize, (areaSize + size) * tilesize * 2); consumes.add(new ConsumePayloadDynamic((UnitAssemblerBuild build) -> build.plan().requirements)); super.init(); } - @Override public void setStats(){ super.setStats(); @@ -137,6 +137,16 @@ public class UnitAssembler extends PayloadBlock{ AssemblerUnitPlan(){} } + public static class YeetData{ + public Vec2 target; + public Block block; + + public YeetData(Vec2 target, Block block){ + this.target = target; + this.block = block; + } + } + public class UnitAssemblerBuild extends PayloadBlockBuild{ protected IntSeq readUnits = new IntSeq(); @@ -146,7 +156,6 @@ public class UnitAssembler extends PayloadBlock{ public float progress, warmup; public float invalidWarmup = 0f; public int currentTier = 0; - public boolean wasOccupied = false; public Vec2 getUnitSpawn(){ @@ -173,7 +182,6 @@ public class UnitAssembler extends PayloadBlock{ public void updateModules(UnitAssemblerModuleBuild build){ modules.addUnique(build); checkTier(); - //TODO tier check } public void removeModule(UnitAssemblerModuleBuild build){ @@ -211,7 +219,7 @@ public class UnitAssembler extends PayloadBlock{ Drawf.selected(module, Pal.accent); } - //TODO draw area + //TODO draw area when no power } //is this necessary? wastes a lot of space @@ -265,10 +273,10 @@ public class UnitAssembler extends PayloadBlock{ readUnits.clear(); } - units.removeAll(u -> !u.isAdded() || u.dead); + units.removeAll(u -> !u.isAdded() || u.dead || !(u.controller() instanceof AssemblerAI)); + //TODO build up units, don't spawn immediately in batches if(efficiency() > 0 && units.size < dronesCreated){ - //TODO build animation? distribute spawning? var unit = droneType.create(team); if(unit instanceof BuildingTetherc bt){ bt.building(this); @@ -281,20 +289,26 @@ public class UnitAssembler extends PayloadBlock{ units.add(unit); } - //TODO units should move stuff into position + //TODO units should pick up and move payloads into position Vec2 spawn = getUnitSpawn(); + if(moveInPayload() && !wasOccupied){ + yeetPayload(payload); + payload = null; + } + //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); - } + var ai = (AssemblerAI)unit.controller(); + + ai.targetPos.trns(i * 90f + 45f, areaSize / 2f * Mathf.sqrt2 * tilesize).add(spawn); + ai.targetAngle = i * 90f + 45f + 180f; } wasOccupied = checkSolid(spawn); - float eff = (units.size / (float)dronesCreated); + float eff = (units.count(u -> ((AssemblerAI)u.controller()).inPosition()) / (float)dronesCreated); invalidWarmup = Mathf.lerpDelta(invalidWarmup, wasOccupied ? 1f : 0f, 0.1f); @@ -312,7 +326,8 @@ public class UnitAssembler extends PayloadBlock{ var unit = plan.unit.create(team); unit.set(spawn.x + Mathf.range(0.001f), spawn.y + Mathf.range(0.001f)); unit.rotation = 90f; - unit.add(); + //TODO annoying so nothing is created yet + //unit.add(); progress = 0f; Fx.spawn.at(unit); @@ -329,7 +344,6 @@ public class UnitAssembler extends PayloadBlock{ @Override public void draw(){ Draw.rect(region, x, y); - //Draw.rect(outRegion, x, y, rotdeg()); //draw input conveyors for(int i = 0; i < 4; i++){ @@ -340,12 +354,13 @@ public class UnitAssembler extends PayloadBlock{ Draw.z(Layer.blockOver); - //payRotation = rotdeg(); - //drawPayload(); + payRotation = rotdeg(); + drawPayload(); Draw.z(Layer.blockOver + 0.1f); - Draw.rect(topRegion, x, y); + //TODO top? + //Draw.rect(topRegion, x, y); Vec2 spawn = getUnitSpawn(); @@ -355,7 +370,6 @@ public class UnitAssembler extends PayloadBlock{ Draw.rect(plan.unit.fullIcon, spawn.x, spawn.y); - //TODO which layer? Draw.z(Layer.buildBeam); //draw unit outline @@ -364,16 +378,16 @@ public class UnitAssembler extends PayloadBlock{ Draw.rect(plan.unit.fullIcon, spawn.x, spawn.y); Draw.alpha(warmup * Draw.getColor().a); - int c = 0; + + //draw build beams for(var unit : units){ - if(!Angles.within(unit.rotation, c * 90f + 45f + 180f, 15f)) continue; + if(!((AssemblerAI)unit.controller()).inPosition()) 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, plan.unit.hitSize/2f); - c ++; } Draw.reset(); @@ -389,37 +403,30 @@ public class UnitAssembler extends PayloadBlock{ Draw.reset(); float outSize = plan.unit.hitSize + 9f; - float hs = size * tilesize/2f, ha = outSize/2f; - - Lines.stroke(2f, Tmp.c3.set(Pal.accent).lerp(Pal.remove, invalidWarmup).a(efficiency())); //draw small square for area - //TODO dash rect for output, fades in/out + Lines.stroke(2f, Tmp.c3.set(Pal.accent).lerp(Pal.remove, invalidWarmup).a(efficiency())); Drawf.dashSquareBasic(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(); - - Draw.z(Layer.overlayUI - 1); - - //TODO dashes bad - - Draw.reset(); } public boolean checkSolid(Vec2 v){ var output = unit(); - return !output.flying && (collisions.overlapsTile(Tmp.r1.setCentered(v.x, v.y, output.hitSize), EntityCollisions::solid) || Units.anyEntities(v.x, v.y, output.hitSize)); + return !output.flying && (collisions.overlapsTile(Tmp.r1.setCentered(v.x, v.y, output.hitSize), EntityCollisions::solid) || Units.anyEntities(v.x, v.y, output.hitSize * 1.4f)); + } + + /** @return true if this block is ready to produce units, e.g. requirements met */ + public boolean ready(){ + return consValid() && !wasOccupied; + } + + public void yeetPayload(BuildPayload payload){ + var spawn = getUnitSpawn(); + blocks.add(payload.block(), 1); + float rot = payload.angleTo(spawn); + Fx.shootPayloadDriver.at(payload.x(), payload.y(), rot); + Fx.payloadDeposit.at(payload.x(), payload.y(), rot, new YeetData(spawn.cpy(), payload.block())); } @Override @@ -427,19 +434,10 @@ public class UnitAssembler extends PayloadBlock{ return blocks; } - @Override - public void handlePayload(Building source, Payload payload){ - //super.handlePayload(source, payload); - - blocks.add(((BuildPayload)payload).block()); - - //payloads.add((BuildPayload)payload); - } - @Override public boolean acceptPayload(Building source, Payload payload){ var plan = plan(); - return payload instanceof BuildPayload bp && plan.requirements.contains(b -> b.block == bp.block() && blocks.get(bp.block()) < b.amount); + return this.payload == null && payload instanceof BuildPayload bp && plan.requirements.contains(b -> b.block == bp.block() && blocks.get(bp.block()) < b.amount); } @Override @@ -453,11 +451,6 @@ public class UnitAssembler extends PayloadBlock{ } blocks.write(write); - - //TODO save: - //- unit IDs - //- progress - //- payloads in position (should they have positions?) } @Override diff --git a/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java b/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java index c1216c068e..201e4514a6 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java +++ b/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java @@ -81,7 +81,7 @@ public class UnitAssemblerModule extends PayloadBlock{ @Override public boolean acceptPayload(Building source, Payload payload){ - return link != null && payload == null && link.acceptPayload(source, payload); + return link != null && this.payload == null && link.acceptPayload(source, payload); } @Override @@ -108,10 +108,9 @@ public class UnitAssemblerModule extends PayloadBlock{ findLink(); } - if(moveInPayload()){ - if(link != null && link.moduleFits(block, x, y, rotation) && link.acceptPayload(this, payload)){ - link.handlePayload(this, payload); - } + if(moveInPayload() && link != null && link.moduleFits(block, x, y, rotation) && link.acceptPayload(this, payload)){ + link.yeetPayload(payload); + payload = null; } } diff --git a/core/src/mindustry/world/consumers/ConsumePayloadFilter.java b/core/src/mindustry/world/consumers/ConsumePayloadFilter.java index 5138532d97..d40099b345 100644 --- a/core/src/mindustry/world/consumers/ConsumePayloadFilter.java +++ b/core/src/mindustry/world/consumers/ConsumePayloadFilter.java @@ -53,8 +53,11 @@ public class ConsumePayloadFilter extends Consume{ var inv = build.getBlockPayloads(); MultiReqImage image = new MultiReqImage(); - content.blocks().each(i -> filter.get(i) && i.unlockedNow(), block -> image.add(new ReqImage(new ItemImage(block.uiIcon, 1), - () -> inv.contains(block, 1)))); + + content.blocks().each(i -> filter.get(i) && i.unlockedNow(), + block -> image.add(new ReqImage(new ItemImage(block.uiIcon, 1), + () -> inv.contains(block, 1))) + ); table.add(image).size(8 * 4); }