Custom wave patterns / Fixed checkbox sprite / Bugfixes

This commit is contained in:
Anuken
2019-03-14 14:01:22 -04:00
parent f2662045ed
commit ed105b102d
46 changed files with 1375 additions and 875 deletions

View File

@@ -24,7 +24,6 @@ import static io.anuke.mindustry.Vars.*;
public class WaveSpawner{
private static final float shockwaveBase = 380f, shockwaveRand = 0f, maxShockwaveDst = shockwaveBase + shockwaveRand;
private Array<SpawnGroup> groups;
private Array<FlyerSpawn> flySpawns = new Array<>();
private Array<GroundSpawn> groundSpawns = new Array<>();
private IntArray loadedSpawns = new IntArray();
@@ -59,7 +58,7 @@ public class WaveSpawner{
public void spawnEnemies(){
for(SpawnGroup group : groups){
for(SpawnGroup group : state.rules.spawns){
int spawned = group.getUnitsSpawned(state.wave);
float spawnX, spawnY;
@@ -105,7 +104,6 @@ public class WaveSpawner{
flySpawns.clear();
groundSpawns.clear();
groups = state.rules.spawns;
for(int x = 0; x < world.width(); x++){
for(int y = 0; y < world.height(); y++){

View File

@@ -8,20 +8,17 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap;
import io.anuke.arc.util.Log;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.effect.Fire;
import io.anuke.mindustry.entities.effect.Lightning;
import io.anuke.mindustry.entities.effect.Puddle;
import io.anuke.mindustry.entities.traits.TypeTrait;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.LegacyColorMapper;
@@ -237,6 +234,10 @@ public class ContentLoader{
return getBy(ContentType.zone);
}
public Array<UnitType> units(){
return getBy(ContentType.unit);
}
/**
* Registers sync IDs for all types of sync entities.
* Do not register units here!

View File

@@ -5,15 +5,18 @@ import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.ApplicationListener;
import io.anuke.arc.Events;
import io.anuke.arc.collection.ObjectSet.ObjectSetIterator;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Effects;
import io.anuke.mindustry.entities.Entities;
import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.EntityQuery;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.EventType.GameOverEvent;
import io.anuke.mindustry.game.EventType.PlayEvent;
import io.anuke.mindustry.game.EventType.ResetEvent;
import io.anuke.mindustry.game.EventType.WaveEvent;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
@@ -58,6 +61,7 @@ public class Logic implements ApplicationListener{
public void play(){
state.set(State.playing);
state.wavetime = state.rules.waveSpacing * 2; //grace period of 2x wave time before game starts
state.rules.spawns = world.getMap().getWaves();
Events.fire(new PlayEvent());
}

View File

@@ -12,6 +12,7 @@ import io.anuke.mindustry.world.Pos;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
public enum EditorTool{
pick{
@@ -99,8 +100,8 @@ public enum EditorTool{
Block block = tile.block();
boolean synth = editor.drawBlock.synthetic();
dest = isfloor ? floor : block;
Block draw = editor.drawBlock;
dest = draw instanceof OreBlock ? tile.oreBlock() : isfloor ? floor : block;
if(dest == draw || block == Blocks.part || block.isMultiblock()){
return;
@@ -189,7 +190,7 @@ public enum EditorTool{
boolean eq(int px, int py){
Tile tile = data.tile(px, py);
return (isfloor ? tile.floor() : tile.block()) == dest;
return (data.drawBlock instanceof OreBlock ? tile.oreBlock() : isfloor ? tile.floor() : tile.block()) == dest && !(data.drawBlock instanceof OreBlock && tile.floor().isLiquid);
}
},
zoom;

View File

@@ -1,18 +1,234 @@
package io.anuke.mindustry.editor;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.ui.TextField.TextFieldFilter;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align;
import io.anuke.arc.util.Strings;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.StatusEffects;
import io.anuke.mindustry.content.UnitTypes;
import io.anuke.mindustry.game.DefaultWaves;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.UnitType;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.game.SpawnGroup.never;
public class WaveInfoDialog extends FloatingDialog{
private final MapEditor editor;
private final static int displayed = 20;
private Array<SpawnGroup> groups;
private Table table, preview;
private int start = 0;
private UnitType lastType = UnitTypes.dagger;
private float updateTimer, updatePeriod = 1f;
public WaveInfoDialog(MapEditor editor){
super("$editor.waves");
super("$waves.title");
this.editor = editor;
shown(this::setup);
hidden(() -> editor.getTags().put("waves", world.maps.writeWaves(groups)));
addCloseButton();
buttons.addButton("$settings.reset", () -> ui.showConfirm("$confirm", "$settings.clear.confirm", () ->{
groups = null;
buildGroups();
})).size(270f, 64f);
}
void setup(){
groups = world.maps.readWaves(editor.getTags().get("waves"));
cont.clear();
cont.table("button-disabled", main -> {
main.pane(t -> table = t).growX().growY().get().setScrollingDisabled(true, false);
main.row();
main.addButton("$add", () -> {
if(groups == null) groups = new Array<>();
groups.add(new SpawnGroup(lastType));
buildGroups();
}).growX().height(80f);
}).width(390f).growY();
cont.table("button-disabled", m -> {
m.add("Preview").color(Color.LIGHT_GRAY).growX().center().get().setAlignment(Align.center, Align.center);
m.row();
m.addButton("-", () -> {}).update(t -> {
if(t.getClickListener().isPressed()){
updateTimer += Time.delta();
if(updateTimer >= updatePeriod){
start = Math.max(start - 1, 0);
updateTimer = 0f;
updateWaves();
}
}
}).growX().height(70f);
m.row();
m.pane(t -> preview = t).grow().get().setScrollingDisabled(true, false);
m.row();
m.addButton("+", () -> {}).update(t -> {
if(t.getClickListener().isPressed()){
updateTimer += Time.delta();
if(updateTimer >= updatePeriod){
start ++;
updateTimer = 0f;
updateWaves();
}
}
}).growX().height(70f);
}).growY().width(200f).growY();
buildGroups();
}
void buildGroups(){
table.clear();
table.top();
table.margin(10f);
if(groups != null){
for(SpawnGroup group : groups){
table.table("button-disabled", t -> {
t.margin(6f).defaults().pad(2).padLeft(5f).growX().left();
t.addButton(b -> {
b.left();
b.addImage(group.type.iconRegion).size(30f).padRight(3);
b.add(group.type.localizedName).color(Pal.accent);
}, () -> showUpdate(group)).pad(-6f).padBottom(0f);
t.row();
t.table(spawns -> {
spawns.addField("" + group.begin, TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePostiveInt(text)){
group.begin = Strings.parseInt(text);
updateWaves();
}
}).width(100f);
spawns.add("$waves.to").padLeft(4).padRight(4);
spawns.addField(group.end == never ? "" : group.end + "", TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePostiveInt(text)){
group.end = Strings.parseInt(text);
updateWaves();
}else if(text.isEmpty()){
group.end = never;
updateWaves();
}
}).width(100f).get().setMessageText(Core.bundle.get("waves.never"));
});
t.row();
t.table(p -> {
p.add("$waves.every").padRight(4);
p.addField(group.spacing + "", TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePostiveInt(text)){
group.spacing = Strings.parseInt(text);
updateWaves();
}
}).width(100f);
p.add("$waves.waves").padLeft(4);
});
t.row();
t.table(a -> {
a.addField(group.unitAmount + "", TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePostiveInt(text)){
group.unitAmount = Strings.parseInt(text);
updateWaves();
}
}).width(80f);
a.add(" + ");
a.addField(Math.max((int)(Mathf.isZero(group.unitScaling) ? 0 : 1f/group.unitScaling), 0) + "", TextFieldFilter.digitsOnly, text -> {
if(Strings.canParsePostiveInt(text)){
group.unitScaling = 1f / Strings.parseInt(text);
updateWaves();
}
}).width(80f);
a.add("$waves.perspawn").padLeft(4);
});
t.row();
t.addCheck("$waves.boss", b -> group.effect = (b ? StatusEffects.boss : null)).padTop(4).update(b -> b.setChecked(group.effect == StatusEffects.boss));
t.row();
t.addButton("$waves.remove", () -> {
groups.remove(group);
table.getCell(t).pad(0f);
t.remove();
updateWaves();
}).growX().pad(-6f).padTop(5);
}).width(340f).pad(3);
table.row();
}
}else{
table.add("$editor.default");
}
updateWaves();
}
void showUpdate(SpawnGroup group){
FloatingDialog dialog = new FloatingDialog("");
dialog.setFillParent(false);
int i = 0;
for(UnitType type : content.units()){
dialog.cont.addButton(t -> {
t.left();
t.addImage(type.iconRegion).size(40f).padRight(2f);
t.add(type.localizedName);
}, () -> {
lastType = type;
group.type = type;
dialog.hide();
buildGroups();
}).pad(2).margin(12f).fillX();
if(++i % 2 == 0)dialog.cont.row();
}
dialog.show();
}
void updateWaves(){
preview.clear();
preview.top();
Array<SpawnGroup> groups = (this.groups == null ? DefaultWaves.getDefaultSpawns() : this.groups);
for(int i = start; i < displayed + start; i ++){
int wave = i;
preview.table("button-disabled", table -> {
table.add(wave + "").color(Pal.accent).center().colspan(2).get().setAlignment(Align.center, Align.center);
table.row();
int[] spawned = new int[Vars.content.getBy(ContentType.unit).size];
for(SpawnGroup spawn : groups){
spawned[spawn.type.id] += spawn.getUnitsSpawned(wave);
}
for(int j = 0; j < spawned.length; j++){
if(spawned[j] > 0){
UnitType type = content.getByID(ContentType.unit, j);
table.addImage(type.iconRegion).size(30f).padRight(4);
table.add(spawned[j] + "x").color(Color.LIGHT_GRAY).padRight(6);
table.row();
}
}
if(table.getChildren().size == 1){
table.add("$none").color(Pal.remove);
}
}).width(130f).pad(2f);
preview.row();
}
}
}

View File

@@ -17,26 +17,26 @@ import static io.anuke.mindustry.Vars.content;
* Each spawn group can have multiple sub-groups spawned in different areas of the map.
*/
public class SpawnGroup implements Serializable{
protected static final int never = Integer.MAX_VALUE;
public static final int never = Integer.MAX_VALUE;
/**The unit type spawned*/
public UnitType type;
/**When this spawn should end*/
protected int end = never;
public int end = never;
/**When this spawn should start*/
protected int begin;
public int begin;
/**The spacing, in waves, of spawns. For example, 2 = spawns every other wave*/
protected int spacing = 1;
public int spacing = 1;
/**Maximum amount of units that spawn*/
protected int max = 40;
public int max = 40;
/**How many waves need to pass before the amount of units spawned increases by 1*/
protected float unitScaling = 9999f;
public float unitScaling = never;
/**Amount of enemies spawned initially, with no scaling*/
protected int unitAmount = 1;
public int unitAmount = 1;
/**Status effect applied to the spawned unit. Null to disable.*/
protected StatusEffect effect;
public StatusEffect effect;
/**Items this unit spawns with. Null to disable.*/
protected ItemStack items;
public ItemStack items;
public SpawnGroup(UnitType type){
this.type = type;
@@ -76,15 +76,14 @@ public class SpawnGroup implements Serializable{
@Override
public void write (Json json) {
json.writeObjectStart();
json.writeValue("type", type.name);
json.writeValue("begin", begin);
json.writeValue("end", end);
json.writeValue("spacing", spacing);
json.writeValue("max", max);
json.writeValue("scaling", unitScaling);
json.writeValue("amount", unitAmount);
json.writeObjectEnd();
if(begin != 0) json.writeValue("begin", begin);
if(end != never) json.writeValue("end", end);
if(spacing != 1) json.writeValue("spacing", spacing);
if(max != 40) json.writeValue("max", max);
if(unitScaling != never) json.writeValue("scaling", unitScaling);
if(unitAmount != 1) json.writeValue("amount", unitAmount);
if(effect != null) json.writeValue("effect", effect.id);
}
@Override
@@ -96,6 +95,7 @@ public class SpawnGroup implements Serializable{
max = data.getInt("spacing", 40);
unitScaling = data.getFloat("scaling", never);
unitAmount = data.getInt("amount", 1);
effect = content.getByID(ContentType.status, data.getInt("effect", -1));
}
@Override

View File

@@ -8,9 +8,7 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap;
import io.anuke.arc.graphics.Pixmap.Format;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Strings;
import io.anuke.arc.util.Structs;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.Team;
@@ -70,6 +68,7 @@ public class MapIO{
}
public static Pixmap generatePreview(Map map) throws IOException{
Time.mark();
Pixmap floor = new Pixmap(map.width, map.height, Format.RGBA8888);
Pixmap wall = new Pixmap(map.width, map.height, Format.RGBA8888);
int black = Color.rgba8888(Color.BLACK);

View File

@@ -6,8 +6,6 @@ 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;
@@ -29,14 +27,11 @@ 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.all().find(m -> m.name().equals(mapname));
if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true);
world.setMap(map);
state.rules.spawns = map.getWaves();
int wave = stream.readInt();
float wavetime = stream.readFloat();

View File

@@ -1,12 +1,18 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture;
import io.anuke.arc.util.Log;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.DefaultWaves;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.io.MapIO;
import static io.anuke.mindustry.Vars.world;
public class Map{
/** Whether this is a custom map.*/
public final boolean custom;
@@ -41,6 +47,20 @@ public class Map{
this(file, width, height, tags, custom, MapIO.version);
}
public Array<SpawnGroup> getWaves(){
if(tags.containsKey("waves")){
try{
return world.maps.readWaves(tags.get("waves"));
}catch(Exception e){
Log.err("Malformed waves: {0}", tags.get("waves"));
e.printStackTrace();
return DefaultWaves.getDefaultSpawns();
}
}else{
return DefaultWaves.getDefaultSpawns();
}
}
public int getHightScore(){
return Core.settings.getInt("hiscore" + file.nameWithoutExtension(), 0);
}

View File

@@ -9,22 +9,25 @@ import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.serialization.Json;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.world.Tile;
import java.io.IOException;
import java.io.StringWriter;
import static io.anuke.mindustry.Vars.*;
public class Maps implements Disposable{
/** List of all built-in maps. Filenames only.*/
private static final String[] defaultMapNames = {"Fortress"};
private static final String[] defaultMapNames = {"fortress"};
/** All maps stored in an ordered array. */
private Array<Map> maps = new Array<>();
/** Serializer for meta.*/
private Json json = new Json();
public Maps(){
}
/** Returns a list of all maps, including custom ones. */
@@ -134,6 +137,28 @@ public class Maps implements Disposable{
map.file.delete();
}
public String writeWaves(Array<SpawnGroup> groups){
if(groups == null){
return "[]";
}
StringWriter buffer = new StringWriter();
json.setWriter(buffer);
json.writeArrayStart();
for(int i = 0; i < groups.size; i++){
json.writeObjectStart(SpawnGroup.class, SpawnGroup.class);
groups.get(i).write(json);
json.writeObjectEnd();
}
json.writeArrayEnd();
return buffer.toString();
}
public Array<SpawnGroup> readWaves(String str){
return str == null ? null : str.equals("[]") ? new Array<>() : Array.with(json.fromJson(SpawnGroup[].class, str));
}
/** Find a new filename to put a map to.*/
private FileHandle findFile(){
//find a map name that isn't used.
@@ -173,6 +198,12 @@ public class Maps implements Disposable{
@Override
public void dispose(){
for(Map map : maps){
if(map.texture != null){
map.texture.dispose();
map.texture = null;
}
}
maps.clear();
}
}

View File

@@ -124,7 +124,7 @@ public class HudFragment extends Fragment{
stuff.add(stack).width(dsize * 4 + 3f);
stuff.row();
stuff.table("button", t -> t.margin(10f).add(new Bar("boss.health", Pal.health, () -> state.boss() == null ? 0f : state.boss().healthf()).blink(Color.WHITE))
.grow()).fillX().visible(() -> world.isZone() && state.boss() != null).height(60f).get();
.grow()).fillX().visible(() -> state.rules.waves && state.boss() != null).height(60f).get();
stuff.row();
}).visible(() -> shown);
});

View File

@@ -138,7 +138,7 @@ public class MenuFragment extends Fragment{
FloatingDialog dialog = new FloatingDialog("$play");
dialog.setFillParent(false);
dialog.addCloseButton();
dialog.cont.defaults().size(230f, 64f);
dialog.cont.defaults().size(210f, 64f);
dialog.cont.add(new MenuButton("icon-editor", "$newgame", () -> {
dialog.hide();
ui.custom.show();