Compare commits

..

27 Commits
v98 ... v99

Author SHA1 Message Date
Anuken
a5cec84be1 Fixed #917 2019-10-27 22:18:53 -04:00
Anuken
a299c39d7b Bugfixes 2019-10-27 20:30:31 -04:00
Anuken
685c41ebf5 Slight balancing 2019-10-27 18:16:41 -04:00
Anuken
1ec09a4679 Merge remote-tracking branch 'origin/master' 2019-10-27 18:07:46 -04:00
Anuken
9e4e58baf9 Added mod dependencies 2019-10-27 18:07:18 -04:00
Patrick 'Quezler' Mounier
bd5c98cc0d 🚧 Add insulator wall (#840)
* Stash insulator prototype

* Fix automatic wires when placing non-powernodes

* Implement requested changes

Stopping lightning has yet to be added.

* ❤️

* Implement lightning absorption

* Visually distinguish insulated power lines

* Stop lightning infront of wall

* Highlight insulators instead of recipients

* Attempt to implement requested changes

* Implement requested changes

* Remove spaces between if and (

* Snap lightning to insulated blocks

* Update bundle.properties
2019-10-27 17:41:00 -04:00
Anuken
ef2817513a Layout fixes 2019-10-27 16:26:33 -04:00
Anuken
d28ae1b304 Bugfixes 2019-10-27 16:05:13 -04:00
Anuken
101f5351bd Bugfixes 2019-10-27 15:25:15 -04:00
Anuken
e103d62b75 Bugfixes / Rework of save discovery system 2019-10-27 14:58:26 -04:00
Patrick 'Quezler' Mounier
d969741f90 ️ Toggle (all) powernode connections by doubleclicking (#907)
* Toggle (all) powernode connections by doubleclicking

* Mimic return false of the above if
2019-10-27 11:01:50 -04:00
Anuken
8a1ea7fd19 Merge remote-tracking branch 'origin/master' 2019-10-27 10:51:41 -04:00
Anuken
34db321577 Disabled 'import github' button on Android because Android is garbage and can't even do something as simple as an HTTP request without 5 workaround for different versions 2019-10-27 10:51:35 -04:00
Patrick 'Quezler' Mounier
cc57f0b19b Fix visual armored conveyor glitch (#891) 2019-10-26 21:23:16 -04:00
Anuken
4e946ae52b Fixed #903 / Fixed #902 2019-10-26 21:20:05 -04:00
키에르
236ed74cae Fix onConnect (#896)
* Update NetClient.java

* import class

* Update NetClient.java
2019-10-26 08:58:25 -04:00
Anuken
cd07d3b13d Update Block.java 2019-10-26 08:52:53 -04:00
Anuken
b582c79593 Update PowerNode.java 2019-10-26 08:51:26 -04:00
Anuken
df0d81db7c Update Tile.java 2019-10-26 08:49:52 -04:00
Patrick 'Quezler' Mounier
a628134a89 Add mobile tutorial intro (#897) 2019-10-26 08:05:20 -04:00
Anuken
708acdfe95 Merge remote-tracking branch 'origin/master' 2019-10-25 15:06:16 -04:00
Anuken
f84a7e7661 Mobile layout fix 2019-10-25 15:06:12 -04:00
Anuken
5fde533bcc a 2019-10-25 15:01:51 -04:00
Anuken
16e57f6f24 Update bug_report.md 2019-10-25 15:01:45 -04:00
Anuken
44dc250a2b Various bugfixes 2019-10-25 14:48:44 -04:00
Anuken
742fb3dece Fixed #880 2019-10-25 14:08:31 -04:00
Anuken
bd0a6636e6 Minor bugfixes 2019-10-25 14:00:38 -04:00
52 changed files with 12938 additions and 12629 deletions

View File

@@ -3,10 +3,10 @@ name: Bug report
about: Create a report to help fix an issue. about: Create a report to help fix an issue.
--- ---
**Platform**: (Android/iOS/Mac/Windows/Linux) **Platform**: *Android/iOS/Mac/Windows/Linux*
**Build**: (The build number under the title in the main menu. Required.) **Build**: *The build number under the title in the main menu. Required.*
**Issue**: (Explain your issue in detail.) **Issue**: *Explain your issue in detail.*
**Steps to reproduce**: (How you happened across the issue, and what you were doing at the time.) **Steps to reproduce**: *How you happened across the issue, and what you were doing at the time.*

View File

@@ -10,7 +10,7 @@ script:
- git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds - git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds
- cd ../MindustryBuilds - cd ../MindustryBuilds
- echo ${TRAVIS_TAG} - echo ${TRAVIS_TAG}
- if [ -n "$TRAVIS_TAG" ]; then echo versionName=4-fdroid-${TRAVIS_TAG:1}$'\n'versionCode=${TRAVIS_TAG:1} > version_fdroid.txt; git add .; git commit -m "Updating to build ${TRAVIS_TAG}"; fi - if [ -n "$TRAVIS_TAG" ]; then echo versionName=5-fdroid-${TRAVIS_TAG:1}$'\n'versionCode=${TRAVIS_TAG:1} > version_fdroid.txt; git add .; git commit -m "Updating to build ${TRAVIS_TAG}"; fi
- git tag ${TRAVIS_BUILD_NUMBER} - git tag ${TRAVIS_BUILD_NUMBER}
- git config --global user.name "Build Uploader" - git config --global user.name "Build Uploader"
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git push https://Anuken:${GH_PUSH_TOKEN}@github.com/Anuken/MindustryBuilds ${TRAVIS_BUILD_NUMBER}; git push https://Anuken:${GH_PUSH_TOKEN}@github.com/Anuken/MindustryBuilds; fi - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git push https://Anuken:${GH_PUSH_TOKEN}@github.com/Anuken/MindustryBuilds ${TRAVIS_BUILD_NUMBER}; git push https://Anuken:${GH_PUSH_TOKEN}@github.com/Anuken/MindustryBuilds; fi

View File

@@ -14,7 +14,6 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:isGame="true" android:isGame="true"
android:appCategory="game" android:appCategory="game"
android:usesCleartextTraffic="true"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/GdxTheme" android:fullBackupContent="@xml/backup_rules"> android:theme="@style/GdxTheme" android:fullBackupContent="@xml/backup_rules">
<meta-data android:name="android.max_aspect" android:value="2.1"/> <meta-data android:name="android.max_aspect" android:value="2.1"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -94,6 +94,8 @@ mods.report = Report Bug
mod.enabled = [lightgray]Enabled mod.enabled = [lightgray]Enabled
mod.disabled = [scarlet]Disabled mod.disabled = [scarlet]Disabled
mod.disable = Disable mod.disable = Disable
mod.missingdependencies = [scarlet]Missing dependencies: {0}
mod.nowdisabled = [scarlet]Mod '{0}' is missing dependencies:[accent] {1}\n[lightgray]These mods need to be downloaded first.\nThis mod will be automatically disabled.
mod.enable = Enable mod.enable = Enable
mod.requiresrestart = The game will now close to apply the mod changes. mod.requiresrestart = The game will now close to apply the mod changes.
mod.reloadrequired = [scarlet]Reload Required mod.reloadrequired = [scarlet]Reload Required
@@ -230,7 +232,7 @@ data.export = Export Data
data.import = Import Data data.import = Import Data
data.exported = Data exported. data.exported = Data exported.
data.invalid = This isn't valid game data. data.invalid = This isn't valid game data.
data.import.confirm = Importing external data will erase[scarlet] all[] your current game data.\n[accent]This cannot be undone![]\n\nOnce the data is imported, your game will exit immediately. data.import.confirm = Importing external data will overwrite[scarlet] all[] your current game data.\n[accent]This cannot be undone![]\n\nOnce the data is imported, your game will exit immediately.
classic.export = Export Classic Data classic.export = Export Classic Data
classic.export.text = [accent]Mindustry[] has just had a major update.\nClassic (v3.5 build 40) save or map data has been detected. Would you like to export these saves to your phone's home folder, for use in the Mindustry Classic app? classic.export.text = [accent]Mindustry[] has just had a major update.\nClassic (v3.5 build 40) save or map data has been detected. Would you like to export these saves to your phone's home folder, for use in the Mindustry Classic app?
quit.confirm = Are you sure you want to quit? quit.confirm = Are you sure you want to quit?
@@ -853,6 +855,8 @@ block.copper-wall.name = Copper Wall
block.copper-wall-large.name = Large Copper Wall block.copper-wall-large.name = Large Copper Wall
block.titanium-wall.name = Titanium Wall block.titanium-wall.name = Titanium Wall
block.titanium-wall-large.name = Large Titanium Wall block.titanium-wall-large.name = Large Titanium Wall
block.plastanium-wall.name = Plastanium Wall
block.plastanium-wall-large.name = Large Plastanium Wall
block.phase-wall.name = Phase Wall block.phase-wall.name = Phase Wall
block.phase-wall-large.name = Large Phase Wall block.phase-wall-large.name = Large Phase Wall
block.thorium-wall.name = Thorium Wall block.thorium-wall.name = Thorium Wall
@@ -991,6 +995,7 @@ unit.lich.name = Lich
unit.reaper.name = Reaper unit.reaper.name = Reaper
tutorial.next = [lightgray]<Tap to continue> tutorial.next = [lightgray]<Tap to continue>
tutorial.intro = You have entered the[scarlet] Mindustry Tutorial.[]\nUse [[WASD] to move.\n[accent] Hold [[Ctrl] while scrolling[] to zoom in and out.\nBegin by[accent] mining copper[]. Move close to it, then tap a copper ore vein near your core to do this.\n\n[accent]{0}/{1} copper tutorial.intro = You have entered the[scarlet] Mindustry Tutorial.[]\nUse [[WASD] to move.\n[accent] Hold [[Ctrl] while scrolling[] to zoom in and out.\nBegin by[accent] mining copper[]. Move close to it, then tap a copper ore vein near your core to do this.\n\n[accent]{0}/{1} copper
tutorial.intro.mobile = You have entered the[scarlet] Mindustry Tutorial.[]\nSwipe the screen to move.\n[accent] Pinch with 2 fingers [] to zoom in and out.\nBegin by[accent] mining copper[]. Move close to it, then tap a copper ore vein near your core to do this.\n\n[accent]{0}/{1} copper
tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nClick the drill tab in the bottom right.\nSelect the[accent] mechanical drill[]. Place it on a copper vein by clicking.\n[accent]Right-click[] to stop building. tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nClick the drill tab in the bottom right.\nSelect the[accent] mechanical drill[]. Place it on a copper vein by clicking.\n[accent]Right-click[] to stop building.
tutorial.drill.mobile = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nTap the drill tab in the bottom right.\nSelect the[accent] mechanical drill[].\nPlace it on a copper vein by tapping, then press the[accent] checkmark[] below to confirm your selection.\nPress the[accent] X button[] to cancel placement. tutorial.drill.mobile = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nTap the drill tab in the bottom right.\nSelect the[accent] mechanical drill[].\nPlace it on a copper vein by tapping, then press the[accent] checkmark[] below to confirm your selection.\nPress the[accent] X button[] to cancel placement.
tutorial.blockinfo = Each block has different stats. Each drill can only mine certain ores.\nTo check a block's info and stats,[accent] tap the "?" button while selecting it in the build menu.[]\n\n[accent]Access the Mechanical Drill's stats now.[] tutorial.blockinfo = Each block has different stats. Each drill can only mine certain ores.\nTo check a block's info and stats,[accent] tap the "?" button while selecting it in the build menu.[]\n\n[accent]Access the Mechanical Drill's stats now.[]
@@ -1075,6 +1080,8 @@ block.copper-wall.description = A cheap defensive block.\nUseful for protecting
block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles.
block.titanium-wall.description = A moderately strong defensive block.\nProvides moderate protection from enemies. block.titanium-wall.description = A moderately strong defensive block.\nProvides moderate protection from enemies.
block.titanium-wall-large.description = A moderately strong defensive block.\nProvides moderate protection from enemies.\nSpans multiple tiles. block.titanium-wall-large.description = A moderately strong defensive block.\nProvides moderate protection from enemies.\nSpans multiple tiles.
block.plastanium-wall.description = A special type of wall that absorbs electric arcs and blocks automatic power node connections.
block.plastanium-wall-large.description = A special type of wall that absorbs electric arcs and blocks automatic power node connections.\nSpans multiple tiles.
block.thorium-wall.description = A strong defensive block.\nDecent protection from enemies. block.thorium-wall.description = A strong defensive block.\nDecent protection from enemies.
block.thorium-wall-large.description = A strong defensive block.\nDecent protection from enemies.\nSpans multiple tiles. block.thorium-wall-large.description = A strong defensive block.\nDecent protection from enemies.\nSpans multiple tiles.
block.phase-wall.description = A wall coated with special phase-based reflective compound. Deflects most bullets upon impact. block.phase-wall.description = A wall coated with special phase-based reflective compound. Deflects most bullets upon impact.

View File

@@ -82,3 +82,4 @@ AzariasB
amrsoll amrsoll
ねらひかだ ねらひかだ
Draco Draco
Quezler

Binary file not shown.

Before

Width:  |  Height:  |  Size: 727 B

After

Width:  |  Height:  |  Size: 737 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 719 KiB

After

Width:  |  Height:  |  Size: 930 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 KiB

After

Width:  |  Height:  |  Size: 331 KiB

View File

@@ -52,7 +52,7 @@ public class Blocks implements ContentList{
//defense //defense
scrapWall, scrapWallLarge, scrapWallHuge, scrapWallGigantic, thruster, //ok, these names are getting ridiculous, but at least I don't have humongous walls yet scrapWall, scrapWallLarge, scrapWallHuge, scrapWallGigantic, thruster, //ok, these names are getting ridiculous, but at least I don't have humongous walls yet
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge, copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, plastaniumWall, plastaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
phaseWall, phaseWallLarge, surgeWall, surgeWallLarge, mender, mendProjector, overdriveProjector, forceProjector, shockMine, phaseWall, phaseWallLarge, surgeWall, surgeWallLarge, mender, mendProjector, overdriveProjector, forceProjector, shockMine,
//transport //transport
@@ -795,6 +795,19 @@ public class Blocks implements ContentList{
size = 2; size = 2;
}}; }};
plastaniumWall = new Wall("plastanium-wall"){{
requirements(Category.defense, ItemStack.with(Items.plastanium, 5, Items.metaglass, 2));
health = 190 * wallHealthMultiplier;
insulated = true;
}};
plastaniumWallLarge = new Wall("plastanium-wall-large"){{
requirements(Category.defense, ItemStack.mult(plastaniumWall.requirements, 4));
health = 190 * wallHealthMultiplier * 4;
size = 2;
insulated = true;
}};
thoriumWall = new Wall("thorium-wall"){{ thoriumWall = new Wall("thorium-wall"){{
requirements(Category.defense, ItemStack.with(Items.thorium, 6)); requirements(Category.defense, ItemStack.with(Items.thorium, 6));
health = 200 * wallHealthMultiplier; health = 200 * wallHealthMultiplier;

View File

@@ -104,6 +104,11 @@ public class TechTree implements ContentList{
node(door, () -> { node(door, () -> {
node(doorLarge); node(doorLarge);
}); });
node(plastaniumWall, () -> {
node(plastaniumWallLarge, () -> {
});
});
node(titaniumWallLarge); node(titaniumWallLarge);
node(thoriumWall, () -> { node(thoriumWall, () -> {
node(thoriumWallLarge); node(thoriumWallLarge);

View File

@@ -205,8 +205,7 @@ public class NetClient implements ApplicationListener{
state.set(State.menu); state.set(State.menu);
logic.reset(); logic.reset();
Vars.netClient.beginConnecting(); ui.join.connect(ip, port);
net.connect(ip, port, () -> {});
} }
@Remote(targets = Loc.client) @Remote(targets = Loc.client)

View File

@@ -331,6 +331,8 @@ public class NetServer implements ApplicationListener{
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?"); player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
}else if(found.isLocal){ }else if(found.isLocal){
player.sendMessage("[scarlet]Local players cannot be kicked."); player.sendMessage("[scarlet]Local players cannot be kicked.");
}else if(found.getTeam() != player.getTeam()){
player.sendMessage("[scarlet]Only players on your team can be kicked.");
}else{ }else{
if(!vtime.get()){ if(!vtime.get()){
player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks."); player.sendMessage("[scarlet]You must wait " + voteTime/60 + " minutes between votekicks.");

View File

@@ -185,6 +185,10 @@ public class World{
Events.fire(new WorldLoadEvent()); Events.fire(new WorldLoadEvent());
} }
public void setGenerating(boolean gen){
this.generating = gen;
}
public boolean isGenerating(){ public boolean isGenerating(){
return generating; return generating;
} }

View File

@@ -20,8 +20,9 @@ import io.anuke.mindustry.entities.type.Unit;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.bulletGroup; import static io.anuke.mindustry.Vars.*;
public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
public static final float lifetime = 10f; public static final float lifetime = 10f;
@@ -34,7 +35,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
private static final float hitRange = 30f; private static final float hitRange = 30f;
private static int lastSeed = 0; private static int lastSeed = 0;
private Array<Position> lines = new Array<>(); private Array<Vector2> lines = new Array<>();
private Color color = Pal.lancerLaser; private Color color = Pal.lancerLaser;
/** For pooling use only. Do not call directly! */ /** For pooling use only. Do not call directly! */
@@ -61,10 +62,30 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
random.setSeed(seed); random.setSeed(seed);
hit.clear(); hit.clear();
boolean[] bhit = {false};
for(int i = 0; i < length / 2; i++){ for(int i = 0; i < length / 2; i++){
Bullet.create(Bullets.damageLightning, l, team, x, y, 0f, 1f, 1f, dmg); Bullet.create(Bullets.damageLightning, l, team, x, y, 0f, 1f, 1f, dmg);
l.lines.add(new Vector2(x + Mathf.range(3f), y + Mathf.range(3f))); l.lines.add(new Vector2(x + Mathf.range(3f), y + Mathf.range(3f)));
if(l.lines.size > 1){
bhit[0] = false;
Position from = l.lines.get(l.lines.size - 2);
Position to = l.lines.get(l.lines.size - 1);
world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> {
Tile tile = world.ltile(wx, wy);
if(tile != null && tile.block().insulated){
bhit[0] = true;
//snap it instead of removing
l.lines.get(l.lines.size -1).set(wx * tilesize, wy * tilesize);
return true;
}
return false;
});
if(bhit[0]) break;
}
rect.setSize(hitRange).setCenter(x, y); rect.setSize(hitRange).setCenter(x, y);
entities.clear(); entities.clear();
if(hit.size < maxChain){ if(hit.size < maxChain){
@@ -83,6 +104,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
y = furthest.y; y = furthest.y;
}else{ }else{
rotation += random.range(20f); rotation += random.range(20f);
x += Angles.trnsx(rotation, hitRange / 2f); x += Angles.trnsx(rotation, hitRange / 2f);
y += Angles.trnsy(rotation, hitRange / 2f); y += Angles.trnsy(rotation, hitRange / 2f);
} }

View File

@@ -357,8 +357,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
public void drawOver(){ public void drawOver(){
if(dead) return; if(dead) return;
if(isBuilding()){ if(isBuilding() && isBuilding){
if(!state.isPaused() && isBuilding){ if(!state.isPaused()){
drawBuilding(); drawBuilding();
} }
}else{ }else{
@@ -458,7 +458,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{
} }
//mine only when not building //mine only when not building
if(buildRequest() == null){ if(buildRequest() == null || !isBuilding){
updateMining(); updateMining();
} }
} }

View File

@@ -66,14 +66,8 @@ public class GlobalData{
throw new IllegalArgumentException("Not valid save data."); throw new IllegalArgumentException("Not valid save data.");
} }
//purge existing data //purge existing tmp data, keep everything else
for(FileHandle f : base.list()){ tmpDirectory.deleteDirectory();
if(f.isDirectory()){
f.deleteDirectory();
}else if(!f.name().equals("zipdata.zip")){
f.delete();
}
}
zipped.walk(f -> f.copyTo(base.child(f.path()))); zipped.walk(f -> f.copyTo(base.child(f.path())));
dest.delete(); dest.delete();

View File

@@ -22,14 +22,12 @@ public class MusicControl{
public Array<Music> darkMusic = Array.with(); public Array<Music> darkMusic = Array.with();
private Music lastRandomPlayed; private Music lastRandomPlayed;
private Interval timer = new Interval(); private Interval timer = new Interval();
private @Nullable private @Nullable Music current;
Music current;
private float fade; private float fade;
private boolean silenced; private boolean silenced;
public MusicControl(){ public MusicControl(){
Events.on(ClientLoadEvent.class, e -> reload()); Events.on(ClientLoadEvent.class, e -> reload());
Events.on(ContentReloadEvent.class, e -> reload());
//only run music 10 seconds after a wave spawns //only run music 10 seconds after a wave spawns
Events.on(WaveEvent.class, e -> Time.run(60f * 10f, () -> { Events.on(WaveEvent.class, e -> Time.run(60f * 10f, () -> {

View File

@@ -22,13 +22,12 @@ import java.util.*;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Saves{ public class Saves{
private int nextSlot;
private Array<SaveSlot> saves = new Array<>(); private Array<SaveSlot> saves = new Array<>();
private IntMap<SaveSlot> saveMap = new IntMap<>();
private SaveSlot current; private SaveSlot current;
private AsyncExecutor previewExecutor = new AsyncExecutor(1); private AsyncExecutor previewExecutor = new AsyncExecutor(1);
private boolean saving; private boolean saving;
private float time; private float time;
private FileHandle zoneFile;
private long totalPlaytime; private long totalPlaytime;
private long lastTimestamp; private long lastTimestamp;
@@ -47,16 +46,13 @@ public class Saves{
public void load(){ public void load(){
saves.clear(); saves.clear();
IntArray slots = Core.settings.getObject("save-slots", IntArray.class, IntArray::new); zoneFile = saveDirectory.child("-1.msav");
for(int i = 0; i < slots.size; i++){ for(FileHandle file : saveDirectory.list()){
int index = slots.get(i); if(!file.name().contains("backup") && SaveIO.isSaveValid(file)){
if(SaveIO.isSaveValid(index)){ SaveSlot slot = new SaveSlot(file);
SaveSlot slot = new SaveSlot(index);
saves.add(slot); saves.add(slot);
saveMap.put(slot.index, slot); slot.meta = SaveIO.getMeta(file);
slot.meta = SaveIO.getMeta(index);
nextSlot = Math.max(index + 1, nextSlot);
} }
} }
} }
@@ -110,73 +106,63 @@ public class Saves{
} }
public void zoneSave(){ public void zoneSave(){
SaveSlot slot = new SaveSlot(-1); SaveSlot slot = new SaveSlot(zoneFile);
slot.setName("zone"); slot.setName("zone");
saves.remove(s -> s.index == -1); saves.remove(s -> s.file.equals(zoneFile));
saves.add(slot); saves.add(slot);
saveMap.put(slot.index, slot);
slot.save(); slot.save();
saveSlots();
} }
public SaveSlot addSave(String name){ public SaveSlot addSave(String name){
SaveSlot slot = new SaveSlot(nextSlot); SaveSlot slot = new SaveSlot(getNextSlotFile());
nextSlot++;
slot.setName(name); slot.setName(name);
saves.add(slot); saves.add(slot);
saveMap.put(slot.index, slot);
slot.save(); slot.save();
saveSlots();
return slot; return slot;
} }
public SaveSlot importSave(FileHandle file) throws IOException{ public SaveSlot importSave(FileHandle file) throws IOException{
SaveSlot slot = new SaveSlot(nextSlot); SaveSlot slot = new SaveSlot(getNextSlotFile());
slot.importFile(file); slot.importFile(file);
nextSlot++;
slot.setName(file.nameWithoutExtension()); slot.setName(file.nameWithoutExtension());
saves.add(slot); saves.add(slot);
saveMap.put(slot.index, slot); slot.meta = SaveIO.getMeta(slot.file);
slot.meta = SaveIO.getMeta(slot.index);
current = slot; current = slot;
saveSlots();
return slot; return slot;
} }
public SaveSlot getZoneSlot(){ public SaveSlot getZoneSlot(){
SaveSlot slot = getByID(-1); SaveSlot slot = getSaveSlots().find(s -> s.file.equals(zoneFile));
return slot == null || slot.getZone() == null ? null : slot; return slot == null || slot.getZone() == null ? null : slot;
} }
public SaveSlot getByID(int id){ public FileHandle getNextSlotFile(){
return saveMap.get(id); int i = 0;
FileHandle file;
while((file = saveDirectory.child(i + "." + saveExtension)).exists()){
i ++;
}
return file;
} }
public Array<SaveSlot> getSaveSlots(){ public Array<SaveSlot> getSaveSlots(){
return saves; return saves;
} }
private void saveSlots(){
IntArray result = new IntArray(saves.size);
for(int i = 0; i < saves.size; i++) result.add(saves.get(i).index);
Core.settings.putObject("save-slots", result);
Core.settings.save();
}
public class SaveSlot{ public class SaveSlot{
public final int index; //public final int index;
public final FileHandle file;
boolean requestedPreview; boolean requestedPreview;
SaveMeta meta; SaveMeta meta;
public SaveSlot(int index){ public SaveSlot(FileHandle file){
this.index = index; this.file = file;
} }
public void load() throws SaveException{ public void load() throws SaveException{
try{ try{
SaveIO.loadFromSlot(index); SaveIO.load(file);
meta = SaveIO.getMeta(index); meta = SaveIO.getMeta(file);
current = this; current = this;
totalPlaytime = meta.timePlayed; totalPlaytime = meta.timePlayed;
savePreview(); savePreview();
@@ -190,8 +176,8 @@ public class Saves{
long prev = totalPlaytime; long prev = totalPlaytime;
totalPlaytime = time; totalPlaytime = time;
SaveIO.saveToSlot(index); SaveIO.save(file);
meta = SaveIO.getMeta(index); meta = SaveIO.getMeta(file);
if(!state.is(State.menu)){ if(!state.is(State.menu)){
current = this; current = this;
} }
@@ -226,8 +212,12 @@ public class Saves{
return null; return null;
} }
private String index(){
return file.nameWithoutExtension();
}
private FileHandle previewFile(){ private FileHandle previewFile(){
return mapPreviewDirectory.child("save_slot_" + index + ".png"); return mapPreviewDirectory.child("save_slot_" + index() + ".png");
} }
private FileHandle loadPreviewFile(){ private FileHandle loadPreviewFile(){
@@ -266,11 +256,11 @@ public class Saves{
} }
public String getName(){ public String getName(){
return Core.settings.getString("save-" + index + "-name", "untitled"); return Core.settings.getString("save-" + index() + "-name", "untitled");
} }
public void setName(String name){ public void setName(String name){
Core.settings.put("save-" + index + "-name", name); Core.settings.put("save-" + index() + "-name", name);
Core.settings.save(); Core.settings.save();
} }
@@ -295,34 +285,33 @@ public class Saves{
} }
public boolean isAutosave(){ public boolean isAutosave(){
return Core.settings.getBool("save-" + index + "-autosave", true); return Core.settings.getBool("save-" + index() + "-autosave", true);
} }
public void setAutosave(boolean save){ public void setAutosave(boolean save){
Core.settings.put("save-" + index + "-autosave", save); Core.settings.put("save-" + index() + "-autosave", save);
Core.settings.save(); Core.settings.save();
} }
public void importFile(FileHandle file) throws IOException{ public void importFile(FileHandle from) throws IOException{
try{ try{
file.copyTo(SaveIO.fileFor(index)); from.copyTo(file);
}catch(Exception e){ }catch(Exception e){
throw new IOException(e); throw new IOException(e);
} }
} }
public void exportFile(FileHandle file) throws IOException{ public void exportFile(FileHandle to) throws IOException{
try{ try{
SaveIO.fileFor(index).copyTo(file); file.copyTo(to);
}catch(Exception e){ }catch(Exception e){
throw new IOException(e); throw new IOException(e);
} }
} }
public void delete(){ public void delete(){
SaveIO.fileFor(index).delete(); file.delete();
saves.removeValue(this, true); saves.removeValue(this, true);
saveMap.remove(index);
if(this == current){ if(this == current){
current = null; current = null;
} }
@@ -330,8 +319,6 @@ public class Saves{
if(Core.assets.isLoaded(loadPreviewFile().path())){ if(Core.assets.isLoaded(loadPreviewFile().path())){
Core.assets.unload(loadPreviewFile().path()); Core.assets.unload(loadPreviewFile().path());
} }
saveSlots();
} }
} }
} }

View File

@@ -19,6 +19,7 @@ import io.anuke.mindustry.input.*;
import io.anuke.mindustry.input.PlaceUtils.*; import io.anuke.mindustry.input.PlaceUtils.*;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.*;
import java.io.*; import java.io.*;
import java.util.zip.*; import java.util.zip.*;
@@ -44,6 +45,12 @@ public class Schematics implements Loadable{
previews.clear(); previews.clear();
shadowBuffer.dispose(); shadowBuffer.dispose();
}); });
Events.on(ContentReloadEvent.class, event -> {
previews.each((schem, m) -> m.dispose());
previews.clear();
load();
});
} }
@Override @Override
@@ -63,9 +70,9 @@ public class Schematics implements Loadable{
all.sort(); all.sort();
Core.app.post(() -> { if(shadowBuffer == null){
shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 8, maxSchematicSize + padding + 8); Core.app.post(() -> shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 8, maxSchematicSize + padding + 8));
}); }
} }
public void overwrite(Schematic target, Schematic newSchematic){ public void overwrite(Schematic target, Schematic newSchematic){
@@ -205,7 +212,7 @@ public class Schematics implements Loadable{
/** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */ /** 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){ 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()); 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() || !s.block.unlocked());
} }
/** Adds a schematic to the list, also copying it into the files.*/ /** Adds a schematic to the list, also copying it into the files.*/
@@ -251,7 +258,7 @@ public class Schematics implements Loadable{
for(int cy = y; cy <= y2; cy++){ for(int cy = y; cy <= y2; cy++){
Tile linked = world.ltile(cx, cy); Tile linked = world.ltile(cx, cy);
if(linked != null && linked.entity != null && linked.entity.block.isVisible()){ if(linked != null && linked.entity != null && linked.entity.block.isVisible() && !(linked.block() instanceof BuildBlock)){
int top = linked.block().size/2; int top = linked.block().size/2;
int bot = linked.block().size % 2 == 1 ? -linked.block().size/2 : -(linked.block().size - 1)/2; int bot = linked.block().size % 2 == 1 ? -linked.block().size/2 : -(linked.block().size - 1)/2;
minx = Math.min(linked.x + bot, minx); minx = Math.min(linked.x + bot, minx);
@@ -279,7 +286,7 @@ public class Schematics implements Loadable{
for(int cy = oy; cy <= oy2; cy++){ for(int cy = oy; cy <= oy2; cy++){
Tile tile = world.ltile(cx, cy); Tile tile = world.ltile(cx, cy);
if(tile != null && tile.entity != null && !counted.contains(tile.pos())){ if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock) && tile.entity.block.isVisible()){
int config = tile.entity.config(); int config = tile.entity.config();
if(tile.block().posConfig){ if(tile.block().posConfig){
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY); config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);

View File

@@ -75,6 +75,8 @@ public class Pal{
surge = Color.valueOf("f3e979"), surge = Color.valueOf("f3e979"),
plastanium = Color.valueOf("a1b46e"),
redSpark = Color.valueOf("fbb97f"), redSpark = Color.valueOf("fbb97f"),
orangeSpark = Color.valueOf("d2b29c"), orangeSpark = Color.valueOf("d2b29c"),

View File

@@ -182,7 +182,7 @@ public class DesktopInput extends InputHandler{
mode = none; mode = none;
} }
if(mode != none){ if(mode == placing || isPlacing()){
selectRequests.clear(); selectRequests.clear();
lastSchematic = null; lastSchematic = null;
} }
@@ -379,7 +379,7 @@ public class DesktopInput extends InputHandler{
deleting = true; deleting = true;
}else if(selected != null){ }else if(selected != null){
//only begin shooting if there's no cursor event //only begin shooting if there's no cursor event
if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem && if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && (player.buildQueue().size == 0 || !player.isBuilding) && !droppingItem &&
!tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){ !tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){
player.isShooting = true; player.isShooting = true;
} }

View File

@@ -153,8 +153,10 @@ public class MobileInput extends InputHandler implements GestureListener{
void removeRequest(BuildRequest request){ void removeRequest(BuildRequest request){
selectRequests.removeValue(request, true); selectRequests.removeValue(request, true);
if(!request.breaking){
removals.add(request); removals.add(request);
} }
}
boolean isLinePlacing(){ boolean isLinePlacing(){
return mode == placing && lineMode && Mathf.dst(lineStartX * tilesize, lineStartY * tilesize, Core.input.mouseWorld().x, Core.input.mouseWorld().y) >= 3 * tilesize; return mode == placing && lineMode && Mathf.dst(lineStartX * tilesize, lineStartY * tilesize, Core.input.mouseWorld().x, Core.input.mouseWorld().y) >= 3 * tilesize;

View File

@@ -34,37 +34,23 @@ public class SaveIO{
return versions.get(version); return versions.get(version);
} }
public static void saveToSlot(int slot){ public static void save(FileHandle file){
FileHandle file = fileFor(slot);
boolean exists = file.exists(); boolean exists = file.exists();
if(exists) file.moveTo(backupFileFor(file)); if(exists) file.moveTo(backupFileFor(file));
try{ try{
write(fileFor(slot)); write(file);
}catch(Exception e){ }catch(Exception e){
if(exists) backupFileFor(file).moveTo(file); if(exists) backupFileFor(file).moveTo(file);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public static void loadFromSlot(int slot) throws SaveException{ public static DataInputStream getStream(FileHandle file){
load(fileFor(slot)); return new DataInputStream(new InflaterInputStream(file.read(bufferSize)));
} }
public static DataInputStream getSlotStream(int slot){ public static DataInputStream getBackupStream(FileHandle file){
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize))); return new DataInputStream(new InflaterInputStream(backupFileFor(file).read(bufferSize)));
}
public static DataInputStream getBackupSlotStream(int slot){
return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize)));
}
public static boolean isSaveValid(int slot){
try{
getMeta(slot);
return true;
}catch(Exception e){
return false;
}
} }
public static boolean isSaveValid(FileHandle file){ public static boolean isSaveValid(FileHandle file){
@@ -85,11 +71,11 @@ public class SaveIO{
} }
} }
public static SaveMeta getMeta(int slot){ public static SaveMeta getMeta(FileHandle file){
try{ try{
return getMeta(getSlotStream(slot)); return getMeta(getStream(file));
}catch(Exception e){ }catch(Exception e){
return getMeta(getBackupSlotStream(slot)); return getMeta(getBackupStream(file));
} }
} }
@@ -167,6 +153,7 @@ public class SaveIO{
}catch(Exception e){ }catch(Exception e){
throw new SaveException(e); throw new SaveException(e);
}finally{ }finally{
world.setGenerating(false);
content.setTemporaryMapper(null); content.setTemporaryMapper(null);
} }
} }

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.io;
import io.anuke.arc.collection.*; import io.anuke.arc.collection.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.arc.util.io.*; import io.anuke.arc.util.io.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.ctype.*; import io.anuke.mindustry.ctype.*;
import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.*;
@@ -166,6 +167,7 @@ public abstract class SaveVersion extends SaveFileReader{
short floorid = stream.readShort(); short floorid = stream.readShort();
short oreid = stream.readShort(); short oreid = stream.readShort();
int consecutives = stream.readUnsignedByte(); int consecutives = stream.readUnsignedByte();
if(content.block(floorid) == Blocks.air) floorid = Blocks.stone.id;
context.create(x, y, floorid, oreid, (short)0); context.create(x, y, floorid, oreid, (short)0);
@@ -182,6 +184,7 @@ public abstract class SaveVersion extends SaveFileReader{
int x = i % width, y = i / width; int x = i % width, y = i / width;
Block block = content.block(stream.readShort()); Block block = content.block(stream.readShort());
Tile tile = context.tile(x, y); Tile tile = context.tile(x, y);
if(block == null) block = Blocks.air;
tile.setBlock(block); tile.setBlock(block);
if(tile.entity != null){ if(tile.entity != null){

View File

@@ -181,12 +181,14 @@ public class Map implements Comparable<Map>, Publishable{
@Override @Override
public FileHandle createSteamFolder(String id){ public FileHandle createSteamFolder(String id){
return null; FileHandle mapFile = tmpDirectory.child("map_" + id).child("map.msav");
file.copyTo(mapFile);
return mapFile.parent();
} }
@Override @Override
public FileHandle createSteamPreview(String id){ public FileHandle createSteamPreview(String id){
return null; return previewFile();
} }
@Override @Override

View File

@@ -164,7 +164,7 @@ public class Mods implements Loadable{
return requiresReload; return requiresReload;
} }
/** Loads all mods from the folder, but does call any methods on them.*/ /** Loads all mods from the folder, but does not call any methods on them.*/
public void load(){ public void load(){
for(FileHandle file : modDirectory.list()){ for(FileHandle file : modDirectory.list()){
if(!file.extension().equals("jar") && !file.extension().equals("zip") && !(file.isDirectory() && file.child("mod.json").exists())) continue; if(!file.extension().equals("jar") && !file.extension().equals("zip") && !(file.isDirectory() && file.child("mod.json").exists())) continue;
@@ -200,14 +200,61 @@ public class Mods implements Loadable{
} }
} }
resolveDependencies();
//sort mods to make sure servers handle them properly. //sort mods to make sure servers handle them properly.
loaded.sort(Structs.comparing(m -> m.name)); loaded.sort(Structs.comparing(m -> m.name));
buildFiles(); buildFiles();
} }
private void buildFiles(){ private void resolveDependencies(){
for(LoadedMod mod : Array.<LoadedMod>withArrays(loaded, disabled)){
updateDependencies(mod);
}
disabled.addAll(loaded.select(LoadedMod::hasUnmetDependencies));
loaded.removeAll(LoadedMod::hasUnmetDependencies);
disabled.each(mod -> setEnabled(mod, false));
disabled.distinct();
loaded.distinct();
}
private void updateDependencies(LoadedMod mod){
mod.dependencies.clear();
mod.missingDependencies.clear();
mod.dependencies = mod.meta.dependencies.map(this::locateMod);
for(int i = 0; i < mod.dependencies.size; i++){
if(mod.dependencies.get(i) == null){
mod.missingDependencies.add(mod.meta.dependencies.get(i));
}
}
}
private void topoSort(LoadedMod mod, Array<LoadedMod> stack, ObjectSet<LoadedMod> visited){
visited.add(mod);
mod.dependencies.each(m -> !visited.contains(m), m -> topoSort(m, stack, visited));
stack.add(mod);
}
/** @return mods ordered in the correct way needed for dependencies. */
private Array<LoadedMod> orderedMods(){
ObjectSet<LoadedMod> visited = new ObjectSet<>();
Array<LoadedMod> result = new Array<>();
for(LoadedMod mod : loaded){ for(LoadedMod mod : loaded){
if(!visited.contains(mod)){
topoSort(mod, result, visited);
}
}
return result;
}
private LoadedMod locateMod(String name){
return loaded.find(mod -> mod.name.equals(name));
}
private void buildFiles(){
for(LoadedMod mod : orderedMods()){
boolean zipFolder = !mod.file.isDirectory() && mod.root.parent() != null; boolean zipFolder = !mod.file.isDirectory() && mod.root.parent() != null;
String parentName = zipFolder ? mod.root.name() : null; String parentName = zipFolder ? mod.root.name() : null;
for(FileHandle file : mod.root.list()){ for(FileHandle file : mod.root.list()){
@@ -256,10 +303,7 @@ public class Mods implements Loadable{
loaded.clear(); loaded.clear();
disabled.clear(); disabled.clear();
load(); load();
buildFiles();
Musics.dispose();
Sounds.dispose(); Sounds.dispose();
Musics.load();
Sounds.load(); Sounds.load();
Core.assets.finishLoading(); Core.assets.finishLoading();
content.clear(); content.clear();
@@ -277,7 +321,7 @@ public class Mods implements Loadable{
/** Creates all the content found in mod files. */ /** Creates all the content found in mod files. */
public void loadContent(){ public void loadContent(){
for(LoadedMod mod : loaded){ for(LoadedMod mod : orderedMods()){
safeRun(mod, () -> { safeRun(mod, () -> {
if(mod.root.child("content").exists()){ if(mod.root.child("content").exists()){
FileHandle contentRoot = mod.root.child("content"); FileHandle contentRoot = mod.root.child("content");
@@ -337,11 +381,13 @@ public class Mods implements Loadable{
requiresReload = true; requiresReload = true;
if(!enabled){ if(!enabled){
loaded.remove(mod); loaded.remove(mod);
disabled.add(mod); if(!disabled.contains(mod)) disabled.add(mod);
}else{ }else{
loaded.add(mod); if(!loaded.contains(mod)) loaded.add(mod);
disabled.remove(mod); disabled.remove(mod);
} }
loaded.each(this::updateDependencies);
disabled.each(this::updateDependencies);
} }
} }
@@ -466,6 +512,10 @@ 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;
/** This mod's dependencies as already-loaded mods. */
public Array<LoadedMod> dependencies = new Array<>();
/** All missing dependencies of this mod as strings. */
public Array<String> missingDependencies = new Array<>();
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;
@@ -479,6 +529,10 @@ public class Mods implements Loadable{
return Core.settings.getBool("mod-" + name + "-enabled", true); return Core.settings.getBool("mod-" + name + "-enabled", true);
} }
public boolean hasUnmetDependencies(){
return !missingDependencies.isEmpty();
}
@Override @Override
public String getSteamID(){ public String getSteamID(){
return Core.settings.getString(name + "-steamid", null); return Core.settings.getString(name + "-steamid", null);
@@ -549,7 +603,7 @@ public class Mods implements Loadable{
/** Plugin metadata information.*/ /** Plugin metadata information.*/
public static class ModMeta{ public static class ModMeta{
public String name, author, description, version, main; public String name, author, description, version, main;
public String[] dependencies = {}; //TODO implement public Array<String> dependencies = Array.with();
/** Hidden mods are only server-side or client-side, and do not support adding new content. */ /** Hidden mods are only server-side or client-side, and do not support adding new content. */
public boolean hidden; public boolean hidden;
} }

View File

@@ -28,6 +28,7 @@ public class NetworkIO{
stream.writeInt(player.id); stream.writeInt(player.id);
player.write(stream); player.write(stream);
SaveIO.getSaveWriter().writeContentHeader(stream);
SaveIO.getSaveWriter().writeMap(stream); SaveIO.getSaveWriter().writeMap(stream);
}catch(IOException e){ }catch(IOException e){
throw new RuntimeException(e); throw new RuntimeException(e);
@@ -51,9 +52,12 @@ public class NetworkIO{
player.resetID(id); player.resetID(id);
player.add(); player.add();
SaveIO.getSaveWriter().readContentHeader(stream);
SaveIO.getSaveWriter().readMap(stream, world.context); SaveIO.getSaveWriter().readMap(stream, world.context);
}catch(IOException e){ }catch(IOException e){
throw new RuntimeException(e); throw new RuntimeException(e);
}finally{
content.setTemporaryMapper(null);
} }
} }

View File

@@ -27,7 +27,7 @@ public class Styles{
public static ButtonStyle defaultb, waveb; public static ButtonStyle defaultb, waveb;
public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet; public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet;
public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali; public static ImageButtonStyle defaulti, nodei, righti, emptyi, emptytogglei, selecti, cleari, clearFulli, clearPartiali, clearPartial2i, clearTogglei, clearTransi, clearToggleTransi, clearTogglePartiali;
public static ScrollPaneStyle defaultPane, horizontalPane; public static ScrollPaneStyle defaultPane, horizontalPane, smallPane;
public static KeybindDialogStyle defaultKeybindDialog; public static KeybindDialogStyle defaultKeybindDialog;
public static SliderStyle defaultSlider, vSlider; public static SliderStyle defaultSlider, vSlider;
public static LabelStyle defaultLabel, outlineLabel; public static LabelStyle defaultLabel, outlineLabel;
@@ -225,6 +225,10 @@ public class Styles{
hScroll = scrollHorizontal; hScroll = scrollHorizontal;
hScrollKnob = scrollKnobHorizontalBlack; hScrollKnob = scrollKnobHorizontalBlack;
}}; }};
smallPane = new ScrollPaneStyle(){{
vScroll = clear;
vScrollKnob = scrollKnobVerticalThin;
}};
defaultKeybindDialog = new KeybindDialogStyle(){{ defaultKeybindDialog = new KeybindDialogStyle(){{
keyColor = Pal.accent; keyColor = Pal.accent;

View File

@@ -29,8 +29,9 @@ public class ModsDialog extends FloatingDialog{
buttons.addImageTextButton("$mods.guide", Icon.wiki, buttons.addImageTextButton("$mods.guide", Icon.wiki,
() -> Core.net.openURI(modGuideURL)) () -> Core.net.openURI(modGuideURL))
.size(210f, 64f); .size(android ? 210f + 250f + 10f : 210, 64f).colspan(android ? 2 : 1);
if(!android){
buttons.addImageTextButton("$mod.import.github", Icon.github, () -> { buttons.addImageTextButton("$mod.import.github", Icon.github, () -> {
ui.showTextInput("$mod.import.github", "", 64, "Anuken/ExampleMod", text -> { ui.showTextInput("$mod.import.github", "", 64, "Anuken/ExampleMod", text -> {
ui.loadfrag.show(); ui.loadfrag.show();
@@ -62,12 +63,18 @@ public class ModsDialog extends FloatingDialog{
}, t -> Core.app.post(() -> ui.showException(t))); }, t -> Core.app.post(() -> ui.showException(t)));
}); });
}).size(250f, 64f); }).size(250f, 64f);
}
shown(this::setup); shown(this::setup);
hidden(() -> { hidden(() -> {
if(mods.requiresReload()){ if(mods.requiresReload()){
ui.loadAnd("$reloading", () -> { ui.loadAnd("$reloading", () -> {
mods.all().each(mod -> {
if(mod.hasUnmetDependencies()){
ui.showErrorMessage(Core.bundle.format("mod.nowdisabled", mod.name, mod.missingDependencies.toString(", ")));
}
});
mods.reloadContent(); mods.reloadContent();
}); });
} }
@@ -96,6 +103,7 @@ public class ModsDialog extends FloatingDialog{
anyDisabled = true; anyDisabled = true;
table.row(); table.row();
table.addImage().growX().height(4f).pad(6f).color(Pal.gray); table.addImage().growX().height(4f).pad(6f).color(Pal.gray);
table.row();
} }
table.table(Styles.black6, t -> { table.table(Styles.black6, t -> {
@@ -138,7 +146,10 @@ public class ModsDialog extends FloatingDialog{
t.labelWrap("[lightgray]" + mod.meta.description).growX(); t.labelWrap("[lightgray]" + mod.meta.description).growX();
t.row(); t.row();
} }
if(mod.hasUnmetDependencies()){
t.labelWrap(Core.bundle.format("mod.missingdependencies", mod.missingDependencies.toString(", "))).growX();
t.row();
}
}).width(500f); }).width(500f);
table.row(); table.row();
} }

View File

@@ -107,6 +107,7 @@ public class MenuFragment extends Fragment{
container.add(join); container.add(join);
container.add(custom); container.add(custom);
container.add(maps); container.add(maps);
container.row();
container.table(table -> { container.table(table -> {
table.defaults().set(container.defaults()); table.defaults().set(container.defaults());

View File

@@ -232,7 +232,7 @@ public class PlacementFragment extends Fragment{
frame.row(); frame.row();
frame.table(Tex.pane2, blocksSelect -> { frame.table(Tex.pane2, blocksSelect -> {
blocksSelect.margin(4).marginTop(0); blocksSelect.margin(4).marginTop(0);
blocksSelect.table(blocks -> blockTable = blocks).grow(); blocksSelect.pane(blocks -> blockTable = blocks).height(194f).grow().get().setStyle(Styles.smallPane);
blocksSelect.row(); blocksSelect.row();
blocksSelect.table(control.input::buildPlacementUI).name("inputTable").growX(); blocksSelect.table(control.input::buildPlacementUI).name("inputTable").growX();
}).fillY().bottom().touchable(Touchable.enabled); }).fillY().bottom().touchable(Touchable.enabled);

View File

@@ -55,6 +55,8 @@ public class Block extends BlockStorage{
public boolean breakable; public boolean breakable;
/** whether this floor can be placed on. */ /** whether this floor can be placed on. */
public boolean placeableOn = true; public boolean placeableOn = true;
/** whether this block has insulating properties. */
public boolean insulated = false;
/** tile entity health */ /** tile entity health */
public int health = -1; public int health = -1;
/** base block explosiveness */ /** base block explosiveness */
@@ -241,7 +243,7 @@ public class Block extends BlockStorage{
} }
public void drawCracks(Tile tile){ public void drawCracks(Tile tile){
if(!tile.entity.damaged()) return; if(!tile.entity.damaged() || size > maxCrackSize) return;
int id = tile.pos(); int id = tile.pos();
TextureRegion region = cracks[size - 1][Mathf.clamp((int)((1f - tile.entity.healthf()) * crackRegions), 0, crackRegions-1)]; TextureRegion region = cracks[size - 1][Mathf.clamp((int)((1f - tile.entity.healthf()) * crackRegions), 0, crackRegions-1)];
Draw.colorl(0.2f, 0.1f + (1f - tile.entity.healthf())* 0.6f); Draw.colorl(0.2f, 0.1f + (1f - tile.entity.healthf())* 0.6f);
@@ -313,7 +315,7 @@ public class Block extends BlockStorage{
tempTiles.clear(); tempTiles.clear();
Geometry.circle(tile.x, tile.y, range, (x, y) -> { Geometry.circle(tile.x, tile.y, range, (x, y) -> {
Tile other = world.ltile(x, y); Tile other = world.ltile(x, y);
if(other != null && other.block instanceof PowerNode && ((PowerNode)other.block).linkValid(other, tile) && !other.entity.proximity().contains(tile) && if(other != null && other.block instanceof PowerNode && ((PowerNode)other.block).linkValid(other, tile) && !PowerNode.insulated(other, tile) && !other.entity.proximity().contains(tile) &&
!(outputsPower && tile.entity.proximity().contains(p -> p.entity != null && p.entity.power != null && p.entity.power.graph == other.entity.power.graph))){ !(outputsPower && tile.entity.proximity().contains(p -> p.entity != null && p.entity.power != null && p.entity.power.graph == other.entity.power.graph))){
tempTiles.add(other); tempTiles.add(other);
} }
@@ -322,7 +324,7 @@ public class Block extends BlockStorage{
if(!tempTiles.isEmpty()){ if(!tempTiles.isEmpty()){
Tile toLink = tempTiles.first(); Tile toLink = tempTiles.first();
if(!toLink.entity.power.links.contains(tile.pos())){ if(!toLink.entity.power.links.contains(tile.pos())){
toLink.configure(tile.pos()); toLink.configureAny(tile.pos());
} }
} }
} }

View File

@@ -4,6 +4,7 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.function.*; import io.anuke.arc.function.*;
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.ArcAnnotate.*;
import io.anuke.mindustry.content.*; import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.type.*;
@@ -93,6 +94,10 @@ public class Tile implements Position, TargetTrait{
Call.onTileConfig(player, this, value); Call.onTileConfig(player, this, value);
} }
public void configureAny(int value){
Call.onTileConfig(null, this, value);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T extends TileEntity> T entity(){ public <T extends TileEntity> T entity(){
return (T)entity; return (T)entity;
@@ -148,7 +153,7 @@ public class Tile implements Position, TargetTrait{
return team; return team;
} }
public void setBlock(Block type, Team team, int rotation){ public void setBlock(@NonNull Block type, Team team, int rotation){
preChanged(); preChanged();
this.block = type; this.block = type;
this.team = (byte)team.ordinal(); this.team = (byte)team.ordinal();
@@ -156,11 +161,12 @@ public class Tile implements Position, TargetTrait{
changed(); changed();
} }
public void setBlock(Block type, Team team){ public void setBlock(@NonNull Block type, Team team){
setBlock(type, team, 0); setBlock(type, team, 0);
} }
public void setBlock(Block type){ public void setBlock(@NonNull Block type){
if(type == null) throw new IllegalArgumentException("Block cannot be null.");
preChanged(); preChanged();
this.block = type; this.block = type;
this.rotation = 0; this.rotation = 0;
@@ -168,13 +174,13 @@ public class Tile implements Position, TargetTrait{
} }
/**This resets the overlay!*/ /**This resets the overlay!*/
public void setFloor(Floor type){ public void setFloor(@NonNull Floor type){
this.floor = type; this.floor = type;
this.overlay = (Floor)Blocks.air; this.overlay = (Floor)Blocks.air;
} }
/** Sets the floor, preserving overlay.*/ /** Sets the floor, preserving overlay.*/
public void setFloorUnder(Floor floor){ public void setFloorUnder(@NonNull Floor floor){
Block overlay = this.overlay; Block overlay = this.overlay;
setFloor(floor); setFloor(floor);
setOverlay(overlay); setOverlay(overlay);

View File

@@ -51,5 +51,4 @@ public class Wall extends Block{
public boolean canReplace(Block other){ public boolean canReplace(Block other){
return super.canReplace(other) && health > other.health; return super.canReplace(other) && health > other.health;
} }
} }

View File

@@ -17,7 +17,7 @@ public class ArmoredConveyor extends Conveyor{
@Override @Override
public boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){ public boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){
return otherblock.outputsItems() && (Point2.equals(tile.x + Geometry.d4(rotation).x, tile.y + Geometry.d4(rotation).y, otherx, othery) return otherblock.outputsItems() && otherblock instanceof Conveyor && (Point2.equals(tile.x + Geometry.d4(rotation).x, tile.y + Geometry.d4(rotation).y, otherx, othery)
|| ((!otherblock.rotate && Edges.getFacingEdge(otherblock, otherx, othery, tile) != null && || ((!otherblock.rotate && Edges.getFacingEdge(otherblock, otherx, othery, tile) != null &&
Edges.getFacingEdge(otherblock, otherx, othery, tile).relativeTo(tile) == tile.rotation()) || Point2.equals(otherx + Geometry.d4(otherrot).x, othery + Geometry.d4(otherrot).y, tile.x, tile.y))); Edges.getFacingEdge(otherblock, otherx, othery, tile).relativeTo(tile) == tile.rotation()) || Point2.equals(otherx + Geometry.d4(otherrot).x, othery + Geometry.d4(otherrot).y, tile.x, tile.y)));
} }

View File

@@ -413,12 +413,12 @@ public class Conveyor extends Block implements Autotiler{
static long toLong(int value){ static long toLong(int value){
byte[] values = Pack.bytes(value, writeByte); byte[] values = Pack.bytes(value, writeByte);
byte itemid = values[0]; short itemid = content.item(values[0]).id;
float x = values[1] / 127f; float x = values[1] / 127f;
float y = ((int)values[2] + 128) / 255f; float y = ((int)values[2] + 128) / 255f;
short[] shorts = writeShort; short[] shorts = writeShort;
shorts[0] = (short)itemid; shorts[0] = itemid;
shorts[1] = (short)(x * Short.MAX_VALUE); shorts[1] = (short)(x * Short.MAX_VALUE);
shorts[2] = (short)((y - 1f) * Short.MAX_VALUE); shorts[2] = (short)((y - 1f) * Short.MAX_VALUE);
return Pack.longShorts(shorts); return Pack.longShorts(shorts);

View File

@@ -108,14 +108,16 @@ public class PowerNode extends PowerBlock{
Geometry.circle(tile.x, tile.y, (int)(laserRange + 1), (x, y) -> { Geometry.circle(tile.x, tile.y, (int)(laserRange + 1), (x, y) -> {
Tile other = world.ltile(x, y); Tile other = world.ltile(x, y);
if(valid.test(other)){ if(valid.test(other)){
if(!insulated(tile, other)) {
tempTiles.add(other); tempTiles.add(other);
} }
}
}); });
tempTiles.sort(Structs.comparingFloat(t -> t.dst2(tile))); tempTiles.sort(Structs.comparingFloat(t -> t.dst2(tile)));
tempTiles.each(valid, other -> { tempTiles.each(valid, other -> {
if(!tile.entity.power.links.contains(other.pos())){ if(!tile.entity.power.links.contains(other.pos())){
tile.configure(other.pos()); tile.configureAny(other.pos());
} }
}); });
@@ -167,6 +169,18 @@ public class PowerNode extends PowerBlock{
tile.configure(other.pos()); tile.configure(other.pos());
return false; return false;
} }
if(tile == other){
if (other.entity.power.links.size == 0){
getPotentialLinks(tile, link -> tile.configure(link.pos()));
} else {
while (entity.power.links.size > 0){
tile.configure(entity.power.links.get(0));
}
}
return false;
}
return true; return true;
} }
@@ -221,7 +235,13 @@ public class PowerNode extends PowerBlock{
Draw.color(Pal.placing); Draw.color(Pal.placing);
Drawf.circles(x * tilesize + offset(), y * tilesize + offset(), laserRange * tilesize); Drawf.circles(x * tilesize + offset(), y * tilesize + offset(), laserRange * tilesize);
getPotentialLinks(tile, other -> Drawf.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f + 2f, Pal.place)); getPotentialLinks(tile, other -> {
Drawf.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f + 2f, Pal.place);
insulators(tile.x, tile.y, other.x, other.y, cause -> {
Drawf.square(cause.drawx(), cause.drawy(), cause.block().size * tilesize / 2f + 2f, Pal.plastanium);
});
});
Draw.reset(); Draw.reset();
} }
@@ -299,4 +319,27 @@ public class PowerNode extends PowerBlock{
Draw.color(); Draw.color();
} }
public static boolean insulated(Tile tile, Tile other){
return insulated(tile.x, tile.y, other.x, other.y);
}
public static boolean insulated(int x, int y, int x2, int y2){
final Boolean[] bool = {false};
insulators(x, y, x2, y2, cause -> {
bool[0] = true;
});
return bool[0];
}
public static void insulators(int x, int y, int x2, int y2, Consumer<Tile> iterator){
world.raycastEach(x, y, x2, y2, (wx, wy) -> {
Tile tile = world.ltile(wx, wy);
if(tile != null && tile.block() != null && tile.block().insulated){
iterator.accept(tile);
}
return false;
});
}
} }

View File

@@ -194,7 +194,9 @@ public class Drill extends Block{
}); });
stats.add(BlockStat.drillSpeed, 60f / drillTime * size * size, StatUnit.itemsSecond); stats.add(BlockStat.drillSpeed, 60f / drillTime * size * size, StatUnit.itemsSecond);
stats.add(BlockStat.boostEffect, liquidBoostIntensity, StatUnit.timesSpeed); if(liquidBoostIntensity > 0){
stats.add(BlockStat.boostEffect, liquidBoostIntensity * liquidBoostIntensity, StatUnit.timesSpeed);
}
} }
void countOre(Tile tile){ void countOre(Tile tile){

View File

@@ -114,7 +114,7 @@ public class MechPad extends Block{
MechFactoryEntity entity = tile.entity(); MechFactoryEntity entity = tile.entity();
if(entity.player != null){ if(entity.player != null){
RespawnBlock.drawRespawn(tile, entity.heat, entity.progress, entity.time, entity.player, (!entity.sameMech && entity.player.mech == mech ? Mechs.starter : mech)); RespawnBlock.drawRespawn(tile, entity.heat, entity.progress, entity.time, entity.player, (!entity.sameMech && entity.player.mech == mech ? mech : Mechs.starter));
} }
} }

View File

@@ -9,6 +9,7 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.function.*; import io.anuke.arc.function.*;
import io.anuke.arc.util.*; import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.*; import io.anuke.arc.util.pooling.*;
import io.anuke.mindustry.core.GameState.*;
import io.anuke.mindustry.core.Version; import io.anuke.mindustry.core.Version;
import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.*;
@@ -245,6 +246,13 @@ public class SNet implements SteamNetworkingCallback, SteamMatchmakingCallback,
return; return;
} }
if(net.active()){
net.disconnect();
net.closeServer();
logic.reset();
state.set(State.menu);
}
currentLobby = steamIDLobby; currentLobby = steamIDLobby;
currentServer = smat.getLobbyOwner(steamIDLobby); currentServer = smat.getLobbyOwner(steamIDLobby);

View File

@@ -162,6 +162,10 @@ public class SWorkshop implements SteamUGCCallback{
ugc.setItemVisibility(h, PublishedFileVisibility.Private); ugc.setItemVisibility(h, PublishedFileVisibility.Private);
} }
ugc.submitItemUpdate(h, changelog == null ? "<Created>" : changelog); ugc.submitItemUpdate(h, changelog == null ? "<Created>" : changelog);
if(p instanceof Map){
SAchievement.publishMap.complete();
}
}, () -> p.addSteamID(sid)); }, () -> p.addSteamID(sid));
} }

View File

@@ -670,28 +670,25 @@ public class ServerControl implements ApplicationListener{
if(state.is(State.playing)){ if(state.is(State.playing)){
err("Already hosting. Type 'stop' to stop hosting first."); err("Already hosting. Type 'stop' to stop hosting first.");
return; return;
}else if(!Strings.canParseInt(arg[0])){
err("Invalid save slot '{0}'.", arg[0]);
return;
} }
int slot = Strings.parseInt(arg[0]); FileHandle file = saveDirectory.child(arg[0] + "." + saveExtension);
if(!SaveIO.isSaveValid(slot)){ if(!SaveIO.isSaveValid(file)){
err("No (valid) save data found for slot."); err("No (valid) save data found for slot.");
return; return;
} }
Core.app.post(() -> { Core.app.post(() -> {
try{ try{
SaveIO.loadFromSlot(slot); SaveIO.load(file);
state.rules.zone = null; state.rules.zone = null;
}catch(Throwable t){
err("Failed to load save. Outdated or corrupt file.");
}
info("Save loaded."); info("Save loaded.");
host(); host();
state.set(State.playing); state.set(State.playing);
}catch(Throwable t){
err("Failed to load save. Outdated or corrupt file.");
}
}); });
}); });
@@ -699,18 +696,25 @@ public class ServerControl implements ApplicationListener{
if(!state.is(State.playing)){ if(!state.is(State.playing)){
err("Not hosting. Host a game first."); err("Not hosting. Host a game first.");
return; return;
}else if(!Strings.canParseInt(arg[0])){
err("Invalid save slot '{0}'.", arg[0]);
return;
} }
FileHandle file = saveDirectory.child(arg[0] + "." + saveExtension);
Core.app.post(() -> { Core.app.post(() -> {
int slot = Strings.parseInt(arg[0]); SaveIO.save(file);
SaveIO.saveToSlot(slot); info("Saved to {0}.", file);
info("Saved to slot {0}.", slot);
}); });
}); });
handler.register("saves", "List all saves in the save directory.", arg -> {
info("Save files: ");
for(FileHandle file : saveDirectory.list()){
if(file.extension().equals(saveExtension)){
info("| &ly{0}", file.nameWithoutExtension());
}
}
});
handler.register("gameover", "Force a game over.", arg -> { handler.register("gameover", "Force a game over.", arg -> {
if(state.is(State.menu)){ if(state.is(State.menu)){
info("Not playing a map."); info("Not playing a map.");

View File

@@ -198,7 +198,7 @@ public class ApplicationTests{
void save(){ void save(){
world.loadMap(testMap); world.loadMap(testMap);
assertTrue(state.teams.get(defaultTeam).cores.size > 0); assertTrue(state.teams.get(defaultTeam).cores.size > 0);
SaveIO.saveToSlot(0); SaveIO.save(saveDirectory.child("0.msav"));
} }
@Test @Test
@@ -206,9 +206,9 @@ public class ApplicationTests{
world.loadMap(testMap); world.loadMap(testMap);
Map map = world.getMap(); Map map = world.getMap();
SaveIO.saveToSlot(0); SaveIO.save(saveDirectory.child("0.msav"));
resetWorld(); resetWorld();
SaveIO.loadFromSlot(0); SaveIO.load(saveDirectory.child("0.msav"));
assertEquals(world.width(), map.width); assertEquals(world.width(), map.width);
assertEquals(world.height(), map.height); assertEquals(world.height(), map.height);