Merge branch 'EggleEgg-dps-dummy'
@@ -6,6 +6,7 @@ atrax=1
|
||||
beta=30
|
||||
block=2
|
||||
corvus=24
|
||||
dummy=49
|
||||
elude=45
|
||||
flare=3
|
||||
gamma=31
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{fields:[{name:abilities,type:"mindustry.entities.abilities.Ability[]"},{name:aimX,type:float},{name:aimY,type:float},{name:building,type:Building},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue<mindustry.entities.units.BuildPlan>},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq<mindustry.entities.units.StatusEntry>},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:updateBuilding,type:boolean},{name:vel,type:arc.math.geom.Vec2},{name:x,type:float},{name:y,type:float}]}
|
||||
|
After Width: | Height: | Size: 966 B |
|
After Width: | Height: | Size: 583 B |
|
After Width: | Height: | Size: 494 B |
|
After Width: | Height: | Size: 366 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 609 B |
@@ -1237,6 +1237,8 @@ stat.reactive = Reacts
|
||||
stat.immunities = Immunities
|
||||
stat.healing = Healing
|
||||
stat.efficiency = [stat]{0}% Efficiency
|
||||
stat.hitsize = Hitbox Size
|
||||
stat.resettime = Reset Time
|
||||
|
||||
ability.forcefield = Force Field
|
||||
ability.forcefield.description = Projects a force shield that absorbs bullets
|
||||
@@ -1324,6 +1326,9 @@ bar.activationtimer = Activates in {0}
|
||||
bar.activated = Activated
|
||||
bar.upto = Up to {0}
|
||||
bar.lerpwarmuptime = Affected by overdrive. Non-linear; uses the exponential decay formula.\nFor reference, this will reach 90% progress in {0} seconds, and 99% in {1} seconds.
|
||||
bar.damagetimetotal = {0} dmg/{1} sec
|
||||
bar.hits = {0} hits
|
||||
bar.dps = {0} DPS
|
||||
|
||||
units.processorcontrol = [lightgray]Processor Controlled
|
||||
|
||||
@@ -1367,6 +1372,7 @@ bullet.notargetsbuildings = [stat] ignores buildings
|
||||
|
||||
unit.blocks = blocks
|
||||
unit.blockssquared = blocks\u00B2
|
||||
unit.worldunits = world units
|
||||
unit.powersecond = power units/second
|
||||
unit.tilessecond = tiles/second
|
||||
unit.liquidsecond = liquid units/second
|
||||
@@ -2657,6 +2663,8 @@ block.large-logic-display.description = Displays arbitrary graphics from a logic
|
||||
block.tile-logic-display.description = Displays arbitrary graphics from a logic processor.\nSeamlessly connects to nearby tiled display blocks.
|
||||
block.interplanetary-accelerator.description = A massive electromagnetic railgun tower. Accelerates cores to escape velocity for interplanetary deployment.
|
||||
block.repair-turret.description = Continuously repairs the closest damaged unit in its vicinity. Optionally accepts coolant.
|
||||
block.target-dummy.name = Target Dummy
|
||||
block.target-dummy.description = Spawns a target dummy to test damage per second. Armor and flight can be configured.\nSandbox only.
|
||||
|
||||
#Erekir
|
||||
block.core-bastion.description = Core of the base. Armored. Once destroyed, the sector is lost.
|
||||
|
||||
@@ -624,3 +624,5 @@
|
||||
63058=metal-wall-3|block-metal-wall-3-ui
|
||||
63057=ore-wall-graphite|block-ore-wall-graphite-ui
|
||||
63056=large-canvas|block-large-canvas-ui
|
||||
63055=target-dummy|block-target-dummy-ui
|
||||
63054=dummy|unit-dummy-ui
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class NoAI implements UnitController{
|
||||
protected Unit unit;
|
||||
|
||||
@Override
|
||||
public Unit unit(){
|
||||
return unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unit(Unit unit){
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidController(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUnit(){}
|
||||
|
||||
@Override
|
||||
public void removed(Unit unit){}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class Blocks{
|
||||
shieldedWall,
|
||||
|
||||
//defense
|
||||
mender, mendProjector, overdriveProjector, overdriveDome, forceProjector, shockMine,
|
||||
mender, mendProjector, overdriveProjector, overdriveDome, forceProjector, shockMine, targetDummy,
|
||||
|
||||
//defense - erekir
|
||||
radar,
|
||||
@@ -6783,6 +6783,12 @@ public class Blocks{
|
||||
allDatabaseTabs = true;
|
||||
}};
|
||||
|
||||
targetDummy = new TargetDummy("target-dummy"){{
|
||||
requirements(Category.defense, BuildVisibility.sandboxOnly, with());
|
||||
size = 2;
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
|
||||
//TODO move
|
||||
illuminator = new LightBlock("illuminator"){{
|
||||
requirements(Category.effect, BuildVisibility.lightingOnly, with(Items.graphite, 12, Items.silicon, 8, Items.lead, 8));
|
||||
|
||||
@@ -78,6 +78,7 @@ public class UnitTypes{
|
||||
|
||||
//special building tethered (has payload capability, because it's necessary sometimes)
|
||||
public static @EntityDef({Unitc.class, BuildingTetherc.class, Payloadc.class}) UnitType manifold, assemblyDrone;
|
||||
public static @EntityDef({TargetDummyc.class, Unitc.class}) UnitType dummy;
|
||||
|
||||
//tank
|
||||
public static @EntityDef({Unitc.class, Tankc.class}) UnitType stell, locus, precept, vanquish, conquer;
|
||||
@@ -4631,6 +4632,40 @@ public class UnitTypes{
|
||||
envDisabled = Env.none;
|
||||
}};
|
||||
|
||||
dummy = new UnitType("dummy"){{
|
||||
controller = u -> new NoAI();
|
||||
|
||||
envEnabled = Env.any;
|
||||
envDisabled = 0;
|
||||
isEnemy = false;
|
||||
allowedInPayloads = false;
|
||||
logicControllable = false;
|
||||
playerControllable = false;
|
||||
hidden = true;
|
||||
hoverable = false;
|
||||
canBoost = true;
|
||||
useUnitCap = false;
|
||||
killable = false;
|
||||
physics = false;
|
||||
|
||||
flyingLayer = Layer.flyingUnit - 1f;
|
||||
drag = 0.33f;
|
||||
hitSize = 12f;
|
||||
hideDetails = false;
|
||||
engineOffset = 7f;
|
||||
engineSize = 2f;
|
||||
for(int i = 0; i < 3; i++){
|
||||
engines.add(new UnitEngine(Geometry.d4x(i) * engineOffset, Geometry.d4y(i) * engineOffset, engineSize, i * 90));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBody(Unit unit) {
|
||||
applyColor(unit);
|
||||
Drawf.spinSprite(region, unit.x, unit.y, unit.rotation - 90);
|
||||
Draw.reset();
|
||||
}};
|
||||
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,9 +506,11 @@ public class BulletType extends Content implements Cloneable{
|
||||
}
|
||||
|
||||
if(entity instanceof Unit unit){
|
||||
Tmp.v3.set(unit).sub(b).nor().scl(knockback * 80f);
|
||||
if(impact) Tmp.v3.setAngle(b.rotation() + (knockback < 0 ? 180f : 0f));
|
||||
unit.impulse(Tmp.v3);
|
||||
if(unit.type.knockbackMultiplier > 0f){
|
||||
Tmp.v3.set(unit).sub(b).nor().scl(knockback * 80f * unit.type.knockbackMultiplier);
|
||||
if(impact) Tmp.v3.setAngle(b.rotation() + (knockback < 0 ? 180f : 0f));
|
||||
unit.impulse(Tmp.v3);
|
||||
}
|
||||
unit.apply(status, statusDuration);
|
||||
|
||||
Events.fire(bulletDamageEvent.set(unit, b));
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.defense.TargetDummy.*;
|
||||
|
||||
@Component
|
||||
abstract class TargetDummyComp implements Unitc, Healthc{
|
||||
public @Nullable Building building;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(building == null || (!building.isPayload() && !building.isValid())){
|
||||
Call.unitDespawn(self()); //Don't despawn even if the building is on another team
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rawDamage(float amount){
|
||||
if(building instanceof TargetDummyBuild td){
|
||||
td.dummyHit(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class ClassMap{
|
||||
classes.put("PrebuildAI", mindustry.ai.types.PrebuildAI.class);
|
||||
classes.put("RepairAI", mindustry.ai.types.RepairAI.class);
|
||||
classes.put("SuicideAI", mindustry.ai.types.SuicideAI.class);
|
||||
classes.put("NoAI", mindustry.ai.types.NoAI.class);
|
||||
classes.put("Ability", mindustry.entities.abilities.Ability.class);
|
||||
classes.put("ArmorPlateAbility", mindustry.entities.abilities.ArmorPlateAbility.class);
|
||||
classes.put("EmptyDataAbility", mindustry.entities.abilities.EmptyDataAbility.class);
|
||||
@@ -190,6 +191,8 @@ public class ClassMap{
|
||||
classes.put("ThrusterBuild", mindustry.world.blocks.defense.Thruster.ThrusterBuild.class);
|
||||
classes.put("Wall", mindustry.world.blocks.defense.Wall.class);
|
||||
classes.put("WallBuild", mindustry.world.blocks.defense.Wall.WallBuild.class);
|
||||
classes.put("TargetDummy", mindustry.world.blocks.defense.TargetDummy.class);
|
||||
classes.put("TargetDummyBuild", mindustry.world.blocks.defense.TargetDummy.TargetDummyBuild.class);
|
||||
classes.put("BaseTurret", mindustry.world.blocks.defense.turrets.BaseTurret.class);
|
||||
classes.put("BaseTurretBuild", mindustry.world.blocks.defense.turrets.BaseTurret.BaseTurretBuild.class);
|
||||
classes.put("ContinuousLiquidTurret", mindustry.world.blocks.defense.turrets.ContinuousLiquidTurret.class);
|
||||
|
||||
@@ -110,6 +110,8 @@ public class UnitType extends UnlockableContent implements Senseable{
|
||||
strafePenalty = 0.5f,
|
||||
/** multiplier for cost of research in tech tree */
|
||||
researchCostMultiplier = 50,
|
||||
/** multiplier for knockback this unit receives */
|
||||
knockbackMultiplier = 1f,
|
||||
|
||||
/** for ground units, the layer upon which this unit is drawn */
|
||||
groundLayer = Layer.groundUnit,
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
package mindustry.world.blocks.defense;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.TextField.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class TargetDummy extends Block{
|
||||
public final int dpsUpdateTime = timers++;
|
||||
public UnitType unitType = UnitTypes.dummy;
|
||||
public float pullScale = 0.33f;
|
||||
public String emptyStr = "---";
|
||||
public @Load(value = "@-tether-end", fallback = "@") TextureRegion tetherEnd;
|
||||
public @Load(value = "@-tether", fallback = "@") TextureRegion tether;
|
||||
public @Load(value = "@-preview", fallback = "@") TextureRegion previewRegion;
|
||||
|
||||
public TargetDummy(String name){
|
||||
super(name);
|
||||
|
||||
update = alwaysUpdateInUnits = true;
|
||||
configurable = logicConfigurable = true;
|
||||
underBullets = true;
|
||||
targetable = false;
|
||||
|
||||
saveConfig = true;
|
||||
|
||||
config(FloatSeq.class, (TargetDummyBuild tile, FloatSeq config) -> {
|
||||
if(config.size > 0){
|
||||
tile.unitTeam = tile.team;
|
||||
if(config.get(0) == 1) tile.unitTeam = Team.get(tile.dummyTeam());
|
||||
}
|
||||
if(config.size > 1) tile.boosting = config.get(1) == 1;
|
||||
if(config.size > 2) tile.unitArmor = config.get(2);
|
||||
if(config.size > 3) tile.resetTime = config.get(3);
|
||||
if(config.size > 4) tile.dummySize = config.get(4);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextureRegion[] icons(){
|
||||
return new TextureRegion[]{previewRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
removeBar("health");
|
||||
|
||||
addBar("dps", (TargetDummyBuild entity) -> new Bar(
|
||||
() -> entity.displayDPS(false),
|
||||
() -> Pal.ammo,
|
||||
() -> 1f - (entity.reset / entity.resetTime)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawDefaultPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
super.drawDefaultPlanRegion(plan, list);
|
||||
|
||||
Draw.rect(unitType.fullIcon, plan.drawx(), plan.drawy());
|
||||
}
|
||||
|
||||
public class TargetDummyBuild extends Building implements UnitTetherBlock{
|
||||
//needs to be "unboxed" after reading, since units are read after buildings.
|
||||
public int readUnitId = -1;
|
||||
public Unit unit;
|
||||
public float resetTime = 120f;
|
||||
public float total, reset = resetTime, time, dummySize = 12f;
|
||||
public float dps, totalDisplay, timeDisplay;
|
||||
public int hits;
|
||||
public int hitsDisplay;
|
||||
public boolean boosting;
|
||||
public float unitArmor;
|
||||
public Team unitTeam;
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
//unit was lost/destroyed somehow
|
||||
if(unit != null && (unit.dead || !unit.isAdded())){
|
||||
unit = null;
|
||||
}
|
||||
|
||||
if(readUnitId != -1){
|
||||
unit = Groups.unit.getByID(readUnitId);
|
||||
if(unit != null || !net.client()){
|
||||
readUnitId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(unitTeam == null) unitTeam = team;
|
||||
|
||||
if(unit == null){
|
||||
if(!net.client()){
|
||||
unit = unitType.create(team);
|
||||
if(unit instanceof TargetDummyUnit td){
|
||||
td.building(this);
|
||||
}
|
||||
unit.set(x, y);
|
||||
unit.rotation = 90f;
|
||||
unit.add();
|
||||
Call.unitTetherBlockSpawned(tile, unit.id);
|
||||
}
|
||||
}
|
||||
|
||||
if(unit != null){
|
||||
unit.updateBoosting(boosting);
|
||||
unit.armor(unitArmor);
|
||||
unit.team(unitTeam);
|
||||
unit.hitSize = dummySize;
|
||||
|
||||
//similar to impulseNet but does not factor in mass
|
||||
Tmp.v1.set(this).sub(unit).limit(dst(unit) * pullScale * Time.delta);
|
||||
unit.vel.add(Tmp.v1);
|
||||
|
||||
//manually move units to simulate velocity for remote players
|
||||
if(unit.isRemote()) unit.move(Tmp.v1);
|
||||
|
||||
if(unit.moving()) unit.lookAt(unit.vel().angle());
|
||||
}
|
||||
|
||||
time += Time.delta;
|
||||
reset += Time.delta;
|
||||
|
||||
if(timer(dpsUpdateTime, 20)){
|
||||
dps = total / time * 60f;
|
||||
totalDisplay = total;
|
||||
timeDisplay = time / 60f;
|
||||
hitsDisplay = hits;
|
||||
}
|
||||
|
||||
if(reset >= resetTime){
|
||||
total = time = dps = hits = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void spawned(int id){
|
||||
Fx.spawn.at(x, y);
|
||||
if(net.client()){
|
||||
readUnitId = id;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
if(tether.found() && unit != null){
|
||||
float z = unit.elevation > 0.5f ? unit.type.flyingLayer - 0.01f : unitType.groundLayer + Mathf.clamp(unitType.hitSize / 4000f, 0, 0.01f);
|
||||
Draw.z(z - 0.01f);
|
||||
Draw.color(team.color);
|
||||
Drawf.laser(tether, tetherEnd, x, y, unit.x, unit.y);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
Draw.z(Layer.overlayUI);
|
||||
String text = displayDPS(true) + "\n" + "(" + displayTotal() + ")" + "\n" + displayHits();
|
||||
Drawf.text(text, x, y + size / (2.5f*2f), team.color, size / 2.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
Drawf.square(x, y, dummySize, 0f, team.color);
|
||||
}
|
||||
|
||||
public String displayDPS(boolean round){
|
||||
if(time > 0){
|
||||
return Core.bundle.format("bar.dps", (round ? (dps > 0 ? Mathf.round(dps) : emptyStr) : Strings.autoFixed(total / time * 60f, 2)));
|
||||
}else{
|
||||
return Core.bundle.format("bar.dps", emptyStr);
|
||||
}
|
||||
}
|
||||
|
||||
public String displayTotal(){
|
||||
if(time > 0){
|
||||
return Core.bundle.format("bar.damagetimetotal", Mathf.round(totalDisplay), Strings.autoFixed(timeDisplay, 2));
|
||||
}else{
|
||||
return Core.bundle.format("bar.damagetimetotal", emptyStr, emptyStr);
|
||||
}
|
||||
}
|
||||
|
||||
public String displayHits(){
|
||||
if(time > 0){
|
||||
return Core.bundle.format("bar.hits", hitsDisplay);
|
||||
}else{
|
||||
return Core.bundle.format("bar.hits", emptyStr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collide(Bullet other){ //Hit the unit, not the building
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collision(Bullet other){ //Hit the unit, not the building
|
||||
return false;
|
||||
}
|
||||
|
||||
public void dummyHit(float damage){
|
||||
reset = 0f;
|
||||
total += damage;
|
||||
hits++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float damage){
|
||||
//just in case
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
table.table(t -> {
|
||||
t.background(Styles.black6);
|
||||
t.defaults().left();
|
||||
|
||||
t.check(Core.bundle.get("rules.enemyteam"), unitTeam != team, b -> configureFloat(0, Mathf.num(b))).colspan(3).row();
|
||||
t.check(Core.bundle.get("stat.flying"), boosting, b -> configureFloat(1, Mathf.num(b))).colspan(3).row();
|
||||
t.add(Core.bundle.get("stat.armor"));
|
||||
t.field("" + unitArmor, TextFieldFilter.floatsOnly, s -> configureFloat(2, Strings.parseFloat(s))).width(200f).padLeft(8f).colspan(2).row();
|
||||
t.add(Core.bundle.get("stat.resettime"));
|
||||
t.field(Strings.autoFixed(resetTime / 60f, 2), TextFieldFilter.floatsOnly, s -> configureFloat(3, Strings.parseFloat(s) * 60f)).padLeft(8f).growX();
|
||||
t.add(StatUnit.seconds.localized()).padLeft(8).row();
|
||||
t.add(Core.bundle.get("stat.hitsize"));
|
||||
t.field("" + (dummySize/tilesize), TextFieldFilter.floatsOnly, s -> configureFloat(4, Strings.parseFloat(s) * tilesize)).padLeft(8f).growX();
|
||||
t.add(StatUnit.blocks.localized()).padLeft(8f);
|
||||
}).top().grow().margin(8f);
|
||||
}
|
||||
|
||||
//helper to send the rest of values, since config does not support arrays
|
||||
public void configureFloat(int index, float value){
|
||||
FloatSeq seq = FloatSeq.with(Mathf.num(unitTeam != team), Mathf.num(boosting), unitArmor, resetTime, dummySize);
|
||||
seq.set(index, value);
|
||||
configure(seq);
|
||||
}
|
||||
|
||||
public int dummyTeam(){
|
||||
if(unitTeam != team) return team.id; //Return to own team
|
||||
return team != state.rules.waveTeam ? state.rules.waveTeam.id : (team.id % (Team.all.length - 1)) + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return FloatSeq.with(Mathf.num(unitTeam != team), Mathf.num(boosting), unitArmor, resetTime, dummySize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.i(unit == null ? -1 : unit.id);
|
||||
write.bool(boosting);
|
||||
write.f(unitArmor);
|
||||
write.f(resetTime);
|
||||
write.f(dummySize);
|
||||
write.i(unitTeam.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
readUnitId = read.i();
|
||||
boosting = read.bool();
|
||||
unitArmor = read.f();
|
||||
resetTime = read.f();
|
||||
dummySize = read.f();
|
||||
unitTeam = Team.get(read.i());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ public class StatUnit{
|
||||
|
||||
blocks = new StatUnit("blocks"),
|
||||
blocksSquared = new StatUnit("blocksSquared"),
|
||||
worldUnits = new StatUnit("worldUnits"),
|
||||
tilesSecond = new StatUnit("tilesSecond"),
|
||||
powerSecond = new StatUnit("powerSecond", "[accent]" + Iconc.power + "[]"),
|
||||
liquidSecond = new StatUnit("liquidSecond", "[sky]" + Iconc.liquid + "[]"),
|
||||
|
||||