Balancing

This commit is contained in:
Anuken 2019-01-17 13:03:06 -05:00
parent 47605583d1
commit ccc20a9716
23 changed files with 333 additions and 553 deletions

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,9 +2,6 @@ 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.Item;
@ -22,7 +19,10 @@ 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;
@ -630,20 +630,20 @@ public class Blocks implements ContentList{
mechanicalDrill = new Drill("mechanical-drill"){{
tier = 2;
drillTime = 300;
drillTime = 600;
size = 2;
drawMineItem = true;
}};
pneumaticDrill = new Drill("pneumatic-drill"){{
tier = 3;
drillTime = 240;
drillTime = 480;
size = 2;
drawMineItem = true;
}};
laserDrill = new Drill("laser-drill"){{
drillTime = 140;
drillTime = 280;
size = 2;
hasPower = true;
tier = 4;
@ -654,7 +654,7 @@ public class Blocks implements ContentList{
}};
blastDrill = new Drill("blast-drill"){{
drillTime = 60;
drillTime = 120;
size = 3;
drawRim = true;
hasPower = true;
@ -670,7 +670,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;
@ -726,6 +726,9 @@ public class Blocks implements ContentList{
core = new CoreBlock("core"){{
health = 1100;
itemCapacity = 2000;
launchThreshold = 1000;
launchTime = 60f * 10;
launchChunkSize = 100;
}};
vault = new Vault("vault"){{
@ -746,6 +749,7 @@ public class Blocks implements ContentList{
size = 3;
itemCapacity = 100;
launchTime = 60f * 6;
hasPower = true;
consumes.power(0.1f);
}};

View file

@ -1,7 +1,9 @@
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.game.SpawnGroup;
import io.anuke.mindustry.maps.generators.MapGenerator;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Zone;
@ -20,6 +22,36 @@ public class Zones implements ContentList{
waves = true;
waveTimer = true;
waveSpacing = 60 * 60;
spawns = Array.with(
new SpawnGroup(UnitTypes.dagger){{
unitScaling = 2;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 5;
unitScaling = 2;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 10;
unitScaling = 1;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 15;
unitScaling = 1;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 20;
unitScaling = 1;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 25;
unitScaling = 1;
}}
);
}};
}};
}

View file

@ -182,6 +182,8 @@ public class World implements ApplicationListener{
}
}
addDarkness(tiles);
EntityQuery.resizeTree(0, 0, tiles.length * tilesize, tiles[0].length * tilesize);
generating = false;
@ -411,11 +413,7 @@ public class World implements ApplicationListener{
prepareTiles(tiles);
}
/**'Prepares' a tile array by:<br>
* - setting up multiblocks<br>
* - updating occlusion<br>
* Usually used before placing structures on a tile array.*/
public void prepareTiles(Tile[][] tiles){
public void addDarkness(Tile[][] tiles){
byte[][] dark = new byte[tiles.length][tiles[0].length];
byte[][] writeBuffer = new byte[tiles.length][tiles[0].length];
@ -454,9 +452,19 @@ public class World implements ApplicationListener{
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
tiles[x][y].setRotation(dark[x][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>
* Usually used before placing structures on a tile array.*/
public void prepareTiles(Tile[][] tiles){
//find multiblocks
IntArray multiblocks = new IntArray();

View file

@ -109,6 +109,12 @@ 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 float drag(){
return mech.drag;

View file

@ -217,6 +217,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();

View file

@ -7,11 +7,14 @@ 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 static io.anuke.mindustry.Vars.*;
/**Stores player unlocks. Clientside only.*/
public class GlobalData{
private ObjectMap<ContentType, ObjectSet<String>> unlocked = new ObjectMap<>();
@ -49,8 +52,7 @@ public class GlobalData{
/** 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());
}
/**

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.*/
@ -26,4 +27,6 @@ public class Rules{
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 = Waves.getDefaultSpawns();
}

View file

@ -11,6 +11,7 @@ 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;
@ -170,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(){

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){
@ -78,17 +50,6 @@ public class SpawnGroup{
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);
}
/**
* Creates a unit, and assigns correct values based on this group's data.
* This method does not add() the unit.
@ -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

@ -8,176 +8,171 @@ import io.anuke.mindustry.content.Weapons;
import io.anuke.mindustry.type.ItemStack;
public class Waves{
private static Array<SpawnGroup> spawns = Array.with(
new SpawnGroup(UnitTypes.dagger){{
end = 8;
unitScaling = 3;
}},
public static Array<SpawnGroup> getSpawns(){
return Array.with(
new SpawnGroup(UnitTypes.dagger){{
end = 8;
unitScaling = 3;
}},
new SpawnGroup(UnitTypes.wraith){{
begin = 12;
end = 14;
}},
new SpawnGroup(UnitTypes.wraith){{
begin = 12;
end = 14;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 11;
unitScaling = 2;
spacing = 2;
max = 4;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 11;
unitScaling = 2;
spacing = 2;
max = 4;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 9;
spacing = 3;
unitScaling = 2;
new SpawnGroup(UnitTypes.titan){{
begin = 9;
spacing = 3;
unitScaling = 2;
end = 30;
}},
end = 30;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 10;
unitScaling = 2;
unitAmount = 1;
spacing = 2;
end = 30;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 10;
unitScaling = 2;
unitAmount = 1;
spacing = 2;
end = 30;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 28;
spacing = 3;
unitScaling = 2;
weapon = Weapons.flamethrower;
end = 40;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 28;
spacing = 3;
unitScaling = 2;
weapon = Weapons.flamethrower;
end = 40;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 45;
spacing = 3;
unitScaling = 2;
weapon = Weapons.flamethrower;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 45;
spacing = 3;
unitScaling = 2;
weapon = Weapons.flamethrower;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 120;
spacing = 2;
unitScaling = 3;
unitAmount = 5;
weapon = Weapons.flakgun;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.titan){{
begin = 120;
spacing = 2;
unitScaling = 3;
unitAmount = 5;
weapon = Weapons.flakgun;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.wraith){{
begin = 16;
unitScaling = 2;
spacing = 2;
new SpawnGroup(UnitTypes.wraith){{
begin = 16;
unitScaling = 2;
spacing = 2;
end = 39;
max = 7;
}},
end = 39;
max = 7;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 82;
spacing = 3;
unitAmount = 4;
unitScaling = 3;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 82;
spacing = 3;
unitAmount = 4;
groupAmount = 2;
unitScaling = 3;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 41;
spacing = 5;
unitAmount = 1;
unitScaling = 3;
effect = StatusEffects.shielded;
max = 10;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 41;
spacing = 5;
unitAmount = 1;
unitScaling = 3;
effect = StatusEffects.shielded;
max = 10;
}},
new SpawnGroup(UnitTypes.fortress){{
begin = 40;
spacing = 5;
unitAmount = 2;
unitScaling = 3;
max = 10;
}},
new SpawnGroup(UnitTypes.fortress){{
begin = 40;
spacing = 5;
unitAmount = 2;
unitScaling = 3;
max = 10;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 35;
spacing = 3;
unitAmount = 4;
effect = StatusEffects.overdrive;
items = new ItemStack(Items.blastCompound, 60);
end = 60;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 35;
spacing = 3;
unitAmount = 4;
groupAmount = 2;
effect = StatusEffects.overdrive;
items = new ItemStack(Items.blastCompound, 60);
end = 60;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 42;
spacing = 3;
unitAmount = 4;
effect = StatusEffects.overdrive;
items = new ItemStack(Items.pyratite, 100);
end = 130;
}},
new SpawnGroup(UnitTypes.dagger){{
begin = 42;
spacing = 3;
unitAmount = 4;
groupAmount = 2;
effect = StatusEffects.overdrive;
items = new ItemStack(Items.pyratite, 100);
end = 130;
}},
new SpawnGroup(UnitTypes.ghoul){{
begin = 40;
unitAmount = 2;
spacing = 2;
unitScaling = 3;
max = 8;
}},
new SpawnGroup(UnitTypes.ghoul){{
begin = 40;
unitAmount = 2;
spacing = 2;
unitScaling = 3;
max = 8;
}},
new SpawnGroup(UnitTypes.wraith){{
begin = 50;
unitAmount = 4;
unitScaling = 3;
spacing = 5;
effect = StatusEffects.overdrive;
max = 8;
}},
new SpawnGroup(UnitTypes.wraith){{
begin = 50;
unitAmount = 4;
unitScaling = 3;
spacing = 5;
groupAmount = 2;
effect = StatusEffects.overdrive;
max = 8;
}},
new SpawnGroup(UnitTypes.revenant){{
begin = 50;
unitAmount = 4;
unitScaling = 3;
spacing = 5;
max = 8;
}},
new SpawnGroup(UnitTypes.revenant){{
begin = 50;
unitAmount = 4;
unitScaling = 3;
spacing = 5;
groupAmount = 2;
max = 8;
}},
new SpawnGroup(UnitTypes.ghoul){{
begin = 53;
unitAmount = 2;
unitScaling = 3;
spacing = 4;
max = 8;
end = 74;
}},
new SpawnGroup(UnitTypes.ghoul){{
begin = 53;
unitAmount = 2;
unitScaling = 3;
spacing = 4;
max = 8;
end = 74;
}},
new SpawnGroup(UnitTypes.ghoul){{
begin = 53;
unitAmount = 2;
unitScaling = 3;
spacing = 4;
max = 8;
end = 74;
}}
);
new SpawnGroup(UnitTypes.ghoul){{
begin = 53;
unitAmount = 2;
unitScaling = 3;
spacing = 4;
max = 8;
end = 74;
}}
);
public static Array<SpawnGroup> getDefaultSpawns(){
return spawns;
}
public static void testWaves(int from, int to){
Array<SpawnGroup> spawns = getSpawns();
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

@ -54,7 +54,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

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

@ -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);
@ -39,8 +45,6 @@ public class Save16 extends SaveFileVersion{
content.setTemporaryMapper(readContentHeader(stream));
world.spawner.read(stream);
readEntities(stream);
readMap(stream);
@ -63,8 +67,6 @@ public class Save16 extends SaveFileVersion{
writeContentHeader(stream);
world.spawner.write(stream); //spawnes
//--ENTITIES--
writeEntities(stream);

View file

@ -5,6 +5,7 @@ import io.anuke.arc.collection.ObjectIntMap;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
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;
@ -66,8 +67,15 @@ public class DeployDialog extends FloatingDialog{
addButton(Core.bundle.format("resume", control.saves.getZoneSlot().getZone().localizedName()), () -> {
hide();
ui.loadAnd(() -> {
control.saves.getZoneSlot().load();
state.set(State.playing);
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);
}

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

@ -92,8 +92,6 @@ public class Block extends BaseBlock {
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.*/

View file

@ -16,7 +16,6 @@ public class BlockPart extends Block{
super("blockpart");
solid = false;
hasPower = hasItems = hasLiquids = true;
viewRange = -1;
}
@Override

View file

@ -92,11 +92,6 @@ public abstract class Turret extends Block{
return false;
}
@Override
public void init(){
super.init();
viewRange = range;
}
@Override
public void load(){
@ -110,10 +105,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);

View file

@ -130,13 +130,6 @@ public class MassDriver extends Block{
stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits);
}
@Override
public void init(){
super.init();
viewRange = range;
}
@Override
public void update(Tile tile){
MassDriverEntity entity = tile.entity();

View file

@ -14,36 +14,29 @@ import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemType;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockFlag;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class CoreBlock extends StorageBlock{
protected TextureRegion openRegion;
public class CoreBlock extends LaunchPad{
protected TextureRegion topRegion;
protected int launchThreshold;
protected int launchChunkSize;
public CoreBlock(String name){
super(name);
solid = false;
solidifes = true;
solid = true;
update = true;
size = 3;
hasItems = true;
viewRange = 200f;
size = 3;
flags = EnumSet.of(BlockFlag.resupplyPoint, BlockFlag.target);
}
@ -53,11 +46,12 @@ public class CoreBlock extends StorageBlock{
CoreEntity entity = tile.entity();
Effects.effect(Fx.spawn, entity);
entity.solid = false;
entity.progress = 0;
entity.currentUnit.onRespawn(tile);
entity.currentUnit = player;
entity.currentUnit.heal();
entity.currentUnit.rotation = 90f;
entity.currentUnit.applyImpulse(0, 8f);
entity.currentUnit.setNet(tile.drawx(), tile.drawy());
entity.currentUnit.add();
entity.currentUnit = null;
@ -67,18 +61,6 @@ public class CoreBlock extends StorageBlock{
}
}
@Remote(called = Loc.server)
public static void setCoreSolid(Tile tile, boolean solid){
if(tile == null) return;
CoreEntity entity = tile.entity();
if(entity != null) entity.solid = solid;
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
return item.type == ItemType.material && super.acceptItem(item, tile, source);
}
@Override
public int getMaximumAccepted(Tile tile, Item item){
return itemCapacity * state.teams.get(tile.getTeam()).cores.size;
@ -118,7 +100,6 @@ public class CoreBlock extends StorageBlock{
public void load(){
super.load();
openRegion = Core.atlas.find(name + "-open");
topRegion = Core.atlas.find(name + "-top");
}
@ -126,7 +107,7 @@ public class CoreBlock extends StorageBlock{
public void draw(Tile tile){
CoreEntity entity = tile.entity();
Draw.rect(entity.solid ? region : openRegion, tile.drawx(), tile.drawy());
Draw.rect(region, tile.drawx(), tile.drawy());
Draw.alpha(entity.heat);
Draw.rect(topRegion, tile.drawx(), tile.drawy());
@ -158,13 +139,6 @@ public class CoreBlock extends StorageBlock{
}
}
@Override
public boolean isSolidFor(Tile tile){
CoreEntity entity = tile.entity();
return entity.solid;
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
if(Net.server() || !Net.active()) super.handleItem(item, tile, source);
@ -174,8 +148,13 @@ public class CoreBlock extends StorageBlock{
public void update(Tile tile){
CoreEntity entity = tile.entity();
if(!entity.solid && !Units.anyEntities(tile)){
Call.setCoreSolid(tile, true);
for(Item item : Vars.content.items()){
if(entity.items.get(item) >= launchThreshold + launchChunkSize && entity.timer.get(timerLaunch, launchTime)){
//TODO play animation of some sort
Effects.effect(Fx.dooropenlarge, tile);
data.addItem(item, launchChunkSize);
entity.items.remove(item, launchChunkSize);
}
}
if(entity.currentUnit != null){
@ -200,7 +179,6 @@ public class CoreBlock extends StorageBlock{
public class CoreEntity extends TileEntity implements SpawnerTrait{
public Unit currentUnit;
boolean solid = true;
float progress;
float time;
float heat;
@ -218,15 +196,5 @@ public class CoreBlock extends StorageBlock{
public float getSpawnProgress(){
return progress;
}
@Override
public void write(DataOutput stream) throws IOException{
stream.writeBoolean(solid);
}
@Override
public void read(DataInput stream) throws IOException{
solid = stream.readBoolean();
}
}
}

View file

@ -6,12 +6,11 @@ import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemType;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.data;
public class LaunchPad extends Block{
public class LaunchPad extends StorageBlock{
protected final int timerLaunch = timers++;
/**Time inbetween launches.*/
protected float launchTime;
@ -19,14 +18,13 @@ public class LaunchPad extends Block{
public LaunchPad(String name){
super(name);
update = true;
hasPower = true;
hasItems = true;
solid = true;
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
return item.type == ItemType.material && tile.entity.items.get(item) < getMaximumAccepted(tile, item);
return item.type == ItemType.material && super.acceptItem(item, tile, source);
}
@Override