Merge branch 'master' of https://github.com/Anuken/Mindustry into mech-rework

# Conflicts:
#	core/assets/sprites/sprites.atlas
#	core/assets/sprites/sprites.png
This commit is contained in:
Anuken
2019-10-20 10:21:35 -04:00
54 changed files with 1643 additions and 336 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -16,6 +16,7 @@ screenshot.invalid = Map too large, potentially not enough memory for screenshot
gameover = Game Over gameover = Game Over
gameover.pvp = The[accent] {0}[] team is victorious! gameover.pvp = The[accent] {0}[] team is victorious!
highscore = [accent]New highscore! highscore = [accent]New highscore!
copied = Copied.
load.sound = Sounds load.sound = Sounds
load.map = Maps load.map = Maps
@@ -24,6 +25,22 @@ load.content = Content
load.system = System load.system = System
load.mod = Mods load.mod = Mods
schematic = Schematic
schematic.add = Save Schematic...
schematics = Schematics
schematic.import = Import Schematic...
schematic.exportfile = Export File
schematic.importfile = Import File
schematic.browseworkshop = Browse Workshop
schematic.copy = Copy to Clipboard
schematic.copy.import = Import from Clipboard
schematic.shareworkshop = Share on Workshop
schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Flip Schematic
schematic.saved = Schematic saved.
schematic.delete.confirm = This schematic will be utterly eradicated.
schematic.rename = Rename Schematic
schematic.info = {0}x{1}, {2} blocks
stat.wave = Waves Defeated:[accent] {0} stat.wave = Waves Defeated:[accent] {0}
stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} stat.enemiesDestroyed = Enemies Destroyed:[accent] {0}
stat.built = Buildings Built:[accent] {0} stat.built = Buildings Built:[accent] {0}
@@ -83,6 +100,8 @@ mod.import.github = Import Github Mod
mod.remove.confirm = This mod will be deleted. mod.remove.confirm = This mod will be deleted.
mod.author = [LIGHT_GRAY]Author:[] {0} mod.author = [LIGHT_GRAY]Author:[] {0}
mod.missing = This save contains mods that you have recently updated or no longer have installed. Save corruption may occur. Are you sure you want to load it?\n[lightgray]Mods:\n{0} mod.missing = This save contains mods that you have recently updated or no longer have installed. Save corruption may occur. Are you sure you want to load it?\n[lightgray]Mods:\n{0}
mod.preview.missing = Before publishing this mod in the workshop, you must add an image preview.\nPlace an image named[accent] preview.png[] into the mod's folder and try again.
mod.folder.missing = Only mods in folder form can be published on the workshop.\nTo convert any mod into a folder, simply unzip its file into a folder and delete the old zip, then restart your game or reload your mods.
about.button = About about.button = About
name = Name: name = Name:
@@ -218,6 +237,7 @@ loading = [accent]Loading...
reloading = [accent]Reloading Mods... reloading = [accent]Reloading Mods...
saving = [accent]Saving... saving = [accent]Saving...
cancelbuilding = [accent][[{0}][] to clear plan cancelbuilding = [accent][[{0}][] to clear plan
selectschematic = [accent][[{0}][] to select+copy
pausebuilding = [accent][[{0}][] to pause building pausebuilding = [accent][[{0}][] to pause building
resumebuilding = [scarlet][[{0}][] to resume building resumebuilding = [scarlet][[{0}][] to resume building
wave = [accent]Wave {0} wave = [accent]Wave {0}
@@ -238,16 +258,17 @@ map.nospawn = This map does not have any cores for the player to spawn in! Add a
map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] non-orange[] cores to this map in the editor. map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] non-orange[] cores to this map in the editor.
map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor.
map.invalid = Error loading map: corrupted or invalid map file. map.invalid = Error loading map: corrupted or invalid map file.
map.publish.error = Error publishing map: {0} workshop.update = Update Item
map.update = Update Map workshop.error = Error fetching workshop details: {0}
map.load.error = Error fetching workshop details: {0}
map.missing = This map has been deleted or moved.\n[lightgray]The workshop listing has now been automatically un-linked from the map.
map.publish.confirm = Are you sure you want to publish this map?\n\n[lightgray]Make sure you agree to the Workshop EULA first, or your maps will not show up! map.publish.confirm = Are you sure you want to publish this map?\n\n[lightgray]Make sure you agree to the Workshop EULA first, or your maps will not show up!
map.menu = Select what you would like to do with this map. map.menu = Select what you would like to do with this map.
map.changelog = Changelog (optional): changelog = Changelog (optional):
eula = Steam EULA eula = Steam EULA
map.publish = Map published. missing = This item has been deleted or moved.\n[lightgray]The workshop listing has now been automatically un-linked.
map.publishing = [accent]Publishing map... publishing = [accent]Publishing...
publish.confirm = Are you sure you want to publish this?\n\n[lightgray]Make sure you agree to the Workshop EULA first, or your items will not show up!
publish.error = Error publishing item: {0}
editor.brush = Brush editor.brush = Brush
editor.openin = Open In Editor editor.openin = Open In Editor
editor.oregen = Ore Generation editor.oregen = Ore Generation
@@ -627,6 +648,10 @@ keybind.press.axis = Press an axis or key...
keybind.screenshot.name = Map Screenshot keybind.screenshot.name = Map Screenshot
keybind.move_x.name = Move x keybind.move_x.name = Move x
keybind.move_y.name = Move y keybind.move_y.name = Move y
keybind.schematic_select.name = Select Region
keybind.schematic_menu.name = Schematic Menu
keybind.schematic_flip_x.name = Flip Schematic X
keybind.schematic_flip_y.name = Flip Schematic Y
keybind.fullscreen.name = Toggle Fullscreen keybind.fullscreen.name = Toggle Fullscreen
keybind.select.name = Select/Shoot keybind.select.name = Select/Shoot
keybind.diagonal_placement.name = Diagonal Placement keybind.diagonal_placement.name = Diagonal Placement

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1348,6 +1348,13 @@ spore-press-top
orig: 64, 64 orig: 64, 64
offset: 0, 0 offset: 0, 0
index: -1 index: -1
unloader-center
rotate: false
xy: 1735, 855
size: 32, 32
orig: 32, 32
offset: 0, 0
index: -1
arc-heat arc-heat
rotate: false rotate: false
xy: 609, 1262 xy: 609, 1262
@@ -1770,14 +1777,14 @@ scale_marker
index: -1 index: -1
scorch1 scorch1
rotate: false rotate: false
xy: 1735, 787 xy: 1735, 753
size: 28, 100 size: 28, 100
orig: 28, 100 orig: 28, 100
offset: 0, 0 offset: 0, 0
index: -1 index: -1
scorch2 scorch2
rotate: false rotate: false
xy: 1765, 787 xy: 1765, 753
size: 28, 100 size: 28, 100
orig: 28, 100 orig: 28, 100
offset: 0, 0 offset: 0, 0
@@ -4332,7 +4339,7 @@ item-copper-small
index: -1 index: -1
item-copper-tiny item-copper-tiny
rotate: false rotate: false
xy: 1735, 769 xy: 1017, 691
size: 16, 16 size: 16, 16
orig: 16, 16 orig: 16, 16
offset: 0, 0 offset: 0, 0
@@ -4367,7 +4374,7 @@ item-graphite-small
index: -1 index: -1
item-graphite-tiny item-graphite-tiny
rotate: false rotate: false
xy: 1017, 691 xy: 1533, 777
size: 16, 16 size: 16, 16
orig: 16, 16 orig: 16, 16
offset: 0, 0 offset: 0, 0
@@ -4402,7 +4409,7 @@ item-lead-small
index: -1 index: -1
item-lead-tiny item-lead-tiny
rotate: false rotate: false
xy: 1533, 777 xy: 1769, 879
size: 16, 16 size: 16, 16
orig: 16, 16 orig: 16, 16
offset: 0, 0 offset: 0, 0
@@ -4752,7 +4759,7 @@ item-thorium-small
index: -1 index: -1
item-thorium-tiny item-thorium-tiny
rotate: false rotate: false
xy: 1753, 769 xy: 1035, 683
size: 16, 16 size: 16, 16
orig: 16, 16 orig: 16, 16
offset: 0, 0 offset: 0, 0
@@ -4787,7 +4794,7 @@ item-titanium-small
index: -1 index: -1
item-titanium-tiny item-titanium-tiny
rotate: false rotate: false
xy: 1035, 683 xy: 1769, 861
size: 16, 16 size: 16, 16
orig: 16, 16 orig: 16, 16
offset: 0, 0 offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 KiB

After

Width:  |  Height:  |  Size: 717 KiB

View File

@@ -9,6 +9,7 @@ import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.arc.util.async.*;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
@@ -81,6 +82,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
add(netClient = new NetClient()); add(netClient = new NetClient());
assets.load(mods); assets.load(mods);
assets.load(schematics);
assets.loadRun("contentinit", ContentLoader.class, () -> { assets.loadRun("contentinit", ContentLoader.class, () -> {
content.init(); content.init();
@@ -118,10 +120,11 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
for(ApplicationListener listener : modules){ for(ApplicationListener listener : modules){
listener.init(); listener.init();
} }
super.resize(graphics.getWidth(), graphics.getHeight());
mods.each(Mod::init); mods.each(Mod::init);
finished = true; finished = true;
Events.fire(new ClientLoadEvent()); Events.fire(new ClientLoadEvent());
super.resize(graphics.getWidth(), graphics.getHeight());
app.post(() -> app.post(() -> app.post(() -> app.post(() -> super.resize(graphics.getWidth(), graphics.getHeight())))));
} }
}else{ }else{
super.update(); super.update();
@@ -133,11 +136,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
long target = (1000 * 1000000) / targetfps; //target in nanos long target = (1000 * 1000000) / targetfps; //target in nanos
long elapsed = Time.timeSinceNanos(lastTime); long elapsed = Time.timeSinceNanos(lastTime);
if(elapsed < target){ if(elapsed < target){
try{ Threads.sleep((target - elapsed) / 1000000, (int)((target - elapsed) % 1000000));
Thread.sleep((target - elapsed) / 1000000, (int)((target - elapsed) % 1000000));
}catch(InterruptedException ignored){
//ignore
}
} }
} }

View File

@@ -33,6 +33,10 @@ public class Vars implements Loadable{
public static boolean loadLocales = true; public static boolean loadLocales = true;
/** Maximum number of broken blocks. TODO implement or remove.*/ /** Maximum number of broken blocks. TODO implement or remove.*/
public static final int maxBrokenBlocks = 256; public static final int maxBrokenBlocks = 256;
/** Maximum schematic size.*/
public static final int maxSchematicSize = 32;
/** All schematic base64 starts with this string.*/
public static final String schematicBaseStart ="bXNjaAB";
/** IO buffer size. */ /** IO buffer size. */
public static final int bufferSize = 8192; public static final int bufferSize = 8192;
/** global charset, since Android doesn't support the Charsets class */ /** global charset, since Android doesn't support the Charsets class */
@@ -128,10 +132,14 @@ public class Vars implements Loadable{
public static FileHandle saveDirectory; public static FileHandle saveDirectory;
/** data subdirectory used for mods */ /** data subdirectory used for mods */
public static FileHandle modDirectory; public static FileHandle modDirectory;
/** data subdirectory used for schematics */
public static FileHandle schematicDirectory;
/** map file extension */ /** map file extension */
public static final String mapExtension = "msav"; public static final String mapExtension = "msav";
/** save file extension */ /** save file extension */
public static final String saveExtension = "msav"; public static final String saveExtension = "msav";
/** schematic file extension */
public static final String schematicExtension = "msch";
/** list of all locales that can be switched to */ /** list of all locales that can be switched to */
public static Locale[] locales; public static Locale[] locales;
@@ -146,6 +154,7 @@ public class Vars implements Loadable{
public static LoopControl loops; public static LoopControl loops;
public static Platform platform = new Platform(){}; public static Platform platform = new Platform(){};
public static Mods mods; public static Mods mods;
public static Schematics schematics = new Schematics();
public static World world; public static World world;
public static Maps maps; public static Maps maps;
@@ -251,6 +260,7 @@ public class Vars implements Loadable{
saveDirectory = dataDirectory.child("saves/"); saveDirectory = dataDirectory.child("saves/");
tmpDirectory = dataDirectory.child("tmp/"); tmpDirectory = dataDirectory.child("tmp/");
modDirectory = dataDirectory.child("mods/"); modDirectory = dataDirectory.child("mods/");
schematicDirectory = dataDirectory.child("schematics/");
modDirectory.mkdirs(); modDirectory.mkdirs();

View File

@@ -1344,7 +1344,8 @@ public class Blocks implements ContentList{
Items.pyratite, Bullets.pyraFlame Items.pyratite, Bullets.pyraFlame
); );
recoil = 0f; recoil = 0f;
reload = 4f; reload = 5f;
coolantMultiplier = 2f;
range = 60f; range = 60f;
shootCone = 50f; shootCone = 50f;
targetAir = false; targetAir = false;

View File

@@ -99,8 +99,7 @@ public class Bullets implements ContentList{
collidesTiles = false; collidesTiles = false;
splashDamageRadius = 25f; splashDamageRadius = 25f;
splashDamage = 30f; splashDamage = 30f;
incendAmount = 4; status = StatusEffects.burning;
incendSpread = 11f;
frontColor = Pal.lightishOrange; frontColor = Pal.lightishOrange;
backColor = Pal.lightOrange; backColor = Pal.lightOrange;
trailEffect = Fx.incendTrail; trailEffect = Fx.incendTrail;
@@ -228,8 +227,7 @@ public class Bullets implements ContentList{
splashDamage = 10f; splashDamage = 10f;
lifetime = 160f; lifetime = 160f;
hitEffect = Fx.blastExplosion; hitEffect = Fx.blastExplosion;
incendSpread = 10f; status = StatusEffects.burning;
incendAmount = 3;
}}; }};
missileSurge = new MissileBulletType(4.4f, 15, "bullet"){{ missileSurge = new MissileBulletType(4.4f, 15, "bullet"){{
@@ -342,9 +340,7 @@ public class Bullets implements ContentList{
bulletHeight = 12f; bulletHeight = 12f;
frontColor = Pal.lightishOrange; frontColor = Pal.lightishOrange;
backColor = Pal.lightOrange; backColor = Pal.lightOrange;
incendSpread = 3f; status = StatusEffects.burning;
incendAmount = 1;
incendChance = 0.3f;
inaccuracy = 3f; inaccuracy = 3f;
lifetime = 60f; lifetime = 60f;
}}; }};
@@ -354,9 +350,7 @@ public class Bullets implements ContentList{
bulletHeight = 12f; bulletHeight = 12f;
frontColor = Color.valueOf("feb380"); frontColor = Color.valueOf("feb380");
backColor = Color.valueOf("ea8878"); backColor = Color.valueOf("ea8878");
incendSpread = 3f; status = StatusEffects.burning;
incendAmount = 1;
incendChance = 0.3f;
lifetime = 60f; lifetime = 60f;
}}; }};
@@ -385,9 +379,7 @@ public class Bullets implements ContentList{
bulletHeight = 21f; bulletHeight = 21f;
frontColor = Pal.lightishOrange; frontColor = Pal.lightishOrange;
backColor = Pal.lightOrange; backColor = Pal.lightOrange;
incendSpread = 3f; status = StatusEffects.burning;
incendAmount = 2;
incendChance = 0.3f;
shootEffect = Fx.shootBig; shootEffect = Fx.shootBig;
}}; }};

View File

@@ -18,7 +18,7 @@ public class StatusEffects implements ContentList{
none = new StatusEffect(); none = new StatusEffect();
burning = new StatusEffect(){{ burning = new StatusEffect(){{
damage = 0.04f; damage = 0.06f;
effect = Fx.burning; effect = Fx.burning;
opposite(() -> wet, () -> freezing); opposite(() -> wet, () -> freezing);

View File

@@ -317,9 +317,9 @@ public class TechTree implements ContentList{
return node(block, () -> {}); return node(block, () -> {});
} }
public static void create(Block parent, Block block){ public static TechNode create(Block parent, Block block){
TechNode.context = all.find(t -> t.block == parent); TechNode.context = all.find(t -> t.block == parent);
node(block, () -> {}); return node(block, () -> {});
} }
public static class TechNode{ public static class TechNode{

View File

@@ -8,9 +8,9 @@ import io.anuke.arc.function.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.util.serialization.*; import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.ui.dialogs.*;
import static io.anuke.mindustry.Vars.mobile; import static io.anuke.mindustry.Vars.mobile;
@@ -24,27 +24,18 @@ public interface Platform{
default void inviteFriends(){} default void inviteFriends(){}
/** Steam: Share a map on the workshop.*/ /** Steam: Share a map on the workshop.*/
default void publishMap(Map map){} default void publish(Publishable pub){}
/** Steam: Return external workshop maps to be loaded.*/
default Array<FileHandle> getExternalMaps(){
return Array.with();
}
/** Steam: Return external workshop mods to be loaded.*/
default Array<FileHandle> getExternalMods(){
return Array.with();
}
/** Steam: View a map listing on the workshop.*/
default void viewMapListing(Map map){}
/** Steam: View a listing on the workshop.*/ /** Steam: View a listing on the workshop.*/
default void viewListing(String mapid){} default void viewListing(Publishable pub){}
/** Steam: View map workshop info, removing the map ID tag if its listing is deleted. /** Steam: View a listing on the workshop by an ID.*/
* Also presents the option to update the map. */ default void viewListingID(String mapid){}
default void viewMapListingInfo(Map map){}
/** Steam: Return external workshop maps to be loaded.*/
default Array<FileHandle> getWorkshopContent(Class<? extends Publishable> type){
return new Array<>(0);
}
/** Steam: Open workshop for maps.*/ /** Steam: Open workshop for maps.*/
default void openWorkshop(){} default void openWorkshop(){}

View File

@@ -68,6 +68,7 @@ public class UI implements ApplicationListener, Loadable{
public DeployDialog deploy; public DeployDialog deploy;
public TechTreeDialog tech; public TechTreeDialog tech;
public MinimapDialog minimap; public MinimapDialog minimap;
public SchematicsDialog schematics;
public ModsDialog mods; public ModsDialog mods;
public Cursor drillCursor, unloadCursor; public Cursor drillCursor, unloadCursor;
@@ -185,6 +186,13 @@ public class UI implements ApplicationListener, Loadable{
Core.scene.act(); Core.scene.act();
Core.scene.draw(); Core.scene.draw();
if(Core.input.keyTap(KeyCode.MOUSE_LEFT) && Core.scene.getKeyboardFocus() instanceof TextField){
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
if(!(e instanceof TextField)){
Core.scene.setKeyboardFocus(null);
}
}
//draw overlay for buttons //draw overlay for buttons
if(state.rules.tutorial){ if(state.rules.tutorial){
control.tutorial.draw(); control.tutorial.draw();
@@ -225,6 +233,7 @@ public class UI implements ApplicationListener, Loadable{
tech = new TechTreeDialog(); tech = new TechTreeDialog();
minimap = new MinimapDialog(); minimap = new MinimapDialog();
mods = new ModsDialog(); mods = new ModsDialog();
schematics = new SchematicsDialog();
Group group = Core.scene.root; Group group = Core.scene.root;
@@ -296,7 +305,7 @@ public class UI implements ApplicationListener, Loadable{
} }
public void showTextInput(String title, String text, String def, Consumer<String> confirmed){ public void showTextInput(String title, String text, String def, Consumer<String> confirmed){
showTextInput(title, text, 24, def, confirmed); showTextInput(title, text, 32, def, confirmed);
} }
public void showTextInput(String titleText, String text, int textLength, String def, Consumer<String> confirmed){ public void showTextInput(String titleText, String text, int textLength, String def, Consumer<String> confirmed){
@@ -307,7 +316,7 @@ public class UI implements ApplicationListener, Loadable{
Table table = new Table(); Table table = new Table();
table.setFillParent(true); table.setFillParent(true);
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.remove()); table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.remove());
table.top().add(info).padTop(10); table.top().add(info).style(Styles.outlineLabel).padTop(10);
Core.scene.add(table); Core.scene.add(table);
} }

View File

@@ -149,15 +149,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
if(steam){ if(steam){
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> { menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
Map builtin = maps.all().find(m -> m.name().equals(editor.getTags().get("name", "").trim())); Map builtin = maps.all().find(m -> m.name().equals(editor.getTags().get("name", "").trim()));
if(editor.getTags().containsKey("steamid") && builtin != null && !builtin.custom){ if(editor.getTags().containsKey("steamid") && builtin != null && !builtin.custom){
platform.viewListing(editor.getTags().get("steamid")); platform.viewListingID(editor.getTags().get("steamid"));
return; return;
} }
Map map = save(); Map map = save();
if(editor.getTags().containsKey("steamid") && map != null){ if(editor.getTags().containsKey("steamid") && map != null){
platform.viewMapListingInfo(map); platform.viewListing(map);
return; return;
} }
@@ -173,7 +174,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
return; return;
} }
platform.publishMap(map); platform.publish(map);
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? editor.getTags().get("author").equals(player.name) ? "$workshop.listing" : "$view.workshop" : "$editor.publish.workshop")); }).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? editor.getTags().get("author").equals(player.name) ? "$workshop.listing" : "$view.workshop" : "$editor.publish.workshop"));
menu.cont.row(); menu.cont.row();

View File

@@ -47,7 +47,7 @@ public abstract class BulletType extends Content{
/** Status effect applied on hit. */ /** Status effect applied on hit. */
public StatusEffect status = StatusEffects.none; public StatusEffect status = StatusEffects.none;
/** Intensity of applied status effect in terms of duration. */ /** Intensity of applied status effect in terms of duration. */
public float statusDuration = 60 * 1f; public float statusDuration = 60 * 10f;
/** Whether this bullet type collides with tiles. */ /** Whether this bullet type collides with tiles. */
public boolean collidesTiles = true; public boolean collidesTiles = true;
/** Whether this bullet type collides with tiles that are of the same team. */ /** Whether this bullet type collides with tiles that are of the same team. */

View File

@@ -104,7 +104,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
if(current.breaking){ if(current.breaking){
entity.deconstruct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); entity.deconstruct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
}else{ }else{
if(entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier)){ if(entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier, current.hasConfig)){
if(current.hasConfig){ if(current.hasConfig){
Call.onTileConfig(null, tile, current.config); Call.onTileConfig(null, tile, current.config);
} }
@@ -267,18 +267,26 @@ public interface BuilderTrait extends Entity, TeamTrait{
/** Class for storing build requests. Can be either a place or remove request. */ /** Class for storing build requests. Can be either a place or remove request. */
class BuildRequest{ class BuildRequest{
/** Position and rotation of this request. */
public int x, y, rotation; public int x, y, rotation;
/** Block being placed. If null, this is a breaking request.*/
public @Nullable Block block; public @Nullable Block block;
/** Whether this is a break request.*/
public boolean breaking; public boolean breaking;
/** Whether this request comes with a config int. If yes, any blocks placed with this request will not call playerPlaced.*/
public boolean hasConfig; public boolean hasConfig;
/** Config int. Not used unless hasConfig is true.*/
public int config; public int config;
/** Original position, only used in schematics.*/
public int originalX, originalY, originalWidth, originalHeight;
/** Last progress.*/
public float progress; public float progress;
/** Whether construction has started for this request.*/
public boolean initialized; public boolean initialized;
//animation variables /** Visual scale. Used only for rendering.*/
public float animScale = 0f; public float animScale = 0f;
public float animInvalid;
/** This creates a build request. */ /** This creates a build request. */
public BuildRequest(int x, int y, int rotation, Block block){ public BuildRequest(int x, int y, int rotation, Block block){
@@ -302,6 +310,31 @@ public interface BuilderTrait extends Entity, TeamTrait{
} }
public BuildRequest copy(){
BuildRequest copy = new BuildRequest();
copy.x = x;
copy.y = y;
copy.rotation = rotation;
copy.block = block;
copy.breaking = breaking;
copy.hasConfig = hasConfig;
copy.config = config;
copy.originalX = originalX;
copy.originalY = originalY;
copy.progress = progress;
copy.initialized = initialized;
copy.animScale = animScale;
return copy;
}
public BuildRequest original(int x, int y, int originalWidth, int originalHeight){
originalX = x;
originalY = y;
this.originalWidth = originalWidth;
this.originalHeight = originalHeight;
return this;
}
public Rectangle bounds(Rectangle rect){ public Rectangle bounds(Rectangle rect){
if(breaking){ if(breaking){
return rect.set(-100f, -100f, 0f, 0f); return rect.set(-100f, -100f, 0f, 0f);

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.entities.type; package io.anuke.mindustry.entities.type;
import io.anuke.mindustry.*;
import io.anuke.mindustry.entities.EntityGroup; import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.traits.Entity; import io.anuke.mindustry.entities.traits.Entity;
@@ -14,6 +15,14 @@ public abstract class BaseEntity implements Entity{
id = lastid++; id = lastid++;
} }
public int tileX(){
return Vars.world.toTile(x);
}
public int tileY(){
return Vars.world.toTile(y);
}
@Override @Override
public int getID(){ public int getID(){
return id; return id;

View File

@@ -801,6 +801,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
textFadeTime = 0f; textFadeTime = 0f;
target = null; target = null;
moveTarget = null; moveTarget = null;
isShooting = isBoosting = isTransferring = isTyping = false;
spawner = lastSpawner = null; spawner = lastSpawner = null;
health = maxHealth(); health = maxHealth();
mining = null; mining = null;

View File

@@ -39,6 +39,9 @@ public class GlobalData{
files.add(Core.settings.getSettingsFile()); files.add(Core.settings.getSettingsFile());
files.addAll(customMapDirectory.list()); files.addAll(customMapDirectory.list());
files.addAll(saveDirectory.list()); files.addAll(saveDirectory.list());
files.addAll(screenshotDirectory.list());
files.addAll(modDirectory.list());
files.addAll(schematicDirectory.list());
String base = Core.settings.getDataDirectory().path(); String base = Core.settings.getDataDirectory().path();
try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){ try(OutputStream fos = file.write(false, 2048); ZipOutputStream zos = new ZipOutputStream(fos)){

View File

@@ -0,0 +1,111 @@
package io.anuke.mindustry.game;
import io.anuke.arc.collection.*;
import io.anuke.arc.collection.IntIntMap.*;
import io.anuke.arc.files.*;
import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.game.Schematics.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
import static io.anuke.mindustry.Vars.*;
public class Schematic implements Publishable{
public final Array<Stile> tiles;
public StringMap tags;
public int width, height;
public @Nullable FileHandle file;
public Schematic(Array<Stile> tiles, StringMap tags, int width, int height){
this.tiles = tiles;
this.tags = tags;
this.width = width;
this.height = height;
}
public Array<ItemStack> requirements(){
IntIntMap amounts = new IntIntMap();
tiles.each(t -> {
for(ItemStack stack : t.block.requirements){
amounts.getAndIncrement(stack.item.id, 0, stack.amount);
}
});
Array<ItemStack> stacks = new Array<>();
for(Entry ent : amounts.entries()){
stacks.add(new ItemStack(Vars.content.item(ent.key), ent.value));
}
stacks.sort();
return stacks;
}
public String name(){
return tags.get("name", "unknown");
}
public void save(){
schematics.saveChanges(this);
}
@Override
public String getSteamID(){
return tags.get("steamid");
}
@Override
public void addSteamID(String id){
tags.put("steamid", id);
save();
}
@Override
public void removeSteamID(){
tags.remove("steamid");
save();
}
@Override
public String steamTitle(){
return name();
}
@Override
public String steamDescription(){
return null;
}
@Override
public String steamTag(){
return "schematic";
}
@Override
public FileHandle createSteamFolder(String id){
FileHandle directory = tmpDirectory.child("schematic_" + id).child("schematic." + schematicExtension);
file.copyTo(directory);
return directory;
}
@Override
public FileHandle createSteamPreview(String id){
FileHandle preview = tmpDirectory.child("schematic_preview_" + id + ".png");
schematics.savePreview(this, PreviewRes.high, preview);
return preview;
}
public static class Stile{
public @NonNull Block block;
public short x, y;
public int config;
public byte rotation;
public Stile(Block block, int x, int y, int config, byte rotation){
this.block = block;
this.x = (short)x;
this.y = (short)y;
this.config = config;
this.rotation = rotation;
}
}
}

View File

@@ -0,0 +1,380 @@
package io.anuke.mindustry.game;
import io.anuke.arc.*;
import io.anuke.arc.assets.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.graphics.glutils.*;
import io.anuke.arc.util.*;
import io.anuke.arc.util.io.Streams.*;
import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Schematic.*;
import io.anuke.mindustry.input.*;
import io.anuke.mindustry.input.PlaceUtils.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
import java.io.*;
import java.util.zip.*;
import static io.anuke.mindustry.Vars.*;
/** Handles schematics.*/
public class Schematics implements Loadable{
private static final byte[] header = {'m', 's', 'c', 'h'};
private static final byte version = 0;
private static final int padding = 2;
private OptimizedByteArrayOutputStream out = new OptimizedByteArrayOutputStream(1024);
private Array<Schematic> all = new Array<>();
private OrderedMap<Schematic, ObjectMap<PreviewRes, FrameBuffer>> previews = new OrderedMap<>();
private FrameBuffer shadowBuffer;
public Schematics(){
Events.on(DisposeEvent.class, e -> {
previews.each((schem, m) -> m.each((res, buffer) -> buffer.dispose()));
previews.clear();
shadowBuffer.dispose();
});
}
@Override
public void loadSync(){
load();
}
/** Load all schematics in the folder immediately.*/
public void load(){
all.clear();
for(FileHandle file : schematicDirectory.list()){
loadFile(file);
}
platform.getWorkshopContent(Schematic.class).each(this::loadFile);
Core.app.post(() -> {
shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 2, maxSchematicSize + padding + 2);
});
}
private void loadFile(FileHandle file){
if(!file.extension().equals(schematicExtension)) return;
try{
Schematic s = read(file);
all.add(s);
//external file from workshop
if(!s.file.parent().equals(schematicDirectory)){
s.tags.put("steamid", s.file.parent().name());
}
}catch(IOException e){
Log.err(e);
}
}
public Array<Schematic> all(){
return all;
}
public void saveChanges(Schematic s){
if(s.file != null){
try{
write(s, s.file);
}catch(Exception e){
ui.showException(e);
}
}
}
public void savePreview(Schematic schematic, PreviewRes res, FileHandle file){
FrameBuffer buffer = getBuffer(schematic, res);
Draw.flush();
buffer.begin();
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, buffer.getWidth(), buffer.getHeight());
file.writePNG(pixmap);
buffer.end();
}
public Texture getPreview(Schematic schematic, PreviewRes res){
return getBuffer(schematic, res).getTexture();
}
public FrameBuffer getBuffer(Schematic schematic, PreviewRes res){
if(!previews.getOr(schematic, ObjectMap::new).containsKey(res)){
int resolution = res.resolution;
Draw.blend();
Draw.reset();
Time.mark();
Tmp.m1.set(Draw.proj());
Tmp.m2.set(Draw.trans());
FrameBuffer buffer = new FrameBuffer((schematic.width + padding) * resolution, (schematic.height + padding) * resolution);
shadowBuffer.beginDraw(Color.clear);
Draw.trans().idt();
Draw.proj().setOrtho(0, 0, shadowBuffer.getWidth(), shadowBuffer.getHeight());
Draw.color();
schematic.tiles.each(t -> {
int size = t.block.size;
int offsetx = -(size - 1) / 2;
int offsety = -(size - 1) / 2;
for(int dx = 0; dx < size; dx++){
for(int dy = 0; dy < size; dy++){
int wx = t.x + dx + offsetx;
int wy = t.y + dy + offsety;
Fill.square(padding/2f + wx + 0.5f, padding/2f + wy + 0.5f, 0.5f);
}
}
});
shadowBuffer.endDraw();
buffer.beginDraw(Color.clear);
Draw.proj().setOrtho(0, buffer.getHeight(), buffer.getWidth(), -buffer.getHeight());
Tmp.tr1.set(shadowBuffer.getTexture(), 0, 0, schematic.width + padding, schematic.height + padding);
Draw.color(0f, 0f, 0f, 1f);
Draw.rect(Tmp.tr1, buffer.getWidth()/2f, buffer.getHeight()/2f, buffer.getWidth(), -buffer.getHeight());
Draw.color();
Array<BuildRequest> requests = schematic.tiles.map(t -> new BuildRequest(t.x, t.y, t.rotation, t.block).configure(t.config));
Draw.flush();
//scale each request to fit schematic
Draw.trans().scale(resolution / tilesize, resolution / tilesize).translate(tilesize*1.5f, tilesize*1.5f);
//draw requests
requests.each(req -> {
req.animScale = 1f;
req.block.drawRequestRegion(req, requests::each);
});
requests.each(req -> req.block.drawRequestConfigTop(req, requests::each));
Draw.flush();
Draw.trans().idt();
buffer.endDraw();
Draw.proj(Tmp.m1);
Draw.trans(Tmp.m2);
previews.getOr(schematic, ObjectMap::new).put(res, buffer);
Log.info("Time taken: {0}", Time.elapsed());
}
return previews.get(schematic).get(res);
}
/** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */
public Array<BuildRequest> toRequests(Schematic schem, int x, int y){
return schem.tiles.map(t -> new BuildRequest(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block).original(t.x, t.y, schem.width, schem.height).configure(t.config)).removeAll(s -> !s.block.isVisible());
}
/** Adds a schematic to the list, also copying it into the files.*/
public void add(Schematic schematic){
all.add(schematic);
try{
write(schematic, schematicDirectory.child(Time.millis() + "." + schematicExtension));
}catch(IOException e){
Log.err(e);
}
}
public void remove(Schematic s){
all.remove(s);
if(s.file != null){
s.file.delete();
}
}
/** Creates a schematic from a world selection. */
public Schematic create(int x, int y, int x2, int y2){
NormalizeResult result = PlaceUtils.normalizeArea(x, y, x2, y2, 0, false, maxSchematicSize);
x = result.x;
y = result.y;
x2 = result.x2;
y2 = result.y2;
Array<Stile> tiles = new Array<>();
int minx = x2, miny = y2, maxx = x, maxy = y;
boolean found = false;
for(int cx = x; cx <= x2; cx++){
for(int cy = y; cy <= y2; cy++){
Tile linked = world.ltile(cx, cy);
if(linked != null && linked.entity != null && linked.entity.block.isVisible()){
int top = linked.block().size/2;
int bot = linked.block().size % 2 == 1 ? -linked.block().size/2 : -(linked.block().size - 1)/2;
minx = Math.min(linked.x + bot, minx);
miny = Math.min(linked.y + bot, miny);
maxx = Math.max(linked.x + top, maxx);
maxy = Math.max(linked.y + top, maxy);
found = true;
}
}
}
if(found){
x = minx;
y = miny;
x2 = maxx;
y2 = maxy;
}else{
return new Schematic(new Array<>(), new StringMap(), 1, 1);
}
int width = x2 - x + 1, height = y2 - y + 1;
int offsetX = -x, offsetY = -y;
for(int cx = x; cx <= x2; cx++){
for(int cy = y; cy <= y2; cy++){
Tile tile = world.tile(cx, cy);
if(tile != null && tile.entity != null){
int config = tile.entity.config();
if(tile.block().posConfig){
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);
}
tiles.add(new Stile(tile.block(), cx + offsetX, cy + offsetY, config, tile.rotation()));
}
}
}
return new Schematic(tiles, new StringMap(), width, height);
}
/** Converts a schematic to base64. Note that the result of this will always start with 'bXNjaAB'.*/
public String writeBase64(Schematic schematic){
try{
out.reset();
write(schematic, out);
return new String(Base64Coder.encode(out.getBuffer(), out.size()));
}catch(IOException e){
throw new RuntimeException(e);
}
}
/** Loads a schematic from base64. May throw an exception. */
public Schematic readBase64(String schematic) throws IOException{
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
}
//region IO methods
public static Schematic read(FileHandle file) throws IOException{
Schematic s = read(new DataInputStream(file.read(1024)));
if(!s.tags.containsKey("name")){
s.tags.put("name", file.nameWithoutExtension());
}
s.file = file;
return s;
}
public static Schematic read(InputStream input) throws IOException{
for(byte b : header){
if(input.read() != b){
throw new IOException("Not a schematic file (missing header).");
}
}
int ver;
if((ver = input.read()) != version){
throw new IOException("Unknown version: " + ver);
}
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
short width = stream.readShort(), height = stream.readShort();
StringMap map = new StringMap();
byte tags = stream.readByte();
for(int i = 0; i < tags; i++){
map.put(stream.readUTF(), stream.readUTF());
}
IntMap<Block> blocks = new IntMap<>();
byte length = stream.readByte();
for(int i = 0; i < length; i++){
Block block = Vars.content.getByName(ContentType.block, stream.readUTF());
blocks.put(i, block == null ? Blocks.air : block);
}
int total = stream.readInt();
Array<Stile> tiles = new Array<>(total);
for(int i = 0; i < total; i++){
Block block = blocks.get(stream.readByte());
int position = stream.readInt();
int config = stream.readInt();
byte rotation = stream.readByte();
if(block != Blocks.air){
tiles.add(new Stile(block, Pos.x(position), Pos.y(position), config, rotation));
}
}
return new Schematic(tiles, map, width, height);
}
}
public static void write(Schematic schematic, FileHandle file) throws IOException{
write(schematic, file.write(false, 1024));
}
public static void write(Schematic schematic, OutputStream output) throws IOException{
output.write(header);
output.write(version);
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
stream.writeShort(schematic.width);
stream.writeShort(schematic.height);
stream.writeByte(schematic.tags.size);
for(ObjectMap.Entry<String, String> e : schematic.tags.entries()){
stream.writeUTF(e.key);
stream.writeUTF(e.value);
}
OrderedSet<Block> blocks = new OrderedSet<>();
schematic.tiles.each(t -> blocks.add(t.block));
//create dictionary
stream.writeByte(blocks.size);
for(int i = 0; i < blocks.size; i++){
stream.writeUTF(blocks.orderedItems().get(i).name);
}
stream.writeInt(schematic.tiles.size);
//write each tile
for(Stile tile : schematic.tiles){
stream.writeByte(blocks.orderedItems().indexOf(tile.block));
stream.writeInt(Pos.get(tile.x, tile.y));
stream.writeInt(tile.config);
stream.writeByte(tile.rotation);
}
}
}
//endregion
public enum PreviewRes{
low(8), med(8), high(32);
public final int resolution;
PreviewRes(int resolution){
this.resolution = resolution;
}
}
}

View File

@@ -40,6 +40,10 @@ public class Tutorial{
Events.on(BlockInfoEvent.class, event -> events.add("blockinfo")); Events.on(BlockInfoEvent.class, event -> events.add("blockinfo"));
Events.on(DepositEvent.class, event -> events.add("deposit")); Events.on(DepositEvent.class, event -> events.add("deposit"));
Events.on(WithdrawEvent.class, event -> events.add("withdraw")); Events.on(WithdrawEvent.class, event -> events.add("withdraw"));
for(TutorialStage stage : TutorialStage.values()){
stage.load();
}
} }
/** update tutorial state, transition if needed */ /** update tutorial state, transition if needed */
@@ -204,13 +208,17 @@ public class Tutorial{
/** displayed tutorial stage text.*/ /** displayed tutorial stage text.*/
public String text(){ public String text(){
if(sentences == null){ if(sentences == null){
this.line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name(); load();
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
} }
String line = sentences.get(control.tutorial.sentence); String line = sentences.get(control.tutorial.sentence);
return line.contains("{") ? text.get(line) : line; return line.contains("{") ? text.get(line) : line;
} }
void load(){
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());
}
/** called every frame when this stage is active.*/ /** called every frame when this stage is active.*/
void update(){ void update(){

View File

@@ -18,6 +18,10 @@ public enum Binding implements KeyBind{
rotateplaced(KeyCode.R), rotateplaced(KeyCode.R),
diagonal_placement(KeyCode.CONTROL_LEFT), diagonal_placement(KeyCode.CONTROL_LEFT),
pick(KeyCode.MOUSE_MIDDLE), pick(KeyCode.MOUSE_MIDDLE),
schematic_select(KeyCode.F),
schematic_flip_x(KeyCode.Z),
schematic_flip_y(KeyCode.X),
schematic_menu(KeyCode.T),
dash(KeyCode.SHIFT_LEFT), dash(KeyCode.SHIFT_LEFT),
gridMode(KeyCode.BACKTICK), gridMode(KeyCode.BACKTICK),
gridModeShift(KeyCode.ALT_LEFT), gridModeShift(KeyCode.ALT_LEFT),

View File

@@ -6,11 +6,15 @@ import io.anuke.arc.Graphics.Cursor.*;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.arc.scene.*; import io.anuke.arc.scene.*;
import io.anuke.arc.scene.event.*;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
@@ -24,9 +28,9 @@ public class DesktopInput extends InputHandler{
/** Current cursor type. */ /** Current cursor type. */
private Cursor cursorType = SystemCursor.arrow; private Cursor cursorType = SystemCursor.arrow;
/** Position where the player started dragging a line. */ /** Position where the player started dragging a line. */
private int selectX, selectY; private int selectX, selectY, schemX, schemY;
/** Last known line positions.*/ /** Last known line positions.*/
private int lastLineX, lastLineY; private int lastLineX, lastLineY, schematicX, schematicY;
/** Whether selecting mode is active. */ /** Whether selecting mode is active. */
private PlaceMode mode; private PlaceMode mode;
/** Animation scale for line. */ /** Animation scale for line. */
@@ -40,14 +44,39 @@ public class DesktopInput extends InputHandler{
public void buildUI(Group group){ public void buildUI(Group group){
group.fill(t -> { group.fill(t -> {
t.bottom().update(() -> t.getColor().a = Mathf.lerpDelta(t.getColor().a, player.isBuilding() ? 1f : 0f, 0.15f)); t.bottom().update(() -> t.getColor().a = Mathf.lerpDelta(t.getColor().a, player.isBuilding() ? 1f : 0f, 0.15f));
t.visible(() -> Core.settings.getBool("hints")); t.visible(() -> Core.settings.getBool("hints") && selectRequests.isEmpty());
t.table(Styles.black6, b -> { t.table(Styles.black6, b -> {
b.defaults().left(); b.defaults().left();
b.label(() -> Core.bundle.format(!player.isBuilding ? "resumebuilding" : "pausebuilding", Core.keybinds.get(Binding.pause_building).key.name())).style(Styles.outlineLabel); b.label(() -> Core.bundle.format(!player.isBuilding ? "resumebuilding" : "pausebuilding", Core.keybinds.get(Binding.pause_building).key.name())).style(Styles.outlineLabel);
b.row(); b.row();
b.add(Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.name())).style(Styles.outlineLabel); b.add(Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.name())).style(Styles.outlineLabel);
b.row();
b.add(Core.bundle.format("selectschematic", Core.keybinds.get(Binding.schematic_select).key.name())).style(Styles.outlineLabel);
}).margin(10f); }).margin(10f);
}); });
group.fill(t -> {
t.visible(() -> lastSchematic != null && !selectRequests.isEmpty());
t.bottom();
t.table(Styles.black6, b -> {
b.touchable(Touchable.enabled);
b.defaults().left();
b.add(Core.bundle.format("schematic.flip",
Core.keybinds.get(Binding.schematic_flip_x).key.name(),
Core.keybinds.get(Binding.schematic_flip_y).key.name())).style(Styles.outlineLabel);
b.row();
b.table(a -> {
a.addImageTextButton("$schematic.add", Icon.saveSmall, () -> {
ui.showTextInput("$schematic.add", "$name", "", text -> {
lastSchematic.tags.put("name", text);
schematics.add(lastSchematic);
ui.showInfoFade("$schematic.saved");
ui.schematics.showInfo(lastSchematic);
});
}).colspan(2).size(250f, 50f);
});
}).margin(6f);
});
} }
@Override @Override
@@ -66,7 +95,7 @@ public class DesktopInput extends InputHandler{
drawRequest(lineRequests.get(i)); drawRequest(lineRequests.get(i));
} }
}else if(mode == breaking){ }else if(mode == breaking){
drawSelection(selectX, selectY, cursorX, cursorY); drawBreakSelection(selectX, selectY, cursorX, cursorY);
}else if(isPlacing()){ }else if(isPlacing()){
if(block.rotate){ if(block.rotate){
drawArrow(block, cursorX, cursorY, rotation); drawArrow(block, cursorX, cursorY, rotation);
@@ -83,6 +112,12 @@ public class DesktopInput extends InputHandler{
} }
} }
//draw schematic requests
for(BuildRequest request : selectRequests){
request.animScale = 1f;
drawRequest(request);
}
if(sreq != null){ if(sreq != null){
boolean valid = validPlace(sreq.x, sreq.y, sreq.block, sreq.rotation, sreq); boolean valid = validPlace(sreq.x, sreq.y, sreq.block, sreq.rotation, sreq);
if(sreq.block.rotate){ if(sreq.block.rotate){
@@ -94,6 +129,10 @@ public class DesktopInput extends InputHandler{
drawSelected(sreq.x, sreq.y, sreq.block, getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null ? Pal.remove : Pal.accent); drawSelected(sreq.x, sreq.y, sreq.block, getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null ? Pal.remove : Pal.accent);
} }
if(Core.input.keyDown(Binding.schematic_select)){
drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize);
}
Draw.reset(); Draw.reset();
} }
@@ -118,7 +157,7 @@ public class DesktopInput extends InputHandler{
if(state.is(State.menu) || Core.scene.hasDialog()) return; if(state.is(State.menu) || Core.scene.hasDialog()) return;
//zoom things //zoom things
if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && (Core.input.keyDown(Binding.zoom_hold))){ if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && Core.input.keyDown(Binding.zoom_hold)){
renderer.scaleCamera(Core.input.axisTap(Binding.zoom)); renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
} }
@@ -134,6 +173,10 @@ public class DesktopInput extends InputHandler{
mode = none; mode = none;
} }
if(mode != none){
selectRequests.clear();
}
if(player.isShooting && !canShoot()){ if(player.isShooting && !canShoot()){
player.isShooting = false; player.isShooting = false;
} }
@@ -151,8 +194,12 @@ public class DesktopInput extends InputHandler{
sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4); sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4);
} }
if(Math.abs((int)Core.input.axisTap(Binding.rotate)) > 0 && isPlacing() && mode == placing){ if(!Core.input.keyDown(Binding.zoom_hold) && Math.abs((int)Core.input.axisTap(Binding.rotate)) > 0){
updateLine(selectX, selectY); if(isPlacing() && mode == placing){
updateLine(selectX, selectY);
}else if(!selectRequests.isEmpty()){
rotateRequests(selectRequests, (int)Core.input.axisTap(Binding.rotate));
}
} }
Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY()); Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY());
@@ -162,7 +209,7 @@ public class DesktopInput extends InputHandler{
cursorType = cursor.block().getCursor(cursor); cursorType = cursor.block().getCursor(cursor);
if(isPlacing()){ if(isPlacing() || !selectRequests.isEmpty()){
cursorType = SystemCursor.hand; cursorType = SystemCursor.hand;
} }
@@ -190,15 +237,49 @@ public class DesktopInput extends InputHandler{
cursorType = SystemCursor.arrow; cursorType = SystemCursor.arrow;
} }
@Override
public void useSchematic(Schematic schem){
block = null;
schematicX = tileX(getMouseX());
schematicY = tileY(getMouseY());
selectRequests.addAll(schematics.toRequests(schem, schematicX, schematicY));
mode = none;
}
@Override @Override
public boolean isBreaking(){ public boolean isBreaking(){
return mode == breaking; return mode == breaking;
} }
@Override
public void buildPlacementUI(Table table){
table.addImage().color(Pal.gray).height(4f).colspan(4).growX();
table.row();
table.left().margin(0f).defaults().size(48f).left();
table.addImageButton(Icon.wikiSmall, Styles.clearPartiali, () -> {
ui.schematics.show();
});
}
void pollInput(){ void pollInput(){
Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY()); Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY());
int cursorX = tileX(Core.input.mouseX()); int cursorX = tileX(Core.input.mouseX());
int cursorY = tileY(Core.input.mouseY()); int cursorY = tileY(Core.input.mouseY());
int rawCursorX = world.toTile(Core.input.mouseWorld().x), rawCursorY = world.toTile(Core.input.mouseWorld().y);
if(!selectRequests.isEmpty()){
int shiftX = rawCursorX - schematicX, shiftY = rawCursorY - schematicY;
selectRequests.each(s -> {
s.x += shiftX;
s.y += shiftY;
});
schematicX += shiftX;
schematicY += shiftY;
}
if(Core.input.keyTap(Binding.deselect)){ if(Core.input.keyTap(Binding.deselect)){
player.setMineTile(null); player.setMineTile(null);
@@ -208,6 +289,38 @@ public class DesktopInput extends InputHandler{
player.clearBuilding(); player.clearBuilding();
} }
if(Core.input.keyTap(Binding.schematic_select)){
schemX = rawCursorX;
schemY = rawCursorY;
}
if(Core.input.keyTap(Binding.schematic_menu)){
ui.schematics.show();
}
if(Core.input.keyTap(Binding.clear_building)){
lastSchematic = null;
selectRequests.clear();
}
if(Core.input.keyRelease(Binding.schematic_select)){
lastSchematic = schematics.create(schemX, schemY, rawCursorX, rawCursorY);
useSchematic(lastSchematic);
if(selectRequests.isEmpty()){
lastSchematic = null;
}
}
if(!selectRequests.isEmpty()){
if(Core.input.keyTap(Binding.schematic_flip_x)){
flipRequests(selectRequests, true);
}
if(Core.input.keyTap(Binding.schematic_flip_y)){
flipRequests(selectRequests, false);
}
}
if(sreq != null){ if(sreq != null){
float offset = ((sreq.block.size + 2) % 2) * tilesize / 2f; float offset = ((sreq.block.size + 2) % 2) * tilesize / 2f;
float x = Core.input.mouseWorld().x + offset; float x = Core.input.mouseWorld().x + offset;
@@ -233,7 +346,10 @@ public class DesktopInput extends InputHandler{
if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){ if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){
BuildRequest req = getRequest(cursorX, cursorY); BuildRequest req = getRequest(cursorX, cursorY);
if(isPlacing()){ if(!selectRequests.isEmpty()){
flushRequests(selectRequests);
//selectRequests.clear();
}else if(isPlacing()){
selectX = cursorX; selectX = cursorX;
selectY = cursorY; selectY = cursorY;
lastLineX = cursorX; lastLineX = cursorX;
@@ -329,6 +445,7 @@ public class DesktopInput extends InputHandler{
mode = none; mode = none;
block = null; block = null;
sreq = null; sreq = null;
selectRequests.clear();
} }
} }
} }

View File

@@ -14,12 +14,14 @@ import io.anuke.arc.scene.*;
import io.anuke.arc.scene.event.*; import io.anuke.arc.scene.event.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.content.*; import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.effect.*; import io.anuke.mindustry.entities.effect.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*; import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.Teams.*; import io.anuke.mindustry.game.Teams.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
@@ -51,6 +53,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public boolean droppingItem; public boolean droppingItem;
public Group uiGroup; public Group uiGroup;
protected @Nullable Schematic lastSchematic;
protected GestureDetector detector; protected GestureDetector detector;
protected PlaceLine line = new PlaceLine(); protected PlaceLine line = new PlaceLine();
protected BuildRequest resultreq; protected BuildRequest resultreq;
@@ -207,7 +210,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(request.breaking){ if(request.breaking){
drawBreaking(request.x, request.y); drawBreaking(request.x, request.y);
}else{ }else{
drawSelected(request.x, request.y, request.tile().block(), Pal.remove); drawSelected(request.x, request.y, request.block, Pal.remove);
} }
} }
@@ -219,6 +222,76 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
drawSelected(x, y, block, Pal.remove); drawSelected(x, y, block, Pal.remove);
} }
public void useSchematic(Schematic schem){
selectRequests.addAll(schematics.toRequests(schem, world.toTile(player.x), world.toTile(player.y)));
}
public void rotateRequests(Array<BuildRequest> requests, int direction){
int ox = rawTileX(), oy = rawTileY();
requests.each(req -> {
//rotate config position
if(req.block.posConfig){
int cx = Pos.x(req.config) - req.originalX, cy = Pos.y(req.config) - req.originalY;
int lx = cx;
if(direction >= 0){
cx = -cy;
cy = lx;
}else{
cx = cy;
cy = -lx;
}
req.config = Pos.get(cx + req.originalX, cy + req.originalY);
}
//rotate actual request, centered on its multiblock position
float wx = (req.x - ox) * tilesize + req.block.offset(), wy = (req.y - oy) * tilesize + req.block.offset();
float x = wx;
if(direction >= 0){
wx = -wy;
wy = x;
}else{
wx = wy;
wy = -x;
}
req.x = world.toTile(wx - req.block.offset()) + ox;
req.y = world.toTile(wy - req.block.offset()) + oy;
req.rotation = Mathf.mod(req.rotation + direction, 4);
});
}
public void flipRequests(Array<BuildRequest> requests, boolean x){
int origin = x ? rawTileX() : rawTileY();
requests.each(req -> {
int value = -((x ? req.x : req.y) - origin) + origin;
if(x){
req.x = value;
}else{
req.y = value;
}
if(req.block.posConfig){
int corigin = x ? req.originalWidth/2 : req.originalHeight/2;
int nvalue = -((x ? Pos.x(req.config) : Pos.y(req.config)) - corigin) + corigin;
if(x){
req.originalX = -(req.originalX - corigin) + corigin;
req.config = Pos.get(nvalue, Pos.y(req.config));
}else{
req.originalY = -(req.originalY - corigin) + corigin;
req.config = Pos.get(Pos.x(req.config), nvalue);
}
}
//flip rotation
if(x == (req.rotation % 2 == 0)){
req.rotation = Mathf.mod(req.rotation + 2, 4);
}
});
}
/** Returns the selection request that overlaps this position, or null. */ /** Returns the selection request that overlaps this position, or null. */
protected BuildRequest getRequest(int x, int y){ protected BuildRequest getRequest(int x, int y){
return getRequest(x, y, 1, null); return getRequest(x, y, 1, null);
@@ -259,7 +332,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
return null; return null;
} }
protected void drawSelection(int x1, int y1, int x2, int y2){ protected void drawBreakSelection(int x1, int y1, int x2, int y2){
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f); NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
NormalizeResult dresult = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength); NormalizeResult dresult = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
@@ -307,10 +380,21 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y); Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
} }
protected void drawSelection(int x1, int y1, int x2, int y2, int maxLength){
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
Lines.stroke(2f);
Draw.color(Pal.accentBack);
Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y);
Draw.color(Pal.accent);
Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y);
}
protected void flushSelectRequests(Array<BuildRequest> requests){ protected void flushSelectRequests(Array<BuildRequest> requests){
for(BuildRequest req : requests){ for(BuildRequest req : requests){
if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){ if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){
selectRequests.add(req); selectRequests.add(req.copy());
} }
} }
} }
@@ -318,7 +402,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
protected void flushRequests(Array<BuildRequest> requests){ protected void flushRequests(Array<BuildRequest> requests){
for(BuildRequest req : requests){ for(BuildRequest req : requests){
if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){ if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){
player.addBuildRequest(req); BuildRequest copy = req.copy();
if(copy.hasConfig && copy.block.posConfig){
copy.config = Pos.get(Pos.x(copy.config) + copy.x - copy.originalX, Pos.y(copy.config) + copy.y - copy.originalY);
}
player.addBuildRequest(copy);
} }
} }
} }
@@ -448,14 +536,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
} }
} }
/*
//clear when the player taps on something else
if(!consumed && !mobile && player.isBuilding() && block == null){
//player.clearBuilding();
block = null;
return true;
}*/
if(!showedInventory){ if(!showedInventory){
frag.inv.hide(); frag.inv.hide();
} }
@@ -499,6 +579,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
return world.tile(tileX(x), tileY(y)); return world.tile(tileX(x), tileY(y));
} }
int rawTileX(){
return world.toTile(Core.input.mouseWorld().x);
}
int rawTileY(){
return world.toTile(Core.input.mouseWorld().y);
}
int tileX(float cursorX){ int tileX(float cursorX){
Vector2 vec = Core.input.mouseWorld(cursorX, 0); Vector2 vec = Core.input.mouseWorld(cursorX, 0);
if(selectedBlock()){ if(selectedBlock()){

View File

@@ -208,7 +208,7 @@ public class MobileInput extends InputHandler implements GestureListener{
} }
//move all current requests to removal array so they fade out //move all current requests to removal array so they fade out
removals.addAll(selectRequests.find(r -> !r.breaking)); removals.addAll(selectRequests.select(r -> !r.breaking));
selectRequests.clear(); selectRequests.clear();
selecting = false; selecting = false;
}).visible(() -> !selectRequests.isEmpty()).name("confirmplace"); }).visible(() -> !selectRequests.isEmpty()).name("confirmplace");
@@ -243,7 +243,6 @@ public class MobileInput extends InputHandler implements GestureListener{
if(tile == null) continue; if(tile == null) continue;
request.animScale = Mathf.lerpDelta(request.animScale, 0f, 0.2f); request.animScale = Mathf.lerpDelta(request.animScale, 0f, 0.2f);
request.animInvalid = Mathf.lerpDelta(request.animInvalid, 0f, 0.2f);
if(request.breaking){ if(request.breaking){
drawSelected(request.x, request.y, tile.block(), Pal.remove); drawSelected(request.x, request.y, tile.block(), Pal.remove);
@@ -263,10 +262,8 @@ public class MobileInput extends InputHandler implements GestureListener{
if((!request.breaking && validPlace(tile.x, tile.y, request.block, request.rotation)) if((!request.breaking && validPlace(tile.x, tile.y, request.block, request.rotation))
|| (request.breaking && validBreak(tile.x, tile.y))){ || (request.breaking && validBreak(tile.x, tile.y))){
request.animScale = Mathf.lerpDelta(request.animScale, 1f, 0.2f); request.animScale = Mathf.lerpDelta(request.animScale, 1f, 0.2f);
request.animInvalid = Mathf.lerpDelta(request.animInvalid, 0f, 0.2f);
}else{ }else{
request.animScale = Mathf.lerpDelta(request.animScale, 0.6f, 0.1f); request.animScale = Mathf.lerpDelta(request.animScale, 0.6f, 0.1f);
request.animInvalid = Mathf.lerpDelta(request.animInvalid, 0.9f, 0.2f);
} }
Tmp.c1.set(Draw.getMixColor()); Tmp.c1.set(Draw.getMixColor());
@@ -305,7 +302,7 @@ public class MobileInput extends InputHandler implements GestureListener{
drawRequest(lineRequests.get(i)); drawRequest(lineRequests.get(i));
} }
}else if(mode == breaking){ }else if(mode == breaking){
drawSelection(lineStartX, lineStartY, tileX, tileY); drawBreakSelection(lineStartX, lineStartY, tileX, tileY);
} }
} }
@@ -340,7 +337,7 @@ public class MobileInput extends InputHandler implements GestureListener{
if(request.breaking){ if(request.breaking){
drawSelected(request.x, request.y, request.tile().block(), Pal.remove); drawSelected(request.x, request.y, request.tile().block(), Pal.remove);
}else{ }else{
drawRequest(request.x, request.y, request.block, request.rotation); request.block.drawRequest(request, allRequests(), validPlace(request.x, request.y, request.block, request.rotation));
drawSelected(request.x, request.y, request.block, Pal.accent); drawSelected(request.x, request.y, request.block, Pal.accent);
} }
} }

View File

@@ -95,14 +95,14 @@ public class PlaceUtils{
}else{ }else{
endx = tilex; endx = tilex;
} }
}
if(Math.abs(endx - tilex) > maxLength){ if(Math.abs(endx - tilex) > maxLength){
endx = Mathf.sign(endx - tilex) * maxLength + tilex; endx = Mathf.sign(endx - tilex) * maxLength + tilex;
} }
if(Math.abs(endy - tiley) > maxLength){ if(Math.abs(endy - tiley) > maxLength){
endy = Mathf.sign(endy - tiley) * maxLength + tiley; endy = Mathf.sign(endy - tiley) * maxLength + tiley;
}
} }
int dx = endx - tilex, dy = endy - tiley; int dx = endx - tilex, dy = endy - tiley;
@@ -141,12 +141,12 @@ public class PlaceUtils{
return result; return result;
} }
static class NormalizeDrawResult{ public static class NormalizeDrawResult{
float x, y, x2, y2; float x, y, x2, y2;
} }
static class NormalizeResult{ public static class NormalizeResult{
int x, y, x2, y2, rotation; public int x, y, x2, y2, rotation;
boolean isX(){ boolean isX(){
return Math.abs(x2 - x) > Math.abs(y2 - y); return Math.abs(x2 - x) > Math.abs(y2 - y);

View File

@@ -4,14 +4,17 @@ import io.anuke.arc.*;
import io.anuke.arc.collection.*; import io.anuke.arc.collection.*;
import io.anuke.arc.files.*; import io.anuke.arc.files.*;
import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.*; import io.anuke.mindustry.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.*; import io.anuke.mindustry.io.*;
import io.anuke.mindustry.maps.filters.*; import io.anuke.mindustry.maps.filters.*;
import io.anuke.mindustry.type.*;
import static io.anuke.mindustry.Vars.maps; import static io.anuke.mindustry.Vars.*;
public class Map implements Comparable<Map>{ public class Map implements Comparable<Map>, Publishable{
/** Whether this is a custom map. */ /** Whether this is a custom map. */
public final boolean custom; public final boolean custom;
/** Metadata. Author description, display name, etc. */ /** Metadata. Author description, display name, etc. */
@@ -131,6 +134,76 @@ public class Map implements Comparable<Map>{
return tags.containsKey(name); return tags.containsKey(name);
} }
@Override
public String getSteamID(){
return tags.get("steamid");
}
@Override
public void addSteamID(String id){
tags.put("steamid", id);
ui.editor.editor.getTags().put("steamid", id);
try{
ui.editor.save();
}catch(Exception e){
Log.err(e);
}
Events.fire(new MapPublishEvent());
}
@Override
public void removeSteamID(){
tags.remove("steamid");
ui.editor.editor.getTags().remove("steamid");
try{
ui.editor.save();
}catch(Exception e){
Log.err(e);
}
}
@Override
public String steamTitle(){
return name();
}
@Override
public String steamDescription(){
return description();
}
@Override
public String steamTag(){
return "map";
}
@Override
public FileHandle createSteamFolder(String id){
return null;
}
@Override
public FileHandle createSteamPreview(String id){
return null;
}
@Override
public Array<String> extraTags(){
Gamemode mode = Gamemode.attack.valid(this) ? Gamemode.attack : Gamemode.survival;
return Array.with(mode.name());
}
@Override
public boolean prePublish(){
tags.put("author", player.name);
ui.editor.editor.getTags().put("author", tags.get("author"));
ui.editor.save();
return true;
}
@Override @Override
public int compareTo(Map map){ public int compareTo(Map map){
int work = -Boolean.compare(workshop, map.workshop); int work = -Boolean.compare(workshop, map.workshop);

View File

@@ -104,7 +104,7 @@ public class Maps{
} }
//workshop //workshop
for(FileHandle file : platform.getExternalMaps()){ for(FileHandle file : platform.getWorkshopContent(Map.class)){
try{ try{
Map map = loadMap(file, false); Map map = loadMap(file, false);
map.workshop = true; map.workshop = true;

View File

@@ -15,6 +15,7 @@ import io.anuke.arc.util.serialization.*;
import io.anuke.arc.util.serialization.Json.*; import io.anuke.arc.util.serialization.Json.*;
import io.anuke.mindustry.*; import io.anuke.mindustry.*;
import io.anuke.mindustry.content.*; import io.anuke.mindustry.content.*;
import io.anuke.mindustry.content.TechTree.*;
import io.anuke.mindustry.entities.Effects.*; import io.anuke.mindustry.entities.Effects.*;
import io.anuke.mindustry.entities.bullet.*; import io.anuke.mindustry.entities.bullet.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
@@ -42,21 +43,12 @@ public class ContentParser{
if(data.isString()){ if(data.isString()){
return field(Bullets.class, data); return field(Bullets.class, data);
} }
Class<? extends BulletType> bc = data.has("type") ? resolve(data.getString("type"), "io.anuke.mindustry.entities.bullets") : BasicBulletType.class; Class<? extends BulletType> bc = data.has("type") ? resolve(data.getString("type"), "io.anuke.mindustry.entities.bullet") : BasicBulletType.class;
data.remove("type"); data.remove("type");
BulletType result = make(bc); BulletType result = make(bc);
readFields(result, data); readFields(result, data);
return result; return result;
}); });
/*
put(Music.class, (type, data) -> {
if(fieldOpt(Musics.class, data) != null) return fieldOpt(Musics.class, data);
String path = "music/" + data.asString() + (Vars.ios ? ".mp3" : ".ogg");
Core.assets.load(path, Music.class);
Core.assets.finishLoadingAsset(path);
return Core.assets.get(path);
});*/
put(Sound.class, (type, data) -> { put(Sound.class, (type, data) -> {
if(fieldOpt(Sounds.class, data) != null) return fieldOpt(Sounds.class, data); if(fieldOpt(Sounds.class, data) != null) return fieldOpt(Sounds.class, data);
@@ -78,6 +70,7 @@ public class ContentParser{
/** Stores things that need to be parsed fully, e.g. reading fields of content. /** Stores things that need to be parsed fully, e.g. reading fields of content.
* This is done to accomodate binding of content names first.*/ * This is done to accomodate binding of content names first.*/
private Array<Runnable> reads = new Array<>(); private Array<Runnable> reads = new Array<>();
private Array<Runnable> postreads = new Array<>();
private LoadedMod currentMod; private LoadedMod currentMod;
private Content currentContent; private Content currentContent;
@@ -147,6 +140,15 @@ public class ContentParser{
} }
currentContent = block; currentContent = block;
String[] research = {null};
//add research tech node
if(value.has("research")){
research[0] = value.get("research").asString();
value.remove("research");
}
read(() -> { read(() -> {
if(value.has("consumes")){ if(value.has("consumes")){
for(JsonValue child : value.get("consumes")){ for(JsonValue child : value.get("consumes")){
@@ -174,8 +176,16 @@ public class ContentParser{
readFields(block, value, true); readFields(block, value, true);
//add research tech node //add research tech node
if(value.has("research")){ if(research[0] != null){
TechTree.create(find(ContentType.block, value.get("research").asString()), block); Block parent = find(ContentType.block, research[0]);
TechNode baseNode = TechTree.create(parent, block);
postreads.add(() -> {
TechNode parnode = TechTree.all.find(t -> t.block == parent);
if(!parnode.children.contains(baseNode)){
parnode.children.add(baseNode);
}
});
} }
//make block visible by default if there are requirements and no visibility set //make block visible by default if there are requirements and no visibility set
@@ -275,10 +285,12 @@ public class ContentParser{
public void finishParsing(){ public void finishParsing(){
try{ try{
reads.each(Runnable::run); reads.each(Runnable::run);
postreads.each(Runnable::run);
}catch(Exception e){ }catch(Exception e){
Vars.mods.handleError(new ModLoadException("Error occurred parsing content: " + currentContent, currentContent, e), currentMod); Vars.mods.handleError(new ModLoadException("Error occurred parsing content: " + currentContent, currentContent, e), currentMod);
} }
reads.clear(); reads.clear();
postreads.clear();
} }
/** /**
@@ -395,9 +407,7 @@ public class ContentParser{
FieldMetadata metadata = fields.get(child.name().replace(" ", "_")); FieldMetadata metadata = fields.get(child.name().replace(" ", "_"));
if(metadata == null){ if(metadata == null){
if(ignoreUnknownFields){ if(ignoreUnknownFields){
if(!child.name.equals("research")){ Log.err("{0}: Ignoring unknown field: " + child.name + " (" + type.getName() + ")", object);
Log.err("{0}: Ignoring unknown field: " + child.name + " (" + type.getName() + ")", object);
}
continue; continue;
}else{ }else{
SerializationException ex = new SerializationException("Field not found: " + child.name + " (" + type.getName() + ")"); SerializationException ex = new SerializationException("Field not found: " + child.name + " (" + type.getName() + ")");

View File

@@ -60,7 +60,7 @@ public class Mods implements Loadable{
file.copyTo(dest); file.copyTo(dest);
try{ try{
loaded.add(loadMod(file, false)); loaded.add(loadMod(dest, false));
requiresReload = true; requiresReload = true;
}catch(IOException e){ }catch(IOException e){
dest.delete(); dest.delete();
@@ -180,7 +180,7 @@ public class Mods implements Loadable{
} }
//load workshop mods now //load workshop mods now
for(FileHandle file : platform.getExternalMods()){ for(FileHandle file : platform.getWorkshopContent(LoadedMod.class)){
try{ try{
LoadedMod mod = loadMod(file, true); LoadedMod mod = loadMod(file, true);
if(mod.enabled()){ if(mod.enabled()){
@@ -442,7 +442,7 @@ public class Mods implements Loadable{
} }
/** Represents a plugin that has been loaded from a jar file.*/ /** Represents a plugin that has been loaded from a jar file.*/
public static class LoadedMod{ public static class LoadedMod implements Publishable{
/** The location of this mod's zip file/folder on the disk. */ /** The location of this mod's zip file/folder on the disk. */
public final FileHandle file; public final FileHandle file;
/** The root zip file; points to the contents of this mod. In the case of folders, this is the same as the mod's file. */ /** The root zip file; points to the contents of this mod. In the case of folders, this is the same as the mod's file. */
@@ -453,8 +453,6 @@ public class Mods implements Loadable{
public final String name; public final String name;
/** This mod's metadata. */ /** This mod's metadata. */
public final ModMeta meta; public final ModMeta meta;
/** The ID of this mod in the workshop.*/
public @Nullable String workshopID;
public LoadedMod(FileHandle file, FileHandle root, Mod mod, ModMeta meta){ public LoadedMod(FileHandle file, FileHandle root, Mod mod, ModMeta meta){
this.root = root; this.root = root;
@@ -468,6 +466,63 @@ public class Mods implements Loadable{
return Core.settings.getBool(name + "-enabled", true); return Core.settings.getBool(name + "-enabled", true);
} }
@Override
public String getSteamID(){
return Core.settings.getString(name + "-steamid", null);
}
@Override
public void addSteamID(String id){
Core.settings.put(name + "-steamid", id);
Core.settings.save();
}
@Override
public void removeSteamID(){
Core.settings.remove(name + "-steamid");
Core.settings.save();
}
@Override
public String steamTitle(){
return meta.name;
}
@Override
public String steamDescription(){
return meta.description;
}
@Override
public String steamTag(){
return "mod";
}
@Override
public FileHandle createSteamFolder(String id){
return file;
}
@Override
public FileHandle createSteamPreview(String id){
return file.child("preview.png");
}
@Override
public boolean prePublish(){
if(!file.isDirectory()){
ui.showErrorMessage("$mod.folder.missing");
return false;
}
if(!file.child("preview.png").exists()){
ui.showErrorMessage("$mod.preview.missing");
return false;
}
return true;
}
@Override @Override
public String toString(){ public String toString(){
return "LoadedMod{" + return "LoadedMod{" +

View File

@@ -0,0 +1,40 @@
package io.anuke.mindustry.type;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.*;
/** Defines a piece of content that can be published on the Workshop. */
public interface Publishable{
/** @return workshop item ID, or null if this isn't on the workshop. */
@Nullable String getSteamID();
/** adds a steam ID to this item once it's published. should save the item to make sure this change is persisted. */
void addSteamID(String id);
/** removes the item ID; called when the item isn't found. */
void removeSteamID();
/** @return default title of the listing. */
String steamTitle();
/** @return standard steam listing description, may be null. this is editable by users after release.*/
@Nullable String steamDescription();
/** @return the tag that this content has. e.g. 'schematic' or 'map'. */
String steamTag();
/** @return a folder with everything needed for this piece of content in it; does not need to be a copy. */
FileHandle createSteamFolder(String id);
/** @return a preview file PNG. */
FileHandle createSteamPreview(String id);
/** @return any extra tags to add to this item.*/
default Array<String> extraTags(){
return new Array<>(0);
}
/** @return whether this item is or was once on the workshop.*/
default boolean hasSteamID(){
return getSteamID() != null && Vars.steam;
}
/** called before this item is published.
* @return true to signify that everything is cool and good, or false to significy that the user has done something wrong.
* if false is returned, make sure to show a dialog explaining the error. */
default boolean prePublish(){
return true;
}
}

View File

@@ -55,6 +55,7 @@ public class FloatingDialog extends Dialog{
@Override @Override
public void addCloseButton(){ public void addCloseButton(){
buttons.defaults().size(210f, 64f);
buttons.addImageTextButton("$back", Icon.arrowLeft, this::hide).size(210f, 64f); buttons.addImageTextButton("$back", Icon.arrowLeft, this::hide).size(210f, 64f);
keyDown(key -> { keyDown(key -> {

View File

@@ -205,7 +205,7 @@ public class MapsDialog extends FloatingDialog{
table.addImageTextButton(map.workshop && steam ? "$view.workshop" : "$delete", map.workshop && steam ? Icon.linkSmall : Icon.trash16Small, () -> { table.addImageTextButton(map.workshop && steam ? "$view.workshop" : "$delete", map.workshop && steam ? Icon.linkSmall : Icon.trash16Small, () -> {
if(map.workshop && steam){ if(map.workshop && steam){
platform.viewMapListing(map); platform.viewListing(map);
}else{ }else{
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> { ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
maps.removeMap(map); maps.removeMap(map);

View File

@@ -41,11 +41,11 @@ public class ModsDialog extends FloatingDialog{
mods.reloadContent(); mods.reloadContent();
setup(); setup();
ui.loadfrag.hide(); ui.loadfrag.hide();
}catch(Exception e){ }catch(Throwable e){
ui.showException(e); ui.showException(e);
} }
}); });
}catch(Exception e){ }catch(Throwable e){
ui.showException(e); ui.showException(e);
} }
}, t -> Core.app.post(() -> ui.showException(t))); }, t -> Core.app.post(() -> ui.showException(t)));
@@ -101,14 +101,20 @@ public class ModsDialog extends FloatingDialog{
setup(); setup();
}).height(50f).margin(8f).width(130f); }).height(50f).margin(8f).width(130f);
title.addImageButton(mod.workshopID != null ? Icon.linkSmall : Icon.trash16Small, Styles.cleari, () -> { if(steam && !mod.hasSteamID()){
if(mod.workshopID == null){ title.addImageButton(Icon.loadMapSmall, Styles.cleari, () -> {
platform.publish(mod);
}).size(50f);
}
title.addImageButton(mod.hasSteamID() ? Icon.linkSmall : Icon.trash16Small, Styles.cleari, () -> {
if(!mod.hasSteamID()){
ui.showConfirm("$confirm", "$mod.remove.confirm", () -> { ui.showConfirm("$confirm", "$mod.remove.confirm", () -> {
mods.removeMod(mod); mods.removeMod(mod);
setup(); setup();
}); });
}else{ }else{
platform.viewListing(mod.workshopID); platform.viewListing(mod);
} }
}).size(50f); }).size(50f);
}).growX().left().padTop(-14f).padRight(-14f); }).growX().left().padTop(-14f).padRight(-14f);

View File

@@ -0,0 +1,287 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.Texture.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.ImageButton.*;
import io.anuke.arc.scene.ui.TextButton.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.game.Schematics.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*;
import static io.anuke.mindustry.Vars.*;
public class SchematicsDialog extends FloatingDialog{
private SchematicInfoDialog info = new SchematicInfoDialog();
private String search = "";
public SchematicsDialog(){
super("$schematics");
Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> {
((Texture)t).setWrap(TextureWrap.Repeat);
};
shouldPause = true;
addCloseButton();
buttons.addImageTextButton("$schematic.import", Icon.loadMapSmall, this::showImport);
shown(this::setup);
}
void setup(){
search = "";
Runnable[] rebuildPane = {null};
cont.top();
cont.clear();
cont.table(s -> {
s.left();
s.addImage(Icon.zoom);
s.addField(search, res -> {
search = res;
rebuildPane[0].run();
}).growX();
}).fillX().padBottom(4);
cont.row();
cont.pane(t -> {
t.top();
t.margin(20f);
rebuildPane[0] = () -> {
t.clear();
int i = 0;
if(!schematics.all().contains(s -> search.isEmpty() || s.name().contains(search))){
t.add("$none");
}
for(Schematic s : schematics.all()){
if(!search.isEmpty() && !s.name().contains(search)) continue;
Button[] sel = {null};
sel[0] = t.addButton(b -> {
b.top();
b.margin(0f);
b.table(buttons -> {
buttons.left();
buttons.defaults().size(50f);
ImageButtonStyle style = Styles.clearPartiali;
buttons.addImageButton(Icon.infoSmall, style, () -> {
showInfo(s);
});
buttons.addImageButton(Icon.loadMapSmall, style, () -> {
showExport(s);
});
buttons.addImageButton(Icon.pencilSmall, style, () -> {
ui.showTextInput("$schematic.rename", "$name", s.name(), res -> {
s.tags.put("name", res);
s.save();
rebuildPane[0].run();
});
});
if(s.hasSteamID()){
buttons.addImageButton(Icon.linkSmall, style, () -> platform.viewListing(s));
}else{
buttons.addImageButton(Icon.trash16Small, style, () -> {
ui.showConfirm("$confirm", "$schematic.delete.confirm", () -> {
schematics.remove(s);
rebuildPane[0].run();
});
});
}
}).growX().height(50f);
b.row();
b.stack(new SchematicImage(s).setScaling(Scaling.fit), new Table(n -> {
n.top();
n.table(Styles.black3, c -> {
Label label = c.add(s.name()).style(Styles.outlineLabel).color(Color.white).top().growX().get();
label.setEllipsis(true);
label.setAlignment(Align.center);
}).growX().margin(1).pad(4).padBottom(0);
})).size(200f);
}, () -> {
if(sel[0].childrenPressed()) return;
control.input.useSchematic(s);
hide();
}).pad(4).style(Styles.cleari).get();
sel[0].getStyle().up = Tex.pane;
if(++i % 4 == 0){
t.row();
}
}
};
rebuildPane[0].run();
}).get().setScrollingDisabled(true, false);
}
public void showInfo(Schematic schematic){
info.show(schematic);
}
public void showImport(){
FloatingDialog dialog = new FloatingDialog("$editor.export");
dialog.cont.pane(p -> {
p.margin(10f);
p.table(Tex.button, t -> {
TextButtonStyle style = Styles.cleart;
t.defaults().size(280f, 60f).left();
t.row();
t.addImageTextButton("$schematic.copy.import", Icon.copySmall, style, () -> {
dialog.hide();
try{
Schematic s = schematics.readBase64(Core.app.getClipboardText());
schematics.add(s);
setup();
ui.showInfoFade("$schematic.saved");
showInfo(s);
}catch(Exception e){
ui.showException(e);
}
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));
t.row();
t.addImageTextButton("$schematic.importfile", Icon.saveMapSmall, style, () -> platform.showFileChooser(true, schematicExtension, file -> {
dialog.hide();
try{
Schematic s = Schematics.read(file);
schematics.add(s);
setup();
showInfo(s);
}catch(Exception e){
ui.showException(e);
}
})).marginLeft(12f);
t.row();
if(steam){
t.addImageTextButton("$schematic.browseworkshop", Icon.wikiSmall, style, () -> {
dialog.hide();
platform.openWorkshop();
}).marginLeft(12f);
}
});
});
dialog.addCloseButton();
dialog.show();
}
public void showExport(Schematic s){
FloatingDialog dialog = new FloatingDialog("$editor.export");
dialog.cont.pane(p -> {
p.margin(10f);
p.table(Tex.button, t -> {
TextButtonStyle style = Styles.cleart;
t.defaults().size(280f, 60f).left();
if(steam && !s.hasSteamID()){
t.addImageTextButton("$schematic.shareworkshop", Icon.wikiSmall, style,
() -> platform.publish(s)).marginLeft(12f);
t.row();
}
t.addImageTextButton("$schematic.copy", Icon.copySmall, style, () -> {
dialog.hide();
ui.showInfoFade("$copied");
Core.app.setClipboardText(schematics.writeBase64(s));
}).marginLeft(12f);
t.row();
t.addImageTextButton("$schematic.exportfile", Icon.saveMapSmall, style, () -> platform.showFileChooser(false, schematicExtension, file -> {
dialog.hide();
try{
Schematics.write(s, file);
}catch(Exception e){
ui.showException(e);
}
})).marginLeft(12f);
});
});
dialog.addCloseButton();
dialog.show();
}
public static class SchematicImage extends Image{
public float scaling = 16f;
public float thickness = 4f;
public Color borderColor = Pal.gray;
public SchematicImage(Schematic s){
super(schematics.getPreview(s, PreviewRes.high));
setScaling(Scaling.fit);
}
@Override
public void draw(){
boolean checked = getParent().getParent() instanceof Button
&& ((Button)getParent().getParent()).isOver();
Texture background = Core.assets.get("sprites/schematic-background.png", Texture.class);
TextureRegion region = Draw.wrap(background);
float xr = width / scaling;
float yr = height / scaling;
region.setU2(xr);
region.setV2(yr);
Draw.color();
Draw.alpha(parentAlpha);
Draw.rect(region, x + width/2f, y + height/2f, width, height);
super.draw();
Draw.color(checked ? Pal.accent : borderColor);
Draw.alpha(parentAlpha);
Lines.stroke(Scl.scl(thickness));
Lines.rect(x, y, width, height);
Draw.reset();
}
}
public static class SchematicInfoDialog extends FloatingDialog{
SchematicInfoDialog(){
super("");
setFillParent(true);
addCloseButton();
}
public void show(Schematic schem){
cont.clear();
title.setText("[[" + Core.bundle.get("schematic") + "] " +schem.name());
cont.add(Core.bundle.format("schematic.info", schem.width, schem.height, schem.tiles.size)).color(Color.lightGray);
cont.row();
cont.add(new SchematicImage(schem)).maxSize(800f);
cont.row();
Array<ItemStack> arr = schem.requirements();
cont.table(r -> {
int i = 0;
for(ItemStack s : arr){
r.addImage(s.item.icon(Cicon.small)).left();
r.add(s.amount + "").padLeft(2).left().color(Color.lightGray).padRight(4);
if(++i % 4 == 0){
r.row();
}
}
});
show();
}
}
}

View File

@@ -42,6 +42,10 @@ public class TechTreeDialog extends FloatingDialog{
margin(0f).marginBottom(8); margin(0f).marginBottom(8);
cont.stack(view = new View(), items = new ItemsDisplay()).grow(); cont.stack(view = new View(), items = new ItemsDisplay()).grow();
Events.on(ContentReloadEvent.class, e -> {
root = new TechTreeNode(TechTree.root, null);
});
shown(() -> { shown(() -> {
checkNodes(root); checkNodes(root);
treeLayout(); treeLayout();
@@ -105,8 +109,18 @@ public class TechTreeDialog extends FloatingDialog{
RadialTreeLayout layout = new RadialTreeLayout(); RadialTreeLayout layout = new RadialTreeLayout();
LayoutNode node = new LayoutNode(root, null); LayoutNode node = new LayoutNode(root, null);
layout.layout(node); layout.layout(node);
//bounds.y += nodeSize*1.5f; float minx = 0f, miny = 0f, maxx = 0f, maxy = 0f;
copyInfo(node); copyInfo(node);
for(TechTreeNode n : nodes){
if(!n.visible) continue;
minx = Math.min(n.x - n.width/2f, minx);
maxx = Math.max(n.x + n.width/2f, maxx);
miny = Math.min(n.y - n.height/2f, miny);
maxy = Math.max(n.y + n.height/2f, maxy);
}
bounds = new Rectangle(minx, miny, maxx - minx, maxy - miny);
bounds.y += nodeSize*1.5f;
} }
void copyInfo(LayoutNode node){ void copyInfo(LayoutNode node){
@@ -262,7 +276,7 @@ public class TechTreeDialog extends FloatingDialog{
float rx = bounds.x + panX + ox, ry = panY + oy + bounds.y; float rx = bounds.x + panX + ox, ry = panY + oy + bounds.y;
float rw = bounds.width, rh = bounds.height; float rw = bounds.width, rh = bounds.height;
rx = Mathf.clamp(rx, -rw + pad, Core.graphics.getWidth() - pad); rx = Mathf.clamp(rx, -rw + pad, Core.graphics.getWidth() - pad);
ry = Mathf.clamp(ry, pad, Core.graphics.getHeight() - rh - pad); ry = Mathf.clamp(ry, -rh + pad, Core.graphics.getHeight() - pad);
panX = rx - bounds.x - ox; panX = rx - bounds.x - ox;
panY = ry - bounds.y - oy; panY = ry - bounds.y - oy;
} }

View File

@@ -250,8 +250,9 @@ public class HudFragment extends Fragment{
}); });
parent.fill(t -> { parent.fill(t -> {
t.visible(() -> Core.settings.getBool("minimap") && !state.rules.tutorial);
//minimap //minimap
t.add(new Minimap().visible(() -> Core.settings.getBool("minimap") && !state.rules.tutorial)); t.add(new Minimap());
t.row(); t.row();
//position //position
t.label(() -> world.toTile(player.x) + "," + world.toTile(player.y)).style(Styles.outlineLabel) t.label(() -> world.toTile(player.x) + "," + world.toTile(player.y)).style(Styles.outlineLabel)

View File

@@ -22,7 +22,6 @@ public class LoadingFragment extends Fragment{
t.visible(false); t.visible(false);
t.touchable(Touchable.enabled); t.touchable(Touchable.enabled);
t.add().height(133f).row(); t.add().height(133f).row();
t.addImage().growX().height(3f).pad(4f).growX().get().setColor(Pal.accent); t.addImage().growX().height(3f).pad(4f).growX().get().setColor(Pal.accent);
t.row(); t.row();
t.add("$loading").name("namelabel").pad(10f); t.add("$loading").name("namelabel").pad(10f);

View File

@@ -87,6 +87,8 @@ public class Block extends BlockStorage{
public boolean configurable; public boolean configurable;
/** Whether this block consumes touchDown events when tapped. */ /** Whether this block consumes touchDown events when tapped. */
public boolean consumesTap; public boolean consumesTap;
/** Whether the config is positional and needs to be shifted. */
public boolean posConfig;
/** /**
* The color of this block when displayed on the minimap or map preview. * The color of this block when displayed on the minimap or map preview.
* Do not set manually! This is overriden when loading for most blocks. * Do not set manually! This is overriden when loading for most blocks.
@@ -116,7 +118,7 @@ public class Block extends BlockStorage{
public float idleSoundVolume = 0.5f; public float idleSoundVolume = 0.5f;
/** Cost of constructing this block. */ /** Cost of constructing this block. */
public ItemStack[] requirements = new ItemStack[]{}; public ItemStack[] requirements = {};
/** Category in place menu. */ /** Category in place menu. */
public Category category = Category.distribution; public Category category = Category.distribution;
/** Cost of building this block; do not modify directly! */ /** Cost of building this block; do not modify directly! */
@@ -676,8 +678,32 @@ public class Block extends BlockStorage{
public void drawRequestRegion(BuildRequest req, Eachable<BuildRequest> list){ public void drawRequestRegion(BuildRequest req, Eachable<BuildRequest> list){
TextureRegion reg = icon(Cicon.full); TextureRegion reg = icon(Cicon.full);
Draw.rect(icon(Cicon.full), req.drawx(), req.drawy(), Draw.rect(icon(Cicon.full), req.drawx(), req.drawy(),
reg.getWidth() * req.animScale * Draw.scl, reg.getHeight() * req.animScale * Draw.scl, reg.getWidth() * req.animScale * Draw.scl,
!rotate ? 0 : req.rotation * 90); reg.getHeight() * req.animScale * Draw.scl,
!rotate ? 0 : req.rotation * 90);
if(req.hasConfig){
drawRequestConfig(req, list);
}
}
public void drawRequestConfig(BuildRequest req, Eachable<BuildRequest> list){
}
public void drawRequestConfigCenter(BuildRequest req, Content content, String region){
Color color = content instanceof Item ? ((Item)content).color : content instanceof Liquid ? ((Liquid)content).color : null;
if(color == null) return;
Draw.color(color);
Draw.scl *= req.animScale;
Draw.rect(region, req.drawx(), req.drawy());
Draw.scl /= req.animScale;
Draw.color();
}
public void drawRequestConfigTop(BuildRequest req, Eachable<BuildRequest> list){
} }
@Override @Override

View File

@@ -56,7 +56,7 @@ public class BuildBlock extends Block{
} }
@Remote(called = Loc.server) @Remote(called = Loc.server)
public static void onConstructFinish(Tile tile, Block block, int builderID, byte rotation, Team team){ public static void onConstructFinish(Tile tile, Block block, int builderID, byte rotation, Team team, boolean skipConfig){
if(tile == null) return; if(tile == null) return;
float healthf = tile.entity == null ? 1f : tile.entity.healthf(); float healthf = tile.entity == null ? 1f : tile.entity.healthf();
world.setBlock(tile, block, team, rotation); world.setBlock(tile, block, team, rotation);
@@ -70,7 +70,9 @@ public class BuildBlock extends Block{
if(!headless && builderID == player.id){ if(!headless && builderID == player.id){
//this is run delayed, since if this is called on the server, all clients need to recieve the onBuildFinish() //this is run delayed, since if this is called on the server, all clients need to recieve the onBuildFinish()
//event first before they can recieve the placed() event modification results //event first before they can recieve the placed() event modification results
Core.app.post(() -> tile.block().playerPlaced(tile)); if(!skipConfig){
Core.app.post(() -> tile.block().playerPlaced(tile));
}
} }
Core.app.post(() -> Events.fire(new BlockBuildEndEvent(tile, playerGroup.getByID(builderID), team, false))); Core.app.post(() -> Events.fire(new BlockBuildEndEvent(tile, playerGroup.getByID(builderID), team, false)));
Sounds.place.at(tile, Mathf.random(0.7f, 1.4f)); Sounds.place.at(tile, Mathf.random(0.7f, 1.4f));
@@ -185,7 +187,7 @@ public class BuildBlock extends Block{
private float[] accumulator; private float[] accumulator;
private float[] totalAccumulator; private float[] totalAccumulator;
public boolean construct(Unit builder, @Nullable TileEntity core, float amount){ public boolean construct(Unit builder, @Nullable TileEntity core, float amount, boolean configured){
if(cblock == null){ if(cblock == null){
kill(); kill();
return false; return false;
@@ -208,7 +210,7 @@ public class BuildBlock extends Block{
} }
if(progress >= 1f || state.rules.infiniteResources){ if(progress >= 1f || state.rules.infiniteResources){
Call.onConstructFinish(tile, cblock, builderID, tile.rotation(), builder.getTeam()); Call.onConstructFinish(tile, cblock, builderID, tile.rotation(), builder.getTeam(), configured);
return true; return true;
} }
return false; return false;

View File

@@ -52,11 +52,13 @@ public class Conduit extends LiquidBlock implements Autotiler{
Draw.colorl(0.34f); Draw.colorl(0.34f);
Draw.alpha(0.5f); Draw.alpha(0.5f);
Draw.rect(botRegions[bits[0]], req.drawx(), req.drawy(), req.rotation * 90); Draw.rect(botRegions[bits[0]], req.drawx(), req.drawy(),
botRegions[bits[0]].getWidth() * Draw.scl * req.animScale, botRegions[bits[0]].getHeight() * Draw.scl * req.animScale,
req.rotation * 90);
Draw.color(); Draw.color();
Draw.rect(topRegions[bits[0]], req.drawx(), req.drawy(), req.rotation * 90); Draw.rect(topRegions[bits[0]], req.drawx(), req.drawy(), topRegions[bits[0]].getWidth() * Draw.scl * req.animScale, topRegions[bits[0]].getHeight() * Draw.scl * req.animScale, req.rotation * 90);
} }
@Override @Override

View File

@@ -107,7 +107,7 @@ public class Conveyor extends Block implements Autotiler{
if(bits == null) return; if(bits == null) return;
TextureRegion region = regions[bits[0]][0]; TextureRegion region = regions[bits[0]][0];
Draw.rect(region, req.drawx(), req.drawy(), region.getWidth() * bits[1] * Draw.scl, region.getHeight() * bits[2] * Draw.scl, req.rotation * 90); Draw.rect(region, req.drawx(), req.drawy(), region.getWidth() * bits[1] * Draw.scl * req.animScale, region.getHeight() * bits[2] * Draw.scl * req.animScale, req.rotation * 90);
} }
@Override @Override

View File

@@ -3,11 +3,13 @@ package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.*; import io.anuke.arc.*;
import io.anuke.arc.collection.*; import io.anuke.arc.collection.*;
import io.anuke.arc.collection.IntSet.*; import io.anuke.arc.collection.IntSet.*;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.arc.math.geom.*; import io.anuke.arc.math.geom.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
@@ -23,6 +25,7 @@ public class ItemBridge extends Block{
protected int range; protected int range;
protected float transportTime = 2f; protected float transportTime = 2f;
protected TextureRegion endRegion, bridgeRegion, arrowRegion; protected TextureRegion endRegion, bridgeRegion, arrowRegion;
protected BuildRequest otherReq;
private static int lastPlaced = Pos.invalid; private static int lastPlaced = Pos.invalid;
@@ -34,6 +37,7 @@ public class ItemBridge extends Block{
layer = Layer.power; layer = Layer.power;
expanded = true; expanded = true;
itemCapacity = 10; itemCapacity = 10;
posConfig = true;
configurable = true; configurable = true;
hasItems = true; hasItems = true;
unloadable = false; unloadable = false;
@@ -65,6 +69,27 @@ public class ItemBridge extends Block{
arrowRegion = Core.atlas.find(name + "-arrow"); arrowRegion = Core.atlas.find(name + "-arrow");
} }
@Override
public void drawRequestConfigTop(BuildRequest req, Eachable<BuildRequest> list){
otherReq = null;
list.each(other -> {
if(other.block == this && req.config == Pos.get(other.x, other.y)){
otherReq = other;
}
});
if(otherReq == null) return;
Lines.stroke(8f);
Lines.line(bridgeRegion,
req.drawx(),
req.drawy(),
otherReq.drawx(),
otherReq.drawy(), CapStyle.none, -tilesize / 2f);
Draw.rect(arrowRegion, (req.drawx() + otherReq.drawx()) / 2f, (req.drawy() + otherReq.drawy()) / 2f,
Angles.angle(req.drawx(), req.drawy(), otherReq.drawx(), otherReq.drawy()));
}
@Override @Override
public void playerPlaced(Tile tile){ public void playerPlaced(Tile tile){
Tile link = findLink(tile.x, tile.y); Tile link = findLink(tile.x, tile.y);

View File

@@ -36,6 +36,7 @@ public class MassDriver extends Block{
super(name); super(name);
update = true; update = true;
solid = true; solid = true;
posConfig = true;
configurable = true; configurable = true;
hasItems = true; hasItems = true;
layer = Layer.turret; layer = Layer.turret;

View File

@@ -1,10 +1,12 @@
package io.anuke.mindustry.world.blocks.distribution; package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.*; import io.anuke.arc.*;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.*; import io.anuke.arc.math.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -46,6 +48,11 @@ public class Sorter extends Block{
tile.<SorterEntity>entity().sortItem = content.item(value); tile.<SorterEntity>entity().sortItem = content.item(value);
} }
@Override
public void drawRequestConfig(BuildRequest req, Eachable<BuildRequest> list){
drawRequestConfigCenter(req, content.item(req.config), "center");
}
@Override @Override
public void draw(Tile tile){ public void draw(Tile tile){
super.draw(tile); super.draw(tile);

View File

@@ -33,53 +33,6 @@ public class PowerNode extends PowerBlock{
consumesPower = false; consumesPower = false;
outputsPower = false; outputsPower = false;
} }
/*
@Remote(targets = Loc.both, called = Loc.server, forward = true)
public static void linkPowerNodes(Player player, Tile tile, Tile other){
if(tile.entity == null || other == null || tile.entity.power == null || !((PowerNode)tile.block()).linkValid(tile, other)
|| tile.entity.power.links.size >= ((PowerNode)tile.block()).maxNodes) return;
if(!Units.canInteract(player, tile)) return;
TileEntity entity = tile.entity();
if(!entity.power.links.contains(other.pos())){
entity.power.links.add(other.pos());
}
if(other.getTeamID() == tile.getTeamID()){
if(!other.entity.power.links.contains(tile.pos())){
other.entity.power.links.add(tile.pos());
}
}
entity.power.graph.add(other.entity.power.graph);
}
@Remote(targets = Loc.both, called = Loc.server, forward = true)
public static void unlinkPowerNodes(Player player, Tile tile, Tile other){
if(tile.entity.power == null || other.entity == null || other.entity.power == null) return;
if(!Units.canInteract(player, tile)) return;
TileEntity entity = tile.entity();
entity.power.links.removeValue(other.pos());
other.entity.power.links.removeValue(tile.pos());
PowerGraph newgraph = new PowerGraph();
//reflow from this point, covering all tiles on this side
newgraph.reflow(tile);
if(other.entity.power.graph != newgraph){
//create new graph for other end
PowerGraph og = new PowerGraph();
//reflow from other end
og.reflow(other);
}
}
*/
@Override @Override
public void configured(Tile tile, Player player, int value){ public void configured(Tile tile, Player player, int value){

View File

@@ -34,6 +34,11 @@ public class Fracker extends SolidPump{
topRegion = Core.atlas.find(name + "-top"); topRegion = Core.atlas.find(name + "-top");
} }
@Override
public boolean outputsItems(){
return false;
}
@Override @Override
public void drawCracks(Tile tile){} public void drawCracks(Tile tile){}

View File

@@ -1,8 +1,10 @@
package io.anuke.mindustry.world.blocks.sandbox; package io.anuke.mindustry.world.blocks.sandbox;
import io.anuke.arc.*; import io.anuke.arc.*;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -43,6 +45,11 @@ public class ItemSource extends Block{
bars.remove("items"); bars.remove("items");
} }
@Override
public void drawRequestConfig(BuildRequest req, Eachable<BuildRequest> list){
drawRequestConfigCenter(req, content.item(req.config), "center");
}
@Override @Override
public boolean outputsItems(){ public boolean outputsItems(){
return true; return true;

View File

@@ -2,11 +2,13 @@ package io.anuke.mindustry.world.blocks.sandbox;
import io.anuke.arc.*; import io.anuke.arc.*;
import io.anuke.arc.collection.*; import io.anuke.arc.collection.*;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.style.*; import io.anuke.arc.scene.style.*;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.ArcAnnotate.*; import io.anuke.arc.util.ArcAnnotate.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
@@ -57,6 +59,11 @@ public class LiquidSource extends Block{
} }
} }
@Override
public void drawRequestConfig(BuildRequest req, Eachable<BuildRequest> list){
drawRequestConfigCenter(req, content.liquid(req.config), "center");
}
@Override @Override
public void draw(Tile tile){ public void draw(Tile tile){
super.draw(tile); super.draw(tile);

View File

@@ -1,9 +1,11 @@
package io.anuke.mindustry.world.blocks.storage; package io.anuke.mindustry.world.blocks.storage;
import io.anuke.arc.*; import io.anuke.arc.*;
import io.anuke.arc.function.*;
import io.anuke.arc.graphics.*; import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.scene.ui.layout.*; import io.anuke.arc.scene.ui.layout.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
@@ -28,6 +30,11 @@ public class Unloader extends Block{
configurable = true; configurable = true;
} }
@Override
public void drawRequestConfig(BuildRequest req, Eachable<BuildRequest> list){
drawRequestConfigCenter(req, content.item(req.config), "unloader-center");
}
@Override @Override
public boolean canDump(Tile tile, Tile to, Item item){ public boolean canDump(Tile tile, Tile to, Item item){
return !(to.block() instanceof StorageBlock); return !(to.block() instanceof StorageBlock);
@@ -109,7 +116,7 @@ public class Unloader extends Block{
UnloaderEntity entity = tile.entity(); UnloaderEntity entity = tile.entity();
Draw.color(entity.sortItem == null ? Color.clear : entity.sortItem.color); Draw.color(entity.sortItem == null ? Color.clear : entity.sortItem.color);
Fill.square(tile.worldx(), tile.worldy(), 1f); Draw.rect("unloader-center", tile.worldx(), tile.worldy());
Draw.color(); Draw.color();
} }

View File

@@ -22,10 +22,10 @@ import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.desktop.steam.*; import io.anuke.mindustry.desktop.steam.*;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.mod.Mods.*; import io.anuke.mindustry.mod.Mods.*;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.*; import io.anuke.mindustry.net.Net.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.*; import io.anuke.mindustry.ui.*;
import java.io.*; import java.io.*;
@@ -249,28 +249,18 @@ public class DesktopLauncher extends ClientLauncher{
} }
@Override @Override
public Array<FileHandle> getExternalMaps(){ public Array<FileHandle> getWorkshopContent(Class<? extends Publishable> type){
return !steam ? super.getExternalMaps() : SVars.workshop.getMapFiles(); return !steam ? super.getWorkshopContent(type) : SVars.workshop.getWorkshopFiles(type);
} }
@Override @Override
public Array<FileHandle> getExternalMods(){ public void viewListing(Publishable pub){
return !steam ? super.getExternalMods() : SVars.workshop.getModFiles(); SVars.workshop.viewListing(pub);
} }
@Override @Override
public void viewMapListing(Map map){ public void viewListingID(String id){
viewListing(map.file.parent().name()); SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + id);
}
@Override
public void viewListing(String mapid){
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + mapid);
}
@Override
public void viewMapListingInfo(Map map){
SVars.workshop.viewMapListingInfo(map);
} }
@Override @Override
@@ -284,8 +274,8 @@ public class DesktopLauncher extends ClientLauncher{
} }
@Override @Override
public void publishMap(Map map){ public void publish(Publishable pub){
SVars.workshop.publishMap(map); SVars.workshop.publish(pub);
} }
@Override @Override

View File

@@ -9,10 +9,11 @@ import io.anuke.arc.files.*;
import io.anuke.arc.function.*; import io.anuke.arc.function.*;
import io.anuke.arc.scene.ui.*; import io.anuke.arc.scene.ui.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*; import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.maps.*; import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.mod.Mods.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.ui.dialogs.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -20,10 +21,10 @@ import static io.anuke.mindustry.Vars.*;
public class SWorkshop implements SteamUGCCallback{ public class SWorkshop implements SteamUGCCallback{
public final SteamUGC ugc = new SteamUGC(this); public final SteamUGC ugc = new SteamUGC(this);
private Map lastMap; private ObjectMap<Class<? extends Publishable>, Array<FileHandle>> workshopFiles = new ObjectMap<>();
private Array<FileHandle> mapFiles;
private Array<FileHandle> modFiles;
private ObjectMap<SteamUGCQuery, BiConsumer<Array<SteamUGCDetails>, SteamResult>> detailHandlers = new ObjectMap<>(); private ObjectMap<SteamUGCQuery, BiConsumer<Array<SteamUGCDetails>, SteamResult>> detailHandlers = new ObjectMap<>();
private Array<Consumer<SteamPublishedFileID>> itemHandlers = new Array<>();
private ObjectMap<SteamPublishedFileID, Runnable> updatedHandlers = new ObjectMap<>();
public SWorkshop(){ public SWorkshop(){
int items = ugc.getNumSubscribedItems(); int items = ugc.getNumSubscribedItems();
@@ -36,71 +37,56 @@ public class SWorkshop implements SteamUGCCallback{
return new FileHandle(info.getFolder()); return new FileHandle(info.getFolder());
}).select(f -> f != null && f.list().length > 0); }).select(f -> f != null && f.list().length > 0);
mapFiles = folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(mapExtension)).map(f -> f.list()[0]); workshopFiles.put(Map.class, folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(mapExtension)).map(f -> f.list()[0]));
modFiles = folders.select(f -> f.child("mod.json").exists()); workshopFiles.put(Schematic.class, folders.select(f -> f.list().length == 1 && f.list()[0].extension().equals(schematicExtension)).map(f -> f.list()[0]));
workshopFiles.put(LoadedMod.class, folders.select(f -> f.child("mod.json").exists()));
if(!mapFiles.isEmpty()){ if(!workshopFiles.get(Map.class).isEmpty()){
SAchievement.downloadMapWorkshop.complete(); SAchievement.downloadMapWorkshop.complete();
} }
Log.info("Fetching {0} subscribed maps.", mapFiles.size); workshopFiles.each((type, list) -> {
Log.info("Fetching {0} subscribed mods.", modFiles.size); Log.info("Fetched content ({0}): {1}", type.getSimpleName(), list.size);
});
} }
public Array<FileHandle> getMapFiles(){ public Array<FileHandle> getWorkshopFiles(Class<? extends Publishable> type){
return mapFiles; return workshopFiles.getOr(type, () -> new Array<>(0));
} }
public Array<FileHandle> getModFiles(){ /** Publish a new item and submit an update for it.
return modFiles; * If it is already published, redirects to its page.*/
} public void publish(Publishable p){
if(p.hasSteamID()){
public void publishMap(Map map){ Log.info("Content already published, redirecting to ID.");
if(map.tags.containsKey("steamid")){ viewListing(p);
Log.info("Map already published, redirecting to ID.");
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + map.tags.get("steamid"));
return; return;
} }
//update author name when publishing if(!p.prePublish()){
map.tags.put("author", SVars.net.friends.getPersonaName()); return;
ui.editor.editor.getTags().put("author", map.tags.get("author")); }
ui.editor.save();
FloatingDialog dialog = new FloatingDialog("$confirm"); showPublish(id -> update(p, id, null));
dialog.setFillParent(false);
dialog.cont.add("$map.publish.confirm").width(600f).wrap();
dialog.addCloseButton();
dialog.buttons.addImageTextButton("$eula", Icon.linkSmall, () -> {
SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement");
}).size(210f, 64f);
dialog.buttons.addImageTextButton("$ok", Icon.checkSmall, () -> {
this.lastMap = map;
ugc.createItem(SVars.steamID, WorkshopFileType.Community);
ui.loadfrag.show("$map.publishing");
Log.info("Publish map " + map.name());
dialog.hide();
}).size(170f, 64f);
dialog.show();
} }
public void viewMapListingInfo(Map map){ /** Update an existing item with a changelog. */
String id = map.tags.get("steamid"); public void updateItem(Publishable p, String changelog){
String id = p.getSteamID();
long handle = Strings.parseLong(id, -1); long handle = Strings.parseLong(id, -1);
SteamPublishedFileID fid = new SteamPublishedFileID(handle); SteamPublishedFileID fid = new SteamPublishedFileID(handle);
update(p, fid, changelog);
}
Log.info("Requesting map listing view; id = " + id); /** Fetches info for an item, checking to make sure that it exists.*/
public void viewListing(Publishable p){
long handle = Strings.parseLong(p.getSteamID(), -1);
SteamPublishedFileID id = new SteamPublishedFileID(handle);
ui.loadfrag.show(); ui.loadfrag.show();
SteamUGCQuery query = ugc.createQueryUGCDetailsRequest(fid); query(ugc.createQueryUGCDetailsRequest(id), (detailsList, result) -> {
Log.info("POST " + query);
detailHandlers.put(query, (detailsList, result) -> {
ui.loadfrag.hide(); ui.loadfrag.hide();
Log.info("Map listing result: " + result + " " + detailsList);
if(result == SteamResult.OK){ if(result == SteamResult.OK){
SteamUGCDetails details = detailsList.first(); SteamUGCDetails details = detailsList.first();
if(details.getResult() == SteamResult.OK){ if(details.getResult() == SteamResult.OK){
@@ -112,54 +98,114 @@ public class SWorkshop implements SteamUGCCallback{
dialog.addCloseButton(); dialog.addCloseButton();
dialog.buttons.addImageTextButton("$view.workshop", Icon.linkSmall, () -> { dialog.buttons.addImageTextButton("$view.workshop", Icon.linkSmall, () -> {
platform.viewListing(id); viewListingID(id);
dialog.hide(); dialog.hide();
}).size(210f, 64f); }).size(210f, 64f);
dialog.buttons.addImageTextButton("$map.update", Icon.upgradeSmall, () -> { dialog.buttons.addImageTextButton("$workshop.update", Icon.upgradeSmall, () -> {
new FloatingDialog("$map.update"){{ new FloatingDialog("$workshop.update"){{
setFillParent(false); setFillParent(false);
cont.margin(10).add("$map.changelog").padRight(6f); cont.margin(10).add("$changelog").padRight(6f);
cont.row(); cont.row();
TextArea field = cont.addArea("", t -> {}).size(500f, 160f).get(); TextArea field = cont.addArea("", t -> {}).size(500f, 160f).get();
field.setMaxLength(400); field.setMaxLength(400);
buttons.defaults().size(120, 54).pad(4); buttons.defaults().size(120, 54).pad(4);
buttons.addButton("$ok", () -> { buttons.addButton("$ok", () -> {
ui.loadfrag.show("$map.publishing"); ui.loadfrag.show("$publishing");
lastMap = map; updateItem(p, field.getText().replace("\r", "\n"));
updateMap(map, details.getPublishedFileID(), field.getText().replace("\r", "\n"));
dialog.hide(); dialog.hide();
hide(); hide();
Log.info("Update map " + map.name());
}); });
buttons.addButton("$cancel", this::hide); buttons.addButton("$cancel", this::hide);
}}.show(); }}.show();
}).size(210f, 64f); }).size(210f, 64f);
dialog.show(); dialog.show();
}else{ }else{
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + SteamNativeHandle.getNativeHandle(details.getPublishedFileID())); SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + SteamNativeHandle.getNativeHandle(details.getPublishedFileID()));
} }
}else if(details.getResult() == SteamResult.FileNotFound){ }else if(details.getResult() == SteamResult.FileNotFound){
//force-remove tags p.removeSteamID();
ui.editor.editor.getTags().remove("steamid"); ui.showErrorMessage("$missing");
map.tags.remove("steamid");
ui.editor.save();
ui.showErrorMessage("$map.missing");
}else{ }else{
ui.showErrorMessage(Core.bundle.format("map.load.error", result.name())); ui.showErrorMessage(Core.bundle.format("workshop.error", result.name()));
} }
}else{ }else{
ui.showErrorMessage(Core.bundle.format("map.load.error", result.name())); ui.showErrorMessage(Core.bundle.format("workshop.error", result.name()));
} }
}); });
}
void viewListingID(SteamPublishedFileID id){
SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + SteamNativeHandle.getNativeHandle(id));
}
void update(Publishable p, SteamPublishedFileID id, String changelog){
String sid = SteamNativeHandle.getNativeHandle(id) + "";
updateItem(id, h -> {
if(p.steamDescription() != null){
ugc.setItemDescription(h, p.steamDescription());
}
Array<String> tags = p.extraTags();
tags.add(p.steamTag());
ugc.setItemTitle(h, p.steamTitle());
ugc.setItemTags(h, tags.toArray(String.class));
ugc.setItemPreview(h, p.createSteamPreview(sid).absolutePath());
ugc.setItemContent(h, p.createSteamFolder(sid).absolutePath());
if(changelog == null){
ugc.setItemVisibility(h, PublishedFileVisibility.Private);
}
ugc.submitItemUpdate(h, changelog == null ? "<Created>" : changelog);
}, () -> p.addSteamID(sid));
}
void showPublish(Consumer<SteamPublishedFileID> published){
FloatingDialog dialog = new FloatingDialog("$confirm");
dialog.setFillParent(false);
dialog.cont.add("$publish.confirm").width(600f).wrap();
dialog.addCloseButton();
dialog.buttons.addImageTextButton("$eula", Icon.linkSmall,
() -> SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement"))
.size(210f, 64f);
dialog.buttons.addImageTextButton("$ok", Icon.checkSmall, () -> {
ugc.createItem(SVars.steamID, WorkshopFileType.Community);
ui.loadfrag.show("$publishing");
dialog.hide();
itemHandlers.add(published);
}).size(170f, 64f);
dialog.show();
}
void query(SteamUGCQuery query, BiConsumer<Array<SteamUGCDetails>, SteamResult> handler){
Log.info("POST QUERY " + query);
detailHandlers.put(query, handler);
ugc.sendQueryUGCRequest(query); ugc.sendQueryUGCRequest(query);
} }
void updateItem(SteamPublishedFileID publishedFileID, Consumer<SteamUGCUpdateHandle> tagger, Runnable updated){
SteamUGCUpdateHandle h = ugc.startItemUpdate(SVars.steamID, publishedFileID);
tagger.accept(h);
ItemUpdateInfo info = new ItemUpdateInfo();
ui.loadfrag.setProgress(() -> {
ItemUpdateStatus status = ugc.getItemUpdateProgress(h, info);
ui.loadfrag.setText("$" + status.name().toLowerCase());
if(status == ItemUpdateStatus.Invalid){
ui.loadfrag.setText("$done");
return 1f;
}
return (float)status.ordinal() / (float)ItemUpdateStatus.values().length;
});
updatedHandlers.put(publishedFileID, updated);
}
@Override @Override
public void onRequestUGCDetails(SteamUGCDetails details, SteamResult result){ public void onRequestUGCDetails(SteamUGCDetails details, SteamResult result){
@@ -167,7 +213,7 @@ public class SWorkshop implements SteamUGCCallback{
@Override @Override
public void onUGCQueryCompleted(SteamUGCQuery query, int numResultsReturned, int totalMatchingResults, boolean isCachedData, SteamResult result){ public void onUGCQueryCompleted(SteamUGCQuery query, int numResultsReturned, int totalMatchingResults, boolean isCachedData, SteamResult result){
Log.info("GET " + query); Log.info("GET QUERY " + query);
if(detailHandlers.containsKey(query)){ if(detailHandlers.containsKey(query)){
if(numResultsReturned > 0){ if(numResultsReturned > 0){
@@ -202,55 +248,15 @@ public class SWorkshop implements SteamUGCCallback{
@Override @Override
public void onCreateItem(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){ public void onCreateItem(SteamPublishedFileID publishedFileID, boolean needsToAcceptWLA, SteamResult result){
if(lastMap == null){ if(!itemHandlers.isEmpty()){
Log.err("No map to publish?"); if(result == SteamResult.OK){
return; itemHandlers.first().accept(publishedFileID);
} }else{
ui.showErrorMessage(Core.bundle.format("publish.error ", result.name()));
//SVars.net.friends.activateGameOverlayToWebPage("steam://url/CommunityFilePage/" + publishedFileID.toString());
Map map = lastMap;
Log.info("Create item {0} result {1} {2}", SteamNativeHandle.getNativeHandle(publishedFileID), result, needsToAcceptWLA);
if(result == SteamResult.OK){
updateMap(map, publishedFileID, "<Map Created>");
}else{
ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name()));
}
lastMap = null;
}
void updateMap(Map map, SteamPublishedFileID publishedFileID, String changelog){
SteamUGCUpdateHandle h = ugc.startItemUpdate(SVars.steamID, publishedFileID);
Gamemode mode = Gamemode.attack.valid(map) ? Gamemode.attack : Gamemode.survival;
FileHandle mapFile = tmpDirectory.child("map_" + publishedFileID.toString()).child("map.msav");
lastMap.file.copyTo(mapFile);
Log.info(mapFile.parent().absolutePath());
Log.info(map.previewFile().absolutePath());
ugc.setItemTitle(h, map.name());
ugc.setItemDescription(h, map.description());
ugc.setItemTags(h, new String[]{"map", mode.name()});
ugc.setItemVisibility(h, PublishedFileVisibility.Private);
ugc.setItemPreview(h, map.previewFile().absolutePath());
ugc.setItemContent(h, mapFile.parent().absolutePath());
ugc.addItemKeyValueTag(h, "mode", mode.name());
ugc.submitItemUpdate(h, changelog);
ItemUpdateInfo info = new ItemUpdateInfo();
ui.loadfrag.setProgress(() -> {
ItemUpdateStatus status = ugc.getItemUpdateProgress(h, info);
ui.loadfrag.setText("$" + status.name().toLowerCase());
if(status == ItemUpdateStatus.Invalid){
ui.loadfrag.setText("$done");
return 1f;
} }
return (float)status.ordinal() / (float)ItemUpdateStatus.values().length;
}); itemHandlers.remove(0);
}
} }
@Override @Override
@@ -263,15 +269,12 @@ public class SWorkshop implements SteamUGCCallback{
if(needsToAcceptWLA){ if(needsToAcceptWLA){
SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement"); SVars.net.friends.activateGameOverlayToWebPage("https://steamcommunity.com/sharedfiles/workshoplegalagreement");
} }
ui.editor.editor.getTags().put("steamid", SteamNativeHandle.getNativeHandle(publishedFileID) + "");
try{ if(updatedHandlers.containsKey(publishedFileID)){
ui.editor.save(); updatedHandlers.get(publishedFileID).run();
}catch(Exception e){
Log.err(e);
} }
Events.fire(new MapPublishEvent());
}else{ }else{
ui.showErrorMessage(Core.bundle.format("map.publish.error ", result.name())); ui.showErrorMessage(Core.bundle.format("publish.error ", result.name()));
} }
} }

View File

@@ -1,3 +1,3 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=983942d521c1e1714fac3ddfc54ee7248734b2ed archash=e82f446a81abba5d6f712b9053c1b84ec9a73156