diff --git a/core/src/io/anuke/mindustry/content/Loadouts.java b/core/src/io/anuke/mindustry/content/Loadouts.java index af4a5962c0..631066a24f 100644 --- a/core/src/io/anuke/mindustry/content/Loadouts.java +++ b/core/src/io/anuke/mindustry/content/Loadouts.java @@ -1,10 +1,12 @@ package io.anuke.mindustry.content; -import io.anuke.mindustry.ctype.ContentList; -import io.anuke.mindustry.type.Loadout; +import io.anuke.mindustry.ctype.*; +import io.anuke.mindustry.game.*; + +import java.io.*; public class Loadouts implements ContentList{ - public static Loadout + public static Schematic basicShard, advancedShard, basicFoundation, @@ -12,43 +14,13 @@ public class Loadouts implements ContentList{ @Override public void load(){ - basicShard = new Loadout( - " ### ", - " #1# ", - " ### ", - " ^ ^ ", - " ## ## ", - " C# C# " - ); - - advancedShard = new Loadout( - " ### ", - " #1# ", - "#######", - "C#^ ^C#", - " ## ## ", - " C# C# " - ); - - basicFoundation = new Loadout( - " #### ", - " #### ", - " #2## ", - " #### ", - " ^^^^ ", - " ###### ", - " C#C#C# " - ); - - basicNucleus = new Loadout( - " ##### ", - " ##### ", - " ##3## ", - " ##### ", - " >#####< ", - " ^ ^ ^ ^ ", - "#### ####", - "C#C# C#C#" - ); + try{ + basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA=="); + advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ="); + basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA=="); + basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1"); + }catch(IOException e){ + throw new RuntimeException(e); + } } } diff --git a/core/src/io/anuke/mindustry/game/Schematic.java b/core/src/io/anuke/mindustry/game/Schematic.java index cfa4d25d19..1ff6efd77c 100644 --- a/core/src/io/anuke/mindustry/game/Schematic.java +++ b/core/src/io/anuke/mindustry/game/Schematic.java @@ -7,6 +7,7 @@ import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.mindustry.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.storage.*; import static io.anuke.mindustry.Vars.*; @@ -39,6 +40,16 @@ public class Schematic implements Publishable, Comparable{ return stacks; } + public boolean hasCore(){ + return tiles.contains(s -> s.block instanceof CoreBlock); + } + + public @NonNull CoreBlock findCore(){ + CoreBlock block = (CoreBlock)tiles.find(s -> s.block instanceof CoreBlock).block; + if(block == null) throw new IllegalArgumentException("Schematic is missing a core!"); + return block; + } + public String name(){ return tags.get("name", "unknown"); } diff --git a/core/src/io/anuke/mindustry/game/Schematics.java b/core/src/io/anuke/mindustry/game/Schematics.java index be3ae0fa75..f6086b8b2e 100644 --- a/core/src/io/anuke/mindustry/game/Schematics.java +++ b/core/src/io/anuke/mindustry/game/Schematics.java @@ -20,6 +20,8 @@ import io.anuke.mindustry.input.Placement.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.world.blocks.production.*; +import io.anuke.mindustry.world.blocks.storage.*; import java.io.*; import java.util.zip.*; @@ -28,6 +30,8 @@ import static io.anuke.mindustry.Vars.*; /** Handles schematics.*/ public class Schematics implements Loadable{ + public static final String base64Header = "bXNjaAB"; + private static final byte[] header = {'m', 's', 'c', 'h'}; private static final byte version = 0; @@ -231,6 +235,27 @@ public class Schematics implements Loadable{ .removeAll(s -> !s.block.isVisible() || !s.block.unlockedCur()); } + public void placeLoadout(Schematic schem, int x, int y){ + Stile coreTile = schem.tiles.find(s -> s.block instanceof CoreBlock); + int ox = x - coreTile.x, oy = y - coreTile.y; + schem.tiles.each(st -> { + Tile tile = world.tile(st.x + ox, st.y + oy); + if(tile == null) return; + + world.setBlock(tile, st.block, defaultTeam); + tile.rotation(st.rotation); + if(st.block.posConfig){ + tile.configureAny(Pos.get(tile.x - st.x + Pos.x(st.config), tile.y - st.y + Pos.y(st.config))); + }else{ + tile.configureAny(st.config); + } + + if(st.block instanceof Drill){ + tile.getLinkedTiles(t -> t.setOverlay(Blocks.oreCopper)); + } + }); + } + /** Adds a schematic to the list, also copying it into the files.*/ public void add(Schematic schematic){ all.add(schematic); @@ -328,13 +353,13 @@ public class Schematics implements Loadable{ } } + //region IO methods + /** Loads a schematic from base64. May throw an exception. */ - public Schematic readBase64(String schematic) throws IOException{ + public static Schematic readBase64(String schematic) throws IOException{ return read(new ByteArrayInputStream(Base64Coder.decode(schematic))); } - //region IO methods - public static Schematic read(FileHandle file) throws IOException{ Schematic s = read(new DataInputStream(file.read(1024))); if(!s.tags.containsKey("name")){ diff --git a/core/src/io/anuke/mindustry/game/Stats.java b/core/src/io/anuke/mindustry/game/Stats.java index afdd5c0706..aa152d377c 100644 --- a/core/src/io/anuke/mindustry/game/Stats.java +++ b/core/src/io/anuke/mindustry/game/Stats.java @@ -33,7 +33,7 @@ public class Stats{ score += (float)((wavesLasted - zone.conditionWave) / zone.launchPeriod + 1) * 1.2f; } - int capacity = zone.loadout.core().itemCapacity; + int capacity = zone.loadout.findCore().itemCapacity; //weigh used fractions float frac = 0f; diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 017733a840..d8d1698205 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -122,7 +122,7 @@ public class DesktopInput extends InputHandler{ drawSelected(sreq.x, sreq.y, sreq.block, getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null ? Pal.remove : Pal.accent); } - if(Core.input.keyDown(Binding.schematic_select)){ + if(Core.input.keyDown(Binding.schematic_select) && !ui.chatfrag.chatOpen()){ drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize); } @@ -305,7 +305,7 @@ public class DesktopInput extends InputHandler{ selectRequests.clear(); } - if(Core.input.keyRelease(Binding.schematic_select)){ + if(Core.input.keyRelease(Binding.schematic_select) && !ui.chatfrag.chatOpen()){ lastSchematic = schematics.create(schemX, schemY, rawCursorX, rawCursorY); useSchematic(lastSchematic); if(selectRequests.isEmpty()){ diff --git a/core/src/io/anuke/mindustry/maps/generators/Generator.java b/core/src/io/anuke/mindustry/maps/generators/Generator.java index 965c6cf0e7..0b64a067de 100644 --- a/core/src/io/anuke/mindustry/maps/generators/Generator.java +++ b/core/src/io/anuke/mindustry/maps/generators/Generator.java @@ -1,11 +1,11 @@ package io.anuke.mindustry.maps.generators; -import io.anuke.mindustry.type.Loadout; -import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.world.*; public abstract class Generator{ public int width, height; - protected Loadout loadout; + protected Schematic loadout; public Generator(int width, int height){ this.width = width; @@ -15,7 +15,7 @@ public abstract class Generator{ public Generator(){ } - public void init(Loadout loadout){ + public void init(Schematic loadout){ this.loadout = loadout; } diff --git a/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java b/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java index 8c7da50621..0f819e8d16 100644 --- a/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java @@ -5,6 +5,7 @@ import io.anuke.arc.math.*; import io.anuke.arc.math.geom.*; import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; +import io.anuke.mindustry.game.*; import io.anuke.mindustry.io.*; import io.anuke.mindustry.maps.*; import io.anuke.mindustry.type.*; @@ -52,7 +53,7 @@ public class MapGenerator extends Generator{ } @Override - public void init(Loadout loadout){ + public void init(Schematic loadout){ this.loadout = loadout; map = maps.loadInternalMap(mapName); width = map.width; @@ -149,7 +150,7 @@ public class MapGenerator extends Generator{ throw new IllegalArgumentException("All zone maps must have a core."); } - loadout.setup(core.x, core.y); + schematics.placeLoadout(loadout, core.x, core.y); world.prepareTiles(tiles); world.setMap(map); diff --git a/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java b/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java index 31ed7981dd..a82b121c31 100644 --- a/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java +++ b/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java @@ -5,6 +5,8 @@ import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.maps.generators.BasicGenerator; import io.anuke.mindustry.world.Tile; +import static io.anuke.mindustry.Vars.schematics; + public class DesertWastesGenerator extends BasicGenerator{ public DesertWastesGenerator(int width, int height){ @@ -42,6 +44,6 @@ public class DesertWastesGenerator extends BasicGenerator{ //scatter(tiles, Blocks.sandRocks, Blocks.creeptree, 1f); tiles[endX][endY].setOverlay(Blocks.spawn); - loadout.setup(spawnX, spawnY); + schematics.placeLoadout(loadout, spawnX, spawnY); } } diff --git a/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java b/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java index 0fec4b4879..527eab7d0d 100644 --- a/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java +++ b/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java @@ -5,6 +5,8 @@ import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.maps.generators.BasicGenerator; import io.anuke.mindustry.world.Tile; +import static io.anuke.mindustry.Vars.schematics; + public class OvergrowthGenerator extends BasicGenerator{ public OvergrowthGenerator(int width, int height){ @@ -38,6 +40,6 @@ public class OvergrowthGenerator extends BasicGenerator{ //scatter(tiles, Blocks.sporePine, Blocks.whiteTreeDead, 1f); tiles[endX][endY].setOverlay(Blocks.spawn); - loadout.setup(spawnX, spawnY); + schematics.placeLoadout(loadout, spawnX, spawnY); } } diff --git a/core/src/io/anuke/mindustry/mod/ContentParser.java b/core/src/io/anuke/mindustry/mod/ContentParser.java index 14121f67ee..afd5b3c2b8 100644 --- a/core/src/io/anuke/mindustry/mod/ContentParser.java +++ b/core/src/io/anuke/mindustry/mod/ContentParser.java @@ -39,7 +39,19 @@ public class ContentParser{ private ObjectMap, FieldParser> classParsers = new ObjectMap, FieldParser>(){{ put(Effect.class, (type, data) -> field(Fx.class, data)); put(StatusEffect.class, (type, data) -> field(StatusEffects.class, data)); - put(Loadout.class, (type, data) -> field(Loadouts.class, data)); + put(Schematic.class, (type, data) -> { + Object result = fieldOpt(Loadouts.class, data); + if(result != null){ + return result; + }else{ + String str = data.asString(); + if(str.startsWith(Schematics.base64Header)){ + return Schematics.readBase64(str); + }else{ + return Schematics.read(Vars.tree.get("schematics/" + str + "." + Vars.schematicExtension)); + } + } + }); put(Color.class, (type, data) -> Color.valueOf(data.asString())); put(BulletType.class, (type, data) -> { if(data.isString()){ diff --git a/core/src/io/anuke/mindustry/type/Loadout.java b/core/src/io/anuke/mindustry/type/Loadout.java deleted file mode 100644 index 0b669c3f8f..0000000000 --- a/core/src/io/anuke/mindustry/type/Loadout.java +++ /dev/null @@ -1,113 +0,0 @@ -package io.anuke.mindustry.type; - -import io.anuke.arc.collection.*; -import io.anuke.mindustry.content.*; -import io.anuke.mindustry.ctype.Content; -import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.storage.*; - -import static io.anuke.mindustry.Vars.*; - -//TODO this class is a disappointment -public class Loadout extends Content{ - private final Array outArray = new Array<>(); - private final IntMap entries = new IntMap(){{ - put('>', new BlockEntry(Blocks.conveyor, 0)); - put('^', new BlockEntry(Blocks.conveyor, 1)); - put('<', new BlockEntry(Blocks.conveyor, 2)); - put('v', new BlockEntry(Blocks.conveyor, 3)); - - put('1', new BlockEntry(Blocks.coreShard)); - put('2', new BlockEntry(Blocks.coreFoundation)); - put('3', new BlockEntry(Blocks.coreNucleus)); - - put('C', new BlockEntry(Blocks.mechanicalDrill, Blocks.oreCopper)); - }}; - - private final IntMap blocks = new IntMap<>(); - private Block core; - - public Loadout(String... layout){ - int coreX = -1, coreY = -1; - - outer: - for(int y = 0; y < layout.length; y++){ - for(int x = 0; x < layout[0].length(); x++){ - char c = layout[y].charAt(x); - if(entries.get(c) != null && entries.get(c).block instanceof CoreBlock){ - core = entries.get(c).block; - coreX = x; - coreY = y; - break outer; - } - } - } - - if(coreX == -1) throw new IllegalArgumentException("Schematic does not have a core."); - - for(int y = 0; y < layout.length; y++){ - for(int x = 0; x < layout[0].length(); x++){ - char c = layout[y].charAt(x); - if(entries.containsKey(c)){ - BlockEntry entry = entries.get(c); - blocks.put(Pos.get(x - coreX, -(y - coreY)), entry); - } - } - } - } - - public Loadout(){ - - } - - public Block core(){ - return core; - } - - public void setup(int x, int y){ - for(IntMap.Entry entry : blocks.entries()){ - int rx = Pos.x(entry.key); - int ry = Pos.y(entry.key); - Tile tile = world.tile(x + rx, y + ry); - if(tile == null) continue; - - world.setBlock(tile, entry.value.block, defaultTeam); - tile.rotation((byte)entry.value.rotation); - if(entry.value.ore != null){ - for(Tile t : tile.getLinkedTiles(outArray)){ - t.setOverlay(entry.value.ore); - } - } - } - } - - @Override - public ContentType getContentType(){ - return ContentType.loadout; - } - - static class BlockEntry{ - final Block block; - final Block ore; - final int rotation; - - BlockEntry(Block block, Block ore){ - this.block = block; - this.ore = ore; - this.rotation = 0; - } - - BlockEntry(Block block, int rotation){ - this.block = block; - this.ore = null; - this.rotation = rotation; - } - - BlockEntry(Block block){ - this.block = block; - this.ore = null; - this.rotation = 0; - } - } - -} diff --git a/core/src/io/anuke/mindustry/type/Zone.java b/core/src/io/anuke/mindustry/type/Zone.java index a1daf7f7f5..e0084f0b73 100644 --- a/core/src/io/anuke/mindustry/type/Zone.java +++ b/core/src/io/anuke/mindustry/type/Zone.java @@ -26,7 +26,7 @@ public class Zone extends UnlockableContent{ public boolean alwaysUnlocked; public int conditionWave = Integer.MAX_VALUE; public int launchPeriod = 10; - public Loadout loadout = Loadouts.basicShard; + public Schematic loadout = Loadouts.basicShard; public TextureRegion preview; protected Array baseLaunchCost = new Array<>(); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SchematicsDialog.java index fe24b31bdc..0109b959e4 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SchematicsDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SchematicsDialog.java @@ -153,7 +153,7 @@ public class SchematicsDialog extends FloatingDialog{ t.addImageTextButton("$schematic.copy.import", Icon.copySmall, style, () -> { dialog.hide(); try{ - Schematic s = schematics.readBase64(Core.app.getClipboardText()); + Schematic s = Schematics.readBase64(Core.app.getClipboardText()); schematics.add(s); setup(); ui.showInfoFade("$schematic.saved"); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java index e563d9c07c..32d2e264f1 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java @@ -143,7 +143,7 @@ public class ZoneInfoDialog extends FloatingDialog{ cont.row(); cont.addButton(zone.canConfigure() ? "$configure" : Core.bundle.format("configure.locked", zone.configureObjective.display()), - () -> loadout.show(zone.loadout.core().itemCapacity, zone.getStartingItems(), zone::resetStartingItems, zone::updateLaunchCost, rebuildItems) + () -> loadout.show(zone.loadout.findCore().itemCapacity, zone.getStartingItems(), zone::resetStartingItems, zone::updateLaunchCost, rebuildItems) ).fillX().pad(3).disabled(b -> !zone.canConfigure()); cont.row(); diff --git a/core/src/io/anuke/mindustry/world/Build.java b/core/src/io/anuke/mindustry/world/Build.java index c8a137f520..5f7834ed25 100644 --- a/core/src/io/anuke/mindustry/world/Build.java +++ b/core/src/io/anuke/mindustry/world/Build.java @@ -16,7 +16,6 @@ import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import static io.anuke.mindustry.Vars.*; public class Build{ - private static final Rectangle rect = new Rectangle(); /** Returns block type that was broken, or null if unsuccesful. */ @Remote(called = Loc.server) diff --git a/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java b/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java index 10f678173c..c39246314c 100644 --- a/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java @@ -7,6 +7,7 @@ import io.anuke.arc.Graphics.Cursor.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.*; import io.anuke.arc.util.ArcAnnotate.*; +import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.effect.*; @@ -29,6 +30,10 @@ public class BuildBlock extends Block{ public static final int maxSize = 9; private static final BuildBlock[] buildBlocks = new BuildBlock[maxSize]; + private static long lastTime = 0; + private static int pitchSeq = 0; + private static long lastPlayed; + public BuildBlock(int size){ super("build" + size); this.size = size; @@ -53,7 +58,7 @@ public class BuildBlock extends Block{ Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), block.size); world.removeBlock(tile); Events.fire(new BlockBuildEndEvent(tile, playerGroup.getByID(builderID), team, true)); - Sounds.breaks.at(tile, Mathf.random(0.7f, 1.4f)); + if(shouldPlay()) Sounds.breaks.at(tile, calcPitch(false)); } @Remote(called = Loc.server) @@ -73,12 +78,36 @@ public class BuildBlock extends Block{ Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), block.size); } + static boolean shouldPlay(){ + if(Time.timeSinceMillis(lastPlayed) >= 32){ + lastPlayed = Time.millis(); + return true; + }else{ + return false; + } + } + + static float calcPitch(boolean up){ + if(Time.timeSinceMillis(lastTime) < 16 * 30){ + lastTime = Time.millis(); + pitchSeq ++; + if(pitchSeq > 30){ + pitchSeq = 0; + } + return 1f + Mathf.clamp(pitchSeq / 30f) * (up ? 1.9f : -0.4f); + }else{ + pitchSeq = 0; + lastTime = Time.millis(); + return Mathf.random(0.7f, 1.3f); + } + } + public static void constructed(Tile tile, Block block, int builderID, byte rotation, Team team, boolean skipConfig){ Call.onConstructFinish(tile, block, builderID, rotation, team, skipConfig); tile.block().placed(tile); Events.fire(new BlockBuildEndEvent(tile, playerGroup.getByID(builderID), team, false)); - Sounds.place.at(tile, Mathf.random(0.7f, 1.4f)); + if(shouldPlay()) Sounds.place.at(tile, calcPitch(true)); } @Override