Merge branches '6.0' and 'splinterface-impl' of https://github.com/Anuken/Mindustry into splinterface-impl

# Conflicts:
#	core/assets/sprites/block_colors.png
#	core/assets/sprites/sprites.atlas
#	core/assets/sprites/sprites.png
#	core/assets/sprites/sprites3.png
#	core/assets/sprites/sprites5.png
#	core/src/mindustry/Vars.java
#	core/src/mindustry/entities/traits/SaveTrait.java
#	core/src/mindustry/maps/generators/MapGenerator.java
#	core/src/mindustry/ui/dialogs/DeployDialog.java
#	core/src/mindustry/world/blocks/Floor.java
#	desktop/src/mindustry/desktop/DesktopLauncher.java
#	gradle.properties
This commit is contained in:
Anuken
2020-02-04 12:25:18 -05:00
82 changed files with 1708 additions and 502 deletions

1
.gitignore vendored
View File

@@ -42,6 +42,7 @@ changelog
*.gif *.gif
/core/assets/saves/ /core/assets/saves/
/out/ /out/
/core/assets-raw/fontgen/out/
version.properties version.properties

View File

@@ -144,7 +144,6 @@ public class AndroidLauncher extends AndroidApplication{
}, new AndroidApplicationConfiguration(){{ }, new AndroidApplicationConfiguration(){{
useImmersiveMode = true; useImmersiveMode = true;
depth = 0;
hideStatusBar = true; hideStatusBar = true;
errorHandler = CrashSender::log; errorHandler = CrashSender::log;
}}); }});

View File

@@ -249,6 +249,7 @@ project(":core"){
compile "org.lz4:lz4-java:1.4.1" compile "org.lz4:lz4-java:1.4.1"
compile arcModule("arc-core") compile arcModule("arc-core")
compile arcModule("extensions:freetype") compile arcModule("extensions:freetype")
compile arcModule("extensions:g3d")
compile arcModule("extensions:arcnet") compile arcModule("extensions:arcnet")
compile "org.mozilla:rhino:1.7.11" compile "org.mozilla:rhino:1.7.11"
if(localArc() && debugged()) compile arcModule("extensions:recorder") if(localArc() && debugged()) compile arcModule("extensions:recorder")

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -394,6 +394,8 @@ toolmode.drawteams.description = Draw teams instead of blocks.
filters.empty = [lightgray]No filters! Add one with the button below. filters.empty = [lightgray]No filters! Add one with the button below.
filter.distort = Distort filter.distort = Distort
filter.noise = Noise filter.noise = Noise
filter.enemyspawn = Enemy Spawn Select
filter.corespawn = Core Select
filter.median = Median filter.median = Median
filter.oremedian = Ore Median filter.oremedian = Ore Median
filter.blend = Blend filter.blend = Blend
@@ -413,6 +415,7 @@ filter.option.circle-scale = Circle Scale
filter.option.octaves = Octaves filter.option.octaves = Octaves
filter.option.falloff = Falloff filter.option.falloff = Falloff
filter.option.angle = Angle filter.option.angle = Angle
filter.option.amount = Amount
filter.option.block = Block filter.option.block = Block
filter.option.floor = Floor filter.option.floor = Floor
filter.option.flooronto = Target Floor filter.option.flooronto = Target Floor
@@ -850,6 +853,7 @@ liquid.temperature = [lightgray]Temperature: {0}
block.sand-boulder.name = Sand Boulder block.sand-boulder.name = Sand Boulder
block.grass.name = Grass block.grass.name = Grass
block.slag.name = Slag
block.salt.name = Salt block.salt.name = Salt
block.saltrocks.name = Salt Rocks block.saltrocks.name = Salt Rocks
block.pebbles.name = Pebbles block.pebbles.name = Pebbles

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,9 @@
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_col;
void main(){
gl_FragColor = v_col;
}

View File

@@ -0,0 +1,18 @@
attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec4 a_color;
uniform mat4 u_projModelView;
varying vec4 v_col;
const vec3 ambientColor = vec3(1.0);
const vec3 ambientDir = normalize(vec3(1.0, 1.0, 1.0));
const vec3 diffuse = vec3(0.5);
const vec3 v1 = vec3(1.0, 0.0, 1.0);
const vec3 v2 = vec3(1.0, 0.5, 0.0);
void main(){
vec3 norc = ambientColor * clamp((dot(a_normal, ambientDir) + 1.0) / 2.0, 0.0, 1.0);
v_col = a_color * vec4(norc, 1.0);
gl_Position = u_projModelView * a_position;
}

View File

@@ -0,0 +1,86 @@
#ifdef GL_ES
precision highp float;
precision mediump int;
#endif
//shade 1 + 2
#define s2 vec3(100.0, 93.0, 49.0) / 100.0
#define s1 vec3(100.0, 60.0, 25.0) / 100.0
uniform sampler2D u_texture;
uniform vec2 camerapos;
uniform vec2 screensize;
uniform float time;
varying vec4 v_color;
varying vec2 v_texCoord;
vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }
float snoise(vec2 v){
const vec4 C = vec4(0.211324865405187, 0.366025403784439,
-0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod(i, 289.0);
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
void main(){
vec2 c = v_texCoord.xy;
vec4 color = texture2D(u_texture, c);
vec2 v = vec2(1.0/screensize.x, 1.0/screensize.y);
vec2 coords = c / v + camerapos;
float stime = time / 5.0;
float mscl = 30.0;
float mth = 5.0;
//if there's something actually there
if(color.r > 0.01){
vec4 old = color;
color = texture2D(u_texture, c + vec2(sin(stime/3.0 + coords.y/0.75) * v.x, 0.0)) * vec4(0.9, 0.9, 1, 1.0);
if(color.r < 0.01){
color = old;
}
const float bs = 1.1;
float n1 = snoise(coords / (30.0 * bs) + vec2(time) / 280.0);
float n2 = snoise((coords + vec2(632.0)) / (14.0 * bs) + vec2(0.0, -time) / 290.0);
float n3 = snoise((coords + vec2(2233.0)) / (25.0 * bs) + vec2(time, 0.0) / 380.0);
float r = (n1 + n2 + n3) / 3.0;
if(r < -0.5){
color = vec4(s2, color.a);
}else if(r < -0.2){
color = vec4(s1, color.a);
}
}
gl_FragColor = color;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 283 KiB

View File

@@ -90,10 +90,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
assets.load(mods); assets.load(mods);
assets.load(schematics); assets.load(schematics);
assets.loadRun("contentinit", ContentLoader.class, () -> { assets.loadRun("contentinit", ContentLoader.class, () -> content.init(), () -> content.load());
content.init();
content.load();
});
} }
@Override @Override

View File

@@ -33,7 +33,7 @@ public class Blocks implements ContentList{
public static Block public static Block
//environment //environment
air, spawn, deepwater, water, taintedWater, tar, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater, air, spawn, deepwater, water, taintedWater, tar, slag, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater,
holostone, rocks, sporerocks, icerocks, cliffs, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster, holostone, rocks, sporerocks, icerocks, cliffs, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster,
iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, sandBoulder, grass, salt, iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, sandBoulder, grass, salt,
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks, metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks,
@@ -202,6 +202,17 @@ public class Blocks implements ContentList{
cacheLayer = CacheLayer.tar; cacheLayer = CacheLayer.tar;
}}; }};
slag = new Floor("slag"){{
drownTime = 150f;
status = StatusEffects.melting;
statusDuration = 240f;
speedMultiplier = 0.19f;
variants = 0;
liquidDrop = Liquids.slag;
isLiquid = true;
cacheLayer = CacheLayer.slag;
}};
stone = new Floor("stone"){{ stone = new Floor("stone"){{
}}; }};

View File

@@ -0,0 +1,18 @@
package mindustry.content;
import mindustry.ctype.*;
import mindustry.maps.planet.*;
import mindustry.type.*;
public class Planets implements ContentList{
//TODO make all names
public static Planet starter;
@Override
public void load(){
starter = new Planet("//TODO"){{
detail = 6;
generator = new TestPlanetGenerator();
}};
}
}

View File

@@ -0,0 +1,15 @@
package mindustry.content;
import mindustry.ctype.*;
import mindustry.type.*;
public class Weathers implements ContentList{
public static Weather
rain,
snow;
@Override
public void load(){
}
}

View File

@@ -1,15 +1,15 @@
package mindustry.content; package mindustry.content;
import mindustry.ctype.ContentList; import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.game.Objectives.*; import mindustry.game.Objectives.*;
import mindustry.game.*;
import mindustry.maps.generators.*; import mindustry.maps.generators.*;
import mindustry.maps.generators.MapGenerator.*;
import mindustry.maps.zonegen.*; import mindustry.maps.zonegen.*;
import mindustry.type.*; import mindustry.type.*;
import static arc.struct.Array.with; import static arc.struct.Array.with;
import static mindustry.content.Items.*; import static mindustry.content.Items.*;
import static mindustry.content.Planets.starter;
import static mindustry.type.ItemStack.list; import static mindustry.type.ItemStack.list;
public class Zones implements ContentList{ public class Zones implements ContentList{
@@ -22,7 +22,7 @@ public class Zones implements ContentList{
@Override @Override
public void load(){ public void load(){
groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{ groundZero = new Zone("groundZero", starter, new MapGenerator("groundZero")){{
baseLaunchCost = list(copper, -60); baseLaunchCost = list(copper, -60);
startingItems = list(copper, 60); startingItems = list(copper, 60);
alwaysUnlocked = true; alwaysUnlocked = true;
@@ -31,7 +31,7 @@ public class Zones implements ContentList{
resources = with(copper, scrap, lead); resources = with(copper, scrap, lead);
}}; }};
desertWastes = new Zone("desertWastes", new DesertWastesGenerator(260, 260)){{ desertWastes = new Zone("desertWastes", starter, new DesertWastesGenerator(260, 260)){{
startingItems = list(copper, 120); startingItems = list(copper, 120);
conditionWave = 20; conditionWave = 20;
launchPeriod = 10; launchPeriod = 10;
@@ -82,7 +82,7 @@ public class Zones implements ContentList{
); );
}}; }};
saltFlats = new Zone("saltFlats", new MapGenerator("saltFlats")){{ saltFlats = new Zone("saltFlats", starter, new MapGenerator("saltFlats")){{
startingItems = list(copper, 200, Items.silicon, 200, lead, 200); startingItems = list(copper, 200, Items.silicon, 200, lead, 200);
loadout = Loadouts.basicFoundation; loadout = Loadouts.basicFoundation;
conditionWave = 10; conditionWave = 10;
@@ -98,8 +98,7 @@ public class Zones implements ContentList{
); );
}}; }};
frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1) frozenForest = new Zone("frozenForest", starter, new MapGenerator("frozenForest")){{
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{
loadout = Loadouts.basicFoundation; loadout = Loadouts.basicFoundation;
startingItems = list(copper, 250); startingItems = list(copper, 250);
conditionWave = 10; conditionWave = 10;
@@ -111,7 +110,7 @@ public class Zones implements ContentList{
); );
}}; }};
craters = new Zone("craters", new MapGenerator("craters", 1).decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.004))){{ craters = new Zone("craters", starter, new MapGenerator("craters")){{
startingItems = list(copper, 100); startingItems = list(copper, 100);
conditionWave = 10; conditionWave = 10;
resources = with(copper, lead, coal, sand, scrap); resources = with(copper, lead, coal, sand, scrap);
@@ -122,7 +121,7 @@ public class Zones implements ContentList{
); );
}}; }};
ruinousShores = new Zone("ruinousShores", new MapGenerator("ruinousShores", 1)){{ ruinousShores = new Zone("ruinousShores", starter, new MapGenerator("ruinousShores")){{
loadout = Loadouts.basicFoundation; loadout = Loadouts.basicFoundation;
startingItems = list(copper, 140, lead, 50); startingItems = list(copper, 140, lead, 50);
conditionWave = 20; conditionWave = 20;
@@ -138,8 +137,7 @@ public class Zones implements ContentList{
); );
}}; }};
stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2) stainedMountains = new Zone("stainedMountains", starter, new MapGenerator("stainedMountains")){{
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
loadout = Loadouts.basicFoundation; loadout = Loadouts.basicFoundation;
startingItems = list(copper, 200, lead, 50); startingItems = list(copper, 200, lead, 50);
conditionWave = 10; conditionWave = 10;
@@ -153,7 +151,7 @@ public class Zones implements ContentList{
); );
}}; }};
fungalPass = new Zone("fungalPass", new MapGenerator("fungalPass")){{ fungalPass = new Zone("fungalPass", starter, new MapGenerator("fungalPass")){{
startingItems = list(copper, 250, lead, 250, Items.metaglass, 100, Items.graphite, 100); startingItems = list(copper, 250, lead, 250, Items.metaglass, 100, Items.graphite, 100);
resources = with(copper, lead, coal, titanium, sand); resources = with(copper, lead, coal, titanium, sand);
configureObjective = new Launched(this); configureObjective = new Launched(this);
@@ -166,7 +164,7 @@ public class Zones implements ContentList{
); );
}}; }};
overgrowth = new Zone("overgrowth", new MapGenerator("overgrowth")){{ overgrowth = new Zone("overgrowth", starter, new MapGenerator("overgrowth")){{
startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250); startingItems = list(copper, 1500, lead, 1000, Items.silicon, 500, Items.metaglass, 250);
conditionWave = 12; conditionWave = 12;
launchPeriod = 4; launchPeriod = 4;
@@ -183,8 +181,7 @@ public class Zones implements ContentList{
); );
}}; }};
tarFields = new Zone("tarFields", new MapGenerator("tarFields") tarFields = new Zone("tarFields", starter, new MapGenerator("tarFields")){{
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
loadout = Loadouts.basicFoundation; loadout = Loadouts.basicFoundation;
startingItems = list(copper, 250, lead, 100); startingItems = list(copper, 250, lead, 100);
conditionWave = 15; conditionWave = 15;
@@ -198,7 +195,7 @@ public class Zones implements ContentList{
); );
}}; }};
desolateRift = new Zone("desolateRift", new MapGenerator("desolateRift")){{ desolateRift = new Zone("desolateRift", starter, new MapGenerator("desolateRift")){{
loadout = Loadouts.basicNucleus; loadout = Loadouts.basicNucleus;
startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250); startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250);
conditionWave = 3; conditionWave = 3;
@@ -223,8 +220,7 @@ public class Zones implements ContentList{
resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
}};*/ }};*/
nuclearComplex = new Zone("nuclearComplex", new MapGenerator("nuclearProductionComplex", 1) nuclearComplex = new Zone("nuclearComplex", starter, new MapGenerator("nuclearProductionComplex")){{
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.01))){{
loadout = Loadouts.basicNucleus; loadout = Loadouts.basicNucleus;
startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250); startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250);
conditionWave = 30; conditionWave = 30;

View File

@@ -39,6 +39,8 @@ public class ContentLoader{
new Blocks(), new Blocks(),
new Loadouts(), new Loadouts(),
new TechTree(), new TechTree(),
new Weathers(),
new Planets(),
new Zones(), new Zones(),
new TypeIDs(), new TypeIDs(),

View File

@@ -249,7 +249,9 @@ public class Control implements ApplicationListener, Loadable{
}); });
} }
public void playZone(Zone zone){ //TODO remove, make it viable on a server
/*public void playZone(Zone zone){
ui.loadAnd(() -> { ui.loadAnd(() -> {
logic.reset(); logic.reset();
net.reset(); net.reset();
@@ -267,6 +269,16 @@ public class Control implements ApplicationListener, Loadable{
logic.play(); logic.play();
Events.fire(Trigger.newGame); Events.fire(Trigger.newGame);
}); });
}*/
public void playSector(Sector sector){
ui.loadAnd(() -> {
logic.reset();
world.loadSector(sector);
state.set(State.playing);
logic.play();
ui.planet.hide();
});
} }
public void playTutorial(){ public void playTutorial(){
@@ -277,8 +289,8 @@ public class Control implements ApplicationListener, Loadable{
world.beginMapLoad(); world.beginMapLoad();
world.createTiles(zone.generator.width, zone.generator.height); world.resize(zone.generator.width, zone.generator.height);
zone.generator.generate(world.getTiles()); zone.generator.generate(world.tiles);
Tile coreb = null; Tile coreb = null;

View File

@@ -236,6 +236,7 @@ public class Logic implements ApplicationListener{
bulletGroup.update(); bulletGroup.update();
tileGroup.update(); tileGroup.update();
fireGroup.update(); fireGroup.update();
weatherGroup.update();
}else{ }else{
unitGroup.updateEvents(); unitGroup.updateEvents();
collisions.updatePhysics(unitGroup); collisions.updatePhysics(unitGroup);

View File

@@ -63,7 +63,7 @@ public class UI implements ApplicationListener, Loadable{
public TraceDialog traces; public TraceDialog traces;
public DatabaseDialog database; public DatabaseDialog database;
public ContentInfoDialog content; public ContentInfoDialog content;
public DeployDialog deploy; public PlanetDialog planet;
public TechTreeDialog tech; public TechTreeDialog tech;
//public MinimapDialog minimap; //public MinimapDialog minimap;
public SchematicsDialog schematics; public SchematicsDialog schematics;
@@ -176,7 +176,7 @@ public class UI implements ApplicationListener, Loadable{
traces = new TraceDialog(); traces = new TraceDialog();
maps = new MapsDialog(); maps = new MapsDialog();
content = new ContentInfoDialog(); content = new ContentInfoDialog();
deploy = new DeployDialog(); planet = new PlanetDialog();
tech = new TechTreeDialog(); tech = new TechTreeDialog();
mods = new ModsDialog(); mods = new ModsDialog();
schematics = new SchematicsDialog(); schematics = new SchematicsDialog();

View File

@@ -1,11 +1,13 @@
package mindustry.core; package mindustry.core;
import arc.*; import arc.*;
import arc.func.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*; import arc.struct.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import arc.util.*; import arc.util.*;
import mindustry.content.*;
import mindustry.core.GameState.*; import mindustry.core.GameState.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.*; import mindustry.game.*;
@@ -14,7 +16,6 @@ import mindustry.io.*;
import mindustry.maps.*; import mindustry.maps.*;
import mindustry.maps.filters.*; import mindustry.maps.filters.*;
import mindustry.maps.filters.GenerateFilter.*; import mindustry.maps.filters.GenerateFilter.*;
import mindustry.maps.generators.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.*; import mindustry.world.blocks.*;
@@ -25,7 +26,7 @@ public class World{
public final Context context = new Context(); public final Context context = new Context();
private Map currentMap; private Map currentMap;
private Tile[][] tiles; public @NonNull Tiles tiles = new Tiles(0, 0);
private boolean generating, invalidMap; private boolean generating, invalidMap;
@@ -67,11 +68,11 @@ public class World{
} }
public int width(){ public int width(){
return tiles == null ? 0 : tiles.length; return tiles.width();
} }
public int height(){ public int height(){
return tiles == null ? 0 : tiles[0].length; return tiles.height();
} }
public int unitWidth(){ public int unitWidth(){
@@ -88,11 +89,7 @@ public class World{
} }
public @Nullable Tile tile(int x, int y){ public @Nullable Tile tile(int x, int y){
if(tiles == null){ return tiles.get(x, y);
return null;
}
if(!Structs.inBounds(x, y, tiles)) return null;
return tiles[x][y];
} }
public @Nullable Tile ltile(int x, int y){ public @Nullable Tile ltile(int x, int y){
@@ -102,7 +99,7 @@ public class World{
} }
public Tile rawTile(int x, int y){ public Tile rawTile(int x, int y){
return tiles[x][y]; return tiles.getn(x, y);
} }
public @Nullable Tile tileWorld(float x, float y){ public @Nullable Tile tileWorld(float x, float y){
@@ -117,16 +114,10 @@ public class World{
return Math.round(coord / tilesize); return Math.round(coord / tilesize);
} }
public Tile[][] getTiles(){
return tiles;
}
private void clearTileEntities(){ private void clearTileEntities(){
for(int x = 0; x < tiles.length; x++){ for(Tile tile : tiles){
for(int y = 0; y < tiles[0].length; y++){ if(tile != null && tile.entity != null){
if(tiles[x][y] != null && tiles[x][y].entity != null){ tile.entity.remove();
tiles[x][y].entity.remove();
}
} }
} }
} }
@@ -135,15 +126,11 @@ public class World{
* Resizes the tile array to the specified size and returns the resulting tile array. * Resizes the tile array to the specified size and returns the resulting tile array.
* Only use for loading saves! * Only use for loading saves!
*/ */
public Tile[][] createTiles(int width, int height){ public Tiles resize(int width, int height){
if(tiles != null){ clearTileEntities();
clearTileEntities();
if(tiles.length != width || tiles[0].length != height){ if(tiles.width() != width || tiles.height() != height){
tiles = new Tile[width][height]; tiles = new Tiles(width, height);
}
}else{
tiles = new Tile[width][height];
} }
return tiles; return tiles;
@@ -164,14 +151,11 @@ public class World{
public void endMapLoad(){ public void endMapLoad(){
prepareTiles(tiles); prepareTiles(tiles);
for(int x = 0; x < tiles.length; x++){ for(Tile tile : tiles){
for(int y = 0; y < tiles[0].length; y++){ tile.updateOcclusion();
Tile tile = tiles[x][y];
tile.updateOcclusion();
if(tile.entity != null){ if(tile.entity != null){
tile.entity.updateProximity(); tile.entity.updateProximity();
}
} }
} }
@@ -179,7 +163,7 @@ public class World{
addDarkness(tiles); addDarkness(tiles);
} }
entities.all().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2)); entities.all().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.width() * tilesize + finalWorldBounds * 2, tiles.height() * tilesize + finalWorldBounds * 2));
generating = false; generating = false;
Events.fire(new WorldLoadEvent()); Events.fire(new WorldLoadEvent());
@@ -201,15 +185,32 @@ public class World{
return state.rules.zone; return state.rules.zone;
} }
public void loadGenerator(Generator generator){ public void loadGenerator(int width, int height, Cons<Tiles> generator){
beginMapLoad(); beginMapLoad();
createTiles(generator.width, generator.height); resize(width, height);
generator.generate(tiles); generator.get(tiles);
endMapLoad(); endMapLoad();
} }
public void loadSector(Sector sector){
int size = (int)(sector.rect.radius * 2500);
loadGenerator(size, size, tiles -> {
TileGen gen = new TileGen();
tiles.each((x, y) -> {
gen.reset();
Vec3 position = sector.rect.project(x / (float)size, y / (float)size);
sector.planet.generator.generate(position, gen);
tiles.set(x, y, new Tile(x, y, gen.floor, gen.overlay, gen.block));
});
tiles.get(size/2, size/2).setBlock(Blocks.coreShard, Team.sharded);
});
}
public void loadMap(Map map){ public void loadMap(Map map){
loadMap(map, new Rules()); loadMap(map, new Rules());
} }
@@ -297,58 +298,56 @@ public class World{
} }
} }
public void addDarkness(Tile[][] tiles){ public void addDarkness(Tiles tiles){
byte[][] dark = new byte[tiles.length][tiles[0].length]; byte[] dark = new byte[tiles.width() * tiles.height()];
byte[][] writeBuffer = new byte[tiles.length][tiles[0].length]; byte[] writeBuffer = new byte[tiles.width() * tiles.height()];
byte darkIterations = 4; byte darkIterations = 4;
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){ for(int i = 0; i < dark.length; i++){
Tile tile = tiles[x][y]; Tile tile = tiles.geti(i);
if(tile.isDarkened()){ if(tile.isDarkened()){
dark[x][y] = darkIterations; dark[i] = darkIterations;
}
} }
} }
for(int i = 0; i < darkIterations; i++){ for(int i = 0; i < darkIterations; i++){
for(int x = 0; x < tiles.length; x++){ for(Tile tile : tiles){
for(int y = 0; y < tiles[0].length; y++){ int idx = tile.y * tiles.width() + tile.x;
boolean min = false; boolean min = false;
for(Point2 point : Geometry.d4){ for(Point2 point : Geometry.d4){
int newX = x + point.x, newY = y + point.y; int newX = tile.x + point.x, newY = tile.y + point.y;
if(Structs.inBounds(newX, newY, tiles) && dark[newX][newY] < dark[x][y]){ int nidx = newY * tiles.width() + newX;
min = true; if(tiles.in(newX, newY) && dark[nidx] < dark[idx]){
break; min = true;
} break;
} }
writeBuffer[x][y] = (byte)Math.max(0, dark[x][y] - Mathf.num(min));
} }
writeBuffer[idx] = (byte)Math.max(0, dark[idx] - Mathf.num(min));
} }
for(int x = 0; x < tiles.length; x++){ System.arraycopy(writeBuffer, 0, dark, 0, writeBuffer.length);
System.arraycopy(writeBuffer[x], 0, dark[x], 0, tiles[0].length);
}
} }
for(int x = 0; x < tiles.length; x++){ for(Tile tile : tiles){
for(int y = 0; y < tiles[0].length; y++){ int idx = tile.y * tiles.width() + tile.x;
Tile tile = tiles[x][y];
if(tile.isDarkened()){
tiles[x][y].rotation(dark[x][y]);
}
if(dark[x][y] == 4){
boolean full = true;
for(Point2 p : Geometry.d4){
int px = p.x + x, py = p.y + y;
if(Structs.inBounds(px, py, tiles) && !(tiles[px][py].isDarkened() && dark[px][py] == 4)){
full = false;
break;
}
}
if(full) tiles[x][y].rotation(5); if(tile.isDarkened()){
tile.rotation(dark[idx]);
}
if(dark[idx] == 4){
boolean full = true;
for(Point2 p : Geometry.d4){
int px = p.x + tile.x, py = p.y + tile.y;
int nidx = py * tiles.width() + px;
if(tiles.in(px, py) && !(tile.isDarkened() && dark[nidx] == 4)){
full = false;
break;
}
} }
if(full) tile.rotation(5);
} }
} }
} }
@@ -359,18 +358,13 @@ public class World{
* - updating occlusion<br> * - updating occlusion<br>
* Usually used before placing structures on a tile array. * Usually used before placing structures on a tile array.
*/ */
public void prepareTiles(Tile[][] tiles){ public void prepareTiles(Tiles tiles){
//find multiblocks //find multiblocks
IntArray multiblocks = new IntArray(); IntArray multiblocks = new IntArray();
for(Tile tile : tiles){
for(int x = 0; x < tiles.length; x++){ if(tile.block().isMultiblock()){
for(int y = 0; y < tiles[0].length; y++){ multiblocks.add(tile.pos());
Tile tile = tiles[x][y];
if(tile.block().isMultiblock()){
multiblocks.add(tile.pos());
}
} }
} }
@@ -380,9 +374,10 @@ public class World{
int x = Pos.x(pos); int x = Pos.x(pos);
int y = Pos.y(pos); int y = Pos.y(pos);
Tile tile = tiles.getn(x, y);
Block result = tiles[x][y].block(); Block result = tile.block();
Team team = tiles[x][y].getTeam(); Team team = tile.getTeam();
int offsetx = -(result.size - 1) / 2; int offsetx = -(result.size - 1) / 2;
int offsety = -(result.size - 1) / 2; int offsety = -(result.size - 1) / 2;
@@ -409,17 +404,19 @@ public class World{
private class Context implements WorldContext{ private class Context implements WorldContext{
@Override @Override
public Tile tile(int x, int y){ public Tile tile(int x, int y){
return tiles[x][y]; return tiles.get(x, y);
} }
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
createTiles(width, height); World.this.resize(width, height);
} }
@Override @Override
public Tile create(int x, int y, int floorID, int overlayID, int wallID){ public Tile create(int x, int y, int floorID, int overlayID, int wallID){
return (tiles[x][y] = new Tile(x, y, floorID, overlayID, wallID)); Tile tile = new Tile(x, y, floorID, overlayID, wallID);
tiles.set(x, y, tile);
return tile;
} }
@Override @Override
@@ -454,23 +451,8 @@ public class World{
GenerateInput input = new GenerateInput(); GenerateInput input = new GenerateInput();
for(GenerateFilter filter : filters){ for(GenerateFilter filter : filters){
input.begin(filter, width(), height(), (x, y) -> tiles[x][y]); input.begin(filter, width(), height(), (x, y) -> tiles.getn(x, y));
filter.apply(tiles, input);
//actually apply the filter
for(int x = 0; x < width(); x++){
for(int y = 0; y < height(); y++){
Tile tile = rawTile(x, y);
input.apply(x, y, tile.floor(), tile.block(), tile.overlay());
filter.apply(input);
tile.setFloor((Floor)input.floor);
tile.setOverlay(input.ore);
if(!tile.block().synthetic() && !input.block.synthetic()){
tile.setBlock(input.block);
}
}
}
} }
} }

View File

@@ -14,7 +14,8 @@ public enum ContentType{
zone, zone,
loadout, loadout,
typeid, typeid,
error; error,
planet;
public static final ContentType[] all = values(); public static final ContentType[] all = values();
} }

View File

@@ -54,7 +54,7 @@ public abstract class UnlockableContent extends MappableContent{
public void onUnlock(){ public void onUnlock(){
} }
/** Whether this content is always hidden in the content info dialog. */ /** Whether this content is always hidden in the content database dialog. */
public boolean isHidden(){ public boolean isHidden(){
return false; return false;
} }

View File

@@ -13,7 +13,6 @@ import mindustry.world.modules.*;
import static mindustry.Vars.state; import static mindustry.Vars.state;
import static mindustry.Vars.ui; import static mindustry.Vars.ui;
//TODO somehow remove or replace this class with a more flexible solution
public class EditorTile extends Tile{ public class EditorTile extends Tile{
public EditorTile(int x, int y, int floor, int overlay, int wall){ public EditorTile(int x, int y, int floor, int overlay, int wall){

View File

@@ -69,25 +69,23 @@ public class MapEditor{
} }
//adds missing blockparts //adds missing blockparts
//TODO remove, may not be necessary with blockpart refactor later
public void checkLinkedTiles(){ public void checkLinkedTiles(){
Tile[][] tiles = world.getTiles(); Tiles tiles = world.tiles;
//clear block parts first //clear old parts
for(int x = 0; x < width(); x++){ for(Tile tile : tiles){
for(int y = 0; y < height(); y++){ if(tile.block() instanceof BlockPart){
if(tiles[x][y].block() instanceof BlockPart){ tile.setBlock(Blocks.air);
tiles[x][y].setBlock(Blocks.air);
}
} }
} }
//set up missing blockparts //re-add them
for(int x = 0; x < width(); x++){ for(Tile tile : tiles){
for(int y = 0; y < height(); y++){ if(tile.block().isMultiblock()){
if(tiles[x][y].block().isMultiblock()){ tile.set(tile.block(), tile.getTeam());
tiles[x][y].set(tiles[x][y].block(), tiles[x][y].getTeam());
}
} }
} }
} }
@@ -99,11 +97,11 @@ public class MapEditor{
/** Creates a 2-D array of EditorTiles with stone as the floor block. */ /** Creates a 2-D array of EditorTiles with stone as the floor block. */
private void createTiles(int width, int height){ private void createTiles(int width, int height){
Tile[][] tiles = world.createTiles(width, height); Tiles tiles = world.resize(width, height);
for(int x = 0; x < width; x++){ for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){ for(int y = 0; y < height; y++){
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0); tiles.set(x, y, new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0));
} }
} }
} }
@@ -119,8 +117,8 @@ public class MapEditor{
tags = new StringMap(); tags = new StringMap();
} }
public Tile[][] tiles(){ public Tiles tiles(){
return world.getTiles(); return world.tiles;
} }
public Tile tile(int x, int y){ public Tile tile(int x, int y){
@@ -245,20 +243,20 @@ public class MapEditor{
public void resize(int width, int height){ public void resize(int width, int height){
clearOp(); clearOp();
Tile[][] previous = world.getTiles(); Tiles previous = world.tiles;
int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2; int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2;
loading = true; loading = true;
Tile[][] tiles = world.createTiles(width, height); Tiles tiles = world.resize(width, height);
for(int x = 0; x < width; x++){ for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){ for(int y = 0; y < height; y++){
int px = offsetX + x, py = offsetY + y; int px = offsetX + x, py = offsetY + y;
if(Structs.inBounds(px, py, previous.length, previous[0].length)){ if(previous.in(px, py)){
tiles[x][y] = previous[px][py]; tiles.set(x, y, previous.getn(px, py));
tiles[x][y].x = (short)x; tiles.getn(x, y).x = (short)x;
tiles[x][y].y = (short)y; tiles.getn(x, y).y = (short)y;
}else{ }else{
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0); tiles.set(x, y, new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0));
} }
} }
} }
@@ -314,12 +312,14 @@ public class MapEditor{
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
world.createTiles(width, height); world.resize(width, height);
} }
@Override @Override
public Tile create(int x, int y, int floorID, int overlayID, int wallID){ public Tile create(int x, int y, int floorID, int overlayID, int wallID){
return (tiles()[x][y] = new EditorTile(x, y, floorID, overlayID, wallID)); Tile tile = new EditorTile(x, y, floorID, overlayID, wallID);
tiles().set(x, y, tile);
return tile;
} }
@Override @Override

View File

@@ -30,7 +30,7 @@ public class MapGenerateDialog extends FloatingDialog{
private final Prov<GenerateFilter>[] filterTypes = new Prov[]{ private final Prov<GenerateFilter>[] filterTypes = new Prov[]{
NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new,
RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new, RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new,
BlendFilter::new, MirrorFilter::new, ClearFilter::new BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, EnemySpawnFilter::new
}; };
private final MapEditor editor; private final MapEditor editor;
private final boolean applied; private final boolean applied;
@@ -263,7 +263,7 @@ public class MapGenerateDialog extends FloatingDialog{
//all the options //all the options
c.table(f -> { c.table(f -> {
f.left().top(); f.left().top();
for(FilterOption option : filter.options){ for(FilterOption option : filter.options()){
option.changed = this::update; option.changed = this::update;
f.table(t -> { f.table(t -> {
@@ -292,7 +292,7 @@ public class MapGenerateDialog extends FloatingDialog{
for(Prov<GenerateFilter> gen : filterTypes){ for(Prov<GenerateFilter> gen : filterTypes){
GenerateFilter filter = gen.get(); GenerateFilter filter = gen.get();
if(!applied && filter.buffered) continue; if((!applied && filter.isBuffered()) || (filter.isPost() && applied)) continue;
selection.cont.addButton(filter.name(), () -> { selection.cont.addButton(filter.name(), () -> {
filters.add(filter); filters.add(filter);
@@ -360,21 +360,17 @@ public class MapGenerateDialog extends FloatingDialog{
for(GenerateFilter filter : copy){ for(GenerateFilter filter : copy){
input.begin(filter, editor.width(), editor.height(), (x, y) -> buffer1[Mathf.clamp(x / scaling, 0, pixmap.getWidth()-1)][Mathf.clamp(y / scaling, 0, pixmap.getHeight()-1)].tile()); input.begin(filter, editor.width(), editor.height(), (x, y) -> buffer1[Mathf.clamp(x / scaling, 0, pixmap.getWidth()-1)][Mathf.clamp(y / scaling, 0, pixmap.getHeight()-1)].tile());
//read from buffer1 and write to buffer2 //read from buffer1 and write to buffer2
for(int px = 0; px < pixmap.getWidth(); px++){ pixmap.each((px, py) -> {
for(int py = 0; py < pixmap.getHeight(); py++){ int x = px * scaling, y = py * scaling;
int x = px * scaling, y = py * scaling; GenTile tile = buffer1[px][py];
GenTile tile = buffer1[px][py]; input.apply(x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore));
input.apply(x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore)); filter.apply(input);
filter.apply(input); buffer2[px][py].set(input.floor, input.block, input.ore, Team.get(tile.team), tile.rotation);
buffer2[px][py].set(input.floor, input.block, input.ore, Team.get(tile.team), tile.rotation); });
}
} pixmap.each((px, py) -> buffer1[px][py].set(buffer2[px][py]));
for(int px = 0; px < pixmap.getWidth(); px++){
for(int py = 0; py < pixmap.getHeight(); py++){
buffer1[px][py].set(buffer2[px][py]);
}
}
} }
for(int px = 0; px < pixmap.getWidth(); px++){ for(int px = 0; px < pixmap.getWidth(); px++){

View File

@@ -109,7 +109,7 @@ public class MapRenderer implements Disposable{
private void render(int wx, int wy){ private void render(int wx, int wy){
int x = wx / chunkSize, y = wy / chunkSize; int x = wx / chunkSize, y = wy / chunkSize;
IndexedRenderer mesh = chunks[x][y]; IndexedRenderer mesh = chunks[x][y];
Tile tile = editor.tiles()[wx][wy]; Tile tile = editor.tiles().getn(wx, wy);
Team team = tile.getTeam(); Team team = tile.getTeam();
Block floor = tile.floor(); Block floor = tile.floor();

View File

@@ -2,6 +2,7 @@ package mindustry.entities;
import java.io.*; import java.io.*;
/** Marks something as saveable; not necessarily used for entities. */
public interface Saveable{ public interface Saveable{
void writeSave(DataOutput stream) throws IOException; void writeSave(DataOutput stream) throws IOException;
void readSave(DataInput stream, byte version) throws IOException; void readSave(DataInput stream, byte version) throws IOException;

View File

@@ -48,7 +48,7 @@ public class MusicControl{
public void update(){ public void update(){
if(state.is(State.menu)){ if(state.is(State.menu)){
silenced = false; silenced = false;
if(ui.deploy.isShown()){ if(ui.planet.isShown()){
play(Musics.launch); play(Musics.launch);
}else if(ui.editor.isShown()){ }else if(ui.editor.isShown()){
play(Musics.editor); play(Musics.editor);

View File

@@ -31,6 +31,17 @@ public enum CacheLayer{
endShader(Shaders.tar); endShader(Shaders.tar);
} }
}, },
slag{
@Override
public void begin(){
beginShader();
}
@Override
public void end(){
endShader(Shaders.slag);
}
},
normal, normal,
walls; walls;

View File

@@ -43,7 +43,7 @@ public class MenuRenderer implements Disposable{
} }
private void generate(){ private void generate(){
Tile[][] tiles = world.createTiles(width, height); Tiles tiles = world.resize(width, height);
Array<Block> ores = content.blocks().select(b -> b instanceof OreBlock); Array<Block> ores = content.blocks().select(b -> b instanceof OreBlock);
shadows = new FrameBuffer(width, height); shadows = new FrameBuffer(width, height);
int offset = Mathf.random(100000); int offset = Mathf.random(100000);
@@ -154,10 +154,10 @@ public class MenuRenderer implements Disposable{
} }
Tile tile; Tile tile;
tiles[x][y] = (tile = new CachedTile()); tiles.set(x, y, (tile = new CachedTile()));
tile.x = (short)x; tile.x = (short)x;
tile.y = (short)y; tile.y = (short)y;
tile.setFloor((Floor) floor); tile.setFloor(floor.asFloor());
tile.setBlock(wall); tile.setBlock(wall);
tile.setOverlay(ore); tile.setOverlay(ore);
} }

View File

@@ -0,0 +1,266 @@
package mindustry.graphics;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
//TODO clean this up somehow
public class PlanetGrid{
private static final PlanetGrid[] cache = new PlanetGrid[10];
private static final float x = -0.525731112119133606f;
private static final float z = -0.850650808352039932f;
private static final Vec3[] iTiles = {
new Vec3(-x, 0, z), new Vec3(x, 0, z), new Vec3(-x, 0, -z), new Vec3(x, 0, -z),
new Vec3(0, z, x), new Vec3(0, z, -x), new Vec3(0, -z, x), new Vec3(0, -z, -x),
new Vec3(z, x, 0), new Vec3(-z, x, 0), new Vec3(z, -x, 0), new Vec3(-z, -x, 0)
};
private static final int[][] iTilesP = {
{9, 4, 1, 6, 11}, {4, 8, 10, 6, 0}, {11, 7, 3, 5, 9}, {2, 7, 10, 8, 5},
{9, 5, 8, 1, 0}, {2, 3, 8, 4, 9}, {0, 1, 10, 7, 11}, {11, 6, 10, 3, 2},
{5, 3, 10, 1, 4}, {2, 5, 4, 0, 11}, {3, 7, 6, 1, 8}, {7, 2, 9, 0, 6}
};
public final int size;
public final Ptile[] tiles;
public final Corner[] corners;
public final Edge[] edges;
PlanetGrid(int size){
this.size = size;
tiles = new Ptile[tileCount(size)];
for(int i = 0; i < tiles.length; i++){
tiles[i] = new Ptile(i, i < 12 ? 5 : 6);
}
corners = new Corner[cornerCount(size)];
for(int i = 0; i < corners.length; i++){
corners[i] = new Corner(i);
}
edges = new Edge[edgeCount(size)];
for(int i = 0; i < edges.length; i++){
edges[i] = new Edge(i);
}
}
public static PlanetGrid newGrid(int size){
//cache grids between calls, since only ~5 different grids total are needed
if(size < cache.length && cache[size] != null){
return cache[size];
}
PlanetGrid result;
if(size == 0){
result = initialGrid();
}else{
result = subdividedGrid(newGrid(size - 1));
}
//store grid in cache
if(size < cache.length){
cache[size] = result;
}
return result;
}
static PlanetGrid initialGrid(){
PlanetGrid grid = new PlanetGrid(0);
for(Ptile t : grid.tiles){
t.v = iTiles[t.id];
for(int k = 0; k < 5; k++){
t.tiles[k] = grid.tiles[iTilesP[t.id][k]];
}
}
for(int i = 0; i < 5; i++){
addCorner(i, grid, 0, iTilesP[0][(i + 4) % 5], iTilesP[0][i]);
}
for(int i = 0; i < 5; i++){
addCorner(i + 5, grid, 3, iTilesP[3][(i + 4) % 5], iTilesP[3][i]);
}
addCorner(10, grid, 10, 1, 8);
addCorner(11, grid, 1, 10, 6);
addCorner(12, grid, 6, 10, 7);
addCorner(13, grid, 6, 7, 11);
addCorner(14, grid, 11, 7, 2);
addCorner(15, grid, 11, 2, 9);
addCorner(16, grid, 9, 2, 5);
addCorner(17, grid, 9, 5, 4);
addCorner(18, grid, 4, 5, 8);
addCorner(19, grid, 4, 8, 1);
//add corners to corners
for(Corner c : grid.corners){
for(int k = 0; k < 3; k++){
c.corners[k] = c.tiles[k].corners[(pos(c.tiles[k], c) + 1) % 5];
}
}
//new edges
int nextEdge = 0;
for(Ptile t : grid.tiles){
for(int k = 0; k < 5; k++){
if(t.edges[k] == null){
addEdge(nextEdge++, grid, t.id, iTilesP[t.id][k]);
}
}
}
return grid;
}
static PlanetGrid subdividedGrid(PlanetGrid prev){
PlanetGrid grid = new PlanetGrid(prev.size + 1);
int prevTiles = prev.tiles.length;
int prevCorners = prev.corners.length;
//old tiles
for(int i = 0; i < prevTiles; i++){
grid.tiles[i].v = prev.tiles[i].v;
for(int k = 0; k < grid.tiles[i].edgeCount; k++){
grid.tiles[i].tiles[k] = grid.tiles[prev.tiles[i].corners[k].id + prevTiles];
}
}
//old corners become tiles
for(int i = 0; i < prevCorners; i++){
grid.tiles[i + prevTiles].v = prev.corners[i].v;
for(int k = 0; k < 3; k++){
grid.tiles[i + prevTiles].tiles[2 * k] = grid.tiles[prev.corners[i].corners[k].id + prevTiles];
grid.tiles[i + prevTiles].tiles[2 * k + 1] = grid.tiles[prev.corners[i].tiles[k].id];
}
}
//new corners
int nextCorner = 0;
for(Ptile n : prev.tiles){
Ptile t = grid.tiles[n.id];
for(int k = 0; k < t.edgeCount; k++){
addCorner(nextCorner, grid, t.id, t.tiles[(k + t.edgeCount - 1) % t.edgeCount].id, t.tiles[k].id);
nextCorner++;
}
}
//connect corners
for(Corner c : grid.corners){
for(int k = 0; k < 3; k++){
c.corners[k] = c.tiles[k].corners[(pos(c.tiles[k], c) + 1) % (c.tiles[k].edgeCount)];
}
}
//new edges
int nextEdge = 0;
for(Ptile t : grid.tiles){
for(int k = 0; k < t.edgeCount; k++){
if(t.edges[k] == null){
addEdge(nextEdge, grid, t.id, t.tiles[k].id);
nextEdge++;
}
}
}
return grid;
}
static void addCorner(int id, PlanetGrid grid, int t1, int t2, int t3){
Corner c = grid.corners[id];
Ptile[] t = {grid.tiles[t1], grid.tiles[t2], grid.tiles[t3]};
c.v = Tmp.v31.set(t[0].v).add(t[1].v).add(t[2].v).cpy().nor();
for(int i = 0; i < 3; i++){
t[i].corners[pos(t[i], t[(i + 2) % 3])] = c;
c.tiles[i] = t[i];
}
}
static void addEdge(int id, PlanetGrid grid, int t1, int t2){
Edge e = grid.edges[id];
Ptile[] t = {grid.tiles[t1], grid.tiles[t2]};
Corner[] c = {
grid.corners[t[0].corners[pos(t[0], t[1])].id],
grid.corners[t[0].corners[(pos(t[0], t[1]) + 1) % t[0].edgeCount].id]};
for(int i = 0; i < 2; i++){
t[i].edges[pos(t[i], t[(i + 1) % 2])] = e;
e.tiles[i] = t[i];
c[i].edges[pos(c[i], c[(i + 1) % 2])] = e;
e.corners[i] = c[i];
}
}
static int pos(Ptile t, Ptile n){
for(int i = 0; i < t.edgeCount; i++)
if(t.tiles[i] == n)
return i;
return -1;
}
static int pos(Ptile t, Corner c){
for(int i = 0; i < t.edgeCount; i++)
if(t.corners[i] == c)
return i;
return -1;
}
static int pos(Corner c, Corner n){
for(int i = 0; i < 3; i++)
if(c.corners[i] == n)
return i;
return -1;
}
static int tileCount(int size){
return 10 * Mathf.pow(3, size) + 2;
}
static int cornerCount(int size){
return 20 * Mathf.pow(3, size);
}
static int edgeCount(int size){
return 30 * Mathf.pow(3, size);
}
public static class Ptile{
public final int id;
public final int edgeCount;
public final Ptile[] tiles;
public final Corner[] corners;
public final Edge[] edges;
public Vec3 v = new Vec3();
public Ptile(int id, int edgeCount){
this.id = id;
this.edgeCount = edgeCount;
tiles = new Ptile[edgeCount];
corners = new Corner[edgeCount];
edges = new Edge[edgeCount];
}
}
public static class Corner{
public final int id;
public final Ptile[] tiles = new Ptile[3];
public final Corner[] corners = new Corner[3];
public final Edge[] edges = new Edge[3];
public Vec3 v = new Vec3();
public Vec3 bv = new Vec3();
public Corner(int id){
this.id = id;
}
}
public static class Edge{
public final int id;
public final Ptile[] tiles = new Ptile[2];
public final Corner[] corners = new Corner[2];
public Edge(int id){
this.id = id;
}
}
}

View File

@@ -0,0 +1,140 @@
package mindustry.graphics;
import arc.graphics.*;
import arc.graphics.VertexAttributes.*;
import arc.graphics.gl.*;
import arc.math.geom.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.graphics.PlanetGrid.*;
import mindustry.maps.planet.*;
public class PlanetMesh{
private float[] floats = new float[3 + 3 + 1];
private Vec3 vec = new Vec3();
private Mesh mesh;
private PlanetGrid grid;
private Vec3 center = new Vec3();
private boolean lines;
private float radius, intensity = 0.2f;
private final PlanetGenerator gen;
public PlanetMesh(int divisions, PlanetGenerator gen){
this(divisions, gen, 1f, false);
}
public PlanetMesh(int divisions, PlanetGenerator gen, float radius, boolean lines){
this.gen = gen;
this.radius = radius;
this.grid = PlanetGrid.newGrid(divisions);
this.lines = lines;
int vertices = grid.tiles.length * 12 * (3 + 3 + 1);
mesh = new Mesh(true, vertices, 0,
new VertexAttribute(Usage.position, 3, Shader.positionAttribute),
new VertexAttribute(Usage.normal, 3, Shader.normalAttribute),
new VertexAttribute(Usage.colorPacked, 4, Shader.colorAttribute));
mesh.getVerticesBuffer().limit(mesh.getMaxVertices());
mesh.getVerticesBuffer().position(0);
generateMesh();
}
/** @return the sector that is hit by this ray, or null if nothing intersects it. */
public @Nullable Ptile getTile(Ray ray){
boolean found = Intersector3D.intersectRaySphere(ray, center, radius, Tmp.v33);
if(!found) return null;
return Structs.findMin(grid.tiles, t -> t.v.dst(Tmp.v33));
}
public void render(Mat3D mat){
Shaders.planet.begin();
Shaders.planet.setUniformMatrix4("u_projModelView", mat.val);
mesh.render(Shaders.planet, lines ? Gl.lines : Gl.triangles);
Shaders.planet.end();
}
private void generateMesh(){
for(Ptile tile : grid.tiles){
Vec3 nor = Tmp.v31.setZero();
Corner[] c = tile.corners;
for(Corner corner : c){
corner.bv.set(corner.v).setLength(radius);
}
for(Corner corner : c){
corner.v.setLength(radius + elevation(corner.bv)*intensity);
}
for(Corner corner : c){
nor.add(corner.v);
}
nor.nor();
Vec3 realNormal = normal(c[0].v, c[2].v, c[4].v);
nor.set(realNormal);
Color color = color(tile.v);
if(lines){
nor.set(1f, 1f, 1f);
for(int i = 0; i < c.length; i++){
Vec3 v1 = c[i].v;
Vec3 v2 = c[(i + 1) % c.length].v;
vert(v1, nor, color);
vert(v2, nor, color);
}
}else{
verts(c[0].v, c[1].v, c[2].v, nor, color);
verts(c[0].v, c[2].v, c[3].v, nor, color);
verts(c[0].v, c[3].v, c[4].v, nor, color);
if(c.length > 5){
verts(c[0].v, c[4].v, c[5].v, nor, color);
}else{
verts(c[0].v, c[3].v, c[4].v, nor, color);
}
}
}
}
private Vec3 normal(Vec3 v1, Vec3 v2, Vec3 v3){
return Tmp.v32.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor();
}
private float elevation(Vec3 v){
return gen.getHeight(vec.set(v).scl(1f / radius));
}
private Color color(Vec3 v){
return gen.getColor(vec.set(v).scl(1f / radius));
}
private void verts(Vec3 a, Vec3 b, Vec3 c, Vec3 normal, Color color){
vert(a, normal, color);
vert(b, normal, color);
vert(c, normal, color);
}
private void vert(Vec3 a, Vec3 normal, Color color){
floats[0] = a.x;
floats[1] = a.y;
floats[2] = a.z;
floats[3] = normal.x;
floats[4] = normal.y;
floats[5] = normal.z;
floats[6] = color.toFloatBits();
mesh.getVerticesBuffer().put(floats);
}
}

View File

@@ -0,0 +1,123 @@
package mindustry.graphics;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.graphics.g3d.*;
import arc.input.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.graphics.PlanetGrid.*;
import mindustry.maps.planet.*;
import mindustry.type.*;
import mindustry.type.Sector.*;
import mindustry.world.*;
import static mindustry.Vars.*;
public class PlanetRenderer implements PlanetGenerator{
private final Color outlineColor = Pal.accent.cpy().a(0.7f);
private final float camLength = 4f, outlineRad = 1.15f;
private final boolean drawnRect = true;
private final PlanetMesh[] outlines = new PlanetMesh[10];
private final Camera3D cam = new Camera3D();
private final VertexBatch3D batch = new VertexBatch3D(false, true, 0);
private float lastX, lastY;
public PlanetRenderer(){
Tmp.v1.trns(0, camLength);
cam.position.set(Tmp.v1.x, 0f, Tmp.v1.y);
}
public void render(Planet planet){
Draw.flush();
Gl.clear(Gl.depthBufferBit);
Gl.enable(Gl.depthTest);
input();
cam.resize(Core.graphics.getWidth(), Core.graphics.getHeight());
cam.update();
cam.lookAt(0, 0, 0);
cam.update();
PlanetMesh outline = outline(planet.size);
planet.mesh.render(cam.combined());
outline.render(cam.combined());
Ptile tile = outline.getTile(cam.getPickRay(Core.input.mouseX(), Core.input.mouseY()));
if(tile != null){
Sector sector = planet.getSector(tile);
for(int i = 0; i < sector.tile.corners.length; i++){
batch.color(outlineColor);
batch.vertex(sector.tile.corners[i].v);
}
batch.flush(cam.combined(), Gl.triangleFan);
if(drawnRect){
//TODO hack.
SectorRect rect = sector.rect;
rect.center.scl(outlineRad);
rect.right.scl(outlineRad);
rect.top.scl(outlineRad);
batch.color(Pal.place);
batch.vertex(rect.project(0, 0));
batch.color(Pal.place);
batch.vertex(rect.project(1, 0));
batch.color(Pal.place);
batch.vertex(rect.project(1, 1));
batch.color(Pal.place);
batch.vertex(rect.project(0, 1));
batch.flush(cam.combined(), Gl.lineLoop);
rect.center.scl(1f / outlineRad);
rect.right.scl(1f / outlineRad);
rect.top.scl(1f / outlineRad);
if(Core.input.keyTap(KeyCode.SPACE)){
control.playSector(sector);
ui.planet.hide();
}
}
}
Gl.disable(Gl.depthTest);
}
private PlanetMesh outline(int size){
if(outlines[size] == null){
outlines[size] = new PlanetMesh(size, this, outlineRad, true);
}
return outlines[size];
}
private void input(){
Vec3 v = Tmp.v33.set(Core.input.mouseX(), Core.input.mouseY(), 0);
if(Core.input.keyDown(KeyCode.MOUSE_LEFT)){
cam.position.rotate(Vec3.Y, (v.x - lastX) / 10);
}
lastX = v.x;
lastY = v.y;
}
@Override
public float getHeight(Vec3 position){
return 0;
}
@Override
public Color getColor(Vec3 position){
return outlineColor;
}
@Override
public void generate(Vec3 position, TileGen tile){
}
}

View File

@@ -11,13 +11,13 @@ import arc.util.Time;
public class Shaders{ public class Shaders{
public static Shadow shadow; public static Shadow shadow;
public static BlockBuild blockbuild; public static BlockBuild blockbuild;
public static @Nullable public static @Nullable Shield shield;
Shield shield;
public static UnitBuild build; public static UnitBuild build;
public static FogShader fog; public static FogShader fog;
public static MenuShader menu; public static MenuShader menu;
public static LightShader light; public static LightShader light;
public static SurfaceShader water, tar; public static SurfaceShader water, tar, slag;
public static Shader planet;
public static void init(){ public static void init(){
shadow = new Shadow(); shadow = new Shadow();
@@ -35,6 +35,8 @@ public class Shaders{
light = new LightShader(); light = new LightShader();
water = new SurfaceShader("water"); water = new SurfaceShader("water");
tar = new SurfaceShader("tar"); tar = new SurfaceShader("tar");
slag = new SurfaceShader("slag");
planet = new LoadShader("planet", "planet");
} }
public static class LightShader extends LoadShader{ public static class LightShader extends LoadShader{

View File

@@ -133,11 +133,11 @@ public class MapIO{
} }
} }
public static Pixmap generatePreview(Tile[][] tiles){ public static Pixmap generatePreview(Tiles tiles){
Pixmap pixmap = new Pixmap(tiles.length, tiles[0].length, Format.RGBA8888); Pixmap pixmap = new Pixmap(tiles.width(), tiles.height(), Format.RGBA8888);
for(int x = 0; x < pixmap.getWidth(); x++){ for(int x = 0; x < pixmap.getWidth(); x++){
for(int y = 0; y < pixmap.getHeight(); y++){ for(int y = 0; y < pixmap.getHeight(); y++){
Tile tile = tiles[x][y]; Tile tile = tiles.getn(x, y);
pixmap.draw(x, pixmap.getHeight() - 1 - y, colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam())); pixmap.draw(x, pixmap.getHeight() - 1 - y, colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam()));
} }
} }
@@ -152,12 +152,12 @@ public class MapIO{
} }
/** Reads a pixmap in the 3.5 pixmap format. */ /** Reads a pixmap in the 3.5 pixmap format. */
public static void readPixmap(Pixmap pixmap, Tile[][] tiles){ public static void readPixmap(Pixmap pixmap, Tiles tiles){
for(int x = 0; x < pixmap.getWidth(); x++){ for(int x = 0; x < pixmap.getWidth(); x++){
for(int y = 0; y < pixmap.getHeight(); y++){ for(int y = 0; y < pixmap.getHeight(); y++){
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y); int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
LegacyBlock block = LegacyColorMapper.get(color); LegacyBlock block = LegacyColorMapper.get(color);
Tile tile = tiles[x][y]; Tile tile = tiles.getn(x, y);
tile.setFloor(block.floor); tile.setFloor(block.floor);
tile.setBlock(block.wall); tile.setBlock(block.wall);

View File

@@ -212,7 +212,7 @@ public class Maps{
for(int x = 0; x < map.width; x++){ for(int x = 0; x < map.width; x++){
for(int y = 0; y < map.height; y++){ for(int y = 0; y < map.height; y++){
Tile tile = world.getTiles()[x][y]; Tile tile = world.rawTile(x, y);
if(tile.block() instanceof CoreBlock){ if(tile.block() instanceof CoreBlock){
map.teams.add(tile.getTeamID()); map.teams.add(tile.getTeamID());
@@ -228,7 +228,7 @@ public class Maps{
Core.assets.unload(map.previewFile().path() + "." + mapExtension); Core.assets.unload(map.previewFile().path() + "." + mapExtension);
} }
Pixmap pix = MapIO.generatePreview(world.getTiles()); Pixmap pix = MapIO.generatePreview(world.tiles);
executor.submit(() -> map.previewFile().writePNG(pix)); executor.submit(() -> map.previewFile().writePNG(pix));
writeCache(map); writeCache(map);

View File

@@ -1,6 +1,7 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.math.*; import arc.math.*;
import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.maps.filters.FilterOption.*; import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*; import mindustry.world.*;
@@ -11,16 +12,21 @@ public class BlendFilter extends GenerateFilter{
float radius = 2f; float radius = 2f;
Block block = Blocks.stone, floor = Blocks.ice, ignore = Blocks.air; Block block = Blocks.stone, floor = Blocks.ice, ignore = Blocks.air;
{ @Override
buffered = true; public FilterOption[] options(){
options( return Structs.arr(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f), new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f),
new BlockOption("block", () -> block, b -> block = b, anyOptional), new BlockOption("block", () -> block, b -> block = b, anyOptional),
new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly), new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly),
new BlockOption("ignore", () -> ignore, b -> ignore = b, floorsOptional) new BlockOption("ignore", () -> ignore, b -> ignore = b, floorsOptional)
); );
} }
@Override
public boolean isBuffered(){
return true;
}
@Override @Override
public void apply(){ public void apply(){
if(in.floor == block || block == Blocks.air || in.floor == ignore) return; if(in.floor == block || block == Blocks.air || in.floor == ignore) return;
@@ -31,7 +37,7 @@ public class BlendFilter extends GenerateFilter{
outer: outer:
for(int x = -rad; x <= rad; x++){ for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){ for(int y = -rad; y <= rad; y++){
if(Mathf.dst2(x, y) > rad*rad) continue; if(Mathf.within(x, y, rad)) continue;
Tile tile = in.tile(in.x + x, in.y + y); Tile tile = in.tile(in.x + x, in.y + y);
if(tile.floor() == block || tile.block() == block || tile.overlay() == block){ if(tile.floor() == block || tile.block() == block || tile.overlay() == block){

View File

@@ -1,5 +1,6 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.world.*; import mindustry.world.*;
@@ -8,10 +9,9 @@ import static mindustry.maps.filters.FilterOption.*;
public class ClearFilter extends GenerateFilter{ public class ClearFilter extends GenerateFilter{
protected Block block = Blocks.air; protected Block block = Blocks.air;
{ @Override
options( public FilterOption[] options(){
new BlockOption("block", () -> block, b -> block = b, wallsOnly) return Structs.arr(new BlockOption("block", () -> block, b -> block = b, wallsOnly));
);
} }
@Override @Override

View File

@@ -0,0 +1,43 @@
package mindustry.maps.filters;
import arc.struct.*;
import arc.util.*;
import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*;
import mindustry.world.blocks.storage.*;
import static mindustry.Vars.*;
/** Selects X spawns from the core spawn pool.*/
public class CoreSpawnFilter extends GenerateFilter{
int amount = 1;
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("amount", () -> amount, f -> amount = (int)f, 1, 10).display()
);
}
@Override
public void apply(Tiles tiles, GenerateInput in){
IntArray spawns = new IntArray();
for(Tile tile : tiles){
if(tile.getTeam() == state.rules.defaultTeam && tile.block() instanceof CoreBlock){
spawns.add(tile.pos());
}
}
spawns.shuffle();
int used = Math.min(spawns.size, amount);
for(int i = used; i < spawns.size; i++){
tiles.getp(spawns.get(i)).remove();
}
}
@Override
public boolean isPost(){
return true;
}
}

View File

@@ -1,5 +1,6 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.util.*;
import mindustry.maps.filters.FilterOption.*; import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.*; import mindustry.world.blocks.*;
@@ -7,14 +8,19 @@ import mindustry.world.blocks.*;
public class DistortFilter extends GenerateFilter{ public class DistortFilter extends GenerateFilter{
float scl = 40, mag = 5; float scl = 40, mag = 5;
{ @Override
buffered = true; public FilterOption[] options(){
options( return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 200f), new SliderOption("scale", () -> scl, f -> scl = f, 1f, 200f),
new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f) new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f)
); );
} }
@Override
public boolean isBuffered(){
return true;
}
@Override @Override
public void apply(){ public void apply(){
Tile tile = in.tile(in.x + noise(in.x, in.y, scl, mag) - mag / 2f, in.y + noise(in.x, in.y + o, scl, mag) - mag / 2f); Tile tile = in.tile(in.x + noise(in.x, in.y, scl, mag) - mag / 2f, in.y + noise(in.x, in.y + o, scl, mag) - mag / 2f);

View File

@@ -0,0 +1,42 @@
package mindustry.maps.filters;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*;
/** Selects X spawns from the spawn pool.*/
public class EnemySpawnFilter extends GenerateFilter{
int amount = 1;
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("amount", () -> amount, f -> amount = (int)f, 1, 10).display()
);
}
@Override
public void apply(Tiles tiles, GenerateInput in){
IntArray spawns = new IntArray();
for(Tile tile : tiles){
if(tile.overlay() == Blocks.spawn){
spawns.add(tile.pos());
}
}
spawns.shuffle();
int used = Math.min(spawns.size, amount);
for(int i = used; i < spawns.size; i++){
Tile tile = tiles.getp(spawns.get(i));
tile.clearOverlay();
}
}
@Override
public boolean isPost(){
return true;
}
}

View File

@@ -35,6 +35,8 @@ public abstract class FilterOption{
final Floatc setter; final Floatc setter;
final float min, max, step; final float min, max, step;
boolean display;
SliderOption(String name, Floatp getter, Floatc setter, float min, float max){ SliderOption(String name, Floatp getter, Floatc setter, float min, float max){
this(name, getter, setter, min, max, (max - min) / 200); this(name, getter, setter, min, max, (max - min) / 200);
} }
@@ -48,9 +50,18 @@ public abstract class FilterOption{
this.step = step; this.step = step;
} }
public SliderOption display(){
display = true;
return this;
}
@Override @Override
public void build(Table table){ public void build(Table table){
table.add("$filter.option." + name); if(!display){
table.add("$filter.option." + name);
}else{
table.label(() -> Core.bundle.get("filter.option." + name) + ": " + (int)getter.get());
}
table.row(); table.row();
Slider slider = table.addSlider(min, max, step, setter).growX().get(); Slider slider = table.addSlider(min, max, step, setter).growX().get();
slider.setValue(getter.get()); slider.setValue(getter.get());

View File

@@ -7,32 +7,37 @@ import arc.util.*;
import arc.util.noise.*; import arc.util.noise.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.*;
public abstract class GenerateFilter{ public abstract class GenerateFilter{
protected transient float o = (float)(Math.random() * 10000000.0); protected transient float o = (float)(Math.random() * 10000000.0);
protected transient long seed; protected transient long seed;
protected transient GenerateInput in; protected transient GenerateInput in;
public transient boolean buffered = false; public void apply(Tiles tiles, GenerateInput in){
public transient FilterOption[] options; this.in = in;
for(Tile tile : tiles){
in.apply(tile.x, tile.y, tile.floor(), tile.block(), tile.overlay());
apply();
tile.setFloor(in.floor.asFloor());
tile.setOverlay(in.floor.asFloor().isLiquid ? Blocks.air : in.ore);
if(!tile.block().synthetic() && !in.block.synthetic()){
tile.setBlock(in.block);
}
}
}
public final void apply(GenerateInput in){ public final void apply(GenerateInput in){
this.in = in; this.in = in;
apply(); apply();
//remove extra ores on liquids
if(((Floor)in.floor).isLiquid){
in.ore = Blocks.air;
}
} }
/** sets up the options; this is necessary since the constructor can't access subclass variables. */ /** @return a new array of options for configuring this filter */
protected void options(FilterOption... options){ public abstract FilterOption[] options();
this.options = options;
}
/** apply the actual filter on the input */ /** apply the actual filter on the input */
protected abstract void apply(); protected void apply(){}
/** draw any additional guides */ /** draw any additional guides */
public void draw(Image image){} public void draw(Image image){}
@@ -47,6 +52,16 @@ public abstract class GenerateFilter{
seed = Mathf.random(99999999); seed = Mathf.random(99999999);
} }
/** @return whether this filter needs a read/write buffer (e.g. not a 1:1 tile mapping). */
public boolean isBuffered(){
return false;
}
/** @return whether this filter can *only* be used while generating the map, e.g. is not undoable. */
public boolean isPost(){
return false;
}
//utility generation functions //utility generation functions
protected float noise(float x, float y, float scl, float mag){ protected float noise(float x, float y, float scl, float mag){

View File

@@ -2,6 +2,7 @@ package mindustry.maps.filters;
import arc.struct.*; import arc.struct.*;
import arc.math.*; import arc.math.*;
import arc.util.*;
import mindustry.maps.filters.FilterOption.*; import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*; import mindustry.world.*;
@@ -12,14 +13,19 @@ public class MedianFilter extends GenerateFilter{
float percentile = 0.5f; float percentile = 0.5f;
IntArray blocks = new IntArray(), floors = new IntArray(); IntArray blocks = new IntArray(), floors = new IntArray();
{ @Override
buffered = true; public FilterOption[] options(){
options( return Structs.arr(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f), new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f) new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
); );
} }
@Override
public boolean isBuffered(){
return true;
}
@Override @Override
public void apply(){ public void apply(){
int rad = (int)radius; int rad = (int)radius;

View File

@@ -15,8 +15,11 @@ public class MirrorFilter extends GenerateFilter{
int angle = 45; int angle = 45;
{ @Override
options(new SliderOption("angle", () -> angle, f -> angle = (int)f, 0, 360, 45)); public FilterOption[] options(){
return Structs.arr(
new SliderOption("angle", () -> angle, f -> angle = (int)f, 0, 360, 45)
);
} }
@Override @Override

View File

@@ -1,5 +1,6 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.Blocks; import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption; import mindustry.maps.filters.FilterOption.BlockOption;
import mindustry.maps.filters.FilterOption.SliderOption; import mindustry.maps.filters.FilterOption.SliderOption;
@@ -12,8 +13,9 @@ public class NoiseFilter extends GenerateFilter{
float scl = 40, threshold = 0.5f, octaves = 3f, falloff = 0.5f; float scl = 40, threshold = 0.5f, octaves = 3f, falloff = 0.5f;
Block floor = Blocks.stone, block = Blocks.rocks; Block floor = Blocks.stone, block = Blocks.rocks;
{ @Override
options( public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f),
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f), new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),

View File

@@ -1,5 +1,6 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.Blocks; import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.SliderOption; import mindustry.maps.filters.FilterOption.SliderOption;
import mindustry.world.Block; import mindustry.world.Block;
@@ -11,13 +12,14 @@ public class OreFilter extends GenerateFilter{
public float scl = 23, threshold = 0.81f, octaves = 2f, falloff = 0.3f; public float scl = 23, threshold = 0.81f, octaves = 2f, falloff = 0.3f;
public Block ore = Blocks.oreCopper; public Block ore = Blocks.oreCopper;
{ @Override
options( public FilterOption[] options(){
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), return Structs.arr(
new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f), new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f),
new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f), new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),
new BlockOption("ore", () -> ore, b -> ore = b, oresOnly) new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f),
new BlockOption("ore", () -> ore, b -> ore = b, oresOnly)
); );
} }

View File

@@ -2,6 +2,7 @@ package mindustry.maps.filters;
import arc.struct.*; import arc.struct.*;
import arc.math.*; import arc.math.*;
import arc.util.*;
import mindustry.*; import mindustry.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.maps.filters.FilterOption.*; import mindustry.maps.filters.FilterOption.*;
@@ -13,14 +14,19 @@ public class OreMedianFilter extends GenerateFilter{
private IntArray blocks = new IntArray(); private IntArray blocks = new IntArray();
{ @Override
buffered = true; public FilterOption[] options(){
options( return Structs.arr(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f), new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f) new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
); );
} }
@Override
public boolean isBuffered(){
return true;
}
@Override @Override
public void apply(){ public void apply(){
if(in.ore == Blocks.spawn) return; if(in.ore == Blocks.spawn) return;

View File

@@ -0,0 +1,35 @@
package mindustry.maps.filters;
import arc.math.*;
import arc.struct.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.storage.*;
public class RandomItemFilter extends GenerateFilter{
public Array<ItemStack> drops = new Array<>();
public float chance = 0.3f;
@Override
public FilterOption[] options(){
return new FilterOption[0];
}
@Override
public void apply(Tiles tiles, GenerateInput in){
for(Tile tile : tiles){
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock)){
for(ItemStack stack : drops){
if(Mathf.chance(chance)){
tile.entity.items.add(stack.item, Math.min(Mathf.random(stack.amount), tile.block().itemCapacity));
}
}
}
}
}
@Override
public boolean isPost(){
return true;
}
}

View File

@@ -1,5 +1,6 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.Blocks; import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption; import mindustry.maps.filters.FilterOption.BlockOption;
import mindustry.maps.filters.FilterOption.SliderOption; import mindustry.maps.filters.FilterOption.SliderOption;
@@ -12,8 +13,9 @@ public class RiverNoiseFilter extends GenerateFilter{
float scl = 40, threshold = 0f, threshold2 = 0.1f; float scl = 40, threshold = 0f, threshold2 = 0.1f;
Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandRocks; Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandRocks;
{ @Override
options( public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, -1f, 0.3f), new SliderOption("threshold", () -> threshold, f -> threshold = f, -1f, 0.3f),
new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, -1f, 0.3f), new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, -1f, 0.3f),

View File

@@ -1,5 +1,6 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.Blocks; import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption; import mindustry.maps.filters.FilterOption.BlockOption;
import mindustry.maps.filters.FilterOption.SliderOption; import mindustry.maps.filters.FilterOption.SliderOption;
@@ -11,8 +12,9 @@ public class ScatterFilter extends GenerateFilter{
protected float chance = 0.014f; protected float chance = 0.014f;
protected Block flooronto = Blocks.air, floor = Blocks.air, block = Blocks.air; protected Block flooronto = Blocks.air, floor = Blocks.air, block = Blocks.air;
{ @Override
options( public FilterOption[] options(){
return Structs.arr(
new SliderOption("chance", () -> chance, f -> chance = f, 0f, 1f), new SliderOption("chance", () -> chance, f -> chance = f, 0f, 1f),
new BlockOption("flooronto", () -> flooronto, b -> flooronto = b, floorsOptional), new BlockOption("flooronto", () -> flooronto, b -> flooronto = b, floorsOptional),
new BlockOption("floor", () -> floor, b -> floor = b, floorsOptional), new BlockOption("floor", () -> floor, b -> floor = b, floorsOptional),

View File

@@ -1,6 +1,7 @@
package mindustry.maps.filters; package mindustry.maps.filters;
import arc.math.Mathf; import arc.math.Mathf;
import arc.util.*;
import mindustry.content.Blocks; import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption; import mindustry.maps.filters.FilterOption.BlockOption;
import mindustry.maps.filters.FilterOption.SliderOption; import mindustry.maps.filters.FilterOption.SliderOption;
@@ -13,8 +14,9 @@ public class TerrainFilter extends GenerateFilter{
float scl = 40, threshold = 0.9f, octaves = 3f, falloff = 0.5f, magnitude = 1f, circleScl = 2.1f; float scl = 40, threshold = 0.9f, octaves = 3f, falloff = 0.5f, magnitude = 1f, circleScl = 2.1f;
Block floor = Blocks.stone, block = Blocks.rocks; Block floor = Blocks.stone, block = Blocks.rocks;
{ @Override
options( public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("mag", () -> magnitude, f -> magnitude = f, 0f, 2f), new SliderOption("mag", () -> magnitude, f -> magnitude = f, 0f, 2f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f),

View File

@@ -0,0 +1,4 @@
package mindustry.maps.generators;
public class BaseGenerator{
}

View File

@@ -26,14 +26,14 @@ public abstract class BasicGenerator extends RandomGenerator{
} }
@Override @Override
public void generate(Tile[][] tiles){ public void generate(Tiles tiles){
int seed = Mathf.random(99999999); int seed = Mathf.random(99999999);
sim.setSeed(seed); sim.setSeed(seed);
sim2.setSeed(seed + 1); sim2.setSeed(seed + 1);
super.generate(tiles); super.generate(tiles);
} }
public void ores(Tile[][] tiles){ public void ores(Tiles tiles){
pass(tiles, (x, y) -> { pass(tiles, (x, y) -> {
if(ores != null){ if(ores != null){
int offsetX = x - 4, offsetY = y + 23; int offsetX = x - 4, offsetY = y + 23;
@@ -49,7 +49,7 @@ public abstract class BasicGenerator extends RandomGenerator{
}); });
} }
public void terrain(Tile[][] tiles, Block dst, float scl, float mag, float cmag){ public void terrain(Tiles tiles, Block dst, float scl, float mag, float cmag){
pass(tiles, (x, y) -> { pass(tiles, (x, y) -> {
double rocks = sim.octaveNoise2D(5, 0.5, 1f / scl, x, y) * mag double rocks = sim.octaveNoise2D(5, 0.5, 1f / scl, x, y) * mag
+ Mathf.dst((float)x / width, (float)y / height, 0.5f, 0.5f) * cmag; + Mathf.dst((float)x / width, (float)y / height, 0.5f, 0.5f) * cmag;
@@ -66,11 +66,11 @@ public abstract class BasicGenerator extends RandomGenerator{
}); });
} }
public void noise(Tile[][] tiles, Block floor, Block block, int octaves, float falloff, float scl, float threshold){ public void noise(Tiles tiles, Block floor, Block block, int octaves, float falloff, float scl, float threshold){
sim.setSeed(Mathf.random(99999)); sim.setSeed(Mathf.random(99999));
pass(tiles, (x, y) -> { pass(tiles, (x, y) -> {
if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold){ if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold){
Tile tile = tiles[x][y]; Tile tile = tiles.getn(x, y);
this.floor = floor; this.floor = floor;
if(tile.block().solid){ if(tile.block().solid){
this.block = block; this.block = block;
@@ -79,16 +79,16 @@ public abstract class BasicGenerator extends RandomGenerator{
}); });
} }
public void overlay(Tile[][] tiles, Block floor, Block block, float chance, int octaves, float falloff, float scl, float threshold){ public void overlay(Tiles tiles, Block floor, Block block, float chance, int octaves, float falloff, float scl, float threshold){
sim.setSeed(Mathf.random(99999)); sim.setSeed(Mathf.random(99999));
pass(tiles, (x, y) -> { pass(tiles, (x, y) -> {
if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold && Mathf.chance(chance) && tiles[x][y].floor() == floor){ if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold && Mathf.chance(chance) && tiles.getn(x, y).floor() == floor){
ore = block; ore = block;
} }
}); });
} }
public void tech(Tile[][] tiles){ public void tech(Tiles tiles){
Block[] blocks = {Blocks.darkPanel3}; Block[] blocks = {Blocks.darkPanel3};
int secSize = 20; int secSize = 20;
pass(tiles, (x, y) -> { pass(tiles, (x, y) -> {
@@ -109,13 +109,13 @@ public abstract class BasicGenerator extends RandomGenerator{
}); });
} }
public void distort(Tile[][] tiles, float scl, float mag){ public void distort(Tiles tiles, float scl, float mag){
Block[][] blocks = new Block[width][height]; Block[][] blocks = new Block[width][height];
Floor[][] floors = new Floor[width][height]; Floor[][] floors = new Floor[width][height];
each((x, y) -> { each((x, y) -> {
float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x, y + 1525215f, scl, mag) - mag / 2f; float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x, y + 1525215f, scl, mag) - mag / 2f;
Tile other = tiles[Mathf.clamp((int)cx, 0, width-1)][Mathf.clamp((int)cy, 0, height-1)]; Tile other = tiles.getn(Mathf.clamp((int)cx, 0, width-1), Mathf.clamp((int)cy, 0, height-1));
blocks[x][y] = other.block(); blocks[x][y] = other.block();
floors[x][y] = other.floor(); floors[x][y] = other.floor();
}); });
@@ -126,7 +126,7 @@ public abstract class BasicGenerator extends RandomGenerator{
}); });
} }
public void scatter(Tile[][] tiles, Block target, Block dst, float chance){ public void scatter(Tiles tiles, Block target, Block dst, float chance){
pass(tiles, (x, y) -> { pass(tiles, (x, y) -> {
if(!Mathf.chance(chance)) return; if(!Mathf.chance(chance)) return;
if(floor == target){ if(floor == target){
@@ -149,40 +149,40 @@ public abstract class BasicGenerator extends RandomGenerator{
return (float)sim2.octaveNoise2D(1f, 0f, 1f / scl, x + 0x361266f, y + 0x251259f) * mag; return (float)sim2.octaveNoise2D(1f, 0f, 1f / scl, x + 0x361266f, y + 0x251259f) * mag;
} }
public void pass(Tile[][] tiles, Intc2 r){ public void pass(Tiles tiles, Intc2 r){
for(int x = 0; x < width; x++){ for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){ for(int y = 0; y < height; y++){
floor = tiles[x][y].floor(); floor = tiles.getn(x, y).floor();
block = tiles[x][y].block(); block = tiles.getn(x, y).block();
ore = tiles[x][y].overlay(); ore = tiles.getn(x, y).overlay();
r.get(x, y); r.get(x, y);
tiles[x][y] = new Tile(x, y, floor.id, ore.id, block.id); tiles.set(x, y, new Tile(x, y, floor, ore, block));
} }
} }
} }
public void brush(Tile[][] tiles, Array<Tile> path, int rad){ public void brush(Tiles tiles, Array<Tile> path, int rad){
path.each(tile -> erase(tiles, tile.x, tile.y, rad)); path.each(tile -> erase(tiles, tile.x, tile.y, rad));
} }
public void erase(Tile[][] tiles, int cx, int cy, int rad){ public void erase(Tiles tiles, int cx, int cy, int rad){
for(int x = -rad; x <= rad; x++){ for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){ for(int y = -rad; y <= rad; y++){
int wx = cx + x, wy = cy + y; int wx = cx + x, wy = cy + y;
if(Structs.inBounds(wx, wy, width, height) && Mathf.dst(x, y, 0, 0) <= rad){ if(Structs.inBounds(wx, wy, width, height) && Mathf.dst(x, y, 0, 0) <= rad){
Tile other = tiles[wx][wy]; Tile other = tiles.getn(wx, wy);
other.setBlock(Blocks.air); other.setBlock(Blocks.air);
} }
} }
} }
} }
public Array<Tile> pathfind(Tile[][] tiles, int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh){ public Array<Tile> pathfind(Tiles tiles, int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh){
Tile start = tiles[startX][startY]; Tile start = tiles.getn(startX, startY);
Tile end = tiles[endX][endY]; Tile end = tiles.getn(endX, endY);
GridBits closed = new GridBits(width, height); GridBits closed = new GridBits(width, height);
IntFloatMap costs = new IntFloatMap(); IntFloatMap costs = new IntFloatMap();
PriorityQueue<Tile> queue = new PriorityQueue<>(tiles.length * tiles[0].length / 2, (a, b) -> Float.compare(costs.get(a.pos(), 0f) + dh.cost(a.x, a.y, end.x, end.y), costs.get(b.pos(), 0f) + dh.cost(b.x, b.y, end.x, end.y))); PriorityQueue<Tile> queue = new PriorityQueue<>(tiles.width() * tiles.height()/4, (a, b) -> Float.compare(costs.get(a.pos(), 0f) + dh.cost(a.x, a.y, end.x, end.y), costs.get(b.pos(), 0f) + dh.cost(b.x, b.y, end.x, end.y)));
queue.add(start); queue.add(start);
boolean found = false; boolean found = false;
while(!queue.isEmpty()){ while(!queue.isEmpty()){
@@ -196,7 +196,7 @@ public abstract class BasicGenerator extends RandomGenerator{
for(Point2 point : Geometry.d4){ for(Point2 point : Geometry.d4){
int newx = next.x + point.x, newy = next.y + point.y; int newx = next.x + point.x, newy = next.y + point.y;
if(Structs.inBounds(newx, newy, width, height)){ if(Structs.inBounds(newx, newy, width, height)){
Tile child = tiles[newx][newy]; Tile child = tiles.getn(newx, newy);
if(!closed.get(child.x, child.y)){ if(!closed.get(child.x, child.y)){
closed.set(child.x, child.y); closed.set(child.x, child.y);
child.rotation(child.relativeTo(next.x, next.y)); child.rotation(child.relativeTo(next.x, next.y));
@@ -215,7 +215,7 @@ public abstract class BasicGenerator extends RandomGenerator{
while(current != start){ while(current != start){
out.add(current); out.add(current);
Point2 p = Geometry.d4(current.rotation()); Point2 p = Geometry.d4(current.rotation());
current = tiles[current.x + p.x][current.y + p.y]; current = tiles.getn(current.x + p.x, current.y + p.y);
} }
out.reverse(); out.reverse();
@@ -223,17 +223,17 @@ public abstract class BasicGenerator extends RandomGenerator{
return out; return out;
} }
public void inverseFloodFill(Tile[][] tiles, Tile start, Block block){ public void inverseFloodFill(Tiles tiles, Tile start, Block block){
IntArray arr = new IntArray(); IntArray arr = new IntArray();
arr.add(start.pos()); arr.add(start.pos());
while(!arr.isEmpty()){ while(!arr.isEmpty()){
int i = arr.pop(); int i = arr.pop();
int x = Pos.x(i), y = Pos.y(i); int x = Pos.x(i), y = Pos.y(i);
tiles[x][y].cost = 2; tiles.getn(x, y).cost = 2;
for(Point2 point : Geometry.d4){ for(Point2 point : Geometry.d4){
int newx = x + point.x, newy = y + point.y; int newx = x + point.x, newy = y + point.y;
if(Structs.inBounds(newx, newy, width, height)){ if(tiles.in(newx, newy)){
Tile child = tiles[newx][newy]; Tile child = tiles.getn(newx, newy);
if(child.block() == Blocks.air && child.cost != 2){ if(child.block() == Blocks.air && child.cost != 2){
child.cost = 2; child.cost = 2;
arr.add(child.pos()); arr.add(child.pos());
@@ -244,7 +244,7 @@ public abstract class BasicGenerator extends RandomGenerator{
for(int x = 0; x < width; x ++){ for(int x = 0; x < width; x ++){
for(int y = 0; y < height; y++){ for(int y = 0; y < height; y++){
Tile tile = tiles[x][y]; Tile tile = tiles.getn(x, y);
if(tile.cost != 2 && tile.block() == Blocks.air){ if(tile.cost != 2 && tile.block() == Blocks.air){
tile.setBlock(block); tile.setBlock(block);
} }

View File

@@ -19,5 +19,5 @@ public abstract class Generator{
this.loadout = loadout; this.loadout = loadout;
} }
public abstract void generate(Tile[][] tiles); public abstract void generate(Tiles tiles);
} }

View File

@@ -1,53 +1,33 @@
package mindustry.maps.generators; package mindustry.maps.generators;
import arc.struct.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.io.*; import mindustry.io.*;
import mindustry.maps.*; import mindustry.maps.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
//TODO:
//- limited # of enemy spawns as filter
//- spawn loadout selection as filter
//- configure map loadout, make 1 core the default
public class MapGenerator extends Generator{ public class MapGenerator extends Generator{
private Map map; private Map map;
private String mapName; private String mapName;
private Array<Decoration> decorations = Array.with(new Decoration(Blocks.stone, Blocks.rock, 0.003f));
/**
* The amount of final enemy spawns used. -1 to use everything in the map.
* This amount of enemy spawns is selected randomly from the map.
*/
public int enemySpawns = -1;
/** Whether floor is distorted along with blocks. */
public boolean distortFloor = false;
public MapGenerator(String mapName){ public MapGenerator(String mapName){
this.mapName = mapName; this.mapName = mapName;
} }
public MapGenerator(String mapName, int enemySpawns){
this.mapName = mapName;
this.enemySpawns = enemySpawns;
}
public MapGenerator decor(Decoration... decor){
this.decorations.addAll(decor);
return this;
}
public void removePrefix(String name){ public void removePrefix(String name){
this.mapName = this.mapName.substring(name.length() + 1); this.mapName = this.mapName.substring(name.length() + 1);
} }
{
decor(new Decoration(Blocks.snow, Blocks.snowrock, 0.01), new Decoration(Blocks.ignarock, Blocks.pebbles, 0.03f));
}
public Map getMap(){ public Map getMap(){
return map; return map;
} }
@@ -61,110 +41,44 @@ public class MapGenerator extends Generator{
} }
@Override @Override
public void generate(Tile[][] tiles){ public void generate(Tiles tiles){
for(int x = 0; x < width; x++){ tiles.fill();
for(int y = 0; y < height; y++){
tiles[x][y] = new Tile(x, y);
}
}
SaveIO.load(map.file); SaveIO.load(map.file);
Array<Point2> players = new Array<>();
Array<Point2> enemies = new Array<>();
for(int x = 0; x < width; x++){ for(Tile tile : tiles){
for(int y = 0; y < height; y++){ if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock) && world.getZone() != null){
if(tiles[x][y].block() instanceof CoreBlock && tiles[x][y].getTeam() == state.rules.defaultTeam){ for(Item item : world.getZone().resources){
players.add(new Point2(x, y)); if(Mathf.chance(0.3)){
tiles[x][y].setBlock(Blocks.air); tile.entity.items().add(item, Math.min(Mathf.random(500), tile.block().itemCapacity));
}
if(tiles[x][y].overlay() == Blocks.spawn && enemySpawns != -1){
enemies.add(new Point2(x, y));
tiles[x][y].setOverlay(Blocks.air);
}
if(tiles[x][y].block() instanceof BlockPart){
tiles[x][y].setBlock(Blocks.air);
}
}
}
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
Tile tile = tiles[x][y];
for(Decoration decor : decorations){
if(x > 0 && y > 0 && (tiles[x - 1][y].block() == decor.wall || tiles[x][y - 1].block() == decor.wall)){
continue;
}
if(tile.block() == Blocks.air && !(decor.wall instanceof Floor) && tile.floor() == decor.floor && Mathf.chance(decor.chance)){
tile.setBlock(decor.wall);
}else if(tile.floor() == decor.floor && decor.wall.isOverlay() && Mathf.chance(decor.chance)){
tile.setOverlay(decor.wall);
}else if(tile.floor() == decor.floor && decor.wall.isFloor() && !decor.wall.isOverlay() && Mathf.chance(decor.chance)){
tile.setFloor((Floor)decor.wall);
}
}
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock) && world.getZone() != null){
for(Item item : world.getZone().resources){
if(Mathf.chance(0.3)){
tile.entity.items().add(item, Math.min(Mathf.random(500), tile.block().itemCapacity));
}
} }
} }
} }
} }
if(enemySpawns != -1){ boolean anyCores = false;
if(enemySpawns > enemies.size){
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number for map: " + mapName); for(Tile tile : tiles){
if(tile.overlay() == Blocks.spawn){
int rad = 10;
Geometry.circle(tile.x, tile.y, tiles.width(), tiles.height(), rad, (wx, wy) -> {
if(tile.overlay().itemDrop != null){
tile.clearOverlay();
}
});
} }
enemies.shuffle(); if(tile.block() instanceof CoreBlock && tile.getTeam() == state.rules.defaultTeam){
for(int i = 0; i < enemySpawns; i++){ schematics.placeLoadout(loadout, tile.x, tile.y);
Point2 point = enemies.get(i); anyCores = true;
tiles[point.x][point.y].setOverlay(Blocks.spawn);
int rad = 10, frad = 12;
for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){
int wx = x + point.x, wy = y + point.y;
double dst = Mathf.dst(x, y);
if(dst < frad && Structs.inBounds(wx, wy, tiles) && (dst <= rad || Mathf.chance(0.5))){
Tile tile = tiles[wx][wy];
if(tile.overlay() != Blocks.spawn){
tile.clearOverlay();
}
}
}
}
} }
} }
Point2 core = players.random(); if(!anyCores){
if(core == null){
throw new IllegalArgumentException("All zone maps must have a core."); throw new IllegalArgumentException("All zone maps must have a core.");
} }
schematics.placeLoadout(loadout, core.x, core.y);
world.prepareTiles(tiles); world.prepareTiles(tiles);
world.setMap(map); world.setMap(map);
} }
public static class Decoration{
public final Block floor;
public final Block wall;
public final double chance;
public Decoration(Block floor, Block wall, double chance){
this.floor = floor;
this.wall = wall;
this.chance = chance;
}
}
} }

View File

@@ -1,10 +1,9 @@
package mindustry.maps.generators; package mindustry.maps.generators;
import arc.struct.StringMap; import arc.struct.*;
import mindustry.content.Blocks; import mindustry.content.*;
import mindustry.maps.Map; import mindustry.maps.*;
import mindustry.world.Block; import mindustry.world.*;
import mindustry.world.Tile;
import static mindustry.Vars.world; import static mindustry.Vars.world;
@@ -18,14 +17,14 @@ public abstract class RandomGenerator extends Generator{
} }
@Override @Override
public void generate(Tile[][] tiles){ public void generate(Tiles tiles){
for(int x = 0; x < width; x++){ for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){ for(int y = 0; y < height; y++){
floor = Blocks.air; floor = Blocks.air;
block = Blocks.air; block = Blocks.air;
ore = Blocks.air; ore = Blocks.air;
generate(x, y); generate(x, y);
tiles[x][y] = new Tile(x, y, floor.id, ore.id, block.id); tiles.set(x, y, new Tile(x, y, floor, ore, block));
} }
} }
@@ -34,7 +33,7 @@ public abstract class RandomGenerator extends Generator{
world.setMap(new Map(new StringMap())); world.setMap(new Map(new StringMap()));
} }
public abstract void decorate(Tile[][] tiles); public abstract void decorate(Tiles tiles);
/** /**
* Sets {@link #floor} and {@link #block} to the correct values as output. * Sets {@link #floor} and {@link #block} to the correct values as output.

View File

@@ -0,0 +1,11 @@
package mindustry.maps.planet;
import arc.graphics.*;
import arc.math.geom.*;
import mindustry.world.*;
public interface PlanetGenerator{
float getHeight(Vec3 position);
Color getColor(Vec3 position);
void generate(Vec3 position, TileGen tile);
}

View File

@@ -0,0 +1,60 @@
package mindustry.maps.planet;
import arc.graphics.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.noise.*;
import mindustry.content.*;
import mindustry.world.*;
public class TestPlanetGenerator implements PlanetGenerator{
Pixmap pix;
Simplex noise = new Simplex();
int waterLevel = 5;
float water;
float scl = 5f;
Array<Block> blocks = Array.with(Blocks.sporeMoss, Blocks.moss, Blocks.ice, Blocks.snow, Blocks.sand, Blocks.darksand, Blocks.darksandWater, Blocks.darksandTaintedWater);
public TestPlanetGenerator(){
try{
pix = new Pixmap("planets/colors.png");
water = waterLevel / (float)(pix.getHeight());
}catch(Exception ignored){
}
}
@Override
public float getHeight(Vec3 position){
position = Tmp.v33.set(position).scl(scl);
float height = Mathf.pow((float)noise.octaveNoise3D(7, 0.48f, 1f/3f, position.x, position.y, position.z), 2.4f);
if(height <= water){
return water;
}
return height;
}
@Override
public Color getColor(Vec3 position){
float height = getHeight(position);
position = Tmp.v33.set(position).scl(scl);
float rad = scl;
float temp = Mathf.clamp(Math.abs(position.y * 2f) / (rad));
float tnoise = (float)noise.octaveNoise3D(7, 0.48f, 1f/3f, position.x, position.y + 999f, position.z);
temp = Mathf.lerp(temp, tnoise, 0.5f);
height *= 1.2f;
height = Mathf.clamp(height);
Color color = Tmp.c1.set(pix.getPixel((int)(temp * (pix.getWidth()-1)), (int)((1f-height) * (pix.getHeight()-1))));
return blocks.min(c -> color.diff(c.color)).color;
}
@Override
public void generate(Vec3 position, TileGen tile){
Color color = getColor(position);
tile.floor = blocks.min(c -> color.diff(c.color));
}
}

View File

@@ -3,7 +3,7 @@ package mindustry.maps.zonegen;
import arc.math.Mathf; import arc.math.Mathf;
import mindustry.content.Blocks; import mindustry.content.Blocks;
import mindustry.maps.generators.BasicGenerator; import mindustry.maps.generators.BasicGenerator;
import mindustry.world.Tile; import mindustry.world.*;
import static mindustry.Vars.schematics; import static mindustry.Vars.schematics;
@@ -19,7 +19,7 @@ public class DesertWastesGenerator extends BasicGenerator{
} }
@Override @Override
public void decorate(Tile[][] tiles){ public void decorate(Tiles tiles){
ores(tiles); ores(tiles);
terrain(tiles, Blocks.sandRocks, 60f, 1.5f, 0.9f); terrain(tiles, Blocks.sandRocks, 60f, 1.5f, 0.9f);
@@ -34,7 +34,7 @@ public class DesertWastesGenerator extends BasicGenerator{
erase(tiles, endX, endY, 10); erase(tiles, endX, endY, 10);
erase(tiles, spawnX, spawnY, 20); erase(tiles, spawnX, spawnY, 20);
distort(tiles, 20f, 4f); distort(tiles, 20f, 4f);
inverseFloodFill(tiles, tiles[spawnX][spawnY], Blocks.sandRocks); inverseFloodFill(tiles, tiles.getn(spawnX, spawnY), Blocks.sandRocks);
noise(tiles, Blocks.salt, Blocks.saltRocks, 5, 0.6f, 200f, 0.55f); noise(tiles, Blocks.salt, Blocks.saltRocks, 5, 0.6f, 200f, 0.55f);
noise(tiles, Blocks.darksand, Blocks.duneRocks, 5, 0.7f, 120f, 0.5f); noise(tiles, Blocks.darksand, Blocks.duneRocks, 5, 0.7f, 120f, 0.5f);
@@ -43,7 +43,7 @@ public class DesertWastesGenerator extends BasicGenerator{
overlay(tiles, Blocks.sand, Blocks.pebbles, 0.15f, 5, 0.8f, 30f, 0.62f); overlay(tiles, Blocks.sand, Blocks.pebbles, 0.15f, 5, 0.8f, 30f, 0.62f);
//scatter(tiles, Blocks.sandRocks, Blocks.creeptree, 1f); //scatter(tiles, Blocks.sandRocks, Blocks.creeptree, 1f);
tiles[endX][endY].setOverlay(Blocks.spawn); tiles.getn(endX, endY).setOverlay(Blocks.spawn);
schematics.placeLoadout(loadout, spawnX, spawnY); schematics.placeLoadout(loadout, spawnX, spawnY);
} }
} }

View File

@@ -1,9 +1,9 @@
package mindustry.maps.zonegen; package mindustry.maps.zonegen;
import arc.math.Mathf; import arc.math.*;
import mindustry.content.Blocks; import mindustry.content.*;
import mindustry.maps.generators.BasicGenerator; import mindustry.maps.generators.*;
import mindustry.world.Tile; import mindustry.world.*;
import static mindustry.Vars.schematics; import static mindustry.Vars.schematics;
@@ -19,7 +19,7 @@ public class OvergrowthGenerator extends BasicGenerator{
} }
@Override @Override
public void decorate(Tile[][] tiles){ public void decorate(Tiles tiles){
ores(tiles); ores(tiles);
terrain(tiles, Blocks.sporePine, 70f, 1.4f, 1f); terrain(tiles, Blocks.sporePine, 70f, 1.4f, 1f);
@@ -34,12 +34,12 @@ public class OvergrowthGenerator extends BasicGenerator{
erase(tiles, endX, endY, 10); erase(tiles, endX, endY, 10);
erase(tiles, spawnX, spawnY, 20); erase(tiles, spawnX, spawnY, 20);
distort(tiles, 20f, 4f); distort(tiles, 20f, 4f);
inverseFloodFill(tiles, tiles[spawnX][spawnY], Blocks.sporerocks); inverseFloodFill(tiles, tiles.getn(spawnX, spawnY), Blocks.sporerocks);
noise(tiles, Blocks.darksandTaintedWater, Blocks.duneRocks, 4, 0.7f, 120f, 0.64f); noise(tiles, Blocks.darksandTaintedWater, Blocks.duneRocks, 4, 0.7f, 120f, 0.64f);
//scatter(tiles, Blocks.sporePine, Blocks.whiteTreeDead, 1f); //scatter(tiles, Blocks.sporePine, Blocks.whiteTreeDead, 1f);
tiles[endX][endY].setOverlay(Blocks.spawn); tiles.getn(endX, endY).setOverlay(Blocks.spawn);
schematics.placeLoadout(loadout, spawnX, spawnY); schematics.placeLoadout(loadout, spawnX, spawnY);
} }
} }

View File

@@ -0,0 +1,76 @@
package mindustry.type;
import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.ctype.*;
import mindustry.graphics.*;
import mindustry.graphics.PlanetGrid.*;
import mindustry.maps.planet.*;
public class Planet extends UnlockableContent{
/** Mesh used for rendering. Created on load() - will be null on the server! */
public PlanetMesh mesh;
/** Grid used for the sectors on the planet. */
public @NonNull PlanetGrid grid;
/** Generator that will make the planet. */
public @NonNull PlanetGenerator generator;
/** Array of sectors; directly maps to tiles in the grid. */
public @NonNull Array<Sector> sectors;
/** Detail in divisions. Must be between 1 and 10. 6 is a good number for this.*/
public int detail = 3;
/** Size in terms of divisions. This only controls the amount of sectors on the planet, not the visuals. */
public int size = 3;
/** Radius of the mesh/sphere. */
public float radius = 1f;
public Planet(String name){
super(name);
}
@Override
public void load(){
mesh = new PlanetMesh(detail, generator);
}
@Override
public void init(){
grid = PlanetGrid.newGrid(size);
sectors = new Array<>(grid.tiles.length);
for(int i = 0; i < grid.tiles.length; i++){
sectors.add(new Sector(this, grid.tiles[i]));
}
}
/** Gets a sector a tile position. */
public Sector getSector(Ptile tile){
return sectors.get(tile.id);
}
/** @return the sector that is hit by this ray, or null if nothing intersects it.
* @param center the center of this planet in 3D space, usually (0,0,0). */
public @Nullable Sector getSector(Vec3 center, Ray ray){
boolean found = Intersector3D.intersectRaySphere(ray, center, radius, Tmp.v33);
if(!found) return null;
//TODO fix O(N) search
return sectors.min(t -> t.tile.v.dst(Tmp.v33));
}
/** Planets cannot be viewed in the database dialog. */
@Override
public boolean isHidden(){
return true;
}
@Override
public void displayInfo(Table table){
}
@Override
public ContentType getContentType(){
return ContentType.planet;
}
}

View File

@@ -0,0 +1,66 @@
package mindustry.type;
import arc.math.geom.*;
import arc.util.*;
import mindustry.graphics.PlanetGrid.*;
/** A small section of a planet. */
//TODO should this be content?
public class Sector{
public final SectorRect rect;
public final Planet planet;
public final Ptile tile;
public Sector(Planet planet, Ptile tile){
this.planet = planet;
this.tile = tile;
this.rect = makeRect();
}
/** Projects this sector onto a 4-corner square for use in map gen.
* Allocates a new object. Do not call in the main loop. */
private SectorRect makeRect(){
Vec3[] corners = new Vec3[tile.corners.length];
for(int i = 0; i < corners.length; i++){
corners[i] = tile.corners[i].v.cpy().setLength(planet.radius);
}
Tmp.v33.setZero();
for(Vec3 c : corners){
Tmp.v33.add(c);
}
//v33 is now the center of this shape
Vec3 center = Tmp.v33.scl(1f / corners.length).cpy();
//radius of circle
float radius = Tmp.v33.dst(corners[0]) * 0.9f;
//get plane that these points are on
Plane plane = new Plane();
plane.set(corners[0], corners[2], corners[4]);
Vec3 planeTop = plane.project(center.cpy().add(0f, 1f, 0f)).sub(center).setLength(radius).add(center);
Vec3 planeRight = plane.project(center.cpy().rotate(Vec3.Y, -4f)).sub(center).setLength(radius).add(center);
return new SectorRect(radius, center, planeTop.sub(center), planeRight.sub(center));
}
public static class SectorRect{
public final Vec3 center, top, right;
public final Vec3 result = new Vec3();
public final float radius;
public SectorRect(float radius, Vec3 center, Vec3 top, Vec3 right){
this.center = center;
this.top = top;
this.right = right;
this.radius = radius;
}
/** Project a coordinate into 3D space.
* Both coordinates should be normalized to floats in the range [0, 1] */
public Vec3 project(float x, float y){
float nx = (x - 0.5f) * 2f, ny = (y - 0.5f) * 2f;
return result.set(center).add(right, nx).add(top, ny);
}
}
}

View File

@@ -0,0 +1,81 @@
package mindustry.type;
import arc.util.ArcAnnotate.*;
import mindustry.annotations.Annotations.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.traits.*;
import mindustry.entities.type.*;
import mindustry.type.Weather.*;
import java.io.*;
import static mindustry.Vars.*;
public abstract class Weather<T extends WeatherEntity> extends MappableContent{
protected float duration = 100f;
public Weather(String name){
super(name);
}
public abstract void update(T entity);
public abstract void draw(T entity);
@Override
public ContentType getContentType(){
return ContentType.weather;
}
/** Represents the in-game state of a weather event. */
@SuppressWarnings("unchecked")
public static class WeatherEntity extends BaseEntity implements SaveTrait, DrawTrait{
/** How long this event has been occuring in ticks. */
protected float life;
/** Type of weather that is being simulated. */
protected @NonNull
Weather weather;
public WeatherEntity(Weather weather){
this.weather = weather;
}
@Override
public void update(){
weather.update(this);
}
@Override
public void draw(){
weather.draw(this);
}
@CallSuper
@Override
public void writeSave(DataOutput stream) throws IOException{
stream.writeShort(weather.id);
}
@CallSuper
@Override
public void readSave(DataInput stream, byte version) throws IOException{
weather = content.getByID(ContentType.weather, stream.readShort());
}
@Override
public byte version(){
return 0;
}
@Override
public TypeID getTypeID(){
return null;
}
@Override
public EntityGroup targetGroup(){
return weatherGroup;
}
}
}

View File

@@ -1,18 +0,0 @@
package mindustry.type;
import mindustry.ctype.Content;
import mindustry.ctype.ContentType;
//currently unimplemented, see trello for implementation plans
public class WeatherEvent extends Content{
public final String name;
public WeatherEvent(String name){
this.name = name;
}
@Override
public ContentType getContentType(){
return ContentType.weather;
}
}

View File

@@ -1,14 +1,12 @@
package mindustry.type; package mindustry.type;
import arc.*; import arc.*;
import arc.struct.*;
import arc.func.*; import arc.func.*;
import arc.graphics.g2d.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.ctype.ContentType; import mindustry.ctype.*;
import mindustry.ctype.UnlockableContent;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.game.Objectives.*; import mindustry.game.Objectives.*;
@@ -19,6 +17,7 @@ import static mindustry.Vars.*;
public class Zone extends UnlockableContent{ public class Zone extends UnlockableContent{
public @NonNull Generator generator; public @NonNull Generator generator;
public @NonNull Objective configureObjective = new ZoneWave(this, 15); public @NonNull Objective configureObjective = new ZoneWave(this, 15);
public @NonNull Planet planet;
public Array<Objective> requirements = new Array<>(); public Array<Objective> requirements = new Array<>();
//TODO autogenerate //TODO autogenerate
public Array<Item> resources = new Array<>(); public Array<Item> resources = new Array<>();
@@ -28,7 +27,6 @@ public class Zone extends UnlockableContent{
public int conditionWave = Integer.MAX_VALUE; public int conditionWave = Integer.MAX_VALUE;
public int launchPeriod = 10; public int launchPeriod = 10;
public Schematic loadout = Loadouts.basicShard; public Schematic loadout = Loadouts.basicShard;
public TextureRegion preview;
protected Array<ItemStack> baseLaunchCost = new Array<>(); protected Array<ItemStack> baseLaunchCost = new Array<>();
protected Array<ItemStack> startingItems = new Array<>(); protected Array<ItemStack> startingItems = new Array<>();
@@ -36,18 +34,14 @@ public class Zone extends UnlockableContent{
private Array<ItemStack> defaultStartingItems = new Array<>(); private Array<ItemStack> defaultStartingItems = new Array<>();
public Zone(String name, Generator generator){ public Zone(String name, Planet planet, Generator generator){
super(name); super(name);
this.generator = generator; this.generator = generator;
this.planet = planet;
} }
public Zone(String name){ public Zone(String name){
this(name, new MapGenerator(name)); this(name, Planets.starter, new MapGenerator(name));
}
@Override
public void load(){
preview = Core.atlas.find("zone-" + name, Core.atlas.find(name + "-zone"));
} }
public Rules getRules(){ public Rules getRules(){

View File

@@ -1,7 +1,6 @@
package mindustry.ui.dialogs; package mindustry.ui.dialogs;
import arc.*; import arc.*;
import arc.struct.*;
import arc.func.*; import arc.func.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
@@ -10,10 +9,10 @@ import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.scene.*; import arc.scene.*;
import arc.scene.event.*; import arc.scene.event.*;
import arc.scene.style.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.scene.utils.*; import arc.scene.utils.*;
import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.core.GameState.*; import mindustry.core.GameState.*;
@@ -29,6 +28,7 @@ import mindustry.ui.layout.TreeLayout.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
//TODO remove (legacy, no longer needed)
public class DeployDialog extends FloatingDialog{ public class DeployDialog extends FloatingDialog{
private final float nodeSize = Scl.scl(230f); private final float nodeSize = Scl.scl(230f);
private ObjectSet<ZoneNode> nodes = new ObjectSet<>(); private ObjectSet<ZoneNode> nodes = new ObjectSet<>();
@@ -105,7 +105,7 @@ public class DeployDialog extends FloatingDialog{
bounds.y += nodeSize*0.4f; bounds.y += nodeSize*0.4f;
} }
public void setup(){ void setup(){
platform.updateRPC(); platform.updateRPC();
cont.clear(); cont.clear();
@@ -122,7 +122,7 @@ public class DeployDialog extends FloatingDialog{
setFilter(TextureFilter.Linear); setFilter(TextureFilter.Linear);
}}){{ }}){{
float[] time = {0}; float[] time = {0};
setColor(Color.grays(0.3f)); setColor(Color.gray(0.3f));
setScale(1.5f); setScale(1.5f);
update(() -> { update(() -> {
setOrigin(Align.center); setOrigin(Align.center);
@@ -140,19 +140,19 @@ public class DeployDialog extends FloatingDialog{
Stack sub = new Stack(); Stack sub = new Stack();
if(slot.getZone() != null){ if(slot.getZone() != null){
sub.add(new Table(f -> f.margin(4f).add(new Image()).color(Color.grays(0.1f)).grow())); sub.add(new Table(f -> f.margin(4f).add(new Image()).color(Color.gray(0.1f)).grow()));
sub.add(new Table(f -> f.margin(4f).add(new Image(slot.getZone().preview).setScaling(Scaling.fit)).update(img -> { //sub.add(new Table(f -> f.margin(4f).add(new Image(slot.getZone().preview).setScaling(Scaling.fit)).update(img -> {
TextureRegionDrawable draw = (TextureRegionDrawable)img.getDrawable(); //TextureRegionDrawable draw = (TextureRegionDrawable)img.getDrawable();
if(draw.getRegion().getTexture().isDisposed()){ //if(draw.getRegion().getTexture().isDisposed()){
draw.setRegion(slot.getZone().preview); // draw.setRegion(slot.getZone().preview);
} // }
Texture text = slot.previewTexture(); Texture text = slot.previewTexture();
if(draw.getRegion() == slot.getZone().preview && text != null){ //if(draw.getRegion() == slot.getZone().preview && text != null){
draw.setRegion(new TextureRegion(text)); // draw.setRegion(new TextureRegion(text));
} //}
}).color(Color.darkGray).grow())); // }).color(Color.darkGray).grow()));
} }
TextButton button = Elements.newButton(Core.bundle.format("resume", slot.getZone().localizedName), Styles.squaret, () -> { TextButton button = Elements.newButton(Core.bundle.format("resume", slot.getZone().localizedName), Styles.squaret, () -> {
@@ -249,12 +249,12 @@ public class DeployDialog extends FloatingDialog{
for(ZoneNode node : nodes){ for(ZoneNode node : nodes){
Stack stack = new Stack(); Stack stack = new Stack();
Tmp.v1.set(node.width, node.height); Tmp.v1.set(node.width, node.height);
if(node.zone.preview != null){ //if(node.zone.preview != null){
Tmp.v1.set(Scaling.fit.apply(node.zone.preview.getWidth(), node.zone.preview.getHeight(), node.width, node.height)); // Tmp.v1.set(Scaling.fit.apply(node.zone.preview.getWidth(), node.zone.preview.getHeight(), node.width, node.height));
} //}
stack.setSize(Tmp.v1.x, Tmp.v1.y); // stack.setSize(Tmp.v1.x, Tmp.v1.y);
stack.add(new Table(t -> t.margin(4f).add(new Image(node.zone.preview).setScaling(Scaling.stretch)).color(node.zone.unlocked() ? Color.darkGray : Color.grays(0.2f)).grow())); // stack.add(new Table(t -> t.margin(4f).add(new Image(node.zone.preview).setScaling(Scaling.stretch)).color(node.zone.unlocked() ? Color.darkGray : Color.gray(0.2f)).grow()));
stack.update(() -> stack.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f, Align.center)); stack.update(() -> stack.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f, Align.center));
Button button = new Button(Styles.squaret); Button button = new Button(Styles.squaret);

View File

@@ -200,7 +200,6 @@ public class FileChooser extends FloatingDialog{
files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2); files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2);
files.row(); files.row();
ButtonGroup<TextButton> group = new ButtonGroup<>(); ButtonGroup<TextButton> group = new ButtonGroup<>();
group.setMinCheckCount(0); group.setMinCheckCount(0);

View File

@@ -92,7 +92,7 @@ public class GameOverDialog extends FloatingDialog{
hide(); hide();
state.set(State.menu); state.set(State.menu);
logic.reset(); logic.reset();
ui.deploy.show(); ui.planet.show();
}).size(130f, 60f); }).size(130f, 60f);
}else{ }else{
buttons.addButton("$menu", () -> { buttons.addButton("$menu", () -> {

View File

@@ -0,0 +1,30 @@
package mindustry.ui.dialogs;
import mindustry.content.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.ui.*;
import static mindustry.Vars.ui;
public class PlanetDialog extends FloatingDialog{
private PlanetRenderer renderer = new PlanetRenderer();
public PlanetDialog(){
super("", Styles.fullDialog);
addCloseButton();
buttons.addImageTextButton("$techtree", Icon.tree, () -> ui.tech.show()).size(230f, 64f);
shown(this::setup);
}
void setup(){
cont.clear();
titleTable.remove();
cont.addRect((x, y, w, h) -> {
renderer.render(Planets.starter);
}).grow();
}
}

View File

@@ -59,7 +59,7 @@ public class TechTreeDialog extends FloatingDialog{
treeLayout(); treeLayout();
}); });
hidden(ui.deploy::setup); hidden(ui.planet::setup);
addCloseButton(); addCloseButton();

View File

@@ -14,6 +14,7 @@ import mindustry.ui.Cicon;
import static mindustry.Vars.*; import static mindustry.Vars.*;
//TODO remove
public class ZoneInfoDialog extends FloatingDialog{ public class ZoneInfoDialog extends FloatingDialog{
private LoadoutDialog loadout = new LoadoutDialog(); private LoadoutDialog loadout = new LoadoutDialog();
@@ -152,13 +153,13 @@ public class ZoneInfoDialog extends FloatingDialog{
if(!data.isUnlocked(zone)){ if(!data.isUnlocked(zone)){
Sounds.unlock.play(); Sounds.unlock.play();
data.unlockContent(zone); data.unlockContent(zone);
ui.deploy.setup(); ui.planet.setup();
setup(zone); setup(zone);
}else{ }else{
ui.deploy.hide(); ui.planet.hide();
data.removeItems(zone.getLaunchCost()); data.removeItems(zone.getLaunchCost());
hide(); hide();
control.playZone(zone); //control.playZone(zone);
} }
}).minWidth(200f).margin(13f).padTop(5).disabled(b -> zone.locked() ? !zone.canUnlock() : !data.hasItems(zone.getLaunchCost())).uniformY().get(); }).minWidth(200f).margin(13f).padTop(5).disabled(b -> zone.locked() ? !zone.canUnlock() : !data.hasItems(zone.getLaunchCost())).uniformY().get();

View File

@@ -21,9 +21,9 @@ public class FadeInFragment extends Fragment{
@Override @Override
public void draw(){ public void draw(){
Draw.color(0f, 0f, 0f, Mathf.clamp(1f - time)); Draw.color(0f, 0f, 0f, Mathf.clamp(1f - time));
Fill.crect(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight()); Fill.crect(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
Draw.color(); Draw.color();
} }
@Override @Override

View File

@@ -100,7 +100,7 @@ public class MenuFragment extends Fragment{
container.defaults().size(size).pad(5).padTop(4f); container.defaults().size(size).pad(5).padTop(4f);
MobileButton MobileButton
play = new MobileButton(Icon.play, "$campaign", () -> checkPlay(ui.deploy::show)), play = new MobileButton(Icon.play, "$campaign", () -> checkPlay(ui.planet::show)),
custom = new MobileButton(Icon.rightOpenOut, "$customgame", () -> checkPlay(ui.custom::show)), custom = new MobileButton(Icon.rightOpenOut, "$customgame", () -> checkPlay(ui.custom::show)),
maps = new MobileButton(Icon.download, "$loadgame", () -> checkPlay(ui.load::show)), maps = new MobileButton(Icon.download, "$loadgame", () -> checkPlay(ui.load::show)),
join = new MobileButton(Icon.add, "$joingame", () -> checkPlay(ui.join::show)), join = new MobileButton(Icon.add, "$joingame", () -> checkPlay(ui.join::show)),
@@ -165,7 +165,7 @@ public class MenuFragment extends Fragment{
buttons(t, buttons(t,
new Buttoni("$play", Icon.play, new Buttoni("$play", Icon.play,
new Buttoni("$campaign", Icon.play, () -> checkPlay(ui.deploy::show)), new Buttoni("$campaign", Icon.play, () -> checkPlay(ui.planet::show)),
new Buttoni("$joingame", Icon.add, () -> checkPlay(ui.join::show)), new Buttoni("$joingame", Icon.add, () -> checkPlay(ui.join::show)),
new Buttoni("$customgame", Icon.terrain, () -> checkPlay(ui.custom::show)), new Buttoni("$customgame", Icon.terrain, () -> checkPlay(ui.custom::show)),
new Buttoni("$loadgame", Icon.download, () -> checkPlay(ui.load::show)), new Buttoni("$loadgame", Icon.download, () -> checkPlay(ui.load::show)),

View File

@@ -35,17 +35,21 @@ public class Tile implements Position{
block = floor = overlay = (Floor)Blocks.air; block = floor = overlay = (Floor)Blocks.air;
} }
public Tile(int x, int y, int floor, int overlay, int wall){ public Tile(int x, int y, Block floor, Block overlay, Block wall){
this.x = (short)x; this.x = (short)x;
this.y = (short)y; this.y = (short)y;
this.floor = (Floor)content.block(floor); this.floor = (Floor)floor;
this.overlay = (Floor)content.block(overlay); this.overlay = (Floor)overlay;
this.block = content.block(wall); this.block = wall;
//update entity and create it if needed //update entity and create it if needed
changed(); changed();
} }
public Tile(int x, int y, int floor, int overlay, int wall){
this(x, y, content.block(floor), content.block(overlay), content.block(wall));
}
/** Returns this tile's position as a {@link Pos}. */ /** Returns this tile's position as a {@link Pos}. */
public int pos(){ public int pos(){
return Pos.get(x, y); return Pos.get(x, y);

View File

@@ -0,0 +1,19 @@
package mindustry.world;
import mindustry.content.*;
public class TileGen{
public Block floor;
public Block block ;
public Block overlay;
{
reset();
}
public void reset(){
floor = Blocks.stone;
block = Blocks.air;
overlay = Blocks.air;
}
}

View File

@@ -0,0 +1,93 @@
package mindustry.world;
import arc.func.*;
import arc.util.ArcAnnotate.*;
import java.util.*;
/** A tile container. */
public class Tiles implements Iterable<Tile>{
private final Tile[] array;
private final int width, height;
private final TileIterator iterator = new TileIterator();
public Tiles(int width, int height){
this.array = new Tile[width * height];
this.width = width;
this.height = height;
}
public void each(Intc2 cons){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
cons.get(x, y);
}
}
}
/** fills this tile set with empty air tiles. */
public void fill(){
for(int i = 0; i < array.length; i++){
array[i] = new Tile(i % width, i / width);
}
}
/** set a tile at a position; does not range-check. use with caution. */
public void set(int x, int y, Tile tile){
array[y*width + x] = tile;
}
/** @return whether these coordinates are in bounds */
public boolean in(int x, int y){
return x >= 0 && x < width && y >= 0 && y < height;
}
/** @return a tile at coordinates, or null if out of bounds */
public @Nullable Tile get(int x, int y){
return (x < 0 || x >= width || y < 0 || y >= height) ? null : array[y*width + x];
}
/** @return a tile at coordinates; throws an exception if out of bounds */
public @NonNull Tile getn(int x, int y){
if(x < 0 || x >= width || y < 0 || y >= height) throw new IllegalArgumentException(x + ", " + y + " out of bounds: width=" + width + ", height=" + height);
return array[y*width + x];
}
/** @return a tile at an iteration index [0, width * height] */
public @NonNull Tile geti(int idx){
return array[idx];
}
/** @return a tile at an int position (not equivalent to geti) */
public @Nullable Tile getp(int pos){
return get(Pos.x(pos), Pos.y(pos));
}
public int width(){
return width;
}
public int height(){
return height;
}
@Override
public Iterator<Tile> iterator(){
iterator.index = 0;
return iterator;
}
private class TileIterator implements Iterator<Tile>{
int index = 0;
@Override
public boolean hasNext(){
return index < array.length;
}
@Override
public Tile next(){
return array[index++];
}
}
}

View File

@@ -6,6 +6,7 @@ import arc.graphics.g2d.*;
import arc.graphics.g2d.TextureAtlas.*; import arc.graphics.g2d.TextureAtlas.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.util.ArcAnnotate.*;
import arc.struct.*; import arc.struct.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.entities.*; import mindustry.entities.*;
@@ -35,13 +36,13 @@ public class Floor extends Block{
/** Effect displayed when drowning on this floor. */ /** Effect displayed when drowning on this floor. */
public Effect drownUpdateEffect = Fx.bubble; public Effect drownUpdateEffect = Fx.bubble;
/** Status effect applied when walking on. */ /** Status effect applied when walking on. */
public StatusEffect status = StatusEffects.none; public @NonNull StatusEffect status = StatusEffects.none;
/** Intensity of applied status effect. */ /** Intensity of applied status effect. */
public float statusDuration = 60f; public float statusDuration = 60f;
/** liquids that drop from this block, used for pumps */ /** liquids that drop from this block, used for pumps */
public Liquid liquidDrop = null; public @Nullable Liquid liquidDrop = null;
/** item that drops from this block, used for drills */ /** item that drops from this block, used for drills */
public Item itemDrop = null; public @Nullable Item itemDrop = null;
/** whether this block can be drowned in */ /** whether this block can be drowned in */
public boolean isLiquid; public boolean isLiquid;
/** if true, this block cannot be mined by players. useful for annoying things like sand. */ /** if true, this block cannot be mined by players. useful for annoying things like sand. */

View File

@@ -46,7 +46,6 @@ public class DesktopLauncher extends ClientLauncher{
new SdlApplication(new DesktopLauncher(arg), new SdlConfig(){{ new SdlApplication(new DesktopLauncher(arg), new SdlConfig(){{
title = "Mindustry"; title = "Mindustry";
maximized = true; maximized = true;
depth = 0;
stencil = 0; stencil = 0;
width = 900; width = 900;
height = 700; height = 700;
@@ -267,7 +266,7 @@ public class DesktopLauncher extends ClientLauncher{
}else{ }else{
if(ui.editor != null && ui.editor.isShown()){ if(ui.editor != null && ui.editor.isShown()){
uiState = "In Editor"; uiState = "In Editor";
}else if(ui.deploy != null && ui.deploy.isShown()){ }else if(ui.planet != null && ui.planet.isShown()){
uiState = "In Launch Selection"; uiState = "In Launch Selection";
}else{ }else{
uiState = "In Menu"; uiState = "In Menu";

View File

@@ -28,6 +28,7 @@ if(!hasProperty("release")){
use(':Arc:extensions:recorder') use(':Arc:extensions:recorder')
use(':Arc:extensions:arcnet') use(':Arc:extensions:arcnet')
use(':Arc:extensions:packer') use(':Arc:extensions:packer')
use(':Arc:extensions:g3d')
use(':Arc:backends') use(':Arc:backends')
use(':Arc:backends:backend-sdl') use(':Arc:backends:backend-sdl')
use(':Arc:backends:backend-android') use(':Arc:backends:backend-android')

View File

@@ -109,14 +109,10 @@ public class ApplicationTests{
@Test @Test
void createMap(){ void createMap(){
Tile[][] tiles = world.createTiles(8, 8); Tiles tiles = world.resize(8, 8);
world.beginMapLoad(); world.beginMapLoad();
for(int x = 0; x < tiles.length; x++){ tiles.fill();
for(int y = 0; y < tiles[0].length; y++){
tiles[x][y] = new Tile(x, y);
}
}
world.endMapLoad(); world.endMapLoad();
} }
@@ -379,29 +375,29 @@ public class ApplicationTests{
@Test @Test
void allBlockTest(){ void allBlockTest(){
Tile[][] tiles = world.createTiles(256*2 + 20, 10); Tiles tiles = world.resize(256*2 + 20, 10);
world.beginMapLoad(); world.beginMapLoad();
for(int x = 0; x < tiles.length; x++){ for(int x = 0; x < tiles.width(); x++){
for(int y = 0; y < tiles[0].length; y++){ for(int y = 0; y < tiles.height(); y++){
tiles[x][y] = new Tile(x, y, Blocks.stone.id, (byte)0, (byte)0); tiles.set(x, y, new Tile(x, y, Blocks.stone, Blocks.air, Blocks.air));
} }
} }
int i = 0; int i = 0;
for(int x = 5; x < tiles.length && i < content.blocks().size; ){ for(int x = 5; x < tiles.width() && i < content.blocks().size; ){
Block block = content.block(i++); Block block = content.block(i++);
if(block.isBuildable()){ if(block.isBuildable()){
x += block.size; x += block.size;
tiles[x][5].setBlock(block); tiles.get(x, 5).setBlock(block);
x += block.size; x += block.size;
} }
} }
world.endMapLoad(); world.endMapLoad();
for(int x = 0; x < tiles.length; x++){ for(int x = 0; x < tiles.width(); x++){
for(int y = 0; y < tiles[0].length; y++){ for(int y = 0; y < tiles.height(); y++){
Tile tile = world.tile(x, y); Tile tile = world.rawTile(x, y);
if(tile.entity != null){ if(tile.entity != null){
try{ try{
tile.entity.update(); tile.entity.update();
@@ -429,7 +425,7 @@ public class ApplicationTests{
void depositTest(Block block, Item item){ void depositTest(Block block, Item item){
BaseUnit unit = UnitTypes.spirit.create(Team.derelict); BaseUnit unit = UnitTypes.spirit.create(Team.derelict);
Tile tile = new Tile(0, 0, Blocks.air.id, (byte)0, block.id); Tile tile = new Tile(0, 0, Blocks.air, Blocks.air, block);
int capacity = tile.block().itemCapacity; int capacity = tile.block().itemCapacity;
assertNotNull(tile.entity, "Tile should have an entity, but does not: " + tile); assertNotNull(tile.entity, "Tile should have an entity, but does not: " + tile);

View File

@@ -32,12 +32,14 @@ public class ZoneTests{
Array<DynamicTest> out = new Array<>(); Array<DynamicTest> out = new Array<>();
if(world == null) world = new World(); if(world == null) world = new World();
fail("Zone validity tests need to be refactored!");
for(Zone zone : content.zones()){ for(Zone zone : content.zones()){
out.add(dynamicTest(zone.name, () -> { out.add(dynamicTest(zone.name, () -> {
zone.generator.init(zone.loadout); zone.generator.init(zone.loadout);
logic.reset(); logic.reset();
try{ try{
world.loadGenerator(zone.generator); //world.loadGenerator(zone.generator);
}catch(SaveException e){ }catch(SaveException e){
e.printStackTrace(); e.printStackTrace();
return; return;