Merge remote-tracking branch 'upstream/master'

This commit is contained in:
LeoDog896
2020-09-30 08:43:08 -04:00
85 changed files with 8552 additions and 8104 deletions

View File

@@ -45,7 +45,7 @@ public class GroundAI extends AIController{
}
}
if(unit.type().canBoost && unit.canPassOn()){
if(unit.type().canBoost && unit.tileOn() != null && !unit.tileOn().solid()){
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
}

View File

@@ -74,7 +74,7 @@ public class Blocks implements ContentList{
coreShard, coreFoundation, coreNucleus, vault, container, unloader,
//turrets
duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown, segment, parallax,
duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, foreshadow, spectre, meltdown, segment, parallax,
//units
commandCenter,
@@ -209,6 +209,10 @@ public class Blocks implements ContentList{
isLiquid = true;
cacheLayer = CacheLayer.slag;
attributes.set(Attribute.heat, 0.85f);
emitLight = true;
lightRadius = 40f;
lightColor = Color.orange.cpy().a(0.38f);
}};
stone = new Floor("stone");
@@ -243,7 +247,7 @@ public class Blocks implements ContentList{
blendGroup = basalt;
emitLight = true;
lightRadius = 60f;
lightRadius = 50f;
lightColor = Color.orange.cpy().a(0.3f);
}};
@@ -866,7 +870,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();
}};
@@ -1577,7 +1582,7 @@ public class Blocks implements ContentList{
health = 250 * size * size;
range = 140f;
hasPower = true;
consumes.power(8f);
consumes.powerCond(8f, (PointDefenseBuild b) -> b.target != null);
size = 2;
shootLength = 5f;
bulletDamage = 25f;
@@ -1660,6 +1665,48 @@ public class Blocks implements ContentList{
health = 145 * size * size;
}};
foreshadow = new ItemTurret("foreshadow"){{
float brange = range = 500f;
requirements(Category.turret, with(Items.copper, 1000, Items.metaglass, 600, Items.surgealloy, 300, Items.plastanium, 175, Items.thorium, 350));
ammo(
Items.surgealloy, new PointBulletType(){{
shootEffect = Fx.instShoot;
hitEffect = Fx.instHit;
smokeEffect = Fx.smokeCloud;
trailEffect = Fx.instTrail;
despawnEffect = Fx.instBomb;
trailSpacing = 20f;
damage = 1300;
tileDamageMultiplier = 0.5f;
speed = brange;
hitShake = 6f;
ammoMultiplier = 1f;
}}
);
rotateSpeed = 2.5f;
reloadTime = 200f;
restitution = 0.2f;
ammoUseEffect = Fx.shellEjectBig;
recoilAmount = 5f;
restitution = 0.009f;
cooldown = 0.009f;
shootShake = 4f;
shots = 1;
size = 4;
shootCone = 2f;
shootSound = Sounds.shootBig;
unitSort = (u, x, y) -> -u.maxHealth;
coolantMultiplier = 0.09f;
health = 150 * size * size;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 2f)).update(false).optional(true, true);
consumes.powerCond(10f, (TurretBuild entity) -> entity.target != null || (entity.logicControlled() && entity.logicShooting));
}};
spectre = new ItemTurret("spectre"){{
requirements(Category.turret, with(Items.copper, 900, Items.graphite, 300, Items.surgealloy, 250, Items.plastanium, 175, Items.thorium, 250));
ammo(
@@ -1682,7 +1729,7 @@ public class Blocks implements ContentList{
shootCone = 24f;
shootSound = Sounds.shootBig;
health = 155 * size * size;
health = 160 * size * size;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 2f)).update(false).optional(true, true);
}};

View File

@@ -455,6 +455,79 @@ public class Fx{
}),
instBomb = new Effect(15f, 100f, e -> {
color(Pal.bulletYellowBack);
stroke(e.fout() * 4f);
Lines.circle(e.x, e.y, 4f + e.finpow() * 20f);
for(int i = 0; i < 4; i++){
Drawf.tri(e.x, e.y, 6f, 80f * e.fout(), i*90 + 45);
}
color();
for(int i = 0; i < 4; i++){
Drawf.tri(e.x, e.y, 3f, 30f * e.fout(), i*90 + 45);
}
}),
instTrail = new Effect(30, e -> {
for(int i = 0; i < 2; i++){
color(i == 0 ? Pal.bulletYellowBack : Pal.bulletYellow);
float m = i == 0 ? 1f : 0.5f;
float rot = e.rotation + 180f;
float w = 15f * e.fout() * m;
Drawf.tri(e.x, e.y, w, (30f + Mathf.randomSeedRange(e.id, 15f)) * m, rot);
Drawf.tri(e.x, e.y, w, 10f * m, rot + 180f);
}
}),
instShoot = new Effect(24f, e -> {
e.scaled(10f, b -> {
color(Color.white, Pal.bulletYellowBack, b.fin());
stroke(b.fout() * 3f + 0.2f);
Lines.circle(b.x, b.y, b.fin() * 50f);
});
color(Pal.bulletYellowBack);
for(int i : Mathf.signs){
Drawf.tri(e.x, e.y, 13f * e.fout(), 85f, e.rotation + 90f * i);
Drawf.tri(e.x, e.y, 13f * e.fout(), 50f, e.rotation + 20f * i);
}
}),
instHit = new Effect(20f, 200f, e -> {
color(Pal.bulletYellowBack);
for(int i = 0; i < 2; i++){
color(i == 0 ? Pal.bulletYellowBack : Pal.bulletYellow);
float m = i == 0 ? 1f : 0.5f;
for(int j = 0; j < 5; j++){
float rot = e.rotation + Mathf.randomSeedRange(e.id + j, 50f);
float w = 23f * e.fout() * m;
Drawf.tri(e.x, e.y, w, (80f + Mathf.randomSeedRange(e.id + j, 40f)) * m, rot);
Drawf.tri(e.x, e.y, w, 20f * m, rot + 180f);
}
}
e.scaled(10f, c -> {
color(Pal.bulletYellow);
stroke(c.fout() * 2f + 0.2f);
Lines.circle(e.x, e.y, c.fin() * 30f);
});
e.scaled(12f, c -> {
color(Pal.bulletYellowBack);
randLenVectors(e.id, 25, 5f + e.fin() * 80f, e.rotation, 60f, (x, y) -> {
Fill.square(e.x + x, e.y + y, c.fout() * 3f, 45f);
});
});
}),
hitLaser = new Effect(8, e -> {
color(Color.white, Pal.heal, e.fin());
stroke(0.5f + e.fout());
@@ -1074,28 +1147,11 @@ public class Fx{
}),
railHit = new Effect(18f, 200f, e -> {
if(true){
color(Pal.orangeSpark);
color(Pal.orangeSpark);
for(int i : Mathf.signs){
Drawf.tri(e.x, e.y, 10f * e.fout(), 60f, e.rotation + 140f * i);
}
}else{
e.scaled(7f, b -> {
color(Color.white, Color.lightGray, b.fin());
stroke(b.fout() * 2f + 0.2f);
Lines.circle(b.x, b.y, b.fin() * 28f);
});
color(Pal.orangeSpark);
float rot = e.rotation + Mathf.randomSeedRange(e.id, 20f);
float w = 9f * e.fout();
Drawf.tri(e.x, e.y, w, 100f, rot);
Drawf.tri(e.x, e.y, w, 10f, rot + 180f);
for(int i : Mathf.signs){
Drawf.tri(e.x, e.y, 10f * e.fout(), 60f, e.rotation + 140f * i);
}
}),
lancerLaserShoot = new Effect(21f, e -> {
@@ -1109,7 +1165,7 @@ public class Fx{
lancerLaserShootSmoke = new Effect(26f, e -> {
color(Color.white);
float length = e.data == null || !(e.data instanceof Float) ? 70f : (Float)e.data;
float length = !(e.data instanceof Float) ? 70f : (Float)e.data;
randLenVectors(e.id, 7, length, e.rotation, 0f, (x, y) -> {
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fout() * 9f);

View File

@@ -109,6 +109,9 @@ public class TechTree implements ContentList{
node(Items.coal, with(Items.lead, 3000), () -> {
node(Items.graphite, with(Items.coal, 1000), () -> {
node(illuminator, () -> {
});
node(graphitePress, () -> {
node(Items.titanium, with(Items.graphite, 6000, Items.copper, 10000, Items.lead, 10000), () -> {
node(pneumaticDrill, () -> {
@@ -347,8 +350,10 @@ public class TechTree implements ContentList{
});
node(lancer, () -> {
node(meltdown, () -> {
node(foreshadow, () -> {
node(meltdown, () -> {
});
});
node(shockMine, () -> {

View File

@@ -419,7 +419,7 @@ public class UnitTypes implements ContentList{
continuous = true;
cooldownTime = 200f;
bullet = new ContinuousLaserBulletType(17){{
bullet = new ContinuousLaserBulletType(20){{
length = 150f;
hitEffect = Fx.hitMeltHeal;
drawSize = 420f;
@@ -430,7 +430,7 @@ public class UnitTypes implements ContentList{
shootEffect = Fx.greenLaserChargeSmall;
incendChance = 0.02f;
incendChance = 0.05f;
incendSpread = 5f;
incendAmount = 1;
@@ -446,7 +446,7 @@ public class UnitTypes implements ContentList{
mineTier = 1;
hitSize = 29f;
itemCapacity = 80;
health = 19000f;
health = 18000f;
buildSpeed = 1.7f;
armor = 9f;
landShake = 1.5f;
@@ -488,8 +488,8 @@ public class UnitTypes implements ContentList{
firstShotDelay = Fx.greenLaserCharge.lifetime;
bullet = new LaserBulletType(){{
length = 500f;
damage = 520f;
length = 460f;
damage = 550f;
width = 75f;
lifetime = 65f;

View File

@@ -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

@@ -112,7 +112,7 @@ public class EditorTile extends Tile{
Block block = block();
if(block.hasEntity()){
if(block.hasBuilding()){
build = entityprov.get().init(this, team, false, rotation);
build.cons = new ConsumeModule(build);
if(block.hasItems) build.items = new ItemModule();

View File

@@ -417,7 +417,7 @@ public class MapGenerateDialog extends BaseDialog{
public void set(Block floor, Block wall, Block ore, Team team){
this.floor = floor.id;
this.block = wall.id;
this.ore = ore.id;
this.ore = floor.asFloor().isLiquid ? 0 : ore.id;
this.team = (byte)team.id;
}

View File

@@ -175,6 +175,18 @@ public class Units{
}
}
/** Returns the closest target enemy. First, units are checked, then tile entities. */
public static Teamc bestTarget(Team team, float x, float y, float range, Boolf<Unit> unitPred, Boolf<Building> tilePred, Sortf sort){
if(team == Team.derelict) return null;
Unit unit = bestEnemy(team, x, y, range, unitPred, sort);
if(unit != null){
return unit;
}else{
return findEnemyTile(team, x, y, range, tilePred);
}
}
/** Returns the closest enemy of this team. Filter by predicate. */
public static Unit closestEnemy(Team team, float x, float y, float range, Boolf<Unit> predicate){
if(team == Team.derelict) return null;
@@ -195,6 +207,26 @@ public class Units{
return result;
}
/** Returns the closest enemy of this team using a custom comparison function. Filter by predicate. */
public static Unit bestEnemy(Team team, float x, float y, float range, Boolf<Unit> predicate, Sortf sort){
if(team == Team.derelict) return null;
result = null;
cdist = 0f;
nearbyEnemies(team, x - range, y - range, range*2f, range*2f, e -> {
if(e.dead() || !predicate.get(e)) return;
float dst2 = sort.cost(e, x, y);
if(dst2 < range*range && (result == null || dst2 < cdist)){
result = e;
cdist = dst2;
}
});
return result;
}
/** Returns the closest ally of this team. Filter by predicate. No range. */
public static Unit closest(Team team, float x, float y, Boolf<Unit> predicate){
result = null;
@@ -297,4 +329,7 @@ public class Units{
nearbyEnemies(team, rect.x, rect.y, rect.width, rect.height, cons);
}
public interface Sortf{
float cost(Unit unit, float x, float y);
}
}

View File

@@ -71,6 +71,9 @@ public abstract class BulletType extends Content{
public boolean hittable = true;
/** Whether this bullet can be reflected. */
public boolean reflectable = true;
/** Whether to move the bullet back depending on delta to fix some delta-time realted issues.
* Do not change unless you know what you're doing. */
public boolean backMove = true;
/** Bullet range override. */
public float range = -1f;
@@ -212,7 +215,7 @@ public abstract class BulletType extends Content{
}
if(instantDisappear){
b.time(lifetime);
b.time = lifetime;
}
}
@@ -274,7 +277,11 @@ public abstract class BulletType extends Content{
bullet.owner = owner;
bullet.team = team;
bullet.vel.trns(angle, speed * velocityScl);
bullet.set(x - bullet.vel.x * Time.delta, y - bullet.vel.y * Time.delta);
if(backMove){
bullet.set(x - bullet.vel.x * Time.delta, y - bullet.vel.y * Time.delta);
}else{
bullet.set(x, y);
}
bullet.lifetime = lifetime * lifetimeScl;
bullet.data = data;
bullet.drag = drag;

View File

@@ -0,0 +1,70 @@
package mindustry.entities.bullet;
import arc.math.geom.*;
import arc.util.*;
import mindustry.*;
import mindustry.entities.*;
import mindustry.gen.*;
public class PointBulletType extends BulletType{
private static float cdist = 0f;
private static Unit result;
public float trailSpacing = 10f;
public PointBulletType(){
scaleVelocity = true;
lifetime = 100f;
collides = false;
keepVelocity = false;
backMove = false;
}
@Override
public void init(Bullet b){
super.init(b);
float px = b.x + b.lifetime * b.vel.x,
py = b.y + b.lifetime * b.vel.y,
rot = b.rotation();
Geometry.iterateLine(0f, b.x, b.y, px, py, trailSpacing, (x, y) -> {
trailEffect.at(x, y, rot);
});
b.time = b.lifetime;
b.set(px, py);
//calculate hit entity
cdist = 0f;
result = null;
float range = 1f;
Units.nearbyEnemies(b.team, px - range, py - range, range*2f, range*2f, e -> {
if(e.dead()) return;
e.hitbox(Tmp.r1);
if(!Tmp.r1.contains(px, py)) return;
float dst = e.dst(px, py) - e.hitSize;
if((result == null || dst < cdist)){
result = e;
cdist = dst;
}
});
if(result != null){
b.collision(result, px, py);
}else{
Building build = Vars.world.buildWorld(px, py);
if(build != null && build.team != b.team){
build.collision(b);
}
}
b.remove();
b.vel.setZero();
}
}

View File

@@ -1213,31 +1213,35 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
@Override
public double sense(LAccess sensor){
if(sensor == LAccess.x) return x;
if(sensor == LAccess.y) return y;
if(sensor == LAccess.team) return team.id;
if(sensor == LAccess.health) return health;
if(sensor == LAccess.maxHealth) return maxHealth();
if(sensor == LAccess.efficiency) return efficiency();
if(sensor == LAccess.rotation) return rotation;
if(sensor == LAccess.totalItems && items != null) return items.total();
if(sensor == LAccess.totalLiquids && liquids != null) return liquids.total();
if(sensor == LAccess.totalPower && power != null && block.consumes.hasPower()) return power.status * (block.consumes.getPower().buffered ? block.consumes.getPower().capacity : 1f);
if(sensor == LAccess.itemCapacity) return block.itemCapacity;
if(sensor == LAccess.liquidCapacity) return block.liquidCapacity;
if(sensor == LAccess.powerCapacity) return block.consumes.hasPower() ? block.consumes.getPower().capacity : 0f;
if(sensor == LAccess.powerNetIn && power != null) return power.graph.getLastScaledPowerIn() * 60;
if(sensor == LAccess.powerNetOut && power != null) return power.graph.getLastScaledPowerOut() * 60;
if(sensor == LAccess.powerNetStored && power != null) return power.graph.getLastPowerStored();
if(sensor == LAccess.powerNetCapacity && power != null) return power.graph.getLastCapacity();
return 0;
return switch(sensor){
case x -> x;
case y -> y;
case team -> team.id;
case health -> health;
case maxHealth -> maxHealth();
case efficiency -> efficiency();
case rotation -> rotation;
case totalItems -> items == null ? 0 : items.total();
case totalLiquids -> liquids == null ? 0 : liquids.total();
case totalPower -> power == null || !block.consumes.hasPower() ? 0 : power.status * (block.consumes.getPower().buffered ? block.consumes.getPower().capacity : 1f);
case itemCapacity -> block.itemCapacity;
case liquidCapacity -> block.liquidCapacity;
case powerCapacity -> block.consumes.hasPower() ? block.consumes.getPower().capacity : 0f;
case powerNetIn -> power == null ? 0 : power.graph.getLastScaledPowerIn() * 60;
case powerNetOut -> power == null ? 0 : power.graph.getLastScaledPowerOut() * 60;
case powerNetStored -> power == null ? 0 : power.graph.getLastPowerStored();
case powerNetCapacity -> power == null ? 0 : power.graph.getLastCapacity();
default -> 0;
};
}
@Override
public Object senseObject(LAccess sensor){
if(sensor == LAccess.type) return block;
return switch(sensor){
case type -> block;
default -> noSensed;
};
return noSensed;
}
@Override

View File

@@ -25,7 +25,7 @@ abstract class MechComp implements Posc, Flyingc, Hitboxc, Unitc, Mechc, Elevati
@Override
public void update(){
//trigger animation only when walking manually
if(walked){
if(walked || net.client()){
float len = deltaLen();
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed / Time.delta) * Time.delta);
walkTime += len;

View File

@@ -122,7 +122,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
Building tile = payload.entity;
int tx = Vars.world.toTile(x - tile.block.offset), ty = Vars.world.toTile(y - tile.block.offset);
Tile on = Vars.world.tile(tx, ty);
if(on != null && Build.validPlace(tile.block, tile.team, tx, ty, tile.rotation)){
if(on != null && Build.validPlace(tile.block, tile.team, tx, ty, tile.rotation, false)){
int rot = (int)((rotation + 45f) / 90f) % 4;
payload.place(on, rot);

View File

@@ -75,24 +75,28 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Override
public double sense(LAccess sensor){
if(sensor == LAccess.totalItems) return stack().amount;
if(sensor == LAccess.rotation) return rotation;
if(sensor == LAccess.health) return health;
if(sensor == LAccess.maxHealth) return maxHealth;
if(sensor == LAccess.x) return x;
if(sensor == LAccess.y) return y;
if(sensor == LAccess.team) return team.id;
if(sensor == LAccess.shooting) return isShooting() ? 1 : 0;
if(sensor == LAccess.shootX) return aimX();
if(sensor == LAccess.shootY) return aimY();
return 0;
return switch(sensor){
case totalItems -> stack().amount;
case rotation -> rotation;
case health -> health;
case maxHealth -> maxHealth;
case x -> x;
case y -> y;
case team -> team.id;
case shooting -> isShooting() ? 1 : 0;
case shootX -> aimX();
case shootY -> aimY();
default -> 0;
};
}
@Override
public Object senseObject(LAccess sensor){
if(sensor == LAccess.type) return type;
return switch(sensor){
case type -> type;
default -> noSensed;
};
return noSensed;
}
@Override
@@ -417,7 +421,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
dead = true;
//don't waste time when the unit is already on the ground, just destroy it
if(isGrounded()){
if(!type.flying){
destroy();
}
}

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,6 +1,8 @@
package mindustry.game;
import arc.math.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.type.*;
@@ -197,7 +199,8 @@ public class DefaultWaves{
return spawns == null ? new Seq<>() : spawns;
}
private static void generate(){
//TODO move elsewhere
public static Seq<SpawnGroup> generate(){
UnitType[][] species = {
{dagger, mace, fortress, scepter, reign},
{nova, pulsar, quasar, vela, corvus},
@@ -208,16 +211,63 @@ public class DefaultWaves{
};
//required progression:
//- main progression of units, up to wave ~20
//- changes from
//- extra periodic patterns
Seq<SpawnGroup> out = new Seq<>();
//max reasonable wave, after which everything gets boring
int cap = 300;
for(int i = 0; i < cap; i++){
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

@@ -35,7 +35,7 @@ public class BlockRenderer implements Disposable{
private FrameBuffer dark = new FrameBuffer();
private Seq<Building> outArray2 = new Seq<>();
private Seq<Tile> shadowEvents = new Seq<>();
private IntSet processedEntities = new IntSet();
private IntSet processedEntities = new IntSet(), processedLinks = new IntSet();
private boolean displayStatus = false;
public BlockRenderer(){
@@ -180,6 +180,7 @@ public class BlockRenderer implements Disposable{
tileview.clear();
lightview.clear();
processedEntities.clear();
processedLinks.clear();
int minx = Math.max(avgx - rangex - expandr, 0);
int miny = Math.max(avgy - rangey - expandr, 0);
@@ -196,10 +197,15 @@ public class BlockRenderer implements Disposable{
tile = tile.build.tile;
}
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !processedEntities.contains(tile.build.id()))){
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !processedEntities.contains(tile.build.id))){
if(block.expanded || !expanded){
tileview.add(tile);
if(tile.build != null) processedEntities.add(tile.build.id());
if(tile.build == null || processedLinks.add(tile.build.id)){
tileview.add(tile);
if(tile.build != null){
processedEntities.add(tile.build.id);
processedLinks.add(tile.build.id);
}
}
}
//lights are drawn even in the expanded range
@@ -209,7 +215,7 @@ public class BlockRenderer implements Disposable{
if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){
for(Building other : tile.build.getPowerConnections(outArray2)){
if(other.block instanceof PowerNode){ //TODO need a generic way to render connections!
if(other.block instanceof PowerNode && processedLinks.add(other.id)){ //TODO need a generic way to render connections!
tileview.add(other.tile);
}
}

View File

@@ -20,7 +20,7 @@ public class FloorRenderer implements Disposable{
//TODO find out number with best performance
private static final int chunksize = mobile ? 16 : 32;
private Chunk[][] cache;
private int[][][] cache;
private MultiCacheBatch cbatch;
private IntSet drawnLayerSet = new IntSet();
private IntSet recacheSet = new IntSet();
@@ -63,11 +63,11 @@ public class FloorRenderer implements Disposable{
if(!Structs.inBounds(worldx, worldy, cache))
continue;
Chunk chunk = cache[worldx][worldy];
int[] chunk = cache[worldx][worldy];
//loop through all layers, and add layer index if it exists
for(int i = 0; i < layers; i++){
if(chunk.caches[i] != -1 && i != CacheLayer.walls.ordinal()){
if(chunk[i] != -1 && i != CacheLayer.walls.ordinal()){
drawnLayerSet.add(i);
}
}
@@ -154,9 +154,9 @@ public class FloorRenderer implements Disposable{
continue;
}
Chunk chunk = cache[worldx][worldy];
if(chunk.caches[layer.ordinal()] == -1) continue;
cbatch.drawCache(chunk.caches[layer.ordinal()]);
int[] chunk = cache[worldx][worldy];
if(chunk[layer.ordinal()] == -1) continue;
cbatch.drawCache(chunk[layer.ordinal()]);
}
}
@@ -165,7 +165,7 @@ public class FloorRenderer implements Disposable{
private void cacheChunk(int cx, int cy){
used.clear();
Chunk chunk = cache[cx][cy];
int[] chunk = cache[cx][cy];
for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize && tilex < world.width(); tilex++){
for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize && tiley < world.height(); tiley++){
@@ -184,15 +184,15 @@ public class FloorRenderer implements Disposable{
}
}
private void cacheChunkLayer(int cx, int cy, Chunk chunk, CacheLayer layer){
private void cacheChunkLayer(int cx, int cy, int[] chunk, CacheLayer layer){
Batch current = Core.batch;
Core.batch = cbatch;
//begin a new cache
if(chunk.caches[layer.ordinal()] == -1){
if(chunk[layer.ordinal()] == -1){
cbatch.beginCache();
}else{
cbatch.beginCache(chunk.caches[layer.ordinal()]);
cbatch.beginCache(chunk[layer.ordinal()]);
}
for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize; tilex++){
@@ -218,7 +218,7 @@ public class FloorRenderer implements Disposable{
Core.batch = current;
cbatch.reserve(layer.capacity * chunksize * chunksize);
chunk.caches[layer.ordinal()] = cbatch.endCache();
chunk[layer.ordinal()] = cbatch.endCache();
}
public void clearTiles(){
@@ -227,15 +227,14 @@ public class FloorRenderer implements Disposable{
recacheSet.clear();
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 * 8);
cache = new int[chunksx][chunksy][CacheLayer.all.length];
cbatch = new MultiCacheBatch(chunksize * chunksize * 9);
Time.mark();
for(int x = 0; x < chunksx; x++){
for(int y = 0; y < chunksy; y++){
cache[x][y] = new Chunk();
Arrays.fill(cache[x][y].caches, -1);
Arrays.fill(cache[x][y], -1);
cacheChunk(x, y);
}
@@ -251,13 +250,4 @@ public class FloorRenderer implements Disposable{
cbatch = null;
}
}
private static class Chunk{
/** Maps cache layer ID to cache ID in the batch.
* -1 means that this cache is unoccupied. */
int[] caches = new int[CacheLayer.all.length];
Chunk(){
}
}
}

View File

@@ -257,7 +257,7 @@ public abstract class SaveVersion extends SaveFileReader{
if(hadEntity){
if(isCenter){ //only read entity for center blocks
if(block.hasEntity()){
if(block.hasBuilding()){
try{
readChunk(stream, true, in -> {
byte revision = in.readByte();

View File

@@ -61,7 +61,7 @@ public abstract class LegacySaveVersion extends SaveVersion{
tile.setBlock(block);
}
if(block.hasEntity()){
if(block.hasBuilding()){
try{
readChunk(stream, true, in -> {
byte version = in.readByte();

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

@@ -25,15 +25,15 @@ import static mindustry.Vars.net;
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 " + Vars.reportIssueURL + "\n\n";
String report = "Mindustry has crashed. How unforunate.\n";
if(mods.list().size == 0 && Version.build != -1){
report += "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().isEmpty() ? "none" : mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version))
+ mods.list().size + " Mods" + (mods.list().isEmpty() ? "" : ": " + mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version))
+ "\n\n" + error;
}

View File

@@ -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;
@@ -218,6 +219,10 @@ public class UnitType extends UnlockableContent{
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;

View File

@@ -17,7 +17,7 @@ import java.util.*;
public class FileChooser extends BaseDialog{
private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
static Fi lastDirectory = homeDirectory;
static Fi lastDirectory = Core.files.absolute(Core.settings.getString("lastDirectory", homeDirectory.absolutePath()));
private Table files;
Fi directory = lastDirectory;
@@ -108,7 +108,7 @@ public class FileChooser extends BaseDialog{
ImageButton home = new ImageButton(Icon.home);
home.clicked(() -> {
directory = homeDirectory;
lastDirectory = directory;
setLastDirectory(directory);
updateFiles(true);
});
@@ -187,7 +187,7 @@ public class FileChooser extends BaseDialog{
TextButton upbutton = new TextButton(".." + directory.toString(), Styles.clearTogglet);
upbutton.clicked(() -> {
directory = directory.parent();
lastDirectory = directory;
setLastDirectory(directory);
updateFiles(true);
});
@@ -217,7 +217,7 @@ public class FileChooser extends BaseDialog{
updateFileFieldStatus();
}else{
directory = directory.child(filename);
lastDirectory = directory;
setLastDirectory(directory);
updateFiles(true);
}
});
@@ -242,6 +242,11 @@ public class FileChooser extends BaseDialog{
if(open) filefield.clearText();
}
public static void setLastDirectory(Fi directory){
lastDirectory = directory;
Core.settings.put("lastDirectory", directory.absolutePath());
}
private String shorten(String string){
int max = 30;
if(string.length() <= max){
@@ -269,14 +274,14 @@ public class FileChooser extends BaseDialog{
if(!canBack()) return;
index--;
directory = history.get(index - 1);
lastDirectory = directory;
setLastDirectory(directory);
updateFiles(false);
}
public void forward(){
if(!canForward()) return;
directory = history.get(index);
lastDirectory = directory;
setLastDirectory(directory);
index++;
updateFiles(false);
}

View File

@@ -703,7 +703,7 @@ public class HudFragment extends Fragment{
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
b.color.set(player.displayAmmo() ? player.dead() ? Pal.ammo : player.unit().type().ammoType.color : Pal.health);
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type().ammoType.color : Pal.health);
});
t.getChildren().get(1).toFront();

View File

@@ -209,6 +209,7 @@ public class Block extends UnlockableContent{
public @Load("@-team") TextureRegion teamRegion;
public TextureRegion[] teamRegions;
//TODO make this not static
public static TextureRegion[][] cracks;
protected static final Seq<Tile> tempTiles = new Seq<>();
protected static final Seq<Building> tempTileEnts = new Seq<>();
@@ -499,7 +500,7 @@ public class Block extends UnlockableContent{
return variantRegions;
}
public boolean hasEntity(){
public boolean hasBuilding(){
return destructible || update;
}

View File

@@ -68,8 +68,13 @@ public class Build{
/** Returns whether a tile can be placed at this location by this team. */
public static boolean validPlace(Block type, Team team, int x, int y, int rotation){
return validPlace(type, team, x, y, rotation, true);
}
/** Returns whether a tile can be placed at this location by this team. */
public static boolean validPlace(Block type, Team team, int x, int y, int rotation, boolean checkVisible){
//the wave team can build whatever they want as long as it's visible - banned blocks are not applicable
if(type == null || (!type.isPlaceable() && !(state.rules.waves && team == state.rules.waveTeam && type.isVisible()))){
if(type == null || (checkVisible && (!type.isPlaceable() && !(state.rules.waves && team == state.rules.waveTeam && type.isVisible())))){
return false;
}

View File

@@ -26,7 +26,7 @@ public class CachedTile extends Tile{
Block block = block();
if(block.hasEntity()){
if(block.hasBuilding()){
Building n = entityprov.get();
n.cons(new ConsumeModule(build));
n.tile(this);

View File

@@ -508,7 +508,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
}
}
if(block.hasEntity()){
if(block.hasBuilding()){
build = entityprov.get().init(this, team, block.update && !state.isEditor(), rotation);
}
}

View File

@@ -129,6 +129,11 @@ public class LaunchPad extends Block{
@Override
public void buildConfiguration(Table table){
if(!state.isCampaign()){
deselect();
return;
}
table.button(Icon.upOpen, Styles.clearTransi, () -> {
ui.planet.showSelect(state.rules.sector, other -> state.secinfo.destination = other);
deselect();

View File

@@ -14,6 +14,7 @@ import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.Units.*;
import mindustry.entities.bullet.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
@@ -67,6 +68,7 @@ public abstract class Turret extends Block{
public float coolantMultiplier = 5f;
/** Effect displayed when coolant is used. */
public Effect coolEffect = Fx.fuelburn;
public Sortf unitSort = Unit::dst2;
protected Vec2 tr = new Vec2();
protected Vec2 tr2 = new Vec2();
@@ -306,9 +308,9 @@ public abstract class Turret extends Block{
protected void findTarget(){
if(targetAir && !targetGround){
target = Units.closestEnemy(team, x, y, range, e -> !e.dead() && !e.isGrounded());
target = Units.bestEnemy(team, x, y, range, e -> !e.dead() && !e.isGrounded(), unitSort);
}else{
target = Units.closestTarget(team, x, y, range, e -> !e.dead() && (e.isGrounded() || targetAir) && (!e.isGrounded() || targetGround));
target = Units.bestTarget(team, x, y, range, e -> !e.dead() && (e.isGrounded() || targetAir) && (!e.isGrounded() || targetGround), b -> true, unitSort);
}
}
@@ -397,7 +399,7 @@ public abstract class Turret extends Block{
}
protected void bullet(BulletType type, float angle){
float lifeScl = type.scaleVelocity ? Mathf.clamp(Mathf.dst(x, y, targetPos.x, targetPos.y) / type.range(), minRange / type.range(), range / type.range()) : 1f;
float lifeScl = type.scaleVelocity ? Mathf.clamp(Mathf.dst(x + tr.x, y + tr.y, targetPos.x, targetPos.y) / type.range(), minRange / type.range(), range / type.range()) : 1f;
type.create(this, team, x + tr.x, y + tr.y, angle, 1f + Mathf.range(velocityInaccuracy), lifeScl);
}

View File

@@ -37,7 +37,6 @@ public class PowerNode extends PowerBlock{
public PowerNode(String name){
super(name);
expanded = true;
configurable = true;
consumesPower = false;
outputsPower = false;
@@ -392,7 +391,7 @@ public class PowerNode extends PowerBlock{
if(!linkValid(this, link)) continue;
if(link.block instanceof PowerNode && !(link.pos() < tile.pos())) continue;
if(link.block instanceof PowerNode && link.id >= id) continue;
drawLaser(team, x, y, link.x, link.y, size, link.block.size);
}

View File

@@ -101,7 +101,7 @@ public class CoreBlock extends StorageBlock{
CoreBuild core = team.core();
//must have all requirements
if(core == null || (!state.rules.infiniteResources && !core.items.has(requirements))) return false;
return canReplace(tile.block());
return tile.block() instanceof CoreBlock && size > tile.block().size;
}
@Override