1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-09-23 06:18:00 +03:00

Implemented gamemode validation

This commit is contained in:
Anuken 2019-07-24 23:05:16 -04:00
parent c9bd253960
commit bf7803d554
8 changed files with 80 additions and 113 deletions

View File

@ -530,13 +530,13 @@ keybind.drop_unit.name = Drop Unit
keybind.zoom_minimap.name = Zoom minimap
mode.help.title = Description of modes
mode.survival.name = Survival
mode.survival.description = The normal mode. Limited resources and automatic incoming waves.
mode.survival.description = The normal mode. Limited resources and automatic incoming waves.\n[gray]Requires enemy spawns in the map to play.
mode.sandbox.name = Sandbox
mode.sandbox.description = Infinite resources and no timer for waves.
mode.pvp.name = PvP
mode.pvp.description = Fight against other players locally. Requires at least 2 differently-colored cores in the map to play.
mode.pvp.description = Fight against other players locally.\n[gray]Requires at least 2 differently-colored cores in the map to play.
mode.attack.name = Attack
mode.attack.description = Destroy the enemy's base. No waves. Requires a red core in the map to play.
mode.attack.description = Destroy the enemy's base. No waves.\n[gray]Requires a red core in the map to play.
mode.custom = Custom Rules
rules.infiniteresources = Infinite Resources

View File

@ -156,7 +156,7 @@
down: button-down,
up: button,
over: button-over,
disabled: button,
disabled: button-disabled,
disabledFontColor: gray
}
},

View File

@ -1,15 +1,18 @@
package io.anuke.mindustry.game;
import io.anuke.arc.Core;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.*;
import io.anuke.arc.function.*;
import io.anuke.mindustry.maps.*;
/** Defines preset rule sets.. */
import static io.anuke.mindustry.Vars.waveTeam;
/** Defines preset rule sets. */
public enum Gamemode{
survival(rules -> {
rules.waveTimer = true;
rules.waves = true;
rules.unitDrops = true;
}),
}, map -> map.spawns > 0),
sandbox(rules -> {
rules.infiniteResources = true;
rules.waves = true;
@ -21,7 +24,7 @@ public enum Gamemode{
rules.unitDrops = true;
rules.waves = false;
rules.attackMode = true;
}),
}, map -> map.teams.contains(waveTeam.ordinal())),
pvp(rules -> {
rules.pvp = true;
rules.enemyCoreBuildRadius = 600f;
@ -33,7 +36,7 @@ public enum Gamemode{
rules.unitBuildSpeedMultiplier = 3f;
rules.unitHealthMultiplier = 3f;
rules.attackMode = true;
}),
}, map -> map.teams.size > 1),
editor(true, rules -> {
rules.infiniteResources = true;
rules.editor = true;
@ -44,15 +47,27 @@ public enum Gamemode{
});
private final Consumer<Rules> rules;
private final Predicate<Map> validator;
public final boolean hidden;
public final static Gamemode[] all = values();
Gamemode(Consumer<Rules> rules){
this(false, rules);
}
Gamemode(boolean hidden, Consumer<Rules> rules){
this(hidden, rules, m -> true);
}
Gamemode(Consumer<Rules> rules, Predicate<Map> validator){
this(false, rules, validator);
}
Gamemode(boolean hidden, Consumer<Rules> rules, Predicate<Map> validator){
this.rules = rules;
this.hidden = hidden;
this.validator = validator;
}
/** Applies this preset to this ruleset. */
@ -61,6 +76,11 @@ public enum Gamemode{
return in;
}
/** @return whether this mode can be played on the specified map. */
public boolean valid(Map map){
return validator.test(map);
}
public String description(){
return Core.bundle.get("mode." + name() + ".description");
}

View File

@ -1,21 +1,18 @@
package io.anuke.mindustry.io;
import io.anuke.arc.collection.IntSet;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap;
import io.anuke.arc.graphics.Pixmap.Format;
import io.anuke.arc.util.io.CounterInputStream;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.Pixmap.*;
import io.anuke.arc.util.io.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import io.anuke.mindustry.world.blocks.storage.*;
import java.io.*;
import java.util.zip.InflaterInputStream;
import java.util.zip.*;
import static io.anuke.mindustry.Vars.*;
@ -65,9 +62,8 @@ public class MapIO{
}
public static Pixmap generatePreview(Map map) throws IOException{
//by default, it does not have an enemy core or any other cores
map.tags.put("enemycore", "false");
map.tags.put("othercore", "false");
map.spawns = 0;
map.teams.clear();
try(InputStream is = new InflaterInputStream(map.file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
SaveIO.readHeader(stream);
@ -79,7 +75,6 @@ public class MapIO{
Pixmap walls = new Pixmap(map.width, map.height, Format.RGBA8888);
int black = Color.rgba8888(Color.BLACK);
int shade = Color.rgba8888(0f, 0f, 0f, 0.5f);
IntSet teams = new IntSet();
CachedTile tile = new CachedTile(){
@Override
public void setBlock(Block type){
@ -95,7 +90,7 @@ public class MapIO{
public void setTeam(Team team){
super.setTeam(team);
if(block instanceof CoreBlock){
teams.add(team.ordinal());
map.teams.add(team.ordinal());
}
}
};
@ -121,20 +116,13 @@ public class MapIO{
}else{
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(content.block(floorID), Blocks.air, Blocks.air, Team.none));
}
if(content.block(overlayID) == Blocks.spawn){
map.spawns ++;
}
return tile;
}
}));
if(teams.size > 1){
//map must have other team's cores
map.tags.put("othercore", "true");
}
if(teams.contains(waveTeam.ordinal())){
//map must have default enemy team's core
map.tags.put("enemycore", "true");
}
floors.drawPixmap(walls, 0, 0);
walls.dispose();
return floors;

View File

@ -1,11 +1,11 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.JsonIO;
public class Map implements Comparable<Map>{
@ -23,6 +23,10 @@ public class Map implements Comparable<Map>{
public Texture texture;
/** Build that this map was created in. -1 = unknown or custom build. */
public int build;
/** All teams present on this map.*/
public IntSet teams = new IntSet();
/** Number of enemy spawns on this map.*/
public int spawns = 0;
public Map(FileHandle file, int width, int height, StringMap tags, boolean custom, int version, int build){
this.custom = custom;
@ -63,20 +67,16 @@ public class Map implements Comparable<Map>{
}
/** Whether this map has a core of the enemy 'wave' team. Default: true.
* Used for checking Attack mode validity.*/
* Used for checking Attack mode validity.
public boolean hasEnemyCore(){
return tags.get("enemycore", "true").equals("true");
}
/** Whether this map has a core of any team except the default player team. Default: true.
* Used for checking PvP mode validity.*/
* Used for checking PvP mode validity.
public boolean hasOtherCores(){
return tags.get("othercore", "true").equals("true");
}
public boolean attribute(MapAttribute attr){
return tags.getBool(attr.name());
}
}*/
public String author(){
return tag("author");

View File

@ -1,29 +0,0 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.collection.*;
import io.anuke.arc.function.*;
import static io.anuke.mindustry.Vars.*;
/** Defines a specific type of attribute for a map, usually whether or not it supports a certain type of mode.*/
public enum MapAttribute{
/** Whether a map has a player spawnpoint in it.*/
spawnpoint(teams -> teams.contains(defaultTeam.ordinal())),
/** Whether a map has a wave team core to attack.*/
attack(teams -> teams.contains(waveTeam.ordinal())),
/** Whether this map supports PvP.*/
pvp(teams -> teams.size > 1);
private final Predicate<IntSet> validator;
public static final MapAttribute[] all = values();
MapAttribute(Predicate<IntSet> set){
this.validator = set;
}
//todo also take into account enemy spawnpoints
public boolean validate(IntSet teams){
return validator.test(teams);
}
}

View File

@ -1,26 +1,25 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
import io.anuke.arc.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.ExceptionRunnable;
import io.anuke.arc.graphics.Texture;
import io.anuke.arc.files.*;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.util.*;
import io.anuke.arc.util.serialization.Json;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.io.LegacyMapIO;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.storage.*;
import java.io.IOException;
import java.io.StringWriter;
import java.io.*;
import static io.anuke.mindustry.Vars.*;
public class Maps implements Disposable{
/** List of all built-in maps. Filenames only. */
private static String[] defaultMapNames = {"fortress", "labyrinth", "islands", "tendrils", "caldera", "glacier", "vein"};
private static String[] defaultMapNames = {"fortress", "labyrinth", "islands", "tendrils", "caldera", "glacier", "veins"};
/** All maps stored in an ordered array. */
private Array<Map> maps = new Array<>();
/** Serializer for meta. */
@ -110,31 +109,24 @@ public class Maps implements Disposable{
MapIO.writeMap(file, map);
if(!headless){
//by default, it does not have an enemy core or any other cores
map.tags.put("enemycore", "false");
map.tags.put("othercore", "false");
IntSet teams = new IntSet();
//reset attributes
map.teams.clear();
map.spawns = 0;
for(int x = 0; x < map.width; x++){
for(int y = 0; y < map.height; y++){
Tile tile = world.getTiles()[x][y];
if(tile.block() instanceof CoreBlock){
teams.add(tile.getTeamID());
map.teams.add(tile.getTeamID());
}
if(tile.overlay() == Blocks.spawn){
map.spawns ++;
}
}
}
if(teams.size > 1){
//map must have other team's cores
map.tags.put("othercore", "true");
}
if(teams.contains(waveTeam.ordinal())){
//map must have default enemy team's core
map.tags.put("enemycore", "true");
}
map.texture = new Texture(MapIO.generatePreview(world.getTiles()));
}
maps.add(map);

View File

@ -3,7 +3,7 @@ package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Scaling;
import io.anuke.arc.util.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
@ -32,9 +32,9 @@ public class MapPlayDialog extends FloatingDialog{
title.setText(map.name());
cont.clearChildren();
//reset to a valid mode after switching to attack
if((selectedGamemode == Gamemode.attack && !map.hasEnemyCore()) || (selectedGamemode == Gamemode.pvp && !map.hasOtherCores())){
selectedGamemode = Gamemode.survival;
//reset to any valid mode after switching to attack (one must exist)
if(!selectedGamemode.valid(map)){
selectedGamemode = Structs.find(Gamemode.all, m -> m.valid(map));
}
rules = map.rules();
@ -50,14 +50,10 @@ public class MapPlayDialog extends FloatingDialog{
for(Gamemode mode : Gamemode.values()){
if(mode.hidden) continue;
if((mode == Gamemode.attack && !map.hasEnemyCore()) || (mode == Gamemode.pvp && !map.hasOtherCores())){
continue;
}
modes.addButton(mode.toString(), "toggle", () -> {
selectedGamemode = mode;
rules = mode.apply(map.rules());
}).update(b -> b.setChecked(selectedGamemode == mode)).size(140f, 54f);
}).update(b -> b.setChecked(selectedGamemode == mode)).size(140f, 54f).disabled(!mode.valid(map));
if(i++ % 2 == 1) modes.row();
}
selmode.add(modes);