Merge; internal changes

This commit is contained in:
Anuken
2020-01-10 11:30:42 -05:00
161 changed files with 7865 additions and 5181 deletions

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -139,7 +139,7 @@ public class Pathfinder implements Runnable{
//stop looping when interrupted externally
return;
}
}catch(Exception e){
}catch(Throwable e){
e.printStackTrace();
}
}

View File

@@ -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));
}};

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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()){

View File

@@ -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();
}
}

View File

@@ -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){

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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());
}};
}
}

View File

@@ -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. */

View File

@@ -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");

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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){

View File

@@ -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. */

View File

@@ -0,0 +1,4 @@
package mindustry.entities;
public class Weapons{
}

View File

@@ -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++){

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -13,7 +13,7 @@ public interface VelocityTrait extends MoveTrait{
}
default float maxVelocity(){
return Float.MAX_VALUE;
return 50f;
}
default float mass(){

View File

@@ -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;

View File

@@ -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);

View File

@@ -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));

View File

@@ -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;
}
}

View File

@@ -32,6 +32,7 @@ public abstract class BaseDrone extends FlyingUnit{
}
};
@Override
public boolean countsAsEnemy(){
return false;
}

View File

@@ -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;
}
}

View File

@@ -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. */

View File

@@ -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{

View File

@@ -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));

View File

@@ -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(){

View File

@@ -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;

View File

@@ -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(){

View File

@@ -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);
}

View File

@@ -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()));

View File

@@ -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));

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View 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 +
'}';
}
}

View File

@@ -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));
}
}
}

View File

@@ -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();

View File

@@ -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
}
}

View 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);
}
});
}
}

View File

@@ -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){

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View 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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();
}
});

View File

@@ -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);

View File

@@ -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,

View File

@@ -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(() -> {

View File

@@ -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);

View File

@@ -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));

View File

@@ -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));

View File

@@ -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);

View File

@@ -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));

View 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;
}
}

View File

@@ -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,

View File

@@ -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(() -> {

View File

@@ -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. */

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -26,6 +26,7 @@ public class ShockMine extends Block{
solid = false;
targetable = false;
layer = Layer.overlay;
rebuildable = false;
}
@Override

View File

@@ -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);

View File

@@ -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);

View File

@@ -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{

View File

@@ -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{

View File

@@ -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;

View File

@@ -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

View File

@@ -46,6 +46,7 @@ public class NuclearReactor extends PowerGenerator{
hasItems = true;
hasLiquids = true;
entityType = NuclearReactorEntity::new;
rebuildable = false;
}
@Override

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View 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){}
}

View File

@@ -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 -> {

View File

@@ -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);

View File

@@ -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));
}
}