Merge branch 'master' into balancing-payload_system
This commit is contained in:
@@ -51,6 +51,8 @@ public class Vars implements Loadable{
|
||||
public static final int minModGameVersion = 136;
|
||||
/** Min game version for java mods specifically - this is higher, as Java mods have more breaking changes. */
|
||||
public static final int minJavaModGameVersion = 147;
|
||||
/** If true, a button to view sector submission threads is shown. */
|
||||
public static boolean showSectorSubmissions = true;
|
||||
/** If true, the BE server list is always used. */
|
||||
public static boolean forceBeServers = false;
|
||||
/** If true, mod code and scripts do not run. For internal testing only. This WILL break mods if enabled. */
|
||||
|
||||
@@ -540,7 +540,7 @@ public class Pathfinder implements Runnable{
|
||||
if(!targets.isEmpty()){
|
||||
boolean any = false;
|
||||
for(Building other : targets){
|
||||
if((other.items != null && other.items.any()) || other.status() != BlockStatus.noInput){
|
||||
if(((other.items != null && other.items.any()) || other.status() != BlockStatus.noInput) && other.block.targetable){
|
||||
out.add(other.tile.array());
|
||||
any = true;
|
||||
}
|
||||
|
||||
@@ -141,6 +141,8 @@ public class RtsAI{
|
||||
boolean handleSquad(Seq<Unit> units, boolean noDefenders){
|
||||
if(units.isEmpty()) return false;
|
||||
|
||||
boolean naval = units.first() instanceof WaterMovec;
|
||||
|
||||
float health = 0f, dps = 0f;
|
||||
float ax = 0f, ay = 0f;
|
||||
boolean targetAir = true, targetGround = true;
|
||||
@@ -165,7 +167,7 @@ public class RtsAI{
|
||||
boolean defendingCore = false;
|
||||
|
||||
//there is something to defend, see if it's worth the time
|
||||
if(damaged.size > 0){
|
||||
if(damaged.size > 0 && !naval){
|
||||
//TODO do the weights matter at all?
|
||||
//for(var build : damaged){
|
||||
//float w = estimateStats(ax, ay, dps, health);
|
||||
@@ -251,7 +253,7 @@ public class RtsAI{
|
||||
}
|
||||
}
|
||||
|
||||
var build = anyDefend ? null : findTarget(ax, ay, units.size, dps, health, units.first().flag == 0, units.first().isFlying());
|
||||
var build = anyDefend ? null : findTarget(ax, ay, units.size, dps, health, units.first().flag == 0, units.first().isFlying(), naval);
|
||||
|
||||
if(build != null || anyDefend){
|
||||
for(var unit : units){
|
||||
@@ -274,7 +276,7 @@ public class RtsAI{
|
||||
return anyDefend;
|
||||
}
|
||||
|
||||
@Nullable Building findTarget(float x, float y, int total, float dps, float health, boolean checkWeight, boolean air){
|
||||
@Nullable Building findTarget(float x, float y, int total, float dps, float health, boolean checkWeight, boolean air, boolean naval){
|
||||
if(total < data.team.rules().rtsMinSquad) return null;
|
||||
|
||||
//flag priority?
|
||||
@@ -282,8 +284,13 @@ public class RtsAI{
|
||||
//2. factory
|
||||
//3. core
|
||||
targets.clear();
|
||||
for(var flag : flags){
|
||||
targets.addAll(Vars.indexer.getEnemy(data.team, flag));
|
||||
if(naval){
|
||||
//naval units can only target enemy cores, because those are assumed to always be reachable. other blocks may not be!
|
||||
targets.addAll(Vars.indexer.getEnemy(data.team, BlockFlag.core));
|
||||
}else{
|
||||
for(var flag : flags){
|
||||
targets.addAll(Vars.indexer.getEnemy(data.team, flag));
|
||||
}
|
||||
}
|
||||
targets.removeAll(b -> assignedTargets.contains(b.id) || invalidTarget.contains(b.pos()));
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ public class UnitGroup{
|
||||
}
|
||||
|
||||
private void updateRaycast(int index, Vec2 dest, Vec2 v1){
|
||||
if(collisionLayer != PhysicsProcess.layerFlying){
|
||||
if(collisionLayer != PhysicsProcess.layerFlying && originalPositions != null && positions != null){
|
||||
|
||||
//coordinates in world space
|
||||
float
|
||||
|
||||
@@ -41,7 +41,7 @@ public class FlyingAI extends AIController{
|
||||
Building closest = null;
|
||||
float cdist = 0f;
|
||||
for(Building t : list){
|
||||
if((t.items != null && t.items.any()) || t.status() != BlockStatus.noInput){
|
||||
if(((t.items != null && t.items.any()) || t.status() != BlockStatus.noInput) && t.block.targetable){
|
||||
float dst = t.dst2(x, y);
|
||||
if(closest == null || dst < cdist){
|
||||
closest = t;
|
||||
|
||||
@@ -3437,7 +3437,6 @@ public class Blocks{
|
||||
hitEffect = Fx.hitLancer;
|
||||
despawnEffect = Fx.none;
|
||||
status = StatusEffects.shocked;
|
||||
statusDuration = 10f;
|
||||
hittable = false;
|
||||
lightColor = Color.white;
|
||||
collidesAir = false;
|
||||
@@ -3489,7 +3488,6 @@ public class Blocks{
|
||||
despawnEffect = Fx.blastExplosion;
|
||||
|
||||
status = StatusEffects.blasted;
|
||||
statusDuration = 60f;
|
||||
|
||||
hitColor = backColor = trailColor = Pal.blastAmmoBack;
|
||||
frontColor = Pal.blastAmmoFront;
|
||||
@@ -3915,7 +3913,6 @@ public class Blocks{
|
||||
collidesGround = true;
|
||||
|
||||
status = StatusEffects.blasted;
|
||||
statusDuration = 60f;
|
||||
|
||||
backColor = hitColor = trailColor = Pal.blastAmmoBack;
|
||||
frontColor = Pal.blastAmmoFront;
|
||||
@@ -4385,6 +4382,7 @@ public class Blocks{
|
||||
targetInterval = 5f;
|
||||
newTargetInterval = 30f;
|
||||
targetUnderBlocks = false;
|
||||
shootY = 8f;
|
||||
|
||||
float r = range = 130f;
|
||||
|
||||
@@ -4421,7 +4419,6 @@ public class Blocks{
|
||||
);
|
||||
|
||||
scaledHealth = 210;
|
||||
shootY = 7f;
|
||||
size = 3;
|
||||
|
||||
researchCost = with(Items.tungsten, 400, Items.silicon, 400, Items.oxide, 80, Items.beryllium, 800);
|
||||
@@ -5455,7 +5452,6 @@ public class Blocks{
|
||||
hitEffect = Fx.hitLancer;
|
||||
despawnEffect = Fx.none;
|
||||
status = StatusEffects.shocked;
|
||||
statusDuration = 10f;
|
||||
hittable = false;
|
||||
lightColor = Color.white;
|
||||
buildingDamageMultiplier = 0.25f;
|
||||
|
||||
@@ -111,7 +111,7 @@ public class SectorPresets{
|
||||
}};
|
||||
|
||||
fungalPass = new SectorPreset("fungalPass", serpulo, 21){{
|
||||
difficulty = 4;
|
||||
difficulty = 2;
|
||||
}};
|
||||
|
||||
infestedCanyons = new SectorPreset("infestedCanyons", serpulo, 210){{
|
||||
|
||||
@@ -3829,8 +3829,10 @@ public class UnitTypes{
|
||||
|
||||
engineSize = 4.8f;
|
||||
engineOffset = 61 / 4f;
|
||||
range = 4.3f * 60f * 1.4f;
|
||||
|
||||
abilities.add(new SuppressionFieldAbility(){{
|
||||
reload = 60f * 8f;
|
||||
orbRadius = 5.3f;
|
||||
y = 1f;
|
||||
}});
|
||||
@@ -3846,36 +3848,59 @@ public class UnitTypes{
|
||||
recoil = 1f;
|
||||
rotationLimit = 60f;
|
||||
|
||||
bullet = new BulletType(){{
|
||||
bullet = new BasicBulletType(4.3f, 70f, "missile-large"){{
|
||||
shootEffect = Fx.shootBig;
|
||||
smokeEffect = Fx.shootBigSmoke2;
|
||||
shake = 1f;
|
||||
speed = 0f;
|
||||
lifetime = 60 * 0.496f;
|
||||
rangeOverride = 361.2f;
|
||||
followAimSpeed = 5f;
|
||||
|
||||
width = 12f;
|
||||
height = 22f;
|
||||
hitSize = 7f;
|
||||
hitColor = backColor = trailColor = Pal.sapBulletBack;
|
||||
trailWidth = 3f;
|
||||
trailLength = 12;
|
||||
hitEffect = despawnEffect = Fx.hitBulletColor;
|
||||
|
||||
keepVelocity = false;
|
||||
collidesGround = true;
|
||||
collidesAir = false;
|
||||
|
||||
spawnUnit = new MissileUnitType("quell-missile"){{
|
||||
targetAir = false;
|
||||
speed = 4.3f;
|
||||
maxRange = 6f;
|
||||
lifetime = 60f * 1.4f;
|
||||
outlineColor = Pal.darkOutline;
|
||||
engineColor = trailColor = Pal.sapBulletBack;
|
||||
engineLayer = Layer.effect;
|
||||
health = 45;
|
||||
loopSoundVolume = 0.1f;
|
||||
//workaround to get the missile to behave like in spawnUnit while still spawning on death
|
||||
fragRandomSpread = 0;
|
||||
fragBullets = 1;
|
||||
fragVelocityMin = 1f;
|
||||
fragOffsetMax = 1f;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
shootSound = Sounds.none;
|
||||
shootCone = 360f;
|
||||
mirror = false;
|
||||
reload = 1f;
|
||||
shootOnDeath = true;
|
||||
bullet = new ExplosionBulletType(110f, 25f){{
|
||||
shootEffect = Fx.massiveExplosion;
|
||||
collidesAir = false;
|
||||
}};
|
||||
}});
|
||||
fragBullet = new BulletType(){{
|
||||
speed = 0f;
|
||||
keepVelocity = false;
|
||||
collidesAir = false;
|
||||
spawnUnit = new MissileUnitType("quell-missile"){{
|
||||
targetAir = false;
|
||||
speed = 4.3f;
|
||||
maxRange = 6f;
|
||||
lifetime = 60f * (1.4f - 0.496f);
|
||||
outlineColor = Pal.darkOutline;
|
||||
engineColor = trailColor = Pal.sapBulletBack;
|
||||
engineLayer = Layer.effect;
|
||||
health = 45;
|
||||
loopSoundVolume = 0.1f;
|
||||
|
||||
weapons.add(new Weapon() {{
|
||||
shootSound = Sounds.none;
|
||||
shootCone = 360f;
|
||||
mirror = false;
|
||||
reload = 1f;
|
||||
shootOnDeath = true;
|
||||
bullet = new ExplosionBulletType(110f, 25f) {{
|
||||
shootEffect = Fx.massiveExplosion;
|
||||
collidesAir = false;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
}};
|
||||
}};
|
||||
}});
|
||||
@@ -3909,6 +3934,8 @@ public class UnitTypes{
|
||||
int parts = 10;
|
||||
|
||||
abilities.add(new SuppressionFieldAbility(){{
|
||||
reload = 60 * 15f;
|
||||
range = 320f;
|
||||
orbRadius = orbRad;
|
||||
particleSize = partRad;
|
||||
y = 10f;
|
||||
|
||||
@@ -156,6 +156,16 @@ public class Logic implements ApplicationListener{
|
||||
if(!net.client() && e.sector == state.getSector() && e.sector.isBeingPlayed()){
|
||||
state.rules.waveTeam.data().destroyToDerelict();
|
||||
}
|
||||
|
||||
if(!net.client() && e.sector.planet.generator != null){
|
||||
e.sector.planet.generator.onSectorCaptured(e.sector);
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(SectorLoseEvent.class, e -> {
|
||||
if(!net.client() && e.sector.planet.generator != null){
|
||||
e.sector.planet.generator.onSectorLost(e.sector);
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(BlockDestroyEvent.class, e -> {
|
||||
@@ -462,7 +472,7 @@ public class Logic implements ApplicationListener{
|
||||
if(rules.fillItems && data.cores.size > 0){
|
||||
var core = data.cores.first();
|
||||
content.items().each(i -> {
|
||||
if(i.isOnPlanet(Vars.state.getPlanet())){
|
||||
if(i.isOnPlanet(Vars.state.getPlanet()) && !i.isHidden()){
|
||||
core.items.set(i, core.getMaximumAccepted(i));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,6 +64,9 @@ public class DrawOperation{
|
||||
|
||||
Block block = content.block(to);
|
||||
tile.setBlock(block, tile.team(), tile.build == null ? 0 : tile.build.rotation);
|
||||
if(tile.build != null){
|
||||
tile.build.enabled = true;
|
||||
}
|
||||
|
||||
tile.getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y));
|
||||
}else if(type == OpType.rotation.ordinal()){
|
||||
|
||||
@@ -222,38 +222,38 @@ public class Damage{
|
||||
public static float collideLaser(Bullet b, float length, boolean large, boolean laser, int pierceCap){
|
||||
float resultLength = findPierceLength(b, pierceCap, laser, length);
|
||||
|
||||
collideLine(b, b.team, b.type.hitEffect, b.x, b.y, b.rotation(), resultLength, large, laser, pierceCap);
|
||||
collideLine(b, b.team, b.x, b.y, b.rotation(), resultLength, large, laser, pierceCap);
|
||||
|
||||
b.fdata = resultLength;
|
||||
|
||||
return resultLength;
|
||||
}
|
||||
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||
collideLine(hitter, team, effect, x, y, angle, length, false);
|
||||
public static void collideLine(Bullet hitter, Team team, float x, float y, float angle, float length){
|
||||
collideLine(hitter, team, x, y, angle, length, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Damages entities in a line.
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
|
||||
collideLine(hitter, team, effect, x, y, angle, length, large, true);
|
||||
public static void collideLine(Bullet hitter, Team team, float x, float y, float angle, float length, boolean large){
|
||||
collideLine(hitter, team, x, y, angle, length, large, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Damages entities in a line.
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large, boolean laser){
|
||||
collideLine(hitter, team, effect, x, y, angle, length, large, laser, -1);
|
||||
public static void collideLine(Bullet hitter, Team team, float x, float y, float angle, float length, boolean large, boolean laser){
|
||||
collideLine(hitter, team, x, y, angle, length, large, laser, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Damages entities in a line.
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large, boolean laser, int pierceCap){
|
||||
public static void collideLine(Bullet hitter, Team team, float x, float y, float angle, float length, boolean large, boolean laser, int pierceCap){
|
||||
length = findLength(hitter, length, laser, pierceCap);
|
||||
hitter.fdata = length;
|
||||
|
||||
@@ -545,8 +545,10 @@ public class Damage{
|
||||
tileDamage(team, x, y, baseRadius, damage, null);
|
||||
}
|
||||
|
||||
public static void tileDamage(Team team, int x, int y, float baseRadius, float damage, @Nullable Bullet source){
|
||||
public static void tileDamage(Team team, int tx, int ty, float baseRadius, float damage, @Nullable Bullet source){
|
||||
Time.run(0f, () -> {
|
||||
int x = Mathf.clamp(tx, -100, world.width() + 100), y = Mathf.clamp(ty, -100, world.height() + 100);
|
||||
|
||||
var in = world.build(x, y);
|
||||
//spawned inside a multiblock. this means that damage needs to be dealt directly.
|
||||
//why? because otherwise the building would absorb everything in one cell, which means much less damage than a nearby explosion.
|
||||
|
||||
@@ -17,6 +17,7 @@ public class SuppressionFieldAbility extends Ability{
|
||||
protected static Rand rand = new Rand();
|
||||
|
||||
public float reload = 60f * 1.5f;
|
||||
public float maxDelay = 60f * 1.5f;
|
||||
public float range = 200f;
|
||||
|
||||
public float orbRadius = 4.1f, orbMidScl = 0.33f, orbSinScl = 8f, orbSinMag = 1f;
|
||||
@@ -55,9 +56,9 @@ public class SuppressionFieldAbility extends Ability{
|
||||
public void update(Unit unit){
|
||||
if(!active) return;
|
||||
|
||||
if((timer += Time.delta) >= reload){
|
||||
if((timer += Time.delta) >= maxDelay){
|
||||
Tmp.v1.set(x, y).rotate(unit.rotation - 90f).add(unit);
|
||||
Damage.applySuppression(unit.team, Tmp.v1.x, Tmp.v1.y, range, reload, reload, applyParticleChance, unit, effectColor);
|
||||
Damage.applySuppression(unit.team, Tmp.v1.x, Tmp.v1.y, range, reload, maxDelay, applyParticleChance, unit, effectColor);
|
||||
timer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class ContinuousBulletType extends BulletType{
|
||||
if(timescaleDamage && b.owner instanceof Building build){
|
||||
b.damage *= build.timeScale();
|
||||
}
|
||||
Damage.collideLine(b, b.team, hitEffect, b.x, b.y, b.rotation(), currentLength(b), largeHit, laserAbsorb, pierceCap);
|
||||
Damage.collideLine(b, b.team, b.x, b.y, b.rotation(), currentLength(b), largeHit, laserAbsorb, pierceCap);
|
||||
b.damage = damage;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,13 +55,13 @@ public class ContinuousLaserBulletType extends ContinuousBulletType{
|
||||
float ellipseLenScl = Mathf.lerp(1 - i / (float)(colors.length), 1f, pointyScaling);
|
||||
|
||||
Lines.stroke(stroke);
|
||||
Lines.lineAngle(b.x, b.y, rot, realLength - frontLength, false);
|
||||
Lines.lineAngle(b.x, b.y, rot, Math.max(0, realLength - frontLength), false);
|
||||
|
||||
//back ellipse
|
||||
Drawf.flameFront(b.x, b.y, divisions, rot + 180f, backLength, stroke / 2f);
|
||||
|
||||
//front ellipse
|
||||
Tmp.v1.trnsExact(rot, realLength - frontLength);
|
||||
Tmp.v1.trnsExact(rot, Math.max(0, realLength - frontLength));
|
||||
Drawf.flameFront(b.x + Tmp.v1.x, b.y + Tmp.v1.y, divisions, rot, frontLength * ellipseLenScl, stroke / 2f);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.distribution.MassDriver.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -89,5 +91,17 @@ public class MassDriverBolt extends BasicBulletType{
|
||||
public void hit(Bullet b, float hitx, float hity){
|
||||
super.hit(b, hitx, hity);
|
||||
despawned(b);
|
||||
if(b.data() instanceof DriverBulletData data){
|
||||
float explosiveness = 0f;
|
||||
float flammability = 0f;
|
||||
float power = 0f;
|
||||
for(int i = 0; i < data.items.length; i++){
|
||||
Item item = content.item(i);
|
||||
explosiveness += item.explosiveness * data.items[i];
|
||||
flammability += item.flammability * data.items[i];
|
||||
power += item.charge * Mathf.pow(data.items[i], 1.1f) * 25f;
|
||||
}
|
||||
Damage.dynamicExplosion(b.x, b.y, flammability / 10f, explosiveness / 10f, power, 1f, state.rules.damageExplosions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class RailBulletType extends BulletType{
|
||||
super.init(b);
|
||||
|
||||
b.fdata = length;
|
||||
Damage.collideLine(b, b.team, b.type.hitEffect, b.x, b.y, b.rotation(), length, false, false, pierceCap);
|
||||
Damage.collideLine(b, b.team, b.x, b.y, b.rotation(), length, false, false, pierceCap);
|
||||
float resultLen = b.fdata;
|
||||
|
||||
Vec2 nor = Tmp.v1.trns(b.rotation(), 1f).nor();
|
||||
|
||||
@@ -35,7 +35,7 @@ import static mindustry.logic.GlobalVars.*;
|
||||
@Component(base = true)
|
||||
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Syncc, Shieldc, Displayable, Ranged, Minerc, Builderc, Senseable, Settable{
|
||||
private static final Vec2 tmp1 = new Vec2(), tmp2 = new Vec2();
|
||||
static final float warpDst = 20f;
|
||||
static final float warpDst = 8f;
|
||||
|
||||
@Import boolean dead, disarmed;
|
||||
@Import float x, y, rotation, maxHealth, drag, armor, hitSize, health, shield, ammo, dragMultiplier, armorOverride, speedMultiplier;
|
||||
@@ -643,11 +643,11 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
//repel unit out of bounds
|
||||
if(x < left) dx += (-(x - left)/warpDst);
|
||||
if(y < bot) dy += (-(y - bot)/warpDst);
|
||||
if(x > right) dx -= (x - right)/warpDst;
|
||||
if(y > top) dy -= (y - top)/warpDst;
|
||||
if(x > right - tilesize) dx -= (x - (right - tilesize))/warpDst;
|
||||
if(y > top - tilesize) dy -= (y - (top - tilesize))/warpDst;
|
||||
|
||||
velAddNet(dx * Time.delta, dy * Time.delta);
|
||||
float margin = tilesize * 2f;
|
||||
float margin = tilesize * 1f;
|
||||
x = Mathf.clamp(x, left - margin, right - tilesize + margin);
|
||||
y = Mathf.clamp(y, bot - margin, top - tilesize + margin);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ public class BuildPlan implements Position, QuadTreeObject{
|
||||
public boolean breaking;
|
||||
/** Config int. Not used unless hasConfig is true.*/
|
||||
public Object config;
|
||||
/** Original position, only used in schematics.*/
|
||||
public int originalX, originalY, originalWidth, originalHeight;
|
||||
|
||||
/** Last progress.*/
|
||||
public float progress;
|
||||
@@ -65,6 +63,7 @@ public class BuildPlan implements Position, QuadTreeObject{
|
||||
public BuildPlan(){
|
||||
|
||||
}
|
||||
|
||||
public boolean placeable(Team team){
|
||||
return Build.validPlace(block, team, x, y, rotation);
|
||||
}
|
||||
@@ -111,22 +110,12 @@ public class BuildPlan implements Position, QuadTreeObject{
|
||||
copy.block = block;
|
||||
copy.breaking = breaking;
|
||||
copy.config = config;
|
||||
copy.originalX = originalX;
|
||||
copy.originalY = originalY;
|
||||
copy.progress = progress;
|
||||
copy.initialized = initialized;
|
||||
copy.animScale = animScale;
|
||||
return copy;
|
||||
}
|
||||
|
||||
public BuildPlan original(int x, int y, int originalWidth, int originalHeight){
|
||||
originalX = x;
|
||||
originalY = y;
|
||||
this.originalWidth = originalWidth;
|
||||
this.originalHeight = originalHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rect bounds(Rect rect){
|
||||
if(breaking){
|
||||
return rect.set(-100f, -100f, 0f, 0f);
|
||||
|
||||
@@ -119,8 +119,7 @@ public final class FogControl implements CustomChunk{
|
||||
|
||||
var data = data(team);
|
||||
if(data == null) return false;
|
||||
if(x < 0 || y < 0 || x >= ww || y >= wh) return false;
|
||||
return data.read.get(x + y * ww);
|
||||
return data.read.get(Mathf.clamp(x, 0, ww - 1) + Mathf.clamp(y, 0, wh - 1) * ww);
|
||||
}
|
||||
|
||||
public void resetFog(){
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.game;
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.Texture.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
@@ -98,7 +99,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerLegacyMarker(String name, Prov<? extends ObjectiveMarker> prov) {
|
||||
public static void registerLegacyMarker(String name, Prov<? extends ObjectiveMarker> prov){
|
||||
Class<?> type = prov.get().getClass();
|
||||
|
||||
markerNameToType.put(name, prov);
|
||||
@@ -663,7 +664,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
}
|
||||
|
||||
/** Marker used for drawing various content to indicate something along with an objective. Mostly used as UI overlay. */
|
||||
/** Marker used for drawing various content to indicate something along with an objective. Mostly used as UI overlay. */
|
||||
public static abstract class ObjectiveMarker{
|
||||
/** Internal use only! Do not access. */
|
||||
public transient int arrayIndex;
|
||||
@@ -714,7 +715,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
state.mapLocales.getProperty(key + ".mobile") :
|
||||
state.mapLocales.containsProperty(key) ?
|
||||
state.mapLocales.getProperty(key) :
|
||||
Core.bundle.get(key);
|
||||
Core.bundle.get(key + ".mobile", Core.bundle.get(key));
|
||||
}else{
|
||||
out =
|
||||
state.mapLocales.containsProperty(key) ?
|
||||
@@ -822,13 +823,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
switch(type){
|
||||
case fontSize -> fontSize = (float)p1;
|
||||
case textHeight -> textHeight = (float)p1;
|
||||
case labelFlags -> {
|
||||
if(!Mathf.equal((float)p1, 0f)){
|
||||
flags |= WorldLabel.flagBackground;
|
||||
}else{
|
||||
flags &= ~WorldLabel.flagBackground;
|
||||
}
|
||||
}
|
||||
case outline -> flags = (byte)Pack.bitmask(flags, WorldLabel.flagOutline, !Mathf.equal((float)p1, 0f));
|
||||
case labelFlags -> flags = (byte)Pack.bitmask(flags, WorldLabel.flagBackground, !Mathf.equal((float)p1, 0f));
|
||||
case radius -> radius = (float)p1;
|
||||
case rotation -> rotation = (float)p1;
|
||||
case color -> color.fromDouble(p1);
|
||||
@@ -838,13 +834,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case labelFlags -> {
|
||||
if(!Mathf.equal((float)p2, 0f)){
|
||||
flags |= WorldLabel.flagOutline;
|
||||
}else{
|
||||
flags &= ~WorldLabel.flagOutline;
|
||||
}
|
||||
}
|
||||
case labelFlags -> flags = (byte)Pack.bitmask(flags, WorldLabel.flagOutline, !Mathf.equal((float)p2, 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -944,7 +934,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation + startAngle, rotation + endAngle);
|
||||
}else{
|
||||
Draw.color(color);
|
||||
if (startAngle < endAngle){
|
||||
if(startAngle < endAngle){
|
||||
Fill.arc(pos.x, pos.y, radius * scaleFactor, (endAngle - startAngle) / 360f, rotation + startAngle, sides);
|
||||
}else{
|
||||
Fill.arc(pos.x, pos.y, radius * scaleFactor, (startAngle - endAngle) / 360f, rotation + endAngle, sides);
|
||||
@@ -962,6 +952,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
switch(type){
|
||||
case radius -> radius = (float)p1;
|
||||
case stroke -> stroke = (float)p1;
|
||||
case outline -> outline = !Mathf.equal((float)p1, 0f);
|
||||
case rotation -> rotation = (float)p1;
|
||||
case color -> color.fromDouble(p1);
|
||||
case shape -> sides = (int)p1;
|
||||
@@ -1025,25 +1016,14 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case fontSize -> fontSize = (float)p1;
|
||||
case labelFlags -> {
|
||||
if(!Mathf.equal((float)p1, 0f)){
|
||||
flags |= WorldLabel.flagBackground;
|
||||
}else{
|
||||
flags &= ~WorldLabel.flagBackground;
|
||||
}
|
||||
}
|
||||
case outline -> flags = (byte)Pack.bitmask(flags, WorldLabel.flagOutline, !Mathf.equal((float)p1, 0f));
|
||||
case labelFlags -> flags = (byte)Pack.bitmask(flags, WorldLabel.flagBackground, !Mathf.equal((float)p1, 0f));
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case labelFlags -> {
|
||||
if(!Mathf.equal((float)p2, 0f)){
|
||||
flags |= WorldLabel.flagOutline;
|
||||
}else{
|
||||
flags &= ~WorldLabel.flagOutline;
|
||||
}
|
||||
}
|
||||
case labelFlags -> flags = (byte)Pack.bitmask(flags, WorldLabel.flagOutline, !Mathf.equal((float)p2, 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1101,6 +1081,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
case endPos -> endPos.x = (float)p1 * tilesize;
|
||||
case stroke -> stroke = (float)p1;
|
||||
case color -> color1.set(color2.fromDouble(p1));
|
||||
case outline -> outline = !Mathf.equal((float)p1, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1111,7 +1092,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p1) && !Double.isNaN(p2)){
|
||||
switch (type){
|
||||
switch(type){
|
||||
case posi -> ((int)p1 == 0 ? pos : (int)p1 == 1 ? endPos : Tmp.v1).x = (float)p2 * tilesize;
|
||||
case colori -> ((int)p1 == 0 ? color1 : (int)p1 == 1 ? color2 : Tmp.c1).fromDouble(p2);
|
||||
}
|
||||
@@ -1199,7 +1180,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
private transient TextureRegion fetchedRegion;
|
||||
|
||||
public QuadMarker() {
|
||||
public QuadMarker(){
|
||||
for(int i = 0; i < 4; i++){
|
||||
vertices[i * 6 + 2] = Color.white.toFloatBits();
|
||||
vertices[i * 6 + 5] = Color.clearFloatBits;
|
||||
@@ -1250,7 +1231,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
boolean firstUpdate = fetchedRegion == null;
|
||||
|
||||
if(fetchedRegion == null) fetchedRegion = new TextureRegion();
|
||||
if(firstUpdate) fetchedRegion = new TextureRegion();
|
||||
Tmp.tr1.set(fetchedRegion);
|
||||
|
||||
lookupRegion(textureName, fetchedRegion);
|
||||
@@ -1258,21 +1239,22 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(firstUpdate){
|
||||
if(mapRegion){
|
||||
mapRegion = false;
|
||||
|
||||
// possibly from the editor, we need to clamp the values
|
||||
for(int i = 0; i < 4; i++){
|
||||
vertices[i * 6 + 3] = Mathf.map(Mathf.clamp(vertices[i * 6 + 3]), fetchedRegion.u, fetchedRegion.u2);
|
||||
vertices[i * 6 + 4] = Mathf.map(1 - Mathf.clamp(vertices[i * 6 + 4]), fetchedRegion.v, fetchedRegion.v2);
|
||||
setUv(i, vertices[i * 6 + 3], vertices[i * 6 + 4]);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for(int i = 0; i < 4; i++){
|
||||
vertices[i * 6 + 3] = Mathf.map(vertices[i * 6 + 3], Tmp.tr1.u, Tmp.tr1.u2, fetchedRegion.u, fetchedRegion.u2);
|
||||
vertices[i * 6 + 4] = Mathf.map(vertices[i * 6 + 4], Tmp.tr1.v, Tmp.tr1.v2, fetchedRegion.v, fetchedRegion.v2);
|
||||
setUv(i, unmap(vertices[i * 6 + 3], Tmp.tr1.u, Tmp.tr1.u2), 1 - unmap(vertices[i * 6 + 4], Tmp.tr1.v, Tmp.tr1.v2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float unmap(float x, float from, float to){
|
||||
if(Mathf.equal(from, to)) return x;
|
||||
return (x - from) / (to - from);
|
||||
}
|
||||
|
||||
private void setPos(int i, double x, double y){
|
||||
if(i >= 0 && i < 4){
|
||||
if(!Double.isNaN(x)) vertices[i * 6] = (float)x * tilesize;
|
||||
@@ -1290,11 +1272,16 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(i >= 0 && i < 4){
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
|
||||
if(!Double.isNaN(u)) vertices[i * 6 + 3] = Mathf.map(Mathf.clamp((float)u), fetchedRegion.u, fetchedRegion.u2);
|
||||
if(!Double.isNaN(v)) vertices[i * 6 + 4] = Mathf.map(1 - Mathf.clamp((float)v), fetchedRegion.v, fetchedRegion.v2);
|
||||
if(!Double.isNaN(u)){
|
||||
boolean clampU = fetchedRegion.texture.getUWrap() != TextureWrap.mirroredRepeat && fetchedRegion.texture.getUWrap() != TextureWrap.repeat;
|
||||
vertices[i * 6 + 3] = Mathf.map(clampU ? Mathf.clamp((float)u) : (float)u, fetchedRegion.u, fetchedRegion.u2);
|
||||
}
|
||||
if(!Double.isNaN(v)){
|
||||
boolean clampV = fetchedRegion.texture.getVWrap() != TextureWrap.mirroredRepeat && fetchedRegion.texture.getVWrap() != TextureWrap.repeat;
|
||||
vertices[i * 6 + 4] = Mathf.map(clampV ? 1 - Mathf.clamp((float)v) : 1 - (float)v, fetchedRegion.v, fetchedRegion.v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void lookupRegion(String name, TextureRegion out){
|
||||
|
||||
@@ -72,7 +72,32 @@ public class Saves{
|
||||
|
||||
lastSectorSave = saves.find(s -> s.isSector() && s.getName().equals(Core.settings.getString("last-sector-save", "<none>")));
|
||||
|
||||
ObjectSet<Sector> infoToClear = new ObjectSet<>(), remapped = new ObjectSet<>();
|
||||
class Remap{
|
||||
//file in the temp folder
|
||||
Fi sourceFile;
|
||||
//slot of source sector to move file for
|
||||
SaveSlot slot;
|
||||
Sector sourceSector;
|
||||
//sector info from source sector to move into
|
||||
SectorInfo sourceInfo;
|
||||
|
||||
//file to copy to
|
||||
Fi destFile;
|
||||
//destination sector to move to
|
||||
Sector destSector;
|
||||
|
||||
Remap(SaveSlot slot, Fi sourceFile, Sector sourceSector, SectorInfo sourceInfo, Fi destFile, Sector destSector){
|
||||
this.slot = slot;
|
||||
this.sourceFile = sourceFile;
|
||||
this.sourceSector = sourceSector;
|
||||
this.sourceInfo = sourceInfo;
|
||||
this.destFile = destFile;
|
||||
this.destSector = destSector;
|
||||
}
|
||||
}
|
||||
|
||||
Seq<Remap> remaps = new Seq<>();
|
||||
ObjectSet<Sector> remapped = new ObjectSet<>();
|
||||
|
||||
//automatically assign sector save slots
|
||||
for(SaveSlot slot : saves){
|
||||
@@ -102,22 +127,13 @@ public class Saves{
|
||||
if(!slot.file.equals(getSectorFile(remapTarget))){
|
||||
Log.info("Remapping sector: @ -> @ (@)", sector.id, remapTarget.id, remapTarget.preset);
|
||||
|
||||
sector.loadInfo();
|
||||
//overwrite the target sector's info with the save's info
|
||||
Core.settings.putJson(remapTarget.planet.name + "-s-" + remapTarget.id + "-info", sector.info);
|
||||
remapTarget.loadInfo();
|
||||
|
||||
//queue a clear of the sector that had its data moved
|
||||
infoToClear.add(sector);
|
||||
//add to the remapped list (if it was remapped, don't clear it!)
|
||||
remapped.add(remapTarget);
|
||||
|
||||
remapTarget.save = slot;
|
||||
try{
|
||||
Fi target = getSectorFile(remapTarget);
|
||||
//move over save file
|
||||
slot.file.moveTo(target);
|
||||
slot.file = target;
|
||||
SectorInfo info = Core.settings.getJson(sector.planet.name + "-s-" + sector.id + "-info", SectorInfo.class, SectorInfo::new);
|
||||
Fi tmpRemapFile = saveDirectory.child("remap_" + sector.planet.name + "_" + sector.id + "." + saveExtension);
|
||||
slot.file.moveTo(tmpRemapFile);
|
||||
|
||||
remaps.add(new Remap(slot, tmpRemapFile, sector, info, getSectorFile(remapTarget), remapTarget));
|
||||
remapped.add(remapTarget);
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to move sector files when remapping: " + sector.id + " -> " + remapTarget.id, e);
|
||||
}
|
||||
@@ -125,6 +141,7 @@ public class Saves{
|
||||
|
||||
remapTarget.save = slot;
|
||||
slot.meta.rules.sector = remapTarget;
|
||||
|
||||
}else{
|
||||
if(sector.save != null){
|
||||
Log.warn("Sector @ has two corresponding saves: @ and @", sector, sector.save.file, slot.file);
|
||||
@@ -134,10 +151,27 @@ public class Saves{
|
||||
}
|
||||
}
|
||||
|
||||
for(var sector : infoToClear){
|
||||
if(!remapped.contains(sector)){
|
||||
sector.clearInfo();
|
||||
}
|
||||
//process remaps later to allow swaps of sectors
|
||||
for(var remap : remaps){
|
||||
var remapTarget = remap.destSector;
|
||||
|
||||
//overwrite the target sector's info with the save's info
|
||||
Core.settings.putJson(remapTarget.planet.name + "-s-" + remapTarget.id + "-info", remap.sourceInfo);
|
||||
remapTarget.loadInfo();
|
||||
|
||||
remapTarget.save = remap.slot;
|
||||
try{
|
||||
//move file from tmp directory back into the correct location
|
||||
remap.sourceFile.moveTo(remap.destFile);
|
||||
remap.slot.file = remap.destFile;
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to move back sector files when remapping: " + remap.sourceSector.id + " -> " + remapTarget.id, e);
|
||||
}
|
||||
|
||||
//clear the info, assuming it wasn't a sector that got mapped to
|
||||
if(!remapped.contains(remap.sourceSector)){
|
||||
remap.sourceSector.clearInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ public class Schematics implements Loadable{
|
||||
|
||||
all.sort();
|
||||
|
||||
if(shadowBuffer == null){
|
||||
if(shadowBuffer == null && !headless){
|
||||
Core.app.post(() -> shadowBuffer = new FrameBuffer(maxSchematicSize + padding + 8, maxSchematicSize + padding + 8));
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class Schematics implements Loadable{
|
||||
|
||||
/** Creates an array of build plans from a schematic's data, centered on the provided x+y coordinates. */
|
||||
public Seq<BuildPlan> toPlans(Schematic schem, int x, int y){
|
||||
return schem.tiles.map(t -> new BuildPlan(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block, t.config).original(t.x, t.y, schem.width, schem.height))
|
||||
return schem.tiles.map(t -> new BuildPlan(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block, t.config))
|
||||
.removeAll(s -> (!s.block.isVisible() && !(s.block instanceof CoreBlock)) || !s.block.unlockedNow()).sort(Structs.comparingInt(s -> -s.block.schematicPriority));
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ public class SectorInfo{
|
||||
public float secondsPassed;
|
||||
/** How many minutes this sector has been captured. */
|
||||
public float minutesCaptured;
|
||||
/** Light coverage in terms of radius. */
|
||||
public float lightCoverage;
|
||||
/** Display name. */
|
||||
public @Nullable String name;
|
||||
/** Displayed icon. */
|
||||
@@ -225,6 +227,15 @@ public class SectorInfo{
|
||||
damage = 0;
|
||||
hasSpawns = spawner.countSpawns() > 0;
|
||||
|
||||
lightCoverage = 0f;
|
||||
for(var build : state.rules.defaultTeam.data().buildings){
|
||||
if(build.block.emitLight){
|
||||
lightCoverage += build.block.lightRadius * build.efficiency;
|
||||
}
|
||||
}
|
||||
|
||||
lightCoverage += state.rules.defaultTeam.data().units.sumf(u -> u.type.lightRadius/2f);
|
||||
|
||||
//cap production at raw production.
|
||||
production.each((item, stat) -> {
|
||||
stat.mean = Math.min(stat.mean, rawProduction.get(item, ExportStat::new).mean);
|
||||
@@ -242,6 +253,10 @@ public class SectorInfo{
|
||||
if(sector.planet.allowWaveSimulation){
|
||||
SectorDamage.writeParameters(sector);
|
||||
}
|
||||
|
||||
if(sector.planet.generator != null){
|
||||
sector.planet.generator.beforeSaveWrite(sector);
|
||||
}
|
||||
}
|
||||
|
||||
/** Update averages of various stats, updates some special sector logic.
|
||||
|
||||
@@ -322,6 +322,13 @@ public class Universe{
|
||||
return net.client() ? netSeconds : seconds;
|
||||
}
|
||||
|
||||
public void setSeconds(float seconds){
|
||||
this.seconds = (int)seconds;
|
||||
this.secondCounter = seconds - this.seconds;
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public float secondsf(){
|
||||
return seconds() + secondCounter;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public class LoadRenderer implements Disposable{
|
||||
private float testprogress = 0f;
|
||||
private StringBuilder assetText = new StringBuilder();
|
||||
private Bar[] bars;
|
||||
private Mesh mesh = MeshBuilder.buildHex(colorRed, 2, true, 1f);
|
||||
private Mesh mesh = MeshBuilder.buildPlanetGrid(PlanetGrid.create(2), colorRed, 1f);
|
||||
private Camera3D cam = new Camera3D();
|
||||
private int lastLength = -1;
|
||||
private FxProcessor fx;
|
||||
|
||||
@@ -109,6 +109,7 @@ public class Shaders{
|
||||
public Color ambientColor = Color.white.cpy();
|
||||
public Vec3 camDir = new Vec3();
|
||||
public Vec3 camPos = new Vec3();
|
||||
public boolean emissive;
|
||||
public Planet planet;
|
||||
|
||||
public PlanetShader(){
|
||||
@@ -123,6 +124,7 @@ public class Shaders{
|
||||
setUniformf("u_ambientColor", ambientColor.r, ambientColor.g, ambientColor.b);
|
||||
setUniformf("u_camdir", camDir);
|
||||
setUniformf("u_campos", renderer.planets.cam.position);
|
||||
setUniformf("u_emissive", emissive ? 1f : 0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package mindustry.graphics.g3d;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
|
||||
public interface GenericMesh{
|
||||
public interface GenericMesh extends Disposable{
|
||||
void render(PlanetParams params, Mat3D projection, Mat3D transform);
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ import mindustry.type.*;
|
||||
public class HexMesh extends PlanetMesh{
|
||||
|
||||
public HexMesh(Planet planet, int divisions){
|
||||
super(planet, MeshBuilder.buildHex(planet.generator, divisions, false, planet.radius, 0.2f), Shaders.planet);
|
||||
super(planet, MeshBuilder.buildHex(planet.generator, divisions, planet.radius, 0.2f), Shaders.planet);
|
||||
}
|
||||
|
||||
public HexMesh(Planet planet, HexMesher mesher, int divisions, Shader shader){
|
||||
super(planet, MeshBuilder.buildHex(mesher, divisions, false, planet.radius, 0.2f), shader);
|
||||
super(planet, MeshBuilder.buildHex(mesher, divisions, planet.radius, 0.2f), shader);
|
||||
}
|
||||
|
||||
public HexMesh(){
|
||||
@@ -21,6 +21,7 @@ public class HexMesh extends PlanetMesh{
|
||||
@Override
|
||||
public void preRender(PlanetParams params){
|
||||
Shaders.planet.planet = planet;
|
||||
Shaders.planet.emissive = planet.generator != null && planet.generator.isEmissive();
|
||||
Shaders.planet.lightDir.set(planet.solarSystem.position).sub(planet.position).rotate(Vec3.Y, planet.getRotation()).nor();
|
||||
Shaders.planet.ambientColor.set(planet.solarSystem.lightColor);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,23 @@ import arc.math.geom.*;
|
||||
|
||||
/** Defines color and height for a planet mesh. */
|
||||
public interface HexMesher{
|
||||
float getHeight(Vec3 position);
|
||||
Color getColor(Vec3 position);
|
||||
|
||||
default float getHeight(Vec3 position){
|
||||
return 0f;
|
||||
}
|
||||
|
||||
default void getColor(Vec3 position, Color out){
|
||||
|
||||
}
|
||||
|
||||
default void getEmissiveColor(Vec3 position, Color out){
|
||||
|
||||
}
|
||||
|
||||
default boolean isEmissive(){
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean skip(Vec3 position){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@ public class HexSkyMesh extends PlanetMesh{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
return color;
|
||||
public void getColor(Vec3 position, Color out){
|
||||
out.set(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean skip(Vec3 position){
|
||||
return Simplex.noise3d(7 + seed, octaves, persistence, scl, position.x, position.y * 3f, position.z) >= thresh;
|
||||
}
|
||||
}, divisions, false, planet.radius, radius), Shaders.clouds);
|
||||
}, divisions, planet.radius, radius), Shaders.clouds);
|
||||
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
@@ -19,4 +19,9 @@ public class MatMesh implements GenericMesh{
|
||||
public void render(PlanetParams params, Mat3D projection, Mat3D transform){
|
||||
mesh.render(params, projection, tmp.set(transform).mul(mat));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
mesh.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,72 @@
|
||||
package mindustry.graphics.g3d;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.graphics.g3d.PlanetGrid.*;
|
||||
import mindustry.maps.generators.*;
|
||||
|
||||
public class MeshBuilder{
|
||||
private static final Vec3 v1 = new Vec3(), v2 = new Vec3(), v3 = new Vec3(), v4 = new Vec3();
|
||||
private static final float[] floats = new float[3 + 3 + 1];
|
||||
private static Mesh mesh;
|
||||
|
||||
public static Mesh buildIcosphere(int divisions, float radius, Color color){
|
||||
begin(20 * (2 << (2 * divisions - 1)) * 3);
|
||||
private static final boolean gl30 = Core.gl30 != null;
|
||||
private static volatile float[] tmpHeights = new float[14580]; //highest amount of corners in vanilla
|
||||
|
||||
/** Note that the resulting icosphere does not have normals or a color. */
|
||||
public static Mesh buildIcosphere(int divisions, float radius){
|
||||
MeshResult result = Icosphere.create(divisions);
|
||||
for(int i = 0; i < result.indices.size; i+= 3){
|
||||
v1.set(result.vertices.items, result.indices.items[i] * 3).setLength(radius);
|
||||
v2.set(result.vertices.items, result.indices.items[i + 1] * 3).setLength(radius);
|
||||
v3.set(result.vertices.items, result.indices.items[i + 2] * 3).setLength(radius);
|
||||
|
||||
verts(v1, v3, v2, normal(v1, v2, v3).scl(-1f), color);
|
||||
Mesh mesh = begin(result.vertices.size / 3, result.indices.size, false, false);
|
||||
|
||||
if(result.vertices.size >= 65535) throw new RuntimeException("Due to index size limits, only meshes with a maximum of 65535 vertices are supported. If you want more than that, make your own non-indexed mesh builder.");
|
||||
|
||||
float[] items = result.vertices.items;
|
||||
for(int i = 0; i < result.vertices.size; i ++){
|
||||
items[i] *= radius;
|
||||
}
|
||||
|
||||
return end();
|
||||
}
|
||||
mesh.getVerticesBuffer().put(items, 0, result.vertices.size);
|
||||
|
||||
public static Mesh buildIcosphere(int divisions, float radius){
|
||||
return buildIcosphere(divisions, radius, Color.white);
|
||||
short[] indices = new short[result.indices.size];
|
||||
for(int i = 0; i < result.indices.size; i++){
|
||||
indices[i] = (short)result.indices.items[i];
|
||||
}
|
||||
|
||||
mesh.getIndicesBuffer().put(indices);
|
||||
|
||||
return end(mesh);
|
||||
}
|
||||
|
||||
public static Mesh buildPlanetGrid(PlanetGrid grid, Color color, float scale){
|
||||
int total = 0;
|
||||
for(Ptile tile : grid.tiles){
|
||||
total += tile.corners.length * 2;
|
||||
}
|
||||
Mesh mesh = begin(grid.tiles.length * 12, 0, false, false);
|
||||
|
||||
float col = color.toFloatBits();
|
||||
float[] floats = new float[8];
|
||||
|
||||
begin(total);
|
||||
for(Ptile tile : grid.tiles){
|
||||
Corner[] c = tile.corners;
|
||||
for(int i = 0; i < c.length; i++){
|
||||
Vec3 a = v1.set(c[i].v).scl(scale);
|
||||
Vec3 b = v2.set(c[(i + 1) % c.length].v).scl(scale);
|
||||
|
||||
vert(a, Vec3.Z, color);
|
||||
vert(b, Vec3.Z, color);
|
||||
for(int i = 0; i < c.length; i++){
|
||||
Vec3 v1 = c[i].v;
|
||||
Vec3 v2 = c[(i + 1) % c.length].v;
|
||||
|
||||
floats[0] = v1.x * scale;
|
||||
floats[1] = v1.y * scale;
|
||||
floats[2] = v1.z * scale;
|
||||
floats[3] = col;
|
||||
|
||||
floats[4] = v2.x * scale;
|
||||
floats[5] = v2.y * scale;
|
||||
floats[6] = v2.z * scale;
|
||||
floats[7] = col;
|
||||
|
||||
mesh.getVerticesBuffer().put(floats);
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
return end(mesh);
|
||||
}
|
||||
|
||||
public static Mesh buildHex(Color color, int divisions, boolean lines, float radius){
|
||||
public static Mesh buildHex(Color color, int divisions, float radius){
|
||||
return buildHex(new HexMesher(){
|
||||
@Override
|
||||
public float getHeight(Vec3 position){
|
||||
@@ -58,20 +74,46 @@ public class MeshBuilder{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
return color;
|
||||
public void getColor(Vec3 position, Color out){
|
||||
out.set(color);
|
||||
}
|
||||
}, divisions, lines, radius, 0);
|
||||
}, divisions, radius, 0);
|
||||
}
|
||||
|
||||
public static Mesh buildHex(HexMesher mesher, int divisions, boolean lines, float radius, float intensity){
|
||||
//TODO: in principle this should not be synchronized, but I would rather not realloc tmpHeights every time, and it is unlikely that two planets will be reloading at the same time
|
||||
public static synchronized Mesh buildHex(HexMesher mesher, int divisions, float radius, float intensity){
|
||||
PlanetGrid grid = PlanetGrid.create(divisions);
|
||||
|
||||
//TODO: this is NOT thread safe, but in practice, it should never cause a problem
|
||||
if(mesher instanceof PlanetGenerator generator){
|
||||
generator.seed = generator.baseSeed;
|
||||
}
|
||||
|
||||
begin(grid.tiles.length * 12);
|
||||
boolean emit = mesher.isEmissive();
|
||||
|
||||
if(grid.tiles.length * 6 >= 65535) throw new RuntimeException("Due to index size limits, only meshes with a maximum of 65535 vertices are supported. If you want more than that, make your own non-indexed mesh builder.");
|
||||
|
||||
Mesh mesh = begin(grid.tiles.length * 6, grid.tiles.length * 4 * 3, true, emit);
|
||||
|
||||
float[] heights;
|
||||
|
||||
if(tmpHeights == null || tmpHeights.length < grid.corners.length){
|
||||
heights = tmpHeights = new float[grid.corners.length];
|
||||
}else{
|
||||
heights = tmpHeights;
|
||||
}
|
||||
|
||||
//cache heights in an array to prevent redundant calls to getHeight
|
||||
for(int i = 0; i < grid.corners.length; i++){
|
||||
heights[i] = (1f + mesher.getHeight(grid.corners[i].v) * intensity) * radius;
|
||||
}
|
||||
int position = 0;
|
||||
|
||||
short[] shorts = new short[12];
|
||||
float[] floats = new float[3 + (gl30 ? 1 : 3) + 1 + (emit ? 1 : 0)];
|
||||
Vec3 nor = new Vec3();
|
||||
|
||||
Color tmpCol = new Color();
|
||||
|
||||
for(Ptile tile : grid.tiles){
|
||||
if(mesher.skip(tile.v)){
|
||||
@@ -80,81 +122,155 @@ public class MeshBuilder{
|
||||
|
||||
Corner[] c = tile.corners;
|
||||
|
||||
for(Corner corner : c){
|
||||
corner.v.setLength((1f + mesher.getHeight(v2.set(corner.v)) * intensity) * radius);
|
||||
float
|
||||
h1 = heights[c[0].id],
|
||||
h2 = heights[c[2].id],
|
||||
h3 = heights[c[4].id];
|
||||
|
||||
Vec3
|
||||
v1 = c[0].v,
|
||||
v2 = c[2].v,
|
||||
v3 = c[4].v;
|
||||
|
||||
normal(
|
||||
v1.x * h1, v1.y * h1, v1.z * h1,
|
||||
v2.x * h2, v2.y * h2, v2.z * h2,
|
||||
v3.x * h3, v3.y * h3, v3.z * h3,
|
||||
nor);
|
||||
|
||||
tmpCol.set(1f, 1f, 1f, 1f);
|
||||
mesher.getColor(tile.v, tmpCol);
|
||||
float color = tmpCol.toFloatBits();
|
||||
|
||||
float emissive = 0f;
|
||||
|
||||
if(emit){
|
||||
tmpCol.set(0f, 0f, 0f, 0f);
|
||||
mesher.getEmissiveColor(tile.v, tmpCol);
|
||||
emissive = tmpCol.toFloatBits();
|
||||
}
|
||||
|
||||
Vec3 nor = normal(c[0].v, c[2].v, c[4].v);
|
||||
Color color = mesher.getColor(v2.set(tile.v));
|
||||
for(var corner : c){
|
||||
float height = heights[corner.id];
|
||||
|
||||
if(lines){
|
||||
nor.set(1f, 1f, 1f);
|
||||
|
||||
for(int i = 0; i < c.length; i++){
|
||||
Vec3 v1 = c[i].v;
|
||||
Vec3 v2 = c[(i + 1) % c.length].v;
|
||||
|
||||
vert(v1, nor, color);
|
||||
vert(v2, nor, color);
|
||||
}
|
||||
}else{
|
||||
verts(c[0].v, c[1].v, c[2].v, nor, color);
|
||||
verts(c[0].v, c[2].v, c[3].v, nor, color);
|
||||
verts(c[0].v, c[3].v, c[4].v, nor, color);
|
||||
|
||||
if(c.length > 5){
|
||||
verts(c[0].v, c[4].v, c[5].v, nor, color);
|
||||
}
|
||||
vert(mesh, floats, corner.v.x * height, corner.v.y * height, corner.v.z * height, nor, color, emissive);
|
||||
}
|
||||
|
||||
//restore mutated corners
|
||||
for(Corner corner : c){
|
||||
corner.v.nor();
|
||||
shorts[0] = (short)(position);
|
||||
shorts[1] = (short)(position + 1);
|
||||
shorts[2] = (short)(position + 2);
|
||||
|
||||
shorts[3] = (short)(position);
|
||||
shorts[4] = (short)(position + 2);
|
||||
shorts[5] = (short)(position + 3);
|
||||
|
||||
shorts[6] = (short)(position);
|
||||
shorts[7] = (short)(position + 3);
|
||||
shorts[8] = (short)(position + 4);
|
||||
|
||||
if(c.length > 5){
|
||||
shorts[9] = (short)(position);
|
||||
shorts[10] = (short)(position + 4);
|
||||
shorts[11] = (short)(position + 5);
|
||||
}
|
||||
|
||||
mesh.getIndicesBuffer().put(shorts, 0, c.length > 5 ? 12 : 9);
|
||||
position += c.length;
|
||||
}
|
||||
|
||||
return end();
|
||||
return end(mesh);
|
||||
}
|
||||
|
||||
private static void begin(int count){
|
||||
mesh = new Mesh(true, count, 0,
|
||||
VertexAttribute.position3,
|
||||
VertexAttribute.normal,
|
||||
VertexAttribute.color
|
||||
private static Mesh begin(int vertices, int indices, boolean normal, boolean emissive){
|
||||
Seq<VertexAttribute> attributes = Seq.with(
|
||||
VertexAttribute.position3
|
||||
);
|
||||
|
||||
if(normal){
|
||||
//only GL30 supports GL_INT_2_10_10_10_REV
|
||||
attributes.add(gl30 ? VertexAttribute.packedNormal : VertexAttribute.normal);
|
||||
}
|
||||
|
||||
attributes.add(VertexAttribute.color);
|
||||
|
||||
if(emissive){
|
||||
attributes.add(new VertexAttribute(4, GL20.GL_UNSIGNED_BYTE, true, "a_emissive"));
|
||||
}
|
||||
|
||||
Mesh mesh = new Mesh(true, vertices, indices, attributes.toArray(VertexAttribute.class));
|
||||
|
||||
mesh.getVerticesBuffer().limit(mesh.getVerticesBuffer().capacity());
|
||||
mesh.getVerticesBuffer().position(0);
|
||||
|
||||
if(indices > 0){
|
||||
mesh.getIndicesBuffer().limit(mesh.getIndicesBuffer().capacity());
|
||||
mesh.getIndicesBuffer().position(0);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static Mesh end(){
|
||||
Mesh last = mesh;
|
||||
last.getVerticesBuffer().limit(last.getVerticesBuffer().position());
|
||||
mesh = null;
|
||||
return last;
|
||||
private static Mesh end(Mesh mesh){
|
||||
mesh.getVerticesBuffer().limit(mesh.getVerticesBuffer().position());
|
||||
if(mesh.getNumIndices() > 0){
|
||||
mesh.getIndicesBuffer().limit(mesh.getIndicesBuffer().position());
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
private static Vec3 normal(Vec3 v1, Vec3 v2, Vec3 v3){
|
||||
return v4.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor();
|
||||
private static Vec3 normal(Vec3 v1, Vec3 v2, Vec3 v3, Vec3 out){
|
||||
return out.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor();
|
||||
}
|
||||
|
||||
private static void verts(Vec3 a, Vec3 b, Vec3 c, Vec3 normal, Color color){
|
||||
vert(a, normal, color);
|
||||
vert(b, normal, color);
|
||||
vert(c, normal, color);
|
||||
private static void normal(float v1x, float v1y, float v1z, float v2x, float v2y, float v2z, float v3x, float v3y, float v3z, Vec3 out){
|
||||
float
|
||||
x = v2x - v1x,
|
||||
y = v2y - v1y,
|
||||
z = v2z - v1z,
|
||||
vx = v3x - v1x,
|
||||
vy = v3y - v1y,
|
||||
vz = v3z - v1z;
|
||||
|
||||
float
|
||||
cx = y * vz - z * vy,
|
||||
cy = z * vx - x * vz,
|
||||
cz = x * vy - y * vx;
|
||||
|
||||
out.set(cx, cy, cz).nor();
|
||||
}
|
||||
|
||||
private static void vert(Vec3 a, Vec3 normal, Color color){
|
||||
floats[0] = a.x;
|
||||
floats[1] = a.y;
|
||||
floats[2] = a.z;
|
||||
private static void vert(Mesh mesh, float[] floats, float x, float y, float z, Vec3 normal, float color, float emissive){
|
||||
floats[0] = x;
|
||||
floats[1] = y;
|
||||
floats[2] = z;
|
||||
|
||||
floats[3] = normal.x;
|
||||
floats[4] = normal.y;
|
||||
floats[5] = normal.z;
|
||||
if(gl30){
|
||||
floats[3] = packNormals(normal.x, normal.y, normal.z);
|
||||
|
||||
floats[4] = color;
|
||||
if(floats.length > 5) floats[5] = emissive;
|
||||
}else{
|
||||
floats[3] = normal.x;
|
||||
floats[4] = normal.x;
|
||||
floats[5] = normal.x;
|
||||
|
||||
floats[6] = color;
|
||||
if(floats.length > 7) floats[7] = emissive;
|
||||
}
|
||||
|
||||
floats[6] = color.toFloatBits();
|
||||
mesh.getVerticesBuffer().put(floats);
|
||||
}
|
||||
|
||||
private static float packNormals(float x, float y, float z){
|
||||
int xs = x < -1f/512f ? 1 : 0;
|
||||
int ys = y < -1f/512f ? 1 : 0;
|
||||
int zs = z < -1f/512f ? 1 : 0;
|
||||
|
||||
int vi =
|
||||
zs << 29 | ((int)(z * 511 + (zs << 9)) & 511) << 20 |
|
||||
ys << 19 | ((int)(y * 511 + (ys << 9)) & 511) << 10 |
|
||||
xs << 9 | ((int)(x * 511 + (xs << 9)) & 511);
|
||||
return Float.intBitsToFloat(vi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,11 @@ public class MultiMesh implements GenericMesh{
|
||||
v.render(params, projection, transform);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(var mesh : meshes){
|
||||
mesh.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ public class NoiseMesh extends HexMesh{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
return color;
|
||||
public void getColor(Vec3 position, Color out){
|
||||
out.set(color);
|
||||
}
|
||||
}, divisions, false, radius, 0.2f);
|
||||
}, divisions, radius, 0.2f);
|
||||
}
|
||||
|
||||
/** Two-color variant. */
|
||||
@@ -35,9 +35,9 @@ public class NoiseMesh extends HexMesh{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
return Simplex.noise3d(8 + seed, coct, cper, cscl, 5f + position.x, 5f + position.y, 5f + position.z) > cthresh ? color2 : color1;
|
||||
public void getColor(Vec3 position, Color out){
|
||||
out.set(Simplex.noise3d(8 + seed, coct, cper, cscl, 5f + position.x, 5f + position.y, 5f + position.z) > cthresh ? color2 : color1);
|
||||
}
|
||||
}, divisions, false, radius, 0.2f);
|
||||
}, divisions, radius, 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class PlanetGrid{
|
||||
}
|
||||
}
|
||||
|
||||
public static PlanetGrid create(int size){
|
||||
public static synchronized PlanetGrid create(int size){
|
||||
//cache grids between calls, since only ~5 different grids total are needed
|
||||
if(size < cache.length && cache[size] != null){
|
||||
return cache[size];
|
||||
@@ -240,6 +240,14 @@ public class PlanetGrid{
|
||||
corners = new Corner[edgeCount];
|
||||
edges = new Edge[edgeCount];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Ptile{" +
|
||||
"id=" + id +
|
||||
" " + v +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class Corner{
|
||||
|
||||
@@ -26,6 +26,8 @@ public abstract class PlanetMesh implements GenericMesh{
|
||||
|
||||
@Override
|
||||
public void render(PlanetParams params, Mat3D projection, Mat3D transform){
|
||||
if(mesh.isDisposed()) return;
|
||||
|
||||
preRender(params);
|
||||
shader.bind();
|
||||
shader.setUniformMatrix4("u_proj", projection.val);
|
||||
@@ -33,4 +35,9 @@ public abstract class PlanetMesh implements GenericMesh{
|
||||
shader.apply();
|
||||
mesh.render(shader, Gl.triangles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
mesh.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class PlanetRenderer implements Disposable{
|
||||
setThreshold(0.8f);
|
||||
blurPasses = 6;
|
||||
}};
|
||||
public final Mesh atmosphere = MeshBuilder.buildHex(Color.white, 2, false, 1.5f);
|
||||
public final Mesh atmosphere = MeshBuilder.buildHex(Color.white, 2, 1.5f);
|
||||
|
||||
//seed: 8kmfuix03fw
|
||||
public final CubemapMesh skybox = new CubemapMesh(new Cubemap("cubemaps/stars/"));
|
||||
|
||||
@@ -3,7 +3,6 @@ package mindustry.graphics.g3d;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
@@ -19,9 +18,9 @@ public class SunMesh extends HexMesh{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
public void getColor(Vec3 position, Color out){
|
||||
double height = Math.pow(Simplex.noise3d(0, octaves, persistence, scl, position.x, position.y, position.z), pow) * mag;
|
||||
return Tmp.c1.set(colors[Mathf.clamp((int)(height * colors.length), 0, colors.length - 1)]).mul(colorScale);
|
||||
out.set(colors[Mathf.clamp((int)(height * colors.length), 0, colors.length - 1)]).mul(colorScale);
|
||||
}
|
||||
}, divisions, Shaders.unlit);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@ public class DesktopInput extends InputHandler{
|
||||
/** Time of most recent control group selection */
|
||||
public long lastCtrlGroupSelectMillis;
|
||||
|
||||
/** Time of most recent payload pickup/drop key press*/
|
||||
public long lastPayloadKeyTapMillis;
|
||||
/** Time of most recent payload pickup/drop key hold*/
|
||||
public long lastPayloadKeyHoldMillis;
|
||||
|
||||
private float buildPlanMouseOffsetX, buildPlanMouseOffsetY;
|
||||
private boolean changedCursor;
|
||||
|
||||
@@ -425,10 +430,6 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.input.keyRelease(Binding.select)){
|
||||
player.shooting = false;
|
||||
}
|
||||
|
||||
if(state.isGame() && !scene.hasDialog() && !scene.hasField()){
|
||||
if(Core.input.keyTap(Binding.minimap)) ui.minimapfrag.toggle();
|
||||
if(Core.input.keyTap(Binding.planetMap) && state.isCampaign()) ui.planet.toggle();
|
||||
@@ -555,6 +556,10 @@ public class DesktopInput extends InputHandler{
|
||||
changedCursor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.input.keyRelease(Binding.select)){
|
||||
player.shooting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -728,6 +733,7 @@ public class DesktopInput extends InputHandler{
|
||||
mode = none;
|
||||
}else if(!selectPlans.isEmpty()){
|
||||
flushPlans(selectPlans);
|
||||
movedPlan = true;
|
||||
}else if(isPlacing()){
|
||||
selectX = cursorX;
|
||||
selectY = cursorY;
|
||||
@@ -970,10 +976,26 @@ public class DesktopInput extends InputHandler{
|
||||
if(unit instanceof Payloadc){
|
||||
if(Core.input.keyTap(Binding.pickupCargo)){
|
||||
tryPickupPayload();
|
||||
lastPayloadKeyTapMillis = Time.millis();
|
||||
}
|
||||
|
||||
if(Core.input.keyDown(Binding.pickupCargo)
|
||||
&& Time.timeSinceMillis(lastPayloadKeyHoldMillis) > 20
|
||||
&& Time.timeSinceMillis(lastPayloadKeyTapMillis) > 200){
|
||||
tryPickupPayload();
|
||||
lastPayloadKeyHoldMillis = Time.millis();
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.dropCargo)){
|
||||
tryDropPayload();
|
||||
lastPayloadKeyTapMillis = Time.millis();
|
||||
}
|
||||
|
||||
if(Core.input.keyDown(Binding.dropCargo)
|
||||
&& Time.timeSinceMillis(lastPayloadKeyHoldMillis) > 20
|
||||
&& Time.timeSinceMillis(lastPayloadKeyTapMillis) > 200){
|
||||
tryDropPayload();
|
||||
lastPayloadKeyHoldMillis = Time.millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1331,9 +1331,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
plans.each(plan -> {
|
||||
if(plan.breaking) return;
|
||||
|
||||
float off = plan.block.size % 2 == 0 ? -0.5f : 0f;
|
||||
|
||||
plan.pointConfig(p -> {
|
||||
int cx = p.x, cy = p.y;
|
||||
int lx = cx;
|
||||
float cx = p.x + off, cy = p.y + off;
|
||||
float lx = cx;
|
||||
|
||||
if(direction >= 0){
|
||||
cx = -cy;
|
||||
@@ -1342,7 +1344,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
cx = cy;
|
||||
cy = -lx;
|
||||
}
|
||||
p.set(cx, cy);
|
||||
p.set(Mathf.floor(cx - off), Mathf.floor(cy - off));
|
||||
});
|
||||
|
||||
//rotate actual plan, centered on its multiblock position
|
||||
@@ -1376,14 +1378,12 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
plan.pointConfig(p -> {
|
||||
int corigin = x ? plan.originalWidth/2 : plan.originalHeight/2;
|
||||
int nvalue = -(x ? p.x : p.y);
|
||||
if(x){
|
||||
plan.originalX = -(plan.originalX - corigin) + corigin;
|
||||
p.x = nvalue;
|
||||
if(plan.block.size % 2 == 0) p.x --;
|
||||
p.x = -p.x;
|
||||
}else{
|
||||
plan.originalY = -(plan.originalY - corigin) + corigin;
|
||||
p.y = nvalue;
|
||||
if(plan.block.size % 2 == 0) p.y --;
|
||||
p.y = -p.y;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}else{
|
||||
Building tile = world.buildWorld(x, y);
|
||||
|
||||
if((tile != null && player.team() != tile.team && (tile.team != Team.derelict || state.rules.coreCapture)) || (tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged())){
|
||||
if((tile != null && (player.team() != tile.team && (tile.team != Team.derelict || state.rules.coreCapture)) && player.unit().type.canAttack) || (tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged())){
|
||||
player.unit().mineTile = null;
|
||||
target = tile;
|
||||
}
|
||||
@@ -1078,7 +1078,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
//this may be a bad idea, aiming for a point far in front could work better, test it out
|
||||
unit.aim(Core.input.mouseWorldX(), Core.input.mouseWorldY());
|
||||
}else{
|
||||
Vec2 intercept = Predict.intercept(unit, target, bulletSpeed);
|
||||
Vec2 intercept = player.unit().type.weapons.contains(w -> w.predictTarget) ? Predict.intercept(unit, target, bulletSpeed) : Tmp.v1.set(target);
|
||||
|
||||
player.mouseX = intercept.x;
|
||||
player.mouseY = intercept.y;
|
||||
|
||||
@@ -172,7 +172,7 @@ public class MapIO{
|
||||
for(Tile tile : tiles){
|
||||
//while synthetic blocks are possible, most of their data is lost, so in order to avoid questions like
|
||||
//"why is there air under my drill" and "why are all my conveyors facing right", they are disabled
|
||||
int color = tile.block().hasColor && !tile.block().synthetic() ? tile.block().mapColor.rgba() : tile.floor().mapColor.rgba();
|
||||
int color = tile.block().hasColor && !tile.block().hasBuilding() ? tile.block().mapColor.rgba() : tile.floor().mapColor.rgba();
|
||||
pix.set(tile.x, tiles.height - 1 - tile.y, color);
|
||||
}
|
||||
return pix;
|
||||
@@ -183,6 +183,9 @@ public class MapIO{
|
||||
int color = pixmap.get(tile.x, pixmap.height - 1 - tile.y);
|
||||
Block block = ColorMapper.get(color);
|
||||
|
||||
//ignore buildings; reading images is only intended for environment tiles
|
||||
if(block.hasBuilding()) continue;
|
||||
|
||||
if(block.isOverlay()){
|
||||
tile.setOverlay(block.asFloor());
|
||||
}else if(block.isFloor()){
|
||||
@@ -194,7 +197,6 @@ public class MapIO{
|
||||
}
|
||||
}
|
||||
|
||||
//guess at floors by grabbing a random adjacent floor
|
||||
for(Tile tile : tiles){
|
||||
//default to stone floor
|
||||
if(tile.floor() == Blocks.air){
|
||||
|
||||
@@ -1104,7 +1104,7 @@ public class TypeIO{
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents a unit that has not been resolved yet. TODO unimplemented / unused*/
|
||||
/** Represents a unit that has not been resolved yet. */
|
||||
public static class UnitBox implements Boxed<Unit>{
|
||||
public int id;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ public enum LAccess{
|
||||
displayWidth,
|
||||
displayHeight,
|
||||
bufferUsage,
|
||||
operations,
|
||||
size,
|
||||
solid,
|
||||
dead,
|
||||
|
||||
@@ -25,6 +25,7 @@ import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.logic.*;
|
||||
import mindustry.world.blocks.logic.CanvasBlock.*;
|
||||
import mindustry.world.blocks.logic.LogicBlock.*;
|
||||
import mindustry.world.blocks.logic.LogicDisplay.*;
|
||||
import mindustry.world.blocks.logic.MemoryBlock.*;
|
||||
@@ -581,6 +582,8 @@ public class LExecutor{
|
||||
}
|
||||
}else if(target.isobj && target.objval instanceof CharSequence str){
|
||||
output.setnum(address < 0 || address >= str.length() ? Double.NaN : (int)str.charAt(address));
|
||||
}else if(from instanceof CanvasBuild canvas && (exec.privileged || (from.team == exec.team))){
|
||||
output.setnum(canvas.getPixel(address));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -611,6 +614,8 @@ public class LExecutor{
|
||||
toVar.numval = value.numval;
|
||||
toVar.isobj = value.isobj;
|
||||
}
|
||||
}else if(from instanceof CanvasBuild canvas && (exec.privileged || (from.team == exec.team))){
|
||||
canvas.setPixel(address, value.numi());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ public enum LMarkerControl{
|
||||
color("color"),
|
||||
radius("radius"),
|
||||
stroke("stroke"),
|
||||
outline("outline"),
|
||||
rotation("rotation"),
|
||||
shape("sides", "fill", "outline"),
|
||||
arc("start", "end"),
|
||||
|
||||
@@ -221,7 +221,7 @@ public class LogicDialog extends BaseDialog{
|
||||
update(() -> setColor(typeColor(s, color)));
|
||||
}}, new Label(() -> " " + typeName(s) + " "){{
|
||||
setStyle(Styles.outlineLabel);
|
||||
}});
|
||||
}}).minWidth(120f);
|
||||
|
||||
t.row();
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ public enum LogicOp{
|
||||
div("/", (a, b) -> a / b),
|
||||
idiv("//", (a, b) -> Math.floor(a / b)),
|
||||
mod("%", (a, b) -> a % b),
|
||||
emod("%%", (a, b) -> ((a % b) + b) % b),
|
||||
pow("^", Math::pow),
|
||||
|
||||
equal("==", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0, (a, b) -> Structs.eq(a, b) ? 1 : 0),
|
||||
@@ -24,6 +25,7 @@ public enum LogicOp{
|
||||
|
||||
shl("<<", (a, b) -> (long)a << (long)b),
|
||||
shr(">>", (a, b) -> (long)a >> (long)b),
|
||||
ushr(">>>", (a, b) -> (long)a >>> (long)b),
|
||||
or("or", (a, b) -> (long)a | (long)b),
|
||||
and("b-and", (a, b) -> (long)a & (long)b),
|
||||
xor("xor", (a, b) -> (long)a ^ (long)b),
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package mindustry.maps;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
/** Class for temporarily (?) storing links to map submissions on Discord. */
|
||||
public class SectorSubmissions{
|
||||
private static IntMap<String> hiddenMap = new IntMap<>();
|
||||
|
||||
static{
|
||||
//autogenerated
|
||||
hiddenMap.put(0, "https://discord.com/channels/391020510269669376/1379926780860698784");
|
||||
hiddenMap.put(6, "https://discord.com/channels/391020510269669376/1379926782966497322");
|
||||
hiddenMap.put(13, "https://discord.com/channels/391020510269669376/1379926785164312810");
|
||||
hiddenMap.put(16, "https://discord.com/channels/391020510269669376/1379926788280680579");
|
||||
hiddenMap.put(19, "https://discord.com/channels/391020510269669376/1379926792479183019");
|
||||
hiddenMap.put(20, "https://discord.com/channels/391020510269669376/1379926794114961634");
|
||||
hiddenMap.put(24, "https://discord.com/channels/391020510269669376/1379926797042581716");
|
||||
hiddenMap.put(27, "https://discord.com/channels/391020510269669376/1379926798833287289");
|
||||
hiddenMap.put(30, "https://discord.com/channels/391020510269669376/1379926800854945823");
|
||||
hiddenMap.put(47, "https://discord.com/channels/391020510269669376/1379926802591645820");
|
||||
hiddenMap.put(55, "https://discord.com/channels/391020510269669376/1379926823277695189");
|
||||
hiddenMap.put(66, "https://discord.com/channels/391020510269669376/1379926825941078128");
|
||||
hiddenMap.put(67, "https://discord.com/channels/391020510269669376/1379926828696866898");
|
||||
hiddenMap.put(69, "https://discord.com/channels/391020510269669376/1379926831326822610");
|
||||
hiddenMap.put(76, "https://discord.com/channels/391020510269669376/1379926833411391580");
|
||||
hiddenMap.put(92, "https://discord.com/channels/391020510269669376/1379926835621527615");
|
||||
hiddenMap.put(94, "https://discord.com/channels/391020510269669376/1379926838079393802");
|
||||
hiddenMap.put(103, "https://discord.com/channels/391020510269669376/1379926839559979030");
|
||||
hiddenMap.put(111, "https://discord.com/channels/391020510269669376/1379926842659569864");
|
||||
hiddenMap.put(116, "https://discord.com/channels/391020510269669376/1379926845058711734");
|
||||
hiddenMap.put(127, "https://discord.com/channels/391020510269669376/1379926869465632829");
|
||||
hiddenMap.put(133, "https://discord.com/channels/391020510269669376/1379926871227240770");
|
||||
hiddenMap.put(138, "https://discord.com/channels/391020510269669376/1379926873152164004");
|
||||
hiddenMap.put(150, "https://discord.com/channels/391020510269669376/1379926876457537547");
|
||||
hiddenMap.put(157, "https://discord.com/channels/391020510269669376/1379926879502598155");
|
||||
hiddenMap.put(161, "https://discord.com/channels/391020510269669376/1379926882203730024");
|
||||
hiddenMap.put(162, "https://discord.com/channels/391020510269669376/1379926884606808247");
|
||||
hiddenMap.put(176, "https://discord.com/channels/391020510269669376/1379926887203213353");
|
||||
hiddenMap.put(180, "https://discord.com/channels/391020510269669376/1379926889648619580");
|
||||
hiddenMap.put(185, "https://discord.com/channels/391020510269669376/1379926892181983283");
|
||||
hiddenMap.put(191, "https://discord.com/channels/391020510269669376/1379926912004001914");
|
||||
hiddenMap.put(192, "https://discord.com/channels/391020510269669376/1379926914122256449");
|
||||
hiddenMap.put(197, "https://discord.com/channels/391020510269669376/1379926916911599676");
|
||||
hiddenMap.put(200, "https://discord.com/channels/391020510269669376/1379926918429806755");
|
||||
hiddenMap.put(204, "https://discord.com/channels/391020510269669376/1379926921130807447");
|
||||
hiddenMap.put(207, "https://discord.com/channels/391020510269669376/1379926923370827827");
|
||||
hiddenMap.put(225, "https://discord.com/channels/391020510269669376/1379926925719376152");
|
||||
hiddenMap.put(230, "https://discord.com/channels/391020510269669376/1379926927585841163");
|
||||
hiddenMap.put(237, "https://discord.com/channels/391020510269669376/1379926929636851812");
|
||||
hiddenMap.put(242, "https://discord.com/channels/391020510269669376/1379926931923013843");
|
||||
hiddenMap.put(243, "https://discord.com/channels/391020510269669376/1379926955423694978");
|
||||
hiddenMap.put(244, "https://discord.com/channels/391020510269669376/1379926957738954762");
|
||||
hiddenMap.put(245, "https://discord.com/channels/391020510269669376/1379926971286290584");
|
||||
hiddenMap.put(246, "https://discord.com/channels/391020510269669376/1379926973454745600");
|
||||
hiddenMap.put(247, "https://discord.com/channels/391020510269669376/1379926976361533752");
|
||||
hiddenMap.put(248, "https://discord.com/channels/391020510269669376/1379926979129774151");
|
||||
hiddenMap.put(251, "https://discord.com/channels/391020510269669376/1379928042637361382");
|
||||
hiddenMap.put(254, "https://discord.com/channels/391020510269669376/1379928045577703424");
|
||||
hiddenMap.put(259, "https://discord.com/channels/391020510269669376/1379928048245280871");
|
||||
hiddenMap.put(263, "https://discord.com/channels/391020510269669376/1379928050010951694");
|
||||
hiddenMap.put(265, "https://discord.com/channels/391020510269669376/1379928052921929891");
|
||||
}
|
||||
|
||||
/** @return the link to the Discord discussion thread of the specified hidden sector submission. */
|
||||
public static @Nullable String getSectorThread(Sector sector){
|
||||
if(sector.generateEnemyBase){
|
||||
return hiddenMap.get(sector.id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package mindustry.maps.generators;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
@@ -9,16 +7,6 @@ import mindustry.world.*;
|
||||
/** A planet generator that provides no weather, height, color or bases. Override generate().*/
|
||||
public class BlankPlanetGenerator extends PlanetGenerator{
|
||||
|
||||
@Override
|
||||
public float getHeight(Vec3 position){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWeather(Sector sector, Rules rules){
|
||||
|
||||
|
||||
@@ -25,11 +25,22 @@ public abstract class PlanetGenerator extends BasicGenerator implements HexMeshe
|
||||
|
||||
protected @Nullable Sector sector;
|
||||
|
||||
/** Should generate sector bases for a planet. */
|
||||
public void generateSector(Sector sector){
|
||||
|
||||
}
|
||||
|
||||
public void onSectorCaptured(Sector sector){
|
||||
|
||||
}
|
||||
|
||||
public void onSectorLost(Sector sector){
|
||||
|
||||
}
|
||||
|
||||
public void beforeSaveWrite(Sector sector){
|
||||
|
||||
}
|
||||
|
||||
public void getLockedText(Sector hovered, StringBuilder out){
|
||||
out.append("[gray]").append(Iconc.lock).append(" ").append(Core.bundle.get("locked"));
|
||||
}
|
||||
|
||||
@@ -39,20 +39,17 @@ public class ErekirPlanetGenerator extends PlanetGenerator{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
public void getColor(Vec3 position, Color out){
|
||||
Block block = getBlock(position);
|
||||
|
||||
//more obvious color
|
||||
if(block == Blocks.crystallineStone) block = Blocks.crystalFloor;
|
||||
//TODO this might be too green
|
||||
//if(block == Blocks.beryllicStone) block = Blocks.arkyicStone;
|
||||
|
||||
return Tmp.c1.set(block.mapColor).a(1f - block.albedo);
|
||||
out.set(block.mapColor).a(1f - block.albedo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSizeScl(){
|
||||
//TODO should sectors be 600, or 500 blocks?
|
||||
return 2000 * 1.07f * 6f / 5f;
|
||||
}
|
||||
|
||||
@@ -65,17 +62,17 @@ public class ErekirPlanetGenerator extends PlanetGenerator{
|
||||
}
|
||||
|
||||
Block getBlock(Vec3 position){
|
||||
float ice = rawTemp(position);
|
||||
Tmp.v32.set(position);
|
||||
float px = position.x, py = position.y, pz = position.z;
|
||||
|
||||
float ice = rawTemp(position);
|
||||
float height = rawHeight(position);
|
||||
Tmp.v31.set(position);
|
||||
|
||||
height *= 1.2f;
|
||||
height = Mathf.clamp(height);
|
||||
|
||||
Block result = terrain[Mathf.clamp((int)(height * terrain.length), 0, terrain.length - 1)];
|
||||
|
||||
if(ice < 0.3 + Math.abs(Ridged.noise3d(seed + crystalSeed, position.x + 4f, position.y + 8f, position.z + 1f, crystalOct, crystalScl)) * crystalMag){
|
||||
if(ice < 0.3 + Math.abs(Ridged.noise3d(seed + crystalSeed, px + 4f, py + 8f, pz + 1f, crystalOct, crystalScl)) * crystalMag){
|
||||
return Blocks.crystallineStone;
|
||||
}
|
||||
|
||||
@@ -86,11 +83,9 @@ public class ErekirPlanetGenerator extends PlanetGenerator{
|
||||
}
|
||||
}
|
||||
|
||||
position = Tmp.v32;
|
||||
|
||||
//TODO tweak this to make it more natural
|
||||
//TODO edge distortion?
|
||||
if(ice < redThresh - noArkThresh && Ridged.noise3d(seed + arkSeed, position.x + 2f, position.y + 8f, position.z + 1f, arkOct, arkScl) > arkThresh){
|
||||
if(ice < redThresh - noArkThresh && Ridged.noise3d(seed + arkSeed, px + 2f, py + 8f, pz + 1f, arkOct, arkScl) > arkThresh){
|
||||
//TODO arkyic in middle
|
||||
result = Blocks.beryllicStone;
|
||||
}
|
||||
|
||||
@@ -20,13 +20,16 @@ import mindustry.world.blocks.environment.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
//alternate, less direct generation (wip)
|
||||
public static boolean alt = false;
|
||||
//alternate, less direct generation
|
||||
public static boolean indirectPaths = false;
|
||||
//random water patches
|
||||
public static boolean genLakes = false;
|
||||
|
||||
BaseGenerator basegen = new BaseGenerator();
|
||||
float heightYOffset = 42.7f;
|
||||
float scl = 5f;
|
||||
float waterOffset = 0.05f;
|
||||
boolean genLakes = false;
|
||||
float waterOffset = 0.04f;
|
||||
float heightScl = 1.01f;
|
||||
|
||||
Block[][] arr =
|
||||
{
|
||||
@@ -58,10 +61,30 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
);
|
||||
|
||||
float water = 2f / arr[0].length;
|
||||
Vec3 basePos = new Vec3(0.9341721, 0.0, 0.3568221);
|
||||
|
||||
float rawHeight(Vec3 position){
|
||||
position = Tmp.v33.set(position).scl(scl);
|
||||
return (Mathf.pow(Simplex.noise3d(seed, 7, 0.5f, 1f/3f, position.x, position.y, position.z), 2.3f) + waterOffset) / (1f + waterOffset);
|
||||
return (Mathf.pow(Simplex.noise3d(seed, 7, 0.5f, 1f/3f, position.x * scl, position.y * scl + heightYOffset, position.z * scl) * heightScl, 2.3f) + waterOffset) / (1f + waterOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSectorCaptured(Sector sector){
|
||||
sector.planet.reloadMeshAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSectorLost(Sector sector){
|
||||
sector.planet.reloadMeshAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSaveWrite(Sector sector){
|
||||
sector.planet.reloadMeshAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmissive(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,16 +109,57 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
public void getColor(Vec3 position, Color out){
|
||||
Block block = getBlock(position);
|
||||
//replace salt with sand color
|
||||
if(block == Blocks.salt) return Blocks.sand.mapColor;
|
||||
return Tmp.c1.set(block.mapColor).a(1f - block.albedo);
|
||||
if(block == Blocks.salt) block = Blocks.sand;
|
||||
out.set(block.mapColor).a(1f - block.albedo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getEmissiveColor(Vec3 position, Color out){
|
||||
float dst = 999f, captureDst = 999f, lightScl = 0f;
|
||||
|
||||
Object[] sectors = Planets.serpulo.sectors.items;
|
||||
int size = Planets.serpulo.sectors.size;
|
||||
|
||||
for(int i = 0; i < size; i ++){
|
||||
var sector = (Sector)sectors[i];
|
||||
|
||||
if(sector.hasEnemyBase() && !sector.isCaptured()){
|
||||
dst = Math.min(dst, position.dst(sector.tile.v) - (sector.preset != null ? sector.preset.difficulty/10f * 0.03f - 0.03f : 0f));
|
||||
}else if(sector.hasBase()){
|
||||
float cdst = position.dst(sector.tile.v);
|
||||
if(cdst < captureDst){
|
||||
captureDst = cdst;
|
||||
lightScl = sector.info.lightCoverage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lightScl = Math.min(lightScl / 50000f, 1.3f);
|
||||
if(lightScl < 1f) lightScl = Interp.pow5Out.apply(lightScl);
|
||||
|
||||
float freq = 0.05f;
|
||||
if(position.dst(basePos) < 0.55f ?
|
||||
|
||||
dst*metalDstScl + Simplex.noise3d(seed + 1, 3, 0.4, 5.5f, position.x, position.y + 200f, position.z)*0.08f + ((basePos.dst(position) + 0.00f) % freq < freq/2f ? 1f : 0f) * 0.07f < 0.08f/* || dst <= 0.0001f*/ :
|
||||
dst*metalDstScl + Simplex.noise3d(seed, 3, 0.4, 9f, position.x, position.y + 370f, position.z)*0.06f < 0.045){
|
||||
|
||||
out.set(Team.crux.color)
|
||||
.mul(0.8f + Simplex.noise3d(seed, 1, 1, 9f, position.x, position.y + 99f, position.z) * 0.4f)
|
||||
.lerp(Team.sharded.color, 0.2f*Simplex.noise3d(seed, 1, 1, 9f, position.x, position.y + 999f, position.z)).toFloatBits();
|
||||
}else if(captureDst*metalDstScl + Simplex.noise3d(seed, 3, 0.4, 9f, position.x, position.y + 600f, position.z)*0.07f < 0.05 * lightScl){
|
||||
out.set(Team.sharded.color).mul(0.7f + Simplex.noise3d(seed, 1, 1, 9f, position.x, position.y + 99f, position.z) * 0.4f)
|
||||
.lerp(Team.crux.color, 0.3f*Simplex.noise3d(seed, 1, 1, 9f, position.x, position.y + 999f, position.z)).toFloatBits();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void genTile(Vec3 position, TileGen tile){
|
||||
tile.floor = getBlock(position);
|
||||
if(tile.floor == Blocks.darkPanel6) tile.floor = Blocks.darkPanel3;
|
||||
tile.block = tile.floor.asFloor().wall;
|
||||
|
||||
if(Ridged.noise3d(seed + 1, position.x, position.y, position.z, 2, 22) > 0.31){
|
||||
@@ -103,23 +167,46 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
}
|
||||
}
|
||||
|
||||
static double metalDstScl = 0.25;
|
||||
|
||||
Block getBlock(Vec3 position){
|
||||
float height = rawHeight(position);
|
||||
Tmp.v31.set(position);
|
||||
position = Tmp.v33.set(position).scl(scl);
|
||||
float px = position.x * scl, py = position.y * scl, pz = position.z * scl;
|
||||
|
||||
float rad = scl;
|
||||
float temp = Mathf.clamp(Math.abs(position.y * 2f) / (rad));
|
||||
float tnoise = Simplex.noise3d(seed, 7, 0.56, 1f/3f, position.x, position.y + 999f, position.z);
|
||||
float temp = Mathf.clamp(Math.abs(py * 2f) / (rad));
|
||||
float tnoise = Simplex.noise3d(seed, 7, 0.56, 1f/3f, px, py + 999f - 0.1f, pz);
|
||||
temp = Mathf.lerp(temp, tnoise, 0.5f);
|
||||
height *= 1.2f;
|
||||
height = Mathf.clamp(height);
|
||||
|
||||
float tar = Simplex.noise3d(seed, 4, 0.55f, 1f/2f, position.x, position.y + 999f, position.z) * 0.3f + Tmp.v31.dst(0, 0, 1f) * 0.2f;
|
||||
float tar = Simplex.noise3d(seed, 4, 0.55f, 1f/2f, px, py + 999f, pz) * 0.3f + position.dst(0, 0, 1f) * 0.2f;
|
||||
|
||||
Block res = arr[Mathf.clamp((int)(temp * arr.length), 0, arr[0].length - 1)][Mathf.clamp((int)(height * arr[0].length), 0, arr[0].length - 1)];
|
||||
if(tar > 0.5f){
|
||||
return tars.get(res, res);
|
||||
}else{
|
||||
if(position.within(basePos, 0.65f)){
|
||||
|
||||
float dst = 999f;
|
||||
|
||||
Object[] sectors = Planets.serpulo.sectors.items;
|
||||
int size = Planets.serpulo.sectors.size;
|
||||
|
||||
for(int i = 0; i < size; i ++){
|
||||
var sector = (Sector)sectors[i];
|
||||
|
||||
if(sector.hasEnemyBase()){
|
||||
dst = Math.min(dst, position.dst(sector.tile.v));
|
||||
}
|
||||
}
|
||||
|
||||
float freq = 0.05f, freq2 = 0.07f;
|
||||
|
||||
if(dst*0.85f + Simplex.noise3d(seed, 3, 0.4, 5.5f, position.x, position.y + 200f, position.z)*0.015f + ((basePos.dst(position) + 0.00f) % freq < freq/2f ? 1f : 0f) * 0.07f < 0.15f){
|
||||
return ((basePos.dst(position) + 0.01f) % freq2 < freq2*0.65f) ? Blocks.metalFloor : Blocks.darkPanel6;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -156,7 +243,7 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
Vec2 midpoint = Tmp.v1.set(to.x, to.y).add(x, y).scl(0.5f);
|
||||
rand.nextFloat();
|
||||
|
||||
if(alt){
|
||||
if(indirectPaths){
|
||||
midpoint.add(Tmp.v2.set(1, 0f).setAngle(Angles.angle(to.x, to.y, x, y) + 90f * (rand.chance(0.5) ? 1f : -1f)).scl(Tmp.v1.dst(x, y) * 2f));
|
||||
}else{
|
||||
//add randomized offset to avoid straight lines
|
||||
|
||||
@@ -14,7 +14,7 @@ import mindustry.world.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class TantrosPlanetGenerator extends PlanetGenerator{
|
||||
Color c1 = Color.valueOf("5057a6"), c2 = Color.valueOf("272766"), out = new Color();
|
||||
Color c1 = Color.valueOf("5057a6"), c2 = Color.valueOf("272766");
|
||||
|
||||
Block[][] arr = {
|
||||
{Blocks.redmat, Blocks.redmat, Blocks.darksand, Blocks.bluemat, Blocks.bluemat}
|
||||
@@ -30,9 +30,9 @@ public class TantrosPlanetGenerator extends PlanetGenerator{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
public void getColor(Vec3 position, Color out){
|
||||
float depth = Simplex.noise3d(seed, 2, 0.56, 1.7f, position.x, position.y, position.z) / 2f;
|
||||
return c1.write(out).lerp(c2, Mathf.clamp(Mathf.round(depth, 0.15f))).a(0.2f);
|
||||
out.set(c1).lerp(c2, Mathf.clamp(Mathf.round(depth, 0.15f))).a(1f - 0.2f).toFloatBits();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -413,11 +413,22 @@ public class Mods implements Loadable{
|
||||
|
||||
/** Removes a mod file and marks it for requiring a restart. */
|
||||
public void removeMod(LoadedMod mod){
|
||||
if(!android && mod.loader != null){
|
||||
try{
|
||||
ClassLoaderCloser.close(mod.loader);
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
boolean deleted = true;
|
||||
|
||||
if(mod.loader != null){
|
||||
if(android){
|
||||
//Try to remove cache for Android 14 security problem
|
||||
Fi cacheDir = new Fi(Core.files.getCachePath()).child("mods");
|
||||
Fi modCacheDir = cacheDir.child(mod.file.nameWithoutExtension());
|
||||
if(modCacheDir.exists()){
|
||||
deleted = modCacheDir.deleteDirectory();
|
||||
}
|
||||
}else{
|
||||
try{
|
||||
ClassLoaderCloser.close(mod.loader);
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +436,7 @@ public class Mods implements Loadable{
|
||||
mod.root.delete();
|
||||
}
|
||||
|
||||
boolean deleted = mod.file.isDirectory() ? mod.file.deleteDirectory() : mod.file.delete();
|
||||
deleted &= mod.file.isDirectory() ? mod.file.deleteDirectory() : mod.file.delete();
|
||||
|
||||
if(!deleted){
|
||||
ui.showErrorMessage("@mod.delete.error");
|
||||
@@ -1112,6 +1123,11 @@ public class Mods implements Loadable{
|
||||
//close the classloader for jar mods
|
||||
if(!android){
|
||||
ClassLoaderCloser.close(other.loader);
|
||||
}else if(other.loader != null){
|
||||
//Try to remove cache for Android 14 security problem
|
||||
Fi cacheDir = new Fi(Core.files.getCachePath()).child("mods");
|
||||
Fi modCacheDir = cacheDir.child(other.file.nameWithoutExtension());
|
||||
modCacheDir.deleteDirectory();
|
||||
}
|
||||
|
||||
//close zip file
|
||||
|
||||
@@ -91,6 +91,10 @@ public class Administration{
|
||||
dosBlacklist.add(address);
|
||||
}
|
||||
|
||||
public synchronized void unBlacklistDos(String address){
|
||||
dosBlacklist.remove(address);
|
||||
}
|
||||
|
||||
public synchronized boolean isDosBlacklisted(String address){
|
||||
return dosBlacklist.contains(address);
|
||||
}
|
||||
|
||||
@@ -113,6 +113,8 @@ public class ArcNetProvider implements NetProvider{
|
||||
|
||||
//kill connections above the limit to prevent spam
|
||||
if((playerLimitCache > 0 && server.getConnections().length > playerLimitCache) || netServer.admins.isDosBlacklisted(ip)){
|
||||
Log.info("Closing connection @ - IP marked as a potential DOS attack.", ip);
|
||||
|
||||
connection.close(DcReason.closed);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ public class CrashHandler{
|
||||
report += "Report this at " + Vars.reportIssueURL + "\n\n";
|
||||
}
|
||||
|
||||
var enabledMods = mods == null ? null : mods.list().select(m -> m.shouldBeEnabled() && m.isSupported());
|
||||
|
||||
return report
|
||||
+ "Version: " + Version.combined() + (Version.buildDate.equals("unknown") ? "" : " (Built " + Version.buildDate + ")") + (Vars.headless ? " (Server)" : "") + "\n"
|
||||
+ "Date: " + new SimpleDateFormat("MMMM d, yyyy HH:mm:ss a", Locale.getDefault()).format(new Date()) + "\n"
|
||||
@@ -37,7 +39,7 @@ public class CrashHandler{
|
||||
+ "Runtime Available Memory: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "mb\n"
|
||||
+ "Cores: " + OS.cores + "\n"
|
||||
+ (cause == null ? "" : "Likely Cause: " + cause.meta.displayName + " (" + cause.name + " v" + cause.meta.version + ")\n")
|
||||
+ (mods == null ? "<no mod init>" : "Mods: " + (!mods.list().contains(LoadedMod::shouldBeEnabled) ? "none (vanilla)" : mods.list().select(LoadedMod::shouldBeEnabled).toString(", ", mod -> mod.name + ":" + mod.meta.version)))
|
||||
+ (enabledMods == null ? "<no mod init>" : "Mods: " + (enabledMods.isEmpty() ? "none (vanilla)" : enabledMods.toString(", ", mod -> mod.name + ":" + mod.meta.version)))
|
||||
+ "\n\n" + error;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ public enum Achievement{
|
||||
|
||||
have10mItems(SStat.totalCampaignItems, 10_000_000),
|
||||
killEclipseDuo,
|
||||
killMassDriver,
|
||||
|
||||
completeErekir,
|
||||
completeSerpulo,
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.SectorInfo.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -394,6 +395,10 @@ public class GameService{
|
||||
if(e.unit.type == UnitTypes.eclipse && e.bullet.owner instanceof TurretBuild turret && turret.block == Blocks.duo){
|
||||
killEclipseDuo.complete();
|
||||
}
|
||||
|
||||
if(e.bullet.type instanceof MassDriverBolt){
|
||||
killMassDriver.complete();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.content.TechTree.*;
|
||||
import mindustry.ctype.*;
|
||||
@@ -348,11 +347,31 @@ public class Planet extends UnlockableContent{
|
||||
return mat.setToTranslation(position).rotate(Vec3.Y, getRotation());
|
||||
}
|
||||
|
||||
/** Regenerates the planet mesh. For debugging only. */
|
||||
/** Regenerates the planet mesh. */
|
||||
public void reloadMesh(){
|
||||
if(headless) return;
|
||||
|
||||
if(mesh != null){
|
||||
mesh.dispose();
|
||||
}
|
||||
mesh = meshLoader.get();
|
||||
}
|
||||
|
||||
public void reloadMeshAsync(){
|
||||
if(headless) return;
|
||||
|
||||
mainExecutor.submit(() -> {
|
||||
var newMesh = meshLoader.get();
|
||||
|
||||
Core.app.post(() -> {
|
||||
if(mesh != null){
|
||||
mesh.dispose();
|
||||
}
|
||||
mesh = newMesh;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
@@ -383,7 +402,6 @@ public class Planet extends UnlockableContent{
|
||||
}
|
||||
|
||||
if(generator != null){
|
||||
Noise.setSeed(sectorSeed < 0 ? id + 1 : sectorSeed);
|
||||
|
||||
for(Sector sector : sectors){
|
||||
generator.generateSector(sector);
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.io.*;
|
||||
|
||||
public class Fonts{
|
||||
private static final String mainFont = "fonts/font.woff";
|
||||
private static final ObjectSet<String> unscaled = ObjectSet.with("iconLarge");
|
||||
private static final ObjectSet<String> unscaled = ObjectSet.with("iconLarge", "logic");
|
||||
private static ObjectIntMap<String> unicodeIcons = new ObjectIntMap<>();
|
||||
private static IntMap<String> unicodeToName = new IntMap<>();
|
||||
private static ObjectMap<String, String> stringIcons = new ObjectMap<>();
|
||||
|
||||
@@ -152,7 +152,6 @@ public class KeybindDialog extends Dialog{
|
||||
rebindKey = name;
|
||||
|
||||
rebindDialog.titleTable.getCells().first().pad(4);
|
||||
|
||||
rebindDialog.addListener(new InputListener(){
|
||||
@Override
|
||||
public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){
|
||||
|
||||
@@ -365,7 +365,7 @@ public class ModsDialog extends BaseDialog{
|
||||
|
||||
private @Nullable String getStateDetails(LoadedMod item){
|
||||
if(item.isOutdated()){
|
||||
return Core.bundle.format("mod.outdated.details", item.isJava() ? minJavaModGameVersion : minModGameVersion);
|
||||
return "@mod.incompatiblemod.details";
|
||||
}else if(item.isBlacklisted()){
|
||||
return "@mod.blacklisted.details";
|
||||
}else if(!item.isSupported()){
|
||||
|
||||
@@ -652,6 +652,17 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
if(scene.getDialog() == PlanetDialog.this && (scene.getHoverElement() == null || !scene.getHoverElement().isDescendantOf(e -> e instanceof ScrollPane))){
|
||||
scene.setScrollFocus(PlanetDialog.this);
|
||||
|
||||
if(debugSectorAttackEdit){
|
||||
int timeShift = input.keyDown(KeyCode.rightBracket) ? 1 : input.keyDown(KeyCode.leftBracket) ? -1 : 0;
|
||||
if(timeShift != 0){
|
||||
universe.setSeconds(universe.secondsf() + timeShift * Time.delta * 2.5f);
|
||||
}
|
||||
|
||||
if(input.keyTap(KeyCode.r)){
|
||||
state.planet.reloadMeshAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if(debugSectorAttackEdit && input.ctrl() && input.keyTap(KeyCode.s)){
|
||||
try{
|
||||
PlanetData data = new PlanetData();
|
||||
@@ -665,10 +676,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
data.presets.put(sector.preset.name, sector.id);
|
||||
}
|
||||
}
|
||||
Log.info("Saving sectors for @: @ presets, @ procedural attack sectors", state.planet.name, data.presets.size, attack.size);
|
||||
data.attackSectors = attack.toArray();
|
||||
files.local("planets/" + state.planet.name + ".json").writeString(JsonIO.write(data));
|
||||
|
||||
Vars.ui.showInfoFade("@editor.saved");
|
||||
ui.showInfoFade("@editor.saved");
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
}
|
||||
@@ -1137,7 +1149,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
void selectSector(Sector sector){
|
||||
public void selectSector(Sector sector){
|
||||
selected = sector;
|
||||
updateSelected();
|
||||
}
|
||||
@@ -1262,7 +1274,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
if(sector.isAttacked()){
|
||||
addSurvivedInfo(sector, stable, false);
|
||||
}else if(sector.hasBase() && sector.planet.campaignRules.sectorInvasion && sector.near().contains(Sector::hasEnemyBase)){
|
||||
}else if(sector.hasBase() && sector.planet.campaignRules.sectorInvasion && sector.near().contains(s -> s.hasEnemyBase() && (s.preset == null || !s.preset.requireUnlock))){
|
||||
stable.add("@sectors.vulnerable");
|
||||
stable.row();
|
||||
}else if(!sector.hasBase() && sector.hasEnemyBase()){
|
||||
@@ -1287,6 +1299,15 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
|
||||
if((sector.hasBase() && mode == look) || canSelect(sector) || (sector.preset != null && sector.preset.alwaysUnlocked) || debugSelect){
|
||||
if(Vars.showSectorSubmissions){
|
||||
String link = SectorSubmissions.getSectorThread(sector);
|
||||
if(link != null){
|
||||
stable.button("@sectors.viewsubmission", Icon.link, () -> {
|
||||
Core.app.openURI(link);
|
||||
}).growX().height(54f).minWidth(170f).padTop(2f).row();
|
||||
}
|
||||
}
|
||||
|
||||
stable.button(
|
||||
mode == select ? "@sectors.select" :
|
||||
sector.isBeingPlayed() ? "@sectors.resume" :
|
||||
|
||||
@@ -557,7 +557,7 @@ public class SchematicsDialog extends BaseDialog{
|
||||
next.pack();
|
||||
float w = next.getWidth() + Scl.scl(9f);
|
||||
|
||||
if(w + sum >= Core.graphics.getWidth() * 0.9f){
|
||||
if(w*2f + sum >= Core.graphics.getWidth() * 0.9f){
|
||||
p.add(current).row();
|
||||
current = new Table();
|
||||
current.left();
|
||||
|
||||
@@ -838,8 +838,12 @@ public class HudFragment{
|
||||
|
||||
t.add(new SideBar(() -> player.dead() ? 0f : 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() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color() : Pal.health);
|
||||
|
||||
Boolp playerHasPayloads = () -> player.unit() instanceof Payloadc pay && !pay.payloads().isEmpty();
|
||||
Floatp playerPayloadCapacityUsed = () -> player.unit() instanceof Payloadc pay ? pay.payloadUsed() / player.unit().type().payloadCapacity : 0f;
|
||||
|
||||
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : playerHasPayloads.get() ? playerPayloadCapacityUsed.get() : player.unit().healthf(), () -> !(player.displayAmmo() || playerHasPayloads.get()), false)).width(bw).growY().padLeft(pad).update(b -> {
|
||||
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color() : playerHasPayloads.get() ? Pal.items : Pal.health);
|
||||
});
|
||||
|
||||
t.getChildren().get(1).toFront();
|
||||
|
||||
@@ -251,12 +251,16 @@ public class PlacementFragment{
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.blockInfo)){
|
||||
var build = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
Block hovering = build == null ? null : build instanceof ConstructBuild c ? c.current : build.block;
|
||||
Block displayBlock = menuHoverBlock != null ? menuHoverBlock : input.block != null ? input.block : hovering;
|
||||
if(displayBlock != null && displayBlock.unlockedNow()){
|
||||
ui.content.show(displayBlock);
|
||||
Events.fire(new BlockInfoEvent());
|
||||
if(hovered() instanceof Unit unit && unit.type.unlockedNow()){
|
||||
ui.content.show(unit.type());
|
||||
}else{
|
||||
var build = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
Block hovering = build == null ? null : build instanceof ConstructBuild c ? c.current : build.block;
|
||||
Block displayBlock = menuHoverBlock != null ? menuHoverBlock : input.block != null ? input.block : hovering;
|
||||
if(displayBlock != null && displayBlock.unlockedNow()){
|
||||
ui.content.show(displayBlock);
|
||||
Events.fire(new BlockInfoEvent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ public class ConstructBlock extends Block{
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void deconstructFinish(Tile tile, Block block, Unit builder){
|
||||
if(tile == null) return;
|
||||
|
||||
Team team = tile.team();
|
||||
if(!headless && fogControl.isVisibleTile(Vars.player.team(), tile.x, tile.y)){
|
||||
block.breakEffect.at(tile.drawx(), tile.drawy(), block.size, block.mapColor);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package mindustry.world.blocks;
|
||||
|
||||
public class TileBitmask{
|
||||
/** Autotile bitmasks for 8-directional sprites (see <a href="https://github.com/GglLfr/tile-gen">tile-gen</a>)*/
|
||||
public static final int[] values = {
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
3, 4, 3, 4, 15, 40, 15, 20, 3, 4, 3, 4, 15, 40, 15, 20,
|
||||
5, 28, 5, 28, 29, 10, 29, 23, 5, 28, 5, 28, 31, 11, 31, 32,
|
||||
3, 4, 3, 4, 15, 40, 15, 20, 3, 4, 3, 4, 15, 40, 15, 20,
|
||||
2, 30, 2, 30, 9, 46, 9, 22, 2, 30, 2, 30, 14, 44, 14, 6,
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
|
||||
5, 8, 5, 8, 29, 35, 29, 33, 5, 8, 5, 8, 31, 34, 31, 7,
|
||||
3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
|
||||
2, 1, 2, 1, 9, 45, 9, 19, 2, 1, 2, 1, 14, 18, 14, 13,
|
||||
};
|
||||
}
|
||||
@@ -223,7 +223,7 @@ public class Accelerator extends Block{
|
||||
}
|
||||
|
||||
public boolean canLaunch(){
|
||||
return isValid() && state.isCampaign() && efficiency > 0f && power.graph.getBatteryStored() >= powerBufferRequirement-0.00001f && progress >= 1f && !launching;
|
||||
return isValid() && !net.client() && state.isCampaign() && efficiency > 0f && power.graph.getBatteryStored() >= powerBufferRequirement-0.00001f && progress >= 1f && !launching;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -383,21 +383,24 @@ public class LandingPad extends Block{
|
||||
t.background(Styles.black6);
|
||||
|
||||
t.button(Icon.downOpen, Styles.clearNonei, 40f, () -> {
|
||||
if(config != null && state.isCampaign()){
|
||||
for(Sector sector : state.getPlanet().sectors){
|
||||
if(sector.hasBase() && sector != state.getSector() && sector.info.destination != state.getSector() && sector.info.hasExport(config)){
|
||||
sector.info.destination = state.getSector();
|
||||
sector.saveInfo();
|
||||
}
|
||||
}
|
||||
state.getSector().info.refreshImportRates(state.getPlanet());
|
||||
if(config == null || !state.isCampaign()) return;
|
||||
|
||||
for(Sector sector : state.getPlanet().sectors){
|
||||
if(!canRedirectExports(sector)) continue;
|
||||
sector.info.destination = state.getSector();
|
||||
sector.saveInfo();
|
||||
}
|
||||
}).disabled(b -> config == null || !state.isCampaign() || (!state.getPlanet().sectors.contains(s -> s.hasBase() && s.info.hasExport(config) && s.info.destination != state.getSector())))
|
||||
state.getSector().info.refreshImportRates(state.getPlanet());
|
||||
}).disabled(button -> config == null || !state.isCampaign() || (!state.getPlanet().sectors.contains(this::canRedirectExports)))
|
||||
.tooltip("@sectors.redirect").get();
|
||||
}).fillX().left();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canRedirectExports(Sector sector){
|
||||
return sector.hasBase() && sector != state.getSector() && sector.info.hasExport(config) && sector.info.destination != state.getSector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Table table){
|
||||
super.display(table);
|
||||
@@ -416,14 +419,13 @@ public class LandingPad extends Block{
|
||||
|
||||
int sources = 0;
|
||||
float perSecond = 0f;
|
||||
for(var s : state.getPlanet().sectors){
|
||||
if(s != state.getSector() && s.hasBase() && s.info.destination == state.getSector()){
|
||||
float amount = s.info.getExport(config);
|
||||
if(amount > 0){
|
||||
sources ++;
|
||||
perSecond += s.info.getExport(config);
|
||||
}
|
||||
}
|
||||
for(var otherSector : state.getPlanet().sectors){
|
||||
if(otherSector == state.getSector() || !otherSector.hasBase() || otherSector.info.destination != state.getSector()) continue;
|
||||
|
||||
float amount = otherSector.info.getExport(config);
|
||||
if(amount <= 0) continue;
|
||||
sources ++;
|
||||
perSecond += amount;
|
||||
}
|
||||
|
||||
String str = Core.bundle.format("landing.sources", sources == 0 ? Core.bundle.get("none") : sources);
|
||||
|
||||
@@ -96,8 +96,7 @@ public class DirectionalUnloader extends Block{
|
||||
front.handleItem(this, item);
|
||||
back.items.remove(item, 1);
|
||||
back.itemTaken(item);
|
||||
offset ++;
|
||||
offset %= itemc;
|
||||
offset = item.id + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ public class MassDriver extends Block{
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(1f - reloadCounter / reload);
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(1f - reloadCounter);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@@ -299,13 +299,13 @@ public class MassDriver extends Block{
|
||||
|
||||
bullet.create(this, team,
|
||||
x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation),
|
||||
angle, -1f, bulletSpeed, bulletLifetime, data);
|
||||
angle, totalUsed/2f, bulletSpeed, bulletLifetime, data);
|
||||
|
||||
shootEffect.at(x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation), angle);
|
||||
smokeEffect.at(x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation), angle);
|
||||
|
||||
Effect.shake(shake, shake, this);
|
||||
|
||||
|
||||
shootSound.at(tile, Mathf.random(0.9f, 1.1f));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import mindustry.graphics.*;
|
||||
import mindustry.graphics.MultiPacker.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -77,8 +78,11 @@ public class Floor extends Block{
|
||||
public int blendId = -1;
|
||||
/** If >0, this floor is drawn as parts of a large texture. */
|
||||
public int tilingVariants = 0;
|
||||
/** If true, this floor uses autotiling; variants are not supported. See https://github.com/GglLfr/tile-gen*/
|
||||
public boolean autotile = false;
|
||||
|
||||
protected TextureRegion[][][] tilingRegions;
|
||||
protected TextureRegion[] autotileRegions;
|
||||
protected int tilingSize;
|
||||
protected TextureRegion[][] edges;
|
||||
protected Seq<Floor> blenders = new Seq<>();
|
||||
@@ -104,6 +108,10 @@ public class Floor extends Block{
|
||||
public void load(){
|
||||
super.load();
|
||||
|
||||
if(autotile){
|
||||
variants = 0;
|
||||
}
|
||||
|
||||
int tsize = (int)(tilesize / Draw.scl);
|
||||
|
||||
if(tilingVariants > 0 && !headless){
|
||||
@@ -132,6 +140,13 @@ public class Floor extends Block{
|
||||
variantRegions[0] = Core.atlas.find(name);
|
||||
}
|
||||
|
||||
if(autotile){
|
||||
autotileRegions = new TextureRegion[47];
|
||||
for(int i = 0; i < 47; i++){
|
||||
autotileRegions[i] = Core.atlas.find(name + "-" + i);
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.atlas.has(name + "-edge")){
|
||||
edges = Core.atlas.find(name + "-edge").split(tsize, tsize);
|
||||
}
|
||||
@@ -208,6 +223,17 @@ public class Floor extends Block{
|
||||
int index = Mathf.randomSeed(Point2.pack(tile.x / tilingSize, tile.y / tilingSize), 0, tilingVariants - 1);
|
||||
TextureRegion[][] regions = tilingRegions[index];
|
||||
Draw.rect(regions[tile.x % tilingSize][tilingSize - 1 - tile.y % tilingSize], tile.worldx(), tile.worldy());
|
||||
}else if(autotile){
|
||||
int bits = 0;
|
||||
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = tile.nearby(Geometry.d8[i]);
|
||||
if(other != null && other.floor().blendGroup == blendGroup){
|
||||
bits |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(autotileRegions[TileBitmask.values[bits]], tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Draw.rect(variantRegions[variant(tile.x, tile.y)], tile.worldx(), tile.worldy());
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ public class SteamVent extends Floor{
|
||||
parent.drawBase(tile);
|
||||
|
||||
if(checkAdjacent(tile)){
|
||||
Mathf.rand.setSeed(tile.pos());
|
||||
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx() - tilesize, tile.worldy() - tilesize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
@@ -35,7 +36,7 @@ public class CanvasBlock extends Block{
|
||||
public @Load("@-corner1") TextureRegion corner1;
|
||||
public @Load("@-corner2") TextureRegion corner2;
|
||||
|
||||
protected @Nullable Pixmap previewPixmap;
|
||||
protected @Nullable Pixmap previewPixmap; // please use only for previews
|
||||
protected @Nullable Texture previewTexture;
|
||||
protected int tempBlend = 0;
|
||||
|
||||
@@ -49,7 +50,7 @@ public class CanvasBlock extends Block{
|
||||
|
||||
config(byte[].class, (CanvasBuild build, byte[] bytes) -> {
|
||||
if(build.data.length == bytes.length){
|
||||
build.data = bytes;
|
||||
System.arraycopy(bytes, 0, build.data, 0, bytes.length);
|
||||
build.updateTexture();
|
||||
}
|
||||
});
|
||||
@@ -65,13 +66,15 @@ public class CanvasBlock extends Block{
|
||||
bitsPerPixel = Mathf.log2(Mathf.nextPowerOfTwo(palette.length));
|
||||
|
||||
clipSize = Math.max(clipSize, size * 8 - padding);
|
||||
|
||||
previewPixmap = new Pixmap(canvasSize, canvasSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
//only draw the preview in schematics, as it lags otherwise
|
||||
if(!plan.worldContext && plan.config instanceof byte[] data){
|
||||
Pixmap pix = makePixmap(data);
|
||||
Pixmap pix = makePixmap(data, previewPixmap);
|
||||
|
||||
if(previewTexture == null){
|
||||
previewTexture = new Texture(pix);
|
||||
@@ -123,20 +126,15 @@ public class CanvasBlock extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
/** returns the same pixmap instance each time, use with care */
|
||||
public Pixmap makePixmap(byte[] data){
|
||||
if(previewPixmap == null){
|
||||
previewPixmap = new Pixmap(canvasSize, canvasSize);
|
||||
}
|
||||
|
||||
public Pixmap makePixmap(byte[] data, Pixmap target){
|
||||
int bpp = bitsPerPixel;
|
||||
int pixels = canvasSize * canvasSize;
|
||||
for(int i = 0; i < pixels; i++){
|
||||
int bitOffset = i * bpp;
|
||||
int pal = getByte(data, bitOffset);
|
||||
previewPixmap.set(i % canvasSize, i / canvasSize, palette[pal]);
|
||||
target.set(i % canvasSize, i / canvasSize, palette[pal]);
|
||||
}
|
||||
return previewPixmap;
|
||||
return target;
|
||||
}
|
||||
|
||||
protected int getByte(byte[] data, int bitOffset){
|
||||
@@ -152,11 +150,41 @@ public class CanvasBlock extends Block{
|
||||
public @Nullable Texture texture;
|
||||
public byte[] data = new byte[Mathf.ceil(canvasSize * canvasSize * bitsPerPixel / 8f)];
|
||||
public int blending;
|
||||
|
||||
protected boolean updated = false;
|
||||
|
||||
public void setPixel(int pos, int index){
|
||||
if(pos < canvasSize * canvasSize && pos >= 0 && index >= 0 && index < palette.length){
|
||||
setByte(data, pos * bitsPerPixel, index);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPixel(int x, int y, int index){
|
||||
if(x >= 0 && y >= 0 && x < canvasSize && y < canvasSize && index >= 0 && index < palette.length){
|
||||
setByte(data, (y * canvasSize + x) * bitsPerPixel, index);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
public double getPixel(int pos){
|
||||
if(pos >= 0 && pos < canvasSize * canvasSize){
|
||||
return getByte(data, pos * bitsPerPixel);
|
||||
}
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
public int getPixel(int x, int y){
|
||||
if(x >= 0 && y >= 0 && x < canvasSize && y < canvasSize){
|
||||
return getByte(data, (y * canvasSize + x) * bitsPerPixel);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void updateTexture(){
|
||||
if(headless) return;
|
||||
|
||||
Pixmap pix = makePixmap(data);
|
||||
Pixmap pix = makePixmap(data, previewPixmap);
|
||||
if(texture != null){
|
||||
texture.draw(pix);
|
||||
}else{
|
||||
@@ -214,7 +242,8 @@ public class CanvasBlock extends Block{
|
||||
super.draw();
|
||||
}
|
||||
|
||||
if(texture == null){
|
||||
if(texture == null || updated){
|
||||
updated = false;
|
||||
updateTexture();
|
||||
}
|
||||
Tmp.tr1.set(texture);
|
||||
@@ -237,6 +266,14 @@ public class CanvasBlock extends Block{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
return switch(sensor){
|
||||
case displayWidth, displayHeight -> canvasSize;
|
||||
default -> super.sense(sensor);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
@@ -252,12 +289,17 @@ public class CanvasBlock extends Block{
|
||||
table.button(Icon.pencil, Styles.cleari, () -> {
|
||||
Dialog dialog = new Dialog();
|
||||
|
||||
Pixmap pix = makePixmap(data);
|
||||
Pixmap pix = makePixmap(data, new Pixmap(canvasSize, canvasSize));
|
||||
Texture texture = new Texture(pix);
|
||||
int[] curColor = {palette[0]};
|
||||
boolean[] modified = {false};
|
||||
boolean[] fill = {false};
|
||||
|
||||
|
||||
dialog.hidden(() -> {
|
||||
texture.dispose();
|
||||
pix.dispose();
|
||||
});
|
||||
|
||||
dialog.resized(dialog::hide);
|
||||
|
||||
dialog.cont.table(Tex.pane, body -> {
|
||||
@@ -395,7 +437,6 @@ public class CanvasBlock extends Block{
|
||||
dialog.buttons.button("@ok", Icon.ok, () -> {
|
||||
if(modified[0]){
|
||||
configure(packPixmap(pix));
|
||||
texture.dispose();
|
||||
}
|
||||
dialog.hide();
|
||||
});
|
||||
|
||||
@@ -78,6 +78,7 @@ public class LogicDisplay extends Block{
|
||||
public float stroke = 1f;
|
||||
public LongQueue commands = new LongQueue(256);
|
||||
public @Nullable Mat transform;
|
||||
public long operations;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
@@ -111,6 +112,7 @@ public class LogicDisplay extends Block{
|
||||
return switch(sensor){
|
||||
case displayWidth, displayHeight -> displaySize;
|
||||
case bufferUsage -> commands.size;
|
||||
case operations -> operations;
|
||||
default -> super.sense(sensor);
|
||||
};
|
||||
}
|
||||
@@ -121,6 +123,8 @@ public class LogicDisplay extends Block{
|
||||
for(int i = 0; i < added; i++){
|
||||
commands.addLast(graphicsBuffer.items[i]);
|
||||
}
|
||||
|
||||
operations++;
|
||||
}
|
||||
|
||||
public void processCommands(){
|
||||
|
||||
@@ -13,6 +13,7 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -27,25 +28,6 @@ public class TileableLogicDisplay extends LogicDisplay{
|
||||
public @Load(value = "@-#", length = 47) TextureRegion[] tileRegion;
|
||||
public @Load("@-back") TextureRegion backRegion;
|
||||
|
||||
static final int[] bitmasks = {
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
3, 4, 3, 4, 15, 40, 15, 20, 3, 4, 3, 4, 15, 40, 15, 20,
|
||||
5, 28, 5, 28, 29, 10, 29, 23, 5, 28, 5, 28, 31, 11, 31, 32,
|
||||
3, 4, 3, 4, 15, 40, 15, 20, 3, 4, 3, 4, 15, 40, 15, 20,
|
||||
2, 30, 2, 30, 9, 46, 9, 22, 2, 30, 2, 30, 14, 44, 14, 6,
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
39, 36, 39, 36, 27, 16, 27, 24, 39, 36, 39, 36, 27, 16, 27, 24,
|
||||
38, 37, 38, 37, 17, 41, 17, 43, 38, 37, 38, 37, 26, 21, 26, 25,
|
||||
3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
|
||||
5, 8, 5, 8, 29, 35, 29, 33, 5, 8, 5, 8, 31, 34, 31, 7,
|
||||
3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
|
||||
2, 1, 2, 1, 9, 45, 9, 19, 2, 1, 2, 1, 14, 18, 14, 13,
|
||||
};
|
||||
|
||||
public TileableLogicDisplay(String name){
|
||||
super(name);
|
||||
|
||||
@@ -247,7 +229,7 @@ public class TileableLogicDisplay extends LogicDisplay{
|
||||
|
||||
Draw.z(Layer.block + 0.02f);
|
||||
|
||||
Draw.rect(tileRegion[bitmasks[bits]], x, y);
|
||||
Draw.rect(tileRegion[TileBitmask.values[bits]], x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -48,12 +48,12 @@ public class PayloadConveyor extends Block{
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
int ntrns = 1 + size/2;
|
||||
int ntrns = size;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Building other = world.build(x + Geometry.d4x[i] * ntrns, y + Geometry.d4y[i] * ntrns);
|
||||
if(other != null && other.block.outputsPayload && other.block.size == size){
|
||||
Drawf.selected(other.tileX(), other.tileY(), other.block, other.team.color);
|
||||
Tile tile = world.tile(x + Geometry.d4x[i] * ntrns, y + Geometry.d4y[i] * ntrns);
|
||||
if(tile != null && tile.build != null && tile.isCenter() && tile.build.block.outputsPayload && tile.build.block.size == size && (i == rotation || tile.block().rotate && i == (tile.build.rotation + 2) % 4)){
|
||||
Drawf.selected(tile.x, tile.y, tile.block(), tile.build.team.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mindustry.world.blocks.power;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
@@ -58,14 +57,30 @@ public class LightBlock extends Block{
|
||||
Placement.calculateNodes(points, this, rotation, (point, other) -> point.dst2(other) <= placeRadius2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minimapColor(Tile tile){
|
||||
var build = (LightBuild)tile.build;
|
||||
//make sure A is 255
|
||||
return build == null ? 0 : build.color | 0xff;
|
||||
}
|
||||
|
||||
public class LightBuild extends Building{
|
||||
public int color = Pal.accent.rgba();
|
||||
public float smoothTime = 1f;
|
||||
|
||||
@Override
|
||||
public void configured(Unit player, Object value){
|
||||
super.configured(player, value);
|
||||
|
||||
if(!headless) renderer.minimap.update(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, double p1, double p2, double p3, double p4){
|
||||
if(type == LAccess.color){
|
||||
color = Tmp.c1.fromDouble(p1).rgba8888();
|
||||
|
||||
if(!headless) renderer.minimap.update(tile);
|
||||
}
|
||||
|
||||
super.control(type, p1, p2, p3, p4);
|
||||
@@ -80,11 +95,9 @@ public class LightBlock extends Block{
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
Draw.blend(Blending.additive);
|
||||
Draw.color(Tmp.c1.set(color), efficiency * 0.3f);
|
||||
Draw.color(Tmp.c1.set(color).a(0.4f));
|
||||
Draw.rect(topRegion, x, y);
|
||||
Draw.color();
|
||||
Draw.blend();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -328,6 +328,11 @@ public class PowerNode extends PowerBlock{
|
||||
}
|
||||
});
|
||||
|
||||
//uncomment for debugging connection translation issues in schematics
|
||||
//Draw.color(Color.red);
|
||||
//Lines.line(plan.drawx(), plan.drawy(), px * tilesize, py * tilesize);
|
||||
//Draw.color();
|
||||
|
||||
if(otherReq == null || otherReq.block == null) continue;
|
||||
|
||||
drawLaser(plan.drawx(), plan.drawy(), otherReq.drawx(), otherReq.drawy(), size, otherReq.block.size);
|
||||
|
||||
@@ -262,7 +262,7 @@ public class BeamDrill extends Block{
|
||||
time %= drillTime;
|
||||
}
|
||||
|
||||
if(timer(timerDump, dumpTime)){
|
||||
if(timer(timerDump, dumpTime / timeScale)){
|
||||
dump();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public class BurstDrill extends Drill{
|
||||
|
||||
if(invertTime > 0f) invertTime -= delta() / invertedTime;
|
||||
|
||||
if(timer(timerDump, dumpTime)){
|
||||
if(timer(timerDump, dumpTime / timeScale)){
|
||||
dump(items.has(dominantItem) ? dominantItem : null);
|
||||
}
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ public class Drill extends Block{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(timer(timerDump, dumpTime)){
|
||||
if(timer(timerDump, dumpTime / timeScale)){
|
||||
dump(dominantItem != null && items.has(dominantItem) ? dominantItem : null);
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ public class Separator extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
if(timer(timerDump, dumpTime)){
|
||||
if(timer(timerDump, dumpTime / timeScale)){
|
||||
dump();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ public class WallCrafter extends Block{
|
||||
|
||||
totalTime += edelta() * warmup * (eff <= 0f ? 0f : 1f);
|
||||
|
||||
if(timer(timerDump, dumpTime)){
|
||||
if(timer(timerDump, dumpTime / timeScale)){
|
||||
dump(output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public class ConsumePayloadDynamic extends Consume{
|
||||
var inv = build.getPayloads();
|
||||
var pay = payloads.get(build);
|
||||
|
||||
table.clear();
|
||||
table.table(c -> {
|
||||
int i = 0;
|
||||
for(var stack : pay){
|
||||
|
||||
@@ -16,7 +16,7 @@ public class BuildVisibility{
|
||||
sandboxOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.infiniteResources),
|
||||
campaignOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.isCampaign()),
|
||||
legacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.isCampaign() && Vars.state.getPlanet().campaignRules.legacyLaunchPads) && Blocks.advancedLaunchPad != null && Blocks.advancedLaunchPad.unlocked()),
|
||||
notLegacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.isCampaign() && !Vars.state.getPlanet().campaignRules.legacyLaunchPads)),
|
||||
notLegacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.rules.infiniteResources || Vars.state.isCampaign() && !Vars.state.getPlanet().campaignRules.legacyLaunchPads)),
|
||||
lightingOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign()),
|
||||
ammoOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.unitAmmo),
|
||||
fogOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor);
|
||||
|
||||
@@ -710,7 +710,7 @@ public class StatValues{
|
||||
|
||||
if(type.status != StatusEffects.none){
|
||||
sep(bt, (type.status.hasEmoji() ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" +
|
||||
((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds"))).with(c -> withTooltip(c, type.status));
|
||||
Strings.autoFixed(type.statusDuration / 60f, 1) + "[lightgray] " + Core.bundle.get("unit.seconds"))).with(c -> withTooltip(c, type.status));
|
||||
}
|
||||
|
||||
if(!type.targetMissiles){
|
||||
|
||||
Reference in New Issue
Block a user