Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
Conflicts: core/src/mindustry/ui/dialogs/PlanetDialog.java
This commit is contained in:
@@ -87,7 +87,7 @@ public class Vars implements Loadable{
|
||||
public static final int maxNameLength = 40;
|
||||
/** displayed item size when ingame. */
|
||||
public static final float itemSize = 5f;
|
||||
/** units outside of this bound will die instantly */
|
||||
/** units outside this bound will die instantly */
|
||||
public static final float finalWorldBounds = 250;
|
||||
/** range for building */
|
||||
public static final float buildingRange = 220f;
|
||||
|
||||
@@ -52,6 +52,7 @@ public class BaseAI{
|
||||
}
|
||||
|
||||
public void update(){
|
||||
|
||||
if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 2.5f) && data.hasCore()){
|
||||
CoreBlock block = (CoreBlock)data.core().block;
|
||||
int coreUnits = Groups.unit.count(u -> u.team == data.team && u.type == block.unitType);
|
||||
@@ -90,49 +91,51 @@ public class BaseAI{
|
||||
}else{
|
||||
var field = pathfinder.getField(state.rules.waveTeam, Pathfinder.costGround, Pathfinder.fieldCore);
|
||||
|
||||
int[][] weights = field.weights;
|
||||
for(int i = 0; i < pathStep; i++){
|
||||
int minCost = Integer.MAX_VALUE;
|
||||
int cx = calcTile.x, cy = calcTile.y;
|
||||
boolean foundAny = false;
|
||||
for(Point2 p : Geometry.d4){
|
||||
int nx = cx + p.x, ny = cy + p.y;
|
||||
if(field.weights != null){
|
||||
int[][] weights = field.weights;
|
||||
for(int i = 0; i < pathStep; i++){
|
||||
int minCost = Integer.MAX_VALUE;
|
||||
int cx = calcTile.x, cy = calcTile.y;
|
||||
boolean foundAny = false;
|
||||
for(Point2 p : Geometry.d4){
|
||||
int nx = cx + p.x, ny = cy + p.y;
|
||||
|
||||
Tile other = world.tile(nx, ny);
|
||||
if(other != null && weights[nx][ny] < minCost && weights[nx][ny] != -1){
|
||||
minCost = weights[nx][ny];
|
||||
calcTile = other;
|
||||
foundAny = true;
|
||||
Tile other = world.tile(nx, ny);
|
||||
if(other != null && weights[nx][ny] < minCost && weights[nx][ny] != -1){
|
||||
minCost = weights[nx][ny];
|
||||
calcTile = other;
|
||||
foundAny = true;
|
||||
}
|
||||
}
|
||||
|
||||
//didn't find anything, break out of loop, this will trigger a clear later
|
||||
if(!foundAny){
|
||||
calcCount = Integer.MAX_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
calcPath.add(calcTile.pos());
|
||||
for(Point2 p : Geometry.d8){
|
||||
calcPath.add(Point2.pack(p.x + calcTile.x, p.y + calcTile.y));
|
||||
}
|
||||
|
||||
//found the end.
|
||||
if(calcTile.build instanceof CoreBuild b && b.team == state.rules.defaultTeam){
|
||||
//clean up calculations and flush results
|
||||
calculating = false;
|
||||
calcCount = 0;
|
||||
path.clear();
|
||||
path.addAll(calcPath);
|
||||
calcPath.clear();
|
||||
calcTile = null;
|
||||
totalCalcs ++;
|
||||
foundPath = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
calcCount ++;
|
||||
}
|
||||
|
||||
//didn't find anything, break out of loop, this will trigger a clear later
|
||||
if(!foundAny){
|
||||
calcCount = Integer.MAX_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
calcPath.add(calcTile.pos());
|
||||
for(Point2 p : Geometry.d8){
|
||||
calcPath.add(Point2.pack(p.x + calcTile.x, p.y + calcTile.y));
|
||||
}
|
||||
|
||||
//found the end.
|
||||
if(calcTile.build instanceof CoreBuild b && b.team == state.rules.defaultTeam){
|
||||
//clean up calculations and flush results
|
||||
calculating = false;
|
||||
calcCount = 0;
|
||||
path.clear();
|
||||
path.addAll(calcPath);
|
||||
calcPath.clear();
|
||||
calcTile = null;
|
||||
totalCalcs ++;
|
||||
foundPath = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
calcCount ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static mindustry.Vars.*;
|
||||
public class WaveSpawner{
|
||||
private static final float margin = 40f, coreMargin = tilesize * 2f, maxSteps = 30;
|
||||
|
||||
private int tmpCount;
|
||||
private Seq<Tile> spawns = new Seq<>();
|
||||
private boolean spawning = false;
|
||||
private boolean any = false;
|
||||
@@ -162,6 +163,18 @@ public class WaveSpawner{
|
||||
}
|
||||
}
|
||||
|
||||
public int countGroundSpawns(){
|
||||
tmpCount = 0;
|
||||
eachGroundSpawn((x, y) -> tmpCount ++);
|
||||
return tmpCount;
|
||||
}
|
||||
|
||||
public int countFlyerSpawns(){
|
||||
tmpCount = 0;
|
||||
eachFlyerSpawn((x, y) -> tmpCount ++);
|
||||
return tmpCount;
|
||||
}
|
||||
|
||||
public boolean isSpawning(){
|
||||
return spawning && !net.client();
|
||||
}
|
||||
|
||||
@@ -5,21 +5,17 @@ import arc.math.geom.*;
|
||||
import mindustry.ai.formations.*;
|
||||
|
||||
public class CircleFormation extends FormationPattern{
|
||||
/** Angle offset. */
|
||||
public float angleOffset = 0;
|
||||
|
||||
@Override
|
||||
public Vec3 calculateSlotLocation(Vec3 outLocation, int slotNumber){
|
||||
if(slots > 1){
|
||||
float angle = (360f * slotNumber) / slots;
|
||||
float angle = (360f * slotNumber) / slots + (slots == 8 ? 22.5f : 0);
|
||||
float radius = spacing / (float)Math.sin(180f / slots * Mathf.degRad);
|
||||
outLocation.set(Angles.trnsx(angle, radius), Angles.trnsy(angle, radius), angle);
|
||||
}else{
|
||||
outLocation.set(0, spacing * 1.1f, 360f * slotNumber);
|
||||
}
|
||||
|
||||
outLocation.z += angleOffset;
|
||||
|
||||
return outLocation;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@ import static mindustry.Vars.*;
|
||||
public class BuilderAI extends AIController{
|
||||
public static float buildRadius = 1500, retreatDst = 110f, fleeRange = 370f, retreatDelay = Time.toSeconds * 2f;
|
||||
|
||||
public @Nullable Unit following;
|
||||
public @Nullable Teamc enemy;
|
||||
public @Nullable BlockPlan lastPlan;
|
||||
|
||||
boolean found = false;
|
||||
@Nullable Unit following;
|
||||
@Nullable Teamc enemy;
|
||||
float retreatTimer;
|
||||
@Nullable BlockPlan lastPlan;
|
||||
|
||||
@Override
|
||||
public void updateMovement(){
|
||||
|
||||
@@ -21,12 +21,12 @@ public class DefenderAI extends AIController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateTargeting(){
|
||||
public void updateTargeting(){
|
||||
if(retarget()) target = findTarget(unit.x, unit.y, unit.range(), true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
|
||||
//find unit to follow if not in rally mode
|
||||
if(command() != UnitCommand.rally){
|
||||
|
||||
@@ -32,7 +32,7 @@ public class FlyingAI extends AIController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
var result = findMainTarget(x, y, range, air, ground);
|
||||
|
||||
//if the main target is in range, use it, otherwise target whatever is closest
|
||||
@@ -40,7 +40,7 @@ public class FlyingAI extends AIController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Teamc findMainTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc findMainTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
var core = targetFlag(x, y, BlockFlag.core, true);
|
||||
|
||||
if(core != null && Mathf.within(x, y, core.getX(), core.getY(), range)){
|
||||
|
||||
@@ -44,7 +44,7 @@ public class LogicAI extends AIController{
|
||||
private ObjectSet<Object> radars = new ObjectSet<>();
|
||||
|
||||
@Override
|
||||
protected void updateMovement(){
|
||||
public void updateMovement(){
|
||||
if(itemTimer >= 0) itemTimer -= Time.delta;
|
||||
if(payTimer >= 0) payTimer -= Time.delta;
|
||||
|
||||
@@ -114,7 +114,7 @@ public class LogicAI extends AIController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void moveTo(Position target, float circleLength, float smooth){
|
||||
public void moveTo(Position target, float circleLength, float smooth){
|
||||
if(target == null) return;
|
||||
|
||||
vec.set(target).sub(unit);
|
||||
@@ -141,29 +141,29 @@ public class LogicAI extends AIController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkTarget(Teamc target, float x, float y, float range){
|
||||
public boolean checkTarget(Teamc target, float x, float y, float range){
|
||||
return false;
|
||||
}
|
||||
|
||||
//always retarget
|
||||
@Override
|
||||
protected boolean retarget(){
|
||||
public boolean retarget(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean invalid(Teamc target){
|
||||
public boolean invalid(Teamc target){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldShoot(){
|
||||
public boolean shouldShoot(){
|
||||
return shoot && !(unit.type.canBoost && boost);
|
||||
}
|
||||
|
||||
//always aim for the main target
|
||||
@Override
|
||||
protected Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
return switch(aimControl){
|
||||
case target -> posTarget;
|
||||
case targetp -> mainTarget;
|
||||
|
||||
@@ -9,12 +9,12 @@ import mindustry.world.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MinerAI extends AIController{
|
||||
boolean mining = true;
|
||||
Item targetItem;
|
||||
Tile ore;
|
||||
public boolean mining = true;
|
||||
public Item targetItem;
|
||||
public Tile ore;
|
||||
|
||||
@Override
|
||||
protected void updateMovement(){
|
||||
public void updateMovement(){
|
||||
Building core = unit.closestCore();
|
||||
|
||||
if(!(unit.canMine()) || core == null) return;
|
||||
|
||||
@@ -13,7 +13,7 @@ public class RepairAI extends AIController{
|
||||
float retreatTimer;
|
||||
|
||||
@Override
|
||||
protected void updateMovement(){
|
||||
public void updateMovement(){
|
||||
if(target instanceof Building){
|
||||
boolean shoot = false;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class RepairAI extends AIController{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateTargeting(){
|
||||
public void updateTargeting(){
|
||||
Building target = Units.findDamagedTile(unit.team, unit.x, unit.y);
|
||||
|
||||
if(target instanceof ConstructBuild) target = null;
|
||||
|
||||
@@ -111,7 +111,7 @@ public class SuicideAI extends GroundAI{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
return Units.closestTarget(unit.team, x, y, range, u -> u.checkTarget(air, ground), t -> ground &&
|
||||
!(t.block instanceof Conveyor || t.block instanceof Conduit)); //do not target conveyors/conduits
|
||||
}
|
||||
|
||||
@@ -393,12 +393,6 @@ public class Fx{
|
||||
Lines.circle(e.x, e.y, 2f + e.finpow() * 7f);
|
||||
}),
|
||||
|
||||
healDenamic = new Effect(11, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fout() * 2f);
|
||||
Lines.circle(e.x, e.y, 2f + e.finpow() * e.rotation);
|
||||
}),
|
||||
|
||||
shieldWave = new Effect(22, e -> {
|
||||
color(e.color, 0.7f);
|
||||
stroke(e.fout() * 2f);
|
||||
@@ -1000,7 +994,9 @@ public class Fx{
|
||||
float length = 20f * e.finpow();
|
||||
float size = 7f * e.fout();
|
||||
|
||||
rect(((Item)e.data).fullIcon, e.x + trnsx(e.rotation, length), e.y + trnsy(e.rotation, length), size, size);
|
||||
if(!(e.data instanceof Item item)) return;
|
||||
|
||||
rect(item.fullIcon, e.x + trnsx(e.rotation, length), e.y + trnsy(e.rotation, length), size, size);
|
||||
}),
|
||||
|
||||
shockwave = new Effect(10f, 80f, e -> {
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -163,7 +164,7 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
boss = new StatusEffect("boss"){{
|
||||
color = Pal.health;
|
||||
color = Team.sharded.color;
|
||||
permanent = true;
|
||||
damageMultiplier = 1.3f;
|
||||
healthMultiplier = 1.5f;
|
||||
|
||||
@@ -1789,7 +1789,7 @@ public class UnitTypes implements ContentList{
|
||||
shootY = 6f;
|
||||
beamWidth = 0.8f;
|
||||
mirror = false;
|
||||
repairSpeed = 0.7f;
|
||||
repairSpeed = 0.75f;
|
||||
|
||||
bullet = new BulletType(){{
|
||||
maxRange = 120f;
|
||||
@@ -1835,8 +1835,8 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
speed = 0f;
|
||||
|
||||
splashDamage = 50f;
|
||||
splashDamageRadius = 40f;
|
||||
splashDamage = 55f;
|
||||
splashDamageRadius = 45f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
@@ -383,6 +383,8 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Groups.fire.clear();
|
||||
Groups.puddle.clear();
|
||||
|
||||
//reset to 0, so replaced cores don't count
|
||||
state.rules.defaultTeam.data().unitCap = 0;
|
||||
Schematics.placeLaunchLoadout(spawn.x, spawn.y);
|
||||
|
||||
//set up camera/player locations
|
||||
|
||||
@@ -105,8 +105,8 @@ public class Renderer implements ApplicationListener{
|
||||
for(int i = 0; i < splashes.length; i++) splashes[i] = atlas.find("splash-" + i);
|
||||
|
||||
assets.load("sprites/clouds.png", Texture.class).loaded = t -> {
|
||||
((Texture)t).setWrap(TextureWrap.repeat);
|
||||
((Texture)t).setFilter(TextureFilter.linear);
|
||||
t.setWrap(TextureWrap.repeat);
|
||||
t.setFilter(TextureFilter.linear);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
this.numeric = inumeric;
|
||||
this.maxLength = textLength;
|
||||
this.accepted = confirmed;
|
||||
this.allowEmpty = false;
|
||||
}});
|
||||
}else{
|
||||
new Dialog(titleText){{
|
||||
|
||||
@@ -358,30 +358,23 @@ public class World{
|
||||
}
|
||||
|
||||
public void raycastEach(int x0f, int y0f, int x1, int y1, Raycaster cons){
|
||||
int x0 = x0f;
|
||||
int y0 = y0f;
|
||||
int dx = Math.abs(x1 - x0);
|
||||
int dy = Math.abs(y1 - y0);
|
||||
int x0 = x0f, dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int y0 = y0f, dy = Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int e2, err = dx - dy;
|
||||
|
||||
int sx = x0 < x1 ? 1 : -1;
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
|
||||
int err = dx - dy;
|
||||
int e2;
|
||||
while(true){
|
||||
|
||||
if(cons.accept(x0, y0)) break;
|
||||
if(x0 == x1 && y0 == y1) break;
|
||||
|
||||
e2 = 2 * err;
|
||||
if(e2 > -dy){
|
||||
err = err - dy;
|
||||
x0 = x0 + sx;
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
|
||||
if(e2 < dx){
|
||||
err = err + dx;
|
||||
y0 = y0 + sy;
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
}else{
|
||||
Core.scene.setScrollFocus(null);
|
||||
}
|
||||
}).grow().uniformX().get().setScrollingDisabled(true, false);
|
||||
}).grow().uniformX().scrollX(false);
|
||||
}).grow();
|
||||
|
||||
buffer1 = create();
|
||||
@@ -350,7 +350,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
update();
|
||||
selection.hide();
|
||||
}).with(Table::left).get().getLabelCell().growX().left().padLeft(5).labelAlign(Align.left);
|
||||
}).get().setScrollingDisabled(true, false);
|
||||
}).scrollX(false);
|
||||
|
||||
selection.addCloseButton();
|
||||
selection.show();
|
||||
|
||||
@@ -38,7 +38,7 @@ public class MapInfoDialog extends BaseDialog{
|
||||
|
||||
TextField name = t.field(tags.get("name", ""), text -> {
|
||||
tags.put("name", text);
|
||||
}).size(400, 55f).addInputDialog(50).get();
|
||||
}).size(400, 55f).maxTextLength(50).get();
|
||||
name.setMessageText("@unknown");
|
||||
|
||||
t.row();
|
||||
@@ -46,7 +46,7 @@ public class MapInfoDialog extends BaseDialog{
|
||||
|
||||
TextArea description = t.area(tags.get("description", ""), Styles.areaField, text -> {
|
||||
tags.put("description", text);
|
||||
}).size(400f, 140f).addInputDialog(1000).get();
|
||||
}).size(400f, 140f).maxTextLength(1000).get();
|
||||
|
||||
t.row();
|
||||
t.add("@editor.author").padRight(8).left();
|
||||
@@ -54,7 +54,7 @@ public class MapInfoDialog extends BaseDialog{
|
||||
TextField author = t.field(tags.get("author", Core.settings.getString("mapAuthor", "")), text -> {
|
||||
tags.put("author", text);
|
||||
Core.settings.put("mapAuthor", text);
|
||||
}).size(400, 55f).addInputDialog(50).get();
|
||||
}).size(400, 55f).maxTextLength(50).get();
|
||||
author.setMessageText("@unknown");
|
||||
|
||||
t.row();
|
||||
|
||||
@@ -29,7 +29,7 @@ public class MapResizeDialog extends BaseDialog{
|
||||
table.field((w ? width : height) + "", TextFieldFilter.digitsOnly, value -> {
|
||||
int val = Integer.parseInt(value);
|
||||
if(w) width = val; else height = val;
|
||||
}).valid(value -> Strings.canParsePositiveInt(value) && Integer.parseInt(value) <= maxSize && Integer.parseInt(value) >= minSize).addInputDialog(3);
|
||||
}).valid(value -> Strings.canParsePositiveInt(value) && Integer.parseInt(value) <= maxSize && Integer.parseInt(value) >= minSize).maxTextLength(3);
|
||||
|
||||
table.row();
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class WaveGraph extends Table{
|
||||
for(UnitType o : hidden) used.remove(o);
|
||||
}).update(b -> b.setChecked(hidden.contains(type)));
|
||||
}
|
||||
}).get().setScrollingDisabled(false, true);
|
||||
}).scrollY(false);
|
||||
|
||||
for(UnitType type : hidden){
|
||||
used.remove(type);
|
||||
|
||||
@@ -128,7 +128,7 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
|
||||
cont.clear();
|
||||
cont.stack(new Table(Tex.clear, main -> {
|
||||
main.pane(t -> table = t).growX().growY().padRight(8f).get().setScrollingDisabled(true, false);
|
||||
main.pane(t -> table = t).growX().growY().padRight(8f).scrollX(false);
|
||||
main.row();
|
||||
main.button("@add", () -> {
|
||||
if(groups == null) groups = new Seq<>();
|
||||
|
||||
@@ -19,7 +19,8 @@ import static mindustry.Vars.*;
|
||||
public class Effect{
|
||||
private static final float shakeFalloff = 10000f;
|
||||
private static final EffectContainer container = new EffectContainer();
|
||||
private static final Seq<Effect> all = new Seq<>();
|
||||
|
||||
public static final Seq<Effect> all = new Seq<>();
|
||||
|
||||
private boolean initialized;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
@@ -23,7 +24,9 @@ public class EntityCollisions{
|
||||
private Rect r2 = new Rect();
|
||||
|
||||
//entity collisions
|
||||
private Seq<Hitboxc> arrOut = new Seq<>();
|
||||
private Seq<Hitboxc> arrOut = new Seq<>(Hitboxc.class);
|
||||
private Cons<Hitboxc> hitCons = this::updateCollision;
|
||||
private Cons<QuadTree> treeCons = tree -> tree.intersect(r2, arrOut);
|
||||
|
||||
public void moveCheck(Hitboxc entity, float deltax, float deltay, SolidPred solidCheck){
|
||||
if(!solidCheck.solid(entity.tileX(), entity.tileY())){
|
||||
@@ -36,7 +39,7 @@ public class EntityCollisions{
|
||||
}
|
||||
|
||||
public void move(Hitboxc entity, float deltax, float deltay, SolidPred solidCheck){
|
||||
if(Math.abs(deltax) < 0.0001f & Math.abs(deltay) < 0.0001f) return;
|
||||
if(Math.abs(deltax) < 0.0001f & Math.abs(deltay) < 0.0001f) return;
|
||||
|
||||
boolean movedx = false;
|
||||
|
||||
@@ -213,28 +216,34 @@ public class EntityCollisions{
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Hitboxc> void collide(EntityGroup<T> groupa){
|
||||
groupa.each(solid -> {
|
||||
solid.hitbox(r1);
|
||||
r1.x += (solid.lastX() - solid.getX());
|
||||
r1.y += (solid.lastY() - solid.getY());
|
||||
groupa.each((Cons<T>)hitCons);
|
||||
}
|
||||
|
||||
solid.hitbox(r2);
|
||||
r2.merge(r1);
|
||||
private void updateCollision(Hitboxc solid){
|
||||
solid.hitbox(r1);
|
||||
r1.x += (solid.lastX() - solid.getX());
|
||||
r1.y += (solid.lastY() - solid.getY());
|
||||
|
||||
arrOut.clear();
|
||||
solid.hitbox(r2);
|
||||
r2.merge(r1);
|
||||
|
||||
//get all targets based on what entity wants to collide with
|
||||
solid.getCollisions(tree -> tree.intersect(r2, arrOut));
|
||||
arrOut.clear();
|
||||
|
||||
for(Hitboxc sc : arrOut){
|
||||
sc.hitbox(r1);
|
||||
if(r2.overlaps(r1)){
|
||||
checkCollide(solid, sc);
|
||||
//break out of loop when this object hits something
|
||||
if(!solid.isAdded()) return;
|
||||
}
|
||||
//get all targets based on what entity wants to collide with
|
||||
solid.getCollisions(treeCons);
|
||||
|
||||
var items = arrOut.items;
|
||||
int size = arrOut.size;
|
||||
|
||||
for(int i = 0; i < size; i++){
|
||||
Hitboxc sc = items[i];
|
||||
sc.hitbox(r1);
|
||||
if(r2.overlaps(r1)){
|
||||
checkCollide(solid, sc);
|
||||
//break out of loop when this object hits something
|
||||
if(!solid.isAdded()) return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public interface SolidPred{
|
||||
|
||||
@@ -10,6 +10,7 @@ import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
@@ -105,7 +106,11 @@ public class EnergyFieldAbility extends Ability{
|
||||
});
|
||||
|
||||
if(hitBuildings){
|
||||
Units.nearbyBuildings(rx, ry, range, all::add);
|
||||
Units.nearbyBuildings(rx, ry, range, b -> {
|
||||
if(b.team != Team.derelict || state.rules.coreCapture){
|
||||
all.add(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
all.sort(h -> h.dst2(rx, ry));
|
||||
|
||||
@@ -118,36 +118,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
type.update(self());
|
||||
|
||||
if(type.collidesTiles && type.collides && type.collidesGround){
|
||||
world.raycastEach(World.toTile(lastX()), World.toTile(lastY()), tileX(), tileY(), (x, y) -> {
|
||||
|
||||
Building build = world.build(x, y);
|
||||
if(build == null || !isAdded()) return false;
|
||||
|
||||
if(build.collide(self()) && type.testCollision(self(), build) && !build.dead() && (type.collidesTeam || build.team != team) && !(type.pierceBuilding && hasCollided(build.id))){
|
||||
boolean remove = false;
|
||||
|
||||
float health = build.health;
|
||||
|
||||
if(build.team != team){
|
||||
remove = build.collision(self());
|
||||
}
|
||||
|
||||
if(remove || type.collidesTeam){
|
||||
if(!type.pierceBuilding){
|
||||
hit = true;
|
||||
remove();
|
||||
}else{
|
||||
collided.add(build.id);
|
||||
}
|
||||
}
|
||||
|
||||
type.hitTile(self(), build, health, true);
|
||||
|
||||
return !type.pierceBuilding;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
tileRaycast(World.toTile(lastX()), World.toTile(lastY()), tileX(), tileY());
|
||||
}
|
||||
|
||||
if(type.pierceCap != -1 && collided.size >= type.pierceCap){
|
||||
@@ -156,6 +127,55 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
}
|
||||
}
|
||||
|
||||
//copy-paste of World#raycastEach, inlined for lambda capture performance.
|
||||
@Override
|
||||
public void tileRaycast(int x0f, int y0f, int x1, int y1){
|
||||
int x = x0f, dx = Math.abs(x1 - x), sx = x < x1 ? 1 : -1;
|
||||
int y = y0f, dy = Math.abs(y1 - y), sy = y < y1 ? 1 : -1;
|
||||
int e2, err = dx - dy;
|
||||
|
||||
while(true){
|
||||
Building build = world.build(x, y);
|
||||
if(build != null && isAdded() && build.collide(self()) && type.testCollision(self(), build)
|
||||
&& !build.dead() && (type.collidesTeam || build.team != team) && !(type.pierceBuilding && hasCollided(build.id))){
|
||||
|
||||
boolean remove = false;
|
||||
float health = build.health;
|
||||
|
||||
if(build.team != team){
|
||||
remove = build.collision(self());
|
||||
}
|
||||
|
||||
if(remove || type.collidesTeam){
|
||||
if(!type.pierceBuilding){
|
||||
hit = true;
|
||||
remove();
|
||||
}else{
|
||||
collided.add(build.id);
|
||||
}
|
||||
}
|
||||
|
||||
type.hitTile(self(), build, health, true);
|
||||
|
||||
//stop raycasting when building is hit
|
||||
if(type.pierceBuilding) return;
|
||||
}
|
||||
|
||||
if(x == x1 && y == y1) break;
|
||||
|
||||
e2 = 2 * err;
|
||||
if(e2 > -dy){
|
||||
err -= dy;
|
||||
x += sx;
|
||||
}
|
||||
|
||||
if(e2 < dx){
|
||||
err += dx;
|
||||
y += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(type.layer);
|
||||
|
||||
@@ -45,6 +45,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
transient String lastText = "";
|
||||
transient float textFadeTime;
|
||||
transient private Unit lastReadUnit = Nulls.unit;
|
||||
transient private int wrongReadUnits;
|
||||
transient @Nullable Unit justSwitchFrom, justSwitchTo;
|
||||
|
||||
public boolean isBuilder(){
|
||||
@@ -104,9 +105,15 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
//reason: we know the server is lying here, essentially skip the unit snapshot because we know the client's information is more recent
|
||||
if(isLocal() && unit == justSwitchFrom && justSwitchFrom != null && justSwitchTo != null){
|
||||
unit = justSwitchTo;
|
||||
//if several snapshots have passed and this unit is still incorrect, something's wrong
|
||||
if(++wrongReadUnits >= 2){
|
||||
justSwitchFrom = null;
|
||||
wrongReadUnits = 0;
|
||||
}
|
||||
}else{
|
||||
justSwitchFrom = null;
|
||||
justSwitchTo = null;
|
||||
wrongReadUnits = 0;
|
||||
}
|
||||
|
||||
//simulate a unit change after sync
|
||||
|
||||
@@ -40,8 +40,8 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
|
||||
@Import int id;
|
||||
@Import float x, y;
|
||||
@Import boolean added;
|
||||
|
||||
transient private boolean mismatch = false;
|
||||
transient float accepting, updateTime, lastRipple = Time.time + Mathf.random(40f);
|
||||
float amount;
|
||||
Tile tile;
|
||||
@@ -79,9 +79,12 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
return;
|
||||
}
|
||||
|
||||
if(Puddles.get(tile) != self()){
|
||||
mismatch = true;
|
||||
remove();
|
||||
if(Puddles.get(tile) != self() && added){
|
||||
//force removal without pool free
|
||||
Groups.all.remove(self());
|
||||
Groups.draw.remove(self());
|
||||
Groups.puddle.remove(self());
|
||||
added = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,9 +137,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
if(!mismatch){
|
||||
Puddles.remove(tile);
|
||||
}
|
||||
Puddles.remove(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,19 +46,19 @@ public class AIController implements UnitController{
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected AIController fallback(){
|
||||
public AIController fallback(){
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean useFallback(){
|
||||
public boolean useFallback(){
|
||||
return false;
|
||||
}
|
||||
|
||||
protected UnitCommand command(){
|
||||
public UnitCommand command(){
|
||||
return unit.team.data().command;
|
||||
}
|
||||
|
||||
protected void updateVisuals(){
|
||||
public void updateVisuals(){
|
||||
if(unit.isFlying()){
|
||||
unit.wobble();
|
||||
|
||||
@@ -66,21 +66,21 @@ public class AIController implements UnitController{
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateMovement(){
|
||||
public void updateMovement(){
|
||||
|
||||
}
|
||||
|
||||
protected void updateTargeting(){
|
||||
public void updateTargeting(){
|
||||
if(unit.hasWeapons()){
|
||||
updateWeapons();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean invalid(Teamc target){
|
||||
public boolean invalid(Teamc target){
|
||||
return Units.invalidateTarget(target, unit.team, unit.x, unit.y);
|
||||
}
|
||||
|
||||
protected void pathfind(int pathTarget){
|
||||
public void pathfind(int pathTarget){
|
||||
int costType = unit.pathType();
|
||||
|
||||
Tile tile = unit.tileOn();
|
||||
@@ -92,7 +92,7 @@ public class AIController implements UnitController{
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile.worldx(), targetTile.worldy()), unit.speed()));
|
||||
}
|
||||
|
||||
protected void updateWeapons(){
|
||||
public void updateWeapons(){
|
||||
float rotation = unit.rotation - 90;
|
||||
boolean ret = retarget();
|
||||
|
||||
@@ -146,45 +146,45 @@ public class AIController implements UnitController{
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean checkTarget(Teamc target, float x, float y, float range){
|
||||
public boolean checkTarget(Teamc target, float x, float y, float range){
|
||||
return Units.invalidateTarget(target, unit.team, x, y, range);
|
||||
}
|
||||
|
||||
protected boolean shouldShoot(){
|
||||
public boolean shouldShoot(){
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Teamc targetFlag(float x, float y, BlockFlag flag, boolean enemy){
|
||||
public Teamc targetFlag(float x, float y, BlockFlag flag, boolean enemy){
|
||||
if(unit.team == Team.derelict) return null;
|
||||
Tile target = Geometry.findClosest(x, y, enemy ? indexer.getEnemy(unit.team, flag) : indexer.getAllied(unit.team, flag));
|
||||
return target == null ? null : target.build;
|
||||
}
|
||||
|
||||
protected Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
return Units.closestTarget(unit.team, x, y, range, u -> u.checkTarget(air, ground), t -> ground);
|
||||
}
|
||||
|
||||
protected boolean retarget(){
|
||||
public boolean retarget(){
|
||||
return timer.get(timerTarget, target == null ? 40 : 90);
|
||||
}
|
||||
|
||||
protected Teamc findMainTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc findMainTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
return findTarget(x, y, range, air, ground);
|
||||
}
|
||||
|
||||
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
public Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
return target(x, y, range, air, ground);
|
||||
}
|
||||
|
||||
protected void init(){
|
||||
public void init(){
|
||||
|
||||
}
|
||||
|
||||
protected @Nullable Tile getClosestSpawner(){
|
||||
public @Nullable Tile getClosestSpawner(){
|
||||
return Geometry.findClosest(unit.x, unit.y, Vars.spawner.getSpawns());
|
||||
}
|
||||
|
||||
protected void unloadPayloads(){
|
||||
public void unloadPayloads(){
|
||||
if(unit instanceof Payloadc pay && pay.hasPayload() && target instanceof Building && pay.payloads().peek() instanceof UnitPayload){
|
||||
if(target.within(unit, Math.max(unit.type().range + 1f, 75f))){
|
||||
pay.dropLastPayload();
|
||||
@@ -192,11 +192,11 @@ public class AIController implements UnitController{
|
||||
}
|
||||
}
|
||||
|
||||
protected void circle(Position target, float circleLength){
|
||||
public void circle(Position target, float circleLength){
|
||||
circle(target, circleLength, unit.speed());
|
||||
}
|
||||
|
||||
protected void circle(Position target, float circleLength, float speed){
|
||||
public void circle(Position target, float circleLength, float speed){
|
||||
if(target == null) return;
|
||||
|
||||
vec.set(target).sub(unit);
|
||||
@@ -210,11 +210,11 @@ public class AIController implements UnitController{
|
||||
unit.moveAt(vec);
|
||||
}
|
||||
|
||||
protected void moveTo(Position target, float circleLength){
|
||||
public void moveTo(Position target, float circleLength){
|
||||
moveTo(target, circleLength, 100f);
|
||||
}
|
||||
|
||||
protected void moveTo(Position target, float circleLength, float smooth){
|
||||
public void moveTo(Position target, float circleLength, float smooth){
|
||||
if(target == null) return;
|
||||
|
||||
vec.set(target).sub(unit);
|
||||
|
||||
@@ -5,8 +5,6 @@ import arc.func.*;
|
||||
import arc.util.*;
|
||||
import mindustry.maps.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Defines preset rule sets. */
|
||||
public enum Gamemode{
|
||||
survival(rules -> {
|
||||
@@ -25,7 +23,7 @@ public enum Gamemode{
|
||||
|
||||
rules.waveSpacing = 2f * Time.toMinutes;
|
||||
rules.teams.get(rules.waveTeam).infiniteResources = true;
|
||||
}, map -> map.teams.contains(state.rules.waveTeam.id)),
|
||||
}, map -> map.teams.size > 1),
|
||||
pvp(rules -> {
|
||||
rules.pvp = true;
|
||||
rules.enemyCoreBuildRadius = 600f;
|
||||
|
||||
@@ -33,6 +33,7 @@ import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.blocks.sandbox.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -450,6 +451,10 @@ public class Schematics implements Loadable{
|
||||
if(st.block instanceof Drill){
|
||||
tile.getLinkedTiles(t -> t.setOverlay(resource));
|
||||
}
|
||||
|
||||
if(tile.build instanceof CoreBuild cb){
|
||||
state.teams.registerCore(cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ public class Teams{
|
||||
return Geometry.findClosest(x, y, get(team).cores);
|
||||
}
|
||||
|
||||
public boolean eachEnemyCore(Team team, Boolf<CoreBuild> ret){
|
||||
public boolean anyEnemyCoresWithin(Team team, float x, float y, float radius){
|
||||
for(TeamData data : active){
|
||||
if(team != data.team){
|
||||
for(CoreBuild tile : data.cores){
|
||||
if(ret.get(tile)){
|
||||
if(tile.within(x, y, radius)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,10 @@ public class Waves{
|
||||
}
|
||||
|
||||
public static Seq<SpawnGroup> generate(float difficulty, Rand rand, boolean attack){
|
||||
return generate(difficulty, rand, attack, false);
|
||||
}
|
||||
|
||||
public static Seq<SpawnGroup> generate(float difficulty, Rand rand, boolean attack, boolean airOnly){
|
||||
UnitType[][] species = {
|
||||
{dagger, mace, fortress, scepter, reign},
|
||||
{nova, pulsar, quasar, vela, corvus},
|
||||
@@ -268,6 +272,12 @@ public class Waves{
|
||||
{flare, horizon, zenith, rand.chance(0.5) ? quad : antumbra, rand.chance(0.1) ? quad : eclipse}
|
||||
};
|
||||
|
||||
if(airOnly){
|
||||
species = Structs.filter(UnitType[].class, species, v -> v[0].flying);
|
||||
}
|
||||
|
||||
UnitType[][] fspec = species;
|
||||
|
||||
//required progression:
|
||||
//- extra periodic patterns
|
||||
|
||||
@@ -281,7 +291,7 @@ public class Waves{
|
||||
|
||||
Intc createProgression = start -> {
|
||||
//main sequence
|
||||
UnitType[] curSpecies = Structs.random(species);
|
||||
UnitType[] curSpecies = Structs.random(fspec);
|
||||
int curTier = 0;
|
||||
|
||||
for(int i = start; i < cap;){
|
||||
@@ -326,7 +336,7 @@ public class Waves{
|
||||
|
||||
//small chance to switch species
|
||||
if(rand.chance(0.3)){
|
||||
curSpecies = Structs.random(species);
|
||||
curSpecies = Structs.random(fspec);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -235,7 +235,7 @@ public class Shaders{
|
||||
super(frag);
|
||||
|
||||
Core.assets.load("sprites/space.png", Texture.class).loaded = t -> {
|
||||
texture = (Texture)t;
|
||||
texture = t;
|
||||
texture.setFilter(TextureFilter.linear);
|
||||
texture.setWrap(TextureWrap.mirroredRepeat);
|
||||
};
|
||||
@@ -274,8 +274,8 @@ public class Shaders{
|
||||
|
||||
public void loadNoise(){
|
||||
Core.assets.load("sprites/" + textureName() + ".png", Texture.class).loaded = t -> {
|
||||
((Texture)t).setFilter(TextureFilter.linear);
|
||||
((Texture)t).setWrap(TextureWrap.repeat);
|
||||
t.setFilter(TextureFilter.linear);
|
||||
t.setWrap(TextureWrap.repeat);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -136,8 +136,12 @@ public class PlanetRenderer implements Disposable{
|
||||
public void renderPlanet(Planet planet){
|
||||
if(!planet.visible()) return;
|
||||
|
||||
//render planet at offsetted position in the world
|
||||
planet.draw(cam.combined, planet.getTransform(mat));
|
||||
cam.update();
|
||||
|
||||
if(cam.frustum.containsSphere(planet.position, planet.clipRadius)){
|
||||
//render planet at offsetted position in the world
|
||||
planet.draw(cam.combined, planet.getTransform(mat));
|
||||
}
|
||||
|
||||
for(Planet child : planet.children){
|
||||
renderPlanet(child);
|
||||
@@ -151,7 +155,7 @@ public class PlanetRenderer implements Disposable{
|
||||
renderSectors(planet);
|
||||
}
|
||||
|
||||
if(planet.parent != null && planet.hasAtmosphere && Core.settings.getBool("atmosphere")){
|
||||
if(cam.frustum.containsSphere(planet.position, planet.clipRadius) && planet.parent != null && planet.hasAtmosphere && Core.settings.getBool("atmosphere")){
|
||||
Gl.depthMask(false);
|
||||
|
||||
Blending.additive.apply();
|
||||
|
||||
@@ -379,15 +379,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
throw new ValidateException(player, "Player cannot control a unit.");
|
||||
}
|
||||
|
||||
//TODO problem:
|
||||
//1. server send snapshot
|
||||
//2. client requests to control unit, becomes unit locally
|
||||
//3. snapshot arrives, client now thinks they're in the old unit (!!!)
|
||||
//4. server gets packet that player is in the right unit
|
||||
//5. server sends snapshot
|
||||
//6. client gets snapshot, realizes that they are actually in the unit they selected
|
||||
//7. client gets switched to new unit -> rubberbanding (!!!)
|
||||
|
||||
//clear player unit when they possess a core
|
||||
if(unit == null){ //just clear the unit (is this used?)
|
||||
player.clearUnit();
|
||||
@@ -404,6 +395,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
if(!player.dead()){
|
||||
Fx.unitSpirit.at(player.x, player.y, 0f, unit);
|
||||
}
|
||||
}else if(net.server()){
|
||||
//reject forwarding the packet if the unit was dead, AI or team
|
||||
throw new ValidateException(player, "Player attempted to control invalid unit.");
|
||||
}
|
||||
|
||||
Events.fire(new UnitControlEvent(player, unit));
|
||||
@@ -462,7 +456,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
player.unit().updateBuilding(isBuilding);
|
||||
}
|
||||
|
||||
if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){
|
||||
if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && !player.team().rules().infiniteAmmo && player.unit().ammo <= 0){
|
||||
player.unit().type.weapons.first().noAmmoSound.at(player.unit());
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public abstract class LStatement{
|
||||
|
||||
protected Cell<TextField> field(Table table, String value, Cons<String> setter){
|
||||
return table.field(value, Styles.nodeField, s -> setter.get(sanitize(s)))
|
||||
.size(144f, 40f).pad(2f).color(table.color).maxTextLength(LAssembler.maxTokenLength).addInputDialog();
|
||||
.size(144f, 40f).pad(2f).color(table.color).maxTextLength(LAssembler.maxTokenLength);
|
||||
}
|
||||
|
||||
protected Cell<TextField> fields(Table table, String desc, String value, Cons<String> setter){
|
||||
@@ -175,7 +175,7 @@ public abstract class LStatement{
|
||||
t.top().pane(inner -> {
|
||||
inner.top();
|
||||
hideCons.get(inner, hide);
|
||||
}).pad(0f).top().get().setScrollingDisabled(true, false);
|
||||
}).pad(0f).top().scrollX(false);
|
||||
|
||||
t.pack();
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ public class Maps{
|
||||
//try to load preview
|
||||
if(map.previewFile().exists()){
|
||||
//this may fail, but calls queueNewPreview
|
||||
Core.assets.load(new AssetDescriptor<>(map.previewFile().path() + "." + mapExtension, Texture.class, new MapPreviewParameter(map))).loaded = t -> map.texture = (Texture)t;
|
||||
Core.assets.load(new AssetDescriptor<>(map.previewFile().path() + "." + mapExtension, Texture.class, new MapPreviewParameter(map))).loaded = t -> map.texture = t;
|
||||
|
||||
try{
|
||||
readCache(map);
|
||||
|
||||
@@ -490,7 +490,8 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
state.rules.waves = sector.info.waves = true;
|
||||
state.rules.enemyCoreBuildRadius = 600f;
|
||||
|
||||
state.rules.spawns = Waves.generate(difficulty, new Rand(), state.rules.attackMode);
|
||||
//spawn air only when spawn is blocked
|
||||
state.rules.spawns = Waves.generate(difficulty, new Rand(sector.id), state.rules.attackMode, state.rules.attackMode && spawner.countGroundSpawns() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -77,9 +77,10 @@ public class ContentParser{
|
||||
}
|
||||
});
|
||||
put(StatusEffect.class, (type, data) -> {
|
||||
Object result = fieldOpt(StatusEffects.class, data);
|
||||
if(result != null){
|
||||
return result;
|
||||
if(data.isString()){
|
||||
StatusEffect result = locate(ContentType.status, data.asString());
|
||||
if(result != null) return result;
|
||||
throw new IllegalArgumentException("Unknown status effect: '" + data.asString() + "'");
|
||||
}
|
||||
StatusEffect effect = new StatusEffect(currentMod.name + "-" + data.getString("name"));
|
||||
readFields(effect, data);
|
||||
@@ -99,7 +100,7 @@ public class ContentParser{
|
||||
put(AmmoType.class, (type, data) -> {
|
||||
//string -> item
|
||||
//if liquid ammo support is added, this should scan for liquids as well
|
||||
if(data.isString()) return find(ContentType.item, data.asString());
|
||||
if(data.isString()) return new ItemAmmoType(find(ContentType.item, data.asString()));
|
||||
//number -> power
|
||||
if(data.isNumber()) return new PowerAmmoType(data.asFloat());
|
||||
|
||||
|
||||
@@ -267,6 +267,7 @@ public class Mods implements Loadable{
|
||||
|
||||
TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.linear : TextureFilter.nearest;
|
||||
|
||||
Time.mark();
|
||||
//generate new icons
|
||||
for(Seq<Content> arr : content.getContentMap()){
|
||||
arr.each(c -> {
|
||||
@@ -277,6 +278,7 @@ public class Mods implements Loadable{
|
||||
}
|
||||
});
|
||||
}
|
||||
Log.debug("Time to generate icons: @", Time.elapsed());
|
||||
|
||||
//dispose old atlas data
|
||||
Core.atlas = packer.flush(filter, new TextureAtlas());
|
||||
@@ -287,7 +289,7 @@ public class Mods implements Loadable{
|
||||
|
||||
packer.dispose();
|
||||
packer = null;
|
||||
Log.debug("Time to update textures: @", Time.elapsed());
|
||||
Log.debug("Total time to generate & flush textures synchronously: @", Time.elapsed());
|
||||
}
|
||||
|
||||
private PageType getPage(AtlasRegion region){
|
||||
|
||||
@@ -21,10 +21,10 @@ import java.net.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class Scripts implements Disposable{
|
||||
private final Context context;
|
||||
private final Scriptable scope;
|
||||
private boolean errored;
|
||||
public final Context context;
|
||||
public final Scriptable scope;
|
||||
|
||||
private boolean errored;
|
||||
LoadedMod currentMod = null;
|
||||
|
||||
public Scripts(){
|
||||
|
||||
@@ -342,24 +342,9 @@ public class ArcNetProvider implements NetProvider{
|
||||
//for debugging total read/write speeds
|
||||
private static final boolean debug = false;
|
||||
|
||||
ThreadLocal<ByteBuffer> decompressBuffer = new ThreadLocal<>(){
|
||||
@Override
|
||||
protected ByteBuffer initialValue(){
|
||||
return ByteBuffer.allocate(32768);
|
||||
}
|
||||
};
|
||||
ThreadLocal<Reads> reads = new ThreadLocal<>(){
|
||||
@Override
|
||||
protected Reads initialValue(){
|
||||
return new Reads(new ByteBufferInput(decompressBuffer.get()));
|
||||
}
|
||||
};
|
||||
ThreadLocal<Writes> writes = new ThreadLocal<>(){
|
||||
@Override
|
||||
protected Writes initialValue(){
|
||||
return new Writes(new ByteBufferOutput(decompressBuffer.get()));
|
||||
}
|
||||
};
|
||||
ThreadLocal<ByteBuffer> decompressBuffer = Threads.local(() -> ByteBuffer.allocate(32768));
|
||||
ThreadLocal<Reads> reads = Threads.local(() -> new Reads(new ByteBufferInput(decompressBuffer.get())));
|
||||
ThreadLocal<Writes> writes = Threads.local(() -> new Writes(new ByteBufferOutput(decompressBuffer.get())));
|
||||
|
||||
//for debugging network write counts
|
||||
static WindowedMean upload = new WindowedMean(5), download = new WindowedMean(5);
|
||||
|
||||
@@ -274,6 +274,7 @@ public class Net{
|
||||
builder.add(c.data);
|
||||
|
||||
ui.loadfrag.setProgress(builder.progress());
|
||||
ui.loadfrag.snapProgress();
|
||||
netClient.resetTimeout();
|
||||
|
||||
if(builder.isDone()){
|
||||
|
||||
@@ -41,6 +41,8 @@ public class Planet extends UnlockableContent{
|
||||
public boolean drawOrbit = true;
|
||||
/** Atmosphere radius adjustment parameters. */
|
||||
public float atmosphereRadIn = 0, atmosphereRadOut = 0.3f;
|
||||
/** Frustrum sphere clip radius. */
|
||||
public float clipRadius = -1f;
|
||||
/** Orbital radius around the sun. Do not change unless you know exactly what you are doing.*/
|
||||
public float orbitRadius;
|
||||
/** Total radius of this planet and all its children. */
|
||||
@@ -55,7 +57,7 @@ public class Planet extends UnlockableContent{
|
||||
public float sectorApproxRadius;
|
||||
/** Whether this planet is tidally locked relative to its parent - see https://en.wikipedia.org/wiki/Tidal_locking */
|
||||
public boolean tidalLock = false;
|
||||
/** Whether or not this planet is listed in the planet access UI. **/
|
||||
/** Whether this planet is listed in the planet access UI. **/
|
||||
public boolean accessible = true;
|
||||
/** If true, a day/night cycle is simulated. */
|
||||
public boolean updateLighting = true;
|
||||
@@ -252,6 +254,7 @@ public class Planet extends UnlockableContent{
|
||||
updateBaseCoverage();
|
||||
}
|
||||
|
||||
clipRadius = Math.max(clipRadius, radius + atmosphereRadOut + 0.5f);
|
||||
}
|
||||
|
||||
/** Gets a sector a tile position. */
|
||||
|
||||
@@ -623,9 +623,9 @@ public class UnitType extends UnlockableContent{
|
||||
Draw.reset();
|
||||
a.draw(unit);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public <T extends Unit & Payloadc> void drawPayload(T unit){
|
||||
|
||||
@@ -62,6 +62,10 @@ public class Bar extends Element{
|
||||
update(() -> this.name = name.get());
|
||||
}
|
||||
|
||||
public void snap(){
|
||||
lastValue = value = fraction.get();
|
||||
}
|
||||
|
||||
public Bar outline(Color color, float stroke){
|
||||
outlineColor.set(color);
|
||||
outlineRadius = Scl.scl(stroke);
|
||||
|
||||
@@ -74,19 +74,19 @@ public class Fonts{
|
||||
largeIcons.clear();
|
||||
FreeTypeFontParameter param = fontParameter();
|
||||
|
||||
Core.assets.load("default", Font.class, new FreeTypeFontLoaderParameter(mainFont, param)).loaded = f -> Fonts.def = (Font)f;
|
||||
Core.assets.load("default", Font.class, new FreeTypeFontLoaderParameter(mainFont, param)).loaded = f -> Fonts.def = f;
|
||||
Core.assets.load("icon", Font.class, new FreeTypeFontLoaderParameter("fonts/icon.ttf", new FreeTypeFontParameter(){{
|
||||
size = 30;
|
||||
incremental = true;
|
||||
characters = "\0";
|
||||
}})).loaded = f -> Fonts.icon = (Font)f;
|
||||
}})).loaded = f -> Fonts.icon = f;
|
||||
Core.assets.load("iconLarge", Font.class, new FreeTypeFontLoaderParameter("fonts/icon.ttf", new FreeTypeFontParameter(){{
|
||||
size = 48;
|
||||
incremental = false;
|
||||
characters = "\0" + Iconc.all;
|
||||
borderWidth = 5f;
|
||||
borderColor = Color.darkGray;
|
||||
}})).loaded = f -> Fonts.iconLarge = (Font)f;
|
||||
}})).loaded = f -> Fonts.iconLarge = f;
|
||||
}
|
||||
|
||||
public static TextureRegion getLargeIcon(String name){
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ItemsDisplay extends Table{
|
||||
label.actions(Actions.color(Color.white, 0.75f, Interp.fade));
|
||||
}
|
||||
}
|
||||
}).get().setScrollingDisabled(true, false), false).setDuration(0.3f);
|
||||
}).scrollX(false), false).setDuration(0.3f);
|
||||
|
||||
c.button("@globalitems", Icon.downOpen, Styles.clearTogglet, col::toggle).update(t -> {
|
||||
t.setChecked(col.isCollapsed());
|
||||
|
||||
@@ -1,59 +1,6 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
//TODO remove, unlikely to be used anywhere else.
|
||||
public class SearchBar{
|
||||
|
||||
public static <T> Table add(Table parent, Seq<T> list, Func<String, String> queryf,
|
||||
Func<T, String> namef, Cons2<Table, T> itemc, boolean show){
|
||||
Table[] pane = {null};
|
||||
|
||||
Cons<String> rebuild = str -> {
|
||||
String query = queryf.get(str);
|
||||
|
||||
pane[0].clear();
|
||||
boolean any = false;
|
||||
for(T item : list){
|
||||
if(query.isEmpty() || matches(query, namef.get(item))){
|
||||
any = true;
|
||||
itemc.get(pane[0], item);
|
||||
}
|
||||
}
|
||||
|
||||
if(!any){
|
||||
pane[0].add("@none.found").color(Color.lightGray).pad(4);
|
||||
}
|
||||
};
|
||||
|
||||
if(show){
|
||||
parent.table(search -> {
|
||||
search.image(Icon.zoom).padRight(8f);
|
||||
search.field("", rebuild).growX();
|
||||
}).fillX().padBottom(4);
|
||||
}
|
||||
|
||||
parent.row();
|
||||
parent.pane(table -> {
|
||||
pane[0] = table;
|
||||
rebuild.get("");
|
||||
}).get().setScrollingDisabled(true, false);
|
||||
return pane[0];
|
||||
}
|
||||
|
||||
public static <T> Table add(Table parent, Seq<T> list, Func<String, String> queryf, Func<T, String> namef, Cons2<Table, T> itemc){
|
||||
return add(parent, list, queryf, namef, itemc, true);
|
||||
}
|
||||
|
||||
public static <T> Table add(Table parent, Seq<T> list, Func<T, String> namef, Cons2<Table, T> itemc, boolean show){
|
||||
return add(parent, list, String::toLowerCase, namef, itemc, show);
|
||||
}
|
||||
|
||||
/** Match a list item with the search query, case insensitive */
|
||||
public static boolean matches(String query, String name){
|
||||
return name != null && !name.isEmpty() && name.toLowerCase().contains(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
|
||||
void setup(){
|
||||
cont.clear();
|
||||
cont.pane(m -> main = m).get().setScrollingDisabled(true, false);
|
||||
cont.pane(m -> main = m).scrollX(false);
|
||||
main.margin(10f);
|
||||
main.button("@settings.reset", () -> {
|
||||
rules = resetter.get();
|
||||
@@ -273,7 +273,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
t.add(text).left().padRight(5);
|
||||
t.field((prov.get()) + "", s -> cons.get(Strings.parseInt(s)))
|
||||
.padRight(100f)
|
||||
.valid(f -> Strings.parseInt(f) >= min && Strings.parseInt(f) <= max).width(120f).left().addInputDialog();
|
||||
.valid(f -> Strings.parseInt(f) >= min && Strings.parseInt(f) <= max).width(120f).left();
|
||||
}).padTop(0).row();
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
t.field((integer ? (int)prov.get() : prov.get()) + "", s -> cons.get(Strings.parseFloat(s)))
|
||||
.padRight(100f)
|
||||
.update(a -> a.setDisabled(!condition.get()))
|
||||
.valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left().addInputDialog();
|
||||
.valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left();
|
||||
}).padTop(0);
|
||||
main.row();
|
||||
}
|
||||
@@ -309,7 +309,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
Cell<TextField> field(Table table, float value, Floatc setter){
|
||||
return table.field(Strings.autoFixed(value, 2), v -> setter.get(Strings.parseFloat(v)))
|
||||
.valid(Strings::canParsePositiveFloat)
|
||||
.size(90f, 40f).pad(2f).addInputDialog();
|
||||
.size(90f, 40f).pad(2f);
|
||||
}
|
||||
|
||||
void weatherDialog(){
|
||||
|
||||
@@ -67,7 +67,7 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
TextField field = add.cont.field(Core.settings.getString("ip"), text -> {
|
||||
Core.settings.put("ip", text);
|
||||
}).size(320f, 54f).maxTextLength(100).addInputDialog().get();
|
||||
}).size(320f, 54f).maxTextLength(100).get();
|
||||
|
||||
add.cont.row();
|
||||
add.buttons.defaults().size(140f, 60f).pad(4f);
|
||||
@@ -287,7 +287,7 @@ public class JoinDialog extends BaseDialog{
|
||||
t.field(Core.settings.getString("name"), text -> {
|
||||
player.name(text);
|
||||
Core.settings.put("name", text);
|
||||
}).grow().pad(8).addInputDialog(maxNameLength);
|
||||
}).grow().pad(8).maxTextLength(maxNameLength);
|
||||
|
||||
ImageButton button = t.button(Tex.whiteui, Styles.clearFulli, 40, () -> {
|
||||
new PaletteDialog().show(color -> {
|
||||
|
||||
@@ -131,7 +131,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
|
||||
}).growX().get().setScrollingDisabled(true, false);
|
||||
}).growX().scrollX(false);
|
||||
|
||||
cont.row();
|
||||
cont.pane(items);
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ModsDialog extends BaseDialog{
|
||||
browser.cont.pane(tablebrow -> {
|
||||
tablebrow.margin(10f).top();
|
||||
browserTable = tablebrow;
|
||||
}).get().setScrollingDisabled(true, false);
|
||||
}).scrollX(false);
|
||||
browser.addCloseButton();
|
||||
|
||||
browser.onResize(this::rebuildBrowser);
|
||||
@@ -208,90 +208,113 @@ public class ModsDialog extends BaseDialog{
|
||||
|
||||
if(!mods.list().isEmpty()){
|
||||
boolean[] anyDisabled = {false};
|
||||
SearchBar.add(cont, mods.list(),
|
||||
mod -> mod.meta.displayName(),
|
||||
(table, mod) -> {
|
||||
if(!mod.enabled() && !anyDisabled[0] && mods.list().size > 0){
|
||||
anyDisabled[0] = true;
|
||||
table.row();
|
||||
table.image().growX().height(4f).pad(6f).color(Pal.gray);
|
||||
table.row();
|
||||
}
|
||||
Table[] pane = {null};
|
||||
|
||||
table.button(t -> {
|
||||
t.top().left();
|
||||
t.margin(12f);
|
||||
Cons<String> rebuild = query -> {
|
||||
pane[0].clear();
|
||||
boolean any = false;
|
||||
for(LoadedMod item : mods.list()){
|
||||
if(Strings.matches(query, item.meta.displayName())){
|
||||
any = true;
|
||||
if(!item.enabled() && !anyDisabled[0] && mods.list().size > 0){
|
||||
anyDisabled[0] = true;
|
||||
pane[0].row();
|
||||
pane[0].image().growX().height(4f).pad(6f).color(Pal.gray).row();
|
||||
}
|
||||
|
||||
t.defaults().left().top();
|
||||
t.table(title -> {
|
||||
title.left();
|
||||
pane[0].button(t -> {
|
||||
t.top().left();
|
||||
t.margin(12f);
|
||||
|
||||
title.add(new BorderImage(){{
|
||||
if(mod.iconTexture != null){
|
||||
setDrawable(new TextureRegion(mod.iconTexture));
|
||||
}else{
|
||||
setDrawable(Tex.nomap);
|
||||
}
|
||||
border(Pal.accent);
|
||||
}}).size(h - 8f).padTop(-8f).padLeft(-8f).padRight(8f);
|
||||
t.defaults().left().top();
|
||||
t.table(title1 -> {
|
||||
title1.left();
|
||||
|
||||
title.table(text -> {
|
||||
boolean hideDisabled = !mod.isSupported() || mod.hasUnmetDependencies() || mod.hasContentErrors();
|
||||
title1.add(new BorderImage(){{
|
||||
if(item.iconTexture != null){
|
||||
setDrawable(new TextureRegion(item.iconTexture));
|
||||
}else{
|
||||
setDrawable(Tex.nomap);
|
||||
}
|
||||
border(Pal.accent);
|
||||
}}).size(h - 8f).padTop(-8f).padLeft(-8f).padRight(8f);
|
||||
|
||||
text.add("[accent]" + Strings.stripColors(mod.meta.displayName()) + "\n[lightgray]v" + Strings.stripColors(trimText(mod.meta.version)) + (mod.enabled() || hideDisabled ? "" : "\n" + Core.bundle.get("mod.disabled") + ""))
|
||||
title1.table(text -> {
|
||||
boolean hideDisabled = !item.isSupported() || item.hasUnmetDependencies() || item.hasContentErrors();
|
||||
|
||||
text.add("[accent]" + Strings.stripColors(item.meta.displayName()) + "\n[lightgray]v" + Strings.stripColors(trimText(item.meta.version)) + (item.enabled() || hideDisabled ? "" : "\n" + Core.bundle.get("mod.disabled") + ""))
|
||||
.wrap().top().width(300f).growX().left();
|
||||
|
||||
text.row();
|
||||
|
||||
if(mod.isOutdated()){
|
||||
text.labelWrap("@mod.outdated").growX();
|
||||
text.row();
|
||||
}else if(!mod.isSupported()){
|
||||
text.labelWrap(Core.bundle.format("mod.requiresversion", mod.meta.minGameVersion)).growX();
|
||||
text.row();
|
||||
}else if(mod.hasUnmetDependencies()){
|
||||
text.labelWrap(Core.bundle.format("mod.missingdependencies", mod.missingDependencies.toString(", "))).growX();
|
||||
t.row();
|
||||
}else if(mod.hasContentErrors()){
|
||||
text.labelWrap("@mod.erroredcontent").growX();
|
||||
text.row();
|
||||
}else if(mod.meta.hidden){
|
||||
text.labelWrap("@mod.multiplayer.compatible").growX();
|
||||
text.row();
|
||||
}
|
||||
}).top().growX();
|
||||
|
||||
title.add().growX();
|
||||
}).growX().growY().left();
|
||||
if(item.isOutdated()){
|
||||
text.labelWrap("@mod.outdated").growX();
|
||||
text.row();
|
||||
}else if(!item.isSupported()){
|
||||
text.labelWrap(Core.bundle.format("mod.requiresversion", item.meta.minGameVersion)).growX();
|
||||
text.row();
|
||||
}else if(item.hasUnmetDependencies()){
|
||||
text.labelWrap(Core.bundle.format("mod.missingdependencies", item.missingDependencies.toString(", "))).growX();
|
||||
t.row();
|
||||
}else if(item.hasContentErrors()){
|
||||
text.labelWrap("@mod.erroredcontent").growX();
|
||||
text.row();
|
||||
}else if(item.meta.hidden){
|
||||
text.labelWrap("@mod.multiplayer.compatible").growX();
|
||||
text.row();
|
||||
}
|
||||
}).top().growX();
|
||||
|
||||
t.table(right -> {
|
||||
right.right();
|
||||
right.button(mod.enabled() ? Icon.downOpen : Icon.upOpen, Styles.clearPartiali, () -> {
|
||||
mods.setEnabled(mod, !mod.enabled());
|
||||
setup();
|
||||
}).size(50f).disabled(!mod.isSupported());
|
||||
title1.add().growX();
|
||||
}).growX().growY().left();
|
||||
|
||||
right.button(mod.hasSteamID() ? Icon.link : Icon.trash, Styles.clearPartiali, () -> {
|
||||
if(!mod.hasSteamID()){
|
||||
ui.showConfirm("@confirm", "@mod.remove.confirm", () -> {
|
||||
mods.removeMod(mod);
|
||||
setup();
|
||||
});
|
||||
}else{
|
||||
platform.viewListing(mod);
|
||||
}
|
||||
}).size(50f);
|
||||
t.table(right -> {
|
||||
right.right();
|
||||
right.button(item.enabled() ? Icon.downOpen : Icon.upOpen, Styles.clearPartiali, () -> {
|
||||
mods.setEnabled(item, !item.enabled());
|
||||
setup();
|
||||
}).size(50f).disabled(!item.isSupported());
|
||||
|
||||
if(steam && !mod.hasSteamID()){
|
||||
right.row();
|
||||
right.button(Icon.export, Styles.clearPartiali, () -> {
|
||||
platform.publish(mod);
|
||||
right.button(item.hasSteamID() ? Icon.link : Icon.trash, Styles.clearPartiali, () -> {
|
||||
if(!item.hasSteamID()){
|
||||
ui.showConfirm("@confirm", "@mod.remove.confirm", () -> {
|
||||
mods.removeMod(item);
|
||||
setup();
|
||||
});
|
||||
}else{
|
||||
platform.viewListing(item);
|
||||
}
|
||||
}).size(50f);
|
||||
}
|
||||
}).growX().right().padRight(-8f).padTop(-8f);
|
||||
}, Styles.clearPartialt, () -> showMod(mod)).size(w, h).growX().pad(4f);
|
||||
table.row();
|
||||
}, !mobile || Core.graphics.isPortrait()).margin(10f).top();
|
||||
|
||||
if(steam && !item.hasSteamID()){
|
||||
right.row();
|
||||
right.button(Icon.export, Styles.clearPartiali, () -> {
|
||||
platform.publish(item);
|
||||
}).size(50f);
|
||||
}
|
||||
}).growX().right().padRight(-8f).padTop(-8f);
|
||||
}, Styles.clearPartialt, () -> showMod(item)).size(w, h).growX().pad(4f);
|
||||
pane[0].row();
|
||||
}
|
||||
}
|
||||
|
||||
if(!any){
|
||||
pane[0].add("@none.found").color(Color.lightGray).pad(4);
|
||||
}
|
||||
};
|
||||
|
||||
if(!mobile || Core.graphics.isPortrait()){
|
||||
cont.table(search -> {
|
||||
search.image(Icon.zoom).padRight(8f);
|
||||
search.field("", rebuild).growX();
|
||||
}).fillX().padBottom(4);
|
||||
}
|
||||
|
||||
cont.row();
|
||||
cont.pane(table1 -> {
|
||||
pane[0] = table1.margin(10f).top();
|
||||
rebuild.get("");
|
||||
}).scrollX(false);
|
||||
}else{
|
||||
cont.table(Styles.black6, t -> t.add("@mods.none")).height(80f);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import mindustry.graphics.*;
|
||||
import mindustry.graphics.g3d.*;
|
||||
import mindustry.graphics.g3d.PlanetGrid.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.io.legacy.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
@@ -65,7 +64,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
public boolean showed = false, sectorsShown;
|
||||
public String searchText = "";
|
||||
|
||||
public Table sectorTop = new Table(), notifs;
|
||||
public Table sectorTop = new Table(), notifs = new Table();
|
||||
public Label hoverLabel = new Label("");
|
||||
|
||||
public PlanetDialog(){
|
||||
@@ -131,7 +130,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
@Override
|
||||
public boolean scrolled(InputEvent event, float x, float y, float amountX, float amountY){
|
||||
if(event.targetActor == PlanetDialog.this){
|
||||
zoom = Mathf.clamp(zoom + y / 10f, planets.planet.minZoom, 2f);
|
||||
zoom = Mathf.clamp(zoom + amountY / 10f, planets.planet.minZoom, 2f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -166,16 +165,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
return this;
|
||||
}
|
||||
|
||||
//load legacy research
|
||||
if(Core.settings.has("unlocks") && !Core.settings.has("junction-unlocked")){
|
||||
Core.app.post(() -> {
|
||||
ui.showCustomConfirm("@research", "@research.legacy", "@research.load", "@research.discard", () -> {
|
||||
LegacyIO.readResearch();
|
||||
Core.settings.remove("unlocks");
|
||||
}, () -> Core.settings.remove("unlocks"));
|
||||
});
|
||||
}
|
||||
|
||||
rebuildButtons();
|
||||
mode = look;
|
||||
selected = hovered = launchSector = null;
|
||||
@@ -545,7 +534,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}),
|
||||
|
||||
new Table(c -> {
|
||||
if(!(graphics.isPortrait() && mobile) && planets.planet.sectors.contains(Sector::hasBase)){
|
||||
c.visible(() -> !(graphics.isPortrait() && mobile));
|
||||
if(planets.planet.sectors.contains(Sector::hasBase)){
|
||||
int attacked = planets.planet.sectors.count(Sector::isAttacked);
|
||||
|
||||
//sector notifications & search
|
||||
@@ -571,8 +561,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
})).grow();
|
||||
}
|
||||
|
||||
//TODO
|
||||
void rebuildList(){
|
||||
if(notifs == null) return;
|
||||
|
||||
notifs.clear();
|
||||
|
||||
var all = planets.planet.sectors.select(Sector::hasBase);
|
||||
@@ -586,7 +577,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
s.field(searchText, t -> {
|
||||
searchText = t;
|
||||
readd[0].run();
|
||||
}).growX().height(50f).addInputDialog();
|
||||
}).growX().height(50f);
|
||||
}).growX().row();
|
||||
|
||||
Table con = p.table().growX().get();
|
||||
@@ -632,7 +623,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
};
|
||||
|
||||
readd[0].run();
|
||||
}).grow().get().setScrollingDisabled(true, false);
|
||||
}).grow().scrollX(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,7 +41,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
|
||||
public SchematicsDialog(){
|
||||
super("@schematics");
|
||||
Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> ((Texture)t).setWrap(TextureWrap.repeat);
|
||||
Core.assets.load("sprites/schematic-background.png", Texture.class).loaded = t -> t.setWrap(TextureWrap.repeat);
|
||||
|
||||
tags = Core.settings.getJson("schematic-tags", Seq.class, String.class, Seq::new);
|
||||
|
||||
@@ -69,7 +69,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
searchField = s.field(search, res -> {
|
||||
search = res;
|
||||
rebuildPane.run();
|
||||
}).growX().addInputDialog().get();
|
||||
}).growX().get();
|
||||
}).fillX().padBottom(4);
|
||||
|
||||
cont.row();
|
||||
@@ -97,7 +97,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
}
|
||||
};
|
||||
rebuildTags.run();
|
||||
}).fillX().height(tagh).get().setScrollingDisabled(false, true);
|
||||
}).fillX().height(tagh).scrollY(false);
|
||||
|
||||
in.button(Icon.pencilSmall, () -> {
|
||||
showAllTags();
|
||||
@@ -164,12 +164,12 @@ public class SchematicsDialog extends BaseDialog{
|
||||
cont.table(tags -> buildTags(s, tags, false)).maxWidth(400f).fillX().left().row();
|
||||
|
||||
cont.margin(30).add("@name").padRight(6f);
|
||||
TextField nameField = cont.field(s.name(), null).size(400f, 55f).addInputDialog().left().get();
|
||||
TextField nameField = cont.field(s.name(), null).size(400f, 55f).left().get();
|
||||
|
||||
cont.row();
|
||||
|
||||
cont.margin(30).add("@editor.description").padRight(6f);
|
||||
TextField descField = cont.area(s.description(), Styles.areaField, t -> {}).size(400f, 140f).left().addInputDialog().get();
|
||||
TextField descField = cont.area(s.description(), Styles.areaField, t -> {}).size(400f, 140f).left().get();
|
||||
|
||||
Runnable accept = () -> {
|
||||
s.tags.put("name", nameField.getText());
|
||||
@@ -246,7 +246,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
};
|
||||
|
||||
rebuildPane.run();
|
||||
}).grow().get().setScrollingDisabled(true, false);
|
||||
}).grow().scrollX(false);
|
||||
}
|
||||
|
||||
public void showInfo(Schematic schematic){
|
||||
@@ -573,7 +573,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
});
|
||||
}
|
||||
|
||||
}).fillX().left().height(tagh).get().setScrollingDisabled(false, true);
|
||||
}).fillX().left().height(tagh).scrollY(false);
|
||||
|
||||
t.button(Icon.addSmall, () -> {
|
||||
var dialog = new BaseDialog("@schematic.addtag");
|
||||
|
||||
@@ -48,12 +48,12 @@ public class HintsFragment extends Fragment{
|
||||
}else if(!current.show()){ //current became hidden
|
||||
hide();
|
||||
}
|
||||
}else if(hints.size > 0 && !renderer.isCutscene()){
|
||||
}else if(hints.size > 0){
|
||||
//check one hint each frame to see if it should be shown.
|
||||
Hint hint = hints.find(Hint::show);
|
||||
if(hint != null && hint.complete()){
|
||||
hints.remove(hint);
|
||||
}else if(hint != null){
|
||||
}else if(hint != null && !renderer.isCutscene() && state.isGame() && control.saves.getTotalPlaytime() > 8000){
|
||||
display(hint);
|
||||
}else{
|
||||
//moused over a derelict structure
|
||||
@@ -92,7 +92,7 @@ public class HintsFragment extends Fragment{
|
||||
hints.sort(Hint::order);
|
||||
|
||||
Hint first = hints.find(Hint::show);
|
||||
if(first != null){
|
||||
if(first != null && !renderer.isCutscene() && state.isGame()){
|
||||
hints.remove(first);
|
||||
display(first);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ public class LoadingFragment extends Fragment{
|
||||
bar.set(() -> ((int)(progress.get() * 100) + "%"), progress, Pal.accent);
|
||||
}
|
||||
|
||||
public void snapProgress(){
|
||||
bar.snap();
|
||||
}
|
||||
|
||||
public void setProgress(float progress){
|
||||
progValue = progress;
|
||||
if(!bar.visible){
|
||||
|
||||
@@ -52,7 +52,7 @@ public class PlayerListFragment extends Fragment{
|
||||
search.setMessageText(Core.bundle.get("players.search"));
|
||||
|
||||
pane.row();
|
||||
pane.pane(content).grow().get().setScrollingDisabled(true, false);
|
||||
pane.pane(content).grow().scrollX(false);
|
||||
pane.row();
|
||||
|
||||
pane.table(menu -> {
|
||||
|
||||
@@ -37,8 +37,11 @@ import java.util.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Block extends UnlockableContent{
|
||||
/** If true, buildings have an ItemModule. */
|
||||
public boolean hasItems;
|
||||
/** If true, buildings have a LiquidModule. */
|
||||
public boolean hasLiquids;
|
||||
/** If true, buildings have a PowerModule. */
|
||||
public boolean hasPower;
|
||||
|
||||
public boolean outputsLiquid = false;
|
||||
@@ -46,8 +49,6 @@ public class Block extends UnlockableContent{
|
||||
public boolean outputsPower = false;
|
||||
public boolean outputsPayload = false;
|
||||
public boolean acceptsPayload = false;
|
||||
public boolean outputFacing = true;
|
||||
public boolean noSideBlend = false;
|
||||
public boolean acceptsItems = false;
|
||||
|
||||
public int itemCapacity = 10;
|
||||
@@ -57,6 +58,11 @@ public class Block extends UnlockableContent{
|
||||
public final BlockBars bars = new BlockBars();
|
||||
public final Consumers consumes = new Consumers();
|
||||
|
||||
/** If true, this block outputs to its facing direction, when applicable.
|
||||
* Used for blending calculations. */
|
||||
public boolean outputFacing = true;
|
||||
/** if true, this block does not accept input from the sides (used for armored conveyors) */
|
||||
public boolean noSideBlend = false;
|
||||
/** whether to display flow rate */
|
||||
public boolean displayFlow = true;
|
||||
/** whether this block is visible in the editor */
|
||||
|
||||
@@ -151,7 +151,7 @@ public class Build{
|
||||
if(closest != null && closest.team != team){
|
||||
return false;
|
||||
}
|
||||
}else if(state.teams.eachEnemyCore(team, core -> Mathf.dst(x * tilesize + type.offset, y * tilesize + type.offset, core.x, core.y) < state.rules.enemyCoreBuildRadius + type.size * tilesize / 2f)){
|
||||
}else if(state.teams.anyEnemyCoresWithin(team, x * tilesize + type.offset, y * tilesize + type.offset, state.rules.enemyCoreBuildRadius + tilesize)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class LaunchPad extends Block{
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
//increment launchCounter then launch when full and base conditions are met
|
||||
if((launchCounter += edelta()) >= launchTime && items.total() >= itemCapacity){
|
||||
if((launchCounter += edelta()) >= launchTime && edelta() >= 0.001f && items.total() >= itemCapacity){
|
||||
launchSound.at(x, y);
|
||||
LaunchPayload entity = LaunchPayload.create();
|
||||
items.each((item, amount) -> entity.stacks.add(new ItemStack(item, amount)));
|
||||
|
||||
@@ -38,8 +38,8 @@ public class LaserTurret extends PowerTurret{
|
||||
}
|
||||
|
||||
public class LaserTurretBuild extends PowerTurretBuild{
|
||||
Bullet bullet;
|
||||
float bulletLife;
|
||||
public Bullet bullet;
|
||||
public float bulletLife;
|
||||
|
||||
@Override
|
||||
protected void updateCooling(){
|
||||
|
||||
@@ -43,6 +43,12 @@ public class DuctBridge extends Block{
|
||||
drawArrow = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
clipSize = Math.max(clipSize, (range + 0.5f) * 2 * tilesize);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
|
||||
@@ -113,7 +113,7 @@ public class PayloadConveyor extends Block{
|
||||
|
||||
int ntrns = 1 + size/2;
|
||||
Tile next = tile.nearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns);
|
||||
blocked = (next != null && next.solid() && !next.block().outputsPayload) || (this.next != null && (this.next.rotation + 2)%4 == rotation);
|
||||
blocked = (next != null && next.solid() && !(next.block().outputsPayload || next.block().acceptsPayload)) || (this.next != null && this.next.block.rotate && (this.next.rotation + 2) % 4 == rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,11 +23,9 @@ import mindustry.world.modules.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PowerNode extends PowerBlock{
|
||||
protected static boolean returnValue = false;
|
||||
protected static BuildPlan otherReq;
|
||||
|
||||
protected final static ObjectSet<PowerGraph> graphs = new ObjectSet<>();
|
||||
protected static int returnInt = 0;
|
||||
protected final static ObjectSet<PowerGraph> graphs = new ObjectSet<>();
|
||||
|
||||
public @Load("laser") TextureRegion laser;
|
||||
public @Load("laser-end") TextureRegion laserEnd;
|
||||
@@ -134,6 +132,13 @@ public class PowerNode extends PowerBlock{
|
||||
stats.add(Stat.powerConnections, maxNodes, StatUnit.none);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
clipSize = Math.max(clipSize, laserRange * tilesize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SolidPump extends Pump{
|
||||
bars.add("efficiency", (SolidPumpBuild entity) -> new Bar(() -> Core.bundle.formatFloat("bar.pumpspeed",
|
||||
entity.lastPump / Time.delta * 60, 1),
|
||||
() -> Pal.ammo,
|
||||
() -> entity.warmup));
|
||||
() -> entity.warmup * entity.efficiency()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,7 +116,7 @@ public class SolidPump extends Pump{
|
||||
lastPump = 0f;
|
||||
}
|
||||
|
||||
pumpTime += warmup * delta();
|
||||
pumpTime += warmup * edelta();
|
||||
|
||||
dumpLiquid(result);
|
||||
}
|
||||
|
||||
@@ -143,10 +143,15 @@ public class UnitFactory extends UnitBlock{
|
||||
@Override
|
||||
public Object senseObject(LAccess sensor){
|
||||
if(sensor == LAccess.config) return currentPlan == -1 ? null : plans.get(currentPlan).unit;
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(fraction());
|
||||
return super.senseObject(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(fraction());
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
Seq<UnitType> units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow() && !u.isBanned());
|
||||
|
||||
@@ -45,6 +45,6 @@ public class DrawAnimation extends DrawBlock{
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(Block block){
|
||||
return new TextureRegion[]{block.region, top};
|
||||
return top.found() ? new TextureRegion[]{block.region, top} : new TextureRegion[]{block.region};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user