Merge branch 'Anuken:master' into do-you-hear-the-voices-too
This commit is contained in:
@@ -28,6 +28,7 @@ import mindustry.net.*;
|
||||
import mindustry.service.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -105,8 +106,8 @@ public class Vars implements Loadable{
|
||||
public static final float invasionGracePeriod = 20;
|
||||
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
||||
public static final float minArmorDamage = 0.1f;
|
||||
/** land/launch animation duration */
|
||||
public static final float coreLandDuration = 160f;
|
||||
/** @deprecated see {@link CoreBlock#landDuration} instead! */
|
||||
public static final @Deprecated float coreLandDuration = 160f;
|
||||
/** size of tiles in units */
|
||||
public static final int tilesize = 8;
|
||||
/** size of one tile payload (^2) */
|
||||
|
||||
@@ -12,30 +12,12 @@ import mindustry.async.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
public class UnitGroup{
|
||||
public Seq<Unit> units = new Seq<>();
|
||||
public int collisionLayer;
|
||||
public volatile float[] positions, originalPositions;
|
||||
public volatile boolean valid;
|
||||
public long lastSpeedUpdate = -1;
|
||||
public float minSpeed = 999999f;
|
||||
|
||||
public void updateMinSpeed(){
|
||||
if(lastSpeedUpdate == Vars.state.updateId) return;
|
||||
|
||||
lastSpeedUpdate = Vars.state.updateId;
|
||||
minSpeed = 999999f;
|
||||
|
||||
for(Unit unit : units){
|
||||
//don't factor in the floor speed multiplier
|
||||
Floor on = unit.isFlying() ? Blocks.air.asFloor() : unit.floorOn();
|
||||
minSpeed = Math.min(unit.speed() / on.speedMultiplier, minSpeed);
|
||||
}
|
||||
|
||||
if(Float.isInfinite(minSpeed) || Float.isNaN(minSpeed)) minSpeed = 999999f;
|
||||
}
|
||||
|
||||
public void calculateFormation(Vec2 dest, int collisionLayer){
|
||||
this.collisionLayer = collisionLayer;
|
||||
@@ -58,8 +40,6 @@ public class UnitGroup{
|
||||
unit.command().groupIndex = i;
|
||||
}
|
||||
|
||||
updateMinSpeed();
|
||||
|
||||
//run on new thread to prevent stutter
|
||||
Vars.mainExecutor.submit(() -> {
|
||||
//unused space between circles that needs to be reached for compression to end
|
||||
|
||||
@@ -148,10 +148,6 @@ public class CommandAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
if(group != null){
|
||||
group.updateMinSpeed();
|
||||
}
|
||||
|
||||
if(!net.client() && command == UnitCommand.enterPayloadCommand && unit.buildOn() != null && (targetPos == null || (world.buildWorld(targetPos.x, targetPos.y) != null && world.buildWorld(targetPos.x, targetPos.y) == unit.buildOn()))){
|
||||
var build = unit.buildOn();
|
||||
tmpPayload.unit = unit;
|
||||
@@ -363,11 +359,6 @@ public class CommandAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float prefSpeed(){
|
||||
return group == null ? super.prefSpeed() : Math.min(group.minSpeed, unit.speed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFire(){
|
||||
return stance != UnitStance.holdFire;
|
||||
|
||||
@@ -1181,6 +1181,7 @@ public class Blocks{
|
||||
rotate = true;
|
||||
invertFlip = true;
|
||||
group = BlockGroup.liquids;
|
||||
itemCapacity = 0;
|
||||
|
||||
liquidCapacity = 50f;
|
||||
|
||||
@@ -1231,6 +1232,7 @@ public class Blocks{
|
||||
}});
|
||||
|
||||
researchCostMultiplier = 1.1f;
|
||||
itemCapacity = 0;
|
||||
liquidCapacity = 40f;
|
||||
consumePower(2f);
|
||||
ambientSound = Sounds.extractLoop;
|
||||
@@ -2782,6 +2784,7 @@ public class Blocks{
|
||||
ambientSoundVolume = 0.06f;
|
||||
hasLiquids = true;
|
||||
boostScale = 1f / 9f;
|
||||
itemCapacity = 0;
|
||||
outputLiquid = new LiquidStack(Liquids.water, 30f / 60f);
|
||||
consumePower(0.5f);
|
||||
liquidCapacity = 60f;
|
||||
@@ -5791,6 +5794,8 @@ public class Blocks{
|
||||
heatOutput = 1000f;
|
||||
warmupRate = 1000f;
|
||||
regionRotated1 = 1;
|
||||
itemCapacity = 0;
|
||||
alwaysUnlocked = true;
|
||||
ambientSound = Sounds.none;
|
||||
}};
|
||||
|
||||
|
||||
@@ -1318,6 +1318,7 @@ public class UnitTypes{
|
||||
|
||||
healPercent = 5.5f;
|
||||
collidesTeam = true;
|
||||
reflectable = false;
|
||||
backColor = Pal.heal;
|
||||
trailColor = Pal.heal;
|
||||
}};
|
||||
@@ -3894,7 +3895,7 @@ public class UnitTypes{
|
||||
x = 43f * i / 4f;
|
||||
particles = parts;
|
||||
//visual only, the middle one does the actual suppressing
|
||||
display = active = false;
|
||||
active = false;
|
||||
}});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public class Weathers{
|
||||
suspendParticles;
|
||||
|
||||
public static void load(){
|
||||
snow = new ParticleWeather("snow"){{
|
||||
snow = new ParticleWeather("snowing"){{
|
||||
particleRegion = "particle";
|
||||
sizeMax = 13f;
|
||||
sizeMin = 2.6f;
|
||||
|
||||
@@ -314,6 +314,14 @@ public class ContentLoader{
|
||||
return getByName(ContentType.planet, name);
|
||||
}
|
||||
|
||||
public Seq<Weather> weathers(){
|
||||
return getBy(ContentType.weather);
|
||||
}
|
||||
|
||||
public Weather weather(String name){
|
||||
return getByName(ContentType.weather, name);
|
||||
}
|
||||
|
||||
public Seq<UnitStance> unitStances(){
|
||||
return getBy(ContentType.unitStance);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import mindustry.net.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -191,43 +192,29 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
Events.run(Trigger.newGame, () -> {
|
||||
var core = player.bestCore();
|
||||
|
||||
if(core == null) return;
|
||||
|
||||
camera.position.set(core);
|
||||
player.set(core);
|
||||
|
||||
float coreDelay = 0f;
|
||||
|
||||
if(!settings.getBool("skipcoreanimation") && !state.rules.pvp){
|
||||
coreDelay = coreLandDuration;
|
||||
coreDelay = core.landDuration();
|
||||
//delay player respawn so animation can play.
|
||||
player.deathTimer = Player.deathDelay - coreLandDuration;
|
||||
player.deathTimer = Player.deathDelay - core.landDuration();
|
||||
//TODO this sounds pretty bad due to conflict
|
||||
if(settings.getInt("musicvol") > 0){
|
||||
Musics.land.stop();
|
||||
Musics.land.play();
|
||||
Musics.land.setVolume(settings.getInt("musicvol") / 100f);
|
||||
//TODO what to do if another core with different music is already playing?
|
||||
Music music = core.landMusic();
|
||||
music.stop();
|
||||
music.play();
|
||||
music.setVolume(settings.getInt("musicvol") / 100f);
|
||||
}
|
||||
|
||||
app.post(() -> ui.hudfrag.showLand());
|
||||
renderer.showLanding();
|
||||
|
||||
Time.run(coreLandDuration, () -> {
|
||||
Fx.launch.at(core);
|
||||
Effect.shake(5f, 5f, core);
|
||||
core.thrusterTime = 1f;
|
||||
|
||||
if(state.isCampaign() && Vars.showSectorLandInfo && (state.rules.sector.preset == null || state.rules.sector.preset.showSectorLandInfo)){
|
||||
ui.announce("[accent]" + state.rules.sector.name() + "\n" +
|
||||
(state.rules.sector.info.resources.any() ? "[lightgray]" + bundle.get("sectors.resources") + "[white] " +
|
||||
state.rules.sector.info.resources.toString(" ", u -> u.emoji()) : ""), 5);
|
||||
}
|
||||
});
|
||||
renderer.showLanding(core);
|
||||
}
|
||||
|
||||
if(state.isCampaign()){
|
||||
|
||||
//don't run when hosting, that doesn't really work.
|
||||
if(state.rules.sector.planet.prebuildBase){
|
||||
toBePlaced.clear();
|
||||
|
||||
@@ -13,7 +13,6 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -30,11 +29,6 @@ public class Renderer implements ApplicationListener{
|
||||
/** These are global variables, for headless access. Cached. */
|
||||
public static float laserOpacity = 0.5f, bridgeOpacity = 0.75f;
|
||||
|
||||
private static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f;
|
||||
private static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f};
|
||||
private static final float cloudAlpha = 0.81f;
|
||||
private static final Interp landInterp = Interp.pow3;
|
||||
|
||||
public final BlockRenderer blocks = new BlockRenderer();
|
||||
public final FogRenderer fog = new FogRenderer();
|
||||
public final MinimapRenderer minimap = new MinimapRenderer();
|
||||
@@ -55,18 +49,15 @@ public class Renderer implements ApplicationListener{
|
||||
public TextureRegion[] bubbles = new TextureRegion[16], splashes = new TextureRegion[12];
|
||||
public TextureRegion[][] fluidFrames;
|
||||
|
||||
//currently landing core, null if there are no cores or it has finished landing.
|
||||
private @Nullable CoreBuild landCore;
|
||||
private @Nullable CoreBlock launchCoreType;
|
||||
private Color clearColor = new Color(0f, 0f, 0f, 1f);
|
||||
private float
|
||||
//seed for cloud visuals, 0-1
|
||||
cloudSeed = 0f,
|
||||
//target camera scale that is lerp-ed to
|
||||
targetscale = Scl.scl(4),
|
||||
//current actual camera scale
|
||||
camerascale = targetscale,
|
||||
//minimum camera zoom value for landing/launching; constant TODO make larger?
|
||||
minZoomScl = Scl.scl(0.02f),
|
||||
//starts at coreLandDuration, ends at 0. if positive, core is landing.
|
||||
landTime,
|
||||
//timer for core landing particles
|
||||
@@ -113,10 +104,6 @@ public class Renderer implements ApplicationListener{
|
||||
setupBloom();
|
||||
}
|
||||
|
||||
Events.run(Trigger.newGame, () -> {
|
||||
landCore = player.bestCore();
|
||||
});
|
||||
|
||||
EnvRenderers.init();
|
||||
for(int i = 0; i < bubbles.length; i++) bubbles[i] = atlas.find("bubble-" + i);
|
||||
for(int i = 0; i < splashes.length; i++) splashes[i] = atlas.find("splash-" + i);
|
||||
@@ -181,32 +168,26 @@ public class Renderer implements ApplicationListener{
|
||||
enableEffects = settings.getBool("effects");
|
||||
drawDisplays = !settings.getBool("hidedisplays");
|
||||
drawLight = settings.getBool("drawlight", true);
|
||||
pixelate = Core.settings.getBool("pixelate");
|
||||
pixelate = settings.getBool("pixelate");
|
||||
|
||||
//don't bother drawing landing animation if core is null
|
||||
if(landCore == null) landTime = 0f;
|
||||
if(landTime > 0){
|
||||
if(!state.isPaused()){
|
||||
CoreBuild build = landCore == null ? player.bestCore() : landCore;
|
||||
if(build != null){
|
||||
build.updateLandParticles();
|
||||
}
|
||||
}
|
||||
if(!state.isPaused()) landCore.updateLaunching();
|
||||
|
||||
if(!state.isPaused()){
|
||||
landTime -= Time.delta;
|
||||
}
|
||||
float fin = landTime / coreLandDuration;
|
||||
if(!launching) fin = 1f - fin;
|
||||
camerascale = landInterp.apply(minZoomScl, Scl.scl(4f), fin);
|
||||
weatherAlpha = 0f;
|
||||
camerascale = landCore.zoomLaunching();
|
||||
|
||||
//snap camera to cutscene core regardless of player input
|
||||
if(landCore != null){
|
||||
camera.position.set(landCore);
|
||||
}
|
||||
if(!state.isPaused()) landTime -= Time.delta;
|
||||
}else{
|
||||
weatherAlpha = Mathf.lerpDelta(weatherAlpha, 1f, 0.08f);
|
||||
}
|
||||
|
||||
if(landCore != null && landTime <= 0f){
|
||||
landCore.endLaunch();
|
||||
landCore = null;
|
||||
}
|
||||
|
||||
camera.width = graphics.getWidth() / camerascale;
|
||||
camera.height = graphics.getHeight() / camerascale;
|
||||
|
||||
@@ -304,7 +285,7 @@ public class Renderer implements ApplicationListener{
|
||||
graphics.clear(clearColor);
|
||||
Draw.reset();
|
||||
|
||||
if(Core.settings.getBool("animatedwater") || animateShields){
|
||||
if(settings.getBool("animatedwater") || animateShields){
|
||||
effectBuffer.resize(graphics.getWidth(), graphics.getHeight());
|
||||
}
|
||||
|
||||
@@ -393,7 +374,10 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
Draw.draw(Layer.overlayUI, overlays::drawTop);
|
||||
if(state.rules.fog) Draw.draw(Layer.fogOfWar, fog::drawFog);
|
||||
Draw.draw(Layer.space, this::drawLanding);
|
||||
Draw.draw(Layer.space, () -> {
|
||||
if(landCore == null || landTime <= 0f) return;
|
||||
landCore.drawLanding(launching && launchCoreType != null ? launchCoreType : (CoreBlock)landCore.block);
|
||||
});
|
||||
|
||||
Events.fire(Trigger.drawOver);
|
||||
blocks.drawBlocks();
|
||||
@@ -481,61 +465,6 @@ public class Renderer implements ApplicationListener{
|
||||
if(state.rules.customBackgroundCallback != null && customBackgrounds.containsKey(state.rules.customBackgroundCallback)){
|
||||
customBackgrounds.get(state.rules.customBackgroundCallback).run();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void drawLanding(){
|
||||
CoreBuild build = landCore == null ? player.bestCore() : landCore;
|
||||
var clouds = assets.get("sprites/clouds.png", Texture.class);
|
||||
if(landTime > 0 && build != null){
|
||||
float fout = landTime / coreLandDuration;
|
||||
if(launching) fout = 1f - fout;
|
||||
float fin = 1f - fout;
|
||||
float scl = Scl.scl(4f) / camerascale;
|
||||
float pfin = Interp.pow3Out.apply(fin), pf = Interp.pow2In.apply(fout);
|
||||
|
||||
//draw particles
|
||||
Draw.color(Pal.lightTrail);
|
||||
Angles.randLenVectors(1, pfin, 100, 800f * scl * pfin, (ax, ay, ffin, ffout) -> {
|
||||
Lines.stroke(scl * ffin * pf * 3f);
|
||||
Lines.lineAngle(build.x + ax, build.y + ay, Mathf.angle(ax, ay), (ffin * 20 + 1f) * scl);
|
||||
});
|
||||
Draw.color();
|
||||
|
||||
CoreBlock block = launching && launchCoreType != null ? launchCoreType : (CoreBlock)build.block;
|
||||
block.drawLanding(build, build.x, build.y);
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.white, Interp.pow5In.apply(fout));
|
||||
|
||||
//accent tint indicating that the core was just constructed
|
||||
if(launching){
|
||||
float f = Mathf.clamp(1f - fout * 12f);
|
||||
if(f > 0.001f){
|
||||
Draw.mixcol(Pal.accent, f);
|
||||
}
|
||||
}
|
||||
|
||||
//draw clouds
|
||||
if(state.rules.cloudColor.a > 0.0001f){
|
||||
float scaling = cloudScaling;
|
||||
float sscl = Math.max(1f + Mathf.clamp(fin + cfinOffset)* cfinScl, 0f) * camerascale;
|
||||
|
||||
Tmp.tr1.set(clouds);
|
||||
Tmp.tr1.set(
|
||||
(camera.position.x - camera.width/2f * sscl) / scaling,
|
||||
(camera.position.y - camera.height/2f * sscl) / scaling,
|
||||
(camera.position.x + camera.width/2f * sscl) / scaling,
|
||||
(camera.position.y + camera.height/2f * sscl) / scaling);
|
||||
|
||||
Tmp.tr1.scroll(10f * cloudSeed, 10f * cloudSeed);
|
||||
|
||||
Draw.alpha(Mathf.sample(cloudAlphas, fin + calphaFinOffset) * cloudAlpha);
|
||||
Draw.mixcol(state.rules.cloudColor, state.rules.cloudColor.a);
|
||||
Draw.rect(Tmp.tr1, camera.position.x, camera.position.y, camera.width, camera.height);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void scaleCamera(float amount){
|
||||
@@ -580,6 +509,13 @@ public class Renderer implements ApplicationListener{
|
||||
return landTime;
|
||||
}
|
||||
|
||||
public float getLandTimeIn(){
|
||||
if(landCore == null) return 0f;
|
||||
float fin = landTime / landCore.landDuration();
|
||||
if(!launching) fin = 1f - fin;
|
||||
return fin;
|
||||
}
|
||||
|
||||
public float getLandPTimer(){
|
||||
return landPTimer;
|
||||
}
|
||||
@@ -588,25 +524,37 @@ public class Renderer implements ApplicationListener{
|
||||
this.landPTimer = landPTimer;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void showLanding(){
|
||||
launching = false;
|
||||
camerascale = minZoomScl;
|
||||
landTime = coreLandDuration;
|
||||
cloudSeed = Mathf.random(1f);
|
||||
var core = player.bestCore();
|
||||
if(core != null) showLanding(core);
|
||||
}
|
||||
|
||||
public void showLanding(CoreBuild landCore){
|
||||
this.landCore = landCore;
|
||||
launching = false;
|
||||
landTime = landCore.landDuration();
|
||||
|
||||
landCore.beginLaunch(null);
|
||||
camerascale = landCore.zoomLaunching();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void showLaunch(CoreBlock coreType){
|
||||
Vars.ui.hudfrag.showLaunch();
|
||||
Vars.control.input.config.hideConfig();
|
||||
Vars.control.input.inv.hide();
|
||||
launchCoreType = coreType;
|
||||
var core = player.team().core();
|
||||
if(core != null) showLaunch(core, coreType);
|
||||
}
|
||||
|
||||
public void showLaunch(CoreBuild landCore, CoreBlock coreType){
|
||||
control.input.config.hideConfig();
|
||||
control.input.inv.hide();
|
||||
|
||||
this.landCore = landCore;
|
||||
launching = true;
|
||||
landCore = player.team().core();
|
||||
cloudSeed = Mathf.random(1f);
|
||||
landTime = coreLandDuration;
|
||||
if(landCore != null){
|
||||
Fx.coreLaunchConstruct.at(landCore.x, landCore.y, coreType.size);
|
||||
}
|
||||
landTime = landCore.landDuration();
|
||||
launchCoreType = coreType;
|
||||
|
||||
landCore.beginLaunch(coreType);
|
||||
}
|
||||
|
||||
public void takeMapScreenshot(){
|
||||
@@ -648,7 +596,7 @@ public class Renderer implements ApplicationListener{
|
||||
Fi file = screenshotDirectory.child("screenshot-" + Time.millis() + ".png");
|
||||
PixmapIO.writePng(file, fullPixmap);
|
||||
fullPixmap.dispose();
|
||||
app.post(() -> ui.showInfoFade(Core.bundle.format("screenshot", file.toString())));
|
||||
app.post(() -> ui.showInfoFade(bundle.format("screenshot", file.toString())));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
public TextureRegion uiIcon;
|
||||
/** Icon of the full content. Unscaled.*/
|
||||
public TextureRegion fullIcon;
|
||||
/** Override for the full icon. Useful for mod content with duplicate icons. Overrides any other full icon.*/
|
||||
public String fullOverride = "";
|
||||
/** The tech tree node for this content, if applicable. Null if not part of a tech tree. */
|
||||
public @Nullable TechNode techNode;
|
||||
/** Tech nodes for all trees that this content is part of. */
|
||||
@@ -62,11 +64,12 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
@Override
|
||||
public void loadIcon(){
|
||||
fullIcon =
|
||||
Core.atlas.find(fullOverride,
|
||||
Core.atlas.find(getContentType().name() + "-" + name + "-full",
|
||||
Core.atlas.find(name + "-full",
|
||||
Core.atlas.find(name,
|
||||
Core.atlas.find(getContentType().name() + "-" + name,
|
||||
Core.atlas.find(name + "1")))));
|
||||
Core.atlas.find(name + "1"))))));
|
||||
|
||||
uiIcon = Core.atlas.find(getContentType().name() + "-" + name + "-ui", fullIcon);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class MapEditor{
|
||||
for(int i = 0; i < tiles.width * tiles.height; i++){
|
||||
Tile tile = tiles.geti(i);
|
||||
var build = tile.build;
|
||||
if(build != null){
|
||||
if(build != null && tile.isCenter()){
|
||||
builds.add(build);
|
||||
}
|
||||
tiles.seti(i, new EditorTile(tile.x, tile.y, tile.floorID(), tile.overlayID(), build == null ? tile.blockID() : 0));
|
||||
|
||||
@@ -5,7 +5,6 @@ import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.Button.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.scene.utils.*;
|
||||
|
||||
@@ -293,7 +293,6 @@ public class Damage{
|
||||
collided.each(c -> {
|
||||
if(hitter.damage > 0 && (pierceCap <= 0 || collideCount[0] < pierceCap)){
|
||||
if(c.target instanceof Unit u){
|
||||
effect.at(c.x, c.y);
|
||||
u.collision(hitter, c.x, c.y);
|
||||
hitter.collision(u, c.x, c.y);
|
||||
collideCount[0]++;
|
||||
@@ -344,7 +343,6 @@ public class Damage{
|
||||
|
||||
Units.nearbyEnemies(team, rect.setCentered(x, y, 1f), u -> {
|
||||
if(u.checkTarget(hitter.type.collidesAir, hitter.type.collidesGround) && u.hittable()){
|
||||
effect.at(x, y);
|
||||
u.collision(hitter, x, y);
|
||||
hitter.collision(u, x, y);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class Puddles{
|
||||
Puddle puddle = Puddle.create();
|
||||
puddle.tile = tile;
|
||||
puddle.liquid = liquid;
|
||||
puddle.amount = amount;
|
||||
puddle.amount = Math.min(amount, maxLiquid);
|
||||
puddle.set(ax, ay);
|
||||
register(puddle);
|
||||
puddle.add();
|
||||
|
||||
@@ -6,6 +6,7 @@ import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public abstract class Ability implements Cloneable{
|
||||
protected static final float descriptionWidth = 350f;
|
||||
/** If false, this ability does not show in unit stats. */
|
||||
public boolean display = true;
|
||||
//the one and only data variable that is synced.
|
||||
@@ -16,7 +17,16 @@ public abstract class Ability implements Cloneable{
|
||||
public void death(Unit unit){}
|
||||
public void init(UnitType type){}
|
||||
public void displayBars(Unit unit, Table bars){}
|
||||
public void addStats(Table t){}
|
||||
public void addStats(Table t){
|
||||
if(Core.bundle.has(getBundle() + ".description")){
|
||||
t.add(Core.bundle.get(getBundle() + ".description")).wrap().width(descriptionWidth);
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
|
||||
public String abilityStat(String stat, Object... values){
|
||||
return Core.bundle.format("ability.stat." + stat, values);
|
||||
}
|
||||
|
||||
public Ability copy(){
|
||||
try{
|
||||
@@ -29,7 +39,11 @@ public abstract class Ability implements Cloneable{
|
||||
|
||||
/** @return localized ability name; mods should override this. */
|
||||
public String localized(){
|
||||
return Core.bundle.get(getBundle());
|
||||
}
|
||||
|
||||
public String getBundle(){
|
||||
var type = getClass();
|
||||
return Core.bundle.get("ability." + (type.isAnonymousClass() ? type.getSuperclass() : type).getSimpleName().replace("Ability", "").toLowerCase());
|
||||
return "ability." + (type.isAnonymousClass() ? type.getSuperclass() : type).getSimpleName().replace("Ability", "").toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class ArmorPlateAbility extends Ability{
|
||||
public TextureRegion plateRegion;
|
||||
@@ -39,7 +38,8 @@ public class ArmorPlateAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Stat.healthMultiplier.localized() + ": [white]" + Math.round(healthMultiplier * 100f) + 100 + "%");
|
||||
super.addStats(t);
|
||||
t.add(abilityStat("damagereduction", Strings.autoFixed(-healthMultiplier * 100f, 1)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,7 +14,6 @@ import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -53,23 +52,29 @@ public class EnergyFieldAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add(Core.bundle.format("bullet.damage", damage));
|
||||
if(displayHeal){
|
||||
t.add(Core.bundle.get(getBundle() + ".healdescription")).wrap().width(descriptionWidth);
|
||||
}else{
|
||||
t.add(Core.bundle.get(getBundle() + ".description")).wrap().width(descriptionWidth);
|
||||
}
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.reload.localized() + ": [white]" + Strings.autoFixed(60f / reload, 2) + " " + StatUnit.perSecond.localized());
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized());
|
||||
t.row();
|
||||
t.add(Core.bundle.format("ability.energyfield.maxtargets", maxTargets));
|
||||
|
||||
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2)));
|
||||
t.row();
|
||||
t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2)));
|
||||
t.row();
|
||||
t.add(abilityStat("maxtargets", maxTargets));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("bullet.damage", damage));
|
||||
if(status != StatusEffects.none){
|
||||
t.row();
|
||||
t.add((status.hasEmoji() ? status.emoji() : "") + "[stat]" + status.localizedName);
|
||||
}
|
||||
if(displayHeal){
|
||||
t.row();
|
||||
t.add(Core.bundle.format("bullet.healpercent", Strings.autoFixed(healPercent, 2)));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("ability.energyfield.sametypehealmultiplier", Math.round(sameTypeHealMult * 100f)));
|
||||
}
|
||||
if(status != StatusEffects.none){
|
||||
t.row();
|
||||
t.add(status.emoji() + " " + status.localizedName);
|
||||
t.add(abilityStat("sametypehealmultiplier", (sameTypeHealMult < 1f ? "[negstat]" : "") + Strings.autoFixed(sameTypeHealMult * 100f, 2)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
@@ -12,7 +13,6 @@ import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -73,14 +73,14 @@ public class ForceFieldAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Stat.health.localized() + ": [white]" + Math.round(max));
|
||||
super.addStats(t);
|
||||
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(radius / tilesize, 2)));
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(radius / tilesize, 2) + " " + StatUnit.blocks.localized());
|
||||
t.add(abilityStat("shield", Strings.autoFixed(max, 2)));
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(regen * 60f, 2) + StatUnit.perSecond.localized());
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.cooldownTime.localized() + ": [white]" + Strings.autoFixed(cooldown / 60f, 2) + " " + StatUnit.seconds.localized());
|
||||
t.add(abilityStat("repairspeed", Strings.autoFixed(regen * 60f, 2)));
|
||||
t.row();
|
||||
t.add(abilityStat("cooldown", Strings.autoFixed(cooldown / 60f, 2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -16,6 +17,12 @@ public class LiquidExplodeAbility extends Ability{
|
||||
public float radAmountScale = 5f, radScale = 1f;
|
||||
public float noiseMag = 6.5f, noiseScl = 5f;
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
super.addStats(t);
|
||||
t.add((liquid.hasEmoji() ? liquid.emoji() : "") + "[stat]" + liquid.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void death(Unit unit){
|
||||
//TODO what if noise is radial, so it looks like a splat?
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -17,6 +18,14 @@ public class LiquidRegenAbility extends Ability{
|
||||
public float slurpEffectChance = 0.4f;
|
||||
public Effect slurpEffect = Fx.heal;
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
super.addStats(t);
|
||||
t.add((liquid.hasEmoji() ? liquid.emoji() : "") + "[stat]" + liquid.localizedName);
|
||||
t.row();
|
||||
t.add(abilityStat("slurpheal", Strings.autoFixed(regenPerSlurp, 2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Unit unit){
|
||||
//TODO timer?
|
||||
|
||||
@@ -5,12 +5,15 @@ import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MoveLightningAbility extends Ability{
|
||||
/** Lightning damage */
|
||||
public float damage = 35f;
|
||||
@@ -63,7 +66,15 @@ public class MoveLightningAbility extends Ability{
|
||||
this.maxSpeed = maxSpeed;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
super.addStats(t);
|
||||
t.add(abilityStat("minspeed", Strings.autoFixed(minSpeed * 60f / tilesize, 2)));
|
||||
t.row();
|
||||
t.add(Core.bundle.format("bullet.damage", damage));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Unit unit){
|
||||
float scl = Mathf.clamp((unit.vel().len() - minSpeed) / (maxSpeed - minSpeed));
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.Core;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class RegenAbility extends Ability{
|
||||
/** Amount healed as percent per tick. */
|
||||
@@ -14,13 +12,16 @@ public class RegenAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
if(amount > 0.01f){
|
||||
t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(amount * 60f, 2) + StatUnit.perSecond.localized());
|
||||
t.row();
|
||||
}
|
||||
super.addStats(t);
|
||||
|
||||
if(percentAmount > 0.01f){
|
||||
t.add(Core.bundle.format("bullet.healpercent", Strings.autoFixed(percentAmount * 60f, 2)) + StatUnit.perSecond.localized()); //stupid but works
|
||||
boolean flat = amount >= 0.001f;
|
||||
boolean percent = percentAmount >= 0.001f;
|
||||
|
||||
if(flat || percent){
|
||||
t.add(abilityStat("regen",
|
||||
(flat ? Strings.autoFixed(amount * 60f, 2) + (percent ? " [lightgray]+[stat] " : "") : "")
|
||||
+ (percent ? Strings.autoFixed(percentAmount * 60f, 2) + "%" : "")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.tilesize;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class RepairFieldAbility extends Ability{
|
||||
public float amount = 1, reload = 100, range = 60;
|
||||
@@ -28,9 +28,10 @@ public class RepairFieldAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(amount * 60f / reload, 2) + StatUnit.perSecond.localized());
|
||||
super.addStats(t);
|
||||
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2)));
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized());
|
||||
t.add(abilityStat("repairspeed", Strings.autoFixed(amount * 60f / reload, 2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,6 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class ShieldArcAbility extends Ability{
|
||||
private static Unit paramUnit;
|
||||
@@ -69,12 +68,12 @@ public class ShieldArcAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Stat.health.localized() + ": [white]" + Math.round(max));
|
||||
super.addStats(t);
|
||||
t.add(abilityStat("shield", Strings.autoFixed(max, 2)));
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.repairSpeed.localized() + ": [white]" + Strings.autoFixed(regen * 60f, 2) + StatUnit.perSecond.localized());
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.cooldownTime.localized() + ": [white]" + Strings.autoFixed(cooldown / 60f, 2) + " " + StatUnit.seconds.localized());
|
||||
t.add(abilityStat("repairspeed", Strings.autoFixed(regen * 60f, 2)));
|
||||
t.row();
|
||||
t.add(abilityStat("cooldown", Strings.autoFixed(cooldown / 60f, 2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.Core;
|
||||
import arc.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.tilesize;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ShieldRegenFieldAbility extends Ability{
|
||||
public float amount = 1, max = 100f, reload = 100, range = 60;
|
||||
@@ -30,12 +29,12 @@ public class ShieldRegenFieldAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Core.bundle.get("waves.shields") + ": [white]" + Math.round(max)); //extremely stupid usage
|
||||
super.addStats(t);
|
||||
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2)));
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized());
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.reload.localized() + ": [white]" + Strings.autoFixed(60f / reload, 2) + " " + StatUnit.perSecond.localized());
|
||||
t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2)));
|
||||
t.row();
|
||||
t.add(abilityStat("shield", Strings.autoFixed(max, 2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,7 +27,8 @@ public class SpawnDeathAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add((randAmount > 0 ? amount + "-" + (amount + randAmount) : amount) + " " + unit.emoji() + " " + unit.localizedName);
|
||||
super.addStats(t);
|
||||
t.add("[stat]" + (randAmount > 0 ? amount + "x-" + (amount + randAmount) : amount) + "x[] " + (unit.hasEmoji() ? unit.emoji() : "") + "[stat]" + unit.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.tilesize;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class StatusFieldAbility extends Ability{
|
||||
public StatusEffect effect;
|
||||
@@ -33,11 +33,12 @@ public class StatusFieldAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Stat.reload.localized() + ": [white]" + Strings.autoFixed(60f / reload, 2) + " " + StatUnit.perSecond.localized());
|
||||
super.addStats(t);
|
||||
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2)));
|
||||
t.row();
|
||||
t.add("[lightgray]" + Stat.shootRange.localized() + ": [white]" + Strings.autoFixed(range / tilesize, 2) + " " + StatUnit.blocks.localized());
|
||||
t.add(abilityStat("firingrate", Strings.autoFixed(60f / reload, 2)));
|
||||
t.row();
|
||||
t.add(effect.emoji() + " " + effect.localizedName);
|
||||
t.add((effect.hasEmoji() ? effect.emoji() : "") + "[stat]" + effect.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SuppressionFieldAbility extends Ability{
|
||||
protected static Rand rand = new Rand();
|
||||
@@ -33,6 +38,19 @@ public class SuppressionFieldAbility extends Ability{
|
||||
|
||||
protected float timer;
|
||||
|
||||
@Override
|
||||
public void init(UnitType type){
|
||||
if(!active) display = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
super.addStats(t);
|
||||
t.add(Core.bundle.format("bullet.range", Strings.autoFixed(range / tilesize, 2)));
|
||||
t.row();
|
||||
t.add(abilityStat("duration", Strings.autoFixed(reload / 60f, 2)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Unit unit){
|
||||
if(!active) return;
|
||||
|
||||
@@ -12,7 +12,6 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -36,9 +35,10 @@ public class UnitSpawnAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void addStats(Table t){
|
||||
t.add("[lightgray]" + Stat.buildTime.localized() + ": [white]" + Strings.autoFixed(spawnTime / 60f, 2) + " " + StatUnit.seconds.localized());
|
||||
super.addStats(t);
|
||||
t.add(abilityStat("buildtime", Strings.autoFixed(spawnTime / 60f, 2)));
|
||||
t.row();
|
||||
t.add(unit.emoji() + " " + unit.localizedName);
|
||||
t.add((unit.hasEmoji() ? unit.emoji() : "") + "[stat]" + unit.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -176,6 +176,8 @@ public class BulletType extends Content implements Cloneable{
|
||||
public float fragLifeMin = 1f, fragLifeMax = 1f;
|
||||
/** Random offset of frag bullets from the parent bullet. */
|
||||
public float fragOffsetMin = 1f, fragOffsetMax = 7f;
|
||||
/** How many times this bullet can release frag bullets, if pierce = true. */
|
||||
public int pierceFragCap = -1;
|
||||
|
||||
/** Bullet that is created at a fixed interval. */
|
||||
public @Nullable BulletType intervalBullet;
|
||||
@@ -509,12 +511,13 @@ public class BulletType extends Content implements Cloneable{
|
||||
}
|
||||
|
||||
public void createFrags(Bullet b, float x, float y){
|
||||
if(fragBullet != null && (fragOnAbsorb || !b.absorbed)){
|
||||
if(fragBullet != null && (fragOnAbsorb || !b.absorbed) && !(b.frags >= pierceFragCap && pierceFragCap > 0)){
|
||||
for(int i = 0; i < fragBullets; i++){
|
||||
float len = Mathf.random(fragOffsetMin, fragOffsetMax);
|
||||
float a = b.rotation() + Mathf.range(fragRandomSpread / 2) + fragAngle + ((i - fragBullets/2) * fragSpread);
|
||||
fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax), Mathf.random(fragLifeMin, fragLifeMax));
|
||||
}
|
||||
b.frags++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public class ContinuousFlameBulletType extends ContinuousBulletType{
|
||||
lifetime = 16f;
|
||||
hitColor = colors[1].cpy().a(1f);
|
||||
lightColor = hitColor;
|
||||
lightOpacity = 0.7f;
|
||||
laserAbsorb = false;
|
||||
ammoMultiplier = 1f;
|
||||
pierceArmor = true;
|
||||
@@ -87,7 +88,7 @@ public class ContinuousFlameBulletType extends ContinuousBulletType{
|
||||
}
|
||||
|
||||
Tmp.v1.trns(b.rotation(), realLength * 1.1f);
|
||||
Drawf.light(b.x, b.y, b.x + Tmp.v1.x, b.y + Tmp.v1.y, lightStroke, lightColor, 0.7f);
|
||||
Drawf.light(b.x, b.y, b.x + Tmp.v1.x, b.y + Tmp.v1.y, lightStroke, lightColor, lightOpacity);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
transient @Nullable Mover mover;
|
||||
transient boolean absorbed, hit;
|
||||
transient @Nullable Trail trail;
|
||||
transient int frags;
|
||||
|
||||
@Override
|
||||
public void getCollisions(Cons<QuadTree> consumer){
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
@Component
|
||||
abstract class ChildComp implements Posc, Rotc{
|
||||
@@ -18,9 +19,14 @@ abstract class ChildComp implements Posc, Rotc{
|
||||
if(parent != null){
|
||||
offsetX = x - parent.getX();
|
||||
offsetY = y - parent.getY();
|
||||
if(rotWithParent && parent instanceof Rotc r){
|
||||
offsetPos = -r.rotation();
|
||||
offsetRot = rotation - r.rotation();
|
||||
if(rotWithParent){
|
||||
if(parent instanceof Rotc r){
|
||||
offsetPos = -r.rotation();
|
||||
offsetRot = rotation - r.rotation();
|
||||
}else if(parent instanceof RotBlock rot){
|
||||
offsetPos = -rot.buildRotation();
|
||||
offsetRot = rotation - rot.buildRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,10 +34,16 @@ abstract class ChildComp implements Posc, Rotc{
|
||||
@Override
|
||||
public void update(){
|
||||
if(parent != null){
|
||||
if(rotWithParent && parent instanceof Rotc r){
|
||||
x = parent.getX() + Angles.trnsx(r.rotation() + offsetPos, offsetX, offsetY);
|
||||
y = parent.getY() + Angles.trnsy(r.rotation() + offsetPos, offsetX, offsetY);
|
||||
rotation = r.rotation() + offsetRot;
|
||||
if(rotWithParent){
|
||||
if(parent instanceof Rotc r){
|
||||
x = parent.getX() + Angles.trnsx(r.rotation() + offsetPos, offsetX, offsetY);
|
||||
y = parent.getY() + Angles.trnsy(r.rotation() + offsetPos, offsetX, offsetY);
|
||||
rotation = r.rotation() + offsetRot;
|
||||
}else if(parent instanceof RotBlock rot){
|
||||
x = parent.getX() + Angles.trnsx(rot.buildRotation() + offsetPos, offsetX, offsetY);
|
||||
y = parent.getY() + Angles.trnsy(rot.buildRotation() + offsetPos, offsetX, offsetY);
|
||||
rotation = rot.buildRotation() + offsetRot;
|
||||
}
|
||||
}else{
|
||||
x = parent.getX() + offsetX;
|
||||
y = parent.getY() + offsetY;
|
||||
|
||||
@@ -60,6 +60,11 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(){
|
||||
if(Vars.state.rules.unitPayloadsExplode) payloads.each(Payload::destroyed);
|
||||
}
|
||||
|
||||
float payloadUsed(){
|
||||
return payloads.sumf(p -> p.size() * p.size());
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc, Syncc{
|
||||
|
||||
amount -= Time.delta * (1f - liquid.viscosity) / (5f + addSpeed);
|
||||
amount += accepting;
|
||||
amount = Math.min(amount, maxLiquid);
|
||||
accepting = 0f;
|
||||
|
||||
if(amount >= maxLiquid / 1.5f){
|
||||
|
||||
@@ -39,6 +39,7 @@ public class EventType{
|
||||
socketConfigChanged,
|
||||
update,
|
||||
unitCommandChange,
|
||||
unitCommandPosition,
|
||||
unitCommandAttack,
|
||||
importMod,
|
||||
draw,
|
||||
|
||||
@@ -851,7 +851,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
/** Displays a shape with an outline and color. */
|
||||
public static class ShapeMarker extends PosMarker{
|
||||
public float radius = 8f, rotation = 0f, stroke = 1f;
|
||||
public float radius = 8f, rotation = 0f, stroke = 1f, startAngle = 0f, endAngle = 360f;
|
||||
public boolean fill = false, outline = true;
|
||||
public int sides = 4;
|
||||
public Color color = Color.valueOf("ffd37f");
|
||||
@@ -877,14 +877,18 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(!fill){
|
||||
if(outline){
|
||||
Lines.stroke((stroke + 2f) * scaleFactor, Pal.gray);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation + startAngle, rotation + endAngle);
|
||||
}
|
||||
|
||||
Lines.stroke(stroke * scaleFactor, color);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation + startAngle, rotation + endAngle);
|
||||
}else{
|
||||
Draw.color(color);
|
||||
Fill.poly(pos.x, pos.y, sides, radius * scaleFactor, rotation);
|
||||
if (startAngle < endAngle){
|
||||
Fill.arc(pos.x, pos.y, radius * scaleFactor, (endAngle - startAngle) / 360f, rotation + startAngle, sides);
|
||||
}else{
|
||||
Fill.arc(pos.x, pos.y, radius * scaleFactor, (startAngle - endAngle) / 360f, rotation + endAngle, sides);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
@@ -901,12 +905,14 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
case rotation -> rotation = (float)p1;
|
||||
case color -> color.fromDouble(p1);
|
||||
case shape -> sides = (int)p1;
|
||||
case arc -> startAngle = (float)p1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case shape -> fill = !Mathf.equal((float)p2, 0f);
|
||||
case arc -> endAngle = (float)p2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,8 @@ public class Rules{
|
||||
public boolean unitAmmo = false;
|
||||
/** EXPERIMENTAL! If true, blocks will update in units and share power. */
|
||||
public boolean unitPayloadUpdate = false;
|
||||
/** If true, units' payloads are destroy()ed when the unit is destroyed. */
|
||||
public boolean unitPayloadsExplode = false;
|
||||
/** Whether cores add to unit limit */
|
||||
public boolean unitCapVariable = true;
|
||||
/** If true, unit spawn points are shown. */
|
||||
|
||||
@@ -176,10 +176,7 @@ public class MinimapRenderer{
|
||||
if(fullView && net.active()){
|
||||
for(Player player : Groups.player){
|
||||
if(!player.dead()){
|
||||
float rx = player.x / (world.width() * tilesize) * w;
|
||||
float ry = player.y / (world.height() * tilesize) * h;
|
||||
|
||||
drawLabel(x + rx, y + ry, player.name, player.color);
|
||||
drawLabel(player.x, player.y, player.name, player.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,12 @@ public class Trail{
|
||||
x2 = items[i + 3];
|
||||
y2 = items[i + 4];
|
||||
w2 = items[i + 5];
|
||||
|
||||
if(i == 0 && points.size >= (length - 1) * 3){
|
||||
x1 = Mathf.lerp(x1, x2, counter);
|
||||
y1 = Mathf.lerp(y1, y2, counter);
|
||||
w1 = Mathf.lerp(w1, w2, counter);
|
||||
}
|
||||
}else{
|
||||
x2 = lastX;
|
||||
y2 = lastY;
|
||||
@@ -97,12 +103,11 @@ public class Trail{
|
||||
|
||||
/** Removes the last point from the trail at intervals. */
|
||||
public void shorten(){
|
||||
if((counter += Time.delta) >= 1f){
|
||||
if(points.size >= 3){
|
||||
points.removeRange(0, 2);
|
||||
}
|
||||
int count = (int)(counter += Time.delta);
|
||||
counter -= count;
|
||||
|
||||
counter %= 1f;
|
||||
if(points.size + ((count - 1) * 3) > length * 3 && points.size > 0){
|
||||
points.removeRange(0, Math.min(3 * count - 1, points.size - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,18 +118,27 @@ public class Trail{
|
||||
|
||||
/** Adds a new point to the trail at intervals. */
|
||||
public void update(float x, float y, float width){
|
||||
//TODO fix longer trails at low FPS
|
||||
if((counter += Time.delta) >= 1f){
|
||||
if(points.size > length*3){
|
||||
points.removeRange(0, 2);
|
||||
int count = (int)(counter += Time.delta);
|
||||
counter -= count;
|
||||
|
||||
if(count > 0){
|
||||
int toRemove = points.size + (count - 1 - length) * 3;
|
||||
if(toRemove > 0 && points.size > 0){
|
||||
points.removeRange(0, Math.min(toRemove - 1, points.size - 1));
|
||||
}
|
||||
|
||||
points.add(x, y, width);
|
||||
|
||||
counter %= 1f;
|
||||
//if lastX is -1, this trail has never updated, so only add one point - there is nothing to interpolate with
|
||||
if(count == 1 || lastX == -1f){
|
||||
points.add(x, y, width);
|
||||
}else{
|
||||
for(int i = 0; i < count; i++){
|
||||
float f = (i + 1f) / count;
|
||||
points.add(Mathf.lerp(lastX, x, f), Mathf.lerp(lastY, y, f), Mathf.lerp(lastW, width, f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update last position regardless, so it joins
|
||||
//update last position regardless, so it joins at the origin
|
||||
lastAngle = -Angles.angleRad(x, y, lastX, lastY);
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
|
||||
@@ -451,7 +451,7 @@ public class DesktopInput extends InputHandler{
|
||||
cursorType = cursor.build.getCursor();
|
||||
}
|
||||
|
||||
if(cursor.build != null && cursor.build.team == Team.derelict && Build.validPlace(cursor.block(), player.team(), cursor.build.tileX(), cursor.build.tileY(), cursor.build.rotation)){
|
||||
if(cursor.build != null && player.team() != Team.derelict && cursor.build.team == Team.derelict && Build.validPlace(cursor.block(), player.team(), cursor.build.tileX(), cursor.build.tileY(), cursor.build.rotation)){
|
||||
cursorType = ui.repairCursor;
|
||||
}
|
||||
|
||||
@@ -568,7 +568,7 @@ public class DesktopInput extends InputHandler{
|
||||
schematicY += shiftY;
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.deselect) && !isPlacing() && player.unit().plans.isEmpty() && !commandMode){
|
||||
if(Core.input.keyTap(Binding.deselect) && !ui.minimapfrag.shown() && !isPlacing() && player.unit().plans.isEmpty() && !commandMode){
|
||||
player.unit().mineTile = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -311,18 +311,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
}
|
||||
|
||||
float minSpeed = 100000000f;
|
||||
for(int i = 0; i < groups.length; i ++){
|
||||
var group = groups[i];
|
||||
if(group != null && group.units.size > 0){
|
||||
group.calculateFormation(targetAsVec, i);
|
||||
minSpeed = Math.min(group.minSpeed, minSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
for(var group : groups){
|
||||
if(group != null){
|
||||
group.minSpeed = minSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1012,6 +1004,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
if(attack != null){
|
||||
Events.fire(Trigger.unitCommandAttack);
|
||||
}else{
|
||||
Events.fire(Trigger.unitCommandPosition);
|
||||
}
|
||||
|
||||
int maxChunkSize = 200;
|
||||
@@ -1661,7 +1655,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
boolean tryRepairDerelict(Tile selected){
|
||||
if(selected != null && selected.build != null && selected.build.block.unlockedNow() && selected.build.team == Team.derelict && Build.validPlace(selected.block(), player.team(), selected.build.tileX(), selected.build.tileY(), selected.build.rotation)){
|
||||
if(selected != null && player.team() != Team.derelict && selected.build != null && selected.build.block.unlockedNow() && selected.build.team == Team.derelict && Build.validPlace(selected.block(), player.team(), selected.build.tileX(), selected.build.tileY(), selected.build.rotation)){
|
||||
player.unit().addBuild(new BuildPlan(selected.build.tileX(), selected.build.tileY(), selected.build.rotation, selected.block(), selected.build.config()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,10 @@ public class GlobalVars{
|
||||
put("@" + type.name, type);
|
||||
}
|
||||
|
||||
for(Weather weather : Vars.content.weathers()){
|
||||
put("@" + weather.name, weather);
|
||||
}
|
||||
|
||||
//store sensor constants
|
||||
for(LAccess sensor : LAccess.all){
|
||||
put("@" + sensor.name(), sensor);
|
||||
|
||||
@@ -13,8 +13,8 @@ import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.MapObjectives.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -1363,13 +1363,21 @@ public class LExecutor{
|
||||
TeamData data = t.data();
|
||||
|
||||
switch(type){
|
||||
case unit -> exec.setobj(result, i < 0 || i >= data.units.size ? null : data.units.get(i));
|
||||
case unit -> {
|
||||
UnitType type = exec.obj(extra) instanceof UnitType u ? u : null;
|
||||
if(type == null){
|
||||
exec.setobj(result, i < 0 || i >= data.units.size ? null : data.units.get(i));
|
||||
}else{
|
||||
var units = data.unitCache(type);
|
||||
exec.setobj(result, units == null || i < 0 || i >= units.size ? null : units.get(i));
|
||||
}
|
||||
}
|
||||
case player -> exec.setobj(result, i < 0 || i >= data.players.size || data.players.get(i).unit().isNull() ? null : data.players.get(i).unit());
|
||||
case core -> exec.setobj(result, i < 0 || i >= data.cores.size ? null : data.cores.get(i));
|
||||
case build -> {
|
||||
Block block = exec.obj(extra) instanceof Block b ? b : null;
|
||||
if(block == null){
|
||||
exec.setobj(result, null);
|
||||
exec.setobj(result, i < 0 || i >= data.buildings.size ? null : data.buildings.get(i));
|
||||
}else{
|
||||
var builds = data.getBuildings(block);
|
||||
exec.setobj(result, i < 0 || i >= builds.size ? null : builds.get(i));
|
||||
@@ -1380,7 +1388,7 @@ public class LExecutor{
|
||||
if(type == null){
|
||||
exec.setnum(result, data.units.size);
|
||||
}else{
|
||||
exec.setnum(result, data.unitsByType[type.id].size);
|
||||
exec.setnum(result, data.unitCache(type) == null ? 0 : data.unitCache(type).size);
|
||||
}
|
||||
}
|
||||
case coreCount -> exec.setnum(result, data.cores.size);
|
||||
@@ -1511,6 +1519,47 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
public static class SenseWeatherI implements LInstruction{
|
||||
public int type, to;
|
||||
|
||||
public SenseWeatherI(int type, int to){
|
||||
this.type = type;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
exec.setbool(to, exec.obj(type) instanceof Weather weather && weather.isActive());
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetWeatherI implements LInstruction{
|
||||
public int type, state;
|
||||
|
||||
public SetWeatherI(int type, int state){
|
||||
this.type = type;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
if(exec.obj(type) instanceof Weather weather){
|
||||
if(exec.bool(state)){
|
||||
if(!weather.isActive()){ //Create is not already active
|
||||
Tmp.v1.setToRandomDirection();
|
||||
Call.createWeather(weather, 1f, WeatherState.fadeTime, Tmp.v1.x, Tmp.v1.y);
|
||||
}else{
|
||||
weather.instance().life(WeatherState.fadeTime);
|
||||
}
|
||||
}else{
|
||||
if(weather.isActive() && weather.instance().life > WeatherState.fadeTime){
|
||||
weather.instance().life(WeatherState.fadeTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApplyEffectI implements LInstruction{
|
||||
public boolean clear;
|
||||
public String effect;
|
||||
|
||||
@@ -13,6 +13,7 @@ public enum LMarkerControl{
|
||||
stroke("stroke"),
|
||||
rotation("rotation"),
|
||||
shape("sides", "fill", "outline"),
|
||||
arc("start", "end"),
|
||||
flushText("fetch"),
|
||||
fontSize("size"),
|
||||
textHeight("height"),
|
||||
|
||||
@@ -16,7 +16,7 @@ import mindustry.logic.LCanvas.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static mindustry.Vars.ui;
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.logic.LCanvas.*;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1380,6 +1380,115 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("weathersense")
|
||||
public static class WeatherSenseStatement extends LStatement{
|
||||
public String to = "result";
|
||||
public String weather = "@rain";
|
||||
|
||||
private transient TextField tfield;
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
field(table, to, str -> to = str);
|
||||
|
||||
table.add(" = weather ");
|
||||
|
||||
row(table);
|
||||
|
||||
tfield = field(table, weather, str -> weather = str).padRight(0f).get();
|
||||
|
||||
table.button(b -> {
|
||||
b.image(Icon.pencilSmall);
|
||||
|
||||
b.clicked(() -> showSelectTable(b, (t, hide) -> {
|
||||
t.row();
|
||||
t.table(i -> {
|
||||
i.left();
|
||||
int c = 0;
|
||||
for(Weather w : Vars.content.weathers()){
|
||||
i.button(w.name, Styles.flatt, () -> {
|
||||
weather = "@" + w.name;
|
||||
tfield.setText(weather);
|
||||
hide.run();
|
||||
}).height(40f).uniformX().wrapLabel(false).growX();
|
||||
|
||||
if(++c % 2 == 0) i.row();
|
||||
}
|
||||
}).left();
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(40f).padLeft(-1).color(table.color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean privileged(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new SenseWeatherI(builder.var(weather), builder.var(to));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.world;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("weatherset")
|
||||
public static class WeatherSetStatement extends LStatement{
|
||||
public String weather = "@rain", state = "true";
|
||||
|
||||
private transient TextField tfield;
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
table.add(" set weather ");
|
||||
|
||||
tfield = field(table, weather, str -> weather = str).padRight(0f).get();
|
||||
|
||||
table.button(b -> {
|
||||
b.image(Icon.pencilSmall);
|
||||
|
||||
b.clicked(() -> showSelectTable(b, (t, hide) -> {
|
||||
t.row();
|
||||
t.table(i -> {
|
||||
i.left();
|
||||
int c = 0;
|
||||
for(Weather w : Vars.content.weathers()){
|
||||
i.button(w.name, Styles.flatt, () -> {
|
||||
weather = "@" + w.name;
|
||||
tfield.setText(weather);
|
||||
hide.run();
|
||||
}).height(40f).uniformX().wrapLabel(false).growX();
|
||||
|
||||
if(++c % 2 == 0) i.row();
|
||||
}
|
||||
}).left();
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(40f).padLeft(-1).color(table.color);
|
||||
|
||||
table.add(" state ");
|
||||
|
||||
fields(table, state, str -> state = str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean privileged(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new SetWeatherI(builder.var(weather), builder.var(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.world;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("spawnwave")
|
||||
public static class SpawnWaveStatement extends LStatement{
|
||||
public String x = "10", y = "10", natural = "false";
|
||||
@@ -1747,11 +1856,17 @@ public class LStatements{
|
||||
fields(table, index, i -> index = i);
|
||||
}
|
||||
|
||||
if(type == FetchType.buildCount || type == FetchType.build || type == FetchType.unitCount){
|
||||
if(type == FetchType.buildCount || type == FetchType.build){
|
||||
row(table);
|
||||
|
||||
fields(table, "block", extra, i -> extra = i);
|
||||
}
|
||||
|
||||
if(type == FetchType.unitCount || type == FetchType.unit){
|
||||
row(table);
|
||||
|
||||
fields(table, "unit", extra, i -> extra = i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,20 +47,20 @@ public class BaseGenerator{
|
||||
if(bases.cores.isEmpty()) return;
|
||||
|
||||
Mathf.rand.setSeed(sector.id);
|
||||
Mathf.rand.nextDouble();
|
||||
|
||||
float bracketRange = 0.17f;
|
||||
float baseChance = Mathf.lerp(0.7f, 2.1f, difficulty);
|
||||
int wallAngle = 70; //180 for full coverage
|
||||
double resourceChance = 0.5 * baseChance;
|
||||
double nonResourceChance = 0.002 * baseChance;
|
||||
BasePart coreschem = bases.cores.getFrac(difficulty);
|
||||
int passes = difficulty < 0.4 ? 1 : difficulty < 0.8 ? 3 : 5;
|
||||
|
||||
Block wall = getDifficultyWall(1, difficulty), wallLarge = getDifficultyWall(2, difficulty);
|
||||
|
||||
for(Tile tile : cores){
|
||||
tile.clearOverlay();
|
||||
Schematics.placeLoadout(coreschem.schematic, tile.x, tile.y, team, false);
|
||||
Schematics.placeLoadout(bases.cores.getFrac((difficulty + Mathf.rand.range(0.4f)) / 1.4f).schematic, tile.x, tile.y, team, false);
|
||||
|
||||
//fill core with every type of item (even non-material)
|
||||
Building entity = tile.build;
|
||||
@@ -81,7 +81,7 @@ public class BaseGenerator{
|
||||
tryPlace(parts.getFrac(difficulty + Mathf.range(bracketRange)), tile.x, tile.y, team);
|
||||
}
|
||||
}else if(Mathf.chance(nonResourceChance)){
|
||||
tryPlace(bases.parts.getFrac(Mathf.random(1f)), tile.x, tile.y, team);
|
||||
tryPlace(bases.parts.getFrac(Mathf.rand.random(1f)), tile.x, tile.y, team);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -235,7 +235,7 @@ public class BaseGenerator{
|
||||
set(placed, item);
|
||||
}
|
||||
|
||||
Tile rand = world.tiles.getc(ex + Mathf.range(1), ey + Mathf.range(1));
|
||||
Tile rand = world.tiles.getc(ex + Mathf.rand.range(1), ey + Mathf.rand.range(1));
|
||||
if(rand.floor().hasSurface()){
|
||||
//random ores nearby to make it look more natural
|
||||
set(rand, item);
|
||||
|
||||
@@ -1054,7 +1054,21 @@ public class ContentParser{
|
||||
}
|
||||
Field field = metadata.field;
|
||||
try{
|
||||
field.set(object, parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType));
|
||||
boolean mergeMap = ObjectMap.class.isAssignableFrom(field.getType()) && child.has("add") && child.get("add").isBoolean() && child.getBoolean("add", false);
|
||||
|
||||
if(mergeMap){
|
||||
child.remove("add");
|
||||
}
|
||||
|
||||
Object readField = parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType);
|
||||
|
||||
//if a map has add: true, add its contents to the map instead
|
||||
if(mergeMap && field.get(object) instanceof ObjectMap<?,?> baseMap){
|
||||
baseMap.putAll((ObjectMap)readField);
|
||||
}else{
|
||||
field.set(object, readField);
|
||||
}
|
||||
|
||||
}catch(IllegalAccessException ex){
|
||||
throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
|
||||
}catch(SerializationException ex){
|
||||
@@ -1102,8 +1116,8 @@ public class ContentParser{
|
||||
}
|
||||
|
||||
//all items have a produce requirement unless already specified
|
||||
if(object instanceof Item i && !node.objectives.contains(o -> o instanceof Produce p && p.content == i)){
|
||||
node.objectives.add(new Produce(i));
|
||||
if((unlock instanceof Item || unlock instanceof Liquid) && !node.objectives.contains(o -> o instanceof Produce p && p.content == unlock)){
|
||||
node.objectives.add(new Produce(unlock));
|
||||
}
|
||||
|
||||
//remove old node from parent
|
||||
|
||||
@@ -218,7 +218,10 @@ public class Mods implements Loadable{
|
||||
}
|
||||
//this returns a *runnable* which actually packs the resulting pixmap; this has to be done synchronously outside the method
|
||||
return () -> {
|
||||
String fullName = (prefix ? mod.name + "-" : "") + baseName;
|
||||
//don't prefix with mod name if it's already prefixed by a category, e.g. `block-modname-content-full`.
|
||||
int hyphen = baseName.indexOf('-');
|
||||
String fullName = ((prefix && !(hyphen != -1 && baseName.substring(hyphen + 1).startsWith(mod.name + "-"))) ? mod.name + "-" : "") + baseName;
|
||||
|
||||
packer.add(getPage(file), fullName, new PixmapRegion(pix));
|
||||
if(textureScale != 1.0f){
|
||||
textureResize.put(fullName, textureScale);
|
||||
@@ -358,7 +361,24 @@ public class Mods implements Loadable{
|
||||
Log.debug("Time to generate icons: @", Time.elapsed());
|
||||
|
||||
//dispose old atlas data
|
||||
Core.atlas = packer.flush(filter, new TextureAtlas());
|
||||
Core.atlas = packer.flush(filter, new TextureAtlas(){
|
||||
PixmapRegion fake = new PixmapRegion(new Pixmap(1, 1));
|
||||
boolean didWarn = false;
|
||||
|
||||
@Override
|
||||
public PixmapRegion getPixmap(AtlasRegion region){
|
||||
var other = super.getPixmap(region);
|
||||
if(other.pixmap.isDisposed()){
|
||||
if(!didWarn){
|
||||
Log.err(new RuntimeException("Calling getPixmap outside of createIcons is not supported! This will be a crash in the future."));
|
||||
didWarn = true;
|
||||
}
|
||||
return fake;
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
});
|
||||
|
||||
textureResize.each(e -> Core.atlas.find(e.key).scale = e.value);
|
||||
|
||||
@@ -856,7 +876,7 @@ public class Mods implements Loadable{
|
||||
return false;
|
||||
// If dependency present, resolve it, or if it's not required, ignore it
|
||||
}else if(context.dependencies.containsKey(dependency.name)){
|
||||
if(!context.ordered.contains(dependency.name) && !resolve(dependency.name, context) && dependency.required){
|
||||
if(((!context.ordered.contains(dependency.name) && !resolve(dependency.name, context)) || !Core.settings.getBool("mod-" + dependency.name + "-enabled", true)) && dependency.required){
|
||||
context.invalid.put(element, ModState.incompleteDependencies);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ public class Item extends UnlockableContent implements Senseable{
|
||||
|
||||
Pixmap res = Pixmaps.blend(pixmaps[i], pixmaps[(i + 1) % frames], f);
|
||||
packer.add(PageType.main, name + "-t" + index, res);
|
||||
res.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@ public class SectorPreset extends UnlockableContent{
|
||||
/** Internal use only! */
|
||||
public SectorPreset(String name, LoadedMod mod){
|
||||
super(name);
|
||||
this.minfo.mod = mod;
|
||||
this.generator = new FileMapGenerator(name, this);
|
||||
if(mod != null){
|
||||
this.minfo.mod = mod;
|
||||
}
|
||||
this.generator = new FileMapGenerator(this.name, this);
|
||||
}
|
||||
|
||||
/** Internal use only! */
|
||||
|
||||
@@ -1270,6 +1270,7 @@ public class UnitType extends UnlockableContent implements Senseable{
|
||||
DrawPart.params.life = s.fin();
|
||||
}
|
||||
|
||||
applyColor(unit);
|
||||
part.draw(DrawPart.params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ public class Weapon implements Cloneable{
|
||||
var part = parts.get(i);
|
||||
DrawPart.params.setRecoil(part.recoilIndex >= 0 && mount.recoils != null ? mount.recoils[part.recoilIndex] : mount.recoil);
|
||||
if(part.under){
|
||||
unit.type.applyColor(unit);
|
||||
part.draw(DrawPart.params);
|
||||
}
|
||||
}
|
||||
@@ -262,6 +263,7 @@ public class Weapon implements Cloneable{
|
||||
var part = parts.get(i);
|
||||
DrawPart.params.setRecoil(part.recoilIndex >= 0 && mount.recoils != null ? mount.recoils[part.recoilIndex] : mount.recoil);
|
||||
if(!part.under){
|
||||
unit.type.applyColor(unit);
|
||||
part.draw(DrawPart.params);
|
||||
}
|
||||
}
|
||||
@@ -297,31 +299,9 @@ public class Weapon implements Cloneable{
|
||||
mount.warmup = Mathf.lerpDelta(mount.warmup, warmupTarget, shootWarmupSpeed);
|
||||
}
|
||||
|
||||
//rotate if applicable
|
||||
if(rotate && (mount.rotate || mount.shoot) && can){
|
||||
float axisX = unit.x + Angles.trnsx(unit.rotation - 90, x, y),
|
||||
axisY = unit.y + Angles.trnsy(unit.rotation - 90, x, y);
|
||||
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - unit.rotation;
|
||||
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, rotateSpeed * Time.delta);
|
||||
if(rotationLimit < 360){
|
||||
float dst = Angles.angleDist(mount.rotation, baseRotation);
|
||||
if(dst > rotationLimit/2f){
|
||||
mount.rotation = Angles.moveToward(mount.rotation, baseRotation, dst - rotationLimit/2f);
|
||||
}
|
||||
}
|
||||
}else if(!rotate){
|
||||
mount.rotation = baseRotation;
|
||||
mount.targetRotation = unit.angleTo(mount.aimX, mount.aimY);
|
||||
}
|
||||
|
||||
float
|
||||
weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : baseRotation),
|
||||
mountX = unit.x + Angles.trnsx(unit.rotation - 90, x, y),
|
||||
mountY = unit.y + Angles.trnsy(unit.rotation - 90, x, y),
|
||||
bulletX = mountX + Angles.trnsx(weaponRotation, this.shootX, this.shootY),
|
||||
bulletY = mountY + Angles.trnsy(weaponRotation, this.shootX, this.shootY),
|
||||
shootAngle = bulletRotation(unit, mount, bulletX, bulletY);
|
||||
mountY = unit.y + Angles.trnsy(unit.rotation - 90, x, y);
|
||||
|
||||
//find a new target
|
||||
if(!controllable && autoTarget){
|
||||
@@ -355,6 +335,30 @@ public class Weapon implements Cloneable{
|
||||
//logic will return shooting as false even if these return true, which is fine
|
||||
}
|
||||
|
||||
//rotate if applicable
|
||||
if(rotate && (mount.rotate || mount.shoot) && can){
|
||||
float axisX = unit.x + Angles.trnsx(unit.rotation - 90, x, y),
|
||||
axisY = unit.y + Angles.trnsy(unit.rotation - 90, x, y);
|
||||
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - unit.rotation;
|
||||
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, rotateSpeed * Time.delta);
|
||||
if(rotationLimit < 360){
|
||||
float dst = Angles.angleDist(mount.rotation, baseRotation);
|
||||
if(dst > rotationLimit/2f){
|
||||
mount.rotation = Angles.moveToward(mount.rotation, baseRotation, dst - rotationLimit/2f);
|
||||
}
|
||||
}
|
||||
}else if(!rotate){
|
||||
mount.rotation = baseRotation;
|
||||
mount.targetRotation = unit.angleTo(mount.aimX, mount.aimY);
|
||||
}
|
||||
|
||||
float
|
||||
weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : baseRotation),
|
||||
bulletX = mountX + Angles.trnsx(weaponRotation, this.shootX, this.shootY),
|
||||
bulletY = mountY + Angles.trnsy(weaponRotation, this.shootX, this.shootY),
|
||||
shootAngle = bulletRotation(unit, mount, bulletX, bulletY);
|
||||
|
||||
if(alwaysShooting) mount.shoot = true;
|
||||
|
||||
//update continuous state
|
||||
|
||||
@@ -314,7 +314,7 @@ public class Weather extends UnlockableContent{
|
||||
@EntityDef(value = {WeatherStatec.class}, pooled = true, isFinal = false)
|
||||
@Component(base = true)
|
||||
abstract static class WeatherStateComp implements Drawc, Syncc{
|
||||
private static final float fadeTime = 60 * 4;
|
||||
public static final float fadeTime = 60 * 4;
|
||||
|
||||
Weather weather;
|
||||
float intensity = 1f, opacity = 0f, life, effectTimer;
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.ui.dialogs;
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.KeyCode;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.ImageButton.*;
|
||||
@@ -30,6 +31,12 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
private Table main;
|
||||
private Prov<Rules> resetter;
|
||||
private LoadoutDialog loadoutDialog;
|
||||
public Seq<Table> categories;
|
||||
public Table current;
|
||||
public Seq<String> categoryNames;
|
||||
public String currentName;
|
||||
public String ruleSearch = "";
|
||||
public Seq<Runnable> additionalSetup; // for modding to easily add new rules
|
||||
|
||||
public CustomRulesDialog(){
|
||||
super("@mode.custom");
|
||||
@@ -40,6 +47,12 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
shown(this::setup);
|
||||
addCloseButton();
|
||||
|
||||
additionalSetup = new Seq<>();
|
||||
categories = new Seq<>();
|
||||
categoryNames = new Seq<>();
|
||||
currentName = "";
|
||||
ruleSearch = "";
|
||||
|
||||
buttons.button("@edit", Icon.pencil, () -> {
|
||||
BaseDialog dialog = new BaseDialog("@waves.edit");
|
||||
dialog.addCloseButton();
|
||||
@@ -176,13 +189,26 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
void setup(){
|
||||
categories.clear();
|
||||
cont.clear();
|
||||
cont.table(t -> {
|
||||
t.add("@search").padRight(10);
|
||||
t.field(ruleSearch, text ->
|
||||
ruleSearch = text.trim().replaceAll(" +", " ").toLowerCase()
|
||||
).grow().pad(8).get().keyDown(KeyCode.enter, this::setup);
|
||||
t.button(Icon.cancel, Styles.emptyi, () -> {
|
||||
ruleSearch = "";
|
||||
setup();
|
||||
}).padLeft(10f).size(35f);
|
||||
t.button(Icon.zoom, Styles.emptyi, this::setup).size(54f);
|
||||
}).row();
|
||||
cont.pane(m -> main = m).scrollX(false);
|
||||
main.margin(10f);
|
||||
main.left().defaults().fillX().left().pad(5);
|
||||
main.left().defaults().fillX().left();
|
||||
main.row();
|
||||
|
||||
title("@rules.title.waves");
|
||||
|
||||
category("waves");
|
||||
check("@rules.waves", b -> rules.waves = b, () -> rules.waves);
|
||||
check("@rules.wavesending", b -> rules.waveSending = b, () -> rules.waveSending, () -> rules.waves);
|
||||
check("@rules.wavetimer", b -> rules.waveTimer = b, () -> rules.waveTimer, () -> rules.waves);
|
||||
@@ -195,7 +221,8 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
}
|
||||
number("@rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> rules.waves);
|
||||
|
||||
title("@rules.title.resourcesbuilding");
|
||||
|
||||
category("resourcesbuilding");
|
||||
check("@rules.infiniteresources", b -> {
|
||||
rules.infiniteResources = b;
|
||||
|
||||
@@ -207,7 +234,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
setup();
|
||||
}
|
||||
}, () -> rules.infiniteResources);
|
||||
check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore);
|
||||
withInfo("@rules.onlydepositcore.info", () -> check("@rules.onlydepositcore", b -> rules.onlyDepositCore = b, () -> rules.onlyDepositCore));
|
||||
check("@rules.derelictrepair", b -> rules.derelictRepair = b, () -> rules.derelictRepair);
|
||||
check("@rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions);
|
||||
check("@rules.schematic", b -> rules.schematicsAllowed = b, () -> rules.schematicsAllowed);
|
||||
@@ -220,19 +247,25 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
number("@rules.blockhealthmultiplier", f -> rules.blockHealthMultiplier = f, () -> rules.blockHealthMultiplier);
|
||||
number("@rules.blockdamagemultiplier", f -> rules.blockDamageMultiplier = f, () -> rules.blockDamageMultiplier);
|
||||
|
||||
main.button("@configure",
|
||||
() -> loadoutDialog.show(999999, rules.loadout,
|
||||
i -> true,
|
||||
() -> rules.loadout.clear().add(new ItemStack(Items.copper, 100)),
|
||||
() -> {}, () -> {}
|
||||
)).left().width(300f).row();
|
||||
if(Core.bundle.get("configure").toLowerCase().contains(ruleSearch)){
|
||||
current.button("@configure",
|
||||
() -> loadoutDialog.show(999999, rules.loadout,
|
||||
i -> true,
|
||||
() -> rules.loadout.clear().add(new ItemStack(Items.copper, 100)),
|
||||
() -> {}, () -> {}
|
||||
)).left().width(300f).row();
|
||||
}
|
||||
|
||||
main.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f).row();
|
||||
if(Core.bundle.get("bannedblocks").toLowerCase().contains(ruleSearch)){
|
||||
current.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f).row();
|
||||
}
|
||||
check("@rules.hidebannedblocks", b -> rules.hideBannedBlocks = b, () -> rules.hideBannedBlocks);
|
||||
check("@bannedblocks.whitelist", b -> rules.blockWhitelist = b, () -> rules.blockWhitelist);
|
||||
|
||||
title("@rules.title.unit");
|
||||
|
||||
category("unit");
|
||||
check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable);
|
||||
check("@rules.unitpayloadsexplode", b -> rules.unitPayloadsExplode = b, () -> rules.unitPayloadsExplode);
|
||||
numberi("@rules.unitcap", f -> rules.unitCap = f, () -> rules.unitCap, -999, 999);
|
||||
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
|
||||
number("@rules.unitcrashdamagemultiplier", f -> rules.unitCrashDamageMultiplier = f, () -> rules.unitCrashDamageMultiplier);
|
||||
@@ -240,17 +273,21 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
number("@rules.unitcostmultiplier", f -> rules.unitCostMultiplier = f, () -> rules.unitCostMultiplier);
|
||||
number("@rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
|
||||
|
||||
main.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f).row();
|
||||
if(Core.bundle.get("bannedunits").toLowerCase().contains(ruleSearch)){
|
||||
current.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f).row();
|
||||
}
|
||||
check("@bannedunits.whitelist", b -> rules.unitWhitelist = b, () -> rules.unitWhitelist);
|
||||
|
||||
title("@rules.title.enemy");
|
||||
|
||||
category("enemy");
|
||||
check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
|
||||
check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture);
|
||||
check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck);
|
||||
withInfo("@rules.placerangecheck.info",() -> check("@rules.placerangecheck", b -> rules.placeRangeCheck = b, () -> rules.placeRangeCheck));
|
||||
check("@rules.polygoncoreprotection", b -> rules.polygonCoreProtection = b, () -> rules.polygonCoreProtection);
|
||||
number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200), () -> !rules.polygonCoreProtection);
|
||||
|
||||
title("@rules.title.environment");
|
||||
|
||||
category("environment");
|
||||
check("@rules.explosions", b -> rules.damageExplosions = b, () -> rules.damageExplosions);
|
||||
check("@rules.fire", b -> rules.fire = b, () -> rules.fire);
|
||||
check("@rules.fog", b -> rules.fog = b, () -> rules.fog);
|
||||
@@ -266,63 +303,70 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
|
||||
number("@rules.solarmultiplier", f -> rules.solarMultiplier = f, () -> rules.solarMultiplier);
|
||||
|
||||
main.button(b -> {
|
||||
b.left();
|
||||
b.table(Tex.pane, in -> {
|
||||
in.stack(new Image(Tex.alphaBg), new Image(Tex.whiteui){{
|
||||
update(() -> setColor(rules.ambientLight));
|
||||
}}).grow();
|
||||
}).margin(4).size(50f).padRight(10);
|
||||
b.add("@rules.ambientlight");
|
||||
}, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f).row();
|
||||
if(Core.bundle.get("rules.ambientlight").toLowerCase().contains(ruleSearch)){
|
||||
current.button(b -> {
|
||||
b.left();
|
||||
b.table(Tex.pane, in -> {
|
||||
in.stack(new Image(Tex.alphaBg), new Image(Tex.whiteui){{
|
||||
update(() -> setColor(rules.ambientLight));
|
||||
}}).grow();
|
||||
}).margin(4).size(50f).padRight(10);
|
||||
b.add("@rules.ambientlight");
|
||||
}, () -> ui.picker.show(rules.ambientLight, rules.ambientLight::set)).left().width(250f).row();
|
||||
}
|
||||
|
||||
main.button("@rules.weather", this::weatherDialog).width(250f).left().row();
|
||||
if(Core.bundle.get("rules.weather").toLowerCase().contains(ruleSearch)){
|
||||
current.button("@rules.weather", this::weatherDialog).width(250f).left().row();
|
||||
}
|
||||
|
||||
title("@rules.title.planet");
|
||||
|
||||
main.table(Tex.button, t -> {
|
||||
t.margin(10f);
|
||||
var group = new ButtonGroup<>();
|
||||
var style = Styles.flatTogglet;
|
||||
category("planet");
|
||||
if(Core.bundle.get("rules.title.planet").toLowerCase().contains(ruleSearch)){
|
||||
current.table(Tex.button, t -> {
|
||||
t.margin(10f);
|
||||
var group = new ButtonGroup<>();
|
||||
var style = Styles.flatTogglet;
|
||||
|
||||
t.defaults().size(140f, 50f);
|
||||
t.defaults().size(140f, 50f);
|
||||
|
||||
for(Planet planet : content.planets().select(p -> p.accessible && p.visible && p.isLandable())){
|
||||
t.button(planet.localizedName, style, () -> {
|
||||
planet.applyRules(rules);
|
||||
}).group(group).checked(b -> rules.planet == planet);
|
||||
for(Planet planet : content.planets().select(p -> p.accessible && p.visible && p.isLandable())){
|
||||
t.button(planet.localizedName, style, () -> {
|
||||
planet.applyRules(rules);
|
||||
}).group(group).checked(b -> rules.planet == planet);
|
||||
|
||||
if(t.getChildren().size % 3 == 0){
|
||||
t.row();
|
||||
if(t.getChildren().size % 3 == 0){
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.button("@rules.anyenv", style, () -> {
|
||||
rules.env = Vars.defaultEnv;
|
||||
rules.hiddenBuildItems.clear();
|
||||
rules.planet = Planets.sun;
|
||||
}).group(group).checked(b -> rules.planet == Planets.sun);
|
||||
}).left().fill(false).expand(false, false).row();
|
||||
t.button("@rules.anyenv", style, () -> {
|
||||
rules.env = Vars.defaultEnv;
|
||||
rules.hiddenBuildItems.clear();
|
||||
rules.planet = Planets.sun;
|
||||
}).group(group).checked(b -> rules.planet == Planets.sun);
|
||||
}).left().fill(false).expand(false, false).row();
|
||||
}
|
||||
|
||||
title("@rules.title.teams");
|
||||
|
||||
category("teams");
|
||||
team("@rules.playerteam", t -> rules.defaultTeam = t, () -> rules.defaultTeam);
|
||||
team("@rules.enemyteam", t -> rules.waveTeam = t, () -> rules.waveTeam);
|
||||
|
||||
for(Team team : Team.baseTeams){
|
||||
boolean[] shown = {false};
|
||||
Table wasMain = main;
|
||||
Table wasCurrent = current;
|
||||
|
||||
main.button(team.coloredName(), Icon.downOpen, Styles.togglet, () -> {
|
||||
Table teamRules = new Table(); // just button and collapser in one table
|
||||
teamRules.button(team.coloredName(), Icon.downOpen, Styles.togglet, () -> {
|
||||
shown[0] = !shown[0];
|
||||
}).marginLeft(14f).width(260f).height(55f).update(t -> {
|
||||
((Image)t.getChildren().get(1)).setDrawable(shown[0] ? Icon.upOpen : Icon.downOpen);
|
||||
t.setChecked(shown[0]);
|
||||
}).row();
|
||||
}).left().padBottom(2f).row();
|
||||
|
||||
main.collapser(t -> {
|
||||
t.left().defaults().fillX().left().pad(5);
|
||||
main = t;
|
||||
teamRules.collapser(c -> {
|
||||
c.left().defaults().fillX().left().pad(5);
|
||||
current = c;
|
||||
TeamRule teams = rules.teams.get(team);
|
||||
|
||||
number("@rules.blockhealthmultiplier", f -> teams.blockHealthMultiplier = f, () -> teams.blockHealthMultiplier);
|
||||
@@ -346,13 +390,42 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
number("@rules.unitcostmultiplier", f -> teams.unitCostMultiplier = f, () -> teams.unitCostMultiplier);
|
||||
number("@rules.unithealthmultiplier", f -> teams.unitHealthMultiplier = f, () -> teams.unitHealthMultiplier);
|
||||
|
||||
main = wasMain;
|
||||
}, () -> shown[0]).growX().row();
|
||||
if(!current.hasChildren()){
|
||||
teamRules.clear();
|
||||
}else{
|
||||
wasCurrent.add(teamRules).row();
|
||||
}
|
||||
|
||||
current = wasCurrent;
|
||||
}, () -> shown[0]).left().growX().row();
|
||||
}
|
||||
|
||||
additionalSetup.each(Runnable::run);
|
||||
|
||||
for(var i = 0; i < categories.size; i++){
|
||||
addToMain(categories.get(i), Core.bundle.get("rules.title." + categoryNames.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
void team(String text, Cons<Team> cons, Prov<Team> prov){
|
||||
main.table(t -> {
|
||||
public void category(String name){
|
||||
current = new Table();
|
||||
current.left().defaults().fillX().left().pad(5);
|
||||
currentName = name;
|
||||
categories.add(current);
|
||||
categoryNames.add(currentName);
|
||||
}
|
||||
|
||||
void addToMain(Table category, String title){
|
||||
if(category.hasChildren()){
|
||||
main.add(title).color(Pal.accent).padTop(20).padRight(100f).padBottom(-3).fillX().left().pad(5).row();
|
||||
main.image().color(Pal.accent).height(3f).padRight(100f).padBottom(20).fillX().left().pad(5).row();
|
||||
main.add(category).row();
|
||||
}
|
||||
}
|
||||
|
||||
public void team(String text, Cons<Team> cons, Prov<Team> prov){
|
||||
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
|
||||
current.table(t -> {
|
||||
t.left();
|
||||
t.add(text).left().padRight(5);
|
||||
|
||||
@@ -364,28 +437,29 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
}).padTop(0).row();
|
||||
}
|
||||
|
||||
void number(String text, Floatc cons, Floatp prov){
|
||||
public void number(String text, Floatc cons, Floatp prov){
|
||||
number(text, false, cons, prov, () -> true, 0, Float.MAX_VALUE);
|
||||
}
|
||||
|
||||
void number(String text, Floatc cons, Floatp prov, float min, float max){
|
||||
public void number(String text, Floatc cons, Floatp prov, float min, float max){
|
||||
number(text, false, cons, prov, () -> true, min, max);
|
||||
}
|
||||
|
||||
void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition){
|
||||
public void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition){
|
||||
number(text, integer, cons, prov, condition, 0, Float.MAX_VALUE);
|
||||
}
|
||||
|
||||
void number(String text, Floatc cons, Floatp prov, Boolp condition){
|
||||
public void number(String text, Floatc cons, Floatp prov, Boolp condition){
|
||||
number(text, false, cons, prov, condition, 0, Float.MAX_VALUE);
|
||||
}
|
||||
|
||||
void numberi(String text, Intc cons, Intp prov, int min, int max){
|
||||
public void numberi(String text, Intc cons, Intp prov, int min, int max){
|
||||
numberi(text, cons, prov, () -> true, min, max);
|
||||
}
|
||||
|
||||
void numberi(String text, Intc cons, Intp prov, Boolp condition, int min, int max){
|
||||
main.table(t -> {
|
||||
public void numberi(String text, Intc cons, Intp prov, Boolp condition, int min, int max){
|
||||
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
|
||||
current.table(t -> {
|
||||
t.left();
|
||||
t.add(text).left().padRight(5)
|
||||
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
|
||||
@@ -396,8 +470,9 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
}).padTop(0).row();
|
||||
}
|
||||
|
||||
void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition, float min, float max){
|
||||
main.table(t -> {
|
||||
public void number(String text, boolean integer, Floatc cons, Floatp prov, Boolp condition, float min, float max){
|
||||
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
|
||||
current.table(t -> {
|
||||
t.left();
|
||||
t.add(text).left().padRight(5)
|
||||
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
|
||||
@@ -406,23 +481,34 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
.update(a -> a.setDisabled(!condition.get()))
|
||||
.valid(f -> Strings.canParsePositiveFloat(f) && Strings.parseFloat(f) >= min && Strings.parseFloat(f) <= max).width(120f).left();
|
||||
}).padTop(0);
|
||||
main.row();
|
||||
current.row();
|
||||
}
|
||||
|
||||
void check(String text, Boolc cons, Boolp prov){
|
||||
public void check(String text, Boolc cons, Boolp prov){
|
||||
check(text, cons, prov, () -> true);
|
||||
}
|
||||
|
||||
void check(String text, Boolc cons, Boolp prov, Boolp condition){
|
||||
main.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left();
|
||||
main.row();
|
||||
public void check(String text, Boolc cons, Boolp prov, Boolp condition){
|
||||
if(!Core.bundle.get(text.substring(1)).toLowerCase().contains(ruleSearch)) return;
|
||||
current.check(text, cons).checked(prov.get()).update(a -> a.setDisabled(!condition.get())).padRight(100f).get().left();
|
||||
current.row();
|
||||
}
|
||||
|
||||
void title(String text){
|
||||
main.add(text).color(Pal.accent).padTop(20).padRight(100f).padBottom(-3);
|
||||
main.row();
|
||||
main.image().color(Pal.accent).height(3f).padRight(100f).padBottom(20);
|
||||
main.row();
|
||||
public void withInfo(String info, Runnable add){
|
||||
Table wasCurrent = current;
|
||||
current = new Table();
|
||||
current.left().defaults().fillX().left();
|
||||
|
||||
current.button(Icon.infoSmall, () -> ui.showInfo(info)).size(32f).padRight(10f);
|
||||
add.run();
|
||||
|
||||
// rule does not match search pattern (runnable returned without adding anything)
|
||||
if(current.getCells().size < 2){
|
||||
current.clear();
|
||||
}else{
|
||||
wasCurrent.add(current).row();
|
||||
}
|
||||
current = wasCurrent;
|
||||
}
|
||||
|
||||
Cell<TextField> field(Table table, float value, Floatc setter){
|
||||
|
||||
@@ -35,6 +35,7 @@ import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.*;
|
||||
@@ -1244,7 +1245,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
Events.fire(new SectorLaunchLoadoutEvent(sector, from, loadout));
|
||||
|
||||
if(settings.getBool("skipcoreanimation")){
|
||||
CoreBuild core = player.team().core();
|
||||
if(core == null || settings.getBool("skipcoreanimation")){
|
||||
//just... go there
|
||||
control.playSector(from, sector);
|
||||
//hide only after load screen is shown
|
||||
@@ -1256,9 +1258,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
//allow planet dialog to finish hiding before actually launching
|
||||
Time.runTask(5f, () -> {
|
||||
Runnable doLaunch = () -> {
|
||||
renderer.showLaunch(schemCore);
|
||||
renderer.showLaunch(core, schemCore);
|
||||
//run with less delay, as the loading animation is delayed by several frames
|
||||
Time.runTask(coreLandDuration - 8f, () -> control.playSector(from, sector));
|
||||
Time.runTask(core.landDuration() - 8f, () -> control.playSector(from, sector));
|
||||
};
|
||||
|
||||
//load launchFrom sector right before launching so animation is correct
|
||||
@@ -1276,7 +1278,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}else if(mode == select){
|
||||
listener.get(sector);
|
||||
}else if(mode == planetLaunch){ //TODO make sure it doesn't have a base already.
|
||||
|
||||
//TODO animation
|
||||
//schematic selection and cost handled by listener
|
||||
listener.get(sector);
|
||||
|
||||
@@ -11,6 +11,7 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
@@ -169,7 +170,8 @@ public class HintsFragment{
|
||||
depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()),
|
||||
desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active() && state.wave >= 2, () -> Core.input.keyTap(Binding.pause)),
|
||||
unitControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore),
|
||||
unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(), () -> control.input.commandMode && control.input.selectedUnits.size > 0),
|
||||
unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(),
|
||||
() -> control.input.commandMode && control.input.selectedUnits.size > 0 && control.input.selectedUnits.first().controller() instanceof CommandAI ai && ai.targetPos != null),
|
||||
respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore),
|
||||
launch(() -> (isTutorial.get() || Vars.state.rules.sector == SectorPresets.onset.sector) && state.rules.sector.isCaptured(), () -> ui.planet.isShown()),
|
||||
schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router) || ui.hints.placedBlocks.contains(Blocks.ductRouter), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)),
|
||||
|
||||
@@ -27,6 +27,8 @@ import mindustry.input.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.gen.Tex.*;
|
||||
@@ -584,6 +586,8 @@ public class HudFragment{
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated see {@link CoreBuild#beginLaunch(CoreBlock)} */
|
||||
@Deprecated
|
||||
public void showLaunch(){
|
||||
float margin = 30f;
|
||||
|
||||
@@ -602,6 +606,8 @@ public class HudFragment{
|
||||
Core.scene.add(image);
|
||||
}
|
||||
|
||||
/** @deprecated see {@link CoreBuild#beginLaunch(CoreBlock)} */
|
||||
@Deprecated
|
||||
public void showLand(){
|
||||
Image image = new Image();
|
||||
image.color.a = 1f;
|
||||
|
||||
@@ -993,6 +993,11 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
return consume(new ConsumePowerDynamic((Floatf<Building>)usage));
|
||||
}
|
||||
|
||||
/** Creates a consumer that consumes a dynamic amount of power. */
|
||||
public <T extends Building> ConsumePower consumePowerDynamic(float displayed, Floatf<T> usage){
|
||||
return consume(new ConsumePowerDynamic(displayed, (Floatf<Building>)usage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a consumer which stores power.
|
||||
* @param powerCapacity The maximum capacity in power units.
|
||||
|
||||
@@ -164,8 +164,9 @@ public class ConstructBlock extends Block{
|
||||
public boolean wasConstructing, activeDeconstruct;
|
||||
public float constructColor;
|
||||
|
||||
private float[] accumulator;
|
||||
private float[] totalAccumulator;
|
||||
private @Nullable float[] accumulator;
|
||||
private @Nullable float[] totalAccumulator;
|
||||
private @Nullable int[] itemsLeft;
|
||||
|
||||
@Override
|
||||
public String getDisplayName(){
|
||||
@@ -270,7 +271,7 @@ public class ConstructBlock extends Block{
|
||||
|
||||
for(int i = 0; i < current.requirements.length; i++){
|
||||
int reqamount = Math.round(state.rules.buildCostMultiplier * current.requirements[i].amount);
|
||||
accumulator[i] += Math.min(reqamount * maxProgress, reqamount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator
|
||||
accumulator[i] += Math.min(reqamount * maxProgress, reqamount - totalAccumulator[i]); //add min amount progressed to the accumulator
|
||||
totalAccumulator[i] = Math.min(totalAccumulator[i] + reqamount * maxProgress, reqamount);
|
||||
}
|
||||
|
||||
@@ -279,9 +280,28 @@ public class ConstructBlock extends Block{
|
||||
progress = Mathf.clamp(progress + maxProgress);
|
||||
|
||||
if(progress >= 1f || state.rules.infiniteResources){
|
||||
if(lastBuilder == null) lastBuilder = builder;
|
||||
if(!net.client()){
|
||||
constructed(tile, current, lastBuilder, (byte)rotation, builder.team, config);
|
||||
boolean canFinish = true;
|
||||
|
||||
//look at leftover resources to consume, get them from the core if necessary, delay building if not
|
||||
if(!state.rules.infiniteResources){
|
||||
for(int i = 0; i < itemsLeft.length; i++){
|
||||
if(itemsLeft[i] > 0){
|
||||
if(core != null && core.items.has(current.requirements[i].item, itemsLeft[i])){
|
||||
core.items.remove(current.requirements[i].item, itemsLeft[i]);
|
||||
itemsLeft[i] = 0;
|
||||
}else{
|
||||
canFinish = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(canFinish){
|
||||
if(lastBuilder == null) lastBuilder = builder;
|
||||
if(!net.client()){
|
||||
constructed(tile, current, lastBuilder, (byte)rotation, builder.team, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,6 +341,7 @@ public class ConstructBlock extends Block{
|
||||
int accepting = Math.min(accumulated, core.storageCapacity - core.items.get(requirements[i].item));
|
||||
//transfer items directly, as this is not production.
|
||||
core.items.add(requirements[i].item, accepting);
|
||||
itemsLeft[i] += accepting;
|
||||
accumulator[i] -= accepting;
|
||||
}else{
|
||||
accumulator[i] -= accumulated;
|
||||
@@ -331,6 +352,17 @@ public class ConstructBlock extends Block{
|
||||
progress = Mathf.clamp(progress - amount);
|
||||
|
||||
if(progress <= current.deconstructThreshold || state.rules.infiniteResources){
|
||||
//add any leftover items that weren't obtained due to rounding errors
|
||||
if(core != null){
|
||||
for(int i = 0; i < itemsLeft.length; i++){
|
||||
int target = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier * state.rules.deconstructRefundMultiplier);
|
||||
int remaining = target - itemsLeft[i];
|
||||
|
||||
core.items.add(current.requirements[i].item, Mathf.clamp(remaining, 0, core.storageCapacity - core.items.get(current.requirements[i].item)));
|
||||
itemsLeft[i] = target;
|
||||
}
|
||||
}
|
||||
|
||||
if(lastBuilder == null) lastBuilder = builder;
|
||||
Call.deconstructFinish(tile, this.current, lastBuilder);
|
||||
}
|
||||
@@ -341,6 +373,11 @@ public class ConstructBlock extends Block{
|
||||
boolean infinite = team.rules().infiniteResources || state.rules.infiniteResources;
|
||||
|
||||
for(int i = 0; i < current.requirements.length; i++){
|
||||
//there is no need to remove items that have already been fully taken out
|
||||
if(itemsLeft[i] == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
int sclamount = Math.round(state.rules.buildCostMultiplier * current.requirements[i].amount);
|
||||
int required = (int)(accumulator[i]); //calculate items that are required now
|
||||
|
||||
@@ -360,6 +397,7 @@ public class ConstructBlock extends Block{
|
||||
//remove stuff that is actually used
|
||||
if(remove && !infinite){
|
||||
inventory.remove(current.requirements[i].item, maxUse);
|
||||
itemsLeft[i] -= maxUse;
|
||||
}
|
||||
}
|
||||
//else, no items are required yet, so just keep going
|
||||
@@ -368,6 +406,7 @@ public class ConstructBlock extends Block{
|
||||
return maxProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float progress(){
|
||||
return progress;
|
||||
}
|
||||
@@ -380,8 +419,14 @@ public class ConstructBlock extends Block{
|
||||
this.current = block;
|
||||
this.previous = previous;
|
||||
this.buildCost = block.buildCost * state.rules.buildCostMultiplier;
|
||||
this.itemsLeft = new int[block.requirements.length];
|
||||
this.accumulator = new float[block.requirements.length];
|
||||
this.totalAccumulator = new float[block.requirements.length];
|
||||
|
||||
ItemStack[] requirements = current.requirements;
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
this.itemsLeft[i] = Mathf.round(requirements[i].amount * state.rules.buildCostMultiplier);
|
||||
}
|
||||
pathfinder.updateTile(tile);
|
||||
}
|
||||
|
||||
@@ -394,11 +439,17 @@ public class ConstructBlock extends Block{
|
||||
this.progress = 1f;
|
||||
this.current = previous;
|
||||
this.buildCost = previous.buildCost * state.rules.buildCostMultiplier;
|
||||
this.itemsLeft = new int[previous.requirements.length];
|
||||
this.accumulator = new float[previous.requirements.length];
|
||||
this.totalAccumulator = new float[previous.requirements.length];
|
||||
pathfinder.updateTile(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
@@ -413,6 +464,7 @@ public class ConstructBlock extends Block{
|
||||
for(int i = 0; i < accumulator.length; i++){
|
||||
write.f(accumulator[i]);
|
||||
write.f(totalAccumulator[i]);
|
||||
write.i(itemsLeft[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,9 +480,13 @@ public class ConstructBlock extends Block{
|
||||
if(acsize != -1){
|
||||
accumulator = new float[acsize];
|
||||
totalAccumulator = new float[acsize];
|
||||
itemsLeft = new int[acsize];
|
||||
for(int i = 0; i < acsize; i++){
|
||||
accumulator[i] = read.f();
|
||||
totalAccumulator[i] = read.f();
|
||||
if(revision >= 1){
|
||||
itemsLeft[i] = read.i();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
core/src/mindustry/world/blocks/RotBlock.java
Normal file
6
core/src/mindustry/world/blocks/RotBlock.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package mindustry.world.blocks;
|
||||
|
||||
/** Any block that has 360-degree rotation */
|
||||
public interface RotBlock{
|
||||
float buildRotation();
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public class BuildTurret extends BaseTurret{
|
||||
return new TextureRegion[]{baseRegion, region};
|
||||
}
|
||||
|
||||
public class BuildTurretBuild extends BaseTurretBuild implements ControlBlock{
|
||||
public class BuildTurretBuild extends BaseTurretBuild implements ControlBlock, RotBlock{
|
||||
public BlockUnitc unit = (BlockUnitc)unitType.create(team);
|
||||
public @Nullable Unit following;
|
||||
public @Nullable BlockPlan lastPlan;
|
||||
@@ -92,6 +92,11 @@ public class BuildTurret extends BaseTurret{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float buildRotation(){
|
||||
return unit.rotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unit unit(){
|
||||
//make sure stats are correct
|
||||
|
||||
@@ -9,6 +9,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -79,7 +80,7 @@ public class BaseTurret extends Block{
|
||||
stats.add(Stat.shootRange, range / tilesize, StatUnit.blocks);
|
||||
}
|
||||
|
||||
public class BaseTurretBuild extends Building implements Ranged{
|
||||
public class BaseTurretBuild extends Building implements Ranged, RotBlock{
|
||||
public float rotation = 90;
|
||||
|
||||
@Override
|
||||
@@ -87,6 +88,11 @@ public class BaseTurret extends Block{
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float buildRotation(){
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
Drawf.dashCircle(x, y, range(), team.color);
|
||||
|
||||
@@ -18,6 +18,7 @@ import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -104,7 +105,7 @@ public class MassDriver extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
public class MassDriverBuild extends Building{
|
||||
public class MassDriverBuild extends Building implements RotBlock{
|
||||
public int link = -1;
|
||||
public float rotation = 90;
|
||||
public float reloadCounter = 0f;
|
||||
@@ -112,6 +113,11 @@ public class MassDriver extends Block{
|
||||
//TODO use queue? this array usually holds about 3 shooters max anyway
|
||||
public OrderedSet<Building> waitingShooters = new OrderedSet<>();
|
||||
|
||||
@Override
|
||||
public float buildRotation(){
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public Building currentShooter(){
|
||||
return waitingShooters.isEmpty() ? null : waitingShooters.first();
|
||||
}
|
||||
|
||||
@@ -189,6 +189,13 @@ public class StackConveyor extends Block implements Autotiler{
|
||||
Draw.rect(lastItem.fullIcon, Tmp.v1.x, Tmp.v1.y, size, size, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropped(){
|
||||
super.dropped();
|
||||
var prev = Geometry.d4[(rotation + 2) % 4];
|
||||
link = Point2.pack(tile.x + prev.x, tile.y + prev.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawCracks(){
|
||||
Draw.z(Layer.block - 0.15f);
|
||||
|
||||
@@ -51,6 +51,11 @@ public class BuildPayload implements Payload{
|
||||
build.updatePayload(unitHolder, buildingHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyed(){
|
||||
build.onDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] requirements(){
|
||||
return build.block.requirements;
|
||||
|
||||
@@ -54,6 +54,8 @@ public interface Payload extends Position{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
default void destroyed(){};
|
||||
|
||||
/** writes the payload for saving. */
|
||||
void write(Writes write);
|
||||
|
||||
|
||||
@@ -151,6 +151,12 @@ public class PayloadBlock extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyed(){
|
||||
if(payload != null) payload.destroyed();
|
||||
super.onDestroyed();
|
||||
}
|
||||
|
||||
public boolean blends(int direction){
|
||||
return PayloadBlock.blends(this, direction);
|
||||
}
|
||||
|
||||
@@ -190,6 +190,12 @@ public class PayloadConveyor extends Block{
|
||||
super.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyed(){
|
||||
if(item != null) item.destroyed();
|
||||
super.onDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
@@ -12,6 +12,7 @@ import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.payloads.PayloadUnloader.*;
|
||||
import mindustry.world.blocks.sandbox.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -76,7 +77,7 @@ public class PayloadLoader extends PayloadBlock{
|
||||
public void init(){
|
||||
if(loadPowerDynamic){
|
||||
basePowerUse = consPower != null ? consPower.usage : 0f;
|
||||
consumePowerDynamic((PayloadLoaderBuild loader) -> loader.hasBattery() && !loader.exporting ? maxPowerConsumption + basePowerUse : basePowerUse);
|
||||
consumePowerDynamic(basePowerUse, (PayloadLoaderBuild loader) -> loader.shouldConsume() ? (loader.hasBattery() && !loader.exporting ? maxPowerConsumption + basePowerUse : basePowerUse) : 0f);
|
||||
}
|
||||
|
||||
super.init();
|
||||
@@ -152,6 +153,7 @@ public class PayloadLoader extends PayloadBlock{
|
||||
|
||||
//load up items
|
||||
if(payload.block().hasItems && items.any()){
|
||||
boolean acceptedAny = false;
|
||||
if(efficiency > 0.01f && timer(timerLoad, loadTime / efficiency)){
|
||||
//load up items a set amount of times
|
||||
for(int j = 0; j < itemsLoaded && items.any(); j++){
|
||||
@@ -162,6 +164,7 @@ public class PayloadLoader extends PayloadBlock{
|
||||
if(payload.build.acceptItem(payload.build, item)){
|
||||
payload.build.handleItem(payload.build, item);
|
||||
items.remove(item, 1);
|
||||
acceptedAny = true;
|
||||
break;
|
||||
}else if(payload.block().separateItemCapacity || payload.block().consumesItem(item)){
|
||||
exporting = true;
|
||||
@@ -171,6 +174,9 @@ public class PayloadLoader extends PayloadBlock{
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!acceptedAny){
|
||||
exporting = true;
|
||||
}
|
||||
}
|
||||
|
||||
//load up liquids
|
||||
@@ -180,8 +186,12 @@ public class PayloadLoader extends PayloadBlock{
|
||||
float flow = Math.min(Math.min(liquidsLoaded * edelta(), payload.block().liquidCapacity - payload.build.liquids.get(liq)), total);
|
||||
//TODO potential crash here
|
||||
if(payload.build.acceptLiquid(payload.build, liq)){
|
||||
payload.build.liquids.add(liq, flow);
|
||||
if(!(payload.block() instanceof LiquidVoid)){
|
||||
payload.build.liquids.add(liq, flow);
|
||||
}
|
||||
liquids.remove(liq, flow);
|
||||
}else{
|
||||
exporting = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -126,7 +127,7 @@ public class PayloadMassDriver extends PayloadBlock{
|
||||
return new TextureRegion[]{leftRegion, rightRegion, capRegion};
|
||||
}
|
||||
|
||||
public class PayloadDriverBuild extends PayloadBlockBuild<Payload>{
|
||||
public class PayloadDriverBuild extends PayloadBlockBuild<Payload> implements RotBlock{
|
||||
public int link = -1;
|
||||
public float turretRotation = 90;
|
||||
public float reloadCounter = 0f, charge = 0f;
|
||||
@@ -143,6 +144,11 @@ public class PayloadMassDriver extends PayloadBlock{
|
||||
return waitingShooters.isEmpty() ? null : waitingShooters.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float buildRotation(){
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
@@ -87,7 +87,7 @@ public class PayloadUnloader extends PayloadLoader{
|
||||
(liquids.current() == payload.build.liquids.current() || liquids.currentAmount() <= 0.2f)){
|
||||
var liq = payload.build.liquids.current();
|
||||
float remaining = liquidCapacity - liquids.currentAmount();
|
||||
float flow = Math.min(Math.min(liquidsLoaded * delta(), remaining), payload.build.liquids.currentAmount());
|
||||
float flow = Math.min(Math.min(liquidsLoaded * edelta(), remaining), payload.build.liquids.currentAmount());
|
||||
|
||||
liquids.add(liq, flow);
|
||||
payload.build.liquids.remove(liq, flow);
|
||||
|
||||
@@ -24,6 +24,12 @@ public class LiquidVoid extends Block{
|
||||
}
|
||||
|
||||
public class LiquidVoidBuild extends Building{
|
||||
@Override
|
||||
public void placed(){
|
||||
super.placed();
|
||||
liquids.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptLiquid(Building source, Liquid liquid){
|
||||
return enabled;
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package mindustry.world.blocks.storage;
|
||||
|
||||
import arc.*;
|
||||
import arc.audio.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
@@ -29,6 +33,9 @@ import mindustry.world.modules.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class CoreBlock extends StorageBlock{
|
||||
protected static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f, cloudAlpha = 0.81f;
|
||||
protected static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f};
|
||||
|
||||
//hacky way to pass item modules between methods
|
||||
private static ItemModule nextItems;
|
||||
protected static final float[] thrusterSizes = {0f, 0f, 0f, 0f, 0.3f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0f};
|
||||
@@ -42,6 +49,12 @@ public class CoreBlock extends StorageBlock{
|
||||
public boolean incinerateNonBuildable = false;
|
||||
|
||||
public UnitType unitType = UnitTypes.alpha;
|
||||
public float landDuration = 160f;
|
||||
public Music landMusic = Musics.land;
|
||||
public Effect launchEffect = Fx.launch;
|
||||
|
||||
public Interp landZoomInterp = Interp.pow3;
|
||||
public float landZoomFrom = 0.02f, landZoomTo = 4f;
|
||||
|
||||
public float captureInvicibility = 60f * 15f;
|
||||
|
||||
@@ -217,11 +230,8 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
|
||||
public void drawLanding(CoreBuild build, float x, float y){
|
||||
float fout = renderer.getLandTime() / coreLandDuration;
|
||||
|
||||
if(renderer.isLaunching()) fout = 1f - fout;
|
||||
|
||||
float fin = 1f - fout;
|
||||
float fin = renderer.getLandTimeIn();
|
||||
float fout = 1f - fin;
|
||||
|
||||
float scl = Scl.scl(4f) / renderer.getDisplayScale();
|
||||
float shake = 0f;
|
||||
@@ -312,6 +322,17 @@ public class CoreBlock extends StorageBlock{
|
||||
public float iframes = -1f;
|
||||
public float thrusterTime = 0f;
|
||||
|
||||
protected float cloudSeed;
|
||||
|
||||
//utility methods for less Block-to-CoreBlock casts. also allows for more customization
|
||||
public float landDuration(){
|
||||
return landDuration;
|
||||
}
|
||||
|
||||
public Music landMusic(){
|
||||
return landMusic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
//draw thrusters when just landed
|
||||
@@ -331,6 +352,115 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
}
|
||||
|
||||
// `launchType` is null if it's landing instead of launching.
|
||||
public void beginLaunch(@Nullable CoreBlock launchType){
|
||||
cloudSeed = Mathf.random(1f);
|
||||
if(launchType != null){
|
||||
Fx.coreLaunchConstruct.at(x, y, launchType.size);
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
// Add fade-in and fade-out foreground when landing or launching.
|
||||
if(renderer.isLaunching()){
|
||||
float margin = 30f;
|
||||
|
||||
Image image = new Image();
|
||||
image.color.a = 0f;
|
||||
image.touchable = Touchable.disabled;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.delay((landDuration() - margin) / 60f), Actions.fadeIn(margin / 60f, Interp.pow2In), Actions.delay(6f / 60f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
ui.loadfrag.toFront();
|
||||
if(state.isMenu()){
|
||||
image.remove();
|
||||
}
|
||||
});
|
||||
Core.scene.add(image);
|
||||
}else{
|
||||
Image image = new Image();
|
||||
image.color.a = 1f;
|
||||
image.touchable = Touchable.disabled;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.fadeOut(35f / 60f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
ui.loadfrag.toFront();
|
||||
if(state.isMenu()){
|
||||
image.remove();
|
||||
}
|
||||
});
|
||||
Core.scene.add(image);
|
||||
|
||||
Time.run(landDuration(), () -> {
|
||||
launchEffect.at(this);
|
||||
Effect.shake(5f, 5f, this);
|
||||
thrusterTime = 1f;
|
||||
|
||||
if(state.isCampaign() && Vars.showSectorLandInfo && (state.rules.sector.preset == null || state.rules.sector.preset.showSectorLandInfo)){
|
||||
ui.announce("[accent]" + state.rules.sector.name() + "\n" +
|
||||
(state.rules.sector.info.resources.any() ? "[lightgray]" + Core.bundle.get("sectors.resources") + "[white] " +
|
||||
state.rules.sector.info.resources.toString(" ", UnlockableContent::emoji) : ""), 5);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void endLaunch(){}
|
||||
|
||||
public void drawLanding(CoreBlock block){
|
||||
var clouds = Core.assets.get("sprites/clouds.png", Texture.class);
|
||||
|
||||
float fin = renderer.getLandTimeIn();
|
||||
float cameraScl = renderer.getDisplayScale();
|
||||
|
||||
float fout = 1f - fin;
|
||||
float scl = Scl.scl(4f) / cameraScl;
|
||||
float pfin = Interp.pow3Out.apply(fin), pf = Interp.pow2In.apply(fout);
|
||||
|
||||
//draw particles
|
||||
Draw.color(Pal.lightTrail);
|
||||
Angles.randLenVectors(1, pfin, 100, 800f * scl * pfin, (ax, ay, ffin, ffout) -> {
|
||||
Lines.stroke(scl * ffin * pf * 3f);
|
||||
Lines.lineAngle(x + ax, y + ay, Mathf.angle(ax, ay), (ffin * 20 + 1f) * scl);
|
||||
});
|
||||
Draw.color();
|
||||
|
||||
block.drawLanding(this, x, y);
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.white, Interp.pow5In.apply(fout));
|
||||
|
||||
//accent tint indicating that the core was just constructed
|
||||
if(renderer.isLaunching()){
|
||||
float f = Mathf.clamp(1f - fout * 12f);
|
||||
if(f > 0.001f){
|
||||
Draw.mixcol(Pal.accent, f);
|
||||
}
|
||||
}
|
||||
|
||||
//draw clouds
|
||||
if(state.rules.cloudColor.a > 0.0001f){
|
||||
float scaling = cloudScaling;
|
||||
float sscl = Math.max(1f + Mathf.clamp(fin + cfinOffset) * cfinScl, 0f) * cameraScl;
|
||||
|
||||
Tmp.tr1.set(clouds);
|
||||
Tmp.tr1.set(
|
||||
(Core.camera.position.x - Core.camera.width/2f * sscl) / scaling,
|
||||
(Core.camera.position.y - Core.camera.height/2f * sscl) / scaling,
|
||||
(Core.camera.position.x + Core.camera.width/2f * sscl) / scaling,
|
||||
(Core.camera.position.y + Core.camera.height/2f * sscl) / scaling);
|
||||
|
||||
Tmp.tr1.scroll(10f * cloudSeed, 10f * cloudSeed);
|
||||
|
||||
Draw.alpha(Mathf.sample(cloudAlphas, fin + calphaFinOffset) * cloudAlpha);
|
||||
Draw.mixcol(state.rules.cloudColor, state.rules.cloudColor.a);
|
||||
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, Core.camera.height);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void drawThrusters(float frame){
|
||||
float length = thrusterLength * (frame - 1f) - 1f/4f;
|
||||
for(int i = 0; i < 4; i++){
|
||||
@@ -409,9 +539,19 @@ public class CoreBlock extends StorageBlock{
|
||||
thrusterTime -= Time.delta/90f;
|
||||
}
|
||||
|
||||
/** @return Camera zoom while landing or launching. May optionally do other things such as setting camera position to itself. */
|
||||
public float zoomLaunching(){
|
||||
Core.camera.position.set(this);
|
||||
return landZoomInterp.apply(Scl.scl(landZoomFrom), Scl.scl(landZoomTo), renderer.getLandTimeIn());
|
||||
}
|
||||
|
||||
public void updateLaunching(){
|
||||
updateLandParticles();
|
||||
}
|
||||
|
||||
public void updateLandParticles(){
|
||||
float time = renderer.isLaunching() ? coreLandDuration - renderer.getLandTime() : renderer.getLandTime();
|
||||
float tsize = Mathf.sample(thrusterSizes, (time + 35f) / coreLandDuration);
|
||||
float in = renderer.getLandTimeIn() * landDuration();
|
||||
float tsize = Mathf.sample(thrusterSizes, (in + 35f) / landDuration());
|
||||
|
||||
renderer.setLandPTimer(renderer.getLandPTimer() + tsize * Time.delta);
|
||||
if(renderer.getLandTime() >= 1f){
|
||||
|
||||
@@ -16,6 +16,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -147,11 +148,16 @@ public class RepairTurret extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
public class RepairPointBuild extends Building implements Ranged{
|
||||
public class RepairPointBuild extends Building implements Ranged, RotBlock{
|
||||
public Unit target;
|
||||
public Vec2 offset = new Vec2(), lastEnd = new Vec2();
|
||||
public float strength, rotation = 90;
|
||||
|
||||
@Override
|
||||
public float buildRotation(){
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(baseRegion, x, y);
|
||||
|
||||
@@ -23,6 +23,8 @@ import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.ConstructBlock.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.units.UnitAssemblerModule.*;
|
||||
import mindustry.world.consumers.*;
|
||||
@@ -85,7 +87,9 @@ public class UnitAssembler extends PayloadBlock{
|
||||
public boolean canPlaceOn(Tile tile, Team team, int rotation){
|
||||
//overlapping construction areas not allowed; grow by a tiny amount so edges can't overlap either.
|
||||
Rect rect = getRect(Tmp.r1, tile.worldx() + offset, tile.worldy() + offset, rotation).grow(0.1f);
|
||||
return !indexer.getFlagged(team, BlockFlag.unitAssembler).contains(b -> getRect(Tmp.r2, b.x, b.y, b.rotation).overlaps(rect));
|
||||
return
|
||||
!indexer.getFlagged(team, BlockFlag.unitAssembler).contains(b -> getRect(Tmp.r2, b.x, b.y, b.rotation).overlaps(rect)) &&
|
||||
!team.data().getBuildings(ConstructBlock.get(size)).contains(b -> ((ConstructBuild)b).current instanceof UnitAssembler && getRect(Tmp.r2, b.x, b.y, b.rotation).overlaps(rect));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -137,7 +137,7 @@ public class UnitFactory extends UnitBlock{
|
||||
}
|
||||
|
||||
ItemStack stack = plan.requirements[i];
|
||||
req.add(new ItemDisplay(stack.item, stack.amount, false)).pad(5);
|
||||
req.add(new ItemDisplay(stack.item, stack.amount, plan.time, true)).pad(5);
|
||||
}
|
||||
}).right().grow().pad(10f);
|
||||
}else{
|
||||
|
||||
@@ -7,12 +7,19 @@ import mindustry.world.meta.*;
|
||||
/** A power consumer that uses a dynamic amount of power. */
|
||||
public class ConsumePowerDynamic extends ConsumePower{
|
||||
private final Floatf<Building> usage;
|
||||
private float displayedPowerUsage;
|
||||
|
||||
public ConsumePowerDynamic(Floatf<Building> usage){
|
||||
super(0, 0, false);
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
public ConsumePowerDynamic(float displayed, Floatf<Building> usage){
|
||||
super(0, 0, false);
|
||||
this.displayedPowerUsage = displayed;
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float requestedPower(Building entity){
|
||||
return usage.get(entity);
|
||||
@@ -20,6 +27,8 @@ public class ConsumePowerDynamic extends ConsumePower{
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
|
||||
if(displayedPowerUsage != 0f){
|
||||
stats.add(Stat.powerUse, displayedPowerUsage * 60f, StatUnit.powerSecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,17 +374,23 @@ public class StatValues{
|
||||
public static StatValue abilities(Seq<Ability> abilities){
|
||||
return table -> {
|
||||
table.row();
|
||||
table.table(t -> abilities.each(ability -> {
|
||||
if(ability.display){
|
||||
t.row();
|
||||
t.table(Styles.grayPanel, a -> {
|
||||
a.add("[accent]" + ability.localized()).padBottom(4);
|
||||
a.row();
|
||||
a.left().top().defaults().left();
|
||||
ability.addStats(a);
|
||||
}).pad(5).margin(10).growX();
|
||||
}
|
||||
}));
|
||||
table.table(t -> {
|
||||
int count = 0;
|
||||
for(Ability ability : abilities){
|
||||
if(ability.display){
|
||||
t.table(Styles.grayPanel, a -> {
|
||||
a.add("[accent]" + ability.localized()).padBottom(4).center().top().expandX();
|
||||
a.row();
|
||||
a.left().top().defaults().left();
|
||||
ability.addStats(a);
|
||||
}).pad(5).margin(10).growX().top().uniformX();
|
||||
if((++count) == 2){
|
||||
count = 0;
|
||||
t.row();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -496,7 +502,7 @@ public class StatValues{
|
||||
}
|
||||
|
||||
if(type.status != StatusEffects.none){
|
||||
sep(bt, (type.status.minfo.mod == null ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" + ((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds")));
|
||||
sep(bt, (type.status.hasEmoji() ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" + ((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds")));
|
||||
}
|
||||
|
||||
if(type.intervalBullet != null){
|
||||
@@ -554,4 +560,4 @@ public class StatValues{
|
||||
private static TextureRegion icon(UnlockableContent t){
|
||||
return t.uiIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user