New WIP custom map selection

This commit is contained in:
Anuken
2019-05-12 10:32:08 -04:00
parent e5d19a9819
commit 4ae2370137
9 changed files with 225 additions and 153 deletions

View File

@@ -20,7 +20,6 @@ import io.anuke.arc.util.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Gamemode;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
@@ -226,7 +225,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
hide();
//only reset the player; logic.reset() will clear entities, which we do not want
player.reset();
state.rules = Gamemode.editor.get();
// state.rules = Gamemode.editor.get();
world.setMap(new Map(StringMap.of(
"name", "Editor Playtesting",
"width", editor.width(),

View File

@@ -1,63 +1,64 @@
package io.anuke.mindustry.game;
import io.anuke.arc.Core;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.function.Consumer;
/** Defines preset rule sets.. */
public enum Gamemode{
survival(() -> new Rules(){{
waveTimer = true;
waves = true;
unitDrops = true;
spawns = DefaultWaves.get();
}}),
sandbox(() -> new Rules(){{
infiniteResources = true;
waves = true;
waveTimer = false;
respawnTime = 0f;
}}),
attack(() -> new Rules(){{
enemyCheat = true;
unitDrops = true;
waves = false;
attackMode = true;
}}),
pvp(() -> new Rules(){{
pvp = true;
enemyCoreBuildRadius = 600f;
respawnTime = 60 * 10;
buildCostMultiplier = 0.5f;
buildSpeedMultiplier = 2f;
playerDamageMultiplier = 0.45f;
playerHealthMultiplier = 0.8f;
unitBuildSpeedMultiplier = 3f;
unitHealthMultiplier = 2f;
attackMode = true;
}}),
editor(true, () -> new Rules(){{
infiniteResources = true;
editor = true;
waves = false;
enemyCoreBuildRadius = 0f;
waveTimer = false;
respawnTime = 0f;
}}),;
survival(rules -> {
rules.waveTimer = true;
rules.waves = true;
rules.unitDrops = true;
}),
sandbox(rules -> {
rules.infiniteResources = true;
rules.waves = true;
rules.waveTimer = false;
rules.respawnTime = 0f;
}),
attack(rules -> {
rules.enemyCheat = true;
rules.unitDrops = true;
rules.waves = false;
rules.attackMode = true;
}),
pvp(rules -> {
rules.pvp = true;
rules.enemyCoreBuildRadius = 600f;
rules.respawnTime = 60 * 10;
rules.buildCostMultiplier = 0.5f;
rules.buildSpeedMultiplier = 2f;
rules.playerDamageMultiplier = 0.45f;
rules.playerHealthMultiplier = 0.8f;
rules.unitBuildSpeedMultiplier = 3f;
rules.unitHealthMultiplier = 2f;
rules.attackMode = true;
}),
editor(true, rules -> {
rules.infiniteResources = true;
rules.editor = true;
rules.waves = false;
rules.enemyCoreBuildRadius = 0f;
rules.waveTimer = false;
rules.respawnTime = 0f;
});
private final Supplier<Rules> rules;
private final Consumer<Rules> rules;
public final boolean hidden;
Gamemode(Supplier<Rules> rules){
Gamemode(Consumer<Rules> rules){
this(false, rules);
}
Gamemode(boolean hidden, Supplier<Rules> rules){
Gamemode(boolean hidden, Consumer<Rules> rules){
this.rules = rules;
this.hidden = hidden;
}
public Rules get(){
return rules.get();
/** Applies this preset to this ruleset. */
public Rules apply(Rules in){
rules.accept(in);
return in;
}
public String description(){

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.game;
import io.anuke.annotations.Annotations.Serialize;
import io.anuke.arc.collection.Array;
import io.anuke.mindustry.io.JsonIO;
import io.anuke.mindustry.type.Zone;
/**
@@ -62,4 +63,9 @@ public class Rules{
public boolean attackMode = false;
/** Whether this is the editor gamemode. */
public boolean editor = false;
/** Copies this ruleset exactly. Not very efficient at all, do not use often. */
public Rules copy(){
return JsonIO.read(Rules.class, JsonIO.write(this));
}
}

View File

@@ -11,12 +11,12 @@ import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import java.io.*;
import java.util.zip.InflaterInputStream;
import static io.anuke.mindustry.Vars.bufferSize;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.*;
/** Reads and writes map files. */
//TODO does this class even need to exist??? move to Maps?
@@ -64,6 +64,10 @@ 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");
try(InputStream is = new InflaterInputStream(map.file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
SaveIO.readHeader(stream);
int version = stream.readInt();
@@ -84,6 +88,22 @@ public class MapIO{
floors.drawPixel(x, floors.getHeight() - 1 - y + 1, shade);
}
}
@Override
public void setTeam(Team team){
super.setTeam(team);
if(block instanceof CoreBlock){
if(team != defaultTeam){
//map must have other team's cores
map.tags.put("othercore", "true");
}
if(team == waveTeam){
//map must have default enemy team's core
map.tags.put("enemycore", "true");
}
}
}
};
ver.region("content", stream, counter, ver::readContentHeader);

View File

@@ -5,6 +5,8 @@ import io.anuke.arc.collection.StringMap;
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.io.JsonIO;
public class Map implements Comparable<Map>{
/** Whether this is a custom map. */
@@ -53,6 +55,22 @@ public class Map implements Comparable<Map>{
Vars.data.modified();
}
public Rules rules(){
return JsonIO.read(Rules.class, tags.get("rules", "{}"));
}
/** Whether this map has a core of the enemy 'wave' team. Default: true.
* 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.*/
public boolean hasOtherCores(){
return tags.get("othercore", "true").equals("true");
}
public String author(){
return tag("author");
}

View File

@@ -2,28 +2,18 @@ package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.ButtonGroup;
import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.ScrollPane;
import io.anuke.arc.scene.ui.TextButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.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.Rules;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.Vars.world;
public class CustomGameDialog extends FloatingDialog{
Difficulty difficulty = Difficulty.normal;
CustomRulesDialog dialog = new CustomRulesDialog();
Rules rules;
Gamemode selectedGamemode;
private MapPlayDialog dialog = new MapPlayDialog();
public CustomGameDialog(){
super("$customgame");
@@ -33,9 +23,9 @@ public class CustomGameDialog extends FloatingDialog{
}
void setup(){
selectedGamemode = Gamemode.survival;
rules = selectedGamemode.get();
clearChildren();
stack(cont, buttons).grow();
buttons.bottom();
cont.clear();
Table maps = new Table();
@@ -44,64 +34,9 @@ public class CustomGameDialog extends FloatingDialog{
pane.setFadeScrollBars(false);
int maxwidth = (Core.graphics.isPortrait() ? 2 : 4);
Table selmode = new Table();
ButtonGroup<TextButton> group = new ButtonGroup<>();
selmode.add("$level.mode").colspan(4);
selmode.row();
int i = 0;
Table modes = new Table();
for(Gamemode mode : Gamemode.values()){
if(mode.hidden) continue;
modes.addButton(mode.toString(), "toggle", () -> {
selectedGamemode = mode;
rules = mode.get();
dialog.selectedGamemode = null;
dialog.rules = null;
}).update(b -> b.setChecked(selectedGamemode == mode)).group(group).size(140f, 54f);
if(i++ % 2 == 1) modes.row();
}
selmode.add(modes);
selmode.addButton("?", this::displayGameModeHelp).width(50f).fillY().padLeft(18f);
cont.add(selmode);
cont.row();
Difficulty[] ds = Difficulty.values();
float s = 50f;
Table sdif = new Table();
sdif.add("$setting.difficulty.name").colspan(3);
sdif.row();
sdif.defaults().height(s + 4);
sdif.addImageButton("icon-arrow-left", 10 * 3, () -> {
difficulty = (ds[Mathf.mod(difficulty.ordinal() - 1, ds.length)]);
state.wavetime = difficulty.waveTime;
}).width(s);
sdif.addButton("", () -> {
})
.update(t -> {
t.setText(difficulty.toString());
t.touchable(Touchable.disabled);
}).width(180f);
sdif.addImageButton("icon-arrow-right", 10 * 3, () -> {
difficulty = (ds[Mathf.mod(difficulty.ordinal() + 1, ds.length)]);
state.wavetime = difficulty.waveTime;
}).width(s);
sdif.addButton("$customize", () -> dialog.show(rules, selectedGamemode)).width(140).padLeft(10);
cont.add(sdif);
cont.row();
float images = 146f;
i = 0;
int i = 0;
maps.defaults().width(170).fillY().top().pad(4f);
for(Map map : world.maps.all()){
@@ -122,10 +57,7 @@ public class CustomGameDialog extends FloatingDialog{
border.setScaling(Scaling.fit);
image.replaceImage(border);
image.clicked(() -> {
hide();
control.playMap(map, (dialog.rules == null) ? rules : dialog.rules);
});
image.clicked(() -> dialog.show(map));
maps.add(image);
@@ -138,23 +70,4 @@ public class CustomGameDialog extends FloatingDialog{
cont.add(pane).uniformX();
}
private void displayGameModeHelp(){
FloatingDialog d = new FloatingDialog(Core.bundle.get("mode.help.title"));
d.setFillParent(false);
Table table = new Table();
table.defaults().pad(1f);
ScrollPane pane = new ScrollPane(table);
pane.setFadeScrollBars(false);
table.row();
for(Gamemode mode : Gamemode.values()){
if(mode.hidden) continue;
table.labelWrap("[accent]" + mode.toString() + ":[] [lightgray]" + mode.description()).width(400f);
table.row();
}
d.cont.add(pane);
d.buttons.addButton("$ok", d::hide).size(110, 50).pad(10f);
d.show();
}
}

View File

@@ -5,7 +5,6 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Strings;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Gamemode;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.graphics.Pal;
@@ -14,7 +13,6 @@ import static io.anuke.mindustry.Vars.tilesize;
public class CustomRulesDialog extends FloatingDialog{
private Table main;
public Rules rules;
public Gamemode selectedGamemode;
public CustomRulesDialog(){
super("$mode.custom");
@@ -24,9 +22,9 @@ public class CustomRulesDialog extends FloatingDialog{
addCloseButton();
}
public void show(Rules rules, Gamemode gamemode){
public void show(Rules rules){
this.rules = rules;
this.selectedGamemode = gamemode;
// this.selectedGamemode = gamemode;
show();
}
@@ -35,7 +33,7 @@ public class CustomRulesDialog extends FloatingDialog{
cont.pane(m -> main = m);
main.margin(10f);
main.addButton("$settings.reset", () -> {
rules = selectedGamemode.get();
//rules = selectedGamemode.get();
setup();
}).size(300f, 50f);
main.left().defaults().fillX().left().pad(5);
@@ -63,7 +61,7 @@ public class CustomRulesDialog extends FloatingDialog{
number("$rules.playerhealthmultiplier", f -> rules.playerHealthMultiplier = f, () -> rules.playerHealthMultiplier);
title("$rules.title.unit");
check("$rules.unitdrops", b -> rules.unitDrops = b, () -> rules.unitDrops, ()->true);
check("$rules.unitdrops", b -> rules.unitDrops = b, () -> rules.unitDrops, () -> true);
number("$rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier);
number("$rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
number("$rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);

View File

@@ -0,0 +1,117 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.state;
public class MapPlayDialog extends FloatingDialog{
Difficulty difficulty = Difficulty.normal;
CustomRulesDialog dialog = new CustomRulesDialog();
Rules rules;
Gamemode selectedGamemode;
public MapPlayDialog(){
super("");
addCloseButton();
}
public void show(Map map){
title.setText(map.name());
cont.clearChildren();
selectedGamemode = Gamemode.survival;
rules = selectedGamemode.apply(new Rules());
Table selmode = new Table();
ButtonGroup<TextButton> group = new ButtonGroup<>();
selmode.add("$level.mode").colspan(4);
selmode.row();
int i = 0;
Table modes = new Table();
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.get();
//dialog.selectedGamemode = null;
dialog.rules = null;
}).update(b -> b.setChecked(selectedGamemode == mode)).group(group).size(140f, 54f);
if(i++ % 2 == 1) modes.row();
}
selmode.add(modes);
selmode.addButton("?", this::displayGameModeHelp).width(50f).fillY().padLeft(18f);
cont.add(selmode);
cont.row();
Difficulty[] ds = Difficulty.values();
float s = 50f;
Table sdif = new Table();
sdif.add("$setting.difficulty.name").colspan(3);
sdif.row();
sdif.defaults().height(s + 4);
sdif.addImageButton("icon-arrow-left", 10 * 3, () -> {
difficulty = (ds[Mathf.mod(difficulty.ordinal() - 1, ds.length)]);
state.wavetime = difficulty.waveTime;
}).width(s);
sdif.addButton("", () -> {}).update(t -> {
t.setText(difficulty.toString());
t.touchable(Touchable.disabled);
}).width(180f);
sdif.addImageButton("icon-arrow-right", 10 * 3, () -> {
difficulty = (ds[Mathf.mod(difficulty.ordinal() + 1, ds.length)]);
state.wavetime = difficulty.waveTime;
}).width(s);
sdif.addButton("$customize", () -> dialog.show(rules)).width(140).padLeft(10);
cont.add(sdif);
cont.row();
if(map.hasTag("description")){
cont.add(map.description()).color(Color.LIGHT_GRAY);
cont.row();
}
cont.add(new BorderImage(map.texture, 3f)).grow().get().setScaling(Scaling.fit);
show();
}
private void displayGameModeHelp(){
FloatingDialog d = new FloatingDialog(Core.bundle.get("mode.help.title"));
d.setFillParent(false);
Table table = new Table();
table.defaults().pad(1f);
ScrollPane pane = new ScrollPane(table);
pane.setFadeScrollBars(false);
table.row();
for(Gamemode mode : Gamemode.values()){
if(mode.hidden) continue;
table.labelWrap("[accent]" + mode.toString() + ":[] [lightgray]" + mode.description()).width(400f);
table.row();
}
d.cont.add(pane);
d.buttons.addButton("$ok", d::hide).size(110, 50).pad(10f);
d.show();
}
}