diff --git a/core/assets/sprites/sprites5.png b/core/assets/sprites/sprites5.png index e6443c9a51..53497141de 100644 Binary files a/core/assets/sprites/sprites5.png and b/core/assets/sprites/sprites5.png differ diff --git a/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java index 81da65344d..1bc00443c6 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java @@ -20,6 +20,10 @@ public class ArtilleryBulletType extends BasicBulletType{ hitSound = Sounds.explosion; } + public ArtilleryBulletType(){ + this(1f, 1f, "shell"); + } + @Override public void update(io.anuke.mindustry.entities.type.Bullet b){ super.update(b); diff --git a/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java index 8f54b58995..bf20c811ab 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java @@ -22,6 +22,11 @@ public class BasicBulletType extends BulletType{ this.bulletSprite = bulletSprite; } + /** For mods. */ + public BasicBulletType(){ + this(1f, 1f, "bullet"); + } + @Override public void load(){ backRegion = Core.atlas.find(bulletSprite + "-back"); diff --git a/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java index bf2e5ecd43..55c0a3b87d 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java @@ -17,4 +17,8 @@ public class BombBulletType extends BasicBulletType{ collidesAir = false; hitSound = Sounds.explosion; } + + public BombBulletType(){ + this(1f, 1f, "shell"); + } } diff --git a/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java index 3669909097..c6e2928b28 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java @@ -19,6 +19,10 @@ public abstract class FlakBulletType extends BasicBulletType{ bulletHeight = 10f; } + public FlakBulletType(){ + this(1f, 1f); + } + @Override public void update(Bullet b){ super.update(b); diff --git a/core/src/io/anuke/mindustry/entities/bullet/HealBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/HealBulletType.java index dab576883c..95dbef7b63 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/HealBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/HealBulletType.java @@ -22,6 +22,10 @@ public class HealBulletType extends BulletType{ collidesTeam = true; } + public HealBulletType(){ + this(1f, 1f); + } + @Override public boolean collides(Bullet b, Tile tile){ return tile.getTeam() != b.getTeam() || tile.entity.healthf() < 1f; diff --git a/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java index 186ea47425..7d53a852ec 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java @@ -3,6 +3,7 @@ package io.anuke.mindustry.entities.bullet; import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.effect.*; @@ -13,14 +14,17 @@ import io.anuke.mindustry.world.*; import static io.anuke.mindustry.Vars.*; public class LiquidBulletType extends BulletType{ - Liquid liquid; + @NonNull Liquid liquid; - public LiquidBulletType(Liquid liquid){ + public LiquidBulletType(@Nullable Liquid liquid){ super(3.5f, 0); - this.liquid = liquid; + + if(liquid != null){ + this.liquid = liquid; + this.status = liquid.effect; + } lifetime = 74f; - status = liquid.effect; statusDuration = 90f; despawnEffect = Fx.none; hitEffect = Fx.hitLiquid; @@ -30,13 +34,17 @@ public class LiquidBulletType extends BulletType{ knockback = 0.55f; } + public LiquidBulletType(){ + this(null); + } + @Override public float range(){ return speed * lifetime / 2f; } @Override - public void update(io.anuke.mindustry.entities.type.Bullet b){ + public void update(Bullet b){ super.update(b); if(liquid.canExtinguish()){ @@ -50,7 +58,7 @@ public class LiquidBulletType extends BulletType{ } @Override - public void draw(io.anuke.mindustry.entities.type.Bullet b){ + public void draw(Bullet b){ Draw.color(liquid.color, Color.white, b.fout() / 100f); Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f); diff --git a/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java index 06d05d1110..aee06f9e31 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java @@ -23,6 +23,10 @@ public class MissileBulletType extends BasicBulletType{ hitSound = Sounds.explosion; } + public MissileBulletType(){ + this(1f, 1f, "missile"); + } + @Override public void update(Bullet b){ super.update(b); diff --git a/core/src/io/anuke/mindustry/mod/ContentParser.java b/core/src/io/anuke/mindustry/mod/ContentParser.java index fda221c100..62892e55d8 100644 --- a/core/src/io/anuke/mindustry/mod/ContentParser.java +++ b/core/src/io/anuke/mindustry/mod/ContentParser.java @@ -22,6 +22,7 @@ import io.anuke.mindustry.gen.*; import io.anuke.mindustry.mod.Mods.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.consumers.*; import java.lang.reflect.*; @@ -35,6 +36,13 @@ public class ContentParser{ put(StatusEffect.class, (type, data) -> field(StatusEffects.class, data)); put(Loadout.class, (type, data) -> field(Loadouts.class, data)); put(Color.class, (type, data) -> Color.valueOf(data.asString())); + put(BulletType.class, (type, data) -> { + Class bc = data.has("type") ? resolve(data.getString("type"), "io.anuke.mindustry.entities.bullets") : BasicBulletType.class; + data.remove("type"); + BulletType result = bc.getDeclaredConstructor().newInstance(); + readFields(result, data); + return result; + }); put(Music.class, (type, data) -> { if(fieldOpt(Musics.class, data) != null) return fieldOpt(Musics.class, data); @@ -57,12 +65,18 @@ public class ContentParser{ * This is done to accomodate binding of content names first.*/ private Array reads = new Array<>(); private LoadedMod currentMod; + private Content currentContent; private Json parser = new Json(){ - public T readValue(Class type, Class elementType, JsonValue jsonData){ + @Override + public T readValue(Class type, Class elementType, JsonValue jsonData, Class keyType){ if(type != null){ if(classParsers.containsKey(type)){ - return (T)classParsers.get(type).parse(type, jsonData); + try{ + return (T)classParsers.get(type).parse(type, jsonData); + }catch(Exception e){ + throw new RuntimeException(e); + } } if(Content.class.isAssignableFrom(type)){ @@ -70,16 +84,21 @@ public class ContentParser{ String prefix = currentMod != null ? currentMod.name + "-" : ""; T one = (T)Vars.content.getByName(ctype, prefix + jsonData.asString()); if(one != null) return one; - return (T)Vars.content.getByName(ctype, jsonData.asString()); + T two = (T)Vars.content.getByName(ctype, jsonData.asString()); + + if(two != null) return two; + throw new IllegalArgumentException("No " + ctype + " found with name: '" + jsonData.asString() + "'"); } } - return super.readValue(type, elementType, jsonData); + return super.readValue(type, elementType, jsonData, keyType); } }; private ObjectMap> parsers = ObjectMap.of( ContentType.block, (TypeParser)(mod, name, value) -> { + readBundle(ContentType.block, name, value); + //TODO generate dynamically instead of doing.. this Class type = resolve(value.getString("type"), "io.anuke.mindustry.world", @@ -100,19 +119,17 @@ public class ContentParser{ if(value.has("consumes")){ for(JsonValue child : value.get("consumes")){ if(child.name.equals("item")){ - if(child.isString()){ - block.consumes.item(Vars.content.getByName(ContentType.item, child.asString())); - }else{ - ItemStack stack = parser.readValue(ItemStack.class, child); - block.consumes.item(stack.item, stack.amount); - } + block.consumes.item(Vars.content.getByName(ContentType.item, child.asString())); }else if(child.name.equals("items")){ - block.consumes.items(parser.readValue(ItemStack[].class, child)); + block.consumes.add((Consume)parser.readValue(ConsumeItems.class, child)); }else if(child.name.equals("liquid")){ - LiquidStack stack = parser.readValue(LiquidStack.class, child); - block.consumes.liquid(stack.liquid, stack.amount); + block.consumes.add((Consume)parser.readValue(ConsumeLiquid.class, child)); }else if(child.name.equals("power")){ - block.consumes.power(child.asFloat()); + if(child.isDouble()){ + block.consumes.power(child.asFloat()); + }else{ + block.consumes.add((Consume)parser.readValue(ConsumePower.class, child)); + } }else if(child.name.equals("powerBuffered")){ block.consumes.powerBuffered(child.asFloat()); }else{ @@ -138,6 +155,8 @@ public class ContentParser{ return block; }, ContentType.unit, (TypeParser)(mod, name, value) -> { + readBundle(ContentType.unit, name, value); + Class type = resolve(value.getString("type"), "io.anuke.mindustry.entities.type.base"); UnitType unit = new UnitType(mod + "-" + name, supply(type)); read(() -> readFields(unit, value, true)); @@ -156,6 +175,8 @@ public class ContentParser{ if(Vars.content.getByName(type, name) != null){ item = (T)Vars.content.getByName(type, name); }else{ + readBundle(type, name, value); + item = constructor.get(mod + "-" + name); } read(() -> readFields(item, value)); @@ -163,6 +184,22 @@ public class ContentParser{ }; } + private void readBundle(ContentType type, String name, JsonValue value){ + String entryName = type + "." + currentMod.name + "-" + name + "."; + I18NBundle bundle = Core.bundle; + while(bundle.getParent() != null) bundle = bundle.getParent(); + + if(value.has("name")){ + bundle.getProperties().put(entryName + "name", value.getString("name")); + value.remove("name"); + } + + if(value.has("description")){ + bundle.getProperties().put(entryName + "description", value.getString("description")); + value.remove("description"); + } + } + /** Call to read a content's extra info later.*/ private void read(Runnable run){ LoadedMod mod = currentMod; @@ -308,7 +345,7 @@ public class ContentParser{ } Field field = metadata.field; try{ - field.set(object, parser.readValue(field.getType(), metadata.elementType, child)); + field.set(object, parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType)); }catch(ReflectionException ex){ throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex); }catch(SerializationException ex){ @@ -324,7 +361,7 @@ public class ContentParser{ } /** Tries to resolve a class from a list of potential class names. */ - private Class resolve(String base, String... potentials) throws Exception{ + private Class resolve(String base, String... potentials){ for(String type : potentials){ try{ return (Class)Class.forName(type + '.' + base); @@ -335,7 +372,7 @@ public class ContentParser{ } private interface FieldParser{ - Object parse(Class type, JsonValue value); + Object parse(Class type, JsonValue value) throws Exception; } private interface TypeParser{ diff --git a/core/src/io/anuke/mindustry/mod/Mods.java b/core/src/io/anuke/mindustry/mod/Mods.java index 97a4e06255..4d2686aa48 100644 --- a/core/src/io/anuke/mindustry/mod/Mods.java +++ b/core/src/io/anuke/mindustry/mod/Mods.java @@ -121,7 +121,7 @@ public class Mods implements Loadable{ //get textures packed if(totalSprites > 0){ - TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.Linear : TextureFilter.Nearest; + TextureFilter filter = TextureFilter.Nearest; packer.updateTextureAtlas(Core.atlas, filter, filter, false); //generate new icons @@ -175,7 +175,7 @@ public class Mods implements Loadable{ } } - //load mods now + //load workshop mods now for(FileHandle file : platform.getExternalMods()){ try{ LoadedMod mod = loadMod(file, true); @@ -185,7 +185,7 @@ public class Mods implements Loadable{ disabled.add(mod); } }catch(Exception e){ - Log.err("Failed to load mod file {0}. Skipping.", file); + Log.err("Failed to load mod workshop file {0}. Skipping.", file); Log.err(e); } } diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 29b9d10913..7f886a05e1 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -96,6 +96,8 @@ public class Block extends BlockStorage{ public boolean targetable = true; /** Whether the overdrive core has any effect on this block. */ public boolean canOverdrive = true; + /** Outlined icon color.*/ + public Color outlineColor = Color.valueOf("404049"); /** Whether the icon region has an outline added. */ public boolean outlineIcon = false; /** Whether this block has a shadow under it. */ @@ -385,6 +387,10 @@ public class Block extends BlockStorage{ buildCost += stack.amount * stack.item.cost; } + if(consumes.has(ConsumeType.power)) hasPower = true; + if(consumes.has(ConsumeType.item)) hasItems = true; + if(consumes.has(ConsumeType.liquid)) hasLiquids = true; + setStats(); setBars(); @@ -668,8 +674,8 @@ public class Block extends BlockStorage{ } @Override - public void createIcons(PixmapPacker out, PixmapPacker editor){ - super.createIcons(out, editor); + public void createIcons(PixmapPacker packer, PixmapPacker editor){ + super.createIcons(packer, editor); editor.pack(name + "-icon-editor", Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)).crop()); @@ -677,6 +683,56 @@ public class Block extends BlockStorage{ PixmapRegion image = Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)); color.set(image.getPixel(image.width/2, image.height/2)); } + + getGeneratedIcons(); + + Pixmap last = null; + + if(outlineIcon){ + final int radius = 4; + PixmapRegion region = Core.atlas.getPixmap(getGeneratedIcons()[getGeneratedIcons().length-1]); + Pixmap out = new Pixmap(region.width, region.height); + Color color = new Color(); + for(int x = 0; x < region.width; x++){ + for(int y = 0; y < region.height; y++){ + + region.getPixel(x, y, color); + out.draw(x, y, color); + if(color.a < 1f){ + boolean found = false; + outer: + for(int rx = -radius; rx <= radius; rx++){ + for(int ry = -radius; ry <= radius; ry++){ + if(Structs.inBounds(rx + x, ry + y, region.width, region.height) && Mathf.dst2(rx, ry) <= radius*radius && color.set(region.getPixel(rx + x, ry + y)).a > 0.01f){ + found = true; + break outer; + } + } + } + if(found){ + out.draw(x, y, outlineColor); + } + } + } + } + last = out; + + packer.pack(name, out); + } + + if(generatedIcons.length > 1){ + Pixmap base = Core.atlas.getPixmap(generatedIcons[0]).crop(); + for(int i = 1; i < generatedIcons.length; i++){ + if(i == generatedIcons.length - 1 && last != null){ + base.drawPixmap(last); + }else{ + base.draw(Core.atlas.getPixmap(generatedIcons[i])); + } + } + packer.pack("block-" + name + "-full", base); + generatedIcons = null; + Arrays.fill(cicons, null); + } } /** Never use outside of the editor! */ diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumeItems.java b/core/src/io/anuke/mindustry/world/consumers/ConsumeItems.java index 967685e693..f6b047bf6f 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumeItems.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumeItems.java @@ -2,6 +2,7 @@ package io.anuke.mindustry.world.consumers; import io.anuke.arc.collection.*; import io.anuke.arc.scene.ui.layout.*; +import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; import io.anuke.mindustry.type.*; @@ -11,12 +12,17 @@ import io.anuke.mindustry.world.meta.*; import io.anuke.mindustry.world.meta.values.*; public class ConsumeItems extends Consume{ - public final ItemStack[] items; + public final @NonNull ItemStack[] items; public ConsumeItems(ItemStack[] items){ this.items = items; } + /** Mods.*/ + protected ConsumeItems(){ + this(new ItemStack[]{}); + } + @Override public void applyItemFilter(Bits filter){ for(ItemStack stack : items){ diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumeLiquid.java b/core/src/io/anuke/mindustry/world/consumers/ConsumeLiquid.java index 96d9bc9de1..066fb29836 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumeLiquid.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumeLiquid.java @@ -1,14 +1,13 @@ package io.anuke.mindustry.world.consumers; import io.anuke.arc.collection.*; -import io.anuke.arc.scene.ui.layout.Table; -import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.arc.scene.ui.layout.*; +import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; -import io.anuke.mindustry.type.Liquid; -import io.anuke.mindustry.ui.ReqImage; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.BlockStats; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.ui.*; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.meta.*; public class ConsumeLiquid extends ConsumeLiquidBase{ public final Liquid liquid; @@ -18,6 +17,10 @@ public class ConsumeLiquid extends ConsumeLiquidBase{ this.liquid = liquid; } + protected ConsumeLiquid(){ + this(null, 0f); + } + @Override public void applyLiquidFilter(Bits filter){ filter.set(liquid.id); diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index 18b2d422db..d183b3969f 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -20,6 +20,10 @@ public class ConsumePower extends Consume{ this.buffered = buffered; } + protected ConsumePower(){ + this(0f, 0f, false); + } + @Override public ConsumeType type(){ return ConsumeType.power; diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index 0950aaf6bb..92530555a1 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -149,6 +149,7 @@ public class DesktopLauncher extends ClientLauncher{ SVars.stats = new SStats(); SVars.workshop = new SWorkshop(); SVars.user = new SUser(); + boolean[] isShutdown = {false}; Events.on(ClientLoadEvent.class, event -> { player.name = SVars.net.friends.getPersonaName(); @@ -177,8 +178,18 @@ public class DesktopLauncher extends ClientLauncher{ } }); }); + + Events.on(DisposeEvent.class, event -> { + SteamAPI.shutdown(); + isShutdown[0] = true; + }); + //steam shutdown hook - Runtime.getRuntime().addShutdownHook(new Thread(SteamAPI::shutdown)); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + if(!isShutdown[0]){ + SteamAPI.shutdown(); + } + })); } static void handleCrash(Throwable e){ diff --git a/gradle.properties b/gradle.properties index 8a43b1cc6a..baf26c820e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=e17b152d4f597837640fe4d659ca5a820e8a2b15 +archash=9f567f04a005ea8b4132b31d64f325ec105def5e diff --git a/tools/build.gradle b/tools/build.gradle index c1ab47a151..189eb07bd9 100644 --- a/tools/build.gradle +++ b/tools/build.gradle @@ -320,7 +320,7 @@ task pack(dependsOn: classes){ //antialias everything except UI elements fileTree(dir: '../core/assets-raw/sprites_out/', include: "**/*.png").visit{ file -> - if(file.isDirectory() || file.toString().replace("\\", "/").contains("zones/") || (file.toString().replace("\\", "/").contains("/ui/") && !file.toString().contains("icon-small") && !file.toString().contains("icon-medium") && !file.toString().contains("icon-large"))) return + if(file.isDirectory() || file.toString().replace("\\", "/").contains("zones/") || (file.toString().replace("\\", "/").contains("/ui/") && file.toString().startsWith("icon-"))) return antialias(file.file) } diff --git a/tools/src/io/anuke/mindustry/Generators.java b/tools/src/io/anuke/mindustry/Generators.java index 38c4a9cf15..651657070d 100644 --- a/tools/src/io/anuke/mindustry/Generators.java +++ b/tools/src/io/anuke/mindustry/Generators.java @@ -68,7 +68,6 @@ public class Generators{ ImagePacker.generate("block-icons", () -> { Image colors = new Image(content.blocks().size, 1); - Color outlineColor = Color.valueOf("404049"); for(Block block : content.blocks()){ TextureRegion[] regions = block.getGeneratedIcons(); @@ -114,7 +113,7 @@ public class Generators{ } } if(found){ - out.draw(x, y, outlineColor); + out.draw(x, y, block.outlineColor); } } }