diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index f07d54b45b..d816e2f14e 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -45,7 +45,6 @@ database = Core Database savegame = Save Game loadgame = Load Game joingame = Join Game -addplayers = Add/Remove Players customgame = Custom Game newgame = New Game none = @@ -164,7 +163,6 @@ server.port = Port: server.addressinuse = Address already in use! server.invalidport = Invalid port number! server.error = [crimson]Error hosting server. -save.old = This save is for an older version of the game, and can no longer be used.\n\n[lightgray]Save backwards compatibility will be implemented in the full 4.0 release. save.new = New Save save.overwrite = Are you sure you want to overwrite\nthis save slot? overwrite = Overwrite @@ -378,7 +376,6 @@ campaign = Campaign load = Load save = Save fps = FPS: {0} -tps = TPS: {0} ping = Ping: {0}ms language.restart = Please restart your game for the language settings to take effect. settings = Settings @@ -392,8 +389,9 @@ abandon = Abandon abandon.text = This zone and all its resources will be lost to the enemy. locked = Locked complete = [lightgray]Complete: -zone.requirement.wave = Wave {0} in {1} -zone.requirement = Destroy core: {0} +requirement.wave = Reach Wave {0} in {1} +requirement.core = Destroy Enemy Core in {0} +requirement.unlock = Unlock {0} resume = Resume Zone:\n[lightgray]{0} bestwave = [lightgray]Best Wave: {0} launch = < LAUNCH > @@ -406,11 +404,11 @@ uncover = Uncover configure = Configure Loadout bannedblocks = Banned Blocks addall = Add All -configure.locked = [lightgray]Unlock configuring loadout: Wave {0}. +configure.locked = [lightgray]Unlock configuring loadout: {0}. configure.invalid = Amount must be a number between 0 and {0}. zone.unlocked = [lightgray]{0} unlocked. -zone.requirements.met = {0}: zone requirements met. -zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.requirement.complete = Requirement for {0} completed:[lightgray]\n{1} +zone.config.unlocked = Loadout unlocked:[lightgray]\n{0} zone.resources = [lightgray]Resources Detected: zone.objective = [lightgray]Objective: [accent]{0} zone.objective.survival = Survive @@ -471,12 +469,13 @@ settings.cleardata = Clear Game Data... settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. paused = [accent]< Paused > +clear = Clear +banned = [scarlet]Banned yes = Yes no = No info.title = Info error.title = [crimson]An error has occured error.crashtitle = An error has occured -attackpvponly = [scarlet]Only available in Attack/PvP modes blocks.input = Input blocks.output = Output blocks.booster = Booster diff --git a/core/src/io/anuke/mindustry/content/Zones.java b/core/src/io/anuke/mindustry/content/Zones.java index ca7013fa3f..90794dd80e 100644 --- a/core/src/io/anuke/mindustry/content/Zones.java +++ b/core/src/io/anuke/mindustry/content/Zones.java @@ -1,13 +1,15 @@ package io.anuke.mindustry.content; -import io.anuke.arc.collection.Array; -import io.anuke.mindustry.game.ContentList; -import io.anuke.mindustry.game.SpawnGroup; -import io.anuke.mindustry.maps.generators.MapGenerator; -import io.anuke.mindustry.maps.generators.MapGenerator.Decoration; -import io.anuke.mindustry.maps.zonegen.DesertWastesGenerator; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Objectives.*; +import io.anuke.mindustry.maps.generators.*; +import io.anuke.mindustry.maps.generators.MapGenerator.*; +import io.anuke.mindustry.maps.zonegen.*; import io.anuke.mindustry.type.*; -import io.anuke.mindustry.world.Block; + +import static io.anuke.arc.collection.Array.with; +import static io.anuke.mindustry.content.Items.*; +import static io.anuke.mindustry.type.ItemStack.list; public class Zones implements ContentList{ public static Zone @@ -20,28 +22,26 @@ public class Zones implements ContentList{ public void load(){ groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{ - baseLaunchCost = ItemStack.with(Items.copper, -60); - startingItems = ItemStack.list(Items.copper, 60); + baseLaunchCost = list(copper, -60); + startingItems = list(copper, 60); alwaysUnlocked = true; conditionWave = 5; launchPeriod = 5; - resources = new Item[]{Items.copper, Items.scrap, Items.lead}; + resources = with(copper, scrap, lead); }}; desertWastes = new Zone("desertWastes", new DesertWastesGenerator(260, 260)){{ - startingItems = ItemStack.list(Items.copper, 120); + startingItems = list(copper, 120); conditionWave = 20; launchPeriod = 10; loadout = Loadouts.advancedShard; - zoneRequirements = ZoneRequirement.with(groundZero, 20); - blockRequirements = new Block[]{Blocks.combustionGenerator}; - resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.sand}; + resources = with(copper, lead, coal, sand); rules = r -> { r.waves = true; r.waveTimer = true; r.launchWaveMultiplier = 3f; r.waveSpacing = 60 * 50f; - r.spawns = Array.with( + r.spawns = with( new SpawnGroup(UnitTypes.crawler){{ unitScaling = 3f; }}, @@ -75,96 +75,140 @@ public class Zones implements ContentList{ }} ); }; + requirements = with( + new ZoneWave(groundZero, 20), + new Unlock(Blocks.combustionGenerator) + ); }}; saltFlats = new Zone("saltFlats", new MapGenerator("saltFlats")){{ - startingItems = ItemStack.list(Items.copper, 200, Items.silicon, 200, Items.lead, 200); + startingItems = list(copper, 200, Items.silicon, 200, lead, 200); loadout = Loadouts.basicFoundation; conditionWave = 10; launchPeriod = 5; - zoneRequirements = ZoneRequirement.with(desertWastes, 60); - blockRequirements = new Block[]{Blocks.daggerFactory, Blocks.draugFactory, Blocks.door, Blocks.waterExtractor}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand, Items.titanium}; + configureObjective = new Launched(this); + resources = with(copper, scrap, lead, coal, sand, titanium); + requirements = with( + new ZoneWave(desertWastes, 60), + new Unlock(Blocks.daggerFactory), + new Unlock(Blocks.draugFactory), + new Unlock(Blocks.door), + new Unlock(Blocks.waterExtractor) + ); }}; frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1) .decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{ loadout = Loadouts.basicFoundation; - baseLaunchCost = ItemStack.with(); - startingItems = ItemStack.list(Items.copper, 250); + startingItems = list(copper, 250); conditionWave = 10; - blockRequirements = new Block[]{Blocks.junction, Blocks.router}; - zoneRequirements = ZoneRequirement.with(groundZero, 10); - resources = new Item[]{Items.copper, Items.lead, Items.coal}; + resources = with(copper, lead, coal); + requirements = with( + new ZoneWave(groundZero, 10), + new Unlock(Blocks.junction), + new Unlock(Blocks.router) + ); }}; craters = new Zone("craters", new MapGenerator("craters", 1).decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.004))){{ - startingItems = ItemStack.list(Items.copper, 100); + startingItems = list(copper, 100); conditionWave = 10; - zoneRequirements = ZoneRequirement.with(frozenForest, 10); - blockRequirements = new Block[]{Blocks.mender, Blocks.combustionGenerator}; - resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.sand, Items.scrap}; + resources = with(copper, lead, coal, sand, scrap); + requirements = with( + new ZoneWave(frozenForest, 10), + new Unlock(Blocks.mender), + new Unlock(Blocks.combustionGenerator) + ); }}; ruinousShores = new Zone("ruinousShores", new MapGenerator("ruinousShores", 1)){{ loadout = Loadouts.basicFoundation; - baseLaunchCost = ItemStack.with(); - startingItems = ItemStack.list(Items.copper, 140, Items.lead, 50); + startingItems = list(copper, 140, lead, 50); conditionWave = 20; launchPeriod = 20; - zoneRequirements = ZoneRequirement.with(desertWastes, 20, craters, 15); - blockRequirements = new Block[]{Blocks.graphitePress, Blocks.combustionGenerator, Blocks.kiln, Blocks.mechanicalPump}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; + resources = with(copper, scrap, lead, coal, sand); + requirements = with( + new ZoneWave(desertWastes, 20), + new ZoneWave(craters, 15), + new Unlock(Blocks.graphitePress), + new Unlock(Blocks.combustionGenerator), + new Unlock(Blocks.kiln), + new Unlock(Blocks.mechanicalPump) + ); }}; stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2) .decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{ loadout = Loadouts.basicFoundation; - startingItems = ItemStack.list(Items.copper, 200, Items.lead, 50); + startingItems = list(copper, 200, lead, 50); conditionWave = 10; launchPeriod = 10; - zoneRequirements = ZoneRequirement.with(frozenForest, 15); - blockRequirements = new Block[]{Blocks.pneumaticDrill, Blocks.powerNode, Blocks.turbineGenerator}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand}; + resources = with(copper, scrap, lead, coal, titanium, sand); + requirements = with( + new ZoneWave(frozenForest, 15), + new Unlock(Blocks.pneumaticDrill), + new Unlock(Blocks.powerNode), + new Unlock(Blocks.turbineGenerator) + ); }}; fungalPass = new Zone("fungalPass", new MapGenerator("fungalPass")){{ - startingItems = ItemStack.list(Items.copper, 250, Items.lead, 250, Items.metaglass, 100, Items.graphite, 100); - zoneRequirements = ZoneRequirement.with(stainedMountains, 15); - blockRequirements = new Block[]{Blocks.daggerFactory, Blocks.crawlerFactory, Blocks.door, Blocks.siliconSmelter}; - resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.titanium, Items.sand}; + startingItems = list(copper, 250, lead, 250, Items.metaglass, 100, Items.graphite, 100); + resources = with(copper, lead, coal, titanium, sand); + configureObjective = new Launched(this); + requirements = with( + new ZoneWave(stainedMountains, 15), + new Unlock(Blocks.daggerFactory), + new Unlock(Blocks.crawlerFactory), + new Unlock(Blocks.door), + new Unlock(Blocks.siliconSmelter) + ); }}; overgrowth = new Zone("overgrowth", new MapGenerator("overgrowth")){{ - startingItems = ItemStack.list(Items.copper, 1500, Items.lead, 1000, Items.silicon, 500, Items.metaglass, 250); + startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250); conditionWave = 12; launchPeriod = 4; loadout = Loadouts.basicNucleus; - zoneRequirements = ZoneRequirement.with(craters, 40, fungalPass, 10); - blockRequirements = new Block[]{Blocks.cultivator, Blocks.sporePress, Blocks.titanFactory, Blocks.wraithFactory}; - resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.titanium, Items.sand, Items.thorium, Items.scrap}; + configureObjective = new Launched(this); + resources = with(copper, lead, coal, titanium, sand, thorium, scrap); + requirements = with( + new ZoneWave(craters, 40), + new Launched(fungalPass), + new Unlock(Blocks.cultivator), + new Unlock(Blocks.sporePress), + new Unlock(Blocks.titanFactory), + new Unlock(Blocks.wraithFactory) + ); }}; tarFields = new Zone("tarFields", new MapGenerator("tarFields") .decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{ loadout = Loadouts.basicFoundation; - startingItems = ItemStack.list(Items.copper, 250, Items.lead, 100); + startingItems = list(copper, 250, lead, 100); conditionWave = 15; launchPeriod = 10; - zoneRequirements = ZoneRequirement.with(ruinousShores, 20); - blockRequirements = new Block[]{Blocks.coalCentrifuge, Blocks.conduit, Blocks.wave}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium, Items.sand}; + requirements = with(new ZoneWave(ruinousShores, 20)); + resources = with(copper, scrap, lead, coal, titanium, thorium, sand); + requirements = with( + new ZoneWave(ruinousShores, 20), + new Unlock(Blocks.coalCentrifuge), + new Unlock(Blocks.conduit), + new Unlock(Blocks.wave) + ); }}; desolateRift = new Zone("desolateRift", new MapGenerator("desolateRift")){{ loadout = Loadouts.basicNucleus; - baseLaunchCost = ItemStack.with(); - startingItems = ItemStack.list(Items.copper, 1000, Items.lead, 1000, Items.graphite, 250, Items.titanium, 250, Items.silicon, 250); + startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250); conditionWave = 3; launchPeriod = 2; - zoneRequirements = ZoneRequirement.with(tarFields, 20); - blockRequirements = new Block[]{Blocks.thermalGenerator, Blocks.thoriumReactor}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand, Items.thorium}; + resources = with(copper, scrap, lead, coal, titanium, sand, thorium); + requirements = with( + new ZoneWave(tarFields, 20), + new Unlock(Blocks.thermalGenerator), + new Unlock(Blocks.thoriumReactor) + ); }}; /* @@ -174,21 +218,23 @@ public class Zones implements ContentList{ startingItems = ItemStack.list(Items.copper, 2000, Items.lead, 2000, Items.graphite, 500, Items.titanium, 500, Items.silicon, 500); conditionWave = 3; launchPeriod = 2; - zoneRequirements = ZoneRequirement.with(stainedMountains, 40); + requirements = with(stainedMountains, 40); blockRequirements = new Block[]{Blocks.thermalGenerator}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; + resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; }};*/ nuclearComplex = new Zone("nuclearComplex", new MapGenerator("nuclearProductionComplex", 1) .decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.01))){{ loadout = Loadouts.basicNucleus; - baseLaunchCost = ItemStack.with(); - startingItems = ItemStack.list(Items.copper, 1250, Items.lead, 1500, Items.silicon, 400, Items.metaglass, 250); + startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250); conditionWave = 30; launchPeriod = 15; - zoneRequirements = ZoneRequirement.with(fungalPass, 8); - blockRequirements = new Block[]{Blocks.thermalGenerator, Blocks.laserDrill}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium, Items.sand}; + resources = with(copper, scrap, lead, coal, titanium, thorium, sand); + requirements = with( + new Launched(fungalPass), + new Unlock(Blocks.thermalGenerator), + new Unlock(Blocks.laserDrill) + ); }}; /* @@ -198,9 +244,9 @@ public class Zones implements ContentList{ startingItems = ItemStack.list(Items.copper, 2000, Items.lead, 2000, Items.graphite, 500, Items.titanium, 500, Items.silicon, 500); conditionWave = 3; launchPeriod = 2; - zoneRequirements = ZoneRequirement.with(nuclearComplex, 40); + requirements = with(nuclearComplex, 40); blockRequirements = new Block[]{Blocks.thermalGenerator}; - resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium}; + resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium}; }};*/ } } diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 2dcbcb42c0..ddfa1b2a46 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -146,11 +146,15 @@ public class Control implements ApplicationListener, Loadable{ }); Events.on(ZoneRequireCompleteEvent.class, e -> { - ui.hudfrag.showToast(Core.bundle.format("zone.requirements.met", e.zone.localizedName)); + if(e.objective.display() != null){ + ui.hudfrag.showToast(Core.bundle.format("zone.requirement.complete", e.zoneForMet.localizedName, e.objective.display())); + } }); Events.on(ZoneConfigureCompleteEvent.class, e -> { - ui.hudfrag.showToast(Core.bundle.format("zone.config.complete", e.zone.configureWave)); + if(e.zone.configureObjective.display() != null){ + ui.hudfrag.showToast(Core.bundle.format("zone.config.unlocked", e.zone.configureObjective.display())); + } }); Events.on(Trigger.newGame, () -> { diff --git a/core/src/io/anuke/mindustry/core/Logic.java b/core/src/io/anuke/mindustry/core/Logic.java index 34014ba874..460085c058 100644 --- a/core/src/io/anuke/mindustry/core/Logic.java +++ b/core/src/io/anuke/mindustry/core/Logic.java @@ -136,8 +136,7 @@ public class Logic implements ApplicationListener{ public void runWave(){ spawner.spawnEnemies(); state.wave++; - state.wavetime = world.isZone() && world.getZone().isBossWave(state.wave) ? state.rules.waveSpacing * state.rules.bossWaveMultiplier : - world.isZone() && world.getZone().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing; + state.wavetime = world.isZone() && world.getZone().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing; Events.fire(new WaveEvent()); } diff --git a/core/src/io/anuke/mindustry/game/Content.java b/core/src/io/anuke/mindustry/game/Content.java index 20d8612697..b0b4d03b1b 100644 --- a/core/src/io/anuke/mindustry/game/Content.java +++ b/core/src/io/anuke/mindustry/game/Content.java @@ -1,13 +1,13 @@ package io.anuke.mindustry.game; import io.anuke.arc.util.ArcAnnotate.*; -import io.anuke.mindustry.Vars; +import io.anuke.mindustry.*; import io.anuke.mindustry.mod.Mods.*; -import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.type.*; /** Base class for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}. */ -public abstract class Content{ +public abstract class Content implements Comparable{ public final short id; /** The mod that loaded this piece of content. */ public @Nullable LoadedMod mod; @@ -34,6 +34,11 @@ public abstract class Content{ public void load(){ } + @Override + public int compareTo(Content c){ + return Integer.compare(id, c.id); + } + @Override public String toString(){ return getContentType().name() + "#" + id; diff --git a/core/src/io/anuke/mindustry/game/EventType.java b/core/src/io/anuke/mindustry/game/EventType.java index f26dc08970..b25c5faea0 100644 --- a/core/src/io/anuke/mindustry/game/EventType.java +++ b/core/src/io/anuke/mindustry/game/EventType.java @@ -61,11 +61,13 @@ public class EventType{ /** Called when a zone's requirements are met. */ public static class ZoneRequireCompleteEvent{ - public final Zone zone, required; + public final Zone zoneMet, zoneForMet; + public final Objective objective; - public ZoneRequireCompleteEvent(Zone zone, Zone required){ - this.zone = zone; - this.required = required; + public ZoneRequireCompleteEvent(Zone zoneMet, Zone zoneForMet, Objective objective){ + this.zoneMet = zoneMet; + this.zoneForMet = zoneForMet; + this.objective = objective; } } diff --git a/core/src/io/anuke/mindustry/game/GlobalData.java b/core/src/io/anuke/mindustry/game/GlobalData.java index a644c3eed8..5ef1ae09fb 100644 --- a/core/src/io/anuke/mindustry/game/GlobalData.java +++ b/core/src/io/anuke/mindustry/game/GlobalData.java @@ -91,12 +91,17 @@ public class GlobalData{ state.stats.itemsDelivered.getAndIncrement(item, 0, amount); } + public boolean hasItems(Array stacks){ + return !stacks.contains(s -> items.get(s.item, 0) < s.amount); + } + public boolean hasItems(ItemStack[] stacks){ for(ItemStack stack : stacks){ - if(items.get(stack.item, 0) < stack.amount){ + if(!has(stack.item, stack.amount)){ return false; } } + return true; } @@ -107,6 +112,13 @@ public class GlobalData{ modified = true; } + public void removeItems(Array stacks){ + for(ItemStack stack : stacks){ + items.getAndIncrement(stack.item, 0, -stack.amount); + } + modified = true; + } + public boolean has(Item item, int amount){ return items.get(item, 0) >= amount; } diff --git a/core/src/io/anuke/mindustry/game/Objective.java b/core/src/io/anuke/mindustry/game/Objective.java index 1ed588d4a3..dcd1146a10 100644 --- a/core/src/io/anuke/mindustry/game/Objective.java +++ b/core/src/io/anuke/mindustry/game/Objective.java @@ -1,5 +1,10 @@ package io.anuke.mindustry.game; +import io.anuke.arc.scene.ui.layout.*; +import io.anuke.arc.util.ArcAnnotate.*; +import io.anuke.mindustry.game.Objectives.*; +import io.anuke.mindustry.type.*; + /** Defines a specific objective for a game. */ public interface Objective{ @@ -7,6 +12,16 @@ public interface Objective{ boolean complete(); /** @return the string displayed when this objective is completed, in imperative form. - * e.g. when the objective is 'complete 10 waves', this would display "complete 10 waves".*/ - String display(); + * e.g. when the objective is 'complete 10 waves', this would display "complete 10 waves". + * If this objective should not be displayed, should return null.*/ + @Nullable String display(); + + /** Build a display for this zone requirement.*/ + default void build(Table table){ + + } + + default Zone zone(){ + return this instanceof ZoneObjective ? ((ZoneObjective)this).zone : null; + } } diff --git a/core/src/io/anuke/mindustry/game/Objectives.java b/core/src/io/anuke/mindustry/game/Objectives.java index a2ca6d506b..c901fe4f68 100644 --- a/core/src/io/anuke/mindustry/game/Objectives.java +++ b/core/src/io/anuke/mindustry/game/Objectives.java @@ -1,16 +1,21 @@ package io.anuke.mindustry.game; +import io.anuke.arc.*; +import io.anuke.arc.util.ArcAnnotate.*; +import io.anuke.mindustry.type.*; + /** Holds objective classes. */ public class Objectives{ - public static class WaveObjective implements Objective{ + //TODO + public static class Wave implements Objective{ public int wave; - public WaveObjective(int wave){ + public Wave(int wave){ this.wave = wave; } - protected WaveObjective(){} + protected Wave(){} @Override public boolean complete(){ @@ -19,20 +24,72 @@ public class Objectives{ @Override public String display(){ + //TODO return null; } } - public static class LaunchObjective implements Objective{ + public static class Unlock implements Objective{ + public @NonNull UnlockableContent content; + + public Unlock(UnlockableContent content){ + this.content = content; + } + + protected Unlock(){} @Override public boolean complete(){ - return false; + return content.unlocked(); } @Override public String display(){ - return null; + return Core.bundle.format("requirement.unlock", content.localizedName); } } + + public static class ZoneWave extends ZoneObjective{ + public int wave; + + public ZoneWave(Zone zone, int wave){ + this.zone = zone; + this.wave = wave; + } + + protected ZoneWave(){} + + @Override + public boolean complete(){ + return zone.bestWave() >= wave; + } + + @Override + public String display(){ + return Core.bundle.format("requirement.wave", wave, zone.localizedName); + } + } + + public static class Launched extends ZoneObjective{ + + public Launched(Zone zone){ + this.zone = zone; + } + + protected Launched(){} + + @Override + public boolean complete(){ + return zone.hasLaunched(); + } + + @Override + public String display(){ + return Core.bundle.format("requirement.core", zone.localizedName); + } + } + + public abstract static class ZoneObjective implements Objective{ + public @NonNull Zone zone; + } } diff --git a/core/src/io/anuke/mindustry/mod/ContentParser.java b/core/src/io/anuke/mindustry/mod/ContentParser.java index 62892e55d8..06c06f96e5 100644 --- a/core/src/io/anuke/mindustry/mod/ContentParser.java +++ b/core/src/io/anuke/mindustry/mod/ContentParser.java @@ -18,6 +18,7 @@ import io.anuke.mindustry.entities.Effects.*; import io.anuke.mindustry.entities.bullet.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Objectives.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.mod.Mods.*; import io.anuke.mindustry.type.*; @@ -60,6 +61,13 @@ public class ContentParser{ Log.info(Core.assets.get(path)); return Core.assets.get(path); }); + put(Objective.class, (type, data) -> { + Class oc = data.has("type") ? resolve(data.getString("type"), "io.anuke.mindustry.game.objectives") : ZoneWave.class; + data.remove("type"); + Objective obj = oc.getDeclaredConstructor().newInstance(); + readFields(obj, data); + return obj; + }); }}; /** Stores things that need to be parsed fully, e.g. reading fields of content. * This is done to accomodate binding of content names first.*/ diff --git a/core/src/io/anuke/mindustry/type/Item.java b/core/src/io/anuke/mindustry/type/Item.java index fa766dc505..4950f32c10 100644 --- a/core/src/io/anuke/mindustry/type/Item.java +++ b/core/src/io/anuke/mindustry/type/Item.java @@ -10,7 +10,7 @@ import io.anuke.mindustry.world.blocks.*; import static io.anuke.mindustry.Vars.content; -public class Item extends UnlockableContent implements Comparable{ +public class Item extends UnlockableContent{ public final Color color; /** type of the item; used for tabs and core acceptance. default value is {@link ItemType#resource}. */ @@ -61,11 +61,6 @@ public class Item extends UnlockableContent implements Comparable{ return localizedName(); } - @Override - public int compareTo(Item item){ - return Integer.compare(id, item.id); - } - @Override public ContentType getContentType(){ return ContentType.item; diff --git a/core/src/io/anuke/mindustry/type/Zone.java b/core/src/io/anuke/mindustry/type/Zone.java index dcdc0cf124..19e11aa3be 100644 --- a/core/src/io/anuke/mindustry/type/Zone.java +++ b/core/src/io/anuke/mindustry/type/Zone.java @@ -6,23 +6,20 @@ import io.anuke.arc.function.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.ArcAnnotate.*; -import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Objectives.*; import io.anuke.mindustry.maps.generators.*; -import io.anuke.mindustry.world.*; - -import java.util.*; import static io.anuke.mindustry.Vars.*; public class Zone extends UnlockableContent{ public @NonNull Generator generator; - //public Block[] blockRequirements = {}; - public Objective[] requirements = {}; - public Objective configureObjective; //TODO - public Item[] resources = {}; + public @NonNull Objective configureObjective = new ZoneWave(this, 15); + public Array requirements = new Array<>(); + //TODO autogenerate + public Array resources = new Array<>(); public Consumer rules = rules -> {}; public boolean alwaysUnlocked; @@ -31,9 +28,9 @@ public class Zone extends UnlockableContent{ public Loadout loadout = Loadouts.basicShard; public TextureRegion preview; - protected ItemStack[] baseLaunchCost = {}; + protected Array baseLaunchCost = new Array<>(); protected Array startingItems = new Array<>(); - protected ItemStack[] launchCost = null; + protected Array launchCost; private Array defaultStartingItems = new Array<>(); @@ -61,35 +58,15 @@ public class Zone extends UnlockableContent{ } } - public boolean isBossWave(int wave){ - return wave % configureWave == 0 && wave > 0; - } - public boolean isLaunchWave(int wave){ return metCondition() && wave % launchPeriod == 0; } public boolean canUnlock(){ - if(data.isUnlocked(this)){ - return true; - } - - for(ZoneRequirement other : zoneRequirements){ - if(!other.isComplete()){ - return false; - } - } - - for(Block other : blockRequirements){ - if(!data.isUnlocked(other)){ - return false; - } - } - - return true; + return data.isUnlocked(this) || !requirements.contains(r -> !r.complete()); } - public ItemStack[] getLaunchCost(){ + public Array getLaunchCost(){ if(launchCost == null){ updateLaunchCost(); } @@ -110,35 +87,41 @@ public class Zone extends UnlockableContent{ } public void setLaunched(){ - if(!hasLaunched() && getRules().attackMode){ - for(Zone zone : content.zones()){ - ZoneRequirement req = Structs.find(zone.zoneRequirements, f -> f.zone == this); - if(req != null && req.isComplete()){ - Events.fire(new ZoneRequireCompleteEvent(zone, this)); - } - } - } - Core.settings.put(name + "-launched", true); - data.modified(); + updateObjectives(() -> { + Core.settings.put(name + "-launched", true); + data.modified(); + }); } public void updateWave(int wave){ int value = Core.settings.getInt(name + "-wave", 0); + if(value < wave){ - Core.settings.put(name + "-wave", wave); - data.modified(); + updateObjectives(() -> { + Core.settings.put(name + "-wave", wave); + data.modified(); + }); + } + } - for(Zone zone : content.zones()){ - ZoneRequirement req = Structs.find(zone.zoneRequirements, f -> f.zone == this); - if(req != null && wave == req.wave + 1 && req.isComplete()){ - Events.fire(new ZoneRequireCompleteEvent(zone, this)); - } - } + public void updateObjectives(Runnable closure){ + Array incomplete = content.zones() + .map(z -> z.requirements).flatten() + .select(o -> o.zone() == this && !o.complete()) + .as(ZoneObjective.class); - if(wave == configureWave + 1){ - Events.fire(new ZoneConfigureCompleteEvent(this)); + boolean wasConfig = configureObjective.complete(); + + closure.run(); + for(ZoneObjective objective : incomplete){ + if(objective.complete()){ + Events.fire(new ZoneRequireCompleteEvent(objective.zone, content.zones().find(z -> z.requirements.contains(objective)), objective)); } } + + if(!wasConfig && configureObjective.complete()){ + Events.fire(new ZoneConfigureCompleteEvent(this)); + } } public int bestWave(){ @@ -171,7 +154,7 @@ public class Zone extends UnlockableContent{ } stacks.sort(); - launchCost = stacks.toArray(ItemStack.class); + launchCost = stacks; Core.settings.putObject(name + "-starting-items", startingItems); data.modified(); } @@ -183,13 +166,13 @@ public class Zone extends UnlockableContent{ } public boolean canConfigure(){ - return bestWave() >= configureWave; + return configureObjective.complete(); } @Override public void init(){ generator.init(loadout); - Arrays.sort(resources); + resources.sort(); for(ItemStack stack : startingItems){ defaultStartingItems.add(new ItemStack(stack.item, stack.amount)); @@ -227,40 +210,4 @@ public class Zone extends UnlockableContent{ return ContentType.zone; } - /* - public static class ZoneRequirement{ - - public @NonNull Zone zone; - public int wave; - - public ZoneRequirement(Zone zone, int wave){ - this.zone = zone; - this.wave = wave; - } - - public ZoneRequirement(Zone zone){ - this.zone = zone; - } - - protected ZoneRequirement(){ - - } - - public boolean isComplete(){ - if(zone.getRules().attackMode){ - return zone.hasLaunched(); - }else{ - return zone.bestWave() >= wave; - } - } - - public static ZoneRequirement[] with(Object... objects){ - ZoneRequirement[] out = new ZoneRequirement[objects.length / 2]; - for(int i = 0; i < objects.length; i += 2){ - out[i / 2] = new ZoneRequirement((Zone)objects[i], (Integer)objects[i + 1]); - } - return out; - } - }*/ - } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java index a2b6ab1159..00d9db0cd0 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.ui.dialogs; import io.anuke.arc.*; +import io.anuke.arc.collection.*; import io.anuke.arc.function.*; import io.anuke.arc.graphics.*; import io.anuke.arc.scene.style.*; @@ -34,9 +35,14 @@ public class CustomRulesDialog extends FloatingDialog{ banDialog.shown(this::rebuildBanned); banDialog.buttons.addImageTextButton("$addall", Icon.arrow16Small, () -> { - rules.bannedBlocks.addAll(content.blocks()); + rules.bannedBlocks.addAll(content.blocks().select(Block::isBuildable)); rebuildBanned(); - }).size(210f, 64f); + }).size(180, 64f); + + banDialog.buttons.addImageTextButton("$clear", Icon.trash16Small, () -> { + rules.bannedBlocks.clear(); + rebuildBanned(); + }).size(180, 64f); setFillParent(true); shown(this::setup); @@ -44,40 +50,58 @@ public class CustomRulesDialog extends FloatingDialog{ } private void rebuildBanned(){ - float previousScroll = banDialog.getChildren().isEmpty() ? 0f : ((ScrollPane)banDialog.getChildren().first()).getScrollY(); + float previousScroll = banDialog.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)banDialog.cont.getChildren().first()).getScrollY(); banDialog.cont.clear(); banDialog.cont.pane(t -> { t.margin(10f); - for(Block block : rules.bannedBlocks){ - t.table(Styles.flatOver, b -> { - b.left().margin(4f); - b.addImage(block.icon(Cicon.medium)); - b.add(block.localizedName).padLeft(3).growX().left().wrap(); - b.addImageButton(Icon.cancelSmall, () -> { + if(rules.bannedBlocks.isEmpty()){ + t.add("$empty"); + } + + Array array = Array.with(rules.bannedBlocks); + array.sort(); + + int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3; + int i = 0; + + for(Block block : array){ + t.table(Tex.underline, b -> { + b.left().margin(4f); + b.addImage(block.icon(Cicon.medium)).size(Cicon.medium.size).padRight(3); + b.add(block.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap(); + + b.addImageButton(Icon.cancelSmall, Styles.clearPartiali, () -> { rules.bannedBlocks.remove(block); rebuildBanned(); }).size(70f).pad(-4f).padLeft(0f); - }).size(300f, 70f); - t.row(); + }).size(300f, 70f).padRight(5); + + if(++i % cols == 0){ + t.row(); + } } }).get().setScrollYForce(previousScroll); banDialog.cont.row(); banDialog.cont.addImageTextButton("$add", Icon.addSmall, () -> { FloatingDialog dialog = new FloatingDialog("$add"); - content.blocks().each(b -> !rules.bannedBlocks.contains(b), b -> { - int cols = mobile && Core.graphics.isPortrait() ? 4 : 8; - int i = 0; - dialog.cont.addImageButton(new TextureRegionDrawable(b.icon(Cicon.medium)), Styles.cleari, () -> { - rules.bannedBlocks.add(b); - rebuildBanned(); - dialog.hide(); - }).size(80f); + dialog.cont.pane(t -> { + t.left().margin(14f); + int[] i = {0}; + content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.isBuildable(), b -> { + int cols = mobile && Core.graphics.isPortrait() ? 4 : 12; + t.addImageButton(new TextureRegionDrawable(b.icon(Cicon.medium)), Styles.cleari, () -> { + rules.bannedBlocks.add(b); + rebuildBanned(); + dialog.hide(); + }).size(60f).get().resizeImage(Cicon.medium.size); - if(++i % cols == 0){ - dialog.cont.row(); - } + if(++i[0] % cols == 0){ + t.row(); + } + }); }); + dialog.addCloseButton(); dialog.show(); }).size(300f, 64f); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java index c04816139b..2f8a1d804b 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/DeployDialog.java @@ -21,9 +21,7 @@ import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.io.SaveIO.*; import io.anuke.mindustry.type.*; -import io.anuke.mindustry.type.Zone.*; import io.anuke.mindustry.ui.*; -import io.anuke.mindustry.ui.Styles; import io.anuke.mindustry.ui.TreeLayout.*; import static io.anuke.mindustry.Vars.*; @@ -156,7 +154,7 @@ public class DeployDialog extends FloatingDialog{ node.allChildren.clear(); node.allChildren.addAll(node.children); for(ZoneNode other : new ObjectSetIterator<>(nodes)){ - if(Structs.contains(other.zone.zoneRequirements, req -> req.zone == node.zone)){ + if(other.zone.requirements.contains(req -> req.zone() == node.zone)){ node.allChildren.add(other); } } @@ -164,12 +162,7 @@ public class DeployDialog extends FloatingDialog{ } boolean hidden(Zone zone){ - for(ZoneRequirement other : zone.zoneRequirements){ - if(!data.isUnlocked(other.zone)){ - return true; - } - } - return false; + return zone.requirements.contains(o -> o.zone() != null && o.zone().locked()); } void buildButton(Zone zone, Button button){ @@ -258,7 +251,7 @@ public class DeployDialog extends FloatingDialog{ //this.height /= 2f; nodes.add(this); - arr.selectFrom(content.zones(), other -> other.zoneRequirements.length > 0 && other.zoneRequirements[0].zone == zone); + arr.selectFrom(content.zones(), other -> other.requirements.size > 0 && other.requirements.first().zone() == zone); children = new ZoneNode[arr.size]; for(int i = 0; i < children.length; i++){ diff --git a/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java index e2827648de..cf65340627 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/ZoneInfoDialog.java @@ -1,15 +1,15 @@ package io.anuke.mindustry.ui.dialogs; import io.anuke.arc.*; +import io.anuke.arc.collection.*; import io.anuke.arc.graphics.*; import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.layout.*; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Objectives.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.type.*; -import io.anuke.mindustry.type.Zone.*; -import io.anuke.mindustry.world.*; import static io.anuke.mindustry.Vars.*; @@ -38,8 +38,7 @@ public class ZoneInfoDialog extends FloatingDialog{ if(!zone.unlocked()) return; - ItemStack[] stacks = zone.getLaunchCost(); - for(ItemStack stack : stacks){ + for(ItemStack stack : zone.getLaunchCost()){ if(stack.amount == 0) continue; if(i++ % 2 == 0){ @@ -62,31 +61,32 @@ public class ZoneInfoDialog extends FloatingDialog{ cont.table(req -> { req.defaults().left(); - if(zone.zoneRequirements.length > 0){ + Array zones = zone.requirements.select(o -> !(o instanceof Unlock)); + + if(!zones.isEmpty()){ req.table(r -> { r.add("$complete").colspan(2).left(); r.row(); - for(ZoneRequirement zreq : zone.zoneRequirements){ + for(Objective o : zones){ r.addImage(Icon.terrain).padRight(4); - r.add(!zreq.zone.getRules().attackMode ? - Core.bundle.format("zone.requirement.wave", zreq.wave, zreq.zone.localizedName()) : - Core.bundle.format("zone.requirement", zreq.zone.localizedName)).color(Color.lightGray); - r.addImage(zreq.isComplete() ? Icon.checkSmall : Icon.cancelSmall, zreq.isComplete() ? Color.lightGray : Color.scarlet).padLeft(3); + r.add(o.display()).color(Color.lightGray); + r.addImage(o.complete() ? Icon.checkSmall : Icon.cancelSmall, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3); r.row(); } }); } req.row(); + Array blocks = zone.requirements.select(o -> o instanceof Unlock).as(Unlock.class); - if(zone.blockRequirements.length > 0){ + if(!blocks.isEmpty()){ req.table(r -> { r.add("$research.list").colspan(2).left(); r.row(); - for(Block block : zone.blockRequirements){ - r.addImage(block.icon(Cicon.small)).size(8 * 3).padRight(5); - r.add(block.localizedName).color(Color.lightGray).left(); - r.addImage(data.isUnlocked(block) ? Icon.checkSmall : Icon.cancelSmall, data.isUnlocked(block) ? Color.lightGray : Color.scarlet).padLeft(3); + for(Unlock blocko : blocks){ + r.addImage(blocko.content.icon(Cicon.small)).size(8 * 3).padRight(5); + r.add(blocko.content.localizedName).color(Color.lightGray).left(); + r.addImage(blocko.content.unlocked() ? Icon.checkSmall : Icon.cancelSmall, blocko.content.unlocked() ? Color.lightGray : Color.scarlet).padLeft(3); r.row(); } @@ -108,7 +108,7 @@ public class ZoneInfoDialog extends FloatingDialog{ t.left(); t.add("$zone.resources").padRight(6); - if(zone.resources.length > 0){ + if(zone.resources.size > 0){ t.table(r -> { t.left(); int i = 0; @@ -137,7 +137,7 @@ public class ZoneInfoDialog extends FloatingDialog{ cont.row(); - cont.addButton(zone.canConfigure() ? "$configure" : Core.bundle.format("configure.locked", zone.configureWave), + 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) ).fillX().pad(3).disabled(b -> !zone.canConfigure()); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java b/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java index d7231f9878..c21f08a4e2 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/PlacementFragment.java @@ -153,8 +153,7 @@ public class PlacementFragment extends Fragment{ button.setChecked(control.input.block == block); if(state.rules.bannedBlocks.contains(block)){ - button.getStyle().imageUp = Icon.cancelSmall; - button.forEach(elem -> elem.setColor(Color.gray)); + button.forEach(elem -> elem.setColor(Color.darkGray)); } }); @@ -231,6 +230,15 @@ public class PlacementFragment extends Fragment{ } }).growX().left().margin(3); + if(state.rules.bannedBlocks.contains(lastDisplay)){ + topTable.row(); + topTable.table(b -> { + b.addImage(Icon.cancelSmall).padRight(2).color(Color.scarlet); + b.add("$banned"); + b.left(); + }).padTop(2).left(); + } + }else if(tileDisplayBlock() != null){ //show selected tile lastDisplay = tileDisplayBlock(); topTable.table(t -> { @@ -309,7 +317,7 @@ public class PlacementFragment extends Fragment{ returnArray.sort((b1, b2) -> { int locked = -Boolean.compare(unlocked(b1), unlocked(b2)); if(locked != 0) return locked; - return -Boolean.compare(state.rules.bannedBlocks.contains(b1), state.rules.bannedBlocks.contains(b2)); + return Boolean.compare(state.rules.bannedBlocks.contains(b1), state.rules.bannedBlocks.contains(b2)); }); return returnArray; } diff --git a/gradle.properties b/gradle.properties index baf26c820e..6ccbc668ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=9f567f04a005ea8b4132b31d64f325ec105def5e +archash=feb8c35e143c0a048488341ae916c7732ea7a84e