diff --git a/core/assets-raw/sprites/blocks/drills/laser-drill-rim.png b/core/assets-raw/sprites/blocks/drills/laser-drill-rim.png deleted file mode 100644 index 80a830e1c7..0000000000 Binary files a/core/assets-raw/sprites/blocks/drills/laser-drill-rim.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/units/basic-assembler-module.png b/core/assets-raw/sprites/blocks/units/basic-assembler-module.png new file mode 100644 index 0000000000..40ea654c1d Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/basic-assembler-module.png differ diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index 078d883db9..f79639951b 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -488,3 +488,4 @@ 63218=tungsten-wall-large|block-tungsten-wall-large-ui 63217=tank-assembler|block-tank-assembler-ui 63216=assembly-drone|unit-assembly-drone-ui +63215=basic-assembler-module|block-basic-assembler-module-ui diff --git a/core/assets/logicids.dat b/core/assets/logicids.dat index e5d28945ab..98985445b9 100644 Binary files a/core/assets/logicids.dat and b/core/assets/logicids.dat differ diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 48458937a4..659c88f51b 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -382,7 +382,6 @@ public class Vars implements Loadable{ try{ Writer writer = settings.getDataDirectory().child("last_log.txt").writer(false); LogHandler log = Log.logger; - //ignore it Log.logger = (level, text) -> { log.log(level, text); diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 7546eaa6cc..1a6b1b627b 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -132,6 +132,7 @@ public class Blocks{ //unit - erekir tankAssembler, + basicAssemblerModule, //payloads //TODO small deconstructor @@ -3319,15 +3320,20 @@ public class Blocks{ tankAssembler = new UnitAssembler("tank-assembler"){{ requirements(Category.units, with(Items.graphite, 10)); size = 3; - output = UnitTypes.vanquish; droneType = UnitTypes.manifold; - requirements = BlockStack.list(Blocks.thoriumWallLarge, 4, Blocks.duct, 2); + plans.add(new AssemblerUnitPlan(UnitTypes.vanquish, 60f * 5f, BlockStack.list(Blocks.thoriumWallLarge, 4, Blocks.duct, 2))); consumes.power(1f); areaSize = 13; droneType = UnitTypes.assemblyDrone; }}; + basicAssemblerModule = new UnitAssemblerModule("basic-assembler-module"){{ + requirements(Category.units, with(Items.graphite, 10)); + + size = 3; + }}; + //endregion //region payloads diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 4019983cd6..81509d59d0 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -2439,7 +2439,7 @@ public class UnitTypes{ layerOffset = 0.0001f; reload = 120f; shootY = 71f / 4f; - shake = 3f; + shake = 4f; recoil = 4f; rotate = true; rotateSpeed = 1f; diff --git a/core/src/mindustry/graphics/Drawf.java b/core/src/mindustry/graphics/Drawf.java index 9e3af3ce37..ca2af39629 100644 --- a/core/src/mindustry/graphics/Drawf.java +++ b/core/src/mindustry/graphics/Drawf.java @@ -116,6 +116,10 @@ public class Drawf{ Draw.reset(); } + public static void dashLineBasic(float x, float y, float x2, float y2){ + Lines.dashLine(x, y, x2, y2, (int)(Math.max(Math.abs(x - x2), Math.abs(y - y2)) / tilesize * 2)); + } + public static void dashSquare(Color color, float x, float y, float size){ dashRect(color, x - size/2f, y - size/2f, size, size); } @@ -131,6 +135,17 @@ public class Drawf{ dashLine(color, x, y + height, x, y); } + public static void dashSquareBasic(float x, float y, float size){ + dashRectBasic(x - size/2f, y - size/2f, size, size); + } + + public static void dashRectBasic(float x, float y, float width, float height){ + dashLineBasic(x, y, x + width, y); + dashLineBasic(x + width, y, x + width, y + height); + dashLineBasic(x + width, y + height, x, y + height); + dashLineBasic( x, y + height, x, y); + } + public static void target(float x, float y, float rad, Color color){ target(x, y, rad, 1, color); } diff --git a/core/src/mindustry/ui/IconSize.java b/core/src/mindustry/ui/IconSize.java index dd3a4b37db..a94943ca4a 100644 --- a/core/src/mindustry/ui/IconSize.java +++ b/core/src/mindustry/ui/IconSize.java @@ -1,5 +1,7 @@ package mindustry.ui; +/** @deprecated completely pointless */ +@Deprecated public enum IconSize{ def(48), small(32), diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 267a0290cd..44f883c6ac 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -90,6 +90,17 @@ public class Tile implements Position, QuadTreeObject, Displayable{ return -1; } + public static int relativeTo(float x, float y, float cx, float cy){ + if(Math.abs(x - cx) > Math.abs(y - cy)){ + if(x <= cx - 1) return 0; + if(x >= cx + 1) return 2; + }else{ + if(y <= cy - 1) return 1; + if(y >= cy + 1) return 3; + } + return -1; + } + public byte absoluteRelativeTo(int cx, int cy){ //very straightforward for odd sizes diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index 40954fe446..bb7b7280fa 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -1,9 +1,11 @@ package mindustry.world.blocks.units; import arc.*; +import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; +import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import arc.util.io.*; @@ -15,7 +17,9 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; +import mindustry.world.*; import mindustry.world.blocks.payloads.*; +import mindustry.world.blocks.units.UnitAssemblerModule.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -33,10 +37,7 @@ public class UnitAssembler extends PayloadBlock{ 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; + public Seq plans = new Seq<>(4); public UnitAssembler(String name){ super(name); @@ -73,12 +74,12 @@ public class UnitAssembler extends PayloadBlock{ bars.add("units", (UnitAssemblerBuild e) -> new Bar(() -> Core.bundle.format("bar.unitcap", - Fonts.getUnicodeStr(output.name), - e.team.data().countType(output), + Fonts.getUnicodeStr(e.unit().name), + e.team.data().countType(e.unit()), Units.getStringCap(e.team) ), () -> Pal.power, - () -> (float)e.team.data().countType(output) / Units.getCap(e.team) + () -> (float)e.team.data().countType(e.unit()) / Units.getCap(e.team) )); } @@ -91,30 +92,166 @@ public class UnitAssembler extends PayloadBlock{ @Override public void init(){ - consumes.add(new ConsumePayloads(requirements)); + consumes.add(new ConsumePayloadDynamic((UnitAssemblerBuild build) -> build.plan().requirements)); super.init(); } + + @Override + public void setStats(){ + super.setStats(); + + stats.add(Stat.output, table -> { + Seq p = plans.select(u -> u.unit.unlockedNow()); + table.row(); + for(var plan : p){ + if(plan.unit.unlockedNow()){ + table.image(plan.unit.uiIcon).size(8 * 3).padRight(2).right(); + table.add(plan.unit.localizedName).left(); + table.add(Strings.autoFixed(plan.time / 60f, 1) + " " + Core.bundle.get("unit.seconds")).color(Color.lightGray).padLeft(12).left(); + table.row(); + table.add().right(); + table.table(t -> { + t.left(); + for(var stack : plan.requirements){ + t.add(new ItemImage(stack)).padRight(4).padTop(4).left(); + } + }).left().fillX().padLeft(-8 * 3f - 2).padBottom(6).row(); + } + } + }); + } + + public static class AssemblerUnitPlan{ + public UnitType unit; + public Seq requirements; + public float time; + + public AssemblerUnitPlan(UnitType unit, float time, Seq requirements){ + this.unit = unit; + this.time = time; + this.requirements = requirements; + } + + AssemblerUnitPlan(){} + } + public class UnitAssemblerBuild extends PayloadBlockBuild{ protected IntSeq readUnits = new IntSeq(); public Seq units = new Seq<>(); + public Seq modules = new Seq<>(); public BlockSeq blocks = new BlockSeq(); public float progress, warmup; public float invalidWarmup = 0f; + public int currentTier = 0; public boolean wasOccupied = false; - //TODO progress - //TODO how should payloads be stored exactly? counts of blocks? intmap? references? - public Vec2 getUnitSpawn(){ float len = tilesize * (areaSize + size)/2f; float unitX = x + Geometry.d4x(rotation) * len, unitY = y + Geometry.d4y(rotation) * len; return Tmp.v4.set(unitX, unitY); } + public boolean moduleFits(Block other, float ox, float oy, int rotation){ + float + dx = ox + Geometry.d4x(rotation) * (other.size/2 + 1) * tilesize, + dy = oy + Geometry.d4y(rotation) * (other.size/2 + 1) * tilesize; + + Vec2 spawn = getUnitSpawn(); + + if(Tile.relativeTo(ox, oy, spawn.x, spawn.y) != rotation){ + return false; + } + + float dst = Math.max(Math.abs(dx - spawn.x), Math.abs(dy - spawn.y)); + return Mathf.equal(dst, tilesize * areaSize / 2f - tilesize/2f); + } + + public void updateModules(UnitAssemblerModuleBuild build){ + modules.addUnique(build); + checkTier(); + //TODO tier check + } + + public void removeModule(UnitAssemblerModuleBuild build){ + modules.remove(build); + checkTier(); + } + + public void checkTier(){ + modules.sort(b -> b.tier()); + int max = 0; + for(int i = 0; i < modules.size; i++){ + var mod = modules.get(i); + if(mod.tier() == max || mod.tier() == max + 1){ + max = mod.tier(); + }else{ + //tier gap, TODO warning? + break; + } + } + currentTier = max; + } + + public UnitType unit(){ + return plan().unit; + } + + public AssemblerUnitPlan plan(){ + //clamp plan pos + return plans.get(Math.min(currentTier, plans.size - 1)); + } + + @Override + public void drawSelect(){ + for(var module : modules){ + Drawf.selected(module, Pal.accent); + } + + //TODO draw area + } + + //is this necessary? wastes a lot of space + /* + @Override + public void displayConsumption(Table table){ + super.displayConsumption(table); + table.row(); + + table.table(t -> { + t.left(); + for(var mod : modules){ + //TODO crosses for missing reqs? + t.image(mod.block.uiIcon).size(iconMed).padRight(4).padTop(4); + } + }).fillX().row(); + }*/ + + @Override + public void display(Table table){ + super.display(table); + + table.row(); + table.table(t -> { + t.left().defaults().left(); + + Block prev = null; + for(int i = 0; i < modules.size; i++){ + var mod = modules.get(i); + if(prev == mod.block) continue; + //TODO crosses for missing reqs? + t.image(mod.block.uiIcon).size(iconMed).padRight(4); + + prev = mod.block; + } + + t.label(() -> "[accent] -> []" + unit().emoji() + " " + unit().name); + }).pad(4).padLeft(0f).fillX().left(); + } + @Override public void updateTile(){ if(!readUnits.isEmpty()){ @@ -146,8 +283,6 @@ public class UnitAssembler extends PayloadBlock{ //TODO units should move stuff into position - //TODO check if area is occupied - Vec2 spawn = getUnitSpawn(); //arrange units around perimeter @@ -163,16 +298,18 @@ public class UnitAssembler extends PayloadBlock{ invalidWarmup = Mathf.lerpDelta(invalidWarmup, wasOccupied ? 1f : 0f, 0.1f); + var plan = plan(); + //check if all requirements are met - if(!wasOccupied && consValid() && Units.canCreate(team, output)){ + if(!wasOccupied && consValid() && Units.canCreate(team, plan.unit)){ warmup = Mathf.lerpDelta(warmup, efficiency(), 0.1f); - if((progress += edelta() * eff / buildTime) >= 1f){ + if((progress += edelta() * eff / plan.time) >= 1f){ //TODO ???? should this even be part of a trigger consume(); //TODO actually just goes poof - var unit = output.create(team); + 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(); @@ -194,6 +331,7 @@ public class UnitAssembler extends PayloadBlock{ Draw.rect(region, x, y); //Draw.rect(outRegion, x, y, rotdeg()); + //draw input conveyors for(int i = 0; i < 4; i++){ if(blends(i) && i != rotation){ Draw.rect(inRegion, x, y, (i * 90) - 180); @@ -213,14 +351,17 @@ public class UnitAssembler extends PayloadBlock{ Draw.alpha(progress); - Draw.rect(output.fullIcon, spawn.x, spawn.y); + var plan = plan(); + + Draw.rect(plan.unit.fullIcon, spawn.x, spawn.y); //TODO which layer? Draw.z(Layer.buildBeam); + //draw unit outline 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.rect(plan.unit.fullIcon, spawn.x, spawn.y); Draw.alpha(warmup * Draw.getColor().a); int c = 0; @@ -231,23 +372,32 @@ public class UnitAssembler extends PayloadBlock{ 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); + Drawf.buildBeam(px, py, spawn.x, spawn.y, plan.unit.hitSize/2f); c ++; } - Draw.z(Layer.overlayUI - 1); + Draw.reset(); - var color = Tmp.c3.set(Pal.accent).lerp(Pal.remove, invalidWarmup).a(efficiency()); + Draw.z(Layer.buildBeam); - //TODO dashes bad + float fulls = areaSize * tilesize/2f; - float outSize = output.hitSize + 9f; - float hs = size * tilesize/2f, ha = outSize/2f; + //draw full area + Lines.stroke(2f, Pal.accent); + Drawf.dashRectBasic(spawn.x - fulls, spawn.y - fulls, fulls*2f, fulls*2f); Draw.reset(); - //TODO dash rect for output, fades in/out - Drawf.dashSquare(color, spawn.x, spawn.y, outSize); + 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 + Drawf.dashSquareBasic(spawn.x, spawn.y, outSize); + + /* for(int i : Mathf.signs){ Tmp.v1.trns(rotation * 90, hs, hs * i).add(x, y); @@ -255,12 +405,20 @@ public class UnitAssembler extends PayloadBlock{ 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)); } @@ -280,7 +438,8 @@ public class UnitAssembler extends PayloadBlock{ @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); + var plan = plan(); + return payload instanceof BuildPayload bp && plan.requirements.contains(b -> b.block == bp.block() && blocks.get(bp.block()) < b.amount); } @Override diff --git a/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java b/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java index cf90914314..c1216c068e 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java +++ b/core/src/mindustry/world/blocks/units/UnitAssemblerModule.java @@ -1,21 +1,118 @@ package mindustry.world.blocks.units; +import arc.graphics.g2d.*; +import arc.util.*; +import mindustry.*; +import mindustry.game.*; +import mindustry.gen.*; +import mindustry.graphics.*; +import mindustry.world.*; import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.units.UnitAssembler.*; +import mindustry.world.meta.*; + +import static mindustry.Vars.*; public class UnitAssemblerModule extends PayloadBlock{ + public int tier = 1; public UnitAssemblerModule(String name){ super(name); + rotate = true; + rotateDraw = false; + } + + @Override + public void drawPlace(int x, int y, int rotation, boolean valid){ + super.drawPlace(x, y, rotation, valid); + + var link = getLink(player.team(), x, y, rotation); + if(link != null){ + link.block.drawPlace(link.tile.x, link.tile.y, link.rotation, true); + } + } + + @Override + public boolean canPlaceOn(Tile tile, Team team, int rotation){ + return getLink(team, tile.x, tile.y, rotation) != null; + } + + public @Nullable UnitAssemblerBuild getLink(Team team, int x, int y, int rotation){ + var results = Vars.indexer.getFlagged(team, BlockFlag.unitAssembler).as(); + + return results.find(b -> b.moduleFits(this, x * tilesize + offset, y * tilesize + offset, rotation)); } - //TODO how does it link? public class UnitAssemblerModuleBuild extends PayloadBlockBuild{ public UnitAssemblerBuild link; + public int lastChange = -2; + + public void findLink(){ + if(link != null){ + link.removeModule(this); + } + link = getLink(team, tile.x, tile.y, rotation); + if(link != null){ + link.updateModules(this); + } + } + + public int tier(){ + return tier; + } + + @Override + public void draw(){ + Draw.rect(region, x, y); + + //draw input conveyors + 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(); + drawPayload(); + Draw.z(Layer.blockOver + 0.1f); + Draw.rect(topRegion, x, y); + } + + @Override + public boolean acceptPayload(Building source, Payload payload){ + return link != null && payload == null && link.acceptPayload(source, payload); + } + + @Override + public void drawSelect(){ + //TODO draw area + if(link != null){ + Drawf.selected(link, Pal.accent); + } + } + + @Override + public void onRemoved(){ + super.onRemoved(); + + if(link != null){ + link.removeModule(this); + } + } @Override public void updateTile(){ + if(lastChange != world.tileChanges){ + lastChange = world.tileChanges; + findLink(); + } + if(moveInPayload()){ + if(link != null && link.moduleFits(block, x, y, rotation) && link.acceptPayload(this, payload)){ + link.handlePayload(this, payload); + } + } } } diff --git a/core/src/mindustry/world/consumers/ConsumeItemDynamic.java b/core/src/mindustry/world/consumers/ConsumeItemDynamic.java index b94d11d15c..3c4ff07658 100644 --- a/core/src/mindustry/world/consumers/ConsumeItemDynamic.java +++ b/core/src/mindustry/world/consumers/ConsumeItemDynamic.java @@ -9,6 +9,7 @@ import mindustry.ui.*; public class ConsumeItemDynamic extends Consume{ public final Func items; + @SuppressWarnings("unchecked") public ConsumeItemDynamic(Func items){ this.items = (Func)items; } diff --git a/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java b/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java new file mode 100644 index 0000000000..81dea181f2 --- /dev/null +++ b/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java @@ -0,0 +1,68 @@ +package mindustry.world.consumers; + +import arc.func.*; +import arc.scene.ui.layout.*; +import arc.struct.*; +import mindustry.gen.*; +import mindustry.type.*; +import mindustry.ui.*; +import mindustry.world.meta.*; + +public class ConsumePayloadDynamic extends Consume{ + public final Func> payloads; + + @SuppressWarnings("unchecked") + public ConsumePayloadDynamic(Func> payloads){ + this.payloads = (Func>)payloads; + } + + @Override + public boolean valid(Building build){ + return build.getBlockPayloads().contains(payloads.get(build)); + } + + @Override + public void trigger(Building build){ + build.getBlockPayloads().remove(payloads.get(build)); + } + + @Override + public void display(Stats stats){ + //needs to be implemented by the block itself, not enough info to display here + } + + @Override + public void build(Building build, Table table){ + Seq[] current = {payloads.get(build)}; + + table.table(cont -> { + table.update(() -> { + if(current[0] != payloads.get(build)){ + rebuild(build, cont); + current[0] = payloads.get(build); + } + }); + + rebuild(build, cont); + }); + } + + private void rebuild(Building build, Table table){ + var inv = build.getBlockPayloads(); + var pay = payloads.get(build); + + table.table(c -> { + int i = 0; + for(var stack : pay){ + c.add(new ReqImage(new ItemImage(stack.block.uiIcon, stack.amount), + () -> inv.contains(stack.block, stack.amount))).padRight(8); + if(++i % 4 == 0) c.row(); + } + }).left(); + } + + @Override + public ConsumeType type(){ + return ConsumeType.payload; + } +} diff --git a/core/src/mindustry/world/meta/BlockFlag.java b/core/src/mindustry/world/meta/BlockFlag.java index a19e57c824..49f3c7be4a 100644 --- a/core/src/mindustry/world/meta/BlockFlag.java +++ b/core/src/mindustry/world/meta/BlockFlag.java @@ -26,7 +26,10 @@ public enum BlockFlag{ //single-block identifiers launchPad, unitCargoUnloadPoint, - unitAssembler; + unitAssembler, + + /** @deprecated not used anywhere */ + @Deprecated resupply; public final static BlockFlag[] all = values(); diff --git a/gradle.properties b/gradle.properties index 6a75f14441..f6658de073 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,4 +24,4 @@ android.useAndroidX=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=487418bc46 +archash=224e6c07e0