1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-10-26 01:00:01 +03:00

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
commit be50997f94
82 changed files with 1708 additions and 502 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@ -249,6 +249,7 @@ project(":core"){
compile "org.lz4:lz4-java:1.4.1"
compile arcModule("arc-core")
compile arcModule("extensions:freetype")
compile arcModule("extensions:g3d")
compile arcModule("extensions:arcnet")
compile "org.mozilla:rhino:1.7.11"
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.
filter.distort = Distort
filter.noise = Noise
filter.enemyspawn = Enemy Spawn Select
filter.corespawn = Core Select
filter.median = Median
filter.oremedian = Ore Median
filter.blend = Blend
@ -413,6 +415,7 @@ filter.option.circle-scale = Circle Scale
filter.option.octaves = Octaves
filter.option.falloff = Falloff
filter.option.angle = Angle
filter.option.amount = Amount
filter.option.block = Block
filter.option.floor = Floor
filter.option.flooronto = Target Floor
@ -850,6 +853,7 @@ liquid.temperature = [lightgray]Temperature: {0}
block.sand-boulder.name = Sand Boulder
block.grass.name = Grass
block.slag.name = Slag
block.salt.name = Salt
block.saltrocks.name = Salt Rocks
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(schematics);
assets.loadRun("contentinit", ContentLoader.class, () -> {
content.init();
content.load();
});
assets.loadRun("contentinit", ContentLoader.class, () -> content.init(), () -> content.load());
}
@Override

View File

@ -33,7 +33,7 @@ public class Blocks implements ContentList{
public static Block
//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,
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,
@ -202,6 +202,17 @@ public class Blocks implements ContentList{
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"){{
}};

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;
import mindustry.ctype.ContentList;
import mindustry.game.*;
import mindustry.ctype.*;
import mindustry.game.Objectives.*;
import mindustry.game.*;
import mindustry.maps.generators.*;
import mindustry.maps.generators.MapGenerator.*;
import mindustry.maps.zonegen.*;
import mindustry.type.*;
import static arc.struct.Array.with;
import static mindustry.content.Items.*;
import static mindustry.content.Planets.starter;
import static mindustry.type.ItemStack.list;
public class Zones implements ContentList{
@ -22,7 +22,7 @@ public class Zones implements ContentList{
@Override
public void load(){
groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{
groundZero = new Zone("groundZero", starter, new MapGenerator("groundZero")){{
baseLaunchCost = list(copper, -60);
startingItems = list(copper, 60);
alwaysUnlocked = true;
@ -31,7 +31,7 @@ public class Zones implements ContentList{
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);
conditionWave = 20;
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);
loadout = Loadouts.basicFoundation;
conditionWave = 10;
@ -98,8 +98,7 @@ public class Zones implements ContentList{
);
}};
frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1)
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{
frozenForest = new Zone("frozenForest", starter, new MapGenerator("frozenForest")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 250);
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);
conditionWave = 10;
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;
startingItems = list(copper, 140, lead, 50);
conditionWave = 20;
@ -138,8 +137,7 @@ public class Zones implements ContentList{
);
}};
stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2)
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
stainedMountains = new Zone("stainedMountains", starter, new MapGenerator("stainedMountains")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 200, lead, 50);
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);
resources = with(copper, lead, coal, titanium, sand);
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);
conditionWave = 12;
launchPeriod = 4;
@ -183,8 +181,7 @@ public class Zones implements ContentList{
);
}};
tarFields = new Zone("tarFields", new MapGenerator("tarFields")
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
tarFields = new Zone("tarFields", starter, new MapGenerator("tarFields")){{
loadout = Loadouts.basicFoundation;
startingItems = list(copper, 250, lead, 100);
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;
startingItems = list(copper, 1000, lead, 1000, Items.graphite, 250, titanium, 250, Items.silicon, 250);
conditionWave = 3;
@ -223,8 +220,7 @@ public class Zones implements ContentList{
resources = Array.with(Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand};
}};*/
nuclearComplex = new Zone("nuclearComplex", new MapGenerator("nuclearProductionComplex", 1)
.decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.01))){{
nuclearComplex = new Zone("nuclearComplex", starter, new MapGenerator("nuclearProductionComplex")){{
loadout = Loadouts.basicNucleus;
startingItems = list(copper, 1250, lead, 1500, Items.silicon, 400, Items.metaglass, 250);
conditionWave = 30;

View File

@ -39,6 +39,8 @@ public class ContentLoader{
new Blocks(),
new Loadouts(),
new TechTree(),
new Weathers(),
new Planets(),
new Zones(),
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(() -> {
logic.reset();
net.reset();
@ -267,6 +269,16 @@ public class Control implements ApplicationListener, Loadable{
logic.play();
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(){
@ -277,8 +289,8 @@ public class Control implements ApplicationListener, Loadable{
world.beginMapLoad();
world.createTiles(zone.generator.width, zone.generator.height);
zone.generator.generate(world.getTiles());
world.resize(zone.generator.width, zone.generator.height);
zone.generator.generate(world.tiles);
Tile coreb = null;

View File

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

View File

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

View File

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

View File

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

View File

@ -54,7 +54,7 @@ public abstract class UnlockableContent extends MappableContent{
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(){
return false;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ public class MenuRenderer implements Disposable{
}
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);
shadows = new FrameBuffer(width, height);
int offset = Mathf.random(100000);
@ -154,10 +154,10 @@ public class MenuRenderer implements Disposable{
}
Tile tile;
tiles[x][y] = (tile = new CachedTile());
tiles.set(x, y, (tile = new CachedTile()));
tile.x = (short)x;
tile.y = (short)y;
tile.setFloor((Floor) floor);
tile.setFloor(floor.asFloor());
tile.setBlock(wall);
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 static Shadow shadow;
public static BlockBuild blockbuild;
public static @Nullable
Shield shield;
public static @Nullable Shield shield;
public static UnitBuild build;
public static FogShader fog;
public static MenuShader menu;
public static LightShader light;
public static SurfaceShader water, tar;
public static SurfaceShader water, tar, slag;
public static Shader planet;
public static void init(){
shadow = new Shadow();
@ -35,6 +35,8 @@ public class Shaders{
light = new LightShader();
water = new SurfaceShader("water");
tar = new SurfaceShader("tar");
slag = new SurfaceShader("slag");
planet = new LoadShader("planet", "planet");
}
public static class LightShader extends LoadShader{

View File

@ -133,11 +133,11 @@ public class MapIO{
}
}
public static Pixmap generatePreview(Tile[][] tiles){
Pixmap pixmap = new Pixmap(tiles.length, tiles[0].length, Format.RGBA8888);
public static Pixmap generatePreview(Tiles tiles){
Pixmap pixmap = new Pixmap(tiles.width(), tiles.height(), Format.RGBA8888);
for(int x = 0; x < pixmap.getWidth(); x++){
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()));
}
}
@ -152,12 +152,12 @@ public class MapIO{
}
/** 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 y = 0; y < pixmap.getHeight(); y++){
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
LegacyBlock block = LegacyColorMapper.get(color);
Tile tile = tiles[x][y];
Tile tile = tiles.getn(x, y);
tile.setFloor(block.floor);
tile.setBlock(block.wall);

View File

@ -212,7 +212,7 @@ public class Maps{
for(int x = 0; x < map.width; x++){
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){
map.teams.add(tile.getTeamID());
@ -228,7 +228,7 @@ public class Maps{
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));
writeCache(map);

View File

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

View File

@ -1,5 +1,6 @@
package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.*;
import mindustry.world.*;
@ -8,10 +9,9 @@ import static mindustry.maps.filters.FilterOption.*;
public class ClearFilter extends GenerateFilter{
protected Block block = Blocks.air;
{
options(
new BlockOption("block", () -> block, b -> block = b, wallsOnly)
);
@Override
public FilterOption[] options(){
return Structs.arr(new BlockOption("block", () -> block, b -> block = b, wallsOnly));
}
@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;
import arc.util.*;
import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
@ -7,14 +8,19 @@ import mindustry.world.blocks.*;
public class DistortFilter extends GenerateFilter{
float scl = 40, mag = 5;
{
buffered = true;
options(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 200f),
new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f)
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 200f),
new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f)
);
}
@Override
public boolean isBuffered(){
return true;
}
@Override
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);

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 float min, max, step;
boolean display;
SliderOption(String name, Floatp getter, Floatc setter, float min, float max){
this(name, getter, setter, min, max, (max - min) / 200);
}
@ -48,9 +50,18 @@ public abstract class FilterOption{
this.step = step;
}
public SliderOption display(){
display = true;
return this;
}
@Override
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();
Slider slider = table.addSlider(min, max, step, setter).growX().get();
slider.setValue(getter.get());

View File

@ -7,32 +7,37 @@ import arc.util.*;
import arc.util.noise.*;
import mindustry.content.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
public abstract class GenerateFilter{
protected transient float o = (float)(Math.random() * 10000000.0);
protected transient long seed;
protected transient GenerateInput in;
public transient boolean buffered = false;
public transient FilterOption[] options;
public void apply(Tiles tiles, GenerateInput in){
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){
this.in = in;
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. */
protected void options(FilterOption... options){
this.options = options;
}
/** @return a new array of options for configuring this filter */
public abstract FilterOption[] options();
/** apply the actual filter on the input */
protected abstract void apply();
protected void apply(){}
/** draw any additional guides */
public void draw(Image image){}
@ -47,6 +52,16 @@ public abstract class GenerateFilter{
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
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.math.*;
import arc.util.*;
import mindustry.maps.filters.FilterOption.*;
import mindustry.world.*;
@ -12,14 +13,19 @@ public class MedianFilter extends GenerateFilter{
float percentile = 0.5f;
IntArray blocks = new IntArray(), floors = new IntArray();
{
buffered = true;
options(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
);
}
@Override
public boolean isBuffered(){
return true;
}
@Override
public void apply(){
int rad = (int)radius;

View File

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

View File

@ -1,5 +1,6 @@
package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption;
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;
Block floor = Blocks.stone, block = Blocks.rocks;
{
options(
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f),
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),

View File

@ -1,5 +1,6 @@
package mindustry.maps.filters;
import arc.util.*;
import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.SliderOption;
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 Block ore = Blocks.oreCopper;
{
options(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f),
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),
new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f),
new BlockOption("ore", () -> ore, b -> ore = b, oresOnly)
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f),
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),
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.math.*;
import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.maps.filters.FilterOption.*;
@ -13,14 +14,19 @@ public class OreMedianFilter extends GenerateFilter{
private IntArray blocks = new IntArray();
{
buffered = true;
options(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
);
}
@Override
public boolean isBuffered(){
return true;
}
@Override
public void apply(){
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;
import arc.util.*;
import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption;
import mindustry.maps.filters.FilterOption.SliderOption;
@ -12,8 +13,9 @@ public class RiverNoiseFilter extends GenerateFilter{
float scl = 40, threshold = 0f, threshold2 = 0.1f;
Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandRocks;
{
options(
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("threshold", () -> threshold, f -> threshold = f, -1f, 0.3f),
new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, -1f, 0.3f),

View File

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

View File

@ -1,6 +1,7 @@
package mindustry.maps.filters;
import arc.math.Mathf;
import arc.util.*;
import mindustry.content.Blocks;
import mindustry.maps.filters.FilterOption.BlockOption;
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;
Block floor = Blocks.stone, block = Blocks.rocks;
{
options(
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
new SliderOption("mag", () -> magnitude, f -> magnitude = f, 0f, 2f),
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
public void generate(Tile[][] tiles){
public void generate(Tiles tiles){
int seed = Mathf.random(99999999);
sim.setSeed(seed);
sim2.setSeed(seed + 1);
super.generate(tiles);
}
public void ores(Tile[][] tiles){
public void ores(Tiles tiles){
pass(tiles, (x, y) -> {
if(ores != null){
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) -> {
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;
@ -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));
pass(tiles, (x, y) -> {
if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold){
Tile tile = tiles[x][y];
Tile tile = tiles.getn(x, y);
this.floor = floor;
if(tile.block().solid){
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));
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;
}
});
}
public void tech(Tile[][] tiles){
public void tech(Tiles tiles){
Block[] blocks = {Blocks.darkPanel3};
int secSize = 20;
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];
Floor[][] floors = new Floor[width][height];
each((x, y) -> {
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();
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) -> {
if(!Mathf.chance(chance)) return;
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;
}
public void pass(Tile[][] tiles, Intc2 r){
public void pass(Tiles tiles, Intc2 r){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
floor = tiles[x][y].floor();
block = tiles[x][y].block();
ore = tiles[x][y].overlay();
floor = tiles.getn(x, y).floor();
block = tiles.getn(x, y).block();
ore = tiles.getn(x, y).overlay();
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));
}
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 y = -rad; y <= rad; y++){
int wx = cx + x, wy = cy + y;
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);
}
}
}
}
public Array<Tile> pathfind(Tile[][] tiles, int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh){
Tile start = tiles[startX][startY];
Tile end = tiles[endX][endY];
public Array<Tile> pathfind(Tiles tiles, int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh){
Tile start = tiles.getn(startX, startY);
Tile end = tiles.getn(endX, endY);
GridBits closed = new GridBits(width, height);
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);
boolean found = false;
while(!queue.isEmpty()){
@ -196,7 +196,7 @@ public abstract class BasicGenerator extends RandomGenerator{
for(Point2 point : Geometry.d4){
int newx = next.x + point.x, newy = next.y + point.y;
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)){
closed.set(child.x, child.y);
child.rotation(child.relativeTo(next.x, next.y));
@ -215,7 +215,7 @@ public abstract class BasicGenerator extends RandomGenerator{
while(current != start){
out.add(current);
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();
@ -223,17 +223,17 @@ public abstract class BasicGenerator extends RandomGenerator{
return out;
}
public void inverseFloodFill(Tile[][] tiles, Tile start, Block block){
public void inverseFloodFill(Tiles tiles, Tile start, Block block){
IntArray arr = new IntArray();
arr.add(start.pos());
while(!arr.isEmpty()){
int i = arr.pop();
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){
int newx = x + point.x, newy = y + point.y;
if(Structs.inBounds(newx, newy, width, height)){
Tile child = tiles[newx][newy];
if(tiles.in(newx, newy)){
Tile child = tiles.getn(newx, newy);
if(child.block() == Blocks.air && child.cost != 2){
child.cost = 2;
arr.add(child.pos());
@ -244,7 +244,7 @@ public abstract class BasicGenerator extends RandomGenerator{
for(int x = 0; x < width; x ++){
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){
tile.setBlock(block);
}

View File

@ -19,5 +19,5 @@ public abstract class Generator{
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;
import arc.struct.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.game.*;
import mindustry.io.*;
import mindustry.maps.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.blocks.storage.*;
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{
private Map map;
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){
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){
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(){
return map;
}
@ -61,110 +41,44 @@ public class MapGenerator extends Generator{
}
@Override
public void generate(Tile[][] tiles){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
tiles[x][y] = new Tile(x, y);
}
}
public void generate(Tiles tiles){
tiles.fill();
SaveIO.load(map.file);
Array<Point2> players = new Array<>();
Array<Point2> enemies = new Array<>();
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
if(tiles[x][y].block() instanceof CoreBlock && tiles[x][y].getTeam() == state.rules.defaultTeam){
players.add(new Point2(x, y));
tiles[x][y].setBlock(Blocks.air);
}
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));
}
for(Tile tile : tiles){
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){
if(enemySpawns > enemies.size){
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number for map: " + mapName);
boolean anyCores = false;
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();
for(int i = 0; i < enemySpawns; i++){
Point2 point = enemies.get(i);
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();
}
}
}
}
if(tile.block() instanceof CoreBlock && tile.getTeam() == state.rules.defaultTeam){
schematics.placeLoadout(loadout, tile.x, tile.y);
anyCores = true;
}
}
Point2 core = players.random();
if(core == null){
if(!anyCores){
throw new IllegalArgumentException("All zone maps must have a core.");
}
schematics.placeLoadout(loadout, core.x, core.y);
world.prepareTiles(tiles);
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;
import arc.struct.StringMap;
import mindustry.content.Blocks;
import mindustry.maps.Map;
import mindustry.world.Block;
import mindustry.world.Tile;
import arc.struct.*;
import mindustry.content.*;
import mindustry.maps.*;
import mindustry.world.*;
import static mindustry.Vars.world;
@ -18,14 +17,14 @@ public abstract class RandomGenerator extends Generator{
}
@Override
public void generate(Tile[][] tiles){
public void generate(Tiles tiles){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
floor = Blocks.air;
block = Blocks.air;
ore = Blocks.air;
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()));
}
public abstract void decorate(Tile[][] tiles);
public abstract void decorate(Tiles tiles);
/**
* 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 mindustry.content.Blocks;
import mindustry.maps.generators.BasicGenerator;
import mindustry.world.Tile;
import mindustry.world.*;
import static mindustry.Vars.schematics;
@ -19,7 +19,7 @@ public class DesertWastesGenerator extends BasicGenerator{
}
@Override
public void decorate(Tile[][] tiles){
public void decorate(Tiles tiles){
ores(tiles);
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, spawnX, spawnY, 20);
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.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);
//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);
}
}

View File

@ -1,9 +1,9 @@
package mindustry.maps.zonegen;
import arc.math.Mathf;
import mindustry.content.Blocks;
import mindustry.maps.generators.BasicGenerator;
import mindustry.world.Tile;
import arc.math.*;
import mindustry.content.*;
import mindustry.maps.generators.*;
import mindustry.world.*;
import static mindustry.Vars.schematics;
@ -19,7 +19,7 @@ public class OvergrowthGenerator extends BasicGenerator{
}
@Override
public void decorate(Tile[][] tiles){
public void decorate(Tiles tiles){
ores(tiles);
terrain(tiles, Blocks.sporePine, 70f, 1.4f, 1f);
@ -34,12 +34,12 @@ public class OvergrowthGenerator extends BasicGenerator{
erase(tiles, endX, endY, 10);
erase(tiles, spawnX, spawnY, 20);
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);
//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);
}
}

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

View File

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

View File

@ -92,7 +92,7 @@ public class GameOverDialog extends FloatingDialog{
hide();
state.set(State.menu);
logic.reset();
ui.deploy.show();
ui.planet.show();
}).size(130f, 60f);
}else{
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();
});
hidden(ui.deploy::setup);
hidden(ui.planet::setup);
addCloseButton();

View File

@ -14,6 +14,7 @@ import mindustry.ui.Cicon;
import static mindustry.Vars.*;
//TODO remove
public class ZoneInfoDialog extends FloatingDialog{
private LoadoutDialog loadout = new LoadoutDialog();
@ -152,13 +153,13 @@ public class ZoneInfoDialog extends FloatingDialog{
if(!data.isUnlocked(zone)){
Sounds.unlock.play();
data.unlockContent(zone);
ui.deploy.setup();
ui.planet.setup();
setup(zone);
}else{
ui.deploy.hide();
ui.planet.hide();
data.removeItems(zone.getLaunchCost());
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();

View File

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

View File

@ -100,7 +100,7 @@ public class MenuFragment extends Fragment{
container.defaults().size(size).pad(5).padTop(4f);
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)),
maps = new MobileButton(Icon.download, "$loadgame", () -> checkPlay(ui.load::show)),
join = new MobileButton(Icon.add, "$joingame", () -> checkPlay(ui.join::show)),
@ -165,7 +165,7 @@ public class MenuFragment extends Fragment{
buttons(t,
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("$customgame", Icon.terrain, () -> checkPlay(ui.custom::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;
}
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.y = (short)y;
this.floor = (Floor)content.block(floor);
this.overlay = (Floor)content.block(overlay);
this.block = content.block(wall);
this.floor = (Floor)floor;
this.overlay = (Floor)overlay;
this.block = wall;
//update entity and create it if needed
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}. */
public int pos(){
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.math.*;
import arc.math.geom.*;
import arc.util.ArcAnnotate.*;
import arc.struct.*;
import mindustry.content.*;
import mindustry.entities.*;
@ -35,13 +36,13 @@ public class Floor extends Block{
/** Effect displayed when drowning on this floor. */
public Effect drownUpdateEffect = Fx.bubble;
/** Status effect applied when walking on. */
public StatusEffect status = StatusEffects.none;
public @NonNull StatusEffect status = StatusEffects.none;
/** Intensity of applied status effect. */
public float statusDuration = 60f;
/** 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 */
public Item itemDrop = null;
public @Nullable Item itemDrop = null;
/** whether this block can be drowned in */
public boolean isLiquid;
/** 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(){{
title = "Mindustry";
maximized = true;
depth = 0;
stencil = 0;
width = 900;
height = 700;
@ -267,7 +266,7 @@ public class DesktopLauncher extends ClientLauncher{
}else{
if(ui.editor != null && ui.editor.isShown()){
uiState = "In Editor";
}else if(ui.deploy != null && ui.deploy.isShown()){
}else if(ui.planet != null && ui.planet.isShown()){
uiState = "In Launch Selection";
}else{
uiState = "In Menu";

View File

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

View File

@ -109,14 +109,10 @@ public class ApplicationTests{
@Test
void createMap(){
Tile[][] tiles = world.createTiles(8, 8);
Tiles tiles = world.resize(8, 8);
world.beginMapLoad();
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
tiles[x][y] = new Tile(x, y);
}
}
tiles.fill();
world.endMapLoad();
}
@ -379,29 +375,29 @@ public class ApplicationTests{
@Test
void allBlockTest(){
Tile[][] tiles = world.createTiles(256*2 + 20, 10);
Tiles tiles = world.resize(256*2 + 20, 10);
world.beginMapLoad();
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
tiles[x][y] = new Tile(x, y, Blocks.stone.id, (byte)0, (byte)0);
for(int x = 0; x < tiles.width(); x++){
for(int y = 0; y < tiles.height(); y++){
tiles.set(x, y, new Tile(x, y, Blocks.stone, Blocks.air, Blocks.air));
}
}
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++);
if(block.isBuildable()){
x += block.size;
tiles[x][5].setBlock(block);
tiles.get(x, 5).setBlock(block);
x += block.size;
}
}
world.endMapLoad();
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
Tile tile = world.tile(x, y);
for(int x = 0; x < tiles.width(); x++){
for(int y = 0; y < tiles.height(); y++){
Tile tile = world.rawTile(x, y);
if(tile.entity != null){
try{
tile.entity.update();
@ -429,7 +425,7 @@ public class ApplicationTests{
void depositTest(Block block, Item item){
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;
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<>();
if(world == null) world = new World();
fail("Zone validity tests need to be refactored!");
for(Zone zone : content.zones()){
out.add(dynamicTest(zone.name, () -> {
zone.generator.init(zone.loadout);
logic.reset();
try{
world.loadGenerator(zone.generator);
//world.loadGenerator(zone.generator);
}catch(SaveException e){
e.printStackTrace();
return;