Merge branches 'master' and 'new-map-format' of https://github.com/Anuken/Mindustry into new-map-format

# Conflicts:
#	core/src/io/anuke/mindustry/editor/MapEditorDialog.java
#	core/src/io/anuke/mindustry/io/MapIO.java
#	core/src/io/anuke/mindustry/maps/MapTileData.java
This commit is contained in:
Anuken
2019-01-23 13:52:36 -05:00
202 changed files with 9917 additions and 6290 deletions

View File

@@ -205,7 +205,7 @@ public class BlockIndexer{
for(int x = Math.max(0, tile.x - oreQuadrantSize / 2); x < tile.x + oreQuadrantSize / 2 && x < world.width(); x++){
for(int y = Math.max(0, tile.y - oreQuadrantSize / 2); y < tile.y + oreQuadrantSize / 2 && y < world.height(); y++){
Tile res = world.tile(x, y);
if(res.block() == Blocks.air && res.floor().drops != null && res.floor().drops.item == item){
if(res.block() == Blocks.air && res.floor().itemDrop == item){
return res;
}
}
@@ -243,9 +243,9 @@ public class BlockIndexer{
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
Tile result = world.tile(x, y);
if( result == null || result.block().drops == null || !scanOres.contains(result.block().drops.item)) continue;
if( result == null || result.floor().itemDrop == null || !scanOres.contains(result.floor().itemDrop)) continue;
itemSet.add(result.block().drops.item);
itemSet.add(result.floor().itemDrop);
}
}
@@ -322,8 +322,8 @@ public class BlockIndexer{
Tile tile = world.tile(x, y);
//add position of quadrant to list when an ore is found
if(tile.floor().drops != null && scanOres.contains(tile.floor().drops.item) && tile.block() == Blocks.air){
ores.get(tile.floor().drops.item).add(world.tile(
if(tile.floor().itemDrop != null && scanOres.contains(tile.floor().itemDrop) && tile.block() == Blocks.air){
ores.get(tile.floor().itemDrop).add(world.tile(
//make sure to clamp quadrant middle position, since it might go off bounds
Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1),
Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1)));

View File

@@ -182,8 +182,6 @@ public class Pathfinder{
createFor(team);
}
}
world.spawner.checkAllQuadrants();
}
class PathData{

View File

@@ -2,32 +2,18 @@ package io.anuke.mindustry.ai;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.GridBits;
import io.anuke.arc.math.Angles;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.Squad;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Waves;
import io.anuke.mindustry.world.Tile;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class WaveSpawner{
private static final int quadsize = 6;
private GridBits quadrants;
private Array<SpawnGroup> groups;
private boolean dynamicSpawn;
private Array<FlyerSpawn> flySpawns = new Array<>();
private Array<GroundSpawn> groundSpawns = new Array<>();
@@ -35,273 +21,76 @@ public class WaveSpawner{
Events.on(WorldLoadEvent.class, e -> reset());
}
public void write(DataOutput output) throws IOException{
output.writeShort(flySpawns.size);
for(FlyerSpawn spawn : flySpawns){
output.writeFloat(spawn.angle);
}
output.writeShort(groundSpawns.size);
for(GroundSpawn spawn : groundSpawns){
output.writeShort((short) spawn.x);
output.writeShort((short) spawn.y);
}
}
public void read(DataInput input) throws IOException{
short flya = input.readShort();
for(int i = 0; i < flya; i++){
FlyerSpawn spawn = new FlyerSpawn();
spawn.angle = input.readFloat();
flySpawns.add(spawn);
}
short grounda = input.readShort();
for(int i = 0; i < grounda; i++){
GroundSpawn spawn = new GroundSpawn();
spawn.x = input.readShort();
spawn.y = input.readShort();
groundSpawns.add(spawn);
}
}
public void spawnEnemies(){
int flyGroups = 0;
int groundGroups = 0;
//count total subgroups spawned by flying/group types
for(SpawnGroup group : groups){
int amount = group.getGroupsSpawned(state.wave);
if(group.type.isFlying){
flyGroups += amount;
}else if(dynamicSpawn){
groundGroups += amount;
}
}
int addGround = groundGroups - groundSpawns.size, addFly = flyGroups - flySpawns.size;
//add extra groups if the total exceeds it
if(dynamicSpawn){
for(int i = 0; i < addGround; i++){
GroundSpawn spawn = new GroundSpawn();
findLocation(spawn);
groundSpawns.add(spawn);
}
}
for(int i = 0; i < addFly; i++){
FlyerSpawn spawn = new FlyerSpawn();
findLocation(spawn);
flySpawns.add(spawn);
}
//store index of last used fly/ground spawn locations
int flyCount = 0, groundCount = 0;
for(SpawnGroup group : groups){
int groups = group.getGroupsSpawned(state.wave);
int spawned = group.getUnitsSpawned(state.wave);
for(int i = 0; i < groups; i++){
Squad squad = new Squad();
float spawnX, spawnY;
float spread;
if(!group.type.isFlying && groundCount >= groundSpawns.size) continue;
if(group.type.isFlying){
FlyerSpawn spawn = flySpawns.get(flyCount);
float spawnX, spawnY;
float spread;
if(group.type.isFlying){
for(FlyerSpawn spawn : flySpawns){
Squad squad = new Squad();
float margin = 40f; //how far away from the edge flying units spawn
spawnX = world.width() * tilesize / 2f + sqrwavex(spawn.angle) * (world.width() / 2f * tilesize + margin);
spawnY = world.height() * tilesize / 2f + sqrwavey(spawn.angle) * (world.height() / 2f * tilesize + margin);
float trns = (world.width() + world.height()) * tilesize;
spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(spawn.angle, trns), -margin, world.width() * tilesize + margin);
spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(spawn.angle, trns), -margin, world.height() * tilesize + margin);
spread = margin / 1.5f;
flyCount++;
}else{ //make sure it works for non-dynamic spawns
GroundSpawn spawn = groundSpawns.get(groundCount);
if(dynamicSpawn){
checkQuadrant(spawn.x, spawn.y);
if(!getQuad(spawn.x, spawn.y)){
findLocation(spawn);
}
for(int i = 0; i < spawned; i++){
BaseUnit unit = group.createUnit(waveTeam);
unit.setWave();
unit.setSquad(squad);
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
unit.add();
}
spawnX = spawn.x * quadsize * tilesize + quadsize * tilesize / 2f;
spawnY = spawn.y * quadsize * tilesize + quadsize * tilesize / 2f;
spread = quadsize * tilesize / 3f;
groundCount++;
}
}else{
for(GroundSpawn spawn : groundSpawns){
Squad squad = new Squad();
spawnX = spawn.x * tilesize;
spawnY = spawn.y * tilesize;
spread = tilesize;
for(int j = 0; j < spawned; j++){
BaseUnit unit = group.createUnit(Team.red);
unit.setWave();
unit.setSquad(squad);
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
unit.add();
}
}
}
}
public void checkAllQuadrants(){
for(int x = 0; x < quadWidth(); x++){
for(int y = 0; y < quadHeight(); y++){
checkQuadrant(x, y);
}
}
}
private void checkQuadrant(int quadx, int quady){
setQuad(quadx, quady, true);
outer:
for(int x = quadx * quadsize; x < world.width() && x < (quadx + 1) * quadsize; x++){
for(int y = quady * quadsize; y < world.height() && y < (quady + 1) * quadsize; y++){
Tile tile = world.tile(x, y);
if(tile == null || tile.solid() || tile.getTeam() == defaultTeam || world.pathfinder.getValueforTeam(Team.red, x, y) == Float.MAX_VALUE || tile.floor().isLiquid){
setQuad(quadx, quady, false);
break outer;
for(int i = 0; i < spawned; i++){
BaseUnit unit = group.createUnit(waveTeam);
unit.setWave();
unit.setSquad(squad);
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
unit.add();
}
}
}
}
}
private void reset(){
dynamicSpawn = false;
flySpawns.clear();
groundSpawns.clear();
quadrants = new GridBits(quadWidth(), quadHeight());
groups = Waves.getSpawns();
dynamicSpawn = true;
groups = state.rules.spawns;
for(int x = 0; x < world.width(); x++){
for(int y = 0; y < world.height(); y++){
if(world.tile(x, y).block() == Blocks.spawn){
dynamicSpawn = false;
GroundSpawn spawn = new GroundSpawn();
spawn.x = x/quadsize;
spawn.y = y/quadsize;
spawn.x = x;
spawn.y = y;
groundSpawns.add(spawn);
FlyerSpawn fspawn = new FlyerSpawn();
fspawn.angle = Angles.angle(world.width()/2f, world.height()/2f, x, y);
flySpawns.add(fspawn);
}
}
}
}
private boolean getQuad(int quadx, int quady){
return quadrants.get(quadx, quady);
}
private void setQuad(int quadx, int quady, boolean valid){
if(quadrants == null){
quadrants = new GridBits(quadWidth(), quadHeight());
}
if(!Structs.inBounds(quadx, quady, quadWidth(), quadHeight())){
return;
}
quadrants.set(quadx, quady, valid);
}
//TODO instead of randomly scattering locations around the map, find spawns close to each other
private void findLocation(GroundSpawn spawn){
spawn.x = Mathf.random(quadWidth()-1);
spawn.y = Mathf.random(quadHeight()-1);
int shellWidth = quadWidth() * 2 + quadHeight() * 2 * 6;
shellWidth = Math.min(quadWidth() * quadHeight() / 4, shellWidth);
traverseSpiral(quadWidth(), quadHeight(), Mathf.random(shellWidth), (x, y) -> {
if(getQuad(x, y)){
spawn.x = x;
spawn.y = y;
return true;
}
return false;
});
}
//TODO instead of randomly scattering locations around the map, find spawns close to each other
private void findLocation(FlyerSpawn spawn){
spawn.angle = Mathf.random(360f);
}
private int quadWidth(){
return Mathf.ceil(world.width() / (float) quadsize);
}
private int quadHeight(){
return Mathf.ceil(world.height() / (float) quadsize);
}
private class FlyerSpawn{
//square angle
float angle;
}
private class GroundSpawn{
//quadrant spawn coordinates
int x, y;
}
//utility methods
float sqrwavex(float degrees){
degrees = Mathf.mod(degrees, 360f);
if(degrees < 45){
return 1;
}else if(degrees < 135){
return 1f - (degrees - 45f) / 90f;
}else if(degrees < 225){
return -1f;
}else if(degrees < 315){
return (degrees - 225) / 90f;
}else{
return 1f;
}
}
float sqrwavey(float degrees){
return sqrwavex(degrees + 90f);
}
void traverseSpiral(int width, int height, int offset, SpiralTraverser con){
int directionIdx = 0;
int curRow = 0, curCol = 0;
for(int i = 0; i < height * width; i++){
if(i >= offset && con.accept(curCol, curRow)) break;
int same = 1, row = curRow, col = curCol;
if(row > height - 1 - row){
row = height - 1 - row;
same = 0;
}
if(col >= width - 1 - col){
col = width - 1 - col;
same = 0;
}
row -= same;
if(row == col){
directionIdx = (directionIdx + 1) % 4;
}
curRow += directions[directionIdx][0];
curCol += directions[directionIdx][1];
}
}
interface SpiralTraverser{
boolean accept(int x, int y);
}
private static int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
}

View File

@@ -2,11 +2,9 @@ package io.anuke.mindustry.content;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.graphics.CacheLayer;
import io.anuke.mindustry.type.Category;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block;
@@ -22,16 +20,20 @@ import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import io.anuke.mindustry.world.blocks.storage.LaunchPad;
import io.anuke.mindustry.world.blocks.storage.SortedUnloader;
import io.anuke.mindustry.world.blocks.storage.Vault;
import io.anuke.mindustry.world.blocks.units.*;
import io.anuke.mindustry.world.blocks.units.MechPad;
import io.anuke.mindustry.world.blocks.units.Reconstructor;
import io.anuke.mindustry.world.blocks.units.RepairPoint;
import io.anuke.mindustry.world.blocks.units.UnitFactory;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.state;
public class Blocks implements ContentList{
public static Block
//environment
air, blockpart, spawn, space, metalfloor, deepwater, water, tar, stone, blackstone, dirt, sand, ice, snow,
grass, shrub, rock, icerock, blackrock, rocksSmall, rocksMedium,
air, part, spawn, space, metalfloor, deepwater, water, tar, stone, craters, blackstone, dirt, sand, ice, snow,
grass, shrub, rock, icerock, blackrock, rocks,
//crafting
siliconSmelter, plastaniumCompressor, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer,
@@ -61,7 +63,7 @@ public class Blocks implements ContentList{
core, vault, container, unloader, launchPad,
//turrets
duo, scorch, hail, wave, lancer, arc, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown,
duo, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown,
//units
spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, titanFactory,
@@ -74,9 +76,7 @@ public class Blocks implements ContentList{
public void load(){
//region environment
air = new Floor("air"){
{
blend = false;
air = new Floor("air"){{
alwaysReplace = true;
}
@@ -85,17 +85,10 @@ public class Blocks implements ContentList{
public void init(){}
};
blockpart = new BlockPart();
part = new BlockPart();
spawn = new Block("spawn"){
public void drawShadow(Tile tile){}
public void draw(Tile tile){
Draw.color(Color.SCARLET);
Lines.circle(tile.worldx(), tile.worldy(), 4f +Mathf.absin(Time.time(), 6f, 6f));
Draw.color();
}
};
//Registers build blocks from size 1-6
@@ -109,7 +102,6 @@ public class Blocks implements ContentList{
variants = 0;
cacheLayer = CacheLayer.space;
solid = true;
blend = false;
minimapColor = Color.valueOf("000001");
}};
@@ -157,9 +149,11 @@ public class Blocks implements ContentList{
stone = new Floor("stone"){{
hasOres = true;
blends = block -> block != this && !(block instanceof OreBlock);
minimapColor = Color.valueOf("323232");
playerUnmineable = true;
}};
craters = new Floor("craters"){{
minimapColor = Color.valueOf("323232");
}};
blackstone = new Floor("blackstone"){{
@@ -173,7 +167,7 @@ public class Blocks implements ContentList{
}};
sand = new Floor("sand"){{
drops = new ItemStack(Items.sand, 1);
itemDrop = Items.sand;
minimapColor = Color.valueOf("988a67");
hasOres = true;
playerUnmineable = true;
@@ -210,22 +204,15 @@ public class Blocks implements ContentList{
variants = 1;
}};
rocksSmall = new Rock("rocks-small"){{
variants = 2;
breakable = alwaysReplace = false;
solid = true;
rocks = new StaticWall("rocks"){{
variants = 2;
}};
rocksMedium = new Rock("rocks-medium"){{
variants = 2;
breakable = alwaysReplace = false;
solid = true;
}};
//endregion
//region crafting
siliconSmelter = new PowerSmelter("silicon-smelter"){{
requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.lead, 50));
health = 90;
craftEffect = Fx.smeltsmoke;
result = Items.silicon;
@@ -239,6 +226,7 @@ public class Blocks implements ContentList{
}};
plastaniumCompressor = new PlastaniumCompressor("plastanium-compressor"){{
requirements(Category.crafting, ItemStack.with(Items.silicon, 160, Items.lead, 230, Items.graphite, 120, Items.titanium, 160));
hasItems = true;
liquidCapacity = 60f;
craftTime = 60f;
@@ -255,6 +243,7 @@ public class Blocks implements ContentList{
}};
phaseWeaver = new PhaseWeaver("phase-weaver"){{
requirements(Category.crafting, ItemStack.with(Items.silicon, 260, Items.lead, 240, Items.thorium, 150));
craftEffect = Fx.smeltsmoke;
result = Items.phasefabric;
craftTime = 120f;
@@ -265,6 +254,7 @@ public class Blocks implements ContentList{
}};
surgeSmelter = new PowerSmelter("alloy-smelter"){{
requirements(Category.crafting, ItemStack.with(Items.silicon, 160, Items.lead, 160, Items.thorium, 140));
craftEffect = Fx.smeltsmoke;
result = Items.surgealloy;
craftTime = 75f;
@@ -278,6 +268,7 @@ public class Blocks implements ContentList{
}};
cryofluidMixer = new LiquidMixer("cryofluidmixer"){{
requirements(Category.crafting, ItemStack.with(Items.lead, 130, Items.silicon, 80, Items.thorium, 90));
outputLiquid = Liquids.cryofluid;
liquidPerItem = 50f;
size = 2;
@@ -289,6 +280,7 @@ public class Blocks implements ContentList{
}};
blastMixer = new GenericCrafter("blast-mixer"){{
requirements(Category.crafting, ItemStack.with(Items.lead, 60, Items.titanium, 40));
hasItems = true;
hasPower = true;
hasLiquids = true;
@@ -301,6 +293,7 @@ public class Blocks implements ContentList{
}};
pyratiteMixer = new PowerSmelter("pyratite-mixer"){{
requirements(Category.crafting, ItemStack.with(Items.copper, 100, Items.lead, 50));
flameColor = Color.CLEAR;
hasItems = true;
hasPower = true;
@@ -313,6 +306,7 @@ public class Blocks implements ContentList{
}};
melter = new PowerCrafter("melter"){{
requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.lead, 70, Items.graphite, 90));
health = 200;
outputLiquid = Liquids.slag;
outputLiquidAmount = 1.5f;
@@ -324,11 +318,12 @@ public class Blocks implements ContentList{
}};
separator = new Separator("separator"){{
requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.titanium, 50));
results = new ItemStack[]{
new ItemStack(Items.copper, 5),
new ItemStack(Items.lead, 3),
new ItemStack(Items.titanium, 2),
new ItemStack(Items.thorium, 1)
new ItemStack(Items.copper, 5),
new ItemStack(Items.lead, 3),
new ItemStack(Items.titanium, 2),
new ItemStack(Items.thorium, 1)
};
hasPower = true;
filterTime = 15f;
@@ -343,6 +338,7 @@ public class Blocks implements ContentList{
}};
biomatterCompressor = new Compressor("biomattercompressor"){{
requirements(Category.crafting, ItemStack.with(Items.lead, 70, Items.silicon, 60));
liquidCapacity = 60f;
craftTime = 20f;
outputLiquid = Liquids.oil;
@@ -356,6 +352,7 @@ public class Blocks implements ContentList{
}};
pulverizer = new Pulverizer("pulverizer"){{
requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.lead, 50));
output = Items.sand;
craftEffect = Fx.pulverize;
craftTime = 40f;
@@ -367,73 +364,107 @@ public class Blocks implements ContentList{
}};
incinerator = new Incinerator("incinerator"){{
requirements(Category.crafting, ItemStack.with(Items.graphite, 10, Items.lead, 30));
health = 90;
}};
//endregion
//region sandbox
powerVoid = new PowerVoid("power-void");
powerSource = new PowerSource("power-source");
itemSource = new ItemSource("item-source");
itemVoid = new ItemVoid("item-void");
liquidSource = new LiquidSource("liquid-source");
powerVoid = new PowerVoid("power-void"){{
requirements(Category.power, () -> state.rules.infiniteResources, ItemStack.with());
alwaysUnlocked = true;
}};
powerSource = new PowerSource("power-source"){{
requirements(Category.power, () -> state.rules.infiniteResources, ItemStack.with());
alwaysUnlocked = true;
}};
itemSource = new ItemSource("item-source"){{
requirements(Category.distribution, () -> state.rules.infiniteResources, ItemStack.with());
alwaysUnlocked = true;
}};
itemVoid = new ItemVoid("item-void"){{
requirements(Category.distribution, () -> state.rules.infiniteResources, ItemStack.with());
alwaysUnlocked = true;
}};
liquidSource = new LiquidSource("liquid-source"){{
requirements(Category.liquid, () -> state.rules.infiniteResources, ItemStack.with());
alwaysUnlocked = true;
}};
//endregion
//region defense
int wallHealthMultiplier = 3;
copperWall = new Wall("copper-wall"){{
requirements(Category.defense, ItemStack.with(Items.copper, 12));
health = 80 * wallHealthMultiplier;
}};
copperWallLarge = new Wall("copper-wall-large"){{
requirements(Category.defense, ItemStack.with(Items.copper, 12 * 4));
requirements(Category.defense, ItemStack.with(Items.copper, 12));
health = 80 * 4 * wallHealthMultiplier;
size = 2;
}};
titaniumWall = new Wall("titanium-wall"){{
requirements(Category.defense, ItemStack.with(Items.titanium, 12));
health = 110 * wallHealthMultiplier;
}};
titaniumWallLarge = new Wall("titanium-wall-large"){{
requirements(Category.defense, ItemStack.with(Items.titanium, 12 * 4));
requirements(Category.defense, ItemStack.with(Items.titanium, 12));
health = 110 * wallHealthMultiplier * 4;
size = 2;
}};
thoriumWall = new Wall("thorium-wall"){{
requirements(Category.defense, ItemStack.with(Items.thorium, 12));
health = 200 * wallHealthMultiplier;
}};
thoriumWallLarge = new Wall("thorium-wall-large"){{
requirements(Category.defense, ItemStack.with(Items.thorium, 12 * 4));
requirements(Category.defense, ItemStack.with(Items.thorium, 12));
health = 200 * wallHealthMultiplier * 4;
size = 2;
}};
phaseWall = new DeflectorWall("phase-wall"){{
requirements(Category.defense, ItemStack.with(Items.phasefabric, 12));
health = 150 * wallHealthMultiplier;
}};
phaseWallLarge = new DeflectorWall("phase-wall-large"){{
requirements(Category.defense, ItemStack.with(Items.phasefabric, 12 * 4));
requirements(Category.defense, ItemStack.with(Items.phasefabric, 12));
health = 150 * 4 * wallHealthMultiplier;
size = 2;
}};
surgeWall = new SurgeWall("surge-wall"){{
requirements(Category.defense, ItemStack.with(Items.surgealloy, 12));
health = 230 * wallHealthMultiplier;
}};
surgeWallLarge = new SurgeWall("surge-wall-large"){{
requirements(Category.defense, ItemStack.with(Items.surgealloy, 12 * 4));
requirements(Category.defense, ItemStack.with(Items.surgealloy, 12));
health = 230 * 4 * wallHealthMultiplier;
size = 2;
}};
door = new Door("door"){{
requirements(Category.defense, ItemStack.with(Items.titanium, 12, Items.silicon, 8));
health = 100 * wallHealthMultiplier;
}};
doorLarge = new Door("door-large"){{
requirements(Category.defense, ItemStack.with(Items.titanium, 12 * 4, Items.silicon, 8 * 4));
requirements(Category.defense, ItemStack.with(Items.titanium, 12, Items.silicon, 8));
openfx = Fx.dooropenlarge;
closefx = Fx.doorcloselarge;
health = 100 * 4 * wallHealthMultiplier;
@@ -441,85 +472,107 @@ public class Blocks implements ContentList{
}};
mendProjector = new MendProjector("mend-projector"){{
requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.titanium, 50, Items.silicon, 180));
consumes.power(0.2f, 1.0f);
size = 2;
consumes.item(Items.phasefabric).optional(true);
}};
overdriveProjector = new OverdriveProjector("overdrive-projector"){{
requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.titanium, 150, Items.silicon, 250));
consumes.power(0.35f, 1.0f);
size = 2;
consumes.item(Items.phasefabric).optional(true);
}};
forceProjector = new ForceProjector("force-projector"){{
requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.titanium, 150, Items.silicon, 250));
size = 3;
consumes.item(Items.phasefabric).optional(true);
}};
shockMine = new ShockMine("shock-mine"){{
requirements(Category.effect, ItemStack.with(Items.lead, 50, Items.silicon, 25));
health = 40;
damage = 11;
tileDamage = 7f;
length = 10;
tendrils = 5;
}};
//endregion
//region distribution
conveyor = new Conveyor("conveyor"){{
requirements(Category.distribution, ItemStack.with(Items.copper, 1), true);
health = 45;
speed = 0.03f;
}};
titaniumConveyor = new Conveyor("titanium-conveyor"){{
requirements(Category.distribution, ItemStack.with(Items.copper, 2, Items.titanium, 1));
health = 65;
speed = 0.07f;
}};
junction = new Junction("junction"){{
requirements(Category.distribution, ItemStack.with(Items.copper, 2));
speed = 26;
capacity = 32;
}};
itemBridge = new BufferedItemBridge("bridge-conveyor"){{
requirements(Category.distribution, ItemStack.with(Items.titanium, 8, Items.copper, 8));
range = 4;
speed = 60f;
bufferCapacity = 15;
}};
phaseConveyor = new ItemBridge("phase-conveyor"){{
requirements(Category.distribution, ItemStack.with(Items.phasefabric, 10, Items.silicon, 15, Items.lead, 20, Items.graphite, 20));
range = 12;
hasPower = true;
consumes.power(0.03f, 1.0f);
}};
sorter = new Sorter("sorter");
sorter = new Sorter("sorter"){{
requirements(Category.distribution, ItemStack.with(Items.titanium, 4, Items.copper, 4));
router = new Router("router");
}};
router = new Router("router"){{
requirements(Category.distribution, ItemStack.with(Items.copper, 6));
}};
distributor = new Router("distributor"){{
requirements(Category.distribution, ItemStack.with(Items.titanium, 8, Items.copper, 8));
size = 2;
}};
overflowGate = new OverflowGate("overflow-gate");
overflowGate = new OverflowGate("overflow-gate"){{
requirements(Category.distribution, ItemStack.with(Items.titanium, 4, Items.copper, 8));
}};
massDriver = new MassDriver("mass-driver"){{
requirements(Category.distribution, ItemStack.with(Items.titanium, 250, Items.silicon, 150, Items.lead, 250, Items.thorium, 100));
size = 3;
itemCapacity = 60;
range = 440f;
}};
//endregion
//region liquid
mechanicalPump = new Pump("mechanical-pump"){{
requirements(Category.liquid, ItemStack.with(Items.copper, 30, Items.lead, 20));
pumpAmount = 0.1f;
tier = 0;
}};
rotaryPump = new Pump("rotary-pump"){{
requirements(Category.liquid, ItemStack.with(Items.copper, 140, Items.lead, 100, Items.silicon, 40, Items.titanium, 70));
pumpAmount = 0.2f;
consumes.power(0.015f);
liquidCapacity = 30f;
@@ -529,6 +582,7 @@ public class Blocks implements ContentList{
}};
thermalPump = new Pump("thermal-pump"){{
requirements(Category.liquid, ItemStack.with(Items.copper, 160, Items.lead, 130, Items.silicon, 60, Items.titanium, 80, Items.thorium, 70));
pumpAmount = 0.275f;
consumes.power(0.03f);
liquidCapacity = 40f;
@@ -538,47 +592,83 @@ public class Blocks implements ContentList{
}};
conduit = new Conduit("conduit"){{
requirements(Category.liquid, ItemStack.with(Items.lead, 1));
health = 45;
}};
pulseConduit = new Conduit("pulse-conduit"){{
requirements(Category.liquid, ItemStack.with(Items.titanium, 1, Items.lead, 1));
liquidCapacity = 16f;
liquidFlowFactor = 4.9f;
health = 90;
}};
liquidRouter = new LiquidRouter("liquid-router"){{
requirements(Category.liquid, ItemStack.with(Items.titanium, 4, Items.lead, 4));
liquidCapacity = 20f;
}};
liquidTank = new LiquidTank("liquid-tank"){{
requirements(Category.liquid, ItemStack.with(Items.titanium, 50, Items.lead, 50));
size = 3;
liquidCapacity = 1500f;
health = 500;
}};
liquidJunction = new LiquidJunction("liquid-junction");
liquidJunction = new LiquidJunction("liquid-junction"){{
requirements(Category.liquid, ItemStack.with(Items.titanium, 4, Items.lead, 4));
}};
bridgeConduit = new LiquidExtendingBridge("bridge-conduit"){{
requirements(Category.liquid, ItemStack.with(Items.titanium, 8, Items.lead, 8));
range = 4;
hasPower = false;
}};
phaseConduit = new LiquidBridge("phase-conduit"){{
requirements(Category.liquid, ItemStack.with(Items.phasefabric, 10, Items.silicon, 15, Items.lead, 20, Items.titanium, 20));
range = 12;
hasPower = true;
consumes.power(0.03f, 1.0f);
}};
//endregion
//region power
powerNode = new PowerNode("power-node"){{
requirements(Category.power, ItemStack.with(Items.copper, 2, Items.lead, 6));
maxNodes = 4;
laserRange = 6;
}};
powerNodeLarge = new PowerNode("power-node-large"){{
requirements(Category.power, ItemStack.with(Items.titanium, 10, Items.lead, 20, Items.silicon, 6));
requirements(Category.power, ItemStack.with(Items.copper, 2, Items.lead, 6));
size = 2;
maxNodes = 6;
laserRange = 9.5f;
}};
battery = new Battery("battery"){{
requirements(Category.power, ItemStack.with(Items.copper, 8, Items.lead, 30, Items.silicon, 4));
consumes.powerBuffered(320f, 1f);
}};
batteryLarge = new Battery("battery-large"){{
requirements(Category.power, ItemStack.with(Items.titanium, 40, Items.lead, 80, Items.silicon, 30));
requirements(Category.power, ItemStack.with(Items.copper, 8, Items.lead, 30, Items.silicon, 4));
size = 3;
consumes.powerBuffered(2000f, 1f);
}};
combustionGenerator = new BurnerGenerator("combustion-generator"){{
requirements(Category.power, ItemStack.with(Items.copper, 50, Items.lead, 30));
powerProduction = 0.09f;
itemDuration = 40f;
}};
thermalGenerator = new LiquidHeatGenerator("thermal-generator"){{
requirements(Category.power, ItemStack.with(Items.copper, 80, Items.graphite, 70, Items.lead, 100, Items.silicon, 70, Items.thorium, 70));
maxLiquidGenerate = 2f;
powerProduction = 2f;
generateEffect = Fx.redgeneratespark;
@@ -586,6 +676,7 @@ public class Blocks implements ContentList{
}};
turbineGenerator = new TurbineGenerator("turbine-generator"){{
requirements(Category.power, ItemStack.with(Items.copper, 70, Items.graphite, 50, Items.lead, 80, Items.silicon, 60));
powerProduction = 0.28f;
itemDuration = 30f;
consumes.liquid(Liquids.water, 0.05f);
@@ -593,21 +684,25 @@ public class Blocks implements ContentList{
}};
rtgGenerator = new DecayGenerator("rtg-generator"){{
requirements(Category.power, ItemStack.with(Items.lead, 200, Items.silicon, 150, Items.phasefabric, 50, Items.plastanium, 150, Items.thorium, 100));
size = 2;
powerProduction = 0.3f;
itemDuration = 220f;
}};
solarPanel = new SolarGenerator("solar-panel"){{
requirements(Category.power, ItemStack.with(Items.lead, 20, Items.silicon, 30));
powerProduction = 0.0045f;
}};
largeSolarPanel = new SolarGenerator("solar-panel-large"){{
requirements(Category.power, ItemStack.with(Items.lead, 200, Items.silicon, 290, Items.phasefabric, 30));
size = 3;
powerProduction = 0.055f;
}};
thoriumReactor = new NuclearReactor("thorium-reactor"){{
requirements(Category.power, ItemStack.with(Items.lead, 600, Items.silicon, 400, Items.graphite, 300, Items.thorium, 300));
size = 3;
health = 700;
powerProduction = 1.1f;
@@ -618,45 +713,28 @@ public class Blocks implements ContentList{
health = 600;
}};
battery = new Battery("battery"){{
consumes.powerBuffered(320f, 1f);
}};
batteryLarge = new Battery("battery-large"){{
size = 3;
consumes.powerBuffered(2000f, 1f);
}};
powerNode = new PowerNode("power-node"){{
maxNodes = 4;
laserRange = 6;
}};
powerNodeLarge = new PowerNode("power-node-large"){{
size = 2;
maxNodes = 6;
laserRange = 9.5f;
}};
//endregion power
//region production
mechanicalDrill = new Drill("mechanical-drill"){{
requirements(Category.production, ItemStack.with(Items.copper, 20), true);
tier = 2;
drillTime = 300;
drillTime = 600;
size = 2;
drawMineItem = true;
}};
pneumaticDrill = new Drill("pneumatic-drill"){{
requirements(Category.production, ItemStack.with(Items.copper, 60, Items.graphite, 50));
tier = 3;
drillTime = 240;
drillTime = 480;
size = 2;
drawMineItem = true;
}};
laserDrill = new Drill("laser-drill"){{
drillTime = 140;
requirements(Category.production, ItemStack.with(Items.copper, 70, Items.graphite, 90, Items.silicon, 60, Items.titanium, 50));
drillTime = 280;
size = 2;
hasPower = true;
tier = 4;
@@ -667,7 +745,8 @@ public class Blocks implements ContentList{
}};
blastDrill = new Drill("blast-drill"){{
drillTime = 60;
requirements(Category.production, ItemStack.with(Items.copper, 130, Items.silicon, 120, Items.titanium, 100, Items.thorium, 60));
drillTime = 120;
size = 3;
drawRim = true;
hasPower = true;
@@ -683,7 +762,7 @@ public class Blocks implements ContentList{
plasmaDrill = new Drill("plasma-drill"){{
heatColor = Color.valueOf("ff461b");
drillTime = 50;
drillTime = 100;
size = 4;
hasLiquids = true;
hasPower = true;
@@ -699,6 +778,7 @@ public class Blocks implements ContentList{
}};
waterExtractor = new SolidPump("water-extractor"){{
requirements(Category.production, ItemStack.with(Items.copper, 50, Items.graphite, 50, Items.lead, 40));
result = Liquids.water;
pumpAmount = 0.065f;
size = 2;
@@ -709,6 +789,7 @@ public class Blocks implements ContentList{
}};
oilExtractor = new Fracker("oil-extractor"){{
requirements(Category.production, ItemStack.with(Items.copper, 300, Items.graphite, 350, Items.lead, 230, Items.thorium, 230, Items.silicon, 150));
result = Liquids.oil;
updateEffect = Fx.pulverize;
liquidCapacity = 50f;
@@ -723,6 +804,7 @@ public class Blocks implements ContentList{
}};
cultivator = new Cultivator("cultivator"){{
requirements(Category.production, ItemStack.with(Items.copper, 20, Items.lead, 50, Items.silicon, 20));
result = Items.biomatter;
drillTime = 200;
size = 2;
@@ -732,61 +814,74 @@ public class Blocks implements ContentList{
consumes.power(0.08f);
consumes.liquid(Liquids.water, 0.15f);
}};
//endregion
//region storage
core = new CoreBlock("core"){{
requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 2000));
alwaysUnlocked = true;
health = 1100;
itemCapacity = 2000;
itemCapacity = 1000;
launchThreshold = 500;
launchTime = 60f * 10;
launchChunkSize = 100;
}};
vault = new Vault("vault"){{
requirements(Category.effect, ItemStack.with(Items.titanium, 500, Items.thorium, 250));
size = 3;
itemCapacity = 1000;
}};
container = new Vault("container"){{
requirements(Category.effect, ItemStack.with(Items.titanium, 200));
size = 2;
itemCapacity = 300;
}};
unloader = new SortedUnloader("unloader"){{
requirements(Category.distribution, ItemStack.with(Items.titanium, 50, Items.silicon, 60));
speed = 7f;
}};
launchPad = new LaunchPad("launch-pad"){{
requirements(Category.effect, ItemStack.with(Items.copper, 500));
size = 3;
itemCapacity = 100;
launchTime = 60f * 6;
hasPower = true;
consumes.power(0.1f);
}};
//endregion
//region turrets
duo = new DoubleTurret("duo"){{
requirements(Category.turret, ItemStack.with(Items.copper, 60), true);
ammo(
Items.copper, Bullets.standardCopper,
Items.graphite, Bullets.standardDense,
Items.pyratite, Bullets.standardIncendiary,
Items.silicon, Bullets.standardHoming
);
reload = 25f;
reload = 20f;
restitution = 0.03f;
range = 90f;
shootCone = 15f;
ammoUseEffect = Fx.shellEjectSmall;
health = 80;
health = 110;
inaccuracy = 2f;
rotatespeed = 10f;
}};
hail = new ArtilleryTurret("hail"){{
requirements(Category.turret, ItemStack.with(Items.copper, 60, Items.graphite, 35));
ammo(
Items.graphite, Bullets.artilleryDense,
Items.silicon, Bullets.artilleryHoming,
Items.pyratite, Bullets.artlleryIncendiary
Items.graphite, Bullets.artilleryDense,
Items.silicon, Bullets.artilleryHoming,
Items.pyratite, Bullets.artlleryIncendiary
);
reload = 60f;
recoil = 2f;
@@ -796,21 +891,13 @@ public class Blocks implements ContentList{
health = 120;
}};
scorch = new LiquidTurret("scorch"){{
ammo(Liquids.oil, Bullets.basicFlame);
recoil = 0f;
reload = 4f;
shootCone = 50f;
ammoUseEffect = Fx.shellEjectSmall;
health = 160;
}};
wave = new LiquidTurret("wave"){{
requirements(Category.turret, ItemStack.with(Items.titanium, 70, Items.lead, 150));
ammo(
Liquids.water, Bullets.waterShot,
Liquids.slag, Bullets.slagShot,
Liquids.cryofluid, Bullets.cryoShot,
Liquids.oil, Bullets.oilShot
Liquids.water, Bullets.waterShot,
Liquids.slag, Bullets.slagShot,
Liquids.cryofluid, Bullets.cryoShot,
Liquids.oil, Bullets.oilShot
);
size = 2;
recoil = 0f;
@@ -832,6 +919,7 @@ public class Blocks implements ContentList{
}};
lancer = new ChargeTurret("lancer"){{
requirements(Category.turret, ItemStack.with(Items.copper, 50, Items.lead, 100, Items.silicon, 90));
range = 90f;
chargeTime = 60f;
chargeMaxDelay = 30f;
@@ -854,6 +942,7 @@ public class Blocks implements ContentList{
}};
arc = new PowerTurret("arc"){{
requirements(Category.turret, ItemStack.with(Items.copper, 70, Items.lead, 60));
shootType = Bullets.arc;
reload = 85f;
shootShake = 1f;
@@ -869,10 +958,11 @@ public class Blocks implements ContentList{
}};
swarmer = new BurstTurret("swarmer"){{
requirements(Category.turret, ItemStack.with(Items.graphite, 70, Items.titanium, 70, Items.plastanium, 90, Items.silicon, 60));
ammo(
Items.blastCompound, Bullets.missileExplosive,
Items.pyratite, Bullets.missileIncendiary,
Items.surgealloy, Bullets.missileSurge
Items.blastCompound, Bullets.missileExplosive,
Items.pyratite, Bullets.missileIncendiary,
Items.surgealloy, Bullets.missileSurge
);
reload = 50f;
shots = 4;
@@ -885,12 +975,13 @@ public class Blocks implements ContentList{
}};
salvo = new BurstTurret("salvo"){{
requirements(Category.turret, ItemStack.with(Items.copper, 210, Items.graphite, 190, Items.thorium, 130));
ammo(
Items.copper, Bullets.standardCopper,
Items.graphite, Bullets.standardDense,
Items.pyratite, Bullets.standardIncendiary,
Items.silicon, Bullets.standardHoming,
Items.thorium, Bullets.standardThorium
Items.copper, Bullets.standardCopper,
Items.graphite, Bullets.standardDense,
Items.pyratite, Bullets.standardIncendiary,
Items.silicon, Bullets.standardHoming,
Items.thorium, Bullets.standardThorium
);
size = 2;
@@ -908,12 +999,13 @@ public class Blocks implements ContentList{
}};
ripple = new ArtilleryTurret("ripple"){{
requirements(Category.turret, ItemStack.with(Items.copper, 300, Items.graphite, 220, Items.thorium, 120));
ammo(
Items.graphite, Bullets.artilleryDense,
Items.silicon, Bullets.artilleryHoming,
Items.pyratite, Bullets.artlleryIncendiary,
Items.blastCompound, Bullets.artilleryExplosive,
Items.plastanium, Bullets.arilleryPlastic
Items.graphite, Bullets.artilleryDense,
Items.silicon, Bullets.artilleryHoming,
Items.pyratite, Bullets.artlleryIncendiary,
Items.blastCompound, Bullets.artilleryExplosive,
Items.plastanium, Bullets.arilleryPlastic
);
size = 3;
shots = 4;
@@ -932,10 +1024,11 @@ public class Blocks implements ContentList{
}};
cyclone = new ItemTurret("cyclone"){{
requirements(Category.turret, ItemStack.with(Items.copper, 400, Items.surgealloy, 200, Items.plastanium, 150));
ammo(
Items.blastCompound, Bullets.flakExplosive,
Items.plastanium, Bullets.flakPlastic,
Items.surgealloy, Bullets.flakSurge
Items.blastCompound, Bullets.flakExplosive,
Items.plastanium, Bullets.flakPlastic,
Items.surgealloy, Bullets.flakSurge
);
xRand = 4f;
reload = 8f;
@@ -950,6 +1043,7 @@ public class Blocks implements ContentList{
}};
fuse = new ItemTurret("fuse"){{
requirements(Category.turret, ItemStack.with(Items.copper, 450, Items.graphite, 450, Items.surgealloy, 250));
ammo(Items.graphite, Bullets.fuseShot);
reload = 50f;
shootShake = 4f;
@@ -962,10 +1056,11 @@ public class Blocks implements ContentList{
}};
spectre = new DoubleTurret("spectre"){{
requirements(Category.turret, ItemStack.with(Items.copper, 700, Items.graphite, 600, Items.surgealloy, 500, Items.plastanium, 350, Items.thorium, 500));
ammo(
Items.graphite, Bullets.standardDenseBig,
Items.pyratite, Bullets.standardIncendiaryBig,
Items.thorium, Bullets.standardThoriumBig
Items.graphite, Bullets.standardDenseBig,
Items.pyratite, Bullets.standardIncendiaryBig,
Items.thorium, Bullets.standardThoriumBig
);
reload = 6f;
coolantMultiplier = 0.5f;
@@ -986,6 +1081,7 @@ public class Blocks implements ContentList{
}};
meltdown = new LaserTurret("meltdown"){{
requirements(Category.turret, ItemStack.with(Items.copper, 500, Items.lead, 700, Items.graphite, 600, Items.surgealloy, 650, Items.silicon, 650));
shootType = Bullets.meltdownLaser;
shootEffect = Fx.shootBigSmoke2;
shootCone = 40f;
@@ -1001,11 +1097,12 @@ public class Blocks implements ContentList{
health = 165 * size * size;
}};
//endregion
//region units
spiritFactory = new UnitFactory("spirit-factory"){{
requirements(Category.units, ItemStack.with(Items.copper, 70, Items.lead, 110, Items.silicon, 130));
type = UnitTypes.spirit;
produceTime = 5700;
size = 2;
@@ -1014,6 +1111,7 @@ public class Blocks implements ContentList{
}};
phantomFactory = new UnitFactory("phantom-factory"){{
requirements(Category.units, ItemStack.with(Items.titanium, 90, Items.thorium, 80, Items.lead, 110, Items.silicon, 210));
type = UnitTypes.phantom;
produceTime = 7300;
size = 2;
@@ -1022,6 +1120,7 @@ public class Blocks implements ContentList{
}};
wraithFactory = new UnitFactory("wraith-factory"){{
requirements(Category.units, ItemStack.with(Items.titanium, 60, Items.lead, 80, Items.silicon, 90));
type = UnitTypes.wraith;
produceTime = 1800;
size = 2;
@@ -1030,6 +1129,7 @@ public class Blocks implements ContentList{
}};
ghoulFactory = new UnitFactory("ghoul-factory"){{
requirements(Category.units, ItemStack.with(Items.plastanium, 80, Items.titanium, 100, Items.lead, 130, Items.silicon, 220));
type = UnitTypes.ghoul;
produceTime = 3600;
size = 3;
@@ -1038,6 +1138,7 @@ public class Blocks implements ContentList{
}};
revenantFactory = new UnitFactory("revenant-factory"){{
requirements(Category.units, ItemStack.with(Items.plastanium, 300, Items.titanium, 400, Items.lead, 300, Items.silicon, 400, Items.surgealloy, 100));
type = UnitTypes.revenant;
produceTime = 8000;
size = 4;
@@ -1046,6 +1147,7 @@ public class Blocks implements ContentList{
}};
daggerFactory = new UnitFactory("dagger-factory"){{
requirements(Category.units, ItemStack.with(Items.lead, 90, Items.silicon, 70));
type = UnitTypes.dagger;
produceTime = 1700;
size = 2;
@@ -1054,6 +1156,7 @@ public class Blocks implements ContentList{
}};
titanFactory = new UnitFactory("titan-factory"){{
requirements(Category.units, ItemStack.with(Items.thorium, 90, Items.lead, 140, Items.silicon, 90));
type = UnitTypes.titan;
produceTime = 3400;
size = 3;
@@ -1062,6 +1165,7 @@ public class Blocks implements ContentList{
}};
fortressFactory = new UnitFactory("fortress-factory"){{
requirements(Category.units, ItemStack.with(Items.thorium, 200, Items.lead, 220, Items.silicon, 150, Items.surgealloy, 100, Items.phasefabric, 50));
type = UnitTypes.fortress;
produceTime = 5000;
size = 3;
@@ -1070,64 +1174,73 @@ public class Blocks implements ContentList{
}};
repairPoint = new RepairPoint("repair-point"){{
requirements(Category.units, ItemStack.with(Items.lead, 30, Items.copper, 30, Items.silicon, 30));
repairSpeed = 0.1f;
}};
reconstructor = new Reconstructor("reconstructor"){{
size = 2;
}};
//endregion
//region upgrades
alphaPad = new MechPad("alpha-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 200, Items.graphite, 100, Items.copper, 150));
mech = Mechs.alpha;
size = 2;
consumes.powerBuffered(50f);
}};
deltaPad = new MechPad("delta-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.titanium, 350, Items.copper, 400, Items.silicon, 450, Items.thorium, 300));
mech = Mechs.delta;
size = 2;
consumes.powerBuffered(70f);
}};
tauPad = new MechPad("tau-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.titanium, 250, Items.copper, 250, Items.silicon, 250));
mech = Mechs.tau;
size = 2;
consumes.powerBuffered(100f);
}};
omegaPad = new MechPad("omega-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.graphite, 550, Items.silicon, 650, Items.thorium, 600, Items.surgealloy, 240));
mech = Mechs.omega;
size = 3;
consumes.powerBuffered(120f);
}};
dartPad = new MechPad("dart-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 150, Items.copper, 150, Items.silicon, 200, Items.titanium, 240));
mech = Mechs.dart;
size = 2;
consumes.powerBuffered(50f);
}};
javelinPad = new MechPad("javelin-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.silicon, 450, Items.titanium, 500, Items.plastanium, 400, Items.phasefabric, 200));
mech = Mechs.javelin;
size = 2;
consumes.powerBuffered(80f);
}};
tridentPad = new MechPad("trident-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.copper, 250, Items.silicon, 250, Items.titanium, 300, Items.plastanium, 200));
mech = Mechs.trident;
size = 2;
consumes.powerBuffered(100f);
}};
glaivePad = new MechPad("glaive-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.silicon, 650, Items.titanium, 700, Items.plastanium, 600, Items.surgealloy, 200));
mech = Mechs.glaive;
size = 3;
consumes.powerBuffered(120f);
}};
//endregion
//region ores

View File

@@ -267,7 +267,7 @@ public class Bullets implements ContentList{
bulletHeight = 9f;
homingPower = 5f;
reloadMultiplier = 1.4f;
ammoMultiplier = 5;
ammoMultiplier = 3;
}};
standardIncendiary = new BasicBulletType(3.2f, 11, "bullet"){{

View File

@@ -24,14 +24,15 @@ public class Fx implements ContentList{
vtolHover, unitDrop, unitPickup, unitLand, pickup, healWave, heal, landShock, reactorsmoke, nuclearsmoke, nuclearcloud,
redgeneratespark, generatespark, fuelburn, plasticburn, pulverize, pulverizeRed, pulverizeRedder, pulverizeSmall, pulverizeMedium,
producesmoke, smeltsmoke, formsmoke, blastsmoke, lava, doorclose, dooropen, dooropenlarge, doorcloselarge, purify, purifyoil, purifystone, generate,
mine, mineBig, mineHuge, smelt, teleportActivate, teleport, teleportOut, ripple, bubble, commandSend,
mine, mineBig, mineHuge, smelt, teleportActivate, teleport, teleportOut, ripple, bubble, launch,
healBlock, healBlockFull, healWaveMend, overdriveWave, overdriveBlockFull, shieldBreak, hitBulletSmall, hitFuse,
hitBulletBig, hitFlameSmall, hitLiquid, hitLaser, hitLancer, hitMeltdown, despawn, flakExplosion, blastExplosion,
plasticExplosion, artilleryTrail, incendTrail, missileTrail, absorb, flakExplosionBig, plasticExplosionFlak, burning, fire,
fireSmoke, steam, fireballsmoke, ballfire, freezing, melting, wet, oily, overdriven, dropItem, shockwave,
bigShockwave, nuclearShockwave, explosion, blockExplosion, blockExplosionSmoke, shootSmall, shootHeal, shootSmallSmoke, shootBig, shootBig2, shootBigSmoke,
shootBigSmoke2, shootSmallFlame, shootLiquid, shellEjectSmall, shellEjectMedium,
shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot;
shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot,
launchFull;
@Override
public void load(){
@@ -1060,7 +1061,7 @@ public class Fx implements ContentList{
Draw.reset();
});
commandSend = new Effect(28, e -> {
launch = new Effect(28, e -> {
Draw.color(Palette.command);
Lines.stroke(e.fout() * 2f);
Lines.poly(e.x, e.y, 40, 4f + e.finpow() * 120f);
@@ -1108,5 +1109,12 @@ public class Fx implements ContentList{
Lines.poly(e.x, e.y, 6, e.rotation + e.fin(), 90);
Draw.reset();
});
launchFull = new Effect(60, 9999999999f, e -> {
Draw.color();
Draw.alpha(e.fslope());
Fill.rect(Core.camera.position.x, Core.camera.position.y, Core.camera.width + 10, Core.camera.height + 10);
Draw.reset();
});
}
}

View File

@@ -7,7 +7,7 @@ import io.anuke.mindustry.type.ItemType;
public class Items implements ContentList{
public static Item scrap, copper, lead, graphite, coal, titanium, thorium, silicon, plastanium, phasefabric, surgealloy,
biomatter, sand, blastCompound, pyratite, bioglass;
biomatter, sand, blastCompound, pyratite, metaglass;
@Override
public void load(){
@@ -26,6 +26,11 @@ public class Items implements ContentList{
genOre = true;
}};
metaglass = new Item("metaglass", Color.valueOf("648b55")){{
type = ItemType.material;
cost = 2f;
}};
graphite = new Item("graphite", Color.valueOf("b2c6d2")){{
type = ItemType.material;
cost = 1.3f;
@@ -55,7 +60,7 @@ public class Items implements ContentList{
}};
scrap = new Item("scrap", Color.valueOf("777777")){{
genOre = true;
}};
silicon = new Item("silicon", Color.valueOf("53565c")){{
@@ -99,10 +104,5 @@ public class Items implements ContentList{
flammability = 0.7f;
explosiveness = 0.2f;
}};
bioglass = new Item("bioglass", Color.valueOf("648b55")){{
type = ItemType.material;
cost = 2f;
}};
}
}

View File

@@ -5,7 +5,7 @@ import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.type.Liquid;
public class Liquids implements ContentList{
public static Liquid water, slag, oil, cryofluid, acid;
public static Liquid water, slag, oil, cryofluid;
@Override
public void load(){
@@ -16,7 +16,7 @@ public class Liquids implements ContentList{
effect = StatusEffects.wet;
}};
slag = new Liquid("slag", Color.valueOf("e37341")){{
slag = new Liquid("slag", Color.valueOf("ffcd66")){{
temperature = 1f;
viscosity = 0.8f;
tier = 2;
@@ -38,10 +38,5 @@ public class Liquids implements ContentList{
tier = 1;
effect = StatusEffects.freezing;
}};
acid = new Liquid("acid", Color.valueOf("e9f9b3")){{
heatCapacity = 0.1f; //don't use acid as coolant, it's bad
effect = StatusEffects.corroded;
}};
}
}

View File

@@ -40,8 +40,8 @@ public class Mechs implements ContentList{
}
@Override
public void updateAlt(Player player){
public boolean alwaysUnlocked(){
return true;
}
};
@@ -182,18 +182,25 @@ public class Mechs implements ContentList{
}
};
dart = new Mech("dart-ship", true){{
drillPower = 1;
mineSpeed = 0.9f;
speed = 0.4f;
drag = 0.1f;
armor = 10f;
weapon = Weapons.blasterSmall;
weaponOffsetX = -1;
weaponOffsetY = -1;
trailColor = Palette.lightTrail;
cellTrnsY = 1f;
}};
dart = new Mech("dart-ship", true){
{
drillPower = 1;
mineSpeed = 0.9f;
speed = 0.4f;
drag = 0.1f;
armor = 10f;
weapon = Weapons.blasterSmall;
weaponOffsetX = -1;
weaponOffsetY = -1;
trailColor = Palette.lightTrail;
cellTrnsY = 1f;
}
@Override
public boolean alwaysUnlocked(){
return true;
}
};
javelin = new Mech("javelin-ship", true){
float minV = 3.6f;

View File

@@ -1,180 +0,0 @@
package io.anuke.mindustry.content;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.type.Recipe.RecipeVisibility;
import static io.anuke.mindustry.type.Category.*;
public class Recipes implements ContentList{
@Override
public void load(){
//DEBUG
new Recipe(distribution, Blocks.itemSource).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
new Recipe(distribution, Blocks.itemVoid).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
new Recipe(liquid, Blocks.liquidSource).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
new Recipe(power, Blocks.powerVoid).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
new Recipe(power, Blocks.powerSource).setVisible(RecipeVisibility.sandboxOnly).setHidden(true).setAlwaysUnlocked(true);
//DEFENSE
//walls
new Recipe(defense, Blocks.copperWall, new ItemStack(Items.copper, 12)).setAlwaysUnlocked(true);
new Recipe(defense, Blocks.copperWallLarge, new ItemStack(Items.copper, 12 * 4)).setAlwaysUnlocked(true);
new Recipe(defense, Blocks.titaniumWall, new ItemStack(Items.titanium, 12));
new Recipe(defense, Blocks.titaniumWallLarge, new ItemStack(Items.titanium, 12 * 4));
new Recipe(defense, Blocks.door, new ItemStack(Items.titanium, 12), new ItemStack(Items.silicon, 8));
new Recipe(defense, Blocks.doorLarge, new ItemStack(Items.titanium, 12 * 4), new ItemStack(Items.silicon, 8 * 4));
new Recipe(defense, Blocks.thoriumWall, new ItemStack(Items.thorium, 12));
new Recipe(defense, Blocks.thoriumWallLarge, new ItemStack(Items.thorium, 12 * 4));
new Recipe(defense, Blocks.phaseWall, new ItemStack(Items.phasefabric, 12));
new Recipe(defense, Blocks.phaseWallLarge, new ItemStack(Items.phasefabric, 12 * 4));
new Recipe(defense, Blocks.surgeWall, new ItemStack(Items.surgealloy, 12));
new Recipe(defense, Blocks.surgeWallLarge, new ItemStack(Items.surgealloy, 12 * 4));
new Recipe(effect, Blocks.container, new ItemStack(Items.titanium, 200));
new Recipe(effect, Blocks.vault, new ItemStack(Items.titanium, 500), new ItemStack(Items.thorium, 250));
new Recipe(effect, Blocks.launchPad, new ItemStack(Items.copper, 500));
//projectors
new Recipe(effect, Blocks.mendProjector, new ItemStack(Items.lead, 200), new ItemStack(Items.titanium, 150), new ItemStack(Items.titanium, 50), new ItemStack(Items.silicon, 180));
new Recipe(effect, Blocks.overdriveProjector, new ItemStack(Items.lead, 200), new ItemStack(Items.titanium, 150), new ItemStack(Items.titanium, 150), new ItemStack(Items.silicon, 250));
new Recipe(effect, Blocks.forceProjector, new ItemStack(Items.lead, 200), new ItemStack(Items.titanium, 150), new ItemStack(Items.titanium, 150), new ItemStack(Items.silicon, 250));
new Recipe(effect, Blocks.shockMine, new ItemStack(Items.lead, 50), new ItemStack(Items.silicon, 25));
//TURRETS
new Recipe(turret, Blocks.duo, new ItemStack(Items.copper, 40)).setAlwaysUnlocked(true);
new Recipe(turret, Blocks.arc, new ItemStack(Items.copper, 50), new ItemStack(Items.lead, 40));
new Recipe(turret, Blocks.hail, new ItemStack(Items.copper, 60), new ItemStack(Items.graphite, 35));
new Recipe(turret, Blocks.lancer, new ItemStack(Items.copper, 50), new ItemStack(Items.lead, 100), new ItemStack(Items.silicon, 90));
new Recipe(turret, Blocks.wave, new ItemStack(Items.titanium, 70), new ItemStack(Items.lead, 150));
new Recipe(turret, Blocks.salvo, new ItemStack(Items.copper, 210), new ItemStack(Items.graphite, 190), new ItemStack(Items.thorium, 130));
new Recipe(turret, Blocks.swarmer, new ItemStack(Items.graphite, 70), new ItemStack(Items.titanium, 70), new ItemStack(Items.plastanium, 90), new ItemStack(Items.silicon, 60));
new Recipe(turret, Blocks.ripple, new ItemStack(Items.copper, 300), new ItemStack(Items.graphite, 220), new ItemStack(Items.thorium, 120));
new Recipe(turret, Blocks.cyclone, new ItemStack(Items.copper, 400), new ItemStack(Items.surgealloy, 200), new ItemStack(Items.plastanium, 150));
new Recipe(turret, Blocks.fuse, new ItemStack(Items.copper, 450), new ItemStack(Items.graphite, 450), new ItemStack(Items.surgealloy, 250));
new Recipe(turret, Blocks.spectre, new ItemStack(Items.copper, 700), new ItemStack(Items.graphite, 600), new ItemStack(Items.surgealloy, 500), new ItemStack(Items.plastanium, 350), new ItemStack(Items.thorium, 500));
new Recipe(turret, Blocks.meltdown, new ItemStack(Items.copper, 500), new ItemStack(Items.lead, 700), new ItemStack(Items.graphite, 600), new ItemStack(Items.surgealloy, 650), new ItemStack(Items.silicon, 650));
//DISTRIBUTION
new Recipe(distribution, Blocks.conveyor, new ItemStack(Items.copper, 1)).setAlwaysUnlocked(true);
new Recipe(distribution, Blocks.titaniumConveyor, new ItemStack(Items.copper, 2), new ItemStack(Items.titanium, 1));
new Recipe(distribution, Blocks.phaseConveyor, new ItemStack(Items.phasefabric, 10), new ItemStack(Items.silicon, 15), new ItemStack(Items.lead, 20), new ItemStack(Items.graphite, 20));
//starter transport
new Recipe(distribution, Blocks.junction, new ItemStack(Items.copper, 2)).setAlwaysUnlocked(true);
new Recipe(distribution, Blocks.router, new ItemStack(Items.copper, 6)).setAlwaysUnlocked(true);
//more advanced transport
new Recipe(distribution, Blocks.distributor, new ItemStack(Items.titanium, 8), new ItemStack(Items.copper, 8));
new Recipe(distribution, Blocks.sorter, new ItemStack(Items.titanium, 4), new ItemStack(Items.copper, 4));
new Recipe(distribution, Blocks.overflowGate, new ItemStack(Items.titanium, 4), new ItemStack(Items.copper, 8));
new Recipe(distribution, Blocks.itemBridge, new ItemStack(Items.titanium, 8), new ItemStack(Items.copper, 8));
new Recipe(distribution, Blocks.unloader, new ItemStack(Items.titanium, 50), new ItemStack(Items.silicon, 60));
new Recipe(distribution, Blocks.massDriver, new ItemStack(Items.titanium, 250), new ItemStack(Items.silicon, 150), new ItemStack(Items.lead, 250), new ItemStack(Items.thorium, 100));
//CRAFTING
//smelting
new Recipe(crafting, Blocks.siliconSmelter, new ItemStack(Items.copper, 60), new ItemStack(Items.lead, 50));
//advanced fabrication
new Recipe(crafting, Blocks.plastaniumCompressor, new ItemStack(Items.silicon, 160), new ItemStack(Items.lead, 230), new ItemStack(Items.graphite, 120), new ItemStack(Items.titanium, 160));
new Recipe(crafting, Blocks.phaseWeaver, new ItemStack(Items.silicon, 260), new ItemStack(Items.lead, 240), new ItemStack(Items.thorium, 150));
new Recipe(crafting, Blocks.surgeSmelter, new ItemStack(Items.silicon, 160), new ItemStack(Items.lead, 160), new ItemStack(Items.thorium, 140));
//misc
new Recipe(crafting, Blocks.pulverizer, new ItemStack(Items.copper, 60), new ItemStack(Items.lead, 50));
new Recipe(crafting, Blocks.pyratiteMixer, new ItemStack(Items.copper, 100), new ItemStack(Items.lead, 50));
new Recipe(crafting, Blocks.blastMixer, new ItemStack(Items.lead, 60), new ItemStack(Items.titanium, 40));
new Recipe(crafting, Blocks.cryofluidMixer, new ItemStack(Items.lead, 130), new ItemStack(Items.silicon, 80), new ItemStack(Items.thorium, 90));
new Recipe(crafting, Blocks.melter, new ItemStack(Items.copper, 60), new ItemStack(Items.lead, 70), new ItemStack(Items.graphite, 90));
new Recipe(crafting, Blocks.incinerator, new ItemStack(Items.graphite, 10), new ItemStack(Items.lead, 30));
//processing
new Recipe(crafting, Blocks.biomatterCompressor, new ItemStack(Items.lead, 70), new ItemStack(Items.silicon, 60));
new Recipe(crafting, Blocks.separator, new ItemStack(Items.copper, 60), new ItemStack(Items.titanium, 50));
//POWER
new Recipe(power, Blocks.powerNode, new ItemStack(Items.copper, 2), new ItemStack(Items.lead, 6));
new Recipe(power, Blocks.powerNodeLarge, new ItemStack(Items.titanium, 10), new ItemStack(Items.lead, 20), new ItemStack(Items.silicon, 6));
new Recipe(power, Blocks.battery, new ItemStack(Items.copper, 8), new ItemStack(Items.lead, 30), new ItemStack(Items.silicon, 4));
new Recipe(power, Blocks.batteryLarge, new ItemStack(Items.titanium, 40), new ItemStack(Items.lead, 80), new ItemStack(Items.silicon, 30));
//generators - combustion
new Recipe(power, Blocks.combustionGenerator, new ItemStack(Items.copper, 50), new ItemStack(Items.lead, 30));
new Recipe(power, Blocks.turbineGenerator, new ItemStack(Items.copper, 70), new ItemStack(Items.graphite, 50), new ItemStack(Items.lead, 80), new ItemStack(Items.silicon, 60));
new Recipe(power, Blocks.thermalGenerator, new ItemStack(Items.copper, 80), new ItemStack(Items.graphite, 70), new ItemStack(Items.lead, 100), new ItemStack(Items.silicon, 70), new ItemStack(Items.thorium, 70));
//generators - solar
new Recipe(power, Blocks.solarPanel, new ItemStack(Items.lead, 20), new ItemStack(Items.silicon, 30));
new Recipe(power, Blocks.largeSolarPanel, new ItemStack(Items.lead, 200), new ItemStack(Items.silicon, 290), new ItemStack(Items.phasefabric, 30));
//generators - nuclear
new Recipe(power, Blocks.thoriumReactor, new ItemStack(Items.lead, 600), new ItemStack(Items.silicon, 400), new ItemStack(Items.graphite, 300), new ItemStack(Items.thorium, 300));
new Recipe(power, Blocks.rtgGenerator, new ItemStack(Items.lead, 200), new ItemStack(Items.silicon, 150), new ItemStack(Items.phasefabric, 50), new ItemStack(Items.plastanium, 150), new ItemStack(Items.thorium, 100));
//DRILLS, PRODUCERS
new Recipe(production, Blocks.mechanicalDrill, new ItemStack(Items.copper, 20)).setAlwaysUnlocked(true);
new Recipe(production, Blocks.pneumaticDrill, new ItemStack(Items.copper, 60), new ItemStack(Items.graphite, 50));
new Recipe(production, Blocks.laserDrill, new ItemStack(Items.copper, 70), new ItemStack(Items.graphite, 90), new ItemStack(Items.silicon, 60), new ItemStack(Items.titanium, 50));
new Recipe(production, Blocks.blastDrill, new ItemStack(Items.copper, 130), new ItemStack(Items.silicon, 120), new ItemStack(Items.titanium, 100), new ItemStack(Items.thorium, 60));
new Recipe(production, Blocks.waterExtractor, new ItemStack(Items.copper, 50), new ItemStack(Items.graphite, 50), new ItemStack(Items.lead, 40));
new Recipe(production, Blocks.cultivator, new ItemStack(Items.copper, 20), new ItemStack(Items.lead, 50), new ItemStack(Items.silicon, 20));
new Recipe(production, Blocks.oilExtractor, new ItemStack(Items.copper, 300), new ItemStack(Items.graphite, 350), new ItemStack(Items.lead, 230), new ItemStack(Items.thorium, 230), new ItemStack(Items.silicon, 150));
//UNITS
//upgrades
new Recipe(upgrade, Blocks.dartPad, new ItemStack(Items.lead, 150), new ItemStack(Items.copper, 150), new ItemStack(Items.silicon, 200), new ItemStack(Items.titanium, 240)).setVisible(RecipeVisibility.desktopOnly);
new Recipe(upgrade, Blocks.tridentPad, new ItemStack(Items.lead, 250), new ItemStack(Items.copper, 250), new ItemStack(Items.silicon, 250), new ItemStack(Items.titanium, 300), new ItemStack(Items.plastanium, 200));
new Recipe(upgrade, Blocks.javelinPad, new ItemStack(Items.lead, 350), new ItemStack(Items.silicon, 450), new ItemStack(Items.titanium, 500), new ItemStack(Items.plastanium, 400), new ItemStack(Items.phasefabric, 200));
new Recipe(upgrade, Blocks.glaivePad, new ItemStack(Items.lead, 450), new ItemStack(Items.silicon, 650), new ItemStack(Items.titanium, 700), new ItemStack(Items.plastanium, 600), new ItemStack(Items.surgealloy, 200));
new Recipe(upgrade, Blocks.alphaPad, new ItemStack(Items.lead, 200), new ItemStack(Items.graphite, 100), new ItemStack(Items.copper, 150)).setVisible(RecipeVisibility.mobileOnly);
new Recipe(upgrade, Blocks.tauPad, new ItemStack(Items.lead, 250), new ItemStack(Items.titanium, 250), new ItemStack(Items.copper, 250), new ItemStack(Items.silicon, 250));
new Recipe(upgrade, Blocks.deltaPad, new ItemStack(Items.lead, 350), new ItemStack(Items.titanium, 350), new ItemStack(Items.copper, 400), new ItemStack(Items.silicon, 450), new ItemStack(Items.thorium, 300));
new Recipe(upgrade, Blocks.omegaPad, new ItemStack(Items.lead, 450), new ItemStack(Items.graphite, 550), new ItemStack(Items.silicon, 650), new ItemStack(Items.thorium, 600), new ItemStack(Items.surgealloy, 240));
//unit factories
new Recipe(units, Blocks.spiritFactory, new ItemStack(Items.copper, 70), new ItemStack(Items.lead, 110), new ItemStack(Items.silicon, 130));
new Recipe(units, Blocks.phantomFactory, new ItemStack(Items.titanium, 90), new ItemStack(Items.thorium, 80), new ItemStack(Items.lead, 110), new ItemStack(Items.silicon, 210));
new Recipe(units, Blocks.daggerFactory, new ItemStack(Items.lead, 90), new ItemStack(Items.silicon, 70));
new Recipe(units, Blocks.titanFactory, new ItemStack(Items.thorium, 90), new ItemStack(Items.lead, 140), new ItemStack(Items.silicon, 90));
new Recipe(units, Blocks.fortressFactory, new ItemStack(Items.thorium, 200), new ItemStack(Items.lead, 220), new ItemStack(Items.silicon, 150), new ItemStack(Items.surgealloy, 100), new ItemStack(Items.phasefabric, 50));
new Recipe(units, Blocks.wraithFactory, new ItemStack(Items.titanium, 60), new ItemStack(Items.lead, 80), new ItemStack(Items.silicon, 90));
new Recipe(units, Blocks.ghoulFactory, new ItemStack(Items.plastanium, 80), new ItemStack(Items.titanium, 100), new ItemStack(Items.lead, 130), new ItemStack(Items.silicon, 220));
new Recipe(units, Blocks.revenantFactory, new ItemStack(Items.plastanium, 300), new ItemStack(Items.titanium, 400), new ItemStack(Items.lead, 300), new ItemStack(Items.silicon, 400), new ItemStack(Items.surgealloy, 100));
new Recipe(units, Blocks.repairPoint, new ItemStack(Items.lead, 30), new ItemStack(Items.copper, 30), new ItemStack(Items.silicon, 30));
//removed for testing MOBA-style unit production
//new Recipe(units, Blocks.commandCenter, new ItemStack(Items.lead, 100), new ItemStack(Items.densealloy, 100), new ItemStack(Items.silicon, 200));
//LIQUIDS
new Recipe(liquid, Blocks.conduit, new ItemStack(Items.lead, 1));
new Recipe(liquid, Blocks.pulseConduit, new ItemStack(Items.titanium, 1), new ItemStack(Items.lead, 1));
new Recipe(liquid, Blocks.phaseConduit, new ItemStack(Items.phasefabric, 10), new ItemStack(Items.silicon, 15), new ItemStack(Items.lead, 20), new ItemStack(Items.titanium, 20));
new Recipe(liquid, Blocks.liquidRouter, new ItemStack(Items.titanium, 4), new ItemStack(Items.lead, 4));
new Recipe(liquid, Blocks.liquidTank, new ItemStack(Items.titanium, 50), new ItemStack(Items.lead, 50));
new Recipe(liquid, Blocks.liquidJunction, new ItemStack(Items.titanium, 4), new ItemStack(Items.lead, 4));
new Recipe(liquid, Blocks.bridgeConduit, new ItemStack(Items.titanium, 8), new ItemStack(Items.lead, 8));
new Recipe(liquid, Blocks.mechanicalPump, new ItemStack(Items.copper, 30), new ItemStack(Items.lead, 20));
new Recipe(liquid, Blocks.rotaryPump, new ItemStack(Items.copper, 140), new ItemStack(Items.lead, 100), new ItemStack(Items.silicon, 40), new ItemStack(Items.titanium, 70));
new Recipe(liquid, Blocks.thermalPump, new ItemStack(Items.copper, 160), new ItemStack(Items.lead, 130), new ItemStack(Items.silicon, 60), new ItemStack(Items.titanium, 80), new ItemStack(Items.thorium, 70));
}
}

View File

@@ -0,0 +1,285 @@
package io.anuke.mindustry.content;
import io.anuke.arc.collection.Array;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block;
import static io.anuke.mindustry.content.Blocks.*;
public class TechTree implements ContentList{
public static TechNode root;
@Override
public void load(){
root = node(core, () -> {
node(conveyor, () -> {
node(launchPad, () -> {
});
node(junction, () -> {
node(itemBridge);
node(router, () -> {
node(distributor);
node(overflowGate);
node(sorter);
node(container, () -> {
node(unloader);
node(vault, () -> {
});
});
});
node(titaniumConveyor, () -> {
node(phaseConveyor, () -> {
node(massDriver, () -> {
});
});
});
});
});
node(duo, () -> {
node(hail, () -> {
node(salvo, () -> {
node(swarmer, () -> {
node(cyclone, () -> {
node(spectre, () -> {
});
});
});
node(ripple, () -> {
node(fuse, () -> {
});
});
});
});
node(arc, () -> {
node(wave, () -> {
});
node(lancer, () -> {
node(meltdown, () -> {
});
node(shockMine, () -> {
});
});
});
node(copperWall, () -> {
node(copperWallLarge);
node(titaniumWall, () -> {
node(door, () -> {
node(doorLarge);
});
node(titaniumWallLarge);
node(thoriumWall, () -> {
node(thoriumWallLarge);
node(surgeWall, () -> {
node(surgeWallLarge);
node(phaseWall, () -> {
node(phaseWallLarge);
});
});
});
});
});
});
node(mechanicalDrill, () -> {
node(pneumaticDrill, () -> {
node(cultivator, () -> {
});
node(laserDrill, () -> {
node(blastDrill, () -> {
});
node(waterExtractor, () -> {
node(oilExtractor, () -> {
});
});
});
});
node(siliconSmelter, () -> {
node(pyratiteMixer, () -> {
node(blastMixer, () -> {
});
});
node(biomatterCompressor, () -> {
node(plastaniumCompressor, () -> {
node(phaseWeaver, () -> {
});
});
});
node(incinerator, () -> {
node(melter, () -> {
node(surgeSmelter, () -> {
});
node(separator, () -> {
node(pulverizer, () -> {
});
});
node(cryofluidMixer, () -> {
});
});
});
});
node(mechanicalPump, () -> {
node(conduit, () -> {
node(liquidJunction, () -> {
node(liquidRouter, () -> {
node(liquidTank);
node(pulseConduit, () -> {
node(phaseConduit, () -> {
});
});
node(rotaryPump, () -> {
node(thermalPump, () -> {
});
});
});
node(bridgeConduit);
});
});
});
});
node(powerNode, () -> {
node(combustionGenerator, () -> {
node(powerNodeLarge, () -> {
node(battery, () -> {
node(batteryLarge, () -> {
});
});
node(mendProjector, () -> {
node(forceProjector, () -> {
node(overdriveProjector, () -> {
});
});
node(repairPoint, () -> {
});
});
});
node(turbineGenerator, () -> {
node(thermalGenerator, () -> {
node(rtgGenerator, () -> {
node(thoriumReactor, () -> {
});
});
});
});
node(solarPanel, () -> {
node(largeSolarPanel, () -> {
});
});
});
node(alphaPad, () -> {
node(dartPad);
node(deltaPad, () -> {
node(javelinPad);
node(tauPad, () -> {
node(tridentPad);
node(omegaPad, () -> {
node(glaivePad);
});
});
});
node(spiritFactory, () -> {
node(daggerFactory, () -> {
node(daggerFactory, () -> {
node(titanFactory, () -> {
node(fortressFactory);
});
node(wraithFactory, () -> {
node(phantomFactory);
node(ghoulFactory, () -> {
node(revenantFactory);
});
});
});
});
});
});
});
});
}
private TechNode node(Block block, Runnable children){
ItemStack[] requirements = new ItemStack[block.buildRequirements.length];
for(int i = 0; i < requirements.length; i++){
requirements[i] = new ItemStack(block.buildRequirements[i].item, block.buildRequirements[i].amount * 50);
}
return new TechNode(block, requirements, children);
}
private TechNode node(Block block){
return node(block, () -> {});
}
public static class TechNode{
static TechNode context;
public final Block block;
public final ItemStack[] requirements;
public final Array<TechNode> children = new Array<>();
TechNode(Block block, ItemStack[] requirements, Runnable children){
if(context != null){
context.children.add(this);
}
//TODO remove requirements... for now
this.block = block;
this.requirements = requirements;
TechNode last = context;
context = this;
children.run();
context = last;
}
}
}

View File

@@ -20,7 +20,7 @@ public class Weapons implements ContentList{
blasterSmall = new Weapon("blaster"){{
length = 1.5f;
reload = 15f;
reload = 20f;
roundrobin = true;
ejectEffect = Fx.shellEjectSmall;
ammo = Bullets.standardCopper;
@@ -28,7 +28,7 @@ public class Weapons implements ContentList{
glaiveBlaster = new Weapon("bomber"){{
length = 1.5f;
reload = 10f;
reload = 13f;
roundrobin = true;
ejectEffect = Fx.shellEjectSmall;
ammo = Bullets.standardGlaive;
@@ -54,7 +54,7 @@ public class Weapons implements ContentList{
missiles = new Weapon("missiles"){{
length = 1.5f;
reload = 60f;
reload = 70f;
shots = 4;
inaccuracy = 2f;
roundrobin = true;

View File

@@ -1,25 +1,207 @@
package io.anuke.mindustry.content;
import io.anuke.arc.collection.Array;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.maps.generators.BasicGenerator;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.maps.generators.MapGenerator;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.world.Block;
public class Zones implements ContentList{
public Zone wasteland;
public static Zone groundZero, craters, frozenForest, ruinousShores, crags, stainedMountains,
impact, desolateRift, arcticDesert, dryWastes, nuclearComplex, moltenFault;
@Override
public void load(){
wasteland = new Zone("wasteland", new BasicGenerator(256, 256, Items.lead, Items.copper)){{
deployCost = ItemStack.with(Items.copper, 100);
groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{
deployCost = ItemStack.with(Items.copper, 60);
startingItems = ItemStack.with(Items.copper, 50);
alwaysUnlocked = true;
conditionWave = 10;
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 60;
waveSpacing = 60 * 60 * 2; //2 mins
spawns = Array.with(
new SpawnGroup(UnitTypes.dagger){{
unitScaling = 2;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 10;
unitScaling = 2;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 15;
unitScaling = 1;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 20;
unitScaling = 1;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 25;
unitScaling = 1;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 30;
unitScaling = 1;
}}
);
}};
}};
craters = new Zone("craters", new MapGenerator("craters", 1){{ distortion = 1.44f; }}){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
itemRequirements = ItemStack.with(Items.copper, 2000);
zoneRequirements = new Zone[]{groundZero};
blockRequirements = new Block[]{Blocks.router};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
frozenForest = new Zone("frozenForest", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
ruinousShores = new Zone("ruinousShores", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
crags = new Zone("crags", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
stainedMountains = new Zone("stainedMountains", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
impact = new Zone("impact", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
desolateRift = new Zone("desolateRift", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
arcticDesert = new Zone("arcticDesert", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
dryWastes = new Zone("dryWastes", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
nuclearComplex = new Zone("nuclearComplex", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
moltenFault = new Zone("moltenFault", new MapGenerator("groundZero", 1)){{ //TODO implement
deployCost = ItemStack.with(Items.copper, 300);
startingItems = ItemStack.with(Items.copper, 200);
conditionWave = 15;
zoneRequirements = new Zone[]{frozenForest};
blockRequirements = new Block[]{Blocks.copperWall};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
waveSpacing = 60 * 80;
}};
}};
frozenForest.zoneRequirements = new Zone[]{frozenForest};
}
}

View File

@@ -44,7 +44,7 @@ public class ContentLoader{
new Mechs(),
new UnitTypes(),
new Blocks(),
new Recipes(),
new TechTree(),
new Zones(),
//these are not really content classes, but this makes initialization easier
@@ -165,7 +165,7 @@ public class ContentLoader{
}
if(id >= contentMap[type.ordinal()].size || id < 0){
throw new RuntimeException("No " + type.name() + " with ID '" + id + "' found!");
return null;
}
return (T)contentMap[type.ordinal()].get(id);
}
@@ -184,14 +184,6 @@ public class ContentLoader{
return (Block) getByID(ContentType.block, id);
}
public Array<Recipe> recipes(){
return getBy(ContentType.recipe);
}
public Recipe recipe(int id){
return (Recipe) getByID(ContentType.recipe, id);
}
public Array<Item> items(){
return getBy(ContentType.item);
}

View File

@@ -116,9 +116,16 @@ public class Control implements ApplicationListener{
//todo high scores for custom maps, as well as other statistics
Events.on(GameOverEvent.class, event -> {
state.stats.wavesLasted = state.wave;
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
//the restart dialog can show info for any number of scenarios
Call.onGameOver(event.winner);
if(state.rules.zone != -1){
//remove zone save on game over
if(saves.getZoneSlot() != null){
saves.getZoneSlot().delete();
}
}
});
//autohost for pvp sectors
@@ -133,6 +140,30 @@ public class Control implements ApplicationListener{
}
}
});
Events.on(UnlockEvent.class, e -> ui.hudfrag.showUnlock(e.content));
Events.on(BlockBuildEndEvent.class, e -> {
if(e.team == players[0].getTeam()){
if(e.breaking){
state.stats.buildingsDeconstructed++;
}else{
state.stats.buildingsBuilt ++;
}
}
});
Events.on(BlockDestroyEvent.class, e -> {
if(e.tile.getTeam() == players[0].getTeam()){
state.stats.buildingsDestroyed ++;
}
});
Events.on(UnitDestroyEvent.class, e -> {
if(e.unit.getTeam() != players[0].getTeam()){
state.stats.enemyUnitsDestroyed ++;
}
});
}
public void addPlayer(int index){

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.core;
import io.anuke.arc.Events;
import io.anuke.mindustry.game.EventType.StateChangeEvent;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.Stats;
import io.anuke.mindustry.game.Teams;
import io.anuke.mindustry.net.Net;
@@ -15,9 +16,11 @@ public class GameState{
/**Wave countdown in ticks.*/
public float wavetime;
/**Whether the game is in game over state.*/
public boolean gameOver = false;
public boolean gameOver = false, launched = false;
/**The current game rules.*/
public Rules rules = new Rules();
/**Statistics for this save/game. Displayed after game over.*/
public Stats stats = new Stats();
/**Team data. Gets reset every new game.*/
public Teams teams = new Teams();
/**Number of enemies in the game; only used clientside in servers.*/

View File

@@ -11,12 +11,8 @@ import io.anuke.arc.util.Time;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Teams;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
@@ -33,8 +29,14 @@ public class Logic implements ApplicationListener{
public Logic(){
Events.on(TileChangeEvent.class, event -> {
if(event.tile.getTeam() == defaultTeam && Recipe.getByResult(event.tile.block()) != null){
handleContent(Recipe.getByResult(event.tile.block()));
if(event.tile.getTeam() == defaultTeam && event.tile.block().isVisible()){
handleContent(event.tile.block());
}
});
Events.on(WaveEvent.class, event -> {
if(world.isZone()){
data.updateWaveScore(world.getZone(), state.wave);
}
});
}
@@ -65,9 +67,11 @@ public class Logic implements ApplicationListener{
public void reset(){
state.wave = 1;
state.wavetime = state.rules.waveSpacing;
state.gameOver = false;
state.gameOver = state.launched = false;
state.teams = new Teams();
state.rules = new Rules();
state.rules.spawns = Waves.getDefaultSpawns();
state.stats = new Stats();
Time.clear();
Entities.clear();

View File

@@ -296,7 +296,7 @@ public class NetServer implements ApplicationListener{
//auto-skip done requests
if(req.breaking && world.tile(req.x, req.y).block() == Blocks.air){
continue;
}else if(!req.breaking && world.tile(req.x, req.y).block() == req.recipe.result && (!req.recipe.result.rotate || world.tile(req.x, req.y).getRotation() == req.rotation)){
}else if(!req.breaking && world.tile(req.x, req.y).block() == req.block && (!req.block.rotate || world.tile(req.x, req.y).getRotation() == req.rotation)){
continue;
}
player.getPlaceQueue().addLast(req);

View File

@@ -47,7 +47,7 @@ public class UI implements ApplicationListener{
public LoadingFragment loadfrag;
public AboutDialog about;
public RestartDialog restart;
public GameOverDialog restart;
public CustomGameDialog levels;
public MapsDialog maps;
public LoadDialog load;
@@ -64,16 +64,17 @@ public class UI implements ApplicationListener{
public TraceDialog traces;
public ChangelogDialog changelog;
public LocalPlayerDialog localplayers;
public UnlocksDialog unlocks;
public DatabaseDialog database;
public ContentInfoDialog content;
public DeployDialog deploy;
public TechTreeDialog tech;
public Cursor drillCursor, unloadCursor;
public UI(){
Skin skin = new Skin(Core.atlas);
generateFonts(skin);
skin.load(Core.files.internal("ui/uiskin.json"));
skin.load(Core.files.internal("sprites/uiskin.json"));
for(BitmapFont font : skin.getAll(BitmapFont.class).values()){
font.setUseIntegerPositions(true);
@@ -158,13 +159,13 @@ public class UI implements ApplicationListener{
editor = new MapEditorDialog();
controls = new ControlsDialog();
restart = new RestartDialog();
restart = new GameOverDialog();
join = new JoinDialog();
discord = new DiscordDialog();
load = new LoadDialog();
levels = new CustomGameDialog();
language = new LanguageDialog();
unlocks = new UnlocksDialog();
database = new DatabaseDialog();
settings = new SettingsMenuDialog();
host = new HostDialog();
paused = new PausedDialog();
@@ -177,6 +178,7 @@ public class UI implements ApplicationListener{
localplayers = new LocalPlayerDialog();
content = new ContentInfoDialog();
deploy = new DeployDialog();
tech = new TechTreeDialog();
Group group = Core.scene.root;
@@ -236,7 +238,7 @@ public class UI implements ApplicationListener{
Table table = new Table();
table.setFillParent(true);
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.removeActor());
table.top().add(info).padTop(8);
table.top().add(info).padTop(40);
Core.scene.add(table);
}

View File

@@ -5,15 +5,21 @@ import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.collection.ObjectSet.ObjectSetIterator;
import io.anuke.arc.entities.Effects;
import io.anuke.arc.entities.EntityQuery;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Structs;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.Tmp;
import io.anuke.mindustry.ai.BlockIndexer;
import io.anuke.mindustry.ai.Pathfinder;
import io.anuke.mindustry.ai.WaveSpawner;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
@@ -22,6 +28,8 @@ import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.Maps;
import io.anuke.mindustry.maps.generators.Generator;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.world.Block;
@@ -178,6 +186,8 @@ public class World implements ApplicationListener{
}
}
addDarkness(tiles);
EntityQuery.resizeTree(0, 0, tiles.length * tilesize, tiles[0].length * tilesize);
generating = false;
@@ -188,6 +198,32 @@ public class World implements ApplicationListener{
return generating;
}
public void launchZone(){
Effects.effect(Fx.launchFull, 0, 0);
for(Tile tile : new ObjectSetIterator<>(state.teams.get(defaultTeam).cores)){
Effects.effect(Fx.launch, tile);
}
Time.runTask(30f, () -> {
for(Tile tile : new ObjectSetIterator<>(state.teams.get(defaultTeam).cores)){
for(Item item : content.items()){
data.addItem(item, tile.entity.items.get(item));
}
world.removeBlock(tile);
}
state.launched = true;
});
}
public boolean isZone(){
return getZone() != null;
}
public Zone getZone(){
return content.getByID(ContentType.zone, state.rules.zone);
}
public void playZone(Zone zone){
ui.loadAnd(() -> {
logic.reset();
@@ -198,6 +234,8 @@ public class World implements ApplicationListener{
core.entity.items.add(stack.item, stack.amount);
}
}
state.rules.zone = zone.id;
control.saves.zoneSave();
logic.play();
});
}
@@ -394,13 +432,60 @@ public class World implements ApplicationListener{
for(int x = 0; x < data.width(); x++){
data.read(marker);
tiles[x][y] = new Tile(x, y, marker.floor, marker.wall == Blocks.blockpart.id ? 0 : marker.wall, marker.rotation, marker.team);
tiles[x][y] = new Tile(x, y, marker.floor, marker.wall == Blocks.part.id ? 0 : marker.wall, marker.rotation, marker.team);
}
}
prepareTiles(tiles);
}
public void addDarkness(Tile[][] tiles){
byte[][] dark = new byte[tiles.length][tiles[0].length];
byte[][] writeBuffer = new byte[tiles.length][tiles[0].length];
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.block().solid && !tile.block().update){
dark[x][y] = 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;
}
}
writeBuffer[x][y] = (byte)Math.max(0, dark[x][y] - Mathf.num(min));
}
}
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
dark[x][y] = writeBuffer[x][y];
}
}
}
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().solid && !tile.block().update){
tiles[x][y].setRotation(dark[x][y]);
}
}
}
}
/**'Prepares' a tile array by:<br>
* - setting up multiblocks<br>
* - updating occlusion<br>

View File

@@ -1,70 +1,28 @@
package io.anuke.mindustry.editor;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntSet;
import io.anuke.arc.collection.LongArray;
import io.anuke.arc.util.Pack;
public class DrawOperation{
/**
* Data to apply operation to.
*/
private MapTileData data;
/**
* List of per-tile operations that occurred.
*/
private Array<TileOperation> operations = new Array<>();
/**
* Checks for duplicate operations, useful for brushes.
*/
private IntSet checks = new IntSet();
public DrawOperation(MapTileData data){
this.data = data;
}
private LongArray array = new LongArray();
public boolean isEmpty(){
return operations.size == 0;
return array.isEmpty();
}
public boolean checkDuplicate(short x, short y){
int i = Pack.shortInt(x, y);
if(checks.contains(i)) return true;
checks.add(i);
return false;
}
public void addOperation(TileOperation op){
operations.add(op);
public void addOperation(int xy, byte type, byte from, byte to){
array.add(Pack.longInt(xy, Pack.intBytes(type, from, to, (byte)0)));
}
public void undo(MapEditor editor){
for(int i = operations.size - 1; i >= 0; i--){
TileOperation op = operations.get(i);
data.position(op.x, op.y);
data.write(op.from);
editor.renderer().updatePoint(op.x, op.y);
for(int i = 0; i < array.size; i++){
long l = array.get(i);
}
}
public void redo(MapEditor editor){
for(TileOperation op : operations){
data.position(op.x, op.y);
data.write(op.to);
editor.renderer().updatePoint(op.x, op.y);
}
}
public static class TileOperation{
public short x, y;
public TileDataMarker from;
public TileDataMarker to;
public TileOperation(short x, short y, TileDataMarker from, TileDataMarker to){
this.x = x;
this.y = y;
this.from = from;
this.to = to;
for(int i = 0; i < array.size; i++){
long l = array.get(i);
}
}
}

View File

@@ -14,7 +14,7 @@ import static io.anuke.mindustry.Vars.ui;
public enum EditorTool{
pick{
public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, editor.getMap().width(), editor.getMap().height())) return;
if(!Structs.inBounds(x, y, world.width(), world.height())) return;
byte bf = editor.getMap().read(x, y, DataPosition.floor);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
@@ -54,7 +54,7 @@ public enum EditorTool{
editor.draw(x, y, Blocks.air);
}
},
elevation{
spray{
{
edit = true;
draggable = true;
@@ -62,7 +62,7 @@ public enum EditorTool{
@Override
public void touched(MapEditor editor, int x, int y){
editor.elevate(x, y);
editor.draw(x, y, editor.getDrawBlock(), 0.012);
}
},
line{
@@ -82,7 +82,7 @@ public enum EditorTool{
MapTileData data;
public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, editor.getMap().width(), editor.getMap().height())) return;
if(!Structs.inBounds(x, y, world.width(), world.height())) return;
if(editor.getDrawBlock().isMultiblock()){
//don't fill multiblocks, thanks
@@ -96,7 +96,6 @@ public enum EditorTool{
byte bf = data.read(x, y, DataPosition.floor);
byte bw = data.read(x, y, DataPosition.wall);
be = data.read(x, y, DataPosition.elevation);
boolean synth = editor.getDrawBlock().synthetic();
byte brt = Pack.byteByte((byte) editor.getDrawRotation(), (byte) editor.getDrawTeam().ordinal());
@@ -107,8 +106,8 @@ public enum EditorTool{
return;
}
width = editor.getMap().width();
int height = editor.getMap().height();
width = world.width();
int height = world.height();
int x1;
boolean spanAbove, spanBelow;

View File

@@ -9,54 +9,42 @@ import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.editor.DrawOperation.TileOperation;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.world;
public class MapEditor{
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15};
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
private MapTileData map;
private ObjectMap<String, String> tags = new ObjectMap<>();
private MapRenderer renderer = new MapRenderer(this);
private int brushSize = 1;
private byte elevation;
private int rotation;
private Block drawBlock = Blocks.stone;
private Team drawTeam = Team.blue;
public MapTileData getMap(){
return map;
}
public ObjectMap<String, String> getTags(){
return tags;
}
public void beginEdit(MapTileData map, ObjectMap<String, String> tags, boolean clear){
this.map = map;
public void beginEdit(Tile[][] map, ObjectMap<String, String> tags, boolean clear){
this.brushSize = 1;
this.tags = tags;
if(clear){
for(int x = 0; x < map.width(); x++){
for(int y = 0; y < map.height(); y++){
map.write(x, y, DataPosition.floor, Blocks.stone.id);
for(int x = 0; x < map.length; x++){
for(int y = 0; y < map[0].length; y++){
map[x][y].setFloor((Floor)Blocks.stone);
}
}
}
drawBlock = Blocks.stone;
renderer.resize(map.width(), map.height());
}
public byte getDrawElevation(){
return elevation;
}
public void setDrawElevation(int elevation){
this.elevation = (byte) elevation;
renderer.resize(map.length, map[0].length);
}
public int getDrawRotation(){
@@ -96,19 +84,19 @@ public class MapEditor{
}
public void draw(int x, int y, Block drawBlock){
if(x < 0 || y < 0 || x >= map.width() || y >= map.height()){
return;
}
draw(x, y, drawBlock, 1.0);
}
public void draw(int x, int y, Block drawBlock, double chance){
byte writeID = drawBlock.id;
byte partID = Blocks.blockpart.id;
byte partID = Blocks.part.id;
byte rotationTeam = Pack.byteByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0);
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
if(drawBlock.isMultiblock()){
x = Mathf.clamp(x, (drawBlock.size-1)/2, map.width() - drawBlock.size/2 - 1);
y = Mathf.clamp(y, (drawBlock.size-1)/2, map.height() - drawBlock.size/2 - 1);
x = Mathf.clamp(x, (drawBlock.size-1)/2, world.width() - drawBlock.size/2 - 1);
y = Mathf.clamp(y, (drawBlock.size-1)/2, world.height() - drawBlock.size/2 - 1);
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
@@ -119,7 +107,7 @@ public class MapEditor{
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, map.width(), map.height())){
if(Structs.inBounds(worldx, worldy, world.width(), world.height())){
TileDataMarker prev = getPrev(worldx, worldy, false);
if(i == 1){
@@ -151,10 +139,9 @@ public class MapEditor{
onWrite(x, y, prev);
}else{
for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){
int wx = x + rx, wy = y + ry;
if(wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height()){
@@ -175,7 +162,6 @@ public class MapEditor{
if(isfloor){
map.write(wx, wy, DataPosition.floor, writeID);
map.write(wx, wy, DataPosition.elevation, elevation);
}else{
map.write(wx, wy, DataPosition.wall, writeID);
map.write(wx, wy, DataPosition.link, (byte) 0);
@@ -189,30 +175,6 @@ public class MapEditor{
}
}
public void elevate(int x, int y){
if(x < 0 || y < 0 || x >= map.width() || y >= map.height()){
return;
}
for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f){
int wx = x + rx, wy = y + ry;
if(wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height()){
continue;
}
TileDataMarker prev = getPrev(wx, wy, true);
map.write(wx, wy, DataPosition.elevation, elevation);
onWrite(x + rx, y + ry, prev);
}
}
}
}
private void removeLinked(int x, int y){
Block block = content.block(map.read(x, y, DataPosition.wall));

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.editor;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer;
@@ -11,21 +12,21 @@ import io.anuke.arc.input.KeyCode;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.scene.utils.UIUtils;
import io.anuke.arc.util.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.blocks.OreBlock;
import java.io.DataInputStream;
import java.io.InputStream;
@@ -42,6 +43,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
private FloatingDialog menu;
private boolean saved = false;
private boolean shownWithMap = false;
private Array<Block> blocksOut = new Array<>();
private ButtonGroup<ImageButton> blockgroup;
@@ -146,7 +148,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
}).padTop(-5).size(swidth * 2f + 10, 60f);
resizeDialog = new MapResizeDialog(editor, (x, y) -> {
if(!(editor.getMap().width() == x && editor.getMap().height() == y)){
if(!(world.width() == x && world.height() == y)){
ui.loadAnd(() -> {
editor.resize(x, y);
view.clearStack();
@@ -386,7 +388,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
tools.row();
addTool.accept(EditorTool.fill);
addTool.accept(EditorTool.elevation);
addTool.accept(EditorTool.spray);
ImageButton rotate = tools.addImageButton("icon-arrow-16", "clear", 16 * 2f, () -> editor.setDrawRotation((editor.getDrawRotation() + 1) % 4)).get();
rotate.getImage().update(() -> {
@@ -497,29 +499,21 @@ public class MapEditorDialog extends Dialog implements Disposable{
int i = 0;
for(Block block : Vars.content.blocks()){
TextureRegion[] regions = block.getCompactIcon();
if((block.synthetic() && (Recipe.getByResult(block) == null || !data.isUnlocked(Recipe.getByResult(block))))
&& block != Blocks.core){
continue;
}
blocksOut.clear();
blocksOut.addAll(Vars.content.blocks());
blocksOut.sort((b1, b2) -> b1.synthetic() && !b2.synthetic() ? 1 : b2.synthetic() && !b1.synthetic() ? -1 :
b1 instanceof OreBlock && !(b2 instanceof OreBlock) ? 1 : !(b1 instanceof OreBlock) && b2 instanceof OreBlock ? -1 :
Integer.compare(b1.id, b2.id));
if(Recipe.getByResult(block) != null && !Recipe.getByResult(block).visibility.shown()){
continue;
}
for(Block block : blocksOut){
TextureRegion region = block.icon(Icon.medium);
if(regions.length == 0 || regions[0] == Core.atlas.find("jjfgj")) continue;
Stack stack = new Stack();
for(TextureRegion region : regions){
stack.add(new Image(region));
}
if(region == Core.atlas.find("jjfgj")) continue;
ImageButton button = new ImageButton("white", "clear-toggle");
button.getStyle().imageUp = new TextureRegionDrawable(region);
button.clicked(() -> editor.setDrawBlock(block));
button.resizeImage(8 * 4f);
button.replaceImage(stack);
button.update(() -> button.setChecked(editor.getDrawBlock() == block));
group.add(button);
content.add(button).size(50f);

View File

@@ -8,9 +8,11 @@ import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Pack;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.IndexedRenderer;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.tilesize;
@@ -22,7 +24,6 @@ public class MapRenderer implements Disposable{
private IntSet delayedUpdates = new IntSet();
private MapEditor editor;
private int width, height;
private Color tmpColor = Color.WHITE.cpy();
public MapRenderer(MapEditor editor){
this.editor = editor;
@@ -109,33 +110,38 @@ public class MapRenderer implements Disposable{
TextureRegion region;
if(bw != 0){
region = wall.getEditorIcon();
int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize;
int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize;
if(bw != 0 && (wall.synthetic() || wall == Blocks.part)){
region = wall.icon(Icon.full) == Core.atlas.find("____") ? Core.atlas.find("clear") : wall.icon(Icon.full);
if(wall.rotate){
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize, region,
mesh.draw(idxWall, region,
wx * tilesize + wall.offset(), wy * tilesize + wall.offset(),
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, rotation * 90 - 90);
}else{
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize, region,
mesh.draw(idxWall, region,
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f,
wy * tilesize + wall.offset() + (tilesize - region.getHeight() * Draw.scl)/2f,
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl);
}
}else{
region = floor.getEditorIcon();
region = floor.icon(Icon.full);
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize, region, wx * tilesize, wy * tilesize, 8, 8);
mesh.draw(idxWall, region, wx * tilesize, wy * tilesize, 8, 8);
}
if(wall.update || wall.destructible){
mesh.setColor(team.color);
region = Core.atlas.find("block-border");
}else if(!wall.synthetic() && bw != 0){
region = wall.icon(Icon.full) == Core.atlas.find("____") ? Core.atlas.find("clear") : wall.icon(Icon.full);
}else{
region = Core.atlas.find("clear");
}
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize, region,
mesh.draw(idxDecal, region,
wx * tilesize - (wall.size/3) * tilesize, wy * tilesize - (wall.size/3) * tilesize,
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl);
mesh.setColor(Color.WHITE);

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.editor;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.GridBits;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
@@ -18,19 +19,19 @@ import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.TextField;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Tmp;
import io.anuke.mindustry.editor.DrawOperation.TileOperation;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.ui.GridImage;
import io.anuke.mindustry.world.Pos;
import static io.anuke.mindustry.Vars.mobile;
import static io.anuke.mindustry.Vars.ui;
import static io.anuke.mindustry.Vars.*;
public class MapView extends Element implements GestureListener{
private MapEditor editor;
private EditorTool tool = EditorTool.pencil;
private OperationStack stack = new OperationStack();
private DrawOperation op;
private GridBits used;
private Bresenham2 br = new Bresenham2();
private boolean updated = false;
private float offsetx, offsety;
@@ -86,7 +87,12 @@ public class MapView extends Element implements GestureListener{
mousex = x;
mousey = y;
op = new DrawOperation(editor.getMap());
op = new DrawOperation();
if(used == null || used.width() != world.width() || used.height() != world.height()){
used = new GridBits(world.width(), world.height());
}else{
used.clear();
}
updated = false;
@@ -146,7 +152,7 @@ public class MapView extends Element implements GestureListener{
Point2 p = project(x, y);
if(drawing && tool.draggable){
if(drawing && tool.draggable && !(p.x == lastx && p.y == lasty)){
ui.editor.resetSaved();
Array<Point2> points = br.line(lastx, lasty, p.x, p.y);
for(Point2 point : points){
@@ -196,12 +202,13 @@ public class MapView extends Element implements GestureListener{
}
}
public void addTileOp(TileOperation t){
op.addOperation(t);
public void addTileOp(int xy, byte type, byte from, byte to){
used.set(Pos.x(xy), Pos.y(xy));
op.addOperation(xy, type, from, to);
}
public boolean checkForDuplicates(short x, short y){
return op.checkDuplicate(x, y);
return used.get(x, y);
}
@Override
@@ -227,12 +234,12 @@ public class MapView extends Element implements GestureListener{
}
private Point2 project(float x, float y){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height());
float ratio = 1f / ((float) world.width() / world.height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.getMap().width();
y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.getMap().height();
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * world.width();
y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * world.height();
if(editor.getDrawBlock().size % 2 == 0 && tool != EditorTool.eraser){
return Tmp.g1.set((int) (x - 0.5f), (int) (y - 0.5f));
@@ -242,26 +249,26 @@ public class MapView extends Element implements GestureListener{
}
private Vector2 unproject(int x, int y){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height());
float ratio = 1f / ((float) world.width() / world.height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
float px = ((float) x / editor.getMap().width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
float py = ((float) (y) / editor.getMap().height()) * sclheight
float px = ((float) x / world.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
float py = ((float) (y) / world.height()) * sclheight
+ offsety * zoom - sclheight / 2 + getHeight() / 2;
return vec.set(px, py);
}
@Override
public void draw(){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height());
float ratio = 1f / ((float) world.width() / world.height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
float centerx = x + width / 2 + offsetx * zoom;
float centery = y + height / 2 + offsety * zoom;
image.setImageSize(editor.getMap().width(), editor.getMap().height());
image.setImageSize(world.width(), world.height());
if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){
return;
@@ -292,7 +299,7 @@ public class MapView extends Element implements GestureListener{
}
}
float scaling = zoom * Math.min(width, height) / editor.getMap().width();
float scaling = zoom * Math.min(width, height) / world.width();
Draw.color(Palette.accent);
Lines.stroke(Unit.dp.scl(1f * zoom));

View File

@@ -26,12 +26,14 @@ import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.io.TypeIO;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.storage.CoreBlock.CoreEntity;
@@ -109,6 +111,31 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
rectangle.setSize(mech.hitsize * 2f / 3f).setCenter(x, y);
}
@Override
public void onRespawn(Tile tile){
boostHeat = 1f;
achievedFlight = true;
}
@Override
public void move(float x, float y){
if(!mech.flying){
EntityQuery.collisions().move(this, x, y);
}else{
moveBy(x, y);
}
}
@Override
public boolean collidesGrid(int x, int y){
Tile tile = world.tile(x, y);
if(!isFlying()) return true;
if(!mech.flying && tile != null && !tile.block().synthetic() && tile.block().solid){
return true;
}
return false;
}
@Override
public float drag(){
return mech.drag;
@@ -304,7 +331,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
Floor floor = getFloorOn();
Draw.color();
Draw.alpha(hitTime / hitDuration);
Draw.alpha(Draw.getShader() != Shaders.mix ? 1f : hitTime / hitDuration);
if(!mech.flying){
if(floor.isLiquid){
@@ -422,7 +449,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
Lines.stroke(2f, Palette.removeBack);
float rad = Mathf.absin(Time.time(), 7f, 1f) + block.size * tilesize / 2f - 1;
Lines.square(
request.x * tilesize + block.offset(),
request.y * tilesize + block.offset() - 1,
@@ -435,21 +461,28 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
request.y * tilesize + block.offset(),
rad);
}else{
//draw place request
Lines.stroke(2f, Palette.accentBack);
float rad = Mathf.absin(Time.time(), 7f, 1f) - 1.5f + request.block.size * tilesize / 2f;
float rad = Mathf.absin(Time.time(), 7f, 1f) - 2f + request.recipe.result.size * tilesize / 2f;
//draw place request
Lines.stroke(1f, Palette.accentBack);
Lines.square(
request.x * tilesize + request.recipe.result.offset(),
request.y * tilesize + request.recipe.result.offset() - 1,
request.x * tilesize + request.block.offset(),
request.y * tilesize + request.block.offset() - 1,
rad);
Draw.color();
Draw.rect(request.block.icon(Icon.full),
request.x * tilesize + request.block.offset(),
request.y * tilesize + request.block.offset(), rad*2, rad*2, request.rotation * 90);
Draw.color(Palette.accent);
Lines.square(
request.x * tilesize + request.recipe.result.offset(),
request.y * tilesize + request.recipe.result.offset(),
request.x * tilesize + request.block.offset(),
request.y * tilesize + request.block.offset(),
rad);
}
}

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.entities;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.entities.Effects;
@@ -16,6 +17,7 @@ import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.game.EventType.BlockDestroyEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.world.Block;
@@ -244,6 +246,7 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
dead = true;
Block block = tile.block();
Events.fire(new BlockDestroyEvent(tile));
block.onDestroyed(tile);
world.removeBlock(tile);
block.afterDestroyed(tile, this);

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.entities;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.entities.Effects;
import io.anuke.arc.entities.impl.DestructibleEntity;
import io.anuke.arc.entities.trait.DamageTrait;
@@ -17,6 +18,7 @@ import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.game.EventType.UnitDestroyEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Teams.TeamData;
import io.anuke.mindustry.net.Interpolator;
@@ -72,6 +74,11 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
return carrier;
}
@Override
public boolean collidesGrid(int x, int y){
return !isFlying();
}
@Override
public void setCarrier(CarryTrait carrier){
this.carrier = carrier;
@@ -121,6 +128,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
inventory.clear();
drownTime = 0f;
status.clear();
Events.fire(new UnitDestroyEvent(this));
}
@Override
@@ -217,6 +225,8 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
return tile == null ? (Floor) Blocks.air : tile.floor();
}
public void onRespawn(Tile tile){}
@Override
public boolean isValid(){
return !isDead() && isAdded();
@@ -239,8 +249,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
velocity.limit(maxVelocity()).scl(1f + (status.getSpeedMultiplier()-1f) * Time.delta());
if(isFlying()){
x += velocity.x * Time.delta();
y += velocity.y * Time.delta();
move(velocity.x * Time.delta(), velocity.y * Time.delta());
}else{
boolean onLiquid = floor.isLiquid;

View File

@@ -14,7 +14,7 @@ import static io.anuke.mindustry.Vars.groundEffectGroup;
* Class for creating block rubble on the ground.
*/
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{
private static final Color color = Color.valueOf("52504e");
private static final Color color = Color.valueOf("3a3635");
@Override
public float lifetime(){

View File

@@ -105,13 +105,9 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
@Override
public void draw(){
float length = fslope() * 6f;
float angle = current.set(x, y).sub(from).angle();
Lines.stroke(fslope() * 2f, Palette.accent);
Lines.circle(x, y, fslope() * 2f);
Lines.lineAngleCenter(x, y, angle, length);
Lines.lineAngle(x, y, angle, fout() * 6f);
Draw.color(item.color);
Fill.circle(x, y, fslope() * 1.5f);

View File

@@ -31,12 +31,16 @@ public class ScorchDecal extends Decal{
@Override
public void drawDecal(){
for(int i = 0; i < 5; i++){
TextureRegion region = regions[Mathf.randomSeed(id - i, 0, scorches - 1)];
float rotation = Mathf.randomSeed(id + i, 0, 360);
float space = 1.5f + Mathf.randomSeed(id + i + 1, 0, 20) / 10f;
Draw.rect(region, x + Angles.trnsx(rotation, space), y + Angles.trnsy(rotation, space) + region.getHeight()/2f, region.getWidth()/2f, 0, rotation - 90);
Draw.rect(region,
x + Angles.trnsx(rotation, space),
y + Angles.trnsy(rotation, space) + region.getHeight()/2f*Draw.scl,
region.getWidth() * Draw.scl,
region.getHeight() * Draw.scl,
region.getWidth()/2f*Draw.scl, 0, rotation - 90);
}
}
}

View File

@@ -26,7 +26,7 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shapes;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Build;
import io.anuke.mindustry.world.Pos;
import io.anuke.mindustry.world.Tile;
@@ -81,7 +81,7 @@ public interface BuilderTrait extends Entity{
output.writeInt(Pos.get(request.x, request.y));
output.writeFloat(request.progress);
if(!request.breaking){
output.writeByte(request.recipe.id);
output.writeByte(request.block.id);
output.writeByte(request.rotation);
}
}else{
@@ -105,9 +105,9 @@ public interface BuilderTrait extends Entity{
if(type == 1){ //remove
request = new BuildRequest(Pos.x(position), Pos.y(position));
}else{ //place
byte recipe = input.readByte();
byte block = input.readByte();
byte rotation = input.readByte();
request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.recipe(recipe));
request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
}
request.progress = progress;
@@ -129,7 +129,7 @@ public interface BuilderTrait extends Entity{
* If a place request matching this signature is present, it is removed.
* Otherwise, a new place request is added to the queue.
*/
default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
default void replaceBuilding(int x, int y, int rotation, Block block){
for(BuildRequest request : getPlaceQueue()){
if(request.x == x && request.y == y){
clearBuilding();
@@ -138,7 +138,7 @@ public interface BuilderTrait extends Entity{
}
}
addBuildRequest(new BuildRequest(x, y, rotation, recipe));
addBuildRequest(new BuildRequest(x, y, rotation, block));
}
/**Clears the placement queue.*/
@@ -184,8 +184,8 @@ public interface BuilderTrait extends Entity{
for(BuildRequest request : removal){
if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) ||
(!request.breaking &&
(world.tile(request.x, request.y).getRotation() == request.rotation || !request.recipe.result.rotate)
&& world.tile(request.x, request.y).block() == request.recipe.result))){
(world.tile(request.x, request.y).getRotation() == request.rotation || !request.block.rotate)
&& world.tile(request.x, request.y).block() == request.block))){
getPlaceQueue().addLast(request);
}
}
@@ -209,8 +209,8 @@ public interface BuilderTrait extends Entity{
}
if(!(tile.block() instanceof BuildBlock)){
if(canCreateBlocks() && !current.breaking && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)){
Build.beginPlace(unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
if(canCreateBlocks() && !current.breaking && Build.validPlace(unit.getTeam(), current.x, current.y, current.block, current.rotation)){
Build.beginPlace(unit.getTeam(), current.x, current.y, current.block, current.rotation);
}else if(canCreateBlocks() && current.breaking && Build.validBreak(unit.getTeam(), current.x, current.y)){
Build.beginBreak(unit.getTeam(), current.x, current.y);
}else{
@@ -264,10 +264,10 @@ public interface BuilderTrait extends Entity{
TileEntity core = unit.getClosestCore();
if(core == null || tile.block() != Blocks.air || unit.dst(tile.worldx(), tile.worldy()) > mineDistance
|| tile.floor().drops == null || !unit.inventory.canAcceptItem(tile.floor().drops.item) || !canMine(tile.floor().drops.item)){
|| tile.floor().itemDrop == null || !unit.inventory.canAcceptItem(tile.floor().itemDrop) || !canMine(tile.floor().itemDrop)){
setMineTile(null);
}else{
Item item = tile.floor().drops.item;
Item item = tile.floor().itemDrop;
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(tile.worldx(), tile.worldy()), 0.4f);
if(Mathf.chance(Time.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){
@@ -370,18 +370,18 @@ public interface BuilderTrait extends Entity{
/**Class for storing build requests. Can be either a place or remove request.*/
class BuildRequest{
public final int x, y, rotation;
public final Recipe recipe;
public final Block block;
public final boolean breaking;
public float progress;
public boolean initialized;
/**This creates a build request.*/
public BuildRequest(int x, int y, int rotation, Recipe recipe){
public BuildRequest(int x, int y, int rotation, Block block){
this.x = x;
this.y = y;
this.rotation = rotation;
this.recipe = recipe;
this.block = block;
this.breaking = false;
}
@@ -390,7 +390,7 @@ public interface BuilderTrait extends Entity{
this.x = x;
this.y = y;
this.rotation = -1;
this.recipe = Recipe.getByResult(world.tile(x, y).block());
this.block = world.tile(x, y).block();
this.breaking = true;
}
@@ -400,7 +400,7 @@ public interface BuilderTrait extends Entity{
"x=" + x +
", y=" + y +
", rotation=" + rotation +
", recipe=" + recipe +
", recipe=" + block +
", breaking=" + breaking +
", progress=" + progress +
", initialized=" + initialized +

View File

@@ -88,9 +88,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return type.drag;
}
/**Called when a command is recieved from the command center.*/
public abstract void onCommand(UnitCommand command);
/**Initialize the type and team of this unit. Only call once!*/
public void init(UnitType type, Team team){
if(this.type != null) throw new RuntimeException("This unit is already initialized!");

View File

@@ -11,6 +11,7 @@ import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.traits.CarriableTrait;
import io.anuke.mindustry.entities.traits.CarryTrait;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockFlag;
@@ -120,11 +121,8 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
};
@Override
public void onCommand(UnitCommand command){
state.set(command == UnitCommand.retreat ? retreat :
command == UnitCommand.attack ? attack :
command == UnitCommand.patrol ? patrol :
null);
public void move(float x, float y){
moveBy(x, y);
}
@Override
@@ -154,7 +152,7 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
@Override
public void draw(){
Draw.alpha(hitTime / hitDuration);
Draw.alpha(Draw.getShader() != Shaders.mix ? 1f : hitTime / hitDuration);
Draw.rect(type.name, x, y, rotation - 90);

View File

@@ -12,6 +12,7 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Weapon;
import io.anuke.mindustry.world.Tile;
@@ -78,14 +79,6 @@ public abstract class GroundUnit extends BaseUnit{
}
};
@Override
public void onCommand(UnitCommand command){
state.set(command == UnitCommand.retreat ? retreat :
command == UnitCommand.attack ? attack :
command == UnitCommand.patrol ? patrol :
null);
}
@Override
public void init(UnitType type, Team team){
super.init(type, team);
@@ -140,7 +133,7 @@ public abstract class GroundUnit extends BaseUnit{
@Override
public void draw(){
Draw.alpha(hitTime / hitDuration);
Draw.alpha(Draw.getShader() != Shaders.mix ? 1f : hitTime / hitDuration);
float ft = Mathf.sin(walkTime * type.speed*5f, 6f, 2f);
@@ -272,7 +265,9 @@ public abstract class GroundUnit extends BaseUnit{
float angle = angleTo(targetTile);
velocity.add(vec.trns(angleTo(targetTile), type.speed*Time.delta()));
rotation = Mathf.slerpDelta(rotation, angle, type.rotatespeed);
if(Units.invalidateTarget(target, this)){
rotation = Mathf.slerpDelta(rotation, angle, type.rotatespeed);
}
}
protected void moveAwayFromCore(){

View File

@@ -1,17 +0,0 @@
package io.anuke.mindustry.entities.units;
import io.anuke.arc.Core;
public enum UnitCommand{
attack, retreat, patrol;
private final String localized;
UnitCommand(){
localized = Core.bundle.get("command." + name());
}
public String localized(){
return localized;
}
}

View File

@@ -13,7 +13,6 @@ import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.BuilderTrait;
import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.FlyingUnit;
import io.anuke.mindustry.entities.units.UnitCommand;
import io.anuke.mindustry.entities.units.UnitState;
import io.anuke.mindustry.game.EventType.BuildSelectEvent;
import io.anuke.mindustry.gen.Call;
@@ -66,13 +65,13 @@ public class Drone extends FlyingUnit implements BuilderTrait{
if(isBreaking){
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
}else{
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.getRotation(), entity.recipe));
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.getRotation(), entity.block));
}
}
//if it's missing requirements, try and mine them
if(entity.recipe != null){
for(ItemStack stack : entity.recipe.requirements){
if(entity.block != null){
for(ItemStack stack : entity.block.buildRequirements){
if(!core.items.has(stack.item, stack.amount) && type.toMine.contains(stack.item)){
targetItem = stack.item;
getPlaceQueue().clear();
@@ -262,11 +261,6 @@ public class Drone extends FlyingUnit implements BuilderTrait{
}
}
@Override
public void onCommand(UnitCommand command){
//no
}
@Override
public boolean canMine(Item item){
return type.toMine.contains(item);

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.game;
import io.anuke.arc.Events.Event;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.traits.BuilderTrait;
import io.anuke.mindustry.world.Tile;
@@ -60,9 +61,9 @@ public class EventType{
}
public static class UnlockEvent implements Event{
public final Content content;
public final UnlockableContent content;
public UnlockEvent(Content content){
public UnlockEvent(UnlockableContent content){
this.content = content;
}
}
@@ -81,6 +82,18 @@ public class EventType{
}
}
public static class BlockBuildEndEvent implements Event{
public final Tile tile;
public final Team team;
public final boolean breaking;
public BlockBuildEndEvent(Tile tile, Team team, boolean breaking){
this.tile = tile;
this.team = team;
this.breaking = breaking;
}
}
/**Called when a player or drone begins building something.
* This does not necessarily happen when a new BuildBlock is created.*/
public static class BuildSelectEvent implements Event{
@@ -97,6 +110,22 @@ public class EventType{
}
}
public static class BlockDestroyEvent implements Event{
public final Tile tile;
public BlockDestroyEvent(Tile tile){
this.tile = tile;
}
}
public static class UnitDestroyEvent implements Event{
public final Unit unit;
public UnitDestroyEvent(Unit unit){
this.unit = unit;
}
}
public static class ResizeEvent implements Event{
}

View File

@@ -6,10 +6,15 @@ import io.anuke.arc.collection.ObjectIntMap;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.UnlockEvent;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Zone;
import static io.anuke.mindustry.Vars.*;
/**Stores player unlocks. Clientside only.*/
public class GlobalData{
@@ -19,11 +24,33 @@ public class GlobalData{
public GlobalData(){
Core.settings.setSerializer(ContentType.class, (stream, t) -> stream.writeInt(t.ordinal()), stream -> ContentType.values()[stream.readInt()]);
Core.settings.setSerializer(Item.class, (stream, t) -> stream.writeUTF(t.name), stream -> content.getByName(ContentType.item, stream.readUTF()));
}
public void updateWaveScore(Zone zone, int wave){
int value = Core.settings.getInt(zone.name + "-wave", 0);
if(value < wave){
Core.settings.put(zone.name + "-wave", wave);
modified = true;
}
}
public int getWaveScore(Zone zone){
return Core.settings.getInt(zone.name + "-wave", 0);
}
public boolean isCompleted(Zone zone){
return getWaveScore(zone) >= zone.conditionWave;
}
public int getItem(Item item){
return items.get(item, 0);
}
public void addItem(Item item, int amount){
modified = true;
items.getAndIncrement(item, 0, amount);
state.stats.itemsDelivered.getAndIncrement(item, 0, amount);
}
public boolean hasItems(ItemStack[] stacks){
@@ -42,37 +69,34 @@ public class GlobalData{
modified = true;
}
public boolean has(Item item, int amount){
return items.get(item, 0) >= amount;
}
public ObjectIntMap<Item> items(){
return items;
}
/** Returns whether or not this piece of content is unlocked yet.*/
public boolean isUnlocked(UnlockableContent content){
return true;
//return content.alwaysUnlocked() || unlocked.getOr(content.getContentType(), ObjectSet::new).contains(content.getContentName());
return (!state.is(State.menu) && !world.isZone()) || content.alwaysUnlocked() || unlocked.getOr(content.getContentType(), ObjectSet::new).contains(content.getContentName());
}
/**
* Makes this piece of content 'unlocked', if possible.
* If this piece of content is already unlocked or cannot be unlocked due to dependencies, nothing changes.
* Results are not saved until you call {@link #save()}.
*
* @return whether or not this content was newly unlocked.
*/
public boolean unlockContent(UnlockableContent content){
if(!content.canBeUnlocked() || content.alwaysUnlocked()) return false;
boolean ret = unlocked.getOr(content.getContentType(), ObjectSet::new).add(content.getContentName());
public void unlockContent(UnlockableContent content){
if(content.alwaysUnlocked()) return;
//fire unlock event so other classes can use it
if(ret){
if(unlocked.getOr(content.getContentType(), ObjectSet::new).add(content.getContentName())){
modified = true;
content.onUnlock();
Events.fire(new UnlockEvent(content));
save();
}
return ret;
}
/** Clears all unlocked content. Automatically saves.*/
@@ -93,6 +117,11 @@ public class GlobalData{
for(Item item : Vars.content.items()){
items.put(item, Core.settings.getInt("item-" + item.name, 0));
}
//set up default values
if(!Core.settings.has("item-" + Items.copper)){
addItem(Items.copper, 500);
}
}
public void save(){

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.game;
import io.anuke.annotations.Annotations.Serialize;
import io.anuke.arc.collection.Array;
/**Defines current rules on how the game should function.
* Does not store game state, just configuration.*/
@@ -24,4 +25,8 @@ public class Rules{
public float respawnTime = 60 * 4;
/**Time between waves in ticks.*/
public float waveSpacing = 60 * 60;
/**Zone ID, -1 for invalid zone.*/
public byte zone = -1;
/**Spawn layout. Since only zones modify this, it should be assigned on save load.*/
public transient Array<SpawnGroup> spawns = new Array<>();
}

View File

@@ -11,8 +11,11 @@ import io.anuke.arc.util.Time;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.StateChangeEvent;
import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.io.SaveIO.SaveException;
import io.anuke.mindustry.io.SaveMeta;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Zone;
import java.io.IOException;
import java.text.SimpleDateFormat;
@@ -105,6 +108,16 @@ public class Saves{
return saving;
}
public void zoneSave(){
SaveSlot slot = new SaveSlot(-1);
slot.setName("zone");
saves.remove(s -> s.index == -1);
saves.add(slot);
saveMap.put(slot.index, slot);
slot.save();
saveSlots();
}
public SaveSlot addSave(String name){
SaveSlot slot = new SaveSlot(nextSlot);
nextSlot++;
@@ -129,6 +142,11 @@ public class Saves{
return slot;
}
public SaveSlot getZoneSlot(){
SaveSlot slot = getByID(-1);
return slot == null || slot.getZone() == null ? null : slot;
}
public SaveSlot getByID(int id){
return saveMap.get(id);
}
@@ -153,11 +171,15 @@ public class Saves{
this.index = index;
}
public void load(){
SaveIO.loadFromSlot(index);
meta = SaveIO.getData(index);
current = this;
totalPlaytime = meta.timePlayed;
public void load() throws SaveException{
try{
SaveIO.loadFromSlot(index);
meta = SaveIO.getData(index);
current = this;
totalPlaytime = meta.timePlayed;
}catch(Exception e){
throw new SaveException(e);
}
}
public void save(){
@@ -203,6 +225,10 @@ public class Saves{
Core.settings.save();
}
public Zone getZone(){
return content.getByID(ContentType.zone, meta.rules.zone);
}
public int getBuild(){
return meta.build;
}
@@ -211,10 +237,6 @@ public class Saves{
return meta.wave;
}
public Difficulty getDifficulty(){
return meta.difficulty;
}
public boolean isAutosave(){
return Core.settings.getBool("save-" + index + "-autosave", true);
}

View File

@@ -13,53 +13,25 @@ import io.anuke.mindustry.type.Weapon;
* Each spawn group can have multiple sub-groups spawned in different areas of the map.
*/
public class SpawnGroup{
/**
* The unit type spawned
*/
/**The unit type spawned*/
public final UnitType type;
/**
* When this spawn should end
*/
/**When this spawn should end*/
protected int end = Integer.MAX_VALUE;
/**
* When this spawn should start
*/
/**When this spawn should start*/
protected int begin;
/**
* The spacing, in waves, of spawns. For example, 2 = spawns every other wave
*/
/**The spacing, in waves, of spawns. For example, 2 = spawns every other wave*/
protected int spacing = 1;
/**
* Maximum amount of units that spawn
*/
/**Maximum amount of units that spawn*/
protected int max = 60;
/**
* How many waves need to pass before the amount of units spawned increases by 1
*/
/**How many waves need to pass before the amount of units spawned increases by 1*/
protected float unitScaling = 9999f;
/**
* How many waves need to pass before the amount of instances of this group increases by 1
*/
protected float groupScaling = 9999f;
/**
* Amount of enemies spawned initially, with no scaling
*/
/**Amount of enemies spawned initially, with no scaling*/
protected int unitAmount = 1;
/**
* Amount of enemies spawned initially, with no scaling
*/
protected int groupAmount = 1;
/**
* Weapon used by the spawned unit. Null to disable. Only applicable to ground units.
*/
/**Weapon used by the spawned unit. Null to disable. Only applicable to ground units.*/
protected Weapon weapon;
/**
* Status effect applied to the spawned unit. Null to disable.
*/
/**Status effect applied to the spawned unit. Null to disable.*/
protected StatusEffect effect;
/**
* Items this unit spawns with. Null to disable.
*/
/**Items this unit spawns with. Null to disable.*/
protected ItemStack items;
public SpawnGroup(UnitType type){
@@ -75,18 +47,7 @@ public class SpawnGroup{
}
float scaling = this.unitScaling;
return Math.min(unitAmount - 1 + Math.max((int) ((wave / spacing) / scaling), 1), max);
}
/**
* Returns the amount of different unit groups at a specific wave.
*/
public int getGroupsSpawned(int wave){
if(wave < begin || wave > end || (wave - begin) % spacing != 0){
return 0;
}
return Math.min(groupAmount - 1 + Math.max((int) ((wave / spacing) / groupScaling), 1), max);
return Math.min(unitAmount - 1 + Math.max((int) (((wave - begin) / spacing) / scaling), 1), max);
}
/**
@@ -110,4 +71,20 @@ public class SpawnGroup{
return unit;
}
@Override
public String toString(){
return "SpawnGroup{" +
"type=" + type +
", end=" + end +
", begin=" + begin +
", spacing=" + spacing +
", max=" + max +
", unitScaling=" + unitScaling +
", unitAmount=" + unitAmount +
", weapon=" + weapon +
", effect=" + effect +
", items=" + items +
'}';
}
}

View File

@@ -0,0 +1,23 @@
package io.anuke.mindustry.game;
import io.anuke.annotations.Annotations.Serialize;
import io.anuke.arc.collection.ObjectIntMap;
import io.anuke.mindustry.type.Item;
@Serialize
public class Stats{
/**Items delivered to global resoure counter. Zones only.*/
public ObjectIntMap<Item> itemsDelivered = new ObjectIntMap<>();
/**Enemy (red team) units destroyed.*/
public int enemyUnitsDestroyed;
/**Total waves lasted.*/
public int wavesLasted;
/**Total (ms) time lasted in this save/zone.*/
public long timeLasted;
/**Friendly buildings fully built.*/
public int buildingsBuilt;
/**Friendly buildings fully deconstructed.*/
public int buildingsDeconstructed;
/**Friendly buildings destroyed.*/
public int buildingsDestroyed;
}

View File

@@ -3,8 +3,6 @@ package io.anuke.mindustry.game;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.layout.Table;
import static io.anuke.mindustry.Vars.data;
/**Base interface for an unlockable content type.*/
public abstract class UnlockableContent extends MappableContent{
/**Returns the localized name of this content.*/
@@ -28,24 +26,4 @@ public abstract class UnlockableContent extends MappableContent{
public boolean alwaysUnlocked(){
return false;
}
/**Lists the content that must be unlocked in order for this specific content to become unlocked. May return null.*/
public UnlockableContent[] getDependencies(){
return null;
}
/**Returns whether dependencies are satisfied for unlocking this content.*/
public boolean canBeUnlocked(){
UnlockableContent[] depend = getDependencies();
if(depend == null){
return true;
}else{
for(UnlockableContent cont : depend){
if(!data.isUnlocked(cont)){
return false;
}
}
return true;
}
}
}

View File

@@ -8,9 +8,11 @@ import io.anuke.mindustry.content.Weapons;
import io.anuke.mindustry.type.ItemStack;
public class Waves{
private static Array<SpawnGroup> spawns;
public static Array<SpawnGroup> getSpawns(){
return Array.with(
public static Array<SpawnGroup> getDefaultSpawns(){
if(spawns == null){
spawns = Array.with(
new SpawnGroup(UnitTypes.dagger){{
end = 8;
unitScaling = 3;
@@ -82,7 +84,6 @@ public class Waves{
begin = 82;
spacing = 3;
unitAmount = 4;
groupAmount = 2;
unitScaling = 3;
effect = StatusEffects.overdrive;
}},
@@ -108,7 +109,6 @@ public class Waves{
begin = 35;
spacing = 3;
unitAmount = 4;
groupAmount = 2;
effect = StatusEffects.overdrive;
items = new ItemStack(Items.blastCompound, 60);
end = 60;
@@ -118,7 +118,6 @@ public class Waves{
begin = 42;
spacing = 3;
unitAmount = 4;
groupAmount = 2;
effect = StatusEffects.overdrive;
items = new ItemStack(Items.pyratite, 100);
end = 130;
@@ -137,7 +136,6 @@ public class Waves{
unitAmount = 4;
unitScaling = 3;
spacing = 5;
groupAmount = 2;
effect = StatusEffects.overdrive;
max = 8;
}},
@@ -147,7 +145,6 @@ public class Waves{
unitAmount = 4;
unitScaling = 3;
spacing = 5;
groupAmount = 2;
max = 8;
}},
@@ -168,16 +165,17 @@ public class Waves{
max = 8;
end = 74;
}}
);
);
}
return spawns;
}
public static void testWaves(int from, int to){
Array<SpawnGroup> spawns = getSpawns();
public static void testWaves(Array<SpawnGroup> spawns, int from, int to){
for(int i = from; i <= to; i++){
System.out.print(i + ": ");
int total = 0;
for(SpawnGroup spawn : spawns){
int a = spawn.getUnitsSpawned(i) * spawn.getGroupsSpawned(i);
int a = spawn.getUnitsSpawned(i);
total += a;
if(a > 0){

View File

@@ -4,11 +4,14 @@ import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.Sort;
import io.anuke.arc.entities.EntityDraw;
import io.anuke.arc.entities.EntityGroup;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.glutils.FrameBuffer;
import io.anuke.arc.util.Tmp;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.game.Team;
@@ -57,7 +60,7 @@ public class BlockRenderer{
public void drawShadows(){
if(disableShadows) return;
if(shadows.getWidth() != Core.graphics.getWidth() || shadows.getHeight() != Core.graphics.getHeight()){
if(!Core.graphics.isHidden() && (shadows.getWidth() != Core.graphics.getWidth() || shadows.getHeight() != Core.graphics.getHeight())){
shadows.resize(Core.graphics.getWidth(), Core.graphics.getHeight());
}
@@ -70,7 +73,16 @@ public class BlockRenderer{
shadows.begin();
Core.graphics.clear(Color.CLEAR);
Draw.color(shadowColor);
floor.beginDraw();
floor.drawLayer(CacheLayer.walls);
floor.endDraw();
drawBlocks(Layer.shadow);
EntityDraw.drawWith(playerGroup, player -> !player.isDead(), Unit::draw);
for(EntityGroup group : unitGroups){
EntityDraw.drawWith(group, unit -> !unit.isDead(), Unit::draw);
}
Draw.color();
Draw.flush();
shadows.end();
@@ -110,11 +122,11 @@ public class BlockRenderer{
Tile tile = world.rawTile(x, y);
Block block = tile.block();
if(!expanded && block != Blocks.air && world.isAccessible(x, y)){
if(!expanded && block != Blocks.air && block.cacheLayer == CacheLayer.normal && world.isAccessible(x, y)){
tile.block().drawShadow(tile);
}
if(block != Blocks.air){
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal){
if(!expanded){
addRequest(tile, Layer.shadow);
addRequest(tile, Layer.block);
@@ -139,6 +151,10 @@ public class BlockRenderer{
lastCamY = avgy;
lastRangeX = rangex;
lastRangeY = rangey;
floor.beginDraw();
floor.drawLayer(CacheLayer.walls);
floor.endDraw();
}
public void drawBlocks(Layer stopAt){
@@ -154,8 +170,13 @@ public class BlockRenderer{
if(req.layer == Layer.shadow){
block.drawShadow(req.tile);
}else if(req.layer == Layer.block){
}else if(req.layer == Layer.block){
block.draw(req.tile);
if(block.synthetic() && req.tile.getTeam() != players[0].getTeam()){
Draw.color(req.tile.getTeam().color);
Draw.rect("block-border", req.tile.drawx() - block.size * tilesize/2f + 4, req.tile.drawy() - block.size * tilesize/2f + 4);
Draw.color();
}
}else if(req.layer == block.layer){
block.drawLayer(req.tile);
}else if(req.layer == block.layer2){

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.graphics;
//TODO implement effects again
public enum CacheLayer{
water{
},
@@ -9,7 +10,9 @@ public enum CacheLayer{
},
space{
},
normal;
normal,
walls{ //TODO implement walls
};
public void begin(){

View File

@@ -22,7 +22,8 @@ import io.anuke.mindustry.world.blocks.Floor;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
public class FloorRenderer{
private final static int chunksize = 64;
@@ -156,7 +157,11 @@ public class FloorRenderer{
Tile tile = world.tile(tilex, tiley);
if(tile != null){
used.add(tile.floor().cacheLayer);
if(tile.block().cacheLayer != CacheLayer.normal){
used.add(tile.block().cacheLayer);
}else{
used.add(tile.floor().cacheLayer);
}
}
}
}
@@ -183,7 +188,9 @@ public class FloorRenderer{
floor = tile.floor();
}
if(floor.cacheLayer == layer){
if(tile.block().cacheLayer == layer && layer == CacheLayer.walls){
tile.block().draw(tile);
}else if(floor.cacheLayer == layer && tile.block().cacheLayer != CacheLayer.walls){
floor.draw(tile);
}
}

View File

@@ -40,7 +40,7 @@ public class IndexedRenderer implements Disposable{
program.setUniformMatrix4("u_projTrans", BatchShader.copyTransform(combined));
program.setUniformi("u_texture", 0);
mesh.render(program, GL20.GL_TRIANGLES, 0, vertices.length / 5);
mesh.render(program, GL20.GL_TRIANGLES, 0, vertices.length / vsize);
program.end();
}
@@ -187,14 +187,6 @@ public class IndexedRenderer implements Disposable{
return transMatrix;
}
public void setTransformMatrix(Matrix3 matrix){
transMatrix = matrix;
}
public Matrix3 getProjectionMatrix(){
return projMatrix;
}
public void setProjectionMatrix(Matrix3 matrix){
projMatrix = matrix;
}

View File

@@ -88,6 +88,8 @@ public class MinimapRenderer implements Disposable{
}
Draw.color();
ScissorStack.popScissors();
}
public TextureRegion getRegion(){

View File

@@ -89,7 +89,7 @@ public class OverlayRenderer{
Draw.reset();
//draw selected block bars and info
if(input.recipe == null && !Core.scene.hasMouse()){
if(input.block == null && !Core.scene.hasMouse()){
Vector2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
Tile tile = world.tileWorld(vec.x, vec.y);

View File

@@ -38,13 +38,11 @@ public class Palette{
stoneGray = Color.valueOf("8f8f8f"),
portalLight = Color.valueOf("9054ea"),
portal = Color.valueOf("6344d7"),
portalDark = Color.valueOf("3f3dac"),
health = Color.valueOf("ff341c"),
heal = Color.valueOf("98ffa9"),
bar = Color.SLATE,
accent = Color.valueOf("ffd37f"),
locked = Color.valueOf("989aa4"),
accentBack = Color.valueOf("d4816b"),
place = Color.valueOf("6335f8"),
remove = Color.valueOf("e55454"),
@@ -54,7 +52,7 @@ public class Palette{
range = Color.valueOf("f4ba6e"),
power = Color.valueOf("fbad67"),
powerLight = Color.valueOf("fbd367"),
placing = Color.valueOf("616161"),
placing = accent,
lightTrail = Color.valueOf("ffe2a9"),

View File

@@ -15,6 +15,7 @@ import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult;
import io.anuke.mindustry.input.PlaceUtils.NormalizeResult;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
@@ -45,13 +46,10 @@ public class DesktopInput extends InputHandler{
if(validPlace(x, y, block, rotation)){
Draw.color();
TextureRegion[] regions = block.getBlockIcon();
for(TextureRegion region : regions){
Draw.rect(region, x * tilesize + block.offset(), y * tilesize + block.offset(),
region.getWidth() * selectScale * Draw.scl,
region.getHeight() * selectScale * Draw.scl, block.rotate ? rotation * 90 : 0);
}
TextureRegion region = block.icon(Icon.full);
Draw.rect(region, x * tilesize + block.offset(), y * tilesize + block.offset(),
region.getWidth() * selectScale * Draw.scl,
region.getHeight() * selectScale * Draw.scl, block.rotate ? rotation * 90 : 0);
}else{
Draw.color(Palette.removeBack);
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f);
@@ -62,7 +60,7 @@ public class DesktopInput extends InputHandler{
@Override
public boolean isDrawing(){
return mode != none || recipe != null;
return mode != none || block != null;
}
@Override
@@ -72,22 +70,30 @@ public class DesktopInput extends InputHandler{
int cursorY = tileY(Core.input.mouseY());
//draw selection(s)
if(mode == placing && recipe != null){
if(mode == placing && block != null){
NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, true, maxLength);
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = selectX + i * Mathf.sign(cursorX - selectX) * Mathf.num(result.isX());
int y = selectY + i * Mathf.sign(cursorY - selectY) * Mathf.num(!result.isX());
if(i + recipe.result.size > result.getLength() && recipe.result.rotate){
Draw.color(!validPlace(x, y, recipe.result, result.rotation) ? Palette.remove : Palette.placeRotate);
Draw.rect(Core.atlas.find("place-arrow"), x * tilesize + recipe.result.offset(),
y * tilesize + recipe.result.offset(),
Core.atlas.find("place-arrow").getWidth() * Draw.scl, Core.atlas.find("place-arrow").getHeight() * Draw.scl,
x * tilesize + recipe.result.offset()/2f, 0f, result.rotation * 90 - 90);
if(i + block.size > result.getLength() && block.rotate){
Draw.color(!validPlace(x, y, block, result.rotation) ? Palette.removeBack : Palette.accentBack);
Draw.rect(Core.atlas.find("place-arrow"),
x * tilesize + block.offset(),
y * tilesize + block.offset() - 1,
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, result.rotation * 90 - 90);
Draw.color(!validPlace(x, y, block, result.rotation) ? Palette.remove : Palette.accent);
Draw.rect(Core.atlas.find("place-arrow"),
x * tilesize + block.offset(),
y * tilesize + block.offset(),
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, result.rotation * 90 - 90);
}
drawPlace(x, y, recipe.result, result.rotation);
drawPlace(x, y, block, result.rotation);
}
Draw.reset();
@@ -113,16 +119,23 @@ public class DesktopInput extends InputHandler{
Draw.color(Palette.remove);
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
}else if(isPlacing()){
if(recipe.result.rotate){
Draw.color(!validPlace(cursorX, cursorY, recipe.result, rotation) ? Palette.remove : Palette.placeRotate);
Draw.rect(Core.atlas.find("place-arrow"), cursorX * tilesize + recipe.result.offset(),
cursorY * tilesize + recipe.result.offset(),
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl,
cursorX * tilesize + recipe.result.offset()/2f, 0, rotation * 90 - 90);
if(block.rotate){
Draw.color(!validPlace(cursorX, cursorY, block, rotation) ? Palette.removeBack : Palette.accentBack);
Draw.rect(Core.atlas.find("place-arrow"),
cursorX * tilesize + block.offset(),
cursorY * tilesize + block.offset() - 1,
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90);
Draw.color(!validPlace(cursorX, cursorY, block, rotation) ? Palette.remove : Palette.accent);
Draw.rect(Core.atlas.find("place-arrow"),
cursorX * tilesize + block.offset(),
cursorY * tilesize + block.offset(),
Core.atlas.find("place-arrow").getWidth() * Draw.scl,
Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90);
}
drawPlace(cursorX, cursorY, recipe.result, rotation);
recipe.result.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, recipe.result, rotation));
drawPlace(cursorX, cursorY, block, rotation);
block.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, block, rotation));
}
Draw.reset();
@@ -167,7 +180,7 @@ public class DesktopInput extends InputHandler{
selectScale = 0f;
}
rotation = Mathf.mod(rotation + (int) Core.input.axis(Binding.rotate), 4);
rotation = Mathf.mod(rotation + (int) Core.input.axisTap(Binding.rotate), 4);
Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY());
@@ -221,13 +234,13 @@ public class DesktopInput extends InputHandler{
}else if(!ui.chatfrag.chatOpen()){ //if it's out of bounds, shooting is just fine
player.isShooting = true;
}
}else if(Core.input.keyTap(Binding.deselect) && (recipe != null || mode != none || player.isBuilding()) &&
}else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) &&
!(player.getCurrentRequest() != null && player.getCurrentRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){
if(recipe == null){
if(block == null){
player.clearBuilding();
}
recipe = null;
block = null;
mode = none;
}else if(Core.input.keyTap(Binding.break_block) && !Core.scene.hasMouse()){
//is recalculated because setting the mode to breaking removes potential multiblock cursor offset
@@ -239,10 +252,10 @@ public class DesktopInput extends InputHandler{
if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){
if(mode == placing && recipe != null){ //touch up while placing, place everything in selection
if(mode == placing && block != null){ //touch up while placing, place everything in selection
NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, true, maxLength);
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = selectX + i * Mathf.sign(cursorX - selectX) * Mathf.num(result.isX());
int y = selectY + i * Mathf.sign(cursorY - selectY) * Mathf.num(!result.isX());
@@ -286,11 +299,6 @@ public class DesktopInput extends InputHandler{
return !controlling ? Core.input.mouseY() : controly;
}
@Override
public boolean isCursorVisible(){
return controlling;
}
@Override
public void updateController(){

View File

@@ -21,7 +21,6 @@ import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.ValidateException;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.fragments.OverlayFragment;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Build;
@@ -41,7 +40,7 @@ public abstract class InputHandler implements InputProcessor{
public final Player player;
public final OverlayFragment frag = new OverlayFragment(this);
public Recipe recipe;
public Block block;
public int rotation;
public boolean droppingItem;
@@ -130,14 +129,6 @@ public abstract class InputHandler implements InputProcessor{
return Core.input.mouseY();
}
public void resetCursor(){
}
public boolean isCursorVisible(){
return false;
}
public void buildUI(Table table){
}
@@ -162,7 +153,7 @@ public abstract class InputHandler implements InputProcessor{
boolean tileTapped(Tile tile){
tile = tile.target();
boolean consumed = false, showedInventory = false, showedConsume = false;
boolean consumed = false, showedInventory = false;
//check if tapped block is configurable
if(tile.block().configurable && tile.getTeam() == player.getTeam()){
@@ -207,7 +198,7 @@ public abstract class InputHandler implements InputProcessor{
if(!consumed && player.isBuilding()){
player.clearBuilding();
recipe = null;
block = null;
return true;
}
@@ -239,9 +230,9 @@ public abstract class InputHandler implements InputProcessor{
boolean canMine(Tile tile){
return !Core.scene.hasMouse()
&& tile.floor().drops != null && tile.floor().drops.item.hardness <= player.mech.drillPower
&& tile.floor().itemDrop != null && tile.floor().itemDrop.hardness <= player.mech.drillPower
&& !tile.floor().playerUnmineable
&& player.inventory.canAcceptItem(tile.floor().drops.item)
&& player.inventory.canAcceptItem(tile.floor().itemDrop)
&& tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= Player.mineDistance;
}
@@ -253,7 +244,7 @@ public abstract class InputHandler implements InputProcessor{
int tileX(float cursorX){
Vector2 vec = Core.input.mouseWorld(cursorX, 0);
if(selectedBlock()){
vec.sub(recipe.result.offset(), recipe.result.offset());
vec.sub(block.offset(), block.offset());
}
return world.toTile(vec.x);
}
@@ -261,7 +252,7 @@ public abstract class InputHandler implements InputProcessor{
int tileY(float cursorY){
Vector2 vec = Core.input.mouseWorld(0, cursorY);
if(selectedBlock()){
vec.sub(recipe.result.offset(), recipe.result.offset());
vec.sub(block.offset(), block.offset());
}
return world.toTile(vec.y);
}
@@ -271,7 +262,7 @@ public abstract class InputHandler implements InputProcessor{
}
public boolean isPlacing(){
return recipe != null;
return block != null;
}
public float mouseAngle(float x, float y){
@@ -284,7 +275,7 @@ public abstract class InputHandler implements InputProcessor{
}
public boolean canShoot(){
return recipe == null && !Core.scene.hasMouse() && !onConfigurable() && !isDroppingItem();
return block == null && !Core.scene.hasMouse() && !onConfigurable() && !isDroppingItem();
}
public boolean onConfigurable(){
@@ -317,8 +308,8 @@ public abstract class InputHandler implements InputProcessor{
}
public void tryPlaceBlock(int x, int y){
if(recipe != null && validPlace(x, y, recipe.result, rotation) && cursorNear()){
placeBlock(x, y, recipe, rotation);
if(block != null && validPlace(x, y, block, rotation) && cursorNear()){
placeBlock(x, y, block, rotation);
}
}
@@ -329,20 +320,16 @@ public abstract class InputHandler implements InputProcessor{
}
public boolean validPlace(int x, int y, Block type, int rotation){
for(Tile tile : state.teams.get(player.getTeam()).cores){
return Build.validPlace(player.getTeam(), x, y, type, rotation) &&
Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}
return false;
return Build.validPlace(player.getTeam(), x, y, type, rotation) &&
Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}
public boolean validBreak(int x, int y){
return Build.validBreak(player.getTeam(), x, y) && Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}
public void placeBlock(int x, int y, Recipe recipe, int rotation){
player.addBuildRequest(new BuildRequest(x, y, rotation, recipe));
public void placeBlock(int x, int y, Block block, int rotation){
player.addBuildRequest(new BuildRequest(x, y, rotation, block));
}
public void breakBlock(int x, int y){

View File

@@ -30,9 +30,9 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult;
import io.anuke.mindustry.input.PlaceUtils.NormalizeResult;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
@@ -72,7 +72,7 @@ public class MobileInput extends InputHandler implements GestureListener{
/** Current place mode. */
private PlaceMode mode = none;
/** Whether no recipe was available when switching to break mode. */
private Recipe lastRecipe;
private Block lastBlock;
/** Last placed request. Used for drawing block overlay. */
private PlaceRequest lastPlaced;
@@ -120,8 +120,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(other == null || req.remove) continue;
r1.setSize(req.recipe.result.size * tilesize);
r1.setCenter(other.worldx() + req.recipe.result.offset(), other.worldy() + req.recipe.result.offset());
r1.setSize(req.block.size * tilesize);
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
if(r2.overlaps(r1)){
return true;
@@ -141,8 +141,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(other == null) continue;
if(!req.remove){
r1.setSize(req.recipe.result.size * tilesize);
r1.setCenter(other.worldx() + req.recipe.result.offset(), other.worldy() + req.recipe.result.offset());
r1.setSize(req.block.size * tilesize);
r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
if(r2.overlaps(r1)){
return req;
@@ -170,18 +170,16 @@ public class MobileInput extends InputHandler implements GestureListener{
if(!request.remove){
//draw placing request
float offset = request.recipe.result.offset();
TextureRegion[] regions = request.recipe.result.getBlockIcon();
float offset = request.block.offset();
TextureRegion region = request.block.icon(Icon.full);
Draw.alpha(Mathf.clamp((1f - request.scale) / 0.5f));
Draw.tint(Color.WHITE, Palette.breakInvalid, request.redness);
for(TextureRegion region : regions){
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
region.getWidth() * request.scale * Draw.scl,
region.getHeight() * request.scale * Draw.scl,
request.recipe.result.rotate ? request.rotation * 90 : 0);
}
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
region.getWidth() * request.scale * Draw.scl,
region.getHeight() * request.scale * Draw.scl,
request.block.rotate ? request.rotation * 90 : 0);
}else{
float rad = (tile.block().size * tilesize / 2f - 1) * request.scale;
Draw.alpha(0f);
@@ -220,8 +218,8 @@ public class MobileInput extends InputHandler implements GestureListener{
table.left().margin(0f).defaults().size(48f);
table.addImageButton("icon-break", "clear-toggle-partial", 16 * 2f, () -> {
mode = mode == breaking ? recipe == null ? none : placing : breaking;
lastRecipe = recipe;
mode = mode == breaking ? block == null ? none : placing : breaking;
lastBlock = block;
if(mode == breaking){
showGuide("deconstruction");
}
@@ -231,13 +229,13 @@ public class MobileInput extends InputHandler implements GestureListener{
table.addImageButton("icon-cancel", "clear-partial", 16 * 2f, () -> {
player.clearBuilding();
mode = none;
recipe = null;
}).visible(() -> player.isBuilding() || recipe != null || mode == breaking);
block = null;
}).visible(() -> player.isBuilding() || block != null || mode == breaking);
//rotate button
table.addImageButton("icon-arrow", "clear-partial", 16 * 2f, () -> rotation = Mathf.mod(rotation + 1, 4))
.update(i -> i.getImage().setRotationOrigin(rotation * 90, Align.center))
.visible(() -> recipe != null && recipe.result.rotate);
.visible(() -> block != null && block.rotate);
//confirm button
table.addImageButton("icon-check", "clear-partial", 16 * 2f, () -> {
@@ -248,10 +246,10 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile != null){
if(!request.remove){
rotation = request.rotation;
Recipe before = recipe;
recipe = request.recipe;
Block before = block;
block = request.block;
tryPlaceBlock(tile.x, tile.y);
recipe = before;
block = before;
}else{
tryBreakBlock(tile.x, tile.y);
}
@@ -300,7 +298,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile == null) continue;
if((!request.remove && validPlace(tile.x, tile.y, request.recipe.result, request.rotation))
if((!request.remove && validPlace(tile.x, tile.y, request.block, request.rotation))
|| (request.remove && validBreak(tile.x, tile.y))){
request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f);
request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f);
@@ -313,8 +311,8 @@ public class MobileInput extends InputHandler implements GestureListener{
drawRequest(request);
//draw last placed request
if(!request.remove && request == lastPlaced && request.recipe != null){
request.recipe.result.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.recipe.result, rotation));
if(!request.remove && request == lastPlaced && request.block != null){
request.block.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.block, rotation));
}
}
@@ -328,34 +326,32 @@ public class MobileInput extends InputHandler implements GestureListener{
int tileY = tileY(Core.input.mouseY());
//draw placing
if(mode == placing && recipe != null){
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tileX, tileY, true, maxLength, lineScale);
if(mode == placing && block != null){
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(block, lineStartX, lineStartY, tileX, tileY, true, maxLength, lineScale);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, true, maxLength);
//go through each cell and draw the block to place if valid
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = lineStartX + i * Mathf.sign(tileX - lineStartX) * Mathf.num(result.isX());
int y = lineStartY + i * Mathf.sign(tileY - lineStartY) * Mathf.num(!result.isX());
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)){
if(!checkOverlapPlacement(x, y, block) && validPlace(x, y, block, result.rotation)){
Draw.color();
TextureRegion[] regions = recipe.result.getBlockIcon();
TextureRegion region = block.icon(Icon.full);
for(TextureRegion region : regions){
Draw.rect(region, x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(),
region.getWidth() * lineScale * Draw.scl,
region.getHeight() * lineScale * Draw.scl,
recipe.result.rotate ? result.rotation * 90 : 0);
}
Draw.rect(region, x * tilesize + block.offset(), y * tilesize + block.offset(),
region.getWidth() * lineScale * Draw.scl,
region.getHeight() * lineScale * Draw.scl,
block.rotate ? result.rotation * 90 : 0);
}else{
Draw.color(Palette.removeBack);
Lines.square(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset() - 1, recipe.result.size * tilesize / 2f);
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f);
Draw.color(Palette.remove);
Lines.square(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(), recipe.result.size * tilesize / 2f);
Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f);
}
}
@@ -444,7 +440,7 @@ public class MobileInput extends InputHandler implements GestureListener{
int tileX = tileX(screenX);
int tileY = tileY(screenY);
if(mode == placing && recipe != null){
if(mode == placing && block != null){
//normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, true, 100);
@@ -452,12 +448,12 @@ public class MobileInput extends InputHandler implements GestureListener{
rotation = result.rotation;
//place blocks on line
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
for(int i = 0; i <= result.getLength(); i += block.size){
int x = lineStartX + i * Mathf.sign(tileX - lineStartX) * Mathf.num(result.isX());
int y = lineStartY + i * Mathf.sign(tileY - lineStartY) * Mathf.num(!result.isX());
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result, result.rotation)){
PlaceRequest request = new PlaceRequest(x * tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(), recipe, result.rotation);
if(!checkOverlapPlacement(x, y, block) && validPlace(x, y, block, result.rotation)){
PlaceRequest request = new PlaceRequest(x * tilesize + block.offset(), y * tilesize + block.offset(), block, result.rotation);
request.scale = 1f;
selection.add(request);
}
@@ -520,8 +516,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if(mode == breaking){
Effects.effect(Fx.tapBlock, cursor.worldx(), cursor.worldy(), 1f);
}else if(recipe != null){
Effects.effect(Fx.tapBlock, cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe.result.size);
}else if(block != null){
Effects.effect(Fx.tapBlock, cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block.size);
}
return false;
@@ -544,9 +540,9 @@ public class MobileInput extends InputHandler implements GestureListener{
//remove if request present
if(hasRequest(cursor)){
removeRequest(getRequest(cursor));
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, recipe.result, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){
//add to selection queue if it's a valid place position
selection.add(lastPlaced = new PlaceRequest(cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe, rotation));
selection.add(lastPlaced = new PlaceRequest(cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block, rotation));
}else if(mode == breaking && validBreak(cursor.target().x, cursor.target().y) && !hasRequest(cursor.target())){
//add to selection queue if it's a valid BREAK position
cursor = cursor.target();
@@ -593,27 +589,27 @@ public class MobileInput extends InputHandler implements GestureListener{
selection.clear();
}
if(lineMode && mode == placing && recipe == null){
if(lineMode && mode == placing && block == null){
lineMode = false;
}
//if there is no mode and there's a recipe, switch to placing
if(recipe != null && mode == none){
if(block != null && mode == none){
mode = placing;
}
if(recipe != null){
if(block != null){
showGuide("construction");
}
if(recipe == null && mode == placing){
if(block == null && mode == placing){
mode = none;
}
//automatically switch to placing after a new recipe is selected
if(lastRecipe != recipe && mode == breaking && recipe != null){
if(lastBlock != block && mode == breaking && block != null){
mode = placing;
lastRecipe = recipe;
lastBlock = block;
}
if(lineMode){
@@ -715,7 +711,7 @@ public class MobileInput extends InputHandler implements GestureListener{
class PlaceRequest{
float x, y;
Recipe recipe;
Block block;
int rotation;
boolean remove;
@@ -723,10 +719,10 @@ public class MobileInput extends InputHandler implements GestureListener{
float scale;
float redness;
PlaceRequest(float x, float y, Recipe recipe, int rotation){
PlaceRequest(float x, float y, Block block, int rotation){
this.x = x;
this.y = y;
this.recipe = recipe;
this.block = block;
this.rotation = rotation;
this.remove = false;
}
@@ -738,7 +734,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}
Tile tile(){
return world.tileWorld(x - (recipe == null ? 0 : recipe.result.offset()), y - (recipe == null ? 0 : recipe.result.offset()));
return world.tileWorld(x - (block == null ? 0 : block.offset()), y - (block == null ? 0 : block.offset()));
}
}
}

View File

@@ -9,12 +9,14 @@ import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.traits.SaveTrait;
import io.anuke.mindustry.entities.traits.TypeTrait;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Serialization;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -33,11 +35,11 @@ public abstract class SaveFileVersion{
long time = stream.readLong();
long playtime = stream.readLong();
int build = stream.readInt();
byte mode = stream.readByte();
Rules rules = Serialization.readRules(stream);
String map = stream.readUTF();
int wave = stream.readInt();
byte difficulty = stream.readByte();
return new SaveMeta(version, time, playtime, build, mode, map, wave, Difficulty.values()[difficulty]);
return new SaveMeta(version, time, playtime, build, map, wave, rules);
}
public void writeMap(DataOutputStream stream) throws IOException{
@@ -99,7 +101,7 @@ public abstract class SaveFileVersion{
Tile tile = new Tile(x, y, floorid, wallid);
if(wallid == Blocks.blockpart.id){
if(wallid == Blocks.part.id){
tile.link = stream.readByte();
}else if(tile.entity != null){
byte tr = stream.readByte();
@@ -122,7 +124,7 @@ public abstract class SaveFileVersion{
tile.entity.readConfig(stream);
tile.entity.read(stream);
if(tile.block() == Blocks.core){
if(tile.block() instanceof CoreBlock){
state.teams.get(t).cores.add(tile);
}
}else if(wallid == 0){

View File

@@ -43,7 +43,7 @@ public class SaveIO{
}
}
public static void loadFromSlot(int slot){
public static void loadFromSlot(int slot) throws SaveException{
load(fileFor(slot));
}
@@ -117,40 +117,41 @@ public class SaveIO{
}
}
public static void load(FileHandle file){
public static void load(FileHandle file) throws SaveException{
try{
//try and load; if any exception at all occurs
load(new InflaterInputStream(file.read()));
}catch(RuntimeException e){
}catch(SaveException e){
e.printStackTrace();
FileHandle backup = file.sibling(file.name() + "-backup." + file.extension());
if(backup.exists()){
load(new InflaterInputStream(backup.read()));
}else{
throw new RuntimeException(e);
throw new SaveException(e.getCause());
}
}
}
public static void load(InputStream is){
logic.reset();
DataInputStream stream;
try{
stream = new DataInputStream(is);
public static void load(InputStream is) throws SaveException{
try(DataInputStream stream = new DataInputStream(is)){
logic.reset();
int version = stream.readInt();
SaveFileVersion ver = versions.get(version);
ver.read(stream);
stream.close();
}catch(Exception e){
content.setTemporaryMapper(null);
throw new RuntimeException(e);
throw new SaveException(e);
}
}
public static SaveFileVersion getVersion(){
return versionArray.peek();
}
public static class SaveException extends RuntimeException{
public SaveException(Throwable throwable){
super(throwable);
}
}
}

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.io;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.maps.Map;
import static io.anuke.mindustry.Vars.world;
@@ -12,15 +12,15 @@ public class SaveMeta{
public long timePlayed;
public Map map;
public int wave;
public Difficulty difficulty;
public Rules rules;
public SaveMeta(int version, long timestamp, long timePlayed, int build, int mode, String map, int wave, Difficulty difficulty){
public SaveMeta(int version, long timestamp, long timePlayed, int build, String map, int wave, Rules rules){
this.version = version;
this.build = build;
this.timestamp = timestamp;
this.timePlayed = timePlayed;
this.map = world.maps.getByName(map);
this.wave = wave;
this.difficulty = difficulty;
this.rules = rules;
}
}

View File

@@ -1,8 +1,11 @@
package io.anuke.mindustry.io;
import io.anuke.arc.graphics.Color;
import io.anuke.annotations.Annotations.ReadClass;
import io.anuke.annotations.Annotations.WriteClass;
import io.anuke.arc.entities.Effects;
import io.anuke.arc.entities.Effects.Effect;
import io.anuke.arc.entities.Entities;
import io.anuke.arc.graphics.Color;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
@@ -12,7 +15,6 @@ import io.anuke.mindustry.entities.traits.CarriableTrait;
import io.anuke.mindustry.entities.traits.CarryTrait;
import io.anuke.mindustry.entities.traits.ShooterTrait;
import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.entities.units.UnitCommand;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.mindustry.net.Packets.KickReason;
@@ -20,9 +22,6 @@ import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Pos;
import io.anuke.mindustry.world.Tile;
import io.anuke.arc.entities.Effects;
import io.anuke.arc.entities.Effects.Effect;
import io.anuke.arc.entities.Entities;
import java.io.DataInput;
import java.io.DataOutput;
@@ -173,7 +172,7 @@ public class TypeIO{
buffer.put(request.breaking ? (byte) 1 : 0);
buffer.putInt(Pos.get(request.x, request.y));
if(!request.breaking){
buffer.put(request.recipe.id);
buffer.put(request.block.id);
buffer.put((byte) request.rotation);
}
}
@@ -191,9 +190,9 @@ public class TypeIO{
if(type == 1){ //remove
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position));
}else{ //place
byte recipe = buffer.get();
byte block = buffer.get();
byte rotation = buffer.get();
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.recipe(recipe));
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
}
reqs[i] = (currentRequest);
@@ -232,16 +231,6 @@ public class TypeIO{
return AdminAction.values()[buffer.get()];
}
@WriteClass(UnitCommand.class)
public static void writeCommand(ByteBuffer buffer, UnitCommand reason){
buffer.put((byte) reason.ordinal());
}
@ReadClass(UnitCommand.class)
public static UnitCommand readCommand(ByteBuffer buffer){
return UnitCommand.values()[buffer.get()];
}
@WriteClass(Effect.class)
public static void writeEffect(ByteBuffer buffer, Effect effect){
buffer.putShort((short) effect.id);
@@ -313,16 +302,6 @@ public class TypeIO{
return id == -1 ? null : content.item(id);
}
@WriteClass(Recipe.class)
public static void writeRecipe(ByteBuffer buffer, Recipe recipe){
buffer.put(recipe.id);
}
@ReadClass(Recipe.class)
public static Recipe readRecipe(ByteBuffer buffer){
return content.recipe(buffer.get());
}
@WriteClass(String.class)
public static void writeString(ByteBuffer buffer, String string){
if(string != null){

View File

@@ -5,6 +5,8 @@ import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Serialization;
import io.anuke.mindustry.io.SaveFileVersion;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Zone;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -26,6 +28,10 @@ public class Save16 extends SaveFileVersion{
//general state
state.rules = Serialization.readRules(stream);
//load zone spawn patterns if applicable
if(content.getByID(ContentType.zone, state.rules.zone) != null){
state.rules.spawns = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get().spawns;
}
String mapname = stream.readUTF();
Map map = world.maps.getByName(mapname);
if(map == null) map = new Map("unknown", 1, 1);
@@ -36,13 +42,11 @@ public class Save16 extends SaveFileVersion{
state.wave = wave;
state.wavetime = wavetime;
state.stats = Serialization.readStats(stream);
content.setTemporaryMapper(readContentHeader(stream));
world.spawner.read(stream);
readEntities(stream);
readMap(stream);
}
@@ -61,9 +65,9 @@ public class Save16 extends SaveFileVersion{
stream.writeInt(state.wave); //wave
stream.writeFloat(state.wavetime); //wave countdown
writeContentHeader(stream);
Serialization.writeStats(stream, state.stats);
world.spawner.write(stream); //spawnes
writeContentHeader(stream);
//--ENTITIES--

View File

@@ -63,14 +63,7 @@ public class Maps implements Disposable{
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
try(DataInputStream ds = new DataInputStream(file.read())) {
MapMeta meta = MapIO.readMapMeta(ds);
Map map = new Map(name, meta, false, file::read);
if (!headless){
map.texture = new Texture(MapIO.generatePixmap(MapIO.readTileData(ds, meta, true)));
}
return map;
return new Map(name, MapIO.readMapMeta(ds), false, file::read);
}catch(IOException e){
throw new RuntimeException(e);
}

View File

@@ -53,7 +53,7 @@ public class BasicGenerator extends RandomGenerator{
}
if(rocks > 0.64){
block = Blocks.rocksSmall;
block = Blocks.rocks;
}
}
}

View File

@@ -12,5 +12,9 @@ public abstract class Generator{
public Generator(){}
public void init(){
}
public abstract void generate(Tile[][] tiles);
}

View File

@@ -1,16 +1,39 @@
package io.anuke.mindustry.maps.generators;
import io.anuke.arc.collection.Array;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.util.noise.Simplex;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import static io.anuke.mindustry.Vars.world;
import static io.anuke.mindustry.Vars.*;
public class MapGenerator extends Generator{
private final Map map;
private Map map;
private String mapName;
/**How much the landscape is randomly distorted.*/
public float distortion = 3;
/**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;
public MapGenerator(String mapName){
this.mapName = mapName;
}
public MapGenerator(String mapName, int enemySpawns){
this.mapName = mapName;
this.enemySpawns = enemySpawns;
}
@Override
public void init(){
map = world.maps.loadInternalMap(mapName);
width = map.meta.width;
height = map.meta.height;
@@ -22,15 +45,60 @@ public class MapGenerator extends Generator{
data.position(0, 0);
TileDataMarker marker = data.newDataMarker();
Array<Point2> players = new Array<>();
Array<Point2> enemies = new Array<>();
for(int y = 0; y < data.height(); y++){
for(int x = 0; x < data.width(); x++){
data.read(marker);
tiles[x][y] = new Tile(x, y, marker.floor, marker.wall == Blocks.blockpart.id ? 0 : marker.wall, marker.rotation, marker.team);
if(content.block(marker.wall) instanceof CoreBlock){
players.add(new Point2(x, y));
marker.wall = 0;
}
if(enemySpawns != -1 && content.block(marker.wall) == Blocks.spawn){
enemies.add(new Point2(x, y));
marker.wall = 0;
}
tiles[x][y] = new Tile(x, y, marker.floor, marker.wall == Blocks.part.id ? 0 : marker.wall, marker.rotation, marker.team);
}
}
Simplex simplex = new Simplex(Mathf.random(99999));
for(int x = 0; x < data.width(); x++){
for(int y = 0; y < data.height(); y++){
final double scl = 10;
int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, data.width()-1);
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, data.height()-1);
if(tiles[newX][newY].block() != Blocks.spawn && !tiles[x][y].block().synthetic()&& !tiles[newX][newY].block().synthetic()){
tiles[x][y].setBlock(tiles[newX][newY].block());
}
}
}
if(enemySpawns > enemies.size){
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number.");
}
if(enemySpawns != -1){
enemies.shuffle();
for(int i = 0; i < enemySpawns; i++){
Point2 point = enemies.get(i);
tiles[point.x][point.y].setBlock(Blocks.spawn);
}
}
Point2 core = players.random();
if(core == null){
throw new IllegalArgumentException("All zone maps must have a core.");
}
//TODO set specific core block?
tiles[core.x][core.y].setBlock(Blocks.core, defaultTeam);
world.prepareTiles(tiles);
world.setMap(map);
}

View File

@@ -3,7 +3,6 @@ package io.anuke.mindustry.type;
/**Do not rearrange, ever!*/
public enum ContentType {
item,
recipe,
block,
mech,
bullet,

View File

@@ -30,7 +30,7 @@ public class Item extends UnlockableContent implements Comparable<Item>{
public float fluxiness = 0f;
/**drill hardness of the item*/
public int hardness = 0;
/**the burning color of this item*/
/**the burning color of this item. TODO unused; implement*/
public Color flameColor = Palette.darkFlame.cpy();
/**
* base material cost of this item, used for calculating place times
@@ -49,7 +49,7 @@ public class Item extends UnlockableContent implements Comparable<Item>{
if(!Core.bundle.has("item." + this.name + ".name")){
Log.err("Warning: item '" + name + "' is missing a localized name. Add the following to bundle.properties:");
Log.err("item." + this.name + ".name=" + Strings.capitalize(name.replace('-', '_')));
Log.err("item." + this.name + ".name = " + Strings.capitalize(name.replace('-', '_')));
}
}
@@ -97,11 +97,8 @@ public class Item extends UnlockableContent implements Comparable<Item>{
return ContentType.item;
}
/**Allocates a new array containing all items the generate ores.*/
public static Array<Item> getAllOres(){
Array<Item> arr = new Array<>();
for(Item item : Vars.content.items()){
if(item.genOre) arr.add(item);
}
return arr;
return Vars.content.items().select(i -> i.genOre);
}
}

View File

@@ -1,169 +0,0 @@
package io.anuke.mindustry.type;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.collection.OrderedMap;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.ui.ContentDisplay;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.ContentStatValue;
import io.anuke.mindustry.world.meta.StatValue;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.*;
public class Recipe extends UnlockableContent{
private static ObjectMap<Block, Recipe> recipeMap = new ObjectMap<>();
private static Array<Recipe> returnArray = new Array<>();
public final Block result;
public final ItemStack[] requirements;
public final Category category;
public final float cost;
public RecipeVisibility visibility = RecipeVisibility.all;
public boolean hidden;
public boolean alwaysUnlocked;
public Recipe(Category category, Block result, ItemStack... requirements){
this.result = result;
this.requirements = requirements;
this.category = category;
Arrays.sort(requirements, (a, b) -> Integer.compare(a.item.id, b.item.id));
float timeToPlace = 0f;
for(ItemStack stack : requirements){
timeToPlace += stack.amount * stack.item.cost;
}
this.cost = timeToPlace;
recipeMap.put(result, this);
}
/**Returns all non-hidden recipes in a category.*/
public static Array<Recipe> getByCategory(Category category){
returnArray.clear();
for(Recipe recipe : content.recipes()){
if(recipe.category == category && recipe.visibility.shown()){
returnArray.add(recipe);
}
}
return returnArray;
}
public static Recipe getByResult(Block block){
return recipeMap.get(block);
}
public Recipe setVisible(RecipeVisibility visibility){
this.visibility = visibility;
return this;
}
public Recipe setHidden(boolean hidden){
this.hidden = hidden;
return this;
}
public Recipe setAlwaysUnlocked(boolean unlocked){
this.alwaysUnlocked = unlocked;
return this;
}
@Override
public boolean alwaysUnlocked(){
return alwaysUnlocked;
}
@Override
public boolean isHidden(){
return !visibility.shown() || hidden;
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayRecipe(table, this);
}
@Override
public String localizedName(){
return result.formalName;
}
@Override
public TextureRegion getContentIcon(){
return result.getEditorIcon();
}
@Override
public void init(){
if(!Core.bundle.has("block." + result.name + ".name")){
Log.err("WARNING: Recipe block '{0}' does not have a formal name defined. Add the following to bundle.properties:", result.name);
Log.err("block.{0}.name={1}", result.name, Strings.capitalize(result.name.replace('-', '_')));
}/*else if(result.fullDescription == null){
Log.err("WARNING: Recipe block '{0}' does not have a description defined.", result.name);
}*/
}
@Override
public String getContentName(){
return result.name;
}
@Override
public ContentType getContentType(){
return ContentType.recipe;
}
@Override
public void onUnlock(){
for(OrderedMap<BlockStat, StatValue> map : result.stats.toMap().values()){
for(StatValue value : map.values()){
if(value instanceof ContentStatValue){
ContentStatValue stat = (ContentStatValue) value;
UnlockableContent[] content = stat.getValueContent();
for(UnlockableContent c : content){
data.unlockContent(c);
}
}
}
}
}
public enum RecipeVisibility{
mobileOnly(true, false),
desktopOnly(false, true),
all(true, true),
sandboxOnly(true, true){
@Override
public boolean usable(){
return state.rules.infiniteResources;
}
};
public final boolean mobile, desktop;
RecipeVisibility(boolean mobile, boolean desktop){
this.mobile = mobile;
this.desktop = desktop;
}
public boolean usable(){
return true;
}
public boolean shown(){
return usable() && ((Vars.mobile && mobile) || (!Vars.mobile && desktop));
}
}
}

View File

@@ -7,19 +7,42 @@ import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.maps.generators.Generator;
import io.anuke.mindustry.world.Block;
import static io.anuke.mindustry.Vars.state;
public class Zone extends UnlockableContent{
public final String name;
public final Generator generator;
public ItemStack[] deployCost = {};
public ItemStack[] startingItems = {};
public Block[] blockRequirements = {};
public ItemStack[] itemRequirements = {};
public Zone[] zoneRequirements = {};
public Supplier<Rules> rules = Rules::new;
public boolean alwaysUnlocked;
public int conditionWave = Integer.MAX_VALUE;
public Zone(String name, Generator generator){
this.name = name;
this.generator = generator;
}
/**Whether this zone has met its condition; if true, the player can leave.*/
public boolean metCondition(){
return state.wave >= conditionWave;
}
@Override
public void init(){
generator.init();
}
@Override
public boolean alwaysUnlocked(){
return alwaysUnlocked;
}
@Override
public boolean isHidden(){
return true;
@@ -46,4 +69,5 @@ public class Zone extends UnlockableContent{
public ContentType getContentType(){
return ContentType.zone;
}
}

View File

@@ -0,0 +1,82 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.Core;
import io.anuke.arc.function.FloatProvider;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.BitmapFont;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.GlyphLayout;
import io.anuke.arc.graphics.g2d.ScissorStack;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Rectangle;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.style.Drawable;
import io.anuke.arc.util.pooling.Pools;
public class Bar extends Element{
private static Rectangle scissor = new Rectangle();
private FloatProvider fraction;
private String name = "";
private float value, lastValue, blink;
private Color blinkColor = new Color();
public Bar(String name, Color color, FloatProvider fraction){
this.fraction = fraction;
this.name = Core.bundle.get(name);
this.blinkColor.set(color);
lastValue = value = fraction.get();
setColor(color);
}
public Bar(Supplier<String> name, Supplier<Color> color, FloatProvider fraction){
this.fraction = fraction;
update(() -> {
this.name = name.get();
setColor(color.get());
});
}
public Bar blink(Color color){
blinkColor.set(color);
return this;
}
@Override
public void draw(){
if(!Mathf.isEqual(lastValue, fraction.get())){
blink = 1f;
lastValue = fraction.get();
}
blink = Mathf.lerpDelta(blink, 0f, 0.2f);
value = Mathf.lerpDelta(value, fraction.get(), 0.15f);
Draw.colorl(0.1f);
Draw.drawable("bar", x, y, width, height);
Draw.color(color, blinkColor, blink);
Drawable top = Core.scene.skin.getDrawable("bar-top");
float topWidth = width * value;
if(topWidth > Core.atlas.find("bar-top").getWidth()){
top.draw(x, y, topWidth, height);
}else{
if(ScissorStack.pushScissors(scissor.set(x, y, topWidth, height))){
top.draw(x, y, Core.atlas.find("bar-top").getWidth(), height);
ScissorStack.popScissors();
}
}
Draw.color();
BitmapFont font = Core.scene.skin.getFont("default-font");
GlyphLayout lay = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
lay.setText(font, name);
font.draw(name, x + width/2f - lay.width/2f, y + height/2f + lay.height/2f + 1);
Pools.free(lay);
}
}

View File

@@ -10,8 +10,8 @@ import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.type.Mech;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.BlockStats;
import io.anuke.mindustry.world.meta.StatCategory;
@@ -19,19 +19,18 @@ import io.anuke.mindustry.world.meta.StatValue;
public class ContentDisplay{
public static void displayRecipe(Table table, Recipe recipe){
Block block = recipe.result;
public static void displayBlock(Table table, Block block){
table.table(title -> {
int size = 8 * 6;
title.addImage(Core.atlas.find("block-icon-" + block.name)).size(size);
title.addImage(block.icon(Icon.large)).size(size);
title.add("[accent]" + block.formalName).padLeft(5);
});
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(8).padLeft(0).padRight(0).fillX();
table.row();
@@ -39,7 +38,7 @@ public class ContentDisplay{
table.add(block.fullDescription).padLeft(5).padRight(5).width(400f).wrap().fillX();
table.row();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX();
table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(8).padLeft(0).padRight(0).fillX();
table.row();
}

View File

@@ -1,14 +0,0 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Stack;
public class ImageStack extends Stack{
public ImageStack(TextureRegion... regions){
for(TextureRegion region : regions){
add(new Image(region));
}
}
}

View File

@@ -0,0 +1,310 @@
package io.anuke.mindustry.ui;
import io.anuke.arc.collection.FloatArray;
import io.anuke.arc.math.geom.Rectangle;
/**
* Algorithm taken from <a href="https://github.com/abego/treelayout">TreeLayout</a>.
*/
public class TreeLayout{
public TreeLocation rootLocation = TreeLocation.top;
public TreeAlignment alignment = TreeAlignment.awayFromRoot;
public float gapBetweenLevels = 10;
public float gapBetweenNodes = 10f;
private final FloatArray sizeOfLevel = new FloatArray();
private float boundsLeft = Float.MAX_VALUE;
private float boundsRight = Float.MIN_VALUE;
private float boundsTop = Float.MAX_VALUE;
private float boundsBottom = Float.MIN_VALUE;
public void layout(TreeNode root){
firstWalk(root, null);
calcSizeOfLevels(root, 0);
secondWalk(root, -root.prelim, 0, 0);
}
private float getWidthOrHeightOfNode(TreeNode treeNode, boolean returnWidth){
return returnWidth ? treeNode.width : treeNode.height;
}
private float getNodeThickness(TreeNode treeNode){
return getWidthOrHeightOfNode(treeNode, !isLevelChangeInYAxis());
}
private float getNodeSize(TreeNode treeNode){
return getWidthOrHeightOfNode(treeNode, isLevelChangeInYAxis());
}
private boolean isLevelChangeInYAxis(){
return rootLocation == TreeLocation.top || rootLocation == TreeLocation.bottom;
}
private int getLevelChangeSign(){
return rootLocation == TreeLocation.bottom || rootLocation == TreeLocation.right ? -1 : 1;
}
private void updateBounds(TreeNode node, float centerX, float centerY){
float width = node.width;
float height = node.height;
float left = centerX - width / 2;
float right = centerX + width / 2;
float top = centerY - height / 2;
float bottom = centerY + height / 2;
if(boundsLeft > left){
boundsLeft = left;
}
if(boundsRight < right){
boundsRight = right;
}
if(boundsTop > top){
boundsTop = top;
}
if(boundsBottom < bottom){
boundsBottom = bottom;
}
}
public Rectangle getBounds(){
return new Rectangle(boundsLeft, boundsBottom, boundsRight - boundsLeft, boundsTop - boundsBottom);
}
private void calcSizeOfLevels(TreeNode node, int level){
float oldSize;
if(sizeOfLevel.size <= level){
sizeOfLevel.add(0);
oldSize = 0;
}else{
oldSize = sizeOfLevel.get(level);
}
float size = getNodeThickness(node);
if(oldSize < size){
sizeOfLevel.set(level, size);
}
if(!node.isLeaf()){
for(TreeNode child : node.children){
calcSizeOfLevels(child, level + 1);
}
}
}
public int getLevelCount(){
return sizeOfLevel.size;
}
public float getGapBetweenNodes(TreeNode a, TreeNode b){
return gapBetweenNodes;
}
public float getSizeOfLevel(int level){
if(!(level >= 0)) throw new IllegalArgumentException("level must be >= 0");
if(!(level < getLevelCount())) throw new IllegalArgumentException("level must be < levelCount");
return sizeOfLevel.get(level);
}
private TreeNode getAncestor(TreeNode node){
return node.ancestor != null ? node.ancestor : node;
}
private float getDistance(TreeNode v, TreeNode w){
float sizeOfNodes = getNodeSize(v) + getNodeSize(w);
return sizeOfNodes / 2 + getGapBetweenNodes(v, w);
}
private TreeNode nextLeft(TreeNode v){
return v.isLeaf() ? v.thread : v.children[0];
}
private TreeNode nextRight(TreeNode v){
return v.isLeaf() ? v.thread : v.children[v.children.length - 1];
}
private int getNumber(TreeNode node, TreeNode parentNode){
if(node.number == -1){
int number = 1;
for(TreeNode child : parentNode.children){
child.number = number++;
}
}
return node.number;
}
private TreeNode ancestor(TreeNode vIMinus, TreeNode parentOfV, TreeNode defaultAncestor){
TreeNode ancestor = getAncestor(vIMinus);
return ancestor.parent == parentOfV ? ancestor : defaultAncestor;
}
private void moveSubtree(TreeNode wMinus, TreeNode wPlus, TreeNode parent, float shift){
int subtrees = getNumber(wPlus, parent) - getNumber(wMinus, parent);
wPlus.change = wPlus.change - shift / subtrees;
wPlus.shift = wPlus.shift + shift;
wMinus.change = wMinus.change + shift / subtrees;
wPlus.prelim = wPlus.prelim + shift;
wPlus.mode = wPlus.mode + shift;
}
private TreeNode apportion(TreeNode v, TreeNode defaultAncestor,
TreeNode leftSibling, TreeNode parentOfV){
if(leftSibling == null){
return defaultAncestor;
}
TreeNode vOPlus = v;
TreeNode vIPlus = v;
TreeNode vIMinus = leftSibling;
TreeNode vOMinus = parentOfV.children[0];
float sIPlus = (vIPlus).mode;
float sOPlus = (vOPlus).mode;
float sIMinus = (vIMinus).mode;
float sOMinus = (vOMinus).mode;
TreeNode nextRightVIMinus = nextRight(vIMinus);
TreeNode nextLeftVIPlus = nextLeft(vIPlus);
while(nextRightVIMinus != null && nextLeftVIPlus != null){
vIMinus = nextRightVIMinus;
vIPlus = nextLeftVIPlus;
vOMinus = nextLeft(vOMinus);
vOPlus = nextRight(vOPlus);
vOPlus.ancestor = v;
float shift = (vIMinus.prelim + sIMinus)
- (vIPlus.prelim + sIPlus)
+ getDistance(vIMinus, vIPlus);
if(shift > 0){
moveSubtree(ancestor(vIMinus, parentOfV, defaultAncestor),
v, parentOfV, shift);
sIPlus = sIPlus + shift;
sOPlus = sOPlus + shift;
}
sIMinus += vIMinus.mode;
sIPlus += vIPlus.mode;
sOMinus += vOMinus.mode;
sOPlus += vOPlus.mode;
nextRightVIMinus = nextRight(vIMinus);
nextLeftVIPlus = nextLeft(vIPlus);
}
if(nextRightVIMinus != null && nextRight(vOPlus) == null){
vOPlus.thread = nextRightVIMinus;
vOPlus.mode += sIMinus - sOPlus;
}
if(nextLeftVIPlus != null && nextLeft(vOMinus) == null){
vOMinus.thread = nextLeftVIPlus;
vOMinus.mode += sIPlus - sOMinus;
defaultAncestor = v;
}
return defaultAncestor;
}
private void executeShifts(TreeNode v){
float shift = 0;
float change = 0;
for(int i = v.children.length - 1; i >= 0; i --){
TreeNode w = v.children[i];
change = change + w.change;
w.prelim += shift;
w.mode += shift;
shift += w.shift + change;
}
}
private void firstWalk(TreeNode v, TreeNode leftSibling){
if(v.isLeaf()){
if(leftSibling != null){
v.prelim = leftSibling.prelim + getDistance(v, leftSibling);
}
}else{
TreeNode defaultAncestor = v.children[0];
TreeNode previousChild = null;
for(TreeNode w : v.children){
firstWalk(w, previousChild);
defaultAncestor = apportion(w, defaultAncestor, previousChild,
v);
previousChild = w;
}
executeShifts(v);
float midpoint = (v.children[0].prelim + v.children[v.children.length-1].prelim) / 2f;
TreeNode w = leftSibling;
if(w != null){
v.prelim = w.prelim + getDistance(v, w);
v.mode = v.prelim - midpoint;
}else{
v.prelim = midpoint;
}
}
}
private void secondWalk(TreeNode v, float m, int level, float levelStart){
float levelChangeSign = getLevelChangeSign();
boolean levelChangeOnYAxis = isLevelChangeInYAxis();
float levelSize = getSizeOfLevel(level);
float x = v.prelim + m;
float y;
if(alignment == TreeAlignment.center){
y = levelStart + levelChangeSign * (levelSize / 2);
}else if(alignment == TreeAlignment.towardsRoot){
y = levelStart + levelChangeSign * (getNodeThickness(v) / 2);
}else{
y = levelStart + levelSize - levelChangeSign
* (getNodeThickness(v) / 2);
}
if(!levelChangeOnYAxis){
float t = x;
x = y;
y = t;
}
v.x = x;
v.y = y;
updateBounds(v, x, y);
if(!v.isLeaf()){
float nextLevelStart = levelStart
+ (levelSize + gapBetweenLevels)
* levelChangeSign;
for(TreeNode w : v.children){
secondWalk(w, m + v.mode, level + 1, nextLevelStart);
}
}
}
public enum TreeLocation{
top, left, bottom, right
}
public enum TreeAlignment{
center, towardsRoot, awayFromRoot
}
public static class TreeNode{
public float width, height, x, y;
//should be initialized by user
public TreeNode[] children;
public TreeNode parent;
private float mode, prelim, change, shift;
private int number = -1;
private TreeNode thread, ancestor;
boolean isLeaf(){
return children == null || children.length == 0;
}
}
}

View File

@@ -48,7 +48,7 @@ public class AboutDialog extends FloatingDialog{
continue;
}
Table table = new Table("underline-2");
Table table = new Table("underline");
table.margin(0);
table.table(img -> {
img.addImage("white").height(h - 5).width(40f).color(link.color);

View File

@@ -15,10 +15,10 @@ import io.anuke.arc.scene.utils.UIUtils;
import static io.anuke.mindustry.Vars.*;
public class UnlocksDialog extends FloatingDialog{
public class DatabaseDialog extends FloatingDialog{
public UnlocksDialog(){
super("$unlocks");
public DatabaseDialog(){
super("database");
shouldPause = true;
addCloseButton();
@@ -58,7 +58,7 @@ public class UnlocksDialog extends FloatingDialog{
if(unlock.isHidden()) continue;
Image image = data.isUnlocked(unlock) ? new Image(unlock.getContentIcon()) : new Image("icon-locked");
Image image = data.isUnlocked(unlock) ? new Image(unlock.getContentIcon()) : new Image("icon-tree-locked");
image.addListener(new HandCursorListener());
list.add(image).size(size).pad(3);

View File

@@ -1,12 +1,20 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.collection.ObjectIntMap;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Saves.SaveSlot;
import io.anuke.mindustry.io.SaveIO.SaveException;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.ItemType;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.*;
@@ -23,41 +31,192 @@ public class DeployDialog extends FloatingDialog{
cont.clear();
addCloseButton();
buttons.addImageTextButton("$techtree", "icon-tree", 16 * 2, () -> ui.tech.show()).size(230f, 64f);
cont.stack(new Table(){{
top().left().margin(10);
ObjectIntMap<Item> items = data.items();
for(Item item : Vars.content.items()){
for(Item item : content.items()){
if(item.type == ItemType.material && data.isUnlocked(item)){
add(items.get(item, 0) + "").left();
label(() -> items.get(item, 0) + "").left();
addImage(item.region).size(8*4).pad(4);
add("[LIGHT_GRAY]" + item.localizedName()).left();
row();
}
}
}}, new Table(){{
}}, new ScrollPane(new Table(){{
for(Zone zone : Vars.content.zones()){
if(data.isUnlocked(zone)){
if(control.saves.getZoneSlot() == null){
int i = 0;
for(Zone zone : content.zones()){
table(t -> {
t.addButton(zone.localizedName(), () -> {
data.removeItems(zone.deployCost);
hide();
world.playZone(zone);
}).size(150f)/*.disabled(b -> !data.hasItems(zone.deployCost))*/;
t.row();
t.table(req -> {
req.left();
for(ItemStack stack : zone.deployCost){
req.addImage(stack.item.region).size(8*3);
req.add(stack.amount + "").left();
TextButton button = t.addButton("", () -> {
if(!data.isUnlocked(zone)){
data.removeItems(zone.itemRequirements);
data.unlockContent(zone);
setup();
}else{
data.removeItems(zone.deployCost);
hide();
world.playZone(zone);
}
}).pad(3).growX();
}).pad(3);
}).size(250f).disabled(b -> !canUnlock(zone)).get();
button.clearChildren();
if(data.isUnlocked(zone)){
button.table(title -> {
title.addImage("icon-zone").padRight(3);
title.add(zone.localizedName());
});
button.row();
if(data.getWaveScore(zone) > 0){
button.add(Core.bundle.format("bestwave", data.getWaveScore(zone)));
}
button.row();
button.add("$launch").color(Color.LIGHT_GRAY).pad(4);
button.row();
button.table(req -> {
for(ItemStack stack : zone.deployCost){
req.addImage(stack.item.region).size(8 * 3);
req.add(stack.amount + "").left();
}
}).pad(3).growX();
}else{
button.addImage("icon-zone-locked");
button.row();
button.add("$locked").padBottom(6);
if(!hidden(zone)){
button.row();
button.table(req -> {
req.defaults().left();
if(zone.zoneRequirements.length > 0){
req.table(r -> {
r.add("$complete").colspan(2).left();
r.row();
for(Zone other : zone.zoneRequirements){
r.addImage("icon-zone").padRight(4);
r.add(other.localizedName()).color(Color.LIGHT_GRAY);
r.addImage(data.isCompleted(zone) ? "icon-check-2" : "icon-cancel-2")
.color(data.isCompleted(zone) ? Color.LIGHT_GRAY : Color.SCARLET).padLeft(3);
r.row();
}
});
}
req.row();
if(zone.itemRequirements.length > 0){
req.table(r -> {
for(ItemStack stack : zone.itemRequirements){
r.addImage(stack.item.region).size(8 * 3).padRight(4);
r.add(Math.min(data.getItem(stack.item), stack.amount) + "/" + stack.amount)
.color(stack.amount > data.getItem(stack.item) ? Color.SCARLET : Color.LIGHT_GRAY).left();
r.row();
}
}).padTop(10);
}
req.row();
if(zone.blockRequirements.length > 0){
req.table(r -> {
r.add("$research.list").colspan(2).left();
r.row();
for(Block block : zone.blockRequirements){
r.addImage(block.icon(Icon.small)).size(8 * 3).padRight(4);
r.add(block.formalName).color(Color.LIGHT_GRAY);
r.addImage(data.isUnlocked(block) ? "icon-check-2" : "icon-cancel-2")
.color(data.isUnlocked(block) ? Color.LIGHT_GRAY : Color.SCARLET).padLeft(3);
r.row();
}
}).padTop(10);
}
}).growX();
}
}
}).pad(4);
if(++i % 2 == 0){
row();
}
}
}else{
SaveSlot slot = control.saves.getZoneSlot();
TextButton b[] = {null};
TextButton button = addButton(Core.bundle.format("resume", slot.getZone().localizedName()), () -> {
if(b[0].childrenPressed()) return;
hide();
ui.loadAnd(() -> {
try{
control.saves.getZoneSlot().load();
state.set(State.playing);
}catch(SaveException e){ //make sure to handle any save load errors!
e.printStackTrace();
if(control.saves.getZoneSlot() != null) control.saves.getZoneSlot().delete();
ui.showInfo("$save.corrupted");
show();
}
});
}).size(200f).get();
b[0] = button;
String color = "[lightgray]";
button.defaults().colspan(2);
button.row();
button.add(Core.bundle.format("save.wave", color + slot.getWave()));
button.row();
button.label(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
button.row();
button.add().grow();
button.row();
button.addButton("$abandon", () -> {
ui.showConfirm("$warning", "$abandon.text", () -> {
slot.delete();
setup();
});
}).growX().height(50f).pad(-12).padTop(10);
}
}}).grow();
}})).grow();
}
boolean hidden(Zone zone){
for(Zone other : zone.zoneRequirements){
if(!data.isUnlocked(other)){
return true;
}
}
return false;
}
boolean canUnlock(Zone zone){
for(Zone other : zone.zoneRequirements){
if(!data.isCompleted(other)){
return false;
}
}
for(Block other : zone.blockRequirements){
if(!data.isUnlocked(other)){
return false;
}
}
return data.hasItems(zone.itemRequirements);
}
}

View File

@@ -0,0 +1,87 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.Item;
import static io.anuke.mindustry.Vars.*;
public class GameOverDialog extends FloatingDialog{
private Team winner;
public GameOverDialog(){
super("$gameover");
setFillParent(false);
shown(this::rebuild);
}
public void show(Team winner){
this.winner = winner;
show();
}
void rebuild(){
title.setText(state.launched ? "$launch.title" : "$gameover");
buttons.clear();
cont.clear();
buttons.margin(10);
if(state.rules.pvp){
cont.add(Core.bundle.format("gameover.pvp",winner.localized())).pad(6);
buttons.addButton("$menu", () -> {
hide();
state.set(State.menu);
logic.reset();
}).size(130f, 60f);
}else{
if(control.isHighScore()){
cont.add("$highscore").pad(6);
cont.row();
}
cont.table(t -> {
cont.left().defaults().left();
cont.add(Core.bundle.format("stat.wave", state.stats.wavesLasted));
cont.row();
cont.add(Core.bundle.format("stat.enemiesDestroyed", state.stats.enemyUnitsDestroyed));
cont.row();
cont.add(Core.bundle.format("stat.built", state.stats.buildingsBuilt));
cont.row();
cont.add(Core.bundle.format("stat.destroyed", state.stats.buildingsDestroyed));
cont.row();
cont.add(Core.bundle.format("stat.deconstructed", state.stats.buildingsDeconstructed));
cont.row();
if(world.isZone() && !state.stats.itemsDelivered.isEmpty()){
cont.add("$stat.delivered");
cont.row();
for(Item item : content.items()){
if(state.stats.itemsDelivered.get(item, 0) > 0){
cont.table(items -> {
items.add(" [LIGHT_GRAY]" + state.stats.itemsDelivered.get(item, 0));
items.addImage(item.region).size(8 *3).pad(4);
}).left();
cont.row();
}
}
}
}).pad(12);
if(world.isZone()){
buttons.addButton("$continue", () -> {
hide();
state.set(State.menu);
logic.reset();
ui.deploy.show();
}).size(130f, 60f);
}else{
buttons.addButton("$menu", () -> {
hide();
state.set(State.menu);
logic.reset();
}).size(130f, 60f);
}
}
}
}

View File

@@ -14,6 +14,7 @@ import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.io.SaveIO.SaveException;
import java.io.IOException;
@@ -116,8 +117,6 @@ public class LoadDialog extends FloatingDialog{
button.row();
button.add(Core.bundle.format("save.wave", color + slot.getWave()));
button.row();
button.add(Core.bundle.format("save.difficulty", color + slot.getDifficulty()));
button.row();
button.label(() -> Core.bundle.format("save.autosave", color + Core.bundle.get(slot.isAutosave() ? "on" : "off")));
button.row();
button.label(() -> Core.bundle.format("save.playtime", color + slot.getPlayTime()));
@@ -175,7 +174,7 @@ public class LoadDialog extends FloatingDialog{
try{
slot.load();
state.set(State.playing);
}catch(Exception e){
}catch(SaveException e){
Log.err(e);
state.set(State.menu);
logic.reset();

View File

@@ -2,8 +2,6 @@ package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.scene.style.Drawable;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.net.Net;
@@ -12,12 +10,10 @@ import static io.anuke.mindustry.Vars.*;
public class PausedDialog extends FloatingDialog{
private SaveDialog save = new SaveDialog();
private LoadDialog load = new LoadDialog();
private Table missionTable;
public PausedDialog(){
super("$menu");
shouldPause = true;
setup();
shown(this::rebuild);
@@ -29,20 +25,14 @@ public class PausedDialog extends FloatingDialog{
}
void rebuild(){
missionTable.clear();
missionTable.background((Drawable) null);
}
cont.clear();
void setup(){
update(() -> {
if(state.is(State.menu) && isShown()){
hide();
}
});
cont.table(t -> missionTable = t).colspan(mobile ? 3 : 2);
cont.row();
if(!mobile){
float dw = 210f;
cont.defaults().width(dw).height(50).pad(5f);
@@ -50,12 +40,14 @@ public class PausedDialog extends FloatingDialog{
cont.addButton("$back", this::hide).colspan(2).width(dw*2 + 20f);
cont.row();
cont.addButton("$unlocks", ui.unlocks::show);
cont.addButton("database", ui.database::show);
cont.addButton("$settings", ui.settings::show);
cont.row();
cont.addButton("$savegame", save::show);
cont.addButton("$loadgame", load::show).disabled(b -> Net.active());
if(!world.isZone()){
cont.row();
cont.addButton("$savegame", save::show);
cont.addButton("$loadgame", load::show).disabled(b -> Net.active());
}
cont.row();
@@ -77,11 +69,15 @@ public class PausedDialog extends FloatingDialog{
cont.addRowImageTextButton("$back", "icon-play-2", isize, this::hide);
cont.addRowImageTextButton("$settings", "icon-tools", isize, ui.settings::show);
cont.addRowImageTextButton("$save", "icon-save", isize, save::show);
cont.row();
if(!world.isZone()){
cont.addRowImageTextButton("$save", "icon-save", isize, save::show);
cont.row();
cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> Net.active());
}
cont.addRowImageTextButton("$load", "icon-load", isize, load::show).disabled(b -> Net.active());
cont.addRowImageTextButton("$hostserver.mobile", "icon-host", isize, ui.host::show).disabled(b -> Net.active());
cont.addRowImageTextButton("$quit", "icon-quit", isize, () -> {
ui.showConfirm("$confirm", "$quit.confirm", () -> {

View File

@@ -1,50 +0,0 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.Team;
import static io.anuke.mindustry.Vars.*;
public class RestartDialog extends FloatingDialog{
private Team winner;
public RestartDialog(){
super("$gameover");
setFillParent(false);
shown(this::rebuild);
}
public void show(Team winner){
this.winner = winner;
show();
}
void rebuild(){
buttons.clear();
cont.clear();
buttons.margin(10);
if(state.rules.pvp){
cont.add(Core.bundle.format("gameover.pvp",winner.localized())).pad(6);
buttons.addButton("$menu", () -> {
hide();
state.set(State.menu);
logic.reset();
}).size(130f, 60f);
}else{
if(control.isHighScore()){
cont.add("$highscore").pad(6);
cont.row();
}
cont.add(Core.bundle.format("wave.lasted", state.wave)).pad(12);
buttons.addButton("$menu", () -> {
hide();
state.set(State.menu);
logic.reset();
}).size(130f, 60f);
}
}
}

View File

@@ -126,7 +126,7 @@ public class SettingsMenuDialog extends SettingsDialog{
if(mobile){
game.checkPref("autotarget", true);
}
game.sliderPref("saveinterval", 120, 10, 5 * 120, i -> Core.bundle.format("setting.seconds", i));
game.sliderPref("saveinterval", 60, 10, 5 * 120, i -> Core.bundle.format("setting.seconds", i));
if(!mobile){
game.checkPref("crashreport", true);

View File

@@ -0,0 +1,284 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.input.KeyCode;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.math.geom.Rectangle;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.event.InputEvent;
import io.anuke.arc.scene.event.InputListener;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.TechTree;
import io.anuke.mindustry.content.TechTree.TechNode;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.ui.TreeLayout;
import io.anuke.mindustry.ui.TreeLayout.TreeNode;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.*;
public class TechTreeDialog extends FloatingDialog{
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
private static final float nodeSize = 60f;
public TechTreeDialog(){
super("");
cont.setFillParent(true);
TreeLayout layout = new TreeLayout();
layout.gapBetweenLevels = 60f;
layout.gapBetweenNodes = 40f;
layout.layout(root);
cont.add(new View()).grow();
{ //debug code; TODO remove
ObjectSet<Block> used = new ObjectSet<Block>().select(t -> true);
for(TechTreeNode node : nodes){
used.add(node.node.block);
}
Array<Block> recipes = content.blocks().select(r -> r.isVisible() && !used.contains(r));
recipes.sort(Structs.comparing(r -> r.buildCost));
if(recipes.size > 0){
Log.info("Recipe tree coverage: {0}%", (int)((float)nodes.size / content.blocks().select(Block::isVisible).size * 100));
Log.info("Missing items: ");
recipes.forEach(r -> Log.info(" {0}", r));
}
}
shown(() -> checkNodes(root));
addCloseButton();
}
@Override
protected void drawBackground(float x, float y){
drawDefaultBackground(x, y);
}
void checkNodes(TechTreeNode node){
boolean locked = locked(node);
if(!locked) node.visible = true;
for(TreeNode child : node.children){
TechTreeNode l = (TechTreeNode)child;
l.visible = !locked && l.node.block.isVisible();
checkNodes(l);
}
}
void showToast(String info){
int maxIndex = 0;
for(Element e : Core.scene.root.getChildren()){
if("toast".equals(e.getName())){
maxIndex = Math.max(maxIndex, (Integer)e.getUserObject() + 1);
}
}
int m = maxIndex;
Table table = new Table();
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.removeActor());
table.top().add(info);
table.setName("toast");
table.setUserObject(maxIndex);
table.update(() -> {
table.toFront();
table.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight() - 21 - m*20f, Align.top);
});
Core.scene.add(table);
}
boolean locked(TreeNode node){
return locked(((TechTreeNode)node).node);
}
boolean locked(TechNode node){
return !data.isUnlocked(node.block);
}
class TechTreeNode extends TreeNode{
final TechNode node;
boolean visible = true;
public TechTreeNode(TechNode node, TreeNode parent){
this.node = node;
this.parent = parent;
this.width = this.height = nodeSize;
nodes.add(this);
if(node.children != null){
children = new TechTreeNode[node.children.size];
for(int i = 0; i < children.length; i++){
children[i] = new TechTreeNode(node.children.get(i), this);
}
}
}
}
class View extends Group{
float panX = 0, panY = -200;
boolean moved = false;
Rectangle clip = new Rectangle();
ImageButton hoverNode;
Table infoTable = new Table();
{
infoTable.touchable(Touchable.enabled);
for(TechTreeNode node : nodes){
ImageButton button = new ImageButton(node.node.block.icon(Icon.medium), "node");
button.clicked(() -> {
if(mobile){
hoverNode = button;
rebuild();
}else if(data.hasItems(node.node.requirements) && locked(node)){
unlock(node.node);
}
});
button.hovered(() -> {
if(!mobile && hoverNode != button && node.visible){
hoverNode = button;
rebuild();
}
});
button.exited(() -> {
if(hoverNode == button && !infoTable.hasMouse() && !hoverNode.hasMouse()){
hoverNode = null;
rebuild();
}
});
button.touchable(() -> !node.visible ? Touchable.disabled : Touchable.enabled);
button.setUserObject(node.node);
button.tapped(() -> moved = false);
button.setSize(nodeSize, nodeSize);
button.update(() -> {
button.setPosition(node.x + panX + width/2f, node.y + panY + height/2f, Align.center);
button.getStyle().up = Core.scene.skin.getDrawable(!locked(node) ? "content-background" : "content-background-locked");
((TextureRegionDrawable)button.getStyle().imageUp)
.setRegion(node.visible ? node.node.block.icon(Icon.medium) : Core.atlas.find("icon-tree-locked"));
button.getImage().setColor(!locked(node) ? Color.WHITE : Color.GRAY);
});
addChild(button);
}
addListener(new InputListener(){
float lastX, lastY;
@Override
public void touchDragged(InputEvent event, float mx, float my, int pointer){
panX -= lastX - mx;
panY -= lastY - my;
lastX = mx;
lastY = my;
moved = true;
}
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){
lastX = x;
lastY = y;
return true;
}
});
}
void unlock(TechNode node){
data.unlockContent(node.block);
data.removeItems(node.requirements);
showToast(Core.bundle.format("researched", node.block.formalName));
checkNodes(root);
hoverNode = null;
rebuild();
}
void rebuild(){
ImageButton button = hoverNode;
infoTable.remove();
infoTable.clear();
infoTable.update(null);
if(button == null) return;
TechNode node = (TechNode)button.getUserObject();
infoTable.exited(() -> {
if(hoverNode == button && !infoTable.hasMouse() && !hoverNode.hasMouse()){
hoverNode = null;
rebuild();
}
});
infoTable.background("content-background");
infoTable.update(() -> infoTable.setPosition(button.getX() + button.getWidth(), button.getY() + button.getHeight(), Align.topLeft));
infoTable.margin(0).left().defaults().left();
infoTable.addImageButton("icon-info", "node", 14*2, () -> ui.content.show(node.block)).growY().width(50f);
infoTable.add().grow();
infoTable.table(desc -> {
desc.left().defaults().left();
desc.add(node.block.formalName);
desc.row();
if(locked(node)){
desc.table(t -> {
t.left();
for(ItemStack req : node.requirements){
t.table(list -> {
list.left();
list.addImage(req.item.getContentIcon()).size(8 * 3).padRight(3);
list.add(req.item.localizedName()).color(Color.LIGHT_GRAY);
list.add(" " + Math.min(data.items().get(req.item, 0), req.amount) + " / " + req.amount)
.color(data.has(req.item, req.amount) ? Color.LIGHT_GRAY : Color.SCARLET);
}).fillX().left();
t.row();
}
});
}else{
desc.add("$completed");
}
}).pad(9);
if(mobile && locked(node)){
infoTable.row();
infoTable.addImageTextButton("$research", "icon-check", "node", 16*2, () -> unlock(node))
.disabled(b -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3);
}
addChild(infoTable);
infoTable.pack();
}
@Override
public void draw(){
float offsetX = panX + width/2f + x, offsetY = panY + height/2f + y;
for(TreeNode node : nodes){
for(TreeNode child : node.children){
Lines.stroke(3f, locked(node) || locked(child) ? Palette.locked : Palette.accent);
Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY);
}
}
super.draw();
}
}
}

View File

@@ -1,131 +0,0 @@
package io.anuke.mindustry.ui.fragments;
import io.anuke.arc.Core;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.scene.Element;
import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.consumers.Consume;
import static io.anuke.mindustry.Vars.*;
public class BlockConsumeFragment extends Fragment{
private Table table;
private Tile lastTile;
private boolean visible;
@Override
public void build(Group parent){
table = new Table();
table.visible(() -> !state.is(State.menu) && visible);
table.setTransform(true);
parent.addChild(new Element(){{update(() -> {
if(!Core.scene.hasMouse()){
Tile tile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
if(tile == null) return;
tile = tile.target();
if(tile != lastTile){
if(tile.getTeam() == players[0].getTeam() && tile.block().consumes.hasAny()){
show(tile);
}else if(visible){
hide();
}
lastTile = tile;
}
}
});}});
parent.setTransform(true);
parent.addChild(table);
}
public void show(Tile tile){
ObjectSet<Consume> consumers = new ObjectSet<>();
TileEntity entity = tile.entity;
Block block = tile.block();
table.clearChildren();
rebuild(block, entity);
visible = true;
table.update(() -> {
if(tile.entity == null || state.is(State.menu)){
hide();
return;
}
boolean rebuild = false;
for(Consume c : block.consumes.array()){
boolean valid = c.isOptional() || c.valid(block, entity);
if(consumers.contains(c) == valid){
if(valid){
consumers.remove(c);
}else{
consumers.add(c);
}
rebuild = true;
}
}
if(rebuild){
rebuild(block, entity);
}
Vector2 v = Core.input.mouseScreen(tile.drawx() - tile.block().size * tilesize / 2f + 0.25f, tile.drawy() + tile.block().size * tilesize / 2f);
table.pack();
table.setPosition(v.x, v.y, Align.topRight);
});
table.act(Core.graphics.getDeltaTime());
}
public void hide(){
table.clear();
table.update(() -> {});
visible = false;
}
private void rebuild(Block block, TileEntity entity){
table.clearChildren();
table.left();
int scale = mobile ? 4 : 3;
for(Consume c : block.consumes.array()){
if(!c.isOptional() && !c.valid(block, entity)){
boolean[] hovered = {false};
table.table("inventory", c::buildTooltip).visible(() -> hovered[0]).height(scale * 10 + 6).padBottom(-4).right().update(t -> {
if(t.getChildren().size == 0) t.remove();
}).get().act(0);
Table result = table.table(out -> {
out.addImage(c.getIcon()).size(10 * scale).color(Color.DARK_GRAY).padRight(-10 * scale).padBottom(-scale * 2);
out.addImage(c.getIcon()).size(10 * scale).color(Palette.accent);
out.addImage("icon-missing").size(10 * scale).color(Palette.remove).padLeft(-10 * scale);
}).size(10 * scale).get();
result.hovered(() -> hovered[0] = true);
if(!mobile){
result.exited(() -> hovered[0] = false);
}
table.row();
}
}
}
}

View File

@@ -4,7 +4,6 @@ import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.Element;
@@ -13,7 +12,6 @@ import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.Label;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Stack;
import io.anuke.arc.scene.ui.layout.Table;
@@ -24,12 +22,12 @@ import io.anuke.arc.util.Time;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.StateChangeEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.IntFormat;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
@@ -76,7 +74,7 @@ public class HudFragment extends Fragment{
if(Net.active()){
i.getStyle().imageUp = Core.scene.skin.getDrawable("icon-players");
}else{
i.setDisabled(Net.active());
i.setDisabled(false);
i.getStyle().imageUp = Core.scene.skin.getDrawable(state.is(State.paused) ? "icon-play" : "icon-pause");
}
}).get();
@@ -89,7 +87,7 @@ public class HudFragment extends Fragment{
ui.chatfrag.toggle();
}
}else{
ui.unlocks.show();
ui.database.show();
}
}).update(i -> {
if(Net.active() && mobile){
@@ -131,7 +129,6 @@ public class HudFragment extends Fragment{
//fps display
infolabel = cont.table(t -> {
IntFormat fps = new IntFormat("fps");
IntFormat tps = new IntFormat("tps");
IntFormat ping = new IntFormat("ping");
t.label(() -> fps.get(Core.graphics.getFramesPerSecond())).padRight(10);
t.row();
@@ -199,6 +196,9 @@ public class HudFragment extends Fragment{
.update(label -> label.getColor().set(Color.ORANGE).lerp(Color.SCARLET, Mathf.absin(Time.time(), 2f, 1f))));
});
parent.fill(t -> t.top().right().addRowImageTextButton("$launch", "icon-arrow-up", 8*3, () -> world.launchZone())
.size(94f, 70f).visible(() -> world.isZone() && world.getZone().metCondition()));
//'saving' indicator
parent.fill(t -> {
t.bottom().visible(() -> !state.is(State.menu) && control.saves.isSaving());
@@ -230,7 +230,9 @@ public class HudFragment extends Fragment{
}
/** Show unlock notification for a new recipe. */
public void showUnlock(Recipe recipe){
public void showUnlock(UnlockableContent content){
//some content may not have icons... yet
if(content.getContentIcon() == null) return;
//if there's currently no unlock notification...
if(lastUnlockTable == null){
@@ -247,14 +249,10 @@ public class HudFragment extends Fragment{
Table in = new Table();
//create texture stack for displaying
Stack stack = new Stack();
for(TextureRegion region : recipe.result.getCompactIcon()){
Image image = new Image(region);
image.setScaling(Scaling.fit);
stack.add(image);
}
Image image = new Image(content.getContentIcon());
image.setScaling(Scaling.fit);
in.add(stack).size(48f).pad(2);
in.add(image).size(48f).pad(2);
//add to table
table.add(in).padRight(8);
@@ -290,11 +288,6 @@ public class HudFragment extends Fragment{
//get size of each element
float size = 48f / Math.min(elements.size + 1, col);
//correct plurals if needed
if(esize == 1){
((Label) lastUnlockLayout.getParent().find(e -> e instanceof Label)).setText("$unlocked.plural");
}
lastUnlockLayout.clearChildren();
lastUnlockLayout.defaults().size(size).pad(2);
@@ -309,14 +302,10 @@ public class HudFragment extends Fragment{
//if there's space, add it
if(esize < cap){
Stack stack = new Stack();
for(TextureRegion region : recipe.result.getCompactIcon()){
Image image = new Image(region);
image.setScaling(Scaling.fit);
stack.add(image);
}
Image image = new Image(content.getContentIcon());
image.setScaling(Scaling.fit);
lastUnlockLayout.add(stack);
lastUnlockLayout.add(image);
}else{ //else, add a specific icon to denote no more space
lastUnlockLayout.addImage("icon-add");
}
@@ -364,20 +353,32 @@ public class HudFragment extends Fragment{
IntFormat wavef = new IntFormat("wave");
IntFormat enemyf = new IntFormat("wave.enemy");
IntFormat enemiesf = new IntFormat("wave.enemies");
IntFormat waitingf = new IntFormat("wave.waiting");
table.clearChildren();
table.touchable(Touchable.enabled);
table.labelWrap(() ->
(state.enemies() > 0 && !state.rules.waveTimer ?
wavef.get(state.wave) + "\n" + (state.enemies() == 1 ?
enemyf.get(state.enemies()) :
enemiesf.get(state.enemies())) :
wavef.get(state.wave) + "\n" +
(state.rules.waveTimer ?
Core.bundle.format("wave.waiting", (int)(state.wavetime/60)) :
Core.bundle.get("waiting")))
).growX().pad(8f);
StringBuilder builder = new StringBuilder();
table.labelWrap(() -> {
builder.setLength(0);
builder.append(wavef.get(state.wave));
builder.append("\n");
if(state.enemies() > 0 && !state.rules.waveTimer){
if(state.enemies() == 1){
builder.append(enemyf.get(state.enemies()));
}else{
builder.append(enemiesf.get(state.enemies()));
}
}else if(state.rules.waveTimer){
builder.append(waitingf.get((int)(state.wavetime/60)));
}else{
builder.append(Core.bundle.get("waiting"));
}
return builder;
}).growX().pad(8f);
table.setDisabled(true);
table.visible(() -> state.rules.waves);

View File

@@ -62,7 +62,7 @@ public class MenuFragment extends Fragment{
join = new MobileButton("icon-add", isize, "$joingame", ui.join::show),
editor = new MobileButton("icon-editor", isize, "$editor", () -> ui.loadAnd(ui.editor::show)),
tools = new MobileButton("icon-tools", isize, "$settings", ui.settings::show),
unlocks = new MobileButton("icon-unlocks", isize, "$unlocks", ui.unlocks::show),
unlocks = new MobileButton("icon-unlocks", isize, "database", ui.database::show),
donate = new MobileButton("icon-donate", isize, "$donate", Platform.instance::openDonations);
if(Core.graphics.getWidth() > Core.graphics.getHeight()){
@@ -127,12 +127,6 @@ public class MenuFragment extends Fragment{
out.row();
out.add(new MenuButton("icon-menu", "$changelog.title", ui.changelog::show));
out.add(new MenuButton("icon-unlocks", "$unlocks", ui.unlocks::show));
out.row();
out.add(new MenuButton("icon-exit", "$quit", Core.app::exit)).width(bw).colspan(2);
});
}

View File

@@ -3,20 +3,16 @@ package io.anuke.mindustry.ui.fragments;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.arc.scene.Group;
/**
* Fragment for displaying overlays such as block inventories. One is created for each input handler.
*/
/**Fragment for displaying overlays such as block inventories. One is created for each input handler.*/
public class OverlayFragment extends Fragment{
public final BlockInventoryFragment inv;
public final BlockConfigFragment config;
public final BlockConsumeFragment consume;
private Group group = new Group();
public OverlayFragment(InputHandler input){
inv = new BlockInventoryFragment(input);
config = new BlockConfigFragment(input);
consume = new BlockConsumeFragment();
}
@Override
@@ -26,9 +22,6 @@ public class OverlayFragment extends Fragment{
inv.build(group);
config.build(group);
consume.build(group);
//input.buildUI(group);
}
public void remove(){

View File

@@ -22,9 +22,8 @@ import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.type.Category;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.ImageStack;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.OreBlock;
@@ -33,8 +32,10 @@ import static io.anuke.mindustry.Vars.*;
public class PlacementFragment extends Fragment{
final int rowWidth = 4;
Category currentCategory = Category.turret;
Array<Block> returnArray = new Array<>();
Category currentCategory = Category.distribution;
Block hovered, lastDisplay;
Tile lastHover;
Tile hoverTile;
Table blockTable, toggler, topTable;
boolean shown = true;
@@ -68,8 +69,8 @@ public class PlacementFragment extends Fragment{
int i = 0;
for(KeyCode key : inputCatGrid){
if(Core.input.keyDown(key)){
input.recipe = Recipe.getByCategory(Category.values()[i]).first();
currentCategory = input.recipe.category;
input.block = getByCategory(Category.values()[i]).first();
currentCategory = input.block.buildCategory;
}
i++;
}
@@ -79,19 +80,19 @@ public class PlacementFragment extends Fragment{
if(tile != null){
tile = tile.target();
Recipe tryRecipe = Recipe.getByResult(tile.block());
if(tryRecipe != null && data.isUnlocked(tryRecipe)){
input.recipe = tryRecipe;
currentCategory = input.recipe.category;
Block tryRecipe = tile.block();
if(tryRecipe.isVisible() && data.isUnlocked(tryRecipe)){
input.block = tryRecipe;
currentCategory = input.block.buildCategory;
return true;
}
}
}else{ //select block
int i = 0;
Array<Recipe> recipes = Recipe.getByCategory(currentCategory);
Array<Block> recipes = getByCategory(currentCategory);
for(KeyCode key : inputGrid){
if(Core.input.keyDown(key))
input.recipe = (i < recipes.size && data.isUnlocked(recipes.get(i))) ? recipes.get(i) : null;
input.block = (i < recipes.size && data.isUnlocked(recipes.get(i))) ? recipes.get(i) : null;
i++;
}
}
@@ -117,7 +118,7 @@ public class PlacementFragment extends Fragment{
ButtonGroup<ImageButton> group = new ButtonGroup<>();
group.setMinCheckCount(0);
for(Recipe recipe : Recipe.getByCategory(currentCategory)){
for(Block block : getByCategory(currentCategory)){
if(index++ % rowWidth == 0){
blockTable.row();
@@ -126,17 +127,17 @@ public class PlacementFragment extends Fragment{
boolean[] unlocked = {false};
ImageButton button = blockTable.addImageButton("icon-locked", "select", 8 * 4, () -> {
if(data.isUnlocked(recipe)){
input.recipe = input.recipe == recipe ? null : recipe;
if(data.isUnlocked(block)){
input.block = input.block == block ? null : block;
}
}).size(46f).group(group).get();
button.update(() -> { //color unplacable things gray
boolean ulock = data.isUnlocked(recipe);
boolean ulock = data.isUnlocked(block);
TileEntity core = players[0].getClosestCore();
Color color = core != null && (core.items.has(recipe.requirements) || state.rules.infiniteResources) ? Color.WHITE : ulock ? Color.GRAY : Color.WHITE;
Color color = core != null && (core.items.has(block.buildRequirements) || state.rules.infiniteResources) ? Color.WHITE : ulock ? Color.GRAY : Color.WHITE;
button.forEach(elem -> elem.setColor(color));
button.setChecked(input.recipe == recipe);
button.setChecked(input.block == block);
if(ulock == unlocked[0]) return;
unlocked[0] = ulock;
@@ -144,13 +145,13 @@ public class PlacementFragment extends Fragment{
if(!ulock){
button.replaceImage(new Image("icon-locked"));
}else{
button.replaceImage(new ImageStack(recipe.result.getCompactIcon()));
button.replaceImage(new Image(block.icon(Icon.medium)));
}
});
button.hovered(() -> hovered = recipe.result);
button.hovered(() -> hovered = block);
button.exited(() -> {
if(hovered == recipe.result){
if(hovered == block){
hovered = null;
}
});
@@ -162,25 +163,29 @@ public class PlacementFragment extends Fragment{
frame.table("button-edge-2", top -> {
topTable = top;
top.add(new Table()).growX().update(topTable -> {
if((tileDisplayBlock() == null && lastDisplay == getSelected() && !lastGround) || (tileDisplayBlock() != null && lastDisplay == tileDisplayBlock() && lastGround))
//don't refresh unnecessarily
if((tileDisplayBlock() == null && lastDisplay == getSelected() && !lastGround)
|| (tileDisplayBlock() != null && lastHover == hoverTile && lastGround))
return;
topTable.clear();
topTable.top().left().margin(5);
lastHover = hoverTile;
lastDisplay = getSelected();
lastGround = tileDisplayBlock() != null;
if(lastDisplay != null){ //show selected recipe
lastGround = false;
topTable.table(header -> {
header.left();
header.add(new ImageStack(lastDisplay.getCompactIcon())).size(8 * 4);
header.labelWrap(() ->
!data.isUnlocked(Recipe.getByResult(lastDisplay)) ? Core.bundle.get("blocks.unknown") : lastDisplay.formalName)
header.add(new Image(lastDisplay.icon(Icon.medium))).size(8 * 4);
header.labelWrap(() -> !data.isUnlocked(lastDisplay) ? Core.bundle.get("blocks.unknown") : lastDisplay.formalName)
.left().width(190f).padLeft(5);
header.add().growX();
if(data.isUnlocked(Recipe.getByResult(lastDisplay))){
header.addButton("?", "clear-partial", () -> ui.content.show(Recipe.getByResult(lastDisplay)))
if(data.isUnlocked(lastDisplay)){
header.addButton("?", "clear-partial", () -> ui.content.show(lastDisplay))
.size(8 * 5).padTop(-5).padRight(-5).right().grow();
}
}).growX().left();
@@ -189,7 +194,7 @@ public class PlacementFragment extends Fragment{
topTable.table(req -> {
req.top().left();
for(ItemStack stack : Recipe.getByResult(lastDisplay).requirements){
for(ItemStack stack : lastDisplay.buildRequirements){
req.table(line -> {
line.left();
line.addImage(stack.item.region).size(8 * 2);
@@ -210,8 +215,18 @@ public class PlacementFragment extends Fragment{
}else if(tileDisplayBlock() != null){ //show selected tile
lastDisplay = tileDisplayBlock();
topTable.add(new ImageStack(lastDisplay.getDisplayIcon(hoverTile))).size(8 * 4);
topTable.labelWrap(lastDisplay.getDisplayName(hoverTile)).left().width(190f).padLeft(5);
topTable.table(t -> {
t.left();
t.add(new Image(lastDisplay.getDisplayIcon(hoverTile))).size(8 * 4);
t.labelWrap(lastDisplay.getDisplayName(hoverTile)).left().width(190f).padLeft(5);
}).growX().left();
if(hoverTile.getTeam() == players[0].getTeam()){
topTable.row();
topTable.table(t -> {
t.left().defaults().left();
lastDisplay.display(hoverTile, t);
}).left().growX();
}
}
});
}).colspan(3).fillX().visible(() -> getSelected() != null || tileDisplayBlock() != null).touchable(Touchable.enabled);
@@ -230,7 +245,7 @@ public class PlacementFragment extends Fragment{
ButtonGroup<ImageButton> group = new ButtonGroup<>();
for(Category cat : Category.values()){
if(Recipe.getByCategory(cat).isEmpty()) continue;
if(getByCategory(cat).isEmpty()) continue;
categories.addImageButton("icon-" + cat.name(), "clear-toggle", 16 * 2, () -> {
currentCategory = cat;
@@ -248,6 +263,16 @@ public class PlacementFragment extends Fragment{
});
});
}
Array<Block> getByCategory(Category cat){
returnArray.clear();
for(Block block : content.blocks()){
if(block.buildCategory == cat && block.isVisible()){
returnArray.add(block);
}
}
return returnArray;
}
/** Returns the currently displayed block in the top box. */
Block getSelected(){
@@ -268,8 +293,8 @@ public class PlacementFragment extends Fragment{
}
//block currently selected
if(control.input(0).recipe != null){
toDisplay = control.input(0).recipe.result;
if(control.input(0).block != null){
toDisplay = control.input(0).block;
}
//block hovered on in build menu

View File

@@ -6,6 +6,7 @@ import io.anuke.arc.Graphics.Cursor.SystemCursor;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.EnumSet;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.function.BooleanProvider;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
@@ -20,20 +21,27 @@ import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.effect.Puddle;
import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.graphics.CacheLayer;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.Category;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.ui.Bar;
import io.anuke.mindustry.ui.ContentDisplay;
import io.anuke.mindustry.world.consumers.ConsumePower;
import io.anuke.mindustry.world.meta.*;
import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.mindustry.world.meta.BlockGroup;
import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.StatUnit;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.*;
public class Block extends BaseBlock {
public class Block extends BlockStorage{
/** internal name */
public final String name;
/** display name */
@@ -58,10 +66,8 @@ public class Block extends BaseBlock {
public int health = -1;
/** base block explosiveness */
public float baseExplosiveness = 0f;
/** whether this block can be placed on liquids. */
/** whether this block can be placed on edges of liquids. */
public boolean floating = false;
/** stuff that drops when broken */
public ItemStack drops = null;
/** multiblock size */
public int size = 1;
/** Whether to draw this block in the expanded draw range. */
@@ -80,34 +86,33 @@ public class Block extends BaseBlock {
public boolean instantTransfer = false;
/** The block group. Unless {@link #canReplace} is overriden, blocks in the same group can replace each other. */
public BlockGroup group = BlockGroup.none;
/** List of block stats. */
public BlockStats stats = new BlockStats(this);
/** List of block flags. Used for AI indexing. */
public EnumSet<BlockFlag> flags;
/** Whether to automatically set the entity to 'sleeping' when created. */
public boolean autoSleep;
/** Whether the block can be tapped and selected to configure. */
public boolean configurable;
/** Whether this block consumes touchDown events when tapped. */
public boolean consumesTap;
/** The color of this block when displayed on the minimap or map preview. */
public Color minimapColor = Color.CLEAR;
/** View range of this block type. Use a value < 0 to disable. */
public float viewRange = 10;
/**Whether the top icon is outlined, like a turret.*/
public boolean turretIcon = false;
/**Whether units target this block.*/
public boolean targetable = true;
/**Whether the overdrive core has any effect on this block.*/
public boolean canOverdrive = true;
protected Array<Tile> tempTiles = new Array<>();
protected TextureRegion[] blockIcon;
protected TextureRegion[] icon;
protected TextureRegion[] compactIcon;
protected TextureRegion editorIcon;
/**Cost of constructing this block.*/
public ItemStack[] buildRequirements = new ItemStack[]{};
/**Category in place menu.*/
public Category buildCategory = Category.distribution;
/**Cost of building this block; do not modify directly!*/
public float buildCost;
/**Whether this block is visible and can currently be built.*/
public BooleanProvider buildVisibility = () -> false;
public boolean alwaysUnlocked = false;
public TextureRegion region;
protected Array<Tile> tempTiles = new Array<>();
protected TextureRegion[] icons = new TextureRegion[Icon.values().length];
protected TextureRegion[] generatedIcons;
protected TextureRegion region;
public Block(String name){
this.name = name;
@@ -116,24 +121,10 @@ public class Block extends BaseBlock {
this.solid = false;
}
/**Populates the array with all blocks that produce this content.*/
public static void getByProduction(Array<Block> arr, Content result){
arr.clear();
for(Block block : content.blocks()){
if(block.produces.get() == result){
arr.add(block);
}
}
}
public boolean canBreak(Tile tile){
return true;
}
public boolean dropsItem(Item item){
return drops != null && drops.item == item;
}
public void onProximityRemoved(Tile tile){
if(tile.entity.power != null){
tile.block().powerGraphRemoved(tile);
@@ -210,6 +201,14 @@ public class Block extends BaseBlock {
public void drawPlace(int x, int y, int rotation, boolean valid){
}
public void draw(Tile tile){
Draw.rect(region, tile.drawx(), tile.drawy(), rotate ? tile.getRotation() * 90 : 0);
}
public void drawShadow(Tile tile){
draw(tile);
}
/** Called after the block is placed by this client. */
public void playerPlaced(Tile tile){
}
@@ -237,6 +236,21 @@ public class Block extends BaseBlock {
}
}
@Override
public String localizedName(){
return formalName;
}
@Override
public TextureRegion getContentIcon(){
return icon(Icon.medium);
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayBlock(table, this);
}
@Override
public ContentType getContentType(){
return ContentType.block;
@@ -255,6 +269,11 @@ public class Block extends BaseBlock {
health = size * size * 40;
}
buildCost = 0f;
for(ItemStack stack : buildRequirements){
buildCost += stack.amount * stack.item.cost;
}
setStats();
consumes.checkRequired(this);
@@ -314,7 +333,7 @@ public class Block extends BaseBlock {
}
public boolean synthetic(){
return update || destructible || solid;
return update || destructible;
}
public void drawConfigure(Tile tile){
@@ -335,10 +354,6 @@ public class Block extends BaseBlock {
if(hasItems) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items);
}
public String name(){
return name;
}
public boolean isSolidFor(Tile tile){
return false;
}
@@ -445,55 +460,51 @@ public class Block extends BaseBlock {
}
public TextureRegion getDisplayIcon(Tile tile){
return getEditorIcon();
return icon(Icon.medium);
}
public TextureRegion getEditorIcon(){
if(editorIcon == null){
editorIcon = Core.atlas.find("block-icon-" + name, Core.atlas.find("clear"));
public void display(Tile tile, Table table){
TileEntity entity = tile.entity;
if(entity != null){
table.table(bars -> {
bars.defaults().growX().height(18f).pad(4);
displayBars(tile, bars);
}).growX();
table.marginBottom(-5);
}
return editorIcon;
}
/** Returns the icon used for displaying this block in the place menu */
public TextureRegion[] getIcon(){
if(icon == null){
if(Core.atlas.has(name + "-icon")){
icon = new TextureRegion[]{Core.atlas.find(name + "-icon")};
}else if(Core.atlas.has(name)){
icon = new TextureRegion[]{Core.atlas.find(name)};
}else if(Core.atlas.has(name + "1")){
icon = new TextureRegion[]{Core.atlas.find(name + "1")};
}else{
icon = new TextureRegion[]{};
}
public void displayBars(Tile tile, Table bars){
TileEntity entity = tile.entity;
bars.add(new Bar("blocks.health", Palette.health, entity::healthf).blink(Color.WHITE));
bars.row();
if(entity.liquids != null){
bars.add(new Bar(() -> entity.liquids.current().localizedName(), () -> entity.liquids.current().color, () -> entity.liquids.total() / liquidCapacity)).growX();
bars.row();
}
return icon;
}
/** Returns a list of regions that represent this block in the world */
public TextureRegion[] getBlockIcon(){
return getIcon();
}
/** Returns a list of icon regions that have been cropped to 8x8 */
public TextureRegion[] getCompactIcon(){
if(compactIcon == null){
compactIcon = new TextureRegion[getIcon().length];
for(int i = 0; i < compactIcon.length; i++){
compactIcon[i] = iconRegion(getIcon()[i]);
}
public TextureRegion icon(Icon icon){
if(icons[icon.ordinal()] == null){
icons[icon.ordinal()] = Core.atlas.find(name + "-icon-" + icon.name());
}
return compactIcon;
return icons[icon.ordinal()];
}
/** Crops a regionto 8x8 */
protected TextureRegion iconRegion(TextureRegion src){
TextureRegion region = new TextureRegion(src);
region.setWidth((int)(8 / Draw.scl));
region.setHeight((int)(8 / Draw.scl));
return region;
protected TextureRegion[] generateIcons(){
return new TextureRegion[]{Core.atlas.find(name)};
}
public TextureRegion[] getGeneratedIcons(){
if(generatedIcons == null){
generatedIcons = generateIcons();
}
return generatedIcons;
}
public boolean hasEntity(){
@@ -504,14 +515,6 @@ public class Block extends BaseBlock {
return new TileEntity();
}
public void draw(Tile tile){
Draw.rect(region, tile.drawx(), tile.drawy(), rotate ? tile.getRotation() * 90 : 0);
}
public void drawShadow(Tile tile){
draw(tile);
}
/** Offset for placing and drawing multiblocks. */
public float offset(){
return ((size + 1) % 2) * tilesize / 2f;
@@ -521,18 +524,44 @@ public class Block extends BaseBlock {
return size > 1;
}
public Array<Object> getDebugInfo(Tile tile){
return Array.with(
"block", tile.block().name,
"floor", tile.floor().name,
"x", tile.x,
"y", tile.y,
"entity.name", tile.entity.getClass(),
"entity.x", tile.entity.x,
"entity.y", tile.entity.y,
"entity.id", tile.entity.id,
"entity.items.total", hasItems ? tile.entity.items.total() : null,
"entity.graph", tile.entity.power != null && tile.entity.power.graph != null ? tile.entity.power.graph.getID() : null
);
public boolean isVisible(){
return buildVisibility.get() && !isHidden();
}
@Override
public boolean alwaysUnlocked(){
return alwaysUnlocked;
}
protected void requirements(Category cat, ItemStack[] stacks, boolean unlocked){
requirements(cat, () -> true, stacks);
this.alwaysUnlocked = unlocked;
}
protected void requirements(Category cat, ItemStack[] stacks){
requirements(cat, () -> true, stacks);
}
/**Sets up requirements. Use only this method to set up requirements.*/
protected void requirements(Category cat, BooleanProvider visible, ItemStack[] stacks){
this.buildCategory = cat;
this.buildRequirements = stacks;
this.buildVisibility = visible;
Arrays.sort(buildRequirements, (a, b) -> Integer.compare(a.item.id, b.item.id));
}
public enum Icon{
small(8 * 3),
medium(8 * 4),
large(8 * 6),
/**uses whatever the size of the block is*/
full(0);
public final int size;
Icon(int size){
this.size = size;
}
}
}

View File

@@ -10,15 +10,16 @@ import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.Puddle;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.world.consumers.ConsumeItem;
import io.anuke.mindustry.world.consumers.ConsumeLiquid;
import io.anuke.mindustry.world.consumers.Consumers;
import io.anuke.mindustry.world.meta.BlockStats;
import io.anuke.mindustry.world.meta.Producers;
public abstract class BaseBlock extends MappableContent{
public abstract class BlockStorage extends UnlockableContent{
public boolean hasItems;
public boolean hasLiquids;
public boolean hasPower;
@@ -32,6 +33,7 @@ public abstract class BaseBlock extends MappableContent{
public float liquidCapacity = 10f;
public float liquidFlowFactor = 4.9f;
public BlockStats stats = new BlockStats();
public Consumers consumes = new Consumers();
public Producers produces = new Producers();
@@ -269,4 +271,9 @@ public abstract class BaseBlock extends MappableContent{
}
return false;
}
/** Returns whether this block's inventory has space and is ready for production.*/
public boolean canProduce(Tile tile){
return true;
}
}

View File

@@ -11,7 +11,6 @@ import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.game.EventType.BlockBuildBeginEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity;
import static io.anuke.mindustry.Vars.*;
@@ -64,8 +63,8 @@ public class Build{
}
/**Places a BuildBlock at this location.*/
public static void beginPlace(Team team, int x, int y, Recipe recipe, int rotation){
if(!validPlace(team, x, y, recipe.result, rotation)){
public static void beginPlace(Team team, int x, int y, Block result, int rotation){
if(!validPlace(team, x, y, result, rotation)){
return;
}
@@ -74,13 +73,12 @@ public class Build{
//just in case
if(tile == null) return;
Block result = recipe.result;
Block previous = tile.block();
Block sub = content.getByName(ContentType.block, "build" + result.size);
tile.setBlock(sub, rotation);
tile.<BuildEntity>entity().setConstruct(previous, recipe);
tile.<BuildEntity>entity().setConstruct(previous, result);
tile.setTeam(team);
if(result.isMultiblock()){
@@ -107,9 +105,7 @@ public class Build{
/**Returns whether a tile can be placed at this location by this team.*/
public static boolean validPlace(Team team, int x, int y, Block type, int rotation){
Recipe recipe = Recipe.getByResult(type);
if(recipe == null || (!recipe.visibility.usable())){
if(!type.isVisible() || type.isHidden()){
return false;
}

View File

@@ -212,9 +212,9 @@ public class Tile implements Position, TargetTrait{
return link != 0;
}
/** Sets this to a linked tile, which sets the block to a blockpart. dx and dy can only be -8-7. */
/** Sets this to a linked tile, which sets the block to a part. dx and dy can only be -8-7. */
public void setLinked(byte dx, byte dy){
setBlock(Blocks.blockpart);
setBlock(Blocks.part);
link = Pack.byteByte((byte)(dx + 8), (byte)(dy + 8));
}
@@ -418,7 +418,7 @@ public class Tile implements Position, TargetTrait{
Block block = block();
Block floor = floor();
return floor.name() + ":" + block.name() + "[" + x + "," + y + "] " + "entity=" + (entity == null ? "null" : (entity.getClass())) +
return floor.name + ":" + block.name + "[" + x + "," + y + "] " + "entity=" + (entity == null ? "null" : (entity.getClass())) +
(link != 0 ? " link=[" + (Pack.leftByte(link) - 8) + ", " + (Pack.rightByte(link) - 8) + "]" : "");
}
}

View File

@@ -6,17 +6,16 @@ import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
/**
* Used for multiblocks. Each block that is not the center of the multiblock is a blockpart.
* Used for multiblocks. Each block that is not the center of the multiblock is a part.
* Think of these as delegates to the actual block; all events are passed to the target block.
* They are made to share all properties from the linked tile/block.
*/
public class BlockPart extends Block{
public BlockPart(){
super("blockpart");
super("part");
solid = false;
hasPower = hasItems = hasLiquids = true;
viewRange = -1;
}
@Override

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.world.blocks;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.Graphics.Cursor;
import io.anuke.arc.Graphics.Cursor.SystemCursor;
import io.anuke.arc.entities.Effects;
@@ -15,13 +16,13 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.effect.RubbleDecal;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.game.EventType.BlockBuildEndEvent;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.modules.ItemModule;
@@ -46,8 +47,10 @@ public class BuildBlock extends Block{
@Remote(called = Loc.server)
public static void onDeconstructFinish(Tile tile, Block block){
Team team = tile.getTeam();
Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), block.size);
world.removeBlock(tile);
Events.fire(new BlockBuildEndEvent(tile, team, true));
}
@Remote(called = Loc.server)
@@ -64,24 +67,25 @@ public class BuildBlock extends Block{
//event first before they can recieve the placed() event modification results
Core.app.post(() -> tile.block().playerPlaced(tile));
}
Core.app.post(() -> Events.fire(new BlockBuildEndEvent(tile, team, false)));
}
@Override
public String getDisplayName(Tile tile){
BuildEntity entity = tile.entity();
return Core.bundle.format("block.constructing", entity.recipe == null ? entity.previous.formalName : entity.recipe.result.formalName);
return Core.bundle.format("block.constructing", entity.block == null ? entity.previous.formalName : entity.block.formalName);
}
@Override
public TextureRegion getDisplayIcon(Tile tile){
BuildEntity entity = tile.entity();
return (entity.recipe == null ? entity.previous : entity.recipe.result).getEditorIcon();
return (entity.block == null ? entity.previous : entity.block).icon(Icon.full);
}
@Override
public boolean isSolidFor(Tile tile){
BuildEntity entity = tile.entity();
return entity == null || (entity.recipe != null && entity.recipe.result.solid) || entity.previous == null || entity.previous.solid;
return entity == null || (entity.block != null && entity.block.solid) || entity.previous == null || entity.previous.solid;
}
@Override
@@ -94,9 +98,9 @@ public class BuildBlock extends Block{
BuildEntity entity = tile.entity();
//if the target is constructible, begin constructing
if(entity.recipe != null){
if(entity.block != null){
player.clearBuilding();
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.recipe));
player.addBuildRequest(new BuildRequest(tile.x, tile.y, tile.getRotation(), entity.block));
}
}
@@ -114,15 +118,13 @@ public class BuildBlock extends Block{
BuildEntity entity = tile.entity();
//When breaking, don't draw the previous block... since it's the thing you were breaking
if(entity.recipe != null && entity.previous == entity.recipe.result){
if(entity.block != null && entity.previous == entity.block){
return;
}
if(entity.previous == null) return;
for(TextureRegion region : entity.previous.getBlockIcon()){
Draw.rect(region, tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.getRotation() * 90 : 0);
}
Draw.rect(entity.previous.icon(Icon.full), tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.getRotation() * 90 : 0);
}
@Override
@@ -132,11 +134,11 @@ public class BuildBlock extends Block{
Shaders.blockbuild.color = Palette.accent;
Block target = entity.recipe == null ? entity.previous : entity.recipe.result;
Block target = entity.block == null ? entity.previous : entity.block;
if(target == null) return;
for(TextureRegion region : target.getBlockIcon()){
for(TextureRegion region : target.getGeneratedIcons()){
Shaders.blockbuild.region = region;
Shaders.blockbuild.progress = entity.progress;
@@ -148,6 +150,7 @@ public class BuildBlock extends Block{
@Override
public void drawShadow(Tile tile){
//don't
//TODO maybe do
}
@Override
@@ -160,7 +163,7 @@ public class BuildBlock extends Block{
* The recipe of the block that is being constructed.
* If there is no recipe for this block, as is the case with rocks, 'previous' is used.
*/
public Recipe recipe;
public Block block;
public float progress = 0;
public float buildCost;
@@ -175,16 +178,16 @@ public class BuildBlock extends Block{
private float[] totalAccumulator;
public void construct(Unit builder, TileEntity core, float amount){
if(recipe == null){
if(block == null){
damage(99999);
return;
}
float maxProgress = checkRequired(core.items, amount, false);
for(int i = 0; i < recipe.requirements.length; i++){
accumulator[i] += Math.min(recipe.requirements[i].amount * maxProgress, recipe.requirements[i].amount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + recipe.requirements[i].amount * maxProgress, recipe.requirements[i].amount);
for(int i = 0; i < block.buildRequirements.length; i++){
accumulator[i] += Math.min(block.buildRequirements[i].amount * maxProgress, block.buildRequirements[i].amount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + block.buildRequirements[i].amount * maxProgress, block.buildRequirements[i].amount);
}
maxProgress = checkRequired(core.items, maxProgress, true);
@@ -196,24 +199,23 @@ public class BuildBlock extends Block{
}
if(progress >= 1f || state.rules.infiniteResources){
Call.onConstructFinish(tile, recipe.result, builderID, tile.getRotation(), builder.getTeam());
Call.onConstructFinish(tile, block, builderID, tile.getRotation(), builder.getTeam());
}
}
public void deconstruct(Unit builder, TileEntity core, float amount){
Recipe recipe = Recipe.getByResult(previous);
if(recipe != null){
ItemStack[] requirements = recipe.requirements;
if(block != null){
ItemStack[] requirements = block.buildRequirements;
if(requirements.length != accumulator.length || totalAccumulator.length != requirements.length){
setDeconstruct(previous);
}
for(int i = 0; i < requirements.length; i++){
accumulator[i] += Math.min(requirements[i].amount * amount / 2f, requirements[i].amount/2f - totalAccumulator[i]); //add scaled amount progressed to the accumulator
accumulator[i] += Math.min(requirements[i].amount * amount / 2f, requirements[i].amount / 2f - totalAccumulator[i]); //add scaled amount progressed to the accumulator
totalAccumulator[i] = Math.min(totalAccumulator[i] + requirements[i].amount * amount / 2f, requirements[i].amount);
int accumulated = (int) (accumulator[i]); //get amount
int accumulated = (int)(accumulator[i]); //get amount
if(amount > 0 && accumulated > 0){ //if it's positive, add it to the core
int accepting = core.tile.block().acceptStack(requirements[i].item, accumulated, core.tile, builder);
@@ -227,21 +229,21 @@ public class BuildBlock extends Block{
progress = Mathf.clamp(progress - amount);
if(progress <= 0 || state.rules.infiniteResources){
Call.onDeconstructFinish(tile, this.recipe == null ? previous : this.recipe.result);
Call.onDeconstructFinish(tile, this.block == null ? previous : this.block);
}
}
private float checkRequired(ItemModule inventory, float amount, boolean remove){
float maxProgress = amount;
for(int i = 0; i < recipe.requirements.length; i++){
for(int i = 0; i < block.buildRequirements.length; i++){
int required = (int) (accumulator[i]); //calculate items that are required now
if(inventory.get(recipe.requirements[i].item) == 0){
if(inventory.get(block.buildRequirements[i].item) == 0){
maxProgress = 0f;
}else if(required > 0){ //if this amount is positive...
//calculate how many items it can actually use
int maxUse = Math.min(required, inventory.get(recipe.requirements[i].item));
int maxUse = Math.min(required, inventory.get(block.buildRequirements[i].item));
//get this as a fraction
float fraction = maxUse / (float) required;
@@ -252,7 +254,7 @@ public class BuildBlock extends Block{
//remove stuff that is actually used
if(remove){
inventory.remove(recipe.requirements[i].item, maxUse);
inventory.remove(block.buildRequirements[i].item, maxUse);
}
}
//else, no items are required yet, so just keep going
@@ -265,24 +267,24 @@ public class BuildBlock extends Block{
return progress;
}
public void setConstruct(Block previous, Recipe recipe){
this.recipe = recipe;
public void setConstruct(Block previous, Block block){
this.block = block;
this.previous = previous;
this.accumulator = new float[recipe.requirements.length];
this.totalAccumulator = new float[recipe.requirements.length];
this.buildCost = recipe.cost;
this.accumulator = new float[block.buildRequirements.length];
this.totalAccumulator = new float[block.buildRequirements.length];
this.buildCost = block.buildCost;
}
public void setDeconstruct(Block previous){
this.previous = previous;
this.progress = 1f;
if(Recipe.getByResult(previous) != null){
this.recipe = Recipe.getByResult(previous);
this.accumulator = new float[Recipe.getByResult(previous).requirements.length];
this.totalAccumulator = new float[Recipe.getByResult(previous).requirements.length];
this.buildCost = Recipe.getByResult(previous).cost;
if(previous.buildCost > 1f){
this.block = previous;
this.accumulator = new float[previous.buildRequirements.length];
this.totalAccumulator = new float[previous.buildRequirements.length];
this.buildCost = previous.buildCost;
}else{
this.buildCost = 20f; //default no-recipe build cost is 20
this.buildCost = 20f; //default no-requirement build cost is 20
}
}
@@ -290,7 +292,7 @@ public class BuildBlock extends Block{
public void write(DataOutput stream) throws IOException{
stream.writeFloat(progress);
stream.writeShort(previous == null ? -1 : previous.id);
stream.writeShort(recipe == null ? -1 : recipe.result.id);
stream.writeShort(block == null ? -1 : block.id);
if(accumulator == null){
stream.writeByte(-1);
@@ -320,10 +322,10 @@ public class BuildBlock extends Block{
}
if(pid != -1) previous = content.block(pid);
if(rid != -1) recipe = Recipe.getByResult(content.block(rid));
if(rid != -1) block = content.block(rid);
if(recipe != null){
buildCost = recipe.cost;
if(block != null){
buildCost = block.buildCost;
}else{
buildCost = 20f;
}

View File

@@ -2,22 +2,18 @@ package io.anuke.mindustry.world.blocks;
import io.anuke.arc.Core;
import io.anuke.arc.entities.Effects.Effect;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.content.StatusEffects;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.type.StatusEffect;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.tilesize;
public class Floor extends Block{
/** number of different variant regions to use */
public int variants;
@@ -43,18 +39,15 @@ public class Floor extends Block{
public Color liquidColor;
/** liquids that drop from this block, used for pumps */
public Liquid liquidDrop = null;
/** item that drops from this block, used for drills */
public Item itemDrop = null;
/** Whether ores generate on this block. */
public boolean hasOres = false;
/** whether this block can be drowned in */
public boolean isLiquid;
/** if true, this block cannot be mined by players. useful for annoying things like stone. */
/** if true, this block cannot be mined by players. useful for annoying things like sand. */
public boolean playerUnmineable = false;
protected TextureRegion edgeRegion;
protected TextureRegion[] edgeRegions;
protected TextureRegion[] variantRegions;
protected Vector2[] offsets;
protected Predicate<Floor> blends = block -> block != this && !block.blendOverride(this);
protected boolean blend = true;
public Floor(String name){
super(name);
@@ -65,36 +58,6 @@ public class Floor extends Block{
public void load(){
super.load();
if(blend){
edgeRegion = Core.atlas.has(name + "edge") ? Core.atlas.find(name + "edge") : Core.atlas.find(edge + "edge");
edgeRegions = new TextureRegion[8];
offsets = new Vector2[8];
for(int i = 0; i < 8; i++){
int dx = Geometry.d8[i].x, dy = Geometry.d8[i].y;
TextureRegion result = new TextureRegion();
int padSize = (int)(tilesize/Draw.scl/2);
int texSize = (int)(tilesize/Draw.scl);
int totSize = padSize + texSize;
int sx = -dx * texSize + padSize/2, sy = -dy * texSize + padSize/2;
int x = Mathf.clamp(sx, 0, totSize);
int y = Mathf.clamp(sy, 0, totSize);
int w = Mathf.clamp(sx + texSize, 0, totSize) - x, h = Mathf.clamp(sy + texSize, 0, totSize) - y;
float rx = Mathf.clamp(dx * texSize, 0, texSize - w);
float ry = Mathf.clamp(dy * texSize, 0, texSize - h);
result.setTexture(edgeRegion.getTexture());
result.set(edgeRegion.getX() + x, edgeRegion.getY() + y + h, w, -h);
edgeRegions[i] = result;
offsets[i] = new Vector2(-padSize + rx, -padSize + ry);
}
}
//load variant regions for drawing
if(variants > 0){
variantRegions = new TextureRegion[variants];
@@ -124,34 +87,11 @@ public class Floor extends Block{
Mathf.random.setSeed(tile.pos());
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
/*
if(tile.hasCliffs() && cliffRegions != null){
for(int i = 0; i < 4; i++){
if((tile.getCliffs() & (1 << i * 2)) != 0){
Draw.colorl(i > 1 ? 0.6f : 1f);
boolean above = (tile.getCliffs() & (1 << ((i + 1) % 4) * 2)) != 0, below = (tile.getCliffs() & (1 << (Mathf.mod(i - 1, 4)) * 2)) != 0;
if(above && below){
Draw.rect(cliffRegions[0], tile.worldx(), tile.worldy(), i * 90);
}else if(above){
Draw.rect(cliffRegions[1], tile.worldx(), tile.worldy(), i * 90);
}else if(below){
Draw.rect(cliffRegions[2], tile.worldx(), tile.worldy(), i * 90);
}else{
Draw.rect(cliffRegions[3], tile.worldx(), tile.worldy(), i * 90);
}
}
}
}
Draw.reset();
drawEdges(tile, false);*/
}
public boolean blendOverride(Block block){
return false;
@Override
public TextureRegion[] generateIcons(){
return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")};
}
}

View File

@@ -30,8 +30,8 @@ public class LiquidBlock extends Block{
}
@Override
public TextureRegion[] getIcon(){
return new TextureRegion[]{Core.atlas.find(name() + "-bottom"), Core.atlas.find(name() + "-top")};
public TextureRegion[] generateIcons(){
return new TextureRegion[]{Core.atlas.find(name + "-bottom"), Core.atlas.find(name + "-top")};
}
@Override

View File

@@ -2,10 +2,8 @@ package io.anuke.mindustry.world.blocks;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
@@ -17,7 +15,7 @@ public class OreBlock extends Floor{
public OreBlock(Item ore, Floor base){
super("ore-" + ore.name + "-" + base.name);
this.formalName = ore.localizedName() + " " + base.formalName;
this.drops = new ItemStack(ore, 1);
this.itemDrop = ore;
this.base = base;
this.variants = 3;
this.minimapColor = ore.color;
@@ -28,15 +26,7 @@ public class OreBlock extends Floor{
@Override
public String getDisplayName(Tile tile){
return drops.item.localizedName();
}
@Override
public TextureRegion getEditorIcon(){
if(editorIcon == null){
editorIcon = variantRegions[0];
}
return editorIcon;
return itemDrop.localizedName();
}
@Override

View File

@@ -19,11 +19,18 @@ public class Rock extends Block{
@Override
public void draw(Tile tile){
Draw.colorl(1f - tile.getRotation() / 4f);
if(variants > 0){
Draw.rect(regions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, regions.length - 1))], tile.worldx(), tile.worldy());
}else{
Draw.rect(region, tile.worldx(), tile.worldy());
}
Draw.color();
}
@Override
public TextureRegion[] generateIcons(){
return new TextureRegion[]{Core.atlas.find(name + "1")};
}
@Override

View File

@@ -0,0 +1,13 @@
package io.anuke.mindustry.world.blocks;
import io.anuke.mindustry.graphics.CacheLayer;
public class StaticWall extends Rock{
public StaticWall(String name){
super(name);
breakable = alwaysReplace = false;
solid = true;
cacheLayer = CacheLayer.walls;
}
}

View File

@@ -14,7 +14,7 @@ import java.io.DataOutput;
import java.io.IOException;
public class ItemTurret extends CooledTurret{
protected int maxAmmo = 50;
protected int maxAmmo = 30;
protected ObjectMap<Item, BulletType> ammo = new ObjectMap<>();
public ItemTurret(String name){

View File

@@ -83,7 +83,6 @@ public abstract class Turret extends Block{
solid = true;
layer = Layer.turret;
group = BlockGroup.turrets;
turretIcon = true;
flags = EnumSet.of(BlockFlag.turret);
}
@@ -92,11 +91,6 @@ public abstract class Turret extends Block{
return false;
}
@Override
public void init(){
super.init();
viewRange = range;
}
@Override
public void load(){
@@ -110,10 +104,6 @@ public abstract class Turret extends Block{
@Override
public void setStats(){
super.setStats();
/*
if(ammo != null) stats.add("ammo", ammo);
if(ammo != null) stats.add("ammocapacity", maxAmmo);
if(ammo != null) stats.add("ammoitem", ammoMultiplier);*/
stats.add(BlockStat.shootRange, range, StatUnit.blocks);
stats.add(BlockStat.inaccuracy, (int) inaccuracy, StatUnit.degrees);
@@ -142,19 +132,8 @@ public abstract class Turret extends Block{
}
@Override
public TextureRegion[] getBlockIcon(){
if(blockIcon == null){
blockIcon = new TextureRegion[]{Core.atlas.find("block-icon-" + name)};
}
return blockIcon;
}
@Override
public TextureRegion[] getCompactIcon(){
if(compactIcon == null){
compactIcon = new TextureRegion[]{iconRegion(Core.atlas.find("block-icon-" + name))};
}
return compactIcon;
public TextureRegion[] generateIcons(){
return new TextureRegion[]{Core.atlas.find("block-" + size), Core.atlas.find(name)};
}
@Override

Some files were not shown because too many files have changed in this diff Show More