diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index 97443f0d3b..2cc928d322 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -1,5 +1,6 @@ package io.anuke.mindustry; +import android.*; import android.app.*; import android.content.*; import android.content.pm.*; @@ -21,6 +22,7 @@ import io.anuke.mindustry.ui.dialogs.*; import java.io.*; import java.lang.System; +import java.util.*; import static io.anuke.mindustry.Vars.*; @@ -68,14 +70,16 @@ public class AndroidLauncher extends AndroidApplication{ @Override public void showFileChooser(boolean open, String extension, Consumer cons){ - if(VERSION.SDK_INT >= 19){ + if(VERSION.SDK_INT >= VERSION_CODES.Q){ Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); + intent.setType(extension.equals("zip") ? "application/zip" : "*/*"); addResultListener(i -> startActivityForResult(intent, i), (code, in) -> { if(code == Activity.RESULT_OK && in != null && in.getData() != null){ Uri uri = in.getData(); + if(uri.getPath().contains("(invalid)")) return; + Core.app.post(() -> Core.app.post(() -> cons.accept(new FileHandle(uri.getPath()){ @Override public InputStream read(){ @@ -97,6 +101,24 @@ public class AndroidLauncher extends AndroidApplication{ }))); } }); + }else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){ + chooser = new FileChooser(open ? "$open" : "$save", file -> file.extension().equalsIgnoreCase(extension), open, file -> { + if(!open){ + cons.accept(file.parent().child(file.nameWithoutExtension() + "." + extension)); + }else{ + cons.accept(file); + } + }); + + ArrayList perms = new ArrayList<>(); + if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ + perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ + perms.add(Manifest.permission.READ_EXTERNAL_STORAGE); + } + requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE); }else{ super.showFileChooser(open, extension, cons); } diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index d0ef17bcad..03c38a2746 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -174,6 +174,7 @@ save.playtime = Playtime: {0} warning = Warning. confirm = Confirm delete = Delete +view.workshop = View In Workshop ok = OK open = Open customize = Customize Rules @@ -202,7 +203,7 @@ wave.enemy = [lightgray]{0} Enemy Remaining loadimage = Load Image saveimage = Save Image unknown = Unknown -custom = Custom +custom = Custom builtin = Built-In map.delete.confirm = Are you sure you want to delete this map? This action cannot be undone! map.random = [accent]Random Map @@ -222,6 +223,7 @@ editor.oregen.info = Ore Generation: editor.mapinfo = Map Info editor.author = Author: editor.description = Description: +editor.nodescription = A map must have a description of at least 4 characters before being published. editor.waves = Waves: editor.rules = Rules: editor.generation = Generation: @@ -288,6 +290,7 @@ editor.resizemap = Resize Map editor.mapname = Map Name: editor.overwrite = [accent]Warning!\nThis overwrites an existing map. editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? +editor.exists = A map with this name already exists. editor.selectmap = Select a map to load: toolmode.replace = Replace diff --git a/core/src/io/anuke/mindustry/ClientLauncher.java b/core/src/io/anuke/mindustry/ClientLauncher.java index e63fc9ac01..fe15d49d88 100644 --- a/core/src/io/anuke/mindustry/ClientLauncher.java +++ b/core/src/io/anuke/mindustry/ClientLauncher.java @@ -40,6 +40,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform batch = new SpriteBatch(); assets = new AssetManager(); assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader()); + assets.load("sprites/error.png", Texture.class); atlas = TextureAtlas.blankAtlas(); Vars.net = new Net(platform.getNet()); diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 720b7d33bf..24807883d1 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -122,8 +122,6 @@ public class Vars implements Loadable{ public static FileHandle saveDirectory; /** data subdirectory used for plugins */ public static FileHandle pluginDirectory; - /** old map file extension, for conversion */ - public static final String oldMapExtension = "mmap"; /** map file extension */ public static final String mapExtension = "msav"; /** save file extension */ @@ -139,7 +137,7 @@ public class Vars implements Loadable{ public static EntityCollisions collisions; public static DefaultWaves defaultWaves; public static LoopControl loops; - public static Platform platform; + public static Platform platform = new Platform(){}; public static Plugins plugins; public static World world; diff --git a/core/src/io/anuke/mindustry/content/StatusEffects.java b/core/src/io/anuke/mindustry/content/StatusEffects.java index cb4035dd0e..fb94a881ce 100644 --- a/core/src/io/anuke/mindustry/content/StatusEffects.java +++ b/core/src/io/anuke/mindustry/content/StatusEffects.java @@ -46,6 +46,7 @@ public class StatusEffects implements ContentList{ if(unit.getTeam() == waveTeam){ Events.fire(Trigger.shock); } + result.set(this, time); })); opposite(() -> burning); }}; diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 4a3d94a3b1..c954235c13 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -371,11 +371,13 @@ public class Control implements ApplicationListener, Loadable{ @Override public void update(){ + //TODO find out why this happens on Android + if(assets == null) return; + saves.update(); + //update and load any requested assets - if(assets != null){ - assets.update(); - } + assets.update(); input.updateController(); diff --git a/core/src/io/anuke/mindustry/core/Platform.java b/core/src/io/anuke/mindustry/core/Platform.java index 7ccb700499..55c57e82a4 100644 --- a/core/src/io/anuke/mindustry/core/Platform.java +++ b/core/src/io/anuke/mindustry/core/Platform.java @@ -2,6 +2,7 @@ package io.anuke.mindustry.core; import io.anuke.arc.*; import io.anuke.arc.Input.*; +import io.anuke.arc.collection.*; import io.anuke.arc.files.*; import io.anuke.arc.function.*; import io.anuke.arc.math.*; @@ -25,6 +26,14 @@ public interface Platform{ /** Steam: Share a map on the workshop.*/ default void publishMap(Map map){} + /** Steam: Return external workshop maps to be loaded.*/ + default Array getExternalMaps(){ + return Array.with(); + } + + /** Steam: View a map listing on the workshop.*/ + default void viewMapListing(Map map){} + /** Steam: Open workshop for maps.*/ default void openWorkshop(){} diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index 5e18483e11..1f5eb0f137 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -325,8 +325,9 @@ public class UI implements ApplicationListener, Loadable{ cont.row(); cont.addImage().width(300f).pad(2).height(4f).color(Color.scarlet); cont.row(); - cont.add(text).pad(2f).growX().wrap(); - buttons.addButton("$ok", this::hide).size(120, 50).pad(4); + cont.add(text).pad(2f).growX().wrap().get().setAlignment(Align.center); + cont.row(); + cont.addButton("$ok", this::hide).size(120, 50).pad(4); }}.show(); } diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java index f73d3909bf..016515c1b0 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -154,10 +154,21 @@ public class MapEditorDialog extends Dialog implements Disposable{ if(steam){ menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> { Map map = save(); - if(map != null){ - platform.publishMap(map); + + if(map == null) return; + + if(map.tags.get("description", "").length() < 4){ + ui.showErrorMessage("$editor.nodescription"); + return; } - }).padTop(-3).size(swidth * 2f + 10, 60f); + + if(!Gamemode.survival.valid(map)){ + ui.showErrorMessage("$map.nospawn"); + return; + } + + platform.publishMap(map); + }).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? "$view.workshop" : "$editor.publish.workshop")); menu.cont.row(); } @@ -276,7 +287,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ }); } - private Map save(){ + public Map save(){ String name = editor.getTags().get("name", "").trim(); editor.getTags().put("rules", JsonIO.write(state.rules)); editor.getTags().remove("width"); diff --git a/core/src/io/anuke/mindustry/editor/MapLoadDialog.java b/core/src/io/anuke/mindustry/editor/MapLoadDialog.java index 6cfaaeca7a..c0567a724c 100644 --- a/core/src/io/anuke/mindustry/editor/MapLoadDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapLoadDialog.java @@ -54,7 +54,7 @@ public class MapLoadDialog extends FloatingDialog{ for(Map map : maps.all()){ TextButton button = new TextButton(map.name(), Styles.togglet); - button.add(new BorderImage(map.texture, 2f).setScaling(Scaling.fit)).size(16 * 4f); + button.add(new BorderImage(map.safeTexture(), 2f).setScaling(Scaling.fit)).size(16 * 4f); button.getCells().reverse(); button.clicked(() -> selected = map); button.getLabelCell().grow().left().padLeft(5f); diff --git a/core/src/io/anuke/mindustry/entities/EntityGroup.java b/core/src/io/anuke/mindustry/entities/EntityGroup.java index ff48a1ad2e..07057d0436 100644 --- a/core/src/io/anuke/mindustry/entities/EntityGroup.java +++ b/core/src/io/anuke/mindustry/entities/EntityGroup.java @@ -18,6 +18,8 @@ public class EntityGroup{ private final Array entityArray = new Array<>(false, 32); private final Array entitiesToRemove = new Array<>(false, 32); private final Array entitiesToAdd = new Array<>(false, 32); + private final Array intersectArray = new Array<>(); + private final Rectangle intersectRect = new Rectangle(); private IntMap map; private QuadTree tree; private Consumer removeListener; @@ -161,6 +163,15 @@ public class EntityGroup{ tree().getIntersect(out, x, y, width, height); } + @SuppressWarnings("unchecked") + public Array intersect(float x, float y, float width, float height){ + intersectArray.clear(); + //don't waste time for empty groups + if(isEmpty()) return intersectArray; + tree().getIntersect(intersectArray, intersectRect.set(x, y, width, height)); + return intersectArray; + } + public QuadTree tree(){ if(!useTree) throw new RuntimeException("This group does not support quadtrees! Enable quadtrees when creating it."); return tree; diff --git a/core/src/io/anuke/mindustry/entities/effect/Lightning.java b/core/src/io/anuke/mindustry/entities/effect/Lightning.java index db8eeb5b1b..83dedb94be 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Lightning.java +++ b/core/src/io/anuke/mindustry/entities/effect/Lightning.java @@ -47,7 +47,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{ } /** Do not invoke! */ - @Remote(called = Loc.server) + @Remote(called = Loc.server, unreliable = true) public static void createLighting(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){ Lightning l = Pools.obtain(Lightning.class, Lightning::new); diff --git a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java index 4a9d8695f3..9d3a92a170 100644 --- a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java +++ b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java @@ -16,6 +16,7 @@ import io.anuke.mindustry.game.*; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.type.*; import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.*; import io.anuke.mindustry.world.blocks.defense.DeflectorWall.*; import io.anuke.mindustry.world.blocks.units.CommandCenter.*; import io.anuke.mindustry.world.blocks.units.UnitFactory.*; @@ -273,7 +274,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ return; } - if(!isFlying() && (world.tileWorld(x, y) != null && world.tileWorld(x, y).solid())){ + if(!isFlying() && (world.tileWorld(x, y) != null && !(world.tileWorld(x, y).block() instanceof BuildBlock) && world.tileWorld(x, y).solid())){ kill(); } diff --git a/core/src/io/anuke/mindustry/entities/type/Unit.java b/core/src/io/anuke/mindustry/entities/type/Unit.java index 3dfd2e0364..63289f53e1 100644 --- a/core/src/io/anuke/mindustry/entities/type/Unit.java +++ b/core/src/io/anuke/mindustry/entities/type/Unit.java @@ -2,6 +2,7 @@ package io.anuke.mindustry.entities.type; import io.anuke.annotations.Annotations.*; import io.anuke.arc.*; +import io.anuke.arc.collection.*; import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.*; @@ -212,15 +213,25 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ float radScl = 1.5f; float fsize = getSize() / radScl; moveVector.setZero(); + float cx = x - fsize/2f, cy = y - fsize/2f; - Units.nearby(x - fsize/2f, y - fsize/2f, fsize, fsize, en -> { - if(en == this || en.isFlying() != isFlying()) return; + for(Team team : Team.all){ + avoid(unitGroups[team.ordinal()].intersect(cx, cy, fsize, fsize)); + } + + avoid(playerGroup.intersect(cx, cy, fsize, fsize)); + velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta()); + } + + private void avoid(Array arr){ + float radScl = 1.5f; + + for(Unit en : arr){ + if(en.isFlying() != 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)); - }); - - velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta()); + } } public @Nullable TileEntity getClosestCore(){ diff --git a/core/src/io/anuke/mindustry/entities/units/Statuses.java b/core/src/io/anuke/mindustry/entities/units/Statuses.java index 62bcec4a2f..b96937bfcb 100644 --- a/core/src/io/anuke/mindustry/entities/units/Statuses.java +++ b/core/src/io/anuke/mindustry/entities/units/Statuses.java @@ -32,12 +32,12 @@ public class Statuses implements Saveable{ if(statuses.size > 0){ //check for opposite effects for(StatusEntry entry : statuses){ - if(entry.effect == null) continue; //extend effect if(entry.effect == effect){ entry.time = Math.max(entry.time, duration); return; }else if(entry.effect.reactsWith(effect)){ //find opposite + globalResult.effect = entry.effect; entry.effect.getTransition(unit, effect, entry.time, duration, globalResult); entry.time = globalResult.time; @@ -84,10 +84,6 @@ public class Statuses implements Saveable{ removals.clear(); for(StatusEntry entry : statuses){ - if(entry.effect == null){ - removals.add(entry); - continue; - } entry.time = Math.max(entry.time - Time.delta(), 0); applied.set(entry.effect.id); diff --git a/core/src/io/anuke/mindustry/game/Tutorial.java b/core/src/io/anuke/mindustry/game/Tutorial.java index d2534c151b..8eb8e9e054 100644 --- a/core/src/io/anuke/mindustry/game/Tutorial.java +++ b/core/src/io/anuke/mindustry/game/Tutorial.java @@ -187,7 +187,7 @@ public class Tutorial{ } },; - protected final String line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name(); + protected String line = ""; protected final Function text; protected Array sentences; protected final BooleanProvider done; @@ -203,7 +203,10 @@ public class Tutorial{ /** displayed tutorial stage text.*/ public String text(){ - if(sentences == null) this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty()); + if(sentences == null){ + this.line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name(); + this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty()); + } String line = sentences.get(control.tutorial.sentence); return line.contains("{") ? text.get(line) : line; } diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index e4e8dadfc7..3ea3302194 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -58,7 +58,7 @@ public abstract class InputHandler implements InputProcessor{ @Remote(targets = Loc.both, forward = true, called = Loc.server) public static void transferInventory(Player player, Tile tile){ - if(!player.timer.get(Player.timerTransfer, 40)) return; + if(player == null || player.timer == null || !player.timer.get(Player.timerTransfer, 40)) return; if(net.server() && (player.item().amount <= 0 || player.isTransferring|| !tile.interactable(player.getTeam()))){ throw new ValidateException(player, "Player cannot transfer an item."); } diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java index 14f86e360f..7564325e1c 100644 --- a/core/src/io/anuke/mindustry/input/MobileInput.java +++ b/core/src/io/anuke/mindustry/input/MobileInput.java @@ -67,33 +67,7 @@ public class MobileInput extends InputHandler implements GestureListener{ public MobileInput(){ Events.on(ClientLoadEvent.class, e -> { - GestureDetector dec = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this){ - boolean clearMouse = false; - - @Override - public boolean touchDown(int x, int y, int pointer, KeyCode button){ - if(Core.scene.hasMouse(x, y)){ - clearMouse = true; - return false; - } - return super.touchDown(x, y, pointer, button); - } - - @Override - public boolean touchDragged(int x, int y, int pointer){ - if(!clearMouse){ - return super.touchDragged(x, y, pointer); - } - return false; - } - - @Override - public boolean touchUp(int x, int y, int pointer, KeyCode button){ - clearMouse = false; - return super.touchUp(x, y, pointer, button); - } - }; - Core.input.getInputProcessors().insert(0, dec); + Core.input.getInputProcessors().add(new GestureDetector(20, 0.5f, 0.4f, 0.15f, this)); }); } diff --git a/core/src/io/anuke/mindustry/maps/Map.java b/core/src/io/anuke/mindustry/maps/Map.java index 92789727dc..94fa841f08 100644 --- a/core/src/io/anuke/mindustry/maps/Map.java +++ b/core/src/io/anuke/mindustry/maps/Map.java @@ -20,6 +20,8 @@ public class Map implements Comparable{ public final FileHandle file; /** Format version. */ public final int version; + /** Whether this map is managed, e.g. downloaded from the Steam workshop.*/ + public boolean workshop; /** Map width/height, shorts. */ public int width, height; /** Preview texture. */ @@ -57,8 +59,12 @@ public class Map implements Comparable{ return Core.settings.getInt("hiscore" + file.nameWithoutExtension(), 0); } + public Texture safeTexture(){ + return texture == null ? Core.assets.get("sprites/error.png") : texture; + } + public FileHandle previewFile(){ - return Vars.mapPreviewDirectory.child(file.nameWithoutExtension() + ".png"); + return Vars.mapPreviewDirectory.child((workshop ? file.parent().name() : file.nameWithoutExtension()) + ".png"); } public FileHandle cacheFile(){ @@ -127,6 +133,8 @@ public class Map implements Comparable{ @Override public int compareTo(Map map){ + int work = -Boolean.compare(workshop, map.workshop); + if(work != 0) return work; int type = -Boolean.compare(custom, map.custom); if(type != 0) return type; int modes = Boolean.compare(Gamemode.pvp.valid(this), Gamemode.pvp.valid(map)); diff --git a/core/src/io/anuke/mindustry/maps/MapPreviewLoader.java b/core/src/io/anuke/mindustry/maps/MapPreviewLoader.java index 262f3dc45f..da229addce 100644 --- a/core/src/io/anuke/mindustry/maps/MapPreviewLoader.java +++ b/core/src/io/anuke/mindustry/maps/MapPreviewLoader.java @@ -6,6 +6,7 @@ import io.anuke.arc.assets.loaders.resolvers.*; import io.anuke.arc.collection.*; import io.anuke.arc.files.*; import io.anuke.arc.graphics.*; +import io.anuke.arc.util.*; import io.anuke.mindustry.*; import io.anuke.mindustry.game.*; @@ -20,7 +21,7 @@ public class MapPreviewLoader extends TextureLoader{ try{ super.loadAsync(manager, fileName, file.sibling(file.nameWithoutExtension()), parameter); }catch(Exception e){ - e.printStackTrace(); + Log.err(e); MapPreviewParameter param = (MapPreviewParameter)parameter; Vars.maps.queueNewPreview(param.map); } @@ -31,11 +32,11 @@ public class MapPreviewLoader extends TextureLoader{ try{ return super.loadSync(manager, fileName, file, parameter); }catch(Throwable e){ - e.printStackTrace(); + Log.err(e); try{ return new Texture(file); }catch(Throwable e2){ - e2.printStackTrace(); + Log.err(e2); return new Texture("sprites/error.png"); } } diff --git a/core/src/io/anuke/mindustry/maps/Maps.java b/core/src/io/anuke/mindustry/maps/Maps.java index 7336e63114..395ceafec6 100644 --- a/core/src/io/anuke/mindustry/maps/Maps.java +++ b/core/src/io/anuke/mindustry/maps/Maps.java @@ -81,6 +81,7 @@ public class Maps{ /** Load all maps. Should be called at application start. */ public void load(){ + //defaults; must work try{ for(String name : defaultMapNames){ FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension); @@ -90,7 +91,27 @@ public class Maps{ throw new RuntimeException(e); } - loadCustomMaps(); + //custom + for(FileHandle file : customMapDirectory.list()){ + try{ + if(file.extension().equalsIgnoreCase(mapExtension)){ + loadMap(file, true); + } + }catch(Exception e){ + Log.err("Failed to load custom map file '{0}'!", file); + Log.err(e); + } + } + + //workshop + for(FileHandle file : platform.getExternalMaps()){ + try{ + loadMap(file, false).workshop = true; + }catch(Exception e){ + Log.err("Failed to load workshop map file '{0}'!", file); + Log.err(e); + } + } } public void reload(){ @@ -174,14 +195,6 @@ public class Maps{ } } - /** Creates a legacy map by converting it to a non-legacy map and pasting it in a temp directory. - * Should be followed up by {@link #importMap(FileHandle)} .*/ - public Map makeLegacyMap(FileHandle file) throws IOException{ - FileHandle dst = tmpDirectory.child("conversion_map." + mapExtension); - LegacyMapIO.convertMap(file, dst); - return MapIO.createMap(dst, true); - } - /** Import a map, then save it. This updates all values and stored data necessary. */ public void importMap(FileHandle file) throws IOException{ FileHandle dest = findFile(); @@ -203,7 +216,6 @@ public class Maps{ if(error[0] != null){ throw new IOException(error[0]); } - } /** Attempts to run the following code; @@ -314,7 +326,7 @@ public class Maps{ for(Map map : maps){ //try to load preview if(map.previewFile().exists()){ - //this may fail, but calls createNewPreview + //this may fail, but calls queueNewPreview Core.assets.load(new AssetDescriptor<>(map.previewFile().path() + "." + mapExtension, Texture.class, new MapPreviewParameter(map))).loaded = t -> map.texture = (Texture)t; try{ @@ -332,7 +344,7 @@ public class Maps{ private void createAllPreviews(){ Core.app.post(() -> { for(Map map : previewList){ - createNewPreview(map, e -> Core.app.post(() -> map.texture = new Texture("sprites/error.png"))); + createNewPreview(map, e -> Core.app.post(() -> map.texture = Core.assets.get("sprites/error.png"))); } previewList.clear(); }); @@ -407,16 +419,4 @@ public class Maps{ return map; } - private void loadCustomMaps(){ - for(FileHandle file : customMapDirectory.list()){ - try{ - if(file.extension().equalsIgnoreCase(mapExtension)){ - loadMap(file, true); - } - }catch(Exception e){ - Log.err("Failed to load custom map file '{0}'!", file); - Log.err(e); - } - } - } } \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/net/CrashSender.java b/core/src/io/anuke/mindustry/net/CrashSender.java index 6970e9521b..cc29071615 100644 --- a/core/src/io/anuke/mindustry/net/CrashSender.java +++ b/core/src/io/anuke/mindustry/net/CrashSender.java @@ -25,8 +25,8 @@ public class CrashSender{ try{ exception.printStackTrace(); - //don't create crash logs for me (anuke) or custom builds, as it's expected - if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return; + //don't create crash logs for custom builds, as it's expected + if(Version.build == -1) return; //attempt to load version regardless if(Version.number == 0){ diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 66482ab729..eddd19c1e6 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -46,7 +46,9 @@ public class Net{ t = t.getCause(); } - String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase(); + String baseError = Strings.getFinalMesage(e); + + String error = baseError == null ? "" : baseError.toLowerCase(); String type = t.getClass().toString().toLowerCase(); boolean isError = false; diff --git a/core/src/io/anuke/mindustry/plugin/Plugins.java b/core/src/io/anuke/mindustry/plugin/Plugins.java index dcd7d8501b..946ec00f4f 100644 --- a/core/src/io/anuke/mindustry/plugin/Plugins.java +++ b/core/src/io/anuke/mindustry/plugin/Plugins.java @@ -67,7 +67,7 @@ public class Plugins{ URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader()); Class main = classLoader.loadClass(meta.main); metas.put(main, meta); - return new LoadedPlugin(jar, zip, (Plugin)main.newInstance(), meta); + return new LoadedPlugin(jar, zip, (Plugin)main.getDeclaredConstructor().newInstance(), meta); } /** Represents a plugin that has been loaded from a jar file.*/ diff --git a/core/src/io/anuke/mindustry/type/StatusEffect.java b/core/src/io/anuke/mindustry/type/StatusEffect.java index 19b662d76c..ae74288758 100644 --- a/core/src/io/anuke/mindustry/type/StatusEffect.java +++ b/core/src/io/anuke/mindustry/type/StatusEffect.java @@ -5,7 +5,7 @@ import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.function.Supplier; import io.anuke.arc.graphics.Color; import io.anuke.arc.math.Mathf; -import io.anuke.arc.util.Time; +import io.anuke.arc.util.*; import io.anuke.mindustry.content.Fx; import io.anuke.mindustry.entities.Effects; import io.anuke.mindustry.entities.Effects.Effect; diff --git a/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java index efbdfa65df..74eb693fec 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java @@ -48,7 +48,7 @@ public class CustomGameDialog extends FloatingDialog{ maps.row(); } - ImageButton image = new ImageButton(new TextureRegion(map.texture), Styles.cleari); + ImageButton image = new ImageButton(new TextureRegion(map.safeTexture()), Styles.cleari); image.margin(5); image.top(); @@ -72,7 +72,7 @@ public class CustomGameDialog extends FloatingDialog{ image.add(img).size(images); - BorderImage border = new BorderImage(map.texture, 3f); + BorderImage border = new BorderImage(map.safeTexture(), 3f); border.setScaling(Scaling.fit); image.replaceImage(border); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/MapPlayDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/MapPlayDialog.java index c55b4f6fb8..b3da2b2f3f 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/MapPlayDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/MapPlayDialog.java @@ -69,7 +69,7 @@ public class MapPlayDialog extends FloatingDialog{ cont.row(); cont.addImageTextButton("$customize", Icon.toolsSmall, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).width(230); cont.row(); - cont.add(new BorderImage(map.texture, 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit); + cont.add(new BorderImage(map.safeTexture(), 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit); //only maps with survival are valid for high scores if(Gamemode.survival.valid(map)){ cont.row(); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java index 956c76bbd5..c6b2dabe60 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java @@ -4,7 +4,6 @@ import io.anuke.arc.*; import io.anuke.arc.graphics.*; import io.anuke.arc.input.*; import io.anuke.arc.math.*; -import io.anuke.arc.scene.event.*; import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.util.*; @@ -53,12 +52,18 @@ public class MapsDialog extends FloatingDialog{ buttons.addImageTextButton("$editor.newmap", Icon.add, () -> { ui.showTextInput("$editor.newmap", "$name", "", text -> { - ui.loadAnd(() -> { + Runnable show = () -> ui.loadAnd(() -> { hide(); ui.editor.show(); ui.editor.editor.getTags().put("name", text); Events.fire(new MapMakeEvent()); }); + + if(maps.byName(text) != null){ + ui.showErrorMessage("$editor.exists"); + }else{ + show.run(); + } }); }).size(210f, 64f); @@ -137,9 +142,9 @@ public class MapsDialog extends FloatingDialog{ button.row(); button.addImage().growX().pad(4).color(Pal.gray); button.row(); - button.stack(new Image(map.texture).setScaling(Scaling.fit), new BorderImage(map.texture).setScaling(Scaling.fit)).size(mapsize - 20f); + button.stack(new Image(map.safeTexture()).setScaling(Scaling.fit), new BorderImage(map.safeTexture()).setScaling(Scaling.fit)).size(mapsize - 20f); button.row(); - button.add(map.custom ? "$custom" : "$builtin").color(Color.gray).padTop(3); + button.add(map.custom ? "$custom" : map.workshop ? "$workshop" : "$builtin").color(Color.gray).padTop(3); i++; } @@ -160,7 +165,7 @@ public class MapsDialog extends FloatingDialog{ float mapsize = Core.graphics.isPortrait() ? 160f : 300f; Table table = dialog.cont; - table.stack(new Image(map.texture).setScaling(Scaling.fit), new BorderImage(map.texture).setScaling(Scaling.fit)).size(mapsize); + table.stack(new Image(map.safeTexture()).setScaling(Scaling.fit), new BorderImage(map.safeTexture()).setScaling(Scaling.fit)).size(mapsize); table.table(Styles.black, desc -> { desc.top(); @@ -199,13 +204,17 @@ public class MapsDialog extends FloatingDialog{ } }).fillX().height(54f).marginLeft(10); - table.addImageTextButton("$delete", Icon.trash16Small, () -> { - ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> { - maps.removeMap(map); - dialog.hide(); - setup(); - }); - }).fillX().height(54f).marginLeft(10).disabled(!map.custom).touchable(map.custom ? Touchable.enabled : Touchable.disabled); + table.addImageTextButton(map.workshop ? "$view.workshop" : "$delete", map.workshop ? Icon.linkSmall : Icon.trash16Small, () -> { + if(map.workshop){ + platform.viewMapListing(map); + }else{ + ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> { + maps.removeMap(map); + dialog.hide(); + setup(); + }); + } + }).fillX().height(54f).marginLeft(10).disabled(!map.workshop && !map.custom); dialog.show(); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java b/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java index fc80e13ac8..0f1b31c3ad 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/BlockInventoryFragment.java @@ -32,7 +32,7 @@ import static io.anuke.mindustry.Vars.*; public class BlockInventoryFragment extends Fragment{ private final static float holdWithdraw = 20f; - private Table table; + private Table table = new Table(); private Tile tile; private float holdTime = 0f; private boolean holding; @@ -52,7 +52,6 @@ public class BlockInventoryFragment extends Fragment{ @Override public void build(Group parent){ - table = new Table(); table.setName("inventory"); table.setTransform(true); parent.setTransform(true); diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index 37c48d27a0..5196a97119 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -572,9 +572,6 @@ public class HudFragment extends Fragment{ } shown = !shown; - if(flip != null){ - flip.getParent().act(Core.graphics.getDeltaTime()); - } } private void addWaveTable(Button table){ diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/CooledTurret.java b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/CooledTurret.java index 7ed149ba2e..86f15dce93 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/CooledTurret.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/CooledTurret.java @@ -55,7 +55,7 @@ public class CooledTurret extends Turret{ Liquid liquid = entity.liquids.current(); float used = Math.min(Math.min(entity.liquids.get(liquid), maxUsed * Time.delta()), Math.max(0, ((reload - entity.reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed(tile); - entity.reload += (used * liquid.heatCapacity) / liquid.heatCapacity; + entity.reload += used * liquid.heatCapacity * coolantMultiplier; entity.liquids.remove(liquid, used); if(Mathf.chance(0.06 * used)){ diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java index ba3e8dda79..948161dfd1 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java @@ -100,7 +100,7 @@ public abstract class Turret extends Block{ stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks); stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees); - stats.add(BlockStat.reload, 60f / reload * shots, StatUnit.none); + stats.add(BlockStat.reload, 60f / reload, StatUnit.none); stats.add(BlockStat.shots, shots, StatUnit.none); stats.add(BlockStat.targetsAir, targetAir); stats.add(BlockStat.targetsGround, targetGround); diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java b/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java index f0cac836d6..782d94fdda 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/ItemBridge.java @@ -24,7 +24,6 @@ public class ItemBridge extends Block{ protected int timerTransport = timers++; protected int range; protected float transportTime = 2f; - protected IntArray removals = new IntArray(); protected TextureRegion endRegion, bridgeRegion, arrowRegion; private static int lastPlaced = Pos.invalid; @@ -162,21 +161,15 @@ public class ItemBridge extends Block{ entity.time += entity.cycleSpeed * entity.delta(); entity.time2 += (entity.cycleSpeed - 1f) * entity.delta(); - removals.clear(); - IntSetIterator it = entity.incoming.iterator(); - while(it.hasNext){ int i = it.next(); Tile other = world.tile(i); if(!linkValid(tile, other, false)){ - removals.add(i); + it.remove(); } } - for(int j = 0; j < removals.size; j++) - entity.incoming.remove(removals.get(j)); - Tile other = world.tile(entity.link); if(!linkValid(tile, other)){ entity.link = Pos.invalid; @@ -184,7 +177,7 @@ public class ItemBridge extends Block{ entity.uptime = 0f; }else{ - if(entity.cons.valid()){ + if(entity.cons.valid() && (!hasPower || Mathf.isZero(1f - entity.power.satisfaction))){ entity.uptime = Mathf.lerpDelta(entity.uptime, 1f, 0.04f); }else{ entity.uptime = Mathf.lerpDelta(entity.uptime, 0f, 0.02f); diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java index 2bce0d1904..60822e9f08 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -12,7 +12,6 @@ import io.anuke.mindustry.content.*; import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.Effects.*; import io.anuke.mindustry.entities.type.*; -import io.anuke.mindustry.entities.type.Bullet; import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.type.*; @@ -120,26 +119,28 @@ public class MassDriver extends Block{ if( tile.entity.items.total() >= minDistribute && //must shoot minimum amount of items - link.block().itemCapacity - link.entity.items.total() >= minDistribute && //must have minimum amount of space - entity.reload <= 0.0001f //must have reloaded + link.block().itemCapacity - link.entity.items.total() >= minDistribute //must have minimum amount of space ){ MassDriverEntity other = link.entity(); other.waitingShooters.add(tile); - //align to target location - entity.rotation = Mathf.slerpDelta(entity.rotation, targetRotation, rotateSpeed * entity.power.satisfaction); + if(entity.reload <= 0.0001f){ - //fire when it's the first in the queue and angles are ready. - if(other.currentShooter() == tile && + //align to target location + entity.rotation = Mathf.slerpDelta(entity.rotation, targetRotation, rotateSpeed * entity.power.satisfaction); + + //fire when it's the first in the queue and angles are ready. + if(other.currentShooter() == tile && other.state == DriverState.accepting && Angles.near(entity.rotation, targetRotation, 2f) && Angles.near(other.rotation, targetRotation + 180f, 2f)){ - //actually fire - fire(tile, link); - //remove waiting shooters, it's done firing - other.waitingShooters.remove(tile); - //set both states to idle - entity.state = DriverState.idle; - other.state = DriverState.idle; + //actually fire + fire(tile, link); + //remove waiting shooters, it's done firing + other.waitingShooters.remove(tile); + //set both states to idle + entity.state = DriverState.idle; + other.state = DriverState.idle; + } } } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/Router.java b/core/src/io/anuke/mindustry/world/blocks/distribution/Router.java index 382a5dd470..bd92126717 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/Router.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/Router.java @@ -2,6 +2,7 @@ package io.anuke.mindustry.world.blocks.distribution; import io.anuke.arc.collection.Array; import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.*; import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.*; @@ -63,7 +64,7 @@ public class Router extends Block{ for(int i = 0; i < proximity.size; i++){ Tile other = proximity.get((i + counter) % proximity.size); if(set) tile.rotation((byte)((tile.rotation() + 1) % proximity.size)); - if(other == from) continue; + if(other == from && from.block() == Blocks.overflowGate) continue; if(other.block().acceptItem(item, other, Edges.getFacingEdge(tile, other))){ return other; } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java b/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java index 428697d853..eb76325965 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java @@ -127,6 +127,13 @@ public class GenericCrafter extends Block{ } } + @Override + public boolean outputsItems(){ + return outputItem != null; + } + + + @Override public boolean canProduce(Tile tile){ if(outputItem != null && tile.entity.items.get(outputItem.item) >= itemCapacity){ diff --git a/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java b/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java index 5cafa70f57..ec7cd5f616 100644 --- a/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/storage/CoreBlock.java @@ -97,8 +97,10 @@ public class CoreBlock extends StorageBlock{ entity.storageCapacity += other.block().itemCapacity + other.entity.proximity().sum(e -> isContainer(e) ? e.block().itemCapacity : 0); } - for(Item item : content.items()){ - entity.items.set(item, Math.min(entity.items.get(item), entity.storageCapacity)); + if(!world.isGenerating()){ + for(Item item : content.items()){ + entity.items.set(item, Math.min(entity.items.get(item), entity.storageCapacity)); + } } for(Tile other : state.teams.get(tile.getTeam()).cores){ diff --git a/core/src/io/anuke/mindustry/world/blocks/storage/LaunchPad.java b/core/src/io/anuke/mindustry/world/blocks/storage/LaunchPad.java index d432944ea4..0e8d0fc7db 100644 --- a/core/src/io/anuke/mindustry/world/blocks/storage/LaunchPad.java +++ b/core/src/io/anuke/mindustry/world/blocks/storage/LaunchPad.java @@ -77,8 +77,9 @@ public class LaunchPad extends StorageBlock{ for(Item item : Vars.content.items()){ Events.fire(Trigger.itemLaunch); Effects.effect(Fx.padlaunch, tile); - data.addItem(item, entity.items.get(item)); - entity.items.set(item, 0); + int used = Math.min(entity.items.get(item), itemCapacity); + data.addItem(item, used); + entity.items.remove(item, used); } } } diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index 59b665b31b..d215e28d9c 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -7,6 +7,7 @@ import io.anuke.arc.Files.*; import io.anuke.arc.backends.sdl.*; import io.anuke.arc.backends.sdl.jni.*; import io.anuke.arc.collection.*; +import io.anuke.arc.files.*; import io.anuke.arc.function.*; import io.anuke.arc.input.*; import io.anuke.arc.math.*; @@ -48,11 +49,12 @@ public class DesktopLauncher extends ClientLauncher{ setWindowIcon(FileType.Internal, "icons/icon_64.png"); }}); }catch(Throwable e){ - DesktopLauncher.handleCrash(e); + handleCrash(e); } } public DesktopLauncher(String[] args){ + Log.setUseColors(false); Version.init(); boolean useSteam = Version.modifier.equals("steam"); testMobile = Array.with(args).contains("-testMobile"); @@ -72,6 +74,16 @@ public class DesktopLauncher extends ClientLauncher{ if(useSteam){ if(showConsole){ + StringBuilder base = new StringBuilder(); + Log.setLogger(new LogHandler(){ + @Override + public void print(String text, Object... args){ + String out = Log.format(text, false, args); + + base.append(out).append("\n"); + } + }); + Events.on(ClientLoadEvent.class, event -> { Label[] label = {null}; boolean[] visible = {false}; @@ -86,6 +98,7 @@ public class DesktopLauncher extends ClientLauncher{ t.toFront(); }); t.table(Styles.black3, f -> label[0] = f.add("").get()).visible(() -> visible[0]); + label[0].getText().append(base); }); Log.setLogger(new LogHandler(){ @@ -177,12 +190,23 @@ public class DesktopLauncher extends ClientLauncher{ boolean fbgp = badGPU; CrashSender.send(e, file -> { + Throwable cause = Strings.getFinalCause(e); if(!fbgp){ - dialog.accept(() -> message("A crash has occured. It has been saved in:\n" + file.getAbsolutePath() + "\n" + (e.getMessage() == null ? "" : "\n" + e.getMessage()))); + dialog.accept(() -> message("A crash has occured. It has been saved in:\n" + file.getAbsolutePath() + "\n" + cause.getClass().getSimpleName().replace("Exception", "") + (cause.getMessage() == null ? "" : ":\n" + cause.getMessage()))); } }); } + @Override + public Array getExternalMaps(){ + return !steam ? super.getExternalMaps() : SVars.workshop.getMapFiles(); + } + + @Override + public void viewMapListing(Map map){ + SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + map.file.parent().name()); + } + @Override public NetProvider getNet(){ return steam ? SVars.net : new ArcNetImpl(); diff --git a/desktop/src/io/anuke/mindustry/desktop/steam/SWorkshop.java b/desktop/src/io/anuke/mindustry/desktop/steam/SWorkshop.java index be4e1348d7..f233218fd1 100644 --- a/desktop/src/io/anuke/mindustry/desktop/steam/SWorkshop.java +++ b/desktop/src/io/anuke/mindustry/desktop/steam/SWorkshop.java @@ -4,6 +4,7 @@ import com.codedisaster.steamworks.*; import com.codedisaster.steamworks.SteamRemoteStorage.*; import com.codedisaster.steamworks.SteamUGC.*; import io.anuke.arc.*; +import io.anuke.arc.collection.*; import io.anuke.arc.files.*; import io.anuke.arc.util.*; import io.anuke.mindustry.game.EventType.*; @@ -18,8 +19,35 @@ public class SWorkshop implements SteamUGCCallback{ public final SteamUGC ugc = new SteamUGC(this); private Map lastMap; + private Array mapFiles; + + public SWorkshop(){ + int items = ugc.getNumSubscribedItems(); + SteamPublishedFileID[] ids = new SteamPublishedFileID[items]; + ItemInstallInfo info = new ItemInstallInfo(); + ugc.getSubscribedItems(ids); + mapFiles = Array.with(ids).map(f -> { + ugc.getItemInstallInfo(f, info); + return new FileHandle(info.getFolder()); + }).select(f -> f.list().length > 0).map(f -> f.list()[0]); + + Log.info("Fetching {0} subscribed maps.", items); + } + + public Array getMapFiles(){ + return mapFiles; + } public void publishMap(Map map){ + if(map.tags.containsKey("steamid")){ + Log.info("Map already published, redirecting to ID."); + SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + map.tags.get("steamid")); + return; + } + + //update author name when publishing + map.tags.put("author", SVars.net.friends.getPersonaName()); + FloatingDialog dialog = new FloatingDialog("$confirm"); dialog.setFillParent(false); dialog.cont.add("$map.publish.confirm").width(600f).wrap(); @@ -122,6 +150,12 @@ public class SWorkshop implements SteamUGCCallback{ if(needsToAcceptWLA){ SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement"); } + ui.editor.editor.getTags().put("steamid", SteamNativeHandle.getNativeHandle(publishedFileID) + ""); + try{ + ui.editor.save(); + }catch(Exception e){ + Log.err(e); + } Events.fire(new MapPublishEvent()); }else{ ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name())); diff --git a/gradle.properties b/gradle.properties index f686bb2212..f47982435b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=1fda7fc4ed52e00fdbd6b9a5fab9af6add78dc63 +archash=818e26ff093403031fcf31d3424dacb9c646d4b6