Various database entry stats (#11583)

* Add Firerate Stat to Lustre and sublimate

* 8 votes

* forgot comment

* Show shock mine damage stats

* make spawnBullets appear in core database

* spawned instead

* typo h

* fuck impact stats

* segment and point defense stuff

* Change repair & shieldregen field's shown stats

* Grant "Airborne" achievement when commanding units

* more database entries

* navanax entries (holy hell there are so many)

* boosting speed entry

* add arrow icon and stuff

* dont break tests

* junction storage per side

* Apply suggestion from @Anuken

---------

Co-authored-by: Anuken <arnukren@gmail.com>
This commit is contained in:
EggleEgg
2026-02-10 18:05:42 +01:00
committed by GitHub
parent cb1ba51238
commit 121afa0c95
22 changed files with 228 additions and 75 deletions

View File

@@ -1069,6 +1069,20 @@
"search": [ "search": [
"planet" "planet"
] ]
},
{
"uid": "ec43f846ca53702f30310718d627f2b5",
"css": "arrow-note",
"code": 59444,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M50 0L0 50V150 250 350 450 550 650L50 700H150 250 350 450 550 650 750L800 750 750 800 700 850V950L750 1000H850L900 950 950 900 1000 850 1050 800 1100 750 1150 700 1200 650V550L1150 500 1100 450 1050 400 1000 350 950 300 900 250 850 200H750L700 250V350L750 400 800 450 750 500H650 550 450 350 250L200 450V350 250 150 50L150 0Z",
"width": 1200
},
"search": [
"arrow-note"
]
} }
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -1019,7 +1019,6 @@ stat.targetsair = Targets Air
stat.targetsground = Targets Ground stat.targetsground = Targets Ground
stat.crushdamage = Crush Damage stat.crushdamage = Crush Damage
stat.legsplashdamage = Leg Splash Damage stat.legsplashdamage = Leg Splash Damage
stat.legsplashrange = Leg Splash Range
stat.itemsmoved = Move Speed stat.itemsmoved = Move Speed
stat.launchtime = Time Between Launches stat.launchtime = Time Between Launches
stat.shootrange = Range stat.shootrange = Range
@@ -1036,6 +1035,7 @@ stat.itemcapacity = Item Capacity
stat.memorycapacity = Memory Capacity stat.memorycapacity = Memory Capacity
stat.basepowergeneration = Base Power Generation stat.basepowergeneration = Base Power Generation
stat.productiontime = Production Time stat.productiontime = Production Time
stat.warmuptime = Warmup Time
stat.repairtime = Block Full Repair Time stat.repairtime = Block Full Repair Time
stat.repairspeed = Repair Speed stat.repairspeed = Repair Speed
stat.weapons = Weapons stat.weapons = Weapons
@@ -1078,6 +1078,7 @@ stat.minetier = Mine Tier
stat.payloadcapacity = Payload Capacity stat.payloadcapacity = Payload Capacity
stat.abilities = Abilities stat.abilities = Abilities
stat.canboost = Can Boost stat.canboost = Can Boost
stat.boostingspeed = Boosting Speed
stat.flying = Flying stat.flying = Flying
stat.ammouse = Ammo Use stat.ammouse = Ammo Use
stat.ammocapacity = Ammo Capacity stat.ammocapacity = Ammo Capacity
@@ -1176,6 +1177,8 @@ bar.activated = Activated
units.processorcontrol = [lightgray]Processor Controlled units.processorcontrol = [lightgray]Processor Controlled
weapon.pointdefense = [stat]Point Defense
bullet.damage = [stat]{0}[lightgray] damage bullet.damage = [stat]{0}[lightgray] damage
bullet.splashdamage = [stat]{0}[lightgray] area dmg ~ [stat]{1}[lightgray] tiles bullet.splashdamage = [stat]{0}[lightgray] area dmg ~ [stat]{1}[lightgray] tiles
bullet.incendiary = [stat]incendiary bullet.incendiary = [stat]incendiary
@@ -1186,10 +1189,17 @@ bullet.armorpiercing = [stat]{0}%[lightgray] armor piercing
bullet.antiarmor = [stat]{0}x[lightgray] anti-armor bullet.antiarmor = [stat]{0}x[lightgray] anti-armor
bullet.maxdamagefraction = [stat]{0}%[lightgray] damage limit bullet.maxdamagefraction = [stat]{0}%[lightgray] damage limit
bullet.suppression = [stat]{0}[lightgray] seconds of repair suppression ~ [stat]{1}[lightgray] tiles bullet.suppression = [stat]{0}[lightgray] seconds of repair suppression ~ [stat]{1}[lightgray] tiles
bullet.empradius = [stat]{0}[lightgray] tiles EMP radius
bullet.empboost = [stat]{0}%[lightgray] overdrive ~ [stat]{1}[lightgray]
bullet.empdamage = [stat]{0}%[lightgray] power damage
bullet.empslowdown = [stat]{0}%[lightgray] enemy power overdrive ~ [stat]{1}[lightgray]
bullet.empunitdamage = [stat]{0}%[lightgray] unit damage
bullet.interval = [stat]{0}/sec[lightgray] interval bullets: bullet.interval = [stat]{0}/sec[lightgray] interval bullets:
bullet.frags = [stat]{0}x[lightgray] frag bullets: bullet.frags = [stat]{0}x[lightgray] frag bullets:
bullet.lightning = [stat]{0}x[lightgray] lightning ~ [stat]{1}[lightgray] damage bullet.lightning = [stat]{0}x[lightgray] lightning ~ [stat]{1}[lightgray] damage
bullet.lightninginterval = [stat]{0}[lightgray] tiles interval ~ [stat]{1}[lightgray] tiles length
bullet.buildingdamage = [stat]{0}%[lightgray] building damage bullet.buildingdamage = [stat]{0}%[lightgray] building damage
bullet.spawnBullets = [stat]{0}x[lightgray] spawned bullets:
bullet.shielddamage = [stat]{0}%[lightgray] shield damage bullet.shielddamage = [stat]{0}%[lightgray] shield damage
bullet.knockback = [stat]{0}[lightgray] knockback bullet.knockback = [stat]{0}[lightgray] knockback
bullet.pierce = [stat]{0}x[lightgray] pierce bullet.pierce = [stat]{0}x[lightgray] pierce
@@ -1210,6 +1220,7 @@ unit.liquidsecond = liquid units/second
unit.itemssecond = items/second unit.itemssecond = items/second
unit.liquidunits = liquid units unit.liquidunits = liquid units
unit.powerunits = power units unit.powerunits = power units
unit.powerequilibrium = power equilibrium
unit.heatunits = heat units unit.heatunits = heat units
unit.degrees = degrees unit.degrees = degrees
unit.seconds = seconds unit.seconds = seconds
@@ -1227,6 +1238,7 @@ unit.billions = b
unit.shots = shots unit.shots = shots
unit.pershot = /shot unit.pershot = /shot
unit.perleg = per leg unit.perleg = per leg
unit.perside = per side
category.purpose = Purpose category.purpose = Purpose
category.general = General category.general = General
category.power = Power category.power = Power
@@ -2847,4 +2859,4 @@ lenum.ambientlight = Ambient light color. Used when lighting is enabled.
lenum.solarmultiplier = Multiplies power output of solar panels. lenum.solarmultiplier = Multiplies power output of solar panels.
lenum.dragmultiplier = Environment drag multiplier. lenum.dragmultiplier = Environment drag multiplier.
lenum.ban = Blocks or units that cannot be placed or built. lenum.ban = Blocks or units that cannot be placed or built.
lenum.unban = Unban a unit or block. lenum.unban = Unban a unit or block.

Binary file not shown.

Binary file not shown.

View File

@@ -10,7 +10,7 @@ public class BoostAI extends AIController{
public void updateUnit(){ public void updateUnit(){
if(unit.controller() instanceof CommandAI ai){ if(unit.controller() instanceof CommandAI ai){
ai.defaultBehavior(); ai.defaultBehavior();
unit.updateBoosting(true); unit.updateBoosting(true, true);
//auto land when near target //auto land when near target
if(ai.attackTarget != null && unit.within(ai.attackTarget, unit.range())){ if(ai.attackTarget != null && unit.within(ai.attackTarget, unit.range())){

View File

@@ -2368,64 +2368,6 @@ public class UnitTypes{
buildSpeed = 3.5f; buildSpeed = 3.5f;
rotateToBuilding = false; rotateToBuilding = false;
for(float mountY : new float[]{-117/4f, 50/4f}){
for(float sign : Mathf.signs){
weapons.add(new Weapon("plasma-laser-mount"){{
shadow = 20f;
controllable = false;
autoTarget = true;
mirror = false;
shake = 3f;
shootY = 7f;
rotate = true;
x = 84f/4f * sign;
y = mountY;
targetInterval = 20f;
targetSwitchInterval = 35f;
rotateSpeed = 3.5f;
reload = 170f;
recoil = 1f;
shootSound = Sounds.beamPlasmaSmall;
initialShootSound = Sounds.shootBeamPlasmaSmall;
continuous = true;
cooldownTime = reload;
immunities.add(StatusEffects.burning);
bullet = new ContinuousLaserBulletType(){{
maxRange = 90f;
damage = 27f;
length = 95f;
hitEffect = Fx.hitMeltHeal;
drawSize = 200f;
lifetime = 155f;
shake = 1f;
shootEffect = Fx.shootHeal;
smokeEffect = Fx.none;
width = 4f;
largeHit = false;
incendChance = 0.03f;
incendSpread = 5f;
incendAmount = 1;
healPercent = 0.4f;
collidesTeam = true;
colors = new Color[]{Pal.heal.cpy().a(.2f), Pal.heal.cpy().a(.5f), Pal.heal.cpy().mul(1.2f), Color.white};
}};
}});
}
}
abilities.add(new SuppressionFieldAbility(){{
orbRadius = 5;
particleSize = 3;
y = -10f;
particles = 10;
color = particleColor = effectColor = Pal.heal;
}});
weapons.add(new Weapon("emp-cannon-mount"){{ weapons.add(new Weapon("emp-cannon-mount"){{
rotate = true; rotate = true;
@@ -2508,6 +2450,66 @@ public class UnitTypes{
}); });
}}; }};
}}); }});
for(float mountY : new float[]{-117/4f, 50/4f}){
for(float sign : Mathf.signs){
weapons.add(new Weapon("plasma-laser-mount"){{
shadow = 20f;
controllable = false;
autoTarget = true;
mirror = false;
shake = 3f;
shootY = 7f;
rotate = true;
x = 84f/4f * sign;
y = mountY;
targetInterval = 20f;
targetSwitchInterval = 35f;
rotateSpeed = 3.5f;
reload = 170f;
recoil = 1f;
shootSound = Sounds.beamPlasmaSmall;
initialShootSound = Sounds.shootBeamPlasmaSmall;
continuous = true;
cooldownTime = reload;
immunities.add(StatusEffects.burning);
bullet = new ContinuousLaserBulletType(){{
maxRange = 90f;
damage = 27f;
length = 95f;
hitEffect = Fx.hitMeltHeal;
drawSize = 200f;
lifetime = 155f;
shake = 1f;
shootEffect = Fx.shootHeal;
smokeEffect = Fx.none;
width = 4f;
largeHit = false;
incendChance = 0.03f;
incendSpread = 5f;
incendAmount = 1;
healPercent = 0.4f;
collidesTeam = true;
colors = new Color[]{Pal.heal.cpy().a(.2f), Pal.heal.cpy().a(.5f), Pal.heal.cpy().mul(1.2f), Color.white};
}};
}});
}
}
abilities.add(new SuppressionFieldAbility(){{
orbRadius = 5;
particleSize = 3;
y = -10f;
particles = 10;
color = particleColor = effectColor = Pal.heal;
}});
}}; }};
//endregion //endregion
@@ -3115,6 +3117,7 @@ public class UnitTypes{
float fin = 0.05f + (j + 1) / (float)count; float fin = 0.05f + (j + 1) / (float)count;
float spd = speed; float spd = speed;
float life = lifetime / Mathf.lerp(fin, 1f, 0.5f); float life = lifetime / Mathf.lerp(fin, 1f, 0.5f);
boolean show = j == 0 && i > 0;
spawnBullets.add(new BasicBulletType(spd * fin, 60){{ spawnBullets.add(new BasicBulletType(spd * fin, 60){{
drag = 0.002f; drag = 0.002f;
width = 12f; width = 12f;
@@ -3124,6 +3127,7 @@ public class UnitTypes{
hitSize = 5f; hitSize = 5f;
pierceCap = 2; pierceCap = 2;
pierce = true; pierce = true;
showStats = show;
pierceBuilding = true; pierceBuilding = true;
hitColor = backColor = trailColor = Color.valueOf("feb380"); hitColor = backColor = trailColor = Color.valueOf("feb380");
frontColor = Color.white; frontColor = Color.white;

View File

@@ -43,11 +43,15 @@ public class RepairFieldAbility extends Ability{
super.addStats(t); super.addStats(t);
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2))); t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2)));
t.row(); t.row();
t.add(abilityStat("repairspeed", Strings.autoFixed(amount * 60f / reload, 2))); t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2)));
t.row(); t.row();
if(amount > 0){
t.add(Core.bundle.format("bullet.healamount", Strings.autoFixed(amount, 2)) + "[lightgray] ~ []" + abilityStat("repairspeed", Strings.autoFixed(amount * 60f / reload, 2)));
t.row();
}
if(healPercent > 0f){ if(healPercent > 0f){
t.row(); t.row();
t.add(Core.bundle.format("bullet.healpercent", Strings.autoFixed(healPercent, 2))); t.add(Core.bundle.format("bullet.healpercent", Strings.autoFixed(healPercent, 2)) + "[lightgray] ~ []" + abilityStat("repairspeed", Strings.autoFixed(healPercent * 60f / reload, 2) + "%"));
} }
if(sameTypeHealMult != 1f){ if(sameTypeHealMult != 1f){
t.row(); t.row();

View File

@@ -38,7 +38,7 @@ public class ShieldRegenFieldAbility extends Ability{
t.row(); t.row();
t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2))); t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2)));
t.row(); t.row();
t.add(abilityStat("pulseregen", Strings.autoFixed(amount, 2))); t.add(abilityStat("pulseregen", Strings.autoFixed(amount, 2)) + "[lightgray] ~ []" + abilityStat("regen", Strings.autoFixed(amount * 60f / reload, 2)));
t.row(); t.row();
t.add(abilityStat("shield", Strings.autoFixed(max, 2))); t.add(abilityStat("shield", Strings.autoFixed(max, 2)));
} }

View File

@@ -241,6 +241,8 @@ public class BulletType extends Content implements Cloneable{
public Effect healEffect = Fx.healBlockFull; public Effect healEffect = Fx.healBlockFull;
/** Bullets spawned when this bullet is created. Rarely necessary, used for visuals. */ /** Bullets spawned when this bullet is created. Rarely necessary, used for visuals. */
public Seq<BulletType> spawnBullets = new Seq<>(); public Seq<BulletType> spawnBullets = new Seq<>();
/** Whether to display the stats of the spawned bullet. */
public boolean showStats = false;
/** Random angle spread of spawn bullets. */ /** Random angle spread of spawn bullets. */
public float spawnBulletRandomSpread = 0f; public float spawnBulletRandomSpread = 0f;
/** Unit spawned _instead of_ this bullet. Useful for missiles. */ /** Unit spawned _instead of_ this bullet. Useful for missiles. */

View File

@@ -113,10 +113,17 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
} }
public void updateBoosting(boolean boost){ public void updateBoosting(boolean boost){
updateBoosting(boost, false);
}
public void updateBoosting(boolean boost, boolean event){
if(!type.canBoost || dead) return; if(!type.canBoost || dead) return;
boolean shouldBoost = boost || onSolid() || (isFlying() && !canLand()); boolean shouldBoost = boost || onSolid() || (isFlying() && !canLand());
elevation = Mathf.approachDelta(elevation, type.canBoost ? Mathf.num(shouldBoost) : 0f, shouldBoost ? type.riseSpeed : type.descentSpeed); elevation = Mathf.approachDelta(elevation, type.canBoost ? Mathf.num(shouldBoost) : 0f, shouldBoost ? type.riseSpeed : type.descentSpeed);
if(event){
Events.fire(Trigger.unitCommandBoost);
}
} }
/** Move based on preferred unit movement type. */ /** Move based on preferred unit movement type. */

View File

@@ -46,6 +46,7 @@ public class EventType{
unitCommandChange, unitCommandChange,
unitCommandPosition, unitCommandPosition,
unitCommandAttack, unitCommandAttack,
unitCommandBoost,
importMod, importMod,
draw, draw,
drawOver, drawOver,

View File

@@ -177,6 +177,12 @@ public class GameService{
} }
}); });
Events.run(Trigger.unitCommandBoost, () -> {
if(campaign()){
boostUnit.complete();
}
});
Events.run(Trigger.newGame, () -> Core.app.post(() -> { Events.run(Trigger.newGame, () -> Core.app.post(() -> {
if(campaign() && player.core() != null && player.core().items.total() >= 10 * 1000){ if(campaign() && player.core() != null && player.core().items.total() >= 10 * 1000){
drop10kitems.complete(); drop10kitems.complete();

View File

@@ -794,8 +794,10 @@ public class UnitType extends UnlockableContent implements Senseable{
} }
if(legSplashDamage > 0 && legSplashRange > 0){ if(legSplashDamage > 0 && legSplashRange > 0){
stats.add(Stat.legSplashDamage, legSplashDamage, StatUnit.perLeg); stats.add(Stat.legSplashDamage, table -> {
stats.add(Stat.legSplashRange, Strings.autoFixed(legSplashRange / tilesize, 1), StatUnit.blocks); table.add((String)(Core.bundle.format("bullet.splashdamage", Strings.autoFixed(legSplashDamage, 2),
Strings.autoFixed(legSplashRange / tilesize, 2))).replace("[stat]", "[white]") + " " + StatUnit.perLeg.localized());
});
} }
stats.add(Stat.targetsAir, targetAir); stats.add(Stat.targetsAir, targetAir);
@@ -809,6 +811,9 @@ public class UnitType extends UnlockableContent implements Senseable{
if(!flying){ if(!flying){
stats.add(Stat.canBoost, canBoost); stats.add(Stat.canBoost, canBoost);
if(canBoost){
stats.add(Stat.boostingSpeed, boostMultiplier * speed * 60f / tilesize, StatUnit.tilesSecond);
}
} }
if(mineTier >= 1){ if(mineTier >= 1){

View File

@@ -1,13 +1,18 @@
package mindustry.type.weapons; package mindustry.type.weapons;
import arc.*;
import arc.graphics.*; import arc.graphics.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.entities.*; import mindustry.entities.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -38,6 +43,12 @@ public class PointDefenseWeapon extends Weapon{
targetInterval = 10f; targetInterval = 10f;
} }
@Override
public void addStats(UnitType u, Table t){
t.add(Core.bundle.format("weapon.pointdefense"));
super.addStats(u, t);
}
@Override @Override
protected Teamc findTarget(Unit unit, float x, float y, float range, boolean air, boolean ground){ protected Teamc findTarget(Unit unit, float x, float y, float range, boolean air, boolean ground){
return Groups.bullet.intersect(x - range, y - range, range*2, range*2).min(b -> b.team != unit.team && b.type().hittable, b -> b.dst2(x, y)); return Groups.bullet.intersect(x - range, y - range, range*2, range*2).min(b -> b.team != unit.team && b.type().hittable, b -> b.dst2(x, y));

View File

@@ -1,5 +1,6 @@
package mindustry.world.blocks.defense; package mindustry.world.blocks.defense;
import arc.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.math.*; import arc.math.*;
@@ -11,6 +12,7 @@ import mindustry.entities.bullet.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.meta.*;
public class ShockMine extends Block{ public class ShockMine extends Block{
public final int timerDamage = timers++; public final int timerDamage = timers++;
@@ -35,6 +37,14 @@ public class ShockMine extends Block{
targetable = false; targetable = false;
} }
@Override
public void setStats(){
super.setStats();
stats.add(Stat.damage, table -> {
table.add((String)(Core.bundle.format("bullet.lightning", tendrils, Strings.autoFixed(damage, 2)).replace("[stat]", "[white]")));
});
}
public class ShockMineBuild extends Building{ public class ShockMineBuild extends Building{
@Override @Override

View File

@@ -52,6 +52,7 @@ public class PointDefenseTurret extends ReloadTurret{
super.setStats(); super.setStats();
stats.add(Stat.reload, 60f / reload, StatUnit.perSecond); stats.add(Stat.reload, 60f / reload, StatUnit.perSecond);
stats.add(Stat.damage, bulletDamage, StatUnit.none);
} }
public class PointDefenseBuild extends ReloadTurretBuild{ public class PointDefenseBuild extends ReloadTurretBuild{

View File

@@ -30,7 +30,9 @@ public class Junction extends Block{
//(60f / speed * capacity) returns 13.84 which is not the actual value (non linear, depends on fps) //(60f / speed * capacity) returns 13.84 which is not the actual value (non linear, depends on fps)
stats.add(Stat.itemsMoved, displayedSpeed, StatUnit.itemsSecond); stats.add(Stat.itemsMoved, displayedSpeed, StatUnit.itemsSecond);
stats.add(Stat.itemCapacity, capacity, StatUnit.items); stats.add(Stat.itemCapacity, table -> {
table.add(Strings.autoFixed(capacity, 2) + " " + StatUnit.items.localized() + " " + StatUnit.perSide.localized());
});
} }
@Override @Override

View File

@@ -59,6 +59,13 @@ public class ImpactReactor extends PowerGenerator{
if(hasItems){ if(hasItems){
stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds); stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds);
} }
//exponential decay formula
float max = -(float)Math.log(0.001f) / warmupSpeed / 60f;
float equal = -(float)Math.log(1f - Mathf.pow(consPower.usage / powerProduction, 1f / 5f)) / warmupSpeed / 60f;
stats.add(Stat.warmupTime, t -> {
t.add(Strings.autoFixed(max, 2) + " " + StatUnit.seconds.localized() + (consPower != null ?
" ~ " + Strings.autoFixed(equal, 2) + " " + StatUnit.seconds.localized() + " " + StatUnit.powerEquilibrium.localized() : ""));
});
} }
public class ImpactReactorBuild extends GeneratorBuild{ public class ImpactReactorBuild extends GeneratorBuild{

View File

@@ -36,6 +36,7 @@ public class Stat implements Comparable<Stat>{
lightningDamage = new Stat("lightningDamage"), lightningDamage = new Stat("lightningDamage"),
abilities = new Stat("abilities"), abilities = new Stat("abilities"),
canBoost = new Stat("canBoost"), canBoost = new Stat("canBoost"),
boostingSpeed = new Stat("boostingspeed"),
maxUnits = new Stat("maxUnits"), maxUnits = new Stat("maxUnits"),
damageMultiplier = new Stat("damageMultiplier"), damageMultiplier = new Stat("damageMultiplier"),
@@ -60,6 +61,7 @@ public class Stat implements Comparable<Stat>{
powerRange = new Stat("powerRange", StatCat.power), powerRange = new Stat("powerRange", StatCat.power),
powerConnections = new Stat("powerConnections", StatCat.power), powerConnections = new Stat("powerConnections", StatCat.power),
basePowerGeneration = new Stat("basePowerGeneration", StatCat.power), basePowerGeneration = new Stat("basePowerGeneration", StatCat.power),
warmupTime = new Stat("warmupTime", StatCat.power),
tiles = new Stat("tiles", StatCat.crafting), tiles = new Stat("tiles", StatCat.crafting),
input = new Stat("input", StatCat.crafting), input = new Stat("input", StatCat.crafting),

View File

@@ -20,6 +20,7 @@ public class StatUnit{
itemsSecond = new StatUnit("itemsSecond"), itemsSecond = new StatUnit("itemsSecond"),
liquidUnits = new StatUnit("liquidUnits", "[sky]" + Iconc.liquid + "[]"), liquidUnits = new StatUnit("liquidUnits", "[sky]" + Iconc.liquid + "[]"),
powerUnits = new StatUnit("powerUnits", "[accent]" + Iconc.power + "[]"), powerUnits = new StatUnit("powerUnits", "[accent]" + Iconc.power + "[]"),
powerEquilibrium = new StatUnit("powerEquilibrium"),
heatUnits = new StatUnit("heatUnits", "[red]" + Iconc.waves + "[]"), heatUnits = new StatUnit("heatUnits", "[red]" + Iconc.waves + "[]"),
degrees = new StatUnit("degrees"), degrees = new StatUnit("degrees"),
seconds = new StatUnit("seconds"), seconds = new StatUnit("seconds"),
@@ -29,6 +30,7 @@ public class StatUnit{
perMinute = new StatUnit("perMinute", false), perMinute = new StatUnit("perMinute", false),
perShot = new StatUnit("perShot", false), perShot = new StatUnit("perShot", false),
perLeg = new StatUnit("perLeg"), perLeg = new StatUnit("perLeg"),
perSide = new StatUnit("perSide"),
timesSpeed = new StatUnit("timesSpeed", false), timesSpeed = new StatUnit("timesSpeed", false),
multiplier = new StatUnit("multiplier", false), multiplier = new StatUnit("multiplier", false),
percent = new StatUnit("percent", false), percent = new StatUnit("percent", false),

View File

@@ -7,6 +7,7 @@ import arc.graphics.g2d.*;
import arc.math.*; import arc.math.*;
import arc.scene.*; import arc.scene.*;
import arc.scene.event.*; import arc.scene.event.*;
import arc.scene.style.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.scene.ui.Tooltip.*; import arc.scene.ui.Tooltip.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
@@ -19,6 +20,7 @@ import mindustry.ctype.*;
import mindustry.entities.abilities.*; import mindustry.entities.abilities.*;
import mindustry.entities.bullet.*; import mindustry.entities.bullet.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.maps.*; import mindustry.maps.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
@@ -31,6 +33,9 @@ import static mindustry.Vars.*;
/** Utilities for displaying certain stats in a table. */ /** Utilities for displaying certain stats in a table. */
public class StatValues{ public class StatValues{
//only allocate once, dont break unit tests
static @Nullable TextureRegionDrawable noteIcon = Icon.arrowNoteSmall != null ? new TextureRegionDrawable(Icon.arrowNoteSmall) : null;
public static StatValue string(String value, Object... args){ public static StatValue string(String value, Object... args){
String result = Strings.format(value, args); String result = Strings.format(value, args);
return table -> table.add(result); return table -> table.add(result);
@@ -640,11 +645,8 @@ public class StatValues{
} }
if(type.damage > 0 && (type.collides || type.splashDamage <= 0)){ if(type.damage > 0 && (type.collides || type.splashDamage <= 0)){
if(type.continuousDamage() > 0){ bt.add(Core.bundle.format("bullet.damage", type.damage) + (type.continuousDamage() > 0 ?
bt.add(Core.bundle.format("bullet.damage", type.continuousDamage()) + StatUnit.perSecond.localized()); "[lightgray] ~ [stat]" + Core.bundle.format("bullet.damage", type.continuousDamage()) + StatUnit.perSecond.localized() : ""));
}else{
bt.add(Core.bundle.format("bullet.damage", type.damage));
}
} }
if(type.buildingDamageMultiplier != 1){ if(type.buildingDamageMultiplier != 1){
@@ -700,17 +702,44 @@ public class StatValues{
sep(bt, Core.bundle.format("bullet.lightning", type.lightning, type.lightningDamage < 0 ? type.damage : type.lightningDamage)); sep(bt, Core.bundle.format("bullet.lightning", type.lightning, type.lightningDamage < 0 ? type.damage : type.lightningDamage));
} }
if(type instanceof LaserBulletType b && b.lightningSpacing > 0){
int count = (int)(b.length / b.lightningSpacing) * 2 + 2;
float damage = b.lightningDamage < 0 ? b.damage : b.lightningDamage;
sep(bt, Core.bundle.format("bullet.lightning", count, damage));
note(bt, Core.bundle.format("bullet.lightninginterval", Strings.autoFixed(b.lightningSpacing / tilesize, 2), Strings.autoFixed(b.lightningLength, 2))).left();
}
if(type instanceof EmpBulletType b && b.radius > 0f){
sep(bt, Core.bundle.format("bullet.empradius", Strings.fixed(b.radius / tilesize, 1)));
if(b.timeDuration > 0f && b.timeIncrease > 1f){
sep(bt, Core.bundle.format("bullet.empboost", Strings.autoFixed(b.timeIncrease * 100f, 2),
Strings.autoFixed(b.timeDuration / 60f, 1)) + " " + StatUnit.seconds.localized());
}
if(b.timeDuration > 0f && b.powerSclDecrease < 1f){
sep(bt, Core.bundle.format("bullet.empslowdown",
(b.powerSclDecrease < 1f ? "[negstat]" : "") + Strings.autoFixed((b.powerSclDecrease - 1f) * 100f, 2),
Strings.autoFixed(b.timeDuration / 60f, 1)) + " " + StatUnit.seconds.localized());
}
if(!Mathf.equal(b.powerDamageScl, 1f)){
sep(bt, Core.bundle.format("bullet.empdamage", Strings.autoFixed(b.powerDamageScl * 100f, 2)));
}
if(b.hitUnits){
sep(bt, Core.bundle.format("bullet.empunitdamage",
(b.unitDamageScl < 1f ? "[negstat]" : "") + Strings.autoFixed(b.unitDamageScl * 100f, 2)));
}
}
if(type.pierceArmor){ if(type.pierceArmor){
sep(bt, "@bullet.armorpierce"); sep(bt, "@bullet.armorpierce");
} }
if(type.armorMultiplier != 1f){ if(type.armorMultiplier != 1f){
if(type.armorMultiplier > 1f){ if(type.armorMultiplier > 1f){
sep(bt, Core.bundle.format("bullet.armorweakness", (int)(type.armorMultiplier * 100))); sep(bt, Core.bundle.format("bullet.armorweakness", (int)(type.armorMultiplier * 100)));
}else if(Mathf.sign(type.armorMultiplier) == 1){ }else if(Mathf.sign(type.armorMultiplier) == 1){
sep(bt, Core.bundle.format("bullet.armorpiercing", (int)((1 - type.armorMultiplier) * 100))); sep(bt, Core.bundle.format("bullet.armorpiercing", (int)((1 - type.armorMultiplier) * 100)));
}else{ }else{
sep(bt, Core.bundle.format("bullet.antiarmor", (-type.armorMultiplier))); sep(bt, Core.bundle.format("bullet.antiarmor", (-type.armorMultiplier)));
} }
} }
@@ -770,6 +799,27 @@ public class StatValues{
bt.row(); bt.row();
bt.add(coll); bt.add(coll);
} }
if(type.spawnBullets != null && type.spawnBullets.size > 0){
bt.row();
Table sc = new Table();
for(BulletType spawn : type.spawnBullets){
if(spawn.showStats) ammo(ObjectMap.of(t, spawn), true, false).display(sc);
}
Collapser coll = new Collapser(sc, true);
coll.setDuration(0.1f);
bt.table(st -> {
st.left().defaults().left();
st.add(Core.bundle.format("bullet.spawnBullets", type.spawnBullets.size));
if(sc.getChildren().size > 0) st.button(Icon.downOpen, Styles.emptyi, () -> coll.toggle(false)).update(i -> i.getStyle().imageUp = (!coll.isCollapsed() ? Icon.upOpen : Icon.downOpen)).size(8).padLeft(16f).expandX();
});
bt.row();
bt.add(coll);
}
}).padLeft(5).padTop(5).padBottom(compact ? 0 : 5).growX().margin(compact ? 0 : 10); }).padLeft(5).padTop(5).padBottom(compact ? 0 : 5).growX().margin(compact ? 0 : 10);
table.row(); table.row();
} }
@@ -782,6 +832,19 @@ public class StatValues{
return table.add(text); return table.add(text);
} }
//add a note under a value
private static Cell<?> note(Table table, String text){
table.row();
return table.table(t -> {
if(noteIcon != null){
noteIcon.setMinWidth(15f);
noteIcon.setMinHeight(15f);
t.image(noteIcon).color(Pal.stat).scaling(Scaling.fit).padRight(6).padLeft(12);
}
t.add(text);
});
}
//for AmmoListValue //for AmmoListValue
private static String ammoStat(float val){ private static String ammoStat(float val){
return (val > 0 ? "[stat]+" : "[negstat]") + Strings.autoFixed(val, 1); return (val > 0 ? "[stat]+" : "[negstat]") + Strings.autoFixed(val, 1);