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 ++;
}