diff --git a/annotations/src/main/java/mindustry/annotations/Annotations.java b/annotations/src/main/java/mindustry/annotations/Annotations.java index 95bd2b2a4a..fd4fe37f51 100644 --- a/annotations/src/main/java/mindustry/annotations/Annotations.java +++ b/annotations/src/main/java/mindustry/annotations/Annotations.java @@ -26,6 +26,12 @@ public class Annotations{ public @interface Replace{ } + /** Indicates that a method should be final in all implementing classes. */ + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.SOURCE) + public @interface Final{ + } + /** Indicates that a component field is imported from other components. */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.SOURCE) diff --git a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java index 3e072d2fa8..f47eafc237 100644 --- a/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java +++ b/annotations/src/main/java/mindustry/annotations/entity/EntityProcess.java @@ -308,7 +308,7 @@ public class EntityProcess extends BaseProcessor{ //build method using same params/returns MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(first.is(Modifier.PRIVATE) ? Modifier.PRIVATE : Modifier.PUBLIC); - if(isFinal) mbuilder.addModifiers(Modifier.FINAL); + if(isFinal || entry.value.contains(s -> s.has(Final.class))) mbuilder.addModifiers(Modifier.FINAL); if(first.is(Modifier.STATIC)) mbuilder.addModifiers(Modifier.STATIC); mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get)); mbuilder.returns(first.retn()); diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index c274d1a412..27765522cb 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1194,7 +1194,7 @@ block.force-projector.description = Creates a hexagonal force field around itsel block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. block.conveyor.description = Basic item transport block. Moves items forward and automatically deposits them into blocks. Rotatable. block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. -block.plastanium-conveyor.description = The \uf81c moves items in batches.\nOnly accepts items at the back.\nLoads & unloads on all 3 sides. +block.plastanium-conveyor.description = The \uf818 moves items in batches.\nOnly accepts items at the back.\nLoads & unloads on all 3 sides. block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index 8885e7623d..6b1a6d3c03 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -225,6 +225,8 @@ 63519=ground-factory|block-ground-factory-medium 63518=legacy-unit-factory|block-legacy-unit-factory-medium 63517=mass-conveyor|block-mass-conveyor-medium -63516=crater|crater -63515=plastanium-conveyor|block-plastanium-conveyor-medium -63514=legacy-command-center|block-legacy-command-center-medium +63516=legacy-command-center|block-legacy-command-center-medium +63515=block-forge|block-block-forge-medium +63514=block-launcher|block-block-launcher-medium +63513=plastanium-conveyor|block-plastanium-conveyor-medium +63512=crater|crater diff --git a/core/assets/sprites/block_colors.png b/core/assets/sprites/block_colors.png index da67eb0440..3a560bb861 100644 Binary files a/core/assets/sprites/block_colors.png and b/core/assets/sprites/block_colors.png differ diff --git a/core/assets/sprites/sprites5.png b/core/assets/sprites/sprites5.png index 11874bfdeb..4be47b43ec 100644 Binary files a/core/assets/sprites/sprites5.png and b/core/assets/sprites/sprites5.png differ diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index b191eb9757..46c0095133 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -18,6 +18,7 @@ import mindustry.world.blocks.defense.*; import mindustry.world.blocks.defense.turrets.*; import mindustry.world.blocks.distribution.*; import mindustry.world.blocks.environment.*; +import mindustry.world.blocks.experimental.*; import mindustry.world.blocks.legacy.*; import mindustry.world.blocks.liquid.*; import mindustry.world.blocks.logic.*; @@ -76,7 +77,11 @@ public class Blocks implements ContentList{ duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown, //units - groundFactory, repairPoint + groundFactory, repairPoint, + + //misc experimental + + blockForge, blockLauncher; ; @@ -1733,6 +1738,23 @@ public class Blocks implements ContentList{ new LegacyUnitFactory("legacy-unit-factory"); new LegacyCommandCenter("legacy-command-center"); + //endregion + //region experimental + + blockForge = new BlockForge("block-forge"){{ + requirements(Category.production, ItemStack.with(Items.thorium, 100)); + hasPower = true; + consumes.power(2f); + size = 3; + }}; + + blockLauncher = new BlockLauncher("block-launcher"){{ + requirements(Category.production, ItemStack.with(Items.thorium, 100)); + size = 3; + hasPower = true; + consumes.power(2f); + }}; + //endregion } } diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 19741e8892..05ecf2abc6 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -12,6 +12,8 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; +import mindustry.world.*; +import mindustry.world.blocks.experimental.BlockLauncher.*; import static arc.graphics.g2d.Draw.*; import static arc.graphics.g2d.Lines.*; @@ -86,6 +88,20 @@ public class Fx{ Fill.circle(x, y, e.fslope() * 1.5f * size); }), + blockTransfer = new Effect(25f, e -> { + if(!(e.data instanceof LaunchedBlock)) return; + + LaunchedBlock l = e.data(); + + Block block = l.block; + Position to = Tmp.v3.set(l.x * tilesize, l.y * tilesize).add(block.offset(), block.offset()); + + Tmp.v1.set(e.x, e.y).interpolate(Tmp.v2.set(to), e.fin(), Interpolation.linear); + float x = Tmp.v1.x, y = Tmp.v1.y; + + Draw.rect(block.icon(Cicon.full), x, y); + }), + lightning = new Effect(10f, 500f, e -> { if(!(e.data instanceof Array)) return; Array lines = e.data(); diff --git a/core/src/mindustry/entities/def/TileComp.java b/core/src/mindustry/entities/def/TileComp.java index 516708b13e..fb3e9d381f 100644 --- a/core/src/mindustry/entities/def/TileComp.java +++ b/core/src/mindustry/entities/def/TileComp.java @@ -1012,6 +1012,7 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree remove(); } + @Final @Override public void update(){ timeScaleDuration -= Time.delta(); diff --git a/core/src/mindustry/graphics/LightRenderer.java b/core/src/mindustry/graphics/LightRenderer.java index 4867200a3e..bb2c411ea8 100644 --- a/core/src/mindustry/graphics/LightRenderer.java +++ b/core/src/mindustry/graphics/LightRenderer.java @@ -13,7 +13,9 @@ import static mindustry.Vars.state; /** Renders overlay lights. Client only. */ public class LightRenderer{ + public static boolean enable = true; private static final int scaling = 4; + private float[] vertices = new float[24]; private FrameBuffer buffer = new FrameBuffer(2, 2); private Array lights = new Array<>(); @@ -178,6 +180,11 @@ public class LightRenderer{ } public void draw(){ + if(!enable){ + lights.clear(); + return; + } + if(buffer.getWidth() != Core.graphics.getWidth()/scaling || buffer.getHeight() != Core.graphics.getHeight()/scaling){ buffer.resize(Core.graphics.getWidth()/scaling, Core.graphics.getHeight()/scaling); } diff --git a/core/src/mindustry/world/blocks/experimental/BlockForge.java b/core/src/mindustry/world/blocks/experimental/BlockForge.java new file mode 100644 index 0000000000..0134c86ed2 --- /dev/null +++ b/core/src/mindustry/world/blocks/experimental/BlockForge.java @@ -0,0 +1,140 @@ +package mindustry.world.blocks.experimental; + +import arc.graphics.g2d.*; +import arc.math.*; +import arc.scene.ui.layout.*; +import arc.struct.*; +import arc.util.ArcAnnotate.*; +import arc.util.io.*; +import mindustry.*; +import mindustry.gen.*; +import mindustry.graphics.*; +import mindustry.type.*; +import mindustry.ui.*; +import mindustry.world.*; +import mindustry.world.blocks.*; +import mindustry.world.blocks.payloads.*; + +public class BlockForge extends Block{ + public float buildSpeed = 0.4f; + + public BlockForge(String name){ + super(name); + + size = 3; + update = true; + outputsPayload = true; + hasItems = true; + configurable = true; + hasPower = true; + + config(Block.class, (tile, block) -> ((BlockForgeEntity)tile).recipe = block); + } + + @Override + public void setBars(){ + super.setBars(); + + bars.add("progress", entity -> new Bar("bar.progress", Pal.ammo, () -> ((BlockForgeEntity)entity).progress)); + } + + public class BlockForgeEntity extends TileEntity{ + public @Nullable Payload payload; + public @Nullable Block recipe; + public float progress, time, heat; + + @Override + public boolean acceptItem(Tilec source, Item item){ + return items.get(item) < getMaximumAccepted(item); + } + + @Override + public int getMaximumAccepted(Item item){ + if(recipe == null) return 0; + for(ItemStack stack : recipe.requirements){ + if(stack.item == item) return stack.amount * 2; + } + return 0; + } + + @Override + public void updateTile(){ + boolean produce = recipe != null && consValid() && payload == null && items.has(recipe.requirements); + + if(produce){ + progress += buildSpeed * edelta(); + + if(progress >= recipe.buildCost){ + items.remove(recipe.requirements); + payload = new BlockPayload(recipe); + progress = 0f; + } + }else{ + progress = 0; + } + + heat = Mathf.lerpDelta(heat, Mathf.num(produce), 0.3f); + time += heat * delta(); + + if(payload != null && dumpPayload(payload)){ + payload = null; + } + } + + @Override + public void buildConfiguration(Table table){ + Array blocks = Vars.content.blocks().select(b -> b.isVisible() && b.size <= 2 && b.requirements.length <= 3); + + ItemSelection.buildTable(table, blocks, () -> recipe, block -> recipe = block); + } + + @Override + public Object config(){ + return recipe; + } + + @Override + public void draw(){ + super.draw(); + + if(payload != null){ + payload.draw(x, y, 0); + } + + if(recipe != null){ + TextureRegion region = recipe.icon(Cicon.full); + + Shaders.build.region = region; + Shaders.build.progress = progress / recipe.buildCost; + Shaders.build.color.set(Pal.accent); + Shaders.build.color.a = heat; + Shaders.build.time = -time / 20f; + + Draw.shader(Shaders.build); + Draw.rect(region, x, y); + Draw.shader(); + + Draw.color(Pal.accent); + Draw.alpha(heat); + + Lines.lineAngleCenter(x + Mathf.sin(time, 20f, Vars.tilesize / 2f * size - 2f), y, 90, size * Vars.tilesize - 4f); + + Draw.reset(); + } + } + + @Override + public void write(Writes write){ + super.write(write); + write.s(recipe == null ? -1 : recipe.id); + write.f(progress); + } + + @Override + public void read(Reads read, byte revision){ + super.read(read, revision); + recipe = Vars.content.block(read.s()); + progress = read.f(); + } + } +} diff --git a/core/src/mindustry/world/blocks/experimental/BlockLauncher.java b/core/src/mindustry/world/blocks/experimental/BlockLauncher.java new file mode 100644 index 0000000000..ffe540e8c3 --- /dev/null +++ b/core/src/mindustry/world/blocks/experimental/BlockLauncher.java @@ -0,0 +1,90 @@ +package mindustry.world.blocks.experimental; + +import arc.math.geom.*; +import arc.struct.*; +import arc.util.*; +import mindustry.content.*; +import mindustry.entities.*; +import mindustry.game.*; +import mindustry.gen.*; +import mindustry.world.*; +import mindustry.world.blocks.payloads.*; +import mindustry.world.blocks.production.*; + +import static mindustry.Vars.*; + +//pointless, will definitely be removed eventually +public class BlockLauncher extends PayloadAcceptor{ + static final IntArray positions = new IntArray(); + + public float range = 150; + + public BlockLauncher(String name){ + super(name); + + update = true; + size = 3; + } + + public class BlockLauncherEntity extends PayloadAcceptorEntity{ + + @Override + public void draw(){ + super.draw(); + + drawPayload(); + } + + @Override + public boolean acceptPayload(Tilec source, Payload payload){ + return this.payload == null && payload instanceof BlockPayload; + } + + @Override + public void updateTile(){ + if(updatePayload() && efficiency() >= 0.99f){ + Effects.shake(4f, 4f, this); + Fx.producesmoke.at(this); + + positions.clear(); + BlockPayload pay = (BlockPayload)payload; + + Geometry.circle(tileX(), tileY(), world.width(), world.height(), (int)(range / tilesize), (cx, cy) -> { + if(Build.validPlace(team, cx, cy, pay.block, 0)){ + positions.add(Point2.pack(cx, cy)); + } + }); + + if(positions.isEmpty()) return; + + int pick = positions.random(); + LaunchedBlock launch = new LaunchedBlock(Point2.x(pick), Point2.y(pick), pay.block, team); + Fx.blockTransfer.at(x, y, 0, launch); + Time.run(Fx.blockTransfer.lifetime, () -> { + float ex = launch.x * tilesize + launch.block.offset(), ey = launch.y * tilesize + launch.block.offset(); + if(Build.validPlace(launch.team, launch.x, launch.y, launch.block, 0)){ + world.tile(launch.x, launch.y).setBlock(launch.block, launch.team); + Fx.placeBlock.at(ex, ey, launch.block.size); + }else{ + Fx.breakBlock.at(ex, ey, launch.block.size); + Fx.explosion.at(ex, ey); + } + }); + payload = null; + } + } + } + + public class LaunchedBlock{ + public final int x, y; + public final Block block; + public final Team team; + + public LaunchedBlock(int x, int y, Block block, Team team){ + this.x = x; + this.y = y; + this.block = block; + this.team = team; + } + } +} diff --git a/core/src/mindustry/world/blocks/logic/LogicExecutor.java b/core/src/mindustry/world/blocks/experimental/LogicExecutor.java similarity index 98% rename from core/src/mindustry/world/blocks/logic/LogicExecutor.java rename to core/src/mindustry/world/blocks/experimental/LogicExecutor.java index 65b8b9599a..e7e7add43b 100644 --- a/core/src/mindustry/world/blocks/logic/LogicExecutor.java +++ b/core/src/mindustry/world/blocks/experimental/LogicExecutor.java @@ -1,4 +1,4 @@ -package mindustry.world.blocks.logic; +package mindustry.world.blocks.experimental; import arc.math.*; import mindustry.gen.*; diff --git a/core/src/mindustry/world/blocks/units/PayloadUnitFactory.java b/core/src/mindustry/world/blocks/experimental/PayloadUnitFactory.java similarity index 99% rename from core/src/mindustry/world/blocks/units/PayloadUnitFactory.java rename to core/src/mindustry/world/blocks/experimental/PayloadUnitFactory.java index 7b85aa9511..1d0214dbfc 100644 --- a/core/src/mindustry/world/blocks/units/PayloadUnitFactory.java +++ b/core/src/mindustry/world/blocks/experimental/PayloadUnitFactory.java @@ -1,4 +1,4 @@ -package mindustry.world.blocks.units; +package mindustry.world.blocks.experimental; import arc.*; import arc.graphics.g2d.*; diff --git a/core/src/mindustry/world/blocks/logic/ProcessorBlock.java b/core/src/mindustry/world/blocks/experimental/ProcessorBlock.java similarity index 94% rename from core/src/mindustry/world/blocks/logic/ProcessorBlock.java rename to core/src/mindustry/world/blocks/experimental/ProcessorBlock.java index e1b4834c58..97ffad9c34 100644 --- a/core/src/mindustry/world/blocks/logic/ProcessorBlock.java +++ b/core/src/mindustry/world/blocks/experimental/ProcessorBlock.java @@ -1,4 +1,4 @@ -package mindustry.world.blocks.logic; +package mindustry.world.blocks.experimental; import arc.scene.ui.layout.*; import arc.struct.*; diff --git a/core/src/mindustry/world/blocks/payloads/BlockPayload.java b/core/src/mindustry/world/blocks/payloads/BlockPayload.java new file mode 100644 index 0000000000..eb25fae32a --- /dev/null +++ b/core/src/mindustry/world/blocks/payloads/BlockPayload.java @@ -0,0 +1,22 @@ +package mindustry.world.blocks.payloads; + +import arc.graphics.g2d.*; +import mindustry.graphics.*; +import mindustry.ui.*; +import mindustry.world.*; + +import static mindustry.Vars.tilesize; + +public class BlockPayload implements Payload{ + public Block block; + + public BlockPayload(Block block){ + this.block = block; + } + + @Override + public void draw(float x, float y, float rotation){ + Drawf.shadow(x, y, block.size * tilesize * 2f); + Draw.rect(block.icon(Cicon.full), x, y, 0); + } +} diff --git a/core/src/mindustry/world/blocks/production/PayloadAcceptor.java b/core/src/mindustry/world/blocks/production/PayloadAcceptor.java new file mode 100644 index 0000000000..3861753b5c --- /dev/null +++ b/core/src/mindustry/world/blocks/production/PayloadAcceptor.java @@ -0,0 +1,53 @@ +package mindustry.world.blocks.production; + +import arc.math.*; +import arc.math.geom.*; +import arc.util.ArcAnnotate.*; +import mindustry.gen.*; +import mindustry.world.*; +import mindustry.world.blocks.payloads.*; + +import static mindustry.Vars.tilesize; + +public class PayloadAcceptor extends Block{ + + public PayloadAcceptor(String name){ + super(name); + + update = true; + } + + public class PayloadAcceptorEntity extends TileEntity{ + public @Nullable Payload payload; + public Vec2 inputVector = new Vec2(); + public float inputRotation; + + @Override + public boolean acceptPayload(Tilec source, Payload payload){ + return this.payload == null; + } + + @Override + public void handlePayload(Tilec source, Payload payload){ + this.payload = payload; + this.inputVector.set(source).sub(this).clamp(-size * tilesize / 2f, size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f); + this.inputRotation = source.angleTo(this); + } + + /** @return true if the payload is in position. */ + public boolean updatePayload(){ + if(payload == null) return false; + + inputRotation = Mathf.slerpDelta(inputRotation, 90f, 0.3f); + inputVector.lerpDelta(Vec2.ZERO, 0.2f); + + return inputVector.isZero(0.5f); + } + + public void drawPayload(){ + if(payload != null){ + payload.draw(x + inputVector.x, y + inputVector.y, inputRotation); + } + } + } +} diff --git a/core/src/mindustry/world/modules/ItemModule.java b/core/src/mindustry/world/modules/ItemModule.java index 14a5a1814e..476ccd6618 100644 --- a/core/src/mindustry/world/modules/ItemModule.java +++ b/core/src/mindustry/world/modules/ItemModule.java @@ -161,6 +161,10 @@ public class ItemModule extends BlockModule{ total -= amount; } + public void remove(ItemStack[] stacks){ + for(ItemStack stack : stacks) remove(stack.item, stack.amount); + } + public void remove(ItemStack stack){ remove(stack.item, stack.amount); } diff --git a/tools/build.gradle b/tools/build.gradle index 760734ba61..a4b5ed9e1e 100644 --- a/tools/build.gradle +++ b/tools/build.gradle @@ -209,7 +209,7 @@ def tileImage = { File file -> if(index != -1){ int resultIndex = (x == y ? 1 : index == 2 ? 0 : index == 0 ? 2 : 1); - result.setRGB(x, y, Color.argb8888(list[resultIndex])) + result.setRGB(x, y, list[resultIndex].argb8888()) } } }