Gamemodes removed

This commit is contained in:
Anuken 2019-01-12 16:55:24 -05:00
parent 777bd60f88
commit 8aa1509f47
49 changed files with 741 additions and 678 deletions

View File

@ -12,6 +12,7 @@ import javax.lang.model.util.ElementFilter;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;
@ -60,14 +61,12 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
.returns(void.class)
.addParameter(DataOutput.class, "stream")
.addParameter(type, "object")
.addAnnotation(Override.class)
.addException(IOException.class)
.addModifiers(Modifier.PUBLIC);
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("read")
.returns(type)
.addParameter(DataInput.class, "stream")
.addAnnotation(Override.class)
.addException(IOException.class)
.addModifiers(Modifier.PUBLIC);
@ -96,6 +95,17 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
serializer.addMethod(readMethod.build());
method.addStatement("io.anuke.arc.Core.settings.setSerializer($N, $L)", Utils.elementUtils.getBinaryName(elem).toString().replace('$', '.') + ".class", serializer.build());
String sname = type.toString().substring(type.toString().lastIndexOf('.') + 1);
name(writeMethod, "write" + sname);
name(readMethod, "read" + sname);
writeMethod.addModifiers(Modifier.STATIC);
readMethod.addModifiers(Modifier.STATIC);
classBuilder.addMethod(writeMethod.build());
classBuilder.addMethod(readMethod.build());
}
classBuilder.addMethod(method.build());
@ -109,4 +119,14 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
throw new RuntimeException(e);
}
}
static void name(MethodSpec.Builder builder, String name){
try{
Field field = builder.getClass().getDeclaredField("name");
field.setAccessible(true);
field.set(builder, name);
}catch(Exception e){
throw new RuntimeException(e);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

After

Width:  |  Height:  |  Size: 338 KiB

View File

@ -39,12 +39,8 @@ public class Vars{
public static final String contributorsURL = "https://api.github.com/repos/Anuken/Mindustry/contributors";
/**URL for sending crash reports to*/
public static final String crashReportURL = "http://mindustry.us.to/report";
/**time between waves in ticks (on normal mode)*/
public static final float wavespace = 60 * 60 * 1.5f;
/**maximum distance between mine and core that supports automatic transferring*/
public static final float mineTransferRange = 220f;
/**maximum distance from core that the player can be before it is no longer used for building*/
public static final float coreBuildRange = 999999f;
/**team of the player by default*/
public static final Team defaultTeam = Team.blue;
/**team of the enemy in waves/sectors*/

View File

@ -82,7 +82,7 @@ public class Pathfinder{
}
public float getValueforTeam(Team team, int x, int y){
return paths == null || team.ordinal() >= paths.length ? 0 : Structs.inBounds(x, y, paths[team.ordinal()].weights) ? paths[team.ordinal()].weights[x][y] : 0;
return paths == null || paths[team.ordinal()].weights == null || team.ordinal() >= paths.length ? 0 : Structs.inBounds(x, y, paths[team.ordinal()].weights) ? paths[team.ordinal()].weights[x][y] : 0;
}
private boolean passable(Tile tile, Team team){

View File

@ -31,7 +31,7 @@ public class Blocks implements ContentList{
//environment
air, blockpart, spawn, space, metalfloor, deepwater, water, tar, stone, blackstone, dirt, sand, ice, snow,
grass, shrub, rock, icerock, blackrock,
grass, shrub, rock, icerock, blackrock, rocksSmall, rocksMedium,
//crafting
siliconSmelter, plastaniumCompressor, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer,
@ -209,6 +209,18 @@ public class Blocks implements ContentList{
blackrock = new Rock("blackrock"){{
variants = 1;
}};
rocksSmall = new Rock("rocks-small"){{
variants = 2;
breakable = alwaysReplace = false;
solid = true;
}};
rocksMedium = new Rock("rocks-medium"){{
variants = 2;
breakable = alwaysReplace = false;
solid = true;
}};
//endregion
//region crafting

View File

@ -1,7 +1,6 @@
package io.anuke.mindustry.content;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.type.Recipe.RecipeVisibility;
@ -13,11 +12,11 @@ public class Recipes implements ContentList{
@Override
public void load(){
//DEBUG
new Recipe(distribution, Blocks.itemSource).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true);
new Recipe(distribution, Blocks.itemVoid).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true);
new Recipe(liquid, Blocks.liquidSource).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true);
new Recipe(power, Blocks.powerVoid).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true);
new Recipe(power, Blocks.powerSource).setMode(GameMode.sandbox).setHidden(true).setAlwaysUnlocked(true);
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

View File

@ -113,18 +113,7 @@ public class Control implements ApplicationListener{
saves.resetSave();
});
Events.on(WaveEvent.class, event -> {
int last = Core.settings.getInt("hiscore" + world.getMap().name, 0);
if(state.wave > last && !state.mode.infiniteResources && !state.mode.disableWaveTimer){
Core.settings.put("hiscore" + world.getMap().name, state.wave);
Core.settings.save();
hiscore = true;
}
Platform.instance.updateRPC();
});
//todo high scores for custom maps, as well as other statistics
Events.on(GameOverEvent.class, event -> {
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
@ -134,7 +123,7 @@ public class Control implements ApplicationListener{
//autohost for pvp sectors
Events.on(WorldLoadEvent.class, event -> {
if(state.mode.isPvp && !Net.active()){
if(state.rules.pvp && !Net.active()){
try{
Net.host(port);
players[0].isAdmin = true;

View File

@ -1,11 +1,10 @@
package io.anuke.mindustry.core;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.arc.Events;
import io.anuke.mindustry.game.EventType.StateChangeEvent;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.Teams;
import io.anuke.mindustry.net.Net;
import io.anuke.arc.Events;
import static io.anuke.mindustry.Vars.unitGroups;
import static io.anuke.mindustry.Vars.waveTeam;
@ -13,14 +12,12 @@ import static io.anuke.mindustry.Vars.waveTeam;
public class GameState{
/**Current wave number, can be anything in non-wave modes.*/
public int wave = 1;
/**Wave countdown in ticks.*/
/**Wave time in ticks.*/
public float wavetime;
/**Whether the game is in game over state.*/
public boolean gameOver = false;
/**The current game mode.*/
public GameMode mode = GameMode.waves;
/**The current difficulty for wave modes.*/
public Difficulty difficulty = Difficulty.normal;
/**The current game rules.*/
public Rules rules = new Rules();
/**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,6 +11,7 @@ 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;
@ -56,16 +57,17 @@ public class Logic implements ApplicationListener{
public void play(){
state.set(State.playing);
state.wavetime = wavespace * state.difficulty.timeScaling * 2;
state.wavetime = 0;
Events.fire(new PlayEvent());
}
public void reset(){
state.wave = 1;
state.wavetime = wavespace * state.difficulty.timeScaling;
state.wavetime = 0;
state.gameOver = false;
state.teams = new Teams();
state.rules = new Rules();
Time.clear();
Entities.clear();
@ -77,16 +79,16 @@ public class Logic implements ApplicationListener{
public void runWave(){
world.spawner.spawnEnemies();
state.wave++;
state.wavetime = wavespace * state.difficulty.timeScaling;
state.wavetime = 0;
Events.fire(new WaveEvent());
}
private void checkGameOver(){
if(!state.mode.isPvp && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){
if(!state.rules.pvp && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){
state.gameOver = true;
Events.fire(new GameOverEvent(waveTeam));
}else if(state.mode.isPvp){
}else if(state.rules.pvp){
Team alive = null;
for(Team team : Team.all){
@ -119,16 +121,17 @@ public class Logic implements ApplicationListener{
if(!state.isPaused()){
Time.update();
if(!state.mode.disableWaveTimer && !state.mode.disableWaves && !state.gameOver){
state.wavetime -= Time.delta();
if(state.rules.waves && state.rules.waveTimer && !state.gameOver){
state.wavetime += Time.delta();
}
if(!Net.client() && state.wavetime <= 0 && !state.mode.disableWaves){
if(!Net.client() && state.wavetime >= state.rules.waveSpacing && state.rules.waves){
runWave();
}
if(!Entities.defaultGroup().isEmpty())
throw new RuntimeException("Do not add anything to the default group!");
if(!Entities.defaultGroup().isEmpty()){
throw new IllegalArgumentException("Do not add anything to the default group!");
}
if(!headless){
Entities.update(effectGroup);

View File

@ -193,7 +193,8 @@ public class NetServer implements ApplicationListener{
return;
}
if(state.mode.isPvp){
//playing in pvp mode automatically assigns players to teams
if(state.rules.pvp){
//find team with minimum amount of players and auto-assign player to that.
Team min = Structs.findMin(Team.all, team -> {
if(state.teams.isActive(team)){
@ -382,7 +383,7 @@ public class NetServer implements ApplicationListener{
}
public boolean isWaitingForPlayers(){
if(state.mode.isPvp){
if(state.rules.pvp){
int used = 0;
for(Team t : Team.all){
if(playerGroup.count(p -> p.getTeam() == t) > 0){

View File

@ -235,7 +235,7 @@ public class World implements ApplicationListener{
if(state.teams.get(players[0].getTeam()).cores.size == 0){
ui.showError("$map.nospawn");
invalidMap = true;
}else if(state.mode.isPvp){
}else if(state.rules.pvp){ //pvp maps need two cores to be valid
invalidMap = true;
for(Team team : Team.all){
if(state.teams.get(team).cores.size != 0 && team != players[0].getTeam()){

View File

@ -12,7 +12,7 @@ public class UnitDrops{
public static void dropItems(BaseUnit unit){
//items only dropped in waves for enemy team
if(unit.getTeam() != Vars.waveTeam || Vars.state.mode.disableWaves){
if(unit.getTeam() != Vars.waveTeam || !Vars.state.rules.unitDrops){
return;
}

View File

@ -2,23 +2,21 @@ package io.anuke.mindustry.game;
import io.anuke.arc.Core;
/**Presets for time between waves.
* TODO specify correct time*/
public enum Difficulty{
training(3f, 3f),
easy(1.4f, 1.5f),
normal(1f, 1f),
hard(0.5f, 0.75f),
insane(0.25f, 0.5f);
easy(1.4f),
normal(1f),
hard(0.5f),
insane(0.25f);
/**Multiplier of the time between waves.*/
public final float timeScaling;
/**Multiplier of spawner grace period.*/
public final float spawnerScaling;
public final float waveTime;
private String value;
Difficulty(float timeScaling, float spawnerScaling){
this.timeScaling = timeScaling;
this.spawnerScaling = spawnerScaling;
Difficulty(float waveTime){
this.waveTime = waveTime;
}
@Override

View File

@ -1,44 +0,0 @@
package io.anuke.mindustry.game;
import io.anuke.arc.Core;
public enum GameMode{
waves,
sandbox{{
infiniteResources = true;
disableWaveTimer = true;
}},
freebuild{{
disableWaveTimer = true;
}},
attack{{
disableWaves = true;
enemyCheat = true;
}},
victory{{
disableWaves = true;
hidden = true;
enemyCheat = false;
showMission = false;
}},
pvp{{
disableWaves = true;
isPvp = true;
enemyCoreBuildRadius = 600f;
respawnTime = 60 * 10;
}};
public boolean infiniteResources, disableWaveTimer, disableWaves, showMission = true, hidden, enemyCheat, isPvp;
public float enemyCoreBuildRadius = 400f;
public float respawnTime = 60 * 4;
public String description(){
return Core.bundle.get("mode." + name() + ".description");
}
@Override
public String toString(){
return Core.bundle.get("mode." + name() + ".name");
}
}

View File

@ -0,0 +1,46 @@
package io.anuke.mindustry.game;
import io.anuke.arc.Core;
import io.anuke.arc.function.Supplier;
/**Defines preset rule sets..*/
public enum RulePreset{
survival(() -> new Rules(){{
waveTimer = true;
waves = true;
unitDrops = true;
}}),
sandbox(() -> new Rules(){{
infiniteResources = true;
waves = true;
waveTimer = false;
}}),
attack(() -> new Rules(){{
enemyCheat = true;
unitDrops = true;
}}),
pvp(() -> new Rules(){{
pvp = true;
enemyCoreBuildRadius = 600f;
respawnTime = 60 * 10;
}});
private final Supplier<Rules> rules;
RulePreset(Supplier<Rules> rules){
this.rules = rules;
}
public Rules get(){
return rules.get();
}
public String description(){
return Core.bundle.get("mode." + name() + ".description");
}
@Override
public String toString(){
return Core.bundle.get("mode." + name() + ".name");
}
}

View File

@ -0,0 +1,27 @@
package io.anuke.mindustry.game;
import io.anuke.annotations.Annotations.Serialize;
/**Defines current rules on how the game should function.
* Does not store game state, just configuration.*/
@Serialize
public class Rules{
/**Whether the player has infinite resources.*/
public boolean infiniteResources;
/**Whether the waves come automatically on a timer. If not, waves come when the play button is pressed.*/
public boolean waveTimer = true;
/**Whether waves are spawnable at all.*/
public boolean waves;
/**Whether the enemy AI has infinite resources in most of their buildings and turrets.*/
public boolean enemyCheat;
/**Whether the game objective is PvP. Note that this enables automatic hosting.*/
public boolean pvp;
/**Whether enemy units drop random items on death.*/
public boolean unitDrops;
/**No-build zone around enemy core radius.*/
public float enemyCoreBuildRadius = 400f;
/**Player respawn time in ticks.*/
public float respawnTime = 60 * 4;
/**Time between waves in ticks.*/
public float waveSpacing = 60 * 60;
}

View File

@ -215,10 +215,6 @@ public class Saves{
return meta.difficulty;
}
public GameMode getMode(){
return meta.mode;
}
public boolean isAutosave(){
return Core.settings.getBool("save-" + index + "-autosave", true);
}

View File

@ -35,7 +35,7 @@ public class Teams{
/**Returns whether a team is active, e.g. whether it has any cores remaining.*/
public boolean isActive(Team team){
//the enemy wave team is always active
return (!Vars.state.mode.disableWaves && team == Vars.waveTeam) || get(team).cores.size > 0;
return (Vars.state.rules.waves && team == Vars.waveTeam) || get(team).cores.size > 0;
}
/**Returns a set of all teams that are enemies of this team.*/

View File

@ -76,11 +76,11 @@ public class OverlayRenderer{
for(Team enemy : state.teams.enemiesOf(player.getTeam())){
for(Tile core : state.teams.get(enemy).cores){
float dst = Mathf.dst(player.x, player.y, core.drawx(), core.drawy());
if(dst < state.mode.enemyCoreBuildRadius * 1.5f){
if(dst < state.rules.enemyCoreBuildRadius * 1.5f){
Draw.color(Color.DARK_GRAY);
Lines.poly(core.drawx(), core.drawy() - 2, 200, state.mode.enemyCoreBuildRadius);
Lines.poly(core.drawx(), core.drawy() - 2, 200, state.rules.enemyCoreBuildRadius);
Draw.color(Palette.accent, enemy.color, 0.5f + Mathf.absin(Time.time(), 10f, 0.5f));
Lines.poly(core.drawx(), core.drawy(), 200, state.mode.enemyCoreBuildRadius);
Lines.poly(core.drawx(), core.drawy(), 200, state.rules.enemyCoreBuildRadius);
}
}
}

View File

@ -330,10 +330,8 @@ 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){
if(tile.dst(x * tilesize, y * tilesize) < coreBuildRange){
return Build.validPlace(player.getTeam(), x, y, type, rotation) &&
Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}
return Build.validPlace(player.getTeam(), x, y, type, rotation) &&
Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
}
return false;

View File

@ -1,7 +1,6 @@
package io.anuke.mindustry.io;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.maps.Map;
import static io.anuke.mindustry.Vars.world;
@ -11,7 +10,6 @@ public class SaveMeta{
public int build;
public long timestamp;
public long timePlayed;
public GameMode mode;
public Map map;
public int wave;
public Difficulty difficulty;
@ -21,7 +19,6 @@ public class SaveMeta{
this.build = build;
this.timestamp = timestamp;
this.timePlayed = timePlayed;
this.mode = GameMode.values()[mode];
this.map = world.maps.getByName(map);
this.wave = wave;
this.difficulty = difficulty;

View File

@ -1,9 +1,8 @@
package io.anuke.mindustry.io.versions;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Serialization;
import io.anuke.mindustry.io.SaveFileVersion;
import io.anuke.mindustry.maps.Map;
@ -26,18 +25,15 @@ public class Save16 extends SaveFileVersion{
stream.readInt(); //build
//general state
byte mode = stream.readByte();
state.rules = Serialization.readRules(stream);
String mapname = stream.readUTF();
Map map = world.maps.getByName(mapname);
if(map == null) map = new Map("unknown", 1, 1);
world.setMap(map);
int wave = stream.readInt();
byte difficulty = stream.readByte();
float wavetime = stream.readFloat();
state.difficulty = Difficulty.values()[difficulty];
state.mode = GameMode.values()[mode];
state.wave = wave;
state.wavetime = wavetime;
@ -59,11 +55,10 @@ public class Save16 extends SaveFileVersion{
stream.writeInt(Version.build); //build
//--GENERAL STATE--
stream.writeByte(state.mode.ordinal()); //gamemode
stream.writeUTF(world.getMap().name); //map ID
Serialization.writeRules(stream, state.rules);
stream.writeUTF(world.getMap().name); //map name
stream.writeInt(state.wave); //wave
stream.writeByte(state.difficulty.ordinal()); //difficulty ordinal
stream.writeFloat(state.wavetime); //wave countdown
writeContentHeader(stream);

View File

@ -4,6 +4,7 @@ import io.anuke.arc.Core;
import io.anuke.arc.collection.IntIntMap;
import io.anuke.arc.collection.ObjectMap;
//todo: specify preferred game rules here; can be overriden
public class MapMeta{
public final int version;
public final ObjectMap<String, String> tags;

View File

@ -21,6 +21,7 @@ public class BasicGenerator extends RandomGenerator{
@Override
public void generate(Tile[][] tiles){
//todo use set seed
int seed = Mathf.random(99999999);
sim.setSeed(seed);
sim2.setSeed(seed + 1);
@ -42,5 +43,17 @@ public class BasicGenerator extends RandomGenerator{
}
}
}
//rock outcrops
double rocks = sim.octaveNoise2D(3, 0.7, 1f / 70f, x, y);
double edgeDist = Math.min(x, Math.min(y, Math.min(Math.abs(x - (width - 1)), Math.abs(y - (height - 1)))));
double transition = 8;
if(edgeDist < transition){
rocks += (transition - edgeDist) / transition / 1.5;
}
if(rocks > 0.64){
block = Blocks.rocksSmall;
}
}
}

View File

@ -1,7 +1,5 @@
package io.anuke.mindustry.net;
import io.anuke.mindustry.game.GameMode;
public class Host{
public final String name;
public final String address;
@ -10,9 +8,8 @@ public class Host{
public final int players;
public final int version;
public final String versionType;
public final GameMode mode;
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, GameMode mode){
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType){
this.name = name;
this.address = address;
this.players = players;
@ -20,6 +17,5 @@ public class Host{
this.wave = wave;
this.version = version;
this.versionType = versionType;
this.mode = mode;
}
}

View File

@ -8,11 +8,11 @@ import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Teams;
import io.anuke.mindustry.game.Teams.TeamData;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Serialization;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.MapMeta;
import io.anuke.mindustry.world.Tile;
@ -30,7 +30,7 @@ public class NetworkIO{
try(DataOutputStream stream = new DataOutputStream(os)){
//--GENERAL STATE--
stream.writeByte(state.mode.ordinal()); //gamemode
Serialization.writeRules(stream, state.rules);
stream.writeUTF(world.getMap().name); //map name
//write tags
@ -121,7 +121,7 @@ public class NetworkIO{
Time.clear();
//general state
byte mode = stream.readByte();
state.rules = Serialization.readRules(stream);
String map = stream.readUTF();
ObjectMap<String, String> tags = new ObjectMap<>();
@ -138,7 +138,6 @@ public class NetworkIO{
state.wave = wave;
state.wavetime = wavetime;
state.mode = GameMode.values()[mode];
Entities.clear();
int id = stream.readInt();
@ -256,7 +255,6 @@ public class NetworkIO{
buffer.putInt(Version.build);
buffer.put((byte)Version.type.getBytes(StandardCharsets.UTF_8).length);
buffer.put(Version.type.getBytes(StandardCharsets.UTF_8));
buffer.put((byte)state.mode.ordinal());
return buffer;
}
@ -279,8 +277,7 @@ public class NetworkIO{
byte[] tb = new byte[tlength];
buffer.get(tb);
String vertype = new String(tb, StandardCharsets.UTF_8);
GameMode mode = GameMode.values()[buffer.get()];
return new Host(host, hostAddress, map, wave, players, version, vertype, mode);
return new Host(host, hostAddress, map, wave, players, version, vertype);
}
}

View File

@ -9,7 +9,6 @@ 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.GameMode;
import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.ui.ContentDisplay;
import io.anuke.mindustry.world.Block;
@ -31,13 +30,9 @@ public class Recipe extends UnlockableContent{
public final float cost;
public RecipeVisibility visibility = RecipeVisibility.all;
//the only gamemode in which the recipe shows up
public GameMode mode;
public boolean hidden;
public boolean alwaysUnlocked;
private UnlockableContent[] dependencies;
public Recipe(Category category, Block result, ItemStack... requirements){
this.result = result;
this.requirements = requirements;
@ -59,7 +54,7 @@ public class Recipe extends UnlockableContent{
public static Array<Recipe> getByCategory(Category category){
returnArray.clear();
for(Recipe recipe : content.recipes()){
if(recipe.category == category && recipe.visibility.shown() && (recipe.mode == state.mode || recipe.mode == null)){
if(recipe.category == category && recipe.visibility.shown()){
returnArray.add(recipe);
}
}
@ -75,11 +70,6 @@ public class Recipe extends UnlockableContent{
return this;
}
public Recipe setMode(GameMode mode){
this.mode = mode;
return this;
}
public Recipe setHidden(boolean hidden){
this.hidden = hidden;
return this;
@ -153,7 +143,13 @@ public class Recipe extends UnlockableContent{
public enum RecipeVisibility{
mobileOnly(true, false),
desktopOnly(false, true),
all(true, true);
all(true, true),
sandboxOnly(true, true){
@Override
public boolean usable(){
return state.rules.infiniteResources;
}
};
public final boolean mobile, desktop;
@ -162,8 +158,12 @@ public class Recipe extends UnlockableContent{
this.desktop = desktop;
}
public boolean usable(){
return true;
}
public boolean shown(){
return (Vars.mobile && mobile) || (!Vars.mobile && desktop);
return usable() && ((Vars.mobile && mobile) || (!Vars.mobile && desktop));
}
}
}

View File

@ -12,13 +12,14 @@ import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.RulePreset;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.*;
public class CustomGameDialog extends FloatingDialog{
Difficulty difficulty = Difficulty.normal;
public CustomGameDialog(){
super("$customgame");
@ -46,11 +47,11 @@ public class CustomGameDialog extends FloatingDialog{
Table modes = new Table();
modes.marginBottom(5);
for(GameMode mode : GameMode.values()){
if(mode.hidden) continue;
for(RulePreset mode : RulePreset.values()){
modes.addButton("$mode." + mode.name() + ".name", "toggle", () -> state.mode = mode)
.update(b -> b.setChecked(state.mode == mode)).group(group).size(140f, 54f);
//todo fix presets
modes.addButton(mode.toString(), "toggle", () -> state.rules = mode.get())/*
.update(b -> b.setChecked(state.rules == mode))*/.group(group).size(140f, 54f);
if(i++ % 2 == 1) modes.row();
}
selmode.add(modes);
@ -66,20 +67,21 @@ public class CustomGameDialog extends FloatingDialog{
Table sdif = new Table();
sdif.add("$setting.difficulty.name").padRight(15f);
sdif.defaults().height(s + 4);
sdif.addImageButton("icon-arrow-left", 10 * 3, () -> {
state.difficulty = (ds[Mathf.mod(state.difficulty.ordinal() - 1, ds.length)]);
difficulty = (ds[Mathf.mod(difficulty.ordinal() - 1, ds.length)]);
state.wavetime = difficulty.waveTime;
}).width(s);
sdif.addButton("", () -> {})
.update(t -> {
t.setText(state.difficulty.toString());
t.setText(difficulty.toString());
t.touchable(Touchable.disabled);
}).width(180f);
sdif.addImageButton("icon-arrow-right", 10 * 3, () -> {
state.difficulty = (ds[Mathf.mod(state.difficulty.ordinal() + 1, ds.length)]);
difficulty = (ds[Mathf.mod(difficulty.ordinal() + 1, ds.length)]);
state.wavetime = difficulty.waveTime;
}).width(s);
cont.add(sdif);
@ -141,8 +143,7 @@ public class CustomGameDialog extends FloatingDialog{
ScrollPane pane = new ScrollPane(table);
pane.setFadeScrollBars(false);
table.row();
for(GameMode mode : GameMode.values()){
if(mode.hidden) continue;
for(RulePreset mode : RulePreset.values()){
table.labelWrap("[accent]" + mode.toString() + ":[] [lightgray]" + mode.description()).width(400f);
table.row();
}

View File

@ -112,9 +112,7 @@ public class LoadDialog extends FloatingDialog{
button.defaults().padBottom(3);
button.row();
button.add(Core.bundle.format("save.map", color + (slot.getMap() == null ? "Unknown" : slot.getMap().meta.name())));
button.row();
button.add(Core.bundle.get("level.mode") + " " + color + slot.getMode());
button.add(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().meta.name())));
button.row();
button.add(Core.bundle.format("save.wave", color + slot.getWave()));
button.row();

View File

@ -26,7 +26,7 @@ public class RestartDialog extends FloatingDialog{
buttons.margin(10);
if(state.mode.isPvp){
if(state.rules.pvp){
cont.add(Core.bundle.format("gameover.pvp",winner.localized())).pad(6);
buttons.addButton("$menu", () -> {
hide();

View File

@ -369,18 +369,18 @@ public class HudFragment extends Fragment{
table.touchable(Touchable.enabled);
table.labelWrap(() ->
(state.enemies() > 0 && state.mode.disableWaveTimer ?
(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.mode.disableWaveTimer ?
(state.rules.waveTimer ?
Core.bundle.format("wave.waiting", (int)(state.wavetime/60)) :
Core.bundle.get("waiting")))
).growX().pad(8f);
table.setDisabled(true);
table.visible(() -> !(state.mode.disableWaves || !state.mode.showMission));
table.visible(() -> state.rules.waves);
}
private void addPlayButton(Table table){
@ -391,11 +391,11 @@ public class HudFragment extends Fragment{
state.wavetime = 0f;
}
}).growY().fillX().right().width(40f).update(l -> {
boolean vis = state.mode.disableWaveTimer && ((Net.server() || players[0].isAdmin) || !Net.active());
boolean vis = !state.rules.waveTimer && ((Net.server() || players[0].isAdmin) || !Net.active());
boolean paused = state.is(State.paused) || !vis;
l.getStyle().imageUp = Core.scene.skin.getDrawable(vis ? "icon-play" : "clear");
l.touchable(!paused ? Touchable.enabled : Touchable.disabled);
}).visible(() -> state.mode.disableWaveTimer && ((Net.server() || players[0].isAdmin) || !Net.active()) && unitGroups[Team.red.ordinal()].size() == 0);
}).visible(() -> !state.rules.waveTimer && ((Net.server() || players[0].isAdmin) || !Net.active()) && unitGroups[Team.red.ordinal()].size() == 0);
}
}

View File

@ -134,7 +134,7 @@ public class PlacementFragment extends Fragment{
button.update(() -> { //color unplacable things gray
boolean ulock = data.isUnlocked(recipe);
TileEntity core = players[0].getClosestCore();
Color color = core != null && (core.items.has(recipe.requirements) || state.mode.infiniteResources) ? Color.WHITE : ulock ? Color.GRAY : Color.WHITE;
Color color = core != null && (core.items.has(recipe.requirements) || state.rules.infiniteResources) ? Color.WHITE : ulock ? Color.GRAY : Color.WHITE;
button.forEach(elem -> elem.setColor(color));
button.setChecked(input.recipe == recipe);
@ -196,7 +196,7 @@ public class PlacementFragment extends Fragment{
line.add(stack.item.localizedName()).color(Color.LIGHT_GRAY).padLeft(2).left();
line.labelWrap(() -> {
TileEntity core = players[0].getClosestCore();
if(core == null || state.mode.infiniteResources) return "*/*";
if(core == null || state.rules.infiniteResources) return "*/*";
int amount = core.items.get(stack.item);
String color = (amount < stack.amount / 2f ? "[red]" : amount < stack.amount ? "[accent]" : "[white]");

View File

@ -1,13 +1,6 @@
package io.anuke.mindustry.ui.fragments;
import io.anuke.arc.Core;
import io.anuke.arc.util.Interval;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.Packets.AdminAction;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.scene.Group;
@ -15,7 +8,13 @@ import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Timer;
import io.anuke.arc.util.Interval;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.Packets.AdminAction;
import static io.anuke.mindustry.Vars.*;
@ -137,7 +136,7 @@ public class PlayerListFragment extends Fragment{
content.add(button).padBottom(-6).width(350f).maxHeight(h + 14);
content.row();
content.addImage("blank").height(3f).color(state.mode.isPvp ? player.getTeam().color : Palette.accent).growX();
content.addImage("blank").height(3f).color(state.rules.pvp ? player.getTeam().color : Palette.accent).growX();
content.row();
});

View File

@ -109,7 +109,7 @@ public class Build{
public static boolean validPlace(Team team, int x, int y, Block type, int rotation){
Recipe recipe = Recipe.getByResult(type);
if(recipe == null || (recipe.mode != null && recipe.mode != state.mode)){
if(recipe == null || (!recipe.visibility.usable())){
return false;
}
@ -121,7 +121,7 @@ public class Build{
//check for enemy cores
for(Team enemy : state.teams.enemiesOf(team)){
for(Tile core : state.teams.get(enemy).cores){
if(Mathf.dst(x*tilesize + type.offset(), y*tilesize + type.offset(), core.drawx(), core.drawy()) < state.mode.enemyCoreBuildRadius + type.size*tilesize/2f){
if(Mathf.dst(x*tilesize + type.offset(), y*tilesize + type.offset(), core.drawx(), core.drawy()) < state.rules.enemyCoreBuildRadius + type.size*tilesize/2f){
return false;
}
}

View File

@ -205,7 +205,7 @@ public class Tile implements Position, TargetTrait{
}
public boolean isEnemyCheat(){
return getTeam() == waveTeam && !state.mode.isPvp;
return getTeam() == waveTeam && !state.rules.pvp;
}
public boolean isLinked(){

View File

@ -195,7 +195,7 @@ public class BuildBlock extends Block{
builderID = builder.getID();
}
if(progress >= 1f || state.mode.infiniteResources){
if(progress >= 1f || state.rules.infiniteResources){
Call.onConstructFinish(tile, recipe.result, builderID, tile.getRotation(), builder.getTeam());
}
}
@ -226,7 +226,7 @@ public class BuildBlock extends Block{
progress = Mathf.clamp(progress - amount);
if(progress <= 0 || state.mode.infiniteResources){
if(progress <= 0 || state.rules.infiniteResources){
Call.onDeconstructFinish(tile, this.recipe == null ? previous : this.recipe.result);
}
}

View File

@ -185,7 +185,7 @@ public class CoreBlock extends StorageBlock{
}
entity.heat = Mathf.lerpDelta(entity.heat, 1f, 0.1f);
entity.time += entity.delta();
entity.progress += 1f / state.mode.respawnTime * entity.delta();
entity.progress += 1f / state.rules.respawnTime * entity.delta();
if(entity.progress >= 1f){
Call.onUnitRespawn(tile, entity.currentUnit);

View File

@ -154,8 +154,8 @@ public class UnitFactory extends Block{
entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f);
}
//check if grace period had passed
}else if(entity.warmup > produceTime*gracePeriodMultiplier * Vars.state.difficulty.spawnerScaling){
float speedMultiplier = Math.min(0.1f + (entity.warmup - produceTime * gracePeriodMultiplier * Vars.state.difficulty.spawnerScaling) / speedupTime, maxSpeedup);
}else if(entity.warmup > produceTime*gracePeriodMultiplier){
float speedMultiplier = Math.min(0.1f + (entity.warmup - produceTime * gracePeriodMultiplier) / speedupTime, maxSpeedup);
//otherwise, it's an enemy, cheat by not requiring resources
entity.buildTime += entity.delta() * speedMultiplier;
entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f);

View File

@ -77,7 +77,6 @@ public class CrashHandler{
ex(() -> value.addChild("build", new JsonValue(Version.build)));
ex(() -> value.addChild("net", new JsonValue(fn)));
ex(() -> value.addChild("server", new JsonValue(fs)));
ex(() -> value.addChild("gamemode", new JsonValue(Vars.state.mode.name())));
ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name())));
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name"))));
ex(() -> value.addChild("trace", new JsonValue(parseException(e))));

View File

@ -11,7 +11,6 @@ import io.anuke.arc.util.Strings;
import io.anuke.arc.util.serialization.Base64Coder;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.ui.dialogs.FileChooser;
@ -51,22 +50,18 @@ public class DesktopPlatform extends Platform{
DiscordRichPresence presence = new DiscordRichPresence();
if(!state.is(State.menu)){
presence.state = Strings.capitalize(state.mode.name());
presence.state = state.rules.waves ? "Survival" : "Attack";
if(world.getMap() == null){
presence.details = "Unknown Map";
}else if(state.mode.disableWaves){
}else if(!state.rules.waves){
presence.details = Strings.capitalize(world.getMap().name);
}else{
presence.details = Strings.capitalize(world.getMap().name) + " | Wave " + state.wave;
presence.largeImageText = "Wave " + state.wave;
}
if(state.mode != GameMode.attack){
presence.state = Strings.capitalize(state.mode.name());
}else{
presence.state = unitGroups[players[0].getTeam().ordinal()].size() == 1 ? "1 Unit Active" :
(unitGroups[players[0].getTeam().ordinal()].size() + " Units Active");
}
presence.state = unitGroups[players[0].getTeam().ordinal()].size() == 1 ? "1 Unit Active" :
(unitGroups[players[0].getTeam().ordinal()].size() + " Units Active");
if(Net.active()){
presence.partyMax = 16;

View File

@ -48,9 +48,7 @@ public class CrashHandler{
ex(() -> value.addChild("versionNumber", new JsonValue(Version.number)));
ex(() -> value.addChild("versionModifier", new JsonValue(Version.modifier)));
ex(() -> value.addChild("build", new JsonValue(Version.build)));
ex(() -> value.addChild("mode", new JsonValue(Vars.state.mode.name())));
ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name())));
ex(() -> value.addChild("difficulty", new JsonValue(Vars.state.difficulty.name())));
ex(() -> value.addChild("players", new JsonValue(Vars.playerGroup.size())));
ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name"))));
ex(() -> value.addChild("trace", new JsonValue(parseException(e))));

View File

@ -16,8 +16,6 @@ import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.EventType.GameOverEvent;
import io.anuke.mindustry.game.EventType.SectorCompleteEvent;
import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Call;
@ -139,7 +137,7 @@ public class ServerControl implements ApplicationListener{
while(map == previous) map = maps.random();
}
Call.onInfoMessage((state.mode.isPvp
Call.onInfoMessage((state.rules.pvp
? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]")
+ "\nNext selected map:[accent] "+map.name+"[]"
+ (map.meta.author() != null ? " by[accent] " + map.meta.author() + "[]" : "") + "."+
@ -188,7 +186,7 @@ public class ServerControl implements ApplicationListener{
info("Stopped server.");
});
handler.register("host", "[mapname] [mode]", "Open the server with a specific map.", arg -> {
handler.register("host", "[mapname]", "Open the server with a specific map.", arg -> {
if(state.is(State.playing)){
err("Already hosting. Type 'stop' to stop hosting first.");
return;
@ -212,18 +210,7 @@ public class ServerControl implements ApplicationListener{
info("Loading map...");
if(arg.length > 1){
GameMode mode;
try{
mode = GameMode.valueOf(arg[1]);
}catch(IllegalArgumentException e){
err("No gamemode '{0}' found.", arg[1]);
return;
}
state.mode = mode;
}
err("TODO select gamemode");
logic.reset();
world.loadMap(result);
@ -231,7 +218,7 @@ public class ServerControl implements ApplicationListener{
}else{
//TODO
info("TODO play generated map");
err("TODO play generated map");
}
info("Map loaded.");
@ -266,9 +253,9 @@ public class ServerControl implements ApplicationListener{
info("Status: &rserver closed");
}else{
info("Status:");
info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1} &lb/&ly {2} &lb/&ly {3}", Strings.capitalize(world.getMap().name), state.wave, Strings.capitalize(state.difficulty.name()), Strings.capitalize(state.mode.name()));
info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1} &lb/&ly {2} &lb/&ly {3}", Strings.capitalize(world.getMap().name), state.wave);
if(state.mode.disableWaveTimer){
if(!state.rules.waves){
info("&ly {0} enemies.", unitGroups[Team.red.ordinal()].size());
}else{
info("&ly {0} seconds until next wave.", (int) (state.wavetime / 60));
@ -301,7 +288,7 @@ public class ServerControl implements ApplicationListener{
handler.register("difficulty", "<difficulty>", "Set game difficulty.", arg -> {
try{
state.difficulty = Difficulty.valueOf(arg[0]);
state.rules.waveSpacing = Difficulty.valueOf(arg[0]).waveTime;
info("Difficulty set to '{0}'.", arg[0]);
}catch(IllegalArgumentException e){
err("No difficulty with name '{0}' found.", arg[0]);

View File

@ -198,7 +198,7 @@ public class ApplicationTests{
@Test
void inventoryDeposit(){
depositTest(Blocks.smelter, Items.copper);
depositTest(Blocks.surgeSmelter, Items.copper);
depositTest(Blocks.vault, Items.copper);
depositTest(Blocks.thoriumReactor, Items.thorium);
}