Merge remote-tracking branch 'origin/4.0' into 4.0

# Conflicts:
#	core/src/io/anuke/mindustry/Vars.java
This commit is contained in:
Anuken
2018-07-13 19:54:20 -04:00
433 changed files with 25227 additions and 23221 deletions

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry;
import com.badlogic.gdx.utils.async.AsyncExecutor;
import io.anuke.mindustry.core.*;
import io.anuke.mindustry.io.BundleLoader;
import io.anuke.ucore.core.Timers;
@@ -9,36 +8,35 @@ import io.anuke.ucore.util.Log;
import static io.anuke.mindustry.Vars.*;
public class Mindustry extends ModuleCore {
private AsyncExecutor exec = new AsyncExecutor(1);
public class Mindustry extends ModuleCore{
@Override
public void init(){
Timers.mark();
@Override
public void init(){
Timers.mark();
Vars.init();
Vars.init();
debug = Platform.instance.isDebug();
debug = Platform.instance.isDebug();
Log.setUseColors(false);
BundleLoader.load();
ContentLoader.load();
Log.setUseColors(false);
BundleLoader.load();
ContentLoader.load();
module(logic = new Logic());
module(world = new World());
module(control = new Control());
module(renderer = new Renderer());
module(ui = new UI());
module(netServer = new NetServer());
module(netClient = new NetClient());
module(logic = new Logic());
module(world = new World());
module(control = new Control());
module(renderer = new Renderer());
module(ui = new UI());
module(netServer = new NetServer());
module(netClient = new NetClient());
Log.info("Time to load [total]: {0}", Timers.elapsed());
}
}
@Override
public void render(){
super.render();
threads.handleRender();
}
@Override
public void render(){
super.render();
threads.handleRender();
}
}

View File

@@ -19,8 +19,8 @@ import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.DrawTrait;
import io.anuke.ucore.entities.impl.EffectEntity;
import io.anuke.ucore.entities.trait.DrawTrait;
import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.OS;
import io.anuke.ucore.util.Translator;
@@ -28,167 +28,150 @@ import io.anuke.ucore.util.Translator;
import java.util.Locale;
public class Vars{
public static boolean testMobile;
//shorthand for whether or not this is running on android or ios
public static boolean mobile;
public static boolean ios;
public static boolean android;
//shorthand for whether or not this is running on GWT
public static boolean gwt;
//respawn time in frames
public static final float respawnduration = 60*4;
//time between waves in frames (on normal mode)
public static final float wavespace = 60*60*2f;
//waves can last no longer than 3 minutes, otherwise the next one spawns
public static final float maxwavespace = 60*60*4f;
//set ridiculously high for now
public static final float coreBuildRange = 800999f;
//discord group URL
public static final String discordURL = "https://discord.gg/BKADYds";
public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases";
//directory for user-created map data
public static FileHandle customMapDirectory;
//save file directory
public static FileHandle saveDirectory;
public static String mapExtension = "mmap";
public static String saveExtension = "msav";
//scale of the font
public static float fontScale;
//camera zoom displayed on startup
public static int baseCameraScale;
//if true, player speed will be increased, massive amounts of resources will be given on start, and other debug options will be available
public static boolean debug = false;
public static boolean console = false;
//whether the player can clip through walls
public static boolean noclip = false;
//whether turrets have infinite ammo (only with debug)
public static boolean infiniteAmmo = true;
//whether to show paths of enemies
public static boolean showPaths = false;
//if false, player is always hidden
public static boolean showPlayer = true;
//whether to hide ui, only on debug
public static boolean showUI = true;
//whether to show block debug
public static boolean showBlockDebug = false;
public static boolean showFog = true;
//respawn time in frames
public static final float respawnduration = 60 * 4;
//time between waves in frames (on normal mode)
public static final float wavespace = 60 * 60 * 2f;
//waves can last no longer than 3 minutes, otherwise the next one spawns
public static final float maxwavespace = 60 * 60 * 4f;
//set ridiculously high for now
public static final float coreBuildRange = 800999f;
//discord group URL
public static final String discordURL = "https://discord.gg/BKADYds";
public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases";
public static final int maxTextLength = 150;
public static final int maxNameLength = 40;
public static final int maxCharNameLength = 20;
public static final int saveSlots = 64;
public static final float itemSize = 5f;
public static final int tilesize = 8;
public static final Locale[] locales = {new Locale("en"), new Locale("fr"), new Locale("ru"), new Locale("uk", "UA"), new Locale("pl"),
new Locale("de"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID"), new Locale("ita"), new Locale("es")};
public static final Color[] playerColors = {
Color.valueOf("82759a"),
Color.valueOf("c0c1c5"),
Color.valueOf("fff0e7"),
Color.valueOf("7d2953"),
Color.valueOf("ff074e"),
Color.valueOf("ff072a"),
Color.valueOf("ff76a6"),
Color.valueOf("a95238"),
Color.valueOf("ffa108"),
Color.valueOf("feeb2c"),
Color.valueOf("ffcaa8"),
Color.valueOf("008551"),
Color.valueOf("00e339"),
Color.valueOf("423c7b"),
Color.valueOf("4b5ef1"),
Color.valueOf("2cabfe"),
};
//server port
public static final int port = 6567;
public static final int webPort = 6568;
public static boolean testMobile;
//shorthand for whether or not this is running on android or ios
public static boolean mobile;
public static boolean ios;
public static boolean android;
//shorthand for whether or not this is running on GWT
public static boolean gwt;
//directory for user-created map data
public static FileHandle customMapDirectory;
//save file directory
public static FileHandle saveDirectory;
public static String mapExtension = "mmap";
public static String saveExtension = "msav";
//scale of the font
public static float fontScale;
//camera zoom displayed on startup
public static int baseCameraScale;
//if true, player speed will be increased, massive amounts of resources will be given on start, and other debug options will be available
public static boolean debug = false;
public static boolean console = false;
//whether the player can clip through walls
public static boolean noclip = false;
//whether turrets have infinite ammo (only with debug)
public static boolean infiniteAmmo = true;
//whether to show paths of enemies
public static boolean showPaths = false;
//if false, player is always hidden
public static boolean showPlayer = true;
//whether to hide ui, only on debug
public static boolean showUI = true;
//whether to show block debug
public static boolean showBlockDebug = false;
public static boolean showFog = true;
public static boolean headless = false;
public static float controllerMin = 0.25f;
public static float baseControllerSpeed = 11f;
//only if smoothCamera
public static boolean snapCamera = true;
public static GameState state;
public static ThreadHandler threads;
public static boolean headless = false;
public static Control control;
public static Logic logic;
public static Renderer renderer;
public static UI ui;
public static World world;
public static NetServer netServer;
public static NetClient netClient;
public static float controllerMin = 0.25f;
public static Player[] players = {};
public static float baseControllerSpeed = 11f;
public static EntityGroup<Player> playerGroup;
public static EntityGroup<TileEntity> tileGroup;
public static EntityGroup<Bullet> bulletGroup;
public static EntityGroup<Shield> shieldGroup;
public static EntityGroup<EffectEntity> effectGroup;
public static EntityGroup<DrawTrait> groundEffectGroup;
public static EntityGroup<ItemDrop> itemGroup;
public static final int saveSlots = 64;
public static EntityGroup<Puddle> puddleGroup;
public static EntityGroup<Fire> fireGroup;
public static EntityGroup<BaseUnit>[] unitGroups;
public static final float itemSize = 5f;
public static final Translator[] tmptr = new Translator[]{new Translator(), new Translator(), new Translator(), new Translator()};
//only if smoothCamera
public static boolean snapCamera = true;
public static final int tilesize = 8;
public static void init(){
Version.init();
public static final Translator[] tmptr = new Translator[]{new Translator(), new Translator(), new Translator(), new Translator()};
playerGroup = Entities.addGroup(Player.class).enableMapping();
tileGroup = Entities.addGroup(TileEntity.class, false);
bulletGroup = Entities.addGroup(Bullet.class).enableMapping();
shieldGroup = Entities.addGroup(Shield.class, false);
effectGroup = Entities.addGroup(EffectEntity.class, false);
groundEffectGroup = Entities.addGroup(DrawTrait.class, false);
puddleGroup = Entities.addGroup(Puddle.class, false).enableMapping();
itemGroup = Entities.addGroup(ItemDrop.class).enableMapping();
fireGroup = Entities.addGroup(Fire.class, false).enableMapping();
unitGroups = new EntityGroup[Team.all.length];
public static final Locale[] locales = {new Locale("en"), new Locale("fr"), new Locale("ru"), new Locale("uk", "UA"), new Locale("pl"),
new Locale("de"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID"), new Locale("ita"), new Locale("es")};
for(Team team : Team.all){
unitGroups[team.ordinal()] = Entities.addGroup(BaseUnit.class).enableMapping();
}
public static final Color[] playerColors = {
Color.valueOf("82759a"),
Color.valueOf("c0c1c5"),
Color.valueOf("fff0e7"),
Color.valueOf("7d2953"),
Color.valueOf("ff074e"),
Color.valueOf("ff072a"),
Color.valueOf("ff76a6"),
Color.valueOf("a95238"),
Color.valueOf("ffa108"),
Color.valueOf("feeb2c"),
Color.valueOf("ffcaa8"),
Color.valueOf("008551"),
Color.valueOf("00e339"),
Color.valueOf("423c7b"),
Color.valueOf("4b5ef1"),
Color.valueOf("2cabfe"),
};
for(EntityGroup<?> group : Entities.getAllGroups()){
group.setRemoveListener(entity -> {
if(entity instanceof SyncTrait && Net.client()){
netClient.addRemovedEntity((entity).getID());
}
});
}
//server port
public static final int port = 6567;
public static final int webPort = 6568;
threads = new ThreadHandler(Platform.instance.getThreadProvider());
public static GameState state;
public static ThreadHandler threads;
mobile = Gdx.app.getType() == ApplicationType.Android || Gdx.app.getType() == ApplicationType.iOS || testMobile;
ios = Gdx.app.getType() == ApplicationType.iOS;
android = Gdx.app.getType() == ApplicationType.Android;
gwt = Gdx.app.getType() == ApplicationType.WebGL;
public static Control control;
public static Logic logic;
public static Renderer renderer;
public static UI ui;
public static World world;
public static NetServer netServer;
public static NetClient netClient;
public static Player[] players = {};
if(!gwt){
customMapDirectory = OS.getAppDataDirectory("Mindustry").child("maps/");
saveDirectory = OS.getAppDataDirectory("Mindustry").child("saves/");
}
public static EntityGroup<Player> playerGroup;
public static EntityGroup<TileEntity> tileGroup;
public static EntityGroup<Bullet> bulletGroup;
public static EntityGroup<Shield> shieldGroup;
public static EntityGroup<EffectEntity> effectGroup;
public static EntityGroup<DrawTrait> groundEffectGroup;
public static EntityGroup<ItemDrop> itemGroup;
public static EntityGroup<Puddle> puddleGroup;
public static EntityGroup<Fire> fireGroup;
public static EntityGroup<BaseUnit>[] unitGroups;
public static void init(){
Version.init();
playerGroup = Entities.addGroup(Player.class).enableMapping();
tileGroup = Entities.addGroup(TileEntity.class, false);
bulletGroup = Entities.addGroup(Bullet.class).enableMapping();
shieldGroup = Entities.addGroup(Shield.class, false);
effectGroup = Entities.addGroup(EffectEntity.class, false);
groundEffectGroup = Entities.addGroup(DrawTrait.class, false);
puddleGroup = Entities.addGroup(Puddle.class, false).enableMapping();
itemGroup = Entities.addGroup(ItemDrop.class).enableMapping();
fireGroup = Entities.addGroup(Fire.class, false).enableMapping();
unitGroups = new EntityGroup[Team.all.length];
for(Team team : Team.all){
unitGroups[team.ordinal()] = Entities.addGroup(BaseUnit.class).enableMapping();
}
for(EntityGroup<?> group : Entities.getAllGroups()){
group.setRemoveListener(entity -> {
if(entity instanceof SyncTrait && Net.client()){
netClient.addRemovedEntity(((SyncTrait) entity).getID());
}
});
}
threads = new ThreadHandler(Platform.instance.getThreadProvider());
mobile = Gdx.app.getType() == ApplicationType.Android || Gdx.app.getType() == ApplicationType.iOS || testMobile;
ios = Gdx.app.getType() == ApplicationType.iOS;
android = Gdx.app.getType() == ApplicationType.Android;
gwt = Gdx.app.getType() == ApplicationType.WebGL;
if(!gwt) {
customMapDirectory = OS.getAppDataDirectory("Mindustry").child("maps/");
saveDirectory = OS.getAppDataDirectory("Mindustry").child("saves/");
}
fontScale = Math.max(Unit.dp.scl(1f)/2f, 0.5f);
baseCameraScale = Math.round(Unit.dp.scl(4));
}
fontScale = Math.max(Unit.dp.scl(1f) / 2f, 0.5f);
baseCameraScale = Math.round(Unit.dp.scl(4));
}
}

View File

@@ -26,32 +26,53 @@ import static io.anuke.mindustry.Vars.*;
//TODO consider using quadtrees for finding specific types of blocks within an area
//TODO maybe use Arrays instead of ObjectSets?
/**Class used for indexing special target blocks for AI.*/
public class BlockIndexer {
/**Size of one ore quadrant.*/
/**
* Class used for indexing special target blocks for AI.
*/
public class BlockIndexer{
/**
* Size of one ore quadrant.
*/
private final static int oreQuadrantSize = 20;
/**Size of one structure quadrant.*/
/**
* Size of one structure quadrant.
*/
private final static int structQuadrantSize = 12;
/**Set of all ores that are being scanned.*/
/**
* Set of all ores that are being scanned.
*/
private final ObjectSet<Item> scanOres = ObjectSet.with(Items.tungsten, Items.coal, Items.lead, Items.thorium, Items.titanium);
/**Stores all ore quadtrants on the map.*/
private ObjectMap<Item, ObjectSet<Tile>> ores;
private final ObjectSet<Item> itemSet = new ObjectSet<>();
/**Tags all quadrants.*/
/**
* Stores all ore quadtrants on the map.
*/
private ObjectMap<Item, ObjectSet<Tile>> ores;
/**
* Tags all quadrants.
*/
private Bits[] structQuadrants;
/**Maps teams to a map of flagged tiles by type.*/
/**
* Maps teams to a map of flagged tiles by type.
*/
private ObjectMap<BlockFlag, ObjectSet<Tile>> enemyMap = new ObjectMap<>();
/**Maps teams to a map of flagged tiles by type.*/
/**
* Maps teams to a map of flagged tiles by type.
*/
private ObjectMap<BlockFlag, ObjectSet<Tile>> allyMap = new ObjectMap<>();
/**Empty map for invalid teams.*/
/**
* Empty map for invalid teams.
*/
private ObjectMap<BlockFlag, ObjectSet<Tile>> emptyMap = new ObjectMap<>();
/**Maps tile positions to their last known tile index data.*/
/**
* Maps tile positions to their last known tile index data.
*/
private IntMap<TileIndex> typeMap = new IntMap<>();
/**Empty array used for returning.*/
/**
* Empty array used for returning.
*/
private ObjectSet<Tile> emptyArray = new ObjectSet<>();
public BlockIndexer(){
@@ -74,18 +95,18 @@ public class BlockIndexer {
//create bitset for each team type that contains each quadrant
structQuadrants = new Bits[Team.all.length];
for(int i = 0; i < Team.all.length; i ++){
structQuadrants[i] = new Bits(Mathf.ceil(world.width() / (float)structQuadrantSize) * Mathf.ceil(world.height() / (float)structQuadrantSize));
for(int i = 0; i < Team.all.length; i++){
structQuadrants[i] = new Bits(Mathf.ceil(world.width() / (float) structQuadrantSize) * Mathf.ceil(world.height() / (float) structQuadrantSize));
}
for(int x = 0; x < world.width(); x ++){
for (int y = 0; y < world.height(); y++) {
for(int x = 0; x < world.width(); x++){
for(int y = 0; y < world.height(); y++){
process(world.tile(x, y));
}
}
for (int x = 0; x < quadWidth(); x++) {
for (int y = 0; y < quadHeight(); y++) {
for(int x = 0; x < quadWidth(); x++){
for(int y = 0; y < quadHeight(); y++){
updateQuadrant(world.tile(x * structQuadrantSize, y * structQuadrantSize));
}
}
@@ -94,12 +115,16 @@ public class BlockIndexer {
});
}
/**Get all allied blocks with a flag.*/
/**
* Get all allied blocks with a flag.
*/
public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
return (state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
}
/**Get all enemy blocks with a flag.*/
/**
* Get all enemy blocks with a flag.
*/
public ObjectSet<Tile> getEnemy(Team team, BlockFlag type){
return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
}
@@ -108,13 +133,13 @@ public class BlockIndexer {
Entity closest = null;
float dst = 0;
for(int rx = Math.max((int)((x-range)/tilesize/structQuadrantSize), 0); rx <= (int)((x+range)/tilesize/structQuadrantSize) && rx < quadWidth(); rx ++){
for(int ry = Math.max((int)((y-range)/tilesize/structQuadrantSize), 0); ry <= (int)((y+range)/tilesize/structQuadrantSize) && ry < quadHeight(); ry ++){
for(int rx = Math.max((int) ((x - range) / tilesize / structQuadrantSize), 0); rx <= (int) ((x + range) / tilesize / structQuadrantSize) && rx < quadWidth(); rx++){
for(int ry = Math.max((int) ((y - range) / tilesize / structQuadrantSize), 0); ry <= (int) ((y + range) / tilesize / structQuadrantSize) && ry < quadHeight(); ry++){
if(!getQuad(team, rx, ry)) continue;
for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx ++){
for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty ++ ){
for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx++){
for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty++){
Tile other = world.tile(tx, ty);
if(other == null || other.entity == null || !pred.test(other)) continue;
@@ -134,22 +159,26 @@ public class BlockIndexer {
return (TileEntity) closest;
}
/**Returns a set of tiles that have ores of the specified type nearby.
/**
* Returns a set of tiles that have ores of the specified type nearby.
* While each tile in the set is not guaranteed to have an ore directly on it,
* each tile will at least have an ore within {@link #oreQuadrantSize} / 2 blocks of it.
* Only specific ore types are scanned. See {@link #scanOres}.*/
* Only specific ore types are scanned. See {@link #scanOres}.
*/
public ObjectSet<Tile> getOrePositions(Item item){
return ores.get(item, emptyArray);
}
/**Find the closest ore block relative to a position.*/
/**
* Find the closest ore block relative to a position.
*/
public Tile findClosestOre(float xp, float yp, Item item){
Tile tile = Geometry.findClosest(xp, yp, world.indexer().getOrePositions(item));
Tile tile = Geometry.findClosest(xp, yp, world.indexer().getOrePositions(item));
if(tile == null) return null;
for (int x = Math.max(0, tile.x - oreQuadrantSize/2); x < tile.x + oreQuadrantSize/2 && x < world.width(); x++) {
for (int y = Math.max(0, tile.y - oreQuadrantSize/2); y < tile.y + oreQuadrantSize/2 && y < world.height(); y++) {
for(int x = Math.max(0, tile.x - oreQuadrantSize / 2); x < tile.x + oreQuadrantSize / 2 && x < world.width(); x++){
for(int y = Math.max(0, tile.y - oreQuadrantSize / 2); y < tile.y + oreQuadrantSize / 2 && y < world.height(); y++){
Tile res = world.tile(x, y);
if(res.block() == Blocks.air && res.floor().drops != null && res.floor().drops.item == item){
return res;
@@ -186,12 +215,12 @@ public class BlockIndexer {
int quadrantY = tile.y / oreQuadrantSize;
itemSet.clear();
Tile rounded = world.tile(Mathf.clamp(quadrantX * oreQuadrantSize + oreQuadrantSize /2, 0, world.width() - 1),
Mathf.clamp(quadrantY * oreQuadrantSize + oreQuadrantSize /2, 0, world.height() - 1));
Tile rounded = world.tile(Mathf.clamp(quadrantX * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1),
Mathf.clamp(quadrantY * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1));
//find all items that this quadrant contains
for (int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++) {
for (int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++) {
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
Tile result = world.tile(x, y);
if(result.block().drops == null || !scanOres.contains(result.block().drops.item)) continue;
@@ -200,7 +229,7 @@ public class BlockIndexer {
}
//update quadrant at this position
for (Item item : scanOres){
for(Item item : scanOres){
ObjectSet<Tile> set = ores.get(item);
//update quadrant status depending on whether the item is in it
@@ -219,7 +248,7 @@ public class BlockIndexer {
int index = quadrantX + quadrantY * quadWidth();
//Log.info("Updating quadrant: {0} {1}", quadrantX, quadrantY);
for(TeamData data : state.teams.getTeams()) {
for(TeamData data : state.teams.getTeams()){
//fast-set this quadrant to 'occupied' if the tile just placed is already of this team
if(tile.getTeam() == data.team && tile.entity != null){
@@ -230,8 +259,8 @@ public class BlockIndexer {
structQuadrants[data.team.ordinal()].clear(index);
outer:
for (int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++) {
for (int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++) {
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
Tile result = world.tile(x, y);
//when a targetable block is found, mark this quadrant as occupied and stop searching
if(result.entity != null && result.getTeam() == data.team){
@@ -244,16 +273,16 @@ public class BlockIndexer {
}
private boolean getQuad(Team team, int quadrantX, int quadrantY){
int index = quadrantX + quadrantY * Mathf.ceil(world.width() / (float)structQuadrantSize);
int index = quadrantX + quadrantY * Mathf.ceil(world.width() / (float) structQuadrantSize);
return structQuadrants[team.ordinal()].get(index);
}
private int quadWidth(){
return Mathf.ceil(world.width() / (float)structQuadrantSize);
return Mathf.ceil(world.width() / (float) structQuadrantSize);
}
private int quadHeight(){
return Mathf.ceil(world.height() / (float)structQuadrantSize);
return Mathf.ceil(world.height() / (float) structQuadrantSize);
}
private ObjectMap<BlockFlag, ObjectSet<Tile>> getMap(Team team){
@@ -269,10 +298,10 @@ public class BlockIndexer {
ores.put(item, new ObjectSet<>());
}
for(int x = 0; x < world.width(); x ++){
for (int y = 0; y < world.height(); y++) {
int qx = (x/ oreQuadrantSize);
int qy = (y/ oreQuadrantSize);
for(int x = 0; x < world.width(); x++){
for(int y = 0; y < world.height(); y++){
int qx = (x / oreQuadrantSize);
int qy = (y / oreQuadrantSize);
Tile tile = world.tile(x, y);
@@ -280,8 +309,8 @@ public class BlockIndexer {
if(tile.floor().drops != null && scanOres.contains(tile.floor().drops.item) && tile.block() == Blocks.air){
ores.get(tile.floor().drops.item).add(world.tile(
//make sure to clamp quadrant middle position, since it might go off bounds
Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize /2, 0, world.width() - 1),
Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize /2, 0, world.height() - 1)));
Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1),
Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1)));
}
}
}
@@ -291,7 +320,7 @@ public class BlockIndexer {
public final EnumSet<BlockFlag> flags;
public final Team team;
public TileIndex(EnumSet<BlockFlag> flags, Team team) {
public TileIndex(EnumSet<BlockFlag> flags, Team team){
this.flags = flags;
this.team = team;
}

View File

@@ -20,7 +20,7 @@ import io.anuke.ucore.util.Log;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world;
public class Pathfinder {
public class Pathfinder{
private long maxUpdate = TimeUtils.millisToNanos(4);
private PathData[] paths;
private IntArray blocked = new IntArray();
@@ -56,7 +56,7 @@ public class Pathfinder {
Tile target = null;
float tl = 0f;
for(GridPoint2 point : Geometry.d8) {
for(GridPoint2 point : Geometry.d8){
int dx = tile.x + point.x, dy = tile.y + point.y;
Tile other = world.tile(dx, dy);
@@ -89,16 +89,16 @@ public class Pathfinder {
}
private void update(Tile tile, Team team){
if(paths[team.ordinal()] != null) {
if(paths[team.ordinal()] != null){
PathData path = paths[team.ordinal()];
if(!passable(tile, team)){
path.weights[tile.x][tile.y] = Float.MAX_VALUE;
}
path.search ++;
path.search++;
if(path.lastSearchTime + 1000/60*3 > TimeUtils.millis()){
if(path.lastSearchTime + 1000 / 60 * 3 > TimeUtils.millis()){
path.frontier.clear();
}
@@ -115,17 +115,17 @@ public class Pathfinder {
private void createFor(Team team){
PathData path = new PathData();
path.search ++;
path.search++;
path.frontier.ensureCapacity((world.width() + world.height()) * 3);
paths[team.ordinal()] = path;
for (int x = 0; x < world.width(); x++) {
for (int y = 0; y < world.height(); y++) {
for(int x = 0; x < world.width(); x++){
for(int y = 0; y < world.height(); y++){
Tile tile = world.tile(x, y);
if (tile.block().flags != null && state.teams.areEnemies(tile.getTeam(), team)
&& tile.block().flags.contains(BlockFlag.target)) {
if(tile.block().flags != null && state.teams.areEnemies(tile.getTeam(), team)
&& tile.block().flags.contains(BlockFlag.target)){
path.frontier.addFirst(tile);
path.weights[x][y] = 0;
path.searches[x][y] = path.search;
@@ -143,20 +143,20 @@ public class Pathfinder {
long start = TimeUtils.nanoTime();
while (path.frontier.size > 0 && (nsToRun < 0 || TimeUtils.timeSinceNanos(start) <= nsToRun)) {
while(path.frontier.size > 0 && (nsToRun < 0 || TimeUtils.timeSinceNanos(start) <= nsToRun)){
Tile tile = path.frontier.removeLast();
float cost = path.weights[tile.x][tile.y];
if (cost < Float.MAX_VALUE) {
for (GridPoint2 point : Geometry.d4) {
if(cost < Float.MAX_VALUE){
for(GridPoint2 point : Geometry.d4){
int dx = tile.x + point.x, dy = tile.y + point.y;
Tile other = world.tile(dx, dy);
if (other != null && (path.weights[dx][dy] > cost + 1 || path.searches[dx][dy] < path.search)
if(other != null && (path.weights[dx][dy] > cost + 1 || path.searches[dx][dy] < path.search)
&& passable(other, team)){
path.frontier.addFirst(world.tile(dx, dy));
path.weights[dx][dy] = cost + other.cost/2f;
path.weights[dx][dy] = cost + other.cost / 2f;
path.searches[dx][dy] = path.search;
}
}

View File

@@ -18,7 +18,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class WaveSpawner {
public class WaveSpawner{
private static final int quadsize = 4;
private Bits quadrants;
@@ -34,28 +34,28 @@ public class WaveSpawner {
public void write(DataOutput output) throws IOException{
output.writeShort(flySpawns.size);
for (FlyerSpawn spawn : flySpawns){
for(FlyerSpawn spawn : flySpawns){
output.writeFloat(spawn.angle);
}
output.writeShort(groundSpawns.size);
for (GroundSpawn spawn : groundSpawns){
output.writeShort((short)spawn.x);
output.writeShort((short)spawn.y);
for(GroundSpawn spawn : groundSpawns){
output.writeShort((short) spawn.x);
output.writeShort((short) spawn.y);
}
}
public void read(DataInput input) throws IOException{
short flya = input.readShort();
for (int i = 0; i < flya; i++) {
for(int i = 0; i < flya; i++){
FlyerSpawn spawn = new FlyerSpawn();
spawn.angle = input.readFloat();
flySpawns.add(spawn);
}
short grounda = input.readShort();
for (int i = 0; i < grounda; i++) {
for(int i = 0; i < grounda; i++){
GroundSpawn spawn = new GroundSpawn();
spawn.x = input.readShort();
spawn.y = input.readShort();
@@ -80,13 +80,13 @@ public class WaveSpawner {
int addGround = groundGroups - groundSpawns.size, addFly = flyGroups - flySpawns.size;
//add extra groups if the total exceeds it
for (int i = 0; i < addGround; i++) {
for(int i = 0; i < addGround; i++){
GroundSpawn spawn = new GroundSpawn();
findLocation(spawn);
groundSpawns.add(spawn);
}
for (int i = 0; i < addFly; i++) {
for(int i = 0; i < addFly; i++){
FlyerSpawn spawn = new FlyerSpawn();
findLocation(spawn);
flySpawns.add(spawn);
@@ -99,7 +99,7 @@ public class WaveSpawner {
int groups = group.getGroupsSpawned(state.wave);
int spawned = group.getUnitsSpawned(state.wave);
for (int i = 0; i < groups; i++) {
for(int i = 0; i < groups; i++){
Squad squad = new Squad();
float spawnX, spawnY;
float spread;
@@ -109,11 +109,11 @@ public class WaveSpawner {
//TODO verify flyer spawn
float margin = 40f; //how far away from the edge flying units spawn
spawnX = world.width() *tilesize/2f + Mathf.sqrwavex(spawn.angle) * (world.width()/2f*tilesize + margin);
spawnY = world.height() * tilesize/2f + Mathf.sqrwavey(spawn.angle) * (world.height()/2f*tilesize + margin);
spawnX = world.width() * tilesize / 2f + Mathf.sqrwavex(spawn.angle) * (world.width() / 2f * tilesize + margin);
spawnY = world.height() * tilesize / 2f + Mathf.sqrwavey(spawn.angle) * (world.height() / 2f * tilesize + margin);
spread = margin / 1.5f;
flyCount ++;
flyCount++;
}else{
GroundSpawn spawn = groundSpawns.get(groundCount);
checkQuadrant(spawn.x, spawn.y);
@@ -121,14 +121,14 @@ public class WaveSpawner {
findLocation(spawn);
}
spawnX = spawn.x * quadsize * tilesize + quadsize * tilesize/2f;
spawnY = spawn.y * quadsize * tilesize + quadsize * tilesize/2f;
spread = quadsize*tilesize/3f;
spawnX = spawn.x * quadsize * tilesize + quadsize * tilesize / 2f;
spawnY = spawn.y * quadsize * tilesize + quadsize * tilesize / 2f;
spread = quadsize * tilesize / 3f;
groundCount ++;
groundCount++;
}
for (int j = 0; j < spawned; j++) {
for(int j = 0; j < spawned; j++){
BaseUnit unit = group.createUnit(Team.red);
unit.setWave();
unit.setSquad(squad);
@@ -140,19 +140,19 @@ public class WaveSpawner {
}
public void checkAllQuadrants(){
for(int x = 0; x < quadWidth(); x ++){
for(int y = 0; y < quadHeight(); y ++){
for(int x = 0; x < quadWidth(); x++){
for(int y = 0; y < quadHeight(); y++){
checkQuadrant(x, y);
}
}
}
private void checkQuadrant(int quadx, int quady){
setQuad(quadx, quady, true);
outer:
for (int x = quadx * quadsize; x < world.width() && x < (quadx + 1)*quadsize; x++) {
for (int y = quady * quadsize; y < world.height() && y < (quady + 1)*quadsize; y++) {
for(int x = quadx * quadsize; x < world.width() && x < (quadx + 1) * quadsize; x++){
for(int y = quady * quadsize; y < world.height() && y < (quady + 1) * quadsize; y++){
Tile tile = world.tile(x, y);
if(tile == null || tile.solid() || world.pathfinder().getValueforTeam(Team.red, x, y) == Float.MAX_VALUE){
@@ -190,7 +190,7 @@ public class WaveSpawner {
spawn.x = -1;
spawn.y = -1;
int shellWidth = quadWidth()*2 + quadHeight() * 2 * 6;
int shellWidth = quadWidth() * 2 + quadHeight() * 2 * 6;
shellWidth = Math.min(quadWidth() * quadHeight() / 4, shellWidth);
Mathf.traverseSpiral(quadWidth(), quadHeight(), Mathf.random(shellWidth), (x, y) -> {
@@ -210,11 +210,11 @@ public class WaveSpawner {
}
private int quadWidth(){
return Mathf.ceil(world.width() / (float)quadsize);
return Mathf.ceil(world.width() / (float) quadsize);
}
private int quadHeight(){
return Mathf.ceil(world.height() / (float)quadsize);
return Mathf.ceil(world.height() / (float) quadsize);
}
private class FlyerSpawn{

View File

@@ -9,7 +9,7 @@ import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.type.ContentList;
public class AmmoTypes implements ContentList {
public class AmmoTypes implements ContentList{
public static AmmoType bulletTungsten, bulletLead, bulletCarbide, bulletThorium, bulletSilicon, bulletPyratite,
shotgunTungsten, bombExplosive, bombIncendiary, bombOil, shellCarbide, flamerThermite, weaponMissile,
flakLead, flakExplosive, flakPlastic, flakSurge, missileExplosive, missileIncindiary, missileSurge,
@@ -17,41 +17,41 @@ public class AmmoTypes implements ContentList {
basicFlame, lancerLaser, lightning, spectreLaser, meltdownLaser, fuseShotgun, oil, water, lava, cryofluid;
@Override
public void load() {
public void load(){
//weapon specific
shotgunTungsten = new AmmoType(Items.tungsten, WeaponBullets.tungstenShotgun, 2) {{
shotgunTungsten = new AmmoType(Items.tungsten, WeaponBullets.tungstenShotgun, 2){{
shootEffect = ShootFx.shootBig;
smokeEffect = ShootFx.shootBigSmoke;
recoil = 1f;
}};
shellCarbide = new AmmoType(Items.carbide, WeaponBullets.shellCarbide, 2) {{
shellCarbide = new AmmoType(Items.carbide, WeaponBullets.shellCarbide, 2){{
shootEffect = ShootFx.shootBig;
smokeEffect = ShootFx.shootBigSmoke;
}};
bombExplosive = new AmmoType(Items.blastCompound, WeaponBullets.bombExplosive, 3) {{
bombExplosive = new AmmoType(Items.blastCompound, WeaponBullets.bombExplosive, 3){{
shootEffect = Fx.none;
smokeEffect = Fx.none;
}};
bombIncendiary = new AmmoType(Items.pyratite, WeaponBullets.bombIncendiary, 3) {{
bombIncendiary = new AmmoType(Items.pyratite, WeaponBullets.bombIncendiary, 3){{
shootEffect = Fx.none;
smokeEffect = Fx.none;
}};
bombOil = new AmmoType(Items.coal, WeaponBullets.bombOil, 3) {{
bombOil = new AmmoType(Items.coal, WeaponBullets.bombOil, 3){{
shootEffect = Fx.none;
smokeEffect = Fx.none;
}};
flamerThermite = new AmmoType(Items.pyratite, TurretBullets.basicFlame, 3) {{
flamerThermite = new AmmoType(Items.pyratite, TurretBullets.basicFlame, 3){{
shootEffect = ShootFx.shootSmallFlame;
}};
weaponMissile = new AmmoType(Items.carbide, MissileBullets.javelin, 2) {{
weaponMissile = new AmmoType(Items.carbide, MissileBullets.javelin, 2){{
shootEffect = BulletFx.hitBulletSmall;
smokeEffect = Fx.none;
reloadMultiplier = 1.2f;
@@ -59,37 +59,37 @@ public class AmmoTypes implements ContentList {
//bullets
bulletLead = new AmmoType(Items.lead, StandardBullets.lead, 5) {{
bulletLead = new AmmoType(Items.lead, StandardBullets.lead, 5){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
reloadMultiplier = 1.6f;
inaccuracy = 5f;
}};
bulletTungsten = new AmmoType(Items.tungsten, StandardBullets.tungsten, 2) {{
bulletTungsten = new AmmoType(Items.tungsten, StandardBullets.tungsten, 2){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
reloadMultiplier = 0.8f;
}};
bulletCarbide = new AmmoType(Items.carbide, StandardBullets.carbide, 2) {{
bulletCarbide = new AmmoType(Items.carbide, StandardBullets.carbide, 2){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
reloadMultiplier = 0.6f;
}};
bulletThorium = new AmmoType(Items.thorium, StandardBullets.thorium, 2) {{
bulletThorium = new AmmoType(Items.thorium, StandardBullets.thorium, 2){{
shootEffect = ShootFx.shootBig;
smokeEffect = ShootFx.shootBigSmoke;
}};
bulletSilicon = new AmmoType(Items.silicon, StandardBullets.homing, 5) {{
bulletSilicon = new AmmoType(Items.silicon, StandardBullets.homing, 5){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
reloadMultiplier = 1.4f;
}};
bulletPyratite = new AmmoType(Items.pyratite, StandardBullets.tracer, 3) {{
bulletPyratite = new AmmoType(Items.pyratite, StandardBullets.tracer, 3){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
inaccuracy = 3f;
@@ -97,71 +97,71 @@ public class AmmoTypes implements ContentList {
//flak
flakLead = new AmmoType(Items.lead, FlakBullets.lead, 5) {{
flakLead = new AmmoType(Items.lead, FlakBullets.lead, 5){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
}};
flakExplosive = new AmmoType(Items.blastCompound, FlakBullets.explosive, 5) {{
flakExplosive = new AmmoType(Items.blastCompound, FlakBullets.explosive, 5){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
}};
flakPlastic = new AmmoType(Items.plastanium, FlakBullets.plastic, 5) {{
flakPlastic = new AmmoType(Items.plastanium, FlakBullets.plastic, 5){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
}};
flakSurge = new AmmoType(Items.surgealloy, FlakBullets.surge, 5) {{
flakSurge = new AmmoType(Items.surgealloy, FlakBullets.surge, 5){{
shootEffect = ShootFx.shootSmall;
smokeEffect = ShootFx.shootSmallSmoke;
}};
//missiles
missileExplosive = new AmmoType(Items.blastCompound, MissileBullets.explosive, 1) {{
missileExplosive = new AmmoType(Items.blastCompound, MissileBullets.explosive, 1){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
reloadMultiplier = 1.2f;
}};
missileIncindiary = new AmmoType(Items.pyratite, MissileBullets.incindiary, 1) {{
missileIncindiary = new AmmoType(Items.pyratite, MissileBullets.incindiary, 1){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
reloadMultiplier = 1.0f;
}};
missileSurge = new AmmoType(Items.surgealloy, MissileBullets.surge, 1) {{
missileSurge = new AmmoType(Items.surgealloy, MissileBullets.surge, 1){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
}};
//artillery
artilleryCarbide = new AmmoType(Items.carbide, ArtilleryBullets.carbide, 2) {{
artilleryCarbide = new AmmoType(Items.carbide, ArtilleryBullets.carbide, 2){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
}};
artilleryPlastic = new AmmoType(Items.plastanium, ArtilleryBullets.plastic, 2) {{
artilleryPlastic = new AmmoType(Items.plastanium, ArtilleryBullets.plastic, 2){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
reloadMultiplier = 1.4f;
}};
artilleryHoming = new AmmoType(Items.silicon, ArtilleryBullets.homing, 1) {{
artilleryHoming = new AmmoType(Items.silicon, ArtilleryBullets.homing, 1){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
reloadMultiplier = 0.9f;
}};
artilleryIncindiary = new AmmoType(Items.pyratite, ArtilleryBullets.incindiary, 2) {{
artilleryIncindiary = new AmmoType(Items.pyratite, ArtilleryBullets.incindiary, 2){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
reloadMultiplier = 1.2f;
}};
artilleryExplosive = new AmmoType(Items.blastCompound, ArtilleryBullets.explosive, 1) {{
artilleryExplosive = new AmmoType(Items.blastCompound, ArtilleryBullets.explosive, 1){{
shootEffect = ShootFx.shootBig2;
smokeEffect = ShootFx.shootBigSmoke2;
reloadMultiplier = 1.6f;
@@ -169,7 +169,7 @@ public class AmmoTypes implements ContentList {
//flame
basicFlame = new AmmoType(Liquids.oil, TurretBullets.basicFlame, 0.3f) {{
basicFlame = new AmmoType(Liquids.oil, TurretBullets.basicFlame, 0.3f){{
shootEffect = ShootFx.shootSmallFlame;
}};
@@ -198,7 +198,7 @@ public class AmmoTypes implements ContentList {
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return AmmoType.all();
}
}

View File

@@ -12,41 +12,41 @@ public class Items implements ContentList{
biomatter, sand, blastCompound, pyratite;
@Override
public void load() {
public void load(){
stone = new Item("stone", Color.valueOf("777777")) {{
stone = new Item("stone", Color.valueOf("777777")){{
hardness = 3;
}};
tungsten = new Item("tungsten", Color.valueOf("a0b0c8")) {{
tungsten = new Item("tungsten", Color.valueOf("a0b0c8")){{
type = ItemType.material;
hardness = 1;
cost = 0.75f;
}};
lead = new Item("lead", Color.valueOf("8e85a2")) {{
lead = new Item("lead", Color.valueOf("8e85a2")){{
type = ItemType.material;
hardness = 1;
cost = 0.6f;
}};
coal = new Item("coal", Color.valueOf("272727")) {{
coal = new Item("coal", Color.valueOf("272727")){{
explosiveness = 0.2f;
flammability = 0.5f;
hardness = 2;
}};
carbide = new Item("carbide", Color.valueOf("e2e2e2")) {{
carbide = new Item("carbide", Color.valueOf("e2e2e2")){{
type = ItemType.material;
}};
titanium = new Item("titanium", Color.valueOf("8da1e3")) {{
titanium = new Item("titanium", Color.valueOf("8da1e3")){{
type = ItemType.material;
hardness = 3;
cost = 1.1f;
}};
thorium = new Item("thorium", Color.valueOf("f9a3c7")) {{
thorium = new Item("thorium", Color.valueOf("f9a3c7")){{
type = ItemType.material;
explosiveness = 0.1f;
hardness = 4;
@@ -54,49 +54,49 @@ public class Items implements ContentList{
cost = 1.2f;
}};
silicon = new Item("silicon", Color.valueOf("53565c")) {{
silicon = new Item("silicon", Color.valueOf("53565c")){{
type = ItemType.material;
cost = 0.9f;
}};
plastanium = new Item("plastanium", Color.valueOf("e9ead3")) {{
plastanium = new Item("plastanium", Color.valueOf("e9ead3")){{
type = ItemType.material;
flammability = 0.1f;
explosiveness = 0.1f;
cost = 1.5f;
}};
phasematter = new Item("phase-matter", Color.valueOf("f4ba6e")) {{
phasematter = new Item("phase-matter", Color.valueOf("f4ba6e")){{
type = ItemType.material;
cost = 1.5f;
}};
surgealloy = new Item("surge-alloy", Color.valueOf("b4d5c7")) {{
surgealloy = new Item("surge-alloy", Color.valueOf("b4d5c7")){{
type = ItemType.material;
}};
biomatter = new Item("biomatter", Color.valueOf("648b55")) {{
biomatter = new Item("biomatter", Color.valueOf("648b55")){{
flammability = 0.4f;
fluxiness = 0.2f;
}};
sand = new Item("sand", Color.valueOf("e3d39e")) {{
sand = new Item("sand", Color.valueOf("e3d39e")){{
fluxiness = 0.5f;
}};
blastCompound = new Item("blast-compound", Color.valueOf("ff795e")) {{
blastCompound = new Item("blast-compound", Color.valueOf("ff795e")){{
flammability = 0.2f;
explosiveness = 0.6f;
}};
pyratite = new Item("pyratite", Color.valueOf("ffaa5f")) {{
pyratite = new Item("pyratite", Color.valueOf("ffaa5f")){{
flammability = 0.7f;
explosiveness = 0.2f;
}};
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Item.all();
}
}

View File

@@ -6,20 +6,13 @@ import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.Liquid;
public class Liquids implements ContentList {
public static Liquid none, water, lava, oil, cryofluid;
public class Liquids implements ContentList{
public static Liquid water, lava, oil, cryofluid;
@Override
public void load() {
public void load(){
none = new Liquid("none", Color.CLEAR){
@Override
public boolean isHidden(){
return true;
}
};
water = new Liquid("water", Color.valueOf("486acd")) {
water = new Liquid("water", Color.valueOf("486acd")){
{
heatCapacity = 0.4f;
tier = 0;
@@ -27,7 +20,7 @@ public class Liquids implements ContentList {
}
};
lava = new Liquid("lava", Color.valueOf("e37341")) {
lava = new Liquid("lava", Color.valueOf("e37341")){
{
temperature = 0.8f;
viscosity = 0.8f;
@@ -36,7 +29,7 @@ public class Liquids implements ContentList {
}
};
oil = new Liquid("oil", Color.valueOf("313131")) {
oil = new Liquid("oil", Color.valueOf("313131")){
{
viscosity = 0.7f;
flammability = 0.6f;
@@ -46,10 +39,10 @@ public class Liquids implements ContentList {
}
};
cryofluid = new Liquid("cryofluid", Color.SKY) {
cryofluid = new Liquid("cryofluid", Color.SKY){
{
heatCapacity = 0.75f;
temperature = 0.4f;
heatCapacity = 0.9f;
temperature = 0.25f;
tier = 1;
effect = StatusEffects.freezing;
}
@@ -57,7 +50,7 @@ public class Liquids implements ContentList {
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Liquid.all();
}
}

View File

@@ -8,14 +8,16 @@ import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.Mech;
import io.anuke.mindustry.type.Upgrade;
public class Mechs implements ContentList {
public class Mechs implements ContentList{
public static Mech alpha, delta, tau, omega, dart, javelin, trident, halberd;
/**These are not new mechs, just re-assignments for convenience.*/
/**
* These are not new mechs, just re-assignments for convenience.
*/
public static Mech starterDesktop, starterMobile;
@Override
public void load() {
public void load(){
alpha = new Mech("alpha-mech", false){{
drillPower = 1;
@@ -48,7 +50,7 @@ public class Mechs implements ContentList {
}};
dart = new Mech("dart-ship", true){{
drillPower = -1;
drillPower = 1;
speed = 0.4f;
maxSpeed = 3f;
drag = 0.1f;
@@ -85,7 +87,7 @@ public class Mechs implements ContentList {
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Upgrade.all();
}
}

View File

@@ -12,19 +12,19 @@ import static io.anuke.mindustry.type.Category.*;
public class Recipes implements ContentList{
@Override
public void load (){
public void load(){
//WALLS
new Recipe(defense, DefenseBlocks.tungstenWall, new ItemStack(Items.tungsten, 12));
new Recipe(defense, DefenseBlocks.tungstenWallLarge, new ItemStack(Items.tungsten, 12*4));
new Recipe(defense, DefenseBlocks.tungstenWallLarge, new ItemStack(Items.tungsten, 12 * 4));
new Recipe(defense, DefenseBlocks.carbideWall, new ItemStack(Items.carbide, 12));
new Recipe(defense, DefenseBlocks.carbideWallLarge, new ItemStack(Items.carbide, 12*4));
new Recipe(defense, DefenseBlocks.carbideWallLarge, new ItemStack(Items.carbide, 12 * 4));
new Recipe(defense, DefenseBlocks.thoriumWall, new ItemStack(Items.thorium, 12));
new Recipe(defense, DefenseBlocks.thoriumWallLarge, new ItemStack(Items.thorium, 12*4));
new Recipe(defense, DefenseBlocks.thoriumWallLarge, new ItemStack(Items.thorium, 12 * 4));
new Recipe(defense, DefenseBlocks.door, new ItemStack(Items.carbide, 12), new ItemStack(Items.silicon, 8));
new Recipe(defense, DefenseBlocks.doorLarge, new ItemStack(Items.carbide, 12*4), new ItemStack(Items.silicon, 8*4));
new Recipe(defense, DefenseBlocks.doorLarge, new ItemStack(Items.carbide, 12 * 4), new ItemStack(Items.silicon, 8 * 4));
//TURRETS
new Recipe(weapon, TurretBlocks.duo, new ItemStack(Items.tungsten, 40));
@@ -44,11 +44,11 @@ public class Recipes implements ContentList{
//starter lead transporation
new Recipe(distribution, DistributionBlocks.junction, new ItemStack(Items.lead, 2));
new Recipe(distribution, DistributionBlocks.router, new ItemStack(Items.lead, 6));
new Recipe(distribution, DistributionBlocks.splitter, new ItemStack(Items.lead, 6));
//advanced carbide transporation
new Recipe(distribution, DistributionBlocks.splitter, new ItemStack(Items.carbide, 2), new ItemStack(Items.tungsten, 2));
new Recipe(distribution, DistributionBlocks.multiplexer, new ItemStack(Items.carbide, 8), new ItemStack(Items.tungsten, 8));
//new Recipe(distribution, DistributionBlocks.splitter, new ItemStack(Items.carbide, 2), new ItemStack(Items.tungsten, 2));
new Recipe(distribution, DistributionBlocks.distributor, new ItemStack(Items.carbide, 8), new ItemStack(Items.tungsten, 8));
new Recipe(distribution, DistributionBlocks.sorter, new ItemStack(Items.carbide, 4), new ItemStack(Items.tungsten, 4));
new Recipe(distribution, DistributionBlocks.overflowGate, new ItemStack(Items.carbide, 4), new ItemStack(Items.tungsten, 8));
new Recipe(distribution, DistributionBlocks.bridgeConveyor, new ItemStack(Items.carbide, 8), new ItemStack(Items.tungsten, 8));
@@ -171,7 +171,6 @@ public class Recipes implements ContentList{
new Recipe(production, ProductionBlocks.oilextractor, new ItemStack(Items.titanium, 40), new ItemStack(Items.surgealloy, 40));*/
//new Recipe(distribution, DistributionBlocks.massDriver, new ItemStack(Items.carbide, 1));
@@ -282,7 +281,7 @@ public class Recipes implements ContentList{
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Recipe.all();
}
}

View File

@@ -3,30 +3,30 @@ package io.anuke.mindustry.content;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.content.fx.EnvironmentFx;
import io.anuke.mindustry.entities.StatusController.StatusEntry;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.StatusEffect;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.StatusEffect;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Mathf;
public class StatusEffects implements ContentList {
public class StatusEffects implements ContentList{
public static StatusEffect none, burning, freezing, wet, melting, tarred, overdrive, shielded;
@Override
public void load() {
public void load(){
none = new StatusEffect(0);
burning = new StatusEffect(4 * 60f) {
burning = new StatusEffect(4 * 60f){
{
oppositeScale = 0.5f;
}
@Override
public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result) {
if (to == tarred) {
public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){
if(to == tarred){
unit.damage(1f);
Effects.effect(EnvironmentFx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
return result.set(this, Math.min(time + newTime, baseDuration + tarred.baseDuration));
@@ -36,45 +36,45 @@ public class StatusEffects implements ContentList {
}
@Override
public void update(Unit unit, float time) {
public void update(Unit unit, float time){
unit.damagePeriodic(0.04f);
if (Mathf.chance(Timers.delta() * 0.2f)) {
if(Mathf.chance(Timers.delta() * 0.2f)){
Effects.effect(EnvironmentFx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
}
}
};
freezing = new StatusEffect(5 * 60f) {
freezing = new StatusEffect(5 * 60f){
{
oppositeScale = 0.4f;
speedMultiplier = 0.7f;
}
@Override
public void update(Unit unit, float time) {
public void update(Unit unit, float time){
if (Mathf.chance(Timers.delta() * 0.15f)) {
if(Mathf.chance(Timers.delta() * 0.15f)){
Effects.effect(EnvironmentFx.freezing, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
}
}
};
wet = new StatusEffect(3 * 60f) {
wet = new StatusEffect(3 * 60f){
{
oppositeScale = 0.5f;
speedMultiplier = 0.999f;
}
@Override
public void update(Unit unit, float time) {
if (Mathf.chance(Timers.delta() * 0.15f)) {
public void update(Unit unit, float time){
if(Mathf.chance(Timers.delta() * 0.15f)){
Effects.effect(EnvironmentFx.wet, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
}
}
};
melting = new StatusEffect(5 * 60f) {
melting = new StatusEffect(5 * 60f){
{
oppositeScale = 0.2f;
speedMultiplier = 0.8f;
@@ -82,8 +82,8 @@ public class StatusEffects implements ContentList {
}
@Override
public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result) {
if (to == tarred) {
public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){
if(to == tarred){
return result.set(this, Math.min(time + newTime / 2f, baseDuration));
}
@@ -91,30 +91,30 @@ public class StatusEffects implements ContentList {
}
@Override
public void update(Unit unit, float time) {
public void update(Unit unit, float time){
unit.damagePeriodic(0.3f);
if (Mathf.chance(Timers.delta() * 0.2f)) {
if(Mathf.chance(Timers.delta() * 0.2f)){
Effects.effect(EnvironmentFx.melting, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
}
}
};
tarred = new StatusEffect(4 * 60f) {
tarred = new StatusEffect(4 * 60f){
{
speedMultiplier = 0.6f;
}
@Override
public void update(Unit unit, float time) {
if (Mathf.chance(Timers.delta() * 0.15f)) {
public void update(Unit unit, float time){
if(Mathf.chance(Timers.delta() * 0.15f)){
Effects.effect(EnvironmentFx.oily, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f));
}
}
@Override
public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result) {
if (to == melting || to == burning) {
public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){
if(to == melting || to == burning){
return result.set(to, newTime + time);
}
@@ -122,7 +122,7 @@ public class StatusEffects implements ContentList {
}
};
overdrive = new StatusEffect(6f) {
overdrive = new StatusEffect(6f){
{
armorMultiplier = 0.95f;
speedMultiplier = 1.05f;
@@ -130,13 +130,13 @@ public class StatusEffects implements ContentList {
}
@Override
public void update(Unit unit, float time) {
public void update(Unit unit, float time){
//idle regen boosted
unit.health += 0.01f * Timers.delta();
}
};
shielded = new StatusEffect(6f) {
shielded = new StatusEffect(6f){
{
armorMultiplier = 3f;
}
@@ -149,7 +149,7 @@ public class StatusEffects implements ContentList {
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return StatusEffect.all();
}
}

View File

@@ -6,11 +6,11 @@ import io.anuke.mindustry.entities.units.types.*;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.ContentList;
public class UnitTypes implements ContentList {
public class UnitTypes implements ContentList{
public static UnitType drone, scout, vtol, monsoon, titan, fabricator;
@Override
public void load() {
public void load(){
drone = new UnitType("drone", Drone.class, Drone::new){{
isFlying = true;
drag = 0.01f;
@@ -73,7 +73,7 @@ public class UnitTypes implements ContentList {
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return UnitType.all();
}
}

View File

@@ -8,13 +8,13 @@ import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.Upgrade;
import io.anuke.mindustry.type.Weapon;
public class Weapons implements ContentList {
public class Weapons implements ContentList{
public static Weapon blaster, chainBlaster, shockgun, sapper, swarmer, bomber, flakgun, flamethrower, missiles;
@Override
public void load() {
public void load(){
blaster = new Weapon("blaster") {{
blaster = new Weapon("blaster"){{
length = 1.5f;
reload = 15f;
roundrobin = true;
@@ -22,7 +22,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.bulletLead);
}};
missiles = new Weapon("missiles") {{
missiles = new Weapon("missiles"){{
length = 1.5f;
reload = 40f;
shots = 2;
@@ -33,7 +33,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.weaponMissile);
}};
chainBlaster = new Weapon("chain-blaster") {{
chainBlaster = new Weapon("chain-blaster"){{
length = 1.5f;
reload = 30f;
roundrobin = true;
@@ -41,7 +41,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletTungsten, AmmoTypes.bulletSilicon, AmmoTypes.bulletThorium);
}};
shockgun = new Weapon("shockgun") {{
shockgun = new Weapon("shockgun"){{
length = 1f;
reload = 50f;
roundrobin = true;
@@ -53,7 +53,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.shotgunTungsten);
}};
flakgun = new Weapon("flakgun") {{
flakgun = new Weapon("flakgun"){{
length = 1f;
reload = 70f;
roundrobin = true;
@@ -65,7 +65,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.shellCarbide);
}};
flamethrower = new Weapon("flamethrower") {{
flamethrower = new Weapon("flamethrower"){{
length = 1f;
reload = 14f;
roundrobin = true;
@@ -74,7 +74,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.flamerThermite);
}};
sapper = new Weapon("sapper") {{
sapper = new Weapon("sapper"){{
length = 1.5f;
reload = 12f;
roundrobin = true;
@@ -82,7 +82,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.bulletCarbide);
}};
swarmer = new Weapon("swarmer") {{
swarmer = new Weapon("swarmer"){{
length = 1.5f;
reload = 10f;
roundrobin = true;
@@ -90,7 +90,7 @@ public class Weapons implements ContentList {
setAmmo(AmmoTypes.bulletPyratite);
}};
bomber = new Weapon("bomber") {{
bomber = new Weapon("bomber"){{
length = 0f;
width = 2f;
reload = 5f;
@@ -103,7 +103,7 @@ public class Weapons implements ContentList {
}
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Upgrade.all();
}
}

View File

@@ -5,10 +5,10 @@ import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.world.Block;
public abstract class BlockList implements ContentList {
public abstract class BlockList implements ContentList{
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Block.all();
}
}

View File

@@ -15,26 +15,31 @@ public class Blocks extends BlockList implements ContentList{
public static Block air, spawn, blockpart, space, metalfloor, deepwater, water, lava, oil, stone, blackstone, dirt, sand, ice, snow, grass, shrub, rock, icerock, blackrock;
@Override
public void load() {
air = new Floor("air") {
public void load(){
air = new Floor("air"){
{
blend = false;
}
//don't draw
public void draw(Tile tile) {}
public void load() {}
public void init() {}
public void draw(Tile tile){
}
public void load(){
}
public void init(){
}
};
blockpart = new BlockPart();
for(int i = 1; i <= 6; i ++){
for(int i = 1; i <= 6; i++){
new BuildBlock("build" + i);
}
space = new Floor("space") {{
space = new Floor("space"){{
placeableOn = false;
variants = 0;
cacheLayer = CacheLayer.space;
@@ -43,11 +48,11 @@ public class Blocks extends BlockList implements ContentList{
minimapColor = Color.valueOf("000001");
}};
metalfloor = new Floor("metalfloor") {{
metalfloor = new Floor("metalfloor"){{
variants = 6;
}};
deepwater = new Floor("deepwater") {{
deepwater = new Floor("deepwater"){{
liquidColor = Color.valueOf("546bb3");
speedMultiplier = 0.2f;
variants = 0;
@@ -60,7 +65,7 @@ public class Blocks extends BlockList implements ContentList{
minimapColor = Color.valueOf("465a96");
}};
water = new Floor("water") {{
water = new Floor("water"){{
liquidColor = Color.valueOf("546bb3");
speedMultiplier = 0.5f;
variants = 0;
@@ -72,7 +77,7 @@ public class Blocks extends BlockList implements ContentList{
minimapColor = Color.valueOf("506eb4");
}};
lava = new Floor("lava") {{
lava = new Floor("lava"){{
liquidColor = Color.valueOf("ed5334");
speedMultiplier = 0.2f;
damageTaken = 0.5f;
@@ -85,7 +90,7 @@ public class Blocks extends BlockList implements ContentList{
minimapColor = Color.valueOf("ed5334");
}};
oil = new Floor("oil") {{
oil = new Floor("oil"){{
liquidColor = Color.valueOf("292929");
status = StatusEffects.tarred;
statusIntensity = 1f;
@@ -97,7 +102,7 @@ public class Blocks extends BlockList implements ContentList{
minimapColor = Color.valueOf("292929");
}};
stone = new Floor("stone") {{
stone = new Floor("stone"){{
hasOres = true;
drops = new ItemStack(Items.stone, 1);
blends = block -> block != this && !(block instanceof Ore);
@@ -105,7 +110,7 @@ public class Blocks extends BlockList implements ContentList{
playerUnmineable = true;
}};
blackstone = new Floor("blackstone") {{
blackstone = new Floor("blackstone"){{
drops = new ItemStack(Items.stone, 1);
minimapColor = Color.valueOf("252525");
playerUnmineable = true;
@@ -115,14 +120,14 @@ public class Blocks extends BlockList implements ContentList{
minimapColor = Color.valueOf("6e501e");
}};
sand = new Floor("sand") {{
sand = new Floor("sand"){{
drops = new ItemStack(Items.sand, 1);
minimapColor = Color.valueOf("988a67");
hasOres = true;
playerUnmineable = true;
}};
ice = new Floor("ice") {{
ice = new Floor("ice"){{
dragMultiplier = 0.3f;
speedMultiplier = 0.4f;
minimapColor = Color.valueOf("c4e3e7");
@@ -143,19 +148,16 @@ public class Blocks extends BlockList implements ContentList{
shadow = "shrubshadow";
}};
rock = new Rock("rock") {{
rock = new Rock("rock"){{
variants = 2;
varyShadow = true;
}};
icerock = new Rock("icerock") {{
icerock = new Rock("icerock"){{
variants = 2;
varyShadow = true;
}};
blackrock = new Rock("blackrock") {{
blackrock = new Rock("blackrock"){{
variants = 1;
varyShadow = true;
}};
}
}

View File

@@ -10,55 +10,54 @@ import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.production.*;
public class CraftingBlocks extends BlockList implements ContentList {
public class CraftingBlocks extends BlockList implements ContentList{
public static Block smelter, arcsmelter, siliconsmelter, plastaniumCompressor, phaseWeaver, alloysmelter, alloyfuser,
pyratiteMixer, blastMixer,
cryofluidmixer, melter, separator, centrifuge, biomatterCompressor, pulverizer, oilRefinery, solidifier, incinerator;
cryofluidmixer, melter, separator, centrifuge, biomatterCompressor, pulverizer, solidifier, incinerator;
@Override
public void load() {
smelter = new Smelter("smelter") {{
public void load(){
smelter = new Smelter("smelter"){{
health = 70;
inputs = new ItemStack[]{new ItemStack(Items.tungsten, 3)};
fuel = Items.coal;
result = Items.carbide;
craftTime = 45f;
burnDuration = 35f;
useFlux = true;
consumes.items(new ItemStack[]{new ItemStack(Items.tungsten, 3)});
consumes.item(Items.coal);
}};
arcsmelter = new PowerSmelter("arc-smelter") {{
arcsmelter = new PowerSmelter("arc-smelter"){{
health = 90;
craftEffect = BlockFx.smeltsmoke;
inputs = new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.tungsten, 2)};
result = Items.carbide;
powerUse = 0.1f;
craftTime = 30f;
size = 2;
useFlux = true;
fluxNeeded = 2;
consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.tungsten, 2)});
consumes.power(0.1f);
}};
siliconsmelter = new PowerSmelter("silicon-smelter") {{
siliconsmelter = new PowerSmelter("silicon-smelter"){{
health = 90;
craftEffect = BlockFx.smeltsmoke;
inputs = new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.sand, 2)};
result = Items.silicon;
powerUse = 0.05f;
craftTime = 40f;
size = 2;
hasLiquids = false;
flameColor = Color.valueOf("ffef99");
consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.sand, 2)});
consumes.power(0.05f);
}};
plastaniumCompressor = new PlastaniumCompressor("plastanium-compressor") {{
inputLiquid = Liquids.oil;
inputItem = new ItemStack(Items.titanium, 2);
plastaniumCompressor = new PlastaniumCompressor("plastanium-compressor"){{
hasItems = true;
liquidUse = 0.3f;
liquidCapacity = 60f;
powerUse = 0.5f;
craftTime = 80f;
output = Items.plastanium;
itemCapacity = 30;
@@ -67,94 +66,103 @@ public class CraftingBlocks extends BlockList implements ContentList {
hasPower = hasLiquids = true;
craftEffect = BlockFx.formsmoke;
updateEffect = BlockFx.plasticburn;
consumes.liquid(Liquids.oil, 0.3f);
consumes.power(0.4f);
consumes.item(Items.titanium, 2);
}};
phaseWeaver = new PhaseWeaver("phase-weaver") {{
phaseWeaver = new PhaseWeaver("phase-weaver"){{
health = 90;
craftEffect = BlockFx.smeltsmoke;
inputs = new ItemStack[]{new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10)};
result = Items.phasematter;
powerUse = 0.4f;
craftTime = 120f;
size = 2;
consumes.items(new ItemStack[]{new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10)});
consumes.power(0.5f);
}};
alloysmelter = new PowerSmelter("alloy-smelter") {{
alloysmelter = new PowerSmelter("alloy-smelter"){{
health = 90;
craftEffect = BlockFx.smeltsmoke;
inputs = new ItemStack[]{new ItemStack(Items.titanium, 2), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.plastanium, 2)};
result = Items.surgealloy;
powerUse = 0.3f;
craftTime = 50f;
size = 2;
useFlux = true;
fluxNeeded = 4;
consumes.power(0.3f);
consumes.items(new ItemStack[]{new ItemStack(Items.titanium, 2), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.plastanium, 2)});
}};
alloyfuser = new PowerSmelter("alloy-fuser") {{
alloyfuser = new PowerSmelter("alloy-fuser"){{
health = 90;
craftEffect = BlockFx.smeltsmoke;
inputs = new ItemStack[]{new ItemStack(Items.titanium, 3), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.plastanium, 2)};
result = Items.surgealloy;
powerUse = 0.4f;
craftTime = 30f;
size = 3;
useFlux = true;
fluxNeeded = 4;
consumes.items(new ItemStack[]{new ItemStack(Items.titanium, 3), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.plastanium, 2)});
consumes.power(0.4f);
}};
cryofluidmixer = new LiquidMixer("cryofluidmixer") {{
cryofluidmixer = new LiquidMixer("cryofluidmixer"){{
health = 200;
inputLiquid = Liquids.water;
outputLiquid = Liquids.cryofluid;
inputItem = Items.titanium;
liquidPerItem = 50f;
itemCapacity = 50;
powerUse = 0.1f;
size = 2;
hasPower = true;
consumes.power(0.1f);
consumes.item(Items.titanium);
consumes.liquid(Liquids.water, 0.3f);
}};
blastMixer = new GenericCrafter("blast-mixer") {{
blastMixer = new GenericCrafter("blast-mixer"){{
itemCapacity = 20;
hasItems = true;
hasPower = true;
inputLiquid = Liquids.oil;
liquidUse = 0.05f;
inputItem = new ItemStack(Items.pyratite, 1);
hasLiquids = true;
output = Items.blastCompound;
powerUse = 0.04f;
size = 2;
consumes.liquid(Liquids.oil, 0.05f);
consumes.item(Items.pyratite, 1);
consumes.power(0.04f);
}};
pyratiteMixer = new PowerSmelter("pyratite-mixer") {{
pyratiteMixer = new PowerSmelter("pyratite-mixer"){{
flameColor = Color.CLEAR;
itemCapacity = 20;
hasItems = true;
hasPower = true;
inputs = new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.lead, 2), new ItemStack(Items.sand, 2)};
result = Items.pyratite;
powerUse = 0.02f;
size = 2;
consumes.power(0.02f);
consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.lead, 2), new ItemStack(Items.sand, 2)});
}};
melter = new PowerCrafter("melter") {{
melter = new PowerCrafter("melter"){{
health = 200;
outputLiquid = Liquids.lava;
outputLiquidAmount = 0.05f;
input = new ItemStack(Items.stone, 1);
outputLiquidAmount = 0.75f;
itemCapacity = 50;
craftTime = 10f;
powerUse = 0.1f;
hasLiquids = hasPower = true;
consumes.power(0.1f);
consumes.item(Items.stone, 2);
}};
separator = new Separator("separator") {{
liquid = Liquids.water;
item = Items.stone;
separator = new Separator("separator"){{
results = new Item[]{
null, null, null, null, null, null, null, null, null, null,
Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand,
@@ -164,15 +172,15 @@ public class CraftingBlocks extends BlockList implements ContentList {
Items.coal, Items.coal,
Items.titanium
};
liquidUse = 0.2f;
filterTime = 40f;
itemCapacity = 40;
health = 50;
consumes.item(Items.stone, 2);
consumes.liquid(Liquids.water, 0.3f);
}};
centrifuge = new Separator("centrifuge") {{
liquid = Liquids.water;
item = Items.stone;
centrifuge = new Separator("centrifuge"){{
results = new Item[]{
null, null, null, null, null, null, null, null, null, null, null, null, null,
Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand, Items.sand,
@@ -184,9 +192,7 @@ public class CraftingBlocks extends BlockList implements ContentList {
Items.thorium,
};
liquidUse = 0.3f;
hasPower = true;
powerUse = 0.2f;
filterTime = 15f;
itemCapacity = 60;
health = 50 * 4;
@@ -195,56 +201,52 @@ public class CraftingBlocks extends BlockList implements ContentList {
spinnerThickness = 1.5f;
spinnerSpeed = 3f;
size = 2;
consumes.item(Items.stone, 2);
consumes.power(0.2f);
consumes.liquid(Liquids.water, 0.5f);
}};
biomatterCompressor = new Compressor("biomattercompressor") {{
input = new ItemStack(Items.biomatter, 1);
biomatterCompressor = new Compressor("biomattercompressor"){{
liquidCapacity = 60f;
itemCapacity = 50;
powerUse = 0.06f;
craftTime = 25f;
outputLiquid = Liquids.oil;
outputLiquidAmount = 0.1f;
outputLiquidAmount = 0.14f;
size = 2;
health = 320;
hasLiquids = true;
consumes.item(Items.biomatter, 1);
consumes.power(0.06f);
}};
pulverizer = new Pulverizer("pulverizer") {{
inputItem = new ItemStack(Items.stone, 2);
pulverizer = new Pulverizer("pulverizer"){{
itemCapacity = 40;
powerUse = 0.2f;
output = Items.sand;
health = 80;
craftEffect = BlockFx.pulverize;
craftTime = 60f;
updateEffect = BlockFx.pulverizeSmall;
hasItems = hasPower = true;
consumes.item(Items.stone, 2);
consumes.power(0.2f);
}};
oilRefinery = new GenericCrafter("oilrefinery") {{
inputLiquid = Liquids.oil;
powerUse = 0.05f;
liquidUse = 0.1f;
liquidCapacity = 56f;
output = Items.coal;
health = 80;
craftEffect = BlockFx.purifyoil;
hasItems = hasLiquids = hasPower = true;
}};
solidifier = new GenericCrafter("solidifer") {{
inputLiquid = Liquids.lava;
liquidUse = 1f;
solidifier = new GenericCrafter("solidifer"){{
liquidCapacity = 21f;
craftTime = 14;
output = Items.stone;
itemCapacity = 20;
health = 80;
craftEffect = BlockFx.purifystone;
hasLiquids = hasItems = true;
consumes.liquid(Liquids.lava, 1f);
}};
incinerator = new Incinerator("incinerator") {{
incinerator = new Incinerator("incinerator"){{
health = 90;
}};
}

View File

@@ -28,45 +28,52 @@ import java.io.IOException;
public class DebugBlocks extends BlockList implements ContentList{
public static Block powerVoid, powerInfinite, itemSource, liquidSource, itemVoid;
@Remote(targets = Loc.both, called = Loc.both, in = In.blocks, forward = true)
public static void setLiquidSourceLiquid(Player player, Tile tile, Liquid liquid){
LiquidSourceEntity entity = tile.entity();
entity.source = liquid;
}
@Override
public void load() {
powerVoid = new PowerBlock("powervoid") {
public void load(){
powerVoid = new PowerBlock("powervoid"){
{
powerCapacity = Float.MAX_VALUE;
}
};
powerInfinite = new PowerNode("powerinfinite") {
powerInfinite = new PowerNode("powerinfinite"){
{
powerCapacity = 10000f;
powerSpeed = 100f;
}
@Override
public void update(Tile tile) {
public void update(Tile tile){
super.update(tile);
tile.entity.power.amount = powerCapacity;
}
};
itemSource = new Sorter("itemsource") {
itemSource = new Sorter("itemsource"){
{
hasItems = true;
}
@Override
public void update(Tile tile) {
public void update(Tile tile){
SorterEntity entity = tile.entity();
entity.items.items[entity.sortItem.id] = 1;
entity.items.set(entity.sortItem, 1);
tryDump(tile, entity.sortItem);
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source) {
public boolean acceptItem(Item item, Tile tile, Tile source){
return false;
}
};
liquidSource = new Block("liquidsource") {
liquidSource = new Block("liquidsource"){
{
update = true;
solid = true;
@@ -76,16 +83,15 @@ public class DebugBlocks extends BlockList implements ContentList{
}
@Override
public void update(Tile tile) {
public void update(Tile tile){
LiquidSourceEntity entity = tile.entity();
tile.entity.liquids.amount = liquidCapacity;
tile.entity.liquids.liquid = entity.source;
tryDumpLiquid(tile);
tile.entity.liquids.add(entity.source, liquidCapacity);
tryDumpLiquid(tile, entity.source);
}
@Override
public void draw(Tile tile) {
public void draw(Tile tile){
super.draw(tile);
LiquidSourceEntity entity = tile.entity();
@@ -96,7 +102,7 @@ public class DebugBlocks extends BlockList implements ContentList{
}
@Override
public void buildTable(Tile tile, Table table) {
public void buildTable(Tile tile, Table table){
LiquidSourceEntity entity = tile.entity();
Array<Liquid> items = Liquid.all();
@@ -104,8 +110,8 @@ public class DebugBlocks extends BlockList implements ContentList{
ButtonGroup<ImageButton> group = new ButtonGroup<>();
Table cont = new Table();
for (int i = 0; i < items.size; i++) {
if (i == 0) continue;
for(int i = 0; i < items.size; i++){
if(i == 0) continue;
final int f = i;
ImageButton button = cont.addImageButton("white", "toggle", 24, () -> {
CallBlocks.setLiquidSourceLiquid(null, tile, items.get(f));
@@ -113,7 +119,7 @@ public class DebugBlocks extends BlockList implements ContentList{
button.getStyle().imageUpColor = items.get(i).color;
button.setChecked(entity.source.id == f);
if (i % 4 == 3) {
if(i % 4 == 3){
cont.row();
}
}
@@ -122,43 +128,37 @@ public class DebugBlocks extends BlockList implements ContentList{
}
@Override
public TileEntity getEntity() {
public TileEntity getEntity(){
return new LiquidSourceEntity();
}
};
itemVoid = new Block("itemvoid") {
itemVoid = new Block("itemvoid"){
{
update = solid = true;
}
@Override
public void handleItem(Item item, Tile tile, Tile source) {
public void handleItem(Item item, Tile tile, Tile source){
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source) {
public boolean acceptItem(Item item, Tile tile, Tile source){
return true;
}
};
}
@Remote(targets = Loc.both, called = Loc.both, in = In.blocks, forward = true)
public static void setLiquidSourceLiquid(Player player, Tile tile, Liquid liquid){
LiquidSourceEntity entity = tile.entity();
entity.source = liquid;
}
class LiquidSourceEntity extends TileEntity {
class LiquidSourceEntity extends TileEntity{
public Liquid source = Liquids.water;
@Override
public void write(DataOutputStream stream) throws IOException {
public void write(DataOutputStream stream) throws IOException{
stream.writeByte(source.id);
}
@Override
public void read(DataInputStream stream) throws IOException {
public void read(DataInputStream stream) throws IOException{
source = Liquid.getByID(stream.readByte());
}
}

View File

@@ -8,65 +8,65 @@ import io.anuke.mindustry.world.blocks.defense.DeflectorWall;
import io.anuke.mindustry.world.blocks.defense.Door;
import io.anuke.mindustry.world.blocks.defense.PhaseWall;
public class DefenseBlocks extends BlockList implements ContentList {
public class DefenseBlocks extends BlockList implements ContentList{
public static Block tungstenWall, tungstenWallLarge, carbideWall, carbideWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge, deflectorwall, deflectorwalllarge,
phasewall, phasewalllarge;
phasewall, phasewalllarge;
@Override
public void load() {
public void load(){
int wallHealthMultiplier = 4;
tungstenWall = new Wall("tungsten-wall") {{
tungstenWall = new Wall("tungsten-wall"){{
health = 80 * wallHealthMultiplier;
}};
tungstenWallLarge = new Wall("tungsten-wall-large") {{
tungstenWallLarge = new Wall("tungsten-wall-large"){{
health = 80 * 4 * wallHealthMultiplier;
size = 2;
}};
carbideWall = new Wall("carbide-wall") {{
carbideWall = new Wall("carbide-wall"){{
health = 110 * wallHealthMultiplier;
}};
carbideWallLarge = new Wall("carbide-wall-large") {{
health = 110 * wallHealthMultiplier*4;
carbideWallLarge = new Wall("carbide-wall-large"){{
health = 110 * wallHealthMultiplier * 4;
size = 2;
}};
thoriumWall = new Wall("thorium-wall") {{
health = 110 * wallHealthMultiplier;
thoriumWall = new Wall("thorium-wall"){{
health = 200 * wallHealthMultiplier;
}};
thoriumWallLarge = new Wall("thorium-wall-large") {{
health = 110 * wallHealthMultiplier*4;
thoriumWallLarge = new Wall("thorium-wall-large"){{
health = 200 * wallHealthMultiplier * 4;
size = 2;
}};
deflectorwall = new DeflectorWall("deflector-wall") {{
deflectorwall = new DeflectorWall("deflector-wall"){{
health = 150 * wallHealthMultiplier;
}};
deflectorwalllarge = new DeflectorWall("deflector-wall-large") {{
deflectorwalllarge = new DeflectorWall("deflector-wall-large"){{
health = 150 * 4 * wallHealthMultiplier;
size = 2;
}};
phasewall = new PhaseWall("phase-wall") {{
phasewall = new PhaseWall("phase-wall"){{
health = 150 * wallHealthMultiplier;
}};
phasewalllarge = new PhaseWall("phase-wall-large") {{
phasewalllarge = new PhaseWall("phase-wall-large"){{
health = 150 * 4 * wallHealthMultiplier;
size = 2;
regenSpeed = 0.5f;
}};
door = new Door("door") {{
door = new Door("door"){{
health = 100 * wallHealthMultiplier;
}};
doorLarge = new Door("door-large") {{
doorLarge = new Door("door-large"){{
openfx = BlockFx.dooropenlarge;
closefx = BlockFx.doorcloselarge;
health = 100 * 4 * wallHealthMultiplier;

View File

@@ -5,53 +5,51 @@ import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.distribution.*;
public class DistributionBlocks extends BlockList implements ContentList{
public static Block conveyor, titaniumconveyor, router, multiplexer, junction,
bridgeConveyor, phaseConveyor, sorter, splitter, overflowGate, massDriver;
public static Block conveyor, titaniumconveyor, distributor, junction,
bridgeConveyor, phaseConveyor, sorter, splitter, overflowGate, massDriver;
@Override
public void load() {
@Override
public void load(){
conveyor = new Conveyor("conveyor") {{
health = 45;
speed = 0.03f;
}};
conveyor = new Conveyor("conveyor"){{
health = 45;
speed = 0.03f;
}};
titaniumconveyor = new Conveyor("titanium-conveyor") {{
health = 65;
speed = 0.07f;
}};
titaniumconveyor = new Conveyor("titanium-conveyor"){{
health = 65;
speed = 0.07f;
}};
router = new Router("router");
junction = new Junction("junction"){{
speed = 26;
capacity = 32;
}};
multiplexer = new Router("multiplexer") {{
size = 2;
itemCapacity = 80;
}};
bridgeConveyor = new BufferedItemBridge("bridge-conveyor"){{
range = 3;
}};
junction = new Junction("junction") {{
speed = 26;
capacity = 32;
}};
phaseConveyor = new ItemBridge("phase-conveyor"){{
range = 7;
hasPower = false;
consumes.power(0.05f);
}};
bridgeConveyor = new BufferedItemBridge("bridge-conveyor") {{
range = 3;
hasPower = false;
}};
sorter = new Sorter("sorter");
phaseConveyor = new ItemBridge("phase-conveyor") {{
range = 7;
}};
splitter = new Splitter("splitter");
sorter = new Sorter("sorter");
distributor = new Splitter("distributor"){{
size = 2;
}};
splitter = new Splitter("splitter");
overflowGate = new OverflowGate("overflow-gate");
overflowGate = new OverflowGate("overflow-gate");
massDriver = new MassDriver("mass-driver"){{
size = 3;
itemCapacity = 80;
range = 300f;
}};
}
massDriver = new MassDriver("mass-driver"){{
size = 3;
itemCapacity = 80;
range = 300f;
}};
}
}

View File

@@ -9,46 +9,47 @@ public class LiquidBlocks extends BlockList implements ContentList{
public static Block mechanicalPump, rotaryPump, thermalPump, conduit, pulseConduit, liquidRouter, liquidtank, liquidJunction, bridgeConduit, phaseConduit;
@Override
public void load() {
public void load(){
mechanicalPump = new Pump("mechanical-pump") {{
mechanicalPump = new Pump("mechanical-pump"){{
shadow = "shadow-round-1";
pumpAmount = 0.1f;
tier = 0;
}};
rotaryPump = new Pump("rotary-pump") {{
rotaryPump = new Pump("rotary-pump"){{
shadow = "shadow-rounded-2";
pumpAmount = 0.25f;
powerUse = 0.015f;
consumes.power(0.015f);
liquidCapacity = 30f;
hasPower = true;
size = 2;
tier = 1;
}};
thermalPump = new Pump("thermal-pump") {{
thermalPump = new Pump("thermal-pump"){{
pumpAmount = 0.3f;
powerUse = 0.02f;
consumes.power(0.05f);
liquidCapacity = 40f;
size = 2;
tier = 2;
}};
conduit = new Conduit("conduit") {{
conduit = new Conduit("conduit"){{
health = 45;
}};
pulseConduit = new Conduit("pulse-conduit") {{
pulseConduit = new Conduit("pulse-conduit"){{
liquidCapacity = 16f;
liquidFlowFactor = 4.9f;
health = 90;
}};
liquidRouter = new LiquidRouter("liquid-router") {{
liquidRouter = new LiquidRouter("liquid-router"){{
liquidCapacity = 40f;
}};
liquidtank = new LiquidRouter("liquid-tank") {{
liquidtank = new LiquidRouter("liquid-tank"){{
size = 3;
liquidCapacity = 1500f;
health = 500;
@@ -56,13 +57,15 @@ public class LiquidBlocks extends BlockList implements ContentList{
liquidJunction = new LiquidJunction("liquid-junction");
bridgeConduit = new LiquidExtendingBridge("bridge-conduit") {{
bridgeConduit = new LiquidExtendingBridge("bridge-conduit"){{
range = 3;
hasPower = false;
}};
phaseConduit = new LiquidBridge("phase-conduit") {{
phaseConduit = new LiquidBridge("phase-conduit"){{
range = 7;
hasPower = false;
consumes.power(0.05f);
}};
}
}

View File

@@ -7,11 +7,18 @@ import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
public class OreBlocks extends BlockList {
public class OreBlocks extends BlockList{
private static final ObjectMap<Item, ObjectMap<Block, Block>> oreBlockMap = new ObjectMap<>();
public static Block get(Block floor, Item item){
if(!oreBlockMap.containsKey(item)) throw new IllegalArgumentException("Item '" + item + "' is not an ore!");
if(!oreBlockMap.get(item).containsKey(floor))
throw new IllegalArgumentException("Block '" + floor.name + "' does not support ores!");
return oreBlockMap.get(item).get(floor);
}
@Override
public void load() {
public void load(){
Item[] ores = {Items.tungsten, Items.lead, Items.coal, Items.titanium, Items.thorium};
for(Item item : ores){
@@ -25,10 +32,4 @@ public class OreBlocks extends BlockList {
}
}
}
public static Block get(Block floor, Item item){
if(!oreBlockMap.containsKey(item)) throw new IllegalArgumentException("Item '" + item + "' is not an ore!");
if(!oreBlockMap.get(item).containsKey(floor)) throw new IllegalArgumentException("Block '" + floor.name + "' does not support ores!");
return oreBlockMap.get(item).get(floor);
}
}

View File

@@ -1,24 +1,25 @@
package io.anuke.mindustry.content.blocks;
import io.anuke.mindustry.content.Liquids;
import io.anuke.mindustry.content.fx.BlockFx;
import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.distribution.WarpGate;
import io.anuke.mindustry.world.blocks.power.*;
public class PowerBlocks extends BlockList implements ContentList {
public class PowerBlocks extends BlockList implements ContentList{
public static Block combustionGenerator, thermalGenerator, turbineGenerator, rtgGenerator, solarPanel, largeSolarPanel,
nuclearReactor, fusionReactor, battery, batteryLarge, powerNode, powerNodeLarge, warpGate;
@Override
public void load() {
combustionGenerator = new BurnerGenerator("combustion-generator") {{
public void load(){
combustionGenerator = new BurnerGenerator("combustion-generator"){{
powerOutput = 0.09f;
powerCapacity = 40f;
itemDuration = 40f;
}};
thermalGenerator = new LiquidHeatGenerator("thermal-generator") {{
thermalGenerator = new LiquidHeatGenerator("thermal-generator"){{
maxLiquidGenerate = 0.5f;
powerPerLiquid = 0.08f;
powerCapacity = 40f;
@@ -27,55 +28,55 @@ public class PowerBlocks extends BlockList implements ContentList {
size = 2;
}};
turbineGenerator = new TurbineGenerator("turbine-generator") {{
turbineGenerator = new TurbineGenerator("turbine-generator"){{
powerOutput = 0.28f;
powerCapacity = 40f;
itemDuration = 30f;
powerPerLiquid = 0.7f;
auxLiquidUse = 0.05f;
consumes.liquid(Liquids.water, 0.05f);
size = 2;
}};
rtgGenerator = new DecayGenerator("rtg-generator") {{
rtgGenerator = new DecayGenerator("rtg-generator"){{
powerCapacity = 40f;
powerOutput = 0.02f;
itemDuration = 500f;
}};
solarPanel = new SolarGenerator("solar-panel") {{
solarPanel = new SolarGenerator("solar-panel"){{
generation = 0.0045f;
}};
largeSolarPanel = new SolarGenerator("solar-panel-large") {{
largeSolarPanel = new SolarGenerator("solar-panel-large"){{
size = 3;
generation = 0.055f;
}};
nuclearReactor = new NuclearReactor("nuclear-reactor") {{
nuclearReactor = new NuclearReactor("nuclear-reactor"){{
size = 3;
health = 700;
powerMultiplier = 0.8f;
}};
fusionReactor = new FusionReactor("fusion-reactor") {{
fusionReactor = new FusionReactor("fusion-reactor"){{
size = 4;
health = 600;
}};
battery = new PowerDistributor("battery") {{
battery = new PowerDistributor("battery"){{
powerCapacity = 320f;
}};
batteryLarge = new PowerDistributor("battery-large") {{
batteryLarge = new PowerDistributor("battery-large"){{
size = 3;
powerCapacity = 2000f;
}};
powerNode = new PowerNode("power-node") {{
powerNode = new PowerNode("power-node"){{
shadow = "shadow-round-1";
}};
powerNodeLarge = new PowerNode("power-node-large") {{
powerNodeLarge = new PowerNode("power-node-large"){{
size = 2;
powerSpeed = 1f;
maxNodes = 5;

View File

@@ -11,35 +11,35 @@ import io.anuke.mindustry.world.blocks.production.Drill;
import io.anuke.mindustry.world.blocks.production.Fracker;
import io.anuke.mindustry.world.blocks.production.SolidPump;
public class ProductionBlocks extends BlockList implements ContentList {
public class ProductionBlocks extends BlockList implements ContentList{
public static Block tungstenDrill, carbideDrill, laserdrill, blastdrill, plasmadrill, waterextractor, oilextractor, cultivator;
@Override
public void load() {
tungstenDrill = new Drill("tungsten-drill") {{
public void load(){
tungstenDrill = new Drill("tungsten-drill"){{
tier = 2;
drillTime = 340;
}};
carbideDrill = new Drill("carbide-drill") {{
carbideDrill = new Drill("carbide-drill"){{
tier = 3;
drillTime = 280;
}};
laserdrill = new Drill("laser-drill") {{
laserdrill = new Drill("laser-drill"){{
drillTime = 180;
size = 2;
powerUse = 0.2f;
hasPower = true;
tier = 4;
updateEffect = BlockFx.pulverizeMedium;
drillEffect = BlockFx.mineBig;
consumes.power(0.2f);
}};
blastdrill = new Drill("blast-drill") {{
blastdrill = new Drill("blast-drill"){{
drillTime = 120;
size = 3;
powerUse = 0.5f;
drawRim = true;
hasPower = true;
tier = 5;
@@ -48,13 +48,14 @@ public class ProductionBlocks extends BlockList implements ContentList {
drillEffect = BlockFx.mineHuge;
rotateSpeed = 6f;
warmupSpeed = 0.01f;
consumes.power(0.5f);
}};
plasmadrill = new Drill("plasma-drill") {{
plasmadrill = new Drill("plasma-drill"){{
heatColor = Color.valueOf("ff461b");
drillTime = 90;
size = 4;
powerUse = 0.7f;
hasLiquids = true;
hasPower = true;
tier = 5;
@@ -64,38 +65,43 @@ public class ProductionBlocks extends BlockList implements ContentList {
updateEffectChance = 0.04f;
drillEffect = BlockFx.mineHuge;
warmupSpeed = 0.005f;
consumes.power(0.7f);
}};
waterextractor = new SolidPump("water-extractor") {{
waterextractor = new SolidPump("water-extractor"){{
result = Liquids.water;
powerUse = 0.2f;
pumpAmount = 0.1f;
size = 2;
liquidCapacity = 30f;
rotateSpeed = 1.4f;
consumes.power(0.2f);
}};
oilextractor = new Fracker("oil-extractor") {{
oilextractor = new Fracker("oil-extractor"){{
result = Liquids.oil;
inputLiquid = Liquids.water;
updateEffect = BlockFx.pulverize;
liquidCapacity = 50f;
updateEffectChance = 0.05f;
inputLiquidUse = 0.3f;
powerUse = 0.6f;
pumpAmount = 0.06f;
pumpAmount = 0.08f;
size = 3;
liquidCapacity = 30f;
consumes.item(Items.sand);
consumes.power(0.5f);
consumes.liquid(Liquids.water, 0.3f);
}};
cultivator = new Cultivator("cultivator") {{
cultivator = new Cultivator("cultivator"){{
result = Items.biomatter;
inputLiquid = Liquids.water;
liquidUse = 0.2f;
drillTime = 260;
size = 2;
hasLiquids = true;
hasPower = true;
consumes.power(0.08f);
consumes.liquid(Liquids.water, 0.2f);
}};
}

View File

@@ -7,25 +7,26 @@ import io.anuke.mindustry.world.blocks.storage.SortedUnloader;
import io.anuke.mindustry.world.blocks.storage.Unloader;
import io.anuke.mindustry.world.blocks.storage.Vault;
public class StorageBlocks extends BlockList implements ContentList {
public class StorageBlocks extends BlockList implements ContentList{
public static Block core, vault, unloader, sortedunloader;
@Override
public void load() {
core = new CoreBlock("core") {{
public void load(){
core = new CoreBlock("core"){{
health = 800;
}};
vault = new Vault("vault") {{
vault = new Vault("vault"){{
size = 3;
health = 600;
itemCapacity = 2000;
}};
unloader = new Unloader("unloader") {{
unloader = new Unloader("unloader"){{
speed = 5;
}};
sortedunloader = new SortedUnloader("sortedunloader") {{
sortedunloader = new SortedUnloader("sortedunloader"){{
speed = 5;
}};
}

View File

@@ -12,22 +12,23 @@ import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings;
public class TurretBlocks extends BlockList implements ContentList {
public static Block duo, /*scatter,*/ scorch, hail, wave, lancer, arc, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown;
public class TurretBlocks extends BlockList implements ContentList{
public static Block duo, /*scatter,*/
scorch, hail, wave, lancer, arc, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown;
@Override
public void load() {
duo = new DoubleTurret("duo") {{
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletSilicon};
reload = 25f;
restitution = 0.03f;
range = 90f;
shootCone = 15f;
ammoUseEffect = ShootFx.shellEjectSmall;
health = 80;
inaccuracy = 2f;
rotatespeed = 10f;
}};
@Override
public void load(){
duo = new DoubleTurret("duo"){{
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletSilicon};
reload = 25f;
restitution = 0.03f;
range = 90f;
shootCone = 15f;
ammoUseEffect = ShootFx.shellEjectSmall;
health = 80;
inaccuracy = 2f;
rotatespeed = 10f;
}};
/*
scatter = new BurstTurret("scatter") {{
ammoTypes = new AmmoType[]{AmmoTypes.flakLead, AmmoTypes.flakExplosive, AmmoTypes.flakPlastic};
@@ -41,165 +42,165 @@ public class TurretBlocks extends BlockList implements ContentList {
ammoUseEffect = ShootFx.shellEjectSmall;
}};*/
hail = new ArtilleryTurret("hail") {{
ammoTypes = new AmmoType[]{AmmoTypes.artilleryCarbide, AmmoTypes.artilleryHoming, AmmoTypes.artilleryIncindiary};
reload = 100f;
recoil = 2f;
range = 200f;
inaccuracy = 5f;
health = 120;
}};
hail = new ArtilleryTurret("hail"){{
ammoTypes = new AmmoType[]{AmmoTypes.artilleryCarbide, AmmoTypes.artilleryHoming, AmmoTypes.artilleryIncindiary};
reload = 100f;
recoil = 2f;
range = 200f;
inaccuracy = 5f;
health = 120;
}};
scorch = new LiquidTurret("scorch") {{
scorch = new LiquidTurret("scorch"){{
ammoTypes = new AmmoType[]{AmmoTypes.basicFlame};
recoil = 0f;
reload = 4f;
shootCone = 50f;
ammoUseEffect = ShootFx.shellEjectSmall;
health = 160;
health = 160;
drawer = (tile, entity) -> Draw.rect(entity.target != null ? name + "-shoot" : name, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
}};
wave = new LiquidTurret("wave") {{
ammoTypes = new AmmoType[]{AmmoTypes.water, AmmoTypes.lava, AmmoTypes.cryofluid, AmmoTypes.oil};
size = 2;
recoil = 0f;
reload = 4f;
inaccuracy = 5f;
shootCone = 50f;
shootEffect = ShootFx.shootLiquid;
range = 70f;
health = 360;
wave = new LiquidTurret("wave"){{
ammoTypes = new AmmoType[]{AmmoTypes.water, AmmoTypes.lava, AmmoTypes.cryofluid, AmmoTypes.oil};
size = 2;
recoil = 0f;
reload = 4f;
inaccuracy = 5f;
shootCone = 50f;
shootEffect = ShootFx.shootLiquid;
range = 70f;
health = 360;
drawer = (tile, entity) -> {
Draw.rect(name, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
drawer = (tile, entity) -> {
Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
Draw.color(entity.liquids.liquid.color);
Draw.alpha(entity.liquids.amount / liquidCapacity);
Draw.rect(name + "-liquid", tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
Draw.color();
};
}};
Draw.color(entity.liquids.current().color);
Draw.alpha(entity.liquids.total() / liquidCapacity);
Draw.rect(name + "-liquid", tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
Draw.color();
};
}};
lancer = new LaserTurret("lancer") {{
range = 90f;
chargeTime = 60f;
chargeMaxDelay = 30f;
chargeEffects = 7;
shootType = AmmoTypes.lancerLaser;
recoil = 2f;
reload = 100f;
cooldown = 0.03f;
powerUsed = 20f;
powerCapacity = 60f;
shootShake = 2f;
shootEffect = ShootFx.lancerLaserShoot;
smokeEffect = ShootFx.lancerLaserShootSmoke;
chargeEffect = ShootFx.lancerLaserCharge;
chargeBeginEffect = ShootFx.lancerLaserChargeBegin;
heatColor = Color.RED;
size = 2;
health = 320;
}};
lancer = new LaserTurret("lancer"){{
range = 90f;
chargeTime = 60f;
chargeMaxDelay = 30f;
chargeEffects = 7;
shootType = AmmoTypes.lancerLaser;
recoil = 2f;
reload = 100f;
cooldown = 0.03f;
powerUsed = 20f;
powerCapacity = 60f;
shootShake = 2f;
shootEffect = ShootFx.lancerLaserShoot;
smokeEffect = ShootFx.lancerLaserShootSmoke;
chargeEffect = ShootFx.lancerLaserCharge;
chargeBeginEffect = ShootFx.lancerLaserChargeBegin;
heatColor = Color.RED;
size = 2;
health = 320;
}};
arc = new LaserTurret("arc") {{
shootType = AmmoTypes.lightning;
reload = 100f;
chargeTime = 70f;
shootShake = 1f;
chargeMaxDelay = 30f;
chargeEffects = 7;
shootEffect = ShootFx.lightningShoot;
chargeEffect = ShootFx.lightningCharge;
chargeBeginEffect = ShootFx.lancerLaserChargeBegin;
heatColor = Color.RED;
recoil = 3f;
size = 2;
}};
arc = new LaserTurret("arc"){{
shootType = AmmoTypes.lightning;
reload = 100f;
chargeTime = 70f;
shootShake = 1f;
chargeMaxDelay = 30f;
chargeEffects = 7;
shootEffect = ShootFx.lightningShoot;
chargeEffect = ShootFx.lightningCharge;
chargeBeginEffect = ShootFx.lancerLaserChargeBegin;
heatColor = Color.RED;
recoil = 3f;
size = 2;
}};
swarmer = new BurstTurret("swarmer") {{
ammoTypes = new AmmoType[]{AmmoTypes.missileExplosive, AmmoTypes.missileIncindiary/*, AmmoTypes.missileSurge*/};
reload = 60f;
shots = 4;
burstSpacing = 5;
inaccuracy = 10f;
range = 140f;
xRand = 6f;
size = 2;
health = 380;
}};
swarmer = new BurstTurret("swarmer"){{
ammoTypes = new AmmoType[]{AmmoTypes.missileExplosive, AmmoTypes.missileIncindiary/*, AmmoTypes.missileSurge*/};
reload = 60f;
shots = 4;
burstSpacing = 5;
inaccuracy = 10f;
range = 140f;
xRand = 6f;
size = 2;
health = 380;
}};
salvo = new BurstTurret("salvo") {{
size = 2;
range = 120f;
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletThorium, AmmoTypes.bulletSilicon};
reload = 40f;
restitution = 0.03f;
ammoEjectBack = 3f;
cooldown = 0.03f;
recoil = 3f;
shootShake = 2f;
burstSpacing = 4;
shots = 3;
ammoUseEffect = ShootFx.shellEjectBig;
salvo = new BurstTurret("salvo"){{
size = 2;
range = 120f;
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletThorium, AmmoTypes.bulletSilicon};
reload = 40f;
restitution = 0.03f;
ammoEjectBack = 3f;
cooldown = 0.03f;
recoil = 3f;
shootShake = 2f;
burstSpacing = 4;
shots = 3;
ammoUseEffect = ShootFx.shellEjectBig;
drawer = (tile, entity) -> {
Draw.rect(name, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
float offsetx = (int) (Mathf.abscurve(Mathf.curve(entity.reload / reload, 0.3f, 0.2f)) * 3f);
float offsety = -(int) (Mathf.abscurve(Mathf.curve(entity.reload / reload, 0.3f, 0.2f)) * 2f);
drawer = (tile, entity) -> {
Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
float offsetx = (int) (Mathf.abscurve(Mathf.curve(entity.reload / reload, 0.3f, 0.2f)) * 3f);
float offsety = -(int) (Mathf.abscurve(Mathf.curve(entity.reload / reload, 0.3f, 0.2f)) * 2f);
for (int i : Mathf.signs) {
float rot = entity.rotation + 90 * i;
Draw.rect(name + "-panel-" + Strings.dir(i),
tile.drawx() + tr2.x + Angles.trnsx(rot, offsetx, offsety),
tile.drawy() + tr2.y + Angles.trnsy(rot, -offsetx, offsety), entity.rotation - 90);
}
};
for(int i : Mathf.signs){
float rot = entity.rotation + 90 * i;
Draw.rect(name + "-panel-" + Strings.dir(i),
tile.drawx() + tr2.x + Angles.trnsx(rot, offsetx, offsety),
tile.drawy() + tr2.y + Angles.trnsy(rot, -offsetx, offsety), entity.rotation - 90);
}
};
health = 360;
}};
health = 360;
}};
ripple = new ArtilleryTurret("ripple") {{
ammoTypes = new AmmoType[]{AmmoTypes.artilleryCarbide, AmmoTypes.artilleryHoming, AmmoTypes.artilleryIncindiary, AmmoTypes.artilleryExplosive, AmmoTypes.artilleryPlastic};
size = 3;
shots = 4;
inaccuracy = 12f;
reload = 60f;
ammoEjectBack = 5f;
ripple = new ArtilleryTurret("ripple"){{
ammoTypes = new AmmoType[]{AmmoTypes.artilleryCarbide, AmmoTypes.artilleryHoming, AmmoTypes.artilleryIncindiary, AmmoTypes.artilleryExplosive, AmmoTypes.artilleryPlastic};
size = 3;
shots = 4;
inaccuracy = 12f;
reload = 60f;
ammoEjectBack = 5f;
ammoUseEffect = ShootFx.shellEjectBig;
cooldown = 0.03f;
velocityInaccuracy = 0.2f;
velocityInaccuracy = 0.2f;
restitution = 0.02f;
recoil = 6f;
shootShake = 2f;
range = 300f;
health = 550;
}};
health = 550;
}};
cyclone = new ItemTurret("cyclone") {{
ammoTypes = new AmmoType[]{AmmoTypes.flakLead, AmmoTypes.flakExplosive, AmmoTypes.flakPlastic, AmmoTypes.flakSurge};
size = 3;
}};
cyclone = new ItemTurret("cyclone"){{
ammoTypes = new AmmoType[]{AmmoTypes.flakLead, AmmoTypes.flakExplosive, AmmoTypes.flakPlastic, AmmoTypes.flakSurge};
size = 3;
}};
fuse = new ItemTurret("fuse") {{
//TODO make it use power
ammoTypes = new AmmoType[]{AmmoTypes.fuseShotgun};
size = 3;
}};
fuse = new ItemTurret("fuse"){{
//TODO make it use power
ammoTypes = new AmmoType[]{AmmoTypes.fuseShotgun};
size = 3;
}};
spectre = new ItemTurret("spectre") {{
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletThorium, AmmoTypes.bulletSilicon};
reload = 25f;
restitution = 0.03f;
ammoUseEffect = ShootFx.shellEjectSmall;
size = 4;
}};
spectre = new ItemTurret("spectre"){{
ammoTypes = new AmmoType[]{AmmoTypes.bulletTungsten, AmmoTypes.bulletLead, AmmoTypes.bulletCarbide, AmmoTypes.bulletPyratite, AmmoTypes.bulletThorium, AmmoTypes.bulletSilicon};
reload = 25f;
restitution = 0.03f;
ammoUseEffect = ShootFx.shellEjectSmall;
size = 4;
}};
meltdown = new PowerTurret("meltdown") {{
shootType = AmmoTypes.meltdownLaser;
size = 4;
}};
}
meltdown = new PowerTurret("meltdown"){{
shootType = AmmoTypes.meltdownLaser;
size = 4;
}};
}
}

View File

@@ -7,54 +7,51 @@ import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.units.*;
public class UnitBlocks extends BlockList implements ContentList {
public class UnitBlocks extends BlockList implements ContentList{
public static Block resupplyPoint, repairPoint, droneFactory, fabricatorFactory, dropPoint, reconstructor, overdriveProjector, shieldProjector;
@Override
public void load() {
droneFactory = new UnitFactory("drone-factory") {{
public void load(){
droneFactory = new UnitFactory("drone-factory"){{
type = UnitTypes.drone;
produceTime = 800;
size = 2;
requirements = new ItemStack[]{
new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30)
};
consumes.power(0.08f);
consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30)});
}};
fabricatorFactory = new UnitFactory("fabricator-factory") {{
fabricatorFactory = new UnitFactory("fabricator-factory"){{
type = UnitTypes.fabricator;
produceTime = 1600;
size = 2;
powerUse = 0.2f;
requirements = new ItemStack[]{
new ItemStack(Items.silicon, 70), new ItemStack(Items.lead, 80), new ItemStack(Items.titanium, 80)
};
consumes.power(0.2f);
consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 70), new ItemStack(Items.lead, 80), new ItemStack(Items.titanium, 80)});
}};
resupplyPoint = new ResupplyPoint("resupply-point") {{
resupplyPoint = new ResupplyPoint("resupply-point"){{
shadow = "shadow-round-1";
itemCapacity = 30;
}};
dropPoint = new DropPoint("drop-point") {{
dropPoint = new DropPoint("drop-point"){{
shadow = "shadow-round-1";
itemCapacity = 40;
}};
repairPoint = new RepairPoint("repair-point") {{
repairPoint = new RepairPoint("repair-point"){{
shadow = "shadow-round-1";
repairSpeed = 0.1f;
}};
reconstructor = new Reconstructor("reconstructor") {{
reconstructor = new Reconstructor("reconstructor"){{
size = 2;
}};
overdriveProjector = new OverdriveProjector("overdrive-projector") {{
overdriveProjector = new OverdriveProjector("overdrive-projector"){{
size = 2;
}};
shieldProjector = new ShieldProjector("shieldprojector") {{
shieldProjector = new ShieldProjector("shieldprojector"){{
size = 2;
}};
}

View File

@@ -4,11 +4,11 @@ import io.anuke.mindustry.content.Mechs;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.units.MechFactory;
public class UpgradeBlocks extends BlockList {
public class UpgradeBlocks extends BlockList{
public static Block deltaFactory, tauFactory, omegaFactory, dartFactory, javelinFactory, tridentFactory, halberdFactory;
@Override
public void load() {
public void load(){
deltaFactory = new MechFactory("delta-mech-factory"){{
mech = Mechs.delta;
size = 2;

View File

@@ -11,9 +11,9 @@ public class ArtilleryBullets extends BulletList implements ContentList{
public static BulletType carbide, plastic, plasticFrag, homing, incindiary, explosive, surge;
@Override
public void load() {
public void load(){
carbide = new ArtilleryBulletType(3f, 0, "shell") {
carbide = new ArtilleryBulletType(3f, 0, "shell"){
{
hiteffect = BulletFx.flakExplosion;
knockback = 0.8f;
@@ -25,7 +25,7 @@ public class ArtilleryBullets extends BulletList implements ContentList{
}
};
plasticFrag = new BasicBulletType(2.5f, 6, "bullet") {
plasticFrag = new BasicBulletType(2.5f, 6, "bullet"){
{
bulletWidth = 10f;
bulletHeight = 12f;
@@ -36,7 +36,7 @@ public class ArtilleryBullets extends BulletList implements ContentList{
}
};
plastic = new ArtilleryBulletType(3.3f, 0, "shell") {
plastic = new ArtilleryBulletType(3.3f, 0, "shell"){
{
hiteffect = BulletFx.plasticExplosion;
knockback = 1f;
@@ -52,7 +52,7 @@ public class ArtilleryBullets extends BulletList implements ContentList{
}
};
homing = new ArtilleryBulletType(3f, 0, "shell") {
homing = new ArtilleryBulletType(3f, 0, "shell"){
{
hiteffect = BulletFx.flakExplosion;
knockback = 0.8f;
@@ -66,7 +66,7 @@ public class ArtilleryBullets extends BulletList implements ContentList{
}
};
incindiary = new ArtilleryBulletType(3f, 0, "shell") {
incindiary = new ArtilleryBulletType(3f, 0, "shell"){
{
hiteffect = BulletFx.blastExplosion;
knockback = 0.8f;
@@ -83,7 +83,7 @@ public class ArtilleryBullets extends BulletList implements ContentList{
}
};
explosive = new ArtilleryBulletType(2f, 0, "shell") {
explosive = new ArtilleryBulletType(2f, 0, "shell"){
{
hiteffect = BulletFx.blastExplosion;
knockback = 0.8f;
@@ -97,7 +97,7 @@ public class ArtilleryBullets extends BulletList implements ContentList{
}
};
surge = new ArtilleryBulletType(3f, 0, "shell") {
surge = new ArtilleryBulletType(3f, 0, "shell"){
{
//TODO
}

View File

@@ -5,10 +5,10 @@ import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.ContentList;
public abstract class BulletList implements ContentList {
public abstract class BulletList implements ContentList{
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return BulletType.all();
}
}

View File

@@ -4,34 +4,34 @@ import io.anuke.mindustry.entities.bullet.BasicBulletType;
import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.type.ContentList;
public class FlakBullets extends BulletList implements ContentList {
public class FlakBullets extends BulletList implements ContentList{
public static BulletType lead, plastic, explosive, surge;
@Override
public void load() {
public void load(){
lead = new BasicBulletType(3f, 5, "bullet") {
lead = new BasicBulletType(3f, 5, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;
}
};
plastic = new BasicBulletType(3f, 5, "bullet") {
plastic = new BasicBulletType(3f, 5, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;
}
};
explosive = new BasicBulletType(3f, 5, "bullet") {
explosive = new BasicBulletType(3f, 5, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;
}
};
surge = new BasicBulletType(3f, 5, "bullet") {
surge = new BasicBulletType(3f, 5, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;

View File

@@ -6,13 +6,13 @@ import io.anuke.mindustry.entities.bullet.MissileBulletType;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ContentList;
public class MissileBullets extends BulletList implements ContentList {
public class MissileBullets extends BulletList implements ContentList{
public static BulletType explosive, incindiary, surge, javelin;
@Override
public void load() {
public void load(){
explosive = new MissileBulletType(1.8f, 10, "missile") {
explosive = new MissileBulletType(1.8f, 10, "missile"){
{
bulletWidth = 8f;
bulletHeight = 8f;
@@ -26,7 +26,7 @@ public class MissileBullets extends BulletList implements ContentList {
}
};
incindiary = new MissileBulletType(2f, 12, "missile") {
incindiary = new MissileBulletType(2f, 12, "missile"){
{
frontColor = Palette.lightishOrange;
backColor = Palette.lightOrange;
@@ -44,14 +44,14 @@ public class MissileBullets extends BulletList implements ContentList {
}
};
surge = new MissileBulletType(3f, 5, "bullet") {
surge = new MissileBulletType(3f, 5, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;
}
};
javelin = new MissileBulletType(2.5f, 10, "missile") {
javelin = new MissileBulletType(2.5f, 10, "missile"){
{
bulletWidth = 8f;
bulletHeight = 8f;

View File

@@ -5,27 +5,27 @@ import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ContentList;
public class StandardBullets extends BulletList implements ContentList {
public class StandardBullets extends BulletList implements ContentList{
public static BulletType tungsten, lead, carbide, thorium, homing, tracer;
@Override
public void load() {
public void load(){
tungsten = new BasicBulletType(3.2f, 10, "bullet") {
tungsten = new BasicBulletType(3.2f, 10, "bullet"){
{
bulletWidth = 9f;
bulletHeight = 11f;
}
};
lead = new BasicBulletType(2.5f, 5, "bullet") {
lead = new BasicBulletType(2.5f, 5, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;
}
};
carbide = new BasicBulletType(3.5f, 18, "bullet") {
carbide = new BasicBulletType(3.5f, 18, "bullet"){
{
bulletWidth = 9f;
bulletHeight = 12f;
@@ -33,7 +33,7 @@ public class StandardBullets extends BulletList implements ContentList {
}
};
thorium = new BasicBulletType(4f, 29, "bullet") {
thorium = new BasicBulletType(4f, 29, "bullet"){
{
bulletWidth = 10f;
bulletHeight = 13f;
@@ -41,7 +41,7 @@ public class StandardBullets extends BulletList implements ContentList {
}
};
homing = new BasicBulletType(3f, 9, "bullet") {
homing = new BasicBulletType(3f, 9, "bullet"){
{
bulletWidth = 7f;
bulletHeight = 9f;
@@ -49,7 +49,7 @@ public class StandardBullets extends BulletList implements ContentList {
}
};
tracer = new BasicBulletType(3.2f, 11, "bullet") {
tracer = new BasicBulletType(3.2f, 11, "bullet"){
{
bulletWidth = 10f;
bulletHeight = 12f;

View File

@@ -29,13 +29,13 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.world;
public class TurretBullets extends BulletList implements ContentList {
public class TurretBullets extends BulletList implements ContentList{
public static BulletType fireball, basicFlame, lancerLaser, fuseShot, waterShot, cryoShot, lavaShot, oilShot, lightning, driverBolt;
@Override
public void load() {
public void load(){
fireball = new BulletType(1f, 4) {
fireball = new BulletType(1f, 4){
{
pierce = true;
hitTiles = false;
@@ -45,12 +45,12 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void init(Bullet b) {
public void init(Bullet b){
b.getVelocity().setLength(0.6f + Mathf.random(2f));
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
//TODO add color to the bullet depending on the color of the flame it came from
Draw.color(Palette.lightFlame, Palette.darkFlame, Color.GRAY, b.fin());
Fill.circle(b.x, b.y, 3f * b.fout());
@@ -58,25 +58,25 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void update(Bullet b) {
if (Mathf.chance(0.04 * Timers.delta())) {
public void update(Bullet b){
if(Mathf.chance(0.04 * Timers.delta())){
Tile tile = world.tileWorld(b.x, b.y);
if (tile != null) {
if(tile != null){
Fire.create(tile);
}
}
if (Mathf.chance(0.1 * Timers.delta())) {
if(Mathf.chance(0.1 * Timers.delta())){
Effects.effect(EnvironmentFx.fireballsmoke, b.x, b.y);
}
if (Mathf.chance(0.1 * Timers.delta())) {
if(Mathf.chance(0.1 * Timers.delta())){
Effects.effect(EnvironmentFx.ballfire, b.x, b.y);
}
}
};
basicFlame = new BulletType(2f, 5) {
basicFlame = new BulletType(2f, 5){
{
hitsize = 7f;
lifetime = 30f;
@@ -88,11 +88,11 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
}
};
lancerLaser = new BulletType(0.001f, 110) {
lancerLaser = new BulletType(0.001f, 110){
Color[] colors = {Palette.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Palette.lancerLaser, Color.WHITE};
float[] tscales = {1f, 0.7f, 0.5f, 0.2f};
float[] lenscales = {1f, 1.1f, 1.13f, 1.14f};
@@ -107,19 +107,19 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void init(Bullet b) {
public void init(Bullet b){
Damage.collideLine(b, b.getTeam(), hiteffect, b.x, b.y, b.angle(), length);
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
float f = Mathf.curve(b.fin(), 0f, 0.2f);
float baseLen = length * f;
Lines.lineAngle(b.x, b.y, b.angle(), baseLen);
for (int s = 0; s < 3; s++) {
for(int s = 0; s < 3; s++){
Draw.color(colors[s]);
for (int i = 0; i < tscales.length; i++) {
for(int i = 0; i < tscales.length; i++){
Lines.stroke(7f * b.fout() * (s == 0 ? 1.5f : s == 1 ? 1f : 0.3f) * tscales[i]);
Lines.lineAngle(b.x, b.y, b.angle(), baseLen * lenscales[i]);
}
@@ -128,24 +128,24 @@ public class TurretBullets extends BulletList implements ContentList {
}
};
fuseShot = new BulletType(0.01f, 100) {
fuseShot = new BulletType(0.01f, 100){
//TODO
};
waterShot = new LiquidBulletType(Liquids.water) {
waterShot = new LiquidBulletType(Liquids.water){
{
status = StatusEffects.wet;
statusIntensity = 0.5f;
knockback = 0.65f;
}
};
cryoShot = new LiquidBulletType(Liquids.cryofluid) {
cryoShot = new LiquidBulletType(Liquids.cryofluid){
{
status = StatusEffects.freezing;
statusIntensity = 0.5f;
}
};
lavaShot = new LiquidBulletType(Liquids.lava) {
lavaShot = new LiquidBulletType(Liquids.lava){
{
damage = 4;
speed = 1.9f;
@@ -154,7 +154,7 @@ public class TurretBullets extends BulletList implements ContentList {
statusIntensity = 0.5f;
}
};
oilShot = new LiquidBulletType(Liquids.oil) {
oilShot = new LiquidBulletType(Liquids.oil){
{
speed = 2f;
drag = 0.03f;
@@ -162,7 +162,7 @@ public class TurretBullets extends BulletList implements ContentList {
statusIntensity = 0.5f;
}
};
lightning = new BulletType(0.001f, 10) {
lightning = new BulletType(0.001f, 10){
{
lifetime = 1;
despawneffect = Fx.none;
@@ -170,16 +170,16 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
}
@Override
public void init(Bullet b) {
public void init(Bullet b){
Lightning.create(b.getTeam(), hiteffect, Palette.lancerLaser, damage, b.x, b.y, b.angle(), 30);
}
};
driverBolt = new BulletType(5f, 20) {
driverBolt = new BulletType(5f, 20){
{
collidesTiles = false;
lifetime = 200f;
@@ -189,7 +189,7 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
Draw.color(Color.LIGHT_GRAY);
Fill.square(b.x, b.y, 3f, b.angle());
@@ -199,7 +199,7 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void update(Bullet b) {
public void update(Bullet b){
//data MUST be an instance of DriverBulletData
if(!(b.getData() instanceof DriverBulletData)){
hit(b);
@@ -208,7 +208,7 @@ public class TurretBullets extends BulletList implements ContentList {
float hitDst = 7f;
DriverBulletData data = (DriverBulletData)b.getData();
DriverBulletData data = (DriverBulletData) b.getData();
//if the target is dead, just keep flying until the bullet explodes
if(data.to.isDead()){
@@ -245,15 +245,15 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void despawned(Bullet b) {
public void despawned(Bullet b){
super.despawned(b);
if(!(b.getData() instanceof DriverBulletData)) return;
DriverBulletData data = (DriverBulletData)b.getData();
DriverBulletData data = (DriverBulletData) b.getData();
data.to.isRecieving = false;
for(int i = 0; i < data.items.length; i ++){
for(int i = 0; i < data.items.length; i++){
int amountDropped = Mathf.random(0, data.items[i]);
if(amountDropped > 0){
float angle = b.angle() + Mathf.range(100f);
@@ -264,7 +264,7 @@ public class TurretBullets extends BulletList implements ContentList {
}
@Override
public void hit(Bullet b, float hitx, float hity) {
public void hit(Bullet b, float hitx, float hity){
super.hit(b, hitx, hity);
despawned(b);
}

View File

@@ -16,12 +16,12 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.world;
public class WeaponBullets extends BulletList {
public class WeaponBullets extends BulletList{
public static BulletType tungstenShotgun, bombExplosive, bombIncendiary, bombOil, shellCarbide;
@Override
public void load() {
tungstenShotgun = new BasicBulletType(5f, 8, "bullet") {
public void load(){
tungstenShotgun = new BasicBulletType(5f, 8, "bullet"){
{
bulletWidth = 8f;
bulletHeight = 9f;
@@ -49,10 +49,10 @@ public class WeaponBullets extends BulletList {
}
@Override
public void hit(Bullet b, float x, float y) {
public void hit(Bullet b, float x, float y){
super.hit(b, x, y);
for (int i = 0; i < 3; i++) {
for(int i = 0; i < 3; i++){
float cx = x + Mathf.range(10f);
float cy = y + Mathf.range(10f);
Tile tile = world.tileWorld(cx, cy);
@@ -73,17 +73,17 @@ public class WeaponBullets extends BulletList {
}
@Override
public void hit(Bullet b, float x, float y) {
public void hit(Bullet b, float x, float y){
super.hit(b, x, y);
for (int i = 0; i < 3; i++) {
for(int i = 0; i < 3; i++){
Tile tile = world.tileWorld(x + Mathf.range(8f), y + Mathf.range(8f));
Puddle.deposit(tile, Liquids.oil, 5f);
}
}
};
shellCarbide = new BasicBulletType(3.4f, 20, "bullet") {
shellCarbide = new BasicBulletType(3.4f, 20, "bullet"){
{
bulletWidth = 10f;
bulletHeight = 12f;

View File

@@ -19,7 +19,7 @@ public class BlockFx extends FxList implements ContentList{
public static Effect reactorsmoke, nuclearsmoke, nuclearcloud, redgeneratespark, generatespark, fuelburn, plasticburn, pulverize, pulverizeRed, pulverizeRedder, pulverizeSmall, pulverizeMedium, producesmoke, smeltsmoke, formsmoke, blastsmoke, lava, dooropen, doorclose, dooropenlarge, doorcloselarge, purify, purifyoil, purifystone, generate, mine, mineBig, mineHuge, smelt, teleportActivate, teleport, teleportOut, ripple, bubble;
@Override
public void load() {
public void load(){
reactorsmoke = new Effect(17, e -> {
Angles.randLenVectors(e.id, 4, e.fin() * 8f, (x, y) -> {

View File

@@ -10,12 +10,12 @@ import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
public class BulletFx extends FxList implements ContentList {
public class BulletFx extends FxList implements ContentList{
public static Effect hitBulletSmall, hitBulletBig, hitFlameSmall, hitLiquid, hitLancer, despawn, flakExplosion, blastExplosion, plasticExplosion,
artilleryTrail, incendTrail, missileTrail;
@Override
public void load() {
public void load(){
hitBulletSmall = new Effect(14, e -> {
Draw.color(Color.WHITE, Palette.lightOrange, e.fin());

View File

@@ -10,11 +10,11 @@ import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
public class EnvironmentFx extends FxList implements ContentList {
public class EnvironmentFx extends FxList implements ContentList{
public static Effect burning, fire, smoke, steam, fireballsmoke, ballfire, freezing, melting, wet, oily;
@Override
public void load() {
public void load(){
burning = new Effect(35f, e -> {
Draw.color(Palette.lightFlame, Palette.darkFlame, e.fin());

View File

@@ -10,11 +10,11 @@ import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
public class ExplosionFx extends FxList implements ContentList {
public class ExplosionFx extends FxList implements ContentList{
public static Effect shockwave, bigShockwave, nuclearShockwave, explosion, blockExplosion, blockExplosionSmoke;
@Override
public void load() {
public void load(){
shockwave = new Effect(10f, 80f, e -> {
Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin());

View File

@@ -11,59 +11,59 @@ import io.anuke.ucore.util.Angles;
import static io.anuke.mindustry.Vars.tilesize;
public class Fx extends FxList implements ContentList {
public static Effect none, placeBlock, breakBlock, smoke, spawn, tapBlock, select;
public class Fx extends FxList implements ContentList{
public static Effect none, placeBlock, breakBlock, smoke, spawn, tapBlock, select;
@Override
public void load() {
@Override
public void load(){
none = new Effect(0, 0f, e -> {
});
none = new Effect(0, 0f, e -> {
});
placeBlock = new Effect(16, e -> {
Draw.color(Palette.accent);
Lines.stroke(3f - e.fin() * 2f);
Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);
Draw.reset();
});
placeBlock = new Effect(16, e -> {
Draw.color(Palette.accent);
Lines.stroke(3f - e.fin() * 2f);
Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);
Draw.reset();
});
tapBlock = new Effect(12, e -> {
Draw.color(Palette.accent);
Lines.stroke(3f - e.fin() * 2f);
Lines.circle(e.x, e.y, 4f + (tilesize/1.5f * e.rotation) * e.fin());
Draw.reset();
});
tapBlock = new Effect(12, e -> {
Draw.color(Palette.accent);
Lines.stroke(3f - e.fin() * 2f);
Lines.circle(e.x, e.y, 4f + (tilesize / 1.5f * e.rotation) * e.fin());
Draw.reset();
});
breakBlock = new Effect(12, e -> {
Draw.color(Palette.remove);
Lines.stroke(3f - e.fin() * 2f);
Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);
breakBlock = new Effect(12, e -> {
Draw.color(Palette.remove);
Lines.stroke(3f - e.fin() * 2f);
Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);
Angles.randLenVectors(e.id, 3 + (int) (e.rotation * 3), e.rotation * 2f + (tilesize * e.rotation) * e.finpow(), (x, y) -> {
Fill.square(e.x + x, e.y + y, 1f + e.fout() * (3f + e.rotation));
});
Draw.reset();
});
Angles.randLenVectors(e.id, 3 + (int) (e.rotation * 3), e.rotation * 2f + (tilesize * e.rotation) * e.finpow(), (x, y) -> {
Fill.square(e.x + x, e.y + y, 1f + e.fout() * (3f + e.rotation));
});
Draw.reset();
});
select = new Effect(23, e -> {
Draw.color(Palette.accent);
Lines.stroke(e.fout() * 3f);
Lines.circle(e.x, e.y, 3f + e.fin() * 14f);
Draw.reset();
});
select = new Effect(23, e -> {
Draw.color(Palette.accent);
Lines.stroke(e.fout() * 3f);
Lines.circle(e.x, e.y, 3f + e.fin() * 14f);
Draw.reset();
});
smoke = new Effect(100, e -> {
Draw.color(Color.GRAY, Palette.darkishGray, e.fin());
float size = 7f - e.fin() * 7f;
Draw.rect("circle", e.x, e.y, size, size);
Draw.reset();
});
smoke = new Effect(100, e -> {
Draw.color(Color.GRAY, Palette.darkishGray, e.fin());
float size = 7f - e.fin() * 7f;
Draw.rect("circle", e.x, e.y, size, size);
Draw.reset();
});
spawn = new Effect(23, e -> {
Lines.stroke(2f * e.fout());
Draw.color(Palette.accent);
Lines.poly(e.x, e.y, 4, 3f + e.fin() * 8f);
Draw.reset();
});
}
spawn = new Effect(23, e -> {
Lines.stroke(2f * e.fout());
Draw.color(Palette.accent);
Lines.poly(e.x, e.y, 4, 3f + e.fin() * 8f);
Draw.reset();
});
}
}

View File

@@ -7,7 +7,7 @@ import io.anuke.mindustry.type.ContentList;
public abstract class FxList implements ContentList{
@Override
public Array<? extends Content> getAll() {
public Array<? extends Content> getAll(){
return Array.with();
}
}

View File

@@ -12,11 +12,11 @@ import io.anuke.ucore.graphics.Shapes;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
public class ShootFx extends FxList implements ContentList {
public class ShootFx extends FxList implements ContentList{
public static Effect shootSmall, shootSmallSmoke, shootBig, shootBig2, shootBigSmoke, shootBigSmoke2, shootSmallFlame, shootLiquid, shellEjectSmall, shellEjectMedium, shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot;
@Override
public void load() {
public void load(){
shootSmall = new Effect(8, e -> {
Draw.color(Palette.lighterOrange, Palette.lightOrange, e.fin());
@@ -111,7 +111,7 @@ public class ShootFx extends FxList implements ContentList {
shellEjectMedium = new GroundEffect(34f, 400f, e -> {
Draw.color(Palette.lightOrange, Color.LIGHT_GRAY, Palette.lightishGray, e.fin());
float rot = e.rotation + 90f;
for (int i : Mathf.signs) {
for(int i : Mathf.signs){
float len = (2f + e.finpow() * 10f) * i;
float lr = rot + e.fin() * 20f * i;
Draw.rect("casing",
@@ -122,7 +122,7 @@ public class ShootFx extends FxList implements ContentList {
Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin());
for (int i : Mathf.signs) {
for(int i : Mathf.signs){
Angles.randLenVectors(e.id, 4, 1f + e.finpow() * 11f, e.rotation + 90f * i, 20f, (x, y) -> {
Fill.circle(e.x + x, e.y + y, e.fout() * 1.5f);
});
@@ -134,7 +134,7 @@ public class ShootFx extends FxList implements ContentList {
shellEjectBig = new GroundEffect(22f, 400f, e -> {
Draw.color(Palette.lightOrange, Color.LIGHT_GRAY, Palette.lightishGray, e.fin());
float rot = e.rotation + 90f;
for (int i : Mathf.signs) {
for(int i : Mathf.signs){
float len = (4f + e.finpow() * 8f) * i;
float lr = rot + Mathf.randomSeedRange(e.id + i + 6, 20f * e.fin()) * i;
Draw.rect("casing",
@@ -146,7 +146,7 @@ public class ShootFx extends FxList implements ContentList {
Draw.color(Color.LIGHT_GRAY);
for (int i : Mathf.signs) {
for(int i : Mathf.signs){
Angles.randLenVectors(e.id, 4, -e.finpow() * 15f, e.rotation + 90f * i, 25f, (x, y) -> {
Fill.circle(e.x + x, e.y + y, e.fout() * 2f);
});
@@ -158,7 +158,7 @@ public class ShootFx extends FxList implements ContentList {
lancerLaserShoot = new Effect(21f, e -> {
Draw.color(Palette.lancerLaser);
for (int i : Mathf.signs) {
for(int i : Mathf.signs){
Shapes.tri(e.x, e.y, 4f * e.fout(), 29f, e.rotation + 90f * i);
}

View File

@@ -10,11 +10,11 @@ import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
public class UnitFx extends FxList implements ContentList {
public class UnitFx extends FxList implements ContentList{
public static Effect vtolHover, unitDrop, unitPickup, pickup;
@Override
public void load() {
public void load(){
vtolHover = new Effect(40f, e -> {
float len = e.finpow() * 10f;

View File

@@ -25,76 +25,80 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Log;
/**Loads all game content.
* Call load() before doing anything with content.*/
public class ContentLoader {
/**
* Loads all game content.
* Call load() before doing anything with content.
*/
public class ContentLoader{
private static boolean loaded = false;
private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>();
private static OrderedMap<String, Array<Content>> contentMap = new OrderedMap<>();
private static ObjectSet<Consumer<Content>> initialization = new ObjectSet<>();
private static ContentList[] content = {
//effects
new BlockFx(),
new BulletFx(),
new EnvironmentFx(),
new ExplosionFx(),
new Fx(),
new ShootFx(),
new UnitFx(),
//effects
new BlockFx(),
new BulletFx(),
new EnvironmentFx(),
new ExplosionFx(),
new Fx(),
new ShootFx(),
new UnitFx(),
//items
new Items(),
//items
new Items(),
//status effects
new StatusEffects(),
//status effects
new StatusEffects(),
//liquids
new Liquids(),
//liquids
new Liquids(),
//bullets
new ArtilleryBullets(),
new FlakBullets(),
new MissileBullets(),
new StandardBullets(),
new TurretBullets(),
new WeaponBullets(),
//bullets
new ArtilleryBullets(),
new FlakBullets(),
new MissileBullets(),
new StandardBullets(),
new TurretBullets(),
new WeaponBullets(),
//ammotypes
new AmmoTypes(),
//ammotypes
new AmmoTypes(),
//weapons
new Weapons(),
//weapons
new Weapons(),
//mechs
new Mechs(),
//mechs
new Mechs(),
//units
new UnitTypes(),
//units
new UnitTypes(),
//blocks
new Blocks(),
new DefenseBlocks(),
new DistributionBlocks(),
new ProductionBlocks(),
new TurretBlocks(),
new DebugBlocks(),
new LiquidBlocks(),
new StorageBlocks(),
new UnitBlocks(),
new PowerBlocks(),
new CraftingBlocks(),
new UpgradeBlocks(),
new OreBlocks(),
//blocks
new Blocks(),
new DefenseBlocks(),
new DistributionBlocks(),
new ProductionBlocks(),
new TurretBlocks(),
new DebugBlocks(),
new LiquidBlocks(),
new StorageBlocks(),
new UnitBlocks(),
new PowerBlocks(),
new CraftingBlocks(),
new UpgradeBlocks(),
new OreBlocks(),
//not really a content class, but this makes initialization easier
new ColorMapper(),
//not really a content class, but this makes initialization easier
new ColorMapper(),
//recipes
new Recipes(),
//recipes
new Recipes(),
};
/**Creates all content types.*/
/**
* Creates all content types.
*/
public static void load(){
if(loaded){
Log.info("Content already loaded, skipping.");
@@ -103,11 +107,11 @@ public class ContentLoader {
registerTypes();
for (ContentList list : content){
for(ContentList list : content){
list.load();
}
for (ContentList list : content){
for(ContentList list : content){
if(list.getAll().size != 0){
String type = list.getAll().first().getContentTypeName();
@@ -134,7 +138,9 @@ public class ContentLoader {
loaded = true;
}
/**Initializes all content with the specified function.*/
/**
* Initializes all content with the specified function.
*/
public static void initialize(Consumer<Content> callable){
if(initialization.contains(callable)) return;
@@ -155,8 +161,10 @@ public class ContentLoader {
return contentMap;
}
/**Registers sync IDs for all types of sync entities.
* Do not register units here!*/
/**
* Registers sync IDs for all types of sync entities.
* Do not register units here!
*/
private static void registerTypes(){
TypeTrait.registerType(Player.class, Player::new);
TypeTrait.registerType(ItemDrop.class, ItemDrop::new);

View File

@@ -20,7 +20,6 @@ import io.anuke.mindustry.input.MobileInput;
import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.io.Saves;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.ucore.core.*;
@@ -31,142 +30,146 @@ import io.anuke.ucore.util.Atlas;
import static io.anuke.mindustry.Vars.*;
/**Control module.
/**
* Control module.
* Handles all input, saving, keybinds and keybinds.
* Should <i>not</i> handle any logic-critical state.
* This class is not created in the headless server.*/
* This class is not created in the headless server.
*/
public class Control extends Module{
/**Minimum period of time between the same sound being played.*/
private static final long minSoundPeriod = 100;
/** Minimum period of time between the same sound being played.*/
private static final long minSoundPeriod = 100;
private boolean hiscore = false;
private boolean wasPaused = false;
private Saves saves;
private ContentDatabase db;
private InputHandler[] inputs = {};
private ObjectMap<Sound, Long> soundMap = new ObjectMap<>();
private boolean hiscore = false;
private boolean wasPaused = false;
private Saves saves;
private ContentDatabase db;
private InputHandler[] inputs = {};
private ObjectMap<Sound, Long> soundMap = new ObjectMap<>();
private Throwable error;
private Input gdxInput;
public Control(){
public Control(){
saves = new Saves();
db = new ContentDatabase();
saves = new Saves();
db = new ContentDatabase();
Inputs.useControllers(!gwt);
Inputs.useControllers(!gwt);
Gdx.input.setCatchBackKey(true);
Gdx.input.setCatchBackKey(true);
Effects.setShakeFalloff(10000f);
Effects.setShakeFalloff(10000f);
ContentLoader.initialize(Content::init);
Core.atlas = new Atlas("sprites.atlas");
Core.atlas.setErrorRegion("error");
ContentLoader.initialize(Content::load);
ContentLoader.initialize(Content::init);
Core.atlas = new Atlas("sprites.atlas");
Core.atlas.setErrorRegion("error");
ContentLoader.initialize(Content::load);
db.load();
db.load();
gdxInput = Gdx.input;
gdxInput = Gdx.input;
Sounds.load("shoot.mp3", "place.mp3", "explosion.mp3", "enemyshoot.mp3",
"corexplode.mp3", "break.mp3", "spawn.mp3", "flame.mp3", "die.mp3",
"respawn.mp3", "purchase.mp3", "flame2.mp3", "bigshot.mp3", "laser.mp3", "lasershot.mp3",
"ping.mp3", "tesla.mp3", "waveend.mp3", "railgun.mp3", "blast.mp3", "bang2.mp3");
Sounds.load("shoot.mp3", "place.mp3", "explosion.mp3", "enemyshoot.mp3",
"corexplode.mp3", "break.mp3", "spawn.mp3", "flame.mp3", "die.mp3",
"respawn.mp3", "purchase.mp3", "flame2.mp3", "bigshot.mp3", "laser.mp3", "lasershot.mp3",
"ping.mp3", "tesla.mp3", "waveend.mp3", "railgun.mp3", "blast.mp3", "bang2.mp3");
Sounds.setFalloff(9000f);
Sounds.setPlayer((sound, volume) -> {
long time = TimeUtils.millis();
long value = soundMap.get(sound, 0L);
Sounds.setFalloff(9000f);
Sounds.setPlayer((sound, volume) -> {
long time = TimeUtils.millis();
long value = soundMap.get(sound, 0L);
if(TimeUtils.timeSinceMillis(value) >= minSoundPeriod){
threads.run(() -> sound.play(volume));
soundMap.put(sound, time);
}
});
if(TimeUtils.timeSinceMillis(value) >= minSoundPeriod){
threads.run(() -> sound.play(volume));
soundMap.put(sound, time);
}
});
Musics.load("1.mp3", "2.mp3", "3.mp3", "4.mp3", "5.mp3", "6.mp3");
DefaultKeybinds.load();
Settings.defaultList(
"ip", "localhost",
"port", port+"",
"color-0", Color.rgba8888(playerColors[8]),
"color-1", Color.rgba8888(playerColors[11]),
"color-2", Color.rgba8888(playerColors[13]),
"color-3", Color.rgba8888(playerColors[9]),
"name", "player",
"lastBuild", 0
);
Settings.defaultList(
"ip", "localhost",
"port", port + "",
"color-0", Color.rgba8888(playerColors[8]),
"color-1", Color.rgba8888(playerColors[11]),
"color-2", Color.rgba8888(playerColors[13]),
"color-3", Color.rgba8888(playerColors[9]),
"name", "player",
"lastBuild", 0
);
KeyBinds.load();
KeyBinds.load();
addPlayer(0);
addPlayer(0);
saves.load();
saves.load();
Events.on(StateChangeEvent.class, (from, to) -> {
if((from == State.playing && to == State.menu) || (from == State.menu && to != State.menu)){
Timers.runTask(5f, Platform.instance::updateRPC);
}
});
Events.on(StateChangeEvent.class, (from, to) -> {
if((from == State.playing && to == State.menu) || (from == State.menu && to != State.menu)){
Timers.runTask(5f, Platform.instance::updateRPC);
}
});
Events.on(PlayEvent.class, () -> {
for(Player player : players){
Events.on(PlayEvent.class, () -> {
for(Player player : players){
player.add();
}
state.set(State.playing);
});
state.set(State.playing);
});
Events.on(WorldLoadGraphicsEvent.class, () -> {
if(mobile){
Core.camera.position.set(players[0].x, players[0].y, 0);
}
});
Events.on(WorldLoadGraphicsEvent.class, () -> {
if(mobile){
Core.camera.position.set(players[0].x, players[0].y, 0);
}
});
Events.on(ResetEvent.class, () -> {
for(Player player : players){
player.reset();
Events.on(ResetEvent.class, () -> {
for(Player player : players){
player.reset();
}
hiscore = false;
hiscore = false;
saves.resetSave();
});
saves.resetSave();
});
Events.on(WaveEvent.class, () -> {
Events.on(WaveEvent.class, () -> {
int last = Settings.getInt("hiscore" + world.getMap().name, 0);
int last = Settings.getInt("hiscore" + world.getMap().name, 0);
if(state.wave > last && !state.mode.infiniteResources && !state.mode.disableWaveTimer){
Settings.putInt("hiscore" + world.getMap().name, state.wave);
Settings.save();
hiscore = true;
}
if(state.wave > last && !state.mode.infiniteResources && !state.mode.disableWaveTimer){
Settings.putInt("hiscore" + world.getMap().name, state.wave);
Settings.save();
hiscore = true;
}
Platform.instance.updateRPC();
});
Platform.instance.updateRPC();
});
Events.on(GameOverEvent.class, () -> {
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
Events.on(GameOverEvent.class, () -> {
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
//TODO game over effect
ui.restart.show();
//TODO game over effect
ui.restart.show();
Timers.runTask(30f, () -> state.set(State.menu));
});
Timers.runTask(30f, () -> state.set(State.menu));
});
Events.on(WorldLoadEvent.class, () -> threads.runGraphics(() -> Events.fire(WorldLoadGraphicsEvent.class)));
}
Events.on(WorldLoadEvent.class, () -> threads.runGraphics(() -> Events.fire(WorldLoadGraphicsEvent.class)));
}
public void addPlayer(int index){
if(players.length != index + 1){
Player[] old = players;
players = new Player[index + 1];
public void addPlayer(int index){
if(players.length != index + 1){
Player[] old = players;
players = new Player[index + 1];
System.arraycopy(old, 0, players, 0, old.length);
}
if(inputs.length != index + 1){
InputHandler[] oldi = inputs;
inputs = new InputHandler[index + 1];
System.arraycopy(oldi, 0, inputs, 0, oldi.length);
@@ -204,8 +207,8 @@ public class Control extends Module{
}
public void removePlayer(){
players[players.length-1].remove();
inputs[inputs.length-1].remove();
players[players.length - 1].remove();
inputs[inputs.length - 1].remove();
Player[] old = players;
players = new Player[players.length - 1];
@@ -216,195 +219,191 @@ public class Control extends Module{
System.arraycopy(oldi, 0, inputs, 0, inputs.length);
}
public ContentDatabase database() {
return db;
}
public Input gdxInput(){
return gdxInput;
public ContentDatabase database(){
return db;
}
public void setError(Throwable error){
this.error = error;
}
public Saves getSaves(){
return saves;
}
public InputHandler input(int index){
return inputs[index];
}
public void triggerUpdateInput(){
//Gdx.input = proxy;
public Input gdxInput(){
return gdxInput;
}
public void playMap(Map map){
ui.loadfrag.show();
public void setError(Throwable error){
this.error = error;
}
Timers.run(5f, () ->
threads.run(() -> {
logic.reset();
world.loadMap(map);
logic.play();
public Saves getSaves(){
return saves;
}
Gdx.app.postRunnable(ui.loadfrag::hide);
}));
}
public InputHandler input(int index){
return inputs[index];
}
public boolean isHighScore(){
return hiscore;
}
public void triggerUpdateInput(){
//Gdx.input = proxy;
}
private void checkUnlockableBlocks(){
TileEntity entity = players[0].getClosestCore();
public void playMap(Map map){
ui.loadfrag.show();
if(entity == null) return;
Timers.run(5f, () ->
threads.run(() -> {
logic.reset();
world.loadMap(map);
logic.play();
for (int i = 0; i < entity.items.items.length; i++) {
if(entity.items.items[i] <= 0) continue;
Item item = Item.getByID(i);
control.database().unlockContent(item);
}
Gdx.app.postRunnable(ui.loadfrag::hide);
}));
}
if(players[0].inventory.hasItem()){
control.database().unlockContent(players[0].inventory.getItem().item);
}
public boolean isHighScore(){
return hiscore;
}
for(int i = 0 ; i < Recipe.all().size; i ++){
Recipe recipe = Recipe.all().get(i);
if(!recipe.debugOnly && entity.items.hasItems(recipe.requirements, 1.4f)){
if(control.database().unlockContent(recipe)){
ui.hudfrag.showUnlock(recipe);
}
}
}
}
private void checkUnlockableBlocks(){
TileEntity entity = players[0].getClosestCore();
@Override
public void dispose(){
Platform.instance.onGameExit();
ContentLoader.dispose();
Net.dispose();
ui.editor.dispose();
inputs = new InputHandler[]{};
players = new Player[]{};
}
if(entity == null) return;
@Override
public void pause(){
wasPaused = state.is(State.paused);
if(state.is(State.playing)) state.set(State.paused);
}
entity.items.forEach((item, amount) -> control.database().unlockContent(item));
@Override
public void resume(){
if(state.is(State.paused) && !wasPaused){
if(players[0].inventory.hasItem()){
control.database().unlockContent(players[0].inventory.getItem().item);
}
for(int i = 0; i < Recipe.all().size; i++){
Recipe recipe = Recipe.all().get(i);
if(!recipe.debugOnly && entity.items.has(recipe.requirements, 1.4f)){
if(control.database().unlockContent(recipe)){
ui.hudfrag.showUnlock(recipe);
}
}
}
}
@Override
public void dispose(){
Platform.instance.onGameExit();
ContentLoader.dispose();
Net.dispose();
ui.editor.dispose();
inputs = new InputHandler[]{};
players = new Player[]{};
}
@Override
public void pause(){
wasPaused = state.is(State.paused);
if(state.is(State.playing)) state.set(State.paused);
}
@Override
public void resume(){
if(state.is(State.paused) && !wasPaused){
state.set(State.playing);
}
}
}
}
@Override
public void init(){
EntityPhysics.initPhysics();
@Override
public void init(){
EntityPhysics.initPhysics();
Platform.instance.updateRPC();
Platform.instance.updateRPC();
if(!Settings.has("4.0-warning")){
Settings.putBool("4.0-warning", true);
if(!Settings.has("4.0-warning")){
Settings.putBool("4.0-warning", true);
Timers.run(5f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]WARNING![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("The beta version you are about to play should be considered very unstable, and is [accent]not representative of the final 4.0 release.[]\n\n " +
"A large portion of content is still unimplemented. \nAll current art and UI is temporary, and will be re-drawn before release. " +
"\n\n[accent]Saves and maps may be corrupted without warning between updates.[] You have been warned!").wrap().width(500f);
dialog.show();
Timers.run(5f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]WARNING![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("The beta version you are about to play should be considered very unstable, and is [accent]not representative of the final 4.0 release.[]\n\n " +
"A large portion of content is still unimplemented. \nAll current art and UI is temporary, and will be re-drawn before release. " +
"\n\n[accent]Saves and maps may be corrupted without warning between updates.[] You have been warned!").wrap().width(500f);
dialog.show();
});
}
});
}
if(!Settings.has("4.0-no-sound")){
Settings.putBool("4.0-no-sound", true);
if(!Settings.has("4.0-no-sound")){
Settings.putBool("4.0-no-sound", true);
Timers.run(4f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]Attention![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("You might have noticed that 4.0 does not have any sound.\nThis is [orange]intentional![] Sound will be added in a later update.\n\n[LIGHT_GRAY](now stop reporting this as a bug)").wrap().width(500f);
dialog.show();
Timers.run(4f, () -> {
FloatingDialog dialog = new FloatingDialog("[orange]Attention![]");
dialog.buttons().addButton("$text.ok", dialog::hide).size(100f, 60f);
dialog.content().add("You might have noticed that 4.0 does not have any sound.\nThis is [orange]intentional![] Sound will be added in a later update.\n\n[LIGHT_GRAY](now stop reporting this as a bug)").wrap().width(500f);
dialog.show();
});
}
}
});
}
}
/**Called from main logic thread.*/
public void runUpdateLogic(){
if(!state.is(State.menu)) {
renderer.minimap().updateUnitArray();
}
}
/** Called from main logic thread.*/
public void runUpdateLogic(){
if(!state.is(State.menu)){
renderer.minimap().updateUnitArray();
}
}
@Override
public void update(){
@Override
public void update(){
if(error != null){
throw new RuntimeException(error);
}
if(error != null){
throw new RuntimeException(error);
}
if(Inputs.keyTap("console")){
console = !console;
}
console = !console;
}
saves.update();
triggerUpdateInput();
triggerUpdateInput();
for(InputHandler inputHandler : inputs){
inputHandler.updateController();
}
for(InputHandler inputHandler : inputs){
inputHandler.updateController();
}
if(!state.is(State.menu)){
for(InputHandler input : inputs){
input.update();
if(!state.is(State.menu)){
for(InputHandler input : inputs){
input.update();
}
//check unlocks every 2 seconds
if(!state.mode.infiniteResources && Timers.get("timerCheckUnlock", 120)){
checkUnlockableBlocks();
if(!state.mode.infiniteResources && Timers.get("timerCheckUnlock", 120)){
checkUnlockableBlocks();
//save if the db changed, but don't save unlocks
if(db.isDirty() && !debug){
db.save();
}
}
//save if the db changed, but don't save unlocks
if(db.isDirty() && !debug){
db.save();
}
}
if(Inputs.keyTap("pause") && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){
if(Inputs.keyTap("pause") && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){
state.set(state.is(State.playing) ? State.paused : State.playing);
}
}
if(Inputs.keyTap("menu")){
if(state.is(State.paused)){
ui.paused.hide();
if(Inputs.keyTap("menu")){
if(state.is(State.paused)){
ui.paused.hide();
state.set(State.playing);
}else if (!ui.restart.isShown()){
if(ui.chatfrag.chatOpen()) {
ui.chatfrag.hide();
}else{
ui.paused.show();
}else if(!ui.restart.isShown()){
if(ui.chatfrag.chatOpen()){
ui.chatfrag.hide();
}else{
ui.paused.show();
state.set(State.paused);
}
}
}
}
}
}
if(!state.is(State.paused) || Net.active()){
Entities.update(effectGroup);
Entities.update(groundEffectGroup);
}
}else{
if(!state.is(State.paused) || Net.active()){
Timers.update();
}
}
}
if(!state.is(State.paused) || Net.active()){
Entities.update(effectGroup);
Entities.update(groundEffectGroup);
}
}else{
if(!state.is(State.paused) || Net.active()){
Timers.update();
}
}
}
}

View File

@@ -8,31 +8,30 @@ import io.anuke.mindustry.game.TeamInfo;
import io.anuke.ucore.core.Events;
public class GameState{
private State state = State.menu;
public int wave = 1;
public float wavetime;
public boolean gameOver = false;
public GameMode mode = GameMode.waves;
public Difficulty difficulty = Difficulty.normal;
public boolean friendlyFire;
public WaveSpawner spawner = new WaveSpawner();
public TeamInfo teams = new TeamInfo();
private State state = State.menu;
public int wave = 1;
public float wavetime;
public boolean gameOver = false;
public GameMode mode = GameMode.waves;
public Difficulty difficulty = Difficulty.normal;
public boolean friendlyFire;
public WaveSpawner spawner = new WaveSpawner();
public TeamInfo teams = new TeamInfo();
public void set(State astate){
Events.fire(StateChangeEvent.class, state, astate);
state = astate;
}
public boolean is(State astate){
return state == astate;
}
public void set(State astate){
Events.fire(StateChangeEvent.class, state, astate);
state = astate;
}
public State getState(){
return state;
}
public enum State{
paused, playing, menu
}
public boolean is(State astate){
return state == astate;
}
public State getState(){
return state;
}
public enum State{
paused, playing, menu
}
}

View File

@@ -24,13 +24,15 @@ import io.anuke.ucore.modules.Module;
import static io.anuke.mindustry.Vars.*;
/**Logic module.
/**
* Logic module.
* Handles all logic for entities and waves.
* Handles game state events.
* Does not store any game state itself.
*
* This class should <i>not</i> call any outside methods to change state of modules, but instead fire events.*/
public class Logic extends Module {
* <p>
* This class should <i>not</i> call any outside methods to change state of modules, but instead fire events.
*/
public class Logic extends Module{
public boolean doUpdate = true;
public Logic(){
@@ -49,17 +51,17 @@ public class Logic extends Module {
//fill inventory with items for debugging
for (TeamData team : state.teams.getTeams()) {
for (Tile tile : team.cores) {
if(debug) {
for (Item item : Item.all()) {
if (item.type == ItemType.material) {
tile.entity.items.addItem(item, 1000);
for(TeamData team : state.teams.getTeams()){
for(Tile tile : team.cores){
if(debug){
for(Item item : Item.all()){
if(item.type == ItemType.material){
tile.entity.items.add(item, 1000);
}
}
}else{
tile.entity.items.addItem(Items.tungsten, 50);
tile.entity.items.addItem(Items.lead, 20);
tile.entity.items.add(Items.tungsten, 50);
tile.entity.items.add(Items.lead, 20);
}
}
}
@@ -85,7 +87,7 @@ public class Logic extends Module {
public void runWave(){
state.spawner.spawnEnemies();
state.wave ++;
state.wave++;
state.wavetime = wavespace * state.difficulty.timeScaling;
Events.fire(WaveEvent.class);
@@ -137,7 +139,8 @@ public class Logic extends Module {
runWave();
}
if(!Entities.defaultGroup().isEmpty()) throw new RuntimeException("Do not add anything to the default group!");
if(!Entities.defaultGroup().isEmpty())
throw new RuntimeException("Do not add anything to the default group!");
Entities.update(bulletGroup);
for(EntityGroup group : unitGroups){
@@ -158,13 +161,6 @@ public class Logic extends Module {
for(EntityGroup group : unitGroups){
if(!group.isEmpty()){
EntityPhysics.collideGroups(bulletGroup, group);
/*
for(EntityGroup other : unitGroups){
if(!other.isEmpty()){
EntityPhysics.collideGroups(group, other);
}
}*/
}
}

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.IntSet;
@@ -35,38 +36,64 @@ import java.util.Random;
import static io.anuke.mindustry.Vars.*;
public class NetClient extends Module {
private final static float dataTimeout = 60*18;
public class NetClient extends Module{
private final static float dataTimeout = 60 * 18;
private final static float playerSyncTime = 2;
private Timer timer = new Timer(5);
/**Whether the client is currently connecting.*/
/**
* Whether the client is currently connecting.
*/
private boolean connecting = false;
/**If true, no message will be shown on disconnect.*/
/**
* If true, no message will be shown on disconnect.
*/
private boolean quiet = false;
/**Counter for data timeout.*/
/**
* Counter for data timeout.
*/
private float timeoutTime = 0f;
/**Last sent client snapshot ID.*/
/**
* Last sent client snapshot ID.
*/
private int lastSent;
/**Last snapshot ID recieved.*/
/**
* Last snapshot ID recieved.
*/
private int lastSnapshotBaseID = -1;
/**Last snapshot recieved.*/
/**
* Last snapshot recieved.
*/
private byte[] lastSnapshotBase;
/**Current snapshot that is being built from chinks.*/
/**
* Current snapshot that is being built from chinks.
*/
private byte[] currentSnapshot;
/**Array of recieved chunk statuses.*/
/**
* Array of recieved chunk statuses.
*/
private boolean[] recievedChunks;
/**Counter of how many chunks have been recieved.*/
/**
* Counter of how many chunks have been recieved.
*/
private int recievedChunkCounter;
/**ID of snapshot that is currently being constructed.*/
/**
* ID of snapshot that is currently being constructed.
*/
private int currentSnapshotID = -1;
/**Decoder for uncompressing snapshots.*/
/**
* Decoder for uncompressing snapshots.
*/
private DEZDecoder decoder = new DEZDecoder();
/**List of entities that were removed, and need not be added while syncing.*/
/**
* List of entities that were removed, and need not be added while syncing.
*/
private IntSet removed = new IntSet();
/**Byte stream for reading in snapshots.*/
/**
* Byte stream for reading in snapshots.
*/
private ReusableByteArrayInputStream byteStream = new ReusableByteArrayInputStream();
private DataInputStream dataStream = new DataInputStream(byteStream);
@@ -119,7 +146,7 @@ public class NetClient extends Module {
});
Net.handleClient(Disconnect.class, packet -> {
if (quiet) return;
if(quiet) return;
Timers.runTask(3f, ui.loadfrag::hide);
@@ -144,6 +171,166 @@ public class NetClient extends Module {
});
}
@Remote(variants = Variant.one, priority = PacketPriority.high)
public static void onKick(KickReason reason){
netClient.disconnectQuietly();
state.set(State.menu);
if(!reason.quiet) ui.showError("$text.server.kicked." + reason.name());
ui.loadfrag.hide();
}
@Remote(variants = Variant.one)
public static void onPositionSet(float x, float y){
players[0].x = x;
players[0].y = y;
}
@Remote(variants = Variant.one)
public static void onTraceInfo(TraceInfo info){
Player player = playerGroup.getByID(info.playerid);
ui.traces.show(player, info);
}
@Remote
public static void onPlayerDisconnect(int playerid){
playerGroup.removeByID(playerid);
}
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
public static void onSnapshot(byte[] chunk, int snapshotID, short chunkID, int totalLength, int base){
if(NetServer.showSnapshotSize)
Log.info("Recieved snapshot: len {0} ID {1} chunkID {2} totalLength {3} base {4} client-base {5}", chunk.length, snapshotID, chunkID, totalLength, base, netClient.lastSnapshotBaseID);
//skip snapshot IDs that have already been recieved OR snapshots that are too far in front
if(snapshotID < netClient.lastSnapshotBaseID || base != netClient.lastSnapshotBaseID){
if(NetServer.showSnapshotSize) Log.info("//SKIP SNAPSHOT");
return;
}
try{
byte[] snapshot;
//total length exceeds that needed to hold one snapshot, therefore, it is split into chunks
if(totalLength > NetServer.maxSnapshotSize){
//total amount of chunks to recieve
int totalChunks = Mathf.ceil((float) totalLength / NetServer.maxSnapshotSize);
//reset status when a new snapshot sending begins
if(netClient.currentSnapshotID != snapshotID){
netClient.currentSnapshotID = snapshotID;
netClient.currentSnapshot = new byte[totalLength];
netClient.recievedChunkCounter = 0;
netClient.recievedChunks = new boolean[totalChunks];
}
//if this chunk hasn't been recieved yet...
if(!netClient.recievedChunks[chunkID]){
netClient.recievedChunks[chunkID] = true;
netClient.recievedChunkCounter++; //update recieved status
//copy the recieved bytes into the holding array
System.arraycopy(chunk, 0, netClient.currentSnapshot, chunkID * NetServer.maxSnapshotSize,
Math.min(NetServer.maxSnapshotSize, totalLength - chunkID * NetServer.maxSnapshotSize));
}
//when all chunks have been recieved, begin
if(netClient.recievedChunkCounter >= totalChunks){
snapshot = netClient.currentSnapshot;
}else{
return;
}
}else{
snapshot = chunk;
}
if(NetServer.showSnapshotSize)
Log.info("Finished recieving snapshot ID {0} length {1}", snapshotID, chunk.length);
byte[] result;
int length;
if(base == -1){ //fresh snapshot
result = snapshot;
length = snapshot.length;
netClient.lastSnapshotBase = Arrays.copyOf(snapshot, snapshot.length);
}else{ //otherwise, last snapshot must not be null, decode it
if(NetServer.showSnapshotSize)
Log.info("Base size: {0} Patch size: {1}", netClient.lastSnapshotBase.length, snapshot.length);
netClient.decoder.init(netClient.lastSnapshotBase, snapshot);
result = netClient.decoder.decode();
length = netClient.decoder.getDecodedLength();
//set last snapshot to a copy to prevent issues
netClient.lastSnapshotBase = Arrays.copyOf(result, length);
}
netClient.lastSnapshotBaseID = snapshotID;
//set stream bytes to begin snapshot reaeding
netClient.byteStream.setBytes(result, 0, length);
//get data input for reading from the stream
DataInputStream input = netClient.dataStream;
//read wave info
state.wavetime = input.readFloat();
state.wave = input.readInt();
byte cores = input.readByte();
for(int i = 0; i < cores; i++){
int pos = input.readInt();
world.tile(pos).entity.items.read(input);
}
long timestamp = input.readLong();
byte totalGroups = input.readByte();
//for each group...
for(int i = 0; i < totalGroups; i++){
//read group info
byte groupID = input.readByte();
short amount = input.readShort();
EntityGroup group = Entities.getGroup(groupID);
//go through each entity
for(int j = 0; j < amount; j++){
int position = netClient.byteStream.position(); //save position to check read/write correctness
int id = input.readInt();
byte typeID = input.readByte();
SyncTrait entity = (SyncTrait) group.getByID(id);
boolean add = false;
//entity must not be added yet, so create it
if(entity == null){
entity = (SyncTrait) TypeTrait.getTypeByID(typeID).get(); //create entity from supplier
entity.resetID(id);
if(!netClient.isEntityUsed(entity.getID())){
add = true;
}
}
//read the entity
entity.read(input, timestamp);
byte readLength = input.readByte();
if(netClient.byteStream.position() - position - 1 != readLength){
throw new RuntimeException("Error reading entity of type '" + group.getType() + "': Read length mismatch [write=" + readLength + ", read=" + (netClient.byteStream.position() - position - 1) + "]");
}
if(add){
entity.add();
netClient.addRemovedEntity(entity.getID());
}
}
}
//confirm that snapshot has been recieved
netClient.lastSnapshotBaseID = snapshotID;
}catch(Exception e){
throw new RuntimeException(e);
}
}
@Override
public void update(){
if(!Net.client()) return;
@@ -175,7 +362,7 @@ public class NetClient extends Module {
ui.loadfrag.hide();
ui.join.hide();
Net.setClientLoaded(true);
Call.connectConfirm();
Gdx.app.postRunnable(Call::connectConfirm);
Timers.runTask(40f, Platform.instance::updateRPC);
}
@@ -223,161 +410,4 @@ public class NetClient extends Module {
return result;
}
}
@Remote(variants = Variant.one, priority = PacketPriority.high)
public static void onKick(KickReason reason){
netClient.disconnectQuietly();
state.set(State.menu);
if(!reason.quiet) ui.showError("$text.server.kicked." + reason.name());
ui.loadfrag.hide();
}
@Remote(variants = Variant.one)
public static void onPositionSet(float x, float y){
players[0].x = x;
players[0].y = y;
}
@Remote(variants = Variant.one)
public static void onTraceInfo(TraceInfo info){
Player player = playerGroup.getByID(info.playerid);
ui.traces.show(player, info);
}
@Remote
public static void onPlayerDisconnect(int playerid){
playerGroup.removeByID(playerid);
}
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
public static void onSnapshot(byte[] chunk, int snapshotID, short chunkID, short totalLength, int base){
if(NetServer.showSnapshotSize) Log.info("Recieved snapshot: len {0} ID {1} chunkID {2} totalLength {3} base {4} client-base {5}", chunk.length, snapshotID, chunkID, totalLength, base, netClient.lastSnapshotBaseID);
//skip snapshot IDs that have already been recieved OR snapshots that are too far in front
if(snapshotID < netClient.lastSnapshotBaseID || base != netClient.lastSnapshotBaseID){
if(NetServer.showSnapshotSize) Log.info("//SKIP SNAPSHOT");
return;
}
try {
byte[] snapshot;
//total length exceeds that needed to hold one snapshot, therefore, it is split into chunks
if(totalLength > NetServer.maxSnapshotSize) {
//total amount of chunks to recieve
int totalChunks = Mathf.ceil((float) totalLength / NetServer.maxSnapshotSize);
//reset status when a new snapshot sending begins
if (netClient.currentSnapshotID != snapshotID) {
netClient.currentSnapshotID = snapshotID;
netClient.currentSnapshot = new byte[totalLength];
netClient.recievedChunkCounter = 0;
netClient.recievedChunks = new boolean[totalChunks];
}
//if this chunk hasn't been recieved yet...
if (!netClient.recievedChunks[chunkID]) {
netClient.recievedChunks[chunkID] = true;
netClient.recievedChunkCounter ++; //update recieved status
//copy the recieved bytes into the holding array
System.arraycopy(chunk, 0, netClient.currentSnapshot, chunkID * NetServer.maxSnapshotSize,
Math.min(NetServer.maxSnapshotSize, totalLength - chunkID * NetServer.maxSnapshotSize));
}
//when all chunks have been recieved, begin
if(netClient.recievedChunkCounter >= totalChunks){
snapshot = netClient.currentSnapshot;
}else{
return;
}
}else{
snapshot = chunk;
}
if(NetServer.showSnapshotSize) Log.info("Finished recieving snapshot ID {0} length {1}", snapshotID, chunk.length);
byte[] result;
int length;
if (base == -1) { //fresh snapshot
result = snapshot;
length = snapshot.length;
netClient.lastSnapshotBase = Arrays.copyOf(snapshot, snapshot.length);
} else { //otherwise, last snapshot must not be null, decode it
if(NetServer.showSnapshotSize) Log.info("Base size: {0} Patch size: {1}", netClient.lastSnapshotBase.length, snapshot.length);
netClient.decoder.init(netClient.lastSnapshotBase, snapshot);
result = netClient.decoder.decode();
length = netClient.decoder.getDecodedLength();
//set last snapshot to a copy to prevent issues
netClient.lastSnapshotBase = Arrays.copyOf(result, length);
}
netClient.lastSnapshotBaseID = snapshotID;
//set stream bytes to begin snapshot reaeding
netClient.byteStream.setBytes(result, 0, length);
//get data input for reading from the stream
DataInputStream input = netClient.dataStream;
//read wave info
state.wavetime = input.readFloat();
state.wave = input.readInt();
byte cores = input.readByte();
for (int i = 0; i < cores; i++) {
int pos = input.readInt();
world.tile(pos).entity.items.read(input);
}
long timestamp = input.readLong();
byte totalGroups = input.readByte();
//for each group...
for (int i = 0; i < totalGroups; i++) {
//read group info
byte groupID = input.readByte();
short amount = input.readShort();
EntityGroup group = Entities.getGroup(groupID);
//go through each entity
for (int j = 0; j < amount; j++) {
int position = netClient.byteStream.position(); //save position to check read/write correctness
int id = input.readInt();
byte typeID = input.readByte();
SyncTrait entity = (SyncTrait) group.getByID(id);
boolean add = false;
//entity must not be added yet, so create it
if(entity == null){
entity = (SyncTrait) TypeTrait.getTypeByID(typeID).get(); //create entity from supplier
entity.resetID(id);
if(!netClient.isEntityUsed(entity.getID())){
add = true;
}
}
//read the entity
entity.read(input, timestamp);
byte readLength = input.readByte();
if(netClient.byteStream.position() - position - 1 != readLength){
throw new RuntimeException("Error reading entity of type '"+ group.getType() + "': Read length mismatch [write=" + readLength + ", read=" + (netClient.byteStream.position() - position - 1)+ "]");
}
if(add){
entity.add();
netClient.addRemovedEntity(entity.getID());
}
}
}
//confirm that snapshot has been recieved
netClient.lastSnapshotBaseID = snapshotID;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}

View File

@@ -45,20 +45,30 @@ public class NetServer extends Module{
private final static byte[] reusableSnapArray = new byte[maxSnapshotSize];
private final static float serverSyncTime = 4, kickDuration = 30 * 1000;
private final static Vector2 vector = new Vector2();
/**If a play goes away of their server-side coordinates by this distance, they get teleported back.*/
/**
* If a play goes away of their server-side coordinates by this distance, they get teleported back.
*/
private final static float correctDist = 16f;
public final Administration admins = new Administration();
/**Maps connection IDs to players.*/
/**
* Maps connection IDs to players.
*/
private IntMap<Player> connections = new IntMap<>();
private boolean closing = false;
/**Stream for writing player sync data to.*/
/**
* Stream for writing player sync data to.
*/
private CountableByteArrayOutputStream syncStream = new CountableByteArrayOutputStream();
/**Data stream for writing player sync data to.*/
/**
* Data stream for writing player sync data to.
*/
private DataOutputStream dataStream = new DataOutputStream(syncStream);
/**Encoder for computing snapshot deltas.*/
/**
* Encoder for computing snapshot deltas.
*/
private DEZEncoder encoder = new DEZEncoder();
public NetServer(){
@@ -105,14 +115,14 @@ public class NetServer extends Module{
boolean preventDuplicates = headless;
if(preventDuplicates) {
for (Player player : playerGroup.all()) {
if (player.name.equalsIgnoreCase(packet.name)) {
if(preventDuplicates){
for(Player player : playerGroup.all()){
if(player.name.equalsIgnoreCase(packet.name)){
kick(id, KickReason.nameInUse);
return;
}
if (player.uuid.equals(packet.uuid)) {
if(player.uuid.equals(packet.uuid)){
kick(id, KickReason.idInUse);
return;
}
@@ -181,7 +191,7 @@ public class NetServer extends Module{
long elapsed = TimeUtils.timeSinceMillis(connection.lastRecievedClientTime);
float maxSpeed = (packet.boosting && !player.mech.flying ? player.mech.boostSpeed : player.mech.speed)*2.5f;
float maxSpeed = (packet.boosting && !player.mech.flying ? player.mech.boostSpeed : player.mech.speed) * 2.5f;
//extra 1.1x multiplicaton is added just in case
float maxMove = elapsed / 1000f * 60f * maxSpeed * 1.1f;
@@ -238,6 +248,86 @@ public class NetServer extends Module{
});
}
/**
* Sends a raw byte[] snapshot to a client, splitting up into chunks when needed.
*/
private static void sendSplitSnapshot(int userid, byte[] bytes, int snapshotID, int base){
if(bytes.length < maxSnapshotSize){
Call.onSnapshot(userid, bytes, snapshotID, (short) 0, bytes.length, base);
}else{
int remaining = bytes.length;
int offset = 0;
int chunkid = 0;
while(remaining > 0){
int used = Math.min(remaining, maxSnapshotSize);
byte[] toSend;
//re-use sent byte arrays when possible
if(used == maxSnapshotSize){
toSend = reusableSnapArray;
System.arraycopy(bytes, offset, toSend, 0, Math.min(offset + maxSnapshotSize, bytes.length) - offset);
}else{
toSend = Arrays.copyOfRange(bytes, offset, Math.min(offset + maxSnapshotSize, bytes.length));
}
Call.onSnapshot(userid, toSend, snapshotID, (short) chunkid, bytes.length, base);
remaining -= used;
offset += used;
chunkid++;
}
}
}
public static void onDisconnect(Player player){
Call.sendMessage("[accent]" + player.name + " has disconnected.");
Call.onPlayerDisconnect(player.id);
player.remove();
netServer.connections.remove(player.con.id);
}
@Remote(targets = Loc.client, called = Loc.server)
public static void onAdminRequest(Player player, Player other, AdminAction action){
if(!player.isAdmin){
Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.",
player.name, player.con.address);
return;
}
if(other == null || (other.isAdmin && other != player)){ //fun fact: this means you can ban yourself
Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name);
return;
}
if(action == AdminAction.wave){
//no verification is done, so admins can hypothetically spam waves
//not a real issue, because server owners may want to do just that
state.wavetime = 0f;
}else if(action == AdminAction.ban){
netServer.admins.banPlayerIP(other.con.address);
netServer.kick(other.con.id, KickReason.banned);
Log.info("&lc{0} has banned {1}.", player.name, other.name);
}else if(action == AdminAction.kick){
netServer.kick(other.con.id, KickReason.kick);
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
}else if(action == AdminAction.trace){
//TODO
if(player.con != null){
Call.onTraceInfo(player.con.id, netServer.admins.getTraceByID(other.uuid));
}else{
NetClient.onTraceInfo(netServer.admins.getTraceByID(other.uuid));
}
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
}
}
@Remote(targets = Loc.client)
public static void connectConfirm(Player player){
player.add();
player.con.hasConnected = true;
Call.sendMessage("[accent]" + player.name + " has connected.");
Log.info("&y{0} has connected.", player.name);
}
public void update(){
if(!headless && !closing && Net.server() && state.is(State.menu)){
closing = true;
@@ -270,7 +360,7 @@ public class NetServer extends Module{
if((reason == KickReason.kick || reason == KickReason.banned) && admins.getTraceByID(getUUID(con.id)).uuid != null){
PlayerInfo info = admins.getInfo(admins.getTraceByID(getUUID(con.id)).uuid);
info.timesKicked ++;
info.timesKicked++;
info.lastKicked = TimeUtils.millis();
}
@@ -288,7 +378,7 @@ public class NetServer extends Module{
String fixName(String name){
for(int i = 0; i < name.length(); i ++){
for(int i = 0; i < name.length(); i++){
if(name.charAt(i) == '[' && i != name.length() - 1 && name.charAt(i + 1) != '[' && (i == 0 || name.charAt(i - 1) != '[')){
String prev = name.substring(0, i);
String next = name.substring(i);
@@ -303,7 +393,7 @@ public class NetServer extends Module{
String checkColor(String str){
for(int i = 1; i < str.length(); i ++){
for(int i = 1; i < str.length(); i++){
if(str.charAt(i) == ']'){
String color = str.substring(1, i);
@@ -318,7 +408,7 @@ public class NetServer extends Module{
if(result.a <= 0.8f){
return str.substring(i + 1);
}
}catch (Exception e){
}catch(Exception e){
return str;
}
}
@@ -328,10 +418,10 @@ public class NetServer extends Module{
}
void sync(){
try {
try{
//iterate through each player
for (Player player : connections.values()) {
for(Player player : connections.values()){
NetConnection connection = player.con;
if(!connection.isConnected()){
@@ -344,7 +434,8 @@ public class NetServer extends Module{
//if the player hasn't acknowledged that it has recieved the packet, send the same thing again
if(connection.currentBaseID < connection.lastSentSnapshotID){
if(showSnapshotSize) Log.info("Re-sending snapshot: {0} bytes, ID {1} base {2} baselength {3}", connection.lastSentSnapshot.length, connection.lastSentSnapshotID, connection.lastSentBase, connection.currentBaseSnapshot.length);
if(showSnapshotSize)
Log.info("Re-sending snapshot: {0} bytes, ID {1} base {2} baselength {3}", connection.lastSentSnapshot.length, connection.lastSentSnapshotID, connection.lastSentBase, connection.currentBaseSnapshot.length);
sendSplitSnapshot(connection.id, connection.lastSentSnapshot, connection.lastSentSnapshotID, connection.lastSentBase);
return;
}
@@ -371,17 +462,17 @@ public class NetServer extends Module{
int totalGroups = 0;
for (EntityGroup<?> group : Entities.getAllGroups()) {
if (!group.isEmpty() && (group.all().get(0) instanceof SyncTrait)) totalGroups ++;
for(EntityGroup<?> group : Entities.getAllGroups()){
if(!group.isEmpty() && (group.all().get(0) instanceof SyncTrait)) totalGroups++;
}
//write total amount of serializable groups
dataStream.writeByte(totalGroups);
//check for syncable groups
for (EntityGroup<?> group : Entities.getAllGroups()) {
for(EntityGroup<?> group : Entities.getAllGroups()){
//TODO range-check sync positions to optimize?
if (group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue;
if(group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue;
//make sure mapping is enabled for this group
if(!group.mappingEnabled()){
@@ -391,8 +482,8 @@ public class NetServer extends Module{
int amount = 0;
for(Entity entity : group.all()){
if(((SyncTrait)entity).isSyncing()){
amount ++;
if(((SyncTrait) entity).isSyncing()){
amount++;
}
}
@@ -401,15 +492,16 @@ public class NetServer extends Module{
dataStream.writeShort(amount);
for(Entity entity : group.all()){
if(!((SyncTrait)entity).isSyncing()) continue;
if(!((SyncTrait) entity).isSyncing()) continue;
int position = syncStream.position();
//write all entities now
dataStream.writeInt(entity.getID()); //write id
dataStream.writeByte(((SyncTrait)entity).getTypeID()); //write type ID
((SyncTrait)entity).write(dataStream); //write entity
dataStream.writeByte(((SyncTrait) entity).getTypeID()); //write type ID
((SyncTrait) entity).write(dataStream); //write entity
int length = syncStream.position() - position; //length must always be less than 127 bytes
if(length > 127) throw new RuntimeException("Write size for entity of type " + group.getType() + " must not exceed 127!");
if(length > 127)
throw new RuntimeException("Write size for entity of type " + group.getType() + " must not exceed 127!");
dataStream.writeByte(length);
}
}
@@ -433,7 +525,8 @@ public class NetServer extends Module{
//send diff, otherwise
byte[] diff = ByteDeltaEncoder.toDiff(new ByteMatcherHash(connection.currentBaseSnapshot, bytes), encoder);
if(showSnapshotSize) Log.info("Shrank snapshot: {0} -> {1}, Base {2} ID {3} base length = {4}", bytes.length, diff.length, connection.currentBaseID, connection.currentBaseID + 1, connection.currentBaseSnapshot.length);
if(showSnapshotSize)
Log.info("Shrank snapshot: {0} -> {1}, Base {2} ID {3} base length = {4}", bytes.length, diff.length, connection.currentBaseID, connection.currentBaseID + 1, connection.currentBaseSnapshot.length);
sendSplitSnapshot(connection.id, diff, connection.currentBaseID + 1, connection.currentBaseID);
connection.lastSentSnapshot = diff;
connection.lastSentSnapshotID = connection.currentBaseID + 1;
@@ -441,88 +534,8 @@ public class NetServer extends Module{
}
}
}catch (IOException e){
}catch(IOException e){
e.printStackTrace();
}
}
/**Sends a raw byte[] snapshot to a client, splitting up into chunks when needed.*/
private static void sendSplitSnapshot(int userid, byte[] bytes, int snapshotID, int base){
if(bytes.length < maxSnapshotSize){
Call.onSnapshot(userid, bytes, snapshotID, (short)0, (short)bytes.length, base);
}else{
int remaining = bytes.length;
int offset = 0;
int chunkid = 0;
while(remaining > 0){
int used = Math.min(remaining, maxSnapshotSize);
byte[] toSend;
//re-use sent byte arrays when possible
if(used == maxSnapshotSize){
toSend = reusableSnapArray;
System.arraycopy(bytes, offset, toSend, 0, Math.min(offset + maxSnapshotSize, bytes.length) - offset);
}else {
toSend = Arrays.copyOfRange(bytes, offset, Math.min(offset + maxSnapshotSize, bytes.length));
}
Call.onSnapshot(userid, toSend, snapshotID, (short)chunkid, (short)bytes.length, base);
remaining -= used;
offset += used;
chunkid ++;
}
}
}
public static void onDisconnect(Player player){
Call.sendMessage("[accent]" + player.name + " has disconnected.");
Call.onPlayerDisconnect(player.id);
player.remove();
netServer.connections.remove(player.con.id);
}
@Remote(targets = Loc.client, called = Loc.server)
public static void onAdminRequest(Player player, Player other, AdminAction action){
if(!player.isAdmin){
Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.",
player.name, player.con.address);
return;
}
if(other == null || (other.isAdmin && other != player)){ //fun fact: this means you can ban yourself
Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name);
return;
}
String ip = player.con.address;
if(action == AdminAction.wave) {
//no verification is done, so admins can hypothetically spam waves
//not a real issue, because server owners may want to do just that
state.wavetime = 0f;
}else if(action == AdminAction.ban){
netServer.admins.banPlayerIP(ip);
netServer.kick(other.con.id, KickReason.banned);
Log.info("&lc{0} has banned {1}.", player.name, other.name);
}else if(action == AdminAction.kick){
netServer.kick(other.con.id, KickReason.kick);
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
}else if(action == AdminAction.trace){
//TODO
if(player.con != null) {
Call.onTraceInfo(player.con.id, netServer.admins.getTraceByID(other.uuid));
}else{
NetClient.onTraceInfo(netServer.admins.getTraceByID(other.uuid));
}
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
}
}
@Remote(targets = Loc.client)
public static void connectConfirm(Player player){
player.add();
player.con.hasConnected = true;
Call.sendMessage("[accent]" + player.name + " has connected.");
Log.info("&y{0} has connected.", player.name);
}
}

View File

@@ -4,8 +4,6 @@ import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Base64Coder;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.scene.ui.TextField;
@@ -13,63 +11,129 @@ import java.util.Date;
import java.util.Locale;
import java.util.Random;
public abstract class Platform {
/**Each separate game platform should set this instance to their own implementation.*/
public static Platform instance = new Platform() {};
public abstract class Platform{
/**
* Each separate game platform should set this instance to their own implementation.
*/
public static Platform instance = new Platform(){
};
/**Format the date using the default date formatter.*/
public String format(Date date){return "invalid";}
/**Format a number by adding in commas or periods where needed.*/
public String format(int number){return "invalid";}
/**Show a native error dialog.*/
public void showError(String text){}
/**Add a text input dialog that should show up after the field is tapped.*/
public void addDialog(TextField field){
addDialog(field, 16);
}
/**See addDialog().*/
public void addDialog(TextField field, int maxLength){}
/**Update discord RPC.*/
public void updateRPC(){}
/**Called when the game is exited.*/
public void onGameExit(){}
/**Open donation dialog. Currently android only.*/
public void openDonations(){}
/**Whether donating is supported.*/
public boolean canDonate(){
return false;
}
/**Whether discord RPC is supported.*/
public boolean hasDiscord(){return true;}
/**Return the localized name for the locale. This is basically a workaround for GWT not supporting getName().*/
public String getLocaleName(Locale locale){
return locale.toString();
}
/**Whether joining games is supported.*/
public boolean canJoinGame(){
return true;
}
/**Whether debug mode is enabled.*/
public boolean isDebug(){return false;}
/**Must be a base64 string 8 bytes in length.*/
public String getUUID(){
String uuid = Settings.getString("uuid", "");
if(uuid.isEmpty()){
byte[] result = new byte[8];
new Random().nextBytes(result);
uuid = new String(Base64Coder.encode(result));
Settings.putString("uuid", uuid);
Settings.save();
return uuid;
}
return uuid;
}
/**Only used for iOS or android: open the share menu for a map or save.*/
public void shareFile(FileHandle file){}
/**Download a file. Only used on GWT backend.*/
public void downloadFile(String name, byte[] bytes){}
/**
* Format the date using the default date formatter.
*/
public String format(Date date){
return "invalid";
}
/**Show a file chooser. Desktop only.
/**
* Format a number by adding in commas or periods where needed.
*/
public String format(int number){
return "invalid";
}
/**
* Show a native error dialog.
*/
public void showError(String text){
}
/**
* Add a text input dialog that should show up after the field is tapped.
*/
public void addDialog(TextField field){
addDialog(field, 16);
}
/**
* See addDialog().
*/
public void addDialog(TextField field, int maxLength){
}
/**
* Update discord RPC.
*/
public void updateRPC(){
}
/**
* Called when the game is exited.
*/
public void onGameExit(){
}
/**
* Open donation dialog. Currently android only.
*/
public void openDonations(){
}
/**
* Whether donating is supported.
*/
public boolean canDonate(){
return false;
}
/**
* Whether discord RPC is supported.
*/
public boolean hasDiscord(){
return true;
}
/**
* Return the localized name for the locale. This is basically a workaround for GWT not supporting getName().
*/
public String getLocaleName(Locale locale){
return locale.toString();
}
/**
* Whether joining games is supported.
*/
public boolean canJoinGame(){
return true;
}
/**
* Whether debug mode is enabled.
*/
public boolean isDebug(){
return false;
}
/**
* Must be a base64 string 8 bytes in length.
*/
public String getUUID(){
String uuid = Settings.getString("uuid", "");
if(uuid.isEmpty()){
byte[] result = new byte[8];
new Random().nextBytes(result);
uuid = new String(Base64Coder.encode(result));
Settings.putString("uuid", uuid);
Settings.save();
return uuid;
}
return uuid;
}
/**
* Only used for iOS or android: open the share menu for a map or save.
*/
public void shareFile(FileHandle file){
}
/**
* Download a file. Only used on GWT backend.
*/
public void downloadFile(String name, byte[] bytes){
}
/**
* Show a file chooser. Desktop only.
*
* @param text File chooser title text
* @param content Description of the type of files to be loaded
@@ -77,25 +141,54 @@ public abstract class Platform {
* @param open Whether to open or save files
* @param filetype File extension to filter
*/
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filetype){}
/**Use the default thread provider from the kryonet module for this.*/
public ThreadProvider getThreadProvider(){
return new ThreadProvider() {
@Override public boolean isOnThread() {return true;}
@Override public void sleep(long ms) {}
@Override public void start(Runnable run) {}
@Override public void stop() {}
@Override public void notify(Object object) {}
@Override public void wait(Object object) {}
@Override public <T extends Entity> void switchContainer(EntityGroup<T> group) {}
};
}
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filetype){
}
//TODO iOS implementation
/**Forces the app into landscape mode. Currently Android only.*/
public void beginForceLandscape(){}
/**
* Use the default thread provider from the kryonet module for this.
*/
public ThreadProvider getThreadProvider(){
return new ThreadProvider(){
@Override
public boolean isOnThread(){
return true;
}
//TODO iOS implementation
/**Stops forcing the app into landscape orientation. Currently Android only.*/
public void endForceLandscape(){}
@Override
public void sleep(long ms){
}
@Override
public void start(Runnable run){
}
@Override
public void stop(){
}
@Override
public void notify(Object object){
}
@Override
public void wait(Object object){
}
};
}
//TODO iOS implementation
/**
* Forces the app into landscape mode. Currently Android only.
*/
public void beginForceLandscape(){
}
//TODO iOS implementation
/**
* Stops forcing the app into landscape orientation. Currently Android only.
*/
public void endForceLandscape(){
}
}

View File

@@ -47,399 +47,430 @@ import static io.anuke.ucore.core.Core.batch;
import static io.anuke.ucore.core.Core.camera;
public class Renderer extends RendererModule{
public Surface effectSurface;
private int targetscale = baseCameraScale;
private Texture background = new Texture("sprites/background.png");
public Surface effectSurface;
private Rectangle rect = new Rectangle(), rect2 = new Rectangle();
private Vector2 avgPosition = new Translator();
private Vector2 tmpVector1 = new Translator();
private Vector2 tmpVector2 = new Translator();
private int targetscale = baseCameraScale;
private Texture background = new Texture("sprites/background.png");
private BlockRenderer blocks = new BlockRenderer();
private MinimapRenderer minimap = new MinimapRenderer();
private OverlayRenderer overlays = new OverlayRenderer();
private FogRenderer fog = new FogRenderer();
private Rectangle rect = new Rectangle(), rect2 = new Rectangle();
private Vector2 avgPosition = new Translator();
private Vector2 tmpVector1 = new Translator();
private Vector2 tmpVector2 = new Translator();
public Renderer() {
pixelate = true;
Lines.setCircleVertices(14);
private BlockRenderer blocks = new BlockRenderer();
private MinimapRenderer minimap = new MinimapRenderer();
private OverlayRenderer overlays = new OverlayRenderer();
private FogRenderer fog = new FogRenderer();
Shaders.init();
public Renderer(){
pixelate = true;
Lines.setCircleVertices(14);
Core.cameraScale = baseCameraScale;
Effects.setEffectProvider((effect, color, x, y, rotation, data) -> {
if(effect == Fx.none) return;
if(Settings.getBool("effects")){
Rectangle view = rect.setSize(camera.viewportWidth, camera.viewportHeight)
.setCenter(camera.position.x, camera.position.y);
Rectangle pos = rect2.setSize(effect.size).setCenter(x, y);
Shaders.init();
if(view.overlaps(pos)){
Core.cameraScale = baseCameraScale;
Effects.setEffectProvider((effect, color, x, y, rotation, data) -> {
if(effect == Fx.none) return;
if(Settings.getBool("effects")){
Rectangle view = rect.setSize(camera.viewportWidth, camera.viewportHeight)
.setCenter(camera.position.x, camera.position.y);
Rectangle pos = rect2.setSize(effect.size).setCenter(x, y);
if(!(effect instanceof GroundEffect)) {
EffectEntity entity = Pooling.obtain(EffectEntity.class);
entity.effect = effect;
entity.color = color;
entity.rotation = rotation;
entity.data = data;
entity.id ++;
entity.set(x, y);
if(data instanceof BaseEntity){
entity.setParent((BaseEntity)data);
}
threads.runGraphics(() -> effectGroup.add(entity));
}else{
GroundEffectEntity entity = Pooling.obtain(GroundEffectEntity.class);
entity.effect = effect;
entity.color = color;
entity.rotation = rotation;
entity.id ++;
entity.data = data;
entity.set(x, y);
threads.runGraphics(() -> groundEffectGroup.add(entity));
}
}
}
});
if(view.overlaps(pos)){
Cursors.cursorScaling = 3;
Cursors.outlineColor = Color.valueOf("444444");
if(!(effect instanceof GroundEffect)){
EffectEntity entity = Pooling.obtain(EffectEntity.class);
entity.effect = effect;
entity.color = color;
entity.rotation = rotation;
entity.data = data;
entity.id++;
entity.set(x, y);
if(data instanceof BaseEntity){
entity.setParent((BaseEntity) data);
}
threads.runGraphics(() -> effectGroup.add(entity));
}else{
GroundEffectEntity entity = Pooling.obtain(GroundEffectEntity.class);
entity.effect = effect;
entity.color = color;
entity.rotation = rotation;
entity.id++;
entity.data = data;
entity.set(x, y);
threads.runGraphics(() -> groundEffectGroup.add(entity));
}
}
}
});
Cursors.arrow = Cursors.loadCursor("cursor");
Cursors.hand = Cursors.loadCursor("hand");
Cursors.ibeam = Cursors.loadCursor("ibar");
Cursors.loadCustom("drill");
Cursors.loadCustom("unload");
Cursors.cursorScaling = 3;
Cursors.outlineColor = Color.valueOf("444444");
clearColor = Hue.lightness(0.4f);
clearColor.a = 1f;
Cursors.arrow = Cursors.loadCursor("cursor");
Cursors.hand = Cursors.loadCursor("hand");
Cursors.ibeam = Cursors.loadCursor("ibar");
Cursors.restoreCursor();
Cursors.loadCustom("drill");
Cursors.loadCustom("unload");
background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
}
clearColor = Hue.lightness(0.4f);
clearColor.a = 1f;
@Override
public void init(){
int scale = Core.cameraScale;
background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
}
@Override
public void init(){
int scale = Core.cameraScale;
effectSurface = Graphics.createSurface(scale);
pixelSurface = Graphics.createSurface(scale);
}
pixelSurface = Graphics.createSurface(scale);
}
@Override
public void update(){
@Override
public void update(){
if(Core.cameraScale != targetscale){
float targetzoom = (float) Core.cameraScale / targetscale;
camera.zoom = Mathf.lerpDelta(camera.zoom, targetzoom, 0.2f);
if(Core.cameraScale != targetscale){
float targetzoom = (float) Core.cameraScale / targetscale;
camera.zoom = Mathf.lerpDelta(camera.zoom, targetzoom, 0.2f);
if(Mathf.in(camera.zoom, targetzoom, 0.005f)){
camera.zoom = 1f;
Graphics.setCameraScale(targetscale);
for(Player player : players) {
if(Mathf.in(camera.zoom, targetzoom, 0.005f)){
camera.zoom = 1f;
Graphics.setCameraScale(targetscale);
for(Player player : players){
control.input(player.playerIndex).resetCursor();
}
}
}else{
camera.zoom = Mathf.lerpDelta(camera.zoom, 1f, 0.2f);
}
}
}else{
camera.zoom = Mathf.lerpDelta(camera.zoom, 1f, 0.2f);
}
if(state.is(State.menu)){
Graphics.clear(Color.BLACK);
}else{
if(state.is(State.menu)){
Graphics.clear(Color.BLACK);
}else{
Vector2 position = averagePosition();
if(!mobile){
setCamera(position.x + 0.0001f, position.y + 0.0001f);
}
setCamera(position.x + 0.0001f, position.y + 0.0001f);
}
clampCamera(-tilesize / 2f, -tilesize / 2f + 1, world.width() * tilesize - tilesize / 2f, world.height() * tilesize - tilesize / 2f);
clampCamera(-tilesize / 2f, -tilesize / 2f + 1, world.width() * tilesize - tilesize / 2f, world.height() * tilesize - tilesize / 2f);
float prex = camera.position.x, prey = camera.position.y;
updateShake(0.75f);
float prex = camera.position.x, prey = camera.position.y;
updateShake(0.75f);
float deltax = camera.position.x - prex, deltay = camera.position.y - prey;
float lastx = camera.position.x, lasty = camera.position.y;
if(snapCamera){
camera.position.set((int) camera.position.x, (int) camera.position.y, 0);
}
if(Gdx.graphics.getHeight() / Core.cameraScale % 2 == 1){
camera.position.add(0, -0.5f, 0);
}
float deltax = camera.position.x - prex, deltay = camera.position.y - prey;
float lastx = camera.position.x, lasty = camera.position.y;
if(Gdx.graphics.getWidth() / Core.cameraScale % 2 == 1){
camera.position.add(-0.5f, 0, 0);
}
draw();
if(snapCamera){
camera.position.set((int) camera.position.x, (int) camera.position.y, 0);
}
camera.position.set(lastx - deltax, lasty - deltay, 0);
}
if(Gdx.graphics.getHeight() / Core.cameraScale % 2 == 1){
camera.position.add(0, -0.5f, 0);
}
if(debug && !ui.chatfrag.chatOpen()) {
renderer.record(); //this only does something if GdxGifRecorder is on the class path, which it usually isn't
}
}
if(Gdx.graphics.getWidth() / Core.cameraScale % 2 == 1){
camera.position.add(-0.5f, 0, 0);
}
@Override
public void draw(){
camera.update();
draw();
Graphics.clear(clearColor);
batch.setProjectionMatrix(camera.combined);
camera.position.set(lastx - deltax, lasty - deltay, 0);
}
Graphics.surface(pixelSurface, false);
if(debug && !ui.chatfrag.chatOpen()){
renderer.record(); //this only does something if GdxGifRecorder is on the class path, which it usually isn't
}
}
drawPadding();
blocks.drawFloor();
@Override
public void draw(){
camera.update();
drawAndInterpolate(groundEffectGroup, e -> e instanceof BelowLiquidTrait);
drawAndInterpolate(puddleGroup);
drawAndInterpolate(groundEffectGroup, e -> !(e instanceof BelowLiquidTrait));
Graphics.clear(clearColor);
blocks.processBlocks();
blocks.drawBlocks(Layer.block);
batch.setProjectionMatrix(camera.combined);
Graphics.shader(Shaders.blockbuild, false);
Graphics.surface(pixelSurface, false);
drawPadding();
blocks.drawFloor();
drawAndInterpolate(groundEffectGroup, e -> e instanceof BelowLiquidTrait);
drawAndInterpolate(puddleGroup);
drawAndInterpolate(groundEffectGroup, e -> !(e instanceof BelowLiquidTrait));
blocks.processBlocks();
blocks.drawBlocks(Layer.block);
Graphics.shader(Shaders.blockbuild, false);
blocks.drawBlocks(Layer.placement);
Graphics.shader();
blocks.drawBlocks(Layer.overlay);
if(itemGroup.size() > 0){
Shaders.outline.color.set(Team.none.color);
Graphics.beginShaders(Shaders.outline);
drawAndInterpolate(itemGroup);
Graphics.endShaders();
}
Shaders.outline.color.set(Team.none.color);
Graphics.beginShaders(Shaders.outline);
drawAndInterpolate(itemGroup);
Graphics.endShaders();
}
drawAllTeams(false);
blocks.skipLayer(Layer.turret);
blocks.drawBlocks(Layer.laser);
blocks.skipLayer(Layer.turret);
blocks.drawBlocks(Layer.laser);
drawAllTeams(true);
drawFlyerShadows();
drawAndInterpolate(bulletGroup);
drawAndInterpolate(effectGroup);
drawAllTeams(true);
overlays.drawBottom();
drawAndInterpolate(playerGroup, p -> true, Player::drawBuildRequests);
overlays.drawTop();
drawAndInterpolate(bulletGroup);
drawAndInterpolate(effectGroup);
Graphics.flushSurface();
overlays.drawBottom();
drawAndInterpolate(playerGroup, p -> true, Player::drawBuildRequests);
overlays.drawTop();
if(showPaths && debug) drawDebug();
Graphics.flushSurface();
batch.end();
if(showPaths && debug) drawDebug();
if(showFog){
fog.draw();
}
batch.end();
batch.begin();
EntityDraw.setClip(false);
drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
EntityDraw.setClip(true);
batch.end();
}
if(showFog){
fog.draw();
}
private void drawAllTeams(boolean flying){
for(Team team : Team.all){
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
Graphics.beginCam();
EntityDraw.setClip(false);
drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
EntityDraw.setClip(true);
Graphics.end();
}
if(group.count(p -> p.isFlying() == flying) +
playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue;
private void drawFlyerShadows(){
Graphics.surface(effectSurface, true, false);
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder);
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawUnder);
float trnsX = 12, trnsY = -13;
Shaders.outline.color.set(team.color);
Shaders.mix.color.set(Color.WHITE);
Graphics.end();
Core.batch.getTransformMatrix().translate(trnsX, trnsY, 0);
Graphics.begin();
Graphics.beginShaders(Shaders.outline);
Graphics.shader(Shaders.mix, true);
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead());
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team);
Graphics.shader();
blocks.drawTeamBlocks(Layer.turret, team);
Graphics.endShaders();
for(EntityGroup<? extends BaseUnit> group : unitGroups){
if(!group.isEmpty()){
drawAndInterpolate(group, unit -> unit.isFlying() && !unit.isDead(), Unit::drawShadow);
}
}
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver);
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver);
}
}
if(!playerGroup.isEmpty()){
drawAndInterpolate(playerGroup, unit -> unit.isFlying() && !unit.isDead(), Unit::drawShadow);
}
public <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group){
drawAndInterpolate(group, t -> true, DrawTrait::draw);
}
Graphics.end();
Core.batch.getTransformMatrix().translate(-trnsX, -trnsY, 0);
Graphics.begin();
public <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group, Predicate<T> toDraw){
drawAndInterpolate(group, toDraw, DrawTrait::draw);
}
//TODO this actually isn't necessary
Draw.color(0, 0, 0, 0.15f);
Graphics.flushSurface();
Draw.color();
}
public <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group, Predicate<T> toDraw, Consumer<T> drawer){
EntityDraw.drawWith(group, toDraw, t -> {
float lastx = t.getX(), lasty = t.getY(), lastrot = 0f;
private void drawAllTeams(boolean flying){
for(Team team : Team.all){
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
if(threads.doInterpolate() && threads.isEnabled() && t instanceof SolidTrait){
SolidTrait s = (SolidTrait)t;
if(group.count(p -> p.isFlying() == flying) +
playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue;
lastrot = s.getRotation();
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder);
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawUnder);
if(s.lastUpdated() != 0){
float timeSinceUpdate = TimeUtils.timeSinceMillis(s.lastUpdated());
float alpha = Math.min(timeSinceUpdate / s.updateSpacing(), 1f);
Shaders.outline.color.set(team.color);
Shaders.mix.color.set(Color.WHITE);
tmpVector1.set(s.lastPosition().x, s.lastPosition().y)
.lerp(tmpVector2.set(lastx, lasty), alpha);
s.setRotation(Mathf.slerp(s.lastPosition().z, lastrot, alpha));
Graphics.beginShaders(Shaders.outline);
Graphics.shader(Shaders.mix, true);
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead());
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team);
Graphics.shader();
blocks.drawTeamBlocks(Layer.turret, team);
Graphics.endShaders();
s.setX(tmpVector1.x);
s.setY(tmpVector1.y);
}
}
drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver);
drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver);
}
}
//TODO extremely hacky
if(t instanceof Player && ((Player) t).getCarry() != null && ((Player) t).getCarry() instanceof Player && ((Player) ((Player) t).getCarry()).isLocal){
((Player) t).x = ((Player) t).getCarry().getX();
((Player) t).y = ((Player) t).getCarry().getY();
}
public <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group){
drawAndInterpolate(group, t -> true, DrawTrait::draw);
}
drawer.accept(t);
public <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group, Predicate<T> toDraw){
drawAndInterpolate(group, toDraw, DrawTrait::draw);
}
t.setX(lastx);
t.setY(lasty);
public <T extends DrawTrait> void drawAndInterpolate(EntityGroup<T> group, Predicate<T> toDraw, Consumer<T> drawer){
EntityDraw.drawWith(group, toDraw, t -> {
float lastx = t.getX(), lasty = t.getY(), lastrot = 0f;
if(threads.doInterpolate() && threads.isEnabled()) {
if(threads.doInterpolate() && threads.isEnabled() && t instanceof SolidTrait){
SolidTrait s = (SolidTrait) t;
if (t instanceof SolidTrait) {
((SolidTrait) t).setRotation(lastrot);
}
}
});
}
lastrot = s.getRotation();
@Override
public void resize(int width, int height){
super.resize(width, height);
for(Player player : players) {
if(s.lastUpdated() != 0){
float timeSinceUpdate = TimeUtils.timeSinceMillis(s.lastUpdated());
float alpha = Math.min(timeSinceUpdate / s.updateSpacing(), 1f);
tmpVector1.set(s.lastPosition().x, s.lastPosition().y)
.lerp(tmpVector2.set(lastx, lasty), alpha);
s.setRotation(Mathf.slerp(s.lastPosition().z, lastrot, alpha));
s.setX(tmpVector1.x);
s.setY(tmpVector1.y);
}
}
//TODO extremely hacky
if(t instanceof Player && ((Player) t).getCarry() != null && ((Player) t).getCarry() instanceof Player && ((Player) ((Player) t).getCarry()).isLocal){
((Player) t).x = ((Player) t).getCarry().getX();
((Player) t).y = ((Player) t).getCarry().getY();
}
drawer.accept(t);
t.setX(lastx);
t.setY(lasty);
if(threads.doInterpolate() && threads.isEnabled()){
if(t instanceof SolidTrait){
((SolidTrait) t).setRotation(lastrot);
}
}
});
}
@Override
public void resize(int width, int height){
super.resize(width, height);
for(Player player : players){
control.input(player.playerIndex).resetCursor();
}
camera.position.set(players[0].x, players[0].y, 0);
}
camera.position.set(players[0].x, players[0].y, 0);
}
@Override
public void dispose() {
background.dispose();
fog.dispose();
}
@Override
public void dispose(){
background.dispose();
fog.dispose();
}
public Vector2 averagePosition(){
avgPosition.setZero();
public Vector2 averagePosition(){
avgPosition.setZero();
drawAndInterpolate(playerGroup, p -> p.isLocal, p -> {
avgPosition.add(p.x, p.y);
});
drawAndInterpolate(playerGroup, p -> p.isLocal, p -> {
avgPosition.add(p.x, p.y);
});
avgPosition.scl(1f / players.length);
return avgPosition;
}
public FogRenderer fog() {
return fog;
}
public FogRenderer fog(){
return fog;
}
public MinimapRenderer minimap() {
return minimap;
}
public MinimapRenderer minimap(){
return minimap;
}
void drawPadding(){
float vw = world.width() * tilesize;
float cw = camera.viewportWidth * camera.zoom;
float ch = camera.viewportHeight * camera.zoom;
if(vw < cw){
batch.draw(background,
camera.position.x + vw/2,
Mathf.round(camera.position.y - ch/2, tilesize),
(cw - vw) /2,
ch + tilesize,
0, 0,
((cw - vw) / 2 / tilesize), -ch / tilesize + 1);
void drawPadding(){
float vw = world.width() * tilesize;
float cw = camera.viewportWidth * camera.zoom;
float ch = camera.viewportHeight * camera.zoom;
if(vw < cw){
batch.draw(background,
camera.position.x + vw / 2,
Mathf.round(camera.position.y - ch / 2, tilesize),
(cw - vw) / 2,
ch + tilesize,
0, 0,
((cw - vw) / 2 / tilesize), -ch / tilesize + 1);
batch.draw(background,
camera.position.x - vw/2,
Mathf.round(camera.position.y - ch/2, tilesize),
-(cw - vw) /2,
ch + tilesize,
0, 0,
-((cw - vw) / 2 / tilesize), -ch / tilesize + 1);
}
}
batch.draw(background,
camera.position.x - vw / 2,
Mathf.round(camera.position.y - ch / 2, tilesize),
-(cw - vw) / 2,
ch + tilesize,
0, 0,
-((cw - vw) / 2 / tilesize), -ch / tilesize + 1);
}
}
void drawDebug(){
int rangex = (int)(Core.camera.viewportWidth/tilesize/2), rangey = (int)(Core.camera.viewportHeight/tilesize/2);
void drawDebug(){
int rangex = (int) (Core.camera.viewportWidth / tilesize / 2), rangey = (int) (Core.camera.viewportHeight / tilesize / 2);
for(int x = -rangex; x <= rangex; x++) {
for (int y = -rangey; y <= rangey; y++) {
int worldx = Mathf.scl(camera.position.x, tilesize) + x;
int worldy = Mathf.scl(camera.position.y, tilesize) + y;
for(int x = -rangex; x <= rangex; x++){
for(int y = -rangey; y <= rangey; y++){
int worldx = Mathf.scl(camera.position.x, tilesize) + x;
int worldy = Mathf.scl(camera.position.y, tilesize) + y;
if(world.tile(worldx, worldy) == null) continue;
if(world.tile(worldx, worldy) == null) continue;
float value = world.pathfinder().getDebugValue(worldx, worldy);
Draw.color(Color.PURPLE);
Draw.alpha((value % 10f) / 10f);
Lines.square(worldx * tilesize, worldy*tilesize, 4f);
}
}
float value = world.pathfinder().getDebugValue(worldx, worldy);
Draw.color(Color.PURPLE);
Draw.alpha((value % 10f) / 10f);
Lines.square(worldx * tilesize, worldy * tilesize, 4f);
}
}
Draw.color(Color.ORANGE);
Draw.tcolor(Color.ORANGE);
Draw.color(Color.ORANGE);
Draw.tcolor(Color.ORANGE);
ObjectIntMap<Tile> seen = new ObjectIntMap<>();
ObjectIntMap<Tile> seen = new ObjectIntMap<>();
for(BlockFlag flag : BlockFlag.values()){
for(Tile tile : world.indexer().getEnemy(Team.blue, flag)){
int index = seen.getAndIncrement(tile, 0, 1);
Draw.tscl(0.125f);
Draw.text(flag.name(), tile.drawx(), tile.drawy() + tile.block().size * tilesize/2f + 4 + index * 3);
Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize/2f);
}
}
Draw.tscl(fontScale);
Draw.tcolor();
for(BlockFlag flag : BlockFlag.values()){
for(Tile tile : world.indexer().getEnemy(Team.blue, flag)){
int index = seen.getAndIncrement(tile, 0, 1);
Draw.tscl(0.125f);
Draw.text(flag.name(), tile.drawx(), tile.drawy() + tile.block().size * tilesize / 2f + 4 + index * 3);
Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f);
}
}
Draw.tscl(fontScale);
Draw.tcolor();
Draw.color();
}
Draw.color();
}
public BlockRenderer getBlocks() {
return blocks;
}
public BlockRenderer getBlocks(){
return blocks;
}
public void setCameraScale(int amount){
targetscale = amount;
clampScale();
//scale up all surfaces in preparation for the zoom
for(Surface surface : Graphics.getSurfaces()){
surface.setScale(targetscale);
}
}
public void setCameraScale(int amount){
targetscale = amount;
clampScale();
//scale up all surfaces in preparation for the zoom
for(Surface surface : Graphics.getSurfaces()){
surface.setScale(targetscale);
}
}
public void scaleCamera(int amount){
setCameraScale(targetscale + amount);
}
public void scaleCamera(int amount){
setCameraScale(targetscale + amount);
}
public void clampScale(){
float s = io.anuke.ucore.scene.ui.layout.Unit.dp.scl(1f);
targetscale = Mathf.clamp(targetscale, Math.round(s*2), Math.round(s*5));
}
public void clampScale(){
float s = io.anuke.ucore.scene.ui.layout.Unit.dp.scl(1f);
targetscale = Mathf.clamp(targetscale, Math.round(s * 2), Math.round(s * 5));
}
}

View File

@@ -1,43 +1,38 @@
package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Queue;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.EntityGroup.ArrayContainer;
import io.anuke.ucore.entities.trait.Entity;
import io.anuke.ucore.util.Log;
import static io.anuke.mindustry.Vars.control;
import static io.anuke.mindustry.Vars.logic;
public class ThreadHandler {
private final Array<Runnable> toRun = new Array<>();
public class ThreadHandler{
private final Queue<Runnable> toRun = new Queue<>();
private final ThreadProvider impl;
private final Object updateLock = new Object();
private float delta = 1f;
private float smoothDelta = 1f;
private long frame = 0, lastDeltaUpdate;
private float framesSinceUpdate;
private boolean enabled;
private final Object updateLock = new Object();
private boolean rendered = true;
public ThreadHandler(ThreadProvider impl){
this.impl = impl;
Timers.setDeltaProvider(() -> {
float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f;
float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime() * 60f;
return Math.min(Float.isNaN(result) ? 1f : result, 15f);
});
}
public void run(Runnable r){
if(enabled) {
synchronized (toRun) {
toRun.add(r);
if(enabled){
synchronized(toRun){
toRun.addLast(r);
}
}else{
r.run();
@@ -45,7 +40,7 @@ public class ThreadHandler {
}
public void runGraphics(Runnable r){
if(enabled) {
if(enabled){
Gdx.app.postRunnable(r);
}else{
r.run();
@@ -53,9 +48,9 @@ public class ThreadHandler {
}
public void runDelay(Runnable r){
if(enabled) {
synchronized (toRun) {
toRun.add(r);
if(enabled){
synchronized(toRun){
toRun.addLast(r);
}
}else{
Gdx.app.postRunnable(r);
@@ -63,7 +58,7 @@ public class ThreadHandler {
}
public int getTPS(){
return (int)(60/smoothDelta);
return (int) (60 / smoothDelta);
}
public long getFrameID(){
@@ -80,40 +75,34 @@ public class ThreadHandler {
framesSinceUpdate += Timers.delta();
synchronized (updateLock) {
synchronized(updateLock){
rendered = true;
impl.notify(updateLock);
}
}
public void setEnabled(boolean enabled){
if(enabled){
logic.doUpdate = false;
for(EntityGroup<?> group : Entities.getAllGroups()){
impl.switchContainer(group);
}
Timers.runTask(2f, () -> {
impl.start(this::runLogic);
this.enabled = true;
});
}else{
this.enabled = false;
impl.stop();
for(EntityGroup<?> group : Entities.getAllGroups()){
group.setContainer(new ArrayContainer<>());
}
Timers.runTask(2f, () -> {
logic.doUpdate = true;
});
}
}
public boolean isEnabled(){
return enabled;
}
public void setEnabled(boolean enabled){
if(enabled){
logic.doUpdate = false;
Timers.runTask(2f, () -> {
impl.start(this::runLogic);
this.enabled = true;
});
}else{
this.enabled = false;
impl.stop();
Timers.runTask(2f, () -> {
logic.doUpdate = true;
});
}
}
public boolean doInterpolate(){
return enabled && Math.abs(Gdx.graphics.getFramesPerSecond() - getTPS()) > 15;
return enabled && Gdx.graphics.getFramesPerSecond() - getTPS() > 20 && getTPS() < 30;
}
public boolean isOnThread(){
@@ -121,15 +110,21 @@ public class ThreadHandler {
}
private void runLogic(){
try {
while (true) {
try{
while(true){
long time = TimeUtils.nanoTime();
synchronized (toRun) {
for(Runnable r : toRun){
r.run();
while(true){
Runnable r;
synchronized(toRun){
if(toRun.size > 0){
r = toRun.removeFirst();
}else{
break;
}
}
toRun.clear();
r.run();
}
logic.doUpdate = true;
@@ -139,12 +134,12 @@ public class ThreadHandler {
long elapsed = TimeUtils.nanosToMillis(TimeUtils.timeSinceNanos(time));
long target = (long) ((1000) / 60f);
if (elapsed < target) {
if(elapsed < target){
impl.sleep(target - elapsed);
}
synchronized(updateLock) {
while(!rendered) {
synchronized(updateLock){
while(!rendered){
impl.wait(updateLock);
}
rendered = false;
@@ -158,23 +153,27 @@ public class ThreadHandler {
smoothDelta = delta;
}
frame ++;
frame++;
framesSinceUpdate = 0;
}
} catch (InterruptedException ex) {
}catch(InterruptedException ex){
Log.info("Stopping logic thread.");
} catch (Throwable ex) {
}catch(Throwable ex){
control.setError(ex);
}
}
public interface ThreadProvider {
public interface ThreadProvider{
boolean isOnThread();
void sleep(long ms) throws InterruptedException;
void start(Runnable run);
void stop();
void wait(Object object) throws InterruptedException;
void notify(Object object);
<T extends Entity> void switchContainer(EntityGroup<T> group);
}
}

View File

@@ -34,32 +34,11 @@ import java.util.Locale;
import static io.anuke.mindustry.Vars.control;
import static io.anuke.mindustry.Vars.players;
import static io.anuke.mindustry.Vars.threads;
import static io.anuke.ucore.scene.actions.Actions.*;
public class UI extends SceneModule{
public AboutDialog about;
public RestartDialog restart;
public LevelDialog levels;
public MapsDialog maps;
public LoadDialog load;
public DiscordDialog discord;
public JoinDialog join;
public HostDialog host;
public PausedDialog paused;
public SettingsMenuDialog settings;
public ControlsDialog controls;
public MapEditorDialog editor;
public LanguageDialog language;
public BansDialog bans;
public AdminsDialog admins;
public TraceDialog traces;
public RollbackDialog rollback;
public ChangelogDialog changelog;
public LocalPlayerDialog localplayers;
public UnlocksDialog unlocks;
public ContentInfoDialog content;
public final MenuFragment menufrag = new MenuFragment();
public final MenuFragment menufrag = new MenuFragment();
public final HudFragment hudfrag = new HudFragment();
public final ChatFragment chatfrag = new ChatFragment();
public final PlayerListFragment listfrag = new PlayerListFragment();
@@ -67,239 +46,260 @@ public class UI extends SceneModule{
public final LoadingFragment loadfrag = new LoadingFragment();
public final DebugFragment debugfrag = new DebugFragment();
public AboutDialog about;
public RestartDialog restart;
public LevelDialog levels;
public MapsDialog maps;
public LoadDialog load;
public DiscordDialog discord;
public JoinDialog join;
public HostDialog host;
public PausedDialog paused;
public SettingsMenuDialog settings;
public ControlsDialog controls;
public MapEditorDialog editor;
public LanguageDialog language;
public BansDialog bans;
public AdminsDialog admins;
public TraceDialog traces;
public RollbackDialog rollback;
public ChangelogDialog changelog;
public LocalPlayerDialog localplayers;
public UnlocksDialog unlocks;
public ContentInfoDialog content;
private Locale lastLocale;
public UI() {
Dialog.setShowAction(()-> sequence(
alpha(0f),
originCenter(),
moveToAligned(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2, Align.center),
scaleTo(0.0f, 1f),
parallel(
scaleTo(1f, 1f, 0.1f, Interpolation.fade),
fadeIn(0.1f, Interpolation.fade)
)
));
Dialog.setHideAction(()-> sequence(
parallel(
scaleTo(0.01f, 0.01f, 0.1f, Interpolation.fade),
fadeOut(0.1f, Interpolation.fade)
)
));
TooltipManager.getInstance().animations = false;
Settings.setErrorHandler(()-> Timers.run(1f, ()-> showError("[crimson]Failed to access local storage.\nSettings will not be saved.")));
Dialog.closePadR = -1;
Dialog.closePadT = 5;
Colors.put("description", Palette.description);
Colors.put("turretinfo", Palette.turretinfo);
Colors.put("iteminfo", Palette.iteminfo);
Colors.put("powerinfo", Palette.powerinfo);
Colors.put("liquidinfo", Palette.liquidinfo);
Colors.put("craftinfo", Palette.craftinfo);
Colors.put("missingitems", Palette.missingitems);
Colors.put("health", Palette.health);
Colors.put("healthstats", Palette.healthstats);
Colors.put("interact", Palette.interact);
Colors.put("accent", Palette.accent);
Colors.put("place", Palette.place);
Colors.put("remove", Palette.remove);
Colors.put("placeRotate", Palette.placeRotate);
Colors.put("range", Palette.range);
Colors.put("power", Palette.power);
}
@Override
protected void loadSkin(){
skin = new Skin(Gdx.files.internal("ui/uiskin.json"), Core.atlas);
Mathf.each(font -> {
font.setUseIntegerPositions(false);
font.getData().setScale(Vars.fontScale);
font.getData().down += Unit.dp.scl(4f);
font.getData().lineHeight -= Unit.dp.scl(2f);
}, skin.font(), skin.getFont("default-font-chat"), skin.getFont("korean"));
}
public UI(){
Dialog.setShowAction(() -> sequence(
alpha(0f),
originCenter(),
moveToAligned(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, Align.center),
scaleTo(0.0f, 1f),
parallel(
scaleTo(1f, 1f, 0.1f, Interpolation.fade),
fadeIn(0.1f, Interpolation.fade)
)
));
@Override
public synchronized void update(){
if(Vars.debug && !Vars.showUI) return;
Dialog.setHideAction(() -> sequence(
parallel(
scaleTo(0.01f, 0.01f, 0.1f, Interpolation.fade),
fadeOut(0.1f, Interpolation.fade)
)
));
if(Graphics.drawing()) Graphics.end();
act();
TooltipManager.getInstance().animations = false;
Graphics.begin();
Settings.setErrorHandler(() -> Timers.run(1f, () -> showError("[crimson]Failed to access local storage.\nSettings will not be saved.")));
for(int i = 0; i < players.length; i ++){
InputHandler input = control.input(i);
Dialog.closePadR = -1;
Dialog.closePadT = 5;
if(input.isCursorVisible()) {
Colors.put("description", Palette.description);
Colors.put("turretinfo", Palette.turretinfo);
Colors.put("iteminfo", Palette.iteminfo);
Colors.put("powerinfo", Palette.powerinfo);
Colors.put("liquidinfo", Palette.liquidinfo);
Colors.put("craftinfo", Palette.craftinfo);
Colors.put("missingitems", Palette.missingitems);
Colors.put("health", Palette.health);
Colors.put("healthstats", Palette.healthstats);
Colors.put("interact", Palette.interact);
Colors.put("accent", Palette.accent);
Colors.put("place", Palette.place);
Colors.put("remove", Palette.remove);
Colors.put("placeRotate", Palette.placeRotate);
Colors.put("range", Palette.range);
Colors.put("power", Palette.power);
}
@Override
protected void loadSkin(){
skin = new Skin(Gdx.files.internal("ui/uiskin.json"), Core.atlas);
Mathf.each(font -> {
font.setUseIntegerPositions(false);
font.getData().setScale(Vars.fontScale);
font.getData().down += Unit.dp.scl(4f);
font.getData().lineHeight -= Unit.dp.scl(2f);
}, skin.font(), skin.getFont("default-font-chat"), skin.getFont("korean"));
}
@Override
public synchronized void update(){
if(Vars.debug && !Vars.showUI) return;
if(Graphics.drawing()) Graphics.end();
act();
Graphics.begin();
for(int i = 0; i < players.length; i++){
InputHandler input = control.input(i);
if(input.isCursorVisible()){
Draw.color();
float scl = Unit.dp.scl(3f);
Draw.rect("controller-cursor", input.getMouseX(), Gdx.graphics.getHeight() - input.getMouseY(), 16*scl, 16*scl);
Draw.rect("controller-cursor", input.getMouseX(), Gdx.graphics.getHeight() - input.getMouseY(), 16 * scl, 16 * scl);
}
}
Graphics.end();
Graphics.end();
Draw.color();
}
}
@Override
public void init(){
editor = new MapEditorDialog();
controls = new ControlsDialog();
restart = new RestartDialog();
join = new JoinDialog();
discord = new DiscordDialog();
load = new LoadDialog();
levels = new LevelDialog();
language = new LanguageDialog();
settings = new SettingsMenuDialog();
paused = new PausedDialog();
changelog = new ChangelogDialog();
about = new AboutDialog();
host = new HostDialog();
bans = new BansDialog();
admins = new AdminsDialog();
traces = new TraceDialog();
rollback = new RollbackDialog();
maps = new MapsDialog();
localplayers = new LocalPlayerDialog();
unlocks = new UnlocksDialog();
content = new ContentInfoDialog();
@Override
public void init(){
editor = new MapEditorDialog();
controls = new ControlsDialog();
restart = new RestartDialog();
join = new JoinDialog();
discord = new DiscordDialog();
load = new LoadDialog();
levels = new LevelDialog();
language = new LanguageDialog();
settings = new SettingsMenuDialog();
paused = new PausedDialog();
changelog = new ChangelogDialog();
about = new AboutDialog();
host = new HostDialog();
bans = new BansDialog();
admins = new AdminsDialog();
traces = new TraceDialog();
rollback = new RollbackDialog();
maps = new MapsDialog();
localplayers = new LocalPlayerDialog();
unlocks = new UnlocksDialog();
content = new ContentInfoDialog();
build.begin(scene);
build.begin(scene);
Group group = Core.scene.getRoot();
Group group = Core.scene.getRoot();
backfrag.build(group);
hudfrag.build(group);
menufrag.build(group);
chatfrag.container().build(group);
listfrag.build(group);
debugfrag.build(group);
loadfrag.build(group);
backfrag.build(group);
hudfrag.build(group);
menufrag.build(group);
chatfrag.container().build(group);
listfrag.build(group);
debugfrag.build(group);
loadfrag.build(group);
build.end();
}
build.end();
}
@Override
public boolean hasMouse(){
return super.hasMouse();
}
@Override
public boolean hasMouse() {
return super.hasMouse();
}
@Override
public void resize(int width, int height){
super.resize(width, height);
@Override
public void resize(int width, int height) {
super.resize(width, height);
Events.fire(ResizeEvent.class);
}
Events.fire(ResizeEvent.class);
}
public Locale getLocale(){
String loc = Settings.getString("locale");
if(loc.equals("default")){
return Locale.getDefault();
}else{
if(lastLocale == null || !lastLocale.toString().equals(loc)){
if(loc.contains("_")){
String[] split = loc.split("_");
lastLocale = new Locale(split[0], split[1]);
}else{
lastLocale = new Locale(loc);
}
}
public Locale getLocale(){
String loc = Settings.getString("locale");
if(loc.equals("default")){
return Locale.getDefault();
}else{
if(lastLocale == null || !lastLocale.toString().equals(loc)){
if(loc.contains("_")){
String[] split = loc.split("_");
lastLocale = new Locale(split[0], split[1]);
}else{
lastLocale = new Locale(loc);
}
}
return lastLocale;
}
}
return lastLocale;
}
}
public void loadAnd(Callable call){
loadAnd("$text.loading", call);
}
public void loadAnd(Callable call){
loadAnd("$text.loading", call);
}
public void loadAnd(String text, Callable call){
loadfrag.show(text);
Timers.runTask(7f, () -> {
call.run();
loadfrag.hide();
});
}
public void loadAnd(String text, Callable call){
loadfrag.show(text);
Timers.runTask(6f, () -> {
call.run();
loadfrag.hide();
});
}
public void loadLogic(Callable call){
loadLogic("$text.loading", call);
}
public void showTextInput(String title, String text, String def, TextFieldFilter filter, Consumer<String> confirmed){
new Dialog(title, "dialog"){{
content().margin(30).add(text).padRight(6f);
TextField field = content().addField(def, t->{}).size(170f, 50f).get();
field.setTextFieldFilter((f, c) -> field.getText().length() < 12 && filter.acceptChar(f, c));
Platform.instance.addDialog(field);
buttons().defaults().size(120, 54).pad(4);
buttons().addButton("$text.ok", () -> {
confirmed.accept(field.getText());
hide();
}).disabled(b -> field.getText().isEmpty());
buttons().addButton("$text.cancel", this::hide);
}}.show();
}
public void loadLogic(String text, Callable call){
loadfrag.show();
Timers.runTask(7f, () -> {
threads.run(() -> {
call.run();
threads.runGraphics(loadfrag::hide);
});
});
}
public void showTextInput(String title, String text, String def, Consumer<String> confirmed){
showTextInput(title, text, def, (field, c) -> true, confirmed);
}
public void showTextInput(String title, String text, String def, TextFieldFilter filter, Consumer<String> confirmed){
new Dialog(title, "dialog"){{
content().margin(30).add(text).padRight(6f);
TextField field = content().addField(def, t -> {
}).size(170f, 50f).get();
field.setTextFieldFilter((f, c) -> field.getText().length() < 12 && filter.acceptChar(f, c));
Platform.instance.addDialog(field);
buttons().defaults().size(120, 54).pad(4);
buttons().addButton("$text.ok", () -> {
confirmed.accept(field.getText());
hide();
}).disabled(b -> field.getText().isEmpty());
buttons().addButton("$text.cancel", this::hide);
}}.show();
}
public void showInfoFade(String info){
Table table = new Table();
table.setFillParent(true);
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.removeActor());
table.top().add(info).padTop(8);
Core.scene.add(table);
}
public void showTextInput(String title, String text, String def, Consumer<String> confirmed){
showTextInput(title, text, def, (field, c) -> true, confirmed);
}
public void showInfo(String info){
new Dialog("$text.info.title", "dialog"){{
getCell(content()).growX();
content().margin(15).add(info).width(400f).wrap().get().setAlignment(Align.center, Align.center);
buttons().addButton("$text.ok", this::hide).size(90, 50).pad(4);
}}.show();
}
public void showInfoFade(String info){
Table table = new Table();
table.setFillParent(true);
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.removeActor());
table.top().add(info).padTop(8);
Core.scene.add(table);
}
public void showError(String text){
new Dialog("$text.error.title", "dialog"){{
content().margin(15).add(text).width(400f).wrap().get().setAlignment(Align.center, Align.center);
buttons().addButton("$text.ok", this::hide).size(90, 50).pad(4);
}}.show();
}
public void showInfo(String info){
new Dialog("$text.info.title", "dialog"){{
getCell(content()).growX();
content().margin(15).add(info).width(400f).wrap().get().setAlignment(Align.center, Align.center);
buttons().addButton("$text.ok", this::hide).size(90, 50).pad(4);
}}.show();
}
public void showConfirm(String title, String text, Listenable confirmed){
FloatingDialog dialog = new FloatingDialog(title);
dialog.content().add(text).width(400f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
dialog.buttons().defaults().size(200f, 54f).pad(2f);
dialog.buttons().addButton("$text.cancel", dialog::hide);
dialog.buttons().addButton("$text.ok", () -> {
dialog.hide();
confirmed.listen();
});
dialog.keyDown(Keys.ESCAPE, dialog::hide);
dialog.keyDown(Keys.BACK, dialog::hide);
dialog.show();
}
public void showError(String text){
new Dialog("$text.error.title", "dialog"){{
content().margin(15).add(text).width(400f).wrap().get().setAlignment(Align.center, Align.center);
buttons().addButton("$text.ok", this::hide).size(90, 50).pad(4);
}}.show();
}
public void showConfirmListen(String title, String text, Consumer<Boolean> listener){
FloatingDialog dialog = new FloatingDialog(title);
dialog.content().add(text).pad(4f);
dialog.buttons().defaults().size(200f, 54f).pad(2f);
dialog.buttons().addButton("$text.cancel", () -> {
dialog.hide();
listener.accept(true);
});
dialog.buttons().addButton("$text.ok", () -> {
dialog.hide();
listener.accept(true);
});
dialog.show();
}
public void showConfirm(String title, String text, Listenable confirmed){
FloatingDialog dialog = new FloatingDialog(title);
dialog.content().add(text).width(400f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
dialog.buttons().defaults().size(200f, 54f).pad(2f);
dialog.buttons().addButton("$text.cancel", dialog::hide);
dialog.buttons().addButton("$text.ok", () -> {
dialog.hide();
confirmed.listen();
});
dialog.keyDown(Keys.ESCAPE, dialog::hide);
dialog.keyDown(Keys.BACK, dialog::hide);
dialog.show();
}
}

View File

@@ -10,7 +10,11 @@ import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.io.MapMeta;
import io.anuke.mindustry.io.Maps;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.mapgen.WorldGenerator;
@@ -26,124 +30,130 @@ import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.*;
public class World extends Module{
private int seed;
private Map currentMap;
private Tile[][] tiles;
private Pathfinder pathfinder = new Pathfinder();
private BlockIndexer indexer = new BlockIndexer();
private Maps maps = new Maps();
private int seed;
private Array<Tile> tempTiles = new ThreadArray<>();
private boolean generating, invalidMap;
public World(){
maps.load();
}
@Override
public void dispose(){
maps.dispose();
}
public Maps maps(){
return maps;
}
private Map currentMap;
private Tile[][] tiles;
private Pathfinder pathfinder = new Pathfinder();
private BlockIndexer indexer = new BlockIndexer();
private Maps maps = new Maps();
public BlockIndexer indexer() {
return indexer;
}
private Array<Tile> tempTiles = new ThreadArray<>();
private boolean generating, invalidMap;
public Pathfinder pathfinder(){
return pathfinder;
}
public World(){
maps.load();
}
public boolean isInvalidMap() {
return invalidMap;
}
@Override
public void dispose(){
maps.dispose();
}
public boolean solid(int x, int y){
Tile tile = tile(x, y);
return tile == null || tile.solid();
}
public boolean passable(int x, int y){
Tile tile = tile(x, y);
return tile != null && tile.passable();
}
public boolean wallSolid(int x, int y){
Tile tile = tile(x, y);
return tile == null || tile.block().solid;
}
public boolean isAccessible(int x, int y){
return !wallSolid(x, y-1) || !wallSolid(x, y+1) || !wallSolid(x-1, y) ||!wallSolid(x+1, y);
}
public boolean floorBlends(int x, int y, Block block){
Tile tile = tile(x, y);
return tile == null || tile.floor().id <= block.id;
}
public Map getMap(){
return currentMap;
}
public int width(){
return tiles.length;
}
public int height(){
return tiles[0].length;
}
public Maps maps(){
return maps;
}
public int toPacked(int x, int y){
return x + y *width();
}
public BlockIndexer indexer(){
return indexer;
}
public Tile tile(int packed){
return tiles == null ? null : tile(packed % width(), packed / width());
}
public Tile tile(int x, int y){
if(tiles == null){
return null;
}
if(!Mathf.inBounds(x, y, tiles)) return null;
return tiles[x][y];
}
public Pathfinder pathfinder(){
return pathfinder;
}
public Tile rawTile(int x, int y){
return tiles[x][y];
}
public Tile tileWorld(float x, float y){
return tile(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize));
}
public boolean isInvalidMap(){
return invalidMap;
}
public int toTile(float coord){
return Mathf.scl2(coord, tilesize);
}
public Tile[][] getTiles(){
return tiles;
}
private void clearTileEntities(){
for(int x = 0; x < tiles.length; x ++){
for(int y = 0; y < tiles[0].length; y ++){
if(tiles[x][y] != null && tiles[x][y].entity != null){
tiles[x][y].entity.remove();
}
}
}
}
public boolean solid(int x, int y){
Tile tile = tile(x, y);
/**Resizes the tile array to the specified size and returns the resulting tile array.
* Only use for loading saves!*/
return tile == null || tile.solid();
}
public boolean passable(int x, int y){
Tile tile = tile(x, y);
return tile != null && tile.passable();
}
public boolean wallSolid(int x, int y){
Tile tile = tile(x, y);
return tile == null || tile.block().solid;
}
public boolean isAccessible(int x, int y){
return !wallSolid(x, y - 1) || !wallSolid(x, y + 1) || !wallSolid(x - 1, y) || !wallSolid(x + 1, y);
}
public boolean floorBlends(int x, int y, Block block){
Tile tile = tile(x, y);
return tile == null || tile.floor().id <= block.id;
}
public Map getMap(){
return currentMap;
}
public void setMap(Map map){
this.currentMap = map;
}
public int width(){
return tiles == null ? 0 : tiles.length;
}
public int height(){
return tiles == null ? 0 : tiles[0].length;
}
public int toPacked(int x, int y){
return x + y * width();
}
public Tile tile(int packed){
return tiles == null ? null : tile(packed % width(), packed / width());
}
public Tile tile(int x, int y){
if(tiles == null){
return null;
}
if(!Mathf.inBounds(x, y, tiles)) return null;
return tiles[x][y];
}
public Tile rawTile(int x, int y){
return tiles[x][y];
}
public Tile tileWorld(float x, float y){
return tile(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize));
}
public int toTile(float coord){
return Mathf.scl2(coord, tilesize);
}
public Tile[][] getTiles(){
return tiles;
}
private void clearTileEntities(){
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
if(tiles[x][y] != null && tiles[x][y].entity != null){
tiles[x][y].entity.remove();
}
}
}
}
/**
* Resizes the tile array to the specified size and returns the resulting tile array.
* Only use for loading saves!
*/
public Tile[][] createTiles(int width, int height){
if(tiles != null){
clearTileEntities();
@@ -158,181 +168,214 @@ public class World extends Module{
return tiles;
}
/**Call to signify the beginning of map loading.
* TileChangeEvents will not be fired until endMapLoad().*/
public void beginMapLoad(){
generating = true;
}
/**
* Call to signify the beginning of map loading.
* TileChangeEvents will not be fired until endMapLoad().
*/
public void beginMapLoad(){
generating = true;
}
/**Call to signify the end of map loading. Updates tile occlusions and sets up physics for the world.
* A WorldLoadEvent will be fire.*/
public void endMapLoad(){
for(int x = 0; x < tiles.length; x ++) {
for (int y = 0; y < tiles[0].length; y++) {
tiles[x][y].updateOcclusion();
}
}
/**
* Call to signify the end of map loading. Updates tile occlusions and sets up physics for the world.
* A WorldLoadEvent will be fire.
*/
public void endMapLoad(){
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
tiles[x][y].updateOcclusion();
EntityPhysics.resizeTree(0, 0, tiles.length * tilesize, tiles[0].length * tilesize);
if(tiles[x][y].entity != null){
tiles[x][y].entity.updateProximity();
}
}
}
generating = false;
Events.fire(WorldLoadEvent.class);
}
EntityPhysics.resizeTree(0, 0, tiles.length * tilesize, tiles[0].length * tilesize);
/**Loads up a procedural map. This does not call play(), but calls reset().*/
public void loadProceduralMap(){
Timers.mark();
Timers.mark();
generating = false;
Events.fire(WorldLoadEvent.class);
}
logic.reset();
/**
* Loads up a procedural map. This does not call play(), but calls reset().
*/
public void loadProceduralMap(){
Timers.mark();
Timers.mark();
beginMapLoad();
logic.reset();
int width = 400, height = 400;
beginMapLoad();
Tile[][] tiles = createTiles(width, height);
int width = 400, height = 400;
Map map = new Map("Generated Map", new MapMeta(0, new ObjectMap<>(), width, height, null), true, () -> null);
setMap(map);
Tile[][] tiles = createTiles(width, height);
EntityPhysics.resizeTree(0, 0, width * tilesize, height * tilesize);
Map map = new Map("Generated Map", new MapMeta(0, new ObjectMap<>(), width, height, null), true, () -> null);
setMap(map);
Timers.mark();
WorldGenerator.generateMap(tiles, Mathf.random(9999999));
Log.info("Time to generate base map: {0}", Timers.elapsed());
EntityPhysics.resizeTree(0, 0, width * tilesize, height * tilesize);
Log.info("Time to generate fully without additional events: {0}", Timers.elapsed());
Timers.mark();
WorldGenerator.generateMap(tiles, Mathf.random(9999999));
Log.info("Time to generate base map: {0}", Timers.elapsed());
endMapLoad();
Log.info("Time to generate fully without additional events: {0}", Timers.elapsed());
Log.info("Full time to generate: {0}", Timers.elapsed());
}
endMapLoad();
public void setMap(Map map){
this.currentMap = map;
}
public void loadMap(Map map){
loadMap(map, MathUtils.random(0, 999999));
}
public void loadMap(Map map, int seed){
beginMapLoad();
this.currentMap = map;
this.seed = seed;
Log.info("Full time to generate: {0}", Timers.elapsed());
}
int width = map.meta.width, height = map.meta.height;
public void loadMap(Map map){
loadMap(map, MathUtils.random(0, 999999));
}
createTiles(width, height);
EntityPhysics.resizeTree(0, 0, width * tilesize, height * tilesize);
public void loadMap(Map map, int seed){
beginMapLoad();
this.currentMap = map;
this.seed = seed;
WorldGenerator.loadTileData(tiles, MapIO.readTileData(map, true), map.meta.hasOreGen(), seed);
int width = map.meta.width, height = map.meta.height;
if(!headless && state.teams.get(players[0].getTeam()).cores.size == 0){
ui.showError("$text.map.nospawn");
threads.runDelay(() -> state.set(State.menu));
invalidMap = true;
}else{
invalidMap = false;
}
createTiles(width, height);
endMapLoad();
}
EntityPhysics.resizeTree(0, 0, width * tilesize, height * tilesize);
public int getSeed(){
return seed;
}
WorldGenerator.loadTileData(tiles, MapIO.readTileData(map, true), map.meta.hasOreGen(), seed);
public void notifyChanged(Tile tile){
if(!generating){
threads.runDelay(() -> Events.fire(TileChangeEvent.class, tile));
}
}
if(!headless && state.teams.get(players[0].getTeam()).cores.size == 0){
ui.showError("$text.map.nospawn");
threads.runDelay(() -> state.set(State.menu));
invalidMap = true;
}else{
invalidMap = false;
}
public void removeBlock(Tile tile){
if(!tile.block().isMultiblock() && !tile.isLinked()){
tile.setBlock(Blocks.air);
}else{
Tile target = tile.target();
Array<Tile> removals = target.getLinkedTiles(tempTiles);
for(Tile toremove : removals){
//note that setting a new block automatically unlinks it
if(toremove != null) toremove.setBlock(Blocks.air);
}
}
}
endMapLoad();
}
/**Raycast, but with world coordinates.*/
public GridPoint2 raycastWorld(float x, float y, float x2, float y2){
return raycast(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize),
Mathf.scl2(x2, tilesize), Mathf.scl2(y2, tilesize));
}
/**Input is in block coordinates, not world coordinates.
* @return null if no collisions found, block position otherwise.*/
public GridPoint2 raycast(int x0f, int y0f, int x1, int y1){
int x0 = x0f;
int y0 = y0f;
int dx = Math.abs(x1 - x0);
int dy = Math.abs(y1 - y0);
public int getSeed(){
return seed;
}
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
public void notifyChanged(Tile tile){
if(!generating){
threads.runDelay(() -> Events.fire(TileChangeEvent.class, tile));
}
}
int err = dx - dy;
int e2;
while(true){
public void removeBlock(Tile tile){
if(!tile.block().isMultiblock() && !tile.isLinked()){
tile.setBlock(Blocks.air);
}else{
Tile target = tile.target();
Array<Tile> removals = target.getLinkedTiles(tempTiles);
for(Tile toremove : removals){
//note that setting a new block automatically unlinks it
if(toremove != null) toremove.setBlock(Blocks.air);
}
}
}
if(!passable(x0, y0)){
return Tmp.g1.set(x0, y0);
}
if(x0 == x1 && y0 == y1) break;
public void setBlock(Tile tile, Block block, Team team){
tile.setBlock(block);
if(block.isMultiblock()){
int offsetx = -(block.size - 1) / 2;
int offsety = -(block.size - 1) / 2;
e2 = 2 * err;
if(e2 > -dy){
err = err - dy;
x0 = x0 + sx;
}
for(int dx = 0; dx < block.size; dx++){
for(int dy = 0; dy < block.size; dy++){
int worldx = dx + offsetx + tile.x;
int worldy = dy + offsety + tile.y;
if(!(worldx == tile.x && worldy == tile.y)){
Tile toplace = world.tile(worldx, worldy);
if(toplace != null){
toplace.setLinked((byte) (dx + offsetx), (byte) (dy + offsety));
toplace.setTeam(team);
}
}
}
}
}
}
if(e2 < dx){
err = err + dx;
y0 = y0 + sy;
}
}
return null;
}
/**
* Raycast, but with world coordinates.
*/
public GridPoint2 raycastWorld(float x, float y, float x2, float y2){
return raycast(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize),
Mathf.scl2(x2, tilesize), Mathf.scl2(y2, tilesize));
}
public void raycastEach(int x0f, int y0f, int x1, int y1, Raycaster cons){
int x0 = x0f;
int y0 = y0f;
int dx = Math.abs(x1 - x0);
int dy = Math.abs(y1 - y0);
/**
* Input is in block coordinates, not world coordinates.
*
* @return null if no collisions found, block position otherwise.
*/
public GridPoint2 raycast(int x0f, int y0f, int x1, int y1){
int x0 = x0f;
int y0 = y0f;
int dx = Math.abs(x1 - x0);
int dy = Math.abs(y1 - y0);
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int err = dx - dy;
int e2;
while(true){
int err = dx - dy;
int e2;
while(true){
if(cons.accept(x0, y0)) break;
if(x0 == x1 && y0 == y1) break;
if(!passable(x0, y0)){
return Tmp.g1.set(x0, y0);
}
if(x0 == x1 && y0 == y1) break;
e2 = 2 * err;
if(e2 > -dy){
err = err - dy;
x0 = x0 + sx;
}
e2 = 2 * err;
if(e2 > -dy){
err = err - dy;
x0 = x0 + sx;
}
if(e2 < dx){
err = err + dx;
y0 = y0 + sy;
}
}
}
if(e2 < dx){
err = err + dx;
y0 = y0 + sy;
}
}
return null;
}
public interface Raycaster{
boolean accept(int x, int y);
}
public void raycastEach(int x0f, int y0f, int x1, int y1, Raycaster cons){
int x0 = x0f;
int y0 = y0f;
int dx = Math.abs(x1 - x0);
int dy = Math.abs(y1 - y0);
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int err = dx - dy;
int e2;
while(true){
if(cons.accept(x0, y0)) break;
if(x0 == x1 && y0 == y1) break;
e2 = 2 * err;
if(e2 > -dy){
err = err - dy;
x0 = x0 + sx;
}
if(e2 < dx){
err = err + dx;
y0 = y0 + sy;
}
}
}
public interface Raycaster{
boolean accept(int x, int y);
}
}

View File

@@ -7,60 +7,66 @@ import io.anuke.mindustry.io.MapTileData.TileDataMarker;
import io.anuke.ucore.util.Bits;
public class DrawOperation{
/**Data to apply operation to.*/
private MapTileData data;
/**List of per-tile operations that occurred.*/
private Array<TileOperation> operations = new Array<>();
/**Checks for duplicate operations, useful for brushes.*/
private IntSet checks = new IntSet();
/**
* Data to apply operation to.
*/
private MapTileData data;
/**
* List of per-tile operations that occurred.
*/
private Array<TileOperation> operations = new Array<>();
/**
* Checks for duplicate operations, useful for brushes.
*/
private IntSet checks = new IntSet();
public DrawOperation(MapTileData data){
this.data = data;
}
public DrawOperation(MapTileData data){
this.data = data;
}
public boolean isEmpty(){
return operations.size == 0;
}
public boolean isEmpty(){
return operations.size == 0;
}
public boolean checkDuplicate(short x, short y){
int i = Bits.packInt(x, y);
if(checks.contains(i)) return true;
public boolean checkDuplicate(short x, short y){
int i = Bits.packInt(x, y);
if(checks.contains(i)) return true;
checks.add(i);
return false;
}
checks.add(i);
return false;
}
public void addOperation(TileOperation op){
operations.add(op);
}
public void addOperation(TileOperation op){
operations.add(op);
}
public void undo(MapEditor editor) {
for(int i = operations.size - 1; i >= 0; i --){
TileOperation op = operations.get(i);
data.position(op.x, op.y);
data.write(op.from);
editor.renderer().updatePoint(op.x, op.y);
}
}
public void undo(MapEditor editor){
for(int i = operations.size - 1; i >= 0; i--){
TileOperation op = operations.get(i);
data.position(op.x, op.y);
data.write(op.from);
editor.renderer().updatePoint(op.x, op.y);
}
}
public void redo(MapEditor editor) {
for(TileOperation op : operations){
data.position(op.x, op.y);
data.write(op.to);
editor.renderer().updatePoint(op.x, op.y);
}
}
public void redo(MapEditor editor){
for(TileOperation op : operations){
data.position(op.x, op.y);
data.write(op.to);
editor.renderer().updatePoint(op.x, op.y);
}
}
public static class TileOperation{
public short x, y;
public TileDataMarker from;
public TileDataMarker to;
public static class TileOperation{
public short x, y;
public TileDataMarker from;
public TileDataMarker to;
public TileOperation(short x, short y, TileDataMarker from, TileDataMarker to) {
this.x = x;
this.y = y;
this.from = from;
this.to = to;
}
}
public TileOperation(short x, short y, TileDataMarker from, TileDataMarker to){
this.x = x;
this.y = y;
this.from = from;
this.to = to;
}
}
}

View File

@@ -12,132 +12,132 @@ import io.anuke.ucore.util.Bits;
import static io.anuke.mindustry.Vars.ui;
public enum EditorTool{
pick{
public void touched(MapEditor editor, int x, int y){
byte bf = editor.getMap().read(x, y, DataPosition.floor);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
byte link = editor.getMap().read(x, y, DataPosition.link);
pick{
public void touched(MapEditor editor, int x, int y){
byte bf = editor.getMap().read(x, y, DataPosition.floor);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
byte link = editor.getMap().read(x, y, DataPosition.link);
if(link != 0){
x -= (Bits.getLeftByte(link) - 8);
y -= (Bits.getRightByte(link) - 8);
bf = editor.getMap().read(x, y, DataPosition.floor);
bw = editor.getMap().read(x, y, DataPosition.wall);
}
if(link != 0){
x -= (Bits.getLeftByte(link) - 8);
y -= (Bits.getRightByte(link) - 8);
bf = editor.getMap().read(x, y, DataPosition.floor);
bw = editor.getMap().read(x, y, DataPosition.wall);
}
Block block = Block.getByID(bw == 0 ? bf : bw);
editor.setDrawBlock(block);
ui.editor.updateSelectedBlock();
}
},
pencil{
{
edit = true;
draggable = true;
}
Block block = Block.getByID(bw == 0 ? bf : bw);
editor.setDrawBlock(block);
ui.editor.updateSelectedBlock();
}
},
pencil{
{
edit = true;
draggable = true;
}
@Override
public void touched(MapEditor editor, int x, int y){
editor.draw(x, y);
}
},
eraser{
{
edit = true;
draggable = true;
}
@Override
public void touched(MapEditor editor, int x, int y){
editor.draw(x, y);
}
},
eraser{
{
edit = true;
draggable = true;
}
@Override
public void touched(MapEditor editor, int x, int y){
editor.draw(x, y, Blocks.air);
}
},
elevation{
{
edit = true;
draggable = true;
}
@Override
public void touched(MapEditor editor, int x, int y){
editor.draw(x, y, Blocks.air);
}
},
elevation{
{
edit = true;
draggable = true;
}
@Override
public void touched(MapEditor editor, int x, int y){
editor.elevate(x, y);
}
},
line{
{
@Override
public void touched(MapEditor editor, int x, int y){
editor.elevate(x, y);
}
},
line{
{
}
},
fill{
{
edit = true;
}
public void touched(MapEditor editor, int x, int y){
if(editor.getDrawBlock().isMultiblock()){
//don't fill multiblocks, thanks
pencil.touched(editor, x, y);
return;
}
}
},
fill{
{
edit = true;
}
boolean floor = editor.getDrawBlock() instanceof Floor;
public void touched(MapEditor editor, int x, int y){
if(editor.getDrawBlock().isMultiblock()){
//don't fill multiblocks, thanks
pencil.touched(editor, x, y);
return;
}
byte bf = editor.getMap().read(x, y, DataPosition.floor);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
boolean synth = editor.getDrawBlock().synthetic();
byte brt = Bits.packByte((byte)editor.getDrawRotation(), (byte)editor.getDrawTeam().ordinal());
boolean floor = editor.getDrawBlock() instanceof Floor;
byte dest = floor ? bf: bw;
byte draw = (byte)editor.getDrawBlock().id;
byte bf = editor.getMap().read(x, y, DataPosition.floor);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
boolean synth = editor.getDrawBlock().synthetic();
byte brt = Bits.packByte((byte) editor.getDrawRotation(), (byte) editor.getDrawTeam().ordinal());
int width = editor.getMap().width();
int height = editor.getMap().height();
byte dest = floor ? bf : bw;
byte draw = (byte) editor.getDrawBlock().id;
IntSet set = new IntSet();
IntArray points = new IntArray();
points.add(asInt(x, y, editor.getMap().width()));
int width = editor.getMap().width();
int height = editor.getMap().height();
while(points.size != 0){
int pos = points.pop();
int px = pos % width;
int py = pos / width;
set.add(pos);
IntSet set = new IntSet();
IntArray points = new IntArray();
points.add(asInt(x, y, editor.getMap().width()));
byte nbf = editor.getMap().read(px, py, DataPosition.floor);
byte nbw = editor.getMap().read(px, py, DataPosition.wall);
while(points.size != 0){
int pos = points.pop();
int px = pos % width;
int py = pos / width;
set.add(pos);
if((floor ? nbf : nbw) == dest){
TileDataMarker prev = editor.getPrev(px, py, false);
byte nbf = editor.getMap().read(px, py, DataPosition.floor);
byte nbw = editor.getMap().read(px, py, DataPosition.wall);
if(floor) {
editor.getMap().write(px, py, DataPosition.floor, draw);
}else {
editor.getMap().write(px, py, DataPosition.wall, draw);
}
if((floor ? nbf : nbw) == dest){
TileDataMarker prev = editor.getPrev(px, py, false);
if(synth){
editor.getMap().write(px, py, DataPosition.rotationTeam, brt);
}
if(floor){
editor.getMap().write(px, py, DataPosition.floor, draw);
}else{
editor.getMap().write(px, py, DataPosition.wall, draw);
}
if(px > 0 && !set.contains(asInt(px - 1, py, width))) points.add(asInt(px - 1, py, width));
if(py > 0 && !set.contains(asInt(px, py - 1, width))) points.add(asInt(px, py - 1, width));
if(px < width - 1 && !set.contains(asInt(px + 1, py, width))) points.add(asInt(px + 1, py, width));
if(py < height - 1 && !set.contains(asInt(px, py + 1, width))) points.add(asInt(px, py + 1, width));
if(synth){
editor.getMap().write(px, py, DataPosition.rotationTeam, brt);
}
editor.onWrite(px, py, prev);
}
}
}
int asInt(int x, int y, int width){
return x+y*width;
}
},
zoom;
if(px > 0 && !set.contains(asInt(px - 1, py, width))) points.add(asInt(px - 1, py, width));
if(py > 0 && !set.contains(asInt(px, py - 1, width))) points.add(asInt(px, py - 1, width));
if(px < width - 1 && !set.contains(asInt(px + 1, py, width))) points.add(asInt(px + 1, py, width));
if(py < height - 1 && !set.contains(asInt(px, py + 1, width))) points.add(asInt(px, py + 1, width));
boolean edit, draggable;
public void touched(MapEditor editor, int x, int y){
}
editor.onWrite(px, py, prev);
}
}
}
int asInt(int x, int y, int width){
return x + y * width;
}
},
zoom;
boolean edit, draggable;
public void touched(MapEditor editor, int x, int y){
}
}

View File

@@ -14,265 +14,267 @@ import io.anuke.ucore.util.Bits;
import io.anuke.ucore.util.Mathf;
public class MapEditor{
public static final int minMapSize = 128, maxMapSize = 512;
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15};
private MapTileData map;
private ObjectMap<String, String> tags = new ObjectMap<>();
private MapRenderer renderer = new MapRenderer(this);
public static final int minMapSize = 128, maxMapSize = 512;
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15};
private int brushSize = 1;
private byte elevation;
private int rotation;
private Block drawBlock = Blocks.stone;
private Team drawTeam = Team.none;
public MapEditor(){
private MapTileData map;
private ObjectMap<String, String> tags = new ObjectMap<>();
private MapRenderer renderer = new MapRenderer(this);
}
public MapTileData getMap(){
return map;
}
private int brushSize = 1;
private byte elevation;
private int rotation;
private Block drawBlock = Blocks.stone;
private Team drawTeam = Team.none;
public ObjectMap<String, String> getTags() {
return tags;
}
public MapEditor(){
public void beginEdit(MapTileData map, ObjectMap<String, String> tags){
this.map = map;
this.brushSize = 1;
this.tags = tags;
}
for (int x = 0; x < map.width(); x++) {
for (int y = 0; y < map.height(); y++) {
map.write(x, y, DataPosition.floor, (byte)Blocks.stone.id);
}
}
public MapTileData getMap(){
return map;
}
drawBlock = Blocks.stone;
renderer.resize(map.width(), map.height());
}
public ObjectMap<String, String> getTags(){
return tags;
}
public void setDrawElevation(int elevation){
this.elevation = (byte)elevation;
}
public void beginEdit(MapTileData map, ObjectMap<String, String> tags, boolean clear){
this.map = map;
this.brushSize = 1;
this.tags = tags;
public byte getDrawElevation(){
return elevation;
}
if(clear){
for(int x = 0; x < map.width(); x++){
for(int y = 0; y < map.height(); y++){
map.write(x, y, DataPosition.floor, (byte) Blocks.stone.id);
}
}
}
public int getDrawRotation(){
return rotation;
}
drawBlock = Blocks.stone;
renderer.resize(map.width(), map.height());
}
public void setDrawRotation(int rotation){
this.rotation = rotation;
}
public byte getDrawElevation(){
return elevation;
}
public void setDrawTeam(Team team){
this.drawTeam = team;
}
public void setDrawElevation(int elevation){
this.elevation = (byte) elevation;
}
public Team getDrawTeam() {
return drawTeam;
}
public int getDrawRotation(){
return rotation;
}
public Block getDrawBlock(){
return drawBlock;
}
public void setDrawBlock(Block block){
this.drawBlock = block;
}
public void setBrushSize(int size){
this.brushSize = size;
}
public void setDrawRotation(int rotation){
this.rotation = rotation;
}
public int getBrushSize() {
return brushSize;
}
public Team getDrawTeam(){
return drawTeam;
}
public void draw(int x, int y){
draw(x, y, drawBlock);
}
public void setDrawTeam(Team team){
this.drawTeam = team;
}
public void draw(int x, int y, Block drawBlock){
if(x < 0 || y < 0 || x >= map.width() || y >= map.height()){
return;
}
public Block getDrawBlock(){
return drawBlock;
}
byte writeID = (byte)drawBlock.id;
byte partID = (byte)Blocks.blockpart.id;
byte rotationTeam = Bits.packByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0);
public void setDrawBlock(Block block){
this.drawBlock = block;
}
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
public int getBrushSize(){
return brushSize;
}
if(drawBlock.isMultiblock()) {
public void setBrushSize(int size){
this.brushSize = size;
}
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
public void draw(int x, int y){
draw(x, y, drawBlock);
}
for(int i = 0; i < 2; i ++){
for (int dx = 0; dx < drawBlock.size; dx++) {
for (int dy = 0; dy < drawBlock.size; dy++) {
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
public void draw(int x, int y, Block drawBlock){
if(x < 0 || y < 0 || x >= map.width() || y >= map.height()){
return;
}
if (Mathf.inBounds(worldx, worldy, map.width(), map.height())) {
TileDataMarker prev = getPrev(worldx, worldy, false);
byte writeID = (byte) drawBlock.id;
byte partID = (byte) Blocks.blockpart.id;
byte rotationTeam = Bits.packByte(drawBlock.rotate ? (byte) rotation : 0, drawBlock.synthetic() ? (byte) drawTeam.ordinal() : 0);
if(i == 1) {
map.write(worldx, worldy, DataPosition.wall, partID);
map.write(worldx, worldy, DataPosition.rotationTeam, rotationTeam);
map.write(worldx, worldy, DataPosition.link, Bits.packByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
}else{
byte link = map.read(worldx, worldy, DataPosition.link);
byte block = map.read(worldx, worldy, DataPosition.wall);
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
if (link != 0) {
removeLinked(worldx - (Bits.getLeftByte(link) - 8), worldy - (Bits.getRightByte(link) - 8));
}else if(Block.getByID(block).isMultiblock()){
removeLinked(worldx, worldy);
}
}
if(drawBlock.isMultiblock()){
onWrite(worldx, worldy, prev);
}
}
}
}
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
TileDataMarker prev = getPrev(x, y, false);
for(int i = 0; i < 2; i++){
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
map.write(x, y, DataPosition.wall, writeID);
map.write(x, y, DataPosition.link, (byte)0);
map.write(x, y, DataPosition.rotationTeam, rotationTeam);
if(Mathf.inBounds(worldx, worldy, map.width(), map.height())){
TileDataMarker prev = getPrev(worldx, worldy, false);
onWrite(x, y, prev);
}else{
if(i == 1){
map.write(worldx, worldy, DataPosition.wall, partID);
map.write(worldx, worldy, DataPosition.rotationTeam, rotationTeam);
map.write(worldx, worldy, DataPosition.link, Bits.packByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
}else{
byte link = map.read(worldx, worldy, DataPosition.link);
byte block = map.read(worldx, worldy, DataPosition.wall);
for (int rx = -brushSize; rx <= brushSize; rx++) {
for (int ry = -brushSize; ry <= brushSize; ry++) {
if (Mathf.dst(rx, ry) <= brushSize - 0.5f) {
int wx = x + rx, wy = y + ry;
if(link != 0){
removeLinked(worldx - (Bits.getLeftByte(link) - 8), worldy - (Bits.getRightByte(link) - 8));
}else if(Block.getByID(block).isMultiblock()){
removeLinked(worldx, worldy);
}
}
if (wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height()) {
continue;
}
onWrite(worldx, worldy, prev);
}
}
}
}
TileDataMarker prev = getPrev(wx, wy, true);
TileDataMarker prev = getPrev(x, y, false);
if(!isfloor) {
byte link = map.read(wx, wy, DataPosition.link);
map.write(x, y, DataPosition.wall, writeID);
map.write(x, y, DataPosition.link, (byte) 0);
map.write(x, y, DataPosition.rotationTeam, rotationTeam);
if (link != 0) {
removeLinked(wx - (Bits.getLeftByte(link) - 8), wy - (Bits.getRightByte(link) - 8));
}
}
onWrite(x, y, prev);
}else{
if(isfloor){
map.write(wx, wy, DataPosition.floor, writeID);
map.write(wx, wy, DataPosition.elevation, elevation);
}else{
map.write(wx, wy, DataPosition.wall, writeID);
map.write(wx, wy, DataPosition.link, (byte)0);
map.write(wx, wy, DataPosition.rotationTeam, rotationTeam);
}
for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f){
int wx = x + rx, wy = y + ry;
onWrite(x + rx, y + ry, prev);
}
}
}
}
}
if(wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height()){
continue;
}
public void elevate(int x, int y){
if(x < 0 || y < 0 || x >= map.width() || y >= map.height()){
return;
}
TileDataMarker prev = getPrev(wx, wy, true);
for (int rx = -brushSize; rx <= brushSize; rx++) {
for (int ry = -brushSize; ry <= brushSize; ry++) {
if (Mathf.dst(rx, ry) <= brushSize - 0.5f) {
int wx = x + rx, wy = y + ry;
if(!isfloor){
byte link = map.read(wx, wy, DataPosition.link);
if (wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height()) {
continue;
}
if(link != 0){
removeLinked(wx - (Bits.getLeftByte(link) - 8), wy - (Bits.getRightByte(link) - 8));
}
}
TileDataMarker prev = getPrev(wx, wy, true);
if(isfloor){
map.write(wx, wy, DataPosition.floor, writeID);
map.write(wx, wy, DataPosition.elevation, elevation);
}else{
map.write(wx, wy, DataPosition.wall, writeID);
map.write(wx, wy, DataPosition.link, (byte) 0);
map.write(wx, wy, DataPosition.rotationTeam, rotationTeam);
}
map.write(wx, wy, DataPosition.elevation, elevation);
onWrite(x + rx, y + ry, prev);
}
}
}
}
}
onWrite(x + rx, y + ry, prev);
}
}
}
}
public void elevate(int x, int y){
if(x < 0 || y < 0 || x >= map.width() || y >= map.height()){
return;
}
private void removeLinked(int x, int y){
Block block = Block.getByID(map.read(x, y, DataPosition.wall));
for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f){
int wx = x + rx, wy = y + ry;
int offsetx = -(block.size-1)/2;
int offsety = -(block.size-1)/2;
for(int dx = 0; dx < block.size; dx ++){
for(int dy = 0; dy < block.size; dy ++){
int worldx = x + dx + offsetx, worldy = y + dy + offsety;
if(Mathf.inBounds(worldx, worldy, map.width(), map.height())){
TileDataMarker prev = getPrev(worldx, worldy, false);
if(wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height()){
continue;
}
map.write(worldx, worldy, DataPosition.link, (byte)0);
map.write(worldx, worldy, DataPosition.rotationTeam, (byte)0);
map.write(worldx, worldy, DataPosition.wall, (byte)0);
TileDataMarker prev = getPrev(wx, wy, true);
onWrite(worldx, worldy, prev);
}
}
}
}
map.write(wx, wy, DataPosition.elevation, elevation);
boolean checkDupes(int x, int y){
return Vars.ui.editor.getView().checkForDuplicates((short) x, (short) y);
}
onWrite(x + rx, y + ry, prev);
}
}
}
}
void onWrite(int x, int y, TileDataMarker previous){
if(previous == null){
renderer.updatePoint(x, y);
return;
}
private void removeLinked(int x, int y){
Block block = Block.getByID(map.read(x, y, DataPosition.wall));
TileDataMarker current = map.new TileDataMarker();
map.position(x, y);
map.read(current);
int offsetx = -(block.size - 1) / 2;
int offsety = -(block.size - 1) / 2;
for(int dx = 0; dx < block.size; dx++){
for(int dy = 0; dy < block.size; dy++){
int worldx = x + dx + offsetx, worldy = y + dy + offsety;
if(Mathf.inBounds(worldx, worldy, map.width(), map.height())){
TileDataMarker prev = getPrev(worldx, worldy, false);
Vars.ui.editor.getView().addTileOp(new TileOperation((short) x, (short) y, previous, current));
renderer.updatePoint(x, y);
}
map.write(worldx, worldy, DataPosition.link, (byte) 0);
map.write(worldx, worldy, DataPosition.rotationTeam, (byte) 0);
map.write(worldx, worldy, DataPosition.wall, (byte) 0);
TileDataMarker getPrev(int x, int y, boolean checkDupes){
if(checkDupes && checkDupes(x, y)){
return null;
}else{
TileDataMarker marker = map.newDataMarker();
map.position(x, y);
map.read(marker);
return marker;
}
}
onWrite(worldx, worldy, prev);
}
}
}
}
public MapRenderer renderer() {
return renderer;
}
boolean checkDupes(int x, int y){
return Vars.ui.editor.getView().checkForDuplicates((short) x, (short) y);
}
public void resize(int width, int height){
map = new MapTileData(width, height);
for (int x = 0; x < map.width(); x++) {
for (int y = 0; y < map.height(); y++) {
map.write(x, y, DataPosition.floor, (byte)Blocks.stone.id);
}
}
renderer.resize(width, height);
}
void onWrite(int x, int y, TileDataMarker previous){
if(previous == null){
renderer.updatePoint(x, y);
return;
}
TileDataMarker current = map.new TileDataMarker();
map.position(x, y);
map.read(current);
Vars.ui.editor.getView().addTileOp(new TileOperation((short) x, (short) y, previous, current));
renderer.updatePoint(x, y);
}
TileDataMarker getPrev(int x, int y, boolean checkDupes){
if(checkDupes && checkDupes(x, y)){
return null;
}else{
TileDataMarker marker = map.newDataMarker();
map.position(x, y);
map.read(marker);
return marker;
}
}
public MapRenderer renderer(){
return renderer;
}
public void resize(int width, int height){
map = new MapTileData(width, height);
for(int x = 0; x < map.width(); x++){
for(int y = 0; y < map.height(); y++){
map.write(x, y, DataPosition.floor, (byte) Blocks.stone.id);
}
}
renderer.resize(width, height);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,68 +14,68 @@ import io.anuke.ucore.scene.ui.layout.Table;
import static io.anuke.mindustry.Vars.world;
public class MapLoadDialog extends FloatingDialog{
private Map selected = null;
private Map selected = null;
public MapLoadDialog(Consumer<Map> loader) {
super("$text.editor.loadmap");
public MapLoadDialog(Consumer<Map> loader){
super("$text.editor.loadmap");
shown(this::rebuild);
rebuild();
shown(this::rebuild);
rebuild();
TextButton button = new TextButton("$text.load");
button.setDisabled(() -> selected == null);
button.clicked(() -> {
if (selected != null) {
loader.accept(selected);
hide();
}
});
TextButton button = new TextButton("$text.load");
button.setDisabled(() -> selected == null);
button.clicked(() -> {
if(selected != null){
loader.accept(selected);
hide();
}
});
buttons().defaults().size(200f, 50f);
buttons().addButton("$text.cancel", this::hide);
buttons().add(button);
}
buttons().defaults().size(200f, 50f);
buttons().addButton("$text.cancel", this::hide);
buttons().add(button);
}
public void rebuild(){
content().clear();
if(world.maps().all().size > 0){
selected = world.maps().all().first();
}
public void rebuild(){
content().clear();
if(world.maps().all().size > 0){
selected = world.maps().all().first();
}
ButtonGroup<TextButton> group = new ButtonGroup<>();
ButtonGroup<TextButton> group = new ButtonGroup<>();
int maxcol = 3;
int maxcol = 3;
int i = 0;
int i = 0;
Table table = new Table();
table.defaults().size(200f, 90f).pad(4f);
table.margin(10f);
Table table = new Table();
table.defaults().size(200f, 90f).pad(4f);
table.margin(10f);
ScrollPane pane = new ScrollPane(table, "horizontal");
pane.setFadeScrollBars(false);
ScrollPane pane = new ScrollPane(table, "horizontal");
pane.setFadeScrollBars(false);
for (Map map : world.maps().all()) {
for(Map map : world.maps().all()){
TextButton button = new TextButton(map.getDisplayName(), "toggle");
button.add(new BorderImage(map.texture, 2f)).size(16 * 4f);
button.getCells().reverse();
button.clicked(() -> selected = map);
button.getLabelCell().grow().left().padLeft(5f);
group.add(button);
table.add(button);
if (++i % maxcol == 0) table.row();
}
TextButton button = new TextButton(map.getDisplayName(), "toggle");
button.add(new BorderImage(map.texture, 2f)).size(16 * 4f);
button.getCells().reverse();
button.clicked(() -> selected = map);
button.getLabelCell().grow().left().padLeft(5f);
group.add(button);
table.add(button);
if(++i % maxcol == 0) table.row();
}
if(world.maps().all().size == 0){
pane.setStyle(Core.skin.get("clear", ScrollPaneStyle.class));
table.add("$text.maps.none").center();
}else {
content().add("$text.editor.loadmap");
}
if(world.maps().all().size == 0){
pane.setStyle(Core.skin.get("clear", ScrollPaneStyle.class));
table.add("$text.maps.none").center();
}else{
content().add("$text.editor.loadmap");
}
content().row();
content().add(pane);
}
content().row();
content().add(pane);
}
}

View File

@@ -34,18 +34,18 @@ public class MapRenderer implements Disposable{
public void resize(int width, int height){
if(chunks != null){
for(int x = 0; x < chunks.length; x ++){
for(int y = 0; y < chunks[0].length; y ++){
for(int x = 0; x < chunks.length; x++){
for(int y = 0; y < chunks[0].length; y++){
chunks[x][y].dispose();
}
}
}
chunks = new IndexedRenderer[(int)Math.ceil((float)width/chunksize)][(int)Math.ceil((float)height/chunksize )];
chunks = new IndexedRenderer[(int) Math.ceil((float) width / chunksize)][(int) Math.ceil((float) height / chunksize)];
for(int x = 0; x < chunks.length; x ++){
for(int y = 0; y < chunks[0].length; y ++){
chunks[x][y] = new IndexedRenderer(chunksize*chunksize*2);
for(int x = 0; x < chunks.length; x++){
for(int y = 0; y < chunks[0].length; y++){
chunks[x][y] = new IndexedRenderer(chunksize * chunksize * 2);
}
}
this.width = width;
@@ -69,8 +69,8 @@ public class MapRenderer implements Disposable{
updates.addAll(delayedUpdates);
delayedUpdates.clear();
for(int x = 0; x < chunks.length; x ++){
for(int y = 0; y < chunks[0].length; y ++){
for(int x = 0; x < chunks.length; x++){
for(int y = 0; y < chunks[0].length; y++){
IndexedRenderer mesh = chunks[x][y];
mesh.getTransformMatrix().setToTranslation(tx, ty, 0).scl(tw / (width * tilesize),
@@ -86,19 +86,19 @@ public class MapRenderer implements Disposable{
public void updatePoint(int x, int y){
//TODO spread out over multiple frames?
updates.add(x + y*width);
updates.add(x + y * width);
}
public void updateAll(){
for(int x = 0; x < width; x ++){
for(int y = 0; y < height; y ++){
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
render(x, y);
}
}
}
private void render(int wx, int wy){
int x = wx/chunksize, y = wy/chunksize;
int x = wx / chunksize, y = wy / chunksize;
IndexedRenderer mesh = chunks[x][y];
//TileDataMarker data = editor.getMap().readAt(wx, wy);
byte bf = editor.getMap().read(wx, wy, DataPosition.floor);
@@ -111,19 +111,19 @@ public class MapRenderer implements Disposable{
Block floor = Block.getByID(bf);
Block wall = Block.getByID(bw);
int offsetx = -(wall.size-1)/2;
int offsety = -(wall.size-1)/2;
int offsetx = -(wall.size - 1) / 2;
int offsety = -(wall.size - 1) / 2;
TextureRegion region;
if(bw != 0) {
if(bw != 0){
region = wall.getEditorIcon();
if (wall.rotate) {
if(wall.rotate){
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize, region,
wx * tilesize + offsetx * tilesize, wy * tilesize + offsety * tilesize,
region.getRegionWidth(), region.getRegionHeight(), rotation * 90 - 90);
} else {
}else{
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize, region,
wx * tilesize + offsetx * tilesize, wy * tilesize + offsety * tilesize,
region.getRegionWidth(), region.getRegionHeight());
@@ -131,16 +131,16 @@ public class MapRenderer implements Disposable{
}else{
region = floor.getEditorIcon();
mesh.draw((wx % chunksize) + (wy % chunksize)*chunksize, region, wx * tilesize, wy * tilesize, 8, 8);
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize, region, wx * tilesize, wy * tilesize, 8, 8);
}
boolean check = checkElevation(elev, wx, wy);
if(wall.update || wall.destructible) {
if(wall.update || wall.destructible){
mesh.setColor(team.color);
region = Draw.region("block-border");
}else if(elev > 0 && check){
mesh.setColor(tmpColor.fromHsv((360f * elev/127f * 4f) % 360f, 0.5f + (elev / 4f) % 0.5f, 1f));
mesh.setColor(tmpColor.fromHsv((360f * elev / 127f * 4f) % 360f, 0.5f + (elev / 4f) % 0.5f, 1f));
region = Draw.region("block-elevation");
}else if(elev == -1){
region = Draw.region("block-slope");
@@ -148,8 +148,8 @@ public class MapRenderer implements Disposable{
region = Draw.region("clear");
}
mesh.draw((wx % chunksize) + (wy % chunksize)*chunksize + chunksize*chunksize, region,
wx * tilesize + offsetx*tilesize, wy * tilesize + offsety * tilesize,
mesh.draw((wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize, region,
wx * tilesize + offsetx * tilesize, wy * tilesize + offsety * tilesize,
region.getRegionWidth(), region.getRegionHeight());
mesh.setColor(Color.WHITE);
}
@@ -165,19 +165,19 @@ public class MapRenderer implements Disposable{
if(value < elev){
return true;
}else if(value > elev){
delayedUpdates.add(wx + wy*width);
delayedUpdates.add(wx + wy * width);
}
}
return false;
}
@Override
public void dispose() {
public void dispose(){
if(chunks == null){
return;
}
for(int x = 0; x < chunks.length; x ++){
for(int y = 0; y < chunks[0].length; y ++){
for(int x = 0; x < chunks.length; x++){
for(int y = 0; y < chunks[0].length; y++){
if(chunks[x][y] != null){
chunks[x][y].dispose();
}

View File

@@ -9,55 +9,55 @@ import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Mathf;
public class MapResizeDialog extends FloatingDialog{
int[] validMapSizes = {200, 300, 400, 500};
int width, height;
public MapResizeDialog(MapEditor editor, BiConsumer<Integer, Integer> cons){
super("$text.editor.resizemap");
shown(() -> {
content().clear();
MapTileData data = editor.getMap();
width = data.width();
height = data.height();
Table table = new Table();
for(boolean w : Mathf.booleans){
int curr = w ? data.width() : data.height();
int idx = 0;
for(int i = 0; i < validMapSizes.length; i ++) {
if (validMapSizes[i] == curr) idx = i;
}
table.add(w ? "$text.width": "$text.height").padRight(8f);
ButtonGroup<TextButton> group = new ButtonGroup<>();
for(int i = 0; i < validMapSizes.length; i ++){
int size = validMapSizes[i];
TextButton button = new TextButton(size + "", "toggle");
button.clicked(() -> {
if(w)
width = size;
else
height = size;
});
group.add(button);
if(i == idx) button.setChecked(true);
table.add(button).size(100f, 54f).pad(2f);
}
table.row();
}
content().row();
content().add(table);
});
buttons().defaults().size(200f, 50f);
buttons().addButton("$text.cancel", this::hide);
buttons().addButton("$text.editor.resize", () -> {
cons.accept(width, height);
hide();
});
}
int[] validMapSizes = {200, 300, 400, 500};
int width, height;
public MapResizeDialog(MapEditor editor, BiConsumer<Integer, Integer> cons){
super("$text.editor.resizemap");
shown(() -> {
content().clear();
MapTileData data = editor.getMap();
width = data.width();
height = data.height();
Table table = new Table();
for(boolean w : Mathf.booleans){
int curr = w ? data.width() : data.height();
int idx = 0;
for(int i = 0; i < validMapSizes.length; i++){
if(validMapSizes[i] == curr) idx = i;
}
table.add(w ? "$text.width" : "$text.height").padRight(8f);
ButtonGroup<TextButton> group = new ButtonGroup<>();
for(int i = 0; i < validMapSizes.length; i++){
int size = validMapSizes[i];
TextButton button = new TextButton(size + "", "toggle");
button.clicked(() -> {
if(w)
width = size;
else
height = size;
});
group.add(button);
if(i == idx) button.setChecked(true);
table.add(button).size(100f, 54f).pad(2f);
}
table.row();
}
content().row();
content().add(table);
});
buttons().defaults().size(200f, 50f);
buttons().addButton("$text.cancel", this::hide);
buttons().addButton("$text.editor.resize", () -> {
cons.accept(width, height);
hide();
});
}
}

View File

@@ -1,7 +1,7 @@
package io.anuke.mindustry.editor;
import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.scene.ui.TextButton;
@@ -11,65 +11,65 @@ import static io.anuke.mindustry.Vars.ui;
import static io.anuke.mindustry.Vars.world;
public class MapSaveDialog extends FloatingDialog{
private TextField field;
private Consumer<String> listener;
public MapSaveDialog(Consumer<String> cons){
super("$text.editor.savemap");
field = new TextField();
listener = cons;
Platform.instance.addDialog(field);
shown(() -> {
content().clear();
content().label(() ->{
Map map = world.maps().getByName(field.getText());
if(map != null){
if(map.custom){
return "$text.editor.overwrite";
}else{
return "$text.editor.failoverwrite";
}
}
return "";
}).colspan(2);
content().row();
content().add("$text.editor.mapname").padRight(14f);
content().add(field).size(220f, 48f);
});
buttons().defaults().size(200f, 50f).pad(2f);
buttons().addButton("$text.cancel", this::hide);
TextButton button = new TextButton("$text.save");
button.clicked(() -> {
if(!invalid()){
cons.accept(field.getText());
hide();
}
});
button.setDisabled(this::invalid);
buttons().add(button);
}
private TextField field;
private Consumer<String> listener;
public void save(){
if(!invalid()){
listener.accept(field.getText());
}else{
ui.showError("$text.editor.failoverwrite");
}
}
public void setFieldText(String text){
field.setText(text);
}
private boolean invalid(){
if(field.getText().isEmpty()){
return true;
}
Map map = world.maps().getByName(field.getText());
return map != null && !map.custom;
}
public MapSaveDialog(Consumer<String> cons){
super("$text.editor.savemap");
field = new TextField();
listener = cons;
Platform.instance.addDialog(field);
shown(() -> {
content().clear();
content().label(() -> {
Map map = world.maps().getByName(field.getText());
if(map != null){
if(map.custom){
return "$text.editor.overwrite";
}else{
return "$text.editor.failoverwrite";
}
}
return "";
}).colspan(2);
content().row();
content().add("$text.editor.mapname").padRight(14f);
content().add(field).size(220f, 48f);
});
buttons().defaults().size(200f, 50f).pad(2f);
buttons().addButton("$text.cancel", this::hide);
TextButton button = new TextButton("$text.save");
button.clicked(() -> {
if(!invalid()){
cons.accept(field.getText());
hide();
}
});
button.setDisabled(this::invalid);
buttons().add(button);
}
public void save(){
if(!invalid()){
listener.accept(field.getText());
}else{
ui.showError("$text.editor.failoverwrite");
}
}
public void setFieldText(String text){
field.setText(text);
}
private boolean invalid(){
if(field.getText().isEmpty()){
return true;
}
Map map = world.maps().getByName(field.getText());
return map != null && !map.custom;
}
}

View File

@@ -33,261 +33,261 @@ import static io.anuke.mindustry.Vars.mobile;
import static io.anuke.mindustry.Vars.ui;
public class MapView extends Element implements GestureListener{
private MapEditor editor;
private EditorTool tool = EditorTool.pencil;
private OperationStack stack = new OperationStack();
private DrawOperation op;
private Bresenham2 br = new Bresenham2();
private boolean updated = false;
private float offsetx, offsety;
private float zoom = 1f;
private boolean grid = false;
private GridImage image = new GridImage(0, 0);
private Vector2 vec = new Vector2();
private Rectangle rect = new Rectangle();
private Vector2[][] brushPolygons = new Vector2[MapEditor.brushSizes.length][0];
private MapEditor editor;
private EditorTool tool = EditorTool.pencil;
private OperationStack stack = new OperationStack();
private DrawOperation op;
private Bresenham2 br = new Bresenham2();
private boolean updated = false;
private float offsetx, offsety;
private float zoom = 1f;
private boolean grid = false;
private GridImage image = new GridImage(0, 0);
private Vector2 vec = new Vector2();
private Rectangle rect = new Rectangle();
private Vector2[][] brushPolygons = new Vector2[MapEditor.brushSizes.length][0];
private boolean drawing;
private int lastx, lasty;
private int startx, starty;
private float mousex, mousey;
private EditorTool lastTool;
private boolean drawing;
private int lastx, lasty;
private int startx, starty;
private float mousex, mousey;
private EditorTool lastTool;
public void setTool(EditorTool tool){
this.tool = tool;
}
public MapView(MapEditor editor){
this.editor = editor;
public EditorTool getTool() {
return tool;
}
public void clearStack(){
stack.clear();
//TODO clear und obuffer
}
public OperationStack getStack() {
return stack;
}
public void setGrid(boolean grid) {
this.grid = grid;
}
public boolean isGrid() {
return grid;
}
public void undo(){
if(stack.canUndo()){
stack.undo(editor);
}
}
public void redo(){
if(stack.canRedo()){
stack.redo(editor);
}
}
public void addTileOp(TileOperation t){
op.addOperation(t);
}
public boolean checkForDuplicates(short x, short y){
return op.checkDuplicate(x, y);
}
public MapView(MapEditor editor){
this.editor = editor;
for(int i = 0; i < MapEditor.brushSizes.length; i ++){
for(int i = 0; i < MapEditor.brushSizes.length; i++){
float size = MapEditor.brushSizes[i];
brushPolygons[i] = Geometry.pixelCircle(size, (index, x, y) -> Vector2.dst(x, y, index, index) <= index - 0.5f);
}
Inputs.addProcessor(0, new GestureDetector(20, 0.5f, 2, 0.15f, this));
setTouchable(Touchable.enabled);
addListener(new InputListener(){
@Override
public boolean mouseMoved (InputEvent event, float x, float y) {
mousex = x;
mousey = y;
Inputs.addProcessor(0, new GestureDetector(20, 0.5f, 2, 0.15f, this));
setTouchable(Touchable.enabled);
return false;
addListener(new InputListener(){
@Override
public boolean mouseMoved(InputEvent event, float x, float y){
mousex = x;
mousey = y;
return false;
}
@Override
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
if(pointer != 0){
return false;
}
if(!mobile && button != Buttons.LEFT && button != Buttons.MIDDLE){
return true;
}
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
if(pointer != 0){
return false;
}
if(button == Buttons.MIDDLE){
lastTool = tool;
tool = EditorTool.zoom;
}
if(!mobile && button != Buttons.LEFT && button != Buttons.MIDDLE){
return true;
}
mousex = x;
mousey = y;
if(button == Buttons.MIDDLE){
lastTool = tool;
tool = EditorTool.zoom;
}
op = new DrawOperation(editor.getMap());
mousex = x;
mousey = y;
updated = false;
op = new DrawOperation(editor.getMap());
GridPoint2 p = project(x, y);
lastx = p.x;
lasty = p.y;
startx = p.x;
starty = p.y;
tool.touched(editor, p.x, p.y);
if(tool.edit){
updated = true;
ui.editor.resetSaved();
}
updated = false;
drawing = true;
return true;
}
@Override
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
if(!mobile && button != Buttons.LEFT && button != Buttons.MIDDLE){
return;
}
GridPoint2 p = project(x, y);
lastx = p.x;
lasty = p.y;
startx = p.x;
starty = p.y;
tool.touched(editor, p.x, p.y);
drawing = false;
if(tool.edit){
updated = true;
ui.editor.resetSaved();
}
GridPoint2 p = project(x, y);
drawing = true;
return true;
}
if(tool == EditorTool.line){
ui.editor.resetSaved();
Array<GridPoint2> points = br.line(startx, starty, p.x, p.y);
for(GridPoint2 point : points){
editor.draw(point.x, point.y);
}
updated = true;
}
@Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
if(!mobile && button != Buttons.LEFT && button != Buttons.MIDDLE){
return;
}
if(op != null && updated){
if(!op.isEmpty()){
stack.add(op);
}
op = null;
}
drawing = false;
if(lastTool != null){
tool = lastTool;
lastTool = null;
}
GridPoint2 p = project(x, y);
}
@Override
public void touchDragged (InputEvent event, float x, float y, int pointer) {
mousex = x;
mousey = y;
if(tool == EditorTool.line){
ui.editor.resetSaved();
Array<GridPoint2> points = br.line(startx, starty, p.x, p.y);
for(GridPoint2 point : points){
editor.draw(point.x, point.y);
}
updated = true;
}
GridPoint2 p = project(x, y);
if(drawing && tool.draggable){
ui.editor.resetSaved();
Array<GridPoint2> points = br.line(lastx, lasty, p.x, p.y);
for(GridPoint2 point : points){
tool.touched(editor, point.x, point.y);
}
updated = true;
}
lastx = p.x;
lasty = p.y;
}
});
}
@Override
public void act(float delta){
super.act(delta);
if(op != null && updated){
if(!op.isEmpty()){
stack.add(op);
}
op = null;
}
if(Core.scene.getKeyboardFocus() == null || !(Core.scene.getKeyboardFocus() instanceof TextField) &&
!Inputs.keyDown(io.anuke.ucore.input.Input.CONTROL_LEFT)) {
float ax = Inputs.getAxis("move_x");
float ay = Inputs.getAxis("move_y");
offsetx -= ax * 15f / zoom;
offsety -= ay * 15f / zoom;
}
if(lastTool != null){
tool = lastTool;
lastTool = null;
}
if(ui.editor.hasPane()) return;
zoom += Inputs.scroll()/10f * zoom;
clampZoom();
}
private void clampZoom(){
zoom = Mathf.clamp(zoom, 0.2f, 12f);
}
private GridPoint2 project(float x, float y){
float ratio = 1f / ((float)editor.getMap().width() / editor.getMap().height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
x = (x - getWidth()/2 + sclwidth/2 - offsetx*zoom) / sclwidth * editor.getMap().width();
y = (y - getHeight()/2 + sclheight/2 - offsety*zoom) / sclheight * editor.getMap().height();
}
if(editor.getDrawBlock().size % 2 == 0 && tool != EditorTool.eraser){
return Tmp.g1.set((int)(x - 0.5f), (int)(y - 0.5f));
}else{
return Tmp.g1.set((int)x, (int)y);
}
}
@Override
public void touchDragged(InputEvent event, float x, float y, int pointer){
mousex = x;
mousey = y;
private Vector2 unproject(int x, int y){
float ratio = 1f / ((float)editor.getMap().width() / editor.getMap().height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
float px = ((float)x / editor.getMap().width()) * sclwidth + offsetx*zoom - sclwidth/2 + getWidth()/2;
float py = ((float)(y) / editor.getMap().height()) * sclheight
+ offsety*zoom - sclheight/2 + getHeight()/2;
return vec.set(px, py);
}
GridPoint2 p = project(x, y);
@Override
public void draw(Batch batch, float alpha){
float ratio = 1f / ((float)editor.getMap().width() / editor.getMap().height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
float centerx = x + width/2 + offsetx * zoom;
float centery = y + height/2 + offsety * zoom;
if(drawing && tool.draggable){
ui.editor.resetSaved();
Array<GridPoint2> points = br.line(lastx, lasty, p.x, p.y);
for(GridPoint2 point : points){
tool.touched(editor, point.x, point.y);
}
updated = true;
}
lastx = p.x;
lasty = p.y;
}
});
}
image.setImageSize(editor.getMap().width(), editor.getMap().height());
batch.flush();
boolean pop = ScissorStack.pushScissors(rect.set(x, y, width, height));
public EditorTool getTool(){
return tool;
}
Draw.color(Color.LIGHT_GRAY);
Lines.stroke(-2f);
Lines.rect(centerx - sclwidth/2 - 1, centery - sclheight/2 - 1, sclwidth + 2, sclheight + 2);
editor.renderer().draw(centerx - sclwidth/2, centery - sclheight/2, sclwidth, sclheight);
Draw.reset();
public void setTool(EditorTool tool){
this.tool = tool;
}
if(grid){
Draw.color(Color.GRAY);
image.setBounds(centerx - sclwidth/2, centery - sclheight/2, sclwidth, sclheight);
image.draw(batch, alpha);
Draw.color();
}
public void clearStack(){
stack.clear();
//TODO clear und obuffer
}
public OperationStack getStack(){
return stack;
}
public boolean isGrid(){
return grid;
}
public void setGrid(boolean grid){
this.grid = grid;
}
public void undo(){
if(stack.canUndo()){
stack.undo(editor);
}
}
public void redo(){
if(stack.canRedo()){
stack.redo(editor);
}
}
public void addTileOp(TileOperation t){
op.addOperation(t);
}
public boolean checkForDuplicates(short x, short y){
return op.checkDuplicate(x, y);
}
@Override
public void act(float delta){
super.act(delta);
if(Core.scene.getKeyboardFocus() == null || !(Core.scene.getKeyboardFocus() instanceof TextField) &&
!Inputs.keyDown(io.anuke.ucore.input.Input.CONTROL_LEFT)){
float ax = Inputs.getAxis("move_x");
float ay = Inputs.getAxis("move_y");
offsetx -= ax * 15f / zoom;
offsety -= ay * 15f / zoom;
}
if(ui.editor.hasPane()) return;
zoom += Inputs.scroll() / 10f * zoom;
clampZoom();
}
private void clampZoom(){
zoom = Mathf.clamp(zoom, 0.2f, 12f);
}
private GridPoint2 project(float x, float y){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.getMap().width();
y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.getMap().height();
if(editor.getDrawBlock().size % 2 == 0 && tool != EditorTool.eraser){
return Tmp.g1.set((int) (x - 0.5f), (int) (y - 0.5f));
}else{
return Tmp.g1.set((int) x, (int) y);
}
}
private Vector2 unproject(int x, int y){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
float px = ((float) x / editor.getMap().width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
float py = ((float) (y) / editor.getMap().height()) * sclheight
+ offsety * zoom - sclheight / 2 + getHeight() / 2;
return vec.set(px, py);
}
@Override
public void draw(Batch batch, float alpha){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height());
float size = Math.min(width, height);
float sclwidth = size * zoom;
float sclheight = size * zoom * ratio;
float centerx = x + width / 2 + offsetx * zoom;
float centery = y + height / 2 + offsety * zoom;
image.setImageSize(editor.getMap().width(), editor.getMap().height());
batch.flush();
boolean pop = ScissorStack.pushScissors(rect.set(x, y, width, height));
Draw.color(Color.LIGHT_GRAY);
Lines.stroke(-2f);
Lines.rect(centerx - sclwidth / 2 - 1, centery - sclheight / 2 - 1, sclwidth + 2, sclheight + 2);
editor.renderer().draw(centerx - sclwidth / 2, centery - sclheight / 2, sclwidth, sclheight);
Draw.reset();
if(grid){
Draw.color(Color.GRAY);
image.setBounds(centerx - sclwidth / 2, centery - sclheight / 2, sclwidth, sclheight);
image.draw(batch, alpha);
Draw.color();
}
int index = 0;
for(int i = 0; i < MapEditor.brushSizes.length; i ++){
for(int i = 0; i < MapEditor.brushSizes.length; i++){
if(editor.getBrushSize() == MapEditor.brushSizes[i]){
index = i;
break;
@@ -297,102 +297,102 @@ public class MapView extends Element implements GestureListener{
//todo is it really math.max?
float scaling = zoom * Math.min(width, height) / Math.max(editor.getMap().width(), editor.getMap().height());
Draw.color(Palette.accent);
Lines.stroke(Unit.dp.scl(1f * zoom));
Draw.color(Palette.accent);
Lines.stroke(Unit.dp.scl(1f * zoom));
if(!editor.getDrawBlock().isMultiblock() || tool == EditorTool.eraser) {
if (tool == EditorTool.line && drawing) {
Vector2 v1 = unproject(startx, starty).add(x, y);
float sx = v1.x, sy = v1.y;
Vector2 v2 = unproject(lastx, lasty).add(x, y);
if(!editor.getDrawBlock().isMultiblock() || tool == EditorTool.eraser){
if(tool == EditorTool.line && drawing){
Vector2 v1 = unproject(startx, starty).add(x, y);
float sx = v1.x, sy = v1.y;
Vector2 v2 = unproject(lastx, lasty).add(x, y);
Lines.poly(brushPolygons[index], sx, sy, scaling);
Lines.poly(brushPolygons[index], v2.x, v2.y, scaling);
}
Lines.poly(brushPolygons[index], sx, sy, scaling);
Lines.poly(brushPolygons[index], v2.x, v2.y, scaling);
}
if (tool.edit && (!mobile || drawing)) {
GridPoint2 p = project(mousex, mousey);
Vector2 v = unproject(p.x, p.y).add(x, y);
Lines.poly(brushPolygons[index], v.x, v.y, scaling);
}
}else{
if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){
GridPoint2 p = project(mousex, mousey);
Vector2 v = unproject(p.x, p.y).add(x, y);
float offset = (editor.getDrawBlock().size % 2 == 0 ? scaling/2f : 0f);
Lines.square(
v.x + scaling/2f + offset,
v.y + scaling/2f + offset,
scaling * editor.getDrawBlock().size /2f);
}
}
if(tool.edit && (!mobile || drawing)){
GridPoint2 p = project(mousex, mousey);
Vector2 v = unproject(p.x, p.y).add(x, y);
Lines.poly(brushPolygons[index], v.x, v.y, scaling);
}
}else{
if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){
GridPoint2 p = project(mousex, mousey);
Vector2 v = unproject(p.x, p.y).add(x, y);
float offset = (editor.getDrawBlock().size % 2 == 0 ? scaling / 2f : 0f);
Lines.square(
v.x + scaling / 2f + offset,
v.y + scaling / 2f + offset,
scaling * editor.getDrawBlock().size / 2f);
}
}
batch.flush();
if(pop) ScissorStack.popScissors();
Draw.color(Palette.accent);
Lines.stroke(Unit.dp.scl(3f));
Lines.rect(x, y, width, height);
Draw.reset();
}
private boolean active(){
return Core.scene.getKeyboardFocus() != null
&& Core.scene.getKeyboardFocus().isDescendantOf(ui.editor)
&& ui.editor.isShown() && tool == EditorTool.zoom &&
Core.scene.hit(Graphics.mouse().x, Graphics.mouse().y, true) == this;
}
batch.flush();
@Override
public boolean touchDown(float x, float y, int pointer, int button){
return false;
}
if(pop) ScissorStack.popScissors();
@Override
public boolean tap(float x, float y, int count, int button){
return false;
}
Draw.color(Palette.accent);
Lines.stroke(Unit.dp.scl(3f));
Lines.rect(x, y, width, height);
Draw.reset();
}
@Override
public boolean longPress(float x, float y){
return false;
}
private boolean active(){
return Core.scene.getKeyboardFocus() != null
&& Core.scene.getKeyboardFocus().isDescendantOf(ui.editor)
&& ui.editor.isShown() && tool == EditorTool.zoom &&
Core.scene.hit(Graphics.mouse().x, Graphics.mouse().y, true) == this;
}
@Override
public boolean fling(float velocityX, float velocityY, int button){
return false;
}
@Override
public boolean touchDown(float x, float y, int pointer, int button){
return false;
}
@Override
public boolean pan(float x, float y, float deltaX, float deltaY){
if(!active()) return false;
offsetx += deltaX / zoom;
offsety -= deltaY / zoom;
return false;
}
@Override
public boolean tap(float x, float y, int count, int button){
return false;
}
@Override
public boolean panStop(float x, float y, int pointer, int button){
return false;
}
@Override
public boolean longPress(float x, float y){
return false;
}
@Override
public boolean zoom(float initialDistance, float distance){
if(!active()) return false;
float nzoom = distance - initialDistance;
zoom += nzoom / 10000f / Unit.dp.scl(1f) * zoom;
clampZoom();
return false;
}
@Override
public boolean fling(float velocityX, float velocityY, int button){
return false;
}
@Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2){
return false;
}
@Override
public boolean pan(float x, float y, float deltaX, float deltaY){
if(!active()) return false;
offsetx += deltaX / zoom;
offsety -= deltaY / zoom;
return false;
}
@Override
public void pinchStop(){
@Override
public boolean panStop(float x, float y, int pointer, int button){
return false;
}
}
@Override
public boolean zoom(float initialDistance, float distance){
if(!active()) return false;
float nzoom = distance - initialDistance;
zoom += nzoom / 10000f / Unit.dp.scl(1f) * zoom;
clampZoom();
return false;
}
@Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2){
return false;
}
@Override
public void pinchStop(){
}
}

View File

@@ -3,49 +3,49 @@ package io.anuke.mindustry.editor;
import com.badlogic.gdx.utils.Array;
public class OperationStack{
private final static int maxSize = 10;
private Array<DrawOperation> stack = new Array<>();
private int index = 0;
public OperationStack(){
}
public void clear(){
stack.clear();
index = 0;
}
public void add(DrawOperation action){
stack.truncate(stack.size + index);
index = 0;
stack.add(action);
private final static int maxSize = 10;
private Array<DrawOperation> stack = new Array<>();
private int index = 0;
if(stack.size > maxSize){
public OperationStack(){
}
public void clear(){
stack.clear();
index = 0;
}
public void add(DrawOperation action){
stack.truncate(stack.size + index);
index = 0;
stack.add(action);
if(stack.size > maxSize){
stack.removeIndex(0);
}
}
public boolean canUndo(){
return !(stack.size - 1 + index < 0);
}
public boolean canRedo(){
return !(index > -1 || stack.size + index < 0);
}
}
public void undo(MapEditor editor){
if(!canUndo()) return;
public boolean canUndo(){
return !(stack.size - 1 + index < 0);
}
stack.get(stack.size - 1 + index).undo(editor);
index --;
}
public boolean canRedo(){
return !(index > -1 || stack.size + index < 0);
}
public void redo(MapEditor editor){
if(!canRedo()) return;
index ++;
stack.get(stack.size - 1 + index).redo(editor);
}
public void undo(MapEditor editor){
if(!canUndo()) return;
stack.get(stack.size - 1 + index).undo(editor);
index--;
}
public void redo(MapEditor editor){
if(!canRedo()) return;
index++;
stack.get(stack.size - 1 + index).redo(editor);
}
}

View File

@@ -24,84 +24,90 @@ import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*;
/**Utility class for damaging in an area.*/
public class Damage {
private static Rectangle rect = new Rectangle();
private static Rectangle hitrect = new Rectangle();
private static Translator tr = new Translator();
/**
* Utility class for damaging in an area.
*/
public class Damage{
private static Rectangle rect = new Rectangle();
private static Rectangle hitrect = new Rectangle();
private static Translator tr = new Translator();
/**Creates a dynamic explosion based on specified parameters.*/
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i ++){
int branches = 5 + Mathf.clamp((int)(power/30), 1, 20);
Timers.run(i*2f + Mathf.random(4f), () -> Lightning.create(Team.none, Fx.none, Palette.power, 3,
x, y, Mathf.random(360f), branches + Mathf.range(2)));
}
/**
* Creates a dynamic explosion based on specified parameters.
*/
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
int branches = 5 + Mathf.clamp((int) (power / 30), 1, 20);
Timers.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.none, Fx.none, Palette.power, 3,
x, y, Mathf.random(360f), branches + Mathf.range(2)));
}
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i ++){
Timers.run(i/2, () -> CallEntity.createBullet(TurretBullets.fireball, x, y, Mathf.random(360f)));
}
for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){
Timers.run(i / 2, () -> CallEntity.createBullet(TurretBullets.fireball, x, y, Mathf.random(360f)));
}
int waves = Mathf.clamp((int)(explosiveness / 4), 0, 30);
int waves = Mathf.clamp((int) (explosiveness / 4), 0, 30);
for(int i = 0; i < waves; i ++){
int f = i;
Timers.run(i*2f, () -> {
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f)/waves), explosiveness/2f);
Effects.effect(ExplosionFx.blockExplosionSmoke, x + Mathf.range(radius), y + Mathf.range(radius));
});
}
for(int i = 0; i < waves; i++){
int f = i;
Timers.run(i * 2f, () -> {
Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), explosiveness / 2f);
Effects.effect(ExplosionFx.blockExplosionSmoke, x + Mathf.range(radius), y + Mathf.range(radius));
});
}
if(explosiveness > 15f){
Effects.effect(ExplosionFx.shockwave, x, y);
}
if(explosiveness > 15f){
Effects.effect(ExplosionFx.shockwave, x, y);
}
if(explosiveness > 30f){
Effects.effect(ExplosionFx.bigShockwave, x, y);
}
if(explosiveness > 30f){
Effects.effect(ExplosionFx.bigShockwave, x, y);
}
float shake = Math.min(explosiveness/4f + 3f, 9f);
Effects.shake(shake, shake, x, y);
Effects.effect(ExplosionFx.blockExplosion, x, y);
}
float shake = Math.min(explosiveness / 4f + 3f, 9f);
Effects.shake(shake, shake, x, y);
Effects.effect(ExplosionFx.blockExplosion, x, y);
}
public static void createIncend(float x, float y, float range, int amount){
for (int i = 0; i < amount; i++) {
float cx = x + Mathf.range(range);
float cy = y + Mathf.range(range);
Tile tile = world.tileWorld(cx, cy);
if(tile != null){
Fire.create(tile);
}
}
}
public static void createIncend(float x, float y, float range, int amount){
for(int i = 0; i < amount; i++){
float cx = x + Mathf.range(range);
float cy = y + Mathf.range(range);
Tile tile = world.tileWorld(cx, cy);
if(tile != null){
Fire.create(tile);
}
}
}
/**Damages entities in a line.
* Only enemies of the specified team are damaged.*/
public static void collideLine(SolidEntity hitter, Team team, Effect effect, float x, float y, float angle, float length){
tr.trns(angle, length);
rect.setPosition(x, y).setSize(tr.x, tr.y);
float x2 = tr.x + x, y2 = tr.y + y;
/**
* Damages entities in a line.
* Only enemies of the specified team are damaged.
*/
public static void collideLine(SolidEntity hitter, Team team, Effect effect, float x, float y, float angle, float length){
tr.trns(angle, length);
rect.setPosition(x, y).setSize(tr.x, tr.y);
float x2 = tr.x + x, y2 = tr.y + y;
if(rect.width < 0){
rect.x += rect.width;
rect.width *= -1;
}
if(rect.width < 0){
rect.x += rect.width;
rect.width *= -1;
}
if(rect.height < 0){
rect.y += rect.height;
rect.height *= -1;
}
if(rect.height < 0){
rect.y += rect.height;
rect.height *= -1;
}
float expand = 3f;
float expand = 3f;
rect.y -= expand;
rect.x -= expand;
rect.width += expand*2;
rect.height += expand*2;
rect.y -= expand;
rect.x -= expand;
rect.width += expand * 2;
rect.height += expand * 2;
Consumer<Unit> cons = e -> {
e.getHitbox(hitrect);
e.getHitbox(hitrect);
Rectangle other = hitrect;
other.y -= expand;
other.x -= expand;
@@ -110,79 +116,85 @@ public class Damage {
Vector2 vec = Physics.raycastRect(x, y, x2, y2, other);
if (vec != null) {
if(vec != null){
Effects.effect(effect, vec.x, vec.y);
e.collision(hitter, vec.x, vec.y);
hitter.collision(e, vec.x, vec.y);
}
};
Units.getNearbyEnemies(team, rect, cons);
}
Units.getNearbyEnemies(team, rect, cons);
}
/**Damages all entities and blocks in a radius that are enemies of the team.*/
public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate<Unit> predicate, Consumer<Unit> acceptor) {
Consumer<Unit> cons = entity -> {
if(!predicate.test(entity)) return;
/**
* Damages all entities and blocks in a radius that are enemies of the team.
*/
public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate<Unit> predicate, Consumer<Unit> acceptor){
Consumer<Unit> cons = entity -> {
if(!predicate.test(entity)) return;
entity.getHitbox(hitrect);
if (!hitrect.overlaps(rect)) {
return;
}
entity.damage(damage);
acceptor.accept(entity);
};
entity.getHitbox(hitrect);
if(!hitrect.overlaps(rect)){
return;
}
entity.damage(damage);
acceptor.accept(entity);
};
rect.setSize(size * 2).setCenter(x, y);
if (team != null) {
Units.getNearbyEnemies(team, rect, cons);
} else {
Units.getNearby(rect, cons);
}
}
rect.setSize(size * 2).setCenter(x, y);
if(team != null){
Units.getNearbyEnemies(team, rect, cons);
}else{
Units.getNearby(rect, cons);
}
}
/**Damages everything in a radius.*/
public static void damage(float x, float y, float radius, float damage){
damage(null, x, y, radius, damage);
}
/**
* Damages everything in a radius.
*/
public static void damage(float x, float y, float radius, float damage){
damage(null, x, y, radius, damage);
}
/**Damages all entities and blocks in a radius that are enemies of the team.*/
public static void damage(Team team, float x, float y, float radius, float damage){
Consumer<Unit> cons = entity -> {
if(entity.team == team || entity.distanceTo(x, y) > radius){
return;
}
float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
entity.damage(amount);
//TODO better velocity displacement
float dst = tr.set(entity.x - x, entity.y - y).len();
entity.getVelocity().add(tr.setLength((1f-dst/radius) * 2f));
};
/**
* Damages all entities and blocks in a radius that are enemies of the team.
*/
public static void damage(Team team, float x, float y, float radius, float damage){
Consumer<Unit> cons = entity -> {
if(entity.team == team || entity.distanceTo(x, y) > radius){
return;
}
float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage);
entity.damage(amount);
//TODO better velocity displacement
float dst = tr.set(entity.x - x, entity.y - y).len();
entity.getVelocity().add(tr.setLength((1f - dst / radius) * 2f));
};
rect.setSize(radius *2).setCenter(x, y);
if(team != null) {
Units.getNearbyEnemies(team, rect, cons);
}else{
Units.getNearby(rect, cons);
}
rect.setSize(radius * 2).setCenter(x, y);
if(team != null){
Units.getNearbyEnemies(team, rect, cons);
}else{
Units.getNearby(rect, cons);
}
int trad = (int)(radius / tilesize);
for(int dx = -trad; dx <= trad; dx ++){
for(int dy= -trad; dy <= trad; dy ++){
Tile tile = world.tile(Mathf.scl2(x, tilesize) + dx, Mathf.scl2(y, tilesize) + dy);
if(tile != null && tile.entity != null && (team == null || state.teams.areEnemies(team, tile.getTeam())) && Vector2.dst(dx, dy, 0, 0) <= trad){
float amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage);
tile.entity.damage(amount);
}
}
}
int trad = (int) (radius / tilesize);
for(int dx = -trad; dx <= trad; dx++){
for(int dy = -trad; dy <= trad; dy++){
Tile tile = world.tile(Mathf.scl2(x, tilesize) + dx, Mathf.scl2(y, tilesize) + dy);
if(tile != null && tile.entity != null && (team == null || state.teams.areEnemies(team, tile.getTeam())) && Vector2.dst(dx, dy, 0, 0) <= trad){
float amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage);
tile.entity.damage(amount);
}
}
}
}
private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){
float dist = Vector2.dst(x, y, tx, ty);
float falloff = 0.4f;
float scaled = Mathf.lerp(1f - dist/radius, 1f, falloff);
return damage * scaled;
}
}
private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){
float dist = Vector2.dst(x, y, tx, ty);
float falloff = 0.4f;
float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff);
return damage * scaled;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,16 @@ import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.ucore.util.Mathf;
/**Class for predicting shoot angles based on velocities of targets.*/
public class Predict {
/**
* Class for predicting shoot angles based on velocities of targets.
*/
public class Predict{
private static Vector2 vec = new Vector2();
private static Vector2 vresult = new Vector2();
/**Calculates of intercept of a stationary and moving target. Do not call from multiple threads!
/**
* Calculates of intercept of a stationary and moving target. Do not call from multiple threads!
*
* @param srcx X of shooter
* @param srcy Y of shooter
* @param dstx X of target
@@ -17,52 +21,55 @@ public class Predict {
* @param dstvx X velocity of target (subtract shooter X velocity if needed)
* @param dstvy Y velocity of target (subtract shooter Y velocity if needed)
* @param v speed of bullet
* @return the intercept location*/
public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v) {
* @return the intercept location
*/
public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v){
float tx = dstx - srcx,
ty = dsty - srcy;
// Get quadratic equation components
float a = dstvx * dstvx + dstvy * dstvy - v*v;
float a = dstvx * dstvx + dstvy * dstvy - v * v;
float b = 2 * (dstvx * tx + dstvy * ty);
float c = tx*tx + ty*ty;
float c = tx * tx + ty * ty;
// Solve quadratic
Vector2 ts = quad(a, b, c);
// Find smallest positive solution
Vector2 sol = vresult.set(0, 0);
if (ts != null) {
if(ts != null){
float t0 = ts.x, t1 = ts.y;
float t = Math.min(t0, t1);
if (t < 0) t = Math.max(t0, t1);
if (t > 0) {
sol.set(dstx + dstvx*t, dsty + dstvy*t);
if(t < 0) t = Math.max(t0, t1);
if(t > 0){
sol.set(dstx + dstvx * t, dsty + dstvy * t);
}
}
return sol;
}
/**See {@link #intercept(float, float, float, float, float, float, float)}.*/
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v) {
/**
* See {@link #intercept(float, float, float, float, float, float, float)}.
*/
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v){
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getVelocity().x - src.getVelocity().x, dst.getVelocity().x - src.getVelocity().y, v);
}
private static Vector2 quad(float a, float b, float c) {
private static Vector2 quad(float a, float b, float c){
Vector2 sol = null;
if (Math.abs(a) < 1e-6) {
if (Math.abs(b) < 1e-6) {
sol = Math.abs(c) < 1e-6 ? vec.set(0,0) : null;
} else {
vec.set(-c/b, -c/b);
if(Math.abs(a) < 1e-6){
if(Math.abs(b) < 1e-6){
sol = Math.abs(c) < 1e-6 ? vec.set(0, 0) : null;
}else{
vec.set(-c / b, -c / b);
}
} else {
float disc = b*b - 4*a*c;
if (disc >= 0) {
}else{
float disc = b * b - 4 * a * c;
if(disc >= 0){
disc = Mathf.sqrt(disc);
a = 2*a;
sol = vec.set((-b-disc)/a, (-b+disc)/a);
a = 2 * a;
sol = vec.set((-b - disc) / a, (-b + disc) / a);
}
}
return sol;

View File

@@ -12,7 +12,9 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**Class for controlling status effects on an entity.*/
/**
* Class for controlling status effects on an entity.
*/
public class StatusController implements Saveable{
private static final StatusEntry globalResult = new StatusEntry();
private static final Array<StatusEntry> removals = new ThreadArray<>();
@@ -26,20 +28,20 @@ public class StatusController implements Saveable{
public void handleApply(Unit unit, StatusEffect effect, float intensity){
if(effect == StatusEffects.none) return; //don't apply empty effects
float newTime = effect.baseDuration*intensity;
float newTime = effect.baseDuration * intensity;
if(statuses.size > 0){
//check for opposite effects
for(StatusEntry entry : statuses){
//extend effect
if(entry.effect == effect) {
if(entry.effect == effect){
entry.time = Math.max(entry.time, newTime);
return;
}else if(entry.effect.isOpposite(effect)){ //find opposite
entry.effect.getTransition(unit, effect, entry.time, newTime, globalResult);
entry.time = globalResult.time;
if (globalResult.effect != entry.effect) {
if(globalResult.effect != entry.effect){
entry.effect.onTransition(unit, globalResult.effect);
entry.effect = globalResult.effect;
}
@@ -94,7 +96,7 @@ public class StatusController implements Saveable{
return damageMultiplier;
}
public float getArmorMultiplier() {
public float getArmorMultiplier(){
return armorMultiplier;
}
@@ -106,24 +108,24 @@ public class StatusController implements Saveable{
}
@Override
public void writeSave(DataOutput stream) throws IOException {
public void writeSave(DataOutput stream) throws IOException{
stream.writeByte(statuses.size);
for(StatusEntry entry : statuses){
stream.writeByte(entry.effect.id);
stream.writeShort((short)(entry.time * 2));
stream.writeShort((short) (entry.time * 2));
}
}
@Override
public void readSave(DataInput stream) throws IOException {
for (StatusEntry effect : statuses){
public void readSave(DataInput stream) throws IOException{
for(StatusEntry effect : statuses){
Pooling.free(effect);
}
statuses.clear();
byte amount = stream.readByte();
for (int i = 0; i < amount; i++) {
for(int i = 0; i < amount; i++){
byte id = stream.readByte();
float time = stream.readShort() / 2f;
StatusEntry entry = Pooling.obtain(StatusEntry.class);
@@ -132,7 +134,7 @@ public class StatusController implements Saveable{
}
}
public static class StatusEntry {
public static class StatusEntry{
public StatusEffect effect;
public float time;

View File

@@ -1,6 +1,9 @@
package io.anuke.mindustry.entities;
import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectSet;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.fx.Fx;
@@ -10,11 +13,14 @@ import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.CallBlocks;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Edges;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Wall;
import io.anuke.mindustry.world.blocks.modules.InventoryModule;
import io.anuke.mindustry.world.blocks.modules.LiquidModule;
import io.anuke.mindustry.world.blocks.modules.PowerModule;
import io.anuke.mindustry.world.consumers.Consume;
import io.anuke.mindustry.world.modules.ConsumeModule;
import io.anuke.mindustry.world.modules.InventoryModule;
import io.anuke.mindustry.world.modules.LiquidModule;
import io.anuke.mindustry.world.modules.PowerModule;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup;
@@ -29,153 +35,219 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.tileGroup;
import static io.anuke.mindustry.Vars.world;
public class TileEntity extends BaseEntity implements TargetTrait {
public static final float timeToSleep = 60f*4; //4 seconds to fall asleep
/**This value is only used for debugging.*/
public static int sleepingEntities = 0;
public class TileEntity extends BaseEntity implements TargetTrait{
public static final float timeToSleep = 60f * 4; //4 seconds to fall asleep
private static final ObjectSet<Tile> tmpTiles = new ObjectSet<>();
/**
* This value is only used for debugging.
*/
public static int sleepingEntities = 0;
public Tile tile;
public Timer timer;
public float health;
public Tile tile;
public Timer timer;
public float health;
public PowerModule power;
public InventoryModule items;
public LiquidModule liquids;
public ConsumeModule cons;
public PowerModule power;
public InventoryModule items;
public LiquidModule liquids;
/**List of (cached) tiles with entities in proximity, used for outputting to*/
private Array<Tile> proximity = new Array<>(8);
private boolean dead = false;
private boolean sleeping;
private float sleepTime;
private boolean dead = false;
private boolean sleeping;
private float sleepTime;
/**Sets this tile entity data to this tile, and adds it if necessary.*/
public TileEntity init(Tile tile, boolean added){
this.tile = tile;
x = tile.drawx();
y = tile.drawy();
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDamage(Tile tile, float health){
if(tile.entity != null){
tile.entity.health = health;
}
}
health = tile.block().health;
timer = new Timer(tile.block().timers);
if(added){
//if(!tile.block().autoSleep) { //TODO only autosleep when creating a fresh block!
add();
/*}else{
sleeping = true;
sleepingEntities ++;
}*/
}
return this;
}
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDestroyed(Tile tile){
if(tile.entity == null) return;
tile.entity.onDeath();
}
/**Call when nothing is happening to the entity.
* This increments the internal sleep timer.*/
public void sleep(){
sleepTime += Timers.delta();
if(!sleeping && sleepTime >= timeToSleep){
remove();
sleeping = true;
sleepingEntities ++;
}
}
/**Sets this tile entity data to this tile, and adds it if necessary.*/
public TileEntity init(Tile tile, boolean added){
this.tile = tile;
x = tile.drawx();
y = tile.drawy();
/**Call when something just happened to the entity.
* If the entity was sleeping, this enables it. This also resets the sleep timer.*/
public void wakeUp(){
sleepTime = 0f;
if(sleeping){
add();
sleeping = false;
sleepingEntities --;
}
}
health = tile.block().health;
public boolean isSleeping(){
return sleeping;
}
timer = new Timer(tile.block().timers);
public boolean isDead() {
return dead;
}
if(added){
add();
}
public void write(DataOutputStream stream) throws IOException{}
public void read(DataInputStream stream) throws IOException{}
return this;
}
private void onDeath(){
if(!dead) {
dead = true;
Block block = tile.block();
/**
* Call when nothing is happening to the entity.
* This increments the internal sleep timer.
*/
public void sleep(){
sleepTime += Timers.delta();
if(!sleeping && sleepTime >= timeToSleep){
remove();
sleeping = true;
sleepingEntities++;
}
}
block.onDestroyed(tile);
world.removeBlock(tile);
block.afterDestroyed(tile, this);
remove();
}
}
/**
* Call when something just happened to the entity.
* If the entity was sleeping, this enables it. This also resets the sleep timer.
*/
public void wakeUp(){
sleepTime = 0f;
if(sleeping){
add();
sleeping = false;
sleepingEntities--;
}
}
public boolean collide(Bullet other){
return true;
}
public void collision(Bullet other){
tile.block().handleBulletHit(this, other);
}
public void damage(float damage){
if(dead) return;
public boolean isSleeping(){
return sleeping;
}
CallBlocks.onTileDamage(tile, health - tile.block().handleDamage(tile, damage));
public boolean isDead(){
return dead;
}
if(health <= 0){
CallBlocks.onTileDestroyed(tile);
}
}
public void write(DataOutputStream stream) throws IOException{
}
public Tile getTile(){
return tile;
}
public void read(DataInputStream stream) throws IOException{
}
@Override
public Team getTeam() {
return tile.getTeam();
}
private void onDeath(){
if(!dead){
dead = true;
Block block = tile.block();
@Override
public Vector2 getVelocity() {
return Vector2.Zero;
}
block.onDestroyed(tile);
world.removeBlock(tile);
block.afterDestroyed(tile, this);
remove();
}
}
@Override
public void update(){
synchronized (Tile.tileSetLock) {
//TODO better smoke effect, this one is awful
if (health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))) {
public boolean collide(Bullet other){
return true;
}
Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4));
}
public void collision(Bullet other){
tile.block().handleBulletHit(this, other);
}
if (health <= 0) {
onDeath();
}
public void damage(float damage){
if(dead) return;
tile.block().update(tile);
}
}
CallBlocks.onTileDamage(tile, health - tile.block().handleDamage(tile, damage));
@Override
public EntityGroup targetGroup() {
return tileGroup;
}
if(health <= 0){
CallBlocks.onTileDestroyed(tile);
}
}
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDamage(Tile tile, float health){
tile.entity.health = health;
}
public Tile getTile(){
return tile;
}
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDestroyed(Tile tile){
if(tile.entity == null) return;
tile.entity.onDeath();
}
public boolean consumed(Class<? extends Consume> type){
return tile.block().consumes.get(type).valid(tile.block(), this);
}
public void removeFromProximity(){
GridPoint2[] nearby = Edges.getEdges(tile.block().size);
for(GridPoint2 point : nearby){
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
//remove this tile from all nearby tile's proximities
if(other != null){
other = other.target();
other.block().onProximityUpdate(other);
}
if(other != null && other.entity != null){
other.entity.proximity.removeValue(tile, true);
}
}
}
public void updateProximity(){
tmpTiles.clear();
proximity.clear();
GridPoint2[] nearby = Edges.getEdges(tile.block().size);
for(GridPoint2 point : nearby){
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
if(other != null){
other.block().onProximityUpdate(other);
other = other.target();
}
if(other != null && other.entity != null){
tmpTiles.add(other);
//add this tile to proximity of nearby tiles
if(!other.entity.proximity.contains(tile, true)){
other.entity.proximity.add(tile);
}
}
}
//using a set to prevent duplicates
for(Tile tile : tmpTiles){
proximity.add(tile);
}
tile.block().onProximityUpdate(tile);
}
public Array<Tile> proximity(){
return proximity;
}
@Override
public Team getTeam(){
return tile.getTeam();
}
@Override
public Vector2 getVelocity(){
return Vector2.Zero;
}
@Override
public void update(){
synchronized(Tile.tileSetLock){
//TODO better smoke effect, this one is awful
if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))){
Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4));
}
if(health <= 0){
onDeath();
}
tile.block().update(tile);
if(cons != null){
cons.update(this);
}
}
}
@Override
public EntityGroup targetGroup(){
return tileGroup;
}
}

View File

@@ -31,13 +31,20 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world;
public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait, CarriableTrait, InventoryTrait {
/**total duration of hit flash effect*/
public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait, CarriableTrait, InventoryTrait{
/**
* total duration of hit flash effect
*/
public static final float hitDuration = 9f;
/**Percision divisor of velocity, used when writing. For example a value of '2' would mean the percision is 1/2 = 0.5-size chunks.*/
/**
* Percision divisor of velocity, used when writing. For example a value of '2' would mean the percision is 1/2 = 0.5-size chunks.
*/
public static final float velocityPercision = 8f;
/**Maximum absolute value of a velocity vector component.*/
public static final float maxAbsVelocity = 127f/velocityPercision;
/**
* Maximum absolute value of a velocity vector component.
*/
public static final float maxAbsVelocity = 127f / velocityPercision;
public static final float elevationScale = 4f;
private static final Vector2 moveVector = new Vector2();
@@ -52,30 +59,31 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
protected Vector2 velocity = new Translator(0f, 0.0001f);
protected float hitTime;
protected float drownTime;
protected float elevation;
@Override
public UnitInventory getInventory() {
public UnitInventory getInventory(){
return inventory;
}
@Override
public float getRotation() {
public float getRotation(){
return rotation;
}
@Override
public void setRotation(float rotation) {
public void setRotation(float rotation){
this.rotation = rotation;
}
@Override
public void setCarrier(CarryTrait carrier) {
this.carrier = carrier;
public CarryTrait getCarrier(){
return carrier;
}
@Override
public CarryTrait getCarrier() {
return carrier;
public void setCarrier(CarryTrait carrier){
this.carrier = carrier;
}
@Override
@@ -84,7 +92,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
@Override
public void interpolate() {
public void interpolate(){
interpolator.update();
x = interpolator.pos.x;
@@ -96,7 +104,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
@Override
public Interpolator getInterpolator() {
public Interpolator getInterpolator(){
return interpolator;
}
@@ -113,31 +121,31 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
@Override
public void onDeath() {
public void onDeath(){
inventory.clear();
drownTime = 0f;
status.clear();
}
@Override
public Vector2 getVelocity() {
public Vector2 getVelocity(){
return velocity;
}
@Override
public void writeSave(DataOutput stream) throws IOException {
public void writeSave(DataOutput stream) throws IOException{
writeSave(stream, false);
}
@Override
public void readSave(DataInput stream) throws IOException {
public void readSave(DataInput stream) throws IOException{
byte team = stream.readByte();
boolean dead = stream.readBoolean();
float x = stream.readFloat();
float y = stream.readFloat();
byte xv = stream.readByte();
byte yv = stream.readByte();
float rotation = stream.readShort()/2f;
float rotation = stream.readShort() / 2f;
int health = stream.readShort();
this.status.readSave(stream);
@@ -151,21 +159,21 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
this.rotation = rotation;
}
public void writeSave(DataOutput stream, boolean net) throws IOException {
public void writeSave(DataOutput stream, boolean net) throws IOException{
stream.writeByte(team.ordinal());
stream.writeBoolean(isDead());
stream.writeFloat(net ? interpolator.target.x : x);
stream.writeFloat(net ? interpolator.target.y : y);
stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeByte((byte)(Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeShort((short)(rotation*2));
stream.writeShort((short)health);
stream.writeByte((byte) (Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeByte((byte) (Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeShort((short) (rotation * 2));
stream.writeShort((short) health);
status.writeSave(stream);
inventory.writeSave(stream);
}
public float calculateDamage(float amount){
return amount * Mathf.clamp(1f-getArmor()/100f*status.getArmorMultiplier());
return amount * Mathf.clamp(1f - getArmor() / 100f * status.getArmorMultiplier());
}
public float getDamageMultipler(){
@@ -180,7 +188,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
if(state.teams.has(team)){
TeamData data = state.teams.get(team);
Tile tile = Geometry.findClosest(x, y, data.cores);
Tile tile = Geometry.findClosest(x, y, data.cores);
if(tile == null){
return null;
}else{
@@ -198,15 +206,18 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void avoidOthers(float avoidRange){
EntityPhysics.getNearby(getGroup(), x, y, avoidRange*2f, t -> {
if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying()) || ((Unit) t).getCarrier() == this) || getCarrier() == t)) return;
EntityPhysics.getNearby(getGroup(), x, y, avoidRange * 2f, t -> {
if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying()) || ((Unit) t).getCarrier() == this) || getCarrier() == t))
return;
float dst = distanceTo(t);
if(dst > avoidRange) return;
velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange))));
});
}
/**Updates velocity and status effects.*/
/**
* Updates velocity and status effects.
*/
public void updateVelocityStatus(float drag, float maxVelocity){
if(isCarried()){ //carried units do not take into account velocity normally
set(carrier.getX(), carrier.getY());
@@ -221,9 +232,13 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
velocity.limit(maxVelocity).scl(status.getSpeedMultiplier());
if(isFlying()) {
if(isFlying()){
x += velocity.x / getMass() * Timers.delta();
y += velocity.y / getMass() * Timers.delta();
if(tile != null){
elevation = Mathf.lerpDelta(elevation, tile.elevation, 0.04f);
}
}else{
boolean onLiquid = floor.isLiquid;
@@ -239,7 +254,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
}
if(onLiquid && velocity.len() > 0.4f && Timers.get(this, "flooreffect", 14 - (velocity.len() * floor.speedMultiplier)*2f)){
if(onLiquid && velocity.len() > 0.4f && Timers.get(this, "flooreffect", 14 - (velocity.len() * floor.speedMultiplier) * 2f)){
Effects.effect(floor.walkEffect, floor.liquidColor, x, y);
}
@@ -250,7 +265,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
if(onLiquid && floor.drownTime > 0){
drownTime += Timers.delta() * 1f/floor.drownTime;
drownTime += Timers.delta() * 1f / floor.drownTime;
if(Timers.get(this, "drowneffect", 15)){
Effects.effect(floor.drownUpdateEffect, floor.liquidColor, x, y);
}
@@ -270,7 +285,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
if(Math.abs(py - y) <= 0.0001f) velocity.y = 0f;
}
velocity.scl(Mathf.clamp(1f-drag* floor.dragMultiplier* Timers.delta()));
velocity.scl(Mathf.clamp(1f - drag * floor.dragMultiplier * Timers.delta()));
}
public void applyEffect(StatusEffect effect, float intensity){
@@ -293,11 +308,17 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
public float getAmmoFraction(){
return inventory.totalAmmo() / (float)inventory.ammoCapacity();
return inventory.totalAmmo() / (float) inventory.ammoCapacity();
}
public void drawUnder(){}
public void drawOver(){}
public void drawUnder(){
}
public void drawOver(){
}
public void drawShadow(){
}
public void drawView(){
Fill.circle(x, y, getViewDistance());
@@ -312,12 +333,20 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
public abstract TextureRegion getIconRegion();
public abstract int getItemCapacity();
public abstract int getAmmoCapacity();
public abstract float getArmor();
public abstract boolean acceptsAmmo(Item item);
public abstract void addAmmo(Item item);
public abstract float getMass();
public abstract boolean isFlying();
public abstract float getSize();
}

View File

@@ -8,16 +8,17 @@ import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack;
import java.io.*;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class UnitInventory implements Saveable{
private final Unit unit;
private Array<AmmoEntry> ammos = new Array<>();
private int totalAmmo;
private ItemStack item = new ItemStack(Items.stone, 0);
private final Unit unit;
public UnitInventory(Unit unit) {
public UnitInventory(Unit unit){
this.unit = unit;
}
@@ -26,24 +27,24 @@ public class UnitInventory implements Saveable{
}
@Override
public void writeSave(DataOutput stream) throws IOException {
public void writeSave(DataOutput stream) throws IOException{
stream.writeShort(item.amount);
stream.writeByte(item.item.id);
stream.writeShort(totalAmmo);
stream.writeByte(ammos.size);
for(int i = 0; i < ammos.size; i ++){
for(int i = 0; i < ammos.size; i++){
stream.writeByte(ammos.get(i).type.id);
stream.writeShort(ammos.get(i).amount);
}
}
@Override
public void readSave(DataInput stream) throws IOException {
public void readSave(DataInput stream) throws IOException{
short iamount = stream.readShort();
byte iid = stream.readByte();
this.totalAmmo = stream.readShort();
byte ammoa = stream.readByte();
for(int i = 0; i < ammoa; i ++){
for(int i = 0; i < ammoa; i++){
byte aid = stream.readByte();
int am = stream.readShort();
ammos.add(new AmmoEntry(AmmoType.getByID(aid), am));
@@ -53,12 +54,14 @@ public class UnitInventory implements Saveable{
item.amount = iamount;
}
/**Returns ammo range, or MAX_VALUE if this inventory has no ammo.*/
/**
* Returns ammo range, or MAX_VALUE if this inventory has no ammo.
*/
public float getAmmoRange(){
return hasAmmo() ? getAmmo().getRange() : Float.MAX_VALUE;
}
public AmmoType getAmmo() {
public AmmoType getAmmo(){
return ammos.size == 0 ? null : ammos.peek().type;
}
@@ -69,9 +72,9 @@ public class UnitInventory implements Saveable{
public void useAmmo(){
if(unit.isInfiniteAmmo()) return;
AmmoEntry entry = ammos.peek();
entry.amount --;
entry.amount--;
if(entry.amount == 0) ammos.pop();
totalAmmo --;
totalAmmo--;
}
public int totalAmmo(){
@@ -87,22 +90,23 @@ public class UnitInventory implements Saveable{
}
public void addAmmo(AmmoType type){
if(type == null) return;
totalAmmo += type.quantityMultiplier;
//find ammo entry by type
for(int i = ammos.size - 1; i >= 0; i --){
for(int i = ammos.size - 1; i >= 0; i--){
AmmoEntry entry = ammos.get(i);
//if found, put it to the right
if(entry.type == type){
entry.amount += type.quantityMultiplier;
ammos.swap(i, ammos.size-1);
ammos.swap(i, ammos.size - 1);
return;
}
}
//must not be found
AmmoEntry entry = new AmmoEntry(type, (int)type.quantityMultiplier);
AmmoEntry entry = new AmmoEntry(type, (int) type.quantityMultiplier);
ammos.add(entry);
}

View File

@@ -15,12 +15,19 @@ import io.anuke.ucore.function.Predicate;
import static io.anuke.mindustry.Vars.*;
/**Utility class for unit and team interactions.*/
public class Units {
/**
* Utility class for unit and team interactions.
*/
public class Units{
private static Rectangle rect = new Rectangle();
private static Rectangle hitrect = new Rectangle();
private static Unit result;
private static float cdist;
private static boolean boolResult;
/**Validates a target.
/**
* Validates a target.
*
* @param target The target to validate
* @param team The team of the thing doing tha targeting
* @param x The X position of the thing doign the targeting
@@ -28,44 +35,52 @@ public class Units {
* @param range The maximum distance from the target X/Y the targeter can be for it to be valid
* @return whether the target is invalid
*/
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y, float range) {
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y, float range){
return target == null || (range != Float.MAX_VALUE && target.distanceTo(x, y) > range) || target.getTeam() == team || !target.isValid();
}
/**See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}*/
/**
* See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}
*/
public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y){
return invalidateTarget(target, team, x, y, Float.MAX_VALUE);
}
/**See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}*/
/**
* See {@link #invalidateTarget(TargetTrait, Team, float, float, float)}
*/
public static boolean invalidateTarget(TargetTrait target, Unit targeter){
return invalidateTarget(target, targeter.team, targeter.x, targeter.y, targeter.inventory.getAmmoRange());
}
/**Returns whether there are any entities on this tile.*/
/**
* Returns whether there are any entities on this tile.
*/
public static boolean anyEntities(Tile tile){
Block type = tile.block();
rect.setSize(type.size * tilesize, type.size * tilesize);
rect.setCenter(tile.drawx(), tile.drawy());
boolean[] value = new boolean[1];
boolResult = false;
Units.getNearby(rect, unit -> {
if (value[0]) return;
if (!unit.isFlying()) {
if(boolResult) return;
if(!unit.isFlying()){
unit.getHitbox(hitrect);
if (hitrect.overlaps(rect)) {
value[0] = true;
if(hitrect.overlaps(rect)){
boolResult = true;
}
}
});
return value[0];
return boolResult;
}
/**Returns whether there are any entities on this tile, with the hitbox expanded.*/
/**
* Returns whether there are any entities on this tile, with the hitbox expanded.
*/
public static boolean anyEntities(Tile tile, float expansion, Predicate<Unit> pred){
Block type = tile.block();
rect.setSize(type.size * tilesize + expansion, type.size * tilesize + expansion);
@@ -78,7 +93,7 @@ public class Units {
if(!unit.isFlying()){
unit.getHitbox(hitrect);
if(hitrect.overlaps(rect)) {
if(hitrect.overlaps(rect)){
value[0] = true;
}
}
@@ -87,7 +102,9 @@ public class Units {
return value[0];
}
/**Returns the neareset ally tile in a range.*/
/**
* Returns the neareset ally tile in a range.
*/
public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
for(Team enemy : state.teams.alliesOf(team)){
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
@@ -98,7 +115,9 @@ public class Units {
return null;
}
/**Returns the neareset enemy tile in a range.*/
/**
* Returns the neareset enemy tile in a range.
*/
public static TileEntity findEnemyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
for(Team enemy : state.teams.enemiesOf(team)){
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
@@ -109,7 +128,9 @@ public class Units {
return null;
}
/**Iterates over all units on all teams, including players.*/
/**
* Iterates over all units on all teams, including players.
*/
public static void allUnits(Consumer<Unit> cons){
//check all unit groups first
for(EntityGroup<BaseUnit> group : unitGroups){
@@ -126,7 +147,9 @@ public class Units {
}
}
/**Returns the closest target enemy. First, units are checked, then tile entities.*/
/**
* Returns the closest target enemy. First, units are checked, then tile entities.
*/
public static TargetTrait getClosestTarget(Team team, float x, float y, float range){
Unit unit = getClosestEnemy(team, x, y, range, u -> true);
if(unit != null){
@@ -136,74 +159,82 @@ public class Units {
}
}
/**Returns the closest enemy of this team. Filter by predicate.*/
/**
* Returns the closest enemy of this team. Filter by predicate.
*/
public static Unit getClosestEnemy(Team team, float x, float y, float range, Predicate<Unit> predicate){
Unit[] result = {null};
float[] cdist = {0};
result = null;
cdist = 0f;
rect.setSize(range*2f).setCenter(x, y);
rect.setSize(range * 2f).setCenter(x, y);
getNearbyEnemies(team, rect, e -> {
if (e.isDead() || !predicate.test(e))
if(e.isDead() || !predicate.test(e))
return;
float dist = Vector2.dst(e.x, e.y, x, y);
if (dist < range) {
if (result[0] == null || dist < cdist[0]) {
result[0] = e;
cdist[0] = dist;
if(dist < range){
if(result == null || dist < cdist){
result = e;
cdist = dist;
}
}
});
return result[0];
return result;
}
/**Returns the closest ally of this team. Filter by predicate.*/
/**
* Returns the closest ally of this team. Filter by predicate.
*/
public static Unit getClosest(Team team, float x, float y, float range, Predicate<Unit> predicate){
Unit[] result = {null};
float[] cdist = {0};
result = null;
cdist = 0f;
rect.setSize(range*2f).setCenter(x, y);
rect.setSize(range * 2f).setCenter(x, y);
getNearby(team, rect, e -> {
if (!predicate.test(e))
if(!predicate.test(e))
return;
float dist = Vector2.dst(e.x, e.y, x, y);
if (dist < range) {
if (result[0] == null || dist < cdist[0]) {
result[0] = e;
cdist[0] = dist;
if(dist < range){
if(result == null || dist < cdist){
result = e;
cdist = dist;
}
}
});
return result[0];
return result;
}
/**Iterates over all units in a rectangle.*/
/**
* Iterates over all units in a rectangle.
*/
public static void getNearby(Team team, Rectangle rect, Consumer<Unit> cons){
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
if(!group.isEmpty()){
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit)entity));
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit) entity));
}
//now check all players
EntityPhysics.getNearby(playerGroup, rect, player -> {
if(((Unit)player).team == team) cons.accept((Unit)player);
if(((Unit) player).team == team) cons.accept((Unit) player);
});
}
/**Iterates over all units in a circle around this position.*/
/**
* Iterates over all units in a circle around this position.
*/
public static void getNearby(Team team, float x, float y, float radius, Consumer<Unit> cons){
rect.setSize(radius * 2).setCenter(x, y);
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
if(!group.isEmpty()){
EntityPhysics.getNearby(group, rect, entity -> {
if(entity.distanceTo(x, y) <= radius) {
if(entity.distanceTo(x, y) <= radius){
cons.accept((Unit) entity);
}
});
@@ -211,46 +242,52 @@ public class Units {
//now check all players
EntityPhysics.getNearby(playerGroup, rect, player -> {
if(((Unit)player).team == team && player.distanceTo(x, y) <= radius){
cons.accept((Unit)player);
if(((Unit) player).team == team && player.distanceTo(x, y) <= radius){
cons.accept((Unit) player);
}
});
}
/**Iterates over all units in a rectangle.*/
/**
* Iterates over all units in a rectangle.
*/
public static void getNearby(Rectangle rect, Consumer<Unit> cons){
for(Team team : Team.all){
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
if(!group.isEmpty()){
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit)entity));
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit) entity));
}
}
//now check all enemy players
EntityPhysics.getNearby(playerGroup, rect, player -> cons.accept((Unit)player));
EntityPhysics.getNearby(playerGroup, rect, player -> cons.accept((Unit) player));
}
/**Iterates over all units that are enemies of this team.*/
/**
* Iterates over all units that are enemies of this team.
*/
public static void getNearbyEnemies(Team team, Rectangle rect, Consumer<Unit> cons){
ObjectSet<Team> targets = state.teams.enemiesOf(team);
for(Team other : targets){
EntityGroup<BaseUnit> group = unitGroups[other.ordinal()];
if(!group.isEmpty()){
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit)entity));
EntityPhysics.getNearby(group, rect, entity -> cons.accept((Unit) entity));
}
}
//now check all enemy players
EntityPhysics.getNearby(playerGroup, rect, player -> {
if(targets.contains(((Player)player).team)){
cons.accept((Unit)player);
if(targets.contains(((Player) player).team)){
cons.accept((Unit) player);
}
});
}
/**Iterates over all units.*/
/**
* Iterates over all units.
*/
public static void getAllUnits(Consumer<Unit> cons){
for(Team team : Team.all){

View File

@@ -6,10 +6,10 @@ import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.graphics.Draw;
//TODO scale velocity depending on fslope()
public class ArtilleryBulletType extends BasicBulletType {
public class ArtilleryBulletType extends BasicBulletType{
protected Effect trailEffect = BulletFx.artilleryTrail;
public ArtilleryBulletType(float speed, float damage, String bulletSprite) {
public ArtilleryBulletType(float speed, float damage, String bulletSprite){
super(speed, damage, bulletSprite);
collidesTiles = false;
collides = false;
@@ -17,18 +17,18 @@ public class ArtilleryBulletType extends BasicBulletType {
}
@Override
public void update(Bullet b) {
public void update(Bullet b){
super.update(b);
if(b.timer.get(0, 3 + b.fslope()*2f)){
if(b.timer.get(0, 3 + b.fslope() * 2f)){
Effects.effect(trailEffect, backColor, b.x, b.y, b.fslope() * 4f);
}
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
float baseScale = 0.7f;
float scale = (baseScale + b.fslope()*(1f-baseScale));
float scale = (baseScale + b.fslope() * (1f - baseScale));
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());

View File

@@ -12,8 +12,10 @@ import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
/**A BulletType for most ammo-based bullets shot from turrets and units.*/
public class BasicBulletType extends BulletType {
/**
* A BulletType for most ammo-based bullets shot from turrets and units.
*/
public class BasicBulletType extends BulletType{
public Color backColor = Palette.bulletYellowBack, frontColor = Palette.bulletYellow;
public float bulletWidth = 5f, bulletHeight = 7f;
public float bulletShrink = 0.5f;
@@ -23,7 +25,9 @@ public class BasicBulletType extends BulletType {
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f;
public BulletType fragBullet = null;
/**Use a negative value to disable splash damage.*/
/**
* Use a negative value to disable splash damage.
*/
public float splashDamageRadius = -1f;
public float splashDamage = 6f;
@@ -39,19 +43,19 @@ public class BasicBulletType extends BulletType {
public float hitShake = 0f;
public BasicBulletType(float speed, float damage, String bulletSprite) {
public BasicBulletType(float speed, float damage, String bulletSprite){
super(speed, damage);
this.bulletSprite = bulletSprite;
}
@Override
public void load() {
public void load(){
backRegion = Draw.region(bulletSprite + "-back");
frontRegion = Draw.region(bulletSprite);
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout());
Draw.color(backColor);
@@ -62,7 +66,7 @@ public class BasicBulletType extends BulletType {
}
@Override
public void update(Bullet b) {
public void update(Bullet b){
super.update(b);
if(homingPower > 0.0001f){
@@ -74,20 +78,20 @@ public class BasicBulletType extends BulletType {
}
@Override
public void hit(Bullet b, float x, float y) {
public void hit(Bullet b, float x, float y){
super.hit(b, x, y);
Effects.shake(hitShake, hitShake, b);
if(fragBullet != null) {
for (int i = 0; i < fragBullets; i++) {
if(fragBullet != null){
for(int i = 0; i < fragBullets; i++){
float len = Mathf.random(1f, 7f);
float a = Mathf.random(360f);
Bullet.create(fragBullet, b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax));
}
}
if(Mathf.chance(incendChance)) {
if(Mathf.chance(incendChance)){
Damage.createIncend(x, y, incendSpread, incendAmount);
}
@@ -97,7 +101,7 @@ public class BasicBulletType extends BulletType {
}
@Override
public void despawned(Bullet b) {
public void despawned(Bullet b){
if(fragBullet != null || splashDamageRadius > 0){
hit(b);
}

View File

@@ -1,8 +1,8 @@
package io.anuke.mindustry.entities.bullet;
public class BombBulletType extends BasicBulletType {
public class BombBulletType extends BasicBulletType{
public BombBulletType(float damage, float radius, String sprite) {
public BombBulletType(float damage, float radius, String sprite){
super(0.7f, 0, sprite);
splashDamageRadius = radius;
splashDamage = damage;

View File

@@ -27,198 +27,199 @@ import static io.anuke.mindustry.Vars.bulletGroup;
import static io.anuke.mindustry.Vars.world;
public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncTrait{
private static Vector2 vector = new Vector2();
private static Vector2 vector = new Vector2();
public Timer timer = new Timer(3);
private Team team;
private Object data;
private boolean supressCollision;
private Team team;
private Object data;
private boolean supressCollision;
/**
* Internal use only!
*/
public Bullet(){
}
public Timer timer = new Timer(3);
public static void create(BulletType type, TeamTrait owner, float x, float y, float angle){
create(type, owner, owner.getTeam(), x, y, angle);
}
public static void create (BulletType type, TeamTrait owner, float x, float y, float angle){
create(type, owner, owner.getTeam(), x, y, angle);
}
public static void create(BulletType type, Entity owner, Team team, float x, float y, float angle){
create(type, owner, team, x, y, angle, 1f);
}
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle){
create(type, owner, team, x, y, angle, 1f);
}
public static void create(BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl){
create(type, owner, team, x, y, angle, velocityScl, null);
}
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl){
create(type, owner, team, x, y, angle, velocityScl, null);
}
public static void create(BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl, Object data){
Bullet bullet = Pooling.obtain(Bullet.class);
bullet.type = type;
bullet.owner = owner;
bullet.data = data;
public static void create (BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl, Object data){
Bullet bullet = Pooling.obtain(Bullet.class);
bullet.type = type;
bullet.owner = owner;
bullet.data = data;
bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl);
if(type.keepVelocity){
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait) owner).getVelocity() : Vector2.Zero);
}
bullet.hitbox.setSize(type.hitsize);
bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl);
if(type.keepVelocity){
bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait)owner).getVelocity() : Vector2.Zero);
}
bullet.hitbox.setSize(type.hitsize);
bullet.team = team;
bullet.type = type;
bullet.team = team;
bullet.type = type;
//translate bullets backwards, purely for visual reasons
float backDelta = Timers.delta();
//translate bullets backwards, purely for visual reasons
float backDelta = Timers.delta();
bullet.lastPosition().set(x - bullet.velocity.x * backDelta, y - bullet.velocity.y * backDelta, bullet.angle());
bullet.setLastUpdated(TimeUtils.millis());
bullet.setUpdateSpacing((long) ((Timers.delta() / 60f) * 1000));
bullet.set(x - bullet.velocity.x * backDelta, y - bullet.velocity.y * backDelta);
bullet.lastPosition().set(x-bullet.velocity.x * backDelta, y-bullet.velocity.y * backDelta, bullet.angle());
bullet.setLastUpdated(TimeUtils.millis());
bullet.setUpdateSpacing((long)((Timers.delta() / 60f) * 1000));
bullet.set(x-bullet.velocity.x * backDelta, y-bullet.velocity.y * backDelta);
bullet.add();
}
bullet.add();
}
public static void create(BulletType type, Bullet parent, float x, float y, float angle){
create(type, parent.owner, parent.team, x, y, angle);
}
public static void create(BulletType type, Bullet parent, float x, float y, float angle){
create(type, parent.owner, parent.team, x, y, angle);
}
public static void create(BulletType type, Bullet parent, float x, float y, float angle, float velocityScl){
create(type, parent.owner, parent.team, x, y, angle, velocityScl);
}
public static void create(BulletType type, Bullet parent, float x, float y, float angle, float velocityScl){
create(type, parent.owner, parent.team, x, y, angle, velocityScl);
}
@Remote(called = Loc.server, in = In.entities)
public static void createBullet(BulletType type, float x, float y, float angle){
create(type, null, Team.none, x, y, angle);
}
@Remote(called = Loc.server, in = In.entities)
public static void createBullet(BulletType type, float x, float y, float angle){
create(type, null, Team.none, x, y, angle);
}
public boolean collidesTiles(){
return type.collidesTiles;
}
/**Internal use only!*/
public Bullet(){}
public void supressCollision(){
supressCollision = true;
}
public boolean collidesTiles(){
return type.collidesTiles;
}
public void resetOwner(Entity entity, Team team){
this.owner = entity;
this.team = team;
}
public void supressCollision(){
supressCollision = true;
}
public void scaleTime(float add){
time += add;
}
public void resetOwner(Entity entity, Team team){
this.owner = entity;
this.team = team;
}
public Object getData(){
return data;
}
public void scaleTime(float add){
time += add;
}
@Override
public float getDamage(){
if(owner instanceof Unit){
return super.getDamage() * ((Unit) owner).getDamageMultipler();
}
public Object getData() {
return data;
}
return super.getDamage();
}
@Override
public float getDamage(){
if(owner instanceof Unit){
return super.getDamage() * ((Unit) owner).getDamageMultipler();
}
@Override
public boolean isSyncing(){
return type.syncable;
}
return super.getDamage();
}
@Override
public void write(DataOutput data) throws IOException{
data.writeFloat(x);
data.writeFloat(y);
data.writeFloat(velocity.x);
data.writeFloat(velocity.y);
data.writeByte(team.ordinal());
data.writeByte(type.id);
}
@Override
public boolean isSyncing(){
return type.syncable;
}
@Override
public void read(DataInput data, long time) throws IOException{
x = data.readFloat();
y = data.readFloat();
velocity.x = data.readFloat();
velocity.y = data.readFloat();
team = Team.all[data.readByte()];
type = BulletType.getByID(data.readByte());
}
@Override
public void write(DataOutput data) throws IOException {
data.writeFloat(x);
data.writeFloat(y);
data.writeFloat(velocity.x);
data.writeFloat(velocity.y);
data.writeByte(team.ordinal());
data.writeByte(type.id);
}
@Override
public Team getTeam(){
return team;
}
@Override
public void read(DataInput data, long time) throws IOException{
x = data.readFloat();
y = data.readFloat();
velocity.x = data.readFloat();
velocity.y = data.readFloat();
team = Team.all[data.readByte()];
type = BulletType.getByID(data.readByte());
}
@Override
public void draw(){
type.draw(this);
}
@Override
public Team getTeam() {
return team;
}
@Override
public float drawSize(){
return 8;
}
@Override
public void draw(){
type.draw(this);
}
@Override
public boolean collides(SolidTrait other){
return type.collides && super.collides(other);
}
@Override
public float drawSize(){
return 8;
}
@Override
public void collision(SolidTrait other, float x, float y){
super.collision(other, x, y);
@Override
public boolean collides(SolidTrait other){
return type.collides && super.collides(other);
}
if(other instanceof Unit){
Unit unit = (Unit) other;
unit.getVelocity().add(vector.set(other.getX(), other.getY()).sub(x, y).setLength(type.knockback / unit.getMass()));
unit.applyEffect(type.status, type.statusIntensity);
}
}
@Override
public void collision(SolidTrait other, float x, float y){
super.collision(other, x, y);
@Override
public void update(){
super.update();
if(other instanceof Unit){
Unit unit = (Unit)other;
unit.getVelocity().add(vector.set(other.getX(), other.getY()).sub(x, y).setLength(type.knockback / unit.getMass()));
unit.applyEffect(type.status, type.statusIntensity);
}
}
if(type.hitTiles && collidesTiles() && !supressCollision){
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
@Override
public void update(){
super.update();
Tile tile = world.tile(x, y);
if(tile == null) return false;
tile = tile.target();
if (type.hitTiles && collidesTiles() && !supressCollision) {
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
if(tile.entity != null && tile.entity.collide(this) && !tile.entity.isDead() && tile.entity.tile.getTeam() != team){
tile.entity.collision(this);
Tile tile = world.tile(x, y);
if (tile == null) return false;
tile = tile.target();
if(!supressCollision){
type.hit(this);
remove();
}
if (tile.entity != null && tile.entity.collide(this) && !tile.entity.isDead() && tile.entity.tile.getTeam() != team) {
tile.entity.collision(this);
return true;
}
if(!supressCollision){
type.hit(this);
remove();
}
return false;
});
}
return true;
}
supressCollision = false;
}
return false;
});
}
@Override
public void reset(){
super.reset();
timer.clear();
team = null;
data = null;
}
supressCollision = false;
}
@Override
public void removed(){
Pooling.free(this);
}
@Override
public void reset() {
super.reset();
timer.clear();
team = null;
data = null;
}
@Override
public void removed() {
Pooling.free(this);
}
@Override
public EntityGroup targetGroup() {
return bulletGroup;
}
@Override
public EntityGroup targetGroup(){
return bulletGroup;
}
}

View File

@@ -9,65 +9,83 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.impl.BaseBulletType;
public abstract class BulletType extends BaseBulletType<Bullet> implements Content{
private static int lastid = 0;
private static Array<BulletType> types = new Array<>();
private static int lastid = 0;
private static Array<BulletType> types = new Array<>();
public final int id;
/**Knockback in velocity.*/
public float knockback;
/**Whether this bullet hits tiles.*/
public boolean hitTiles = true;
/**Status effect applied on hit.*/
public StatusEffect status = StatusEffects.none;
/**Intensity of applied status effect in terms of duration.*/
public float statusIntensity = 0.5f;
/**What fraction of armor is pierced, 0-1*/
public float armorPierce = 0f;
/**Whether to sync this bullet to clients.*/
public boolean syncable;
/**Whether this bullet type collides with tiles.*/
public boolean collidesTiles = true;
/**Whether this bullet types collides with anything at all.*/
public boolean collides = true;
/**Whether velocity is inherited from the shooter.*/
public boolean keepVelocity = true;
public final int id;
/**
* Knockback in velocity.
*/
public float knockback;
/**
* Whether this bullet hits tiles.
*/
public boolean hitTiles = true;
/**
* Status effect applied on hit.
*/
public StatusEffect status = StatusEffects.none;
/**
* Intensity of applied status effect in terms of duration.
*/
public float statusIntensity = 0.5f;
/**
* What fraction of armor is pierced, 0-1
*/
public float armorPierce = 0f;
/**
* Whether to sync this bullet to clients.
*/
public boolean syncable;
/**
* Whether this bullet type collides with tiles.
*/
public boolean collidesTiles = true;
/**
* Whether this bullet types collides with anything at all.
*/
public boolean collides = true;
/**
* Whether velocity is inherited from the shooter.
*/
public boolean keepVelocity = true;
public BulletType(float speed, float damage){
this.id = lastid ++;
this.speed = speed;
this.damage = damage;
lifetime = 40f;
hiteffect = BulletFx.hitBulletSmall;
despawneffect = BulletFx.despawn;
public BulletType(float speed, float damage){
this.id = lastid++;
this.speed = speed;
this.damage = damage;
lifetime = 40f;
hiteffect = BulletFx.hitBulletSmall;
despawneffect = BulletFx.despawn;
types.add(this);
}
@Override
public void hit(Bullet b, float hitx, float hity){
Effects.effect(hiteffect, hitx, hity, b.angle());
}
types.add(this);
}
@Override
public void despawned(Bullet b){
Effects.effect(despawneffect, b.x, b.y, b.angle());
}
public static BulletType getByID(int id){
return types.get(id);
}
@Override
public String getContentTypeName() {
return "bullettype";
}
public static Array<BulletType> all(){
return types;
}
@Override
public Array<? extends Content> getAll() {
return types;
}
@Override
public void hit(Bullet b, float hitx, float hity){
Effects.effect(hiteffect, hitx, hity, b.angle());
}
public static BulletType getByID(int id){
return types.get(id);
}
@Override
public void despawned(Bullet b){
Effects.effect(despawneffect, b.x, b.y, b.angle());
}
public static Array<BulletType> all(){
return types;
}
@Override
public String getContentTypeName(){
return "bullettype";
}
@Override
public Array<? extends Content> getAll(){
return types;
}
}

View File

@@ -16,10 +16,10 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
public class LiquidBulletType extends BulletType {
public class LiquidBulletType extends BulletType{
Liquid liquid;
public LiquidBulletType(Liquid liquid) {
public LiquidBulletType(Liquid liquid){
super(2.5f, 0);
this.liquid = liquid;
@@ -31,14 +31,14 @@ public class LiquidBulletType extends BulletType {
}
@Override
public void draw(Bullet b) {
public void draw(Bullet b){
Draw.color(liquid.color, Color.WHITE, b.fout() / 100f + Mathf.randomSeedRange(b.id, 0.1f));
Fill.circle(b.x, b.y, 0.5f + b.fout()*2.5f);
Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f);
}
@Override
public void hit(Bullet b, float hitx, float hity) {
public void hit(Bullet b, float hitx, float hity){
Effects.effect(hiteffect, liquid.color, hitx, hity);
Puddle.deposit(world.tileWorld(hitx, hity), liquid, 5f);
@@ -46,7 +46,7 @@ public class LiquidBulletType extends BulletType {
float intensity = 400f;
Fire.extinguish(world.tileWorld(hitx, hity), intensity);
for(GridPoint2 p : Geometry.d4){
Fire.extinguish(world.tileWorld(hitx + p.x*tilesize, hity + p.y*tilesize), intensity);
Fire.extinguish(world.tileWorld(hitx + p.x * tilesize, hity + p.y * tilesize), intensity);
}
}
}

View File

@@ -4,9 +4,9 @@ import io.anuke.mindustry.content.fx.BulletFx;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.ucore.core.Effects;
public class MissileBulletType extends BasicBulletType {
public class MissileBulletType extends BasicBulletType{
public MissileBulletType(float speed, float damage, String bulletSprite) {
public MissileBulletType(float speed, float damage, String bulletSprite){
super(speed, damage, bulletSprite);
backColor = Palette.missileYellowBack;
frontColor = Palette.missileYellow;
@@ -14,7 +14,7 @@ public class MissileBulletType extends BasicBulletType {
}
@Override
public void update(Bullet b) {
public void update(Bullet b){
super.update(b);
if(b.timer.get(0, 4f)){

View File

@@ -10,24 +10,26 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.groundEffectGroup;
/**Class for creating block rubble on the ground.*/
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait {
/**
* Class for creating block rubble on the ground.
*/
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{
private static final Color color = Color.valueOf("52504e");
@Override
public float lifetime() {
public float lifetime(){
return 8200f;
}
@Override
public void draw(){
Draw.color(color.r, color.g, color.b, 1f-Mathf.curve(fin(), 0.98f));
Draw.color(color.r, color.g, color.b, 1f - Mathf.curve(fin(), 0.98f));
drawDecal();
Draw.color();
}
@Override
public EntityGroup targetGroup() {
public EntityGroup targetGroup(){
return groundEffectGroup;
}

View File

@@ -31,7 +31,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable {
public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
private static final IntMap<Fire> map = new IntMap<>();
private static final float baseLifetime = 1000f;
@@ -41,7 +41,15 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
private float baseFlammability = -1, puddleFlammability;
private float lifetime;
/**Start a fire on the tile. If there already is a file there, refreshes its lifetime.*/
/**
* Deserialization use only!
*/
public Fire(){
}
/**
* Start a fire on the tile. If there already is a file there, refreshes its lifetime.
*/
public static void create(Tile tile){
if(Net.client() || tile == null) return; //not clientside.
@@ -60,24 +68,28 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
}
}
/**Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.*/
public static void extinguish(Tile tile, float intensity) {
if (tile != null && map.containsKey(tile.packedPosition())) {
/**
* Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing.
*/
public static void extinguish(Tile tile, float intensity){
if(tile != null && map.containsKey(tile.packedPosition())){
map.get(tile.packedPosition()).time += intensity * Timers.delta();
}
}
/**Deserialization use only!*/
public Fire(){}
@Remote(called = Loc.server, in = In.entities)
public static void onFireRemoved(int fireid){
fireGroup.removeByID(fireid);
}
@Override
public float lifetime() {
public float lifetime(){
return lifetime;
}
@Override
public void update() {
if(Mathf.chance(0.1 * Timers.delta())) {
public void update(){
if(Mathf.chance(0.1 * Timers.delta())){
Effects.effect(EnvironmentFx.fire, x + Mathf.range(4f), y + Mathf.range(4f));
}
@@ -91,9 +103,10 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
time = Mathf.clamp(time + Timers.delta(), 0, lifetime());
if(time >= lifetime()){
if(time >= lifetime() || tile == null){
CallEntity.onFireRemoved(getID());
remove();
return;
}
TileEntity entity = tile.target().entity;
@@ -102,19 +115,19 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
float flammability = baseFlammability + puddleFlammability;
if(!damage && flammability <= 0){
time += Timers.delta()*8;
time += Timers.delta() * 8;
}
if (baseFlammability < 0 || block != tile.block()){
if(baseFlammability < 0 || block != tile.block()){
baseFlammability = tile.block().getFlammability(tile);
block = tile.block();
}
if(damage) {
if(damage){
lifetime += Mathf.clamp(flammability / 8f, 0f, 0.6f) * Timers.delta();
}
if (flammability > 1f && Mathf.chance(0.03 * Timers.delta() * Mathf.clamp(flammability/5f, 0.3f, 2f))) {
if(flammability > 1f && Mathf.chance(0.03 * Timers.delta() * Mathf.clamp(flammability / 5f, 0.3f, 2f))){
GridPoint2 p = Mathf.select(Geometry.d4);
Tile other = world.tile(tile.x + p.x, tile.y + p.y);
create(other);
@@ -127,7 +140,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
if(Mathf.chance(0.1 * Timers.delta())){
Puddle p = Puddle.getPuddle(tile);
if(p != null){
puddleFlammability = p.getFlammability()/3f;
puddleFlammability = p.getFlammability() / 3f;
}else{
puddleFlammability = 0;
}
@@ -140,14 +153,14 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
}
@Override
public void writeSave(DataOutput stream) throws IOException {
public void writeSave(DataOutput stream) throws IOException{
stream.writeInt(tile.packedPosition());
stream.writeFloat(lifetime);
stream.writeFloat(time);
}
@Override
public void readSave(DataInput stream) throws IOException {
public void readSave(DataInput stream) throws IOException{
this.loadedPosition = stream.readInt();
this.lifetime = stream.readFloat();
this.time = stream.readFloat();
@@ -155,19 +168,19 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
}
@Override
public void write(DataOutput data) throws IOException {
public void write(DataOutput data) throws IOException{
data.writeFloat(x);
data.writeFloat(y);
}
@Override
public void read(DataInput data, long time) throws IOException {
public void read(DataInput data, long time) throws IOException{
x = data.readFloat();
y = data.readFloat();
}
@Override
public void reset() {
public void reset(){
loadedPosition = -1;
tile = null;
baseFlammability = -1;
@@ -175,7 +188,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
}
@Override
public void added() {
public void added(){
if(loadedPosition != -1){
map.put(loadedPosition, this);
tile = world.tile(loadedPosition);
@@ -184,7 +197,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
}
@Override
public void removed() {
public void removed(){
if(tile != null){
map.remove(tile.packedPosition());
}
@@ -192,12 +205,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
}
@Override
public EntityGroup targetGroup() {
public EntityGroup targetGroup(){
return fireGroup;
}
@Remote(called = Loc.server, in = In.entities)
public static void onFireRemoved(int fireid){
fireGroup.removeByID(fireid);
}
}

View File

@@ -9,27 +9,29 @@ import io.anuke.ucore.entities.impl.EffectEntity;
import io.anuke.ucore.function.EffectRenderer;
import io.anuke.ucore.util.Mathf;
/**A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer.*/
public class GroundEffectEntity extends EffectEntity {
/**
* A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer.
*/
public class GroundEffectEntity extends EffectEntity{
private boolean once;
@Override
public void update(){
GroundEffect effect = (GroundEffect)this.effect;
GroundEffect effect = (GroundEffect) this.effect;
if(effect.isStatic) {
if(effect.isStatic){
time += Timers.delta();
time = Mathf.clamp(time, 0, effect.staticLife);
if (!once && time >= lifetime()) {
if(!once && time >= lifetime()){
once = true;
time = 0f;
Tile tile = Vars.world.tileWorld(x, y);
if(tile != null && tile.floor().isLiquid){
remove();
}
} else if (once && time >= effect.staticLife) {
}else if(once && time >= effect.staticLife){
remove();
}
}else{
@@ -39,7 +41,7 @@ public class GroundEffectEntity extends EffectEntity {
@Override
public void draw(){
GroundEffect effect = (GroundEffect)this.effect;
GroundEffect effect = (GroundEffect) this.effect;
if(once && effect.isStatic)
Effects.renderEffect(id, effect, color, lifetime(), rotation, x, y, data);
@@ -48,32 +50,38 @@ public class GroundEffectEntity extends EffectEntity {
}
@Override
public void reset() {
public void reset(){
super.reset();
once = false;
}
/**An effect that is rendered on the ground layer as opposed to the top layer.*/
/**
* An effect that is rendered on the ground layer as opposed to the top layer.
*/
public static class GroundEffect extends Effect{
/**How long this effect stays on the ground when static.*/
/**
* How long this effect stays on the ground when static.
*/
public final float staticLife;
/**If true, this effect will stop and lie on the ground for a specific duration,
* after its initial lifetime is over.*/
/**
* If true, this effect will stop and lie on the ground for a specific duration,
* after its initial lifetime is over.
*/
public final boolean isStatic;
public GroundEffect(float life, float staticLife, EffectRenderer draw) {
public GroundEffect(float life, float staticLife, EffectRenderer draw){
super(life, draw);
this.staticLife = staticLife;
this.isStatic = true;
}
public GroundEffect(boolean isStatic, float life, EffectRenderer draw) {
public GroundEffect(boolean isStatic, float life, EffectRenderer draw){
super(life, draw);
this.staticLife = 0f;
this.isStatic = isStatic;
}
public GroundEffect(float life, EffectRenderer draw) {
public GroundEffect(float life, EffectRenderer draw){
super(life, draw);
this.staticLife = 0f;
this.isStatic = false;

View File

@@ -35,7 +35,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawTrait, VelocityTrait, TimeTrait, TargetTrait, Poolable {
public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawTrait, VelocityTrait, TimeTrait, TargetTrait, Poolable{
private static final float sinkLifetime = 80f;
private Interpolator interpolator = new Interpolator();
@@ -46,6 +46,14 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
private float time;
private float sinktime;
/**
* Internal use only!
*/
public ItemDrop(){
hitbox.setSize(5f);
hitboxTile.setSize(5f);
}
public static ItemDrop create(Item item, int amount, float x, float y, float angle){
ItemDrop drop = new ItemDrop();
drop.item = item;
@@ -73,13 +81,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
}
}
/**Internal use only!*/
public ItemDrop(){
hitbox.setSize(5f);
hitboxTile.setSize(5f);
}
public Item getItem() {
public Item getItem(){
return item;
}
@@ -88,66 +90,66 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
}
@Override
public boolean isDead() {
public boolean isDead(){
return !isAdded();
}
@Override
public Team getTeam() {
public Team getTeam(){
return Team.none;
}
@Override
public float lifetime() {
return 60*60;
public float lifetime(){
return 60 * 60;
}
@Override
public void time(float time) {
public void time(float time){
this.time = time;
}
@Override
public float time() {
public float time(){
return time;
}
@Override
public Vector2 getVelocity() {
public Vector2 getVelocity(){
return velocity;
}
@Override
public boolean collides(SolidTrait other) {
public boolean collides(SolidTrait other){
return other instanceof Player && time > 20f;
}
@Override
public void collision(SolidTrait other, float x, float y) {
Unit player = (Unit)other;
public void collision(SolidTrait other, float x, float y){
Unit player = (Unit) other;
if(player.inventory.canAcceptItem(item, 1)){
int used = Math.min(amount, player.inventory.capacity() - player.inventory.getItem().amount);
player.inventory.addItem(item, used);
amount -= used;
if(amount <= 0) {
if(amount <= 0){
CallEntity.onPickup(getID());
}
}
}
@Override
public void draw() {
float size = itemSize * (1f - sinktime/sinkLifetime) * (1f-Mathf.curve(fin(), 0.98f));
public void draw(){
float size = itemSize * (1f - sinktime / sinkLifetime) * (1f - Mathf.curve(fin(), 0.98f));
Tile tile = world.tileWorld(x, y);
Draw.color(Color.WHITE, tile == null || !tile.floor().isLiquid ? Color.WHITE : tile.floor().liquidColor, sinktime/sinkLifetime);
Draw.color(Color.WHITE, tile == null || !tile.floor().isLiquid ? Color.WHITE : tile.floor().liquidColor, sinktime / sinkLifetime);
Draw.rect(item.region, x, y, size, size);
int stored = Mathf.clamp(amount / 6, 1, 8);
for(int i = 0; i < stored; i ++) {
for(int i = 0; i < stored; i++){
float px = stored == 1 ? 0 : Mathf.randomSeedRange(i + 1, 4f);
float py = stored == 1 ? 0 : Mathf.randomSeedRange(i + 2, 4f);
Draw.rect(item.region, x + px, y + py, size, size);
@@ -157,8 +159,8 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
}
@Override
public void update() {
if(Net.client()) {
public void update(){
if(Net.client()){
interpolate();
}else{
updateVelocity(0.2f);
@@ -190,28 +192,28 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
}
@Override
public void reset() {
public void reset(){
time = 0f;
interpolator.reset();
}
@Override
public Interpolator getInterpolator() {
public Interpolator getInterpolator(){
return interpolator;
}
@Override
public float drawSize() {
public float drawSize(){
return 10;
}
@Override
public EntityGroup targetGroup() {
public EntityGroup targetGroup(){
return itemGroup;
}
@Override
public void writeSave(DataOutput data) throws IOException {
public void writeSave(DataOutput data) throws IOException{
data.writeFloat(x);
data.writeFloat(y);
data.writeByte(item.id);
@@ -219,7 +221,7 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
}
@Override
public void readSave(DataInput data) throws IOException {
public void readSave(DataInput data) throws IOException{
x = data.readFloat();
y = data.readFloat();
item = Item.getByID(data.readByte());

View File

@@ -32,17 +32,22 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
private PosTrait to;
private Runnable done;
public ItemTransfer(){
}
@Remote(in = In.entities, called = Loc.server, unreliable = true)
public static void transferAmmo(Item item, float x, float y, Unit to){
if(to == null) return;
to.addAmmo(item);
create(item, x, y, to, () -> {});
create(item, x, y, to, () -> {
});
}
@Remote(in = In.entities, called = Loc.server, unreliable = true)
public static void transferItemEffect(Item item, float x, float y, Unit to){
if(to == null) return;
create(item, x, y, to, () -> {});
create(item, x, y, to, () -> {
});
}
@Remote(in = In.entities, called = Loc.server, unreliable = true)
@@ -54,10 +59,11 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
@Remote(in = In.entities, called = Loc.server)
public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){
if(tile == null) return;
for (int i = 0; i < Mathf.clamp(amount/3, 1, 8); i++) {
Timers.run(i*3, () -> create(item, x, y, tile, () -> {}));
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
Timers.run(i * 3, () -> create(item, x, y, tile, () -> {
}));
}
tile.entity.items.addItem(item, amount);
tile.entity.items.add(item, amount);
}
public static void create(Item item, float fromx, float fromy, PosTrait to, Runnable done){
@@ -70,15 +76,13 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
tr.add();
}
public ItemTransfer(){}
@Override
public float lifetime() {
public float lifetime(){
return 60;
}
@Override
public void reset() {
public void reset(){
super.reset();
item = null;
to = null;
@@ -89,7 +93,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
}
@Override
public void removed() {
public void removed(){
if(done != null){
threads.run(done);
}
@@ -97,7 +101,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
}
@Override
public void update() {
public void update(){
if(to == null){
remove();
return;
@@ -110,24 +114,24 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
}
@Override
public void draw() {
float length = fslope()*6f;
public void draw(){
float length = fslope() * 6f;
float angle = current.set(x, y).sub(from).angle();
Draw.color(Palette.accent);
Lines.stroke(fslope()*2f);
Lines.stroke(fslope() * 2f);
Lines.circle(x, y, fslope()*2f);
Lines.circle(x, y, fslope() * 2f);
Lines.lineAngleCenter(x, y, angle, length);
Lines.lineAngle(x, y, angle, fout()*6f);
Lines.lineAngle(x, y, angle, fout() * 6f);
Draw.color(item.color);
Fill.circle(x, y, fslope()*1.5f);
Fill.circle(x, y, fslope() * 1.5f);
Draw.reset();
}
@Override
public EntityGroup targetGroup() {
public EntityGroup targetGroup(){
return effectGroup;
}

View File

@@ -46,7 +46,15 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
private Color color = Palette.lancerLaser;
private SeedRandom random = new SeedRandom();
/**Create a lighting branch at a location. Use Team.none to damage everyone.*/
/**
* For pooling use only. Do not call directly!
*/
public Lightning(){
}
/**
* Create a lighting branch at a location. Use Team.none to damage everyone.
*/
public static void create(Team team, Effect effect, Color color, float damage, float x, float y, float targetAngle, int length){
CallEntity.createLighting(lastSeed++, team, effect, color, damage, x, y, targetAngle, length);
}
@@ -69,7 +77,7 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
Units.getNearbyEnemies(team, rect, entities::add);
for(int i = 0; i < length; i ++){
for(int i = 0; i < length; i++){
l.lines.add(new Vector2(x, y));
float fx = x, fy = y;
@@ -82,15 +90,15 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
Units.getNearbyEnemies(team, rect, entity -> {
float dst = entity.distanceTo(x2, y2);
if(dst < attractRange) {
if(dst < attractRange){
angle = Mathf.slerp(angle, Angles.angle(x2, y2, entity.x, entity.y), (attractRange - dst) / attractRange / 4f);
}
entity.getHitbox(hitrect);
hitrect.x -= range/2f;
hitrect.y -= range/2f;
hitrect.width += range/2f;
hitrect.height += range/2f;
hitrect.x -= range / 2f;
hitrect.y -= range / 2f;
hitrect.width += range / 2f;
hitrect.height += range / 2f;
if(hitrect.contains(x2, y2) || hitrect.contains(fx, fy)){
float result = damage;
@@ -104,7 +112,7 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
});
if(l.random.chance(0.1f)){
createLighting(l.random.nextInt(), team, effect, color, damage, x2, y2, angle + l.random.range(100f), length/3);
createLighting(l.random.nextInt(), team, effect, color, damage, x2, y2, angle + l.random.range(100f), length / 3);
}
x = x2;
@@ -115,47 +123,44 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
l.add();
}
/**For pooling use only. Do not call directly!*/
public Lightning(){}
@Override
public boolean isSyncing() {
public boolean isSyncing(){
return false;
}
@Override
public void write(DataOutput data) throws IOException {
public void write(DataOutput data) throws IOException{
}
@Override
public void read(DataInput data, long time) throws IOException {
public void read(DataInput data, long time) throws IOException{
}
@Override
public float lifetime() {
public float lifetime(){
return 10;
}
@Override
public void reset() {
public void reset(){
color = Palette.lancerLaser;
lines.clear();
}
@Override
public void removed() {
public void removed(){
Pooling.free(this);
}
@Override
public void draw() {
public void draw(){
float lx = x, ly = y;
Draw.color(color, Color.WHITE, fin());
for(int i = 0; i < lines.size; i ++){
for(int i = 0; i < lines.size; i++){
Vector2 v = lines.get(i);
Lines.stroke(fout() * 3f + 1f-(float)i/lines.size);
Lines.stroke(fout() * 3f + 1f - (float) i / lines.size);
Lines.line(lx, ly, v.x, v.y);
lx = v.x;
ly = v.y;
@@ -164,7 +169,7 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
}
@Override
public EntityGroup targetGroup() {
public EntityGroup targetGroup(){
return bulletGroup;
}
}

View File

@@ -40,7 +40,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.puddleGroup;
import static io.anuke.mindustry.Vars.world;
public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait {
public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait{
private static final IntMap<Puddle> map = new IntMap<>();
private static final float maxLiquid = 70f;
private static final int maxGeneration = 2;
@@ -58,17 +58,29 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
private float accepting;
private byte generation;
/**Deposists a puddle between tile and source.*/
/**
* Deserialization use only!
*/
public Puddle(){
}
/**
* Deposists a puddle between tile and source.
*/
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
deposit(tile, source, liquid, amount, 0);
}
/**Deposists a puddle at a tile.*/
/**
* Deposists a puddle at a tile.
*/
public static void deposit(Tile tile, Liquid liquid, float amount){
deposit(tile, tile, liquid, amount, 0);
}
/**Returns the puddle on the specified tile. May return null.*/
/**
* Returns the puddle on the specified tile. May return null.
*/
public static Puddle getPuddle(Tile tile){
return map.get(tile.packedPosition());
}
@@ -76,11 +88,11 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
private static void deposit(Tile tile, Tile source, Liquid liquid, float amount, int generation){
if(tile.floor().isLiquid && !canStayOn(liquid, tile.floor().liquidDrop)){
reactPuddle(tile.floor().liquidDrop, liquid, amount, tile,
(tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
if(generation == 0 && Timers.get(tile, "ripple", 50)){
Effects.effect(BlockFx.ripple, tile.floor().liquidDrop.color,
(tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
(tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
}
return;
}
@@ -93,28 +105,32 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
puddle.tile = tile;
puddle.liquid = liquid;
puddle.amount = amount;
puddle.generation = (byte)generation;
puddle.set((tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
puddle.generation = (byte) generation;
puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
puddle.add();
map.put(tile.packedPosition(), puddle);
}else if(p.liquid == liquid){
p.accepting = Math.max(amount, p.accepting);
if(generation == 0 && Timers.get(p, "ripple2", 50) && p.amount >= maxLiquid/2f){
Effects.effect(BlockFx.ripple, p.liquid.color, (tile.worldx() + source.worldx())/2f, (tile.worldy() + source.worldy())/2f);
if(generation == 0 && Timers.get(p, "ripple2", 50) && p.amount >= maxLiquid / 2f){
Effects.effect(BlockFx.ripple, p.liquid.color, (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
}
}else{
p.amount -= reactPuddle(p.liquid, liquid, amount, p.tile, p.x, p.y);
}
}
/**Returns whether the first liquid can 'stay' on the second one.
* Currently, the only place where this can happen is oil on water.*/
/**
* Returns whether the first liquid can 'stay' on the second one.
* Currently, the only place where this can happen is oil on water.
*/
private static boolean canStayOn(Liquid liquid, Liquid other){
return liquid == Liquids.oil && other == Liquids.water;
}
/**Reacts two liquids together at a location.*/
/**
* Reacts two liquids together at a location.
*/
private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){
if((dest.flammability > 0.3f && liquid.temperature > 0.7f) ||
(liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid
@@ -126,25 +142,27 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
if(Mathf.chance(0.5f * amount)){
Effects.effect(EnvironmentFx.steam, x, y);
}
return - 0.1f * amount;
return -0.1f * amount;
}else if(liquid.temperature > 0.7f && dest.temperature < 0.55f){ //hot liquid poured onto cold puddle
if(Mathf.chance(0.8f * amount)){
Effects.effect(EnvironmentFx.steam, x, y);
}
return - 0.4f * amount;
return -0.4f * amount;
}
return 0f;
}
/**Deserialization use only!*/
public Puddle(){}
@Remote(called = Loc.server, in = In.entities)
public static void onPuddleRemoved(int puddleid){
puddleGroup.removeByID(puddleid);
}
public float getFlammability(){
return liquid.flammability * amount;
}
@Override
public void update() {
public void update(){
//no updating happens clientside
if(Net.client()){
@@ -158,11 +176,11 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
amount += accepting;
accepting = 0f;
if (amount >= maxLiquid / 1.5f && generation < maxGeneration) {
if(amount >= maxLiquid / 1.5f && generation < maxGeneration){
float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Timers.delta();
for (GridPoint2 point : Geometry.d4) {
for(GridPoint2 point : Geometry.d4){
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
if (other.block() == Blocks.air && other.cliffs == 0) {
if(other.block() == Blocks.air && other.cliffs == 0){
deposit(other, tile, liquid, deposited, generation + 1);
amount -= deposited / 2f; //tweak to speed up/slow down puddle propagation
}
@@ -171,14 +189,14 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
amount = Mathf.clamp(amount, 0, maxLiquid);
if (amount <= 0f) {
if(amount <= 0f){
CallEntity.onPuddleRemoved(getID());
}
}
//effects-only code
if(amount >= maxLiquid/2f && updateTime <= 0f){
Units.getNearby(rect.setSize(Mathf.clamp(amount/(maxLiquid/1.5f))*10f).setCenter(x, y), unit -> {
if(amount >= maxLiquid / 2f && updateTime <= 0f){
Units.getNearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unit -> {
if(unit.isFlying()) return;
unit.getHitbox(rect2);
@@ -186,7 +204,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
unit.applyEffect(liquid.effect, 0.5f);
if(unit.getVelocity().len() > 0.1) {
if(unit.getVelocity().len() > 0.1){
Effects.effect(BlockFx.ripple, liquid.color, unit.x, unit.y);
}
});
@@ -202,30 +220,30 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
}
@Override
public void draw() {
public void draw(){
seeds = id;
boolean onLiquid = tile.floor().isLiquid;
float f = Mathf.clamp(amount/(maxLiquid/1.5f));
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
float smag = onLiquid ? 0.8f : 0f;
float sscl = 20f;
Draw.color(Hue.shift(tmp.set(liquid.color), 2, -0.05f));
Fill.circle(x + Mathf.sin(Timers.time() + seeds*532, sscl, smag), y + Mathf.sin(Timers.time() + seeds*53, sscl, smag), f * 8f);
Fill.circle(x + Mathf.sin(Timers.time() + seeds * 532, sscl, smag), y + Mathf.sin(Timers.time() + seeds * 53, sscl, smag), f * 8f);
Angles.randLenVectors(id, 3, f * 6f, (ex, ey) -> {
Fill.circle(x + ex + Mathf.sin(Timers.time() + seeds*532, sscl, smag),
y + ey + Mathf.sin(Timers.time() + seeds*53, sscl, smag), f * 5f);
seeds ++;
Fill.circle(x + ex + Mathf.sin(Timers.time() + seeds * 532, sscl, smag),
y + ey + Mathf.sin(Timers.time() + seeds * 53, sscl, smag), f * 5f);
seeds++;
});
Draw.color();
}
@Override
public float drawSize() {
public float drawSize(){
return 20;
}
@Override
public void writeSave(DataOutput stream) throws IOException {
public void writeSave(DataOutput stream) throws IOException{
stream.writeInt(tile.packedPosition());
stream.writeFloat(x);
stream.writeFloat(y);
@@ -235,7 +253,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
}
@Override
public void readSave(DataInput stream) throws IOException {
public void readSave(DataInput stream) throws IOException{
this.loadedPosition = stream.readInt();
this.x = stream.readFloat();
this.y = stream.readFloat();
@@ -246,7 +264,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
}
@Override
public void reset() {
public void reset(){
loadedPosition = -1;
tile = null;
liquid = null;
@@ -256,7 +274,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
}
@Override
public void added() {
public void added(){
if(loadedPosition != -1){
map.put(loadedPosition, this);
tile = world.tile(loadedPosition);
@@ -264,38 +282,33 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
}
@Override
public void removed() {
public void removed(){
map.remove(tile.packedPosition());
reset();
}
@Override
public void write(DataOutput data) throws IOException {
public void write(DataOutput data) throws IOException{
data.writeFloat(x);
data.writeFloat(y);
data.writeByte(liquid.id);
data.writeShort((short)(amount * 4));
data.writeShort((short) (amount * 4));
data.writeInt(tile.packedPosition());
}
@Override
public void read(DataInput data, long time) throws IOException {
public void read(DataInput data, long time) throws IOException{
x = data.readFloat();
y = data.readFloat();
liquid = Liquid.getByID(data.readByte());
targetAmount = data.readShort()/4f;
targetAmount = data.readShort() / 4f;
tile = world.tile(data.readInt());
map.put(tile.packedPosition(), this);
}
@Override
public EntityGroup targetGroup() {
public EntityGroup targetGroup(){
return puddleGroup;
}
@Remote(called = Loc.server, in = In.entities)
public static void onPuddleRemoved(int puddleid){
puddleGroup.removeByID(puddleid);
}
}

View File

@@ -3,10 +3,12 @@ package io.anuke.mindustry.entities.effect;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf;
public class RubbleDecal extends Decal {
public class RubbleDecal extends Decal{
private int size;
/**Creates a rubble effect at a position. Provide a block size to use.*/
/**
* Creates a rubble effect at a position. Provide a block size to use.
*/
public static void create(float x, float y, int size){
RubbleDecal decal = new RubbleDecal();
decal.size = size;

View File

@@ -8,21 +8,21 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.world;
public class ScorchDecal extends Decal {
public class ScorchDecal extends Decal{
private static final int scorches = 5;
private static final TextureRegion[] regions = new TextureRegion[scorches];
public static void create(float x, float y){
if(regions[0] == null){
for (int i = 0; i < regions.length; i++) {
regions[i] = Draw.region("scorch" + (i+1));
for(int i = 0; i < regions.length; i++){
regions[i] = Draw.region("scorch" + (i + 1));
}
}
Tile tile = world.tileWorld(x, y);
if(tile == null || tile.floor().liquidDrop != null) return;
ScorchDecal decal = new ScorchDecal();
decal.set(x, y);
decal.add();
@@ -31,10 +31,10 @@ public class ScorchDecal extends Decal {
@Override
public void drawDecal(){
for (int i = 0; i < 5; i++) {
TextureRegion region = regions[Mathf.randomSeed(id - i, 0, scorches-1)];
for(int i = 0; i < 5; i++){
TextureRegion region = regions[Mathf.randomSeed(id - i, 0, scorches - 1)];
float rotation = Mathf.randomSeed(id + i, 0, 360);
float space = 1.5f + Mathf.randomSeed(id + i + 1, 0, 20)/10f;
float space = 1.5f + Mathf.randomSeed(id + i + 1, 0, 20) / 10f;
Draw.grect(region, x + Angles.trnsx(rotation, space), y + Angles.trnsy(rotation, space), rotation - 90);
}
}

View File

@@ -13,44 +13,43 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.shieldGroup;
//todo re-implement
public class Shield extends BaseEntity implements DrawTrait {
public boolean active;
public boolean hitPlayers = false;
public float radius = 0f;
private float uptime = 0f;
private final Tile tile;
public Shield(Tile tile){
this.tile = tile;
this.x = tile.worldx();
this.y = tile.worldy();
}
public float drawSize(){
return 150;
}
@Override
public void update(){
float alpha = 0.1f;
Interpolation interp = Interpolation.fade;
if(active){
uptime = interp.apply(uptime, 1f, alpha * Timers.delta());
}else{
uptime = interp.apply(uptime, 0f, alpha * Timers.delta());
if(uptime <= 0.05f)
remove();
}
uptime = Mathf.clamp(uptime);
if(!(tile.block() instanceof ShieldBlock)){
remove();
return;
}
ShieldBlock block = (ShieldBlock)tile.block();
public class Shield extends BaseEntity implements DrawTrait{
private final Tile tile;
public boolean active;
public boolean hitPlayers = false;
public float radius = 0f;
private float uptime = 0f;
public Shield(Tile tile){
this.tile = tile;
this.x = tile.worldx();
this.y = tile.worldy();
}
public float drawSize(){
return 150;
}
@Override
public void update(){
float alpha = 0.1f;
Interpolation interp = Interpolation.fade;
if(active){
uptime = interp.apply(uptime, 1f, alpha * Timers.delta());
}else{
uptime = interp.apply(uptime, 0f, alpha * Timers.delta());
if(uptime <= 0.05f)
remove();
}
uptime = Mathf.clamp(uptime);
if(!(tile.block() instanceof ShieldBlock)){
remove();
return;
}
ShieldBlock block = (ShieldBlock) tile.block();
/*
Entities.getNearby(bulletGroup, x, y, block.shieldRadius * 2*uptime + 10, entity->{
@@ -64,39 +63,39 @@ public class Shield extends BaseEntity implements DrawTrait {
}
}
});*/
}
@Override
public void draw(){
if(!(tile.block() instanceof ShieldBlock) || radius <= 1f){
return;
}
}
Fill.circle(x, y, drawRadius());
}
float drawRadius(){
return (radius + Mathf.sin(Timers.time(), 25f, 1f));
}
public void removeDelay(){
active = false;
}
@Override
public void draw(){
if(!(tile.block() instanceof ShieldBlock) || radius <= 1f){
return;
}
@Override
public EntityGroup targetGroup() {
return shieldGroup;
}
Fill.circle(x, y, drawRadius());
}
float drawRadius(){
return (radius + Mathf.sin(Timers.time(), 25f, 1f));
}
public void removeDelay(){
active = false;
}
@Override
public EntityGroup targetGroup(){
return shieldGroup;
}
@Override
public void added(){
active = true;
}
@Override
public void removed(){
active = false;
uptime = 0f;
}
@Override
public void added(){
active = true;
}
@Override
public void removed(){
active = false;
uptime = 0f;
}
}

View File

@@ -1,5 +1,7 @@
package io.anuke.mindustry.entities.traits;
/**A flag interface for marking an effect as appearing below liquids.*/
public interface BelowLiquidTrait {
/**
* A flag interface for marking an effect as appearing below liquids.
*/
public interface BelowLiquidTrait{
}

View File

@@ -37,27 +37,46 @@ import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.tmptr;
import static io.anuke.mindustry.Vars.world;
/**Interface for units that build, break or mine things.*/
/**
* Interface for units that build, break or mine things.
*/
public interface BuilderTrait extends Entity{
//these are not instance variables!
float placeDistance = 140f;
float mineDistance = 70f;
/**Returns the queue for storing build requests.*/
/**
* Returns the queue for storing build requests.
*/
Queue<BuildRequest> getPlaceQueue();
/**Returns the tile this builder is currently mining.*/
/**
* Returns the tile this builder is currently mining.
*/
Tile getMineTile();
/**Sets the tile this builder is currently mining.*/
/**
* Sets the tile this builder is currently mining.
*/
void setMineTile(Tile tile);
/**Returns the minining speed of this miner. 1 = standard, 0.5 = half speed, 2 = double speed, etc.*/
/**
* Returns the minining speed of this miner. 1 = standard, 0.5 = half speed, 2 = double speed, etc.
*/
float getMinePower();
/**Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.*/
/**
* Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all.
*/
float getBuildPower(Tile tile);
/**
* Whether this type of builder can begin creating new blocks.
*/
default boolean canCreateBlocks(){
return true;
}
default void writeBuilding(DataOutput output) throws IOException{
BuildRequest request = getCurrentRequest();
@@ -78,17 +97,17 @@ public interface BuilderTrait extends Entity{
}
default void readBuilding(DataInput input, boolean applyChanges) throws IOException{
synchronized (getPlaceQueue()) {
synchronized(getPlaceQueue()){
if(applyChanges) getPlaceQueue().clear();
byte type = input.readByte();
if (type != -1) {
if(type != -1){
int position = input.readInt();
BuildRequest request;
if (type == 1) { //remove
if(type == 1){ //remove
request = new BuildRequest(position % world.width(), position / world.width());
} else { //place
}else{ //place
byte recipe = input.readByte();
byte rotation = input.readByte();
request = new BuildRequest(position % world.width(), position / world.width(), rotation, Recipe.getByID(recipe));
@@ -101,17 +120,21 @@ public interface BuilderTrait extends Entity{
}
}
/**Return whether this builder's place queue contains items.*/
/**
* Return whether this builder's place queue contains items.
*/
default boolean isBuilding(){
return getPlaceQueue().size != 0;
}
/**If a place request matching this signature is present, it is removed.
* Otherwise, a new place request is added to the queue.*/
/**
* If a place request matching this signature is present, it is removed.
* Otherwise, a new place request is added to the queue.
*/
default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
synchronized (getPlaceQueue()) {
for (BuildRequest request : getPlaceQueue()) {
if (request.x == x && request.y == y) {
synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){
if(request.x == x && request.y == y){
clearBuilding();
addBuildRequest(request);
return;
@@ -122,16 +145,20 @@ public interface BuilderTrait extends Entity{
addBuildRequest(new BuildRequest(x, y, rotation, recipe));
}
/**Clears the placement queue.*/
/**
* Clears the placement queue.
*/
default void clearBuilding(){
getPlaceQueue().clear();
}
/**Add another build requests to the tail of the queue, if it doesn't exist there yet.*/
/**
* Add another build requests to the tail of the queue, if it doesn't exist there yet.
*/
default void addBuildRequest(BuildRequest place){
synchronized (getPlaceQueue()) {
for (BuildRequest request : getPlaceQueue()) {
if (request.x == place.x && request.y == place.y) {
synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){
if(request.x == place.x && request.y == place.y){
return;
}
}
@@ -139,16 +166,20 @@ public interface BuilderTrait extends Entity{
}
}
/**Return the build requests currently active, or the one at the top of the queue.
* May return null.*/
/**
* Return the build requests currently active, or the one at the top of the queue.
* May return null.
*/
default BuildRequest getCurrentRequest(){
synchronized (getPlaceQueue()) {
synchronized(getPlaceQueue()){
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
}
}
/**Update building mechanism for this unit.
* This includes mining.*/
/**
* Update building mechanism for this unit.
* This includes mining.
*/
default void updateBuilding(Unit unit){
BuildRequest current = getCurrentRequest();
@@ -171,10 +202,10 @@ public interface BuilderTrait extends Entity{
Tile tile = world.tile(current.x, current.y);
if (!(tile.block() instanceof BuildBlock)) {
if(!current.remove && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)) {
if(!(tile.block() instanceof BuildBlock)){
if(canCreateBlocks() && !current.remove && Build.validPlace(unit.getTeam(), current.x, current.y, current.recipe.result, current.rotation)){
Build.beginPlace(unit.getTeam(), current.x, current.y, current.recipe, current.rotation);
}else if(current.remove && Build.validBreak(unit.getTeam(), current.x, current.y)){
}else if(canCreateBlocks() && current.remove && Build.validBreak(unit.getTeam(), current.x, current.y)){
Build.beginBreak(unit.getTeam(), current.x, current.y);
}else{
getPlaceQueue().removeFirst();
@@ -198,7 +229,9 @@ public interface BuilderTrait extends Entity{
current.progress = entity.progress();
}
/**Do not call directly.*/
/**
* Do not call directly.
*/
default void updateMining(Unit unit){
Tile tile = getMineTile();
@@ -211,26 +244,28 @@ public interface BuilderTrait extends Entity{
if(unit.inventory.canAcceptItem(item) &&
Mathf.chance(Timers.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){
CallEntity.transferItemToUnit(item,
tile.worldx() + Mathf.range(tilesize/2f),
tile.worldy() + Mathf.range(tilesize/2f),
tile.worldx() + Mathf.range(tilesize / 2f),
tile.worldy() + Mathf.range(tilesize / 2f),
unit);
}
if(Mathf.chance(0.06 * Timers.delta())){
Effects.effect(BlockFx.pulverizeSmall,
tile.worldx() + Mathf.range(tilesize/2f),
tile.worldy() + Mathf.range(tilesize/2f), 0f, item.color);
tile.worldx() + Mathf.range(tilesize / 2f),
tile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color);
}
}
}
/**Draw placement effects for an entity. This includes mining*/
/**
* Draw placement effects for an entity. This includes mining
*/
default void drawBuilding(Unit unit){
BuildRequest request;
synchronized (getPlaceQueue()) {
if (!isBuilding()) {
if (getMineTile() != null) {
synchronized(getPlaceQueue()){
if(!isBuilding()){
if(getMineTile() != null){
drawMining(unit);
}
return;
@@ -250,7 +285,7 @@ public interface BuilderTrait extends Entity{
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
float py = unit.y + Angles.trnsy(unit.rotation, focusLen);
float sz = Vars.tilesize*tile.block().size/2f;
float sz = Vars.tilesize * tile.block().size / 2f;
float ang = unit.angleTo(tile);
tmptr[0].set(tile.drawx() - sz, tile.drawy() - sz);
@@ -281,14 +316,16 @@ public interface BuilderTrait extends Entity{
Draw.color();
}
/**Internal use only.*/
/**
* Internal use only.
*/
default void drawMining(Unit unit){
Tile tile = getMineTile();
if(tile == null) return;
float focusLen = 4f + Mathf.absin(Timers.time(), 1.1f, 0.5f);
float swingScl = 12f, swingMag = tilesize/8f;
float swingScl = 12f, swingMag = tilesize / 8f;
float flashScl = 0.3f;
float px = unit.x + Angles.trnsx(unit.rotation, focusLen);
@@ -297,10 +334,10 @@ public interface BuilderTrait extends Entity{
float ex = tile.worldx() + Mathf.sin(Timers.time() + 48, swingScl, swingMag);
float ey = tile.worldy() + Mathf.sin(Timers.time() + 48, swingScl + 2f, swingMag);
Draw.color(Color.LIGHT_GRAY, Color.WHITE, 1f-flashScl + Mathf.absin(Timers.time(), 0.5f, flashScl));
Draw.color(Color.LIGHT_GRAY, Color.WHITE, 1f - flashScl + Mathf.absin(Timers.time(), 0.5f, flashScl));
Shapes.laser("minelaser", "minelaser-end", px, py, ex, ey);
if(unit instanceof Player && ((Player) unit).isLocal) {
if(unit instanceof Player && ((Player) unit).isLocal){
Draw.color(Palette.accent);
Lines.poly(tile.worldx(), tile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Timers.time());
}
@@ -308,16 +345,20 @@ public interface BuilderTrait extends Entity{
Draw.color();
}
/**Class for storing build requests. Can be either a place or remove request.*/
class BuildRequest {
/**
* Class for storing build requests. Can be either a place or remove request.
*/
class BuildRequest{
public final int x, y, rotation;
public final Recipe recipe;
public final boolean remove;
public float progress;
/**This creates a build request.*/
public BuildRequest(int x, int y, int rotation, Recipe recipe) {
/**
* This creates a build request.
*/
public BuildRequest(int x, int y, int rotation, Recipe recipe){
this.x = x;
this.y = y;
this.rotation = rotation;
@@ -325,8 +366,10 @@ public interface BuilderTrait extends Entity{
this.remove = false;
}
/**This creates a remove request.*/
public BuildRequest(int x, int y) {
/**
* This creates a remove request.
*/
public BuildRequest(int x, int y){
this.x = x;
this.y = y;
this.rotation = -1;

View File

@@ -8,6 +8,7 @@ public interface CarriableTrait extends TeamTrait, TargetTrait, SolidTrait{
return getCarrier() != null;
}
void setCarrier(CarryTrait carrier);
CarryTrait getCarrier();
void setCarrier(CarryTrait carrier);
}

View File

@@ -10,28 +10,6 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.trait.SolidTrait;
public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
/**Returns the thing this carrier is carrying.*/
CarriableTrait getCarry();
/**Sets the carrying unit. Internal use only! Use {@link #carry(CarriableTrait)} to set state.*/
void setCarry(CarriableTrait unit);
/**Returns maximum mass this carrier can carry.*/
float getCarryWeight();
/**Drops the unit that is being carried, if applicable.*/
default void dropCarry(){
carry(null);
}
default void dropCarryLocal(){
setCarryOf(null, this, null);
}
/**Do not override unless absolutely necessary.
* Carries a unit. To drop a unit, call with {@code null}.*/
default void carry(CarriableTrait unit){
CallEntity.setCarryOf(this instanceof Player ? (Player)this : null, this, unit);
}
@Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void dropSelf(Player player){
if(player.getCarrier() != null){
@@ -41,6 +19,7 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
@Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void setCarryOf(Player player, CarryTrait trait, CarriableTrait unit){
if(trait == null) return;
if(player != null){ //when a server recieves this called from a player, set the carrier to the player.
trait = player;
}
@@ -61,4 +40,38 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
Effects.effect(UnitFx.unitPickup, trait);
}
}
/**
* Returns the thing this carrier is carrying.
*/
CarriableTrait getCarry();
/**
* Sets the carrying unit. Internal use only! Use {@link #carry(CarriableTrait)} to set state.
*/
void setCarry(CarriableTrait unit);
/**
* Returns maximum mass this carrier can carry.
*/
float getCarryWeight();
/**
* Drops the unit that is being carried, if applicable.
*/
default void dropCarry(){
carry(null);
}
default void dropCarryLocal(){
setCarryOf(null, this, null);
}
/**
* Do not override unless absolutely necessary.
* Carries a unit. To drop a unit, call with {@code null}.
*/
default void carry(CarriableTrait unit){
CallEntity.setCarryOf(this instanceof Player ? (Player) this : null, this, unit);
}
}

View File

@@ -2,6 +2,6 @@ package io.anuke.mindustry.entities.traits;
import io.anuke.mindustry.entities.UnitInventory;
public interface InventoryTrait {
public interface InventoryTrait{
UnitInventory getInventory();
}

View File

@@ -3,7 +3,7 @@ package io.anuke.mindustry.entities.traits;
import io.anuke.ucore.entities.trait.HealthTrait;
//TODO implement
public interface RepairTrait extends TeamTrait {
public interface RepairTrait extends TeamTrait{
HealthTrait getRepairing();

View File

@@ -2,6 +2,8 @@ package io.anuke.mindustry.entities.traits;
import io.anuke.ucore.entities.trait.Entity;
/**Marks an entity as serializable.*/
/**
* Marks an entity as serializable.
*/
public interface SaveTrait extends Entity, TypeTrait, Saveable{
}

View File

@@ -4,7 +4,8 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public interface Saveable {
public interface Saveable{
void writeSave(DataOutput stream) throws IOException;
void readSave(DataInput stream) throws IOException;
}

View File

@@ -7,6 +7,8 @@ import io.anuke.ucore.util.Timer;
public interface ShooterTrait extends VelocityTrait, TeamTrait, InventoryTrait{
Timer getTimer();
int getShootTimer(boolean left);
Weapon getWeapon();
}

View File

@@ -3,8 +3,10 @@ package io.anuke.mindustry.entities.traits;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.world.Tile;
public interface SpawnerTrait {
public interface SpawnerTrait{
Tile getTile();
void updateSpawning(Unit unit);
float getSpawnProgress();
}

View File

@@ -10,18 +10,22 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.threads;
public interface SyncTrait extends Entity, TypeTrait {
public interface SyncTrait extends Entity, TypeTrait{
/**Whether smoothing of entities is enabled when using multithreading; not yet implemented.*/
/**
* Whether smoothing of entities is enabled when using multithreading; not yet implemented.
*/
static boolean isSmoothing(){
return threads.isEnabled() && threads.getTPS() <= Gdx.graphics.getFramesPerSecond() / 2f;
}
/**Sets the position of this entity and updated the interpolator.*/
/**
* Sets the position of this entity and updated the interpolator.
*/
default void setNet(float x, float y){
set(x, y);
if(getInterpolator() != null) {
if(getInterpolator() != null){
getInterpolator().target.set(x, y);
getInterpolator().last.set(x, y);
getInterpolator().pos.set(0, 0);
@@ -30,9 +34,12 @@ public interface SyncTrait extends Entity, TypeTrait {
}
}
/**Interpolate entity position only. Override if you need to interpolate rotations or other values.*/
/**
* Interpolate entity position only. Override if you need to interpolate rotations or other values.
*/
default void interpolate(){
if(getInterpolator() == null) throw new RuntimeException("This entity must have an interpolator to interpolate()!");
if(getInterpolator() == null)
throw new RuntimeException("This entity must have an interpolator to interpolate()!");
getInterpolator().update();
@@ -40,17 +47,22 @@ public interface SyncTrait extends Entity, TypeTrait {
setY(getInterpolator().pos.y);
}
/**Return the interpolator used for smoothing the position. Optional.*/
/**
* Return the interpolator used for smoothing the position. Optional.
*/
default Interpolator getInterpolator(){
return null;
}
/**Whether syncing is enabled for this entity; true by default.*/
/**
* Whether syncing is enabled for this entity; true by default.
*/
default boolean isSyncing(){
return true;
}
//Read and write sync data, usually position
void write(DataOutput data) throws IOException;
void read(DataInput data, long time) throws IOException;
}

View File

@@ -1,16 +1,21 @@
package io.anuke.mindustry.entities.traits;
import io.anuke.mindustry.game.Team;
import io.anuke.ucore.entities.trait.VelocityTrait;
import io.anuke.ucore.entities.trait.PosTrait;
import io.anuke.ucore.entities.trait.VelocityTrait;
/**Base interface for targetable entities.*/
public interface TargetTrait extends PosTrait, VelocityTrait {
/**
* Base interface for targetable entities.
*/
public interface TargetTrait extends PosTrait, VelocityTrait{
boolean isDead();
Team getTeam();
/**Whether this entity is a valid target.*/
/**
* Whether this entity is a valid target.
*/
default boolean isValid(){
return !isDead();
}

Some files were not shown because too many files have changed in this diff Show More