Merge; internal changes
This commit is contained in:
@@ -21,6 +21,7 @@ import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Net;
|
||||
import mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
@@ -53,8 +54,10 @@ public class Vars implements Loadable{
|
||||
public static final String crashReportURL = "http://192.99.169.18/report";
|
||||
/** URL the links to the wiki's modding guide.*/
|
||||
public static final String modGuideURL = "https://mindustrygame.github.io/wiki/modding/";
|
||||
/** URL to the JSON file containing all the global, public servers. */
|
||||
/** URL to the JSON file containing all the global, public servers. Not queried in BE. */
|
||||
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers.json";
|
||||
/** URL to the JSON file containing all the BE servers. Only queried in BE. */
|
||||
public static final String serverJsonBeURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
|
||||
/** URL the links to the wiki's modding guide.*/
|
||||
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?template=bug_report.md";
|
||||
/** list of built-in servers.*/
|
||||
@@ -136,6 +139,8 @@ public class Vars implements Loadable{
|
||||
public static Fi modDirectory;
|
||||
/** data subdirectory used for schematics */
|
||||
public static Fi schematicDirectory;
|
||||
/** data subdirectory used for bleeding edge build versions */
|
||||
public static Fi bebuildDirectory;
|
||||
/** map file extension */
|
||||
public static final String mapExtension = "msav";
|
||||
/** save file extension */
|
||||
@@ -157,6 +162,7 @@ public class Vars implements Loadable{
|
||||
public static Platform platform = new Platform(){};
|
||||
public static Mods mods;
|
||||
public static Schematics schematics = new Schematics();
|
||||
public static BeControl becontrol;
|
||||
|
||||
public static World world;
|
||||
public static Maps maps;
|
||||
@@ -220,6 +226,7 @@ public class Vars implements Loadable{
|
||||
defaultWaves = new DefaultWaves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
becontrol = new BeControl();
|
||||
|
||||
maps = new Maps();
|
||||
spawner = new WaveSpawner();
|
||||
@@ -260,6 +267,7 @@ public class Vars implements Loadable{
|
||||
tmpDirectory = dataDirectory.child("tmp/");
|
||||
modDirectory = dataDirectory.child("mods/");
|
||||
schematicDirectory = dataDirectory.child("schematics/");
|
||||
bebuildDirectory = dataDirectory.child("be_builds/");
|
||||
|
||||
modDirectory.mkdirs();
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
@@ -36,6 +36,8 @@ public class BlockIndexer{
|
||||
private ObjectSet<Tile>[] damagedTiles = new ObjectSet[Team.all().length];
|
||||
/**All ores available on this map.*/
|
||||
private ObjectSet<Item> allOres = new ObjectSet<>();
|
||||
/**Stores teams that are present here as tiles.*/
|
||||
private ObjectSet<Team> activeTeams = new ObjectSet<>();
|
||||
|
||||
/** Maps teams to a map of flagged tiles by type. */
|
||||
private ObjectSet<Tile>[][] flagMap = new ObjectSet[Team.all().length][BlockFlag.all.length];
|
||||
@@ -106,10 +108,11 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
private GridBits structQuadrant(Team t){
|
||||
if(structQuadrants[t.id] == null){
|
||||
structQuadrants[t.id] = new GridBits(Mathf.ceil(world.width() / (float)quadrantSize), Mathf.ceil(world.height() / (float)quadrantSize));
|
||||
int id = Pack.u(t.id);
|
||||
if(structQuadrants[id] == null){
|
||||
structQuadrants[id] = new GridBits(Mathf.ceil(world.width() / (float)quadrantSize), Mathf.ceil(world.height() / (float)quadrantSize));
|
||||
}
|
||||
return structQuadrants[t.id];
|
||||
return structQuadrants[id];
|
||||
}
|
||||
|
||||
/** Updates all the structure quadrants for a newly activated team. */
|
||||
@@ -219,6 +222,19 @@ public class BlockIndexer{
|
||||
set.add(entity.tile);
|
||||
}
|
||||
|
||||
public TileEntity findEnemyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
for(Team enemy : activeTeams){
|
||||
if(!team.isEnemy(enemy)) continue;
|
||||
|
||||
TileEntity entity = indexer.findTile(enemy, x, y, range, pred, true);
|
||||
if(entity != null){
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public TileEntity findTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
return findTile(team, x, y, range, pred, false);
|
||||
}
|
||||
@@ -244,7 +260,7 @@ public class BlockIndexer{
|
||||
TileEntity e = other.entity;
|
||||
|
||||
float ndst = Mathf.dst(x, y, e.x, e.y);
|
||||
if(ndst < range && (closest == null || ndst < dst || (usePriority && closest.block.priority.ordinal() < e.block.priority.ordinal()))){
|
||||
if(ndst < range && (closest == null || ndst < dst || (usePriority && closest.block.priority.ordinal() <= e.block.priority.ordinal()))){
|
||||
dst = ndst;
|
||||
closest = e;
|
||||
}
|
||||
@@ -298,6 +314,7 @@ public class BlockIndexer{
|
||||
}
|
||||
typeMap.put(tile.pos(), new TileIndex(tile.block().flags, tile.getTeam()));
|
||||
}
|
||||
activeTeams.add(tile.getTeam());
|
||||
|
||||
if(ores == null) return;
|
||||
|
||||
@@ -336,13 +353,12 @@ public class BlockIndexer{
|
||||
//this quadrant is now 'dirty', re-scan the whole thing
|
||||
int quadrantX = tile.x / quadrantSize;
|
||||
int quadrantY = tile.y / quadrantSize;
|
||||
int index = quadrantX + quadrantY * quadWidth();
|
||||
|
||||
for(TeamData data : state.teams.getActive()){
|
||||
GridBits bits = structQuadrant(data.team);
|
||||
for(Team team : activeTeams){
|
||||
GridBits bits = structQuadrant(team);
|
||||
|
||||
//fast-set this quadrant to 'occupied' if the tile just placed is already of this team
|
||||
if(tile.getTeam() == data.team && tile.entity != null && tile.block().targetable){
|
||||
if(tile.getTeam() == team && tile.entity != null && tile.block().targetable){
|
||||
bits.set(quadrantX, quadrantY);
|
||||
continue; //no need to process futher
|
||||
}
|
||||
@@ -354,7 +370,7 @@ public class BlockIndexer{
|
||||
for(int y = quadrantY * quadrantSize; y < world.height() && y < (quadrantY + 1) * quadrantSize; y++){
|
||||
Tile result = world.ltile(x, y);
|
||||
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
||||
if(result.entity != null && result.getTeam() == data.team){
|
||||
if(result.entity != null && result.getTeam() == team){
|
||||
bits.set(quadrantX, quadrantY);
|
||||
break outer;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class Pathfinder implements Runnable{
|
||||
//stop looping when interrupted externally
|
||||
return;
|
||||
}
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class Blocks implements ContentList{
|
||||
melter, separator, sporePress, pulverizer, incinerator, coalCentrifuge,
|
||||
|
||||
//sandbox
|
||||
powerSource, powerVoid, itemSource, itemVoid, liquidSource, message, illuminator,
|
||||
powerSource, powerVoid, itemSource, itemVoid, liquidSource, liquidVoid, message, illuminator,
|
||||
|
||||
//defense
|
||||
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, plastaniumWall, plastaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
|
||||
@@ -258,9 +258,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
ice = new Floor("ice"){{
|
||||
//TODO fix drag/speed
|
||||
dragMultiplier = 1f;
|
||||
speedMultiplier = 1f;
|
||||
dragMultiplier = 0.6f;
|
||||
attributes.set(Attribute.water, 0.4f);
|
||||
}};
|
||||
|
||||
@@ -898,10 +896,11 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
junction = new Junction("junction"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.copper, 1), true);
|
||||
requirements(Category.distribution, ItemStack.with(Items.copper, 2), true);
|
||||
speed = 26;
|
||||
capacity = 12;
|
||||
health = 30;
|
||||
buildCostMultiplier = 6f;
|
||||
}};
|
||||
|
||||
itemBridge = new BufferedItemBridge("bridge-conveyor"){{
|
||||
@@ -921,16 +920,18 @@ public class Blocks implements ContentList{
|
||||
|
||||
sorter = new Sorter("sorter"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.lead, 2, Items.copper, 2));
|
||||
buildCostMultiplier = 3f;
|
||||
}};
|
||||
|
||||
invertedSorter = new Sorter("inverted-sorter"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.lead, 2, Items.copper, 2));
|
||||
buildCostMultiplier = 3f;
|
||||
invert = true;
|
||||
}};
|
||||
|
||||
router = new Router("router"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.copper, 3));
|
||||
|
||||
buildCostMultiplier = 2f;
|
||||
}};
|
||||
|
||||
distributor = new Router("distributor"){{
|
||||
@@ -940,6 +941,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
overflowGate = new OverflowGate("overflow-gate"){{
|
||||
requirements(Category.distribution, ItemStack.with(Items.lead, 2, Items.copper, 4));
|
||||
buildCostMultiplier = 3f;
|
||||
}};
|
||||
|
||||
massDriver = new MassDriver("mass-driver"){{
|
||||
@@ -1234,7 +1236,7 @@ public class Blocks implements ContentList{
|
||||
//region storage
|
||||
|
||||
coreShard = new CoreBlock("core-shard"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
|
||||
health = 1100;
|
||||
@@ -1244,7 +1246,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
coreFoundation = new CoreBlock("core-foundation"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 400, Items.silicon, 3000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
|
||||
|
||||
health = 2000;
|
||||
itemCapacity = 9000;
|
||||
@@ -1253,7 +1255,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
coreNucleus = new CoreBlock("core-nucleus"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000, Items.silicon, 2000, Items.surgealloy, 3000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
|
||||
|
||||
health = 4000;
|
||||
itemCapacity = 13000;
|
||||
@@ -1824,6 +1826,11 @@ public class Blocks implements ContentList{
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
|
||||
liquidVoid = new LiquidVoid("liquid-void"){{
|
||||
requirements(Category.liquid, BuildVisibility.sandboxOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
|
||||
message = new MessageBlock("message"){{
|
||||
requirements(Category.effect, ItemStack.with(Items.graphite, 5));
|
||||
}};
|
||||
|
||||
@@ -3,8 +3,6 @@ package mindustry.content;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class Loadouts implements ContentList{
|
||||
public static Schematic
|
||||
basicShard,
|
||||
@@ -14,13 +12,9 @@ public class Loadouts implements ContentList{
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
try{
|
||||
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
|
||||
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
|
||||
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
|
||||
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
|
||||
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
|
||||
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
|
||||
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class Mechs implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
public void update(Player player){
|
||||
if(player.timer.get(Player.timerAbility, healReload)){
|
||||
if(indexer.eachBlock(player, healRange, other -> other.entity.damaged(), other -> {
|
||||
other.entity.healBy(other.entity.maxHealth() * healPercent / 100f);
|
||||
@@ -116,7 +116,7 @@ public class Mechs implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
public void update(Player player){
|
||||
player.healBy(Time.delta() * 0.09f);
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ public class Mechs implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
public void update(Player player){
|
||||
|
||||
if(player.timer.get(Player.timerAbility, healReload)){
|
||||
wasHealed = false;
|
||||
@@ -253,7 +253,7 @@ public class Mechs implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
public void update(Player player){
|
||||
float scl = 1f - player.shootHeat / 2f*Time.delta();
|
||||
player.velocity().scl(scl);
|
||||
}
|
||||
@@ -303,8 +303,8 @@ public class Mechs implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
super.updateAlt(player);
|
||||
public void update(Player player){
|
||||
super.update(player);
|
||||
|
||||
if(player.timer.get(Player.timerAbility, effectReload)){
|
||||
|
||||
@@ -361,7 +361,7 @@ public class Mechs implements ContentList{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlt(Player player){
|
||||
public void update(Player player){
|
||||
float scl = scld(player);
|
||||
if(Mathf.chance(Time.delta() * (0.15 * scl))){
|
||||
Effects.effect(Fx.hitLancer, Pal.lancerLaser, player.x, player.y);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.struct.Array;
|
||||
import mindustry.ctype.ContentList;
|
||||
import mindustry.type.ItemStack;
|
||||
import mindustry.world.Block;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.content.Blocks.*;
|
||||
|
||||
@@ -318,7 +319,7 @@ public class TechTree implements ContentList{
|
||||
private static TechNode node(Block block, Runnable children){
|
||||
ItemStack[] requirements = new ItemStack[block.requirements.length];
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
requirements[i] = new ItemStack(block.requirements[i].item, 30 + block.requirements[i].amount * 6);
|
||||
requirements[i] = new ItemStack(block.requirements[i].item, 40 + Mathf.round(Mathf.pow(block.requirements[i].amount, 1.25f) * 6, 10));
|
||||
}
|
||||
|
||||
return new TechNode(block, requirements, children);
|
||||
|
||||
@@ -456,7 +456,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
state.set(state.is(State.playing) ? State.paused : State.playing);
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.menu) && !ui.restart.isShown()){
|
||||
if(Core.input.keyTap(Binding.menu) && !ui.restart.isShown() && !ui.minimapfrag.shown()){
|
||||
if(ui.chatfrag.shown()){
|
||||
ui.chatfrag.hide();
|
||||
}else if(!ui.paused.isShown() && !scene.hasDialog()){
|
||||
|
||||
@@ -47,8 +47,8 @@ public class Logic implements ApplicationListener{
|
||||
//blocks that get broken are appended to the team's broken block queue
|
||||
Tile tile = event.tile;
|
||||
Block block = tile.block();
|
||||
//skip null entities or nukes, for obvious reasons
|
||||
if(tile.entity == null || tile.block() instanceof NuclearReactor) return;
|
||||
//skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests
|
||||
if(tile.entity == null || !tile.block().rebuildable || net.client()) return;
|
||||
|
||||
if(block instanceof BuildBlock){
|
||||
|
||||
@@ -209,6 +209,7 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Events.fire(Trigger.update);
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(!net.client()){
|
||||
@@ -260,7 +261,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
if(!net.client() && !world.isInvalidMap() && !state.isEditor()){
|
||||
if(!net.client() && !world.isInvalidMap() && !state.isEditor() && state.rules.canGameOver){
|
||||
checkGameOver();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class NetClient implements ApplicationListener{
|
||||
throw new ValidateException(player, "Player has sent a message above the text limit.");
|
||||
}
|
||||
|
||||
String original = message;
|
||||
Events.fire(new PlayerChatEvent(player, message));
|
||||
|
||||
//check if it's a command
|
||||
CommandResponse response = netServer.clientCommands.handleMessage(message, player);
|
||||
@@ -197,8 +197,6 @@ public class NetClient implements ApplicationListener{
|
||||
player.sendMessage(text);
|
||||
}
|
||||
}
|
||||
|
||||
Events.fire(new PlayerChatEvent(player, message, original));
|
||||
}
|
||||
|
||||
public static String colorizeName(int id, String name){
|
||||
|
||||
@@ -15,6 +15,7 @@ import mindustry.entities.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
import mindustry.entities.traits.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.net.Administration;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
@@ -26,14 +27,16 @@ import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static arc.util.Log.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class NetServer implements ApplicationListener{
|
||||
private final static int maxSnapshotSize = 430, timerBlockSync = 0;
|
||||
private final static float serverSyncTime = 12, kickDuration = 30 * 1000, blockSyncTime = 60 * 8;
|
||||
private final static float serverSyncTime = 12, blockSyncTime = 60 * 8;
|
||||
private final static Vec2 vector = new Vec2();
|
||||
private final static Rect viewport = new Rect();
|
||||
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
|
||||
@@ -73,7 +76,7 @@ public class NetServer implements ApplicationListener{
|
||||
public NetServer(){
|
||||
|
||||
net.handleServer(Connect.class, (con, connect) -> {
|
||||
if(admins.isIPBanned(connect.addressTCP)){
|
||||
if(admins.isIPBanned(connect.addressTCP) || admins.isSubnetBanned(connect.addressTCP)){
|
||||
con.kick(KickReason.banned);
|
||||
}
|
||||
});
|
||||
@@ -91,7 +94,7 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
String uuid = packet.uuid;
|
||||
|
||||
if(admins.isIPBanned(con.address)) return;
|
||||
if(admins.isIPBanned(con.address) || admins.isSubnetBanned(con.address)) return;
|
||||
|
||||
if(con.hasBegunConnecting){
|
||||
con.kick(KickReason.idInUse);
|
||||
@@ -113,7 +116,7 @@ public class NetServer implements ApplicationListener{
|
||||
return;
|
||||
}
|
||||
|
||||
if(Time.millis() - info.lastKicked < kickDuration){
|
||||
if(Time.millis() < info.lastKicked){
|
||||
con.kick(KickReason.recentKick);
|
||||
return;
|
||||
}
|
||||
@@ -217,7 +220,6 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
//playing in pvp mode automatically assigns players to teams
|
||||
player.setTeam(assignTeam(player, playerGroup.all()));
|
||||
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
|
||||
|
||||
sendWorldData(player);
|
||||
|
||||
@@ -322,6 +324,11 @@ public class NetServer implements ApplicationListener{
|
||||
VoteSession[] currentlyKicking = {null};
|
||||
|
||||
clientCommands.<Player>register("votekick", "[player...]", "Vote to kick a player, with a cooldown.", (args, player) -> {
|
||||
if(!Config.enableVotekick.bool()){
|
||||
player.sendMessage("[scarlet]Vote-kick is disabled on this server.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(playerGroup.size() < 3){
|
||||
player.sendMessage("[scarlet]At least 3 players are needed to start a votekick.");
|
||||
return;
|
||||
@@ -409,6 +416,12 @@ public class NetServer implements ApplicationListener{
|
||||
if(player.isLocal){
|
||||
player.sendMessage("[scarlet]Re-synchronizing as the host is pointless.");
|
||||
}else{
|
||||
if(Time.timeSinceMillis(player.getInfo().lastSyncTime) < 1000 * 5){
|
||||
player.sendMessage("[scarlet]You may only /sync every 5 seconds.");
|
||||
return;
|
||||
}
|
||||
|
||||
player.getInfo().lastSyncTime = Time.millis();
|
||||
Call.onWorldDataBegin(player.con);
|
||||
netServer.sendWorldData(player);
|
||||
}
|
||||
@@ -444,7 +457,7 @@ public class NetServer implements ApplicationListener{
|
||||
if(!player.con.hasDisconnected){
|
||||
if(player.con.hasConnected){
|
||||
Events.fire(new PlayerLeave(player));
|
||||
Call.sendMessage("[accent]" + player.name + "[accent] has disconnected.");
|
||||
if(Config.showConnectMessages.bool()) Call.sendMessage("[accent]" + player.name + "[accent] has disconnected.");
|
||||
Call.onPlayerDisconnect(player.id);
|
||||
}
|
||||
|
||||
@@ -582,9 +595,13 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
player.add();
|
||||
player.con.hasConnected = true;
|
||||
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
|
||||
if(Config.showConnectMessages.bool()) Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
|
||||
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
|
||||
|
||||
if(!Config.motd.string().equalsIgnoreCase("off")){
|
||||
player.sendMessage(Config.motd.string());
|
||||
}
|
||||
|
||||
Events.fire(new PlayerJoin(player));
|
||||
}
|
||||
|
||||
@@ -601,6 +618,7 @@ public class NetServer implements ApplicationListener{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
if(!headless && !closing && net.server() && state.is(State.menu)){
|
||||
@@ -618,6 +636,20 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
/** Should only be used on the headless backend. */
|
||||
public void openServer(){
|
||||
try{
|
||||
net.host(Config.port.num());
|
||||
info("&lcOpened a server on port {0}.", Config.port.num());
|
||||
}catch(BindException e){
|
||||
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");
|
||||
state.set(State.menu);
|
||||
}catch(IOException e){
|
||||
err(e);
|
||||
state.set(State.menu);
|
||||
}
|
||||
}
|
||||
|
||||
public void kickAll(KickReason reason){
|
||||
for(NetConnection con : net.getConnections()){
|
||||
con.kick(reason);
|
||||
|
||||
@@ -123,12 +123,14 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
if(player.isDead()){
|
||||
TileEntity core = player.getClosestCore();
|
||||
if(core != null && player.spawner == null){
|
||||
camera.position.lerpDelta(core.x, core.y, 0.08f);
|
||||
}else{
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
if(core != null){
|
||||
if(player.spawner == null){
|
||||
camera.position.lerpDelta(core.x, core.y, 0.08f);
|
||||
}else{
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
}
|
||||
}else if(control.input instanceof DesktopInput){
|
||||
}else if(control.input instanceof DesktopInput && !state.isPaused()){
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public HudFragment hudfrag;
|
||||
public ChatFragment chatfrag;
|
||||
public ScriptConsoleFragment scriptfrag;
|
||||
public MinimapFragment minimapfrag;
|
||||
public PlayerListFragment listfrag;
|
||||
public LoadingFragment loadfrag;
|
||||
|
||||
@@ -68,7 +69,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public ContentInfoDialog content;
|
||||
public DeployDialog deploy;
|
||||
public TechTreeDialog tech;
|
||||
public MinimapDialog minimap;
|
||||
//public MinimapDialog minimap;
|
||||
public SchematicsDialog schematics;
|
||||
public ModsDialog mods;
|
||||
public ColorPicker picker;
|
||||
@@ -210,6 +211,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
menufrag = new MenuFragment();
|
||||
hudfrag = new HudFragment();
|
||||
chatfrag = new ChatFragment();
|
||||
minimapfrag = new MinimapFragment();
|
||||
listfrag = new PlayerListFragment();
|
||||
loadfrag = new LoadingFragment();
|
||||
scriptfrag = new ScriptConsoleFragment();
|
||||
@@ -235,7 +237,6 @@ public class UI implements ApplicationListener, Loadable{
|
||||
content = new ContentInfoDialog();
|
||||
deploy = new DeployDialog();
|
||||
tech = new TechTreeDialog();
|
||||
minimap = new MinimapDialog();
|
||||
mods = new ModsDialog();
|
||||
schematics = new SchematicsDialog();
|
||||
|
||||
@@ -254,6 +255,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
hudfrag.build(hudGroup);
|
||||
menufrag.build(menuGroup);
|
||||
chatfrag.container().build(hudGroup);
|
||||
minimapfrag.build(hudGroup);
|
||||
listfrag.build(hudGroup);
|
||||
scriptfrag.container().build(hudGroup);
|
||||
loadfrag.build(group);
|
||||
@@ -305,7 +307,19 @@ public class UI implements ApplicationListener, Loadable{
|
||||
hide();
|
||||
}).disabled(b -> field.getText().isEmpty());
|
||||
buttons.addButton("$cancel", this::hide);
|
||||
}}.show();
|
||||
keyDown(KeyCode.ENTER, () -> {
|
||||
String text = field.getText();
|
||||
if(!text.isEmpty()){
|
||||
confirmed.get(text);
|
||||
hide();
|
||||
}
|
||||
});
|
||||
keyDown(KeyCode.ESCAPE, this::hide);
|
||||
keyDown(KeyCode.BACK, this::hide);
|
||||
show();
|
||||
Core.scene.setKeyboardFocus(field);
|
||||
field.setCursorPosition(def.length());
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import arc.util.io.*;
|
||||
|
||||
public class Version{
|
||||
/** Build type. 'official' for official releases; 'custom' or 'bleeding edge' are also used. */
|
||||
public static String type;
|
||||
public static String type = "unknown";
|
||||
/** Build modifier, e.g. 'alpha' or 'release' */
|
||||
public static String modifier;
|
||||
public static String modifier = "unknown";
|
||||
/** Number specifying the major version, e.g. '4' */
|
||||
public static int number;
|
||||
/** Build number, e.g. '43'. set to '-1' for custom builds. */
|
||||
|
||||
@@ -217,7 +217,7 @@ public class World{
|
||||
public void loadMap(Map map, Rules checkRules){
|
||||
try{
|
||||
SaveIO.load(map.file, new FilterContext(map));
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
Log.err(e);
|
||||
if(!headless){
|
||||
ui.showErrorMessage("$map.invalid");
|
||||
|
||||
@@ -118,7 +118,7 @@ public enum EditorTool{
|
||||
if(editor.drawBlock.isOverlay()){
|
||||
Block dest = tile.overlay();
|
||||
if(dest == editor.drawBlock) return;
|
||||
tester = t -> t.overlay() == dest;
|
||||
tester = t -> t.overlay() == dest && !t.floor().isLiquid;
|
||||
setter = t -> t.setOverlay(editor.drawBlock);
|
||||
}else if(editor.drawBlock.isFloor()){
|
||||
Block dest = tile.floor();
|
||||
|
||||
@@ -7,11 +7,13 @@ import arc.graphics.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.entities.traits.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.collisions;
|
||||
|
||||
/** Represents a group of a certain type of entity.*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EntityGroup<T extends Entity>{
|
||||
public class EntityGroup<T extends Entity> implements Iterable<T>{
|
||||
private final boolean useTree;
|
||||
private final int id;
|
||||
private final Class<T> type;
|
||||
@@ -253,8 +255,13 @@ public class EntityGroup<T extends Entity>{
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the logic-only array for iteration. */
|
||||
/** Returns the array for iteration. */
|
||||
public Array<T> all(){
|
||||
return entityArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator(){
|
||||
return entityArray.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class Predict{
|
||||
* See {@link #intercept(float, float, float, float, float, float, float)}.
|
||||
*/
|
||||
public static Vec2 intercept(TargetTrait src, TargetTrait dst, float v){
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/2f, dst.getTargetVelocityY() - src.getTargetVelocityY()/2f, v);
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), dst.getTargetVelocityX() - src.getTargetVelocityX()/(2f*Time.delta()), dst.getTargetVelocityY() - src.getTargetVelocityY()/(2f*Time.delta()), v);
|
||||
}
|
||||
|
||||
private static Vec2 quad(float a, float b, float c){
|
||||
|
||||
@@ -83,13 +83,7 @@ public class Units{
|
||||
public static TileEntity findEnemyTile(Team team, float x, float y, float range, Boolf<Tile> pred){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
for(Team enemy : team.enemies()){
|
||||
TileEntity entity = indexer.findTile(enemy, x, y, range, pred, true);
|
||||
if(entity != null){
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return indexer.findEnemyTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/** Returns the closest target enemy. First, units are checked, then tile entities. */
|
||||
|
||||
4
core/src/mindustry/entities/Weapons.java
Normal file
4
core/src/mindustry/entities/Weapons.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package mindustry.entities;
|
||||
|
||||
public class Weapons{
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{
|
||||
create(item, x, y, to, () -> to.addItem(item));
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){
|
||||
if(tile == null || tile.entity == null || tile.entity.items == null) return;
|
||||
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.math.*;
|
||||
import arc.util.Time;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.Effects;
|
||||
import mindustry.entities.effect.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.gen.Call;
|
||||
import mindustry.graphics.*;
|
||||
@@ -38,11 +39,26 @@ public interface MinerTrait extends Entity{
|
||||
/** Returns whether or not this builder can mine a specific item type. */
|
||||
boolean canMine(Item item);
|
||||
|
||||
/** @return whether to offload mined items immediately at the core. if false, items are collected and dropped in a burst. */
|
||||
default boolean offloadImmediately(){
|
||||
return false;
|
||||
}
|
||||
|
||||
default void updateMining(){
|
||||
Unit unit = (Unit)this;
|
||||
Tile tile = getMineTile();
|
||||
TileEntity core = unit.getClosestCore();
|
||||
|
||||
if(core != null && tile != null && tile.drop() != null && !unit.acceptsItem(tile.drop()) && unit.dst(core) < mineTransferRange){
|
||||
int accepted = core.tile.block().acceptStack(unit.item().item, unit.item().amount, core.tile, unit);
|
||||
if(accepted > 0){
|
||||
Call.transferItemTo(unit.item().item, accepted,
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f), core.tile);
|
||||
unit.clearItem();
|
||||
}
|
||||
}
|
||||
|
||||
if(tile == null || core == null || tile.block() != Blocks.air || dst(tile.worldx(), tile.worldy()) > getMiningRange()
|
||||
|| tile.drop() == null || !unit.acceptsItem(tile.drop()) || !canMine(tile.drop())){
|
||||
setMineTile(null);
|
||||
@@ -52,12 +68,13 @@ public interface MinerTrait extends Entity{
|
||||
|
||||
if(Mathf.chance(Time.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){
|
||||
|
||||
if(unit.dst(core) < mineTransferRange && core.tile.block().acceptStack(item, 1, core.tile, unit) == 1){
|
||||
if(unit.dst(core) < mineTransferRange && core.tile.block().acceptStack(item, 1, core.tile, unit) == 1 && offloadImmediately()){
|
||||
Call.transferItemTo(item, 1,
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f), core.tile);
|
||||
}else if(unit.acceptsItem(item)){
|
||||
Call.transferItemToUnit(item,
|
||||
//this is clientside, since items are synced anyway
|
||||
ItemTransfer.transferItemToUnit(item,
|
||||
tile.worldx() + Mathf.range(tilesize / 2f),
|
||||
tile.worldy() + Mathf.range(tilesize / 2f),
|
||||
unit);
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package mindustry.entities.traits;
|
||||
|
||||
import arc.util.Interval;
|
||||
import mindustry.type.Weapon;
|
||||
import mindustry.entities.*;
|
||||
|
||||
public interface ShooterTrait extends VelocityTrait, TeamTrait{
|
||||
|
||||
Interval getTimer();
|
||||
|
||||
int getShootTimer(boolean left);
|
||||
|
||||
Weapon getWeapon();
|
||||
Weapons getWeapons();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ public interface VelocityTrait extends MoveTrait{
|
||||
}
|
||||
|
||||
default float maxVelocity(){
|
||||
return Float.MAX_VALUE;
|
||||
return 50f;
|
||||
}
|
||||
|
||||
default float mass(){
|
||||
|
||||
@@ -36,8 +36,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
|
||||
protected static final int timerTarget = timerIndex++;
|
||||
protected static final int timerTarget2 = timerIndex++;
|
||||
protected static final int timerShootLeft = timerIndex++;
|
||||
protected static final int timerShootRight = timerIndex++;
|
||||
|
||||
protected boolean loaded;
|
||||
protected UnitType type;
|
||||
@@ -76,11 +74,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
Core.app.post(unit::remove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drag(){
|
||||
return type.drag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeID getTypeID(){
|
||||
return type.typeID;
|
||||
@@ -210,41 +203,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return status.getDamageMultiplier() * Vars.state.rules.unitDamageMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImmune(StatusEffect effect){
|
||||
return type.immunities.contains(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(){
|
||||
return super.isValid() && isAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interval getTimer(){
|
||||
return timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShootTimer(boolean left){
|
||||
return left ? timerShootLeft : timerShootRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Weapon getWeapon(){
|
||||
return type.weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion getIconRegion(){
|
||||
return type.icon(Cicon.full);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCapacity(){
|
||||
return type.itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate(){
|
||||
super.interpolate();
|
||||
@@ -259,16 +222,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
return type.health * Vars.state.rules.unitHealthMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float mass(){
|
||||
return type.mass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFlying(){
|
||||
return type.flying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(isDead()){
|
||||
@@ -312,11 +265,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public float maxVelocity(){
|
||||
return type.maxVelocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removed(){
|
||||
super.removed();
|
||||
@@ -351,16 +299,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setSize(type.hitsize).setCenter(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitboxTile(Rect rect){
|
||||
rect.setSize(type.hitsizeTile).setCenter(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityGroup targetGroup(){
|
||||
return unitGroup;
|
||||
|
||||
@@ -48,8 +48,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
public float baseRotation;
|
||||
public float pointerX, pointerY;
|
||||
public String name = "noname";
|
||||
public @Nullable
|
||||
String uuid, usid;
|
||||
public @Nullable String uuid, usid;
|
||||
public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping, isBuilding = true;
|
||||
public boolean buildWasAutoPaused = false;
|
||||
public float boostHeat, shootHeat, destructTime;
|
||||
@@ -92,16 +91,6 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
return status.getDamageMultiplier() * state.rules.playerDamageMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setSize(mech.hitsize).setCenter(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitboxTile(Rect rect){
|
||||
rect.setSize(mech.hitsize * 2f / 3f).setCenter(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRespawn(Tile tile){
|
||||
velocity.setZero();
|
||||
@@ -119,6 +108,11 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
heal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean offloadImmediately(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeID getTypeID(){
|
||||
return TypeIDs.player;
|
||||
@@ -133,29 +127,9 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float drag(){
|
||||
return mech.drag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interval getTimer(){
|
||||
return timer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShootTimer(boolean left){
|
||||
return left ? timerShootLeft : timerShootRight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Weapon getWeapon(){
|
||||
return mech.weapon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getMinePower(){
|
||||
return mech.mineSpeed;
|
||||
return mech.minePower;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,11 +137,6 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
return mech.icon(Cicon.full);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCapacity(){
|
||||
return mech.itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpolate(){
|
||||
super.interpolate();
|
||||
@@ -244,11 +213,6 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float maxVelocity(){
|
||||
return mech.maxSpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Queue<BuildRequest> buildQueue(){
|
||||
return placeQueue;
|
||||
@@ -349,13 +313,13 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public void drawBackItems(){
|
||||
drawBackItems(itemtime, isLocal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawStats(){
|
||||
Draw.color(Color.black, team.color, healthf() + Mathf.absin(Time.time(), healthf() * 5f, 1f - healthf()));
|
||||
Draw.rect(getPowerCellRegion(), x + Angles.trnsx(rotation, mech.cellTrnsY, 0f), y + Angles.trnsy(rotation, mech.cellTrnsY, 0f), rotation - 90);
|
||||
Draw.reset();
|
||||
drawBackItems(itemtime, isLocal);
|
||||
drawLight();
|
||||
mech.drawStats(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -523,7 +487,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
boostHeat = Mathf.lerpDelta(boostHeat, (tile != null && tile.solid()) || (isBoosting && ((!movement.isZero() && moved) || !isLocal)) ? 1f : 0f, 0.08f);
|
||||
shootHeat = Mathf.lerpDelta(shootHeat, isShooting() ? 1f : 0f, 0.06f);
|
||||
mech.updateAlt(this); //updated regardless
|
||||
mech.update(this); //updated regardless
|
||||
|
||||
if(boostHeat > liftoffBoost + 0.1f){
|
||||
achievedFlight = true;
|
||||
@@ -572,6 +536,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
protected void updateKeyboard(){
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
boolean canMove = !Core.scene.hasKeyboard() || ui.minimapfrag.shown();
|
||||
|
||||
isBoosting = Core.input.keyDown(Binding.dash) && !mech.flying;
|
||||
|
||||
@@ -598,8 +563,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
}
|
||||
|
||||
if(Core.input.keyDown(Binding.mouse_move)){
|
||||
movement.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2) * 0.005f, -1, 1) * speed;
|
||||
movement.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2) * 0.005f, -1, 1) * speed;
|
||||
movement.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2f) * 0.005f, -1, 1) * speed;
|
||||
movement.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2f) * 0.005f, -1, 1) * speed;
|
||||
}
|
||||
|
||||
Vec2 vec = Core.input.mouseWorld(control.input.getMouseX(), control.input.getMouseY());
|
||||
@@ -609,7 +574,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
|
||||
movement.limit(speed).scl(Time.delta());
|
||||
|
||||
if(!Core.scene.hasKeyboard()){
|
||||
if(canMove){
|
||||
velocity.add(movement.x, movement.y);
|
||||
}else{
|
||||
isShooting = false;
|
||||
@@ -618,7 +583,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
updateVelocityStatus();
|
||||
moved = dst(prex, prey) > 0.001f;
|
||||
|
||||
if(!Core.scene.hasKeyboard()){
|
||||
if(canMove){
|
||||
float baseLerp = mech.getRotationAlpha(this);
|
||||
if(!isShooting() || !mech.turnCursor){
|
||||
if(!movement.isZero()){
|
||||
@@ -635,7 +600,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
if(!state.isEditor() && isShooting() && mech.canShoot(this)){
|
||||
if(!mech.turnCursor){
|
||||
//shoot forward ignoring cursor
|
||||
mech.weapon.update(this, x + Angles.trnsx(rotation, 1f), y + Angles.trnsy(rotation, 1f));
|
||||
mech.weapon.update(this, x + Angles.trnsx(rotation, mech.weapon.targetDistance), y + Angles.trnsy(rotation, mech.weapon.targetDistance));
|
||||
}else{
|
||||
mech.weapon.update(this, pointerX, pointerY);
|
||||
}
|
||||
@@ -735,8 +700,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
|
||||
setMineTile(null);
|
||||
}
|
||||
}
|
||||
}else if(target.isValid() || (target instanceof TileEntity && ((TileEntity)target).damaged() && target.getTeam() == team &&
|
||||
mech.canHeal && dst(target) < getWeapon().bullet.range())){
|
||||
}else if(target.isValid() || (target instanceof TileEntity && ((TileEntity)target).damaged() && target.getTeam() == team && mech.canHeal && dst(target) < getWeapon().bullet.range())){
|
||||
//rotate toward and shoot the target
|
||||
if(mech.turnCursor){
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f);
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package mindustry.entities.type;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.math.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.Events;
|
||||
import arc.struct.Array;
|
||||
import arc.struct.ObjectSet;
|
||||
import arc.math.geom.Point2;
|
||||
import arc.math.geom.Vec2;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
@@ -167,9 +172,16 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait,
|
||||
Call.onTileDestroyed(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float damage){
|
||||
if(dead) return;
|
||||
|
||||
if(Mathf.zero(state.rules.blockHealthMultiplier)){
|
||||
damage = health + 1;
|
||||
}else{
|
||||
damage /= state.rules.blockHealthMultiplier;
|
||||
}
|
||||
|
||||
float preHealth = health;
|
||||
|
||||
Call.onTileDamage(tile, health - block.handleDamage(tile, damage));
|
||||
|
||||
@@ -134,6 +134,22 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
return !isDead() && isAdded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitbox(Rect rect){
|
||||
rect.setSize(def().hitsize).setCenter(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitboxTile(Rect rect){
|
||||
rect.setSize(def().hitsizeTile).setCenter(x, y);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float drag(){
|
||||
return def().drag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSave(DataOutput stream) throws IOException{
|
||||
writeSave(stream, false);
|
||||
@@ -164,6 +180,8 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public abstract UnitDef def();
|
||||
|
||||
public void writeSave(DataOutput stream, boolean net) throws IOException{
|
||||
if(item.item == null) item.item = Items.copper;
|
||||
|
||||
@@ -191,7 +209,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public boolean isImmune(StatusEffect effect){
|
||||
return false;
|
||||
return def().immunities.contains(effect);
|
||||
}
|
||||
|
||||
public boolean isOutOfBounds(){
|
||||
@@ -226,7 +244,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
float radScl = 1.5f;
|
||||
|
||||
for(Unit en : arr){
|
||||
if(en.isFlying() != isFlying() || (en instanceof Player && en.getTeam() != getTeam())) continue;
|
||||
if(en.isFlying() != isFlying() || (en instanceof Player && en.getTeam() != getTeam()) || (this instanceof Player && en.isFlying())) continue;
|
||||
float dst = dst(en);
|
||||
float scl = Mathf.clamp(1f - dst / (getSize()/(radScl*2f) + en.getSize()/(radScl*2f)));
|
||||
moveVector.add(Tmp.v1.set((x - en.x) * scl, (y - en.y) * scl).limit(0.4f));
|
||||
@@ -252,8 +270,9 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
|
||||
status.update(this);
|
||||
item.amount = Mathf.clamp(this.item.amount, 0, getItemCapacity());
|
||||
|
||||
velocity.limit(maxVelocity()).scl(1f + (status.getSpeedMultiplier() - 1f) * Time.delta());
|
||||
//velocity.limit(maxVelocity()).scl(1f + (status.getSpeedMultiplier() - 1f) * Time.delta());
|
||||
|
||||
if(x < -finalWorldBounds || y < -finalWorldBounds || x >= world.width() * tilesize + finalWorldBounds || y >= world.height() * tilesize + finalWorldBounds){
|
||||
kill();
|
||||
@@ -277,7 +296,6 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
if(x > world.unitWidth()) velocity.x -= (x - world.unitWidth())/warpDst;
|
||||
if(y > world.unitHeight()) velocity.y -= (y - world.unitHeight())/warpDst;
|
||||
|
||||
|
||||
if(isFlying()){
|
||||
drownTime = 0f;
|
||||
move(velocity.x * Time.delta(), velocity.y * Time.delta());
|
||||
@@ -327,7 +345,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
if(Math.abs(py - y) <= 0.0001f) velocity.y = 0f;
|
||||
}
|
||||
|
||||
velocity.scl(Mathf.clamp(1f - drag() * (isFlying() ? 1f : floor.dragMultiplier) * Time.delta()));
|
||||
//velocity.scl(Mathf.clamp(1f - drag() * (isFlying() ? 1f : floor.dragMultiplier) * Time.delta()));
|
||||
}
|
||||
|
||||
public boolean acceptsItem(Item item){
|
||||
@@ -341,6 +359,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
public void addItem(Item item, int amount){
|
||||
this.item.amount = this.item.item == item ? this.item.amount + amount : amount;
|
||||
this.item.item = item;
|
||||
this.item.amount = Mathf.clamp(this.item.amount, 0, getItemCapacity());
|
||||
}
|
||||
|
||||
public void clearItem(){
|
||||
@@ -391,7 +410,9 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
}
|
||||
|
||||
public void drawLight(){
|
||||
renderer.lights.add(x, y, 50f, Pal.powerLight, 0.6f);
|
||||
if(def().lightRadius > 0){
|
||||
renderer.lights.add(x, y, def().lightRadius, def().lightColor, 0.6f);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawBackItems(float itemtime, boolean number){
|
||||
@@ -448,11 +469,16 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
||||
|
||||
public abstract TextureRegion getIconRegion();
|
||||
|
||||
public abstract Weapon getWeapon();
|
||||
public final int getItemCapacity(){
|
||||
return def().itemCapacity;
|
||||
}
|
||||
|
||||
public abstract int getItemCapacity();
|
||||
@Override
|
||||
public float mass(){
|
||||
return def().mass;
|
||||
}
|
||||
|
||||
public abstract float mass();
|
||||
|
||||
public abstract boolean isFlying();
|
||||
public boolean isFlying(){
|
||||
return def().flying;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public abstract class BaseDrone extends FlyingUnit{
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean countsAsEnemy(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ public class EventType{
|
||||
exclusionDeath,
|
||||
suicideBomb,
|
||||
openWiki,
|
||||
teamCoreDamage
|
||||
teamCoreDamage,
|
||||
socketConfigChanged,
|
||||
update
|
||||
}
|
||||
|
||||
public static class WinEvent{}
|
||||
@@ -62,13 +64,10 @@ public class EventType{
|
||||
public static class PlayerChatEvent{
|
||||
public final Player player;
|
||||
public final String message;
|
||||
/** The original, unfiltered message. */
|
||||
public final String originalMessage;
|
||||
|
||||
public PlayerChatEvent(Player player, String message, String originalMessage){
|
||||
public PlayerChatEvent(Player player, String message){
|
||||
this.player = player;
|
||||
this.message = message;
|
||||
this.originalMessage = originalMessage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ public class Rules{
|
||||
public float unitHealthMultiplier = 1f;
|
||||
/** How much health players start with. */
|
||||
public float playerHealthMultiplier = 1f;
|
||||
/** How much health blocks start with. */
|
||||
public float blockHealthMultiplier = 1f;
|
||||
/** How much damage player mechs deal. */
|
||||
public float playerDamageMultiplier = 1f;
|
||||
/** How much damage any other units deal. */
|
||||
@@ -70,6 +72,8 @@ public class Rules{
|
||||
public boolean editor = false;
|
||||
/** Whether the tutorial is enabled. False by default. */
|
||||
public boolean tutorial = false;
|
||||
/** Whether a gameover can happen at all. Set this to false to implement custom gameover conditions. */
|
||||
public boolean canGameOver = true;
|
||||
/** Starting items put in cores */
|
||||
public Array<ItemStack> loadout = Array.with(ItemStack.with(Items.copper, 100));
|
||||
/** Blocks that cannot be placed. */
|
||||
|
||||
@@ -142,6 +142,7 @@ public class Schematics implements Loadable{
|
||||
ui.showException(e);
|
||||
}
|
||||
}
|
||||
all.sort();
|
||||
}
|
||||
|
||||
public void savePreview(Schematic schematic, Fi file){
|
||||
@@ -249,6 +250,7 @@ public class Schematics implements Loadable{
|
||||
|
||||
public void placeLoadout(Schematic schem, int x, int y){
|
||||
Stile coreTile = schem.tiles.find(s -> s.block instanceof CoreBlock);
|
||||
if(coreTile == null) throw new IllegalArgumentException("Schematic has no core tile. Exiting.");
|
||||
int ox = x - coreTile.x, oy = y - coreTile.y;
|
||||
schem.tiles.each(st -> {
|
||||
Tile tile = world.tile(st.x + ox, st.y + oy);
|
||||
@@ -279,6 +281,7 @@ public class Schematics implements Loadable{
|
||||
ui.showException(e);
|
||||
Log.err(e);
|
||||
}
|
||||
all.sort();
|
||||
}
|
||||
|
||||
public void remove(Schematic s){
|
||||
@@ -291,6 +294,7 @@ public class Schematics implements Loadable{
|
||||
previews.get(s).dispose();
|
||||
previews.remove(s);
|
||||
}
|
||||
all.sort();
|
||||
}
|
||||
|
||||
/** Creates a schematic from a world selection. */
|
||||
@@ -339,7 +343,8 @@ public class Schematics implements Loadable{
|
||||
for(int cy = oy; cy <= oy2; cy++){
|
||||
Tile tile = world.ltile(cx, cy);
|
||||
|
||||
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock) && tile.entity.block.isVisible()){
|
||||
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock)
|
||||
&& (tile.entity.block.isVisible() || (tile.entity.block instanceof CoreBlock && Core.settings.getBool("coreselect")))){
|
||||
int config = tile.entity.config();
|
||||
if(tile.block().posConfig){
|
||||
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);
|
||||
@@ -368,8 +373,12 @@ public class Schematics implements Loadable{
|
||||
//region IO methods
|
||||
|
||||
/** Loads a schematic from base64. May throw an exception. */
|
||||
public static Schematic readBase64(String schematic) throws IOException{
|
||||
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
|
||||
public static Schematic readBase64(String schematic){
|
||||
try{
|
||||
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Schematic read(Fi file) throws IOException{
|
||||
|
||||
@@ -30,7 +30,7 @@ public class Team implements Comparable<Team>{
|
||||
blue = new Team(5, "blue", Color.royal.cpy());
|
||||
|
||||
static{
|
||||
Mathf.random.setSeed(7);
|
||||
Mathf.random.setSeed(8);
|
||||
//create the whole 256 placeholder teams
|
||||
for(int i = 6; i < all.length; i++){
|
||||
new Team(i, "team#" + i, Color.HSVtoRGB(360f * Mathf.random(), 100f * Mathf.random(0.6f, 1f), 100f * Mathf.random(0.8f, 1f), 1f));
|
||||
|
||||
@@ -80,7 +80,7 @@ public class Teams{
|
||||
/** Returns whether a team is active, e.g. whether it has any cores remaining. */
|
||||
public boolean isActive(Team team){
|
||||
//the enemy wave team is always active
|
||||
return team == state.rules.waveTeam || get(team).cores.size > 0;
|
||||
return get(team).active();
|
||||
}
|
||||
|
||||
/** Returns whether {@param other} is an enemy of {@param #team}. */
|
||||
@@ -150,7 +150,7 @@ public class Teams{
|
||||
}
|
||||
|
||||
public boolean active(){
|
||||
return team == state.rules.waveTeam || cores.size > 0;
|
||||
return (team == state.rules.waveTeam && state.rules.waves) || cores.size > 0;
|
||||
}
|
||||
|
||||
public boolean hasCore(){
|
||||
|
||||
@@ -14,7 +14,6 @@ import arc.graphics.gl.Shader;
|
||||
* @author kalle_h
|
||||
*/
|
||||
public class Bloom{
|
||||
|
||||
/**
|
||||
* To use implement bloom more like a glow. Texture alpha channel can be
|
||||
* used as mask which part are glowing and which are not. see more info at:
|
||||
@@ -61,10 +60,8 @@ public class Bloom{
|
||||
*/
|
||||
public void resume(){
|
||||
bloomShader.begin();
|
||||
{
|
||||
bloomShader.setUniformi("u_texture0", 0);
|
||||
bloomShader.setUniformi("u_texture1", 1);
|
||||
}
|
||||
bloomShader.setUniformi("u_texture0", 0);
|
||||
bloomShader.setUniformi("u_texture1", 1);
|
||||
bloomShader.end();
|
||||
|
||||
setSize(w, h);
|
||||
@@ -83,13 +80,11 @@ public class Bloom{
|
||||
* blending = false 32bits = true
|
||||
*/
|
||||
public Bloom(){
|
||||
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4,
|
||||
null, false, false, true);
|
||||
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, false, true);
|
||||
}
|
||||
|
||||
public Bloom(boolean useBlending){
|
||||
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4,
|
||||
null, false, useBlending, true);
|
||||
initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, useBlending, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +100,7 @@ public class Bloom{
|
||||
* and only do blooming on certain objects param use32bitFBO does
|
||||
* fbo use higher precision than 16bits.
|
||||
*/
|
||||
public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending,
|
||||
boolean use32bitFBO){
|
||||
public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending, boolean use32bitFBO){
|
||||
initialize(FBO_W, FBO_H, null, hasDepth, useBlending, use32bitFBO);
|
||||
|
||||
}
|
||||
@@ -129,18 +123,14 @@ public class Bloom{
|
||||
* and only do blooming on certain objects param use32bitFBO does
|
||||
* fbo use higher precision than 16bits.
|
||||
*/
|
||||
public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere,
|
||||
boolean useBlending, boolean use32bitFBO){
|
||||
|
||||
initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending,
|
||||
use32bitFBO);
|
||||
public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere, boolean useBlending, boolean use32bitFBO){
|
||||
initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending, use32bitFBO);
|
||||
disposeFBO = false;
|
||||
}
|
||||
|
||||
private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo,
|
||||
boolean hasDepth, boolean useBlending, boolean use32bitFBO){
|
||||
private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo, boolean hasDepth, boolean useBlending, boolean use32bitFBO){
|
||||
blending = useBlending;
|
||||
Format format = null;
|
||||
Format format;
|
||||
|
||||
if(use32bitFBO){
|
||||
if(useBlending){
|
||||
@@ -157,8 +147,7 @@ public class Bloom{
|
||||
}
|
||||
}
|
||||
if(fbo == null){
|
||||
frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(),
|
||||
Core.graphics.getHeight(), hasDepth);
|
||||
frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(), Core.graphics.getHeight(), hasDepth);
|
||||
}else{
|
||||
frameBuffer = fbo;
|
||||
}
|
||||
@@ -190,10 +179,8 @@ public class Bloom{
|
||||
setThreshold(0.5f);
|
||||
|
||||
bloomShader.begin();
|
||||
{
|
||||
bloomShader.setUniformi("u_texture0", 0);
|
||||
bloomShader.setUniformi("u_texture1", 1);
|
||||
}
|
||||
bloomShader.setUniformi("u_texture0", 0);
|
||||
bloomShader.setUniformi("u_texture1", 1);
|
||||
bloomShader.end();
|
||||
}
|
||||
|
||||
@@ -219,8 +206,8 @@ public class Bloom{
|
||||
if(!capturing){
|
||||
capturing = true;
|
||||
frameBuffer.begin();
|
||||
Core.gl.glClearColor(r, g, b, a);
|
||||
Core.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||
Gl.clearColor(r, g, b, a);
|
||||
Gl.clear(Gl.colorBufferBit);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -252,23 +239,22 @@ public class Bloom{
|
||||
frameBuffer.end();
|
||||
}
|
||||
|
||||
Core.gl.glDisable(GL20.GL_BLEND);
|
||||
Core.gl.glDisable(GL20.GL_DEPTH_TEST);
|
||||
Core.gl.glDepthMask(false);
|
||||
Gl.disable(Gl.blend);
|
||||
Gl.disable(Gl.depthTest);
|
||||
Gl.depthMask(false);
|
||||
|
||||
gaussianBlur();
|
||||
|
||||
if(blending){
|
||||
Core.gl.glEnable(GL20.GL_BLEND);
|
||||
Core.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
|
||||
Gl.enable(Gl.blend);
|
||||
Gl.blendFunc(Gl.srcAlpha, Gl.oneMinusSrcAlpha);
|
||||
}
|
||||
|
||||
pingPongTex1.bind(1);
|
||||
original.bind(0);
|
||||
|
||||
bloomShader.begin();
|
||||
{
|
||||
fullScreenQuad.render(bloomShader, GL20.GL_TRIANGLE_FAN);
|
||||
}
|
||||
fullScreenQuad.render(bloomShader, Gl.triangleFan);
|
||||
bloomShader.end();
|
||||
|
||||
}
|
||||
@@ -279,15 +265,9 @@ public class Bloom{
|
||||
|
||||
original.bind(0);
|
||||
pingPongBuffer1.begin();
|
||||
{
|
||||
tresholdShader.begin();
|
||||
{
|
||||
// tresholdShader.setUniformi("u_texture0", 0);
|
||||
fullScreenQuad.render(tresholdShader, GL20.GL_TRIANGLE_FAN, 0,
|
||||
4);
|
||||
}
|
||||
tresholdShader.end();
|
||||
}
|
||||
tresholdShader.begin();
|
||||
fullScreenQuad.render(tresholdShader, Gl.triangleFan, 0, 4);
|
||||
tresholdShader.end();
|
||||
pingPongBuffer1.end();
|
||||
|
||||
for(int i = 0; i < blurPasses; i++){
|
||||
@@ -296,30 +276,19 @@ public class Bloom{
|
||||
|
||||
// horizontal
|
||||
pingPongBuffer2.begin();
|
||||
{
|
||||
blurShader.begin();
|
||||
{
|
||||
blurShader.setUniformf("dir", 1f, 0f);
|
||||
fullScreenQuad.render(blurShader, GL20.GL_TRIANGLE_FAN, 0,
|
||||
4);
|
||||
}
|
||||
blurShader.end();
|
||||
}
|
||||
blurShader.begin();
|
||||
blurShader.setUniformf("dir", 1f, 0f);
|
||||
fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4);
|
||||
blurShader.end();
|
||||
pingPongBuffer2.end();
|
||||
|
||||
pingPongTex2.bind(0);
|
||||
// vertical
|
||||
pingPongBuffer1.begin();
|
||||
{
|
||||
blurShader.begin();
|
||||
{
|
||||
blurShader.setUniformf("dir", 0f, 1f);
|
||||
|
||||
fullScreenQuad.render(blurShader, GL20.GL_TRIANGLE_FAN, 0,
|
||||
4);
|
||||
}
|
||||
blurShader.end();
|
||||
}
|
||||
blurShader.begin();
|
||||
blurShader.setUniformf("dir", 0f, 1f);
|
||||
fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4);
|
||||
blurShader.end();
|
||||
pingPongBuffer1.end();
|
||||
}
|
||||
}
|
||||
@@ -334,9 +303,7 @@ public class Bloom{
|
||||
public void setBloomIntesity(float intensity){
|
||||
bloomIntensity = intensity;
|
||||
bloomShader.begin();
|
||||
{
|
||||
bloomShader.setUniformf("BloomIntensity", intensity);
|
||||
}
|
||||
bloomShader.setUniformf("BloomIntensity", intensity);
|
||||
bloomShader.end();
|
||||
}
|
||||
|
||||
@@ -350,9 +317,7 @@ public class Bloom{
|
||||
public void setOriginalIntesity(float intensity){
|
||||
originalIntensity = intensity;
|
||||
bloomShader.begin();
|
||||
{
|
||||
bloomShader.setUniformf("OriginalIntensity", intensity);
|
||||
}
|
||||
bloomShader.setUniformf("OriginalIntensity", intensity);
|
||||
bloomShader.end();
|
||||
}
|
||||
|
||||
@@ -364,10 +329,7 @@ public class Bloom{
|
||||
public void setThreshold(float threshold){
|
||||
this.threshold = threshold;
|
||||
tresholdShader.begin();
|
||||
{
|
||||
tresholdShader.setUniformf("threshold", threshold,
|
||||
1f / (1 - threshold));
|
||||
}
|
||||
tresholdShader.setUniformf("threshold", threshold, 1f / (1 - threshold));
|
||||
tresholdShader.end();
|
||||
}
|
||||
|
||||
@@ -384,8 +346,7 @@ public class Bloom{
|
||||
*/
|
||||
public void dispose(){
|
||||
try{
|
||||
if(disposeFBO)
|
||||
frameBuffer.dispose();
|
||||
if(disposeFBO) frameBuffer.dispose();
|
||||
|
||||
fullScreenQuad.dispose();
|
||||
|
||||
@@ -395,16 +356,17 @@ public class Bloom{
|
||||
blurShader.dispose();
|
||||
bloomShader.dispose();
|
||||
tresholdShader.dispose();
|
||||
}catch(Exception ignored){
|
||||
}catch(Throwable ignored){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static Mesh createFullScreenQuad(){
|
||||
float[] verts = {-1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1, -1, 1, 0, 1};
|
||||
Mesh tmpMesh = new Mesh(true, 4, 0, new VertexAttribute(
|
||||
Usage.Position, 2, "a_position"), new VertexAttribute(
|
||||
Usage.TextureCoordinates, 2, "a_texCoord0"));
|
||||
Mesh tmpMesh = new Mesh(true, 4, 0,
|
||||
new VertexAttribute(Usage.position, 2, "a_position"),
|
||||
new VertexAttribute(Usage.textureCoordinates, 2, "a_texCoord0")
|
||||
);
|
||||
|
||||
tmpMesh.setVertices(verts);
|
||||
return tmpMesh;
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
package mindustry.graphics;
|
||||
|
||||
import arc.Core;
|
||||
import arc.Events;
|
||||
import arc.struct.*;
|
||||
import arc.struct.IntSet.IntSetIterator;
|
||||
import arc.graphics.Camera;
|
||||
import arc.graphics.GL20;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.Mathf;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.IntSet.*;
|
||||
import arc.util.*;
|
||||
import mindustry.game.EventType.WorldLoadEvent;
|
||||
import mindustry.world.Tile;
|
||||
import mindustry.world.blocks.Floor;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.tilesize;
|
||||
import static mindustry.Vars.world;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class FloorRenderer implements Disposable{
|
||||
private final static int chunksize = 64;
|
||||
@@ -104,7 +101,7 @@ public class FloorRenderer implements Disposable{
|
||||
cbatch.setProjection(Core.camera.projection());
|
||||
cbatch.beginDraw();
|
||||
|
||||
Core.gl.glEnable(GL20.GL_BLEND);
|
||||
Gl.enable(Gl.blend);
|
||||
}
|
||||
|
||||
public void endDraw(){
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package mindustry.graphics;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.VertexAttributes.Usage;
|
||||
import arc.graphics.g2d.BatchShader;
|
||||
import arc.graphics.g2d.TextureRegion;
|
||||
import arc.graphics.gl.Shader;
|
||||
import arc.math.Mathf;
|
||||
import arc.math.Matrix3;
|
||||
import arc.util.Disposable;
|
||||
import arc.util.Strings;
|
||||
import arc.graphics.VertexAttributes.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
|
||||
//TODO this class is a trainwreck, remove it
|
||||
public class IndexedRenderer implements Disposable{
|
||||
@@ -17,18 +13,18 @@ public class IndexedRenderer implements Disposable{
|
||||
|
||||
private Shader program = new Shader(
|
||||
Strings.join("\n",
|
||||
"attribute vec4 " + Shader.POSITION_ATTRIBUTE + ";",
|
||||
"attribute vec4 " + Shader.COLOR_ATTRIBUTE + ";",
|
||||
"attribute vec2 " + Shader.TEXCOORD_ATTRIBUTE + "0;",
|
||||
"attribute vec4 " + Shader.positionAttribute + ";",
|
||||
"attribute vec4 " + Shader.colorAttribute + ";",
|
||||
"attribute vec2 " + Shader.texcoordAttribute + "0;",
|
||||
"uniform mat4 u_projTrans;",
|
||||
"varying vec4 v_color;",
|
||||
"varying vec2 v_texCoords;",
|
||||
"",
|
||||
"void main(){",
|
||||
" v_color = " + Shader.COLOR_ATTRIBUTE + ";",
|
||||
" v_color = " + Shader.colorAttribute + ";",
|
||||
" v_color.a = v_color.a * (255.0/254.0);",
|
||||
" v_texCoords = " + Shader.TEXCOORD_ATTRIBUTE + "0;",
|
||||
" gl_Position = u_projTrans * " + Shader.POSITION_ATTRIBUTE + ";",
|
||||
" v_texCoords = " + Shader.texcoordAttribute + "0;",
|
||||
" gl_Position = u_projTrans * " + Shader.positionAttribute + ";",
|
||||
"}"),
|
||||
Strings.join("\n",
|
||||
"#ifdef GL_ES",
|
||||
@@ -60,7 +56,7 @@ public class IndexedRenderer implements Disposable{
|
||||
}
|
||||
|
||||
public void render(Texture texture){
|
||||
Core.gl.glEnable(GL20.GL_BLEND);
|
||||
Gl.enable(Gl.blend);
|
||||
|
||||
updateMatrix();
|
||||
|
||||
@@ -71,7 +67,7 @@ public class IndexedRenderer implements Disposable{
|
||||
program.setUniformMatrix4("u_projTrans", BatchShader.copyTransform(combined));
|
||||
program.setUniformi("u_texture", 0);
|
||||
|
||||
mesh.render(program, GL20.GL_TRIANGLES, 0, vertices.length / vsize);
|
||||
mesh.render(program, Gl.triangles, 0, vertices.length / vsize);
|
||||
|
||||
program.end();
|
||||
}
|
||||
@@ -226,9 +222,9 @@ public class IndexedRenderer implements Disposable{
|
||||
if(mesh != null) mesh.dispose();
|
||||
|
||||
mesh = new Mesh(true, 6 * sprites, 0,
|
||||
new VertexAttribute(Usage.Position, 2, "a_position"),
|
||||
new VertexAttribute(Usage.ColorPacked, 4, "a_color"),
|
||||
new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0"));
|
||||
new VertexAttribute(Usage.position, 2, "a_position"),
|
||||
new VertexAttribute(Usage.colorPacked, 4, "a_color"),
|
||||
new VertexAttribute(Usage.textureCoordinates, 2, "a_texCoord0"));
|
||||
vertices = new float[6 * sprites * vsize];
|
||||
mesh.setVertices(vertices);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.type.*;
|
||||
@@ -42,7 +43,7 @@ public class MinimapRenderer implements Disposable{
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
public Texture getTexture(){
|
||||
public @Nullable Texture getTexture(){
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -70,8 +71,13 @@ public class MinimapRenderer implements Disposable{
|
||||
region = new TextureRegion(texture);
|
||||
}
|
||||
|
||||
public void drawEntities(float x, float y, float w, float h, boolean withLabels){
|
||||
updateUnitArray();
|
||||
public void drawEntities(float x, float y, float w, float h, float scaling, boolean withLabels){
|
||||
if(!withLabels){
|
||||
updateUnitArray();
|
||||
}else{
|
||||
units.clear();
|
||||
Units.all(units::add);
|
||||
}
|
||||
|
||||
float sz = baseSize * zoom;
|
||||
float dx = (Core.camera.position.x / tilesize);
|
||||
@@ -82,8 +88,14 @@ public class MinimapRenderer implements Disposable{
|
||||
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
|
||||
|
||||
for(Unit unit : units){
|
||||
float rx = (unit.x - rect.x) / rect.width * w;
|
||||
float ry = (unit.y - rect.y) / rect.width * h;
|
||||
if(unit.isDead()) continue;
|
||||
float rx = !withLabels ? (unit.x - rect.x) / rect.width * w : unit.x / (world.width() * tilesize) * w;
|
||||
float ry = !withLabels ? (unit.y - rect.y) / rect.width * h : unit.y / (world.height() * tilesize) * h;
|
||||
|
||||
Draw.mixcol(unit.getTeam().color, 1f);
|
||||
float scale = Scl.scl(1f) / 2f * scaling * 32f;
|
||||
Draw.rect(unit.getIconRegion(), x + rx, y + ry, scale, scale, unit.rotation - 90);
|
||||
Draw.reset();
|
||||
|
||||
if(withLabels && unit instanceof Player){
|
||||
Player pl = (Player) unit;
|
||||
@@ -92,19 +104,16 @@ public class MinimapRenderer implements Disposable{
|
||||
drawLabel(x + rx, y + ry, pl.name, unit.getTeam().color);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color(unit.getTeam().color);
|
||||
Fill.rect(x + rx, y + ry, Scl.scl(baseSize / 2f), Scl.scl(baseSize / 2f));
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public void drawEntities(float x, float y, float w, float h){
|
||||
drawEntities(x, y, w, h, true);
|
||||
drawEntities(x, y, w, h, 1f, true);
|
||||
}
|
||||
|
||||
public TextureRegion getRegion(){
|
||||
public @Nullable TextureRegion getRegion(){
|
||||
if(texture == null) return null;
|
||||
|
||||
float sz = Mathf.clamp(baseSize * zoom, baseSize, Math.min(world.width(), world.height()));
|
||||
|
||||
@@ -96,7 +96,7 @@ public class OverlayRenderer{
|
||||
if(buildFadeTime > 0.005f){
|
||||
state.teams.eachEnemyCore(player.getTeam(), core -> {
|
||||
float dst = core.dst(player);
|
||||
if(dst < state.rules.enemyCoreBuildRadius * 1.5f){
|
||||
if(dst < state.rules.enemyCoreBuildRadius * 2.2f){
|
||||
Draw.color(Color.darkGray);
|
||||
Lines.circle(core.x, core.y - 2, state.rules.enemyCoreBuildRadius);
|
||||
Draw.color(Pal.accent, core.getTeam().color, 0.5f + Mathf.absin(Time.time(), 10f, 0.5f));
|
||||
|
||||
@@ -10,6 +10,7 @@ import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
@@ -135,22 +136,29 @@ public class DesktopInput extends InputHandler{
|
||||
ui.listfrag.toggle();
|
||||
}
|
||||
|
||||
if(((player.getClosestCore() == null && player.isDead()) || state.isPaused()) && !ui.chatfrag.shown()){
|
||||
//move camera around
|
||||
float camSpeed = !Core.input.keyDown(Binding.dash) ? 3f : 8f;
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta() * camSpeed));
|
||||
|
||||
if(Core.input.keyDown(Binding.mouse_move)){
|
||||
Core.camera.position.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2f) * 0.005f, -1, 1) * camSpeed;
|
||||
Core.camera.position.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2f) * 0.005f, -1, 1) * camSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.input.keyRelease(Binding.select)){
|
||||
player.isShooting = false;
|
||||
}
|
||||
|
||||
if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && (scene.getKeyboardFocus() == ui.minimap || !scene.hasDialog()) && !Core.scene.hasKeyboard() && !(scene.getKeyboardFocus() instanceof TextField)){
|
||||
if(!ui.minimap.isShown()){
|
||||
ui.minimap.show();
|
||||
}else{
|
||||
ui.minimap.hide();
|
||||
}
|
||||
if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && !scene.hasDialog() && !(scene.getKeyboardFocus() instanceof TextField)){
|
||||
ui.minimapfrag.toggle();
|
||||
}
|
||||
|
||||
if(state.is(State.menu) || Core.scene.hasDialog()) return;
|
||||
|
||||
//zoom camera
|
||||
if(!Core.scene.hasScroll() && !ui.chatfrag.shown() && Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && !Core.input.keyDown(Binding.rotateplaced) && (Core.input.keyDown(Binding.diagonal_placement) || ((!isPlacing() || !block.rotate) && selectRequests.isEmpty()))){
|
||||
if((!Core.scene.hasScroll() || Core.input.keyDown(Binding.diagonal_placement)) && !ui.chatfrag.shown() && Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && !Core.input.keyDown(Binding.rotateplaced) && (Core.input.keyDown(Binding.diagonal_placement) || ((!isPlacing() || !block.rotate) && selectRequests.isEmpty()))){
|
||||
renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Tile tile){
|
||||
if(player == null || player.timer == null || !player.timer.get(Player.timerTransfer, 40)) return;
|
||||
if(player == null || player.timer == null) return;
|
||||
if(net.server() && (player.item().amount <= 0 || player.isTransferring|| !Units.canInteract(player, tile))){
|
||||
throw new ValidateException(player, "Player cannot transfer an item.");
|
||||
}
|
||||
@@ -725,7 +725,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
public void tryDropItems(Tile tile, float x, float y){
|
||||
if(!droppingItem || player.item().amount <= 0 || canTapPlayer(x, y) || state.isPaused() || !player.timer.check(Player.timerTransfer, 40)){
|
||||
if(!droppingItem || player.item().amount <= 0 || canTapPlayer(x, y) || state.isPaused() ){
|
||||
droppingItem = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -439,10 +439,12 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
|
||||
@Override
|
||||
public boolean touchDown(int screenX, int screenY, int pointer, KeyCode button){
|
||||
if(state.is(State.menu) || player.isDead()) return false;
|
||||
if(state.is(State.menu)) return false;
|
||||
|
||||
down = true;
|
||||
|
||||
if(player.isDead()) return false;
|
||||
|
||||
//get tile on cursor
|
||||
Tile cursor = tileAt(screenX, screenY);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -333,8 +333,8 @@ public class ContentParser{
|
||||
}
|
||||
|
||||
private void readBundle(ContentType type, String name, JsonValue value){
|
||||
UnlockableContent cont = Vars.content.getByName(type, name) instanceof UnlockableContent ?
|
||||
Vars.content.getByName(type, name) : null;
|
||||
UnlockableContent cont = locate(type, name) instanceof UnlockableContent ?
|
||||
locate(type, name) : null;
|
||||
|
||||
String entryName = cont == null ? type + "." + currentMod.name + "-" + name + "." : type + "." + cont.name + ".";
|
||||
I18NBundle bundle = Core.bundle;
|
||||
|
||||
19
core/src/mindustry/mod/ModListing.java
Normal file
19
core/src/mindustry/mod/ModListing.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package mindustry.mod;
|
||||
|
||||
/** Mod listing as a data class. */
|
||||
public class ModListing{
|
||||
public String repo, name, author, lastUpdated, description;
|
||||
public int stars;
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "ModListing{" +
|
||||
"repo='" + repo + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", author='" + author + '\'' +
|
||||
", lastUpdated='" + lastUpdated + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", stars=" + stars +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -512,10 +512,8 @@ public class Mods implements Loadable{
|
||||
for(ContentType type : ContentType.all){
|
||||
Fi folder = contentRoot.child(type.name().toLowerCase() + "s");
|
||||
if(folder.exists()){
|
||||
for(Fi file : folder.list()){
|
||||
if(file.extension().equals("json") || file.extension().equals("hjson")){
|
||||
runs.add(new LoadRun(type, file, mod));
|
||||
}
|
||||
for(Fi file : folder.findAll(f -> f.extension().equals("json") || f.extension().equals("hjson"))){
|
||||
runs.add(new LoadRun(type, file, mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ public class Scripts implements Disposable{
|
||||
context.setClassShutter(type -> (ClassAccess.allowedClassNames.contains(type) || type.startsWith("$Proxy") ||
|
||||
type.startsWith("adapter") || type.contains("PrintStream") ||
|
||||
type.startsWith("mindustry")) && !type.equals("mindustry.mod.ClassAccess"));
|
||||
|
||||
context.getWrapFactory().setJavaPrimitiveWrap(false);
|
||||
|
||||
scope = new ImporterTopLevel(context);
|
||||
wrapper = Core.files.internal("scripts/wrapper.js").readString();
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import arc.util.pooling.Pool.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.headless;
|
||||
import static mindustry.game.EventType.*;
|
||||
@@ -16,14 +22,61 @@ public class Administration{
|
||||
private Array<String> bannedIPs = new Array<>();
|
||||
private Array<String> whitelist = new Array<>();
|
||||
private Array<ChatFilter> chatFilters = new Array<>();
|
||||
private Array<ActionFilter> actionFilters = new Array<>();
|
||||
private Array<String> subnetBans = new Array<>();
|
||||
|
||||
public Administration(){
|
||||
Core.settings.defaults(
|
||||
"strict", true,
|
||||
"servername", "Server"
|
||||
);
|
||||
|
||||
load();
|
||||
|
||||
//anti-spam
|
||||
addChatFilter((player, message) -> {
|
||||
long resetTime = Config.messageRateLimit.num() * 1000;
|
||||
if(Config.antiSpam.bool() && !player.isLocal && !player.isAdmin){
|
||||
//prevent people from spamming messages quickly
|
||||
if(resetTime > 0 && Time.timeSinceMillis(player.getInfo().lastMessageTime) < resetTime){
|
||||
//supress message
|
||||
player.sendMessage("[scarlet]You may only send messages every " + Config.messageRateLimit.num() + " seconds.");
|
||||
player.getInfo().messageInfractions ++;
|
||||
//kick player for spamming and prevent connection if they've done this several times
|
||||
if(player.getInfo().messageInfractions >= Config.messageSpamKick.num() && Config.messageSpamKick.num() != 0){
|
||||
player.con.kick("You have been kicked for spamming.", 1000 * 60 * 2);
|
||||
}
|
||||
player.getInfo().lastSentMessage = message;
|
||||
return null;
|
||||
}else{
|
||||
player.getInfo().messageInfractions = 0;
|
||||
}
|
||||
|
||||
//prevent players from sending the same message twice in the span of 50 seconds
|
||||
if(message.equals(player.getInfo().lastSentMessage) && Time.timeSinceMillis(player.getInfo().lastMessageTime) < 1000 * 50){
|
||||
player.sendMessage("[scarlet]You may not send the same message twice.");
|
||||
return null;
|
||||
}
|
||||
|
||||
player.getInfo().lastSentMessage = message;
|
||||
player.getInfo().lastMessageTime = Time.millis();
|
||||
}
|
||||
|
||||
return message;
|
||||
});
|
||||
}
|
||||
|
||||
public Array<String> getSubnetBans(){
|
||||
return subnetBans;
|
||||
}
|
||||
|
||||
public void removeSubnetBan(String ip){
|
||||
subnetBans.remove(ip);
|
||||
save();
|
||||
}
|
||||
|
||||
public void addSubnetBan(String ip){
|
||||
subnetBans.add(ip);
|
||||
save();
|
||||
}
|
||||
|
||||
public boolean isSubnetBanned(String ip){
|
||||
return subnetBans.contains(ip::startsWith);
|
||||
}
|
||||
|
||||
/** Adds a chat filter. This will transform the chat messages of every player.
|
||||
@@ -43,6 +96,25 @@ public class Administration{
|
||||
return current;
|
||||
}
|
||||
|
||||
/** Add a filter to actions, preventing things such as breaking or configuring blocks. */
|
||||
public void addActionFilter(ActionFilter filter){
|
||||
actionFilters.add(filter);
|
||||
}
|
||||
|
||||
/** @return whether this action is allowed by the action filters. */
|
||||
public boolean allowAction(Player player, ActionType type, Tile tile, Cons<PlayerAction> setter){
|
||||
PlayerAction act = Pools.obtain(PlayerAction.class, PlayerAction::new);
|
||||
setter.get(act.set(player, type, tile));
|
||||
for(ActionFilter filter : actionFilters){
|
||||
if(!filter.allow(act)){
|
||||
Pools.free(act);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Pools.free(act);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getPlayerLimit(){
|
||||
return Core.settings.getInt("playerlimit", 0);
|
||||
}
|
||||
@@ -51,21 +123,12 @@ public class Administration{
|
||||
Core.settings.putSave("playerlimit", limit);
|
||||
}
|
||||
|
||||
public void setStrict(boolean on){
|
||||
Core.settings.putSave("strict", on);
|
||||
}
|
||||
|
||||
public boolean getStrict(){
|
||||
return Core.settings.getBool("strict");
|
||||
return Config.strict.bool();
|
||||
}
|
||||
|
||||
public boolean allowsCustomClients(){
|
||||
return Core.settings.getBool("allow-custom", !headless);
|
||||
}
|
||||
|
||||
public void setCustomClients(boolean allowed){
|
||||
Core.settings.put("allow-custom", allowed);
|
||||
Core.settings.save();
|
||||
return Config.allowCustomClients.bool();
|
||||
}
|
||||
|
||||
/** Call when a player joins to update their information here. */
|
||||
@@ -219,11 +282,7 @@ public class Administration{
|
||||
}
|
||||
|
||||
public boolean isWhitelistEnabled(){
|
||||
return Core.settings.getBool("whitelist", false);
|
||||
}
|
||||
|
||||
public void setWhitelist(boolean enabled){
|
||||
Core.settings.putSave("whitelist", enabled);
|
||||
return Config.whitelist.bool();
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String id, String usid){
|
||||
@@ -323,6 +382,7 @@ public class Administration{
|
||||
Core.settings.putObject("player-info", playerInfo);
|
||||
Core.settings.putObject("banned-ips", bannedIPs);
|
||||
Core.settings.putObject("whitelisted", whitelist);
|
||||
Core.settings.putObject("subnet-bans", subnetBans);
|
||||
Core.settings.save();
|
||||
}
|
||||
|
||||
@@ -331,6 +391,87 @@ public class Administration{
|
||||
playerInfo = Core.settings.getObject("player-info", ObjectMap.class, ObjectMap::new);
|
||||
bannedIPs = Core.settings.getObject("banned-ips", Array.class, Array::new);
|
||||
whitelist = Core.settings.getObject("whitelisted", Array.class, Array::new);
|
||||
subnetBans = Core.settings.getObject("subnet-bans", Array.class, Array::new);
|
||||
}
|
||||
|
||||
/** Server configuration definition. Each config value can be a string, boolean or number. */
|
||||
public enum Config{
|
||||
name("The server name as displayed on clients.", "Server", "servername"),
|
||||
port("The port to host on.", Vars.port),
|
||||
autoUpdate("Whether to auto-update and exit when a new bleeding-edge update arrives.", false),
|
||||
showConnectMessages("Whether to display connect/disconnect messages.", true),
|
||||
enableVotekick("Whether votekick is enabled.", true),
|
||||
startCommands("Commands run at startup. This should be a comma-separated list.", ""),
|
||||
crashReport("Whether to send crash reports.", false, "crashreport"),
|
||||
logging("Whether to log everything to files.", true),
|
||||
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
|
||||
antiSpam("Whether spammers are automatically kicked and rate-limited.", true),
|
||||
messageRateLimit("Message rate limit in seconds. 0 to disable.", 0),
|
||||
messageSpamKick("How many times a player must send a message before the cooldown to get kicked. 0 to disable.", 3),
|
||||
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
|
||||
socketInputPort("The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)),
|
||||
socketInputAddress("The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)),
|
||||
allowCustomClients("Whether custom clients are allowed to connect.", !headless, "allow-custom"),
|
||||
whitelist("Whether the whitelist is used.", false),
|
||||
motd("The message displayed to people on connection.", "off");
|
||||
|
||||
public static final Config[] all = values();
|
||||
|
||||
public final Object defaultValue;
|
||||
public final String key, description;
|
||||
final Runnable changed;
|
||||
|
||||
Config(String description, Object def){
|
||||
this(description, def, null, null);
|
||||
}
|
||||
|
||||
Config(String description, Object def, String key){
|
||||
this(description, def, key, null);
|
||||
}
|
||||
|
||||
Config(String description, Object def, Runnable changed){
|
||||
this(description, def, null, changed);
|
||||
}
|
||||
|
||||
Config(String description, Object def, String key, Runnable changed){
|
||||
this.description = description;
|
||||
this.key = key == null ? name() : key;
|
||||
this.defaultValue = def;
|
||||
this.changed = changed == null ? () -> {} : changed;
|
||||
}
|
||||
|
||||
public boolean isNum(){
|
||||
return defaultValue instanceof Integer;
|
||||
}
|
||||
|
||||
public boolean isBool(){
|
||||
return defaultValue instanceof Boolean;
|
||||
}
|
||||
|
||||
public boolean isString(){
|
||||
return defaultValue instanceof String;
|
||||
}
|
||||
|
||||
public Object get(){
|
||||
return Core.settings.get(key, defaultValue);
|
||||
}
|
||||
|
||||
public boolean bool(){
|
||||
return Core.settings.getBool(key, (Boolean)defaultValue);
|
||||
}
|
||||
|
||||
public int num(){
|
||||
return Core.settings.getInt(key, (Integer)defaultValue);
|
||||
}
|
||||
|
||||
public String string(){
|
||||
return Core.settings.getString(key, (String)defaultValue);
|
||||
}
|
||||
|
||||
public void set(Object value){
|
||||
Core.settings.putSave(key, value);
|
||||
changed.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Serialize
|
||||
@@ -343,7 +484,11 @@ public class Administration{
|
||||
public int timesKicked;
|
||||
public int timesJoined;
|
||||
public boolean banned, admin;
|
||||
public long lastKicked; //last kicked timestamp
|
||||
public long lastKicked; //last kicked time to expiration
|
||||
|
||||
public transient long lastMessageTime, lastSyncTime;
|
||||
public transient String lastSentMessage;
|
||||
public transient int messageInfractions;
|
||||
|
||||
PlayerInfo(String id){
|
||||
this.id = id;
|
||||
@@ -353,11 +498,18 @@ public class Administration{
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles chat messages from players and changes their contents. */
|
||||
public interface ChatFilter{
|
||||
/** @return the filtered message; a null string signals that the message should not be sent. */
|
||||
@Nullable String filter(Player player, String message);
|
||||
}
|
||||
|
||||
/** Allows or disallows player actions. */
|
||||
public interface ActionFilter{
|
||||
/** @return whether this action should be permitted. if applicable, make sure to send this player a message specify why the action was prohibited. */
|
||||
boolean allow(PlayerAction action);
|
||||
}
|
||||
|
||||
public static class TraceInfo{
|
||||
public String ip, uuid;
|
||||
public boolean modded, mobile;
|
||||
@@ -370,4 +522,39 @@ public class Administration{
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines a (potentially dangerous) action that a player has done in the world.
|
||||
* These objects are pooled; do not cache them! */
|
||||
public static class PlayerAction implements Poolable{
|
||||
public @NonNull Player player;
|
||||
public @NonNull ActionType type;
|
||||
public @NonNull Tile tile;
|
||||
|
||||
/** valid for configure and rotation-type events only. */
|
||||
public int config;
|
||||
|
||||
/** valid for item-type events only. */
|
||||
public @Nullable Item item;
|
||||
public int itemAmount;
|
||||
|
||||
public PlayerAction set(Player player, ActionType type, Tile tile){
|
||||
this.player = player;
|
||||
this.type = type;
|
||||
this.tile = tile;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(){
|
||||
item = null;
|
||||
itemAmount = config = 0;
|
||||
player = null;
|
||||
type = null;
|
||||
tile = null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ActionType{
|
||||
breakBlock, placeBlock, rotate, configure, withdrawItem, depositItem
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
173
core/src/mindustry/net/BeControl.java
Normal file
173
core/src/mindustry/net/BeControl.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.Net.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Handles control of bleeding edge builds. */
|
||||
public class BeControl{
|
||||
private static final int updateInterval = 60 * 2;
|
||||
|
||||
private AsyncExecutor executor = new AsyncExecutor(1);
|
||||
private boolean checkUpdates = true;
|
||||
private boolean updateAvailable;
|
||||
private String updateUrl;
|
||||
private int updateBuild;
|
||||
|
||||
/** @return whether this is a bleeding edge build. */
|
||||
public boolean active(){
|
||||
return Version.type.equals("bleeding-edge");
|
||||
}
|
||||
|
||||
public BeControl(){
|
||||
if(active()){
|
||||
Timer.schedule(() -> {
|
||||
if(checkUpdates && !mobile){
|
||||
checkUpdate(t -> {});
|
||||
}
|
||||
}, 1, updateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/** asynchronously checks for updates. */
|
||||
public void checkUpdate(Boolc done){
|
||||
Core.net.httpGet("https://api.github.com/repos/Anuken/MindustryBuilds/releases/latest", res -> {
|
||||
if(res.getStatus() == HttpStatus.OK){
|
||||
Jval val = Jval.read(res.getResultAsString());
|
||||
int newBuild = Strings.parseInt(val.getString("tag_name", "0"));
|
||||
if(newBuild > Version.build){
|
||||
Jval asset = val.get("assets").asArray().find(v -> v.getString("name", "").startsWith(headless ? "Mindustry-BE-Server" : "Mindustry-BE-Desktop"));
|
||||
String url = asset.getString("browser_download_url", "");
|
||||
updateAvailable = true;
|
||||
updateBuild = newBuild;
|
||||
updateUrl = url;
|
||||
showUpdateDialog();
|
||||
Core.app.post(() -> done.get(true));
|
||||
}else{
|
||||
Core.app.post(() -> done.get(false));
|
||||
}
|
||||
}else{
|
||||
Core.app.post(() -> done.get(false));
|
||||
}
|
||||
}, error -> {
|
||||
if(!headless){
|
||||
ui.showException(error);
|
||||
}else{
|
||||
error.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @return whether a new update is available */
|
||||
public boolean isUpdateAvailable(){
|
||||
return updateAvailable;
|
||||
}
|
||||
|
||||
/** shows the dialog for updating the game on desktop, or a prompt for doing so on the server */
|
||||
public void showUpdateDialog(){
|
||||
if(!updateAvailable) return;
|
||||
|
||||
if(!headless){
|
||||
checkUpdates = false;
|
||||
ui.showCustomConfirm(Core.bundle.format("be.update", "") + " " + updateBuild, "$be.update.confirm", "$ok", "$be.ignore", () -> {
|
||||
boolean[] cancel = {false};
|
||||
float[] progress = {0};
|
||||
int[] length = {0};
|
||||
Fi file = bebuildDirectory.child("client-be-" + updateBuild + ".jar");
|
||||
|
||||
FloatingDialog dialog = new FloatingDialog("$be.updating");
|
||||
download(updateUrl, file, i -> length[0] = i, v -> progress[0] = v, () -> cancel[0], () -> {
|
||||
try{
|
||||
Runtime.getRuntime().exec(new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()});
|
||||
System.exit(0);
|
||||
}catch(IOException e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}, e -> {
|
||||
dialog.hide();
|
||||
ui.showException(e);
|
||||
});
|
||||
|
||||
dialog.cont.add(new Bar(() -> length[0] == 0 ? Core.bundle.get("be.updating") : (int)(progress[0] * length[0]) / 1024/ 1024 + "/" + length[0]/1024/1024 + " MB", () -> Pal.accent, () -> progress[0])).width(400f).height(70f);
|
||||
dialog.buttons.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
|
||||
cancel[0] = true;
|
||||
dialog.hide();
|
||||
}).size(210f, 64f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.show();
|
||||
}, () -> checkUpdates = false);
|
||||
}else{
|
||||
Log.info("&lcA new update is available: &lyBleeding Edge build {0}", updateBuild);
|
||||
if(Config.autoUpdate.bool()){
|
||||
Log.info("&lcAuto-downloading next version...");
|
||||
|
||||
try{
|
||||
//download new file from github
|
||||
Fi source = Fi.get(BeControl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
Fi dest = source.sibling("server-be-" + updateBuild + ".jar");
|
||||
|
||||
download(updateUrl, dest,
|
||||
len -> Core.app.post(() -> Log.info("&ly| Size: {0} MB.", Strings.fixed((float)len / 1024 / 1024, 2))),
|
||||
progress -> {},
|
||||
() -> false,
|
||||
() -> Core.app.post(() -> {
|
||||
netServer.kickAll(KickReason.serverRestarting);
|
||||
Threads.sleep(32);
|
||||
|
||||
Log.info("&lcVersion downloaded, exiting. Note that if you are not using a auto-restart script, the server will not restart automatically.");
|
||||
//replace old file with new
|
||||
dest.copyTo(source);
|
||||
dest.delete();
|
||||
System.exit(2); //this will cause a restart if using the script
|
||||
}),
|
||||
Throwable::printStackTrace);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
checkUpdates = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void download(String furl, Fi dest, Intc length, Floatc progressor, Boolp canceled, Runnable done, Cons<Throwable> error){
|
||||
executor.submit(() -> {
|
||||
try{
|
||||
HttpURLConnection con = (HttpURLConnection)new URL(furl).openConnection();
|
||||
BufferedInputStream in = new BufferedInputStream(con.getInputStream());
|
||||
OutputStream out = dest.write(false, 4096);
|
||||
|
||||
byte[] data = new byte[4096];
|
||||
long size = con.getContentLength();
|
||||
long counter = 0;
|
||||
length.get((int)size);
|
||||
int x;
|
||||
while((x = in.read(data, 0, data.length)) >= 0 && !canceled.get()){
|
||||
counter += x;
|
||||
progressor.get((float)counter / (float)size);
|
||||
out.write(data, 0, x);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
if(!canceled.get()) done.run();
|
||||
}catch(Throwable e){
|
||||
error.get(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,9 @@ public class CrashSender{
|
||||
exception.printStackTrace();
|
||||
|
||||
//don't create crash logs for custom builds, as it's expected
|
||||
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))) return;
|
||||
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))){
|
||||
ret();
|
||||
}
|
||||
|
||||
//attempt to load version regardless
|
||||
if(Version.number == 0){
|
||||
@@ -63,7 +65,7 @@ public class CrashSender{
|
||||
try{
|
||||
//check crash report setting
|
||||
if(!Core.settings.getBool("crashreport", true)){
|
||||
return;
|
||||
ret();
|
||||
}
|
||||
}catch(Throwable ignored){
|
||||
//if there's no settings init we don't know what the user wants but chances are it's an important crash, so send it anyway
|
||||
@@ -72,14 +74,14 @@ public class CrashSender{
|
||||
try{
|
||||
//check any mods - if there are any, don't send reports
|
||||
if(Vars.mods != null && !Vars.mods.list().isEmpty()){
|
||||
return;
|
||||
ret();
|
||||
}
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
|
||||
//do not send exceptions that occur for versions that can't be parsed
|
||||
if(Version.number == 0){
|
||||
return;
|
||||
ret();
|
||||
}
|
||||
|
||||
boolean netActive = false, netServer = false;
|
||||
@@ -130,12 +132,16 @@ public class CrashSender{
|
||||
while(!sent[0]){
|
||||
Thread.sleep(30);
|
||||
}
|
||||
}catch(InterruptedException ignored){
|
||||
}
|
||||
}catch(InterruptedException ignored){}
|
||||
}catch(Throwable death){
|
||||
death.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
ret();
|
||||
}
|
||||
|
||||
private static void ret(){
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private static void httpPost(String url, String content, Cons<HttpResponse> success, Cons<Throwable> failure){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.net;
|
||||
|
||||
import mindustry.*;
|
||||
import mindustry.game.*;
|
||||
|
||||
public class Host{
|
||||
@@ -11,7 +12,7 @@ public class Host{
|
||||
public final int version;
|
||||
public final String versionType;
|
||||
public final Gamemode mode;
|
||||
public int ping;
|
||||
public int ping, port = Vars.port;
|
||||
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit){
|
||||
this.name = name;
|
||||
|
||||
@@ -15,8 +15,7 @@ import static mindustry.Vars.netServer;
|
||||
public abstract class NetConnection{
|
||||
public final String address;
|
||||
public boolean mobile, modclient;
|
||||
public @Nullable
|
||||
Player player;
|
||||
public @Nullable Player player;
|
||||
|
||||
/** ID of last recieved client snapshot. */
|
||||
public int lastRecievedClientSnapshot = -1;
|
||||
@@ -37,7 +36,7 @@ public abstract class NetConnection{
|
||||
if(player != null && (reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote) && player.uuid != null){
|
||||
PlayerInfo info = netServer.admins.getInfo(player.uuid);
|
||||
info.timesKicked++;
|
||||
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
|
||||
info.lastKicked = Math.max(Time.millis() + 30 * 1000, info.lastKicked);
|
||||
}
|
||||
|
||||
Call.onKick(this, reason);
|
||||
@@ -49,12 +48,17 @@ public abstract class NetConnection{
|
||||
|
||||
/** Kick with an arbitrary reason. */
|
||||
public void kick(String reason){
|
||||
kick(reason, 30 * 1000);
|
||||
}
|
||||
|
||||
/** Kick with an arbitrary reason, and a kick duration in milliseconds. */
|
||||
public void kick(String reason, int kickDuration){
|
||||
Log.info("Kicking connection {0}; Reason: {1}", address, reason.replace("\n", " "));
|
||||
|
||||
if(player != null && player.uuid != null){
|
||||
PlayerInfo info = netServer.admins.getInfo(player.uuid);
|
||||
info.timesKicked++;
|
||||
info.lastKicked = Math.max(Time.millis(), info.lastKicked);
|
||||
info.lastKicked = Math.max(Time.millis() + kickDuration, info.lastKicked);
|
||||
}
|
||||
|
||||
Call.onKick(this, reason);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.Map;
|
||||
import mindustry.net.Administration.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
@@ -62,7 +62,7 @@ public class NetworkIO{
|
||||
}
|
||||
|
||||
public static ByteBuffer writeServerData(){
|
||||
String name = (headless ? Core.settings.getString("servername") : player.name);
|
||||
String name = (headless ? Config.name.string() : player.name);
|
||||
String map = world.getMap() == null ? "None" : world.getMap().name();
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(256);
|
||||
|
||||
@@ -15,7 +15,8 @@ public class Packets{
|
||||
|
||||
public enum KickReason{
|
||||
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit;
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch,
|
||||
whitelist, playerLimit, serverRestarting;
|
||||
|
||||
public final boolean quiet;
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package mindustry.type;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.Color;
|
||||
import arc.graphics.g2d.TextureRegion;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.ctype.ContentType;
|
||||
import mindustry.entities.type.Player;
|
||||
import mindustry.ctype.UnlockableContent;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.ui.ContentDisplay;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class Mech extends UnlockableContent{
|
||||
public class Mech extends UnitDef{
|
||||
/*
|
||||
public boolean flying;
|
||||
public float speed = 1.1f;
|
||||
public float maxSpeed = 10f;
|
||||
@@ -32,7 +34,14 @@ public class Mech extends UnlockableContent{
|
||||
public boolean canHeal = false;
|
||||
public float compoundSpeed, compoundSpeedBoost;
|
||||
|
||||
public float weaponOffsetX, weaponOffsetY, engineOffset = 5f, engineSize = 2.5f;
|
||||
/** draw the health and team indicator
|
||||
public boolean drawCell = true;
|
||||
/** draw the items on its back
|
||||
public boolean drawItems = true;
|
||||
/** draw the engine light if it's flying/boosting
|
||||
public boolean drawLight = true;
|
||||
|
||||
public float weaponOffsetX, weaponOffsetY, engineOffset = 5f, engineSize = 2.5f;*/
|
||||
public @NonNull Weapon weapon;
|
||||
|
||||
public TextureRegion baseRegion, legRegion, region;
|
||||
@@ -46,12 +55,28 @@ public class Mech extends UnlockableContent{
|
||||
this(name, false);
|
||||
}
|
||||
|
||||
public void updateAlt(Player player){
|
||||
public void update(Player player){
|
||||
}
|
||||
|
||||
public void draw(Player player){
|
||||
}
|
||||
|
||||
public void drawStats(Player player){
|
||||
if(drawCell){
|
||||
float health = player.healthf();
|
||||
Draw.color(Color.black, player.getTeam().color, health + Mathf.absin(Time.time(), health * 5f, 1f - health));
|
||||
Draw.rect(player.getPowerCellRegion(),
|
||||
player.x + Angles.trnsx(player.rotation, cellOffsetY, cellOffsetX),
|
||||
player.y + Angles.trnsy(player.rotation, cellOffsetY, cellOffsetX),
|
||||
player.rotation - 90);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
if(drawItems){
|
||||
player.drawBackItems();
|
||||
}
|
||||
}
|
||||
|
||||
public float getExtraArmor(Player player){
|
||||
return 0f;
|
||||
}
|
||||
|
||||
37
core/src/mindustry/type/UnitDef.java
Normal file
37
core/src/mindustry/type/UnitDef.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package mindustry.type;
|
||||
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public abstract class UnitDef extends UnlockableContent{
|
||||
public boolean flying;
|
||||
public float speed = 1.1f, boostSpeed = 0.75f;
|
||||
public float drag = 0.4f, mass = 1f;
|
||||
public float health = 200f;
|
||||
|
||||
public int itemCapacity = 30;
|
||||
public int drillTier = -1;
|
||||
public float buildPower = 1f, minePower = 1f;
|
||||
|
||||
public Color engineColor = Pal.boostTo;
|
||||
public float engineOffset = 5f, engineSize = 2.5f;
|
||||
|
||||
public float hitsize = 6f, hitsizeTile = 4f;
|
||||
public float cellOffsetX = 0f, cellOffsetY = 0f;
|
||||
public float lightRadius = 60f;
|
||||
public Color lightColor = Pal.powerLight;
|
||||
public boolean drawCell = true, drawItems = true;
|
||||
|
||||
public ObjectSet<StatusEffect> immunities = new ObjectSet<>();
|
||||
public Sound deathSound = Sounds.bang;
|
||||
|
||||
public Array<Weapon> weapons = new Array<>();
|
||||
|
||||
public UnitDef(String name){
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,23 @@ package mindustry.type;
|
||||
|
||||
import arc.*;
|
||||
import arc.audio.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.ContentType;
|
||||
import mindustry.ctype.UnlockableContent;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class UnitType extends UnlockableContent{
|
||||
public class UnitType extends UnitDef{
|
||||
public @NonNull TypeID typeID;
|
||||
public @NonNull Prov<? extends BaseUnit> constructor;
|
||||
|
||||
/*
|
||||
public float health = 60;
|
||||
public float hitsize = 7f;
|
||||
public float hitsizeTile = 4f;
|
||||
@@ -40,7 +40,7 @@ public class UnitType extends UnlockableContent{
|
||||
public @NonNull Weapon weapon;
|
||||
public float weaponOffsetY, engineOffset = 6f, engineSize = 2f;
|
||||
public ObjectSet<StatusEffect> immunities = new ObjectSet<>();
|
||||
public Sound deathSound = Sounds.bang;
|
||||
public Sound deathSound = Sounds.bang;*/
|
||||
|
||||
public TextureRegion legRegion, baseRegion, region;
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ public class Weapon{
|
||||
public float shotDelay = 0;
|
||||
/** whether shooter rotation is ignored when shooting. */
|
||||
public boolean ignoreRotation = false;
|
||||
/** if turnCursor is false for a mech, how far away will the weapon target. */
|
||||
public float targetDistance = 1f;
|
||||
|
||||
public Sound shootSound = Sounds.pew;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class Minimap extends Table{
|
||||
Draw.rect(renderer.minimap.getRegion(), x + width / 2f, y + height / 2f, width, height);
|
||||
|
||||
if(renderer.minimap.getTexture() != null){
|
||||
renderer.minimap.drawEntities(x, y, width, height, false);
|
||||
renderer.minimap.drawEntities(x, y, width, height, 0.75f, false);
|
||||
}
|
||||
}
|
||||
}).size(140f);
|
||||
@@ -83,7 +83,7 @@ public class Minimap extends Table{
|
||||
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y){
|
||||
ui.minimap.show();
|
||||
ui.minimapfrag.toggle();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public class AboutDialog extends FloatingDialog{
|
||||
public void showCredits(){
|
||||
FloatingDialog dialog = new FloatingDialog("$credits");
|
||||
dialog.addCloseButton();
|
||||
dialog.cont.add("$credits.text");
|
||||
dialog.cont.add("$credits.text").fillX().wrap().get().setAlignment(Align.center);
|
||||
dialog.cont.row();
|
||||
if(!contributors.isEmpty()){
|
||||
dialog.cont.addImage().color(Pal.accent).fillX().height(3f).pad(3f);
|
||||
|
||||
@@ -142,6 +142,7 @@ public class CustomRulesDialog extends FloatingDialog{
|
||||
check("$rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions);
|
||||
number("$rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources);
|
||||
number("$rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier);
|
||||
number("$rules.blockhealthmultiplier", f -> rules.blockHealthMultiplier = f, () -> rules.blockHealthMultiplier);
|
||||
|
||||
main.addButton("$configure",
|
||||
() -> loadoutDialog.show(Blocks.coreShard.itemCapacity, rules.loadout,
|
||||
|
||||
@@ -288,7 +288,12 @@ public class JoinDialog extends FloatingDialog{
|
||||
local.table(Tex.button, t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
|
||||
net.discoverServers(this::addLocalHost, this::finishLocalHosts);
|
||||
for(String host : defaultServers){
|
||||
net.pingHost(host, port, this::addLocalHost, e -> {});
|
||||
String resaddress = host.contains(":") ? host.split(":")[0] : host;
|
||||
int resport = host.contains(":") ? Strings.parseInt(host.split(":")[1]) : port;
|
||||
net.pingHost(resaddress, resport, res -> {
|
||||
res.port = resport;
|
||||
addLocalHost(res);
|
||||
}, e -> {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +319,7 @@ public class JoinDialog extends FloatingDialog{
|
||||
|
||||
local.row();
|
||||
|
||||
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, port, host.version))
|
||||
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version))
|
||||
.width(w).pad(5f).get();
|
||||
button.clearChildren();
|
||||
buildServer(host, button);
|
||||
@@ -362,7 +367,7 @@ public class JoinDialog extends FloatingDialog{
|
||||
servers = Core.settings.getObject("server-list", Array.class, Array::new);
|
||||
|
||||
//get servers
|
||||
Core.net.httpGet(serverJsonURL, result -> {
|
||||
Core.net.httpGet(becontrol.active() ? serverJsonBeURL : serverJsonURL, result -> {
|
||||
try{
|
||||
Jval val = Jval.read(result.getResultAsString());
|
||||
Core.app.post(() -> {
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ModsDialog extends FloatingDialog{
|
||||
|
||||
buttons.row();
|
||||
|
||||
buttons.addImageTextButton("$mods.guide", Icon.wiki,
|
||||
buttons.addImageTextButton("$mods.guide", Icon.link,
|
||||
() -> Core.net.openURI(modGuideURL))
|
||||
.size(210, 64f);
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ public class SchematicsDialog extends FloatingDialog{
|
||||
setup();
|
||||
ui.showInfoFade("$schematic.saved");
|
||||
showInfo(s);
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));
|
||||
|
||||
@@ -229,6 +229,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
game.checkPref("savecreate", true);
|
||||
game.checkPref("blockreplace", true);
|
||||
game.checkPref("conveyorpathfinding", true);
|
||||
game.checkPref("coreselect", false);
|
||||
game.checkPref("hints", true);
|
||||
if(!mobile){
|
||||
game.checkPref("buildautopause", false);
|
||||
@@ -267,6 +268,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
}
|
||||
return s + "%";
|
||||
});
|
||||
graphics.sliderPref("bridgeopacity", 75, 0, 100, 5, s -> s + "%");
|
||||
|
||||
if(!mobile){
|
||||
graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b));
|
||||
|
||||
@@ -36,7 +36,7 @@ public class BlockInventoryFragment extends Fragment{
|
||||
|
||||
@Remote(called = Loc.server, targets = Loc.both, forward = true)
|
||||
public static void requestItem(Player player, Tile tile, Item item, int amount){
|
||||
if(player == null || tile == null || !player.timer.get(Player.timerTransfer, 20) || !tile.interactable(player.getTeam())) return;
|
||||
if(player == null || tile == null || !tile.interactable(player.getTeam())) return;
|
||||
if(!Units.canInteract(player, tile)) return;
|
||||
|
||||
int removed = tile.block().removeStack(tile, item, amount);
|
||||
|
||||
@@ -59,6 +59,18 @@ public class MenuFragment extends Fragment{
|
||||
if(mobile){
|
||||
parent.fill(c -> c.bottom().left().addButton("", Styles.infot, ui.about::show).size(84, 45));
|
||||
parent.fill(c -> c.bottom().right().addButton("", Styles.discordt, ui.discord::show).size(84, 45));
|
||||
}else if(becontrol.active()){
|
||||
parent.fill(c -> c.bottom().right().addImageTextButton("$be.check", Icon.refreshSmall, () -> {
|
||||
ui.loadfrag.show();
|
||||
becontrol.checkUpdate(result -> {
|
||||
ui.loadfrag.hide();
|
||||
if(!result){
|
||||
ui.showInfo("$be.noupdates");
|
||||
}
|
||||
});
|
||||
}).size(200, 60).update(t -> {
|
||||
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
|
||||
}));
|
||||
}
|
||||
|
||||
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build + (Version.revision == 0 ? "" : "." + Version.revision));
|
||||
|
||||
114
core/src/mindustry/ui/fragments/MinimapFragment.java
Normal file
114
core/src/mindustry/ui/fragments/MinimapFragment.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package mindustry.ui.fragments;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MinimapFragment extends Fragment{
|
||||
private boolean shown;
|
||||
private float panx, pany, zoom = 1f, lastZoom = -1;
|
||||
private float baseSize = Scl.scl(5f);
|
||||
private Element elem;
|
||||
|
||||
@Override
|
||||
public void build(Group parent){
|
||||
elem = parent.fill((x, y, w, h) -> {
|
||||
w = Core.graphics.getWidth();
|
||||
h = Core.graphics.getHeight();
|
||||
float size = baseSize * zoom * world.width();
|
||||
|
||||
Draw.color(Color.black);
|
||||
Fill.crect(x, y, w, h);
|
||||
|
||||
if(renderer.minimap.getTexture() != null){
|
||||
Draw.color();
|
||||
float ratio = (float)renderer.minimap.getTexture().getHeight() / renderer.minimap.getTexture().getWidth();
|
||||
TextureRegion reg = Draw.wrap(renderer.minimap.getTexture());
|
||||
Draw.rect(reg, w/2f + panx*zoom, h/2f + pany*zoom, size, size * ratio);
|
||||
renderer.minimap.drawEntities(w/2f + panx*zoom - size/2f, h/2f + pany*zoom - size/2f * ratio, size, size * ratio, zoom, true);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
elem.visible(() -> shown);
|
||||
elem.update(() -> {
|
||||
elem.requestKeyboard();
|
||||
elem.requestScroll();
|
||||
elem.setFillParent(true);
|
||||
elem.setBounds(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
|
||||
if(Core.input.keyTap(Binding.menu)){
|
||||
shown = false;
|
||||
}
|
||||
});
|
||||
elem.touchable(Touchable.enabled);
|
||||
|
||||
elem.addListener(new ElementGestureListener(){
|
||||
|
||||
@Override
|
||||
public void zoom(InputEvent event, float initialDistance, float distance){
|
||||
if(lastZoom < 0){
|
||||
lastZoom = zoom;
|
||||
}
|
||||
|
||||
zoom = Mathf.clamp(distance / initialDistance * lastZoom, 0.25f, 10f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pan(InputEvent event, float x, float y, float deltaX, float deltaY){
|
||||
panx += deltaX / zoom;
|
||||
pany += deltaY / zoom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
super.touchDown(event, x, y, pointer, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
lastZoom = zoom;
|
||||
}
|
||||
});
|
||||
|
||||
elem.addListener(new InputListener(){
|
||||
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY){
|
||||
zoom = Mathf.clamp(zoom - amountY / 10f * zoom, 0.25f, 10f);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
parent.fill(t -> {
|
||||
t.setFillParent(true);
|
||||
t.visible(() -> shown);
|
||||
t.update(() -> t.setBounds(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight()));
|
||||
|
||||
t.add("$minimap").style(Styles.outlineLabel).pad(10f);
|
||||
t.row();
|
||||
t.add().growY();
|
||||
t.row();
|
||||
t.addImageTextButton("$back", Icon.backSmall, () -> shown = false).size(220f, 60f).pad(10f);
|
||||
});
|
||||
}
|
||||
|
||||
public boolean shown(){
|
||||
return shown;
|
||||
}
|
||||
|
||||
public void toggle(){
|
||||
shown = !shown;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public class PlayerListFragment extends Fragment{
|
||||
t.addImageButton(Icon.zoomSmall, Styles.clearPartiali, () -> Call.onAdminRequest(user, AdminAction.trace));
|
||||
|
||||
}).padRight(12).size(bs + 10f, bs);
|
||||
}else if((!user.isLocal && !user.isAdmin) && net.client() && playerGroup.size() >= 3){ //votekick
|
||||
}else if((!user.isLocal && !user.isAdmin) && net.client() && playerGroup.size() >= 3 && player.getTeam() != user.getTeam()){ //votekick
|
||||
button.add().growY();
|
||||
|
||||
button.addImageButton(Icon.banSmall, Styles.clearPartiali,
|
||||
|
||||
@@ -45,7 +45,7 @@ public class ScriptConsoleFragment extends Table{
|
||||
font = Fonts.def;
|
||||
|
||||
visible(() -> {
|
||||
if(input.keyTap(Binding.console) && !Vars.net.client() && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null)){
|
||||
if(input.keyTap(Binding.console) && (scene.getKeyboardFocus() == chatfield || scene.getKeyboardFocus() == null)){
|
||||
shown = !shown;
|
||||
if(shown && !open && enableConsole){
|
||||
toggle();
|
||||
@@ -53,7 +53,7 @@ public class ScriptConsoleFragment extends Table{
|
||||
clearChatInput();
|
||||
}
|
||||
|
||||
return shown && !Vars.net.active();
|
||||
return shown && Vars.net.active();
|
||||
});
|
||||
|
||||
update(() -> {
|
||||
|
||||
@@ -55,6 +55,8 @@ public class Block extends BlockStorage{
|
||||
public boolean rotate;
|
||||
/** whether you can break this with rightclick */
|
||||
public boolean breakable;
|
||||
/** whether to add this block to brokenblocks */
|
||||
public boolean rebuildable = true;
|
||||
/** whether this floor can be placed on. */
|
||||
public boolean placeableOn = true;
|
||||
/** whether this block has insulating properties. */
|
||||
|
||||
@@ -257,7 +257,7 @@ public class Tile implements Position, TargetTrait{
|
||||
}
|
||||
|
||||
public boolean solid(){
|
||||
return block.solid || block.isSolidFor(this) || (isLinked() && link().solid());
|
||||
return block.solid || block.isSolidFor(this) || (isLinked() && link() != this && link().solid());
|
||||
}
|
||||
|
||||
public boolean breakable(){
|
||||
@@ -376,7 +376,7 @@ public class Tile implements Position, TargetTrait{
|
||||
return state.teams.canInteract(team, getTeam());
|
||||
}
|
||||
|
||||
public Item drop(){
|
||||
public @Nullable Item drop(){
|
||||
return overlay == Blocks.air || overlay.itemDrop == null ? floor.itemDrop : overlay.itemDrop;
|
||||
}
|
||||
|
||||
|
||||
@@ -171,9 +171,9 @@ public class BuildBlock extends Block{
|
||||
return;
|
||||
}
|
||||
|
||||
if(entity.previous == null) return;
|
||||
if(entity.previous == null || entity.cblock == null) return;
|
||||
|
||||
if(Core.atlas.isFound(entity.previous.icon(mindustry.ui.Cicon.full))){
|
||||
if(Core.atlas.isFound(entity.previous.icon(Cicon.full))){
|
||||
Draw.rect(entity.previous.icon(Cicon.full), tile.drawx(), tile.drawy(), entity.previous.rotate ? tile.rotation() * 90 : 0);
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@ public class BuildBlock extends Block{
|
||||
if(cblock != null){
|
||||
ItemStack[] requirements = cblock.requirements;
|
||||
if(requirements.length != accumulator.length || totalAccumulator.length != requirements.length){
|
||||
setDeconstruct(previous);
|
||||
setDeconstruct(cblock);
|
||||
}
|
||||
|
||||
//make sure you take into account that you can't deconstruct more than there is deconstructed
|
||||
@@ -342,12 +342,12 @@ public class BuildBlock extends Block{
|
||||
this.progress = 1f;
|
||||
if(previous.buildCost >= 0.01f){
|
||||
this.cblock = previous;
|
||||
this.accumulator = new float[previous.requirements.length];
|
||||
this.totalAccumulator = new float[previous.requirements.length];
|
||||
this.buildCost = previous.buildCost * state.rules.buildCostMultiplier;
|
||||
}else{
|
||||
this.buildCost = 20f; //default no-requirement build cost is 20
|
||||
}
|
||||
this.accumulator = new float[previous.requirements.length];
|
||||
this.totalAccumulator = new float[previous.requirements.length];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.math.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.net;
|
||||
@@ -20,7 +21,7 @@ public class RespawnBlock{
|
||||
|
||||
Draw.reset();
|
||||
if(player != null){
|
||||
TextureRegion region = player.getIconRegion();
|
||||
TextureRegion region = to.icon(Cicon.full);
|
||||
|
||||
Draw.color(0f, 0f, 0f, 0.4f * progress);
|
||||
Draw.rect("circle-shadow", tile.drawx(), tile.drawy(), region.getWidth() / 3f, region.getWidth() / 3f);
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -37,7 +38,7 @@ public class ForceProjector extends Block{
|
||||
private static ForceProjector paramBlock;
|
||||
private static ForceEntity paramEntity;
|
||||
private static Cons<AbsorbTrait> shieldConsumer = trait -> {
|
||||
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && paramBlock.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
|
||||
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && Intersector.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
|
||||
trait.absorb();
|
||||
Effects.effect(Fx.absorb, trait);
|
||||
paramEntity.hit = 1f;
|
||||
@@ -111,17 +112,6 @@ public class ForceProjector extends Block{
|
||||
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, entity.efficiency(), 0.1f);
|
||||
|
||||
/*
|
||||
if(entity.power.status < relativePowerDraw){
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f);
|
||||
entity.power.status = 0f;
|
||||
if(entity.warmup <= 0.09f){
|
||||
entity.broken = true;
|
||||
}
|
||||
}else{
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f);
|
||||
}*/
|
||||
|
||||
if(entity.buildup > 0){
|
||||
float scale = !entity.broken ? cooldownNormal : cooldownBrokenBase;
|
||||
ConsumeLiquidFilter cons = consumes.get(ConsumeType.liquid);
|
||||
@@ -159,13 +149,6 @@ public class ForceProjector extends Block{
|
||||
return (radius + entity.phaseHeat * phaseRadiusBoost) * entity.radscl;
|
||||
}
|
||||
|
||||
boolean isInsideHexagon(float x0, float y0, float d, float x, float y){
|
||||
float dx = Math.abs(x - x0) / d;
|
||||
float dy = Math.abs(y - y0) / d;
|
||||
float a = 0.25f * Mathf.sqrt3;
|
||||
return (dy <= a) && (a * dx + 0.25 * dy <= 0.5 * a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Tile tile){
|
||||
super.draw(tile);
|
||||
|
||||
@@ -26,6 +26,7 @@ public class ShockMine extends Block{
|
||||
solid = false;
|
||||
targetable = false;
|
||||
layer = Layer.overlay;
|
||||
rebuildable = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.Mathf;
|
||||
import arc.math.geom.Geometry;
|
||||
import mindustry.world.Tile;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -31,6 +32,10 @@ public class ExtendingItemBridge extends ItemBridge{
|
||||
ex *= uptime;
|
||||
ey *= uptime;
|
||||
|
||||
float opacity = Core.settings.getInt("bridgeopacity") / 100f;
|
||||
if(Mathf.zero(opacity)) return;
|
||||
Draw.alpha(opacity);
|
||||
|
||||
Lines.stroke(8f);
|
||||
Lines.line(bridgeRegion,
|
||||
tile.worldx() + Geometry.d4[i].x * tilesize / 2f,
|
||||
@@ -50,7 +55,7 @@ public class ExtendingItemBridge extends ItemBridge{
|
||||
Draw.color();
|
||||
|
||||
for(int a = 0; a < arrows; a++){
|
||||
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * uptime);
|
||||
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * uptime * opacity);
|
||||
Draw.rect(arrowRegion,
|
||||
tile.worldx() + Geometry.d4[i].x * (tilesize / 2f + a * 6f + 2) * uptime,
|
||||
tile.worldy() + Geometry.d4[i].y * (tilesize / 2f + a * 6f + 2) * uptime, i * 90f);
|
||||
|
||||
@@ -219,10 +219,13 @@ public class ItemBridge extends Block{
|
||||
Tile other = world.tile(entity.link);
|
||||
if(!linkValid(tile, other)) return;
|
||||
|
||||
float opacity = Core.settings.getInt("bridgeopacity") / 100f;
|
||||
if(Mathf.zero(opacity)) return;
|
||||
|
||||
int i = tile.absoluteRelativeTo(other.x, other.y);
|
||||
|
||||
Draw.color(Color.white, Color.black, Mathf.absin(Time.time(), 6f, 0.07f));
|
||||
Draw.alpha(Math.max(entity.uptime, 0.25f));
|
||||
Draw.alpha(Math.max(entity.uptime, 0.25f) * opacity);
|
||||
|
||||
Draw.rect(endRegion, tile.drawx(), tile.drawy(), i * 90 + 90);
|
||||
Draw.rect(endRegion, other.drawx(), other.drawy(), i * 90 + 270);
|
||||
@@ -242,7 +245,7 @@ public class ItemBridge extends Block{
|
||||
Draw.color();
|
||||
|
||||
for(int a = 0; a < arrows; a++){
|
||||
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * entity.uptime);
|
||||
Draw.alpha(Mathf.absin(a / (float)arrows - entity.time / 100f, 0.1f, 1f) * entity.uptime * opacity);
|
||||
Draw.rect(arrowRegion,
|
||||
tile.worldx() + Geometry.d4[i].x * (tilesize / 2f + a * 4f + time % 4f),
|
||||
tile.worldy() + Geometry.d4[i].y * (tilesize / 2f + a * 4f + time % 4f), i * 90f);
|
||||
|
||||
@@ -58,7 +58,7 @@ public class Junction extends Block{
|
||||
if(dest != null) dest = dest.link();
|
||||
|
||||
//skip blocks that don't want the item, keep waiting until they do
|
||||
if(dest == null || !dest.block().acceptItem(item, dest, tile)){
|
||||
if(dest == null || !dest.block().acceptItem(item, dest, tile) || dest.getTeam() != tile.getTeam()){
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -82,10 +82,9 @@ public class Junction extends Block{
|
||||
JunctionEntity entity = tile.ent();
|
||||
int relative = source.relativeTo(tile.x, tile.y);
|
||||
|
||||
if(entity == null || relative == -1 || !entity.buffer.accepts(relative))
|
||||
return false;
|
||||
if(entity == null || relative == -1 || !entity.buffer.accepts(relative)) return false;
|
||||
Tile to = tile.getNearby(relative);
|
||||
return to != null && to.link().entity != null;
|
||||
return to != null && to.link().entity != null && to.getTeam() == tile.getTeam();
|
||||
}
|
||||
|
||||
class JunctionEntity extends TileEntity{
|
||||
|
||||
@@ -162,6 +162,24 @@ public class MassDriver extends Block{
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
Drawf.dashCircle(x * tilesize, y*tilesize, range, Pal.accent);
|
||||
|
||||
//check if a mass driver is selected while placing this driver
|
||||
if(!control.input.frag.config.isShown()) return;
|
||||
Tile selected = control.input.frag.config.getSelectedTile();
|
||||
if(selected == null || !(selected.block() instanceof MassDriver) || !(selected.dst(x * tilesize, y * tilesize) <= range)) return;
|
||||
|
||||
//if so, draw a dotted line towards it while it is in range
|
||||
float sin = Mathf.absin(Time.time(), 6f, 1f);
|
||||
Tmp.v1.set(x * tilesize + offset(), y * tilesize + offset()).sub(selected.drawx(), selected.drawy()).limit((size / 2f + 1) * tilesize + sin + 0.5f);
|
||||
float x2 = x * tilesize - Tmp.v1.x, y2 = y * tilesize - Tmp.v1.y,
|
||||
x1 = selected.drawx() + Tmp.v1.x, y1 = selected.drawy() + Tmp.v1.y;
|
||||
int segs = (int)(selected.dst(x * tilesize, y * tilesize) / tilesize);
|
||||
|
||||
Lines.stroke(4f, Pal.gray);
|
||||
Lines.dashLine(x1, y1, x2, y2, segs);
|
||||
Lines.stroke(2f, Pal.placing);
|
||||
Lines.dashLine(x1, y1, x2, y2, segs);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -262,6 +280,7 @@ public class MassDriver extends Block{
|
||||
}
|
||||
|
||||
protected boolean shooterValid(Tile tile, Tile other){
|
||||
|
||||
if(other == null) return true;
|
||||
if(!(other.block() instanceof MassDriver)) return false;
|
||||
MassDriverEntity entity = other.ent();
|
||||
@@ -274,7 +293,7 @@ public class MassDriver extends Block{
|
||||
if(entity == null || entity.link == -1) return false;
|
||||
Tile link = world.tile(entity.link);
|
||||
|
||||
return link != null && link.block() instanceof MassDriver && tile.dst(link) <= range;
|
||||
return link != null && link.block() instanceof MassDriver && link.getTeam() == tile.getTeam() && tile.dst(link) <= range;
|
||||
}
|
||||
|
||||
public static class DriverBulletData implements Poolable{
|
||||
|
||||
@@ -81,11 +81,11 @@ public class OverflowGate extends Block{
|
||||
if(to == null) return null;
|
||||
Tile edge = Edges.getFacingEdge(tile, to);
|
||||
|
||||
if(!to.block().acceptItem(item, to, edge) || (to.block() instanceof OverflowGate)){
|
||||
if(!to.block().acceptItem(item, to, edge) || to.getTeam() != tile.getTeam() || (to.block() instanceof OverflowGate)){
|
||||
Tile a = tile.getNearby(Mathf.mod(from - 1, 4));
|
||||
Tile b = tile.getNearby(Mathf.mod(from + 1, 4));
|
||||
boolean ac = a != null && a.block().acceptItem(item, a, edge) && !(a.block() instanceof OverflowGate);
|
||||
boolean bc = b != null && b.block().acceptItem(item, b, edge) && !(b.block() instanceof OverflowGate);
|
||||
boolean ac = a != null && a.block().acceptItem(item, a, edge) && !(a.block() instanceof OverflowGate) && a.getTeam() == tile.getTeam();
|
||||
boolean bc = b != null && b.block().acceptItem(item, b, edge) && !(b.block() instanceof OverflowGate) && b.getTeam() == tile.getTeam();
|
||||
|
||||
if(!ac && !bc){
|
||||
return null;
|
||||
|
||||
@@ -74,7 +74,7 @@ public class Sorter extends Block{
|
||||
public boolean acceptItem(Item item, Tile tile, Tile source){
|
||||
Tile to = getTileTarget(item, tile, source, false);
|
||||
|
||||
return to != null && to.block().acceptItem(item, to, tile);
|
||||
return to != null && to.block().acceptItem(item, to, tile) && to.getTeam() == tile.getTeam();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,6 +46,7 @@ public class NuclearReactor extends PowerGenerator{
|
||||
hasItems = true;
|
||||
hasLiquids = true;
|
||||
entityType = NuclearReactorEntity::new;
|
||||
rebuildable = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,7 +27,7 @@ public class PowerDiode extends Block{
|
||||
public void update(Tile tile){
|
||||
super.update(tile);
|
||||
|
||||
if(tile.front() == null || tile.back() == null || !tile.back().block().hasPower || !tile.front().block().hasPower) return;
|
||||
if(tile.front() == null || tile.back() == null || !tile.back().block().hasPower || !tile.front().block().hasPower || tile.back().getTeam() != tile.front().getTeam()) return;
|
||||
|
||||
PowerGraph backGraph = tile.back().entity.power.graph;
|
||||
PowerGraph frontGraph = tile.front().entity.power.graph;
|
||||
|
||||
@@ -149,14 +149,6 @@ public class GenericCrafter extends Block{
|
||||
return itemCapacity;
|
||||
}
|
||||
|
||||
public Item outputItem(){
|
||||
return outputItem == null ? null : outputItem.item;
|
||||
}
|
||||
|
||||
public Liquid outputLiquid(){
|
||||
return outputLiquid == null ? null : outputLiquid.liquid;
|
||||
}
|
||||
|
||||
public static class GenericCrafterEntity extends TileEntity{
|
||||
public float progress;
|
||||
public float totalProgress;
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.Core;
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.graphics.g2d.TextureRegion;
|
||||
import arc.math.Mathf;
|
||||
import arc.util.*;
|
||||
import mindustry.content.Fx;
|
||||
import mindustry.content.Liquids;
|
||||
import mindustry.entities.Effects;
|
||||
@@ -51,8 +52,8 @@ public class SolidPump extends Pump{
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
bars.add("efficiency", entity -> new Bar(() ->
|
||||
Core.bundle.formatFloat("bar.efficiency",
|
||||
((((SolidPumpEntity)entity).boost + 1f) * ((SolidPumpEntity)entity).warmup) * 100 * percentSolid(entity.tile.x, entity.tile.y), 1),
|
||||
Core.bundle.formatFloat("bar.pumpspeed",
|
||||
((SolidPumpEntity)entity).lastPump / Time.delta() * 60, 1),
|
||||
() -> Pal.ammo,
|
||||
() -> ((SolidPumpEntity)entity).warmup));
|
||||
}
|
||||
@@ -104,11 +105,13 @@ public class SolidPump extends Pump{
|
||||
if(tile.entity.cons.valid() && typeLiquid(tile) < liquidCapacity - 0.001f){
|
||||
float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction * entity.efficiency());
|
||||
tile.entity.liquids.add(result, maxPump);
|
||||
entity.lastPump = maxPump;
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
|
||||
if(Mathf.chance(entity.delta() * updateEffectChance))
|
||||
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
|
||||
}else{
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f);
|
||||
entity.lastPump = 0f;
|
||||
}
|
||||
|
||||
entity.pumpTime += entity.warmup * entity.delta();
|
||||
@@ -153,5 +156,6 @@ public class SolidPump extends Pump{
|
||||
public float warmup;
|
||||
public float pumpTime;
|
||||
public float boost;
|
||||
public float lastPump;
|
||||
}
|
||||
}
|
||||
|
||||
29
core/src/mindustry/world/blocks/sandbox/LiquidVoid.java
Normal file
29
core/src/mindustry/world/blocks/sandbox/LiquidVoid.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package mindustry.world.blocks.sandbox;
|
||||
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class LiquidVoid extends Block{
|
||||
|
||||
public LiquidVoid(String name){
|
||||
super(name);
|
||||
hasLiquids = true;
|
||||
solid = true;
|
||||
update = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
bars.remove("liquid");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptLiquid(Tile tile, Tile source, Liquid liquid, float amount){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLiquid(Tile tile, Tile source, Liquid liquid, float amount){}
|
||||
|
||||
}
|
||||
@@ -151,7 +151,7 @@ public class CoreBlock extends StorageBlock{
|
||||
@Override
|
||||
public void removed(Tile tile){
|
||||
CoreEntity entity = tile.ent();
|
||||
int total = tile.entity.proximity().count(e -> e.entity.items == tile.entity.items);
|
||||
int total = tile.entity.proximity().count(e -> e.entity != null && e.entity.items != null && e.entity.items == tile.entity.items);
|
||||
float fract = 1f / total / state.teams.cores(tile.getTeam()).size;
|
||||
|
||||
tile.entity.proximity().each(e -> isContainer(e) && e.entity.items == tile.entity.items, t -> {
|
||||
|
||||
@@ -73,7 +73,7 @@ public class LaunchPad extends StorageBlock{
|
||||
public void update(Tile tile){
|
||||
TileEntity entity = tile.entity;
|
||||
|
||||
if(world.isZone() && entity.cons.valid() && world.isZone() && entity.items.total() >= itemCapacity && entity.timer.get(timerLaunch, launchTime / entity.timeScale)){
|
||||
if(world.isZone() && entity.cons.valid() && entity.items.total() >= itemCapacity && entity.timer.get(timerLaunch, launchTime / entity.timeScale)){
|
||||
for(Item item : Vars.content.items()){
|
||||
Events.fire(Trigger.itemLaunch);
|
||||
Effects.effect(Fx.padlaunch, tile);
|
||||
|
||||
@@ -112,7 +112,7 @@ public class MechPad extends Block{
|
||||
MechFactoryEntity entity = tile.ent();
|
||||
|
||||
if(entity.player != null){
|
||||
RespawnBlock.drawRespawn(tile, entity.heat, entity.progress, entity.time, entity.player, (!entity.sameMech && entity.player.mech == mech ? mech : Mechs.starter));
|
||||
RespawnBlock.drawRespawn(tile, entity.heat, entity.progress, entity.time, entity.player, (!entity.sameMech && entity.player.mech == mech ? Mechs.starter : mech));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user