Formatting

This commit is contained in:
Anuken
2018-07-12 20:37:15 -04:00
parent a3bda3a941
commit baaeb229cf
379 changed files with 17470 additions and 16215 deletions
@@ -59,7 +59,8 @@ public class AndroidTextFieldDialog{
while(!isBuild){ while(!isBuild){
try{ try{
Thread.sleep(10); Thread.sleep(10);
} catch (InterruptedException e) { } }catch(InterruptedException e){
}
} }
return this; return this;
@@ -8,12 +8,9 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import org.sufficientlysecure.donations.DonationsFragment; import org.sufficientlysecure.donations.DonationsFragment;
public class DonationsActivity extends FragmentActivity{ public class DonationsActivity extends FragmentActivity{
DonationsFragment donationsFragment;
/** /**
* Google * Google
*/ */
@@ -22,6 +19,7 @@ public class DonationsActivity extends FragmentActivity {
"mindustry.donation.1", "mindustry.donation.2", "mindustry.donation.5", "mindustry.donation.1", "mindustry.donation.2", "mindustry.donation.5",
"mindustry.donation.10", "mindustry.donation.15", "mindustry.donation.10", "mindustry.donation.15",
"mindustry.donation.25", "mindustry.donation.50"}; "mindustry.donation.25", "mindustry.donation.50"};
DonationsFragment donationsFragment;
/** /**
* Called when the activity is first created. * Called when the activity is first created.
@@ -50,7 +48,8 @@ public class DonationsActivity extends FragmentActivity {
super.onStart(); super.onStart();
Button b = ((Button) findViewById(org.sufficientlysecure.donations.R.id.donations__google_android_market_donate_button)); Button b = ((Button) findViewById(org.sufficientlysecure.donations.R.id.donations__google_android_market_donate_button));
b.setOnClickListener(new View.OnClickListener(){ b.setOnClickListener(new View.OnClickListener(){
@Override public void onClick(View view) { @Override
public void onClick(View view){
donationsFragment.donateGoogleOnClick(donationsFragment.getView()); donationsFragment.donateGoogleOnClick(donationsFragment.getView());
b.setEnabled(false); b.setEnabled(false);
} }
@@ -58,7 +57,6 @@ public class DonationsActivity extends FragmentActivity {
} }
/** /**
* Needed for Google Play In-app Billing. It uses startIntentSenderForResult(). The result is not propagated to * Needed for Google Play In-app Billing. It uses startIntentSenderForResult(). The result is not propagated to
* the Fragment like in startActivityForResult(). Thus we need to propagate manually to our Fragment. * the Fragment like in startActivityForResult(). Thus we need to propagate manually to our Fragment.
@@ -15,6 +15,13 @@ public class TextFieldDialogListener extends ClickListener{
private int type; private int type;
private int max; private int max;
//type - 0 is text, 1 is numbers, 2 is decimals
public TextFieldDialogListener(TextField field, int type, int max){
this.field = field;
this.type = type;
this.max = max;
}
public static void add(TextField field, int type, int max){ public static void add(TextField field, int type, int max){
field.addListener(new TextFieldDialogListener(field, type, max)); field.addListener(new TextFieldDialogListener(field, type, max));
field.addListener(new InputListener(){ field.addListener(new InputListener(){
@@ -29,13 +36,6 @@ public class TextFieldDialogListener extends ClickListener{
add(field, 0, 16); add(field, 0, 16);
} }
//type - 0 is text, 1 is numbers, 2 is decimals
public TextFieldDialogListener(TextField field, int type, int max){
this.field = field;
this.type = type;
this.max = max;
}
public void clicked(final InputEvent event, float x, float y){ public void clicked(final InputEvent event, float x, float y){
if(Gdx.app.getType() == ApplicationType.Desktop) return; if(Gdx.app.getType() == ApplicationType.Desktop) return;
@@ -11,45 +11,6 @@ import java.lang.annotation.Target;
*/ */
public class Annotations{ public class Annotations{
/**Marks a method as invokable remotely across a server/client connection.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Remote {
/**Specifies the locations from which this method can be invoked.*/
Loc targets() default Loc.server;
/**Specifies which methods are generated. Only affects server-to-client methods.*/
Variant variants() default Variant.all;
/**The local locations where this method is called locally, when invoked.*/
Loc called() default Loc.none;
/**Whether to forward this packet to all other clients upon recieval. Client only.*/
boolean forward() default false;
/**Whether the packet for this method is sent with UDP instead of TCP.
* UDP is faster, but is prone to packet loss and duplication.*/
boolean unreliable() default false;
/**The simple class name where this method is placed.*/
String in() default "Call";
/**Priority of this event.*/
PacketPriority priority() default PacketPriority.normal;
}
/**Specifies that this method will be used to write classes of the type returned by {@link #value()}.<br>
* This method must return void and have two parameters, the first being of type {@link java.nio.ByteBuffer} and the second
* being the type returned by {@link #value()}.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface WriteClass {
Class<?> value();
}
/**Specifies that this method will be used to read classes of the type returned by {@link #value()}. <br>
* This method must return the type returned by {@link #value()},
* and have one parameter, being of type {@link java.nio.ByteBuffer}.*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ReadClass {
Class<?> value();
}
public enum PacketPriority{ public enum PacketPriority{
/** Gets put in a queue and processed if not connected. */ /** Gets put in a queue and processed if not connected. */
normal, normal,
@@ -96,4 +57,55 @@ public class Annotations {
this.isAll = isAll; this.isAll = isAll;
} }
} }
/** Marks a method as invokable remotely across a server/client connection. */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Remote{
/** Specifies the locations from which this method can be invoked. */
Loc targets() default Loc.server;
/** Specifies which methods are generated. Only affects server-to-client methods. */
Variant variants() default Variant.all;
/** The local locations where this method is called locally, when invoked. */
Loc called() default Loc.none;
/** Whether to forward this packet to all other clients upon recieval. Client only. */
boolean forward() default false;
/**
* Whether the packet for this method is sent with UDP instead of TCP.
* UDP is faster, but is prone to packet loss and duplication.
*/
boolean unreliable() default false;
/** The simple class name where this method is placed. */
String in() default "Call";
/** Priority of this event. */
PacketPriority priority() default PacketPriority.normal;
}
/**
* Specifies that this method will be used to write classes of the type returned by {@link #value()}.<br>
* This method must return void and have two parameters, the first being of type {@link java.nio.ByteBuffer} and the second
* being the type returned by {@link #value()}.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface WriteClass{
Class<?> value();
}
/**
* Specifies that this method will be used to read classes of the type returned by {@link #value()}. <br>
* This method must return the type returned by {@link #value()},
* and have one parameter, being of type {@link java.nio.ByteBuffer}.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface ReadClass{
Class<?> value();
}
} }
@@ -10,12 +10,16 @@ import javax.tools.Diagnostic.Kind;
import java.util.HashMap; import java.util.HashMap;
import java.util.Set; import java.util.Set;
/**This class finds reader and writer methods annotated by the {@link io.anuke.annotations.Annotations.WriteClass} /**
* and {@link io.anuke.annotations.Annotations.ReadClass} annotations.*/ * This class finds reader and writer methods annotated by the {@link io.anuke.annotations.Annotations.WriteClass}
* and {@link io.anuke.annotations.Annotations.ReadClass} annotations.
*/
public class IOFinder{ public class IOFinder{
/**Finds all class serializers for all types and returns them. Logs errors when necessary. /**
* Maps fully qualified class names to their serializers.*/ * Finds all class serializers for all types and returns them. Logs errors when necessary.
* Maps fully qualified class names to their serializers.
*/
public HashMap<String, ClassSerializer> findSerializers(RoundEnvironment env){ public HashMap<String, ClassSerializer> findSerializers(RoundEnvironment env){
HashMap<String, ClassSerializer> result = new HashMap<>(); HashMap<String, ClassSerializer> result = new HashMap<>();
@@ -14,8 +14,10 @@ public class MethodEntry {
public final String targetMethod; public final String targetMethod;
/** Whether this method can be called on a client/server. */ /** Whether this method can be called on a client/server. */
public final Loc where; public final Loc where;
/**Whether an additional 'one' and 'all' method variant is generated. At least one of these must be true. /**
* Only applicable to client (server-invoked) methods.*/ * Whether an additional 'one' and 'all' method variant is generated. At least one of these must be true.
* Only applicable to client (server-invoked) methods.
*/
public final Variant target; public final Variant target;
/** Whether this method is called locally as well as remotely. */ /** Whether this method is called locally as well as remotely. */
public final Loc local; public final Loc local;
@@ -23,11 +23,14 @@ public class RemoteReadGenerator {
this.serializers = serializers; this.serializers = serializers;
} }
/**Generates a class for reading remote invoke packets. /**
* Generates a class for reading remote invoke packets.
*
* @param entries List of methods to use/ * @param entries List of methods to use/
* @param className Simple target class name. * @param className Simple target class name.
* @param packageName Full target package name. * @param packageName Full target package name.
* @param needsPlayer Whether this read method requires a reference to the player sender.*/ * @param needsPlayer Whether this read method requires a reference to the player sender.
*/
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer) public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer)
throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{ throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{
+41 -58
View File
@@ -19,14 +19,54 @@ import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup; 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.impl.EffectEntity;
import io.anuke.ucore.entities.trait.DrawTrait;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.OS; import io.anuke.ucore.util.OS;
import java.util.Locale; import java.util.Locale;
public class Vars{ public class Vars{
//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; public static boolean testMobile;
//shorthand for whether or not this is running on android or ios //shorthand for whether or not this is running on android or ios
public static boolean mobile; public static boolean mobile;
@@ -34,21 +74,6 @@ public class Vars{
public static boolean android; public static boolean android;
//shorthand for whether or not this is running on GWT //shorthand for whether or not this is running on GWT
public static boolean 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 //directory for user-created map data
public static FileHandle customMapDirectory; public static FileHandle customMapDirectory;
//save file directory //save file directory
@@ -74,54 +99,12 @@ public class Vars{
public static boolean showUI = true; public static boolean showUI = true;
//whether to show block debug //whether to show block debug
public static boolean showBlockDebug = false; public static boolean showBlockDebug = false;
public static boolean showFog = true; public static boolean showFog = true;
public static final int maxTextLength = 150;
public static final int maxNameLength = 40;
public static final int maxCharNameLength = 20;
public static boolean headless = false; public static boolean headless = false;
public static float controllerMin = 0.25f; public static float controllerMin = 0.25f;
public static float baseControllerSpeed = 11f; public static float baseControllerSpeed = 11f;
public static final int saveSlots = 64;
public static final float itemSize = 5f;
//only if smoothCamera //only if smoothCamera
public static boolean snapCamera = true; public static boolean snapCamera = true;
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 GameState state; public static GameState state;
public static ThreadHandler threads; public static ThreadHandler threads;
@@ -26,32 +26,53 @@ import static io.anuke.mindustry.Vars.*;
//TODO consider using quadtrees for finding specific types of blocks within an area //TODO consider using quadtrees for finding specific types of blocks within an area
//TODO maybe use Arrays instead of ObjectSets? //TODO maybe use Arrays instead of ObjectSets?
/**Class used for indexing special target blocks for AI.*/
/**
* Class used for indexing special target blocks for AI.
*/
public class BlockIndexer{ public class BlockIndexer{
/**Size of one ore quadrant.*/ /**
* Size of one ore quadrant.
*/
private final static int oreQuadrantSize = 20; private final static int oreQuadrantSize = 20;
/**Size of one structure quadrant.*/ /**
* Size of one structure quadrant.
*/
private final static int structQuadrantSize = 12; 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); 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<>(); 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; 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<>(); 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<>(); 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<>(); 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<>(); private IntMap<TileIndex> typeMap = new IntMap<>();
/**Empty array used for returning.*/ /**
* Empty array used for returning.
*/
private ObjectSet<Tile> emptyArray = new ObjectSet<>(); private ObjectSet<Tile> emptyArray = new ObjectSet<>();
public BlockIndexer(){ public BlockIndexer(){
@@ -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){ public ObjectSet<Tile> getAllied(Team team, BlockFlag type){
return (state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray); 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){ public ObjectSet<Tile> getEnemy(Team team, BlockFlag type){
return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray); return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray);
} }
@@ -134,15 +159,19 @@ public class BlockIndexer {
return (TileEntity) closest; 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, * 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. * 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){ public ObjectSet<Tile> getOrePositions(Item item){
return ores.get(item, emptyArray); 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){ 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));
@@ -11,7 +11,9 @@ 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; 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; public static Mech starterDesktop, starterMobile;
@Override @Override
@@ -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(production, ProductionBlocks.oilextractor, new ItemStack(Items.titanium, 40), new ItemStack(Items.surgealloy, 40));*/
//new Recipe(distribution, DistributionBlocks.massDriver, new ItemStack(Items.carbide, 1)); //new Recipe(distribution, DistributionBlocks.massDriver, new ItemStack(Items.carbide, 1));
@@ -3,10 +3,10 @@ package io.anuke.mindustry.content;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.content.fx.EnvironmentFx; import io.anuke.mindustry.content.fx.EnvironmentFx;
import io.anuke.mindustry.entities.StatusController.StatusEntry; 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.entities.Unit;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.type.ContentList; import io.anuke.mindustry.type.ContentList;
import io.anuke.mindustry.type.StatusEffect;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -15,17 +15,22 @@ 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; public static Block air, spawn, blockpart, space, metalfloor, deepwater, water, lava, oil, stone, blackstone, dirt, sand, ice, snow, grass, shrub, rock, icerock, blackrock;
@Override @Override
public void load(){ public void load(){
air = new Floor("air"){ air = new Floor("air"){
{ {
blend = false; blend = false;
} }
//don't draw //don't draw
public void draw(Tile tile) {} public void draw(Tile tile){
public void load() {} }
public void init() {}
public void load(){
}
public void init(){
}
}; };
blockpart = new BlockPart(); blockpart = new BlockPart();
@@ -28,6 +28,12 @@ import java.io.IOException;
public class DebugBlocks extends BlockList implements ContentList{ public class DebugBlocks extends BlockList implements ContentList{
public static Block powerVoid, powerInfinite, itemSource, liquidSource, itemVoid; 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 @Override
public void load(){ public void load(){
powerVoid = new PowerBlock("powervoid"){ powerVoid = new PowerBlock("powervoid"){
@@ -53,6 +59,7 @@ public class DebugBlocks extends BlockList implements ContentList{
{ {
hasItems = true; hasItems = true;
} }
@Override @Override
public void update(Tile tile){ public void update(Tile tile){
SorterEntity entity = tile.entity(); SorterEntity entity = tile.entity();
@@ -142,12 +149,6 @@ public class DebugBlocks extends BlockList implements ContentList{
}; };
} }
@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; public Liquid source = Liquids.water;
@@ -10,6 +10,13 @@ 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<>(); 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 @Override
public void load(){ public void load(){
Item[] ores = {Items.tungsten, Items.lead, Items.coal, Items.titanium, Items.thorium}; Item[] ores = {Items.tungsten, Items.lead, Items.coal, Items.titanium, Items.thorium};
@@ -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);
}
} }
@@ -13,7 +13,8 @@ import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
public class TurretBlocks extends BlockList implements ContentList{ 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 static Block duo, /*scatter,*/
scorch, hail, wave, lancer, arc, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown;
@Override @Override
public void load(){ public void load(){
@@ -25,8 +25,10 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;
/**Loads all game content. /**
* Call load() before doing anything with content.*/ * Loads all game content.
* Call load() before doing anything with content.
*/
public class ContentLoader{ public class ContentLoader{
private static boolean loaded = false; private static boolean loaded = false;
private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>(); private static ObjectSet<Array<? extends Content>> contentSet = new OrderedSet<>();
@@ -94,7 +96,9 @@ public class ContentLoader {
new Recipes(), new Recipes(),
}; };
/**Creates all content types.*/ /**
* Creates all content types.
*/
public static void load(){ public static void load(){
if(loaded){ if(loaded){
Log.info("Content already loaded, skipping."); Log.info("Content already loaded, skipping.");
@@ -134,7 +138,9 @@ public class ContentLoader {
loaded = true; loaded = true;
} }
/**Initializes all content with the specified function.*/ /**
* Initializes all content with the specified function.
*/
public static void initialize(Consumer<Content> callable){ public static void initialize(Consumer<Content> callable){
if(initialization.contains(callable)) return; if(initialization.contains(callable)) return;
@@ -155,8 +161,10 @@ public class ContentLoader {
return contentMap; 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(){ private static void registerTypes(){
TypeTrait.registerType(Player.class, Player::new); TypeTrait.registerType(Player.class, Player::new);
TypeTrait.registerType(ItemDrop.class, ItemDrop::new); TypeTrait.registerType(ItemDrop.class, ItemDrop::new);
@@ -30,12 +30,16 @@ import io.anuke.ucore.util.Atlas;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
/**Control module. /**
* Control module.
* Handles all input, saving, keybinds and keybinds. * Handles all input, saving, keybinds and keybinds.
* Should <i>not</i> handle any logic-critical state. * 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{ public class Control extends Module{
/**Minimum period of time between the same sound being played.*/ /**
* Minimum period of time between the same sound being played.
*/
private static final long minSoundPeriod = 100; private static final long minSoundPeriod = 100;
private boolean hiscore = false; private boolean hiscore = false;
@@ -8,8 +8,6 @@ import io.anuke.mindustry.game.TeamInfo;
import io.anuke.ucore.core.Events; import io.anuke.ucore.core.Events;
public class GameState{ public class GameState{
private State state = State.menu;
public int wave = 1; public int wave = 1;
public float wavetime; public float wavetime;
public boolean gameOver = false; public boolean gameOver = false;
@@ -18,6 +16,7 @@ public class GameState{
public boolean friendlyFire; public boolean friendlyFire;
public WaveSpawner spawner = new WaveSpawner(); public WaveSpawner spawner = new WaveSpawner();
public TeamInfo teams = new TeamInfo(); public TeamInfo teams = new TeamInfo();
private State state = State.menu;
public void set(State astate){ public void set(State astate){
Events.fire(StateChangeEvent.class, state, astate); Events.fire(StateChangeEvent.class, state, astate);
+7 -4
View File
@@ -24,12 +24,14 @@ import io.anuke.ucore.modules.Module;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
/**Logic module. /**
* Logic module.
* Handles all logic for entities and waves. * Handles all logic for entities and waves.
* Handles game state events. * Handles game state events.
* Does not store any game state itself. * Does not store any game state itself.
* * <p>
* This class should <i>not</i> call any outside methods to change state of modules, but instead fire events.*/ * 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 class Logic extends Module{
public boolean doUpdate = true; public boolean doUpdate = true;
@@ -137,7 +139,8 @@ public class Logic extends Module {
runWave(); 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); Entities.update(bulletGroup);
for(EntityGroup group : unitGroups){ for(EntityGroup group : unitGroups){
+199 -170
View File
@@ -41,33 +41,59 @@ public class NetClient extends Module {
private final static float playerSyncTime = 2; private final static float playerSyncTime = 2;
private Timer timer = new Timer(5); private Timer timer = new Timer(5);
/**Whether the client is currently connecting.*/ /**
* Whether the client is currently connecting.
*/
private boolean connecting = false; 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; private boolean quiet = false;
/**Counter for data timeout.*/ /**
* Counter for data timeout.
*/
private float timeoutTime = 0f; private float timeoutTime = 0f;
/**Last sent client snapshot ID.*/ /**
* Last sent client snapshot ID.
*/
private int lastSent; private int lastSent;
/**Last snapshot ID recieved.*/ /**
* Last snapshot ID recieved.
*/
private int lastSnapshotBaseID = -1; private int lastSnapshotBaseID = -1;
/**Last snapshot recieved.*/ /**
* Last snapshot recieved.
*/
private byte[] lastSnapshotBase; private byte[] lastSnapshotBase;
/**Current snapshot that is being built from chinks.*/ /**
* Current snapshot that is being built from chinks.
*/
private byte[] currentSnapshot; private byte[] currentSnapshot;
/**Array of recieved chunk statuses.*/ /**
* Array of recieved chunk statuses.
*/
private boolean[] recievedChunks; private boolean[] recievedChunks;
/**Counter of how many chunks have been recieved.*/ /**
* Counter of how many chunks have been recieved.
*/
private int recievedChunkCounter; private int recievedChunkCounter;
/**ID of snapshot that is currently being constructed.*/ /**
* ID of snapshot that is currently being constructed.
*/
private int currentSnapshotID = -1; private int currentSnapshotID = -1;
/**Decoder for uncompressing snapshots.*/ /**
* Decoder for uncompressing snapshots.
*/
private DEZDecoder decoder = new DEZDecoder(); 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(); private IntSet removed = new IntSet();
/**Byte stream for reading in snapshots.*/ /**
* Byte stream for reading in snapshots.
*/
private ReusableByteArrayInputStream byteStream = new ReusableByteArrayInputStream(); private ReusableByteArrayInputStream byteStream = new ReusableByteArrayInputStream();
private DataInputStream dataStream = new DataInputStream(byteStream); private DataInputStream dataStream = new DataInputStream(byteStream);
@@ -145,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 @Override
public void update(){ public void update(){
if(!Net.client()) return; if(!Net.client()) return;
@@ -224,161 +410,4 @@ public class NetClient extends Module {
return result; 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, 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);
}
}
} }
+101 -86
View File
@@ -45,20 +45,30 @@ public class NetServer extends Module{
private final static byte[] reusableSnapArray = new byte[maxSnapshotSize]; private final static byte[] reusableSnapArray = new byte[maxSnapshotSize];
private final static float serverSyncTime = 4, kickDuration = 30 * 1000; private final static float serverSyncTime = 4, kickDuration = 30 * 1000;
private final static Vector2 vector = new Vector2(); 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; private final static float correctDist = 16f;
public final Administration admins = new Administration(); public final Administration admins = new Administration();
/**Maps connection IDs to players.*/ /**
* Maps connection IDs to players.
*/
private IntMap<Player> connections = new IntMap<>(); private IntMap<Player> connections = new IntMap<>();
private boolean closing = false; private boolean closing = false;
/**Stream for writing player sync data to.*/ /**
* Stream for writing player sync data to.
*/
private CountableByteArrayOutputStream syncStream = new CountableByteArrayOutputStream(); 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); private DataOutputStream dataStream = new DataOutputStream(syncStream);
/**Encoder for computing snapshot deltas.*/ /**
* Encoder for computing snapshot deltas.
*/
private DEZEncoder encoder = new DEZEncoder(); private DEZEncoder encoder = new DEZEncoder();
public NetServer(){ public NetServer(){
@@ -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(){ public void update(){
if(!headless && !closing && Net.server() && state.is(State.menu)){ if(!headless && !closing && Net.server() && state.is(State.menu)){
closing = true; closing = true;
@@ -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 the player hasn't acknowledged that it has recieved the packet, send the same thing again
if(connection.currentBaseID < connection.lastSentSnapshotID){ 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); sendSplitSnapshot(connection.id, connection.lastSentSnapshot, connection.lastSentSnapshotID, connection.lastSentBase);
return; return;
} }
@@ -409,7 +500,8 @@ public class NetServer extends Module{
dataStream.writeByte(((SyncTrait) entity).getTypeID()); //write type ID dataStream.writeByte(((SyncTrait) entity).getTypeID()); //write type ID
((SyncTrait) entity).write(dataStream); //write entity ((SyncTrait) entity).write(dataStream); //write entity
int length = syncStream.position() - position; //length must always be less than 127 bytes 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); dataStream.writeByte(length);
} }
} }
@@ -433,7 +525,8 @@ public class NetServer extends Module{
//send diff, otherwise //send diff, otherwise
byte[] diff = ByteDeltaEncoder.toDiff(new ByteMatcherHash(connection.currentBaseSnapshot, bytes), encoder); 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); sendSplitSnapshot(connection.id, diff, connection.currentBaseID + 1, connection.currentBaseID);
connection.lastSentSnapshot = diff; connection.lastSentSnapshot = diff;
connection.lastSentSnapshotID = connection.currentBaseID + 1; connection.lastSentSnapshotID = connection.currentBaseID + 1;
@@ -445,82 +538,4 @@ public class NetServer extends Module{
e.printStackTrace(); 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, 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);
}
} }
+138 -42
View File
@@ -12,44 +12,101 @@ import java.util.Locale;
import java.util.Random; import java.util.Random;
public abstract class Platform{ public abstract class Platform{
/**Each separate game platform should set this instance to their own implementation.*/ /**
public static Platform instance = new 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 the date using the default date formatter.
/**Format a number by adding in commas or periods where needed.*/ */
public String format(int number){return "invalid";} public String format(Date date){
/**Show a native error dialog.*/ return "invalid";
public void showError(String text){} }
/**Add a text input dialog that should show up after the field is tapped.*/
/**
* 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){ public void addDialog(TextField field){
addDialog(field, 16); addDialog(field, 16);
} }
/**See addDialog().*/
public void addDialog(TextField field, int maxLength){} /**
/**Update discord RPC.*/ * See addDialog().
public void updateRPC(){} */
/**Called when the game is exited.*/ public void addDialog(TextField field, int maxLength){
public void onGameExit(){} }
/**Open donation dialog. Currently android only.*/
public void openDonations(){} /**
/**Whether donating is supported.*/ * 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(){ public boolean canDonate(){
return false; 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().*/ * 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){ public String getLocaleName(Locale locale){
return locale.toString(); return locale.toString();
} }
/**Whether joining games is supported.*/
/**
* Whether joining games is supported.
*/
public boolean canJoinGame(){ public boolean canJoinGame(){
return true; return true;
} }
/**Whether debug mode is enabled.*/
public boolean isDebug(){return false;} /**
/**Must be a base64 string 8 bytes in length.*/ * Whether debug mode is enabled.
*/
public boolean isDebug(){
return false;
}
/**
* Must be a base64 string 8 bytes in length.
*/
public String getUUID(){ public String getUUID(){
String uuid = Settings.getString("uuid", ""); String uuid = Settings.getString("uuid", "");
if(uuid.isEmpty()){ if(uuid.isEmpty()){
@@ -62,12 +119,21 @@ public abstract class Platform {
} }
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. /**
* 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 text File chooser title text
* @param content Description of the type of files to be loaded * @param content Description of the type of files to be loaded
@@ -75,24 +141,54 @@ public abstract class Platform {
* @param open Whether to open or save files * @param open Whether to open or save files
* @param filetype File extension to filter * @param filetype File extension to filter
*/ */
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filetype){} 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.*/ }
/**
* Use the default thread provider from the kryonet module for this.
*/
public ThreadProvider getThreadProvider(){ public ThreadProvider getThreadProvider(){
return new ThreadProvider(){ return new ThreadProvider(){
@Override public boolean isOnThread() {return true;} @Override
@Override public void sleep(long ms) {} public boolean isOnThread(){
@Override public void start(Runnable run) {} return true;
@Override public void stop() {} }
@Override public void notify(Object object) {}
@Override public void wait(Object object) {} @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 //TODO iOS implementation
/**Forces the app into landscape mode. Currently Android only.*/
public void beginForceLandscape(){} /**
* Forces the app into landscape mode. Currently Android only.
*/
public void beginForceLandscape(){
}
//TODO iOS implementation //TODO iOS implementation
/**Stops forcing the app into landscape orientation. Currently Android only.*/
public void endForceLandscape(){} /**
* Stops forcing the app into landscape orientation. Currently Android only.
*/
public void endForceLandscape(){
}
} }
@@ -12,13 +12,12 @@ import static io.anuke.mindustry.Vars.logic;
public class ThreadHandler{ public class ThreadHandler{
private final Queue<Runnable> toRun = new Queue<>(); private final Queue<Runnable> toRun = new Queue<>();
private final ThreadProvider impl; private final ThreadProvider impl;
private final Object updateLock = new Object();
private float delta = 1f; private float delta = 1f;
private float smoothDelta = 1f; private float smoothDelta = 1f;
private long frame = 0, lastDeltaUpdate; private long frame = 0, lastDeltaUpdate;
private float framesSinceUpdate; private float framesSinceUpdate;
private boolean enabled; private boolean enabled;
private final Object updateLock = new Object();
private boolean rendered = true; private boolean rendered = true;
public ThreadHandler(ThreadProvider impl){ public ThreadHandler(ThreadProvider impl){
@@ -82,6 +81,10 @@ public class ThreadHandler {
} }
} }
public boolean isEnabled(){
return enabled;
}
public void setEnabled(boolean enabled){ public void setEnabled(boolean enabled){
if(enabled){ if(enabled){
logic.doUpdate = false; logic.doUpdate = false;
@@ -98,10 +101,6 @@ public class ThreadHandler {
} }
} }
public boolean isEnabled(){
return enabled;
}
public boolean doInterpolate(){ public boolean doInterpolate(){
return enabled && Gdx.graphics.getFramesPerSecond() - getTPS() > 20 && getTPS() < 30; return enabled && Gdx.graphics.getFramesPerSecond() - getTPS() > 20 && getTPS() < 30;
} }
@@ -166,10 +165,15 @@ public class ThreadHandler {
public interface ThreadProvider{ public interface ThreadProvider{
boolean isOnThread(); boolean isOnThread();
void sleep(long ms) throws InterruptedException; void sleep(long ms) throws InterruptedException;
void start(Runnable run); void start(Runnable run);
void stop(); void stop();
void wait(Object object) throws InterruptedException; void wait(Object object) throws InterruptedException;
void notify(Object object); void notify(Object object);
} }
} }
+10 -25
View File
@@ -37,6 +37,14 @@ import static io.anuke.mindustry.Vars.players;
import static io.anuke.ucore.scene.actions.Actions.*; import static io.anuke.ucore.scene.actions.Actions.*;
public class UI extends SceneModule{ public class UI extends SceneModule{
public final MenuFragment menufrag = new MenuFragment();
public final HudFragment hudfrag = new HudFragment();
public final ChatFragment chatfrag = new ChatFragment();
public final PlayerListFragment listfrag = new PlayerListFragment();
public final BackgroundFragment backfrag = new BackgroundFragment();
public final LoadingFragment loadfrag = new LoadingFragment();
public final DebugFragment debugfrag = new DebugFragment();
public AboutDialog about; public AboutDialog about;
public RestartDialog restart; public RestartDialog restart;
public LevelDialog levels; public LevelDialog levels;
@@ -59,14 +67,6 @@ public class UI extends SceneModule{
public UnlocksDialog unlocks; public UnlocksDialog unlocks;
public ContentInfoDialog content; public ContentInfoDialog content;
public final MenuFragment menufrag = new MenuFragment();
public final HudFragment hudfrag = new HudFragment();
public final ChatFragment chatfrag = new ChatFragment();
public final PlayerListFragment listfrag = new PlayerListFragment();
public final BackgroundFragment backfrag = new BackgroundFragment();
public final LoadingFragment loadfrag = new LoadingFragment();
public final DebugFragment debugfrag = new DebugFragment();
private Locale lastLocale; private Locale lastLocale;
public UI(){ public UI(){
@@ -234,7 +234,8 @@ public class UI extends SceneModule{
public void showTextInput(String title, String text, String def, TextFieldFilter filter, Consumer<String> confirmed){ public void showTextInput(String title, String text, String def, TextFieldFilter filter, Consumer<String> confirmed){
new Dialog(title, "dialog"){{ new Dialog(title, "dialog"){{
content().margin(30).add(text).padRight(6f); content().margin(30).add(text).padRight(6f);
TextField field = content().addField(def, t->{}).size(170f, 50f).get(); TextField field = content().addField(def, t -> {
}).size(170f, 50f).get();
field.setTextFieldFilter((f, c) -> field.getText().length() < 12 && filter.acceptChar(f, c)); field.setTextFieldFilter((f, c) -> field.getText().length() < 12 && filter.acceptChar(f, c));
Platform.instance.addDialog(field); Platform.instance.addDialog(field);
buttons().defaults().size(120, 54).pad(4); buttons().defaults().size(120, 54).pad(4);
@@ -286,20 +287,4 @@ public class UI extends SceneModule{
dialog.keyDown(Keys.BACK, dialog::hide); dialog.keyDown(Keys.BACK, dialog::hide);
dialog.show(); dialog.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();
}
} }
+31 -15
View File
@@ -11,7 +11,10 @@ import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.TileChangeEvent; import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent; import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.*; 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.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.mapgen.WorldGenerator; import io.anuke.mindustry.world.mapgen.WorldGenerator;
@@ -93,6 +96,10 @@ public class World extends Module{
return currentMap; return currentMap;
} }
public void setMap(Map map){
this.currentMap = map;
}
public int width(){ public int width(){
return tiles == null ? 0 : tiles.length; return tiles == null ? 0 : tiles.length;
} }
@@ -143,8 +150,10 @@ public class World extends Module{
} }
} }
/**Resizes the tile array to the specified size and returns the resulting tile array. /**
* Only use for loading saves!*/ * 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){ public Tile[][] createTiles(int width, int height){
if(tiles != null){ if(tiles != null){
clearTileEntities(); clearTileEntities();
@@ -159,14 +168,18 @@ public class World extends Module{
return tiles; return tiles;
} }
/**Call to signify the beginning of map loading. /**
* TileChangeEvents will not be fired until endMapLoad().*/ * Call to signify the beginning of map loading.
* TileChangeEvents will not be fired until endMapLoad().
*/
public void beginMapLoad(){ public void beginMapLoad(){
generating = true; 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.*/ * 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(){ public void endMapLoad(){
for(int x = 0; x < tiles.length; x++){ for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){ for(int y = 0; y < tiles[0].length; y++){
@@ -184,7 +197,9 @@ public class World extends Module{
Events.fire(WorldLoadEvent.class); Events.fire(WorldLoadEvent.class);
} }
/**Loads up a procedural map. This does not call play(), but calls reset().*/ /**
* Loads up a procedural map. This does not call play(), but calls reset().
*/
public void loadProceduralMap(){ public void loadProceduralMap(){
Timers.mark(); Timers.mark();
Timers.mark(); Timers.mark();
@@ -213,10 +228,6 @@ public class World extends Module{
Log.info("Full time to generate: {0}", Timers.elapsed()); Log.info("Full time to generate: {0}", Timers.elapsed());
} }
public void setMap(Map map){
this.currentMap = map;
}
public void loadMap(Map map){ public void loadMap(Map map){
loadMap(map, MathUtils.random(0, 999999)); loadMap(map, MathUtils.random(0, 999999));
} }
@@ -290,14 +301,19 @@ public class World extends Module{
} }
} }
/**Raycast, but with world coordinates.*/ /**
* Raycast, but with world coordinates.
*/
public GridPoint2 raycastWorld(float x, float y, float x2, float y2){ public GridPoint2 raycastWorld(float x, float y, float x2, float y2){
return raycast(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize), return raycast(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize),
Mathf.scl2(x2, tilesize), Mathf.scl2(y2, 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.*/ * 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){ public GridPoint2 raycast(int x0f, int y0f, int x1, int y1){
int x0 = x0f; int x0 = x0f;
int y0 = y0f; int y0 = y0f;
@@ -7,11 +7,17 @@ import io.anuke.mindustry.io.MapTileData.TileDataMarker;
import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.Bits;
public class DrawOperation{ public class DrawOperation{
/**Data to apply operation to.*/ /**
* Data to apply operation to.
*/
private MapTileData data; private MapTileData data;
/**List of per-tile operations that occurred.*/ /**
* List of per-tile operations that occurred.
*/
private Array<TileOperation> operations = new Array<>(); private Array<TileOperation> operations = new Array<>();
/**Checks for duplicate operations, useful for brushes.*/ /**
* Checks for duplicate operations, useful for brushes.
*/
private IntSet checks = new IntSet(); private IntSet checks = new IntSet();
public DrawOperation(MapTileData data){ public DrawOperation(MapTileData data){
@@ -56,14 +56,14 @@ public class MapEditor{
renderer.resize(map.width(), map.height()); renderer.resize(map.width(), map.height());
} }
public void setDrawElevation(int elevation){
this.elevation = (byte)elevation;
}
public byte getDrawElevation(){ public byte getDrawElevation(){
return elevation; return elevation;
} }
public void setDrawElevation(int elevation){
this.elevation = (byte) elevation;
}
public int getDrawRotation(){ public int getDrawRotation(){
return rotation; return rotation;
} }
@@ -72,14 +72,14 @@ public class MapEditor{
this.rotation = rotation; this.rotation = rotation;
} }
public void setDrawTeam(Team team){
this.drawTeam = team;
}
public Team getDrawTeam(){ public Team getDrawTeam(){
return drawTeam; return drawTeam;
} }
public void setDrawTeam(Team team){
this.drawTeam = team;
}
public Block getDrawBlock(){ public Block getDrawBlock(){
return drawBlock; return drawBlock;
} }
@@ -88,14 +88,14 @@ public class MapEditor{
this.drawBlock = block; this.drawBlock = block;
} }
public void setBrushSize(int size){
this.brushSize = size;
}
public int getBrushSize(){ public int getBrushSize(){
return brushSize; return brushSize;
} }
public void setBrushSize(int size){
this.brushSize = size;
}
public void draw(int x, int y){ public void draw(int x, int y){
draw(x, y, drawBlock); draw(x, y, drawBlock);
} }
@@ -284,11 +284,13 @@ public class MapEditorDialog extends Dialog implements Disposable{
saved = true; saved = true;
} }
/**Argument format: /**
* Argument format:
* 0) button name * 0) button name
* 1) description * 1) description
* 2) icon name * 2) icon name
* 3) listener */ * 3) listener
*/
private FloatingDialog createDialog(String title, Object... arguments){ private FloatingDialog createDialog(String title, Object... arguments){
FloatingDialog dialog = new FloatingDialog(title); FloatingDialog dialog = new FloatingDialog(title);
@@ -584,7 +586,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
for(Block block : Block.all()){ for(Block block : Block.all()){
TextureRegion[] regions = block.getCompactIcon(); TextureRegion[] regions = block.getCompactIcon();
if((block.synthetic() && (Recipe.getByResult(block) == null || !control.database().isUnlocked(Recipe.getByResult(block)))) && !debug && block != StorageBlocks.core) continue; if((block.synthetic() && (Recipe.getByResult(block) == null || !control.database().isUnlocked(Recipe.getByResult(block)))) && !debug && block != StorageBlocks.core)
continue;
if(regions.length == 0 || regions[0] == Draw.region("jjfgj")) continue; if(regions.length == 0 || regions[0] == Draw.region("jjfgj")) continue;
@@ -1,7 +1,7 @@
package io.anuke.mindustry.editor; package io.anuke.mindustry.editor;
import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.Map;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.scene.ui.TextButton; import io.anuke.ucore.scene.ui.TextButton;
+45 -45
View File
@@ -53,51 +53,6 @@ public class MapView extends Element implements GestureListener{
private float mousex, mousey; private float mousex, mousey;
private EditorTool lastTool; private EditorTool lastTool;
public void setTool(EditorTool tool){
this.tool = tool;
}
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){ public MapView(MapEditor editor){
this.editor = editor; this.editor = editor;
@@ -211,6 +166,51 @@ public class MapView extends Element implements GestureListener{
}); });
} }
public EditorTool getTool(){
return tool;
}
public void setTool(EditorTool tool){
this.tool = tool;
}
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 @Override
public void act(float delta){ public void act(float delta){
super.act(delta); super.act(delta);
@@ -24,13 +24,17 @@ import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
/**Utility class for damaging in an area.*/ /**
* Utility class for damaging in an area.
*/
public class Damage{ public class Damage{
private static Rectangle rect = new Rectangle(); private static Rectangle rect = new Rectangle();
private static Rectangle hitrect = new Rectangle(); private static Rectangle hitrect = new Rectangle();
private static Translator tr = new Translator(); private static Translator tr = new Translator();
/**Creates a dynamic explosion based on specified parameters.*/ /**
* 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){ 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++){ for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){
int branches = 5 + Mathf.clamp((int) (power / 30), 1, 20); int branches = 5 + Mathf.clamp((int) (power / 30), 1, 20);
@@ -76,8 +80,10 @@ public class Damage {
} }
} }
/**Damages entities in a line. /**
* Only enemies of the specified team are damaged.*/ * 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){ public static void collideLine(SolidEntity hitter, Team team, Effect effect, float x, float y, float angle, float length){
tr.trns(angle, length); tr.trns(angle, length);
rect.setPosition(x, y).setSize(tr.x, tr.y); rect.setPosition(x, y).setSize(tr.x, tr.y);
@@ -120,7 +126,9 @@ public class Damage {
Units.getNearbyEnemies(team, rect, cons); Units.getNearbyEnemies(team, rect, cons);
} }
/**Damages all entities and blocks in a radius that are enemies of the team.*/ /**
* 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){ public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate<Unit> predicate, Consumer<Unit> acceptor){
Consumer<Unit> cons = entity -> { Consumer<Unit> cons = entity -> {
if(!predicate.test(entity)) return; if(!predicate.test(entity)) return;
@@ -141,12 +149,16 @@ public class Damage {
} }
} }
/**Damages everything in a radius.*/ /**
* Damages everything in a radius.
*/
public static void damage(float x, float y, float radius, float damage){ public static void damage(float x, float y, float radius, float damage){
damage(null, x, y, radius, damage); damage(null, x, y, radius, damage);
} }
/**Damages all entities and blocks in a radius that are enemies of the team.*/ /**
* 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){ public static void damage(Team team, float x, float y, float radius, float damage){
Consumer<Unit> cons = entity -> { Consumer<Unit> cons = entity -> {
if(entity.team == team || entity.distanceTo(x, y) > radius){ if(entity.team == team || entity.distanceTo(x, y) > radius){
@@ -26,7 +26,10 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.storage.CoreBlock.CoreEntity; import io.anuke.mindustry.world.blocks.storage.CoreBlock.CoreEntity;
import io.anuke.mindustry.world.blocks.units.MechFactory; import io.anuke.mindustry.world.blocks.units.MechFactory;
import io.anuke.ucore.core.*; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.SolidTrait; import io.anuke.ucore.entities.trait.SolidTrait;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
@@ -40,13 +43,11 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTrait{ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTrait{
public static final int timerSync = 2;
private static final int timerShootLeft = 0; private static final int timerShootLeft = 0;
private static final int timerShootRight = 1; private static final int timerShootRight = 1;
public static final int timerSync = 2;
//region instance variables, constructor //region instance variables, constructor
public float baseRotation; public float baseRotation;
public float pointerX, pointerY; public float pointerX, pointerY;
@@ -82,6 +83,30 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
//region unit and event overrides, utility methods //region unit and event overrides, utility methods
@Remote(in = In.entities, targets = Loc.server, called = Loc.server)
public static void onPlayerDamage(Player player, float amount){
if(player == null) return;
player.hitTime = hitDuration;
player.health -= amount;
}
@Remote(in = In.entities, targets = Loc.server, called = Loc.server)
public static void onPlayerDeath(Player player){
if(player == null) return;
player.dead = true;
player.placeQueue.clear();
player.dropCarry();
float explosiveness = 2f + (player.inventory.hasItem() ? player.inventory.getItem().item.explosiveness * player.inventory.getItem().amount : 0f);
float flammability = (player.inventory.hasItem() ? player.inventory.getItem().item.flammability * player.inventory.getItem().amount : 0f);
Damage.dynamicExplosion(player.x, player.y, flammability, explosiveness, 0f, player.getSize() / 2f, Palette.darkFlame);
ScorchDecal.create(player.x, player.y);
player.onDeath();
}
@Override @Override
public Timer getTimer(){ public Timer getTimer(){
@@ -215,31 +240,6 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
return super.collides(other) || other instanceof ItemDrop; return super.collides(other) || other instanceof ItemDrop;
} }
@Remote(in = In.entities, targets = Loc.server, called = Loc.server)
public static void onPlayerDamage(Player player, float amount){
if(player == null) return;
player.hitTime = hitDuration;
player.health -= amount;
}
@Remote(in = In.entities, targets = Loc.server, called = Loc.server)
public static void onPlayerDeath(Player player){
if(player == null) return;
player.dead = true;
player.placeQueue.clear();
player.dropCarry();
float explosiveness = 2f + (player.inventory.hasItem() ? player.inventory.getItem().item.explosiveness * player.inventory.getItem().amount : 0f);
float flammability = (player.inventory.hasItem() ? player.inventory.getItem().item.flammability * player.inventory.getItem().amount : 0f);
Damage.dynamicExplosion(player.x, player.y, flammability, explosiveness, 0f, player.getSize()/2f, Palette.darkFlame);
ScorchDecal.create(player.x, player.y);
player.onDeath();
}
@Override @Override
public void set(float x, float y){ public void set(float x, float y){
this.x = x; this.x = x;
@@ -402,7 +402,9 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
Draw.tscl(fontScale); Draw.tscl(fontScale);
} }
/**Draw all current build requests. Does not draw the beam effect, only the positions.*/ /**
* Draw all current build requests. Does not draw the beam effect, only the positions.
*/
public void drawBuildRequests(){ public void drawBuildRequests(){
synchronized(getPlaceQueue()){ synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : getPlaceQueue()){
@@ -661,7 +663,9 @@ public class Player extends Unit implements BuilderTrait, CarryTrait, ShooterTra
team = (team == Team.blue ? Team.red : Team.blue); team = (team == Team.blue ? Team.red : Team.blue);
} }
/**Resets all values of the player.*/ /**
* Resets all values of the player.
*/
public void reset(){ public void reset(){
status.clear(); status.clear();
team = Team.blue; team = Team.blue;
@@ -4,12 +4,16 @@ import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
/**Class for predicting shoot angles based on velocities of targets.*/ /**
* Class for predicting shoot angles based on velocities of targets.
*/
public class Predict{ public class Predict{
private static Vector2 vec = new Vector2(); private static Vector2 vec = new Vector2();
private static Vector2 vresult = 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 srcx X of shooter
* @param srcy Y of shooter * @param srcy Y of shooter
* @param dstx X of target * @param dstx X of target
@@ -17,7 +21,8 @@ public class Predict {
* @param dstvx X velocity of target (subtract shooter X velocity if needed) * @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 dstvy Y velocity of target (subtract shooter Y velocity if needed)
* @param v speed of bullet * @param v speed of bullet
* @return the intercept location*/ * @return the intercept location
*/
public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v){ public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v){
float tx = dstx - srcx, float tx = dstx - srcx,
ty = dsty - srcy; ty = dsty - srcy;
@@ -44,7 +49,9 @@ public class Predict {
return sol; return sol;
} }
/**See {@link #intercept(float, float, float, float, float, float, float)}.*/ /**
* See {@link #intercept(float, float, float, float, float, float, float)}.
*/
public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v){ 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); return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getVelocity().x - src.getVelocity().x, dst.getVelocity().x - src.getVelocity().y, v);
} }
@@ -12,7 +12,9 @@ import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; 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{ public class StatusController implements Saveable{
private static final StatusEntry globalResult = new StatusEntry(); private static final StatusEntry globalResult = new StatusEntry();
private static final Array<StatusEntry> removals = new ThreadArray<>(); private static final Array<StatusEntry> removals = new ThreadArray<>();
@@ -37,11 +37,11 @@ import static io.anuke.mindustry.Vars.world;
public class TileEntity extends BaseEntity implements TargetTrait{ public class TileEntity extends BaseEntity implements TargetTrait{
public static final float timeToSleep = 60f * 4; //4 seconds to fall asleep public static final float timeToSleep = 60f * 4; //4 seconds to fall asleep
/**This value is only used for debugging.*/
public static int sleepingEntities = 0;
private static final ObjectSet<Tile> tmpTiles = new ObjectSet<>(); private static final ObjectSet<Tile> tmpTiles = new ObjectSet<>();
/**
* This value is only used for debugging.
*/
public static int sleepingEntities = 0;
public Tile tile; public Tile tile;
public Timer timer; public Timer timer;
public float health; public float health;
@@ -51,13 +51,25 @@ public class TileEntity extends BaseEntity implements TargetTrait {
public LiquidModule liquids; public LiquidModule liquids;
public ConsumeModule cons; public ConsumeModule cons;
//list of (cached) tiles with entities in proximity, used for outputting to /**List of (cached) tiles with entities in proximity, used for outputting to*/
//TODO implement
private Array<Tile> proximity = new Array<>(8); private Array<Tile> proximity = new Array<>(8);
private boolean dead = false; private boolean dead = false;
private boolean sleeping; private boolean sleeping;
private float sleepTime; private float sleepTime;
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDamage(Tile tile, float health){
if(tile.entity != null){
tile.entity.health = health;
}
}
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDestroyed(Tile tile){
if(tile.entity == null) return;
tile.entity.onDeath();
}
/**Sets this tile entity data to this tile, and adds it if necessary.*/ /**Sets this tile entity data to this tile, and adds it if necessary.*/
public TileEntity init(Tile tile, boolean added){ public TileEntity init(Tile tile, boolean added){
this.tile = tile; this.tile = tile;
@@ -75,8 +87,10 @@ public class TileEntity extends BaseEntity implements TargetTrait {
return this; return this;
} }
/**Call when nothing is happening to the entity. /**
* This increments the internal sleep timer.*/ * Call when nothing is happening to the entity.
* This increments the internal sleep timer.
*/
public void sleep(){ public void sleep(){
sleepTime += Timers.delta(); sleepTime += Timers.delta();
if(!sleeping && sleepTime >= timeToSleep){ if(!sleeping && sleepTime >= timeToSleep){
@@ -86,8 +100,10 @@ public class TileEntity extends BaseEntity implements TargetTrait {
} }
} }
/**Call when something just happened to the entity. /**
* If the entity was sleeping, this enables it. This also resets the sleep timer.*/ * 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(){ public void wakeUp(){
sleepTime = 0f; sleepTime = 0f;
if(sleeping){ if(sleeping){
@@ -105,8 +121,11 @@ public class TileEntity extends BaseEntity implements TargetTrait {
return dead; return dead;
} }
public void write(DataOutputStream stream) throws IOException{} public void write(DataOutputStream stream) throws IOException{
public void read(DataInputStream stream) throws IOException{} }
public void read(DataInputStream stream) throws IOException{
}
private void onDeath(){ private void onDeath(){
if(!dead){ if(!dead){
@@ -231,17 +250,4 @@ public class TileEntity extends BaseEntity implements TargetTrait {
public EntityGroup targetGroup(){ public EntityGroup targetGroup(){
return tileGroup; return tileGroup;
} }
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDamage(Tile tile, float health){
if(tile.entity != null){
tile.entity.health = health;
}
}
@Remote(called = Loc.server, in = In.blocks)
public static void onTileDestroyed(Tile tile){
if(tile.entity == null) return;
tile.entity.onDeath();
}
} }
+34 -12
View File
@@ -32,11 +32,17 @@ import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait, CarriableTrait, InventoryTrait{ public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait, CarriableTrait, InventoryTrait{
/**total duration of hit flash effect*/ /**
* total duration of hit flash effect
*/
public static final float hitDuration = 9f; 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; public static final float velocityPercision = 8f;
/**Maximum absolute value of a velocity vector component.*/ /**
* Maximum absolute value of a velocity vector component.
*/
public static final float maxAbsVelocity = 127f / velocityPercision; public static final float maxAbsVelocity = 127f / velocityPercision;
public static final float elevationScale = 4f; public static final float elevationScale = 4f;
@@ -71,13 +77,13 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
} }
@Override @Override
public void setCarrier(CarryTrait carrier) { public CarryTrait getCarrier(){
this.carrier = carrier; return carrier;
} }
@Override @Override
public CarryTrait getCarrier() { public void setCarrier(CarryTrait carrier){
return carrier; this.carrier = carrier;
} }
@Override @Override
@@ -201,14 +207,17 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
public void avoidOthers(float avoidRange){ public void avoidOthers(float avoidRange){
EntityPhysics.getNearby(getGroup(), x, y, avoidRange * 2f, t -> { 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; if(t == this || (t instanceof Unit && (((Unit) t).isDead() || (((Unit) t).isFlying() != isFlying()) || ((Unit) t).getCarrier() == this) || getCarrier() == t))
return;
float dst = distanceTo(t); float dst = distanceTo(t);
if(dst > avoidRange) return; if(dst > avoidRange) return;
velocity.add(moveVector.set(x, y).sub(t.getX(), t.getY()).setLength(1f * (1f - (dst / avoidRange)))); 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){ public void updateVelocityStatus(float drag, float maxVelocity){
if(isCarried()){ //carried units do not take into account velocity normally if(isCarried()){ //carried units do not take into account velocity normally
set(carrier.getX(), carrier.getY()); set(carrier.getX(), carrier.getY());
@@ -302,9 +311,14 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
return inventory.totalAmmo() / (float) inventory.ammoCapacity(); return inventory.totalAmmo() / (float) inventory.ammoCapacity();
} }
public void drawUnder(){} public void drawUnder(){
public void drawOver(){} }
public void drawShadow(){}
public void drawOver(){
}
public void drawShadow(){
}
public void drawView(){ public void drawView(){
Fill.circle(x, y, getViewDistance()); Fill.circle(x, y, getViewDistance());
@@ -319,12 +333,20 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
} }
public abstract TextureRegion getIconRegion(); public abstract TextureRegion getIconRegion();
public abstract int getItemCapacity(); public abstract int getItemCapacity();
public abstract int getAmmoCapacity(); public abstract int getAmmoCapacity();
public abstract float getArmor(); public abstract float getArmor();
public abstract boolean acceptsAmmo(Item item); public abstract boolean acceptsAmmo(Item item);
public abstract void addAmmo(Item item); public abstract void addAmmo(Item item);
public abstract float getMass(); public abstract float getMass();
public abstract boolean isFlying(); public abstract boolean isFlying();
public abstract float getSize(); public abstract float getSize();
} }
@@ -8,15 +8,16 @@ import io.anuke.mindustry.type.AmmoType;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack; 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{ public class UnitInventory implements Saveable{
private final Unit unit;
private Array<AmmoEntry> ammos = new Array<>(); private Array<AmmoEntry> ammos = new Array<>();
private int totalAmmo; private int totalAmmo;
private ItemStack item = new ItemStack(Items.stone, 0); private ItemStack item = new ItemStack(Items.stone, 0);
private final Unit unit;
public UnitInventory(Unit unit){ public UnitInventory(Unit unit){
this.unit = unit; this.unit = unit;
} }
@@ -53,7 +54,9 @@ public class UnitInventory implements Saveable{
item.amount = iamount; 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(){ public float getAmmoRange(){
return hasAmmo() ? getAmmo().getRange() : Float.MAX_VALUE; return hasAmmo() ? getAmmo().getRange() : Float.MAX_VALUE;
} }
+51 -17
View File
@@ -15,7 +15,9 @@ import io.anuke.ucore.function.Predicate;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
/**Utility class for unit and team interactions.*/ /**
* Utility class for unit and team interactions.
*/
public class Units{ public class Units{
private static Rectangle rect = new Rectangle(); private static Rectangle rect = new Rectangle();
private static Rectangle hitrect = new Rectangle(); private static Rectangle hitrect = new Rectangle();
@@ -23,7 +25,9 @@ public class Units {
private static float cdist; private static float cdist;
private static boolean boolResult; private static boolean boolResult;
/**Validates a target. /**
* Validates a target.
*
* @param target The target to validate * @param target The target to validate
* @param team The team of the thing doing tha targeting * @param team The team of the thing doing tha targeting
* @param x The X position of the thing doign the targeting * @param x The X position of the thing doign the targeting
@@ -36,17 +40,23 @@ public class Units {
} }
/**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){ public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y){
return invalidateTarget(target, team, x, y, Float.MAX_VALUE); 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){ public static boolean invalidateTarget(TargetTrait target, Unit targeter){
return invalidateTarget(target, targeter.team, targeter.x, targeter.y, targeter.inventory.getAmmoRange()); 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){ public static boolean anyEntities(Tile tile){
Block type = tile.block(); Block type = tile.block();
rect.setSize(type.size * tilesize, type.size * tilesize); rect.setSize(type.size * tilesize, type.size * tilesize);
@@ -68,7 +78,9 @@ public class Units {
return boolResult; 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){ public static boolean anyEntities(Tile tile, float expansion, Predicate<Unit> pred){
Block type = tile.block(); Block type = tile.block();
rect.setSize(type.size * tilesize + expansion, type.size * tilesize + expansion); rect.setSize(type.size * tilesize + expansion, type.size * tilesize + expansion);
@@ -90,7 +102,9 @@ public class Units {
return value[0]; 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){ public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
for(Team enemy : state.teams.alliesOf(team)){ for(Team enemy : state.teams.alliesOf(team)){
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred); TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
@@ -101,7 +115,9 @@ public class Units {
return null; 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){ public static TileEntity findEnemyTile(Team team, float x, float y, float range, Predicate<Tile> pred){
for(Team enemy : state.teams.enemiesOf(team)){ for(Team enemy : state.teams.enemiesOf(team)){
TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred); TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred);
@@ -112,7 +128,9 @@ public class Units {
return null; 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){ public static void allUnits(Consumer<Unit> cons){
//check all unit groups first //check all unit groups first
for(EntityGroup<BaseUnit> group : unitGroups){ for(EntityGroup<BaseUnit> group : unitGroups){
@@ -129,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){ public static TargetTrait getClosestTarget(Team team, float x, float y, float range){
Unit unit = getClosestEnemy(team, x, y, range, u -> true); Unit unit = getClosestEnemy(team, x, y, range, u -> true);
if(unit != null){ if(unit != null){
@@ -139,7 +159,9 @@ 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){ public static Unit getClosestEnemy(Team team, float x, float y, float range, Predicate<Unit> predicate){
result = null; result = null;
cdist = 0f; cdist = 0f;
@@ -162,7 +184,9 @@ public class Units {
return result; 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){ public static Unit getClosest(Team team, float x, float y, float range, Predicate<Unit> predicate){
result = null; result = null;
cdist = 0f; cdist = 0f;
@@ -185,7 +209,9 @@ public class Units {
return result; 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){ public static void getNearby(Team team, Rectangle rect, Consumer<Unit> cons){
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()]; EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
@@ -199,7 +225,9 @@ public class Units {
}); });
} }
/**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){ public static void getNearby(Team team, float x, float y, float radius, Consumer<Unit> cons){
rect.setSize(radius * 2).setCenter(x, y); rect.setSize(radius * 2).setCenter(x, y);
@@ -220,7 +248,9 @@ public class Units {
}); });
} }
/**Iterates over all units in a rectangle.*/ /**
* Iterates over all units in a rectangle.
*/
public static void getNearby(Rectangle rect, Consumer<Unit> cons){ public static void getNearby(Rectangle rect, Consumer<Unit> cons){
for(Team team : Team.all){ for(Team team : Team.all){
@@ -234,7 +264,9 @@ public class Units {
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){ public static void getNearbyEnemies(Team team, Rectangle rect, Consumer<Unit> cons){
ObjectSet<Team> targets = state.teams.enemiesOf(team); ObjectSet<Team> targets = state.teams.enemiesOf(team);
@@ -253,7 +285,9 @@ public class Units {
}); });
} }
/**Iterates over all units.*/ /**
* Iterates over all units.
*/
public static void getAllUnits(Consumer<Unit> cons){ public static void getAllUnits(Consumer<Unit> cons){
for(Team team : Team.all){ for(Team team : Team.all){
@@ -12,7 +12,9 @@ import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
/**A BulletType for most ammo-based bullets shot from turrets and units.*/ /**
* A BulletType for most ammo-based bullets shot from turrets and units.
*/
public class BasicBulletType extends BulletType{ public class BasicBulletType extends BulletType{
public Color backColor = Palette.bulletYellowBack, frontColor = Palette.bulletYellow; public Color backColor = Palette.bulletYellowBack, frontColor = Palette.bulletYellow;
public float bulletWidth = 5f, bulletHeight = 7f; public float bulletWidth = 5f, bulletHeight = 7f;
@@ -23,7 +25,9 @@ public class BasicBulletType extends BulletType {
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f; public float fragVelocityMin = 0.2f, fragVelocityMax = 1f;
public BulletType fragBullet = null; 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 splashDamageRadius = -1f;
public float splashDamage = 6f; public float splashDamage = 6f;
@@ -28,12 +28,16 @@ import static io.anuke.mindustry.Vars.world;
public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncTrait{ 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 Team team;
private Object data; private Object data;
private boolean supressCollision; private boolean supressCollision;
public Timer timer = new Timer(3); /**
* Internal use only!
*/
public Bullet(){
}
public static void create(BulletType type, TeamTrait owner, float x, float y, float angle){ public static void create(BulletType type, TeamTrait owner, float x, float y, float angle){
create(type, owner, owner.getTeam(), x, y, angle); create(type, owner, owner.getTeam(), x, y, angle);
@@ -86,9 +90,6 @@ public class Bullet extends BulletEntity<BulletType> implements TeamTrait, SyncT
create(type, null, Team.none, x, y, angle); create(type, null, Team.none, x, y, angle);
} }
/**Internal use only!*/
public Bullet(){}
public boolean collidesTiles(){ public boolean collidesTiles(){
return type.collidesTiles; return type.collidesTiles;
} }
@@ -13,23 +13,41 @@ public abstract class BulletType extends BaseBulletType<Bullet> implements Conte
private static Array<BulletType> types = new Array<>(); private static Array<BulletType> types = new Array<>();
public final int id; public final int id;
/**Knockback in velocity.*/ /**
* Knockback in velocity.
*/
public float knockback; public float knockback;
/**Whether this bullet hits tiles.*/ /**
* Whether this bullet hits tiles.
*/
public boolean hitTiles = true; public boolean hitTiles = true;
/**Status effect applied on hit.*/ /**
* Status effect applied on hit.
*/
public StatusEffect status = StatusEffects.none; public StatusEffect status = StatusEffects.none;
/**Intensity of applied status effect in terms of duration.*/ /**
* Intensity of applied status effect in terms of duration.
*/
public float statusIntensity = 0.5f; public float statusIntensity = 0.5f;
/**What fraction of armor is pierced, 0-1*/ /**
* What fraction of armor is pierced, 0-1
*/
public float armorPierce = 0f; public float armorPierce = 0f;
/**Whether to sync this bullet to clients.*/ /**
* Whether to sync this bullet to clients.
*/
public boolean syncable; public boolean syncable;
/**Whether this bullet type collides with tiles.*/ /**
* Whether this bullet type collides with tiles.
*/
public boolean collidesTiles = true; public boolean collidesTiles = true;
/**Whether this bullet types collides with anything at all.*/ /**
* Whether this bullet types collides with anything at all.
*/
public boolean collides = true; public boolean collides = true;
/**Whether velocity is inherited from the shooter.*/ /**
* Whether velocity is inherited from the shooter.
*/
public boolean keepVelocity = true; public boolean keepVelocity = true;
public BulletType(float speed, float damage){ public BulletType(float speed, float damage){
@@ -43,6 +61,14 @@ public abstract class BulletType extends BaseBulletType<Bullet> implements Conte
types.add(this); types.add(this);
} }
public static BulletType getByID(int id){
return types.get(id);
}
public static Array<BulletType> all(){
return types;
}
@Override @Override
public void hit(Bullet b, float hitx, float hity){ public void hit(Bullet b, float hitx, float hity){
Effects.effect(hiteffect, hitx, hity, b.angle()); Effects.effect(hiteffect, hitx, hity, b.angle());
@@ -62,12 +88,4 @@ public abstract class BulletType extends BaseBulletType<Bullet> implements Conte
public Array<? extends Content> getAll(){ public Array<? extends Content> getAll(){
return types; return types;
} }
public static BulletType getByID(int id){
return types.get(id);
}
public static Array<BulletType> all(){
return types;
}
} }
@@ -10,7 +10,9 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.groundEffectGroup; import static io.anuke.mindustry.Vars.groundEffectGroup;
/**Class for creating block rubble on the ground.*/ /**
* Class for creating block rubble on the ground.
*/
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{ public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{
private static final Color color = Color.valueOf("52504e"); private static final Color color = Color.valueOf("52504e");
@@ -41,7 +41,15 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
private float baseFlammability = -1, puddleFlammability; private float baseFlammability = -1, puddleFlammability;
private float lifetime; 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){ public static void create(Tile tile){
if(Net.client() || tile == null) return; //not clientside. if(Net.client() || tile == null) return; //not clientside.
@@ -60,15 +68,19 @@ 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.*/ /**
* 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){ public static void extinguish(Tile tile, float intensity){
if(tile != null && map.containsKey(tile.packedPosition())){ if(tile != null && map.containsKey(tile.packedPosition())){
map.get(tile.packedPosition()).time += intensity * Timers.delta(); map.get(tile.packedPosition()).time += intensity * Timers.delta();
} }
} }
/**Deserialization use only!*/ @Remote(called = Loc.server, in = In.entities)
public Fire(){} public static void onFireRemoved(int fireid){
fireGroup.removeByID(fireid);
}
@Override @Override
public float lifetime(){ public float lifetime(){
@@ -196,9 +208,4 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable
public EntityGroup targetGroup(){ public EntityGroup targetGroup(){
return fireGroup; return fireGroup;
} }
@Remote(called = Loc.server, in = In.entities)
public static void onFireRemoved(int fireid){
fireGroup.removeByID(fireid);
}
} }
@@ -9,7 +9,9 @@ import io.anuke.ucore.entities.impl.EffectEntity;
import io.anuke.ucore.function.EffectRenderer; import io.anuke.ucore.function.EffectRenderer;
import io.anuke.ucore.util.Mathf; 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.*/ /**
* A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer.
*/
public class GroundEffectEntity extends EffectEntity{ public class GroundEffectEntity extends EffectEntity{
private boolean once; private boolean once;
@@ -53,12 +55,18 @@ public class GroundEffectEntity extends EffectEntity {
once = false; 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{ 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; 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 final boolean isStatic;
public GroundEffect(float life, float staticLife, EffectRenderer draw){ public GroundEffect(float life, float staticLife, EffectRenderer draw){
@@ -46,6 +46,14 @@ public class ItemDrop extends SolidEntity implements SaveTrait, SyncTrait, DrawT
private float time; private float time;
private float sinktime; 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){ public static ItemDrop create(Item item, int amount, float x, float y, float angle){
ItemDrop drop = new ItemDrop(); ItemDrop drop = new ItemDrop();
drop.item = item; drop.item = item;
@@ -73,12 +81,6 @@ 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; return item;
} }
@@ -32,17 +32,22 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
private PosTrait to; private PosTrait to;
private Runnable done; private Runnable done;
public ItemTransfer(){
}
@Remote(in = In.entities, called = Loc.server, unreliable = true) @Remote(in = In.entities, called = Loc.server, unreliable = true)
public static void transferAmmo(Item item, float x, float y, Unit to){ public static void transferAmmo(Item item, float x, float y, Unit to){
if(to == null) return; if(to == null) return;
to.addAmmo(item); to.addAmmo(item);
create(item, x, y, to, () -> {}); create(item, x, y, to, () -> {
});
} }
@Remote(in = In.entities, called = Loc.server, unreliable = true) @Remote(in = In.entities, called = Loc.server, unreliable = true)
public static void transferItemEffect(Item item, float x, float y, Unit to){ public static void transferItemEffect(Item item, float x, float y, Unit to){
if(to == null) return; if(to == null) return;
create(item, x, y, to, () -> {}); create(item, x, y, to, () -> {
});
} }
@Remote(in = In.entities, called = Loc.server, unreliable = true) @Remote(in = In.entities, called = Loc.server, unreliable = true)
@@ -55,7 +60,8 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){ public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){
if(tile == null) return; if(tile == null) return;
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){ for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
Timers.run(i*3, () -> create(item, x, y, tile, () -> {})); Timers.run(i * 3, () -> create(item, x, y, tile, () -> {
}));
} }
tile.entity.items.add(item, amount); tile.entity.items.add(item, amount);
} }
@@ -70,8 +76,6 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
tr.add(); tr.add();
} }
public ItemTransfer(){}
@Override @Override
public float lifetime(){ public float lifetime(){
return 60; return 60;
@@ -46,7 +46,15 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
private Color color = Palette.lancerLaser; private Color color = Palette.lancerLaser;
private SeedRandom random = new SeedRandom(); 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){ 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); CallEntity.createLighting(lastSeed++, team, effect, color, damage, x, y, targetAngle, length);
} }
@@ -115,9 +123,6 @@ public class Lightning extends TimedEntity implements Poolable, DrawTrait, SyncT
l.add(); l.add();
} }
/**For pooling use only. Do not call directly!*/
public Lightning(){}
@Override @Override
public boolean isSyncing(){ public boolean isSyncing(){
return false; return false;
@@ -58,17 +58,29 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
private float accepting; private float accepting;
private byte generation; 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){ public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
deposit(tile, source, liquid, amount, 0); 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){ public static void deposit(Tile tile, Liquid liquid, float amount){
deposit(tile, tile, liquid, amount, 0); 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){ public static Puddle getPuddle(Tile tile){
return map.get(tile.packedPosition()); return map.get(tile.packedPosition());
} }
@@ -108,13 +120,17 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
} }
} }
/**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){ private static boolean canStayOn(Liquid liquid, Liquid other){
return liquid == Liquids.oil && other == Liquids.water; 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){ 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) || if((dest.flammability > 0.3f && liquid.temperature > 0.7f) ||
(liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid (liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid
@@ -136,8 +152,10 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
return 0f; return 0f;
} }
/**Deserialization use only!*/ @Remote(called = Loc.server, in = In.entities)
public Puddle(){} public static void onPuddleRemoved(int puddleid){
puddleGroup.removeByID(puddleid);
}
public float getFlammability(){ public float getFlammability(){
return liquid.flammability * amount; return liquid.flammability * amount;
@@ -293,9 +311,4 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait
public EntityGroup targetGroup(){ public EntityGroup targetGroup(){
return puddleGroup; return puddleGroup;
} }
@Remote(called = Loc.server, in = In.entities)
public static void onPuddleRemoved(int puddleid){
puddleGroup.removeByID(puddleid);
}
} }
@@ -6,7 +6,9 @@ import io.anuke.ucore.util.Mathf;
public class RubbleDecal extends Decal{ public class RubbleDecal extends Decal{
private int size; 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){ public static void create(float x, float y, int size){
RubbleDecal decal = new RubbleDecal(); RubbleDecal decal = new RubbleDecal();
decal.size = size; decal.size = size;
@@ -14,12 +14,11 @@ import static io.anuke.mindustry.Vars.shieldGroup;
//todo re-implement //todo re-implement
public class Shield extends BaseEntity implements DrawTrait{ public class Shield extends BaseEntity implements DrawTrait{
private final Tile tile;
public boolean active; public boolean active;
public boolean hitPlayers = false; public boolean hitPlayers = false;
public float radius = 0f; public float radius = 0f;
private float uptime = 0f; private float uptime = 0f;
private final Tile tile;
public Shield(Tile tile){ public Shield(Tile tile){
this.tile = tile; this.tile = tile;
@@ -1,5 +1,7 @@
package io.anuke.mindustry.entities.traits; package io.anuke.mindustry.entities.traits;
/**A flag interface for marking an effect as appearing below liquids.*/ /**
* A flag interface for marking an effect as appearing below liquids.
*/
public interface BelowLiquidTrait{ public interface BelowLiquidTrait{
} }
@@ -36,29 +36,43 @@ import java.util.Arrays;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world; 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{ public interface BuilderTrait extends Entity{
//these are not instance variables! //these are not instance variables!
Translator[] tmptr = {new Translator(), new Translator(), new Translator(), new Translator()}; Translator[] tmptr = {new Translator(), new Translator(), new Translator(), new Translator()};
float placeDistance = 140f; float placeDistance = 140f;
float mineDistance = 70f; float mineDistance = 70f;
/**Returns the queue for storing build requests.*/ /**
* Returns the queue for storing build requests.
*/
Queue<BuildRequest> getPlaceQueue(); Queue<BuildRequest> getPlaceQueue();
/**Returns the tile this builder is currently mining.*/ /**
* Returns the tile this builder is currently mining.
*/
Tile getMineTile(); Tile getMineTile();
/**Sets the tile this builder is currently mining.*/ /**
* Sets the tile this builder is currently mining.
*/
void setMineTile(Tile tile); 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(); 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); float getBuildPower(Tile tile);
/**Whether this type of builder can begin creating new blocks.*/ /**
* Whether this type of builder can begin creating new blocks.
*/
default boolean canCreateBlocks(){ default boolean canCreateBlocks(){
return true; return true;
} }
@@ -106,13 +120,17 @@ 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(){ default boolean isBuilding(){
return getPlaceQueue().size != 0; 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){ default void replaceBuilding(int x, int y, int rotation, Recipe recipe){
synchronized(getPlaceQueue()){ synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : getPlaceQueue()){
@@ -127,12 +145,16 @@ public interface BuilderTrait extends Entity{
addBuildRequest(new BuildRequest(x, y, rotation, recipe)); addBuildRequest(new BuildRequest(x, y, rotation, recipe));
} }
/**Clears the placement queue.*/ /**
* Clears the placement queue.
*/
default void clearBuilding(){ default void clearBuilding(){
getPlaceQueue().clear(); 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){ default void addBuildRequest(BuildRequest place){
synchronized(getPlaceQueue()){ synchronized(getPlaceQueue()){
for(BuildRequest request : getPlaceQueue()){ for(BuildRequest request : getPlaceQueue()){
@@ -144,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(){ default BuildRequest getCurrentRequest(){
synchronized(getPlaceQueue()){ synchronized(getPlaceQueue()){
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first(); 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){ default void updateBuilding(Unit unit){
BuildRequest current = getCurrentRequest(); BuildRequest current = getCurrentRequest();
@@ -203,7 +229,9 @@ public interface BuilderTrait extends Entity{
current.progress = entity.progress(); current.progress = entity.progress();
} }
/**Do not call directly.*/ /**
* Do not call directly.
*/
default void updateMining(Unit unit){ default void updateMining(Unit unit){
Tile tile = getMineTile(); Tile tile = getMineTile();
@@ -229,7 +257,9 @@ public interface BuilderTrait extends Entity{
} }
} }
/**Draw placement effects for an entity. This includes mining*/ /**
* Draw placement effects for an entity. This includes mining
*/
default void drawBuilding(Unit unit){ default void drawBuilding(Unit unit){
BuildRequest request; BuildRequest request;
@@ -286,7 +316,9 @@ public interface BuilderTrait extends Entity{
Draw.color(); Draw.color();
} }
/**Internal use only.*/ /**
* Internal use only.
*/
default void drawMining(Unit unit){ default void drawMining(Unit unit){
Tile tile = getMineTile(); Tile tile = getMineTile();
@@ -313,7 +345,9 @@ public interface BuilderTrait extends Entity{
Draw.color(); Draw.color();
} }
/**Class for storing build requests. Can be either a place or remove request.*/ /**
* Class for storing build requests. Can be either a place or remove request.
*/
class BuildRequest{ class BuildRequest{
public final int x, y, rotation; public final int x, y, rotation;
public final Recipe recipe; public final Recipe recipe;
@@ -321,7 +355,9 @@ public interface BuilderTrait extends Entity{
public float progress; public float progress;
/**This creates a build request.*/ /**
* This creates a build request.
*/
public BuildRequest(int x, int y, int rotation, Recipe recipe){ public BuildRequest(int x, int y, int rotation, Recipe recipe){
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -330,7 +366,9 @@ public interface BuilderTrait extends Entity{
this.remove = false; this.remove = false;
} }
/**This creates a remove request.*/ /**
* This creates a remove request.
*/
public BuildRequest(int x, int y){ public BuildRequest(int x, int y){
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -8,6 +8,7 @@ public interface CarriableTrait extends TeamTrait, TargetTrait, SolidTrait{
return getCarrier() != null; return getCarrier() != null;
} }
void setCarrier(CarryTrait carrier);
CarryTrait getCarrier(); CarryTrait getCarrier();
void setCarrier(CarryTrait carrier);
} }
@@ -10,28 +10,6 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.trait.SolidTrait; import io.anuke.ucore.entities.trait.SolidTrait;
public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{ 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) @Remote(called = Loc.both, targets = Loc.both, forward = true, in = In.entities)
static void dropSelf(Player player){ static void dropSelf(Player player){
if(player.getCarrier() != null){ if(player.getCarrier() != null){
@@ -62,4 +40,38 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
Effects.effect(UnitFx.unitPickup, trait); 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);
}
} }
@@ -2,6 +2,8 @@ package io.anuke.mindustry.entities.traits;
import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.entities.trait.Entity;
/**Marks an entity as serializable.*/ /**
* Marks an entity as serializable.
*/
public interface SaveTrait extends Entity, TypeTrait, Saveable{ public interface SaveTrait extends Entity, TypeTrait, Saveable{
} }
@@ -6,5 +6,6 @@ import java.io.IOException;
public interface Saveable{ public interface Saveable{
void writeSave(DataOutput stream) throws IOException; void writeSave(DataOutput stream) throws IOException;
void readSave(DataInput stream) throws IOException; void readSave(DataInput stream) throws IOException;
} }
@@ -7,6 +7,8 @@ import io.anuke.ucore.util.Timer;
public interface ShooterTrait extends VelocityTrait, TeamTrait, InventoryTrait{ public interface ShooterTrait extends VelocityTrait, TeamTrait, InventoryTrait{
Timer getTimer(); Timer getTimer();
int getShootTimer(boolean left); int getShootTimer(boolean left);
Weapon getWeapon(); Weapon getWeapon();
} }
@@ -5,6 +5,8 @@ import io.anuke.mindustry.world.Tile;
public interface SpawnerTrait{ public interface SpawnerTrait{
Tile getTile(); Tile getTile();
void updateSpawning(Unit unit); void updateSpawning(Unit unit);
float getSpawnProgress(); float getSpawnProgress();
} }
@@ -12,12 +12,16 @@ 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(){ static boolean isSmoothing(){
return threads.isEnabled() && threads.getTPS() <= Gdx.graphics.getFramesPerSecond() / 2f; 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){ default void setNet(float x, float y){
set(x, y); set(x, y);
@@ -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(){ 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(); getInterpolator().update();
@@ -40,17 +47,22 @@ public interface SyncTrait extends Entity, TypeTrait {
setY(getInterpolator().pos.y); 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(){ default Interpolator getInterpolator(){
return null; 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(){ default boolean isSyncing(){
return true; return true;
} }
//Read and write sync data, usually position //Read and write sync data, usually position
void write(DataOutput data) throws IOException; void write(DataOutput data) throws IOException;
void read(DataInput data, long time) throws IOException; void read(DataInput data, long time) throws IOException;
} }
@@ -1,16 +1,21 @@
package io.anuke.mindustry.entities.traits; package io.anuke.mindustry.entities.traits;
import io.anuke.mindustry.game.Team; 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.PosTrait;
import io.anuke.ucore.entities.trait.VelocityTrait;
/**Base interface for targetable entities.*/ /**
* Base interface for targetable entities.
*/
public interface TargetTrait extends PosTrait, VelocityTrait{ public interface TargetTrait extends PosTrait, VelocityTrait{
boolean isDead(); boolean isDead();
Team getTeam(); Team getTeam();
/**Whether this entity is a valid target.*/ /**
* Whether this entity is a valid target.
*/
default boolean isValid(){ default boolean isValid(){
return !isDead(); return !isDead();
} }
@@ -9,7 +9,9 @@ public interface TypeTrait {
Array<Supplier<? extends TypeTrait>> registeredTypes = new Array<>(); Array<Supplier<? extends TypeTrait>> registeredTypes = new Array<>();
ObjectIntMap<Class<? extends TypeTrait>> typeToID = new ObjectIntMap<>(); ObjectIntMap<Class<? extends TypeTrait>> typeToID = new ObjectIntMap<>();
/**Register and return a type ID. The supplier should return a fresh instace of that type.*/ /**
* Register and return a type ID. The supplier should return a fresh instace of that type.
*/
static <T extends TypeTrait> void registerType(Class<T> type, Supplier<T> supplier){ static <T extends TypeTrait> void registerType(Class<T> type, Supplier<T> supplier){
if(typeToID.get(type, -1) != -1){ if(typeToID.get(type, -1) != -1){
throw new RuntimeException("Type is already registered: '" + type + "'!"); throw new RuntimeException("Type is already registered: '" + type + "'!");
@@ -21,7 +23,9 @@ public interface TypeTrait {
lastRegisteredID[0]++; lastRegisteredID[0]++;
} }
/**Registers a syncable type by ID.*/ /**
* Registers a syncable type by ID.
*/
static Supplier<? extends TypeTrait> getTypeByID(int id){ static Supplier<? extends TypeTrait> getTypeByID(int id){
if(id == -1){ if(id == -1){
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?"); throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");
@@ -29,11 +33,14 @@ public interface TypeTrait {
return registeredTypes.get(id); return registeredTypes.get(id);
} }
/**Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX. /**
* Do not override!*/ * Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX.
* Do not override!
*/
default int getTypeID(){ default int getTypeID(){
int id = typeToID.get(getClass(), -1); int id = typeToID.get(getClass(), -1);
if(id == -1) throw new RuntimeException("Class of type '" + getClass() + "' is not registered! Did you forget to register it in ContentLoader#registerTypes()?"); if(id == -1)
throw new RuntimeException("Class of type '" + getClass() + "' is not registered! Did you forget to register it in ContentLoader#registerTypes()?");
return id; return id;
} }
} }
@@ -30,7 +30,10 @@ import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.*; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Timer;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@@ -55,7 +58,37 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
protected Squad squad; protected Squad squad;
protected int spawner; protected int spawner;
/**Initialize the type and team of this unit. Only call once!*/ /**
* internal constructor used for deserialization, DO NOT USE
*/
public BaseUnit(){
}
@Remote(called = Loc.server, in = In.entities)
public static void onUnitDeath(BaseUnit unit){
if(unit == null) return;
if(Net.server() || !Net.active()){
UnitDrops.dropItems(unit);
}
float explosiveness = 2f + (unit.inventory.hasItem() ? unit.inventory.getItem().item.explosiveness * unit.inventory.getItem().amount : 0f);
float flammability = (unit.inventory.hasItem() ? unit.inventory.getItem().item.flammability * unit.inventory.getItem().amount : 0f);
Damage.dynamicExplosion(unit.x, unit.y, flammability, explosiveness, 0f, unit.getSize() / 2f, Palette.darkFlame);
unit.onSuperDeath();
ScorchDecal.create(unit.x, unit.y);
Effects.effect(ExplosionFx.explosion, unit);
Effects.shake(2f, 2f, unit);
//must run afterwards so the unit's group is not null
threads.runDelay(unit::remove);
}
/**
* Initialize the type and team of this unit. Only call once!
*/
public void init(UnitType type, Team team){ public void init(UnitType type, Team team){
if(this.type != null) throw new RuntimeException("This unit is already initialized!"); if(this.type != null) throw new RuntimeException("This unit is already initialized!");
@@ -63,10 +96,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
this.team = team; this.team = team;
} }
public void setSpawner(Tile tile) {
this.spawner = tile.packedPosition();
}
public UnitType getType(){ public UnitType getType(){
return type; return type;
} }
@@ -75,10 +104,13 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return world.tile(spawner); return world.tile(spawner);
} }
/**internal constructor used for deserialization, DO NOT USE*/ public void setSpawner(Tile tile){
public BaseUnit(){} this.spawner = tile.packedPosition();
}
/**Sets this to a 'wave' unit, which means it has slightly different AI and will not run out of ammo.*/ /**
* Sets this to a 'wave' unit, which means it has slightly different AI and will not run out of ammo.
*/
public void setWave(){ public void setWave(){
isWave = true; isWave = true;
} }
@@ -120,7 +152,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
} }
} }
/**Only runs when the unit has a target.*/ /**
* Only runs when the unit has a target.
*/
public void behavior(){ public void behavior(){
} }
@@ -131,6 +165,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
target = null; target = null;
} }
} }
public void targetClosestAllyFlag(BlockFlag flag){ public void targetClosestAllyFlag(BlockFlag flag){
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag)); Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, flag));
if(target != null) this.target = target.entity; if(target != null) this.target = target.entity;
@@ -395,26 +430,4 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
public void onSuperDeath(){ public void onSuperDeath(){
super.onDeath(); super.onDeath();
} }
@Remote(called = Loc.server, in = In.entities)
public static void onUnitDeath(BaseUnit unit){
if(unit == null) return;
if(Net.server() || !Net.active()){
UnitDrops.dropItems(unit);
}
float explosiveness = 2f + (unit.inventory.hasItem() ? unit.inventory.getItem().item.explosiveness * unit.inventory.getItem().amount : 0f);
float flammability = (unit.inventory.hasItem() ? unit.inventory.getItem().item.flammability * unit.inventory.getItem().amount : 0f);
Damage.dynamicExplosion(unit.x, unit.y, flammability, explosiveness, 0f, unit.getSize()/2f, Palette.darkFlame);
unit.onSuperDeath();
ScorchDecal.create(unit.x, unit.y);
Effects.effect(ExplosionFx.explosion, unit);
Effects.shake(2f, 2f, unit);
//must run afterwards so the unit's group is not null
threads.runDelay(unit::remove);
}
} }
@@ -23,7 +23,96 @@ import static io.anuke.mindustry.Vars.world;
public abstract class FlyingUnit extends BaseUnit implements CarryTrait{ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
protected static Translator vec = new Translator(); protected static Translator vec = new Translator();
protected static float wobblyness = 0.6f; protected static float wobblyness = 0.6f;
public final UnitState
resupply = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
state.set(attack);
}else if(!targetHasFlag(BlockFlag.resupplyPoint)){
retarget(() -> targetClosestAllyFlag(BlockFlag.resupplyPoint));
}else{
circle(20f);
}
}
},
idle = new UnitState(){
public void update(){
retarget(() -> {
targetClosest();
targetClosestEnemyFlag(BlockFlag.target);
if(target != null){
setState(attack);
}
});
target = getClosestCore();
if(target != null){
circle(50f);
}
velocity.scl(0.8f);
}
},
attack = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(Units.invalidateTarget(target, team, x, y)){
target = null;
}
if(!inventory.hasAmmo()){
state.set(resupply);
}else if(target == null){
retarget(() -> {
targetClosest();
targetClosestEnemyFlag(BlockFlag.target);
targetClosestEnemyFlag(BlockFlag.producer);
if(target == null){
setState(idle);
}
});
}else{
attack(150f);
if((Mathf.angNear(angleTo(target), rotation, 15f) || !inventory.getAmmo().bullet.keepVelocity) //bombers don't care about rotation
&& distanceTo(target) < inventory.getAmmo().getRange()){
AmmoType ammo = inventory.getAmmo();
inventory.useAmmo();
Vector2 to = Predict.intercept(FlyingUnit.this, target, ammo.bullet.speed);
getWeapon().update(FlyingUnit.this, to.x, to.y);
}
}
}
},
retreat = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(health >= maxHealth()){
state.set(attack);
}else if(!targetHasFlag(BlockFlag.repair)){
retarget(() -> {
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
if(target != null) FlyingUnit.this.target = target.entity;
});
}else{
circle(20f);
}
}
};
protected Trail trail = new Trail(8); protected Trail trail = new Trail(8);
protected CarriableTrait carrying; protected CarriableTrait carrying;
@@ -164,95 +253,4 @@ public abstract class FlyingUnit extends BaseUnit implements CarryTrait{
velocity.add(vec); velocity.add(vec);
} }
public final UnitState
resupply = new UnitState(){
public void entered() {
target = null;
}
public void update() {
if(inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
state.set(attack);
}else if(!targetHasFlag(BlockFlag.resupplyPoint)){
retarget(() -> targetClosestAllyFlag(BlockFlag.resupplyPoint));
}else{
circle(20f);
}
}
},
idle = new UnitState() {
public void update() {
retarget(() -> {
targetClosest();
targetClosestEnemyFlag(BlockFlag.target);
if(target != null){
setState(attack);
}
});
target = getClosestCore();
if(target != null){
circle(50f);
}
velocity.scl(0.8f);
}
},
attack = new UnitState(){
public void entered() {
target = null;
}
public void update() {
if(Units.invalidateTarget(target, team, x, y)){
target = null;
}
if(!inventory.hasAmmo()) {
state.set(resupply);
}else if (target == null){
retarget(() -> {
targetClosest();
targetClosestEnemyFlag(BlockFlag.target);
targetClosestEnemyFlag(BlockFlag.producer);
if(target == null){
setState(idle);
}
});
}else{
attack(150f);
if ((Mathf.angNear(angleTo(target), rotation, 15f) || !inventory.getAmmo().bullet.keepVelocity) //bombers don't care about rotation
&& distanceTo(target) < inventory.getAmmo().getRange()) {
AmmoType ammo = inventory.getAmmo();
inventory.useAmmo();
Vector2 to = Predict.intercept(FlyingUnit.this, target, ammo.bullet.speed);
getWeapon().update(FlyingUnit.this, to.x, to.y);
}
}
}
},
retreat = new UnitState() {
public void entered() {
target = null;
}
public void update() {
if(health >= maxHealth()){
state.set(attack);
}else if(!targetHasFlag(BlockFlag.repair)){
retarget(() -> {
Tile target = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair));
if (target != null) FlyingUnit.this.target = target.entity;
});
}else{
circle(20f);
}
}
};
} }
@@ -31,6 +31,83 @@ public abstract class GroundUnit extends BaseUnit {
protected float walkTime; protected float walkTime;
protected float baseRotation; protected float baseRotation;
public final UnitState
resupply = new UnitState(){
public void entered(){
target = null;
}
public void update(){
Tile tile = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.resupplyPoint));
if(tile != null && distanceTo(tile) > 40){
moveAwayFromCore();
}
//TODO move toward resupply point
if(isWave || inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
state.set(attack);
}
}
},
attack = new UnitState(){
public void entered(){
target = null;
}
public void update(){
TileEntity core = getClosestEnemyCore();
float dst = core == null ? 0 : distanceTo(core);
if(core != null && inventory.hasAmmo() && dst < inventory.getAmmo().getRange() / 1.1f){
target = core;
}else{
retarget(() -> targetClosest());
}
if(!inventory.hasAmmo()){
state.set(resupply);
}else if(target != null){
if(core != null){
if(dst > inventory.getAmmo().getRange() * 0.5f){
moveToCore();
}
}else{
moveToCore();
}
if(distanceTo(target) < inventory.getAmmo().getRange()){
rotate(angleTo(target));
if(Mathf.angNear(angleTo(target), rotation, 13f)){
AmmoType ammo = inventory.getAmmo();
Vector2 to = Predict.intercept(GroundUnit.this, target, ammo.bullet.speed);
getWeapon().update(GroundUnit.this, to.x, to.y);
}
}
}else{
moveToCore();
}
}
},
retreat = new UnitState(){
public void entered(){
target = null;
}
public void update(){
if(health >= health){
state.set(attack);
}
moveAwayFromCore();
}
};
protected Weapon weapon; protected Weapon weapon;
@Override @Override
@@ -75,6 +152,10 @@ public abstract class GroundUnit extends BaseUnit {
return weapon; return weapon;
} }
public void setWeapon(Weapon weapon){
this.weapon = weapon;
}
@Override @Override
public void draw(){ public void draw(){
Draw.alpha(hitTime / hitDuration); Draw.alpha(hitTime / hitDuration);
@@ -159,10 +240,6 @@ public abstract class GroundUnit extends BaseUnit {
super.readSave(stream); super.readSave(stream);
} }
public void setWeapon(Weapon weapon){
this.weapon = weapon;
}
protected void moveToCore(){ protected void moveToCore(){
Tile tile = world.tileWorld(x, y); Tile tile = world.tileWorld(x, y);
if(tile == null) return; if(tile == null) return;
@@ -189,82 +266,4 @@ public abstract class GroundUnit extends BaseUnit {
walkTime += Timers.delta(); walkTime += Timers.delta();
velocity.add(vec); velocity.add(vec);
} }
public final UnitState
resupply = new UnitState(){
public void entered() {
target = null;
}
public void update() {
Tile tile = Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.resupplyPoint));
if (tile != null && distanceTo(tile) > 40) {
moveAwayFromCore();
}
//TODO move toward resupply point
if(isWave || inventory.totalAmmo() + 10 >= inventory.ammoCapacity()){
state.set(attack);
}
}
},
attack = new UnitState(){
public void entered() {
target = null;
}
public void update() {
TileEntity core = getClosestEnemyCore();
float dst = core == null ? 0 :distanceTo(core);
if(core != null && inventory.hasAmmo() && dst < inventory.getAmmo().getRange()/1.1f){
target = core;
}else {
retarget(() -> targetClosest());
}
if(!inventory.hasAmmo()) {
state.set(resupply);
}else if(target != null){
if(core != null){
if(dst > inventory.getAmmo().getRange() * 0.5f){
moveToCore();
}
}else{
moveToCore();
}
if(distanceTo(target) < inventory.getAmmo().getRange()){
rotate(angleTo(target));
if (Mathf.angNear(angleTo(target), rotation, 13f)) {
AmmoType ammo = inventory.getAmmo();
Vector2 to = Predict.intercept(GroundUnit.this, target, ammo.bullet.speed);
getWeapon().update(GroundUnit.this, to.x, to.y);
}
}
}else{
moveToCore();
}
}
},
retreat = new UnitState() {
public void entered() {
target = null;
}
public void update() {
if(health >= health){
state.set(attack);
}
moveAwayFromCore();
}
};
} }
@@ -5,8 +5,10 @@ import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.threads; import static io.anuke.mindustry.Vars.threads;
/**Used to group entities together, for formations and such. /**
* Usually, squads are used by units spawned in the same wave.*/ * Used to group entities together, for formations and such.
* Usually, squads are used by units spawned in the same wave.
*/
public class Squad{ public class Squad{
public Vector2 direction = new Translator(); public Vector2 direction = new Translator();
public int units; public int units;
@@ -1,7 +1,12 @@
package io.anuke.mindustry.entities.units; package io.anuke.mindustry.entities.units;
public interface UnitState{ public interface UnitState{
default void entered(){} default void entered(){
default void exited(){} }
default void update(){}
default void exited(){
}
default void update(){
}
} }
@@ -18,12 +18,9 @@ import io.anuke.ucore.util.Bundles;
public class UnitType implements UnlockableContent{ public class UnitType implements UnlockableContent{
private static byte lastid = 0; private static byte lastid = 0;
private static Array<UnitType> types = new Array<>(); private static Array<UnitType> types = new Array<>();
protected final Supplier<? extends BaseUnit> constructor;
public final String name; public final String name;
public final byte id; public final byte id;
protected final Supplier<? extends BaseUnit> constructor;
public float health = 60; public float health = 60;
public float hitsize = 5f; public float hitsize = 5f;
public float hitsizeTile = 4f; public float hitsizeTile = 4f;
@@ -57,6 +54,14 @@ public class UnitType implements UnlockableContent{
TypeTrait.registerType(type, mainConstructor); TypeTrait.registerType(type, mainConstructor);
} }
public static UnitType getByID(byte id){
return types.get(id);
}
public static Array<UnitType> all(){
return types;
}
@Override @Override
public void displayInfo(Table table){ public void displayInfo(Table table){
ContentDisplay.displayUnit(table, this); ContentDisplay.displayUnit(table, this);
@@ -103,12 +108,4 @@ public class UnitType implements UnlockableContent{
unit.init(this, team); unit.init(this, team);
return unit; return unit;
} }
public static UnitType getByID(byte id){
return types.get(id);
}
public static Array<UnitType> all(){
return types;
}
} }
@@ -47,184 +47,6 @@ public class Drone extends FlyingUnit implements BuilderTrait {
protected Item targetItem; protected Item targetItem;
protected Tile mineTile; protected Tile mineTile;
protected Queue<BuildRequest> placeQueue = new ThreadQueue<>(); protected Queue<BuildRequest> placeQueue = new ThreadQueue<>();
/**Initialize placement event notifier system.
* Static initialization is to be avoided, thus, this is done lazily.*/
private static void initEvents(){
if(initialized) return;
toMine = ObjectSet.with(Items.lead, Items.tungsten);
Events.on(BlockBuildEvent.class, (team, tile) -> {
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
if(!(tile.entity instanceof BuildEntity)) return;
BuildEntity entity = tile.entity();
for(BaseUnit unit : group.all()){
if(unit instanceof Drone){
((Drone) unit).notifyPlaced(entity);
}
}
});
initialized = true;
}
{
initEvents();
}
private void notifyPlaced(BuildEntity entity){
float timeToBuild = entity.recipe.cost;
float dist = Math.min(entity.distanceTo(x, y) - placeDistance, 0);
if(dist / type.maxVelocity < timeToBuild * 0.9f){
//CallEntity.onDroneBeginBuild(this, entity.tile, entity.recipe);
target = entity;
setState(build);
}
}
@Override
public float getBuildPower(Tile tile) {
return type.buildPower;
}
@Override
public float getMinePower() {
return type.minePower;
}
@Override
public Queue<BuildRequest> getPlaceQueue() {
return placeQueue;
}
@Override
public Tile getMineTile() {
return mineTile;
}
@Override
public void setMineTile(Tile tile) {
mineTile = tile;
}
@Override
public void update() {
super.update();
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
updateBuilding(this);
}
@Override
protected void updateRotation() {
if(target != null && (state.is(repair) || state.is(mine))){
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
}else{
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
}
if(velocity.len() <= 0.2f && !(state.is(repair) && target != null)){
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 5f);
}
}
@Override
public void behavior() {
if(health <= health * type.retreatPercent &&
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
setState(retreat);
}
}
@Override
public UnitState getStartState() {
return repair;
}
@Override
public void drawOver() {
trail.draw(Palette.lightTrail, 3f);
TargetTrait entity = target;
if(entity instanceof TileEntity && state.is(repair)){
float len = 5f;
Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f));
Shapes.laser("beam", "beam-end",
x + Angles.trnsx(rotation, len),
y + Angles.trnsy(rotation, len),
entity.getX(), entity.getY());
Draw.color();
}
drawBuilding(this);
}
@Override
public float drawSize() {
return isBuilding() ? placeDistance*2f : 30f;
}
@Override
public float getAmmoFraction() {
return inventory.getItem().amount / (float)type.itemCapacity;
}
protected void findItem(){
TileEntity entity = getClosestCore();
if(entity == null){
return;
}
targetItem = Mathf.findMin(toMine, (a, b) -> -Integer.compare(entity.items.get(a), entity.items.get(b)));
}
protected boolean findItemDrop(){
TileEntity core = getClosestCore();
if(core == null) return false;
//find nearby dropped items to pick up if applicable
ItemDrop drop = EntityPhysics.getClosest(itemGroup, x, y, 60f,
item -> core.tile.block().acceptStack(item.getItem(), item.getAmount(), core.tile, Drone.this) == item.getAmount() &&
inventory.canAcceptItem(item.getItem(), 1));
if(drop != null){
setState(pickup);
target = drop;
return true;
}
return false;
}
@Override
public boolean canCreateBlocks() {
return false;
}
@Override
public void write(DataOutput data) throws IOException {
super.write(data);
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
writeBuilding(data);
}
@Override
public void read(DataInput data, long time) throws IOException {
super.read(data, time);
int mined = data.readInt();
readBuilding(data);
if(mined != -1){
mineTile = world.tile(mined);
}
}
public final UnitState public final UnitState
build = new UnitState(){ build = new UnitState(){
@@ -432,4 +254,183 @@ public class Drone extends FlyingUnit implements BuilderTrait {
} }
}; };
{
initEvents();
}
/**
* Initialize placement event notifier system.
* Static initialization is to be avoided, thus, this is done lazily.
*/
private static void initEvents(){
if(initialized) return;
toMine = ObjectSet.with(Items.lead, Items.tungsten);
Events.on(BlockBuildEvent.class, (team, tile) -> {
EntityGroup<BaseUnit> group = unitGroups[team.ordinal()];
if(!(tile.entity instanceof BuildEntity)) return;
BuildEntity entity = tile.entity();
for(BaseUnit unit : group.all()){
if(unit instanceof Drone){
((Drone) unit).notifyPlaced(entity);
}
}
});
initialized = true;
}
private void notifyPlaced(BuildEntity entity){
float timeToBuild = entity.recipe.cost;
float dist = Math.min(entity.distanceTo(x, y) - placeDistance, 0);
if(dist / type.maxVelocity < timeToBuild * 0.9f){
//CallEntity.onDroneBeginBuild(this, entity.tile, entity.recipe);
target = entity;
setState(build);
}
}
@Override
public float getBuildPower(Tile tile){
return type.buildPower;
}
@Override
public float getMinePower(){
return type.minePower;
}
@Override
public Queue<BuildRequest> getPlaceQueue(){
return placeQueue;
}
@Override
public Tile getMineTile(){
return mineTile;
}
@Override
public void setMineTile(Tile tile){
mineTile = tile;
}
@Override
public void update(){
super.update();
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.07f);
y += Mathf.cos(Timers.time() + id * 999, 25f, 0.07f);
updateBuilding(this);
}
@Override
protected void updateRotation(){
if(target != null && (state.is(repair) || state.is(mine))){
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f);
}else{
rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f);
}
if(velocity.len() <= 0.2f && !(state.is(repair) && target != null)){
rotation += Mathf.sin(Timers.time() + id * 99, 10f, 5f);
}
}
@Override
public void behavior(){
if(health <= health * type.retreatPercent &&
Geometry.findClosest(x, y, world.indexer().getAllied(team, BlockFlag.repair)) != null){
setState(retreat);
}
}
@Override
public UnitState getStartState(){
return repair;
}
@Override
public void drawOver(){
trail.draw(Palette.lightTrail, 3f);
TargetTrait entity = target;
if(entity instanceof TileEntity && state.is(repair)){
float len = 5f;
Draw.color(Color.BLACK, Color.WHITE, 0.95f + Mathf.absin(Timers.time(), 0.8f, 0.05f));
Shapes.laser("beam", "beam-end",
x + Angles.trnsx(rotation, len),
y + Angles.trnsy(rotation, len),
entity.getX(), entity.getY());
Draw.color();
}
drawBuilding(this);
}
@Override
public float drawSize(){
return isBuilding() ? placeDistance * 2f : 30f;
}
@Override
public float getAmmoFraction(){
return inventory.getItem().amount / (float) type.itemCapacity;
}
protected void findItem(){
TileEntity entity = getClosestCore();
if(entity == null){
return;
}
targetItem = Mathf.findMin(toMine, (a, b) -> -Integer.compare(entity.items.get(a), entity.items.get(b)));
}
protected boolean findItemDrop(){
TileEntity core = getClosestCore();
if(core == null) return false;
//find nearby dropped items to pick up if applicable
ItemDrop drop = EntityPhysics.getClosest(itemGroup, x, y, 60f,
item -> core.tile.block().acceptStack(item.getItem(), item.getAmount(), core.tile, Drone.this) == item.getAmount() &&
inventory.canAcceptItem(item.getItem(), 1));
if(drop != null){
setState(pickup);
target = drop;
return true;
}
return false;
}
@Override
public boolean canCreateBlocks(){
return false;
}
@Override
public void write(DataOutput data) throws IOException{
super.write(data);
data.writeInt(mineTile == null ? -1 : mineTile.packedPosition());
writeBuilding(data);
}
@Override
public void read(DataInput data, long time) throws IOException{
super.read(data, time);
int mined = data.readInt();
readBuilding(data);
if(mined != -1){
mineTile = world.tile(mined);
}
}
} }
+21 -9
View File
@@ -2,20 +2,32 @@ package io.anuke.mindustry.game;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
/**Base interface for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}.*/ /**
* Base interface for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}.
*/
public interface Content{ public interface Content{
/**Returns the type name of this piece of content. /**
* This should return the same value for all instances of this content type.*/ * Returns the type name of this piece of content.
* This should return the same value for all instances of this content type.
*/
String getContentTypeName(); String getContentTypeName();
/**Returns a list of all instances of this content.*/ /**
* Returns a list of all instances of this content.
*/
Array<? extends Content> getAll(); Array<? extends Content> getAll();
/**Called after all content is created. Do not use to load regions or texture data!*/ /**
default void init(){} * Called after all content is created. Do not use to load regions or texture data!
*/
default void init(){
}
/**Called after all content is created, only on non-headless versions. /**
* Use for loading regions or other image data.*/ * Called after all content is created, only on non-headless versions.
default void load(){} * Use for loading regions or other image data.
*/
default void load(){
}
} }
@@ -9,12 +9,18 @@ import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
public class ContentDatabase{ public class ContentDatabase{
/**Maps unlockable type names to a set of unlocked content.*/ /**
* Maps unlockable type names to a set of unlocked content.
*/
private ObjectMap<String, ObjectSet<String>> unlocked = new ObjectMap<>(); private ObjectMap<String, ObjectSet<String>> unlocked = new ObjectMap<>();
/**Whether unlockables have changed since the last save.*/ /**
* Whether unlockables have changed since the last save.
*/
private boolean dirty; private boolean dirty;
/**Returns whether or not this piece of content is unlocked yet.*/ /**
* Returns whether or not this piece of content is unlocked yet.
*/
public boolean isUnlocked(UnlockableContent content){ public boolean isUnlocked(UnlockableContent content){
if(!unlocked.containsKey(content.getContentTypeName())){ if(!unlocked.containsKey(content.getContentTypeName())){
unlocked.put(content.getContentTypeName(), new ObjectSet<>()); unlocked.put(content.getContentTypeName(), new ObjectSet<>());
@@ -25,10 +31,13 @@ public class ContentDatabase {
return set.contains(content.getContentName()); return set.contains(content.getContentName());
} }
/**Makes this piece of content 'unlocked', if possible. /**
* Makes this piece of content 'unlocked', if possible.
* If this piece of content is already unlocked or cannot be unlocked due to dependencies, nothing changes. * If this piece of content is already unlocked or cannot be unlocked due to dependencies, nothing changes.
* Results are not saved until you call {@link #save()}. * Results are not saved until you call {@link #save()}.
* @return whether or not this content was newly unlocked.*/ *
* @return whether or not this content was newly unlocked.
*/
public boolean unlockContent(UnlockableContent content){ public boolean unlockContent(UnlockableContent content){
if(!content.canBeUnlocked()) return false; if(!content.canBeUnlocked()) return false;
@@ -48,12 +57,16 @@ public class ContentDatabase {
return ret; return ret;
} }
/**Returns whether unlockables have changed since the last save.*/ /**
* Returns whether unlockables have changed since the last save.
*/
public boolean isDirty(){ public boolean isDirty(){
return dirty; return dirty;
} }
/**Clears all unlocked content.*/ /**
* Clears all unlocked content.
*/
public void reset(){ public void reset(){
unlocked.clear(); unlocked.clear();
dirty = true; dirty = true;
@@ -10,13 +10,19 @@ public enum Difficulty {
//purge removed due to new wave system //purge removed due to new wave system
/*purge(0.25f, 0.01f, 0.25f)*/; /*purge(0.25f, 0.01f, 0.25f)*/;
/**The scaling of how many waves it takes for one more enemy of a type to appear. /**
* The scaling of how many waves it takes for one more enemy of a type to appear.
* For example: with enemeyScaling = 2 and the default scaling being 2, it would take 4 waves for * For example: with enemeyScaling = 2 and the default scaling being 2, it would take 4 waves for
* an enemy spawn to go from 1->2 enemies.*/ * an enemy spawn to go from 1->2 enemies.
*/
public final float enemyScaling; public final float enemyScaling;
/**Multiplier of the time between waves.*/ /**
* Multiplier of the time between waves.
*/
public final float timeScaling; public final float timeScaling;
/**Scaling of max time between waves. Default time is 4 minutes.*/ /**
* Scaling of max time between waves. Default time is 4 minutes.
*/
public final float maxTimeScaling; public final float maxTimeScaling;
private String value; private String value;
@@ -22,19 +22,25 @@ public class EventType {
void handle(); void handle();
} }
/**This event is called from the logic thread. /**
* DO NOT INITIALIZE GRAPHICS HERE.*/ * This event is called from the logic thread.
* DO NOT INITIALIZE GRAPHICS HERE.
*/
public interface WorldLoadEvent extends Event{ public interface WorldLoadEvent extends Event{
void handle(); void handle();
} }
/**Called after the WorldLoadEvent is, and all logic has been loaded. /**
* It is safe to intialize graphics here.*/ * Called after the WorldLoadEvent is, and all logic has been loaded.
* It is safe to intialize graphics here.
*/
public interface WorldLoadGraphicsEvent extends Event{ public interface WorldLoadGraphicsEvent extends Event{
void handle(); void handle();
} }
/**Called from the logic thread. Do not access graphics here!*/ /**
* Called from the logic thread. Do not access graphics here!
*/
public interface TileChangeEvent extends Event{ public interface TileChangeEvent extends Event{
void handle(Tile tile); void handle(Tile tile);
} }
@@ -8,42 +8,72 @@ import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.StatusEffect; import io.anuke.mindustry.type.StatusEffect;
import io.anuke.mindustry.type.Weapon; import io.anuke.mindustry.type.Weapon;
/**A spawn group defines spawn information for a specific type of unit, with optional extra information like /**
* A spawn group defines spawn information for a specific type of unit, with optional extra information like
* weapon equipped, ammo used, and status effects. * weapon equipped, ammo used, and status effects.
* Each spawn group can have multiple sub-groups spawned in different areas of the map.*/ * Each spawn group can have multiple sub-groups spawned in different areas of the map.
*/
public class SpawnGroup{ public class SpawnGroup{
/**The unit type spawned*/ /**
* The unit type spawned
*/
public final UnitType type; public final UnitType type;
/**When this spawn should end*/ /**
* When this spawn should end
*/
protected int end = Integer.MAX_VALUE; protected int end = Integer.MAX_VALUE;
/**When this spawn should start*/ /**
* When this spawn should start
*/
protected int begin; protected int begin;
/**The spacing, in waves, of spawns. For example, 2 = spawns every other wave*/ /**
* The spacing, in waves, of spawns. For example, 2 = spawns every other wave
*/
protected int spacing = 1; protected int spacing = 1;
/**Maximum amount of units that spawn*/ /**
* Maximum amount of units that spawn
*/
protected int max = 60; protected int max = 60;
/**How many waves need to pass before the amount of units spawned increases by 1*/ /**
* How many waves need to pass before the amount of units spawned increases by 1
*/
protected float unitScaling = 9999f; protected float unitScaling = 9999f;
/**How many waves need to pass before the amount of instances of this group increases by 1*/ /**
* How many waves need to pass before the amount of instances of this group increases by 1
*/
protected float groupScaling = 9999f; protected float groupScaling = 9999f;
/**Amount of enemies spawned initially, with no scaling*/ /**
* Amount of enemies spawned initially, with no scaling
*/
protected int unitAmount = 1; protected int unitAmount = 1;
/**Amount of enemies spawned initially, with no scaling*/ /**
* Amount of enemies spawned initially, with no scaling
*/
protected int groupAmount = 1; protected int groupAmount = 1;
/**Weapon used by the spawned unit. Null to disable. Only applicable to ground units.*/ /**
* Weapon used by the spawned unit. Null to disable. Only applicable to ground units.
*/
protected Weapon weapon; protected Weapon weapon;
/**Status effect applied to the spawned unit. Null to disable.*/ /**
* Status effect applied to the spawned unit. Null to disable.
*/
protected StatusEffect effect; protected StatusEffect effect;
/**Items this unit spawns with. Null to disable.*/ /**
* Items this unit spawns with. Null to disable.
*/
protected ItemStack items; protected ItemStack items;
/**Ammo type this unit spawns with. Null to use the first available ammo.*/ /**
* Ammo type this unit spawns with. Null to use the first available ammo.
*/
protected Item ammoItem; protected Item ammoItem;
public SpawnGroup(UnitType type){ public SpawnGroup(UnitType type){
this.type = type; this.type = type;
} }
/**Returns the amount of units spawned on a specific wave.*/ /**
* Returns the amount of units spawned on a specific wave.
*/
public int getUnitsSpawned(int wave){ public int getUnitsSpawned(int wave){
if(wave < begin || wave > end || (wave - begin) % spacing != 0){ if(wave < begin || wave > end || (wave - begin) % spacing != 0){
return 0; return 0;
@@ -53,7 +83,9 @@ public class SpawnGroup {
return Math.min(unitAmount - 1 + Math.max((int) ((wave / spacing) / scaling), 1), max); return Math.min(unitAmount - 1 + Math.max((int) ((wave / spacing) / scaling), 1), max);
} }
/**Returns the amount of different unit groups at a specific wave.*/ /**
* Returns the amount of different unit groups at a specific wave.
*/
public int getGroupsSpawned(int wave){ public int getGroupsSpawned(int wave){
if(wave < begin || wave > end || (wave - begin) % spacing != 0){ if(wave < begin || wave > end || (wave - begin) % spacing != 0){
return 0; return 0;
@@ -63,8 +95,10 @@ public class SpawnGroup {
return Math.min(groupAmount - 1 + Math.max((int) ((wave / spacing) / groupScaling), 1), max); return Math.min(groupAmount - 1 + Math.max((int) ((wave / spacing) / groupScaling), 1), max);
} }
/**Creates a unit, and assigns correct values based on this group's data. /**
* This method does not add() the unit.*/ * Creates a unit, and assigns correct values based on this group's data.
* This method does not add() the unit.
*/
public BaseUnit createUnit(Team team){ public BaseUnit createUnit(Team team){
BaseUnit unit = type.create(team); BaseUnit unit = type.create(team);
+1 -2
View File
@@ -10,11 +10,10 @@ public enum Team {
purple(Color.valueOf("ba5bd9")), purple(Color.valueOf("ba5bd9")),
orange(Color.valueOf("e8c66a")); orange(Color.valueOf("e8c66a"));
public final static Team[] all = values();
public final Color color; public final Color color;
public final int intColor; public final int intColor;
public final static Team[] all = values();
Team(Color color){ Team(Color color){
this.color = color; this.color = color;
intColor = Color.rgba8888(color); intColor = Color.rgba8888(color);
+35 -14
View File
@@ -6,7 +6,9 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.util.ThreadArray; import io.anuke.ucore.util.ThreadArray;
import io.anuke.ucore.util.ThreadSet; import io.anuke.ucore.util.ThreadSet;
/**Class for various team-based utilities.*/ /**
* Class for various team-based utilities.
*/
public class TeamInfo{ public class TeamInfo{
private ObjectMap<Team, TeamData> map = new ObjectMap<>(); private ObjectMap<Team, TeamData> map = new ObjectMap<>();
private ThreadSet<Team> allies = new ThreadSet<>(), private ThreadSet<Team> allies = new ThreadSet<>(),
@@ -18,20 +20,27 @@ public class TeamInfo {
private int allyBits = 0; private int allyBits = 0;
private int enemyBits = 0; private int enemyBits = 0;
/**Returns all teams on a side.*/ /**
* Returns all teams on a side.
*/
public ObjectSet<TeamData> getTeams(boolean ally){ public ObjectSet<TeamData> getTeams(boolean ally){
return ally ? allyData : enemyData; return ally ? allyData : enemyData;
} }
/**Returns all team data.*/ /**
* Returns all team data.
*/
public ObjectSet<TeamData> getTeams(){ public ObjectSet<TeamData> getTeams(){
return allTeamData; return allTeamData;
} }
/**Register a team. /**
* Register a team.
*
* @param team The team type enum. * @param team The team type enum.
* @param ally Whether this team is an ally with the player or an enemy with the player. * @param ally Whether this team is an ally with the player or an enemy with the player.
* In PvP situations with dedicated servers, the sides can be arbitrary.*/ * In PvP situations with dedicated servers, the sides can be arbitrary.
*/
public void add(Team team, boolean ally){ public void add(Team team, boolean ally){
if(has(team)) throw new RuntimeException("Can't define team information twice!"); if(has(team)) throw new RuntimeException("Can't define team information twice!");
@@ -53,19 +62,25 @@ public class TeamInfo {
map.put(team, data); map.put(team, data);
} }
/**Returns team data by type. Call {@link #has(Team)} first to make sure it's active!*/ /**
* Returns team data by type. Call {@link #has(Team)} first to make sure it's active!
*/
public TeamData get(Team team){ public TeamData get(Team team){
if(!has(team)) throw new RuntimeException("This team is not active! Check has() before calling get()."); if(!has(team)) throw new RuntimeException("This team is not active! Check has() before calling get().");
return map.get(team); return map.get(team);
} }
/**Returns whether the specified team is active, e.g. whether it is participating in the game.*/ /**
* Returns whether the specified team is active, e.g. whether it is participating in the game.
*/
public boolean has(Team team){ public boolean has(Team team){
return map.containsKey(team); return map.containsKey(team);
} }
/**Returns a set of all teams that are enemies of this team. /**
* For teams not active, an empty set is returned.*/ * Returns a set of all teams that are enemies of this team.
* For teams not active, an empty set is returned.
*/
public ObjectSet<Team> enemiesOf(Team team){ public ObjectSet<Team> enemiesOf(Team team){
boolean ally = allies.contains(team); boolean ally = allies.contains(team);
boolean enemy = enemies.contains(team); boolean enemy = enemies.contains(team);
@@ -76,8 +91,10 @@ public class TeamInfo {
return ally ? enemies : allies; return ally ? enemies : allies;
} }
/**Returns a set of all teams that are allies of this team. /**
* For teams not active, an empty set is returned.*/ * Returns a set of all teams that are allies of this team.
* For teams not active, an empty set is returned.
*/
public ObjectSet<Team> alliesOf(Team team){ public ObjectSet<Team> alliesOf(Team team){
boolean ally = allies.contains(team); boolean ally = allies.contains(team);
boolean enemy = enemies.contains(team); boolean enemy = enemies.contains(team);
@@ -88,8 +105,10 @@ public class TeamInfo {
return !ally ? enemies : allies; return !ally ? enemies : allies;
} }
/**Returns a set of all teams that are enemies of this team. /**
* For teams not active, an empty set is returned.*/ * Returns a set of all teams that are enemies of this team.
* For teams not active, an empty set is returned.
*/
public ObjectSet<TeamData> enemyDataOf(Team team){ public ObjectSet<TeamData> enemyDataOf(Team team){
boolean ally = allies.contains(team); boolean ally = allies.contains(team);
boolean enemy = enemies.contains(team); boolean enemy = enemies.contains(team);
@@ -100,7 +119,9 @@ public class TeamInfo {
return ally ? enemyData : allyData; return ally ? enemyData : allyData;
} }
/**Returns whether or not these two teams are enemies.*/ /**
* Returns whether or not these two teams are enemies.
*/
public boolean areEnemies(Team team, Team other){ public boolean areEnemies(Team team, Team other){
if(team == other) return false; //fast fail to be more efficient if(team == other) return false; //fast fail to be more efficient
boolean ally = (allyBits & (1 << team.ordinal())) != 0; boolean ally = (allyBits & (1 << team.ordinal())) != 0;
@@ -5,37 +5,54 @@ import io.anuke.ucore.scene.ui.layout.Table;
import static io.anuke.mindustry.Vars.control; import static io.anuke.mindustry.Vars.control;
/**Base interface for an unlockable content type.*/ /**
* Base interface for an unlockable content type.
*/
public interface UnlockableContent extends Content{ public interface UnlockableContent extends Content{
/**Returns the unqiue name of this piece of content. /**
* Returns the unqiue name of this piece of content.
* The name only needs to be unique for all content of this type. * The name only needs to be unique for all content of this type.
* Do not use IDs for names! Make sure this string stays constant with each update unless removed. * Do not use IDs for names! Make sure this string stays constant with each update unless removed.
* (e.g. having a recipe and a block, both with name "wall" is fine, as they are different types).*/ * (e.g. having a recipe and a block, both with name "wall" is fine, as they are different types).
*/
String getContentName(); String getContentName();
/**Returns the localized name of this content.*/ /**
* Returns the localized name of this content.
*/
String localizedName(); String localizedName();
TextureRegion getContentIcon(); TextureRegion getContentIcon();
/**This should show all necessary info about this content in the specified table.*/ /**
* This should show all necessary info about this content in the specified table.
*/
void displayInfo(Table table); void displayInfo(Table table);
/**Called when this content is unlocked. Use this to unlock other related content.*/ /**
default void onUnlock(){} * Called when this content is unlocked. Use this to unlock other related content.
*/
default void onUnlock(){
}
/**Whether this content is always hidden in the content info dialog.*/ /**
* Whether this content is always hidden in the content info dialog.
*/
default boolean isHidden(){ default boolean isHidden(){
return false; return false;
} }
/**Lists the content that must be unlocked in order for this specific content to become unlocked. May return null.*/ /**
* Lists the content that must be unlocked in order for this specific content to become unlocked. May return null.
*/
default UnlockableContent[] getDependencies(){ default UnlockableContent[] getDependencies(){
return null; return null;
} }
/**Returns whether dependencies are satisfied for unlocking this content.*/ /**
* Returns whether dependencies are satisfied for unlocking this content.
*/
default boolean canBeUnlocked(){ default boolean canBeUnlocked(){
UnlockableContent[] depend = getDependencies(); UnlockableContent[] depend = getDependencies();
if(depend == null){ if(depend == null){
@@ -32,22 +32,9 @@ public class BlockRenderer{
} }
} }
private class BlockRequest implements Comparable<BlockRequest>{ /**
Tile tile; * Process all blocks to draw, simultaneously drawing block shadows and static blocks.
Layer layer; */
@Override
public int compareTo(BlockRequest other){
return layer.compareTo(other.layer);
}
@Override
public String toString(){
return tile.block().name + ":" + layer.toString();
}
}
/**Process all blocks to draw, simultaneously drawing block shadows and static blocks.*/
public void processBlocks(){ public void processBlocks(){
requestidx = 0; requestidx = 0;
lastLayer = null; lastLayer = null;
@@ -198,9 +185,11 @@ public class BlockRenderer{
floorRenderer.drawFloor(); floorRenderer.drawFloor();
} }
private void layerBegins(Layer layer){} private void layerBegins(Layer layer){
}
private void layerEnds(Layer layer){} private void layerEnds(Layer layer){
}
private void addRequest(Tile tile, Layer layer){ private void addRequest(Tile tile, Layer layer){
if(requestidx >= requests.size){ if(requestidx >= requests.size){
@@ -214,4 +203,19 @@ public class BlockRenderer{
r.layer = layer; r.layer = layer;
requestidx++; requestidx++;
} }
private class BlockRequest implements Comparable<BlockRequest>{
Tile tile;
Layer layer;
@Override
public int compareTo(BlockRequest other){
return layer.compareTo(other.layer);
}
@Override
public String toString(){
return tile.block().name + ":" + layer.toString();
}
}
} }
@@ -38,6 +38,35 @@ public class FloorRenderer {
Events.on(WorldLoadGraphicsEvent.class, this::clearTiles); Events.on(WorldLoadGraphicsEvent.class, this::clearTiles);
} }
static ShaderProgram createDefaultShader(){
String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projTrans;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main()\n" //
+ "{\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragmentShader = "#ifdef GL_ES\n" //
+ "#define LOWP lowp\n" //
+ "precision mediump float;\n" //
+ "#else\n" //
+ "#define LOWP \n" //
+ "#endif\n" //
+ "varying vec2 v_texCoords;\n" //
+ "uniform sampler2D u_texture;\n" //
+ "void main()\n"//
+ "{\n" //
+ " gl_FragColor = texture2D(u_texture, v_texCoords);\n" //
+ "}";
ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
if(!shader.isCompiled()) throw new IllegalArgumentException("Error compiling shader: " + shader.getLog());
return shader;
}
public void drawFloor(){ public void drawFloor(){
if(cache == null){ if(cache == null){
return; return;
@@ -211,10 +240,6 @@ public class FloorRenderer {
chunk.caches[layer.ordinal()] = cbatch.getLastCache(); chunk.caches[layer.ordinal()] = cbatch.getLastCache();
} }
private class Chunk{
int[] caches = new int[CacheLayer.values().length];
}
public void clearTiles(){ public void clearTiles(){
if(cbatch != null) cbatch.dispose(); if(cbatch != null) cbatch.dispose();
@@ -240,32 +265,7 @@ public class FloorRenderer {
Log.info("Time to cache: {0}", Timers.elapsed()); Log.info("Time to cache: {0}", Timers.elapsed());
} }
static ShaderProgram createDefaultShader () { private class Chunk{
String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" // int[] caches = new int[CacheLayer.values().length];
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projTrans;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main()\n" //
+ "{\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragmentShader = "#ifdef GL_ES\n" //
+ "#define LOWP lowp\n" //
+ "precision mediump float;\n" //
+ "#else\n" //
+ "#define LOWP \n" //
+ "#endif\n" //
+ "varying vec2 v_texCoords;\n" //
+ "uniform sampler2D u_texture;\n" //
+ "void main()\n"//
+ "{\n" //
+ " gl_FragColor = texture2D(u_texture, v_texCoords);\n" //
+ "}";
ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
if (!shader.isCompiled()) throw new IllegalArgumentException("Error compiling shader: " + shader.getLog());
return shader;
} }
} }
@@ -24,7 +24,9 @@ import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
/**Used for rendering fog of war. A framebuffer is used for this.*/ /**
* Used for rendering fog of war. A framebuffer is used for this.
*/
public class FogRenderer implements Disposable{ public class FogRenderer implements Disposable{
private TextureRegion region = new TextureRegion(); private TextureRegion region = new TextureRegion();
private FrameBuffer buffer; private FrameBuffer buffer;
@@ -1,16 +1,28 @@
package io.anuke.mindustry.graphics; package io.anuke.mindustry.graphics;
public enum Layer{ public enum Layer{
/**Base block layer.*/ /**
* Base block layer.
*/
block, block,
/**for placement*/ /**
* for placement
*/
placement, placement,
/**First overlay. Stuff like conveyor items.*/ /**
* First overlay. Stuff like conveyor items.
*/
overlay, overlay,
/**"High" blocks, like turrets.*/ /**
* "High" blocks, like turrets.
*/
turret, turret,
/**Power lasers.*/ /**
* Power lasers.
*/
power, power,
/**Extra lasers, like healing turrets.*/ /**
* Extra lasers, like healing turrets.
*/
laser laser
} }
@@ -12,7 +12,6 @@ import io.anuke.mindustry.game.TeamInfo.TeamData;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.consumers.Consume;
import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.mindustry.world.meta.BlockBar;
import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
@@ -8,7 +8,9 @@ import io.anuke.ucore.graphics.Fill;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
/**Class that renders a trail.*/ /**
* Class that renders a trail.
*/
public class Trail{ public class Trail{
private final static float maxJump = 15f; private final static float maxJump = 15f;
private final int length; private final int length;
@@ -3,7 +3,9 @@ package io.anuke.mindustry.input;
import io.anuke.ucore.function.Callable; import io.anuke.ucore.function.Callable;
import io.anuke.ucore.scene.utils.Cursors; import io.anuke.ucore.scene.utils.Cursors;
/**Type of cursor for displaying on desktop.*/ /**
* Type of cursor for displaying on desktop.
*/
public enum CursorType{ public enum CursorType{
normal(Cursors::restoreCursor), normal(Cursors::restoreCursor),
hand(Cursors::setHand), hand(Cursors::setHand),
@@ -16,7 +18,9 @@ public enum CursorType {
this.call = call; this.call = call;
} }
/**Sets the current system cursor to this.*/ /**
* Sets the current system cursor to this.
*/
void set(){ void set(){
call.run(); call.run();
} }
@@ -27,19 +27,26 @@ import static io.anuke.mindustry.input.CursorType.*;
import static io.anuke.mindustry.input.PlaceMode.*; import static io.anuke.mindustry.input.PlaceMode.*;
public class DesktopInput extends InputHandler{ public class DesktopInput extends InputHandler{
private final String section;
//controller info //controller info
private float controlx, controly; private float controlx, controly;
private boolean controlling; private boolean controlling;
private final String section; /**
* Current cursor type.
/**Current cursor type.*/ */
private CursorType cursorType = normal; private CursorType cursorType = normal;
/**Position where the player started dragging a line.*/ /**
* Position where the player started dragging a line.
*/
private int selectX, selectY; private int selectX, selectY;
/**Whether selecting mode is active.*/ /**
* Whether selecting mode is active.
*/
private PlaceMode mode; private PlaceMode mode;
/**Animation scale for line.*/ /**
* Animation scale for line.
*/
private float selectScale; private float selectScale;
public DesktopInput(Player player){ public DesktopInput(Player player){
@@ -47,7 +54,9 @@ public class DesktopInput extends InputHandler{
this.section = "player_" + (player.playerIndex + 1); this.section = "player_" + (player.playerIndex + 1);
} }
/**Draws a placement icon for a specific block.*/ /**
* Draws a placement icon for a specific block.
*/
void drawPlace(int x, int y, Block block, int rotation){ void drawPlace(int x, int y, Block block, int rotation){
if(validPlace(x, y, block, rotation)){ if(validPlace(x, y, block, rotation)){
Draw.color(); Draw.color();
@@ -33,12 +33,18 @@ import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public abstract class InputHandler extends InputAdapter{ public abstract class InputHandler extends InputAdapter{
/**Used for dropping items.*/ /**
* Used for dropping items.
*/
final static float playerSelectRange = mobile ? 17f : 11f; final static float playerSelectRange = mobile ? 17f : 11f;
/**Maximum line length.*/ /**
* Maximum line length.
*/
final static int maxLength = 100; final static int maxLength = 100;
final static Translator stackTrns = new Translator(); final static Translator stackTrns = new Translator();
/**Distance on the back from where items originate.*/ /**
* Distance on the back from where items originate.
*/
final static float backTrns = 3f; final static float backTrns = 3f;
public final Player player; public final Player player;
@@ -57,6 +63,73 @@ public abstract class InputHandler extends InputAdapter{
//methods to override //methods to override
@Remote(targets = Loc.client, called = Loc.server, in = In.entities)
public static void dropItem(Player player, float angle){
if(Net.server() && !player.inventory.hasItem()){
throw new ValidateException(player, "Player cannot drop an item.");
}
ItemDrop.create(player.inventory.getItem().item, player.inventory.getItem().amount, player.x, player.y, angle);
player.inventory.clearItem();
}
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void transferInventory(Player player, Tile tile){
if(Net.server() && (!player.inventory.hasItem() || player.isTransferring)){
throw new ValidateException(player, "Player cannot transfer an item.");
}
threads.run(() -> {
if(player == null || tile.entity == null) return;
player.isTransferring = true;
ItemStack stack = player.inventory.getItem();
int accepted = tile.block().acceptStack(stack.item, stack.amount, tile, player);
boolean clear = stack.amount == accepted;
int sent = Mathf.clamp(accepted / 4, 1, 8);
int removed = accepted / sent;
int[] remaining = {accepted, accepted};
for(int i = 0; i < sent; i++){
boolean end = i == sent - 1;
Timers.run(i * 3, () -> {
tile.block().getStackOffset(stack.item, tile, stackTrns);
ItemTransfer.create(stack.item,
player.x + Angles.trnsx(player.rotation + 180f, backTrns), player.y + Angles.trnsy(player.rotation + 180f, backTrns),
new Translator(tile.drawx() + stackTrns.x, tile.drawy() + stackTrns.y), () -> {
tile.block().handleStack(stack.item, removed, tile, player);
remaining[1] -= removed;
if(end && remaining[1] > 0){
tile.block().handleStack(stack.item, remaining[1], tile, player);
}
});
stack.amount -= removed;
remaining[0] -= removed;
if(end){
stack.amount -= remaining[0];
if(clear){
player.inventory.clearItem();
}
player.isTransferring = false;
}
});
}
});
}
@Remote(targets = Loc.both, called = Loc.server, forward = true, in = In.blocks)
public static void onTileTapped(Player player, Tile tile){
if(tile == null || player == null) return;
tile.block().tapped(tile, player);
}
public void update(){ public void update(){
} }
@@ -97,7 +170,9 @@ public abstract class InputHandler extends InputAdapter{
return false; return false;
} }
/**Handles tile tap events that are not platform specific.*/ /**
* Handles tile tap events that are not platform specific.
*/
boolean tileTapped(Tile tile){ boolean tileTapped(Tile tile){
tile = tile.target(); tile = tile.target();
@@ -153,7 +228,9 @@ public abstract class InputHandler extends InputAdapter{
return consumed; return consumed;
} }
/**Tries to select the player to drop off items, returns true if successful.*/ /**
* Tries to select the player to drop off items, returns true if successful.
*/
boolean tryTapPlayer(float x, float y){ boolean tryTapPlayer(float x, float y){
if(canTapPlayer(x, y)){ if(canTapPlayer(x, y)){
droppingItem = true; droppingItem = true;
@@ -166,7 +243,9 @@ public abstract class InputHandler extends InputAdapter{
return Vector2.dst(x, y, player.x, player.y) <= playerSelectRange && player.inventory.hasItem(); return Vector2.dst(x, y, player.x, player.y) <= playerSelectRange && player.inventory.hasItem();
} }
/**Tries to begin mining a tile, returns true if successful.*/ /**
* Tries to begin mining a tile, returns true if successful.
*/
boolean tryBeginMine(Tile tile){ boolean tryBeginMine(Tile tile){
if(canMine(tile)){ if(canMine(tile)){
//if a block is clicked twice, reset it //if a block is clicked twice, reset it
@@ -185,7 +264,9 @@ public abstract class InputHandler extends InputAdapter{
&& tile.block() == Blocks.air && player.distanceTo(tile.worldx(), tile.worldy()) <= Player.mineDistance; && tile.block() == Blocks.air && player.distanceTo(tile.worldx(), tile.worldy()) <= Player.mineDistance;
} }
/**Returns the tile at the specified MOUSE coordinates.*/ /**
* Returns the tile at the specified MOUSE coordinates.
*/
Tile tileAt(float x, float y){ Tile tileAt(float x, float y){
Vector2 vec = Graphics.world(x, y); Vector2 vec = Graphics.world(x, y);
if(isPlacing()){ if(isPlacing()){
@@ -279,71 +360,4 @@ public abstract class InputHandler extends InputAdapter{
player.addBuildRequest(new BuildRequest(tile.x, tile.y)); player.addBuildRequest(new BuildRequest(tile.x, tile.y));
} }
@Remote(targets = Loc.client, called = Loc.server, in = In.entities)
public static void dropItem(Player player, float angle){
if(Net.server() && !player.inventory.hasItem()){
throw new ValidateException(player, "Player cannot drop an item.");
}
ItemDrop.create(player.inventory.getItem().item, player.inventory.getItem().amount, player.x, player.y, angle);
player.inventory.clearItem();
}
@Remote(targets = Loc.both, forward = true, called = Loc.server, in = In.blocks)
public static void transferInventory(Player player, Tile tile){
if(Net.server() && (!player.inventory.hasItem() || player.isTransferring)) {
throw new ValidateException(player, "Player cannot transfer an item.");
}
threads.run(() -> {
if (player == null || tile.entity == null) return;
player.isTransferring = true;
ItemStack stack = player.inventory.getItem();
int accepted = tile.block().acceptStack(stack.item, stack.amount, tile, player);
boolean clear = stack.amount == accepted;
int sent = Mathf.clamp(accepted / 4, 1, 8);
int removed = accepted / sent;
int[] remaining = {accepted, accepted};
for (int i = 0; i < sent; i++) {
boolean end = i == sent - 1;
Timers.run(i * 3, () -> {
tile.block().getStackOffset(stack.item, tile, stackTrns);
ItemTransfer.create(stack.item,
player.x + Angles.trnsx(player.rotation + 180f, backTrns), player.y + Angles.trnsy(player.rotation + 180f, backTrns),
new Translator(tile.drawx() + stackTrns.x, tile.drawy() + stackTrns.y), () -> {
tile.block().handleStack(stack.item, removed, tile, player);
remaining[1] -= removed;
if (end && remaining[1] > 0) {
tile.block().handleStack(stack.item, remaining[1], tile, player);
}
});
stack.amount -= removed;
remaining[0] -= removed;
if (end) {
stack.amount -= remaining[0];
if (clear) {
player.inventory.clearItem();
}
player.isTransferring = false;
}
});
}
});
}
@Remote(targets = Loc.both, called = Loc.server, forward = true, in = In.blocks)
public static void onTileTapped(Player player, Tile tile){
if(tile == null || player == null) return;
tile.block().tapped(tile, player);
}
} }
@@ -39,11 +39,14 @@ import static io.anuke.mindustry.Vars.*;
import static io.anuke.mindustry.input.PlaceMode.*; import static io.anuke.mindustry.input.PlaceMode.*;
public class MobileInput extends InputHandler implements GestureListener{ public class MobileInput extends InputHandler implements GestureListener{
private static Rectangle r1 = new Rectangle(), r2 = new Rectangle(); /**
* Maximum speed the player can pan.
/**Maximum speed the player can pan.*/ */
private static final float maxPanSpeed = 1.3f; private static final float maxPanSpeed = 1.3f;
/**Distance to edge of screen to start panning.*/ private static Rectangle r1 = new Rectangle(), r2 = new Rectangle();
/**
* Distance to edge of screen to start panning.
*/
private final float edgePan = io.anuke.ucore.scene.ui.layout.Unit.dp.scl(60f); private final float edgePan = io.anuke.ucore.scene.ui.layout.Unit.dp.scl(60f);
//gesture data //gesture data
@@ -51,31 +54,53 @@ public class MobileInput extends InputHandler implements GestureListener{
private Vector2 vector = new Vector2(); private Vector2 vector = new Vector2();
private float initzoom = -1; private float initzoom = -1;
private boolean zoomed = false; private boolean zoomed = false;
/**Set of completed guides.*/ /**
* Set of completed guides.
*/
private ObjectSet<String> guides = new ObjectSet<>(); private ObjectSet<String> guides = new ObjectSet<>();
/**Position where the player started dragging a line.*/ /**
* Position where the player started dragging a line.
*/
private int lineStartX, lineStartY; private int lineStartX, lineStartY;
/**Animation scale for line.*/ /**
* Animation scale for line.
*/
private float lineScale; private float lineScale;
/**Animation data for crosshair.*/ /**
* Animation data for crosshair.
*/
private float crosshairScale; private float crosshairScale;
private TargetTrait lastTarget; private TargetTrait lastTarget;
/**List of currently selected tiles to place.*/ /**
* List of currently selected tiles to place.
*/
private Array<PlaceRequest> selection = new Array<>(); private Array<PlaceRequest> selection = new Array<>();
/**Place requests to be removed.*/ /**
* Place requests to be removed.
*/
private Array<PlaceRequest> removals = new Array<>(); private Array<PlaceRequest> removals = new Array<>();
/**Whether or not the player is currently shifting all placed tiles.*/ /**
* Whether or not the player is currently shifting all placed tiles.
*/
private boolean selecting; private boolean selecting;
/**Whether the player is currently in line-place mode.*/ /**
* Whether the player is currently in line-place mode.
*/
private boolean lineMode; private boolean lineMode;
/**Current place mode.*/ /**
* Current place mode.
*/
private PlaceMode mode = none; private PlaceMode mode = none;
/**Whether no recipe was available when switching to break mode.*/ /**
* Whether no recipe was available when switching to break mode.
*/
private Recipe lastRecipe; private Recipe lastRecipe;
/**Last placed request. Used for drawing block overlay.*/ /**
* Last placed request. Used for drawing block overlay.
*/
private PlaceRequest lastPlaced; private PlaceRequest lastPlaced;
public MobileInput(Player player){ public MobileInput(Player player){
@@ -85,7 +110,9 @@ public class MobileInput extends InputHandler implements GestureListener{
//region utility methods //region utility methods
/**Check and assign targets for a specific position.*/ /**
* Check and assign targets for a specific position.
*/
void checkTargets(float x, float y){ void checkTargets(float x, float y){
synchronized(Entities.entityLock){ synchronized(Entities.entityLock){
Unit unit = Units.getClosestEnemy(player.getTeam(), x, y, 20f, u -> true); Unit unit = Units.getClosestEnemy(player.getTeam(), x, y, 20f, u -> true);
@@ -103,12 +130,16 @@ public class MobileInput extends InputHandler implements GestureListener{
} }
} }
/**Returns whether this tile is in the list of requests, or at least colliding with one.*/ /**
* Returns whether this tile is in the list of requests, or at least colliding with one.
*/
boolean hasRequest(Tile tile){ boolean hasRequest(Tile tile){
return getRequest(tile) != null; return getRequest(tile) != null;
} }
/**Returns whether this block overlaps any selection requests.*/ /**
* Returns whether this block overlaps any selection requests.
*/
boolean checkOverlapPlacement(int x, int y, Block block){ boolean checkOverlapPlacement(int x, int y, Block block){
r2.setSize(block.size * tilesize); r2.setSize(block.size * tilesize);
r2.setCenter(x * tilesize + block.offset(), y * tilesize + block.offset()); r2.setCenter(x * tilesize + block.offset(), y * tilesize + block.offset());
@@ -128,7 +159,9 @@ public class MobileInput extends InputHandler implements GestureListener{
return false; return false;
} }
/**Returns the selection request that overlaps this tile, or null.*/ /**
* Returns the selection request that overlaps this tile, or null.
*/
PlaceRequest getRequest(Tile tile){ PlaceRequest getRequest(Tile tile){
r2.setSize(tilesize); r2.setSize(tilesize);
r2.setCenter(tile.worldx(), tile.worldy()); r2.setCenter(tile.worldx(), tile.worldy());
@@ -733,8 +766,15 @@ public class MobileInput extends InputHandler implements GestureListener{
zoomed = false; zoomed = false;
} }
@Override public boolean touchDown(float x, float y, int pointer, int button) { return false; } @Override
@Override public boolean fling(float velocityX, float velocityY, int button) { return false; } public boolean touchDown(float x, float y, int pointer, int button){
return false;
}
@Override
public boolean fling(float velocityX, float velocityY, int button){
return false;
}
//endregion //endregion
@@ -9,7 +9,8 @@ public class PlaceUtils {
private static final NormalizeResult result = new NormalizeResult(); private static final NormalizeResult result = new NormalizeResult();
private static final NormalizeDrawResult drawResult = new NormalizeDrawResult(); private static final NormalizeDrawResult drawResult = new NormalizeDrawResult();
/**Normalizes a placement area and returns the result, ready to be used for drawing a rectangle. /**
* Normalizes a placement area and returns the result, ready to be used for drawing a rectangle.
* Returned x2 and y2 will <i>always</i> be greater than x and y. * Returned x2 and y2 will <i>always</i> be greater than x and y.
* *
* @param block block that will be drawn * @param block block that will be drawn
@@ -45,7 +46,8 @@ public class PlaceUtils {
return drawResult; return drawResult;
} }
/**Normalizes a placement area and returns the result. /**
* Normalizes a placement area and returns the result.
* Returned x2 and y2 will <i>always</i> be greater than x and y. * Returned x2 and y2 will <i>always</i> be greater than x and y.
* *
* @param tilex starting X coordinate * @param tilex starting X coordinate
@@ -121,17 +123,23 @@ public class PlaceUtils {
return Math.abs(x2 - x) > Math.abs(y2 - y); return Math.abs(x2 - x) > Math.abs(y2 - y);
} }
/**Returns length of greater edge of the selection.*/ /**
* Returns length of greater edge of the selection.
*/
int getLength(){ int getLength(){
return Math.max(x2 - x, y2 - y); return Math.max(x2 - x, y2 - y);
} }
/**Returns the X position of a specific index along this area as a line.*/ /**
* Returns the X position of a specific index along this area as a line.
*/
int getScaledX(int i){ int getScaledX(int i){
return x + (x2 - x > y2 - y ? i : 0); return x + (x2 - x > y2 - y ? i : 0);
} }
/**Returns the Y position of a specific index along this area as a line.*/ /**
* Returns the Y position of a specific index along this area as a line.
*/
int getScaledY(int i){ int getScaledY(int i){
return y + (x2 - x > y2 - y ? 0 : i); return y + (x2 - x > y2 - y ? 0 : i);
} }
+15 -5
View File
@@ -7,15 +7,25 @@ import io.anuke.ucore.function.Supplier;
import java.io.InputStream; import java.io.InputStream;
public class Map{ public class Map{
/**Internal map name. This is the filename, without any extensions.*/ /**
* Internal map name. This is the filename, without any extensions.
*/
public final String name; public final String name;
/**Whether this is a custom map.*/ /**
* Whether this is a custom map.
*/
public final boolean custom; public final boolean custom;
/**Metadata. Author description, display name, etc.*/ /**
* Metadata. Author description, display name, etc.
*/
public final MapMeta meta; public final MapMeta meta;
/**Supplies a new input stream with the data of this map.*/ /**
* Supplies a new input stream with the data of this map.
*/
public final Supplier<InputStream> stream; public final Supplier<InputStream> stream;
/**Preview texture.*/ /**
* Preview texture.
*/
public Texture texture; public Texture texture;
public Map(String name, MapMeta meta, boolean custom, Supplier<InputStream> streamSupplier){ public Map(String name, MapMeta meta, boolean custom, Supplier<InputStream> streamSupplier){
+12 -4
View File
@@ -18,7 +18,9 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/**Reads and writes map files.*/ /**
* Reads and writes map files.
*/
public class MapIO{ public class MapIO{
private static final int version = 0; private static final int version = 0;
private static IntIntMap defaultBlockMap = new IntIntMap(); private static IntIntMap defaultBlockMap = new IntIntMap();
@@ -94,21 +96,27 @@ public class MapIO {
ds.close(); ds.close();
} }
/**Reads tile data, skipping meta.*/ /**
* Reads tile data, skipping meta.
*/
public static MapTileData readTileData(DataInputStream stream, boolean readOnly) throws IOException{ public static MapTileData readTileData(DataInputStream stream, boolean readOnly) throws IOException{
MapMeta meta = readMapMeta(stream); MapMeta meta = readMapMeta(stream);
return readTileData(stream, meta, readOnly); return readTileData(stream, meta, readOnly);
} }
/**Does not skip meta. Call after reading meta.*/ /**
* Does not skip meta. Call after reading meta.
*/
public static MapTileData readTileData(DataInputStream stream, MapMeta meta, boolean readOnly) throws IOException{ public static MapTileData readTileData(DataInputStream stream, MapMeta meta, boolean readOnly) throws IOException{
byte[] bytes = new byte[stream.available()]; byte[] bytes = new byte[stream.available()];
stream.readFully(bytes); stream.readFully(bytes);
return new MapTileData(bytes, meta.width, meta.height, meta.blockMap, readOnly); return new MapTileData(bytes, meta.width, meta.height, meta.blockMap, readOnly);
} }
/**Reads tile data, skipping meta tags.*/ /**
* Reads tile data, skipping meta tags.
*/
public static MapTileData readTileData(Map map, boolean readOnly){ public static MapTileData readTileData(Map map, boolean readOnly){
try(DataInputStream ds = new DataInputStream(map.stream.get())){ try(DataInputStream ds = new DataInputStream(map.stream.get())){
return MapIO.readTileData(ds, readOnly); return MapIO.readTileData(ds, readOnly);
@@ -6,12 +6,14 @@ import io.anuke.ucore.util.Bits;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public class MapTileData{ public class MapTileData{
/**Tile size: 4 bytes. <br> /**
* Tile size: 4 bytes. <br>
* 0: ground tile <br> * 0: ground tile <br>
* 1: wall tile <br> * 1: wall tile <br>
* 2: rotation + team <br> * 2: rotation + team <br>
* 3: link (x/y) <br> * 3: link (x/y) <br>
* 4: elevation <br>*/ * 4: elevation <br>
*/
private final static int TILE_SIZE = 5; private final static int TILE_SIZE = 5;
private final ByteBuffer buffer; private final ByteBuffer buffer;
@@ -59,28 +61,38 @@ public class MapTileData {
return height; return height;
} }
/**Write a byte to a specific position.*/ /**
* Write a byte to a specific position.
*/
public void write(int x, int y, DataPosition position, byte data){ public void write(int x, int y, DataPosition position, byte data){
buffer.put((x + width * y) * TILE_SIZE + position.ordinal(), data); buffer.put((x + width * y) * TILE_SIZE + position.ordinal(), data);
} }
/**Gets a byte at a specific position.*/ /**
* Gets a byte at a specific position.
*/
public byte read(int x, int y, DataPosition position){ public byte read(int x, int y, DataPosition position){
return buffer.get((x + width * y) * TILE_SIZE + position.ordinal()); return buffer.get((x + width * y) * TILE_SIZE + position.ordinal());
} }
/**Reads and returns the next tile data.*/ /**
* Reads and returns the next tile data.
*/
public TileDataMarker read(TileDataMarker marker){ public TileDataMarker read(TileDataMarker marker){
marker.read(buffer); marker.read(buffer);
return marker; return marker;
} }
/**Writes this tile data marker.*/ /**
* Writes this tile data marker.
*/
public void write(TileDataMarker marker){ public void write(TileDataMarker marker){
marker.write(buffer); marker.write(buffer);
} }
/**Sets read position to the specified coordinates*/ /**
* Sets read position to the specified coordinates
*/
public void position(int x, int y){ public void position(int x, int y){
buffer.position((x + width * y) * TILE_SIZE); buffer.position((x + width * y) * TILE_SIZE);
} }
+42 -14
View File
@@ -17,30 +17,46 @@ import java.io.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Maps implements Disposable{ public class Maps implements Disposable{
/**List of all built-in maps.*/ /**
* List of all built-in maps.
*/
private static final String[] defaultMapNames = {}; private static final String[] defaultMapNames = {};
/**Tile format version.*/ /**
* Tile format version.
*/
private static final int version = 0; private static final int version = 0;
/**Maps map names to the real maps.*/ /**
* Maps map names to the real maps.
*/
private ObjectMap<String, Map> maps = new ObjectMap<>(); private ObjectMap<String, Map> maps = new ObjectMap<>();
/**All maps stored in an ordered array.*/ /**
* All maps stored in an ordered array.
*/
private Array<Map> allMaps = new ThreadArray<>(); private Array<Map> allMaps = new ThreadArray<>();
/**Temporary array used for returning things.*/ /**
* Temporary array used for returning things.
*/
private Array<Map> returnArray = new ThreadArray<>(); private Array<Map> returnArray = new ThreadArray<>();
/**Used for storing a list of custom map names for GWT.*/ /**
* Used for storing a list of custom map names for GWT.
*/
private Array<String> customMapNames; private Array<String> customMapNames;
public Maps(){ public Maps(){
} }
/**Returns a list of all maps, including custom ones.*/ /**
* Returns a list of all maps, including custom ones.
*/
public Array<Map> all(){ public Array<Map> all(){
return allMaps; return allMaps;
} }
/**Returns a list of only custom maps.*/ /**
* Returns a list of only custom maps.
*/
public Array<Map> customMaps(){ public Array<Map> customMaps(){
returnArray.clear(); returnArray.clear();
for(Map map : allMaps){ for(Map map : allMaps){
@@ -49,7 +65,9 @@ public class Maps implements Disposable{
return returnArray; return returnArray;
} }
/**Returns a list of only default maps.*/ /**
* Returns a list of only default maps.
*/
public Array<Map> defaultMaps(){ public Array<Map> defaultMaps(){
returnArray.clear(); returnArray.clear();
for(Map map : allMaps){ for(Map map : allMaps){
@@ -58,12 +76,16 @@ public class Maps implements Disposable{
return returnArray; return returnArray;
} }
/**Returns map by internal name.*/ /**
* Returns map by internal name.
*/
public Map getByName(String name){ public Map getByName(String name){
return maps.get(name); return maps.get(name);
} }
/**Load all maps. Should be called at application start.*/ /**
* Load all maps. Should be called at application start.
*/
public void load(){ public void load(){
try{ try{
for(String name : defaultMapNames){ for(String name : defaultMapNames){
@@ -77,7 +99,9 @@ public class Maps implements Disposable{
loadCustomMaps(); loadCustomMaps();
} }
/**Save a map. This updates all values and stored data necessary.*/ /**
* Save a map. This updates all values and stored data necessary.
*/
public void saveMap(String name, MapTileData data, ObjectMap<String, String> tags){ public void saveMap(String name, MapTileData data, ObjectMap<String, String> tags){
try{ try{
if(!gwt){ if(!gwt){
@@ -114,7 +138,9 @@ public class Maps implements Disposable{
} }
} }
/**Removes a map completely.*/ /**
* Removes a map completely.
*/
public void removeMap(Map map){ public void removeMap(Map map){
if(map.texture != null){ if(map.texture != null){
map.texture.dispose(); map.texture.dispose();
@@ -177,7 +203,9 @@ public class Maps implements Disposable{
} }
} }
/**Returns an input stream supplier for a given map name.*/ /**
* Returns an input stream supplier for a given map name.
*/
private Supplier<InputStream> getStreamFor(String name){ private Supplier<InputStream> getStreamFor(String name){
if(!gwt){ if(!gwt){
return customMapDirectory.child(name + "." + mapExtension)::read; return customMapDirectory.child(name + "." + mapExtension)::read;
@@ -23,5 +23,6 @@ public abstract class SaveFileVersion {
} }
public abstract void read(DataInputStream stream) throws IOException; public abstract void read(DataInputStream stream) throws IOException;
public abstract void write(DataOutputStream stream) throws IOException; public abstract void write(DataOutputStream stream) throws IOException;
} }
+3 -1
View File
@@ -27,7 +27,9 @@ import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
/**Class for specifying read/write methods for code generation.*/ /**
* Class for specifying read/write methods for code generation.
*/
public class TypeIO{ public class TypeIO{
@WriteClass(Player.class) @WriteClass(Player.class)
@@ -19,11 +19,17 @@ public class Administration {
public static final int defaultMaxBrokenBlocks = 15; public static final int defaultMaxBrokenBlocks = 15;
public static final int defaultBreakCooldown = 1000 * 15; public static final int defaultBreakCooldown = 1000 * 15;
/**All player info. Maps UUIDs to info. This persists throughout restarts.*/ /**
* All player info. Maps UUIDs to info. This persists throughout restarts.
*/
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>(); private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
/**Maps UUIDs to trace infos. This is wiped when a player logs off.*/ /**
* Maps UUIDs to trace infos. This is wiped when a player logs off.
*/
private ObjectMap<String, TraceInfo> traceInfo = new ObjectMap<>(); private ObjectMap<String, TraceInfo> traceInfo = new ObjectMap<>();
/**Maps packed coordinates to logs for that coordinate */ /**
* Maps packed coordinates to logs for that coordinate
*/
private IntMap<Array<EditLog>> editLogs = new IntMap<>(); private IntMap<Array<EditLog>> editLogs = new IntMap<>();
private Array<String> bannedIPs = new Array<>(); private Array<String> bannedIPs = new Array<>();
@@ -42,6 +48,11 @@ public class Administration {
return Settings.getBool("antigrief"); return Settings.getBool("antigrief");
} }
public void setAntiGrief(boolean antiGrief){
Settings.putBool("antigrief", antiGrief);
Settings.save();
}
public boolean allowsCustomClients(){ public boolean allowsCustomClients(){
return Settings.getBool("allow-custom", !headless); return Settings.getBool("allow-custom", !headless);
} }
@@ -55,11 +66,6 @@ public class Administration {
return false; return false;
} }
public void setAntiGrief(boolean antiGrief){
Settings.putBool("antigrief", antiGrief);
Settings.save();
}
public void setAntiGriefParams(int maxBreak, int cooldown){ public void setAntiGriefParams(int maxBreak, int cooldown){
Settings.putInt("antigrief-max", maxBreak); Settings.putInt("antigrief-max", maxBreak);
Settings.putInt("antigrief-cooldown", cooldown); Settings.putInt("antigrief-cooldown", cooldown);
@@ -71,11 +77,11 @@ public class Administration {
} }
public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action){ public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action){
if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return; if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock)
return;
if(editLogs.containsKey(x + y * world.width())){ if(editLogs.containsKey(x + y * world.width())){
editLogs.get(x + y * world.width()).add(new EditLog(player.name, block, rotation, action)); editLogs.get(x + y * world.width()).add(new EditLog(player.name, block, rotation, action));
} }else{
else {
Array<EditLog> logs = new Array<>(); Array<EditLog> logs = new Array<>();
logs.add(new EditLog(player.name, block, rotation, action)); logs.add(new EditLog(player.name, block, rotation, action));
editLogs.put(x + y * world.width(), logs); editLogs.put(x + y * world.width(), logs);
@@ -167,7 +173,9 @@ public class Administration {
return true; return true;
} }
/**Call when a player joins to update their information here.*/ /**
* Call when a player joins to update their information here.
*/
public void updatePlayerJoined(String id, String ip, String name){ public void updatePlayerJoined(String id, String ip, String name){
PlayerInfo info = getCreateInfo(id); PlayerInfo info = getCreateInfo(id);
info.lastName = name; info.lastName = name;
@@ -177,7 +185,9 @@ public class Administration {
if(!info.ips.contains(ip, false)) info.ips.add(ip); if(!info.ips.contains(ip, false)) info.ips.add(ip);
} }
/**Returns trace info by IP.*/ /**
* Returns trace info by IP.
*/
public TraceInfo getTraceByID(String uuid){ public TraceInfo getTraceByID(String uuid){
if(!traceInfo.containsKey(uuid)) traceInfo.put(uuid, new TraceInfo(uuid)); if(!traceInfo.containsKey(uuid)) traceInfo.put(uuid, new TraceInfo(uuid));
@@ -188,8 +198,10 @@ public class Administration {
traceInfo.clear(); traceInfo.clear();
} }
/**Bans a player by IP; returns whether this player was already banned. /**
* If there are players who at any point had this IP, they will be UUID banned as well.*/ * Bans a player by IP; returns whether this player was already banned.
* If there are players who at any point had this IP, they will be UUID banned as well.
*/
public boolean banPlayerIP(String ip){ public boolean banPlayerIP(String ip){
if(bannedIPs.contains(ip, false)) if(bannedIPs.contains(ip, false))
return false; return false;
@@ -206,7 +218,9 @@ public class Administration {
return true; return true;
} }
/**Bans a player by UUID; returns whether this player was already banned.*/ /**
* Bans a player by UUID; returns whether this player was already banned.
*/
public boolean banPlayerID(String id){ public boolean banPlayerID(String id){
if(playerInfo.containsKey(id) && playerInfo.get(id).banned) if(playerInfo.containsKey(id) && playerInfo.get(id).banned)
return false; return false;
@@ -218,8 +232,10 @@ public class Administration {
return true; return true;
} }
/**Unbans a player by IP; returns whether this player was banned in the first place. /**
* This method also unbans any player that was banned and had this IP.*/ * Unbans a player by IP; returns whether this player was banned in the first place.
* This method also unbans any player that was banned and had this IP.
*/
public boolean unbanPlayerIP(String ip){ public boolean unbanPlayerIP(String ip){
boolean found = bannedIPs.contains(ip, false); boolean found = bannedIPs.contains(ip, false);
@@ -237,8 +253,10 @@ public class Administration {
return found; return found;
} }
/**Unbans a player by ID; returns whether this player was banned in the first place. /**
* This also unbans all IPs the player used.*/ * Unbans a player by ID; returns whether this player was banned in the first place.
* This also unbans all IPs the player used.
*/
public boolean unbanPlayerID(String id){ public boolean unbanPlayerID(String id){
PlayerInfo info = getCreateInfo(id); PlayerInfo info = getCreateInfo(id);
@@ -252,7 +270,9 @@ public class Administration {
return true; return true;
} }
/**Returns list of all players with admin status*/ /**
* Returns list of all players with admin status
*/
public Array<PlayerInfo> getAdmins(){ public Array<PlayerInfo> getAdmins(){
Array<PlayerInfo> result = new Array<>(); Array<PlayerInfo> result = new Array<>();
for(PlayerInfo info : playerInfo.values()){ for(PlayerInfo info : playerInfo.values()){
@@ -263,7 +283,9 @@ public class Administration {
return result; return result;
} }
/**Returns list of all players with admin status*/ /**
* Returns list of all players with admin status
*/
public Array<PlayerInfo> getBanned(){ public Array<PlayerInfo> getBanned(){
Array<PlayerInfo> result = new Array<>(); Array<PlayerInfo> result = new Array<>();
for(PlayerInfo info : playerInfo.values()){ for(PlayerInfo info : playerInfo.values()){
@@ -274,12 +296,16 @@ public class Administration {
return result; return result;
} }
/**Returns all banned IPs. This does not include the IPs of ID-banned players.*/ /**
* Returns all banned IPs. This does not include the IPs of ID-banned players.
*/
public Array<String> getBannedIPs(){ public Array<String> getBannedIPs(){
return bannedIPs; return bannedIPs;
} }
/**Makes a player an admin. Returns whether this player was already an admin.*/ /**
* Makes a player an admin. Returns whether this player was already an admin.
*/
public boolean adminPlayer(String id, String usid){ public boolean adminPlayer(String id, String usid){
PlayerInfo info = getCreateInfo(id); PlayerInfo info = getCreateInfo(id);
@@ -293,7 +319,9 @@ public class Administration {
return true; return true;
} }
/**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/ /**
* Makes a player no longer an admin. Returns whether this player was an admin in the first place.
*/
public boolean unAdminPlayer(String id){ public boolean unAdminPlayer(String id){
PlayerInfo info = getCreateInfo(id); PlayerInfo info = getCreateInfo(id);
@@ -401,7 +429,8 @@ public class Administration {
this.id = id; this.id = id;
} }
private PlayerInfo(){} private PlayerInfo(){
}
} }
} }
+3 -1
View File
@@ -1,6 +1,8 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
/**Stores class nameas for remote method invocation for consistency's sake.*/ /**
* Stores class nameas for remote method invocation for consistency's sake.
*/
public class In{ public class In{
public static final String normal = "Call"; public static final String normal = "Call";
public static final String entities = "CallEntity"; public static final String entities = "CallEntity";
+175 -71
View File
@@ -38,7 +38,9 @@ public class Net{
private static IntMap<StreamBuilder> streams = new IntMap<>(); private static IntMap<StreamBuilder> streams = new IntMap<>();
/**Display a network error.*/ /**
* Display a network error.
*/
public static void showError(String text){ public static void showError(String text){
if(!headless){ if(!headless){
ui.showError(text); ui.showError(text);
@@ -47,7 +49,9 @@ public class Net{
} }
} }
/**Sets the client loaded status, or whether it will recieve normal packets from the server.*/ /**
* Sets the client loaded status, or whether it will recieve normal packets from the server.
*/
public static void setClientLoaded(boolean loaded){ public static void setClientLoaded(boolean loaded){
clientLoaded = loaded; clientLoaded = loaded;
@@ -62,7 +66,9 @@ public class Net{
packetQueue.clear(); packetQueue.clear();
} }
/**Connect to an address.*/ /**
* Connect to an address.
*/
public static void connect(String ip, int port) throws IOException{ public static void connect(String ip, int port) throws IOException{
if(!active){ if(!active){
clientProvider.connect(ip, port); clientProvider.connect(ip, port);
@@ -73,7 +79,9 @@ public class Net{
} }
} }
/**Host a server at an address*/ /**
* Host a server at an address
*/
public static void host(int port) throws IOException{ public static void host(int port) throws IOException{
serverProvider.host(port); serverProvider.host(port);
active = true; active = true;
@@ -82,7 +90,9 @@ public class Net{
Timers.runTask(60f, Platform.instance::updateRPC); Timers.runTask(60f, Platform.instance::updateRPC);
} }
/**Closes the server.*/ /**
* Closes the server.
*/
public static void closeServer(){ public static void closeServer(){
serverProvider.close(); serverProvider.close();
server = false; server = false;
@@ -95,23 +105,31 @@ public class Net{
active = false; active = false;
} }
/**Starts discovering servers on a different thread. Does not work with GWT. /**
* Callback is run on the main libGDX thread.*/ * Starts discovering servers on a different thread. Does not work with GWT.
* Callback is run on the main libGDX thread.
*/
public static void discoverServers(Consumer<Array<Host>> cons){ public static void discoverServers(Consumer<Array<Host>> cons){
clientProvider.discover(cons); clientProvider.discover(cons);
} }
/**Returns a list of all connections IDs.*/ /**
* Returns a list of all connections IDs.
*/
public static Array<NetConnection> getConnections(){ public static Array<NetConnection> getConnections(){
return (Array<NetConnection>) serverProvider.getConnections(); return (Array<NetConnection>) serverProvider.getConnections();
} }
/**Returns a connection by ID*/ /**
* Returns a connection by ID
*/
public static NetConnection getConnection(int id){ public static NetConnection getConnection(int id){
return serverProvider.getByID(id); return serverProvider.getByID(id);
} }
/**Send an object to all connected clients, or to the server if this is a client.*/ /**
* Send an object to all connected clients, or to the server if this is a client.
*/
public static void send(Object object, SendMode mode){ public static void send(Object object, SendMode mode){
if(server){ if(server){
if(serverProvider != null) serverProvider.send(object, mode); if(serverProvider != null) serverProvider.send(object, mode);
@@ -120,42 +138,58 @@ public class Net{
} }
} }
/**Send an object to a certain client. Server-side only*/ /**
* Send an object to a certain client. Server-side only
*/
public static void sendTo(int id, Object object, SendMode mode){ public static void sendTo(int id, Object object, SendMode mode){
serverProvider.sendTo(id, object, mode); serverProvider.sendTo(id, object, mode);
} }
/**Send an object to everyone EXCEPT certain client. Server-side only*/ /**
* Send an object to everyone EXCEPT certain client. Server-side only
*/
public static void sendExcept(int id, Object object, SendMode mode){ public static void sendExcept(int id, Object object, SendMode mode){
serverProvider.sendExcept(id, object, mode); serverProvider.sendExcept(id, object, mode);
} }
/**Send a stream to a specific client. Server-side only.*/ /**
* Send a stream to a specific client. Server-side only.
*/
public static void sendStream(int id, Streamable stream){ public static void sendStream(int id, Streamable stream){
serverProvider.sendStream(id, stream); serverProvider.sendStream(id, stream);
} }
/**Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server.*/ /**
* Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server.
*/
public static void setClientProvider(ClientProvider provider){ public static void setClientProvider(ClientProvider provider){
Net.clientProvider = provider; Net.clientProvider = provider;
} }
/**Sets the net serverProvider, e.g. what handles hosting a server.*/ /**
* Sets the net serverProvider, e.g. what handles hosting a server.
*/
public static void setServerProvider(ServerProvider provider){ public static void setServerProvider(ServerProvider provider){
Net.serverProvider = provider; Net.serverProvider = provider;
} }
/**Registers a client listener for when an object is recieved.*/ /**
* Registers a client listener for when an object is recieved.
*/
public static <T> void handleClient(Class<T> type, Consumer<T> listener){ public static <T> void handleClient(Class<T> type, Consumer<T> listener){
clientListeners.put(type, listener); clientListeners.put(type, listener);
} }
/**Registers a server listener for when an object is recieved.*/ /**
* Registers a server listener for when an object is recieved.
*/
public static <T> void handleServer(Class<T> type, BiConsumer<Integer, T> listener){ public static <T> void handleServer(Class<T> type, BiConsumer<Integer, T> listener){
serverListeners.put(type, (BiConsumer<Integer, Object>) listener); serverListeners.put(type, (BiConsumer<Integer, Object>) listener);
} }
/**Call to handle a packet being recieved for the client.*/ /**
* Call to handle a packet being recieved for the client.
*/
public static void handleClientReceived(Object object){ public static void handleClientReceived(Object object){
if(object instanceof StreamBegin){ if(object instanceof StreamBegin){
@@ -175,7 +209,8 @@ public class Net{
}else if(clientListeners.get(object.getClass()) != null){ }else if(clientListeners.get(object.getClass()) != null){
if(clientLoaded || ((object instanceof Packet) && ((Packet) object).isImportant())){ if(clientLoaded || ((object instanceof Packet) && ((Packet) object).isImportant())){
if(clientListeners.get(object.getClass()) != null) clientListeners.get(object.getClass()).accept(object); if(clientListeners.get(object.getClass()) != null)
clientListeners.get(object.getClass()).accept(object);
synchronized(packetPoolLock){ synchronized(packetPoolLock){
Pooling.free(object); Pooling.free(object);
} }
@@ -192,11 +227,14 @@ public class Net{
} }
} }
/**Call to handle a packet being recieved for the server.*/ /**
* Call to handle a packet being recieved for the server.
*/
public static void handleServerReceived(int connection, Object object){ public static void handleServerReceived(int connection, Object object){
if(serverListeners.get(object.getClass()) != null){ if(serverListeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null) serverListeners.get(object.getClass()).accept(connection, object); if(serverListeners.get(object.getClass()) != null)
serverListeners.get(object.getClass()).accept(connection, object);
synchronized(packetPoolLock){ synchronized(packetPoolLock){
Pooling.free(object); Pooling.free(object);
} }
@@ -205,32 +243,44 @@ public class Net{
} }
} }
/**Pings a host in an new thread. If an error occured, failed() should be called with the exception. */ /**
* Pings a host in an new thread. If an error occured, failed() should be called with the exception.
*/
public static void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){ public static void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){
clientProvider.pingHost(address, port, valid, failed); clientProvider.pingHost(address, port, valid, failed);
} }
/**Update client ping.*/ /**
* Update client ping.
*/
public static void updatePing(){ public static void updatePing(){
clientProvider.updatePing(); clientProvider.updatePing();
} }
/**Get the client ping. Only valid after updatePing().*/ /**
* Get the client ping. Only valid after updatePing().
*/
public static int getPing(){ public static int getPing(){
return server() ? 0 : clientProvider.getPing(); return server() ? 0 : clientProvider.getPing();
} }
/**Whether the net is active, e.g. whether this is a multiplayer game.*/ /**
* Whether the net is active, e.g. whether this is a multiplayer game.
*/
public static boolean active(){ public static boolean active(){
return active; return active;
} }
/**Whether this is a server or not.*/ /**
* Whether this is a server or not.
*/
public static boolean server(){ public static boolean server(){
return server && active; return server && active;
} }
/**Whether this is a client or not.*/ /**
* Whether this is a client or not.
*/
public static boolean client(){ public static boolean client(){
return !server && active; return !server && active;
} }
@@ -260,54 +310,108 @@ public class Net{
} }
@Override @Override
public void cancelled() {} public void cancelled(){
}
}); });
} }
/**Client implementation.*/
public interface ClientProvider {
/**Connect to a server.*/
void connect(String ip, int port) throws IOException;
/**Send an object to the server.*/
void send(Object object, SendMode mode);
/**Update the ping. Should be done every second or so.*/
void updatePing();
/**Get ping in milliseconds. Will only be valid after a call to updatePing.*/
int getPing();
/**Disconnect from the server.*/
void disconnect();
/**Discover servers. This should run the callback regardless of whether any servers are found. Should not block.
* Callback should be run on libGDX main thread.*/
void discover(Consumer<Array<Host>> callback);
/**Ping a host. If an error occured, failed() should be called with the exception. */
void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed);
/**Close all connections.*/
void dispose();
}
/**Server implementation.*/
public interface ServerProvider {
/**Host a server at specified port.*/
void host(int port) throws IOException;
/**Sends a large stream of data to a specific client.*/
void sendStream(int id, Streamable stream);
/**Send an object to everyone connected.*/
void send(Object object, SendMode mode);
/**Send an object to a specific client ID.*/
void sendTo(int id, Object object, SendMode mode);
/**Send an object to everyone <i>except</i> a client ID.*/
void sendExcept(int id, Object object, SendMode mode);
/**Close the server connection.*/
void close();
/**Return all connected users.*/
Array<? extends NetConnection> getConnections();
/**Returns a connection by ID.*/
NetConnection getByID(int id);
/**Close all connections.*/
void dispose();
}
public enum SendMode{ public enum SendMode{
tcp, udp tcp, udp
} }
/**
* Client implementation.
*/
public interface ClientProvider{
/**
* Connect to a server.
*/
void connect(String ip, int port) throws IOException;
/**
* Send an object to the server.
*/
void send(Object object, SendMode mode);
/**
* Update the ping. Should be done every second or so.
*/
void updatePing();
/**
* Get ping in milliseconds. Will only be valid after a call to updatePing.
*/
int getPing();
/**
* Disconnect from the server.
*/
void disconnect();
/**
* Discover servers. This should run the callback regardless of whether any servers are found. Should not block.
* Callback should be run on libGDX main thread.
*/
void discover(Consumer<Array<Host>> callback);
/**
* Ping a host. If an error occured, failed() should be called with the exception.
*/
void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed);
/**
* Close all connections.
*/
void dispose();
}
/**
* Server implementation.
*/
public interface ServerProvider{
/**
* Host a server at specified port.
*/
void host(int port) throws IOException;
/**
* Sends a large stream of data to a specific client.
*/
void sendStream(int id, Streamable stream);
/**
* Send an object to everyone connected.
*/
void send(Object object, SendMode mode);
/**
* Send an object to a specific client ID.
*/
void sendTo(int id, Object object, SendMode mode);
/**
* Send an object to everyone <i>except</i> a client ID.
*/
void sendExcept(int id, Object object, SendMode mode);
/**
* Close the server connection.
*/
void close();
/**
* Return all connected users.
*/
Array<? extends NetConnection> getConnections();
/**
* Returns a connection by ID.
*/
NetConnection getByID(int id);
/**
* Close all connections.
*/
void dispose();
}
} }
@@ -6,10 +6,14 @@ public abstract class NetConnection {
public final int id; public final int id;
public final String address; public final String address;
/**The current base snapshot that the client is absolutely confirmed to have recieved. /**
* All sent snapshots should be taking the diff from this base snapshot, if it isn't null.*/ * The current base snapshot that the client is absolutely confirmed to have recieved.
* All sent snapshots should be taking the diff from this base snapshot, if it isn't null.
*/
public byte[] currentBaseSnapshot; public byte[] currentBaseSnapshot;
/**ID of the current base snapshot.*/ /**
* ID of the current base snapshot.
*/
public int currentBaseID = -1; public int currentBaseID = -1;
public int lastSentBase = -1; public int lastSentBase = -1;
@@ -17,9 +21,13 @@ public abstract class NetConnection {
public byte[] lastSentRawSnapshot; public byte[] lastSentRawSnapshot;
public int lastSentSnapshotID = -1; public int lastSentSnapshotID = -1;
/**ID of last recieved client snapshot.*/ /**
* ID of last recieved client snapshot.
*/
public int lastRecievedClientSnapshot = -1; public int lastRecievedClientSnapshot = -1;
/**Timestamp of last recieved snapshot.*/ /**
* Timestamp of last recieved snapshot.
*/
public long lastRecievedClientTime; public long lastRecievedClientTime;
public boolean hasConnected = false; public boolean hasConnected = false;
@@ -34,5 +42,6 @@ public abstract class NetConnection {
} }
public abstract void send(Object object, SendMode mode); public abstract void send(Object object, SendMode mode);
public abstract void close(); public abstract void close();
} }
@@ -112,7 +112,9 @@ public class NetworkIO {
} }
} }
/**Return whether a custom map is expected, and thus whether the client should wait for additional data.*/ /**
* Return whether a custom map is expected, and thus whether the client should wait for additional data.
*/
public static void loadWorld(InputStream is){ public static void loadWorld(InputStream is){
Player player = players[0]; Player player = players[0];
+7 -3
View File
@@ -5,10 +5,14 @@ import com.badlogic.gdx.utils.Pool.Poolable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public interface Packet extends Poolable{ public interface Packet extends Poolable{
default void read(ByteBuffer buffer){} default void read(ByteBuffer buffer){
default void write(ByteBuffer buffer){} }
default void reset() {} default void write(ByteBuffer buffer){
}
default void reset(){
}
default boolean isImportant(){ default boolean isImportant(){
return false; return false;

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