Compare commits

...

57 Commits

Author SHA1 Message Date
Anuken
7b1c60c24f Testing tests 2020-12-04 12:23:02 -05:00
Anuken
8b8d990852 Testing tests 2020-12-04 12:16:47 -05:00
Anuken
6583cc0b5d Testing tests 2020-12-04 11:38:43 -05:00
Anuken
5c3ae425ae Display locked sectors / Core incinerate hint 2020-12-04 11:31:27 -05:00
Anuken
744b1b2037 Looking into test failure 2020-12-04 10:56:47 -05:00
Anuken
a313ca8a26 Checking if tests fail 2020-12-04 10:44:39 -05:00
Anuken
87204df3ee Merge remote-tracking branch 'origin/master' 2020-12-04 10:25:51 -05:00
Anuken
bc70c08820 Fixed #3756 2020-12-04 10:25:41 -05:00
Anuken
2c2828617f Create ISSUES.md 2020-12-04 10:03:54 -05:00
Anuken
a451ad895c Slight sei buff 2020-12-03 20:29:58 -05:00
Anuken
d13b69c36f Sort community servers by ping 2020-12-03 16:49:30 -05:00
Anuken
d64f603e08 Merge branch 'master' of https://github.com/Anuken/Mindustry 2020-12-03 12:08:35 -05:00
Anuken
3d2c094056 arc 2020-12-03 12:08:31 -05:00
Anuken
64e99f613e Update bug_report.md 2020-12-03 11:08:00 -05:00
Anuken
51b4078288 Clean tests before running 2020-12-03 11:05:02 -05:00
Anuken
db8d099fcc arc 2020-12-03 10:29:19 -05:00
Anuken
002be46915 Updated miniaudio version 2020-12-03 10:28:57 -05:00
Anuken
9fa2691b02 Fixed campaign idle boss spoofing 2020-12-03 10:05:52 -05:00
Anuken
6686584003 Merge remote-tracking branch 'origin/master' 2020-12-02 18:01:49 -05:00
Anuken
78c8c57649 Made water unlocked again 2020-12-02 18:01:46 -05:00
Anuken
ca65fd31e5 Merge pull request #3728 from Vortetty/patch-2
Update surrealment to v6
2020-12-02 17:23:09 -05:00
Vortetty
e2109f2f56 Update servers_be.json 2020-12-02 14:20:55 -08:00
Vortetty
6b63cd7a46 Update servers_v6.json 2020-12-02 14:19:54 -08:00
Anuken
e19c857d74 Possible crash fix 2020-12-02 16:08:53 -05:00
Anuken
b18f418c61 Reduced easy sector difficulty 2020-12-02 14:48:42 -05:00
Anuken
12ee38476c Minor build fix 2020-12-02 14:35:03 -05:00
Anuken
48c8357ff7 Added community server disclaimer 2020-12-02 14:32:51 -05:00
Anuken
a0702559f3 Fixed #3725 2020-12-02 14:14:42 -05:00
Anuken
55db91e53e Cleanup 2020-12-02 13:53:49 -05:00
Anuken
a333fa3722 Added more hints 2020-12-02 13:40:49 -05:00
Anuken
5284750c6a Merge remote-tracking branch 'origin/master' 2020-12-02 13:02:35 -05:00
Anuken
9c616fd03b Possible fix for notched insets 2020-12-02 13:02:27 -05:00
Anuken
d4bee41103 Merge pull request #3722 from Quezler/patch-77
Fast forward boulder deconstruction
2020-12-02 12:58:58 -05:00
Patrick 'Quezler' Mounier
2e586c4403 Update ConstructBlock.java 2020-12-02 18:54:46 +01:00
Patrick 'Quezler' Mounier
129f21e3a9 Update Boulder.java 2020-12-02 18:01:40 +01:00
Patrick 'Quezler' Mounier
435f15a450 Update ConstructBlock.java 2020-12-02 18:00:50 +01:00
Patrick 'Quezler' Mounier
3eac8cb6ad Update Boulder.java 2020-12-02 17:59:20 +01:00
Patrick 'Quezler' Mounier
ea96bf2675 Update Block.java 2020-12-02 17:58:58 +01:00
Patrick 'Quezler' Mounier
e4c3664cc6 Update ConstructBlock.java 2020-12-02 16:36:07 +01:00
Patrick 'Quezler' Mounier
664cd6a3a0 Update ConstructBlock.java 2020-12-02 16:26:22 +01:00
Anuken
c26a9bd123 Merge pull request #3715 from Quezler/patch-75
Keep items inside reconstructor when construction booped
2020-12-02 10:05:51 -05:00
Anuken
5008573aa5 Merge pull request #3714 from Recessive/patch-7
Update servers_v6.json
2020-12-02 09:58:15 -05:00
Anuken
395f7193ea Merge remote-tracking branch 'origin/master' 2020-12-02 09:56:06 -05:00
Anuken
775e17a726 Fixed #3709 2020-12-02 09:56:02 -05:00
Anuken
900ec8db6a Merge pull request #3717 from Quezler/patch-76
Fixes massdriver typo
2020-12-02 09:54:37 -05:00
Anuken
e23a7c213c Merge pull request #3710 from joshuaptfan/master
Spellcheck English Steam achievements
2020-12-02 09:53:46 -05:00
Anuken
96513e4967 Merge pull request #3712 from MEEPofFaith/patch-1
Why is this protected in the first place?
2020-12-02 09:52:31 -05:00
Patrick 'Quezler' Mounier
b64df565a4 Update MassDriver.java 2020-12-02 14:09:19 +01:00
Patrick 'Quezler' Mounier
382e27e1e4 Populate prevBuild when deconstructting 2020-12-02 12:02:49 +01:00
Patrick 'Quezler' Mounier
0dd95e736d Add items back to reconstructor if it overwrote itself 2020-12-02 12:01:21 +01:00
Recessive
0ae6a33971 Update servers_v6.json
Add {AA} Hub and Campaign servers
2020-12-02 21:56:47 +11:00
MEEP of Faith
380c9ab113 Why is this protected in the first place? 2020-12-01 22:17:32 -08:00
joshuaptfan
1028ade80d Fix Poly unit description 2020-12-01 21:07:44 -08:00
joshuaptfan
a8e34381ef Spellcheck Steam store description 2020-12-01 20:37:00 -08:00
joshuaptfan
5f5987cef2 Spellcheck English Steam achievements 2020-12-01 20:20:37 -08:00
Anuken
430538ea7e Possible fix for "unsupported combination of formats" error 2020-12-01 18:17:57 -05:00
Anuken
ee966d1198 Removed requirements for optional inputs of blocks 2020-12-01 18:02:20 -05:00
51 changed files with 364 additions and 132 deletions

View File

@@ -21,7 +21,7 @@ assignees: ''
If you remove the line above without reading it properly and understanding what it means, I will reap your soul. Even if you're playing on someone's server, you can still save the game to a slot. If you remove the line above without reading it properly and understanding what it means, I will reap your soul. Even if you're playing on someone's server, you can still save the game to a slot.
**Crash report**: *The contents of relevant crash report files. REQUIRED if you are reporting a crash.* **(Crash) logs**: *Either crash reports from the crash folder, or the file you get when you go into Settings -> Game Data -> Export Crash logs. REQUIRED if you are reporting a crash.*
--- ---

View File

@@ -13,7 +13,7 @@ jobs:
with: with:
java-version: 14 java-version: 14
- name: Run unit tests - name: Run unit tests
run: ./gradlew test run: ./gradlew clean cleanTest test
- name: Trigger BE build - name: Trigger BE build
if: ${{ github.repository == 'Anuken/Mindustry' }} if: ${{ github.repository == 'Anuken/Mindustry' }}
run: | run: |

52
ISSUES.md Normal file
View File

@@ -0,0 +1,52 @@
# Why was my issue closed?
This document goes over some common causes for issue closures.
## You did not fill in the template
I can't debug the problem unless you provide the information the template asks for.
If you cannot put in the effort to fill out a template, then don't expect me to put in the effort to fix it.
## Your issue was already reported
If the problem in your issue has already been encountered before, it will be closed - especially if your report doesn't provide any new information.
Make sure you search the *closed* issues before making an issue.
I do not link the specific issue(s) that report the same problem, because searching takes time - if you're interested in finding them, you should be able to do so without my help.
To be clear: I do **not** expect users to look at *all* previous issues, or do a comprehensive stack trace analysis to see if their crash was already reported.
## Your issue was already fixed
The problem you reported has been addressed. Note that this does **not** mean that the latest stable version of Mindustry has the fix!
It simply means that I have committed (or am about to commit) a patch that fixes it *on the current development branch*.
## Your issue is missing a crash report or log
If the game crashes without a specific cause, and you don't send me a log, I can't fix it. There is no way for me to know what went wrong.
During a normal crash, the game should tell you where the log is saved. If not, you should still be able to look in the game's crash folder on most operating systems, or export the logs in *Settings -> Game Data -> Export Crash Logs*.
## Your issue is missing saves or screenshots
Even if you think your problem happens everywhere and saves/screenshots are redundant, this is frequently not the case.
If I cannot reproduce the problem on my own saves and you have not linked any of your own, then the problem is likely to be save-specific. If you do not send me any, the problem cannot be investigated further.
## Your issue is related to an external program
If Mindustry causes something else to crash or misbehave, I am very unlikely to fix it. Unless the problem is serious, widespread and/or clearly a bug *in Mindustry*, it is not my responsibility.
Similarly, if you use another (invasive) program to change how Mindustry works, and something goes wrong, that is not my problem. Don't do it.
## Your issue is caused by mods
Crashes and bugs related to installed mods should be reported in the relevant mod repository, not here.
*Note that problems with the Mindustry modding API are a separate problem, and do not apply.*
## I cannot reproduce your issue
If I follow your instructions and am repeatedly unable to reproduce the problem you've reported, then it is very unlikely to be fixed.
Either the problem is device-specific, or there is not enough information given for me to be able to reproduce it.
I may attempt to change some code if I think it will make the issue less likely to occur, but without knowing for sure, the issue cannot be considered truly "fixed".
As I cannot make any further progress on the problem, there is no reason to keep it open. If it is a common bug/crash, other people will come along with information that may shed some light on the issue.

View File

@@ -253,18 +253,22 @@ project(":ios"){
task incrementConfig{ task incrementConfig{
def vfile = file('robovm.properties') def vfile = file('robovm.properties')
def bversion = getBuildVersion()
def props = new Properties() def props = new Properties()
if(vfile.exists()){ if(vfile.exists()){
props.load(new FileInputStream(vfile)) props.load(new FileInputStream(vfile))
}else{ }else{
props['app.id'] = 'io.anuke.mindustry' props['app.id'] = 'io.anuke.mindustry'
props['app.version'] = '5.0' props['app.version'] = '6.0'
props['app.mainclass'] = 'mindustry.IOSLauncher' props['app.mainclass'] = 'mindustry.IOSLauncher'
props['app.executable'] = 'IOSLauncher' props['app.executable'] = 'IOSLauncher'
props['app.name'] = 'Mindustry' props['app.name'] = 'Mindustry'
} }
props['app.build'] = (!props.containsKey("app.build") ? 40 : props['app.build'].toInteger() + 1) + "" props['app.build'] = (!props.containsKey("app.build") ? 40 : props['app.build'].toInteger() + 1) + ""
if(bversion != "custom build"){
props['app.version'] = versionNumber + "." + bversion + (bversion.contains(".") ? "" : ".0")
}
props.store(vfile.newWriter(), null) props.store(vfile.newWriter(), null)
} }

View File

@@ -69,7 +69,7 @@ stat.delivered = Resources Launched:
stat.playtime = Time Played:[accent] {0} stat.playtime = Time Played:[accent] {0}
stat.rank = Final Rank: [accent]{0} stat.rank = Final Rank: [accent]{0}
globalitems = [accent]Global Items globalitems = [accent]Total Items
map.delete = Are you sure you want to delete the map "[accent]{0}[]"? map.delete = Are you sure you want to delete the map "[accent]{0}[]"?
level.highscore = High Score: [accent]{0} level.highscore = High Score: [accent]{0}
level.select = Level Select level.select = Level Select
@@ -196,6 +196,7 @@ servers.local = Local Servers
servers.remote = Remote Servers servers.remote = Remote Servers
servers.global = Community Servers servers.global = Community Servers
servers.disclaimer = Community servers are [accent]not[] owned or controlled by the developer.\n\nServers may contain user-generated content that is not appropriate for all ages.
servers.showhidden = Show Hidden Servers servers.showhidden = Show Hidden Servers
server.shown = Shown server.shown = Shown
server.hidden = Hidden server.hidden = Hidden
@@ -1279,6 +1280,9 @@ hint.payloadDrop.mobile = [accent]Tap and hold[] an empty location to drop a pay
hint.waveFire = [accent]Wave[] turrets with water as ammunition will automatically put out nearby fires. hint.waveFire = [accent]Wave[] turrets with water as ammunition will automatically put out nearby fires.
hint.generator = \uf879 [accent]Combustion Generators[] burn coal and transmit power to adjacent blocks.\n\nPower transmission range can be extended with \uf87f [accent]Power Nodes[]. hint.generator = \uf879 [accent]Combustion Generators[] burn coal and transmit power to adjacent blocks.\n\nPower transmission range can be extended with \uf87f [accent]Power Nodes[].
hint.guardian = [accent]Guardian[] units are armored. Weak ammo such as [accent]Copper[] and [accent]Lead[] is [scarlet]not effective[].\n\nUse higher tier turrets or \uf835 [accent]Graphite[] \uf861Duo/\uf859Salvo ammunition to take Guardians down. hint.guardian = [accent]Guardian[] units are armored. Weak ammo such as [accent]Copper[] and [accent]Lead[] is [scarlet]not effective[].\n\nUse higher tier turrets or \uf835 [accent]Graphite[] \uf861Duo/\uf859Salvo ammunition to take Guardians down.
hint.coreUpgrade = Cores can be upgraded by [accent]placing higher-tier cores over them[].\n\nPlace a  [accent]Foundation[] core over the  [accent]Shard[] core. Make sure it is free from nearby obstructions.
hint.presetLaunch = Gray [accent]landing zone sectors[], such as [accent]Frozen Forest[], can be launched to from anywhere. They do not require capture of nearby territory.\n\n[accent]Numbered sectors[], such as this one, are [accent]optional[].
hint.coreIncinerate = After the core is filled to capacity with an item, any extra items of that type it receives will be [accent]incinerated[].
item.copper.description = Used in all types of construction and ammunition. item.copper.description = Used in all types of construction and ammunition.
item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced. item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced.
@@ -1473,7 +1477,7 @@ unit.zenith.description = Fires salvos of missiles at all nearby enemies.
unit.antumbra.description = Fires a barrage of bullets at all nearby enemies. unit.antumbra.description = Fires a barrage of bullets at all nearby enemies.
unit.eclipse.description = Fires two piercing lasers and a barrage of flak at all nearby enemies. unit.eclipse.description = Fires two piercing lasers and a barrage of flak at all nearby enemies.
unit.mono.description = Automatically mines copper and lead, depositing it into the core. unit.mono.description = Automatically mines copper and lead, depositing it into the core.
unit.poly.description = Automatically rebuilds damaged structures and assists other units in construction. unit.poly.description = Automatically rebuilds destroyed structures and assists other units in construction.
unit.mega.description = Automatically repairs damaged structures. Capable of carrying blocks and small ground units. unit.mega.description = Automatically repairs damaged structures. Capable of carrying blocks and small ground units.
unit.quad.description = Drops large bombs on ground targets, repairing allied structures and damaging enemies. Capable of carrying medium-sized ground units. unit.quad.description = Drops large bombs on ground targets, repairing allied structures and damaging enemies. Capable of carrying medium-sized ground units.
unit.oct.description = Protects nearby allies with its regenerating shield. Capable of carrying most ground units. unit.oct.description = Protects nearby allies with its regenerating shield. Capable of carrying most ground units.

View File

@@ -12,6 +12,7 @@ public class Liquids implements ContentList{
water = new Liquid("water", Color.valueOf("596ab8")){{ water = new Liquid("water", Color.valueOf("596ab8")){{
heatCapacity = 0.4f; heatCapacity = 0.4f;
alwaysUnlocked = true;
effect = StatusEffects.wet; effect = StatusEffects.wet;
}}; }};

View File

@@ -154,22 +154,22 @@ public class TechTree implements ContentList{
}); });
node(kiln, Seq.with(new SectorComplete(craters)), () -> { node(kiln, Seq.with(new SectorComplete(craters)), () -> {
node(incinerator, () -> { node(pulverizer, () -> {
node(melter, () -> { node(incinerator, () -> {
node(surgeSmelter, () -> { node(melter, () -> {
node(surgeSmelter, () -> {
}); });
node(separator, () -> { node(separator, () -> {
node(pulverizer, () -> {
node(disassembler, () -> { node(disassembler, () -> {
}); });
}); });
});
node(cryofluidMixer, () -> { node(cryofluidMixer, () -> {
});
}); });
}); });
}); });
@@ -714,8 +714,14 @@ public class TechTree implements ContentList{
finishedRequirements[i] = new ItemStack(requirements[i].item, Core.settings == null ? 0 : Core.settings.getInt("req-" + content.name + "-" + requirements[i].item.name)); finishedRequirements[i] = new ItemStack(requirements[i].item, Core.settings == null ? 0 : Core.settings.getInt("req-" + content.name + "-" + requirements[i].item.name));
} }
var used = new ObjectSet<Content>();
//add dependencies as objectives. //add dependencies as objectives.
content.getDependencies(d -> objectives.add(new Research(d))); content.getDependencies(d -> {
if(used.add(d)){
objectives.add(new Research(d));
}
});
map.put(content, this); map.put(content, this);
all.add(this); all.add(this);

View File

@@ -1624,7 +1624,7 @@ public class UnitTypes implements ContentList{
xRand = 8f; xRand = 8f;
shotDelay = 1f; shotDelay = 1f;
bullet = new MissileBulletType(4.2f, 40){{ bullet = new MissileBulletType(4.2f, 42){{
homingPower = 0.12f; homingPower = 0.12f;
width = 8f; width = 8f;
height = 8f; height = 8f;
@@ -1634,7 +1634,7 @@ public class UnitTypes implements ContentList{
keepVelocity = false; keepVelocity = false;
splashDamageRadius = 35f; splashDamageRadius = 35f;
splashDamage = 45f; splashDamage = 45f;
lifetime = 56f; lifetime = 62f;
trailColor = Pal.bulletYellowBack; trailColor = Pal.bulletYellowBack;
backColor = Pal.bulletYellowBack; backColor = Pal.bulletYellowBack;
frontColor = Pal.bulletYellow; frontColor = Pal.bulletYellow;
@@ -1662,11 +1662,11 @@ public class UnitTypes implements ContentList{
shots = 3; shots = 3;
shotDelay = 4f; shotDelay = 4f;
inaccuracy = 1f; inaccuracy = 1f;
bullet = new BasicBulletType(7f, 55){{ bullet = new BasicBulletType(7f, 57){{
width = 13f; width = 13f;
height = 19f; height = 19f;
shootEffect = Fx.shootBig; shootEffect = Fx.shootBig;
lifetime = 30f; lifetime = 35f;
}}; }};
}}); }});
}}; }};

View File

@@ -75,7 +75,7 @@ public class Weathers implements ContentList{
attrs.set(Attribute.spores, 1f); attrs.set(Attribute.spores, 1f);
attrs.set(Attribute.light, -0.15f); attrs.set(Attribute.light, -0.15f);
status = StatusEffects.sporeSlowed; status = StatusEffects.sporeSlowed;
opacityMultiplier = 0.75f; opacityMultiplier = 0.5f;
force = 0.1f; force = 0.1f;
sound = Sounds.wind; sound = Sounds.wind;
soundVol = 0.7f; soundVol = 0.7f;

View File

@@ -10,21 +10,22 @@ import arc.scene.style.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.*;
import mindustry.audio.*; import mindustry.audio.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.content.TechTree.*; import mindustry.content.TechTree.*;
import mindustry.core.GameState.*; import mindustry.core.GameState.*;
import mindustry.entities.*; import mindustry.entities.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.game.Objectives.*; import mindustry.game.Objectives.*;
import mindustry.game.*;
import mindustry.game.Saves.*; import mindustry.game.Saves.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.input.*; import mindustry.input.*;
import mindustry.io.*; import mindustry.io.*;
import mindustry.io.SaveIO.*; import mindustry.io.SaveIO.*;
import mindustry.maps.*;
import mindustry.maps.Map; import mindustry.maps.Map;
import mindustry.maps.*;
import mindustry.net.*; import mindustry.net.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
@@ -506,6 +507,13 @@ public class Control implements ApplicationListener, Loadable{
settings.put("fullscreen", !full); settings.put("fullscreen", !full);
} }
if(Float.isNaN(Vars.player.x) || Float.isNaN(Vars.player.y)){
player.set(0, 0);
if(!player.dead()) player.unit().kill();
}
if(Float.isNaN(camera.position.x)) camera.position.x = world.unitWidth()/2f;
if(Float.isNaN(camera.position.y)) camera.position.y = world.unitHeight()/2f;
if(state.isGame()){ if(state.isGame()){
input.update(); input.update();

View File

@@ -92,6 +92,12 @@ public class UI implements ApplicationListener, Loadable{
Core.scene = new Scene(); Core.scene = new Scene();
Core.input.addProcessor(Core.scene); Core.input.addProcessor(Core.scene);
int[] insets = Core.graphics.getSafeInsets();
Core.scene.marginLeft = insets[0];
Core.scene.marginRight = insets[1];
Core.scene.marginTop = insets[2];
Core.scene.marginBottom = insets[3];
Tex.load(); Tex.load();
Icon.load(); Icon.load();
Styles.load(); Styles.load();

View File

@@ -6,6 +6,8 @@ import arc.graphics.g2d.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.*; import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;
@@ -42,6 +44,11 @@ public abstract class UnlockableContent extends MappableContent{
this.unlocked = Core.settings != null && Core.settings.getBool(this.name + "-unlocked", false); this.unlocked = Core.settings != null && Core.settings.getBool(this.name + "-unlocked", false);
} }
/** @return the tech node for this content. may be null. */
public @Nullable TechNode node(){
return TechTree.get(this);
}
public String displayDescription(){ public String displayDescription(){
return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName()); return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName());
} }

View File

@@ -49,7 +49,8 @@ public class ContinuousLaserBulletType extends BulletType{
@Override @Override
public float estimateDPS(){ public float estimateDPS(){
//assume firing duration is about 100 by default, may not be accurate there's no way of knowing in this method //assume firing duration is about 100 by default, may not be accurate there's no way of knowing in this method
return damage * 100f / 5f; //assume it pierces 3 blocks/units
return damage * 100f / 5f * 3f;
} }
@Override @Override

View File

@@ -6,7 +6,7 @@ import mindustry.gen.*;
import mindustry.content.*; import mindustry.content.*;
public class LaserBoltBulletType extends BasicBulletType{ public class LaserBoltBulletType extends BasicBulletType{
protected float height = 7f, width = 2f; public float width = 2f, height = 7f;
public LaserBoltBulletType(float speed, float damage){ public LaserBoltBulletType(float speed, float damage){
super(speed, damage); super(speed, damage);

View File

@@ -40,9 +40,10 @@ public class LaserBulletType extends BulletType{
this(1f); this(1f);
} }
//assume it pierces at least 3 blocks
@Override @Override
public float estimateDPS(){ public float estimateDPS(){
return super.estimateDPS() * 2f; return super.estimateDPS() * 3f;
} }
@Override @Override

View File

@@ -91,7 +91,7 @@ abstract class BuilderComp implements Posc, Teamc, Rotc{
plans.removeFirst(); plans.removeFirst();
return; return;
} }
}else if(tile.team() != team){ }else if(tile.team() != team && tile.team() != Team.derelict){
plans.removeFirst(); plans.removeFirst();
return; return;
} }

View File

@@ -197,7 +197,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
//region utility methods //region utility methods
public void addPlan(boolean checkPrevious){ public void addPlan(boolean checkPrevious){
if(!block.rebuildable) return; if(!block.rebuildable || (team == state.rules.defaultTeam && state.isCampaign() && !block.isVisible())) return;
if(self() instanceof ConstructBuild entity){ if(self() instanceof ConstructBuild entity){
//update block to reflect the fact that something was being constructed //update block to reflect the fact that something was being constructed
@@ -1099,8 +1099,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
public void displayBars(Table table){ public void displayBars(Table table){
for(Func<Building, Bar> bar : block.bars.list()){ for(Func<Building, Bar> bar : block.bars.list()){
table.add(bar.get(self())).growX(); //TODO fix conclusively
table.row(); try{
table.add(bar.get(self())).growX();
table.row();
}catch(ClassCastException e){
break;
}
} }
} }

View File

@@ -68,13 +68,12 @@ public class Schematics implements Loadable{
shadowBuffer.dispose(); shadowBuffer.dispose();
if(errorTexture != null){ if(errorTexture != null){
errorTexture.dispose(); errorTexture.dispose();
errorTexture = null;
} }
}); });
Events.on(ClientLoadEvent.class, event -> { Events.on(ClientLoadEvent.class, event -> {
Pixmap pixmap = Core.atlas.getPixmap("error").crop(); errorTexture = new Texture("sprites/error.png");
errorTexture = new Texture(pixmap);
pixmap.dispose();
}); });
} }

View File

@@ -73,7 +73,7 @@ public class SectorInfo{
public boolean shown = false; public boolean shown = false;
/** Special variables for simulation. */ /** Special variables for simulation. */
public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope, bossHealth, bossDps; public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope, bossHealth, bossDps, curEnemyHealth, curEnemyDps;
/** Wave where first boss shows up. */ /** Wave where first boss shows up. */
public int bossWave = -1; public int bossWave = -1;

View File

@@ -10,7 +10,7 @@ import mindustry.type.*;
import static mindustry.content.UnitTypes.*; import static mindustry.content.UnitTypes.*;
public class Waves{ public class Waves{
public static final int waveVersion = 4; public static final int waveVersion = 5;
private Seq<SpawnGroup> spawns; private Seq<SpawnGroup> spawns;
@@ -277,7 +277,7 @@ public class Waves{
int cap = 150; int cap = 150;
float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f; float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f;
float[] scaling = {1, 1.5f, 3f, 4f, 5f}; float[] scaling = {1, 2f, 3f, 4f, 5f};
Intc createProgression = start -> { Intc createProgression = start -> {
//main sequence //main sequence

View File

@@ -35,11 +35,18 @@ public class LoadRenderer implements Disposable{
private Mesh mesh = MeshBuilder.buildHex(colorRed, 2, true, 1f); private Mesh mesh = MeshBuilder.buildHex(colorRed, 2, true, 1f);
private Camera3D cam = new Camera3D(); private Camera3D cam = new Camera3D();
private int lastLength = -1; private int lastLength = -1;
private FxProcessor fx = new FxProcessor(Format.rgba8888, 2, 2, false, true); private FxProcessor fx;
private WindowedMean renderTimes = new WindowedMean(20); private WindowedMean renderTimes = new WindowedMean(20);
private long lastFrameTime; private long lastFrameTime;
{ {
//some systems don't support rgba8888 w/ a stencil buffer
try{
fx = new FxProcessor(Format.rgba8888, 2, 2, false, true);
}catch(Exception e){
fx = new FxProcessor(Format.rgb565, 2, 2, false, true);
}
//vignetting is probably too much //vignetting is probably too much
//fx.addEffect(new VignettingFilter(false)); //fx.addEffect(new VignettingFilter(false));
fx.addEffect(new BloomFilter()); fx.addEffect(new BloomFilter());

View File

@@ -836,7 +836,6 @@ public class MobileInput extends InputHandler implements GestureListener{
if(type == null) return; if(type == null) return;
boolean omni = unit.type.omniMovement; boolean omni = unit.type.omniMovement;
boolean ground = unit.isGrounded();
boolean allowHealing = type.canHeal; boolean allowHealing = type.canHeal;
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team && boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
((Building)target).damaged() && target.within(unit, type.range); ((Building)target).damaged() && target.within(unit, type.range);

View File

@@ -83,8 +83,9 @@ public class JsonIO{
@Override @Override
public Sector read(Json json, JsonValue jsonData, Class type){ public Sector read(Json json, JsonValue jsonData, Class type){
String[] split = jsonData.asString().split("-"); String name = jsonData.asString();
return Vars.content.<Planet>getByName(ContentType.planet, split[0]).sectors.get(Integer.parseInt(split[1])); int idx = name.lastIndexOf('-');
return Vars.content.<Planet>getByName(ContentType.planet, name.substring(0, idx)).sectors.get(Integer.parseInt(name.substring(idx + 1)));
} }
}); });

View File

@@ -73,6 +73,11 @@ public class SectorDamage{
enemyHealth += info.bossHealth; enemyHealth += info.bossHealth;
} }
if(i == waveBegin){
enemyDps += info.curEnemyDps;
enemyHealth += info.curEnemyHealth;
}
//happens due to certain regressions //happens due to certain regressions
if(enemyHealth < 0 || enemyDps < 0) continue; if(enemyHealth < 0 || enemyDps < 0) continue;
@@ -293,18 +298,19 @@ public class SectorDamage{
//skip player //skip player
if(unit.isPlayer()) continue; if(unit.isPlayer()) continue;
if(unit.team == state.rules.defaultTeam){ //scale health based on armor - yes, this is inaccurate, but better than nothing
//scale health based on armor - yes, this is inaccurate, but better than nothing float healthMult = 1f + Mathf.clamp(unit.armor / 20f);
float healthMult = 1f + Mathf.clamp(unit.armor / 20f);
if(unit.team == state.rules.defaultTeam){
sumHealth += unit.health*healthMult + unit.shield; sumHealth += unit.health*healthMult + unit.shield;
sumDps += unit.type.dpsEstimate; sumDps += unit.type.dpsEstimate;
if(unit.abilities.find(a -> a instanceof RepairFieldAbility) instanceof RepairFieldAbility h){ if(unit.abilities.find(a -> a instanceof RepairFieldAbility) instanceof RepairFieldAbility h){
sumRps += h.amount / h.reload * 60f; sumRps += h.amount / h.reload * 60f;
} }
}else{ }else{
curEnemyDps += unit.type.dpsEstimate; float bossMult = unit.isBoss() ? 3f : 1f;
curEnemyHealth += unit.health; curEnemyDps += unit.type.dpsEstimate * unit.damageMultiplier() * bossMult;
curEnemyHealth += unit.health * healthMult * unit.healthMultiplier() * bossMult + unit.shield;
} }
} }
@@ -316,12 +322,6 @@ public class SectorDamage{
for(int wave = state.wave; wave < state.wave + 10; wave ++){ for(int wave = state.wave; wave < state.wave + 10; wave ++){
float sumWaveDps = 0f, sumWaveHealth = 0f; float sumWaveDps = 0f, sumWaveHealth = 0f;
//first wave has to take into account current dps
if(wave == state.wave){
sumWaveDps += curEnemyDps;
sumWaveHealth += curEnemyHealth;
}
for(SpawnGroup group : state.rules.spawns){ for(SpawnGroup group : state.rules.spawns){
float healthMult = 1f + Mathf.clamp(group.type.armor / 20f); float healthMult = 1f + Mathf.clamp(group.type.armor / 20f);
StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect); StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect);
@@ -369,6 +369,11 @@ public class SectorDamage{
info.sumDps = sumDps * 1.05f; info.sumDps = sumDps * 1.05f;
info.sumRps = sumRps; info.sumRps = sumRps;
float cmult = 1.5f;
info.curEnemyDps = curEnemyDps*cmult;
info.curEnemyHealth = curEnemyHealth*cmult;
info.wavesSurvived = getWavesSurvived(info); info.wavesSurvived = getWavesSurvived(info);
} }

View File

@@ -356,43 +356,45 @@ public class JoinDialog extends BaseDialog{
continue; continue;
} }
Table[] groupTable = {null};
//table containing all groups //table containing all groups
global.table(g -> { for(String address : group.addresses){
for(String address : group.addresses){ String resaddress = address.contains(":") ? address.split(":")[0] : address;
String resaddress = address.contains(":") ? address.split(":")[0] : address; int resport = address.contains(":") ? Strings.parseInt(address.split(":")[1]) : port;
int resport = address.contains(":") ? Strings.parseInt(address.split(":")[1]) : port; net.pingHost(resaddress, resport, res -> {
net.pingHost(resaddress, resport, res -> { if(refreshes != cur) return;
if(refreshes != cur) return; res.port = resport;
res.port = resport;
//add header //add header
if(g.getChildren().isEmpty()){ if(groupTable[0] == null){
g.table(head -> { global.table(t -> groupTable[0] = t).row();
if(!group.name.isEmpty()){
head.add(group.name).color(Color.lightGray).padRight(4);
}
head.image().height(3f).growX().color(Color.lightGray);
//button for showing/hiding servers groupTable[0].table(head -> {
ImageButton[] image = {null}; if(!group.name.isEmpty()){
image[0] = head.button(hidden ? Icon.eyeOffSmall : Icon.eyeSmall, Styles.accenti, () -> { head.add(group.name).color(Color.lightGray).padRight(4);
group.setHidden(!group.hidden()); }
image[0].getStyle().imageUp = group.hidden() ? Icon.eyeOffSmall : Icon.eyeSmall; head.image().height(3f).growX().color(Color.lightGray);
if(group.hidden() && !showHidden){
g.remove();
}
}).size(40f).get();
image[0].addListener(new Tooltip(t -> t.background(Styles.black6).margin(4).label(() -> !group.hidden() ? "@server.shown" : "@server.hidden")));
}).width(targetWidth()).padBottom(-2).row();
}
addGlobalHost(res, g); //button for showing/hiding servers
ImageButton[] image = {null};
image[0] = head.button(hidden ? Icon.eyeOffSmall : Icon.eyeSmall, Styles.accenti, () -> {
group.setHidden(!group.hidden());
image[0].getStyle().imageUp = group.hidden() ? Icon.eyeOffSmall : Icon.eyeSmall;
if(group.hidden() && !showHidden){
groupTable[0].remove();
}
}).size(40f).get();
image[0].addListener(new Tooltip(t -> t.background(Styles.black6).margin(4).label(() -> !group.hidden() ? "@server.shown" : "@server.hidden")));
}).width(targetWidth()).padBottom(-2).row();
}
g.margin(5f); addGlobalHost(res, groupTable[0]);
g.pack();
}, e -> {}); groupTable[0].margin(5f);
} groupTable[0].pack();
}).row(); }, e -> {});
}
} }
} }
@@ -402,7 +404,16 @@ public class JoinDialog extends BaseDialog{
container.button(b -> buildServer(host, b), Styles.cleart, () -> { container.button(b -> buildServer(host, b), Styles.cleart, () -> {
Events.fire(new ClientPreConnectEvent(host)); Events.fire(new ClientPreConnectEvent(host));
safeConnect(host.address, host.port, host.version); if(!Core.settings.getBool("server-disclaimer", false)){
ui.showCustomConfirm("@warning", "@servers.disclaimer", "@ok", "@back", () -> {
Core.settings.put("server-disclaimer", true);
safeConnect(host.address, host.port, host.version);
}, () -> {
Core.settings.put("server-disclaimer", false);
});
}else{
safeConnect(host.address, host.port, host.version);
}
}).width(w).row(); }).width(w).row();
} }

View File

@@ -15,8 +15,10 @@ import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.game.Objectives.*;
import mindustry.game.SectorInfo.*; import mindustry.game.SectorInfo.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
@@ -144,7 +146,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
//load legacy research //load legacy research
if(Core.settings.has("unlocks") && !Core.settings.has("junction-unlocked")){ if(Core.settings.has("unlocks") && !Core.settings.has("junction-unlocked")){
Core.app.post(() -> { Core.app.post(() -> {
ui.showCustomConfirm("@research", "@research.legacy", "@research.load", "@research.discard", LegacyIO::readResearch, () -> Core.settings.remove("unlocks")); ui.showCustomConfirm("@research", "@research.legacy", "@research.load", "@research.discard", () -> {
LegacyIO.readResearch();
Core.settings.remove("unlocks");
}, () -> Core.settings.remove("unlocks"));
}); });
} }
@@ -250,7 +255,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
boolean canSelect(Sector sector){ boolean canSelect(Sector sector){
if(mode == select) return sector.hasBase(); if(mode == select) return sector.hasBase();
//preset sectors can only be selected once unlocked //preset sectors can only be selected once unlocked
if(sector.preset != null) return sector.preset.unlocked() || sector.hasBase(); if(sector.preset != null){
TechNode node = sector.preset.node();
return node == null || node.parent == null || node.parent.content.unlocked();
}
return sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector return sector.hasBase() || sector.near().contains(Sector::hasBase); //near an occupied sector
} }
@@ -288,7 +296,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
Color color = Color color =
sec.hasBase() ? Tmp.c2.set(Team.sharded.color).lerp(Team.crux.color, sec.hasEnemyBase() ? 0.5f : 0f) : sec.hasBase() ? Tmp.c2.set(Team.sharded.color).lerp(Team.crux.color, sec.hasEnemyBase() ? 0.5f : 0f) :
sec.preset != null ? Tmp.c2.set(Team.derelict.color).lerp(Color.white, Mathf.absin(Time.time, 10f, 1f)) : sec.preset != null ?
sec.preset.unlocked() ? Tmp.c2.set(Team.derelict.color).lerp(Color.white, Mathf.absin(Time.time, 10f, 1f)) :
Color.gray :
sec.hasEnemyBase() ? Team.crux.color : sec.hasEnemyBase() ? Team.crux.color :
null; null;
@@ -349,7 +359,12 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
for(Sector sec : planet.sectors){ for(Sector sec : planet.sectors){
if(sec != hovered){ if(sec != hovered){
var preficon = sec.icon(); var preficon = sec.icon();
var icon = (sec.isAttacked() ? Fonts.getLargeIcon("warning") : !sec.hasBase() && sec.preset != null && sec.preset.unlocked() && preficon == null ? Fonts.getLargeIcon("terrain") : preficon); var icon =
sec.isAttacked() ? Fonts.getLargeIcon("warning") :
!sec.hasBase() && sec.preset != null && sec.preset.unlocked() && preficon == null ?
Fonts.getLargeIcon("terrain") :
sec.preset != null && sec.preset.locked() && sec.preset.node() != null && !sec.preset.node().parent.content.locked() ? Fonts.getLargeIcon("lock") :
preficon;
var color = sec.preset != null && !sec.hasBase() ? Team.derelict.color : Team.sharded.color; var color = sec.preset != null && !sec.hasBase() ? Team.derelict.color : Team.sharded.color;
if(icon != null){ if(icon != null){
@@ -490,7 +505,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
hoverLabel.touchable = Touchable.disabled; hoverLabel.touchable = Touchable.disabled;
Vec3 pos = planets.cam.project(Tmp.v31.set(hovered.tile.v).setLength(PlanetRenderer.outlineRad).rotate(Vec3.Y, -planets.planet.getRotation()).add(planets.planet.position)); Vec3 pos = planets.cam.project(Tmp.v31.set(hovered.tile.v).setLength(PlanetRenderer.outlineRad).rotate(Vec3.Y, -planets.planet.getRotation()).add(planets.planet.position));
hoverLabel.setPosition(pos.x, pos.y, Align.center); hoverLabel.setPosition(pos.x, pos.y - Core.scene.marginBottom, Align.center);
hoverLabel.getText().setLength(0); hoverLabel.getText().setLength(0);
if(hovered != null){ if(hovered != null){
@@ -687,7 +702,21 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row(); stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row();
if(!sector.hasBase()){ boolean locked = sector.preset != null && sector.preset.locked() && sector.preset.node() != null;
if(locked){
stable.table(r -> {
r.add("@complete").colspan(2).left();
r.row();
for(Objective o : sector.preset.node().objectives){
if(o.complete()) continue;
r.add("> " + o.display()).color(Color.lightGray).left();
r.image(o.complete() ? Icon.ok : Icon.cancel, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
r.row();
}
}).row();
}else if(!sector.hasBase()){
stable.add(Core.bundle.get("sectors.threat") + " [accent]" + sector.displayThreat()).row(); stable.add(Core.bundle.get("sectors.threat") + " [accent]" + sector.displayThreat()).row();
} }
@@ -727,13 +756,23 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
} }
if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked) || debugSelect){ if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked) || debugSelect){
stable.button(mode == select ? "@sectors.select" : sector.isBeingPlayed() ? "@sectors.resume" : sector.hasBase() ? "@sectors.go" : "@sectors.launch", Icon.play, () -> { stable.button(
mode == select ? "@sectors.select" :
sector.isBeingPlayed() ? "@sectors.resume" :
sector.hasBase() ? "@sectors.go" :
locked ? "@locked" : "@sectors.launch",
locked ? Icon.lock :Icon.play, () -> {
if(sector.isBeingPlayed()){ if(sector.isBeingPlayed()){
//already at this sector //already at this sector
hide(); hide();
return; return;
} }
if(sector.preset != null && sector.preset.locked()){
return;
}
boolean shouldHide = true; boolean shouldHide = true;
//save before launch. //save before launch.
@@ -775,7 +814,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
} }
if(shouldHide) hide(); if(shouldHide) hide();
}).growX().height(54f).minWidth(170f).padTop(4); }).growX().height(54f).minWidth(170f).padTop(4).disabled(locked);
} }
stable.pack(); stable.pack();

View File

@@ -124,7 +124,7 @@ public class ChatFragment extends Table{
Draw.color(shadowColor); Draw.color(shadowColor);
if(shown){ if(shown){
Fill.crect(offsetx, chatfield.y, chatfield.getWidth() + 15f, chatfield.getHeight() - 1); Fill.crect(offsetx, chatfield.y + scene.marginBottom, chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
} }
super.draw(); super.draw();
@@ -137,7 +137,7 @@ public class ChatFragment extends Table{
Draw.color(shadowColor); Draw.color(shadowColor);
Draw.alpha(shadowColor.a * opacity); Draw.alpha(shadowColor.a * opacity);
float theight = offsety + spacing + getMarginBottom(); float theight = offsety + spacing + getMarginBottom() + scene.marginBottom;
for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos && (i < fadetime || shown); i++){ for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos && (i < fadetime || shown); i++){
layout.setText(font, messages.get(i).formattedMessage, Color.white, textWidth, Align.bottomLeft, true); layout.setText(font, messages.get(i).formattedMessage, Color.white, textWidth, Align.bottomLeft, true);

View File

@@ -162,6 +162,17 @@ public class HintsFragment extends Fragment{
waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0), waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0),
generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)), generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)),
guardian(() -> state.boss() != null && state.boss().armor >= 4, () -> state.boss() == null), guardian(() -> state.boss() != null && state.boss().armor >= 4, () -> state.boss() == null),
coreUpgrade(() -> state.isCampaign() && Blocks.coreFoundation.unlocked()
&& state.rules.defaultTeam.core() != null
&& state.rules.defaultTeam.core().block == Blocks.coreShard
&& state.rules.defaultTeam.core().items.has(Blocks.coreFoundation.requirements),
() -> ui.hints.placedBlocks.contains(Blocks.coreFoundation)),
presetLaunch(() -> state.isCampaign()
&& state.getSector().preset == null
&& SectorPresets.frozenForest.unlocked()
&& SectorPresets.frozenForest.sector.save == null,
() -> state.isCampaign() && state.getSector().preset == SectorPresets.frozenForest),
coreIncinerate(() -> state.isCampaign() && state.rules.defaultTeam.core() != null && state.rules.defaultTeam.core().items.get(Items.copper) >= state.rules.defaultTeam.core().storageCapacity - 10, () -> false),
; ;
@Nullable @Nullable

View File

@@ -1,7 +1,9 @@
package mindustry.ui.fragments; package mindustry.ui.fragments;
import arc.*;
import arc.func.*; import arc.func.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.scene.*; import arc.scene.*;
import arc.scene.actions.*; import arc.scene.actions.*;
import arc.scene.event.*; import arc.scene.event.*;
@@ -18,7 +20,12 @@ public class LoadingFragment extends Fragment{
@Override @Override
public void build(Group parent){ public void build(Group parent){
parent.fill(Styles.black8, t -> { parent.fill(t -> {
//rect must fill screen completely.
t.rect((x, y, w, h) -> {
Draw.alpha(t.color.a);
Styles.black8.draw(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
});
t.visible = false; t.visible = false;
t.touchable = Touchable.enabled; t.touchable = Touchable.enabled;
t.add().height(133f).row(); t.add().height(133f).row();

View File

@@ -77,12 +77,13 @@ public class MenuFragment extends Fragment{
String versionText = ((Version.build == -1) ? "[#fc8140aa]" : "[#ffffffba]") + Version.combined(); String versionText = ((Version.build == -1) ? "[#fc8140aa]" : "[#ffffffba]") + Version.combined();
parent.fill((x, y, w, h) -> { parent.fill((x, y, w, h) -> {
TextureRegion logo = Core.atlas.find("logo"); TextureRegion logo = Core.atlas.find("logo");
float width = Core.graphics.getWidth(), height = Core.graphics.getHeight() - Core.scene.marginTop;
float logoscl = Scl.scl(1); float logoscl = Scl.scl(1);
float logow = Math.min(logo.width * logoscl, Core.graphics.getWidth() - Scl.scl(20)); float logow = Math.min(logo.width * logoscl, Core.graphics.getWidth() - Scl.scl(20));
float logoh = logow * (float)logo.height / logo.width; float logoh = logow * (float)logo.height / logo.width;
float fx = (int)(Core.graphics.getWidth() / 2f); float fx = (int)(width / 2f);
float fy = (int)(Core.graphics.getHeight() - 6 - logoh) + logoh / 2 - (Core.graphics.isPortrait() ? Scl.scl(30f) : 0f); float fy = (int)(height - 6 - logoh) + logoh / 2 - (Core.graphics.isPortrait() ? Scl.scl(30f) : 0f);
Draw.color(); Draw.color();
Draw.rect(logo, fx, fy, logow, logoh); Draw.rect(logo, fx, fy, logow, logoh);
@@ -230,7 +231,7 @@ public class MenuFragment extends Fragment{
submenu.clearChildren(); submenu.clearChildren();
fadeInMenu(); fadeInMenu();
//correctly offset the button //correctly offset the button
submenu.add().height((Core.graphics.getHeight() - out[0].getY(Align.topLeft)) / Scl.scl(1f)); submenu.add().height((Core.graphics.getHeight() - Core.scene.marginTop - Core.scene.marginBottom - out[0].getY(Align.topLeft)) / Scl.scl(1f));
submenu.row(); submenu.row();
buttons(submenu, b.submenu); buttons(submenu, b.submenu);
}else{ }else{

View File

@@ -28,7 +28,7 @@ public class MinimapFragment extends Fragment{
float size = baseSize * zoom * world.width(); float size = baseSize * zoom * world.width();
Draw.color(Color.black); Draw.color(Color.black);
Fill.crect(x, y, w, h); Fill.crect(0, 0, w, h);
if(renderer.minimap.getTexture() != null){ if(renderer.minimap.getTexture() != null){
Draw.color(); Draw.color();

View File

@@ -112,7 +112,7 @@ public class ScriptConsoleFragment extends Table{
Draw.color(shadowColor); Draw.color(shadowColor);
if(open){ if(open){
Fill.crect(offsetx, chatfield.y, chatfield.getWidth() + 15f, chatfield.getHeight() - 1); Fill.crect(offsetx, chatfield.y + scene.marginBottom, chatfield.getWidth() + 15f, chatfield.getHeight() - 1);
} }
super.draw(); super.draw();
@@ -125,7 +125,7 @@ public class ScriptConsoleFragment extends Table{
Draw.color(shadowColor); Draw.color(shadowColor);
Draw.alpha(shadowColor.a * opacity); Draw.alpha(shadowColor.a * opacity);
float theight = offsety + spacing + getMarginBottom(); float theight = offsety + spacing + getMarginBottom() + scene.marginBottom;
for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos; i++){ for(int i = scrollPos; i < messages.size && i < messagesShown + scrollPos; i++){
layout.setText(font, messages.get(i), Color.white, textWidth, Align.bottomLeft, true); layout.setText(font, messages.get(i), Color.white, textWidth, Align.bottomLeft, true);

View File

@@ -126,7 +126,7 @@ public class Block extends UnlockableContent{
public BlockGroup group = BlockGroup.none; public BlockGroup group = BlockGroup.none;
/** List of block flags. Used for AI indexing. */ /** List of block flags. Used for AI indexing. */
public EnumSet<BlockFlag> flags = EnumSet.of(); public EnumSet<BlockFlag> flags = EnumSet.of();
/** Targeting priority of this block, as seen by enemies.*/ /** Targeting priority of this block, as seen by enemies .*/
public TargetPriority priority = TargetPriority.base; public TargetPriority priority = TargetPriority.base;
/** How much this block affects the unit cap by. /** How much this block affects the unit cap by.
* The block flags must contain unitModifier in order for this to work. */ * The block flags must contain unitModifier in order for this to work. */
@@ -139,9 +139,9 @@ public class Block extends UnlockableContent{
public boolean consumesTap; public boolean consumesTap;
/** Whether to draw the glow of the liquid for this block, if it has one. */ /** Whether to draw the glow of the liquid for this block, if it has one. */
public boolean drawLiquidLight = true; public boolean drawLiquidLight = true;
/** Whether to periodically sync this block across the network.*/ /** Whether to periodically sync this block across the network. */
public boolean sync; public boolean sync;
/** Whether this block uses conveyor-type placement mode.*/ /** Whether this block uses conveyor-type placement mode. */
public boolean conveyorPlacement; public boolean conveyorPlacement;
/** /**
* 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.
@@ -173,12 +173,12 @@ public class Block extends UnlockableContent{
/** Radius of the light emitted by this block. */ /** Radius of the light emitted by this block. */
public float lightRadius = 60f; public float lightRadius = 60f;
/** The sound that this block makes while active. One sound loop. Do not overuse.*/ /** The sound that this block makes while active. One sound loop. Do not overuse. */
public Sound loopSound = Sounds.none; public Sound loopSound = Sounds.none;
/** Active sound base volume. */ /** Active sound base volume. */
public float loopSoundVolume = 0.5f; public float loopSoundVolume = 0.5f;
/** The sound that this block makes while idle. Uses one sound loop for all blocks.*/ /** The sound that this block makes while idle. Uses one sound loop for all blocks. */
public Sound ambientSound = Sounds.none; public Sound ambientSound = Sounds.none;
/** Idle sound base volume. */ /** Idle sound base volume. */
public float ambientSoundVolume = 0.05f; public float ambientSoundVolume = 0.05f;
@@ -193,6 +193,8 @@ public class Block extends UnlockableContent{
public BuildVisibility buildVisibility = BuildVisibility.hidden; public BuildVisibility buildVisibility = BuildVisibility.hidden;
/** Multiplier for speed of building this block. */ /** Multiplier for speed of building this block. */
public float buildCostMultiplier = 1f; public float buildCostMultiplier = 1f;
/** Build completion at which deconstruction finishes. */
public float deconstructThreshold = 0f;
/** Multiplier for cost of research in tech tree. */ /** Multiplier for cost of research in tech tree. */
public float researchCostMultiplier = 1; public float researchCostMultiplier = 1;
/** Whether this block has instant transfer.*/ /** Whether this block has instant transfer.*/
@@ -634,6 +636,8 @@ public class Block extends UnlockableContent{
//also requires inputs //also requires inputs
consumes.each(c -> { consumes.each(c -> {
if(c.isOptional()) return;
if(c instanceof ConsumeItems i){ if(c instanceof ConsumeItems i){
for(ItemStack stack : i.items){ for(ItemStack stack : i.items){
cons.get(stack.item); cons.get(stack.item);

View File

@@ -37,8 +37,12 @@ public class Build{
Block previous = tile.block(); Block previous = tile.block();
Block sub = ConstructBlock.get(previous.size); Block sub = ConstructBlock.get(previous.size);
Seq<Building> prevBuild = new Seq<>(1);
if(tile.build != null) prevBuild.add(tile.build);
tile.setBlock(sub, team, rotation); tile.setBlock(sub, team, rotation);
tile.<ConstructBuild>bc().setDeconstruct(previous); tile.<ConstructBuild>bc().setDeconstruct(previous);
tile.<ConstructBuild>bc().prevBuild = prevBuild;
tile.build.health = tile.build.maxHealth * prevPercent; tile.build.health = tile.build.maxHealth * prevPercent;
if(unit != null && unit.isPlayer()) tile.build.lastAccessed = unit.getPlayer().name; if(unit != null && unit.isPlayer()) tile.build.lastAccessed = unit.getPlayer().name;

View File

@@ -289,7 +289,7 @@ public class ConstructBlock extends Block{
progress = Mathf.clamp(progress - amount); progress = Mathf.clamp(progress - amount);
if(progress <= 0 || state.rules.infiniteResources){ if(progress <= (previous == null ? 0 : previous.deconstructThreshold) || state.rules.infiniteResources){
if(lastBuilder == null) lastBuilder = builder; if(lastBuilder == null) lastBuilder = builder;
Call.deconstructFinish(tile, this.cblock == null ? previous : this.cblock, lastBuilder); Call.deconstructFinish(tile, this.cblock == null ? previous : this.cblock, lastBuilder);
} }

View File

@@ -255,7 +255,7 @@ public class Conveyor extends Block implements Autotiler{
} }
public boolean pass(Item item){ public boolean pass(Item item){
if(next != null && next.team == team && next.acceptItem(this, item)){ if(item != null && next != null && next.team == team && next.acceptItem(this, item)){
next.handleItem(this, item); next.handleItem(this, item);
return true; return true;
} }

View File

@@ -242,7 +242,7 @@ public class MassDriver extends Block{
@Override @Override
public boolean acceptItem(Building source, Item item){ public boolean acceptItem(Building source, Item item){
//mass drivers that ouput only cannot accept items //mass drivers that output only cannot accept items
return items.total() < itemCapacity && linkValid(); return items.total() < itemCapacity && linkValid();
} }

View File

@@ -12,6 +12,8 @@ public class Boulder extends Block{
super(name); super(name);
breakable = true; breakable = true;
alwaysReplace = true; alwaysReplace = true;
deconstructThreshold = 0.35f;
} }
@Override @Override

View File

@@ -27,6 +27,8 @@ import java.util.zip.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
public class LogicBlock extends Block{ public class LogicBlock extends Block{
private static final int maxByteLen = 1024 * 500;
public int maxInstructionScale = 5; public int maxInstructionScale = 5;
public int instructionsPerTick = 1; public int instructionsPerTick = 1;
public float range = 8 * 10; public float range = 8 * 10;
@@ -137,6 +139,9 @@ public class LogicBlock extends Block{
stream.read(); stream.read();
int bytelen = stream.readInt(); int bytelen = stream.readInt();
if(bytelen > maxByteLen) throw new RuntimeException("Malformed logic data! Length: " + bytelen);
byte[] bytes = new byte[bytelen]; byte[] bytes = new byte[bytelen];
stream.readFully(bytes); stream.readFully(bytes);
@@ -197,6 +202,7 @@ public class LogicBlock extends Block{
int version = stream.read(); int version = stream.read();
int bytelen = stream.readInt(); int bytelen = stream.readInt();
if(bytelen > maxByteLen) throw new RuntimeException("Malformed logic data! Length: " + bytelen);
byte[] bytes = new byte[bytelen]; byte[] bytes = new byte[bytelen];
stream.readFully(bytes); stream.readFully(bytes);

View File

@@ -114,6 +114,13 @@ public class Reconstructor extends UnitBlock{
return capacities[item.id]; return capacities[item.id];
} }
@Override
public void overwrote(Seq<Building> builds){
if(builds.first().block == block){
items.add(builds.first().items);
}
}
@Override @Override
public void draw(){ public void draw(){
Draw.rect(region, x, y); Draw.rect(region, x, y);

View File

@@ -0,0 +1,5 @@
[This is a truncated changelog, see Github for full notes]
- Fixed two rare crashes
- Made units depend on their production materials in tech tree
- Made conveyors replaceable by distributors
- Translation updates

View File

@@ -0,0 +1,5 @@
[This is a truncated changelog, see Github for full notes]
Yeah, it's another hotfix. I missed these during earlier testing.
- Fixed research deadlock caused by blocks requiring optional inputs as research
- Possible fix for "Unsupported combination of formats" startup error

View File

@@ -0,0 +1,8 @@
[This is a truncated changelog, see Github for full notes]
- Added disclaimer for community servers
- Added hints concerning core upgrades and numbered sectors
- Decreased spore storm opacity
- Made items stay inside reconstructors when rebuilding (Contributed by @Quezler)
- Made early game procedural sectors slightly easier
- Fixed some blocks having duplicate research dependencies
- Fixed legacy research not being cleared after first import

View File

@@ -56,21 +56,21 @@
"NEW_ACHIEVEMENT_21_3_NAME" "Swarm" "NEW_ACHIEVEMENT_21_3_NAME" "Swarm"
"NEW_ACHIEVEMENT_21_3_DESC" "Have 100 units active at once." "NEW_ACHIEVEMENT_21_3_DESC" "Have 100 units active at once."
"NEW_ACHIEVEMENT_21_4_NAME" "Flock" "NEW_ACHIEVEMENT_21_4_NAME" "Flock"
"NEW_ACHIEVEMENT_21_4_DESC" "Have 10 Phantom Poly drones active at once." "NEW_ACHIEVEMENT_21_4_DESC" "Have 10 Poly drones active at once."
"NEW_ACHIEVEMENT_21_5_NAME" "Roboticist" "NEW_ACHIEVEMENT_21_5_NAME" "Roboticist"
"NEW_ACHIEVEMENT_21_5_DESC" "Build every type of unit." "NEW_ACHIEVEMENT_21_5_DESC" "Build every type of unit."
"NEW_ACHIEVEMENT_21_6_NAME" "Legions" "NEW_ACHIEVEMENT_21_6_NAME" "Legions"
"NEW_ACHIEVEMENT_21_6_DESC" "Build 1000 units total." "NEW_ACHIEVEMENT_21_6_DESC" "Build 1000 units total."
"NEW_ACHIEVEMENT_21_9_NAME" "You Should've Listened" "NEW_ACHIEVEMENT_21_9_NAME" "You Should've Listened"
"NEW_ACHIEVEMENT_21_9_DESC" "Die in the drop point exclusion zone." "NEW_ACHIEVEMENT_21_9_DESC" "Die in the drop point exclusion zone."
"NEW_ACHIEVEMENT_21_10_NAME" "There are naval units for that." "NEW_ACHIEVEMENT_21_10_NAME" "There Are Naval Units For That"
"NEW_ACHIEVEMENT_21_10_DESC" "Drown." "NEW_ACHIEVEMENT_21_10_DESC" "Drown."
"NEW_ACHIEVEMENT_21_11_NAME" "Collector" "NEW_ACHIEVEMENT_21_11_NAME" "Collector"
"NEW_ACHIEVEMENT_21_11_DESC" "Fill the core to maximum capacity with every type of material." "NEW_ACHIEVEMENT_21_11_DESC" "Fill the core to maximum capacity with every type of material."
"NEW_ACHIEVEMENT_21_12_NAME" "Crowd" "NEW_ACHIEVEMENT_21_12_NAME" "Crowd"
"NEW_ACHIEVEMENT_21_12_DESC" "Host a server with 10 players on it." "NEW_ACHIEVEMENT_21_12_DESC" "Host a server with 10 players on it."
"NEW_ACHIEVEMENT_21_13_NAME" "Invulnerable" "NEW_ACHIEVEMENT_21_13_NAME" "Invulnerable"
"NEW_ACHIEVEMENT_21_13_DESC" "Build the Meltdown and, Spectre and Foreshadow.." "NEW_ACHIEVEMENT_21_13_DESC" "Build the Meltdown, Spectre and Foreshadow."
"NEW_ACHIEVEMENT_21_14_NAME" "Liftoff" "NEW_ACHIEVEMENT_21_14_NAME" "Liftoff"
"NEW_ACHIEVEMENT_21_14_DESC" "Use the Launch Pad." "NEW_ACHIEVEMENT_21_14_DESC" "Use the Launch Pad."
"NEW_ACHIEVEMENT_21_16_NAME" "Heresy" "NEW_ACHIEVEMENT_21_16_NAME" "Heresy"
@@ -90,7 +90,7 @@
"NEW_ACHIEVEMENT_21_23_NAME" "Ignition" "NEW_ACHIEVEMENT_21_23_NAME" "Ignition"
"NEW_ACHIEVEMENT_21_23_DESC" "Power up an Impact Reactor." "NEW_ACHIEVEMENT_21_23_DESC" "Power up an Impact Reactor."
"NEW_ACHIEVEMENT_21_24_NAME" "Acceleration" "NEW_ACHIEVEMENT_21_24_NAME" "Acceleration"
"NEW_ACHIEVEMENT_21_24_DESC" "Activate the Interplanteary Accelerator." "NEW_ACHIEVEMENT_21_24_DESC" "Activate the Interplanetary Accelerator."
"NEW_ACHIEVEMENT_21_25_NAME" "The Spiral" "NEW_ACHIEVEMENT_21_25_NAME" "The Spiral"
"NEW_ACHIEVEMENT_21_25_DESC" "Round and round it goes..." "NEW_ACHIEVEMENT_21_25_DESC" "Round and round it goes..."
"NEW_ACHIEVEMENT_21_26_NAME" "Escalation" "NEW_ACHIEVEMENT_21_26_NAME" "Escalation"

View File

@@ -43,10 +43,10 @@
[list] [list]
[*] 16 built in maps for custom games, in addition to campaign [*] 16 built in maps for custom games, in addition to campaign
[*] Play Co-op, PvP or sandbox [*] Play co-op, PvP or sandbox
[*] Join a public dedicated server, or invite friends to your own private session [*] Join a public dedicated server, or invite friends to your own private session
[*] Customizable game rules: Change block costs, enemy stats, starting items, wave timing and more [*] Customizable game rules: Change block costs, enemy stats, starting items, wave timing and more
[*] Mix&match gamemodes: Combine PvP and PvE gamemodes together [*] Mix & match gamemodes: Combine PvP and PvE gamemodes together
[/list] [/list]
[h2]Custom Map Editor[/h2] [h2]Custom Map Editor[/h2]

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=e13f9a192be0bad00766ff15a6bf7d2897ba00d1 archash=d44cbae08716cfe97012c84399a1e61854d6b16c

View File

@@ -5,9 +5,6 @@
{ {
"address": "nydustry.nydus.app:6060" "address": "nydustry.nydus.app:6060"
}, },
{
"address": "md.surrealment.com"
},
{ {
"address": "routerchain.ddns.net:6568" "address": "routerchain.ddns.net:6568"
}, },

View File

@@ -5,7 +5,7 @@
}, },
{ {
"name": "{AA}", "name": "{AA}",
"address": ["aamindustry.play.ai:6571", "aamindustry.play.ai:6572"] "address": ["aamindustry.play.ai", "aamindustry.play.ai:6571", "aamindustry.play.ai:6572", "aamindustry.play.ai:6573"]
}, },
{ {
"name": "Atanner", "name": "Atanner",
@@ -42,5 +42,9 @@
{ {
"name": "Gearblock", "name": "Gearblock",
"address": ["attack.gearblock.app"] "address": ["attack.gearblock.app"]
},
{
"name": "Surrealment",
"address": ["md.surrealment.com"]
} }
] ]

View File

@@ -58,6 +58,7 @@ public class ApplicationTests{
add(netServer = new NetServer()); add(netServer = new NetServer());
content.init(); content.init();
} }
@Override @Override
@@ -77,9 +78,15 @@ public class ApplicationTests{
} }
Thread.sleep(10); Thread.sleep(10);
} }
Block block = content.getByName(ContentType.block, "build2");
assertEquals("build2", block == null ? null : block.name, "2x2 construct block doesn't exist?");
}catch(Throwable r){ }catch(Throwable r){
fail(r); fail(r);
} }
Log.info("init app");
} }
@BeforeEach @BeforeEach
@@ -496,8 +503,8 @@ public class ApplicationTests{
void buildingOverlap(){ void buildingOverlap(){
initBuilding(); initBuilding();
Builderc d1 = (Builderc)UnitTypes.poly.create(Team.sharded); Unit d1 = UnitTypes.poly.create(Team.sharded);
Builderc d2 = (Builderc)UnitTypes.poly.create(Team.sharded); Unit d2 = UnitTypes.poly.create(Team.sharded);
//infinite build range //infinite build range
state.rules.editor = true; state.rules.editor = true;
@@ -523,8 +530,8 @@ public class ApplicationTests{
void buildingDestruction(){ void buildingDestruction(){
initBuilding(); initBuilding();
Builderc d1 = (Builderc)UnitTypes.poly.create(Team.sharded); Builderc d1 = UnitTypes.poly.create(Team.sharded);
Builderc d2 = (Builderc)UnitTypes.poly.create(Team.sharded); Builderc d2 = UnitTypes.poly.create(Team.sharded);
d1.set(10f, 20f); d1.set(10f, 20f);
d2.set(10f, 20f); d2.set(10f, 20f);

View File

@@ -34,7 +34,7 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{
public void createGenerator(InputType inputType){ public void createGenerator(InputType inputType){
Vars.state = new GameState(); Vars.state = new GameState();
Vars.state.rules = new Rules(); Vars.state.rules = new Rules();
generator = new ItemLiquidGenerator(inputType != InputType.liquids, inputType != InputType.items, "fakegen"){ generator = new ItemLiquidGenerator(inputType != InputType.liquids, inputType != InputType.items, "fakegen" + System.nanoTime()){
{ {
powerProduction = 0.1f; powerProduction = 0.1f;
itemDuration = fakeItemDuration; itemDuration = fakeItemDuration;

View File

@@ -25,38 +25,46 @@ public class PowerTestFixture{
@BeforeAll @BeforeAll
static void initializeDependencies(){ static void initializeDependencies(){
Log.info("init power test fixture");
headless = true; headless = true;
Core.graphics = new FakeGraphics(); Core.graphics = new FakeGraphics();
Core.files = new MockFiles(); Core.files = new MockFiles();
Vars.content = new ContentLoader(){
@Override
public void handleMappableContent(MappableContent content){
} boolean make = content == null;
};
if(make){
Vars.content = new ContentLoader(){
@Override
public void handleMappableContent(MappableContent content){
}
};
}
Vars.state = new GameState(); Vars.state = new GameState();
Vars.tree = new FileTree(); Vars.tree = new FileTree();
content.createBaseContent(); if(make){
content.createBaseContent();
}
Log.useColors = false; Log.useColors = false;
Time.setDeltaProvider(() -> 0.5f); Time.setDeltaProvider(() -> 0.5f);
} }
protected static PowerGenerator createFakeProducerBlock(float producedPower){ protected static PowerGenerator createFakeProducerBlock(float producedPower){
return new PowerGenerator("fakegen"){{ return new PowerGenerator("fakegen" + System.nanoTime()){{
buildType = () -> new GeneratorBuild(); buildType = () -> new GeneratorBuild();
powerProduction = producedPower; powerProduction = producedPower;
}}; }};
} }
protected static Battery createFakeBattery(float capacity){ protected static Battery createFakeBattery(float capacity){
return new Battery("fakebattery"){{ return new Battery("fakebattery" + System.nanoTime()){{
buildType = () -> new BatteryBuild(); buildType = () -> new BatteryBuild();
consumes.powerBuffered(capacity); consumes.powerBuffered(capacity);
}}; }};
} }
protected static Block createFakeDirectConsumer(float powerPerTick){ protected static Block createFakeDirectConsumer(float powerPerTick){
return new PowerBlock("fakedirectconsumer"){{ return new PowerBlock("fakedirectconsumer" + System.nanoTime()){{
buildType = Building::create; buildType = Building::create;
consumes.power(powerPerTick); consumes.power(powerPerTick);
}}; }};