Merge branch 'master' into crater

# Conflicts:
#	core/assets/sprites/block_colors.png
#	core/assets/sprites/sprites.atlas
#	core/assets/sprites/sprites.png
#	core/assets/sprites/sprites3.png
#	core/assets/sprites/sprites5.png
#	core/src/mindustry/world/modules/ItemModule.java
This commit is contained in:
Patrick 'Quezler' Mounier
2020-03-05 10:00:02 +01:00
656 changed files with 3579 additions and 1832 deletions

View File

@@ -33,6 +33,8 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
@Override
public void setup(){
Events.fire(new ClientCreateEvent());
Vars.loadLogger();
Vars.loadFileLogger();
Vars.platform = this;

View File

@@ -1129,13 +1129,13 @@ public class Blocks implements ContentList{
differentialGenerator = new SingleTypeGenerator("differential-generator"){{
requirements(Category.power, ItemStack.with(Items.copper, 70, Items.titanium, 50, Items.lead, 100, Items.silicon, 65, Items.metaglass, 50));
powerProduction = 16f;
itemDuration = 120f;
itemDuration = 140f;
hasLiquids = true;
hasItems = true;
size = 3;
consumes.item(Items.pyratite).optional(true, false);
consumes.liquid(Liquids.cryofluid, 0.18f);
consumes.liquid(Liquids.cryofluid, 0.15f);
}};
rtgGenerator = new DecayGenerator("rtg-generator"){{

View File

@@ -3,12 +3,12 @@ package mindustry.core;
import arc.*;
import arc.assets.*;
import arc.audio.*;
import arc.struct.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.input.*;
import arc.math.geom.*;
import arc.scene.ui.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.core.GameState.*;
@@ -114,17 +114,17 @@ public class Control implements ApplicationListener, Loadable{
});
//autohost for pvp maps
Events.on(WorldLoadEvent.class, event -> {
Events.on(WorldLoadEvent.class, event -> app.post(() -> {
if(state.rules.pvp && !net.active()){
try{
net.host(port);
player.isAdmin = true;
}catch(IOException e){
ui.showException("$server.error", e);
Core.app.post(() -> state.set(State.menu));
state.set(State.menu);
}
}
});
}));
Events.on(UnlockEvent.class, e -> ui.hudfrag.showUnlock(e.content));

View File

@@ -16,7 +16,6 @@ import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.blocks.BuildBlock.*;
import mindustry.world.blocks.power.*;
import java.util.*;
@@ -246,7 +245,6 @@ public class Logic implements ApplicationListener{
collisions.updatePhysics(unitGroup);
}
playerGroup.update();
//effect group only contains item transfers in the headless version, update it!

View File

@@ -13,6 +13,7 @@ import mindustry.annotations.Annotations.*;
import mindustry.core.GameState.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.Effects.*;
import mindustry.entities.traits.BuilderTrait.*;
import mindustry.entities.traits.*;
import mindustry.entities.type.*;
@@ -101,6 +102,7 @@ public class NetClient implements ApplicationListener{
state.set(State.menu);
logic.reset();
platform.updateRPC();
player.name = Core.settings.getString("name");
if(quiet) return;
@@ -261,36 +263,61 @@ public class NetClient implements ApplicationListener{
ui.loadfrag.hide();
}
@Remote(variants = Variant.both, unreliable = true)
public static void setHudText(String message){
if(message == null) return;
ui.hudfrag.setHudText(message);
}
@Remote(variants = Variant.both)
public static void hideHudText(){
ui.hudfrag.toggleHudText(false);
}
/** TCP version */
@Remote(variants = Variant.both)
public static void setHudTextReliable(String message){
setHudText(message);
}
@Remote(variants = Variant.both)
public static void onInfoMessage(String message){
if(message == null) return;
ui.showText("", message);
}
//TODO these are commented out to enforce compatibility with 103! uncomment before 104 release
/*
@Remote(variants = Variant.both)
public static void onInfoPopup(String message, float duration, int align, int top, int left, int bottom, int right){
if(message == null) return;
ui.showInfoPopup(message, duration, align, top, left, bottom, right);
}
@Remote(variants = Variant.both)
public static void onLabel(String info, float duration, float worldx, float worldy){
ui.showLabel(info, duration, worldx, worldy);
public static void onLabel(String message, float duration, float worldx, float worldy){
if(message == null) return;
ui.showLabel(message, duration, worldx, worldy);
}
@Remote(variants = Variant.both, unreliable = true)
public static void onEffect(Effect effect, float x, float y, float rotation, Color color){
if(effect == null) return;
Effects.effect(effect, color, x, y, rotation);
}
@Remote(variants = Variant.both)
public static void onEffectReliable(Effect effect, float x, float y, float rotation, Color color){
Effects.effect(effect, color, x, y, rotation);
}*/
onEffect(effect, x, y, rotation, color);
}
@Remote(variants = Variant.both)
public static void onInfoToast(String message, float duration){
if(message == null) return;
ui.showInfoToast(message, duration);
}
@@ -541,6 +568,11 @@ public class NetClient implements ApplicationListener{
}
String getUsid(String ip){
//consistently use the latter part of an IP, if possible
if(ip.contains("/")){
ip = ip.substring(ip.indexOf("/") + 1);
}
if(Core.settings.getString("usid-" + ip, null) != null){
return Core.settings.getString("usid-" + ip, null);
}else{

View File

@@ -8,6 +8,7 @@ import arc.struct.*;
import arc.util.*;
import arc.util.CommandHandler.*;
import arc.util.io.*;
import arc.util.serialization.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.core.GameState.*;
@@ -15,7 +16,6 @@ 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.*;
@@ -48,6 +48,8 @@ public class NetServer implements ApplicationListener{
if(state.rules.pvp){
//find team with minimum amount of players and auto-assign player to that.
TeamData re = state.teams.getActive().min(data -> {
if((state.rules.waveTeam == data.team && state.rules.waves) || !data.team.active()) return Integer.MAX_VALUE;
int count = 0;
for(Player other : players){
if(other.getTeam() == data.team && other != player){
@@ -93,6 +95,16 @@ public class NetServer implements ApplicationListener{
}
String uuid = packet.uuid;
byte[] buuid = Base64Coder.decode(uuid);
CRC32 crc = new CRC32();
crc.update(buuid, 0, 8);
ByteBuffer buff = ByteBuffer.allocate(8);
buff.put(buuid, 8, 8);
buff.position(0);
if(crc.getValue() != buff.getLong()){
con.kick(KickReason.clientOutdated);
return;
}
if(admins.isIPBanned(con.address) || admins.isSubnetBanned(con.address)) return;
@@ -121,7 +133,7 @@ public class NetServer implements ApplicationListener{
return;
}
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit()){
if(admins.getPlayerLimit() > 0 && playerGroup.size() >= admins.getPlayerLimit() && !netServer.admins.isAdmin(uuid, packet.usid)){
con.kick(KickReason.playerLimit);
return;
}
@@ -207,6 +219,11 @@ public class NetServer implements ApplicationListener{
player.color.set(packet.color);
player.color.a = 1f;
//save admin ID but don't overwrite it
if(!player.isAdmin && !info.admin){
info.adminUsid = packet.usid;
}
try{
writeBuffer.position(0);
player.write(outputBuffer);
@@ -313,7 +330,7 @@ public class NetServer implements ApplicationListener{
votes += d;
voted.addAll(player.uuid, admins.getInfo(player.uuid).lastIP);
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted to kick[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
Call.sendMessage(Strings.format("[orange]{0}[lightgray] has voted on kicking[orange] {1}[].[accent] ({2}/{3})\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
player.name, target.name, votes, votesRequired()));
}

View File

@@ -270,7 +270,7 @@ public class Renderer implements ApplicationListener{
drawAllTeams(true);
Draw.flush();
if(bloom != null && !pixelator.enabled()){
if(bloom != null){
bloom.capture();
}
@@ -278,7 +278,7 @@ public class Renderer implements ApplicationListener{
effectGroup.draw();
Draw.flush();
if(bloom != null && !pixelator.enabled()){
if(bloom != null){
bloom.render();
}
@@ -417,11 +417,6 @@ public class Renderer implements ApplicationListener{
return;
}
boolean hadShields = Core.settings.getBool("animatedshields");
boolean hadWater = Core.settings.getBool("animatedwater");
Core.settings.put("animatedwater", false);
Core.settings.put("animatedshields", false);
FrameBuffer buffer = new FrameBuffer(w, h);
float vpW = camera.width, vpH = camera.height, px = camera.position.x, py = camera.position.y;
@@ -446,16 +441,13 @@ public class Renderer implements ApplicationListener{
}
buffer.end();
Pixmap fullPixmap = new Pixmap(w, h, Pixmap.Format.RGBA8888);
BufferUtils.copy(lines, 0, fullPixmap.getPixels(), lines.length);
Buffers.copy(lines, 0, fullPixmap.getPixels(), lines.length);
Fi file = screenshotDirectory.child("screenshot-" + Time.millis() + ".png");
PixmapIO.writePNG(file, fullPixmap);
fullPixmap.dispose();
ui.showInfoFade(Core.bundle.format("screenshot", file.toString()));
buffer.dispose();
Core.settings.put("animatedwater", hadWater);
Core.settings.put("animatedshields", hadShields);
}
}

View File

@@ -26,8 +26,8 @@ public class Units{
* Validates a target.
* @param target The target to validate
* @param team The team of the thing doing tha targeting
* @param x The X position of the thing doign the targeting
* @param y The Y position of the thing doign the targeting
* @param x The X position of the thing doing the targeting
* @param y The Y position of the thing doing the targeting
* @param range The maximum distance from the target X/Y the targeter can be for it to be valid
* @return whether the target is invalid
*/

View File

@@ -1,14 +1,15 @@
package mindustry.entities.traits;
import arc.*;
import arc.struct.Queue;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.Queue;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.entities.type.TileEntity;
import mindustry.entities.type.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
@@ -21,7 +22,7 @@ import java.io.*;
import java.util.*;
import static mindustry.Vars.*;
import static mindustry.entities.traits.BuilderTrait.BuildDataStatic.*;
import static mindustry.entities.traits.BuilderTrait.BuildDataStatic.tmptr;
/** Interface for units that build things.*/
public interface BuilderTrait extends Entity, TeamTrait{
@@ -74,6 +75,9 @@ public interface BuilderTrait extends Entity, TeamTrait{
buildQueue().removeFirst();
return;
}
}else if(tile.getTeam() != getTeam()){
buildQueue().removeFirst();
return;
}
if(tile.entity instanceof BuildEntity && !current.initialized){

View File

@@ -92,6 +92,10 @@ public class EventType{
}
}
public static class ClientCreateEvent{
}
/** Called when the client game is first loaded. */
public static class ClientLoadEvent{

View File

@@ -49,7 +49,7 @@ public class GlobalData{
for(Fi add : files){
if(add.isDirectory()) continue;
zos.putNextEntry(new ZipEntry(add.path().substring(base.length())));
Streams.copyStream(add.read(), zos);
Streams.copy(add.read(), zos);
zos.closeEntry();
}

View File

@@ -82,6 +82,9 @@ public class Rules{
public boolean lighting = false;
/** Ambient light color, used when lighting is enabled. */
public Color ambientLight = new Color(0.01f, 0.01f, 0.04f, 0.99f);
/** Multiplier for solar panel power output.
negative = use ambient light if lighting is enabled. */
public float solarPowerMultiplier = -1f;
/** team of the player by default */
public Team defaultTeam = Team.sharded;
/** team of the enemy in waves/sectors */

View File

@@ -98,6 +98,7 @@ public class Teams{
/** Do not modify. */
public Array<TeamData> getActive(){
active.removeAll(t -> !t.active());
return active;
}
@@ -167,6 +168,14 @@ public class Teams{
public CoreEntity core(){
return cores.first();
}
@Override
public String toString(){
return "TeamData{" +
"cores=" + cores +
", team=" + team +
'}';
}
}
/** Represents a block made by this team that was destroyed somewhere on the map.

View File

@@ -168,7 +168,6 @@ public class BlockRenderer implements Disposable{
shadowEvents.clear();
Draw.proj(camera.projection());
renderer.pixelator.rebind();
}
float ww = world.width() * tilesize, wh = world.height() * tilesize;

View File

@@ -24,6 +24,10 @@ public class LightRenderer{
lights.add(run);
}
public void add(Position pos, float radius, Color color, float opacity){
add(pos.getX(), pos.getY(), radius, color, opacity);
}
public void add(float x, float y, float radius, Color color, float opacity){
if(!enabled()) return;
@@ -180,13 +184,14 @@ public class LightRenderer{
Draw.color();
buffer.beginDraw(Color.clear);
Draw.blend(Blending.normal);
Gl.blendEquationSeparate(Gl.funcAdd, Gl.max);
for(Runnable run : lights){
run.run();
}
Draw.reset();
Draw.blend();
buffer.endDraw();
Gl.blendEquationSeparate(Gl.funcAdd, Gl.funcAdd);
Draw.color();
Shaders.light.ambient.set(state.rules.ambientLight);
@@ -196,4 +201,4 @@ public class LightRenderer{
lights.clear();
}
}
}

View File

@@ -28,10 +28,6 @@ public class Pixelator implements Disposable{
camera.width = (int)camera.width;
camera.height = (int)camera.height;
boolean hadShields = Core.settings.getBool("animatedshields");
boolean hadWater = Core.settings.getBool("animatedwater");
Core.settings.put("animatedwater", false);
Core.settings.put("animatedshields", false);
graphics.clear(0f, 0f, 0f, 1f);
float px = Core.camera.position.x, py = Core.camera.position.y;
@@ -58,17 +54,9 @@ public class Pixelator implements Disposable{
playerGroup.draw(p -> !p.isDead(), Player::drawName);
Core.camera.position.set(px, py);
Core.settings.put("animatedwater", hadWater);
Core.settings.put("animatedshields", hadShields);
renderer.setScale(pre);
}
public void rebind(){
if(enabled()){
buffer.begin();
}
}
public boolean enabled(){
return Core.settings.getBool("pixelate");
}

View File

@@ -255,19 +255,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(lastSchematic == null) return;
ui.showTextInput("$schematic.add", "$name", "", text -> {
Schematic replacement = schematics.all().find(s -> s.name().equals(text));
if(replacement != null){
ui.showConfirm("$confirm", "$schematic.replace", () -> {
schematics.overwrite(replacement, lastSchematic);
ui.showInfoFade("$schematic.saved");
ui.schematics.showInfo(replacement);
});
}else{
lastSchematic.tags.put("name", text);
schematics.add(lastSchematic);
ui.showInfoFade("$schematic.saved");
ui.schematics.showInfo(lastSchematic);
}
lastSchematic.tags.put("name", text);
schematics.add(lastSchematic);
ui.showInfoFade("$schematic.saved");
ui.schematics.showInfo(lastSchematic);
});
}

View File

@@ -3,7 +3,7 @@ package mindustry.mod;
import arc.*;
import arc.assets.*;
import arc.audio.*;
import arc.audio.mock.*;
import arc.mock.*;
import arc.struct.Array;
import arc.struct.*;
import arc.files.*;

View File

@@ -1,8 +1,8 @@
package mindustry.mod;
import arc.audio.*;
import arc.audio.mock.*;
import arc.math.geom.*;
import arc.mock.*;
import arc.util.ArcAnnotate.*;
public class ModLoadingSound implements Sound{

View File

@@ -118,7 +118,7 @@ public class Mods implements Loadable{
private void packSprites(Array<Fi> sprites, LoadedMod mod, boolean prefix){
for(Fi file : sprites){
try(InputStream stream = file.read()){
byte[] bytes = Streams.copyStreamToByteArray(stream, Math.max((int)file.length(), 512));
byte[] bytes = Streams.copyBytes(stream, Math.max((int)file.length(), 512));
Pixmap pixmap = new Pixmap(bytes, 0, bytes.length);
packer.add(getPage(file), (prefix ? mod.name + "-" : "") + file.nameWithoutExtension(), new PixmapRegion(pixmap));
pixmap.dispose();
@@ -317,7 +317,7 @@ public class Mods implements Loadable{
return result;
}
private LoadedMod locateMod(String name){
public LoadedMod locateMod(String name){
return mods.find(mod -> mod.enabled() && mod.name.equals(name));
}
@@ -460,22 +460,25 @@ public class Mods implements Loadable{
eachEnabled(mod -> {
if(mod.root.child("scripts").exists()){
content.setCurrentMod(mod);
mod.scripts = mod.root.child("scripts").findAll(f -> f.extension().equals("js"));
Log.debug("[{0}] Found {1} scripts.", mod.meta.name, mod.scripts.size);
for(Fi file : mod.scripts){
//if there's only one script file, use it (for backwards compatibility); if there isn't, use "main.js"
Array<Fi> allScripts = mod.root.child("scripts").findAll(f -> f.extEquals("js"));
Fi main = allScripts.size == 1 ? allScripts.first() : mod.root.child("scripts").child("main.js");
if(main.exists() && !main.isDirectory()){
try{
if(scripts == null){
scripts = platform.createScripts();
}
scripts.run(mod, file);
scripts.run(mod, main);
}catch(Throwable e){
Core.app.post(() -> {
Log.err("Error loading script {0} for mod {1}.", file.name(), mod.meta.name);
Log.err("Error loading main script {0} for mod {1}.", main.name(), mod.meta.name);
e.printStackTrace();
});
break;
}
}else{
Core.app.post(() -> {
Log.err("No main.js found for mod {0}.", mod.meta.name);
});
}
}
});

View File

@@ -8,6 +8,12 @@ import arc.util.Log.*;
import mindustry.*;
import mindustry.mod.Mods.*;
import org.mozilla.javascript.*;
import org.mozilla.javascript.commonjs.module.*;
import org.mozilla.javascript.commonjs.module.provider.*;
import java.io.*;
import java.net.*;
import java.util.regex.*;
public class Scripts implements Disposable{
private final Array<String> blacklist = Array.with("net", "files", "reflect", "javax", "rhino", "file", "channels", "jdk",
@@ -15,9 +21,9 @@ public class Scripts implements Disposable{
".awt", "socket", "classloader", "oracle", "invoke");
private final Array<String> whitelist = Array.with("mindustry.net");
private final Context context;
private final String wrapper;
private Scriptable scope;
private boolean errored;
private LoadedMod currentMod = null;
public Scripts(){
Time.mark();
@@ -25,9 +31,12 @@ public class Scripts implements Disposable{
context = Vars.platform.getScriptContext();
context.setClassShutter(type -> !blacklist.contains(type.toLowerCase()::contains) || whitelist.contains(type.toLowerCase()::contains));
context.getWrapFactory().setJavaPrimitiveWrap(false);
scope = new ImporterTopLevel(context);
wrapper = Core.files.internal("scripts/wrapper.js").readString();
new RequireBuilder()
.setModuleScriptProvider(new SoftCachingModuleScriptProvider(new ScriptModuleProvider()))
.setSandboxed(true).createRequire(context, scope).install(scope);
if(!run(Core.files.internal("scripts/global.js").readString(), "global.js")){
errored = true;
@@ -68,11 +77,17 @@ public class Scripts implements Disposable{
}
public void run(LoadedMod mod, Fi file){
run(wrapper.replace("$SCRIPT_NAME$", mod.name + "/" + file.nameWithoutExtension()).replace("$CODE$", file.readString()).replace("$MOD_NAME$", mod.name), file.name());
currentMod = mod;
run(file.readString(), file.name());
currentMod = null;
}
private boolean run(String script, String file){
try{
if(currentMod != null){
//inject script info into file (TODO maybe rhino handles this?)
context.evaluateString(scope, "modName = \"" + currentMod.name + "\"\nscriptName = \"" + file + "\"", "initscript.js", 1, null);
}
context.evaluateString(scope, script, file, 1, null);
return true;
}catch(Throwable t){
@@ -85,4 +100,38 @@ public class Scripts implements Disposable{
public void dispose(){
Context.exit();
}
private class ScriptModuleProvider extends UrlModuleSourceProvider{
private Pattern directory = Pattern.compile("^(.+?)/(.+)");
public ScriptModuleProvider(){
super(null, null);
}
@Override
public ModuleSource loadSource(String moduleId, Scriptable paths, Object validator) throws IOException, URISyntaxException{
if(currentMod == null) return null;
return loadSource(moduleId, currentMod.root.child("scripts"), validator);
}
private ModuleSource loadSource(String moduleId, Fi root, Object validator) throws URISyntaxException{
Matcher matched = directory.matcher(moduleId);
if(matched.find()){
LoadedMod required = Vars.mods.locateMod(matched.group(1));
String script = matched.group(2);
if(required == null || root.equals(required.root.child("scripts"))){ // Mod not found, or already using a mod
Fi dir = root.child(matched.group(1));
if(!dir.exists()) return null; // Mod and folder not found
return loadSource(script, dir, validator);
}
return loadSource(script, required.root.child("scripts"), validator);
}
Fi module = root.child(moduleId + ".js");
if(!module.exists() || module.isDirectory()) return null;
return new ModuleSource(
new InputStreamReader(new ByteArrayInputStream((module.readString()).getBytes())),
null, new URI(moduleId), root.file().toURI(), validator);
}
}
}

View File

@@ -230,7 +230,7 @@ public class Administration{
}
/**
* Returns list of all players with admin status
* Returns list of all players which are banned
*/
public Array<PlayerInfo> getBanned(){
Array<PlayerInfo> result = new Array<>();
@@ -568,6 +568,7 @@ public class Administration{
player = null;
type = null;
tile = null;
block = null;
}
}

View File

@@ -181,7 +181,7 @@ public class Packets{
usid = TypeIO.readString(buffer);
mobile = buffer.get() == 1;
color = buffer.getInt();
byte[] idbytes = new byte[8];
byte[] idbytes = new byte[16];
buffer.get(idbytes);
uuid = new String(Base64Coder.encode(idbytes));
int totalMods = buffer.get();

View File

@@ -16,7 +16,7 @@ public class Links{
new LinkEntry("changelog", "https://github.com/Anuken/Mindustry/releases", Icon.list, Pal.accent.cpy()),
new LinkEntry("trello", "https://trello.com/b/aE2tcUwF", Icon.trello, Color.valueOf("026aa7")),
new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Icon.book, Color.valueOf("0f142f")),
new LinkEntry("feathub", "https://feathub.com/Anuken/Mindustry/", Icon.add, Color.valueOf("ebebeb")),
new LinkEntry("feathub", "https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose/", Icon.add, Color.valueOf("ebebeb")),
new LinkEntry("reddit", "https://www.reddit.com/r/Mindustry/", Icon.redditAlien, Color.valueOf("ee593b")),
new LinkEntry("itch.io", "https://anuke.itch.io/mindustry", Icon.itchio, Color.valueOf("fa5c5c")),
new LinkEntry("google-play", "https://play.google.com/store/apps/details?id=io.anuke.mindustry", Icon.googleplay, Color.valueOf("689f38")),

View File

@@ -73,7 +73,6 @@ public class CustomGameDialog extends FloatingDialog{
image.row();
image.add(img).size(images);
BorderImage border = new BorderImage(map.safeTexture(), 3f);
border.setScaling(Scaling.fit);
image.replaceImage(border);

View File

@@ -172,6 +172,7 @@ public class CustomRulesDialog extends FloatingDialog{
number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
title("$rules.title.experimental");
number("$rules.solarpowermultiplier", f -> rules.solarPowerMultiplier = f, () -> rules.solarPowerMultiplier);
check("$rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
main.addButton(b -> {

View File

@@ -242,6 +242,9 @@ public class JoinDialog extends FloatingDialog{
}
void setup(){
local.clear();
remote.clear();
global.clear();
float w = targetWidth();
hosts.clear();
@@ -255,7 +258,6 @@ public class JoinDialog extends FloatingDialog{
pane.setScrollingDisabled(true, false);
setupRemote();
refreshRemote();
cont.clear();
cont.table(t -> {

View File

@@ -40,7 +40,7 @@ public class ModsDialog extends FloatingDialog{
}else{
try{
Fi file = tmpDirectory.child(text.replace("/", "") + ".zip");
Streams.copyStream(result.getResultAsStream(), file.write(false));
Streams.copy(result.getResultAsStream(), file.write(false));
mods.importMod(file);
file.delete();
Core.app.post(() -> {

View File

@@ -1,7 +1,7 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.struct.*;
import arc.files.*;
import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
@@ -10,6 +10,7 @@ import arc.scene.ui.*;
import arc.scene.ui.ImageButton.*;
import arc.scene.ui.TextButton.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.core.GameState.*;
import mindustry.game.*;
@@ -214,14 +215,27 @@ public class SchematicsDialog extends FloatingDialog{
Core.app.setClipboardText(schematics.writeBase64(s));
}).marginLeft(12f);
t.row();
t.addImageTextButton("$schematic.exportfile", Icon.export, style, () -> platform.showFileChooser(false, schematicExtension, file -> {
dialog.hide();
try{
Schematics.write(s, file);
}catch(Exception e){
ui.showException(e);
t.addImageTextButton("$schematic.exportfile", Icon.export, style, () -> {
if(!ios){
platform.showFileChooser(false, schematicExtension, file -> {
dialog.hide();
try{
Schematics.write(s, file);
}catch(Throwable e){
ui.showException(e);
}
});
}else{
dialog.hide();
try{
Fi file = Core.files.local(s.name() + "." + schematicExtension);
Schematics.write(s, file);
platform.shareFile(file);
}catch(Throwable e){
ui.showException(e);
}
}
})).marginLeft(12f);
}).marginLeft(12f);
});
});

View File

@@ -330,7 +330,12 @@ public class SettingsMenuDialog extends SettingsDialog{
if(Shaders.shield != null){
graphics.checkPref("animatedshields", !mobile);
}
graphics.checkPref("bloom", !mobile, val -> renderer.toggleBloom(val));
if(!ios){
graphics.checkPref("bloom", !mobile, val -> renderer.toggleBloom(val));
}else{
Core.settings.put("bloom", false);
}
graphics.checkPref("pixelate", false, val -> {
if(val){
Events.fire(Trigger.enablePixelation);

View File

@@ -231,6 +231,8 @@ public class ChatFragment extends Table{
fadetime += 1f;
fadetime = Math.min(fadetime, messagesShown) + 1f;
if(scrollPos > 0) scrollPos++;
}
private static class ChatMessage{

View File

@@ -43,6 +43,9 @@ public class HudFragment extends Fragment{
private boolean shown = true;
private float dsize = 47.2f;
private String hudText = "";
private boolean showHudText;
private long lastToast;
public void build(Group parent){
@@ -341,6 +344,19 @@ public class HudFragment extends Fragment{
t.add("$saveload").style(Styles.outlineLabel);
});
parent.fill(p -> {
p.top().table(Styles.black3, t -> t.margin(4).label(() -> hudText)
.style(Styles.outlineLabel)).padTop(10).visible(p.color.a >= 0.001f);
p.update(() -> {
p.color.a = Mathf.lerpDelta(p.color.a, Mathf.num(showHudText), 0.2f);
if(state.is(State.menu)){
p.color.a = 0f;
showHudText = false;
}
});
p.touchable(Touchable.disabled);
});
blockfrag.build(parent);
}
@@ -368,6 +384,15 @@ public class HudFragment extends Fragment{
}
}
public void setHudText(String text){
showHudText = true;
hudText = text;
}
public void toggleHudText(boolean shown){
showHudText = shown;
}
private void scheduleToast(Runnable run){
long duration = (int)(3.5 * 1000);
long since = Time.timeSinceMillis(lastToast);

View File

@@ -84,7 +84,7 @@ public interface Autotiler{
default boolean blends(Tile tile, int rotation, int direction){
Tile other = tile.getNearby(Mathf.mod(rotation - direction, 4));
if(other != null) other = other.link();
return other != null && blends(tile, rotation, other.x, other.y, other.rotation(), other.block());
return other != null && other.getTeam() == tile.getTeam() && blends(tile, rotation, other.x, other.y, other.rotation(), other.block());
}
default boolean blendsArmored(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){

View File

@@ -1,31 +1,29 @@
package mindustry.world.blocks;
import arc.struct.*;
import arc.func.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.ui.Cicon;
import static mindustry.Vars.*;
public class ItemSelection{
private static float scrollPos = 0f;
public static void buildItemTable(Table table, Prov<Item> holder, Cons<Item> consumer){
Array<Item> items = content.items();
public static <T extends UnlockableContent> void buildTable(Table table, Array<T> items, Prov<T> holder, Cons<T> consumer){
ButtonGroup<ImageButton> group = new ButtonGroup<>();
group.setMinCheckCount(0);
Table cont = new Table();
cont.defaults().size(38);
cont.defaults().size(40);
int i = 0;
for(Item item : items){
for(T item : items){
if(!data.isUnlocked(item) && world.isZone()) continue;
ImageButton button = cont.addImageButton(Tex.whiteui, Styles.clearToggleTransi, 24, () -> control.input.frag.config.hideConfig()).group(group).get();
@@ -38,6 +36,22 @@ public class ItemSelection{
}
}
table.add(cont);
//add extra blank spaces so it looks nice
if(i % 4 != 0){
int remaining = 4 - (i % 4);
for(int j = 0; j < remaining; j++){
cont.addImage(Styles.black6);
}
}
ScrollPane pane = new ScrollPane(cont, Styles.smallPane);
pane.setScrollingDisabled(true, false);
pane.setScrollYForce(scrollPos);
pane.update(() -> {
scrollPos = pane.getScrollY();
});
pane.setOverscroll(false, false);
table.add(pane).maxHeight(Scl.scl(40 * 5));
}
}

View File

@@ -266,6 +266,7 @@ public class Conveyor extends Block implements Autotiler{
@Override
public void handleStack(Item item, int amount, Tile tile, Unit source){
ConveyorEntity e = tile.ent();
amount = Math.min(amount, itemCapacity - e.len);
for(int i = amount - 1; i >= 0; i--){
e.add(0);

View File

@@ -2,13 +2,15 @@ package mindustry.world.blocks.distribution;
import arc.math.Mathf;
import arc.util.Time;
import mindustry.entities.type.TileEntity;
import mindustry.entities.type.*;
import mindustry.type.Item;
import mindustry.world.*;
import mindustry.world.meta.BlockGroup;
import java.io.*;
import static mindustry.Vars.world;
public class OverflowGate extends Block{
public float speed = 1f;
public boolean invert = false;
@@ -28,6 +30,11 @@ public class OverflowGate extends Block{
return true;
}
@Override
public int acceptStack(Item item, int amount, Tile tile, Unit source){
return 0;
}
@Override
public int removeStack(Tile tile, Item item, int amount){
OverflowGateEntity entity = tile.ent();
@@ -47,6 +54,11 @@ public class OverflowGate extends Block{
}
if(entity.lastItem != null){
if(entity.lastInput == null){
entity.lastItem = null;
return;
}
entity.time += 1f / speed * Time.delta();
Tile target = getTileTarget(tile, entity.lastItem, entity.lastInput, false);
@@ -120,19 +132,24 @@ public class OverflowGate extends Block{
@Override
public byte version(){
return 2;
return 3;
}
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
stream.writeInt(lastInput == null ? Pos.invalid : lastInput.pos());
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
if(revision == 1){
new DirectionalItemBuffer(25, 50f).read(stream);
}else if(revision == 3){
lastInput = world.tile(stream.readInt());
lastItem = items.first();
}
}
}

View File

@@ -14,7 +14,7 @@ import mindustry.world.meta.*;
import java.io.*;
import static mindustry.Vars.content;
import static mindustry.Vars.*;
public class Sorter extends Block{
private static Item lastItem;
@@ -46,6 +46,9 @@ public class Sorter extends Block{
@Override
public void configured(Tile tile, Player player, int value){
tile.<SorterEntity>ent().sortItem = content.item(value);
if(!headless){
renderer.minimap.update(tile);
}
}
@Override
@@ -133,7 +136,7 @@ public class Sorter extends Block{
@Override
public void buildConfiguration(Tile tile, Table table){
SorterEntity entity = tile.ent();
ItemSelection.buildItemTable(table, () -> entity.sortItem, item -> {
ItemSelection.buildTable(table, content.items(), () -> entity.sortItem, item -> {
lastItem = item;
tile.configure(item == null ? -1 : item.id);
});

View File

@@ -1,10 +1,29 @@
package mindustry.world.blocks.power;
import arc.graphics.*;
import arc.graphics.g2d.*;
import mindustry.world.*;
import static mindustry.Vars.tilesize;
public class Battery extends PowerDistributor{
public int topRegion = reg("-top");
public Color emptyLightColor = Color.valueOf("f8c266");
public Color fullLightColor = Color.valueOf("fb9567");
public Battery(String name){
super(name);
outputsPower = true;
consumesPower = true;
}
@Override
public void draw(Tile tile){
Draw.color(emptyLightColor, fullLightColor, tile.entity.power.status);
Fill.square(tile.drawx(), tile.drawy(), tilesize * size / 2f - 1);
Draw.color();
Draw.rect(reg(topRegion), tile.drawx(), tile.drawy());
}
}

View File

@@ -17,7 +17,7 @@ public class SolarGenerator extends PowerGenerator{
@Override
public void update(Tile tile){
tile.<GeneratorEntity>ent().productionEfficiency = state.rules.lighting ? 1f - state.rules.ambientLight.a : 1f;
tile.<GeneratorEntity>ent().productionEfficiency = state.rules.solarPowerMultiplier < 0 ? (state.rules.lighting ? 1f - state.rules.ambientLight.a : 1f) : state.rules.solarPowerMultiplier;
}
@Override

View File

@@ -107,6 +107,7 @@ public class Separator extends Block{
entity.cons.trigger();
if(item != null && entity.items.get(item) < itemCapacity){
useContent(tile, item);
offloadNear(tile, item);
}
}

View File

@@ -111,6 +111,7 @@ public class SolidPump extends Pump{
tile.entity.liquids.add(result, maxPump);
entity.lastPump = maxPump;
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
if(tile.entity.timer.get(timerContentCheck, 10)) useContent(tile, result);
if(Mathf.chance(entity.delta() * updateEffectChance))
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
}else{

View File

@@ -81,7 +81,7 @@ public class ItemSource extends Block{
@Override
public void buildConfiguration(Tile tile, Table table){
ItemSourceEntity entity = tile.ent();
ItemSelection.buildItemTable(table, () -> entity.outputItem, item -> {
ItemSelection.buildTable(table, content.items(), () -> entity.outputItem, item -> {
lastItem = item;
tile.configure(item == null ? -1 : item.id);
});

View File

@@ -1,24 +1,19 @@
package mindustry.world.blocks.sandbox;
import arc.*;
import arc.struct.*;
import arc.graphics.g2d.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.entities.traits.BuilderTrait.*;
import mindustry.entities.type.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.ui.Cicon;
import mindustry.world.*;
import mindustry.world.blocks.*;
import java.io.*;
import static mindustry.Vars.*;
import static mindustry.Vars.content;
public class LiquidSource extends Block{
public static Liquid lastLiquid;
@@ -82,29 +77,10 @@ public class LiquidSource extends Block{
public void buildConfiguration(Tile tile, Table table){
LiquidSourceEntity entity = tile.ent();
Array<Liquid> items = content.liquids();
ButtonGroup<ImageButton> group = new ButtonGroup<>();
group.setMinCheckCount(0);
Table cont = new Table();
for(int i = 0; i < items.size; i++){
final int f = i;
ImageButton button = cont.addImageButton(Tex.clear, Styles.clearToggleTransi, 24, () -> control.input.frag.config.hideConfig()).size(38).group(group).get();
button.changed(() -> {
tile.configure(button.isChecked() ? items.get(f).id : -1);
control.input.frag.config.hideConfig();
lastLiquid = items.get(f);
});
button.getStyle().imageUp = new TextureRegionDrawable(items.get(i).icon(Cicon.medium));
button.setChecked(entity.source == items.get(i));
if(i % 4 == 3){
cont.row();
}
}
table.add(cont);
ItemSelection.buildTable(table, content.liquids(), () -> entity.source, liquid -> {
lastLiquid = liquid;
tile.configure(liquid == null ? -1 : liquid.id);
});
}
@Override

View File

@@ -123,7 +123,7 @@ public class Unloader extends Block{
@Override
public void buildConfiguration(Tile tile, Table table){
UnloaderEntity entity = tile.ent();
ItemSelection.buildItemTable(table, () -> entity.sortItem, item -> {
ItemSelection.buildTable(table, content.items(), () -> entity.sortItem, item -> {
lastItem = item;
tile.configure(item == null ? -1 : item.id);
});

View File

@@ -1,10 +1,9 @@
package mindustry.world.modules;
import mindustry.type.Item;
import mindustry.type.ItemStack;
import mindustry.type.*;
import java.io.*;
import java.util.Arrays;
import java.util.*;
import static mindustry.Vars.content;
@@ -69,7 +68,7 @@ public class ItemModule extends BlockModule{
return total;
}
public Item first(){ // fixme: entangle with take()
public Item first(){
for(int i = 0; i < items.length; i++){
if(items[i] > 0){
return content.item(i);
@@ -81,12 +80,12 @@ public class ItemModule extends BlockModule{
public Item take(){
for(int i = 0; i < items.length; i++){
int index = (i + takeRotation);
if(index >= items.length) index -= items.length; //conditional instead of mod
if(index >= items.length) index -= items.length;
if(items[index] > 0){
items[index] --;
total --;
takeRotation = index + 1;
return content.item(index % items.length);
return content.item(index);
}
}
return null;