mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-11 11:17:11 +07:00
Implemented gamemode validation
This commit is contained in:
parent
c9bd253960
commit
bf7803d554
@ -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
|
||||
|
@ -156,7 +156,7 @@
|
||||
down: button-down,
|
||||
up: button,
|
||||
over: button-over,
|
||||
disabled: button,
|
||||
disabled: button-disabled,
|
||||
disabledFontColor: gray
|
||||
}
|
||||
},
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user