Merge branch 'master' of https://github.com/Anuken/Mindustry into v106-alpha

This commit is contained in:
Petr Gašparík
2020-09-27 23:50:48 +02:00
127 changed files with 6471 additions and 6115 deletions

View File

@@ -7,6 +7,8 @@ assignees: ''
---
**Note**: Do not report any new bugs directly relating to the v6 campaign. They will not be fixed or considered at this time.
**Platform**: *Android/iOS/Mac/Windows/Linux*
**Build**: *The build number under the title in the main menu. Required.*

View File

@@ -22,9 +22,9 @@ First, make sure you have [JDK 14](https://adoptopenjdk.net/) installed. Open a
#### Windows
_Running:_ `gradlew.bat desktop:run`
_Building:_ `gradlew.bat desktop:dist`
_Sprite Packing:_ `gradlew.bat tools:pack`
_Running:_ `gradlew desktop:run`
_Building:_ `gradlew desktop:dist`
_Sprite Packing:_ `gradlew tools:pack`
#### Linux/Mac OS
@@ -70,3 +70,7 @@ Post feature requests and feedback [here](https://github.com/Anuken/Mindustry-Su
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/io.anuke.mindustry/)
[<img src="https://flathub.org/assets/badges/flathub-badge-en.svg"
alt="Download On Flathub"
height="60">](https://flathub.org/apps/details/com.github.Anuken.Mindustry)

View File

@@ -7,7 +7,6 @@ import android.content.pm.*;
import android.net.*;
import android.os.Build.*;
import android.os.*;
import android.provider.Settings.*;
import android.telephony.*;
import arc.*;
import arc.backend.android.*;
@@ -15,7 +14,7 @@ import arc.files.*;
import arc.func.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import arc.util.serialization.*;
import dalvik.system.*;
import mindustry.*;
import mindustry.game.Saves.*;
import mindustry.io.*;
@@ -23,7 +22,6 @@ import mindustry.net.*;
import mindustry.ui.dialogs.*;
import java.io.*;
import java.lang.System;
import java.lang.Thread.*;
import java.util.*;
@@ -73,12 +71,25 @@ public class AndroidLauncher extends AndroidApplication{
public void shareFile(Fi file){
}
@Override
public Class<?> loadJar(Fi jar, String mainClass) throws Exception{
DexClassLoader loader = new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, getClassLoader());
return Class.forName(mainClass, true, loader);
}
@Override
public void showFileChooser(boolean open, String extension, Cons<Fi> cons){
showFileChooser(open, cons, extension);
}
void showFileChooser(boolean open, Cons<Fi> cons, String... extensions){
String extension = extensions[0];
if(VERSION.SDK_INT >= VERSION_CODES.Q){
Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(extension.equals("zip") && !open ? "application/zip" : "*/*");
intent.setType(extension.equals("zip") && !open && extensions.length == 1 ? "application/zip" : "*/*");
addResultListener(i -> startActivityForResult(intent, i), (code, in) -> {
if(code == Activity.RESULT_OK && in != null && in.getData() != null){
Uri uri = in.getData();
@@ -108,7 +119,7 @@ public class AndroidLauncher extends AndroidApplication{
});
}else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){
chooser = new FileChooser(open ? "@open" : "@save", file -> file.extension().equalsIgnoreCase(extension), open, file -> {
chooser = new FileChooser(open ? "@open" : "@save", file -> Structs.contains(extensions, file.extension().toLowerCase()), open, file -> {
if(!open){
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
}else{
@@ -125,10 +136,19 @@ public class AndroidLauncher extends AndroidApplication{
}
requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE);
}else{
super.showFileChooser(open, extension, cons);
if(open){
new FileChooser("@open", file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show();
}else{
super.showFileChooser(open, extension, cons);
}
}
}
@Override
public void showMultiFileChooser(Cons<Fi> cons, String... extensions){
showFileChooser(true, cons, extensions);
}
@Override
public void beginForceLandscape(){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);

View File

@@ -1,6 +1,5 @@
package mindustry.annotations.entity;
import arc.*;
import arc.files.*;
import arc.func.*;
import arc.struct.*;
@@ -520,7 +519,7 @@ public class EntityProcess extends BaseProcessor{
//add free code to remove methods - always at the end
//this only gets called next frame.
if(first.name().equals("remove") && ann.pooled()){
mbuilder.addStatement("$T.app.post(() -> $T.free(this))", Core.class, Pools.class);
mbuilder.addStatement("mindustry.gen.Groups.queueFree(($T)this)", Poolable.class);
}
builder.addMethod(mbuilder.build());
@@ -587,6 +586,17 @@ public class EntityProcess extends BaseProcessor{
//write clear
groupsBuilder.addMethod(groupClear.build());
//add method for pool storage
groupsBuilder.addField(FieldSpec.builder(ParameterizedTypeName.get(Seq.class, Poolable.class), "freeQueue", Modifier.PRIVATE, Modifier.STATIC).initializer("new Seq<>()").build());
//method for freeing things
MethodSpec.Builder groupFreeQueue = MethodSpec.methodBuilder("queueFree")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(Poolable.class, "obj")
.addStatement("freeQueue.add(obj)");
groupsBuilder.addMethod(groupFreeQueue.build());
//add method for resizing all necessary groups
MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize")
.addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h")
@@ -595,6 +605,11 @@ public class EntityProcess extends BaseProcessor{
MethodSpec.Builder groupUpdate = MethodSpec.methodBuilder("update")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
//free everything pooled at the start of each updaet
groupUpdate
.addStatement("for($T p : freeQueue) $T.free(p)", Poolable.class, Pools.class)
.addStatement("freeQueue.clear()");
//method resize
for(GroupDefinition group : groupDefs){
if(group.spatial){

View File

@@ -3,6 +3,8 @@ package mindustry.annotations.impl;
import arc.files.*;
import arc.scene.style.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import arc.util.serialization.*;
import com.squareup.javapoet.*;
import mindustry.annotations.Annotations.*;
@@ -33,6 +35,17 @@ public class AssetsProcess extends BaseProcessor{
String resources = rootDirectory + "/core/assets-raw/sprites/ui";
Jval icons = Jval.read(Fi.get(rootDirectory + "/core/assets-raw/fontgen/config.json").readString());
ObjectMap<String, String> texIcons = new OrderedMap<>();
PropertiesUtils.load(texIcons, Fi.get(rootDirectory + "/core/assets/icons/icons.properties").reader());
texIcons.each((key, val) -> {
String[] split = val.split("\\|");
String name = Strings.kebabToCamel(split[1]).replace("Medium", "").replace("Icon", "");
if(SourceVersion.isKeyword(name) || name.equals("char")) name = name + "i";
ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("(char)" + key).build());
});
ictype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectMap.class, String.class, TextureRegionDrawable.class),
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());

View File

@@ -67,7 +67,7 @@ public class LogicStatementProcessor extends BaseProcessor{
int index = 0;
for(Svar field : fields){
if(field.is(Modifier.TRANSIENT)) continue;
if(field.isAny(Modifier.TRANSIENT, Modifier.STATIC)) continue;
writer.addStatement("out.append(\" \")");
writer.addStatement("out.append((($T)obj).$L$L)", c.mirror(), field.name(),

View File

@@ -200,7 +200,6 @@ project(":desktop"){
dependencies{
implementation project(":core")
implementation arcModule("natives:natives-box2d-desktop")
implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop")
implementation 'com.github.MinnDevelopment:java-discord-rpc:v2.0.1'
@@ -239,7 +238,6 @@ project(":ios"){
implementation arcModule("natives:natives-ios")
implementation arcModule("natives:natives-freetype-ios")
implementation arcModule("natives:natives-box2d-ios")
implementation arcModule("backends:backend-robovm")
compileOnly project(":annotations")
@@ -282,7 +280,6 @@ project(":core"){
api "org.lz4:lz4-java:1.4.1"
api arcModule("arc-core")
api arcModule("extensions:freetype")
api arcModule("extensions:box2d")
api arcModule("extensions:g3d")
api arcModule("extensions:fx")
api arcModule("extensions:arcnet")
@@ -299,7 +296,6 @@ project(":server"){
dependencies{
implementation project(":core")
implementation arcModule("natives:natives-box2d-desktop")
implementation arcModule("backends:backend-headless")
}
}
@@ -312,7 +308,6 @@ project(":tests"){
testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
testImplementation arcModule("backends:backend-headless")
testImplementation arcModule("natives:natives-box2d-desktop")
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
}
@@ -334,7 +329,6 @@ project(":tools"){
implementation arcModule("natives:natives-desktop")
implementation arcModule("natives:natives-freetype-desktop")
implementation arcModule("natives:natives-box2d-desktop")
implementation arcModule("backends:backend-headless")
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 B

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 B

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -473,16 +473,9 @@ requirement.wave = Reach Wave {0} in {1}
requirement.core = Destroy Enemy Core in {0}
requirement.research = Research {0}
requirement.capture = Capture {0}
resume = Resume Zone:\n[lightgray]{0}
bestwave = [lightgray]Best Wave: {0}
#TODO fix/remove this
launch = < LAUNCH >
launch.text = Launch
launch.title = Launch Successful
launch.next = [lightgray]next opportunity at wave {0}
launch.unable2 = [scarlet]Unable to LAUNCH.[]
launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base.
launch.skip.confirm = If you skip now, you will not be able to launch until later waves.
campaign.multiplayer = While playing multiplayer in campaign, you can only research using items from [accent]your[] sectors, [scarlet]not[] the host's sector that you are on right now.\n\nTo get items to [accent]your[] sectors in multiplayer, use a [accent]launch pad[].
uncover = Uncover
configure = Configure Loadout
#TODO
@@ -1173,7 +1166,7 @@ tutorial.drillturret = Duo turrets require[accent] copper ammo[] to shoot.\nPlac
tutorial.pause = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press space to pause.
tutorial.pause.mobile = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press this button in the top left to pause.
tutorial.unpause = Now press space again to unpause.
tutorial.unpause.mobile = Now press it again to unpause.d
tutorial.unpause.mobile = Now press it again to unpause.
tutorial.breaking = Blocks frequently need to be destroyed.\n[accent]Hold down right-click[] to destroy all blocks in a selection.[]\n\n[accent]Destroy all the scrap blocks to the left of your core using area selection.
tutorial.breaking.mobile = Blocks frequently need to be destroyed.\n[accent]Select deconstruction mode[], then tap a block to begin breaking it.\nDestroy an area by holding down your finger for a few seconds[] and dragging in a direction.\nPress the checkmark button to confirm breaking.\n\n[accent]Destroy all the scrap blocks to the left of your core using area selection.
tutorial.withdraw = In some situations, taking items directly from blocks is necessary.\nTo do this, [accent]tap a block[] with items in it, then [accent]tap the item[] in the inventory.\nMultiple items can be withdrawn by [accent]tapping and holding[].\n\n[accent]Withdraw some copper from the core.[]

View File

@@ -21,15 +21,8 @@
63723=ice|block-ice-medium
63722=ice-snow|block-ice-snow-medium
63721=cliffs|block-cliffs-medium
63720=rocks|block-rocks-medium
63719=sporerocks|block-sporerocks-medium
63718=rock|block-rock-medium
63717=snowrock|block-snowrock-medium
63716=icerocks|block-icerocks-medium
63715=snowrocks|block-snowrocks-medium
63714=dunerocks|block-dunerocks-medium
63713=sandrocks|block-sandrocks-medium
63712=saltrocks|block-saltrocks-medium
63711=spore-pine|block-spore-pine-medium
63710=snow-pine|block-snow-pine-medium
63709=pine|block-pine-medium
@@ -38,7 +31,6 @@
63706=white-tree|block-white-tree-medium
63705=spore-cluster|block-spore-cluster-medium
63704=shale|block-shale-medium
63703=shalerocks|block-shalerocks-medium
63702=shale-boulder|block-shale-boulder-medium
63701=sand-boulder|block-sand-boulder-medium
63700=moss|block-moss-medium

View File

@@ -154,7 +154,6 @@ const SaveLoadEvent = Packages.mindustry.game.EventType.SaveLoadEvent
const MapPublishEvent = Packages.mindustry.game.EventType.MapPublishEvent
const MapMakeEvent = Packages.mindustry.game.EventType.MapMakeEvent
const ResizeEvent = Packages.mindustry.game.EventType.ResizeEvent
const LaunchEvent = Packages.mindustry.game.EventType.LaunchEvent
const LoseEvent = Packages.mindustry.game.EventType.LoseEvent
const WinEvent = Packages.mindustry.game.EventType.WinEvent
const Trigger = Packages.mindustry.game.EventType.Trigger

Binary file not shown.

Before

Width:  |  Height:  |  Size: 813 B

After

Width:  |  Height:  |  Size: 812 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 KiB

After

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 KiB

After

Width:  |  Height:  |  Size: 668 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 951 KiB

After

Width:  |  Height:  |  Size: 969 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 KiB

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 KiB

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -63,7 +63,7 @@ public class Vars implements Loadable{
/** URL to the JSON file containing all the BE servers. Only queried in the V6 alpha (will be removed once it's out). */
public static final String serverJsonV6URL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_v6.json";
/** URL of the github issue report template.*/
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?template=bug_report.md";
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?labels=bug&template=bug_report.md";
/** list of built-in servers.*/
public static final Seq<String> defaultServers = Seq.with();
/** maximum distance between mine and core that supports automatic transferring */
@@ -72,8 +72,6 @@ public class Vars implements Loadable{
public static final int maxTextLength = 150;
/** max player name length in bytes */
public static final int maxNameLength = 40;
/** shadow color for turrets */
public static final float turretShadowColor = Color.toFloatBits(0, 0, 0, 0.22f);
/** displayed item size when ingame. */
public static final float itemSize = 5f;
/** units outside of this bound will die instantly */

View File

@@ -6,6 +6,7 @@ import arc.math.*;
import arc.math.geom.*;
import arc.struct.EnumSet;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
@@ -165,6 +166,11 @@ public class BlockIndexer{
return flagMap[team.id][type.ordinal()];
}
@Nullable
public Tile findClosestFlag(float x, float y, Team team, BlockFlag flag){
return Geometry.findClosest(x, y, getAllied(team, flag));
}
public boolean eachBlock(Teamc team, float range, Boolf<Building> pred, Cons<Building> cons){
return eachBlock(team.team(), team.getX(), team.getY(), range, pred, cons);
}

View File

@@ -43,7 +43,7 @@ public class Pathfinder implements Runnable{
PathTile.health(tile) * 5 +
(PathTile.nearSolid(tile) ? 2 : 0) +
(PathTile.nearLiquid(tile) ? 6 : 0) +
(PathTile.deep(tile) ? 70 : 0) +
(PathTile.deep(tile) ? 6000 : 0) +
(PathTile.damages(tile) ? 30 : 0),
//legs
@@ -116,7 +116,7 @@ public class Pathfinder implements Runnable{
}
return PathTile.get(
tile.build == null ? 0 : Math.min((int)(tile.build.health / 40), 127),
tile.build == null ? 0 : Math.min((int)(tile.build.health / 40), 80),
tile.getTeamID(),
tile.solid(),
tile.floor().isLiquid,

View File

@@ -16,7 +16,7 @@ public class FlyingAI extends AIController{
moveTo(target, unit.range() * 0.8f);
unit.lookAt(target);
}else{
attack(80f);
attack(100f);
}
}

View File

@@ -51,7 +51,6 @@ public class GroundAI extends AIController{
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type().rotateShooting){
if(unit.type().hasWeapons()){
//TODO certain units should not look at the target, e.g. ships
unit.lookAt(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
}
}else if(unit.moving()){

View File

@@ -1,31 +1,25 @@
package mindustry.async;
import arc.box2d.*;
import arc.box2d.BodyDef.*;
import arc.math.*;
import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.struct.*;
import mindustry.*;
import mindustry.entities.*;
import mindustry.async.PhysicsProcess.PhysicsWorld.*;
import mindustry.gen.*;
public class PhysicsProcess implements AsyncProcess{
private Physics physics;
private static final int
layers = 3,
layerGround = 0,
layerLegs = 1,
layerFlying = 2;
private PhysicsWorld physics;
private Seq<PhysicRef> refs = new Seq<>(false);
private BodyDef def;
private EntityGroup<? extends Physicsc> group;
private Filter flying = new Filter(){{
maskBits = categoryBits = 2;
}}, ground = new Filter(){{
maskBits = categoryBits = 1;
}};
public PhysicsProcess(){
def = new BodyDef();
def.type = BodyType.dynamicBody;
//currently only enabled for units
group = Groups.unit;
}
//currently only enabled for units
private EntityGroup<Unit> group = Groups.unit;
@Override
public void begin(){
@@ -34,47 +28,39 @@ public class PhysicsProcess implements AsyncProcess{
//remove stale entities
refs.removeAll(ref -> {
if(!ref.entity.isAdded()){
physics.destroyBody(ref.body);
physics.remove(ref.body);
ref.entity.physref(null);
return true;
}
return false;
});
//find entities without bodies and assign them
for(Physicsc entity : group){
boolean grounded = entity.isGrounded();
int bits = grounded ? ground.maskBits : flying.maskBits;
//find Unit without bodies and assign them
for(Unit entity : group){
if(entity.physref() == null){
//add bodies to entities that have none
FixtureDef fd = new FixtureDef();
fd.shape = new CircleShape(entity.hitSize() / 2f);
fd.density = 5f;
fd.restitution = 0.0f;
fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits;
def.position.set(entity);
Body body = physics.createBody(def);
body.createFixture(fd);
PhysicsBody body = new PhysicsBody();
body.x = entity.x();
body.y = entity.y();
body.mass = entity.mass();
body.radius = entity.hitSize() / 2f;
PhysicRef ref = new PhysicRef(entity, body);
refs.add(ref);
entity.physref(ref);
physics.add(body);
}
//save last position
PhysicRef ref = entity.physref();
if(ref.body.getFixtureList().any() && ref.body.getFixtureList().first().getFilterData().categoryBits != bits){
//set correct filter
ref.body.getFixtureList().first().setFilterData(grounded ? ground : flying);
}
ref.velocity.set(entity.deltaX(), entity.deltaY());
ref.position.set(entity);
ref.body.layer =
entity.type().allowLegStep ? layerLegs :
entity.isGrounded() ? layerGround : layerFlying;
ref.x = entity.x();
ref.y = entity.y();
}
}
@@ -85,26 +71,11 @@ public class PhysicsProcess implements AsyncProcess{
//get last position vectors before step
for(PhysicRef ref : refs){
//force set target position
ref.body.setPosition(ref.position.x, ref.position.y);
//save last position for delta
ref.lastPosition.set(ref.body.getPosition());
//write velocity
ref.body.setLinearVelocity(ref.velocity);
ref.lastVelocity.set(ref.velocity);
ref.body.x = ref.x;
ref.body.y = ref.y;
}
physics.step(1f/45f, 5, 8);
//get delta vectors
for(PhysicRef ref : refs){
//get delta vector
ref.delta.set(ref.body.getPosition()).sub(ref.lastPosition);
ref.velocity.set(ref.body.getLinearVelocity());
}
physics.update();
}
@Override
@@ -115,13 +86,8 @@ public class PhysicsProcess implements AsyncProcess{
for(PhysicRef ref : refs){
Physicsc entity = ref.entity;
entity.move(ref.delta.x, ref.delta.y);
//save last position
ref.position.set(entity);
//add delta velocity - this doesn't work very well yet
//entity.vel().add(ref.velocity).sub(ref.lastVelocity);
//move by delta
entity.move(ref.body.x - ref.x, ref.body.y - ref.y);
}
}
@@ -129,7 +95,6 @@ public class PhysicsProcess implements AsyncProcess{
public void reset(){
if(physics != null){
refs.clear();
physics.dispose();
physics = null;
}
}
@@ -138,17 +103,95 @@ public class PhysicsProcess implements AsyncProcess{
public void init(){
reset();
physics = new Physics(new Vec2(), true);
physics = new PhysicsWorld(Vars.world.getQuadBounds(new Rect()));
}
public static class PhysicRef{
public Physicsc entity;
public Body body;
public Vec2 lastPosition = new Vec2(), delta = new Vec2(), velocity = new Vec2(), lastVelocity = new Vec2(), position = new Vec2();
public PhysicsBody body;
public float x, y;
public PhysicRef(Physicsc entity, Body body){
public PhysicRef(Physicsc entity, PhysicsBody body){
this.entity = entity;
this.body = body;
}
}
//world for simulating physics in a different thread
public static class PhysicsWorld{
//how much to soften movement by
private static final float scl = 1.25f;
private final QuadTree<PhysicsBody>[] trees = new QuadTree[layers];
private final Seq<PhysicsBody> bodies = new Seq<>(false, 16, PhysicsBody.class);
private final Seq<PhysicsBody> seq = new Seq<>(PhysicsBody.class);
private final Rect rect = new Rect();
private final Vec2 vec = new Vec2();
public PhysicsWorld(Rect bounds){
for(int i = 0; i < layers; i++){
trees[i] = new QuadTree<>(new Rect(bounds));
}
}
public void add(PhysicsBody body){
bodies.add(body);
}
public void remove(PhysicsBody body){
bodies.remove(body);
}
public void update(){
for(int i = 0; i < layers; i++){
trees[i].clear();
}
for(int i = 0; i < bodies.size; i++){
PhysicsBody body = bodies.items[i];
body.collided = false;
trees[body.layer].insert(body);
}
for(int i = 0; i < bodies.size; i++){
PhysicsBody body = bodies.items[i];
body.hitbox(rect);
seq.size = 0;
trees[body.layer].intersect(rect, seq);
for(int j = 0; j < seq.size; j++){
PhysicsBody other = seq.items[j];
if(other == body || other.collided) continue;
float rs = body.radius + other.radius;
float dst = Mathf.dst(body.x, body.y, other.x, other.y);
if(dst < rs){
vec.set(body.x - other.x, body.y - other.y).setLength(rs - dst);
float ms = body.mass + other.mass;
float m1 = other.mass / ms, m2 = body.mass / ms;
body.x += vec.x * m1 / scl;
body.y += vec.y * m1 / scl;
other.x -= vec.x * m2 / scl;
other.y -= vec.y * m2 / scl;
}
}
body.collided = true;
}
}
public static class PhysicsBody implements QuadTreeObject{
public float x, y, radius, mass;
public int layer = 0;
public boolean collided = false;
@Override
public void hitbox(Rect out){
out.setCentered(x, y, radius * 2, radius * 2);
}
}
}
}

View File

@@ -208,6 +208,7 @@ public class Blocks implements ContentList{
liquidDrop = Liquids.slag;
isLiquid = true;
cacheLayer = CacheLayer.slag;
attributes.set(Attribute.heat, 0.85f);
}};
stone = new Floor("stone");
@@ -265,7 +266,7 @@ public class Blocks implements ContentList{
variants = 3;
status = StatusEffects.muddy;
statusDuration = 30f;
attributes.set(Attribute.water, 2f);
attributes.set(Attribute.water, 1f);
cacheLayer = CacheLayer.mud;
albedo = 0.35f;
}};
@@ -865,7 +866,8 @@ public class Blocks implements ContentList{
size = 2;
reload = 250f;
range = 85f;
healPercent = 14f;
healPercent = 11f;
phaseBoost = 15f;
health = 80 * size * size;
consumes.item(Items.phasefabric).boost();
}};
@@ -1142,13 +1144,14 @@ public class Blocks implements ContentList{
powerProduction = 1.8f;
generateEffect = Fx.redgeneratespark;
size = 2;
floating = true;
}};
steamGenerator = new BurnerGenerator("steam-generator"){{
requirements(Category.power, with(Items.copper, 35, Items.graphite, 25, Items.lead, 40, Items.silicon, 30));
powerProduction = 5.5f;
itemDuration = 90f;
consumes.liquid(Liquids.water, 0.09f);
consumes.liquid(Liquids.water, 0.1f);
hasLiquids = true;
size = 2;
}};
@@ -1268,7 +1271,7 @@ public class Blocks implements ContentList{
rotateSpeed = 1.4f;
attribute = Attribute.water;
consumes.power(1.25f);
consumes.power(1.5f);
}};
cultivator = new Cultivator("cultivator"){{
@@ -1751,6 +1754,7 @@ public class Blocks implements ContentList{
};
size = 3;
consumes.power(1.2f);
floating = true;
}};
additiveReconstructor = new Reconstructor("additive-reconstructor"){{

View File

@@ -378,7 +378,11 @@ public class TechTree implements ContentList{
node(nova, () -> {
node(pulsar, () -> {
node(quasar, () -> {
node(vela, () -> {
node(corvus, () -> {
});
});
});
});
});

View File

@@ -69,7 +69,7 @@ public class UnitTypes implements ContentList{
dagger = new UnitType("dagger"){{
speed = 0.5f;
hitsize = 8f;
hitSize = 8f;
health = 140;
weapons.add(new Weapon("large-weapon"){{
reload = 14f;
@@ -83,7 +83,7 @@ public class UnitTypes implements ContentList{
mace = new UnitType("mace"){{
speed = 0.4f;
hitsize = 9f;
hitSize = 9f;
health = 500;
armor = 4f;
@@ -114,7 +114,7 @@ public class UnitTypes implements ContentList{
fortress = new UnitType("fortress"){{
speed = 0.38f;
hitsize = 13f;
hitSize = 13f;
rotateSpeed = 3f;
targetAir = false;
health = 790;
@@ -147,7 +147,7 @@ public class UnitTypes implements ContentList{
scepter = new UnitType("scepter"){{
speed = 0.35f;
hitsize = 20f;
hitSize = 20f;
rotateSpeed = 2.1f;
health = 9000;
armor = 11f;
@@ -156,6 +156,7 @@ public class UnitTypes implements ContentList{
mechStepParticles = true;
mechStepShake = 0.15f;
singleTarget = true;
weapons.add(
new Weapon("scepter-weapon"){{
@@ -207,7 +208,7 @@ public class UnitTypes implements ContentList{
reign = new UnitType("reign"){{
speed = 0.35f;
hitsize = 26f;
hitSize = 26f;
rotateSpeed = 1.65f;
health = 24000;
armor = 14f;
@@ -269,13 +270,14 @@ public class UnitTypes implements ContentList{
canBoost = true;
boostMultiplier = 1.5f;
speed = 0.55f;
hitsize = 8f;
hitSize = 8f;
health = 110f;
buildSpeed = 0.8f;
armor = 1f;
commandLimit = 8;
abilities.add(new HealFieldAbility(10f, 60f * 4, 60f));
ammoType = AmmoTypes.power;
weapons.add(new Weapon("heal-weapon"){{
top = false;
@@ -295,16 +297,17 @@ public class UnitTypes implements ContentList{
canBoost = true;
boostMultiplier = 1.5f;
speed = 0.65f;
hitsize = 10f;
hitSize = 10f;
health = 320f;
buildSpeed = 0.9f;
armor = 4f;
mineTier = 2;
mineSpeed = 5f;
commandLimit = 15;
commandLimit = 8;
abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 5, 60f));
ammoType = AmmoTypes.power;
weapons.add(new Weapon("heal-shotgun-weapon"){{
top = false;
@@ -335,7 +338,7 @@ public class UnitTypes implements ContentList{
quasar = new UnitType("quasar"){{
mineTier = 1;
hitsize = 12f;
hitSize = 12f;
boostMultiplier = 2f;
itemCapacity = 80;
health = 650f;
@@ -344,11 +347,12 @@ public class UnitTypes implements ContentList{
armor = 9f;
landShake = 2f;
commandLimit = 18;
commandLimit = 10;
mechFrontSway = 0.55f;
ammoType = AmmoTypes.power;
speed = 0.4f;
hitsize = 10f;
hitSize = 10f;
mineTier = 2;
mineSpeed = 7f;
@@ -377,7 +381,7 @@ public class UnitTypes implements ContentList{
}};
vela = new UnitType("vela"){{
hitsize = 23f;
hitSize = 23f;
rotateSpeed = 1.6f;
canDrown = false;
@@ -385,6 +389,7 @@ public class UnitTypes implements ContentList{
mechStepParticles = true;
mechStepShake = 0.15f;
ammoType = AmmoTypes.powerHigh;
speed = 0.35f;
boostMultiplier = 2.1f;
@@ -392,12 +397,12 @@ public class UnitTypes implements ContentList{
engineSize = 6f;
lowAltitude = true;
health = 6000f;
health = 6500f;
armor = 7f;
canBoost = true;
landShake = 4f;
commandLimit = 20;
commandLimit = 8;
weapons.add(new Weapon("vela-weapon"){{
mirror = false;
@@ -414,7 +419,7 @@ public class UnitTypes implements ContentList{
continuous = true;
cooldownTime = 200f;
bullet = new ContinuousLaserBulletType(16){{
bullet = new ContinuousLaserBulletType(17){{
length = 150f;
hitEffect = Fx.hitMeltHeal;
drawSize = 420f;
@@ -439,7 +444,7 @@ public class UnitTypes implements ContentList{
corvus = new UnitType("corvus"){{
mineTier = 1;
hitsize = 29f;
hitSize = 29f;
itemCapacity = 80;
health = 19000f;
buildSpeed = 1.7f;
@@ -447,7 +452,7 @@ public class UnitTypes implements ContentList{
landShake = 1.5f;
rotateSpeed = 1.5f;
commandLimit = 20;
commandLimit = 8;
legCount = 4;
legLength = 14f;
@@ -457,6 +462,8 @@ public class UnitTypes implements ContentList{
hovering = true;
visualElevation = 0.2f;
allowLegStep = true;
ammoType = AmmoTypes.powerHigh;
groundLayer = Layer.legUnit;
speed = 0.3f;
@@ -513,7 +520,7 @@ public class UnitTypes implements ContentList{
defaultController = SuicideAI::new;
speed = 0.85f;
hitsize = 8f;
hitSize = 8f;
health = 180;
mechSideSway = 0.25f;
range = 40f;
@@ -541,7 +548,7 @@ public class UnitTypes implements ContentList{
itemCapacity = 80;
speed = 0.5f;
drag = 0.4f;
hitsize = 10f;
hitSize = 10f;
rotateSpeed = 3f;
targetAir = false;
health = 600;
@@ -581,7 +588,7 @@ public class UnitTypes implements ContentList{
spiroct = new UnitType("spiroct"){{
speed = 0.4f;
drag = 0.4f;
hitsize = 12f;
hitSize = 12f;
rotateSpeed = 3f;
health = 760;
immunities = ObjectSet.with(StatusEffects.burning, StatusEffects.melting);
@@ -592,6 +599,7 @@ public class UnitTypes implements ContentList{
legBaseOffset = 2f;
hovering = true;
armor = 5f;
ammoType = AmmoTypes.power;
buildSpeed = 0.75f;
@@ -646,7 +654,7 @@ public class UnitTypes implements ContentList{
arkyid = new UnitType("arkyid"){{
drag = 0.1f;
speed = 0.5f;
hitsize = 21f;
hitSize = 21f;
health = 8000;
armor = 6f;
@@ -663,6 +671,7 @@ public class UnitTypes implements ContentList{
legLengthScl = 0.96f;
rippleScale = 2f;
legSpeed = 0.2f;
ammoType = AmmoTypes.power;
legSplashDamage = 32;
legSplashRange = 30;
@@ -744,7 +753,7 @@ public class UnitTypes implements ContentList{
toxopid = new UnitType("toxopid"){{
drag = 0.1f;
speed = 0.5f;
hitsize = 21f;
hitSize = 21f;
health = 22000;
armor = 13f;
@@ -761,6 +770,7 @@ public class UnitTypes implements ContentList{
legLengthScl = 0.93f;
rippleScale = 3f;
legSpeed = 0.19f;
ammoType = AmmoTypes.powerHigh;
legSplashDamage = 80;
legSplashRange = 60;
@@ -875,7 +885,7 @@ public class UnitTypes implements ContentList{
weapons.add(new Weapon(){{
y = 0f;
x = 2f;
reload = 15f;
reload = 13f;
ejectEffect = Fx.shellEjectSmall;
bullet = Bullets.standardCopper;
shootSound = Sounds.shoot;
@@ -888,7 +898,7 @@ public class UnitTypes implements ContentList{
accel = 0.08f;
drag = 0.016f;
flying = true;
hitsize = 9f;
hitSize = 9f;
targetAir = false;
engineOffset = 7.8f;
range = 140f;
@@ -925,7 +935,7 @@ public class UnitTypes implements ContentList{
drag = 0.016f;
flying = true;
range = 140f;
hitsize = 18f;
hitSize = 20f;
lowAltitude = true;
armor = 5f;
@@ -963,9 +973,9 @@ public class UnitTypes implements ContentList{
}};
antumbra = new UnitType("antumbra"){{
speed = 1.13f;
accel = 0.035f;
drag = 0.05f;
speed = 0.8f;
accel = 0.04f;
drag = 0.04f;
rotateSpeed = 1.9f;
flying = true;
lowAltitude = true;
@@ -973,7 +983,7 @@ public class UnitTypes implements ContentList{
armor = 9f;
engineOffset = 21;
engineSize = 5.3f;
hitsize = 56f;
hitSize = 56f;
BulletType missiles = new MissileBulletType(2.7f, 10){{
width = 8f;
@@ -1036,16 +1046,16 @@ public class UnitTypes implements ContentList{
}};
eclipse = new UnitType("eclipse"){{
speed = 1.09f;
accel = 0.02f;
drag = 0.05f;
speed = 0.52f;
accel = 0.04f;
drag = 0.04f;
rotateSpeed = 1f;
flying = true;
lowAltitude = true;
health = 20000;
engineOffset = 38;
engineSize = 7.3f;
hitsize = 58f;
hitSize = 58f;
destructibleWreck = false;
armor = 13f;
@@ -1128,6 +1138,8 @@ public class UnitTypes implements ContentList{
range = 50f;
isCounted = false;
ammoType = AmmoTypes.powerLow;
mineTier = 1;
mineSpeed = 2.5f;
}};
@@ -1145,10 +1157,12 @@ public class UnitTypes implements ContentList{
health = 400;
buildSpeed = 0.5f;
engineOffset = 6.5f;
hitsize = 8f;
hitSize = 8f;
lowAltitude = true;
isCounted = false;
ammoType = AmmoTypes.power;
mineTier = 2;
mineSpeed = 3.5f;
@@ -1197,11 +1211,13 @@ public class UnitTypes implements ContentList{
flying = true;
engineOffset = 10.5f;
rotateShooting = false;
hitsize = 15f;
hitSize = 15f;
engineSize = 3f;
payloadCapacity = (2 * 2) * tilePayload;
buildSpeed = 2.5f;
ammoType = AmmoTypes.power;
weapons.add(
new Weapon("heal-weapon-mount"){{
reload = 25f;
@@ -1231,12 +1247,14 @@ public class UnitTypes implements ContentList{
engineOffset = 12f;
engineSize = 6f;
rotateShooting = false;
hitsize = 32f;
hitSize = 32f;
payloadCapacity = (3 * 3) * tilePayload;
buildSpeed = 2.5f;
range = 140f;
targetAir = false;
ammoType = AmmoTypes.powerHigh;
weapons.add(
new Weapon(){{
x = y = 0f;
@@ -1291,11 +1309,11 @@ public class UnitTypes implements ContentList{
engineOffset = 46f;
engineSize = 7.8f;
rotateShooting = false;
hitsize = 60f;
hitSize = 60f;
payloadCapacity = (5.3f * 5.3f) * tilePayload;
buildSpeed = 4f;
drawShields = false;
commandLimit = 25;
commandLimit = 6;
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new HealFieldAbility(130f, 60f * 2, 140f));
}};
@@ -1306,7 +1324,7 @@ public class UnitTypes implements ContentList{
risso = new UnitType("risso"){{
speed = 1.1f;
drag = 0.13f;
hitsize = 9f;
hitSize = 9f;
health = 280;
accel = 0.4f;
rotateSpeed = 3.3f;
@@ -1357,7 +1375,7 @@ public class UnitTypes implements ContentList{
health = 600;
speed = 0.9f;
drag = 0.15f;
hitsize = 11f;
hitSize = 11f;
armor = 4f;
accel = 0.3f;
rotateSpeed = 2.6f;
@@ -1400,7 +1418,7 @@ public class UnitTypes implements ContentList{
accel = 0.2f;
rotateSpeed = 1.8f;
drag = 0.17f;
hitsize = 16f;
hitSize = 16f;
armor = 7f;
rotateShooting = false;
@@ -1493,7 +1511,7 @@ public class UnitTypes implements ContentList{
speed = 0.73f;
drag = 0.17f;
hitsize = 39f;
hitSize = 39f;
accel = 0.2f;
rotateSpeed = 1.3f;
rotateShooting = false;
@@ -1576,7 +1594,7 @@ public class UnitTypes implements ContentList{
health = 22000;
speed = 0.62f;
drag = 0.18f;
hitsize = 50f;
hitSize = 50f;
armor = 16f;
accel = 0.19f;
rotateSpeed = 0.9f;
@@ -1639,7 +1657,7 @@ public class UnitTypes implements ContentList{
itemCapacity = 30;
health = 120f;
engineOffset = 6f;
hitsize = 8f;
hitSize = 8f;
weapons.add(new Weapon("small-basic-weapon"){{
reload = 17f;
@@ -1673,7 +1691,7 @@ public class UnitTypes implements ContentList{
itemCapacity = 50;
health = 150f;
engineOffset = 6f;
hitsize = 9f;
hitSize = 9f;
rotateShooting = false;
lowAltitude = true;
@@ -1713,7 +1731,7 @@ public class UnitTypes implements ContentList{
itemCapacity = 70;
health = 190f;
engineOffset = 6f;
hitsize = 10f;
hitSize = 10f;
weapons.add(new Weapon("small-mount-weapon"){{
top = false;
@@ -1743,7 +1761,7 @@ public class UnitTypes implements ContentList{
block = new UnitType("block"){
{
speed = 0f;
hitsize = 0f;
hitSize = 0f;
health = 1;
rotateSpeed = 360f;
itemCapacity = 0;

View File

@@ -170,12 +170,13 @@ public class Weathers implements ContentList{
sandstorm = new Weather("sandstorm"){
TextureRegion region;
float size = 140f, padding = size, invDensity = 1500f, baseSpeed = 6.1f;
float force = 0.45f;
float force = 0.4f * 0;
Color color = Color.valueOf("f7cba4");
Texture noise;
{
attrs.set(Attribute.light, -0.1f);
opacityMultiplier = 0.8f;
}
@Override
@@ -250,7 +251,7 @@ public class Weathers implements ContentList{
sporestorm = new Weather("sporestorm"){
TextureRegion region;
float size = 5f, padding = size, invDensity = 2000f, baseSpeed = 4.3f, force = 0.28f;
float size = 5f, padding = size, invDensity = 2000f, baseSpeed = 4.3f, force = 0.28f * 0;
Color color = Color.valueOf("7457ce");
Texture noise;
@@ -259,6 +260,7 @@ public class Weathers implements ContentList{
attrs.set(Attribute.light, -0.15f);
status = StatusEffects.sporeSlowed;
statusGround = false;
opacityMultiplier = 0.85f;
}
@Override

View File

@@ -33,6 +33,7 @@ public class ContentLoader{
new StatusEffects(),
new Liquids(),
new Bullets(),
new AmmoTypes(),
new UnitTypes(),
new Blocks(),
new Loadouts(),

View File

@@ -17,7 +17,7 @@ public class GameState{
/** Wave countdown in ticks. */
public float wavetime;
/** Whether the game is in game over state. */
public boolean gameOver = false, launched = false, serverPaused = false, wasTimeout;
public boolean gameOver = false, serverPaused = false, wasTimeout;
/** Map that is currently being played on. */
public @NonNull Map map = emptyMap;
/** The current game rules. */
@@ -52,7 +52,7 @@ public class GameState{
/** @return whether the player is in a campaign and they are out of sector time */
public boolean isOutOfTime(){
return isCampaign() && isGame() && getSector().getTimeSpent() >= turnDuration;
return isCampaign() && isGame() && getSector().getTimeSpent() >= turnDuration && !net.active();
}
public boolean hasSector(){

View File

@@ -4,7 +4,6 @@ import arc.*;
import arc.math.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.core.GameState.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
@@ -178,7 +177,7 @@ public class Logic implements ApplicationListener{
public void runWave(){
spawner.spawnEnemies();
state.wave++;
state.wavetime = state.hasSector() && state.getSector().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing;
state.wavetime = state.rules.waveSpacing;
Events.fire(new WaveEvent());
}
@@ -251,58 +250,6 @@ public class Logic implements ApplicationListener{
}
}
@Remote(called = Loc.both)
public static void launchZone(){
if(!state.isCampaign()) return;
if(!headless){
ui.hudfrag.showLaunch();
}
//TODO better core launch effect
for(Building tile : state.teams.playerCores()){
Fx.launch.at(tile);
}
Sector sector = state.rules.sector;
//TODO containers must be launched too
Time.runTask(30f, () -> {
Sector origin = sector.save.meta.secinfo.origin;
if(origin != null){
ItemSeq stacks = origin.getExtraItems();
//add up all items into list
for(Building entity : state.teams.playerCores()){
entity.items.each(stacks::add);
}
//save received items
origin.setExtraItems(stacks);
}
//remove all the cores
state.teams.playerCores().each(b -> b.tile.remove());
state.launched = true;
state.gameOver = true;
//save over the data w/o the cores
sector.save.save();
//run a turn, since launching takes up a turn
universe.runTurn();
//TODO apply extra damage to sector
//sector.setTurnsPassed(sector.getTurnsPassed() + 3);
//TODO load the sector that was launched from
Events.fire(new LaunchEvent());
//manually fire game over event now
Events.fire(new GameOverEvent(state.rules.defaultTeam));
});
}
@Remote(called = Loc.both)
public static void updateGameOver(Team winner){
state.gameOver = true;

View File

@@ -131,7 +131,7 @@ public class NetServer implements ApplicationListener{
return;
}
if(Time.millis() < info.lastKicked){
if(Time.millis() < admins.getKickTime(uuid, con.address)){
con.kick(KickReason.recentKick);
return;
}

View File

@@ -14,10 +14,18 @@ import mindustry.type.*;
import mindustry.ui.dialogs.*;
import rhino.*;
import java.net.*;
import static mindustry.Vars.*;
public interface Platform{
/** Dynamically loads a jar file. */
default Class<?> loadJar(Fi jar, String mainClass) throws Exception{
URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
return classLoader.loadClass(mainClass);
}
/** Steam: Update lobby visibility.*/
default void updateLobby(){}
@@ -121,7 +129,7 @@ public interface Platform{
}
/**
* Show a file chooser for multiple file types. Only supported on desktop.
* Show a file chooser for multiple file types.
* @param cons Selection listener
* @param extensions File extensions to filter
*/

View File

@@ -15,7 +15,8 @@ public enum ContentType{
loadout_UNUSED,
typeid_UNUSED,
error,
planet;
planet,
ammo;
public static final ContentType[] all = values();
}

View File

@@ -257,6 +257,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
player.clearUnit();
Groups.unit.clear();
Groups.build.clear();
logic.play();
});
}

View File

@@ -24,17 +24,17 @@ public class Lightning{
/** Create a lighting branch at a location. Use Team.derelict to damage everyone. */
public static void create(Team team, Color color, float damage, float x, float y, float targetAngle, int length){
createLightingInternal(null, lastSeed++, team, color, damage, x, y, targetAngle, length);
createLightningInternal(null, lastSeed++, team, color, damage, x, y, targetAngle, length);
}
/** Create a lighting branch at a location. Uses bullet parameters. */
public static void create(Bullet bullet, Color color, float damage, float x, float y, float targetAngle, int length){
createLightingInternal(bullet, lastSeed++, bullet.team, color, damage, x, y, targetAngle, length);
createLightningInternal(bullet, lastSeed++, bullet.team, color, damage, x, y, targetAngle, length);
}
//TODO remote method
//@Remote(called = Loc.server, unreliable = true)
private static void createLightingInternal(Bullet hitter, int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
private static void createLightningInternal(Bullet hitter, int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
random.setSeed(seed);
hit.clear();

View File

@@ -77,6 +77,7 @@ public abstract class BulletType extends Content{
//additional effects
public float fragCone = 360f;
public float fragAngle = 0f;
public int fragBullets = 9;
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f, fragLifeMin = 1f, fragLifeMax = 1f;
public BulletType fragBullet = null;
@@ -101,6 +102,8 @@ public abstract class BulletType extends Content{
public int lightningLength = 5, lightningLengthRand = 0;
/** Use a negative value to use default bullet damage. */
public float lightningDamage = -1;
public float lightningCone = 360f;
public float lightningAngle = 0f;
public float weaveScale = 1f;
public float weaveMag = -1f;
@@ -156,7 +159,7 @@ public abstract class BulletType extends Content{
if(fragBullet != null){
for(int i = 0; i < fragBullets; i++){
float len = Mathf.random(1f, 7f);
float a = b.rotation() + Mathf.range(fragCone/2);
float a = b.rotation() + Mathf.range(fragCone/2) + fragAngle;
fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax), Mathf.random(fragLifeMin, fragLifeMax));
}
}
@@ -181,7 +184,7 @@ public abstract class BulletType extends Content{
}
for(int i = 0; i < lightning; i++){
Lightning.create(b, lightningColor, lightningDamage < 0 ? damage : lightningDamage, b.x, b.y, Mathf.random(360f), lightningLength + Mathf.random(lightningLengthRand));
Lightning.create(b, lightningColor, lightningDamage < 0 ? damage : lightningDamage, b.x, b.y, b.rotation() + Mathf.range(lightningCone/2) + lightningAngle, lightningLength + Mathf.random(lightningLengthRand));
}
}

View File

@@ -21,6 +21,7 @@ public class HealBulletType extends BulletType{
despawnEffect = Fx.hitLaser;
collidesTeam = true;
hittable = false;
reflectable = false;
}
public HealBulletType(){

View File

@@ -71,7 +71,7 @@ abstract class BuilderComp implements Unitc{
Tile tile = world.tile(current.x, current.y);
if(within(tile, finalPlaceDst)){
rotation = Mathf.slerpDelta(rotation, angleTo(tile), 0.4f);
lookAt(angleTo(tile));
}
if(!(tile.block() instanceof ConstructBlock)){

View File

@@ -73,7 +73,7 @@ abstract class CommanderComp implements Unitc{
void command(Formation formation, Seq<Unit> units){
clearCommand();
float spacing = hitSize() * 1f;
float spacing = hitSize() * 0.65f;
minFormationSpeed = type().speed;
controlling.addAll(units);

View File

@@ -1,10 +1,12 @@
package mindustry.entities.comp;
import arc.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
import mindustry.world.blocks.environment.*;
@@ -90,7 +92,7 @@ abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
//TODO is the netClient check necessary?
if(drownTime >= 0.999f && !net.client()){
kill();
//TODO drown event!
Events.fire(new UnitDrownEvent(self()));
}
}else{
drownTime = Mathf.lerpDelta(drownTime, 0f, 0.03f);

View File

@@ -1,13 +1,23 @@
package mindustry.entities.comp;
import arc.graphics.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
import static mindustry.Vars.*;
@Component
abstract class MechComp implements Posc, Flyingc, Hitboxc, Unitc, Mechc, ElevationMovec{
@Import float x, y, hitSize;
@Import UnitType type;
@SyncField(false) @SyncLocal float baseRotation;
transient float walkTime, walkExtension;
transient private boolean walked;
@@ -21,6 +31,49 @@ abstract class MechComp implements Posc, Flyingc, Hitboxc, Unitc, Mechc, Elevati
walkTime += len;
walked = false;
}
//update mech effects
float extend = walkExtend(false);
float base = walkExtend(true);
float extendScl = base % 1f;
float lastExtend = walkExtension;
if(extendScl < lastExtend && base % 2f > 1f){
int side = -Mathf.sign(extend);
float width = hitSize / 2f * side, length = type.mechStride * 1.35f;
float cx = x + Angles.trnsx(baseRotation, length, width),
cy = y + Angles.trnsy(baseRotation, length, width);
if(type.mechStepShake > 0){
Effect.shake(type.mechStepShake, type.mechStepShake, cx, cy);
}
if(type.mechStepParticles){
Tile tile = world.tileWorld(cx, cy);
if(tile != null){
Color color = tile.floor().mapColor;
Fx.unitLand.at(cx, cy, hitSize/8f, color);
}
}
}
walkExtension = extendScl;
}
public float walkExtend(boolean scaled){
//now ranges from -maxExtension to maxExtension*3
float raw = walkTime % (type.mechStride * 4);
if(scaled) return raw / type.mechStride;
if(raw > type.mechStride*3) raw = raw - type.mechStride * 4;
else if(raw > type.mechStride*2) raw = type.mechStride * 2 - raw;
else if(raw > type.mechStride) raw = type.mechStride * 2 - raw;
return raw;
}
@Override

View File

@@ -56,7 +56,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
mineTimer = 0f;
}else if(mining()){
Item item = mineTile.drop();
rotation = Mathf.slerpDelta(rotation, angleTo(mineTile.worldx(), mineTile.worldy()), 0.4f);
lookAt(angleTo(mineTile.worldx(), mineTile.worldy()));
mineTimer += Time.delta *type.mineSpeed;
if(Mathf.chance(0.06 * Time.delta)){

View File

@@ -92,7 +92,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
@Replace
public float clipSize(){
return unit.isNull() ? 20 : unit.type().hitsize * 2f;
return unit.isNull() ? 20 : unit.type().hitSize * 2f;
}
@Override

View File

@@ -30,9 +30,8 @@ import static mindustry.Vars.*;
@Component(base = true)
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Displayable, Senseable{
@Import boolean hovering;
@Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health;
@Import boolean dead;
@Import boolean hovering, dead;
@Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health, ammo;
@Import Team team;
@Import int id;
@@ -41,6 +40,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
boolean spawnedByCore;
transient Seq<Ability> abilities = new Seq<>(0);
private transient float resupplyTime = Mathf.random(10f);
public void moveAt(Vec2 vector){
moveAt(vector, type.accel);
@@ -191,7 +191,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
this.maxHealth = type.health;
this.drag = type.drag;
this.armor = type.armor;
this.hitSize = type.hitsize;
this.hitSize = type.hitSize;
this.hovering = type.hovering;
if(controller == null) controller(type.createController());
@@ -245,6 +245,16 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
type.update(self());
if(state.rules.unitAmmo && ammo < type.ammoCapacity - 0.0001f){
resupplyTime += Time.delta;
//resupply only at a fixed interval to prevent lag
if(resupplyTime > 10f){
type.ammoType.resupply(self());
resupplyTime = 0f;
}
}
if(abilities.size > 0){
for(Ability a : abilities){
a.update(self());
@@ -366,7 +376,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
if(!headless){
for(int i = 0; i < type.wreckRegions.length; i++){
if(type.wreckRegions[i].found()){
float range = type.hitsize/4f;
float range = type.hitSize /4f;
Tmp.v1.rnd(range);
Effect.decal(type.wreckRegions[i], x + Tmp.v1.x, y + Tmp.v1.y, rotation - 90);
}

View File

@@ -18,8 +18,6 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
@Import Vec2 vel;
@Import UnitType type;
/** minimum cursor distance from unit, fixes 'cross-eyed' shooting */
static final float minAimDst = 18f;
/** temporary weapon sequence number */
static int sequenceNum = 0;
@@ -67,7 +65,7 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
/** Aim at something. This will make all mounts point at it. */
void aim(float x, float y){
Tmp.v1.set(x, y).sub(this.x, this.y);
if(Tmp.v1.len() < minAimDst) Tmp.v1.setLength(minAimDst);
if(Tmp.v1.len() < type.aimDst) Tmp.v1.setLength(type.aimDst);
x = Tmp.v1.x + this.x;
y = Tmp.v1.y + this.y;

View File

@@ -1,47 +1,51 @@
package mindustry.game;
import arc.struct.Seq;
import arc.math.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.type.ItemStack;
import mindustry.type.*;
import static mindustry.content.UnitTypes.*;
public class DefaultWaves{
private Seq<SpawnGroup> spawns;
public Seq<SpawnGroup> get(){
if(spawns == null && UnitTypes.dagger != null){
if(spawns == null && dagger != null){
spawns = Seq.with(
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
end = 10;
unitScaling = 2f;
}},
new SpawnGroup(UnitTypes.crawler){{
new SpawnGroup(crawler){{
begin = 4;
end = 13;
unitAmount = 2;
unitScaling = 1.5f;
}},
new SpawnGroup(UnitTypes.flare){{
new SpawnGroup(flare){{
begin = 12;
end = 16;
unitScaling = 1f;
}},
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
begin = 11;
unitScaling = 1.7f;
spacing = 2;
max = 4;
}},
new SpawnGroup(UnitTypes.pulsar){{
new SpawnGroup(pulsar){{
begin = 13;
spacing = 3;
unitScaling = 0.5f;
}},
new SpawnGroup(UnitTypes.mace){{
new SpawnGroup(mace){{
begin = 7;
spacing = 3;
unitScaling = 2;
@@ -49,28 +53,28 @@ public class DefaultWaves{
end = 30;
}},
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
begin = 8;
unitScaling = 1;
unitAmount = 4;
spacing = 2;
}},
new SpawnGroup(UnitTypes.mace){{
new SpawnGroup(mace){{
begin = 28;
spacing = 3;
unitScaling = 1;
end = 40;
}},
new SpawnGroup(UnitTypes.mace){{
new SpawnGroup(mace){{
begin = 45;
spacing = 3;
unitScaling = 2;
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.mace){{
new SpawnGroup(mace){{
begin = 120;
spacing = 2;
unitScaling = 3;
@@ -78,13 +82,13 @@ public class DefaultWaves{
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.flare){{
new SpawnGroup(flare){{
begin = 16;
unitScaling = 1;
spacing = 2;
}},
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
begin = 82;
spacing = 3;
unitAmount = 4;
@@ -92,7 +96,7 @@ public class DefaultWaves{
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
begin = 41;
spacing = 5;
unitAmount = 1;
@@ -100,7 +104,7 @@ public class DefaultWaves{
effect = StatusEffects.shielded;
}},
new SpawnGroup(UnitTypes.fortress){{
new SpawnGroup(fortress){{
begin = 40;
spacing = 5;
unitAmount = 2;
@@ -108,7 +112,7 @@ public class DefaultWaves{
max = 20;
}},
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
begin = 35;
spacing = 3;
unitAmount = 4;
@@ -117,7 +121,7 @@ public class DefaultWaves{
end = 60;
}},
new SpawnGroup(UnitTypes.dagger){{
new SpawnGroup(dagger){{
begin = 42;
spacing = 3;
unitAmount = 4;
@@ -126,14 +130,14 @@ public class DefaultWaves{
end = 130;
}},
new SpawnGroup(UnitTypes.horizon){{
new SpawnGroup(horizon){{
begin = 40;
unitAmount = 2;
spacing = 2;
unitScaling = 2;
}},
new SpawnGroup(UnitTypes.flare){{
new SpawnGroup(flare){{
begin = 50;
unitAmount = 4;
unitScaling = 3;
@@ -141,7 +145,7 @@ public class DefaultWaves{
effect = StatusEffects.overdrive;
}},
new SpawnGroup(UnitTypes.zenith){{
new SpawnGroup(zenith){{
begin = 50;
unitAmount = 2;
unitScaling = 3;
@@ -149,42 +153,42 @@ public class DefaultWaves{
max = 16;
}},
new SpawnGroup(UnitTypes.horizon){{
new SpawnGroup(horizon){{
begin = 53;
unitAmount = 2;
unitScaling = 3;
spacing = 4;
}},
new SpawnGroup(UnitTypes.atrax){{
new SpawnGroup(atrax){{
begin = 31;
unitAmount = 4;
unitScaling = 1;
spacing = 3;
}},
new SpawnGroup(UnitTypes.scepter){{
new SpawnGroup(scepter){{
begin = 41;
unitAmount = 1;
unitScaling = 1;
spacing = 30;
}},
new SpawnGroup(UnitTypes.reign){{
new SpawnGroup(reign){{
begin = 81;
unitAmount = 1;
unitScaling = 1;
spacing = 40;
}},
new SpawnGroup(UnitTypes.antumbra){{
new SpawnGroup(antumbra){{
begin = 131;
unitAmount = 1;
unitScaling = 1;
spacing = 40;
}},
new SpawnGroup(UnitTypes.horizon){{
new SpawnGroup(horizon){{
begin = 90;
unitAmount = 2;
unitScaling = 3;
@@ -194,4 +198,76 @@ public class DefaultWaves{
}
return spawns == null ? new Seq<>() : spawns;
}
//TODO move elsewhere
public static Seq<SpawnGroup> generate(){
UnitType[][] species = {
{dagger, mace, fortress, scepter, reign},
{nova, pulsar, quasar, vela, corvus},
{crawler, atrax, spiroct, arkyid, toxopid},
//{risso, minke, bryde, sei, omura}, //questionable choices
//{mono, poly, mega, quad, oct}, //do not attack
{flare, horizon, zenith, antumbra, eclipse}
};
//required progression:
//- extra periodic patterns
Seq<SpawnGroup> out = new Seq<>();
//max reasonable wave, after which everything gets boring
int cap = 400;
//main sequence
float shieldStart = 30, shieldsPerWave = 12;
UnitType[] curSpecies = Structs.random(species);
int curTier = 0;
for(int i = 0; i < cap;){
int f = i;
int next = Mathf.random(15, 25);
float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0);
//main progression
out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{
unitAmount = f == 0 ? 1 : 10;
begin = f;
end = f + next >= cap ? never : f + next;
max = 16;
unitScaling = Mathf.random(1f, 2f);
shields = shieldAmount;
shieldScaling = shieldsPerWave;
}});
//extra progression that tails out, blends in
out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{
unitAmount = 6;
begin = f + next;
end = f + next + Mathf.random(8, 12);
max = 10;
unitScaling = Mathf.random(2f);
spacing = Mathf.random(2, 3);
shields = shieldAmount;
shieldScaling = shieldsPerWave;
}});
i += next;
if(curTier < 3 || Mathf.chance(0.2)){
curTier ++;
}
//do not spawn bosses
curTier = Math.min(curTier, 3);
//small chance to switch species
if(Mathf.chance(0.2)){
curSpecies = Structs.random(species);
}
}
return out;
}
}

View File

@@ -40,7 +40,6 @@ public class EventType{
public static class WinEvent{}
public static class LoseEvent{}
public static class LaunchEvent{}
public static class ResizeEvent{}
public static class MapMakeEvent{}
public static class MapPublishEvent{}
@@ -271,6 +270,14 @@ public class EventType{
}
}
public static class UnitDrownEvent{
public final Unit unit;
public UnitDrownEvent(Unit unit){
this.unit = unit;
}
}
public static class UnitCreateEvent{
public final Unit unit;

View File

@@ -68,8 +68,6 @@ public class Rules{
public float dropZoneRadius = 300f;
/** Time between waves in ticks. */
public float waveSpacing = 60 * 60 * 2;
/** How many times longer a launch wave takes. */
public float launchWaveMultiplier = 2f;
/** Wave after which the player 'wins'. Used in sectors. Use a value <= 0 to disable. */
public int winWave = 0;
/** Base unit cap. Can still be increased by blocks. */

View File

@@ -22,6 +22,7 @@ public class Stats{
/** Friendly buildings destroyed. */
public int buildingsDestroyed;
//TODO fix
public RankResult calculateRank(Sector zone, boolean launched){
float score = 0;

View File

@@ -84,6 +84,18 @@ public class Drawf{
Draw.color();
}
public static void shadow(TextureRegion region, float x, float y, float rotation){
Draw.color(Pal.shadow);
Draw.rect(region, x, y, rotation);
Draw.color();
}
public static void shadow(TextureRegion region, float x, float y){
Draw.color(Pal.shadow);
Draw.rect(region, x, y);
Draw.color();
}
public static void dashCircle(float x, float y, float rad, Color color){
Lines.stroke(3f, Pal.gray);
Lines.dashCircle(x, y, rad);

View File

@@ -228,7 +228,7 @@ public class FloorRenderer implements Disposable{
int chunksx = Mathf.ceil((float)(world.width()) / chunksize),
chunksy = Mathf.ceil((float)(world.height()) / chunksize);
cache = new Chunk[chunksx][chunksy];
cbatch = new MultiCacheBatch(chunksize * chunksize * 7);
cbatch = new MultiCacheBatch(chunksize * chunksize * 8);
Time.mark();

View File

@@ -11,7 +11,7 @@ import arc.util.*;
public class IndexedRenderer implements Disposable{
private static final int vsize = 5;
private Shader program = new Shader(
private final Shader program = new Shader(
"attribute vec4 a_position;\n" +
"attribute vec4 a_color;\n" +
"attribute vec2 a_texCoord0;\n" +

View File

@@ -122,7 +122,7 @@ public class MinimapRenderer implements Disposable{
float dy = (Core.camera.position.y / tilesize);
dx = Mathf.clamp(dx, sz, world.width() - sz);
dy = Mathf.clamp(dy, sz, world.height() - sz);
float invTexWidth = 1f / texture.getWidth();
float invTexWidth = 1f / texture.width;
float invTexHeight = 1f / texture.height;
float x = dx - sz, y = world.height() - dy - sz, width = sz * 2, height = sz * 2;
region.set(x * invTexWidth, y * invTexHeight, (x + width) * invTexWidth, (y + height) * invTexHeight);

View File

@@ -47,6 +47,7 @@ public class Pal{
darkishGray = new Color(0.3f, 0.3f, 0.3f, 1f),
darkerGray = new Color(0.2f, 0.2f, 0.2f, 1f),
darkestGray = new Color(0.1f, 0.1f, 0.1f, 1f),
shadow = new Color(0, 0, 0, 0.22f),
ammo = Color.valueOf("ff8947"),
rubble = Color.valueOf("1c1817"),

View File

@@ -155,7 +155,7 @@ public class Shaders{
setUniformf("u_progress", progress);
setUniformf("u_uv", region.u, region.v);
setUniformf("u_uv2", region.u2, region.v2);
setUniformf("u_texsize", region.texture.getWidth(), region.texture.height);
setUniformf("u_texsize", region.texture.width, region.texture.height);
}
}
@@ -175,7 +175,7 @@ public class Shaders{
setUniformf("u_uv", region.u, region.v);
setUniformf("u_uv2", region.u2, region.v2);
setUniformf("u_time", Time.time());
setUniformf("u_texsize", region.texture.getWidth(), region.texture.height);
setUniformf("u_texsize", region.texture.width, region.texture.height);
}
}

View File

@@ -4,11 +4,10 @@ import arc.graphics.*;
import arc.graphics.VertexAttributes.*;
import arc.graphics.gl.*;
import arc.math.geom.*;
import arc.util.*;
import mindustry.graphics.g3d.PlanetGrid.*;
public class MeshBuilder{
private static final Vec3 v1 = new Vec3(), v2 = new Vec3(), v3 = new Vec3();
private static final Vec3 v1 = new Vec3(), v2 = new Vec3(), v3 = new Vec3(), v4 = new Vec3();
private static final float[] floats = new float[3 + 3 + 1];
private static Mesh mesh;
@@ -111,7 +110,7 @@ public class MeshBuilder{
}
private static Vec3 normal(Vec3 v1, Vec3 v2, Vec3 v3){
return Tmp.v32.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor();
return v4.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor();
}
private static void verts(Vec3 a, Vec3 b, Vec3 c, Vec3 normal, Color color){

View File

@@ -113,7 +113,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
Payloadc pay = (Payloadc)unit;
if(target.isAI() && target.isGrounded() && pay.canPickup(target)
&& target.within(unit, unit.type().hitsize * 2f + target.type().hitsize * 2f)){
&& target.within(unit, unit.type().hitSize * 2f + target.type().hitSize * 2f)){
Call.pickedUnitPayload(player, target);
}
}
@@ -395,7 +395,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(!(unit instanceof Payloadc)) return;
Payloadc pay = (Payloadc)unit;
Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type().hitsize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type().hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
if(target != null){
Call.requestUnitPayload(player, target);
}else{

View File

@@ -6,7 +6,8 @@ public enum ConditionOp{
lessThan("<", (a, b) -> a < b),
lessThanEq("<=", (a, b) -> a <= b),
greaterThan(">", (a, b) -> a > b),
greaterThanEq(">=", (a, b) -> a >= b);
greaterThanEq(">=", (a, b) -> a >= b),
always("always", (a, b) -> true);
public static final ConditionOp[] all = values();

View File

@@ -1,6 +1,7 @@
package mindustry.logic;
import arc.func.*;
import arc.graphics.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
@@ -633,6 +634,8 @@ public class LStatements{
@RegisterStatement("jump")
public static class JumpStatement extends LStatement{
private static Color last = new Color();
public transient StatementElem dest;
public int destIndex;
@@ -644,19 +647,30 @@ public class LStatements{
public void build(Table table){
table.add("if ").padLeft(4);
field(table, value, str -> value = str);
table.button(b -> {
b.label(() -> op.symbol);
b.clicked(() -> showSelect(b, ConditionOp.all, op, o -> op = o));
}, Styles.logict, () -> {}).size(48f, 40f).pad(4f).color(table.color);
field(table, compare, str -> compare = str);
last = table.color;
table.table(this::rebuild);
table.add().growX();
table.add(new JumpButton(() -> dest, s -> dest = s)).size(30).right().padLeft(-8);
}
void rebuild(Table table){
table.clearChildren();
table.setColor(last);
if(op != ConditionOp.always) field(table, value, str -> value = str);
table.button(b -> {
b.label(() -> op.symbol);
b.clicked(() -> showSelect(b, ConditionOp.all, op, o -> {
op = o;
rebuild(table);
}));
}, Styles.logict, () -> {}).size(op == ConditionOp.always ? 80f : 48f, 40f).pad(4f).color(table.color);
if(op != ConditionOp.always) field(table, compare, str -> compare = str);
}
//elements need separate conversion logic
@Override
public void setupUI(){

View File

@@ -6,6 +6,8 @@ import mindustry.gen.*;
public enum RadarSort{
distance((pos, other) -> -pos.dst2(other)),
health((pos, other) -> other.health()),
shield((pos, other) -> other.shield()),
armor((pos, other) -> other.armor()),
maxHealth((pos, other) -> other.maxHealth());
public final RadarSortFunc func;
@@ -17,6 +19,6 @@ public enum RadarSort{
}
public interface RadarSortFunc{
float get(Position pos, Healthc other);
float get(Position pos, Unit other);
}
}

View File

@@ -8,7 +8,9 @@ public enum RadarTarget{
enemy((team, other) -> team != other.team),
ally((team, other) -> team == other.team),
player((team, other) -> other.isPlayer()),
attacker((pos, other) -> other.canShoot()),
flying((team, other) -> other.isFlying()),
boss((team, other) -> other.isBoss()),
ground((team, other) -> other.isGrounded());
public final RadarTargetFunc func;

View File

@@ -25,7 +25,6 @@ import mindustry.type.*;
import mindustry.ui.*;
import java.io.*;
import java.net.*;
import static mindustry.Vars.*;
@@ -634,12 +633,11 @@ public class Mods implements Loadable{
//make sure the main class exists before loading it; if it doesn't just don't put it there
if(mainFile.exists()){
//mobile versions don't support class mods
if(mobile){
throw new IllegalArgumentException("Java class mods are not supported on mobile.");
if(ios){
throw new IllegalArgumentException("Java class mods are not supported on iOS.");
}
URLClassLoader classLoader = new URLClassLoader(new URL[]{sourceFile.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
Class<?> main = classLoader.loadClass(mainClass);
Class<?> main = platform.loadJar(sourceFile, mainClass);
metas.put(main, meta);
mainMod = (Mod)main.getDeclaredConstructor().newInstance();
}else{

View File

@@ -24,6 +24,7 @@ public class Administration{
public Seq<ChatFilter> chatFilters = new Seq<>();
public Seq<ActionFilter> actionFilters = new Seq<>();
public Seq<String> subnetBans = new Seq<>();
public ObjectMap<String, Long> kickedIPs = new ObjectMap<>();
/** All player info. Maps UUIDs to info. This persists throughout restarts. Do not access directly. */
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
@@ -86,6 +87,20 @@ public class Administration{
});
}
/** @return time at which a player would be pardoned for a kick (0 means they were never kicked) */
public long getKickTime(String uuid, String ip){
return Math.max(getInfo(uuid).lastKicked, kickedIPs.get(ip, 0L));
}
/** Sets up kick duration for a player. */
public void handleKicked(String uuid, String ip, long duration){
kickedIPs.put(ip, Math.max(kickedIPs.get(ip, 0L), Time.millis() + duration));
PlayerInfo info = getInfo(uuid);
info.timesKicked++;
info.lastKicked = Math.max(Time.millis() + duration, info.lastKicked);
}
public Seq<String> getSubnetBans(){
return subnetBans;
}

View File

@@ -27,13 +27,13 @@ public class CrashSender{
public static String createReport(String error){
String report = "Oh no, Mindustry crashed!\n";
if(mods.list().size == 0){
report += "Please report this at https://github.com/Anuken/Mindustry/issues/new?labels=bug&template=bug_report.md\n\n";
report += "Please report this at " + Vars.reportIssueURL + "\n\n";
}
return report + "Version: " + Version.combined() + (Vars.headless ? " (Server)" : "") + "\n"
+ "OS: " + System.getProperty("os.name") + " x" + (OS.is64Bit ? "64" : "32") + "\n"
+ "Java Version: " + System.getProperty("java.version") + "\n"
+ "Java Architecture: " + System.getProperty("sun.arch.data.model") + "\n"
+ mods.list().size + " Mods: " + mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version)
+ mods.list().size + " Mods: " + (mods.list().isEmpty() ? "none" : mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version))
+ "\n\n" + error;
}

View File

@@ -65,9 +65,7 @@ public abstract class NetConnection{
Log.info("Kicking connection @; Reason: @", address, reason.replace("\n", " "));
PlayerInfo info = netServer.admins.getInfo(uuid);
info.timesKicked++;
info.lastKicked = Math.max(Time.millis() + kickDuration, info.lastKicked);
netServer.admins.handleKicked(uuid, address, kickDuration);
Call.kick(this, reason);

View File

@@ -0,0 +1,28 @@
package mindustry.type;
import arc.graphics.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
/** Type of ammo that a unit uses. */
public class AmmoType extends Content{
public String icon = Iconc.itemCopper + "";
public Color color = Pal.ammo;
public Color barColor = Pal.ammo;
public AmmoType(char icon, Color color){
this.icon = icon + "";
this.color = color;
}
public AmmoType(){
}
public void resupply(Unit unit){}
@Override
public ContentType getContentType(){
return ContentType.ammo;
}
}

View File

@@ -0,0 +1,86 @@
package mindustry.type;
import arc.util.ArcAnnotate.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.*;
import mindustry.world.meta.*;
public class AmmoTypes implements ContentList{
public static AmmoType
powerLow,
power,
powerHigh,
copper,
thorium;
@Override
public void load(){
powerLow = new PowerAmmoType(500);
power = new PowerAmmoType(1000);
powerHigh = new PowerAmmoType(2000);
copper = new ItemAmmoType(Items.copper);
thorium = new ItemAmmoType(Items.thorium);
}
public static class PowerAmmoType extends AmmoType{
public float totalPower = 1000;
public PowerAmmoType(){
super(Iconc.power, Pal.powerLight);
barColor = color;
}
public PowerAmmoType(float totalPower){
this();
this.totalPower = totalPower;
}
@Override
public void resupply(Unit unit){
float range = unit.hitSize + 60f;
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.powerResupply);
if(closest != null && closest.build != null && unit.within(closest.build, range) && closest.build.power != null){
Building build = closest.build;
if(build.block.consumes.hasPower() && build.block.consumes.getPower().buffered){
float amount = closest.build.power.status * build.block.consumes.getPower().capacity;
float powerPerAmmo = totalPower / unit.type().ammoCapacity;
float ammoRequired = unit.type().ammoCapacity - unit.ammo;
float powerRequired = ammoRequired * powerPerAmmo;
float powerTaken = Math.min(amount, powerRequired);
if(powerTaken > 1){
closest.build.power.status -= powerTaken / build.block.consumes.getPower().capacity;
unit.ammo += powerTaken / powerPerAmmo;
Fx.itemTransfer.at(build.x, build.y, Math.max(powerTaken / 100f, 1f), Pal.power, unit);
}
}
}
}
}
public static class ItemAmmoType extends AmmoType{
public @NonNull Item item;
public ItemAmmoType(Item item){
this.item = item;
this.color = item.color;
}
public ItemAmmoType(){
}
@Override
public void load(){
if(item != null){
icon = item.emoji();
}
}
}
}

View File

@@ -31,9 +31,6 @@ public class Sector{
public float baseCoverage;
public boolean generateEnemyBase;
//TODO implement a dynamic launch period
public int launchPeriod = 10;
public Sector(Planet planet, Ptile tile){
this.planet = planet;
this.tile = tile;
@@ -100,7 +97,7 @@ public class Sector{
public boolean isBeingPlayed(){
//after the launch dialog, a sector is no longer considered being played
return Vars.state.isGame() && Vars.state.rules.sector == this && !Vars.state.launched && !Vars.state.gameOver;
return Vars.state.isGame() && Vars.state.rules.sector == this && !Vars.state.gameOver;
}
public boolean isCaptured(){
@@ -134,16 +131,6 @@ public class Sector{
return res % 2 == 0 ? res : res + 1;
}
//TODO implement
public boolean isLaunchWave(int wave){
return metCondition() && wave % launchPeriod == 0;
}
public boolean metCondition(){
//TODO implement
return false;
}
//TODO this should be stored in a more efficient structure, and be updated each turn
public ItemSeq getExtraItems(){
return Core.settings.getJson(key("extra-items"), ItemSeq.class, ItemSeq::new);

View File

@@ -56,7 +56,7 @@ public class StatusEffect extends MappableContent{
}
if(effect != Fx.none && Mathf.chanceDelta(effectChance)){
Tmp.v1.rnd(unit.type().hitsize/2f);
Tmp.v1.rnd(unit.type().hitSize /2f);
effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y);
}
}

View File

@@ -34,7 +34,7 @@ import mindustry.world.consumers.*;
import static mindustry.Vars.*;
public class UnitType extends UnlockableContent{
public static final float shadowTX = -12, shadowTY = -13, shadowColor = Color.toFloatBits(0, 0, 0, 0.22f), outlineSpace = 0.01f;
public static final float shadowTX = -12, shadowTY = -13, outlineSpace = 0.01f;
private static final Vec2 legOffset = new Vec2();
/** If true, the unit is always at elevation 1. */
@@ -51,6 +51,7 @@ public class UnitType extends UnlockableContent{
public boolean destructibleWreck = true;
public float groundLayer = Layer.groundUnit;
public float payloadCapacity = 8;
public float aimDst = -1f;
public int commandLimit = 24;
public float visualElevation = -1f;
public boolean allowLegStep = false;
@@ -72,7 +73,8 @@ public class UnitType extends UnlockableContent{
public Color mechLegColor = Pal.darkMetal;
public int itemCapacity = -1;
public int ammoCapacity = 220;
public int ammoCapacity = -1;
public AmmoType ammoType = AmmoTypes.copper;
public int mineTier = -1;
public float buildSpeed = 1f, mineSpeed = 1f;
@@ -80,7 +82,7 @@ public class UnitType extends UnlockableContent{
public boolean canDrown = true;
public float engineOffset = 5f, engineSize = 2.5f;
public float strafePenalty = 0.5f;
public float hitsize = 6f;
public float hitSize = 6f;
public float itemOffsetY = 3f;
public float lightRadius = 60f, lightOpacity = 0.6f;
public Color lightColor = Pal.powerLight;
@@ -136,41 +138,6 @@ public class UnitType extends UnlockableContent{
public void update(Unit unit){
if(unit instanceof Mechc && !unit.isFlying()){
updateMechEffects(unit);
}
}
public void updateMechEffects(Unit unit){
Mechc mech = (Mechc)unit;
float extend = walkExtend((Mechc)unit, false);
float base = walkExtend((Mechc)unit, true);
float extendScl = base % 1f;
float lastExtend = mech.walkExtension();
if(extendScl < lastExtend && base % 2f > 1f){
int side = -Mathf.sign(extend);
float width = hitsize / 2f * side, length = mechStride * 1.35f;
float cx = unit.x + Angles.trnsx(mech.baseRotation(), length, width),
cy = unit.y + Angles.trnsy(mech.baseRotation(), length, width);
if(mechStepShake > 0){
Effect.shake(mechStepShake, mechStepShake, cx, cy);
}
if(mechStepParticles){
Tile tile = world.tileWorld(cx, cy);
if(tile != null){
Color color = tile.floor().mapColor;
Fx.unitLand.at(cx, cy, hitsize/8f, color);
}
}
}
mech.walkExtension(extendScl);
}
public void landed(Unit unit){}
@@ -184,13 +151,13 @@ public class UnitType extends UnlockableContent{
table.row();
table.table(bars -> {
bars.defaults().growX().height(18f).pad(4);
bars.defaults().growX().height(20f).pad(4);
bars.add(new Bar("blocks.health", Pal.health, unit::healthf).blink(Color.white));
bars.row();
if(state.rules.unitAmmo){
bars.add(new Bar("blocks.ammo", Pal.ammo, () -> unit.ammo / ammoCapacity));
bars.add(new Bar(ammoType.icon + " " + Core.bundle.get("blocks.ammo"), ammoType.barColor, () -> unit.ammo / ammoCapacity));
bars.row();
}
}).growX();
@@ -237,24 +204,28 @@ public class UnitType extends UnlockableContent{
singleTarget = weapons.size <= 1;
if(itemCapacity < 0){
itemCapacity = Math.max(Mathf.round(hitsize * 7, 20), 20);
itemCapacity = Math.max(Mathf.round(hitSize * 7, 20), 20);
}
//set up default range
if(range < 0){
range = Float.MAX_VALUE;
for(Weapon weapon : weapons){
range = Math.min(range, weapon.bullet.range() + hitsize/2f);
range = Math.min(range, weapon.bullet.range() + hitSize /2f);
}
}
if(mechStride < 0){
mechStride = 4f + (hitsize-8f)/2.1f;
mechStride = 4f + (hitSize -8f)/2.1f;
}
if(aimDst < 0){
aimDst = weapons.contains(w -> !w.rotate) ? hitSize * 2f : hitSize / 2f;
}
if(mechStepShake < 0){
mechStepShake = Mathf.round((hitsize - 11f) / 9f);
mechStepParticles = hitsize > 15f;
mechStepShake = Mathf.round((hitSize - 11f) / 9f);
mechStepParticles = hitSize > 15f;
}
canHeal = weapons.contains(w -> w.bullet instanceof HealBulletType);
@@ -281,6 +252,15 @@ public class UnitType extends UnlockableContent{
}
}
this.weapons = mapped;
//dynamically create ammo capacity based on firing rate
if(ammoCapacity < 0){
float shotsPerSecond = weapons.sumf(w -> 60f / w.reload);
//duration of continuous fire without reload
float targetSeconds = 30;
ammoCapacity = Math.max(1, (int)(shotsPerSecond * targetSeconds));
}
}
@CallSuper
@@ -342,7 +322,7 @@ public class UnitType extends UnlockableContent{
public void draw(Unit unit){
Mechc mech = unit instanceof Mechc ? (Mechc)unit : null;
float z = unit.elevation > 0.5f ? (lowAltitude ? Layer.flyingUnitLow : Layer.flyingUnit) : groundLayer + Mathf.clamp(hitsize/4000f, 0, 0.01f);
float z = unit.elevation > 0.5f ? (lowAltitude ? Layer.flyingUnitLow : Layer.flyingUnit) : groundLayer + Mathf.clamp(hitSize / 4000f, 0, 0.01f);
if(unit.controller().isBeingControlled(player.unit())){
drawControl(unit);
@@ -359,10 +339,10 @@ public class UnitType extends UnlockableContent{
drawMech(mech);
//side
legOffset.trns(mech.baseRotation(), 0f, Mathf.lerp(Mathf.sin(walkExtend(mech, true), 2f/Mathf.PI, 1) * mechSideSway, 0f, unit.elevation));
legOffset.trns(mech.baseRotation(), 0f, Mathf.lerp(Mathf.sin(mech.walkExtend(true), 2f/Mathf.PI, 1) * mechSideSway, 0f, unit.elevation));
//front
legOffset.add(Tmp.v1.trns(mech.baseRotation() + 90, 0f, Mathf.lerp(Mathf.sin(walkExtend(mech, true), 1f/Mathf.PI, 1) * mechFrontSway, 0f, unit.elevation)));
legOffset.add(Tmp.v1.trns(mech.baseRotation() + 90, 0f, Mathf.lerp(Mathf.sin(mech.walkExtend(true), 1f/Mathf.PI, 1) * mechFrontSway, 0f, unit.elevation)));
unit.trns(legOffset.x, legOffset.y);
}
@@ -434,7 +414,7 @@ public class UnitType extends UnlockableContent{
}
public void drawShadow(Unit unit){
Draw.color(shadowColor);
Draw.color(Pal.shadow);
float e = Math.max(unit.elevation, visualElevation);
Draw.rect(shadowRegion, unit.x + shadowTX * e, unit.y + shadowTY * e, unit.rotation - 90);
Draw.color();
@@ -619,7 +599,7 @@ public class UnitType extends UnlockableContent{
if(leg.moving && visualElevation > 0){
float scl = visualElevation;
float elev = Mathf.slope(1f - leg.stage) * scl;
Draw.color(shadowColor);
Draw.color(Pal.shadow);
Draw.rect(footRegion, leg.base.x + shadowTX * elev, leg.base.y + shadowTY * elev, position.angleTo(leg.base));
Draw.color();
}
@@ -656,8 +636,8 @@ public class UnitType extends UnlockableContent{
float e = unit.elevation;
float sin = Mathf.lerp(Mathf.sin(walkExtend(mech, true), 2f / Mathf.PI, 1f), 0f, e);
float extension = Mathf.lerp(walkExtend(mech, false), 0, e);
float sin = Mathf.lerp(Mathf.sin(mech.walkExtend(true), 2f / Mathf.PI, 1f), 0f, e);
float extension = Mathf.lerp(mech.walkExtend(false), 0, e);
float boostTrns = e * 2f;
Floor floor = unit.isFlying() ? Blocks.air.asFloor() : unit.floorOn();
@@ -690,20 +670,6 @@ public class UnitType extends UnlockableContent{
Draw.mixcol();
}
public float walkExtend(Mechc mech, boolean scaled){
//now ranges from -maxExtension to maxExtension*3
float raw = mech.walkTime() % (mechStride * 4);
if(scaled) return raw / mechStride;
if(raw > mechStride*3) raw = raw - mechStride * 4;
else if(raw > mechStride*2) raw = mechStride * 2 - raw;
else if(raw > mechStride) raw = mechStride * 2 - raw;
return raw;
}
public void applyColor(Unit unit){
Draw.color();
Draw.mixcol(Color.white, unit.hitTime);

View File

@@ -17,7 +17,8 @@ import static mindustry.Vars.*;
public abstract class Weather extends UnlockableContent{
/** Default duration of this weather event in ticks. */
public float duration = 9f * Time.toMinutes;
public float duration = 8f * Time.toMinutes;
public float opacityMultiplier = 1f;
public Attributes attrs = new Attributes();
//internals
@@ -122,7 +123,7 @@ public abstract class Weather extends UnlockableContent{
/** Creates a weather entry with some approximate weather values. */
public WeatherEntry(Weather weather){
this(weather, weather.duration * 1f, weather.duration * 3f, weather.duration / 2f, weather.duration * 1.5f);
this(weather, weather.duration * 3f, weather.duration * 6f, weather.duration / 2f, weather.duration * 1.5f);
}
public WeatherEntry(Weather weather, float minFrequency, float maxFrequency, float minDuration, float maxDuration){
@@ -177,14 +178,14 @@ public abstract class Weather extends UnlockableContent{
if(renderer.weatherAlpha() > 0.0001f){
Draw.draw(Layer.weather, () -> {
weather.rand.setSeed(0);
Draw.alpha(renderer.weatherAlpha() * opacity);
Draw.alpha(renderer.weatherAlpha() * opacity * weather.opacityMultiplier);
weather.drawOver(self());
Draw.reset();
});
Draw.draw(Layer.debris, () -> {
weather.rand.setSeed(0);
Draw.alpha(renderer.weatherAlpha() * opacity);
Draw.alpha(renderer.weatherAlpha() * opacity * weather.opacityMultiplier);
weather.drawUnder(self());
Draw.reset();
});

View File

@@ -21,7 +21,7 @@ public class Bar extends Element{
public Bar(String name, Color color, Floatp fraction){
this.fraction = fraction;
this.name = Core.bundle.get(name);
this.name = Core.bundle.get(name, name);
this.blinkColor.set(color);
lastValue = value = fraction.get();
setColor(color);

View File

@@ -130,7 +130,7 @@ public class ContentDisplay{
public static void displayUnit(Table table, UnitType unit){
table.table(title -> {
title.image(unit.icon(Cicon.xlarge));
title.image(unit.icon(Cicon.xlarge)).size(8 * 6).scaling(Scaling.fit);
title.add("[accent]" + unit.localizedName).padLeft(5);
});

View File

@@ -29,7 +29,7 @@ public class GameOverDialog extends BaseDialog{
}
void rebuild(){
title.setText(state.launched ? "@launch.title" : "@gameover");
title.setText("@gameover");
buttons.clear();
cont.clear();
@@ -79,7 +79,7 @@ public class GameOverDialog extends BaseDialog{
}
if(state.hasSector()){
RankResult result = state.stats.calculateRank(state.getSector(), state.launched);
RankResult result = state.stats.calculateRank(state.getSector(), true);
t.add(Core.bundle.format("stat.rank", result.rank + result.modifier));
t.row();
}

View File

@@ -30,6 +30,7 @@ public class JoinDialog extends BaseDialog{
Table global = new Table();
Table hosts = new Table();
int totalHosts;
int refreshes;
public JoinDialog(){
super("@joingame");
@@ -95,6 +96,8 @@ public class JoinDialog extends BaseDialog{
}
void refreshAll(){
refreshes ++;
refreshLocal();
refreshRemote();
refreshGlobal();
@@ -327,12 +330,15 @@ public class JoinDialog extends BaseDialog{
}
void refreshGlobal(){
int cur = refreshes;
global.clear();
global.background(null);
for(String host : defaultServers){
String resaddress = host.contains(":") ? host.split(":")[0] : host;
int resport = host.contains(":") ? Strings.parseInt(host.split(":")[1]) : port;
net.pingHost(resaddress, resport, res -> {
if(refreshes != cur) return;
res.port = resport;
addGlobalHost(res);
}, e -> {});

View File

@@ -163,6 +163,19 @@ public class ResearchDialog extends BaseDialog{
});
}
@Override
public Dialog show(){
Core.app.post(() -> {
if(net.client()){
//TODO make this not display every time
//TODO rework this in the future
ui.showInfo("@campaign.multiplayer");
}
});
return super.show();
}
void treeLayout(){
float spacing = 20f;
LayoutNode node = new LayoutNode(root, null);

Some files were not shown because too many files have changed in this diff Show More