Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features

 Conflicts:
	core/src/mindustry/content/Blocks.java
This commit is contained in:
Anuken
2021-06-05 11:57:14 -04:00
94 changed files with 1030 additions and 714 deletions

View File

@@ -73,7 +73,8 @@ public class Vars implements Loadable{
/** URL to the JSON file containing all the BE servers. Only queried in BE. */
public static final String serverJsonBeURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
/** URL to the JSON file containing all the stable servers. */
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_v6.json";
//TODO this uses BE servers until full v7 release, there's no point in displaying v6 at all
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
/** URL of the github issue report template.*/
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?labels=bug&template=bug_report.md";
/** list of built-in servers.*/

View File

@@ -15,7 +15,7 @@ import static mindustry.Vars.*;
public class LogicAI extends AIController{
/** Minimum delay between item transfers. */
public static final float transferDelay = 60f * 2f;
public static final float transferDelay = 60f * 1.5f;
/** Time after which the unit resets its controlled and reverts to a normal unit. */
public static final float logicControlTimeout = 10f * 60f;

View File

@@ -1168,22 +1168,24 @@ public class Blocks implements ContentList{
reloadTime = 200f;
range = 440f;
consumes.power(1.75f);
bullet = new MassDriverBolt();
}};
//special transport blocks
duct = new Duct("duct"){{
requirements(Category.distribution, with(Items.graphite, 5, Items.copper, 5));
speed = 5f;
requirements(Category.distribution, with(Items.graphite, 5, Items.metaglass, 2));
speed = 4f;
}};
ductRouter = new DuctRouter("duct-router"){{
requirements(Category.distribution, with(Items.graphite, 10, Items.copper, 5));
speed = 5f;
requirements(Category.distribution, with(Items.graphite, 10, Items.metaglass, 4));
speed = 4f;
}};
ductBridge = new DuctBridge("duct-bridge"){{
requirements(Category.distribution, with(Items.graphite, 20, Items.copper, 15));
requirements(Category.distribution, with(Items.graphite, 20, Items.metaglass, 8));
speed = 4f;
}};
//endregion
@@ -2048,7 +2050,7 @@ public class Blocks implements ContentList{
requirements(Category.units, with(Items.copper, 150, Items.lead, 130, Items.metaglass, 120));
plans = Seq.with(
new UnitPlan(UnitTypes.risso, 60f * 45f, with(Items.silicon, 20, Items.metaglass, 35)),
new UnitPlan(UnitTypes.retusa, 60f * 60f, with(Items.silicon, 15, Items.metaglass, 25, Items.titanium, 20))
new UnitPlan(UnitTypes.retusa, 60f * 50f, with(Items.silicon, 15, Items.metaglass, 25, Items.titanium, 20))
);
size = 3;
consumes.power(1.2f);
@@ -2090,7 +2092,8 @@ public class Blocks implements ContentList{
new UnitType[]{UnitTypes.poly, UnitTypes.mega},
new UnitType[]{UnitTypes.minke, UnitTypes.bryde},
new UnitType[]{UnitTypes.pulsar, UnitTypes.quasar},
new UnitType[]{UnitTypes.atrax, UnitTypes.spiroct}
new UnitType[]{UnitTypes.atrax, UnitTypes.spiroct},
new UnitType[]{UnitTypes.oxynoe, UnitTypes.cyerce}
);
}};
@@ -2111,7 +2114,8 @@ public class Blocks implements ContentList{
new UnitType[]{UnitTypes.fortress, UnitTypes.scepter},
new UnitType[]{UnitTypes.bryde, UnitTypes.sei},
new UnitType[]{UnitTypes.mega, UnitTypes.quad},
new UnitType[]{UnitTypes.quasar, UnitTypes.vela}
new UnitType[]{UnitTypes.quasar, UnitTypes.vela},
new UnitType[]{UnitTypes.cyerce, UnitTypes.aegires}
);
}};
@@ -2132,7 +2136,8 @@ public class Blocks implements ContentList{
new UnitType[]{UnitTypes.scepter, UnitTypes.reign},
new UnitType[]{UnitTypes.sei, UnitTypes.omura},
new UnitType[]{UnitTypes.quad, UnitTypes.oct},
new UnitType[]{UnitTypes.vela, UnitTypes.corvus}
new UnitType[]{UnitTypes.vela, UnitTypes.corvus},
new UnitType[]{UnitTypes.aegires, UnitTypes.navanax}
);
}};
@@ -2149,7 +2154,7 @@ public class Blocks implements ContentList{
requirements(Category.units, with(Items.silicon, 70, Items.thorium, 60, Items.plastanium, 60));
size = 2;
length = 6f;
repairSpeed = 5f;
repairSpeed = 4f;
repairRadius = 140f;
powerUse = 5f;
beamWidth = 1.1f;

View File

@@ -36,7 +36,7 @@ public class Bullets implements ContentList{
waterShot, cryoShot, slagShot, oilShot, heavyWaterShot, heavyCryoShot, heavySlagShot, heavyOilShot,
//environment, misc.
damageLightning, damageLightningGround, fireball, basicFlame, pyraFlame, driverBolt;
damageLightning, damageLightningGround, fireball, basicFlame, pyraFlame;
@Override
public void load(){
@@ -510,7 +510,5 @@ public class Bullets implements ContentList{
statusDuration = 60f * 4f;
damage = 0.2f;
}};
driverBolt = new MassDriverBolt();
}
}

View File

@@ -855,6 +855,7 @@ public class Fx{
if(Fire.regions[0] == null) return;
alpha(e.fout());
rect(Fire.regions[((int)(e.rotation + e.fin() * Fire.frames)) % Fire.frames], e.x, e.y);
Drawf.light(e.x, e.y, 50f + Mathf.absin(5f, 5f), Pal.lightFlame, 0.6f * e.fout());
}),
fire = new Effect(50f, e -> {
@@ -962,7 +963,7 @@ public class Fx{
}),
overdriven = new Effect(20f, e -> {
color(Pal.accent);
color(e.color);
randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> {
Fill.square(e.x + x, e.y + y, e.fout() * 2.3f + 0.5f);
@@ -970,7 +971,7 @@ public class Fx{
}),
overclocked = new Effect(50f, e -> {
color(Pal.accent);
color(e.color);
Fill.square(e.x, e.y, e.fslope() * 2f, 45f);
}),

View File

@@ -53,7 +53,9 @@ public class TechTree implements ContentList{
node(titaniumConveyor, Seq.with(new SectorComplete(craters)), () -> {
node(phaseConveyor, () -> {
node(massDriver, () -> {
node(payloadPropulsionTower, () -> {
});
});
});
@@ -233,7 +235,9 @@ public class TechTree implements ContentList{
});
node(repairPoint, () -> {
node(repairTurret, () -> {
});
});
});
});
@@ -416,6 +420,18 @@ public class TechTree implements ContentList{
});
});
});
node(retusa, () -> {
node(oxynoe, () -> {
node(cyerce, () -> {
node(aegires, () -> {
node(navanax, () -> {
});
});
});
});
});
});
});
});

View File

@@ -1375,7 +1375,7 @@ public class UnitTypes implements ContentList{
shrinkX = shrinkY = 0.7f;
speed = 0.001f;
speed = 0f;
collides = false;
healPercent = 15f;
@@ -1759,7 +1759,7 @@ public class UnitTypes implements ContentList{
armor = 3f;
buildSpeed = 2f;
buildSpeed = 1.5f;
weapons.add(new RepairBeamWeapon("repair-beam-weapon-center"){{
x = 0f;
@@ -1810,7 +1810,7 @@ public class UnitTypes implements ContentList{
shrinkX = shrinkY = 0f;
speed = 0.001f;
speed = 0f;
splashDamage = 50f;
splashDamageRadius = 40f;
@@ -1833,7 +1833,7 @@ public class UnitTypes implements ContentList{
trailY = -4f;
trailScl = 1.9f;
buildSpeed = 2.5f;
buildSpeed = 2f;
weapons.add(new Weapon("plasma-mount-weapon"){{
@@ -1906,7 +1906,7 @@ public class UnitTypes implements ContentList{
trailY = -9f;
trailScl = 2f;
buildSpeed = 3f;
buildSpeed = 2f;
weapons.add(new RepairBeamWeapon("repair-beam-weapon-center"){{
x = 11f;
@@ -1936,7 +1936,6 @@ public class UnitTypes implements ContentList{
ejectEffect = Fx.none;
bullet = new FlakBulletType(2.5f, 25){{
sprite = "missile-large";
collides = false;
//for targeting
collidesGround = collidesAir = true;
explodeRange = 40f;
@@ -1950,9 +1949,9 @@ public class UnitTypes implements ContentList{
lightColor = Pal.heal;
splashDamageRadius = 30f;
splashDamage = 28f;
splashDamage = 25f;
lifetime = 90f;
lifetime = 80f;
backColor = Pal.heal;
frontColor = Color.white;
@@ -1976,17 +1975,17 @@ public class UnitTypes implements ContentList{
weaveMag = 1f;
trailColor = Pal.heal;
trailParam = 5f;
trailInterval = 3f;
trailWidth = 4.5f;
trailLength = 29;
fragBullets = 7;
fragVelocityMin = 0.3f;
fragBullet = new MissileBulletType(3.9f, 12){{
fragBullet = new MissileBulletType(3.9f, 11){{
homingPower = 0.2f;
weaveMag = 4;
weaveScale = 4;
lifetime = 70f;
lifetime = 60f;
shootEffect = Fx.shootHeal;
smokeEffect = Fx.hitLaser;
splashDamage = 13f;
@@ -1998,12 +1997,14 @@ public class UnitTypes implements ContentList{
lightRadius = 40f;
lightOpacity = 0.7f;
trailInterval = 2f;
trailParam = 3f;
trailColor = Pal.heal;
trailWidth = 2.5f;
trailLength = 20;
trailChance = -1f;
healPercent = 2.8f;
collidesTeam = true;
backColor = Pal.heal;
trailColor = Pal.heal;
despawnEffect = Fx.none;
hitEffect = new ExplosionEffect(){{
@@ -2043,7 +2044,7 @@ public class UnitTypes implements ContentList{
trailY = -17f;
trailScl = 3.2f;
buildSpeed = 3.5f;
buildSpeed = 3f;
abilities.add(new EnergyFieldAbility(35f, 65f, 180f){{
repair = 35f;
@@ -2084,7 +2085,7 @@ public class UnitTypes implements ContentList{
trailY = -32f;
trailScl = 3.5f;
buildSpeed = 3.8f;
buildSpeed = 3.5f;
for(float mountY : new float[]{-117/4f, 50/4f}){
for(float sign : Mathf.signs){
@@ -2112,8 +2113,8 @@ public class UnitTypes implements ContentList{
bullet = new ContinuousLaserBulletType(){{
maxRange = 90f;
damage = 25f;
length = 90f;
damage = 26f;
length = 95f;
hitEffect = Fx.hitMeltHeal;
drawSize = 200f;
lifetime = 155f;
@@ -2160,7 +2161,7 @@ public class UnitTypes implements ContentList{
timeIncrease = 3f;
timeDuration = 60f * 20f;
powerDamageScl = 3f;
damage = 40;
damage = 50;
hitColor = lightColor = Pal.heal;
lightRadius = 70f;
clipSize = 250f;
@@ -2176,7 +2177,7 @@ public class UnitTypes implements ContentList{
trailWidth = 6f;
trailColor = Pal.heal;
trailInterval = 3f;
splashDamage = 40f;
splashDamage = 60f;
splashDamageRadius = rad;
hitShake = 4f;
trailRotation = true;

View File

@@ -184,6 +184,13 @@ public class Logic implements ApplicationListener{
}
}
}
//heal all cores on game start
for(TeamData team : state.teams.getActive()){
for(var entity : team.cores){
entity.heal();
}
}
}
public void reset(){

View File

@@ -385,7 +385,7 @@ public class Renderer implements ApplicationListener{
lines[i + 3] = (byte)255;
}
Pixmap fullPixmap = new Pixmap(w, h);
Buffers.copy(lines, 0, fullPixmap.getPixels(), lines.length);
Buffers.copy(lines, 0, fullPixmap.pixels, lines.length);
Fi file = screenshotDirectory.child("screenshot-" + Time.millis() + ".png");
PixmapIO.writePng(file, fullPixmap);
fullPixmap.dispose();

View File

@@ -401,8 +401,14 @@ public class BulletType extends Content implements Cloneable{
//pierceBuilding is not enabled by default, because a bullet may want to *not* pierce buildings
}
if(lightningType == null){
lightningType = !collidesAir ? Bullets.damageLightningGround : Bullets.damageLightning;
if(lightning > 0){
if(status == StatusEffects.none){
status = StatusEffects.shocked;
}
if(lightningType == null){
lightningType = !collidesAir ? Bullets.damageLightningGround : Bullets.damageLightning;
}
}
}
@@ -445,7 +451,7 @@ public class BulletType extends Content implements Cloneable{
bullet.owner = owner;
bullet.team = team;
bullet.time = 0f;
bullet.vel.trns(angle, speed * velocityScl);
bullet.initVel(angle, speed * velocityScl);
if(backMove){
bullet.set(x - bullet.vel.x * Time.delta, y - bullet.vel.y * Time.delta);
}else{
@@ -462,7 +468,7 @@ public class BulletType extends Content implements Cloneable{
}
bullet.add();
if(keepVelocity && owner instanceof Velc v) bullet.vel.add(v.vel().x, v.vel().y);
if(keepVelocity && owner instanceof Velc v) bullet.vel.add(v.vel());
return bullet;
}

View File

@@ -23,7 +23,8 @@ public class ContinuousLaserBulletType extends BulletType{
public boolean largeHit = true;
public ContinuousLaserBulletType(float damage){
super(0.001f, damage);
this.damage = damage;
this.speed = 0f;
hitEffect = Fx.hitBeam;
despawnEffect = Fx.none;

View File

@@ -21,7 +21,8 @@ public class LaserBulletType extends BulletType{
public boolean largeHit = false;
public LaserBulletType(float damage){
super(0.01f, damage);
this.damage = damage;
this.speed = 0f;
hitEffect = Fx.hitLaserBlast;
hitColor = colors[2];

View File

@@ -12,8 +12,8 @@ public class LightningBulletType extends BulletType{
public int lightningLength = 25, lightningLengthRand = 0;
public LightningBulletType(){
super(0.0001f, 1f);
damage = 1f;
speed = 0f;
lifetime = 1;
despawnEffect = Fx.none;
hitEffect = Fx.hitLancer;

View File

@@ -16,6 +16,7 @@ public class RailBulletType extends BulletType{
public float updateEffectSeg = 20f;
public RailBulletType(){
speed = 0f;
pierceBuilding = true;
pierce = true;
reflectable = false;
@@ -23,7 +24,6 @@ public class RailBulletType extends BulletType{
despawnEffect = Fx.none;
collides = false;
lifetime = 1f;
speed = 0.01f;
}
@Override
@@ -57,7 +57,7 @@ public class RailBulletType extends BulletType{
Damage.collideLine(b, b.team, b.type.hitEffect, b.x, b.y, b.rotation(), length, false, false);
float resultLen = b.fdata;
Vec2 nor = Tmp.v1.set(b.vel).nor();
Vec2 nor = Tmp.v1.trns(b.rotation(), 1f).nor();
for(float i = 0; i <= resultLen; i += updateEffectSeg){
updateEffect.at(b.x + nor.x * i, b.y + nor.y * i, b.rotation());
}
@@ -70,8 +70,8 @@ public class RailBulletType extends BulletType{
@Override
public void hitEntity(Bullet b, Hitboxc entity, float health){
handle(b, entity, health);
super.hitEntity(b, entity, health);
handle(b, entity, health);
}
@Override

View File

@@ -17,7 +17,7 @@ public class SapBulletType extends BulletType{
public float width = 0.4f;
public SapBulletType(){
speed = 0.0001f;
speed = 0f;
despawnEffect = Fx.none;
pierce = true;
collides = false;

View File

@@ -19,7 +19,7 @@ public class ShrapnelBulletType extends BulletType{
public float serrationLenScl = 10f, serrationWidth = 4f, serrationSpacing = 8f, serrationSpaceOffset = 80f, serrationFadeOffset = 0.5f;
public ShrapnelBulletType(){
speed = 0.01f;
speed = 0f;
hitEffect = Fx.hitLancer;
shootEffect = smokeEffect = Fx.lightningShoot;
lifetime = 10f;

View File

@@ -2,7 +2,6 @@ package mindustry.entities.comp;
import arc.func.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
@@ -22,11 +21,16 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
@Import Team team;
@Import Entityc owner;
@Import float x, y, damage;
@Import Vec2 vel;
IntSeq collided = new IntSeq(6);
Object data;
BulletType type;
float fdata;
@ReadOnly
private float rotation;
transient boolean absorbed, hit;
transient @Nullable Trail trail;
@@ -149,17 +153,20 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
type.drawLight(self());
}
public void initVel(float angle, float amount){
vel.trns(angle, amount);
rotation = angle;
}
/** Sets the bullet's rotation in degrees. */
@Override
public void rotation(float angle){
vel().setAngle(angle);
vel.setAngle(rotation = angle);
}
/** @return the bullet's rotation. */
@Override
public float rotation(){
float angle = Mathf.atan2(vel().x, vel().y) * Mathf.radiansToDegrees;
if(angle < 0) angle += 360;
return angle;
return vel.isZero(0.001f) ? rotation : vel.angle();
}
}

View File

@@ -35,7 +35,7 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
baseFlammability = -1, puddleFlammability, damageTimer = Mathf.random(40f),
spreadTimer = Mathf.random(spreadDelay), fireballTimer = Mathf.random(fireballDelay),
warmup = 0f,
animation = Mathf.random(frames);
animation = Mathf.random(frames - 1);
@Override
public void update(){
@@ -116,8 +116,10 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
Draw.alpha(Mathf.clamp(warmup / warmupDuration));
Draw.z(Layer.effect);
Draw.rect(regions[(int)animation], x, y);
Draw.rect(regions[Math.min((int)animation, regions.length - 1)], x, y);
Draw.reset();
Drawf.light(x, y, 50f + Mathf.absin(5f, 5f), Pal.lightFlame, 0.6f * Mathf.clamp(warmup / warmupDuration));
}
@Replace

View File

@@ -45,6 +45,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
transient String lastText = "";
transient float textFadeTime;
transient private Unit lastReadUnit = Nulls.unit;
transient @Nullable Unit justSwitchFrom, justSwitchTo;
public boolean isBuilder(){
return unit.canBuild();
@@ -100,6 +101,16 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
@Override
public void afterSync(){
//fix rubberbanding:
//when the player recs a unit that they JUST transitioned away from, use the new unit instead
//reason: we know the server is lying here, essentially skip the unit snapshot because we know the client's information is more recent
if(isLocal() && unit == justSwitchFrom && justSwitchFrom != null && justSwitchTo != null){
unit = justSwitchTo;
}else{
justSwitchFrom = null;
justSwitchTo = null;
}
//simulate a unit change after sync
Unit set = unit;
unit = lastReadUnit;
@@ -149,6 +160,13 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
}
public void checkSpawn(){
CoreBuild core = bestCore();
if(core != null){
core.requestSpawn(self());
}
}
@Override
public void remove(){
//clear unit upon removal
@@ -171,6 +189,11 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
}
public void unit(Unit unit){
//refuse to switch when the unit was just transitioned from
if(isLocal() && unit == justSwitchFrom && justSwitchFrom != null && justSwitchTo != null){
return;
}
if(unit == null) throw new IllegalArgumentException("Unit cannot be null. Use clearUnit() instead.");
if(this.unit == unit) return;

View File

@@ -15,7 +15,8 @@ import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.environment.*;
//just a proof of concept
import static mindustry.Vars.*;
@Component
abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
@Import float x, y, rotation;
@@ -32,7 +33,7 @@ abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
int sign = i == 0 ? -1 : 1;
float cx = Angles.trnsx(rotation - 90, type.trailX * sign, type.trailY) + x, cy = Angles.trnsy(rotation - 90, type.trailX * sign, type.trailY) + y;
t.update(cx, cy);
t.update(cx, cy, world.floorWorld(cx, cy).isLiquid ? 1 : 0);
}
}

View File

@@ -144,6 +144,14 @@ public class MinimapRenderer{
public void update(Tile tile){
if(world.isGenerating() || !state.isGame()) return;
if(tile.build != null && tile.isCenter()){
tile.getLinkedTiles(other -> {
if(!other.isCenter()){
update(other);
}
});
}
int color = colorFor(tile);
pixmap.set(tile.x, pixmap.height - 1 - tile.y, color);

View File

@@ -26,6 +26,10 @@ public class MultiPacker implements Disposable{
return null;
}
public PixmapPacker getPacker(PageType type){
return packers[type.ordinal()];
}
public boolean has(String name){
for(var page : PageType.all){
if(packers[page.ordinal()].getRect(name) != null){
@@ -70,8 +74,9 @@ public class MultiPacker implements Disposable{
//main page (sprites.png) - all sprites for units, weapons, placeable blocks, effects, bullets, etc
//environment page (sprites2.png) - all sprites for things in the environmental cache layer
//editor page (sprites3.png) - all sprites needed for rendering in the editor, including block icons and a few minor sprites
//zone page (sprites4.png) - zone previews
//ui page (sprites5.png) - content icons, white icons and UI elements
//zone page (sprites4.png) - zone preview
//rubble page - scorch textures for unit deaths & wrecks
//ui page (sprites5.png) - content icons, white icons, fonts and UI elements
public enum PageType{
main(4096),
environment,

View File

@@ -40,6 +40,7 @@ public class Trail{
float[] items = points.items;
int i = points.size - 3;
float x1 = items[i], y1 = items[i + 1], w1 = items[i + 2], w = w1 * width / (points.size/3) * i/3f * 2f;
if(w1 <= 0.001f) return;
Draw.rect("hcircle", x1, y1, w, w, -Mathf.radDeg * lastAngle + 180f);
Draw.reset();
}
@@ -56,6 +57,7 @@ public class Trail{
float size = width / (points.size/3);
float z1 = lastAngle;
float z2 = -Angles.angleRad(x2, y2, lx, ly);
if(w1 <= 0.001f || w2 <= 0.001f) continue;
float cx = Mathf.sin(z1) * i/3f * size * w1, cy = Mathf.cos(z1) * i/3f * size * w1,
nx = Mathf.sin(z2) * (i/3f + 1) * size * w2, ny = Mathf.cos(z2) * (i/3f + 1) * size * w2;

View File

@@ -229,8 +229,10 @@ public class DesktopInput extends InputHandler{
if(on != null){
Call.unitControl(player, on);
shouldShoot = false;
recentRespawnTimer = 1f;
}else if(build != null){
Call.buildingControlSelect(player, build);
recentRespawnTimer = 1f;
}
}
}
@@ -238,9 +240,10 @@ public class DesktopInput extends InputHandler{
if(!player.dead() && !state.isPaused() && !scene.hasField()){
updateMovement(player.unit());
if(Core.input.keyDown(Binding.respawn)){
Call.unitClear(player);
if(Core.input.keyTap(Binding.respawn)){
controlledType = null;
recentRespawnTimer = 1f;
Call.unitClear(player);
}
}

View File

@@ -58,6 +58,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public Group uiGroup;
public boolean isBuilding = true, buildWasAutoPaused = false, wasShooting = false;
public @Nullable UnitType controlledType;
public float recentRespawnTimer;
public @Nullable Schematic lastSchematic;
public GestureDetector detector;
@@ -375,15 +376,27 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
throw new ValidateException(player, "Player cannot control a unit.");
}
//TODO problem:
//1. server send snapshot
//2. client requests to control unit, becomes unit locally
//3. snapshot arrives, client now thinks they're in the old unit (!!!)
//4. server gets packet that player is in the right unit
//5. server sends snapshot
//6. client gets snapshot, realizes that they are actually in the unit they selected
//7. client gets switched to new unit -> rubberbanding (!!!)
//clear player unit when they possess a core
if(unit == null){ //just clear the unit (is this used?)
player.clearUnit();
//make sure it's AI controlled, so players can't overwrite each other
}else if(unit.isAI() && unit.team == player.team() && !unit.dead){
if(!net.client()){
player.unit(unit);
if(net.client() && player.isLocal()){
player.justSwitchFrom = player.unit();
player.justSwitchTo = unit;
}
player.unit(unit);
Time.run(Fx.unitSpirit.lifetime, () -> Fx.unitControl.at(unit.x, unit.y, 0f, unit));
if(!player.dead()){
Fx.unitSpirit.at(player.x, player.y, 0f, unit);
@@ -393,12 +406,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
Events.fire(new UnitControlEvent(player, unit));
}
@Remote(targets = Loc.both, called = Loc.both, forward = true)
@Remote(targets = Loc.both, called = Loc.server, forward = true)
public static void unitClear(Player player){
if(player == null) return;
//problem: this gets called on both ends. it shouldn't be.
Fx.spawn.at(player);
player.clearUnit();
player.checkSpawn();
player.deathTimer = Player.deathDelay + 1f; //for instant respawn
}
@@ -419,7 +434,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
player.unit().commandNearby(new CircleFormation());
Fx.commandSend.at(player, player.unit().type.commandRadius);
}
}
public Eachable<BuildPlan> allRequests(){
@@ -434,10 +448,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
return !selectRequests.isEmpty();
}
public OverlayFragment getFrag(){
return frag;
}
public void update(){
player.typing = ui.chatfrag.shown();
@@ -451,7 +461,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
wasShooting = player.shooting;
if(!player.dead()){
//only reset the controlled type and control a unit after the timer runs out
//essentially, this means the client waits for ~1 second after controlling something before trying to control something else automatically
if(!player.dead() && (recentRespawnTimer -= Time.delta / 70f) <= 0f && player.justSwitchFrom != player.unit()){
controlledType = player.unit().type;
}
@@ -461,6 +473,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(unit != null){
//only trying controlling once a second to prevent packet spam
if(!net.client() || controlInterval.get(0, 70f)){
recentRespawnTimer = 1f;
Call.unitControl(player, unit);
}
}

View File

@@ -614,8 +614,10 @@ public class MobileInput extends InputHandler implements GestureListener{
//control a unit/block detected on first tap of double-tap
if(unitTapped != null){
Call.unitControl(player, unitTapped);
recentRespawnTimer = 1f;
}else if(buildingTapped != null){
Call.buildingControlSelect(player, buildingTapped);
recentRespawnTimer = 1f;
}else if(!tryBeginMine(cursor)){
tileTapped(linked.build);
}
@@ -856,8 +858,7 @@ public class MobileInput extends InputHandler implements GestureListener{
boolean omni = unit.type.omniMovement;
boolean allowHealing = type.canHeal;
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
((Building)target).damaged() && target.within(unit, type.range);
boolean validHealTarget = allowHealing && target instanceof Building b && b.isValid() && target.team() == unit.team && b.damaged() && target.within(unit, type.range);
boolean boosted = (unit instanceof Mechc && unit.isFlying());
//reset target if:
@@ -912,13 +913,8 @@ public class MobileInput extends InputHandler implements GestureListener{
unit.vel.approachDelta(Vec2.ZERO, unit.speed() * type.accel / 2f);
}
float expansion = 3f;
unit.hitbox(rect);
rect.x -= expansion;
rect.y -= expansion;
rect.width += expansion * 2f;
rect.height += expansion * 2f;
rect.grow(6f);
player.boosting = collisions.overlapsTile(rect) || !unit.within(targetPos, 85f);
@@ -927,7 +923,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}else{
unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len()));
if(!movement.isZero()){
unit.vel.rotateTo(movement.angle(), unit.type.rotateSpeed * Math.max(Time.delta, 1));
unit.rotation = Angles.moveToward(unit.rotation, movement.angle(), unit.type.rotateSpeed * Math.max(Time.delta, 1));
}
}

View File

@@ -8,6 +8,7 @@ import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.meta.*;
import java.io.*;
@@ -111,6 +112,18 @@ public class JsonIO{
}
});
json.setSerializer(Attribute.class, new Serializer<>(){
@Override
public void write(Json json, Attribute object, Class knownType){
json.writeValue(object.name);
}
@Override
public Attribute read(Json json, JsonValue jsonData, Class type){
return Attribute.get(jsonData.asString());
}
});
json.setSerializer(Item.class, new Serializer<>(){
@Override
public void write(Json json, Item object, Class knownType){

View File

@@ -112,7 +112,6 @@ public class LCanvas extends Table{
jumps.cullable = false;
}).grow().get();
//pane.setClip(false);
pane.setFlickScroll(false);
//load old scroll percent

View File

@@ -36,13 +36,20 @@ public enum LogicOp{
abs("abs", a -> Math.abs(a)),
log("log", Math::log),
log10("log10", Math::log10),
sin("sin", d -> Math.sin(d * 0.017453292519943295D)),
cos("cos", d -> Math.cos(d * 0.017453292519943295D)),
tan("tan", d -> Math.tan(d * 0.017453292519943295D)),
floor("floor", Math::floor),
ceil("ceil", Math::ceil),
sqrt("sqrt", Math::sqrt),
rand("rand", d -> Mathf.rand.nextDouble() * d);
rand("rand", d -> Mathf.rand.nextDouble() * d),
sin("sin", d -> Math.sin(d * Mathf.doubleDegRad)),
cos("cos", d -> Math.cos(d * Mathf.doubleDegRad)),
tan("tan", d -> Math.tan(d * Mathf.doubleDegRad)),
asin("asin", d -> Math.asin(d) * Mathf.doubleRadDeg),
acos("acos", d -> Math.acos(d) * Mathf.doubleRadDeg),
atan("atan", d -> Math.atan(d) * Mathf.doubleRadDeg),
;
public static final LogicOp[] all = values();

View File

@@ -3,7 +3,6 @@ package mindustry.maps;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import mindustry.ai.*;
import mindustry.content.*;
import mindustry.entities.*;
@@ -186,7 +185,6 @@ public class SectorDamage{
Tile start = spawns.first();
Time.mark();
var field = pathfinder.getField(state.rules.waveTeam, Pathfinder.costGround, Pathfinder.fieldCore);
Seq<Tile> path = new Seq<>();
boolean found = false;

View File

@@ -111,4 +111,23 @@ public abstract class FilterOption{
table.add("@filter.option." + name);
}
}
static class ToggleOption extends FilterOption{
final String name;
final Boolp getter;
final Boolc setter;
ToggleOption(String name, Boolp getter, Boolc setter){
this.name = name;
this.getter = getter;
this.setter = setter;
}
@Override
public void build(Table table){
table.row();
CheckBox check = table.check("@filter.option." + name, setter).growX().padBottom(5).padTop(5).center().get();
check.changed(changed);
}
}
}

View File

@@ -15,11 +15,13 @@ public class MirrorFilter extends GenerateFilter{
private final Vec2 v1 = new Vec2(), v2 = new Vec2(), v3 = new Vec2();
int angle = 45;
boolean rotate = false;
@Override
public FilterOption[] options(){
return Structs.arr(
new SliderOption("angle", () -> angle, f -> angle = (int)f, 0, 360, 45)
new SliderOption("angle", () -> angle, f -> angle = (int)f, 0, 360, 45),
new ToggleOption("rotate", () -> rotate, f -> rotate = f)
);
}
@@ -72,8 +74,8 @@ public class MirrorFilter extends GenerateFilter{
}
void mirror(Vec2 p, float x0, float y0, float x1, float y1){
//special case: uneven map mirrored at 45 degree angle
if(in.width != in.height && angle % 90 != 0){
//special case: uneven map mirrored at 45 degree angle (or someone might just want rotational symmetry)
if((in.width != in.height && angle % 90 != 0) || rotate){
p.x = in.width - p.x - 1;
p.y = in.height - p.y - 1;
}else{

View File

@@ -61,6 +61,7 @@ public class ContentParser{
});
put(Interp.class, (type, data) -> field(Interp.class, data));
put(CacheLayer.class, (type, data) -> field(CacheLayer.class, data));
put(Attribute.class, (type, data) -> Attribute.get(data.asString()));
put(Schematic.class, (type, data) -> {
Object result = fieldOpt(Loadouts.class, data);
if(result != null){

View File

@@ -607,7 +607,8 @@ public class Mods implements Loadable{
if(mod.root.child("content").exists()){
Fi contentRoot = mod.root.child("content");
for(ContentType type : ContentType.all){
Fi folder = contentRoot.child(type.name().toLowerCase(Locale.ROOT) + "s");
String lower = type.name().toLowerCase(Locale.ROOT);
Fi folder = contentRoot.child(lower + (lower.endsWith("s") ? "" : "s"));
if(folder.exists()){
for(Fi file : folder.findAll(f -> f.extension().equals("json") || f.extension().equals("hjson"))){
runs.add(new LoadRun(type, file, mod));

View File

@@ -385,7 +385,6 @@ public class ArcNetProvider implements NetProvider{
return readFramework(byteBuffer);
}else{
//read length int, followed by compressed lz4 data
//TODO not thread safe!!!
Packet packet = Net.newPacket(id);
var buffer = decompressBuffer.get();
int length = byteBuffer.getShort() & 0xffff;
@@ -396,7 +395,7 @@ public class ArcNetProvider implements NetProvider{
buffer.position(0).limit(length);
buffer.put(byteBuffer.array(), byteBuffer.position(), length);
buffer.position(0);
packet.read(reads.get());
packet.read(reads.get(), length);
//move read packets forward
byteBuffer.position(byteBuffer.position() + buffer.position());
}else{
@@ -405,7 +404,7 @@ public class ArcNetProvider implements NetProvider{
buffer.position(0);
buffer.limit(length);
packet.read(reads.get());
packet.read(reads.get(), length);
//move buffer forward based on bytes read by decompressor
byteBuffer.position(byteBuffer.position() + read);
}

View File

@@ -7,6 +7,7 @@ import arc.func.*;
import arc.util.*;
import arc.util.async.*;
import arc.util.serialization.*;
import mindustry.*;
import mindustry.core.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -39,7 +40,7 @@ public class BeControl{
public BeControl(){
if(active()){
Timer.schedule(() -> {
if(checkUpdates && !mobile){
if(Vars.clientLoaded && checkUpdates && !mobile){
checkUpdate(t -> {});
}
}, updateInterval, updateInterval);

View File

@@ -257,6 +257,7 @@ public class Net{
* Call to handle a packet being received for the client.
*/
public void handleClientReceived(Packet object){
object.handled();
if(object instanceof StreamBegin b){
streams.put(b.id, currentStream = new StreamBuilder(b));
@@ -291,6 +292,8 @@ public class Net{
* Call to handle a packet being received for the server.
*/
public void handleServerReceived(NetConnection connection, Packet object){
object.handled();
try{
//handle object normally
if(serverListeners.get(object.getClass()) != null){

View File

@@ -2,7 +2,14 @@ package mindustry.net;
import arc.util.io.*;
import java.io.*;
public abstract class Packet{
//internally used by generated code
protected static final byte[] NODATA = {};
protected static final ReusableByteInStream BAIS = new ReusableByteInStream();
protected static final Reads READ = new Reads(new DataInputStream(BAIS));
//these are constants because I don't want to bother making an enum to mirror the annotation enum
/** Does not get handled unless client is connected. */
@@ -15,6 +22,12 @@ public abstract class Packet{
public void read(Reads read){}
public void write(Writes write){}
public void read(Reads read, int length){
read(read);
}
public void handled(){}
public int getPriority(){
return priorityNormal;
}

View File

@@ -2,7 +2,6 @@ package mindustry.net;
import arc.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import arc.util.serialization.*;
import mindustry.core.*;
@@ -131,8 +130,6 @@ public class Packets{
crc.update(Base64Coder.decode(uuid), 0, b.length);
buffer.l(crc.getValue());
Log.info("CRC value sent: @", Long.toHexString(crc.getValue()));
buffer.b(mobile ? (byte)1 : 0);
buffer.i(color);
buffer.b((byte)mods.size);

View File

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

View File

@@ -7,6 +7,8 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.audio.*;
import mindustry.content.*;
@@ -15,6 +17,7 @@ import mindustry.entities.bullet.*;
import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
@@ -119,6 +122,17 @@ public class Weapon implements Cloneable{
this("");
}
public void addStats(UnitType u, Table t){
if(inaccuracy > 0){
t.row();
t.add("[lightgray]" + Stat.inaccuracy.localized() + ": [white]" + (int)inaccuracy + " " + StatUnit.degrees.localized());
}
t.row();
t.add("[lightgray]" + Stat.reload.localized() + ": " + (mirror ? "2x " : "") + "[white]" + Strings.autoFixed(60f / reload * shots, 2));
StatValues.ammo(ObjectMap.of(u, bullet)).display(t);
}
public float dps(){
return (bullet.estimateDPS() / reload) * shots * 60f;
}

View File

@@ -5,12 +5,14 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.entities.*;
import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.blocks.units.*;
import mindustry.world.meta.*;
/**
* Note that this weapon requires a bullet with a positive maxRange.
@@ -47,6 +49,12 @@ public class RepairBeamWeapon extends Weapon{
recoil = 0f;
}
@Override
public void addStats(UnitType u, Table w){
w.row();
w.add("[lightgray]" + Stat.repairSpeed.localized() + ": " + (mirror ? "2x " : "") + "[white]" + (int)(repairSpeed * 60) + " " + StatUnit.perSecond.localized());
}
@Override
public float dps(){
return 0f;

View File

@@ -40,7 +40,7 @@ public class SearchBar{
parent.pane(table -> {
pane[0] = table;
rebuild.get("");
});
}).get().setScrollingDisabled(true, false);
return pane[0];
}
@@ -54,9 +54,6 @@ public class SearchBar{
/** Match a list item with the search query, case insensitive */
public static boolean matches(String query, String name){
if(name == null || name.isEmpty()){
return false;
}
return name.toLowerCase().contains(query);
return name != null && !name.isEmpty() && name.toLowerCase().contains(query);
}
}

View File

@@ -4,6 +4,7 @@ import arc.*;
import arc.scene.ui.*;
import arc.util.*;
import mindustry.core.GameState.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -43,7 +44,12 @@ public class BaseDialog extends Dialog{
}
protected void onResize(Runnable run){
resized(run);
Events.on(ResizeEvent.class, event -> {
if(isShown() && Core.scene.getDialog() == this){
run.run();
updateScrollFocus();
}
});
}
public void addCloseListener(){

View File

@@ -127,7 +127,7 @@ public class ModsDialog extends BaseDialog{
try{
return d.parse(text);
}catch(Exception e){
throw new RuntimeException(e);
return new Date();
}
};
@@ -147,12 +147,11 @@ public class ModsDialog extends BaseDialog{
}
void setup(){
boolean squish = Core.graphics.isPortrait();
float h = 110f;
float w = squish ? 410f : 524f;
float w = Math.min(Core.graphics.getWidth() / 1.1f, 520f);
cont.clear();
cont.defaults().width(squish ? 480 : 560f).pad(4);
cont.defaults().width(Math.min(Core.graphics.getWidth() / 1.2f, 520f)).pad(4);
cont.add("@mod.reloadrequired").visible(mods::requiresReload).center().get().setAlignment(Align.center);
cont.row();

View File

@@ -721,6 +721,7 @@ public class HudFragment extends Fragment{
t.clicked(() -> {
if(!player.dead() && mobile){
Call.unitClear(player);
control.input.recentRespawnTimer = 1f;
control.input.controlledType = null;
}
});

View File

@@ -241,7 +241,6 @@ public class Tile implements Position, QuadTreeObject, Displayable{
//assign entity and type to blocks, so they act as proxies for this one
other.build = entity;
other.block = block;
}
}
}

View File

@@ -34,6 +34,8 @@ public class MendProjector extends Block{
group = BlockGroup.projectors;
hasPower = true;
hasItems = true;
emitLight = true;
lightRadius = 50f;
}
@Override
@@ -129,7 +131,7 @@ public class MendProjector extends Block{
@Override
public void drawLight(){
Drawf.light(team, x, y, 50f * smoothEfficiency, baseColor, 0.7f * smoothEfficiency);
Drawf.light(team, x, y, lightRadius * smoothEfficiency, baseColor, 0.7f * smoothEfficiency);
}
@Override

View File

@@ -39,6 +39,8 @@ public class OverdriveProjector extends Block{
hasPower = true;
hasItems = true;
canOverdrive = false;
emitLight = true;
lightRadius = 50f;
}
@Override
@@ -89,7 +91,7 @@ public class OverdriveProjector extends Block{
@Override
public void drawLight(){
Drawf.light(team, x, y, 50f * smoothEfficiency, baseColor, 0.7f * smoothEfficiency);
Drawf.light(team, x, y, lightRadius * smoothEfficiency, baseColor, 0.7f * smoothEfficiency);
}
@Override

View File

@@ -3,6 +3,7 @@ package mindustry.world.blocks.defense;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import mindustry.annotations.Annotations.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -17,6 +18,8 @@ public class ShockMine extends Block{
public int length = 10;
public int tendrils = 6;
public Color lightningColor = Pal.lancerLaser;
public float teamAlpha = 0.3f;
public @Load("@-team-top") TextureRegion teamRegion;
public ShockMine(String name){
super(name);
@@ -24,7 +27,6 @@ public class ShockMine extends Block{
destructible = true;
solid = false;
targetable = false;
rebuildable = false;
}
public class ShockMineBuild extends Building{
@@ -37,12 +39,16 @@ public class ShockMine extends Block{
@Override
public void draw(){
super.draw();
Draw.color(team.color);
Draw.alpha(0.22f);
Fill.rect(x, y, 2f, 2f);
Draw.color(team.color, teamAlpha);
Draw.rect(teamRegion, x, y);
Draw.color();
}
@Override
public void drawCracks(){
}
@Override
public void unitOn(Unit unit){
if(enabled && unit.team != team && timer(timerDamage, cooldown)){

View File

@@ -6,6 +6,7 @@ import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.units.*;
@@ -183,5 +184,25 @@ public class Duct extends Block implements Autotiler{
next = front();
nextc = next instanceof DuctBuild d ? d : null;
}
@Override
public byte version(){
return 1;
}
@Override
public void write(Writes write){
super.write(write);
write.b(recDir);
}
@Override
public void read(Reads read, byte revision){
super.read(read, revision);
if(revision >= 1){
recDir = read.b();
}
current = items.first();
}
}
}

View File

@@ -25,7 +25,7 @@ public class DuctBridge extends Block{
public @Load("@-dir") TextureRegion dirRegion;
public int range = 4;
public float moveDelay = 5f;
public float speed = 5f;
public DuctBridge(String name){
super(name);
@@ -37,6 +37,7 @@ public class DuctBridge extends Block{
group = BlockGroup.transportation;
noUpdateDisabled = true;
envEnabled = Env.space | Env.terrestrial | Env.underwater;
drawArrow = false;
}
@Override
@@ -55,6 +56,37 @@ public class DuctBridge extends Block{
Placement.calculateNodes(points, this, rotation, (point, other) -> Math.max(Math.abs(point.x - other.x), Math.abs(point.y - other.y)) <= range);
}
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
super.drawPlace(x, y, rotation, valid);
int length = range;
Building found = null;
//find the link
for(int i = 1; i <= range; i++){
Tile other = world.tile(x + Geometry.d4x(rotation) * i, y + Geometry.d4y(rotation) * i);
if(other != null && other.build instanceof DuctBridgeBuild build && build.team == player.team()){
length = i;
found = other.build;
break;
}
}
Drawf.dashLine(Pal.placing,
x * tilesize + Geometry.d4[rotation].x * (tilesize / 2f + 2),
y * tilesize + Geometry.d4[rotation].y * (tilesize / 2f + 2),
x * tilesize + Geometry.d4[rotation].x * (length) * tilesize,
y * tilesize + Geometry.d4[rotation].y * (length) * tilesize
);
if(found != null){
Drawf.square(found.x, found.y, found.block.size * tilesize/2f + 2.5f, 0f);
}
}
public boolean positionsValid(int x1, int y1, int x2, int y2){
if(x1 == x2){
return Math.abs(y1 - y2) <= range;
@@ -88,7 +120,6 @@ public class DuctBridge extends Block{
Draw.rect(bridgeBotRegion, cx, cy, len, tilesize, angle);
Draw.reset();
Draw.alpha(Renderer.bridgeOpacity);
//Draw.rect(bridgeTopRegion, cx, cy, len, tilesize, angle);
for(float i = 6f; i <= len + size * tilesize - 5f; i += 5f){
Draw.rect(arrowRegion, x + Geometry.d4x(rotation) * i, y + Geometry.d4y(rotation) * i, angle);
@@ -116,12 +147,12 @@ public class DuctBridge extends Block{
link.occupied[rotation % 4] = this;
if(items.any() && link.items.total() < link.block.itemCapacity){
progress += edelta();
while(progress > moveDelay){
while(progress > speed){
Item next = items.take();
if(next != null && link.items.total() < link.block.itemCapacity){
link.handleItem(this, next);
}
progress -= moveDelay;
progress -= speed;
}
}
}

View File

@@ -87,7 +87,7 @@ public class DuctRouter extends Block{
if(current == null) return null;
for(int i = -1; i <= 1; i++){
Building other = nearby(Mathf.mod(rotation + i + cdump, 4));
Building other = nearby(Mathf.mod(rotation + (((i + cdump + 1) % 3) - 1), 4));
if(other != null && other.team == team && other.acceptItem(this, current)){
return other;
}

View File

@@ -12,6 +12,7 @@ import arc.util.pooling.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.bullet.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.*;
@@ -28,6 +29,7 @@ public class MassDriver extends Block{
public int minDistribute = 10;
public float knockback = 4f;
public float reloadTime = 100f;
public MassDriverBolt bullet;
public float bulletSpeed = 5.5f;
public float bulletLifetime = 200f;
public Effect shootEffect = Fx.shootBig2;
@@ -287,7 +289,7 @@ public class MassDriver extends Block{
float angle = tile.angleTo(target);
Bullets.driverBolt.create(this, team,
bullet.create(this, team,
x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation),
angle, -1f, bulletSpeed, bulletLifetime, data);

View File

@@ -57,6 +57,14 @@ public class PayloadConveyor extends Block{
stats.add(Stat.payloadCapacity, (payloadLimit), StatUnit.blocksSquared);
}
@Override
public void init(){
super.init();
//increase clip size for oversize loads
clipSize = Math.max(clipSize, size * tilesize * 2.1f);
}
public class PayloadConveyorBuild extends Building{
public @Nullable Payload item;
public float progress, itemRotation, animation;

View File

@@ -13,7 +13,7 @@ import mindustry.world.*;
import static mindustry.Vars.*;
public class PayloadBlock extends Block{
public float payloadSpeed = 0.5f, payloadRotateSpeed = 5f;
public float payloadSpeed = 0.7f, payloadRotateSpeed = 5f;
public @Load(value = "@-top", fallback = "factory-top-@size") TextureRegion topRegion;
public @Load(value = "@-out", fallback = "factory-out-@size") TextureRegion outRegion;

View File

@@ -122,7 +122,8 @@ public class PayloadMassDriver extends PayloadBlock{
public float turretRotation = 90;
public float reload = 0f, charge = 0f;
public float targetSize = grabWidth*2f, curSize = targetSize;
public float payLength = 0f;
public float payLength = 0f, effectDelayTimer = -1f;
public PayloadDriverBuild lastOther;
public boolean loaded;
public boolean charging;
public PayloadDriverState state = idle;
@@ -151,6 +152,16 @@ public class PayloadMassDriver extends PayloadBlock{
targetSize = payload.size();
}
boolean pos = effectDelayTimer > 0;
effectDelayTimer -= Time.delta;
if(effectDelayTimer <= 0 && pos && lastOther != null){
var other = lastOther;
float cx = Angles.trnsx(other.turretRotation, length), cy = Angles.trnsy(other.turretRotation, length);
receiveEffect.at(x - cx/2f, y - cy/2f, turretRotation);
reload = 1f;
Effect.shake(shake, shake, this);
}
charging = false;
if(hasLink){
@@ -192,7 +203,7 @@ public class PayloadMassDriver extends PayloadBlock{
payVector.setZero();
payRotation = Angles.moveToward(payRotation, turretRotation + 180f, payloadRotateSpeed * delta());
}
}else{
}else if(effectDelayTimer <= 0){
moveOutPayload();
}
}
@@ -270,26 +281,22 @@ public class PayloadMassDriver extends PayloadBlock{
transferEffect.at(x + cx, y + cy, turretRotation, new PayloadMassDriverData(x + cx, y + cy, other.x - cx, other.y - cy, payload));
Payload pay = payload;
other.recPayload = payload;
other.effectDelayTimer = transferEffect.lifetime;
Time.run(transferEffect.lifetime, () -> {
receiveEffect.at(other.x - cx/2f, other.y - cy/2f, other.turretRotation);
Effect.shake(shake, shake, this);
//transfer payload
other.handlePayload(this, pay);
other.lastOther = this;
other.payVector.set(-cx, -cy);
other.payRotation = turretRotation;
other.payLength = length;
other.loaded = true;
other.updatePayload();
other.recPayload = null;
//transfer payload
other.reload = 1f;
other.handlePayload(this, pay);
other.payVector.set(-cx, -cy);
other.payRotation = turretRotation;
other.payLength = length;
other.loaded = true;
other.updatePayload();
other.recPayload = null;
if(other.waitingShooters.size != 0 && other.waitingShooters.first() == this){
other.waitingShooters.removeFirst();
}
other.state = idle;
});
if(other.waitingShooters.size != 0 && other.waitingShooters.first() == this){
other.waitingShooters.removeFirst();
}
other.state = idle;
//reset state after shooting immediately
payload = null;
@@ -341,8 +348,10 @@ public class PayloadMassDriver extends PayloadBlock{
if(payload != null){
updatePayload();
Draw.z(loaded ? Layer.blockOver + 0.2f : Layer.blockOver);
payload.draw();
if(effectDelayTimer <= 0){
Draw.z(loaded ? Layer.blockOver + 0.2f : Layer.blockOver);
payload.draw();
}
}
Draw.z(Layer.blockOver + 0.1f);
@@ -443,12 +452,22 @@ public class PayloadMassDriver extends PayloadBlock{
return Point2.unpack(link).sub(tile.x, tile.y);
}
@Override
public byte version(){
return 1;
}
@Override
public void write(Writes write){
super.write(write);
write.i(link);
write.f(turretRotation);
write.b((byte)state.ordinal());
write.f(reload);
write.f(charge);
write.bool(loaded);
write.bool(charging);
}
@Override
@@ -457,6 +476,13 @@ public class PayloadMassDriver extends PayloadBlock{
link = read.i();
turretRotation = read.f();
state = PayloadDriverState.all[read.b()];
if(revision >= 1){
reload = read.f();
charge = read.f();
loaded = read.bool();
charging = read.bool();
}
}
}

View File

@@ -24,9 +24,11 @@ public class PayloadSource extends PayloadBlock{
size = 3;
update = true;
outputsPayload = true;
hasPower = true;
hasPower = false;
rotate = true;
configurable = true;
//make sure to display large units.
clipSize = 120;
config(Block.class, (PayloadSourceBuild build, Block block) -> {
if(canProduce(block) && build.block != block){
@@ -45,6 +47,13 @@ public class PayloadSource extends PayloadBlock{
build.scl = 0f;
}
});
configClear((PayloadSourceBuild build) -> {
build.block = null;
build.unit = null;
build.payload = null;
build.scl = 0f;
});
}
@Override

View File

@@ -19,6 +19,9 @@ public class PayloadVoid extends PayloadBlock{
update = true;
rotate = false;
size = 3;
payloadSpeed = 1.2f;
//make sure to display large units.
clipSize = 120;
}
@Override

View File

@@ -4,10 +4,10 @@ import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.*;
import mindustry.core.*;
import mindustry.entities.EntityCollisions.*;
import mindustry.entities.*;
import mindustry.game.EventType.*;
@@ -69,14 +69,12 @@ public class UnitPayload implements Payload{
//check if unit can be dumped here
SolidPred solid = unit.solidity();
if(solid != null){
int tx = unit.tileX(), ty = unit.tileY();
boolean nearEmpty = !solid.solid(tx, ty);
for(Point2 p : Geometry.d4){
nearEmpty |= !solid.solid(tx + p.x, ty + p.y);
}
Tmp.v1.trns(unit.rotation, 1f);
int tx = World.toTile(unit.x + Tmp.v1.x), ty = World.toTile(unit.y + Tmp.v1.y);
//cannot dump on solid blocks
if(!nearEmpty) return false;
if(solid.solid(tx, ty)) return false;
}
//cannnot dump when there's a lot of overlap going on

View File

@@ -49,7 +49,7 @@ public class PowerGraph{
}
public float getPowerBalance(){
return powerBalance.mean();
return powerBalance.rawMean();
}
public float getLastPowerNeeded(){

View File

@@ -18,6 +18,13 @@ public class ThermalGenerator extends PowerGenerator{
super(name);
}
@Override
public void init(){
super.init();
//proper light clipping
clipSize = Math.max(clipSize, 45f * size * 2f * 2f);
}
@Override
public void setStats(){
super.setStats();
@@ -52,7 +59,7 @@ public class ThermalGenerator extends PowerGenerator{
@Override
public void drawLight(){
Drawf.light(team, x, y, (40f + Mathf.absin(10f, 5f)) * productionEfficiency * size, Color.scarlet, 0.4f);
Drawf.light(team, x, y, (40f + Mathf.absin(10f, 5f)) * Math.min(productionEfficiency, 2f) * size, Color.scarlet, 0.4f);
}
@Override

View File

@@ -4,7 +4,6 @@ import mindustry.world.blocks.power.*;
import mindustry.world.meta.*;
public class PowerSource extends PowerNode{
public float powerProduction = 10000f;
public PowerSource(String name){

View File

@@ -1,9 +1,11 @@
package mindustry.world.meta;
import arc.struct.*;
import mindustry.*;
public class Attribute{
public static Attribute[] all = {};
public static ObjectMap<String, Attribute> map = new ObjectMap<>();
public static final Attribute
/** Heat content. Used for thermal generator yield. */
@@ -36,6 +38,11 @@ public class Attribute{
return name;
}
/** Never returns null, may throw an exception if not found. */
public static Attribute get(String name){
return map.getThrow(name, () -> new IllegalArgumentException("Unknown Attribute type: " + name));
}
/** Automatically registers this attribute for use. Do not call after mod init. */
public static Attribute add(String name){
Attribute a = new Attribute(all.length, name);
@@ -43,6 +50,7 @@ public class Attribute{
all = new Attribute[all.length + 1];
System.arraycopy(prev, 0, all, 0, a.id);
all[a.id] = a;
map.put(name, a);
return a;
}
}

View File

@@ -198,15 +198,10 @@ public class StatValues{
table.image(region).size(60).scaling(Scaling.bounded).right().top();
table.table(Tex.underline, w -> {
table.table(Tex.underline, w -> {
w.left().defaults().padRight(3).left();
if(weapon.inaccuracy > 0){
sep(w, "[lightgray]" + Stat.inaccuracy.localized() + ": [white]" + (int)weapon.inaccuracy + " " + StatUnit.degrees.localized());
}
sep(w, "[lightgray]" + Stat.reload.localized() + ": " + (weapon.mirror ? "2x " : "") + "[white]" + Strings.autoFixed(60f / weapon.reload * weapon.shots, 2));
ammo(ObjectMap.of(unit, weapon.bullet)).display(w);
weapon.addStats(unit, w);
}).padTop(-9).left();
table.row();
}
@@ -275,10 +270,6 @@ public class StatValues{
sep(bt, "@bullet.incendiary");
}
if(type.status != StatusEffects.none){
sep(bt, (type.minfo.mod == null ? type.status.emoji() : "") + "[stat]" + type.status.localizedName);
}
if(type.homingPower > 0.01f){
sep(bt, "@bullet.homing");
}
@@ -290,6 +281,10 @@ public class StatValues{
if(type.fragBullet != null){
sep(bt, "@bullet.frag");
}
if(type.status != StatusEffects.none){
sep(bt, (type.minfo.mod == null ? type.status.emoji() : "") + "[stat]" + type.status.localizedName);
}
}).padTop(unit ? 0 : -9).left().get().background(unit ? null : Tex.underline);
table.row();
@@ -298,7 +293,6 @@ public class StatValues{
}
//for AmmoListValue
private static void sep(Table table, String text){
table.row();
table.add(text);