diff --git a/annotations/src/io/anuke/annotations/Annotations.java b/annotations/src/io/anuke/annotations/Annotations.java index 4f8381d177..feb4dc0ec9 100644 --- a/annotations/src/io/anuke/annotations/Annotations.java +++ b/annotations/src/io/anuke/annotations/Annotations.java @@ -5,10 +5,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Goal: To create a system to send events to the server from the client and vice versa, without creating a new packet type each time.
- * These events may optionally also trigger on the caller client/server as well.
- */ public class Annotations{ /** Marks a class as serializable.*/ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 41c3cfe348..d6b2bda7b7 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -21,7 +21,7 @@ text.level.delete.title=Confirm Delete text.map.delete=Are you sure you want to delete the map "[orange]{0}[]"? text.level.select=Level Select text.level.mode=Gamemode: -text.construction.desktop=Desktop controls have been changed.\nTo deselect a block or stop building, [accent]use space[]. +text.construction.desktop=To deselect a block or stop building, [accent]use space[]. text.construction.title=Block Construction Guide text.construction=\ You've just selected [accent]block construction mode[].\n\n\ @@ -58,11 +58,20 @@ text.sector.resume=Resume text.sector.locked=[scarlet][[Incomplete] text.sector.unexplored=[accent][[Unexplored] text.mission=Mission:[LIGHT_GRAY] {0} +text.mission.info=Mission Info text.mission.complete=Mission complete! text.mission.complete.body=Sector {0},{1} has been conquered. -text.mission.wave=Survive [accent]{0}[] waves. -text.mission.battle=Destroy the enemy base. -text.mission.resource=Obtain {0} x{1} +text.mission.wave=Survive[accent] {0}/{1} []waves\nWave in {2} +text.mission.wave.enemies=Survive[accent] {0}/{1} []waves\n{2} Enemies +text.mission.wave.enemy=Survive[accent] {0}/{1} []waves\n{2} Enemy +text.mission.wave.menu=Survive[accent] {0} []waves +text.mission.battle=Destroy the enemy core +text.mission.resource.menu=Obtain {0} x{1} +text.mission.resource=Obtain {0}:\n[accent]{1}/{2}[] +text.mission.block=Create {0} +text.mission.unit=Create {0} Unit +text.mission.linknode=Link Power Node +text.mission.display=[accent]Mission:\n[LIGHT_GRAY]{0} text.none= text.close=Close text.quit=Quit @@ -276,7 +285,7 @@ text.settings.clearall=Clear All text.paused=Paused text.yes=Yes text.no=No -text.info.title=[accent]Info +text.info.title=Info text.error.title=[crimson]An error has occured text.error.crashtitle=An error has occured text.blocks.blockinfo=Block Info @@ -596,9 +605,9 @@ block.spirit-factory.name=Spirit Drone Factory block.phantom-factory.name=Phantom Drone Factory block.wraith-factory.name=Wraith Fighter Factory block.ghoul-factory.name=Ghoul Bomber Factory -block.dagger-factory.name=Dagger Factory -block.titan-factory.name=Titan Factory -block.revenant-factory.name=Revenant Factory +block.dagger-factory.name=Dagger Mech Factory +block.titan-factory.name=Titan Mech Factory +block.revenant-factory.name=Revenant Fighter Factory block.repair-point.name=Repair Point block.resupply-point.name=Resupply Point block.pulse-conduit.name=Pulse Conduit @@ -611,7 +620,7 @@ block.rotary-pump.name=Rotary Pump block.thorium-reactor.name=Thorium Reactor block.command-center.name=Command Center block.mass-driver.name=Mass Driver -block.blast-drill.name=Blast Drill +block.blast-drill.name=Airblast Drill block.thermal-pump.name=Thermal Pump block.thermal-generator.name=Thermal Generator block.alloy-smelter.name=Alloy Smelter @@ -645,3 +654,25 @@ unit.fortress.name=Fortress unit.fortress.description=A heavy artillery ground unit. unit.revenant.name=Revenant unit.revenant.description=A heavy laser platform. + +tutorial.begin=Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill=Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor=[accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper=More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret=Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret=Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves=The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead=More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter=Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy=The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter=The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill=Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator=This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill=Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node=Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink=Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon=Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory=Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router=Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger=Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle=The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. \ No newline at end of file diff --git a/core/assets/ui/uiskin.json b/core/assets/ui/uiskin.json index 2b3645775a..aadef91b12 100644 --- a/core/assets/ui/uiskin.json +++ b/core/assets/ui/uiskin.json @@ -41,6 +41,7 @@ TintedDrawable: { loadDim: {name: white, color: {r: 0, g: 0, b: 0, a: 0.8} }, chatfield: {name: white, color: {r: 0, g: 0, b: 0, a: 0.2}}, clear: {name: white, color: {r: 0.1, g: 0.1, b: 0.1, a: 0.75}}, + none: {name: white, color: {r: 0, g: 0, b: 0, a: 0}}, clear-over: {name: white, color: {r: 1, g: 1, b: 1, a: 0.2} }, clear-down: {name: white, color: {r: 1, g: 1, b: 1, a: 0.4} } }, @@ -64,7 +65,7 @@ ImageButtonStyle: { static: {up: button }, static-down: {up: button-down }, toggle: {checked: button-down, down: button-down, up: button, imageDisabledColor: gray, imageUpColor: white }, - select: {checked: button-select, up: clear }, + select: {checked: button-select, up: none }, clear: {down: clear-down, up: clear, over: clear-over}, }, ScrollPaneStyle: { diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 6376266c97..30bb9cece1 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -36,19 +36,19 @@ public class Vars{ //time between waves in frames (on normal mode) public static final float wavespace = 60 * 60 * 1.5f; - public static final float mineTransferRange = 310f; + public static final float mineTransferRange = 220f; //set ridiculously high for now public static final float coreBuildRange = 999999f; //team of the player by default public static final Team defaultTeam = Team.blue; //team of the enemy in waves public static final Team waveTeam = Team.red; - public static final float unlockResourceScaling = 1.5f; + public static final float unlockResourceScaling = 1f; public static final int maxTextLength = 150; public static final int maxNameLength = 40; public static final float itemSize = 5f; public static final int tilesize = 8; - public static final int sectorSize = 300; + public static final int sectorSize = 120; public static final int mapPadding = 3; public static final int invalidSector = Integer.MAX_VALUE; public static Locale[] locales; diff --git a/core/src/io/anuke/mindustry/ai/Pathfinder.java b/core/src/io/anuke/mindustry/ai/Pathfinder.java index 0e141cca5d..a00a7e756d 100644 --- a/core/src/io/anuke/mindustry/ai/Pathfinder.java +++ b/core/src/io/anuke/mindustry/ai/Pathfinder.java @@ -40,6 +40,10 @@ public class Pathfinder{ }); } + public void activateTeamPath(Team team){ + createFor(team); + } + public void update(){ if(Net.client()) return; diff --git a/core/src/io/anuke/mindustry/content/Recipes.java b/core/src/io/anuke/mindustry/content/Recipes.java index 9719c26bc0..cedd512d39 100644 --- a/core/src/io/anuke/mindustry/content/Recipes.java +++ b/core/src/io/anuke/mindustry/content/Recipes.java @@ -14,17 +14,17 @@ public class Recipes implements ContentList{ @Override public void load(){ //DEBUG - new Recipe(distribution, DebugBlocks.itemSource).setMode(GameMode.sandbox).setHidden(true); - new Recipe(distribution, DebugBlocks.itemVoid).setMode(GameMode.sandbox).setHidden(true); - new Recipe(liquid, DebugBlocks.liquidSource).setMode(GameMode.sandbox).setHidden(true); - new Recipe(power, DebugBlocks.powerVoid).setMode(GameMode.sandbox).setHidden(true); - new Recipe(power, DebugBlocks.powerInfinite).setMode(GameMode.sandbox).setHidden(true); + new Recipe(distribution, DebugBlocks.itemSource).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true); + new Recipe(distribution, DebugBlocks.itemVoid).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true); + new Recipe(liquid, DebugBlocks.liquidSource).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true); + new Recipe(power, DebugBlocks.powerVoid).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true); + new Recipe(power, DebugBlocks.powerInfinite).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true); //DEFENSE //walls - new Recipe(defense, DefenseBlocks.copperWall, new ItemStack(Items.copper, 12)); - new Recipe(defense, DefenseBlocks.copperWallLarge, new ItemStack(Items.copper, 12 * 4)); + new Recipe(defense, DefenseBlocks.copperWall, new ItemStack(Items.copper, 12)).setAlwaysUnlocked(true); + new Recipe(defense, DefenseBlocks.copperWallLarge, new ItemStack(Items.copper, 12 * 4)).setAlwaysUnlocked(true); new Recipe(defense, DefenseBlocks.denseAlloyWall, new ItemStack(Items.densealloy, 12)); new Recipe(defense, DefenseBlocks.denseAlloyWallLarge, new ItemStack(Items.densealloy, 12 * 4)); @@ -51,7 +51,7 @@ public class Recipes implements ContentList{ .setDependencies(Items.blastCompound); //TURRETS - new Recipe(weapon, TurretBlocks.duo, new ItemStack(Items.copper, 40)); + new Recipe(weapon, TurretBlocks.duo, new ItemStack(Items.copper, 40)).setAlwaysUnlocked(true); new Recipe(weapon, TurretBlocks.arc, new ItemStack(Items.copper, 50), new ItemStack(Items.lead, 30), new ItemStack(Items.silicon, 20)); new Recipe(weapon, TurretBlocks.hail, new ItemStack(Items.copper, 60), new ItemStack(Items.densealloy, 35)); new Recipe(weapon, TurretBlocks.lancer, new ItemStack(Items.copper, 50), new ItemStack(Items.lead, 100), new ItemStack(Items.silicon, 90)); @@ -65,13 +65,13 @@ public class Recipes implements ContentList{ new Recipe(weapon, TurretBlocks.meltdown, new ItemStack(Items.copper, 500), new ItemStack(Items.lead, 700), new ItemStack(Items.densealloy, 600), new ItemStack(Items.surgealloy, 650), new ItemStack(Items.silicon, 650)); //DISTRIBUTION - new Recipe(distribution, DistributionBlocks.conveyor, new ItemStack(Items.copper, 1)); + new Recipe(distribution, DistributionBlocks.conveyor, new ItemStack(Items.copper, 1)).setAlwaysUnlocked(true); new Recipe(distribution, DistributionBlocks.titaniumconveyor, new ItemStack(Items.copper, 2), new ItemStack(Items.titanium, 1)); new Recipe(distribution, DistributionBlocks.phaseConveyor, new ItemStack(Items.phasematter, 10), new ItemStack(Items.silicon, 15), new ItemStack(Items.lead, 20), new ItemStack(Items.densealloy, 20)); //starter lead transportation - new Recipe(distribution, DistributionBlocks.junction, new ItemStack(Items.copper, 2)); - new Recipe(distribution, DistributionBlocks.router, new ItemStack(Items.copper, 6)); + new Recipe(distribution, DistributionBlocks.junction, new ItemStack(Items.copper, 2)).setAlwaysUnlocked(true); + new Recipe(distribution, DistributionBlocks.router, new ItemStack(Items.copper, 6)).setAlwaysUnlocked(true); //advanced densealloy transporation new Recipe(distribution, DistributionBlocks.distributor, new ItemStack(Items.densealloy, 8), new ItemStack(Items.copper, 8)); @@ -140,7 +140,7 @@ public class Recipes implements ContentList{ ); //DRILLS, PRODUCERS - new Recipe(production, ProductionBlocks.mechanicalDrill, new ItemStack(Items.copper, 50)); + new Recipe(production, ProductionBlocks.mechanicalDrill, new ItemStack(Items.copper, 45)).setAlwaysUnlocked(true); new Recipe(production, ProductionBlocks.pneumaticDrill, new ItemStack(Items.copper, 60), new ItemStack(Items.densealloy, 50)); new Recipe(production, ProductionBlocks.laserdrill, new ItemStack(Items.copper, 70), new ItemStack(Items.densealloy, 90), new ItemStack(Items.silicon, 60), new ItemStack(Items.titanium, 50)); new Recipe(production, ProductionBlocks.blastdrill, new ItemStack(Items.copper, 130), new ItemStack(Items.densealloy, 180), new ItemStack(Items.silicon, 120), new ItemStack(Items.titanium, 100), new ItemStack(Items.thorium, 60)); diff --git a/core/src/io/anuke/mindustry/content/UnitTypes.java b/core/src/io/anuke/mindustry/content/UnitTypes.java index b7c79e7860..2d981f7597 100644 --- a/core/src/io/anuke/mindustry/content/UnitTypes.java +++ b/core/src/io/anuke/mindustry/content/UnitTypes.java @@ -40,7 +40,7 @@ public class UnitTypes implements ContentList{ speed = 0.2f; maxVelocity = 0.8f; range = 50f; - healSpeed = 0.25f; + healSpeed = 0.22f; health = 60; }}; @@ -50,7 +50,7 @@ public class UnitTypes implements ContentList{ drag = 0.4f; range = 40f; weapon = Weapons.chainBlaster; - health = 150; + health = 130; }}; titan = new UnitType("titan", Titan.class, Titan::new){{ @@ -108,7 +108,7 @@ public class UnitTypes implements ContentList{ health = 220; buildPower = 0.9f; minePower = 1.1f; - healSpeed = 0.55f; + healSpeed = 0.5f; toMine = ObjectSet.with(Items.lead, Items.copper, Items.titanium); }}; } diff --git a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java index abaaaf2a0c..7839c41d06 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java @@ -129,7 +129,6 @@ public class DebugBlocks extends BlockList implements ContentList{ Table cont = new Table(); for(int i = 0; i < items.size; i++){ - if(i == 0) continue; final int f = i; ImageButton button = cont.addImageButton("liquid-icon-" + items.get(i).name, "toggle", 24, () -> { Call.setLiquidSourceLiquid(null, tile, items.get(f)); diff --git a/core/src/io/anuke/mindustry/content/blocks/StorageBlocks.java b/core/src/io/anuke/mindustry/content/blocks/StorageBlocks.java index dee532688d..acf1f63d30 100644 --- a/core/src/io/anuke/mindustry/content/blocks/StorageBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/StorageBlocks.java @@ -12,7 +12,7 @@ public class StorageBlocks extends BlockList implements ContentList{ @Override public void load(){ core = new CoreBlock("core"){{ - health = 1400; + health = 1100; }}; vault = new Vault("vault"){{ diff --git a/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java b/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java index 708b045bff..31e06841b8 100644 --- a/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java @@ -63,7 +63,7 @@ public class UnitBlocks extends BlockList implements ContentList{ produceTime = 1700; size = 2; consumes.power(0.05f); - consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 10), new ItemStack(Items.copper, 10)}); + consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 10)}); }}; titanFactory = new UnitFactory("titan-factory"){{ diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 61246c4245..0720fc4c24 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -13,6 +13,7 @@ import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.game.Content; import io.anuke.mindustry.game.ContentDatabase; import io.anuke.mindustry.game.EventType.*; +import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.Saves; import io.anuke.mindustry.input.DefaultKeybinds; import io.anuke.mindustry.input.DesktopInput; @@ -315,29 +316,20 @@ public class Control extends Module{ Platform.instance.updateRPC(); - if(!Settings.has("4.0-warning")){ - Settings.putBool("4.0-warning", true); + if(!Settings.getBool("4.0-warning-2", false)){ Timers.run(5f, () -> { FloatingDialog dialog = new FloatingDialog("[orange]WARNING![]"); - dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f); - dialog.content().add("The beta version you are about to play should be considered very unstable, and is [accent]not representative of the final 4.0 release.[]\n\n " + - "A large portion of content is still unimplemented. \nAll current art and UI is temporary, and will be re-drawn before release. " + - "\n\n[accent]Saves and maps may be corrupted without warning between updates.[] You have been warned!").wrap().width(400f); + dialog.buttons().addButton("$text.ok", () -> { + dialog.hide(); + Settings.putBool("4.0-warning-2", true); + Settings.save(); + }).size(100f, 60f); + dialog.content().add("Reminder: The beta version you are about to play is very unstable, and is [accent]not representative of the final 4.0 release.[]\n\n " + + "\nThere is currently[scarlet] no sound implemented[]; this is intentional.\n" + + "All current art and UI is temporary, and will be re-drawn before release. " + + "\n\n[accent]Saves and maps may be corrupted without warning between updates.").wrap().width(400f); dialog.show(); - - }); - } - - if(!Settings.has("4.0-no-sound")){ - Settings.putBool("4.0-no-sound", true); - - Timers.run(4f, () -> { - FloatingDialog dialog = new FloatingDialog("[orange]Attention![]"); - dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f); - dialog.content().add("You might have noticed that 4.0 does not have any sound.\nThis is [orange]intentional![] Sound will be added in a later update.\n\n[LIGHT_GRAY](now stop reporting this as a bug)").wrap().width(400f); - dialog.show(); - }); } } @@ -349,6 +341,32 @@ public class Control extends Module{ } } + private void updateSectors(){ + if(world.getSector() == null) return; + + world.getSector().currentMission().update(); + + //TODO move sector code into logic class + //check unlocked sectors + while(!world.getSector().complete && world.getSector().currentMission().isComplete()){ + world.getSector().currentMission().onComplete(); + world.getSector().completedMissions ++; + + state.mode = world.getSector().currentMission().getMode(); + world.getSector().currentMission().onBegin(); + world.sectors().save(); + } + + //check if all assigned missions are complete + if(!world.getSector().complete && world.getSector().completedMissions >= world.getSector().missions.size){ + state.mode = GameMode.victory; + + world.sectors().completeSector(world.getSector().x, world.getSector().y); + world.sectors().save(); + ui.missions.show(world.getSector()); + } + } + @Override public void update(){ @@ -374,19 +392,7 @@ public class Control extends Module{ Platform.instance.updateRPC(); } - //check unlocked sectors - if(world.getSector() != null && !world.getSector().complete){ - //all assigned missions are complete - if(world.getSector().completedMissions >= world.getSector().missions.size){ - - world.sectors().completeSector(world.getSector().x, world.getSector().y); - world.sectors().save(); - ui.missions.show(world.getSector()); - }else if(world.getSector().currentMission().isComplete()){ - //increment completed missions, check next index next frame - world.getSector().completedMissions ++; - } - } + updateSectors(); //check unlocks every 2 seconds if(world.getSector() != null && Timers.get("timerCheckUnlock", 120)){ diff --git a/core/src/io/anuke/mindustry/core/Logic.java b/core/src/io/anuke/mindustry/core/Logic.java index c04577612f..9441767bf5 100644 --- a/core/src/io/anuke/mindustry/core/Logic.java +++ b/core/src/io/anuke/mindustry/core/Logic.java @@ -132,7 +132,7 @@ public class Logic extends Module{ Entities.update(fireGroup); Entities.update(playerGroup); - //effect group only contains item drops in the headless version, update it! + //effect group only contains item transfers in the headless version, update it! if(headless){ Entities.update(effectGroup); } diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index 6ed35d773f..835a603c7c 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -210,11 +210,11 @@ public class UI extends SceneModule{ } } - public void loadAnd(Runnable call){ - loadAnd("$text.loading", call); + public void loadGraphics(Runnable call){ + loadGraphics("$text.loading", call); } - public void loadAnd(String text, Runnable call){ + public void loadGraphics(String text, Runnable call){ loadfrag.show(text); Timers.runTask(7f, () -> { call.run(); diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index 16fd6f710e..bf3f3002b3 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -15,6 +15,7 @@ import io.anuke.mindustry.maps.*; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.maps.generation.WorldGenerator; +import io.anuke.mindustry.world.blocks.OreBlock; import io.anuke.ucore.core.Events; import io.anuke.ucore.core.Timers; import io.anuke.ucore.entities.EntityPhysics; @@ -195,6 +196,12 @@ public class World extends Module{ generating = true; } + /**Call to signal the beginning of loading the map with a custom set of tiles.*/ + public void beginMapLoad(Tile[][] tiles){ + this.tiles = tiles; + generating = true; + } + /** * Call to signify the end of map loading. Updates tile occlusions and sets up physics for the world. * A WorldLoadEvent will be fire. @@ -202,10 +209,15 @@ public class World extends Module{ public void endMapLoad(){ for(int x = 0; x < tiles.length; x++){ for(int y = 0; y < tiles[0].length; y++){ - tiles[x][y].updateOcclusion(); + Tile tile = tiles[x][y]; + tile.updateOcclusion(); - if(tiles[x][y].entity != null){ - tiles[x][y].entity.updateProximity(); + if(tile.floor() instanceof OreBlock && tile.hasCliffs()){ + tile.setFloor(((OreBlock) tile.floor()).base); + } + + if(tile.entity != null){ + tile.entity.updateProximity(); } } } @@ -232,7 +244,7 @@ public class World extends Module{ beginMapLoad(); - int width = sectorSize * sector.size, height = sectorSize * sector.size; + int width = sectorSize * sector.width, height = sectorSize * sector.height; Tile[][] tiles = createTiles(width, height); @@ -322,6 +334,15 @@ public class World extends Module{ } } + public int transform(int packed, int oldWidth, int oldHeight, int newWidth, int shiftX, int shiftY){ + int x = packed % oldWidth; + int y = packed / oldWidth; + if(!Mathf.inBounds(x, y, oldWidth, oldHeight)) return -1; + x += shiftX; + y += shiftY; + return y*newWidth + x; + } + /** * Raycast, but with world coordinates. */ diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java index 472167176d..552ba7f57e 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -95,7 +95,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ "$text.editor.importmap", "$text.editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show, "$text.editor.importfile", "$text.editor.importfile.description", "icon-file", (Runnable) () -> { Platform.instance.showFileChooser("$text.loadimage", "Map Files", file -> { - ui.loadAnd(() -> { + ui.loadGraphics(() -> { try{ DataInputStream stream = new DataInputStream(file.read()); @@ -116,7 +116,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ ui.showError("$text.web.unsupported"); }else { Platform.instance.showFileChooser("$text.loadimage", "Image Files", file -> { - ui.loadAnd(() -> { + ui.loadGraphics(() -> { try{ MapTileData data = MapIO.readPixmap(new Pixmap(file)); @@ -137,7 +137,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ Platform.instance.showFileChooser("$text.saveimage", "Map Files", file -> { file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); FileHandle result = file; - ui.loadAnd(() -> { + ui.loadGraphics(() -> { try{ if(!editor.getTags().containsKey("name")){ @@ -168,7 +168,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ Platform.instance.showFileChooser("$text.saveimage", "Image Files", file -> { file = file.parent().child(file.nameWithoutExtension() + ".png"); FileHandle result = file; - ui.loadAnd(() -> { + ui.loadGraphics(() -> { try{ Pixmaps.write(MapIO.generatePixmap(editor.getMap()), result); }catch (Exception e){ @@ -194,7 +194,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ resizeDialog = new MapResizeDialog(editor, (x, y) -> { if(!(editor.getMap().width() == x && editor.getMap().height() == y)){ - ui.loadAnd(() -> { + ui.loadGraphics(() -> { editor.resize(x, y); view.clearStack(); }); @@ -203,7 +203,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ loadDialog = new MapLoadDialog(map -> { - ui.loadAnd(() -> { + ui.loadGraphics(() -> { try(DataInputStream stream = new DataInputStream(map.stream.get())){ MapMeta meta = MapIO.readMapMeta(stream); MapTileData data = MapIO.readTileData(stream, meta, false); @@ -338,7 +338,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ } public void beginEditMap(InputStream is){ - ui.loadAnd(() -> { + ui.loadGraphics(() -> { try{ shownWithMap = true; DataInputStream stream = new DataInputStream(is); diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java index 1edf7165b6..c49dca1a6a 100644 --- a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java @@ -311,9 +311,6 @@ public interface BuilderTrait extends Entity{ Draw.alpha(0.3f + Mathf.absin(Timers.time(), 0.9f, 0.2f)); - Fill.tri(px, py, x2, y2, x1, y1); - Fill.tri(px, py, x2, y2, x3, y3); - Draw.alpha(1f); Lines.line(px, py, x1, y1); diff --git a/core/src/io/anuke/mindustry/entities/units/BaseUnit.java b/core/src/io/anuke/mindustry/entities/units/BaseUnit.java index cbe63c5118..7aac45600c 100644 --- a/core/src/io/anuke/mindustry/entities/units/BaseUnit.java +++ b/core/src/io/anuke/mindustry/entities/units/BaseUnit.java @@ -121,6 +121,10 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ this.spawner = tile.packedPosition(); } + public void setIntSpawner(int pos){ + this.spawner = pos; + } + /**Sets this to a 'wave' unit, which means it has slightly different AI and will not run out of ammo.*/ public void setWave(){ isWave = true; diff --git a/core/src/io/anuke/mindustry/game/ContentDatabase.java b/core/src/io/anuke/mindustry/game/ContentDatabase.java index 8ec95cfdbc..97c825964a 100644 --- a/core/src/io/anuke/mindustry/game/ContentDatabase.java +++ b/core/src/io/anuke/mindustry/game/ContentDatabase.java @@ -40,7 +40,7 @@ public class ContentDatabase{ * @return whether or not this content was newly unlocked. */ public boolean unlockContent(UnlockableContent content){ - if(!content.canBeUnlocked()) return false; + if(!content.canBeUnlocked() || content.alwaysUnlocked()) return false; if(!unlocked.containsKey(content.getContentType())){ unlocked.put(content.getContentType(), new ObjectSet<>()); diff --git a/core/src/io/anuke/mindustry/game/EventType.java b/core/src/io/anuke/mindustry/game/EventType.java index c6ed9f37fe..150e942fd9 100644 --- a/core/src/io/anuke/mindustry/game/EventType.java +++ b/core/src/io/anuke/mindustry/game/EventType.java @@ -42,9 +42,7 @@ public class EventType{ } - /** - * Called from the logic thread. Do not access graphics here! - */ + /**Called from the logic thread. Do not access graphics here!*/ public static class TileChangeEvent implements Event{ public final Tile tile; diff --git a/core/src/io/anuke/mindustry/game/GameMode.java b/core/src/io/anuke/mindustry/game/GameMode.java index 7d0de33ad9..13fa56e6a2 100644 --- a/core/src/io/anuke/mindustry/game/GameMode.java +++ b/core/src/io/anuke/mindustry/game/GameMode.java @@ -17,6 +17,13 @@ public enum GameMode{ enemyCheat = true; showPads = true; }}, + victory{{ + disableWaves = true; + hidden = true; + enemyCheat = false; + showPads = true; + showMission = false; + }}, pvp{{ showPads = true; disableWaves = true; @@ -26,7 +33,7 @@ public enum GameMode{ respawnTime = 60 * 10; }}; - public boolean infiniteResources, disableWaveTimer, disableWaves, hidden, enemyCheat, isPvp, showPads; + public boolean infiniteResources, disableWaveTimer, disableWaves, showMission = true, hidden, enemyCheat, isPvp, showPads; public float enemyCoreBuildRadius = 400f; public float respawnTime = 60 * 4; diff --git a/core/src/io/anuke/mindustry/game/Saves.java b/core/src/io/anuke/mindustry/game/Saves.java index dfe6c11fee..398a72b2ae 100644 --- a/core/src/io/anuke/mindustry/game/Saves.java +++ b/core/src/io/anuke/mindustry/game/Saves.java @@ -79,7 +79,7 @@ public class Saves{ if(time > Settings.getInt("saveinterval") * 60){ saving = true; - Timers.run(2f, () -> { + Timers.runTask(2f, () -> { try{ current.save(); }catch(Exception e){ diff --git a/core/src/io/anuke/mindustry/game/SpawnGroup.java b/core/src/io/anuke/mindustry/game/SpawnGroup.java index 63faefda7d..93cbc2a30d 100644 --- a/core/src/io/anuke/mindustry/game/SpawnGroup.java +++ b/core/src/io/anuke/mindustry/game/SpawnGroup.java @@ -85,7 +85,6 @@ public class SpawnGroup{ if(wave < begin || wave > end || (wave - begin) % spacing != 0){ return 0; } - float scaling = this.groupScaling; return Math.min(groupAmount - 1 + Math.max((int) ((wave / spacing) / groupScaling), 1), max); } diff --git a/core/src/io/anuke/mindustry/game/Waves.java b/core/src/io/anuke/mindustry/game/Waves.java index 6917ed4f1e..4da4c37701 100644 --- a/core/src/io/anuke/mindustry/game/Waves.java +++ b/core/src/io/anuke/mindustry/game/Waves.java @@ -13,7 +13,7 @@ public class Waves{ return Array.with( new SpawnGroup(UnitTypes.dagger){{ end = 8; - unitScaling = 1; + unitScaling = 3; }}, new SpawnGroup(UnitTypes.wraith){{ diff --git a/core/src/io/anuke/mindustry/graphics/FogRenderer.java b/core/src/io/anuke/mindustry/graphics/FogRenderer.java index 5f65bbbbae..031abfe8c1 100644 --- a/core/src/io/anuke/mindustry/graphics/FogRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/FogRenderer.java @@ -40,21 +40,40 @@ public class FogRenderer implements Disposable{ private Rectangle rect = new Rectangle(); private boolean dirty; + private boolean isOffseted; + private int offsettedX, offsettedY; + public FogRenderer(){ Events.on(WorldLoadGraphicsEvent.class, event -> { - dispose(); + if(!isOffseted){ + dispose(); + } padding = world.getSector() != null ? mapPadding + extraPadding : 0; shadowPadding = world.getSector() != null ? fshadowPadding : -1; + FrameBuffer lastBuffer = buffer; + buffer = new FrameBuffer(Format.RGBA8888, world.width() + padding*2, world.height() + padding*2, false); changeQueue.clear(); //clear buffer to black buffer.begin(); Graphics.clear(0, 0, 0, 1f); + + if(isOffseted){ + Core.batch.getProjectionMatrix().setToOrtho2D(-padding, -padding, buffer.getWidth(), buffer.getHeight()); + Core.batch.begin(); + Core.batch.draw(lastBuffer.getColorBufferTexture(), offsettedX, offsettedY + lastBuffer.getColorBufferTexture().getHeight(), + lastBuffer.getColorBufferTexture().getWidth(), -lastBuffer.getColorBufferTexture().getHeight()); + Core.batch.end(); + } buffer.end(); + if(isOffseted){ + lastBuffer.dispose(); + } + for(int x = 0; x < world.width(); x++){ for(int y = 0; y < world.height(); y++){ Tile tile = world.tile(x, y); @@ -66,6 +85,8 @@ public class FogRenderer implements Disposable{ pixelBuffer = ByteBuffer.allocateDirect(world.width() * world.height() * 4); dirty = true; + + isOffseted = false; }); Events.on(TileChangeEvent.class, event -> threads.runGraphics(() -> { @@ -75,6 +96,12 @@ public class FogRenderer implements Disposable{ })); } + public void setLoadingOffset(int x, int y){ + isOffseted = true; + offsettedX = x; + offsettedY = y; + } + public void writeFog(){ if(buffer == null) return; diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java index bd57cb5c0e..242d7ce543 100644 --- a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java @@ -28,6 +28,10 @@ public class OverlayRenderer{ for(Player player : players){ InputHandler input = control.input(player.playerIndex); + if(world.getSector() != null){ + world.getSector().currentMission().drawOverlay(); + } + if(!input.isDrawing() || player.isDead()) continue; Shaders.outline.color.set(Palette.accent); diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 1806ae81e2..d85f94db96 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -206,6 +206,10 @@ public class DesktopInput extends InputHandler{ return; } + if(Inputs.keyTap(section, "deselect")){ + player.setMineTile(null); + } + if(!ui.hasMouse()){ if(Inputs.keyTap(section, "select")){ if(isPlacing()){ diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index cbd9a3a803..ccef23d108 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -16,7 +16,7 @@ import java.util.zip.InflaterInputStream; import static io.anuke.mindustry.Vars.*; public class SaveIO{ - public static final IntArray breakingVersions = IntArray.with(47, 48, 49, 50, 51, 52); + public static final IntArray breakingVersions = IntArray.with(47, 48, 49, 50, 51, 52, 53, 54, 55, 56); public static final IntMap versions = new IntMap<>(); public static final Array versionArray = Array.with( new Save16() diff --git a/core/src/io/anuke/mindustry/maps/Sector.java b/core/src/io/anuke/mindustry/maps/Sector.java index 4299cb5497..1c07912dfc 100644 --- a/core/src/io/anuke/mindustry/maps/Sector.java +++ b/core/src/io/anuke/mindustry/maps/Sector.java @@ -7,7 +7,7 @@ import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Saves.SaveSlot; import io.anuke.mindustry.game.SpawnGroup; import io.anuke.mindustry.maps.missions.Mission; -import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.maps.missions.VictoryMission; import io.anuke.mindustry.type.ItemStack; import io.anuke.ucore.util.Bits; @@ -15,6 +15,8 @@ import static io.anuke.mindustry.Vars.control; @Serialize public class Sector{ + private static final Mission victoryMission = new VictoryMission(); + /**Position on the map, can be positive or negative.*/ public short x, y; /**Whether this sector has already been completed.*/ @@ -22,7 +24,7 @@ public class Sector{ /**Slot ID of this sector's save. -1 means no save has been created.*/ public int saveID = -1; /**Sector size; if more than 1, the coordinates are the bottom left corner.*/ - public int size = 1; + public int width = 1, height = 1; /**Num of missions in this sector that have been completed so far.*/ public int completedMissions; /**Display texture. Needs to be disposed.*/ @@ -31,8 +33,6 @@ public class Sector{ public transient Array missions = new Array<>(); /**Enemies spawned at this sector.*/ public transient Array spawns; - /**Ores that appear in this sector.*/ - public transient Array ores = new Array<>(); /**Difficulty of the sector, measured by calculating distance from origin and applying scaling.*/ public transient int difficulty; /**Items the player starts with on this sector.*/ @@ -41,7 +41,9 @@ public class Sector{ /**Returns scaled difficulty. This is not just the difficulty ordinal.*/ public Difficulty getDifficulty(){ if(difficulty == 0){ - return Difficulty.easy; + //yes, this means insane tutorial difficulty + //(((have fun))) + return Difficulty.hard; }else if(difficulty < 4){ return Difficulty.normal; }else if(difficulty < 9){ @@ -52,7 +54,7 @@ public class Sector{ } public Mission currentMission(){ - return missions.get(Math.min(completedMissions, missions.size - 1)); + return completedMissions >= missions.size ? victoryMission : missions.get(completedMissions); } public int getSeed(){ diff --git a/core/src/io/anuke/mindustry/maps/Sectors.java b/core/src/io/anuke/mindustry/maps/Sectors.java index 965bbccc1a..153d8681cf 100644 --- a/core/src/io/anuke/mindustry/maps/Sectors.java +++ b/core/src/io/anuke/mindustry/maps/Sectors.java @@ -3,19 +3,24 @@ package io.anuke.mindustry.maps; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.content.Items; import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.io.SaveIO; import io.anuke.mindustry.maps.generation.WorldGenerator.GenResult; import io.anuke.mindustry.maps.missions.BattleMission; +import io.anuke.mindustry.maps.missions.Mission; import io.anuke.mindustry.maps.missions.WaveMission; +import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.world.ColorMapper; -import io.anuke.mindustry.world.Edges; +import io.anuke.mindustry.world.Tile; import io.anuke.ucore.core.Settings; +import io.anuke.ucore.entities.Entities; +import io.anuke.ucore.entities.EntityGroup; +import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.GridMap; import io.anuke.ucore.util.Log; @@ -25,6 +30,7 @@ import static io.anuke.mindustry.Vars.*; public class Sectors{ private static final int sectorImageSize = 32; + private static final boolean checkExpansion = false; private static final float sectorLargeChance = 0.24f; private GridMap grid = new GridMap<>(); @@ -36,17 +42,22 @@ public class Sectors{ } if(!sector.hasSave()){ + for(Mission mission : sector.missions){ + mission.reset(); + } world.loadSector(sector); logic.play(); sector.saveID = control.getSaves().addSave("sector-" + sector.packedPosition()).index; world.sectors().save(); world.setSector(sector); + sector.currentMission().onBegin(); }else if(SaveIO.breakingVersions.contains(sector.getSave().getBuild())){ ui.showInfo("$text.save.old"); }else try{ sector.getSave().load(); world.setSector(sector); state.set(State.playing); + sector.currentMission().onBegin(); }catch(Exception e){ Log.err(e); sector.getSave().delete(); @@ -69,25 +80,145 @@ public class Sectors{ return grid.get(Bits.getLeftShort(position), Bits.getRightShort(position)); } + /**Tries to a sector in a specific direciton, specified by expandX and expandY. + * The player *must* currently be playing in this sector. + * If a sector is in that direction, this method will return false (failure) + * @param sector the sector to expand + * @param expandX spaces in X coordinate to expand, can be negative + * @param expandY spaces in Y coordinate to expand, can be negative*/ + public boolean expandSector(Sector sector, int expandX, int expandY){ + if(world.getSector() != sector){ + throw new IllegalArgumentException("Sector is not being played in!"); + } + + //remove old sector data to clear things up + for(int x = sector.x; x < sector.x+sector.width; x++){ + for(int y = sector.y; y < sector.y+sector.height; y++){ + grid.put(x, y, null); + } + } + + if(expandX < 0) sector.x += expandX; + if(expandY < 0) sector.y += expandY; + + sector.width += Math.abs(expandX); + sector.height += Math.abs(expandY); + + if(checkExpansion) { + for (int x = sector.x; x < sector.x + sector.width; x++) { + for (int y = sector.y; y < sector.y + sector.height; y++) { + if (grid.get(x, y) != null && grid.get(x, y).complete) { + //if a completed sector is hit, expansion failed + if (expandX < 0) sector.x -= expandX; + if (expandY < 0) sector.y -= expandY; + sector.width -= Math.abs(expandX); + sector.height -= Math.abs(expandY); + return false; + } + } + } + } + + //add new sector spaces + for(int x = sector.x; x < sector.x+sector.width; x++){ + for(int y = sector.y; y < sector.y+sector.height; y++){ + grid.put(x, y, sector); + } + } + + //sector map data should now be shifted and generated + int shiftX = expandX < 0 ? -expandX*sectorSize : 0; + int shiftY = expandY < 0 ? -expandY*sectorSize : 0; + + for(EntityGroup group : Entities.getAllGroups()){ + for(Entity entity : group.all()){ + entity.set(entity.getX() + shiftX * tilesize, entity.getY() + shiftY * tilesize); + + if(entity instanceof BaseUnit){ + Tile spawner = ((BaseUnit) entity).getSpawner(); + if(spawner == null) continue; + int i = spawner.packedPosition(); + ((BaseUnit) entity).setIntSpawner(world.transform(i, world.width(), world.height(), sector.width*sectorSize, shiftX, shiftY)); + } + } + } + + if(!headless){ + renderer.fog().setLoadingOffset(shiftX, shiftY); + } + + //create *new* tile array + Tile[][] newTiles = new Tile[sector.width * sectorSize][sector.height * sectorSize]; + + //shift existing tiles to new array + for (int x = 0; x < (sector.width - Math.abs(expandX))*sectorSize; x++) { + for (int y = 0; y < (sector.height - Math.abs(expandY))*sectorSize; y++) { + Tile tile = world.rawTile(x, y); + tile.x = (short)(x + shiftX); + tile.y = (short)(y + shiftY); + newTiles[x + shiftX][y + shiftY] = tile; + tile.block().transformLinks(tile, world.width(), world.height(), sector.width*sectorSize, sector.height*sectorSize, shiftX, shiftY); + } + } + + world.beginMapLoad(newTiles); + + //create new tiles + for (int sx = 0; sx < sector.width; sx++) { + for (int sy = 0; sy < sector.height; sy++) { + //if this sector is a 'new sector (not part of the current save data...) + if(sx < -expandX || sy < -expandY || sx >= sector.width - expandX || sy >= sector.height - expandY){ + GenResult result = new GenResult(); + Array ores = getOres(sx + sector.x, sy + sector.y); + //gen tiles in sector + for (int x = 0; x < sectorSize; x++) { + for (int y = 0; y < sectorSize; y++) { + world.generator().generateTile(result, sx + sector.x, sy + sector.y, x, y, true, null, ores); + newTiles[sx * sectorSize + x][sy * sectorSize + y] = new Tile(x + sx * sectorSize, y + sy*sectorSize, result.floor.id, result.wall.id, (byte)0, (byte)0, result.elevation); + } + } + } + } + } + + //end loading of map + world.endMapLoad(); + + threads.runGraphics(() -> createTexture(sector)); + + return true; + } + + public Array getOres(int x, int y){ + if(x == 0 && y == 0){ + return Array.with(Items.copper); + }else if(x == 1 && y == 0){ + return Array.with(Items.copper, Items.lead, Items.coal); + } + return Array.with(Items.copper); + } + /**Unlocks a sector. This shows nearby sectors.*/ public void completeSector(int x, int y){ createSector(x, y); Sector sector = get(x, y); sector.complete = true; - for(GridPoint2 point : Edges.getEdges(sector.size)){ - createSector(sector.x + point.x, sector.y + point.y); + for(int sx = 0; sx < sector.width + 2; sx++){ + for(int sy = 0; sy < sector.height + 2; sy++){ + if((sx == 0 || sy == 0 || sx == sector.width + 1 || sy == sector.height + 1) && + !((sx == 0 && sy == 0) + || (sx == 0 && sy == sector.height+1) + || (sx == sector.width+1 && sy == 0) + || (sx == sector.width+1 && sy == sector.height+1))){ + createSector(sector.x + sx - 1, sector.y + sy - 1); + } + } } } /**Creates a sector at a location if it is not present, but does not unlock it.*/ public void createSector(int x, int y){ - boolean isLarge = Mathf.randomSeed(3+Bits.packInt((short)round2(x), (short)round2(y))) < sectorLargeChance; - - if(isLarge){ - x = round2(x); - y = round2(y); - } if(grid.containsKey(x, y)) return; @@ -95,11 +226,11 @@ public class Sectors{ sector.x = (short)x; sector.y = (short)y; sector.complete = false; - sector.size = isLarge ? 2 : 1; + sector.width = sector.height = 1; initSector(sector); - for(int cx = 0; cx < sector.size; cx++){ - for(int cy = 0; cy < sector.size; cy++){ + for(int cx = 0; cx < sector.width; cx++){ + for(int cy = 0; cy < sector.height; cy++){ grid.put(x + cx, y + cy, sector); } } @@ -118,8 +249,8 @@ public class Sectors{ for(Sector sector : out){ createTexture(sector); initSector(sector); - for(int cx = 0; cx < sector.size; cx++){ - for(int cy = 0; cy < sector.size; cy++){ + for(int cx = 0; cx < sector.width; cx++){ + for(int cy = 0; cy < sector.height; cy++){ grid.put(sector.x + cx, sector.y + cy, sector); } } @@ -147,16 +278,20 @@ public class Sectors{ sector.difficulty = (int)(Mathf.dst(sector.x, sector.y)); if(sector.difficulty == 0){ - sector.missions.add(new WaveMission(10)); + //TODO make specfic expansion sector have specific ores + sector.missions.addAll(TutorialSector.getMissions()); }else{ sector.missions.add(Mathf.randomSeed(sector.getSeed() + 1) < waveChance ? new WaveMission(Math.min(sector.difficulty*5 + Mathf.randomSeed(sector.getSeed(), 0, 3)*5, 100)) : new BattleMission()); } - sector.spawns = sector.missions.first().getWaves(sector); + sector.spawns = new Array<>(); - //add all ores for now since material differences aren't well handled yet - sector.ores.addAll(Items.copper, Items.coal, Items.lead, Items.thorium, Items.titanium); + for(Mission mission : sector.missions){ + sector.spawns.addAll(mission.getWaves(sector)); + } + + //sector.ores.addAll(Items.copper); //set starter items if(sector.difficulty > 12){ //now with titanium @@ -169,20 +304,20 @@ public class Sectors{ sector.startingItems = Array.with(new ItemStack(Items.copper, 700), new ItemStack(Items.lead, 200), new ItemStack(Items.densealloy, 130)); }else if(sector.difficulty > 1){ //more starter items for faster start sector.startingItems = Array.with(new ItemStack(Items.copper, 400), new ItemStack(Items.lead, 100)); - }else{ //base starting items to prevent grinding much - sector.startingItems = Array.with(new ItemStack(Items.copper, 130)); + }else{ //empty default + sector.startingItems = Array.with(); } } - private int round2(int i){ - if(i < 0) i --; - return i/2*2; - } - private void createTexture(Sector sector){ if(headless) return; //obviously not created or needed on server - Pixmap pixmap = new Pixmap(sectorImageSize * sector.size, sectorImageSize * sector.size, Format.RGBA8888); + if(sector.texture != null){ + sector.texture.dispose(); + } + + Pixmap pixmap = new Pixmap(sectorImageSize * sector.width, sectorImageSize * sector.height, Format.RGBA8888); + GenResult secResult = new GenResult(); for(int x = 0; x < pixmap.getWidth(); x++){ for(int y = 0; y < pixmap.getHeight(); y++){ @@ -190,8 +325,9 @@ public class Sectors{ int toY = y * sectorSize / sectorImageSize; GenResult result = world.generator().generateTile(sector.x, sector.y, toX, toY, false); + world.generator().generateTile(secResult, sector.x, sector.y, toX, toY + sectorSize / sectorImageSize, false, null, null); - int color = ColorMapper.colorFor(result.floor, result.wall, Team.none, result.elevation, (byte)0); + int color = ColorMapper.colorFor(result.floor, result.wall, Team.none, result.elevation, secResult.elevation > result.elevation ? (byte)(1 << 6) : (byte)0); pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, color); } } diff --git a/core/src/io/anuke/mindustry/maps/TutorialSector.java b/core/src/io/anuke/mindustry/maps/TutorialSector.java new file mode 100644 index 0000000000..9fd4f49d6b --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/TutorialSector.java @@ -0,0 +1,176 @@ +package io.anuke.mindustry.maps; + +import com.badlogic.gdx.utils.Array; +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.content.UnitTypes; +import io.anuke.mindustry.content.blocks.*; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.maps.generation.Generation; +import io.anuke.mindustry.maps.generation.WorldGenerator.GenResult; +import io.anuke.mindustry.maps.missions.*; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.ucore.core.Events; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.util.Bundles; + +import static io.anuke.mindustry.Vars.*; + +/**Just a class for returning the list of tutorial missions.*/ +public class TutorialSector{ + private static int droneIndex; + + public static Array getMissions(){ + //int x = sectorSize/2, y = sectorSize/2; + + Array missions = Array.with( + new ItemMission(Items.copper, 60).setMessage("$tutorial.begin"), + + new BlockLocMission(ProductionBlocks.mechanicalDrill, 55, 62).setMessage("$tutorial.drill"), + + new BlockLocMission(DistributionBlocks.conveyor, 57, 62, 0).setShowComplete(false).setMessage("$tutorial.conveyor"), + new BlockLocMission(DistributionBlocks.conveyor, 58, 62, 0).setShowComplete(false), + new BlockLocMission(DistributionBlocks.conveyor, 59, 62, 0).setShowComplete(false), + new BlockLocMission(DistributionBlocks.conveyor, 60, 62, 3).setShowComplete(false), + + new ItemMission(Items.copper, 100).setMessage("$tutorial.morecopper"), + + new BlockLocMission(TurretBlocks.duo, 56, 59).setMessage("$tutorial.turret"), + new BlockLocMission(ProductionBlocks.mechanicalDrill, 55, 60).setMessage("$tutorial.drillturret"), + + new WaveMission(2).setMessage("$tutorial.waves"), + + new ActionMission(() -> { + Timers.runTask(30f, () -> { + Runnable r = () -> { + Array ores = Array.with(Items.copper, Items.coal, Items.lead); + GenResult res = new GenResult(); + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + Tile tile = world.tile(x, y); + world.generator().generateTile(res, 0, 0, x, y, true, null, ores); + if(!tile.hasCliffs()){ + tile.setFloor((Floor) res.floor); + } + } + } + Events.fire(new WorldLoadEvent()); + }; + + if(headless){ + ui.loadLogic(r); + }else{ + threads.run(r); + } + }); + }), + + new ItemMission(Items.lead, 150).setMessage("$tutorial.lead"), + new ItemMission(Items.copper, 250).setMessage("$tutorial.morecopper"), + + new BlockLocMission(CraftingBlocks.smelter, 58, 69).setMessage("$tutorial.smelter"), + + //drills for smelter + new BlockLocMission(ProductionBlocks.mechanicalDrill, 62, 86), + new BlockLocMission(ProductionBlocks.mechanicalDrill, 58, 89), + new BlockLocMission(ProductionBlocks.mechanicalDrill, 54, 68), + + //conveyors for smelter + new LineBlockMission(DistributionBlocks.conveyor, 58, 88, 58, 70, 3), + new LineBlockMission(DistributionBlocks.conveyor, 61, 86, 61, 70, 3), + new LineBlockMission(DistributionBlocks.conveyor, 61, 69, 59, 69, 2), + new LineBlockMission(DistributionBlocks.conveyor, 56, 69, 57, 69, 0), + new LineBlockMission(DistributionBlocks.conveyor, 58, 68, 58, 63, 3), + new BlockLocMission(DistributionBlocks.junction, 58, 62, 0), + new BlockLocMission(DistributionBlocks.conveyor, 58, 61, 0), + + new ItemMission(Items.densealloy, 20).setMessage("$tutorial.densealloy"), + + new MarkerBlockMission(CraftingBlocks.siliconsmelter, 54, 52).setMessage("$tutorial.siliconsmelter"), + + //coal line + new BlockLocMission(ProductionBlocks.mechanicalDrill, 47, 52).setMessage("$tutorial.silicondrill"), + new LineBlockMission(DistributionBlocks.conveyor, 49, 52, 53, 52, 0), + + //sand line + new BlockLocMission(ProductionBlocks.mechanicalDrill, 53, 49), + new BlockLocMission(ProductionBlocks.mechanicalDrill, 56, 49), + new LineBlockMission(DistributionBlocks.conveyor, 55, 50, 55, 51, 1), + + //silicon line + new LineBlockMission(DistributionBlocks.conveyor, 56, 53, 59, 53, 0), + new LineBlockMission(DistributionBlocks.conveyor, 60, 53, 60, 58, 1), + + new BlockLocMission(PowerBlocks.combustionGenerator, 49, 54).setMessage("$tutorial.generator"), + new BlockLocMission(ProductionBlocks.mechanicalDrill, 47, 54).setMessage("$tutorial.generatordrill"), + new BlockLocMission(PowerBlocks.powerNode, 52, 54).setMessage("$tutorial.node"), + new ConditionMission(Bundles.get("text.mission.linknode"), () -> world.tile(54, 52).entity != null && world.tile(54, 52).entity.power != null && world.tile(54, 52).entity.power.amount >= 0.01f) + .setMessage("$tutorial.nodelink"), + + new ItemMission(Items.silicon, 70).setMessage("$tutorial.silicon"), + + new BlockLocMission(UnitBlocks.daggerFactory, 64, 59).setMessage("$tutorial.daggerfactory"), + + //silicon lines for dagger factory + new BlockLocMission(DistributionBlocks.router, 60, 57).setMessage("$tutorial.router"), + new LineBlockMission(DistributionBlocks.conveyor, 61, 57, 63, 57, 0), + new LineBlockMission(DistributionBlocks.conveyor, 64, 57, 64, 58, 1), + + //power for dagger factory + new BlockLocMission(PowerBlocks.powerNode, 57, 54), + new BlockLocMission(PowerBlocks.powerNode, 62, 54), + + new UnitMission(UnitTypes.dagger).setMessage("$tutorial.dagger"), + new ExpandMission(1, 0){ + @Override + public void onComplete(){ + super.onComplete(); + generateBase(); + } + }, + new BattleMission(){ + public void generate(Generation gen){} //no + }.setMessage("$tutorial.battle") + ); + + //find drone marker mission + for(int i = 0; i < missions.size; i++){ + if(missions.get(i) instanceof MarkerBlockMission){ + droneIndex = i; + break; + } + } + + return missions; + } + + public static boolean supressDrone(){ + return world.getSector() != null && world.getSector().x == 0 && world.getSector().y == 0 && world.getSector().completedMissions < droneIndex; + } + + private static void generateBase(){ + int x = sectorSize/2 + sectorSize, y = sectorSize/2; + world.setBlock(world.tile(x, y), StorageBlocks.core, waveTeam); + // world.setBlock(world.tile(x + 1, y + 2), TurretBlocks.duo, waveTeam); + //world.setBlock(world.tile(x + 1, y - 2), TurretBlocks.duo, waveTeam); + world.setBlock(world.tile(x - 1, y + 2), UnitBlocks.daggerFactory, waveTeam); + world.setBlock(world.tile(x - 1, y - 3), UnitBlocks.daggerFactory, waveTeam); + + //fill turret ammo + //world.tile(x + 1, y + 2).block().handleStack(Items.copper, 1, world.tile(x + 1, y + 2), null); + //world.tile(x + 1, y - 2).block().handleStack(Items.copper, 1, world.tile(x + 1, y - 2), null); + + //since placed() is not called here, add core manually + if(!state.teams.get(waveTeam).cores.contains(world.tile(x, y), true)){ + state.teams.get(waveTeam).cores.add(world.tile(x, y)); + } + } + + private static class MarkerBlockMission extends BlockLocMission{ + public MarkerBlockMission(Block block, int x, int y){ + super(block, x, y); + } + } +} diff --git a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java index ade08e36b0..1176e781e3 100644 --- a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java @@ -70,7 +70,7 @@ public class FortressGenerator{ gen.setBlock(enemyX, enemyY, StorageBlocks.core, team); float difficultyScl = Mathf.clamp(gen.sector.difficulty / 20f + gen.random.range(1f/2f), 0f, 0.9999f); - int coreDst = FortressGenerator.coreDst*gen.sector.size; + int coreDst = FortressGenerator.coreDst*gen.sector.width; Array turrets = find(b -> b instanceof ItemTurret); Array powerTurrets = find(b -> b instanceof PowerTurret); diff --git a/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java b/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java index 1fd7c30ead..eaa7ede832 100644 --- a/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java @@ -8,7 +8,6 @@ import com.badlogic.gdx.utils.ObjectMap; import io.anuke.mindustry.content.Items; import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.OreBlocks; -import io.anuke.mindustry.content.blocks.StorageBlocks; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.maps.MapTileData; import io.anuke.mindustry.maps.MapTileData.TileDataMarker; @@ -21,12 +20,12 @@ import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.OreBlock; import io.anuke.ucore.noise.RidgedPerlin; import io.anuke.ucore.noise.Simplex; -import io.anuke.ucore.noise.VoronoiNoise; import io.anuke.ucore.util.Geometry; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.SeedRandom; -import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.sectorSize; +import static io.anuke.mindustry.Vars.world; public class WorldGenerator{ @@ -37,15 +36,12 @@ public class WorldGenerator{ private Simplex sim2 = new Simplex(baseSeed + 1); private Simplex sim3 = new Simplex(baseSeed + 2); private RidgedPerlin rid = new RidgedPerlin(baseSeed + 4, 1); - private VoronoiNoise vn = new VoronoiNoise(baseSeed + 2, (short)0); private SeedRandom random = new SeedRandom(baseSeed + 3); private GenResult result = new GenResult(); private ObjectMap decoration; public WorldGenerator(){ - vn.setUseDistance(true); - decoration = Mathf.map( Blocks.grass, Blocks.shrub, Blocks.stone, Blocks.rock, @@ -73,6 +69,11 @@ public class WorldGenerator{ generateOres(tiles, seed, genOres, null); } + /**'Prepares' a tile array by:
+ * - setting up multiblocks
+ * - updating cliff data
+ * - removing ores on cliffs
+ * Usually used before placing structures on a tile array.*/ public void prepareTiles(Tile[][] tiles){ //find multiblocks @@ -82,14 +83,8 @@ public class WorldGenerator{ for(int y = 0; y < tiles[0].length; y++){ Tile tile = tiles[x][y]; - Team team = tile.getTeam(); - - if(tile.block() == StorageBlocks.core){ - state.teams.get(team).cores.add(tile); - } - - if(tiles[x][y].block().isMultiblock()){ - multiblocks.add(tiles[x][y].packedPosition()); + if(tile.block().isMultiblock()){ + multiblocks.add(tile.packedPosition()); } } } @@ -134,6 +129,7 @@ public class WorldGenerator{ tile.setBlock(Blocks.air); } + //remove ore veins on cliffs if(tile.floor() instanceof OreBlock && tile.hasCliffs()){ tile.setFloor(((OreBlock)tile.floor()).base); } @@ -190,11 +186,12 @@ public class WorldGenerator{ SeedRandom rnd = new SeedRandom(sector.getSeed()); Generation gena = new Generation(sector, tiles, tiles.length, tiles[0].length, rnd); Array spawnpoints = sector.currentMission().getSpawnPoints(gena); + Array ores = world.sectors().getOres(sector.x, sector.y); for(int x = 0; x < width; x++){ for(int y = 0; y < height; y++){ - GenResult result = generateTile(this.result, sector.x, sector.y, x, y, true, spawnpoints); - Tile tile = new Tile(x, y, (byte)result.floor.id, (byte)result.wall.id, (byte)0, (byte)0, result.elevation); + GenResult result = generateTile(this.result, sector.x, sector.y, x, y, true, spawnpoints, ores); + Tile tile = new Tile(x, y, result.floor.id, result.wall.id, (byte)0, (byte)0, result.elevation); tiles[x][y] = tile; } } @@ -218,8 +215,6 @@ public class WorldGenerator{ } } - generateOres(tiles, sector.getSeed(), true, sector.ores); - for(int x = 0; x < tiles.length; x++){ for(int y = 0; y < tiles[0].length; y++){ Tile tile = tiles[x][y]; @@ -242,10 +237,21 @@ public class WorldGenerator{ } public GenResult generateTile(int sectorX, int sectorY, int localX, int localY, boolean detailed){ - return generateTile(result, sectorX, sectorY, localX, localY, detailed, null); + return generateTile(result, sectorX, sectorY, localX, localY, detailed, null, null); } - public GenResult generateTile(GenResult result, int sectorX, int sectorY, int localX, int localY, boolean detailed, Array spawnpoints){ + /** + * Gets the generation result from a specific sector at specific coordinates. + * @param result where to put the generation results + * @param sectorX X of the sector in terms of sector coordinates + * @param sectorY Y of the sector in terms of sector coordinates + * @param localX X in terms of local sector tile coordinates + * @param localY Y in terms of local sector tile coordinates + * @param detailed whether the tile result is 'detailed' (e.g. previews should not be detailed) + * @param spawnpoints list of player spawnpoints, can be null + * @return the GenResult passed in with its values modified + */ + public GenResult generateTile(GenResult result, int sectorX, int sectorY, int localX, int localY, boolean detailed, Array spawnpoints, Array ores){ int x = sectorX * sectorSize + localX + Short.MAX_VALUE; int y = sectorY * sectorSize + localY + Short.MAX_VALUE; @@ -255,8 +261,8 @@ public class WorldGenerator{ double ridge = rid.getValue(x, y, 1f / 400f); double iceridge = rid.getValue(x+99999, y, 1f / 300f) + sim3.octaveNoise2D(2, 1f, 1f/14f, x, y)/11f; double elevation = elevationOf(x, y, detailed); - double temp = vn.noise(x, y, 1f / 300f) * sim3.octaveNoise2D(detailed ? 2 : 1, 1, 1f / 13f, x, y)/13f - + sim3.octaveNoise2D(detailed ? 12 : 9, 0.6, 1f / 1100f, x, y); + double temp = + + sim3.octaveNoise2D(detailed ? 12 : 9, 0.6, 1f / 1100f, x - 120, y); int lerpDst = 20; lerpDst *= lerpDst; @@ -313,6 +319,19 @@ public class WorldGenerator{ wall = decoration.get(floor); } + if(ores != null && ((Floor) floor).hasOres){ + int offsetX = x - 4, offsetY = y + 23; + for(int i = ores.size - 1; i >= 0; i--){ + Item entry = ores.get(i); + if( + Math.abs(0.5f - sim.octaveNoise2D(2, 0.7, 1f / (50 + i * 2), offsetX, offsetY)) > 0.23f && + Math.abs(0.5f - sim2.octaveNoise2D(1, 1, 1f / (40 + i * 4), offsetX, offsetY)) > 0.32f){ + floor = OreBlocks.get(floor, entry); + break; + } + } + } + result.wall = wall; result.floor = floor; result.elevation = (byte) Math.max(elevation, 0); @@ -321,7 +340,7 @@ public class WorldGenerator{ double elevationOf(int x, int y, boolean detailed){ double ridge = rid.getValue(x, y, 1f / 400f); - return sim.octaveNoise2D(detailed ? 7 : 4, 0.62, 1f / 800, x, y) * 6.1 - 1 - ridge; + return sim.octaveNoise2D(detailed ? 7 : 5, 0.62, 1f / 800, x, y) * 6.1 - 1 - ridge; } public static class GenResult{ diff --git a/core/src/io/anuke/mindustry/maps/missions/ActionMission.java b/core/src/io/anuke/mindustry/maps/missions/ActionMission.java new file mode 100644 index 0000000000..55710aa1ff --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/ActionMission.java @@ -0,0 +1,32 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.ucore.util.Bundles; + +import static io.anuke.mindustry.Vars.threads; + +/**A mission which simply runs a single action and is completed instantly.*/ +public class ActionMission extends Mission{ + protected Runnable runner; + + public ActionMission(Runnable runner){ + this.runner = runner; + } + + public ActionMission(){ + } + + @Override + public void onComplete(){ + threads.run(runner); + } + + @Override + public boolean isComplete(){ + return true; + } + + @Override + public String displayString(){ + return Bundles.get("text.loading"); + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/BattleMission.java b/core/src/io/anuke/mindustry/maps/missions/BattleMission.java index 97230888e5..822d8225e3 100644 --- a/core/src/io/anuke/mindustry/maps/missions/BattleMission.java +++ b/core/src/io/anuke/mindustry/maps/missions/BattleMission.java @@ -7,15 +7,10 @@ import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.maps.generation.FortressGenerator; import io.anuke.mindustry.maps.generation.Generation; -import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.util.Bundles; -public class BattleMission implements Mission{ +public class BattleMission extends Mission{ private final static int coreX = 60, coreY = 60; - @Override - public void display(Table table){ - table.add("$text.mission.battle"); - } @Override public GameMode getMode(){ diff --git a/core/src/io/anuke/mindustry/maps/missions/BlockLocMission.java b/core/src/io/anuke/mindustry/maps/missions/BlockLocMission.java new file mode 100644 index 0000000000..412cb2ecac --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/BlockLocMission.java @@ -0,0 +1,72 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.mindustry.graphics.Palette; +import io.anuke.mindustry.world.Block; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.graphics.Draw; +import io.anuke.ucore.graphics.Lines; +import io.anuke.ucore.util.Angles; +import io.anuke.ucore.util.Bundles; +import io.anuke.ucore.util.Mathf; + +import static io.anuke.mindustry.Vars.players; +import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.world; + +public class BlockLocMission extends Mission{ + private final Block block; + private final int x, y, rotation; + + public BlockLocMission(Block block, int x, int y, int rotation){ + this.block = block; + this.x = x; + this.y = y; + this.rotation = rotation; + } + + public BlockLocMission(Block block, int x, int y){ + this.block = block; + this.x = x; + this.y = y; + this.rotation = 0; + } + + @Override + public void drawOverlay(){ + Lines.stroke(2f); + + Draw.color(Palette.accent.r * 0.8f,Palette.accent.g * 0.8f,Palette.accent.b * 0.8f); + Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1f, block.size * tilesize/2f + 1f+ Mathf.absin(Timers.time(), 6f, 2f)); + + Draw.color(Palette.accent); + Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize/2f + 1f+ Mathf.absin(Timers.time(), 6f, 2f)); + + + if(block.rotate){ + Draw.colorl(0.4f); + Draw.rect("icon-arrow", x * tilesize + block.offset(), y * tilesize + block.offset() - 1f, rotation*90); + Draw.colorl(0.6f); + Draw.rect("icon-arrow", x * tilesize + block.offset(), y * tilesize + block.offset(), rotation*90); + } + + float rot = players[0].angleTo(x * tilesize + block.offset(), y * tilesize + block.offset()); + float len = 12f; + + Draw.color(Palette.accent.r * 0.8f,Palette.accent.g * 0.8f,Palette.accent.b * 0.8f); + Draw.rect("icon-arrow", players[0].x + Angles.trnsx(rot, len), players[0].y + Angles.trnsy(rot, len), rot); + Draw.color(Palette.accent); + Draw.rect("icon-arrow", players[0].x + Angles.trnsx(rot, len), players[0].y + Angles.trnsy(rot, len) + 1f, rot); + + Draw.reset(); + } + + @Override + public boolean isComplete(){ + return world.tile(x, y).block() == block && (!block.rotate || world.tile(x,y).getRotation() == rotation); + } + + @Override + public String displayString(){ + return Bundles.format("text.mission.block", block.formalName); + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/BlockMission.java b/core/src/io/anuke/mindustry/maps/missions/BlockMission.java new file mode 100644 index 0000000000..beff68c6d4 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/BlockMission.java @@ -0,0 +1,55 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.mindustry.game.EventType.BlockBuildEvent; +import io.anuke.mindustry.game.GameMode; +import io.anuke.mindustry.world.Block; +import io.anuke.ucore.core.Events; +import io.anuke.ucore.util.Bundles; + +import static io.anuke.mindustry.Vars.defaultTeam; +import static io.anuke.mindustry.Vars.world; + +/**A mission in which the player must place a block.*/ +@Deprecated +public class BlockMission extends Mission{ + private final Block block; + private boolean complete; + + static{ + Events.on(BlockBuildEvent.class, event -> { + if(world.getSector() != null && event.team == defaultTeam){ + Mission mission = world.getSector().currentMission(); + if(mission instanceof BlockMission){ + BlockMission block = (BlockMission)world.getSector().currentMission(); + if(block.block == event.tile.block()){ + block.complete = true; + } + } + } + }); + } + + public BlockMission(Block block){ + this.block = block; + } + + @Override + public void reset(){ + complete = false; + } + + @Override + public boolean isComplete(){ + return complete; + } + + @Override + public String displayString(){ + return Bundles.format("text.mission.block", block.formalName); + } + + @Override + public GameMode getMode(){ + return GameMode.noWaves; + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/ConditionMission.java b/core/src/io/anuke/mindustry/maps/missions/ConditionMission.java new file mode 100644 index 0000000000..68e141633b --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/ConditionMission.java @@ -0,0 +1,23 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.ucore.function.BooleanProvider; + +public class ConditionMission extends Mission{ + private final BooleanProvider complete; + private final String display; + + public ConditionMission(String display, BooleanProvider complete){ + this.complete = complete; + this.display = display; + } + + @Override + public boolean isComplete(){ + return complete.get(); + } + + @Override + public String displayString(){ + return display; + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/ExpandMission.java b/core/src/io/anuke/mindustry/maps/missions/ExpandMission.java new file mode 100644 index 0000000000..660064ff4c --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/ExpandMission.java @@ -0,0 +1,37 @@ +package io.anuke.mindustry.maps.missions; + +import static io.anuke.mindustry.Vars.*; + +/**An action mission which simply expands the sector.*/ +public class ExpandMission extends ActionMission{ + private boolean done = false; + + public ExpandMission(int expandX, int expandY){ + runner = () -> { + if(headless){ + world.sectors().expandSector(world.getSector(), expandX, expandY); + done = true; + }else{ + ui.loadLogic(() -> { + world.sectors().expandSector(world.getSector(), expandX, expandY); + done = true; + }); + } + }; + } + + @Override + public void onBegin(){ + runner.run(); + } + + @Override + public boolean isComplete(){ + return done; + } + + @Override + public void onComplete(){ + done = false; + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/ItemMission.java b/core/src/io/anuke/mindustry/maps/missions/ItemMission.java new file mode 100644 index 0000000000..a65eb4bc31 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/ItemMission.java @@ -0,0 +1,42 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Tile; +import io.anuke.ucore.util.Bundles; + +import static io.anuke.mindustry.Vars.state; + +/**A mission that is completed when the player obtains items in their core.*/ +public class ItemMission extends Mission{ + private final Item item; + private final int amount; + + public ItemMission(Item item, int amount){ + this.item = item; + this.amount = amount; + } + + @Override + public boolean isComplete(){ + for(Tile tile : state.teams.get(Vars.defaultTeam).cores){ + if(tile.entity.items.has(item, amount)){ + return true; + } + } + return false; + } + + @Override + public String displayString(){ + TileEntity core = Vars.players[0].getClosestCore(); + if(core == null) return "imminent doom"; + return Bundles.format("text.mission.resource", item.localizedName(), core.items.get(item), amount); + } + + @Override + public String menuDisplayString(){ + return Bundles.format("text.mission.resource.menu", item.localizedName(), amount); + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/LineBlockMission.java b/core/src/io/anuke/mindustry/maps/missions/LineBlockMission.java new file mode 100644 index 0000000000..918788b55c --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/LineBlockMission.java @@ -0,0 +1,46 @@ +package io.anuke.mindustry.maps.missions; + +import com.badlogic.gdx.math.Bresenham2; +import com.badlogic.gdx.math.GridPoint2; +import com.badlogic.gdx.utils.Array; +import io.anuke.mindustry.world.Block; + +public class LineBlockMission extends Mission{ + private Array points = new Array<>(); + private int completeIndex; + + public LineBlockMission(Block block, int x1, int y1, int x2, int y2, int rotation){ + Array points = new Bresenham2().line(x1, y1, x2, y2); + for(GridPoint2 point : points){ + this.points.add(new BlockLocMission(block, point.x, point.y, rotation)); + } + } + + @Override + public boolean isComplete(){ + while(completeIndex < points.size && points.get(completeIndex).isComplete()){ + completeIndex ++; + } + return completeIndex >= points.size; + } + + @Override + public void drawOverlay(){ + if(completeIndex < points.size){ + points.get(completeIndex).drawOverlay(); + } + } + + @Override + public void reset(){ + completeIndex = 0; + } + + @Override + public String displayString(){ + if(completeIndex < points.size){ + return points.get(completeIndex).displayString(); + } + return points.first().displayString(); + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/MessageMission.java b/core/src/io/anuke/mindustry/maps/missions/MessageMission.java new file mode 100644 index 0000000000..920f2cc111 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/MessageMission.java @@ -0,0 +1,15 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.mindustry.Vars; + +/**A mission that just displays some text.*/ +public class MessageMission extends ActionMission{ + + public MessageMission(String text){ + super(() -> { + if(!Vars.headless){ + Vars.ui.showInfo(text); + } + }); + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/Mission.java b/core/src/io/anuke/mindustry/maps/missions/Mission.java index 5c20056d27..61994587e9 100644 --- a/core/src/io/anuke/mindustry/maps/missions/Mission.java +++ b/core/src/io/anuke/mindustry/maps/missions/Mission.java @@ -8,25 +8,91 @@ import io.anuke.mindustry.game.SpawnGroup; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.maps.Sector; import io.anuke.mindustry.maps.generation.Generation; +import io.anuke.ucore.core.Timers; import io.anuke.ucore.scene.ui.layout.Table; +import io.anuke.ucore.util.Bundles; -public interface Mission{ - boolean isComplete(); - String displayString(); - GameMode getMode(); - void display(Table table); +import static io.anuke.mindustry.Vars.headless; +import static io.anuke.mindustry.Vars.ui; - default Array getWaves(Sector sector){ +public abstract class Mission{ + private String extraMessage; + private boolean showComplete =true; + + public abstract boolean isComplete(); + + /**Returns the string that is displayed in-game near the menu.*/ + public abstract String displayString(); + + /**Returns the info string displayed in the sector dialog (menu)*/ + public String menuDisplayString(){ + return displayString(); + } + + public GameMode getMode(){ + return GameMode.noWaves; + } + + /**Sets the message displayed on mission begin. Returns this mission for chaining.*/ + public Mission setMessage(String message){ + this.extraMessage = message; + return this; + } + + public Mission setShowComplete(boolean complete){ + this.showComplete = complete; + return this; + } + + /**Draw mission overlay.*/ + public void drawOverlay(){ + + } + + public void update(){ + + } + + public void reset(){ + + } + + /**Shows the unique sector message.*/ + public void showMessage(){ + if(!headless && extraMessage != null){ + ui.hudfrag.showTextDialog(extraMessage); + } + } + + public boolean hasMessage(){ + return extraMessage != null; + } + + public void onBegin(){ + Timers.runTask(60f, this::showMessage); + } + + public void onComplete(){ + if(showComplete && !headless){ + ui.hudfrag.showText("[LIGHT_GRAY]"+menuDisplayString() + ":\n" + Bundles.get("text.mission.complete")); + } + } + + public void display(Table table){ + table.add(displayString()); + } + + public Array getWaves(Sector sector){ return new Array<>(); } - default Array getSpawnPoints(Generation gen){ + public Array getSpawnPoints(Generation gen){ return Array.with(); } - default void generate(Generation gen){} + public void generate(Generation gen){} - default void generateCoreAt(Generation gen, int coreX, int coreY, Team team){ + public void generateCoreAt(Generation gen, int coreX, int coreY, Team team){ gen.tiles[coreX][coreY].setBlock(StorageBlocks.core); gen.tiles[coreX][coreY].setTeam(team); } diff --git a/core/src/io/anuke/mindustry/maps/missions/ResourceMission.java b/core/src/io/anuke/mindustry/maps/missions/ResourceMission.java deleted file mode 100644 index d2a21f2975..0000000000 --- a/core/src/io/anuke/mindustry/maps/missions/ResourceMission.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.anuke.mindustry.maps.missions; - -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.type.Item; -import io.anuke.ucore.scene.ui.layout.Table; -import io.anuke.ucore.util.Bundles; - -public class ResourceMission implements Mission{ - private final Item item; - private final int amount; - - public ResourceMission(Item item, int amount){ - this.item = item; - this.amount = amount; - } - - @Override - public void display(Table table){ - - } - - @Override - public GameMode getMode(){ - return GameMode.waves; - } - - @Override - public boolean isComplete(){ - return Vars.state.teams.get(Vars.defaultTeam).cores.first().entity.items.has(item, amount); - } - - @Override - public String displayString(){ - return Bundles.format("text.mission.resource", item.localizedName(), amount); - } -} diff --git a/core/src/io/anuke/mindustry/maps/missions/UnitMission.java b/core/src/io/anuke/mindustry/maps/missions/UnitMission.java new file mode 100644 index 0000000000..efe097a687 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/UnitMission.java @@ -0,0 +1,29 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.units.BaseUnit; +import io.anuke.mindustry.entities.units.UnitType; +import io.anuke.ucore.util.Bundles; + +public class UnitMission extends Mission{ + private final UnitType type; + + public UnitMission(UnitType type){ + this.type = type; + } + + @Override + public boolean isComplete(){ + for(BaseUnit unit : Vars.unitGroups[Vars.defaultTeam.ordinal()].all()){ + if(unit.getType() == type){ + return true; + } + } + return false; + } + + @Override + public String displayString(){ + return Bundles.format("text.mission.unit", type.localizedName()); + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/VictoryMission.java b/core/src/io/anuke/mindustry/maps/missions/VictoryMission.java new file mode 100644 index 0000000000..46b6153dff --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/missions/VictoryMission.java @@ -0,0 +1,26 @@ +package io.anuke.mindustry.maps.missions; + +import io.anuke.mindustry.game.GameMode; +import io.anuke.ucore.scene.ui.layout.Table; + +public class VictoryMission extends Mission{ + @Override + public boolean isComplete(){ + return false; + } + + @Override + public String displayString(){ + return "none"; + } + + @Override + public GameMode getMode(){ + return GameMode.victory; + } + + @Override + public void display(Table table){ + + } +} diff --git a/core/src/io/anuke/mindustry/maps/missions/WaveMission.java b/core/src/io/anuke/mindustry/maps/missions/WaveMission.java index f75450125e..6e65f6c71b 100644 --- a/core/src/io/anuke/mindustry/maps/missions/WaveMission.java +++ b/core/src/io/anuke/mindustry/maps/missions/WaveMission.java @@ -2,15 +2,20 @@ package io.anuke.mindustry.maps.missions; import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.game.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.game.GameMode; +import io.anuke.mindustry.game.SpawnGroup; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Waves; import io.anuke.mindustry.maps.Sector; import io.anuke.mindustry.maps.generation.Generation; -import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.util.Bundles; import static io.anuke.mindustry.Vars.state; +import static io.anuke.mindustry.Vars.waveTeam; +import static io.anuke.mindustry.Vars.world; -public class WaveMission implements Mission{ +public class WaveMission extends Mission{ private final int target; public WaveMission(int target){ @@ -29,8 +34,10 @@ public class WaveMission implements Mission{ } @Override - public void display(Table table){ - table.add(Bundles.format("text.mission.wave", target)); + public void onBegin(){ + super.onBegin(); + + world.pathfinder().activateTeamPath(waveTeam); } @Override @@ -40,12 +47,29 @@ public class WaveMission implements Mission{ @Override public String displayString(){ - return Bundles.format("text.mission.wave", target); + return state.wave > target ? + Bundles.format( + Vars.unitGroups[Vars.waveTeam.ordinal()].size() > 1 ? + "text.mission.wave.enemies" : + "text.mission.wave.enemy", target, target, Vars.unitGroups[Vars.waveTeam.ordinal()].size()) : + Bundles.format("text.mission.wave", state.wave, target, (int)(state.wavetime/60)); + } + + @Override + public String menuDisplayString(){ + return Bundles.format("text.mission.wave.menu", target); + } + + @Override + public void update(){ + if(state.wave > target){ + state.mode = GameMode.noWaves; + } } @Override public boolean isComplete(){ - return state.wave >= target; + return state.wave > target && Vars.unitGroups[Vars.waveTeam.ordinal()].size() == 0; } @Override diff --git a/core/src/io/anuke/mindustry/type/Recipe.java b/core/src/io/anuke/mindustry/type/Recipe.java index 1d484bc25e..ff81d31414 100644 --- a/core/src/io/anuke/mindustry/type/Recipe.java +++ b/core/src/io/anuke/mindustry/type/Recipe.java @@ -33,6 +33,7 @@ public class Recipe extends UnlockableContent{ public GameMode mode; public boolean isPad; public boolean hidden; + public boolean alwaysUnlocked; private UnlockableContent[] dependencies; private Block[] blockDependencies; @@ -58,15 +59,16 @@ public class Recipe extends UnlockableContent{ * Returns unlocked recipes in a category. * Do not call on the server backend, as unlocking does not exist! */ - public static void getUnlockedByCategory(Category category, Array r){ + public static void getUnlockedByCategory(Category category, Array arr){ if(headless){ throw new RuntimeException("Not implemented on the headless backend!"); } - r.clear(); - for(Recipe recipe : content.recipes()){ - if(recipe.category == category && (control.database().isUnlocked(recipe))){ - r.add(recipe); + arr.clear(); + for(Recipe r : content.recipes()){ + if(r.category == category && (control.database().isUnlocked(r)) && + !((r.mode != null && r.mode != state.mode) || (r.desktopOnly && mobile) || (r.isPad && !state.mode.showPads))){ + arr.add(r); } } } @@ -107,9 +109,15 @@ public class Recipe extends UnlockableContent{ return this; } + public Recipe setAlwaysUnlocked(boolean unlocked){ + this.alwaysUnlocked = unlocked; + return this; + } + + @Override public boolean alwaysUnlocked(){ - return hidden; + return alwaysUnlocked; } @Override diff --git a/core/src/io/anuke/mindustry/ui/dialogs/GenViewDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/GenViewDialog.java index 622f22f646..e966a00693 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/GenViewDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/GenViewDialog.java @@ -4,9 +4,12 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.async.AsyncExecutor; +import io.anuke.mindustry.content.Items; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.maps.generation.WorldGenerator.GenResult; +import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.ColorMapper; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.scene.Element; @@ -20,6 +23,7 @@ import static io.anuke.mindustry.Vars.sectorSize; import static io.anuke.mindustry.Vars.world; public class GenViewDialog extends FloatingDialog{ + Array ores = Array.with(Items.copper, Items.lead, Items.coal); public GenViewDialog(){ super("generate view"); @@ -81,7 +85,7 @@ public class GenViewDialog extends FloatingDialog{ Pixmap pixmap = new Pixmap(sectorSize, sectorSize, Format.RGBA8888); for(int i = 0; i < sectorSize; i++){ for(int j = 0; j < sectorSize; j++){ - world.generator().generateTile(result, wx, wy, i, j, true, null); + world.generator().generateTile(result, wx, wy, i, j, true, null, ores); pixmap.drawPixel(i, sectorSize - 1 - j, ColorMapper.colorFor(result.floor, result.wall, Team.none, result.elevation, (byte)0)); } } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java index 4b97bb82f8..c501f796f5 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java @@ -24,7 +24,7 @@ public class SaveDialog extends LoadDialog{ slots.row(); slots.addImageTextButton("$text.save.new", "icon-add", "clear", 14 * 3, () -> ui.showTextInput("$text.save", "$text.save.newslot", "", text -> { - ui.loadAnd("$text.saving", () -> { + ui.loadGraphics("$text.saving", () -> { control.getSaves().addSave(text); threads.runGraphics(() -> threads.run(() -> threads.runGraphics(this::setup))); }); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SectorsDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SectorsDialog.java index e546d1d609..6781a630e7 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SectorsDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SectorsDialog.java @@ -43,8 +43,8 @@ public class SectorsDialog extends FloatingDialog{ (selected.hasSave() ? " [accent]/[white] " + Bundles.format("text.sector.time", selected.getSave().getPlayTime()) : "")))); content().row(); content().label(() -> Bundles.format("text.mission", selected == null || selected.completedMissions >= selected.missions.size - ? Bundles.get("text.none") : selected.missions.get(selected.completedMissions).displayString()) - + "[WHITE] " + (selected == null ? "" : Bundles.format("text.save.difficulty", "[LIGHT_GRAY]" + selected.getDifficulty().toString()))); + ? Bundles.get("text.none") : selected.missions.get(selected.completedMissions).menuDisplayString()) + + "[WHITE] " /*+ (selected == null ? "" : Bundles.format("text.save.difficulty", "[LIGHT_GRAY]" + selected.getDifficulty().toString()))*/); content().row(); content().add(new SectorView()).grow(); content().row(); @@ -113,9 +113,9 @@ public class SectorsDialog extends FloatingDialog{ float padSectorSize = sectorSize + sectorPadding; - float clipSize = Math.min(width, height); - int shownSectors = (int)(clipSize/padSectorSize); - clip.setSize(clipSize).setCenter(x + width/2f, y + height/2f); + int shownSectorsX = (int)(width/padSectorSize); + int shownSectorsY = (int)(height/padSectorSize); + clip.setSize(width, height).setCenter(x + width/2f, y + height/2f); Graphics.flush(); boolean clipped = ScissorStack.pushScissors(clip); @@ -124,8 +124,8 @@ public class SectorsDialog extends FloatingDialog{ Vector2 mouse = Graphics.mouse(); - for(int x = -shownSectors; x <= shownSectors; x++){ - for(int y = -shownSectors; y <= shownSectors; y++){ + for(int x = -shownSectorsX; x <= shownSectorsX; x++){ + for(int y = -shownSectorsY; y <= shownSectorsY; y++){ int sectorX = offsetX + x; int sectorY = offsetY + y; @@ -133,19 +133,21 @@ public class SectorsDialog extends FloatingDialog{ float drawY = y + height/2f + sectorY * padSectorSize - offsetY * padSectorSize - panY % padSectorSize; Sector sector = world.sectors().get(sectorX, sectorY); - int size = (sector == null ? 1 : sector.size); - float padding = (size-1) * sectorPadding; + int width = (sector == null ? 1 : sector.width); + int height = (sector == null ? 1 : sector.height); + float paddingx = (width-1) * sectorPadding; + float paddingy = (height-1) * sectorPadding; if(sector != null && (sector.x != sectorX || sector.y != sectorY)){ continue; } - drawX += (size-1)/2f*padSectorSize; - drawY += (size-1)/2f*padSectorSize; + drawX += (width-1)/2f*padSectorSize; + drawY += (height-1)/2f*padSectorSize; if(sector != null && sector.texture != null){ Draw.color(Color.WHITE); - Draw.rect(sector.texture, drawX, drawY, sectorSize * size + padding, sectorSize * size + padding); + Draw.rect(sector.texture, drawX, drawY, sectorSize * width + paddingx, sectorSize * height + paddingy); } float stroke = 4f; @@ -155,8 +157,8 @@ public class SectorsDialog extends FloatingDialog{ }else if(sector == selected){ Draw.color(Palette.place); stroke = 6f; - }else if(Mathf.inRect(mouse.x, mouse.y, drawX - padSectorSize/2f * size, drawY - padSectorSize/2f * size, - drawX + padSectorSize/2f * size, drawY + padSectorSize/2f * size)){ + }else if(Mathf.inRect(mouse.x, mouse.y, drawX - padSectorSize/2f * width, drawY - padSectorSize/2f * height, + drawX + padSectorSize/2f * width, drawY + padSectorSize/2f * height)){ if(clicked){ selectSector(sector); } @@ -168,13 +170,13 @@ public class SectorsDialog extends FloatingDialog{ } Lines.stroke(Unit.dp.scl(stroke)); - Lines.crect(drawX, drawY, sectorSize * size + padding, sectorSize * size + padding, (int)stroke); + Lines.crect(drawX, drawY, sectorSize * width + paddingx, sectorSize * height + paddingy, (int)stroke); } } Draw.color(Palette.accent); Lines.stroke(Unit.dp.scl(4f)); - Lines.crect(x + width/2f, y + height/2f, clipSize, clipSize); + Lines.crect(x + width/2f, y + height/2f, width, height); Draw.reset(); Graphics.flush(); diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java index 1a4f6107c9..2728a52a58 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlocksFragment.java @@ -81,14 +81,16 @@ public class BlocksFragment extends Fragment{ } }); - container.add(descTable).fillX().uniformX(); + float w = 246f; - container.row(); + main.add(descTable).width(w); + + main.row(); //now add the block selection menu selectTable = main.table("pane", select -> {}) .margin(10f).marginLeft(0f).marginRight(0f).marginTop(-5) - .touchable(Touchable.enabled).right().bottom().get(); + .touchable(Touchable.enabled).right().bottom().width(w).get(); }).bottom().right().get(); }); diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index d8639ddb13..11f0b9a411 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Interpolation; +import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Scaling; import io.anuke.mindustry.core.GameState.State; @@ -15,15 +16,14 @@ import io.anuke.mindustry.net.Packets.AdminAction; import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.ui.IntFormat; import io.anuke.mindustry.ui.Minimap; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.ucore.core.*; import io.anuke.ucore.graphics.Hue; import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.Group; import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.event.Touchable; -import io.anuke.ucore.scene.ui.Image; -import io.anuke.ucore.scene.ui.ImageButton; -import io.anuke.ucore.scene.ui.Label; +import io.anuke.ucore.scene.ui.*; import io.anuke.ucore.scene.ui.layout.Stack; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.util.Bundles; @@ -103,7 +103,8 @@ public class HudFragment extends Fragment{ cont.row(); - Table waves = cont.table(this::addWaveTable).touchable(Touchable.enabled).fillX().height(66f).get(); + TextButton waves = cont.addButton("", ()->{}).fillX().height(66f).get(); + addWaveTable(waves); cont.row(); @@ -182,6 +183,27 @@ public class HudFragment extends Fragment{ blockfrag.build(Core.scene.getRoot()); } + public void showText(String text){ + Table table = new Table("button"); + table.update(() -> { + if(state.is(State.menu)){ + table.remove(); + } + }); + table.margin(12); + table.addImage("icon-check").size(16*2).pad(3); + table.add(text).wrap().width(280f).get().setAlignment(Align.center, Align.center); + table.pack(); + + //create container table which will align and move + Table container = Core.scene.table(); + container.top().add(table); + container.setTranslation(0, table.getPrefHeight()); + container.actions(Actions.translateBy(0, -table.getPrefHeight(), 1f, Interpolation.fade), Actions.delay(4f), + //nesting actions() calls is necessary so the right prefHeight() is used + Actions.run(() -> container.actions(Actions.translateBy(0, table.getPrefHeight(), 1f, Interpolation.fade), Actions.removeActor()))); + } + /** * Show unlock notification for a new recipe. */ @@ -281,6 +303,16 @@ public class HudFragment extends Fragment{ } } + public void showTextDialog(String str){ + new FloatingDialog("$text.mission.info"){{ + shouldPause = true; + setFillParent(false); + getCell(content()).growX(); + content().margin(15).add(str).width(400f).wrap().get().setAlignment(Align.left, Align.left); + buttons().addButton("$text.continue", this::hide).size(140, 60).pad(4); + }}.show(); + } + private void toggleMenus(){ wavetable.clearActions(); infolabel.clearActions(); @@ -312,27 +344,30 @@ public class HudFragment extends Fragment{ } } - private void addWaveTable(Table table){ + private void addWaveTable(TextButton table){ wavetable = table; float uheight = 66f; IntFormat wavef = new IntFormat("text.wave"); IntFormat timef = new IntFormat("text.wave.waiting"); + table.clearChildren(); + table.setTouchable(Touchable.enabled); + table.background("button"); - table.left().table(text -> { - text.left(); - text.label(() -> wavef.get(state.wave)).left().get().setFontScale(fontScale * 1.5f); - text.row(); - text.label(() -> unitGroups[Team.red.ordinal()].size() > 0 && state.mode.disableWaveTimer ? - getEnemiesRemaining() : (state.mode.disableWaveTimer) ? "$text.waiting" : - timef.get((int) (state.wavetime / 60f))).minWidth(126).left(); + table.labelWrap(() -> world.getSector() == null ? wavef.get(state.wave) : + Bundles.format("text.mission.display", world.getSector().currentMission().displayString())).growX(); + + table.clicked(() -> { + if(world.getSector() != null && world.getSector().currentMission().hasMessage()){ + world.getSector().currentMission().showMessage(); + } }); - table.add().growX(); - table.visible(() -> !state.mode.disableWaves); + table.setDisabled(() -> !(world.getSector() != null && world.getSector().currentMission().hasMessage())); + table.visible(() -> !((world.getSector() == null && state.mode.disableWaves) || !state.mode.showMission)); - playButton(uheight); + //playButton(uheight); } private void playButton(float uheight){ diff --git a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java index 9cf67392bd..703a8a6fb9 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java @@ -59,7 +59,7 @@ public class MenuFragment extends Fragment{ maps = new MobileButton("icon-map", isize, "$text.maps", ui.maps::show), load = new MobileButton("icon-load", isize, "$text.load", ui.load::show), join = new MobileButton("icon-add", isize, "$text.joingame", ui.join::show), - editor = new MobileButton("icon-editor", isize, "$text.editor", () -> ui.loadAnd(ui.editor::show)), + editor = new MobileButton("icon-editor", isize, "$text.editor", () -> ui.loadGraphics(ui.editor::show)), tools = new MobileButton("icon-tools", isize, "$text.settings", ui.settings::show), unlocks = new MobileButton("icon-unlocks", isize, "$text.unlocks", ui.unlocks::show), donate = new MobileButton("icon-donate", isize, "$text.donate", Platform.instance::openDonations); @@ -114,7 +114,7 @@ public class MenuFragment extends Fragment{ out.row(); - out.add(new MenuButton("icon-editor", "$text.editor", () -> ui.loadAnd(ui.editor::show))); + out.add(new MenuButton("icon-editor", "$text.editor", () -> ui.loadGraphics(ui.editor::show))); out.add(new MenuButton("icon-map", "$text.maps", ui.maps::show)); diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 9c3b25ad37..5c949ae312 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -3,6 +3,7 @@ package io.anuke.mindustry.world; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntArray; import io.anuke.mindustry.entities.Damage; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.TileEntity; @@ -252,6 +253,19 @@ public class Block extends BaseBlock { region = Draw.region(name); } + /**Called when the world is resized. + * Call super!*/ + public void transformLinks(Tile tile, int oldWidth, int oldHeight, int newWidth, int newHeight, int shiftX, int shiftY){ + if(tile.entity != null && tile.entity.power != null){ + IntArray links = tile.entity.power.links; + IntArray out = new IntArray(); + for(int i = 0; i < links.size; i++){ + out.add(world.transform(links.get(i), oldWidth, oldHeight, newWidth, shiftX, shiftY)); + } + tile.entity.power.links = out; + } + } + /** Called when the block is tapped. */ public void tapped(Tile tile, Player player){ diff --git a/core/src/io/anuke/mindustry/world/ColorMapper.java b/core/src/io/anuke/mindustry/world/ColorMapper.java index 00f64f10e4..14561e95dc 100644 --- a/core/src/io/anuke/mindustry/world/ColorMapper.java +++ b/core/src/io/anuke/mindustry/world/ColorMapper.java @@ -6,7 +6,6 @@ import com.badlogic.gdx.utils.ObjectIntMap; import io.anuke.mindustry.game.ContentList; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.type.ContentType; -import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.content; @@ -34,8 +33,8 @@ public class ColorMapper implements ContentList{ Color tmpColor = tmpColors.get(); tmpColor.set(color); float maxMult = 1f/Math.max(Math.max(tmpColor.r, tmpColor.g), tmpColor.b) ; - float mul = Math.min(1.1f + elevation / 4f, maxMult); - if((cliffs & Mathf.pow2(6)) != 0){ + float mul = Math.min(0.7f + elevation / 5f, maxMult); + if((cliffs & ((1 << 6))) != 0){ mul -= 0.5f; } tmpColor.mul(mul, mul, mul, 1f); diff --git a/core/src/io/anuke/mindustry/world/blocks/OreBlock.java b/core/src/io/anuke/mindustry/world/blocks/OreBlock.java index 79c09df684..7333c7dcd2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/OreBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/OreBlock.java @@ -19,7 +19,7 @@ public class OreBlock extends Floor{ this.base = base; this.variants = 3; this.minimapColor = ore.color; - this.blends = block -> block instanceof OreBlock && ((OreBlock) block).base != base; + this.blends = block -> (block instanceof OreBlock && ((OreBlock) block).base != base) || (!(block instanceof OreBlock) && block != base); this.tileBlends = (tile, other) -> tile.getElevation() < other.getElevation(); this.edge = base.name; } diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java b/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java index 2ca61d9aeb..fa9559e101 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java @@ -34,7 +34,8 @@ public class MendProjector extends Block{ protected float reload = 250f; protected float range = 50f; protected float healPercent = 6f; - protected float phaseBoost = 10f; + protected float phaseBoost = 12f; + protected float phaseRangeBoost = 40f; protected float useTime = 300f; public MendProjector(String name){ @@ -65,7 +66,7 @@ public class MendProjector extends Block{ } if(entity.charge >= reload){ - float realRange = range + entity.phaseHeat * 20f; + float realRange = range + entity.phaseHeat * phaseRangeBoost; Effects.effect(BlockFx.healWaveMend, Hue.mix(color, phase, entity.phaseHeat), tile.drawx(), tile.drawy(), realRange); entity.charge = 0f; diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java b/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java index 5eee09e958..ed3b5c3ebe 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java @@ -286,6 +286,14 @@ public class ItemBridge extends Block{ return rel != rel2; } + @Override + public void transformLinks(Tile tile, int oldWidth, int oldHeight, int newWidth, int newHeight, int shiftX, int shiftY){ + super.transformLinks(tile, oldWidth, oldHeight, newWidth, newHeight, shiftX, shiftY); + + ItemBridgeEntity entity = tile.entity(); + entity.link = world.transform(entity.link, oldWidth, oldHeight, newWidth, shiftX, shiftY); + } + @Override public TileEntity newEntity(){ return new ItemBridgeEntity(); diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java index d336911253..1fe8301645 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -233,6 +233,14 @@ public class MassDriver extends Block{ return tile.entity.items.total() < itemCapacity; } + @Override + public void transformLinks(Tile tile, int oldWidth, int oldHeight, int newWidth, int newHeight, int shiftX, int shiftY){ + super.transformLinks(tile, oldWidth, oldHeight, newWidth, newHeight, shiftX, shiftY); + + MassDriverEntity entity = tile.entity(); + entity.link = world.transform(entity.link, oldWidth, oldHeight, newWidth, shiftX, shiftY); + } + @Override public TileEntity newEntity(){ return new MassDriverEntity(); diff --git a/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java b/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java index 09e3046723..f1410621e9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java @@ -16,6 +16,7 @@ import io.anuke.mindustry.entities.units.UnitType; import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Shaders; +import io.anuke.mindustry.maps.TutorialSector; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemType; @@ -79,6 +80,14 @@ public class CoreBlock extends StorageBlock{ if(entity != null) entity.solid = solid; } + @Override + public void onProximityUpdate(Tile tile) { + //add cores + if(!state.teams.get(tile.getTeam()).cores.contains(tile, true)){ + state.teams.get(tile.getTeam()).cores.add(tile); + } + } + @Override public boolean canBreak(Tile tile){ return state.teams.get(tile.getTeam()).cores.size > 1; @@ -217,7 +226,7 @@ public class CoreBlock extends StorageBlock{ } } - if(!found){ + if(!found && !TutorialSector.supressDrone()){ BaseUnit unit = droneType.create(tile.getTeam()); unit.setSpawner(tile); unit.setDead(true); diff --git a/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java b/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java index 304dba3185..b55fe56774 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java @@ -298,6 +298,14 @@ public class Reconstructor extends Block{ Call.reconstructPlayer(player, tile); } + @Override + public void transformLinks(Tile tile, int oldWidth, int oldHeight, int newWidth, int newHeight, int shiftX, int shiftY){ + super.transformLinks(tile, oldWidth, oldHeight, newWidth, newHeight, shiftX, shiftY); + + ReconstructorEntity entity = tile.entity(); + entity.link = world.transform(entity.link, oldWidth, oldHeight, newWidth, shiftX, shiftY); + } + @Override public TileEntity newEntity(){ return new ReconstructorEntity(); diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index e94f0227b2..d5eafdbc08 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -913,6 +913,7 @@ public class ServerControl extends Module{ playSectorMap(); }else if(world.getSector().currentMission().isComplete()){ + world.getSector().currentMission().onComplete(); //increment completed missions, check next index next frame world.getSector().completedMissions ++; }