Merge branch 'continuous-sectors' of https://github.com/Anuken/Mindustry

# Conflicts:
#	core/assets/bundles/bundle.properties
This commit is contained in:
Anuken 2018-09-24 22:57:49 -04:00
commit d5bb1b72b9
62 changed files with 1218 additions and 263 deletions

View file

@ -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.<br>
* These events may optionally also trigger on the caller client/server as well.<br>
*/
public class Annotations{
/** Marks a class as serializable.*/

View file

@ -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=<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.

View file

@ -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: {

View file

@ -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;

View file

@ -40,6 +40,10 @@ public class Pathfinder{
});
}
public void activateTeamPath(Team team){
createFor(team);
}
public void update(){
if(Net.client()) return;

View file

@ -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));

View file

@ -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);
}};
}

View file

@ -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));

View file

@ -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"){{

View file

@ -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"){{

View file

@ -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)){

View file

@ -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);
}

View file

@ -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();

View file

@ -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.
*/

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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<>());

View file

@ -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;

View file

@ -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;

View file

@ -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){

View file

@ -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);
}

View file

@ -13,7 +13,7 @@ public class Waves{
return Array.with(
new SpawnGroup(UnitTypes.dagger){{
end = 8;
unitScaling = 1;
unitScaling = 3;
}},
new SpawnGroup(UnitTypes.wraith){{

View file

@ -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;

View file

@ -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);

View file

@ -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()){

View file

@ -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<SaveFileVersion> versions = new IntMap<>();
public static final Array<SaveFileVersion> versionArray = Array.with(
new Save16()

View file

@ -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<Mission> missions = new Array<>();
/**Enemies spawned at this sector.*/
public transient Array<SpawnGroup> spawns;
/**Ores that appear in this sector.*/
public transient Array<Item> 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(){

View file

@ -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<Sector> 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<Item> 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<Item> 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);
}
}

View file

@ -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<Mission> getMissions(){
//int x = sectorSize/2, y = sectorSize/2;
Array<Mission> 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<Item> 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);
}
}
}

View file

@ -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<Block> turrets = find(b -> b instanceof ItemTurret);
Array<Block> powerTurrets = find(b -> b instanceof PowerTurret);

View file

@ -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<Block, Block> 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:<br>
* - setting up multiblocks<br>
* - updating cliff data<br>
* - removing ores on cliffs<br>
* 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<GridPoint2> spawnpoints = sector.currentMission().getSpawnPoints(gena);
Array<Item> 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<GridPoint2> 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<GridPoint2> spawnpoints, Array<Item> 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{

View file

@ -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");
}
}

View file

@ -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(){

View file

@ -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);
}
}

View file

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

View file

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

View file

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

View file

@ -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);
}
}

View file

@ -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<BlockLocMission> points = new Array<>();
private int completeIndex;
public LineBlockMission(Block block, int x1, int y1, int x2, int y2, int rotation){
Array<GridPoint2> 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();
}
}

View file

@ -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);
}
});
}
}

View file

@ -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<SpawnGroup> 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<SpawnGroup> getWaves(Sector sector){
return new Array<>();
}
default Array<GridPoint2> getSpawnPoints(Generation gen){
public Array<GridPoint2> 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);
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -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){
}
}

View file

@ -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

View file

@ -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<Recipe> r){
public static void getUnlockedByCategory(Category category, Array<Recipe> 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

View file

@ -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<Item> 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));
}
}

View file

@ -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)));
});

View file

@ -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();

View file

@ -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();
});

View file

@ -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){

View file

@ -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));

View file

@ -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){

View file

@ -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);

View file

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

View file

@ -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;

View file

@ -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();

View file

@ -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();

View file

@ -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);

View file

@ -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();

View file

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