Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
Conflicts: core/src/mindustry/content/Blocks.java
This commit is contained in:
@@ -27,8 +27,6 @@ public class BlockIndexer{
|
||||
private static final Rect rect = new Rect();
|
||||
private static boolean returnBool = false;
|
||||
|
||||
private final IntSet intSet = new IntSet();
|
||||
|
||||
private int quadWidth, quadHeight;
|
||||
|
||||
/** Stores all ore quadrants on the map. Maps ID to qX to qY to a list of tiles with that ore. */
|
||||
@@ -41,9 +39,9 @@ public class BlockIndexer{
|
||||
private Seq<Team> activeTeams = new Seq<>(Team.class);
|
||||
/** Maps teams to a map of flagged tiles by flag. */
|
||||
private TileArray[][] flagMap = new TileArray[Team.all.length][BlockFlag.all.length];
|
||||
/** Counts whether a certain floor is present in the world upon load. */
|
||||
private boolean[] blocksPresent;
|
||||
|
||||
/** Empty set used for returning. */
|
||||
private TileArray emptySet = new TileArray();
|
||||
/** Array used for returning and reusing. */
|
||||
private Seq<Tile> returnArray = new Seq<>();
|
||||
/** Array used for returning and reusing. */
|
||||
@@ -74,6 +72,7 @@ public class BlockIndexer{
|
||||
ores = new IntSeq[content.items().size][][];
|
||||
quadWidth = Mathf.ceil(world.width() / (float)quadrantSize);
|
||||
quadHeight = Mathf.ceil(world.height() / (float)quadrantSize);
|
||||
blocksPresent = new boolean[content.blocks().size];
|
||||
|
||||
for(Tile tile : world.tiles){
|
||||
process(tile);
|
||||
@@ -153,6 +152,12 @@ public class BlockIndexer{
|
||||
seq.removeValue(pos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @return whether a certain block is anywhere on this map. */
|
||||
public boolean isBlockPresent(Block block){
|
||||
return blocksPresent != null && blocksPresent[block.id];
|
||||
}
|
||||
|
||||
private TileArray[] getFlagged(Team team){
|
||||
@@ -383,6 +388,13 @@ public class BlockIndexer{
|
||||
}
|
||||
data.buildings.insert(tile.build);
|
||||
}
|
||||
|
||||
if(!tile.block().isStatic()){
|
||||
blocksPresent[tile.floorID()] = true;
|
||||
blocksPresent[tile.overlayID()] = true;
|
||||
}
|
||||
//bounds checks only needed in very specific scenarios
|
||||
if(tile.blockID() < blocksPresent.length) blocksPresent[tile.blockID()] = true;
|
||||
}
|
||||
|
||||
public static class TileArray implements Iterable<Tile>{
|
||||
|
||||
@@ -156,7 +156,7 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam)){
|
||||
for(Building core : state.teams.get(state.rules.waveTeam).cores){
|
||||
for(Building core : state.rules.waveTeam.data().cores){
|
||||
cons.get(core.x, core.y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,4 @@ public class MinerAI extends AIController{
|
||||
circle(core, unit.type.range / 1.8f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateTargeting(){
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ public class SoundControl{
|
||||
|
||||
/** Whether to play dark music.*/
|
||||
protected boolean isDark(){
|
||||
if(state.teams.get(player.team()).hasCore() && state.teams.get(player.team()).core().healthf() < 0.85f){
|
||||
if(player.team().data().hasCore() && player.team().data().core().healthf() < 0.85f){
|
||||
//core damaged -> dark
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Blocks implements ContentList{
|
||||
regolithWall, yellowStoneWall, rhyoliteWall, carbonWall,
|
||||
graphiticStone,
|
||||
iceSnow, sandWater, darksandWater, duneWall, sandWall, moss, sporeMoss, shale, shaleWall, shaleBoulder, sandBoulder, daciteBoulder, boulder, snowBoulder, basaltBoulder, grass, salt,
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall,
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor4, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall,
|
||||
darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal,
|
||||
pebbles, tendrils,
|
||||
|
||||
@@ -493,32 +493,24 @@ public class Blocks implements ContentList{
|
||||
wall = sporeWall;
|
||||
}};
|
||||
|
||||
metalFloor = new Floor("metal-floor"){{
|
||||
metalFloor = new MetalFloor("metal-floor"){{
|
||||
variants = 0;
|
||||
attributes.set(Attribute.water, -1f);
|
||||
}};
|
||||
|
||||
metalFloorDamaged = new Floor("metal-floor-damaged"){{
|
||||
variants = 3;
|
||||
}};
|
||||
metalFloorDamaged = new MetalFloor("metal-floor-damaged", 3);
|
||||
|
||||
metalFloor2 = new Floor("metal-floor-2"){{
|
||||
variants = 0;
|
||||
}};
|
||||
metalFloor2 = new MetalFloor("metal-floor-2");
|
||||
metalFloor3 = new MetalFloor("metal-floor-3");
|
||||
metalFloor4 = new MetalFloor("metal-floor-4");
|
||||
metalFloor5 = new MetalFloor("metal-floor-5");
|
||||
|
||||
metalFloor3 = new Floor("metal-floor-3"){{
|
||||
variants = 0;
|
||||
}};
|
||||
|
||||
metalFloor5 = new Floor("metal-floor-5"){{
|
||||
variants = 0;
|
||||
}};
|
||||
|
||||
darkPanel1 = new Floor("dark-panel-1"){{ variants = 0; }};
|
||||
darkPanel2 = new Floor("dark-panel-2"){{ variants = 0; }};
|
||||
darkPanel3 = new Floor("dark-panel-3"){{ variants = 0; }};
|
||||
darkPanel4 = new Floor("dark-panel-4"){{ variants = 0; }};
|
||||
darkPanel5 = new Floor("dark-panel-5"){{ variants = 0; }};
|
||||
darkPanel6 = new Floor("dark-panel-6"){{ variants = 0; }};
|
||||
darkPanel1 = new MetalFloor("dark-panel-1");
|
||||
darkPanel2 = new MetalFloor("dark-panel-2");
|
||||
darkPanel3 = new MetalFloor("dark-panel-3");
|
||||
darkPanel4 = new MetalFloor("dark-panel-4");
|
||||
darkPanel5 = new MetalFloor("dark-panel-5");
|
||||
darkPanel6 = new MetalFloor("dark-panel-6");
|
||||
|
||||
darkMetal = new StaticWall("dark-metal");
|
||||
|
||||
@@ -1773,8 +1765,8 @@ public class Blocks implements ContentList{
|
||||
);
|
||||
|
||||
size = 2;
|
||||
range = 180f;
|
||||
reloadTime = 38f;
|
||||
range = 190f;
|
||||
reloadTime = 34f;
|
||||
restitution = 0.03f;
|
||||
ammoEjectBack = 3f;
|
||||
cooldown = 0.03f;
|
||||
@@ -2013,7 +2005,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
health = 200 * size * size;
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.5f)).update(false);
|
||||
consumes.add(new ConsumeCoolant(0.5f)).update(false);
|
||||
}};
|
||||
|
||||
//endregion
|
||||
@@ -2191,10 +2183,11 @@ public class Blocks implements ContentList{
|
||||
payloadPropulsionTower = new PayloadMassDriver("payload-propulsion-tower"){{
|
||||
requirements(Category.units, with(Items.thorium, 300, Items.silicon, 200, Items.plastanium, 200, Items.phaseFabric, 50));
|
||||
size = 5;
|
||||
reloadTime = 150f;
|
||||
reloadTime = 140f;
|
||||
chargeTime = 100f;
|
||||
range = 300f;
|
||||
consumes.power(10f);
|
||||
range = 500f;
|
||||
maxPayloadSize = 3.5f;
|
||||
consumes.power(6f);
|
||||
}};
|
||||
|
||||
//endregion
|
||||
|
||||
@@ -1231,10 +1231,10 @@ public class Fx{
|
||||
});
|
||||
}),
|
||||
|
||||
shootLiquid = new Effect(40f, 80f, e -> {
|
||||
color(e.color, Color.white, e.fout() / 6f + Mathf.randomSeedRange(e.id, 0.1f));
|
||||
shootLiquid = new Effect(15f, 80f, e -> {
|
||||
color(e.color);
|
||||
|
||||
randLenVectors(e.id, 6, e.finpow() * 60f, e.rotation, 11f, (x, y) -> {
|
||||
randLenVectors(e.id, 2, e.finpow() * 15f, e.rotation, 11f, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, 0.5f + e.fout() * 2.5f);
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -709,7 +709,7 @@ public class TechTree implements ContentList{
|
||||
/** Item requirements for this content. */
|
||||
public ItemStack[] requirements;
|
||||
/** Requirements that have been fulfilled. Always the same length as the requirement array. */
|
||||
public final ItemStack[] finishedRequirements;
|
||||
public ItemStack[] finishedRequirements;
|
||||
/** Extra objectives needed to research this. */
|
||||
public Seq<Objective> objectives = new Seq<>();
|
||||
/** Nodes that depend on this node. */
|
||||
@@ -720,14 +720,8 @@ public class TechTree implements ContentList{
|
||||
|
||||
this.parent = parent;
|
||||
this.content = content;
|
||||
this.requirements = requirements;
|
||||
this.depth = parent == null ? 0 : parent.depth + 1;
|
||||
this.finishedRequirements = new ItemStack[requirements.length];
|
||||
|
||||
//load up the requirements that have been finished if settings are available
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
finishedRequirements[i] = new ItemStack(requirements[i].item, Core.settings == null ? 0 : Core.settings.getInt("req-" + content.name + "-" + requirements[i].item.name));
|
||||
}
|
||||
setupRequirements(requirements);
|
||||
|
||||
var used = new ObjectSet<Content>();
|
||||
|
||||
@@ -742,6 +736,16 @@ public class TechTree implements ContentList{
|
||||
all.add(this);
|
||||
}
|
||||
|
||||
public void setupRequirements(ItemStack[] requirements){
|
||||
this.requirements = requirements;
|
||||
this.finishedRequirements = new ItemStack[requirements.length];
|
||||
|
||||
//load up the requirements that have been finished if settings are available
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
finishedRequirements[i] = new ItemStack(requirements[i].item, Core.settings == null ? 0 : Core.settings.getInt("req-" + content.name + "-" + requirements[i].item.name));
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets finished requirements and saves. */
|
||||
public void reset(){
|
||||
for(ItemStack stack : finishedRequirements){
|
||||
|
||||
@@ -611,7 +611,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
bullet = new LiquidBulletType(Liquids.slag){{
|
||||
damage = 11;
|
||||
speed = 2.3f;
|
||||
speed = 2.4f;
|
||||
drag = 0.01f;
|
||||
shootEffect = Fx.shootSmall;
|
||||
lifetime = 56f;
|
||||
|
||||
@@ -117,5 +117,4 @@ public class Weathers implements ContentList{
|
||||
baseSpeed = 0.03f;
|
||||
}};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -247,12 +247,12 @@ public class Control implements ApplicationListener, Loadable{
|
||||
saves.load();
|
||||
}
|
||||
|
||||
/** Automatically unlocks things with no requirements. */
|
||||
/** Automatically unlocks things with no requirements and no locked parents. */
|
||||
void checkAutoUnlocks(){
|
||||
if(net.client()) return;
|
||||
|
||||
for(TechNode node : TechTree.all){
|
||||
if(!node.content.unlocked() && node.requirements.length == 0 && !node.objectives.contains(o -> !o.complete())){
|
||||
if(!node.content.unlocked() && (node.parent == null || node.parent.content.unlocked()) && node.requirements.length == 0 && !node.objectives.contains(o -> !o.complete())){
|
||||
node.content.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
Events.on(BlockBuildEndEvent.class, event -> {
|
||||
if(!event.breaking){
|
||||
TeamData data = state.teams.get(event.team);
|
||||
TeamData data = event.team.data();
|
||||
Iterator<BlockPlan> it = data.blocks.iterator();
|
||||
while(it.hasNext()){
|
||||
BlockPlan b = it.next();
|
||||
|
||||
@@ -21,7 +21,6 @@ import mindustry.gen.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
@@ -160,6 +159,18 @@ public class NetClient implements ApplicationListener{
|
||||
clientPacketReliable(type, contents);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void effect(Effect effect, float x, float y, float rotation, Color color){
|
||||
if(effect == null) return;
|
||||
|
||||
effect.at(x, y, rotation, color);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void effectReliable(Effect effect, float x, float y, float rotation, Color color){
|
||||
effect(effect, x, y, rotation, color);
|
||||
}
|
||||
|
||||
//called on all clients
|
||||
@Remote(targets = Loc.server, variants = Variant.both)
|
||||
public static void sendMessage(String message, String sender, Player playersender){
|
||||
@@ -314,78 +325,6 @@ public class NetClient implements ApplicationListener{
|
||||
ui.loadfrag.hide();
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void setHudText(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.hudfrag.setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void hideHudText(){
|
||||
ui.hudfrag.toggleHudText(false);
|
||||
}
|
||||
|
||||
/** TCP version */
|
||||
@Remote(variants = Variant.both)
|
||||
public static void setHudTextReliable(String message){
|
||||
setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void announce(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.announce(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoMessage(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showText("", message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoPopup(String message, float duration, int align, int top, int left, int bottom, int right){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showInfoPopup(message, duration, align, top, left, bottom, right);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void label(String message, float duration, float worldx, float worldy){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showLabel(message, duration, worldx, worldy);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void effect(Effect effect, float x, float y, float rotation, Color color){
|
||||
if(effect == null) return;
|
||||
|
||||
effect.at(x, y, rotation, color);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void effectReliable(Effect effect, float x, float y, float rotation, Color color){
|
||||
effect(effect, x, y, rotation, color);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoToast(String message, float duration){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showInfoToast(message, duration);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void warningToast(int unicode, String text){
|
||||
if(text == null || Fonts.icon.getData().getGlyph((char)unicode) == null) return;
|
||||
|
||||
ui.hudfrag.showToast(Fonts.getGlyph(Fonts.icon, (char)unicode), text);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void setRules(Rules rules){
|
||||
state.rules = rules;
|
||||
@@ -518,7 +457,7 @@ public class NetClient implements ApplicationListener{
|
||||
int teams = input.readUnsignedByte();
|
||||
for(int i = 0; i < teams; i++){
|
||||
int team = input.readUnsignedByte();
|
||||
TeamData data = state.teams.get(Team.all[team]);
|
||||
TeamData data = Team.all[team].data();
|
||||
if(data.cores.any()){
|
||||
data.cores.first().items.read(dataReads);
|
||||
}else{
|
||||
|
||||
@@ -335,7 +335,7 @@ public class NetServer implements ApplicationListener{
|
||||
votes += d;
|
||||
voted.addAll(player.uuid(), admins.getInfo(player.uuid()).lastIP);
|
||||
|
||||
Call.sendMessage(Strings.format("[lightgray]@[lightgray] has voted on kicking[orange] @[].[accent] (@/@)\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
|
||||
Call.sendMessage(Strings.format("[lightgray]@[lightgray] has voted on kicking[orange] @[lightgray].[accent] (@/@)\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
|
||||
player.name, target.name, votes, votesRequired()));
|
||||
|
||||
checkPass();
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Net.*;
|
||||
@@ -20,9 +21,29 @@ import static mindustry.Vars.*;
|
||||
|
||||
public interface Platform{
|
||||
|
||||
/** Dynamically creates a class loader for a jar file. */
|
||||
/** Dynamically creates a class loader for a jar file. This loader must be child-first. */
|
||||
default ClassLoader loadJar(Fi jar, ClassLoader parent) throws Exception{
|
||||
return new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, parent);
|
||||
return new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, parent){
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
|
||||
//check for loaded state
|
||||
Class<?> loadedClass = findLoadedClass(name);
|
||||
if(loadedClass == null){
|
||||
try{
|
||||
//try to load own class first
|
||||
loadedClass = findClass(name);
|
||||
}catch(ClassNotFoundException e){
|
||||
//use parent if not found
|
||||
loadedClass = super.loadClass(name, resolve);
|
||||
}
|
||||
}
|
||||
|
||||
if(resolve){
|
||||
resolveClass(loadedClass);
|
||||
}
|
||||
return loadedClass;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Steam: Update lobby visibility.*/
|
||||
@@ -64,6 +85,9 @@ public interface Platform{
|
||||
protected Context makeContext(){
|
||||
Context ctx = super.makeContext();
|
||||
ctx.setClassShutter(Scripts::allowClass);
|
||||
if(Vars.mods != null){
|
||||
ctx.setApplicationClassLoader(Vars.mods.mainLoader());
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -201,6 +201,7 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
Draw.proj(camera);
|
||||
|
||||
blocks.checkChanges();
|
||||
blocks.floor.checkChanges();
|
||||
blocks.processBlocks();
|
||||
|
||||
|
||||
@@ -539,6 +539,38 @@ public class UI implements ApplicationListener, Loadable{
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/** Shows a menu that fires a callback when an option is selected. If nothing is selected, -1 is returned. */
|
||||
public void showMenu(String title, String message, String[][] options, Intc callback){
|
||||
new Dialog(title){{
|
||||
cont.row();
|
||||
cont.image().width(400f).pad(2).colspan(2).height(4f).color(Pal.accent);
|
||||
cont.row();
|
||||
cont.add(message).width(400f).wrap().get().setAlignment(Align.center);
|
||||
cont.row();
|
||||
|
||||
int option = 0;
|
||||
for(var optionsRow : options){
|
||||
Table buttonRow = buttons.row().table().get().row();
|
||||
int fullWidth = 400 - (optionsRow.length - 1) * 8; // adjust to count padding as well
|
||||
int width = fullWidth / optionsRow.length;
|
||||
int lastWidth = fullWidth - width * (optionsRow.length - 1); // take the rest of space for uneven table
|
||||
|
||||
for(int i = 0; i < optionsRow.length; i++){
|
||||
if(optionsRow[i] == null) continue;
|
||||
|
||||
String optionName = optionsRow[i];
|
||||
int finalOption = option;
|
||||
buttonRow.button(optionName, () -> {
|
||||
callback.get(finalOption);
|
||||
hide();
|
||||
}).size(i == optionsRow.length - 1 ? lastWidth : width, 50).pad(4);
|
||||
option++;
|
||||
}
|
||||
}
|
||||
closeOnBack(() -> callback.get(-1));
|
||||
}}.show();
|
||||
}
|
||||
|
||||
public static String formatTime(float ticks){
|
||||
int time = (int)(ticks / 60);
|
||||
if(time < 60) return "0:" + (time < 10 ? "0" : "") + time;
|
||||
|
||||
@@ -6,7 +6,6 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.math.geom.Geometry.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectIntMap.*;
|
||||
import arc.util.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.content.*;
|
||||
@@ -21,7 +20,6 @@ import mindustry.maps.*;
|
||||
import mindustry.maps.filters.*;
|
||||
import mindustry.maps.filters.GenerateFilter.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.type.Weather.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.legacy.*;
|
||||
@@ -336,7 +334,7 @@ public class World{
|
||||
ui.showErrorMessage("@map.nospawn.pvp");
|
||||
}
|
||||
}else if(checkRules.attackMode){ //attack maps need two cores to be valid
|
||||
invalidMap = state.teams.get(state.rules.waveTeam).noCores();
|
||||
invalidMap = state.rules.waveTeam.data().noCores();
|
||||
if(invalidMap){
|
||||
ui.showErrorMessage("@map.nospawn.attack");
|
||||
}
|
||||
|
||||
@@ -241,14 +241,14 @@ public class MapView extends Element implements GestureListener{
|
||||
|
||||
image.setImageSize(editor.width(), editor.height());
|
||||
|
||||
if(!ScissorStack.push(rect.set(x, y + Core.scene.marginBottom, width, height))){
|
||||
if(!ScissorStack.push(rect.set(x + Core.scene.marginLeft, y + Core.scene.marginBottom, width, height))){
|
||||
return;
|
||||
}
|
||||
|
||||
Draw.color(Pal.remove);
|
||||
Lines.stroke(2f);
|
||||
Lines.rect(centerx - sclwidth / 2 - 1, centery - sclheight / 2 - 1, sclwidth + 2, sclheight + 2);
|
||||
editor.renderer.draw(centerx - sclwidth / 2, centery - sclheight / 2 + Core.scene.marginBottom, sclwidth, sclheight);
|
||||
editor.renderer.draw(centerx - sclwidth / 2 + Core.scene.marginLeft, centery - sclheight / 2 + Core.scene.marginBottom, sclwidth, sclheight);
|
||||
Draw.reset();
|
||||
|
||||
if(grid){
|
||||
|
||||
@@ -31,11 +31,16 @@ public class Damage{
|
||||
|
||||
/** Creates a dynamic explosion based on specified parameters. */
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage){
|
||||
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, true, null);
|
||||
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, true, null, Fx.dynamicExplosion);
|
||||
}
|
||||
|
||||
/** Creates a dynamic explosion based on specified parameters. */
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam){
|
||||
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, fire, ignoreTeam, Fx.dynamicExplosion);
|
||||
}
|
||||
|
||||
/** Creates a dynamic explosion based on specified parameters. */
|
||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam, Effect explosion){
|
||||
if(damage){
|
||||
for(int i = 0; i < Mathf.clamp(power / 700, 0, 8); i++){
|
||||
int length = 5 + Mathf.clamp((int)(power / 500), 1, 20);
|
||||
@@ -69,7 +74,7 @@ public class Damage{
|
||||
|
||||
float shake = Math.min(explosiveness / 4f + 3f, 9f);
|
||||
Effect.shake(shake, shake, x, y);
|
||||
Fx.dynamicExplosion.at(x, y, radius / 8f);
|
||||
explosion.at(x, y, radius / 8f);
|
||||
}
|
||||
|
||||
public static void createIncend(float x, float y, float range, int amount){
|
||||
|
||||
@@ -163,6 +163,8 @@ public class BulletType extends Content implements Cloneable{
|
||||
public float puddleAmount = 5f;
|
||||
public Liquid puddleLiquid = Liquids.water;
|
||||
|
||||
public boolean displayAmmoMultiplier = true;
|
||||
|
||||
public float lightRadius = -1f;
|
||||
public float lightOpacity = 0.3f;
|
||||
public Color lightColor = Pal.powerLight;
|
||||
@@ -405,10 +407,10 @@ public class BulletType extends Content implements Cloneable{
|
||||
if(status == StatusEffects.none){
|
||||
status = StatusEffects.shocked;
|
||||
}
|
||||
}
|
||||
|
||||
if(lightningType == null){
|
||||
lightningType = !collidesAir ? Bullets.damageLightningGround : Bullets.damageLightning;
|
||||
}
|
||||
if(lightningType == null){
|
||||
lightningType = !collidesAir ? Bullets.damageLightningGround : Bullets.damageLightning;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ public class LiquidBulletType extends BulletType{
|
||||
shootEffect = Fx.none;
|
||||
drag = 0.001f;
|
||||
knockback = 0.55f;
|
||||
displayAmmoMultiplier = false;
|
||||
}
|
||||
|
||||
public LiquidBulletType(){
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@@ -12,7 +11,6 @@ abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
|
||||
static final float warpDst = 40f;
|
||||
|
||||
@Import float x, y;
|
||||
@Import Vec2 vel;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
@@ -222,7 +222,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
}
|
||||
|
||||
TeamData data = state.teams.get(team);
|
||||
TeamData data = team.data();
|
||||
|
||||
if(checkPrevious){
|
||||
//remove existing blocks that have been placed here.
|
||||
@@ -1399,7 +1399,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
return switch(sensor){
|
||||
case type -> block;
|
||||
case firstItem -> items == null ? null : items.first();
|
||||
case config -> block.configurations.containsKey(Item.class) || block.configurations.containsKey(Liquid.class) ? config() : null;
|
||||
case config -> block.configSenseable() ? config() : null;
|
||||
case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type : getPayload() instanceof BuildPayload p2 ? p2.block() : null;
|
||||
default -> noSensed;
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
|
||||
return;
|
||||
}
|
||||
|
||||
if(time >= lifetime || tile == null){
|
||||
if(time >= lifetime || tile == null || Float.isNaN(lifetime)){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
transient float healTime;
|
||||
private transient float resupplyTime = Mathf.random(10f);
|
||||
private transient boolean wasPlayer;
|
||||
private transient float lastHealth;
|
||||
private transient boolean wasHealed;
|
||||
|
||||
public void moveAt(Vec2 vector){
|
||||
moveAt(vector, type.accel);
|
||||
@@ -320,16 +320,23 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
type.landed(self());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(float amount){
|
||||
if(health < maxHealth && amount > 0){
|
||||
wasHealed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
type.update(self());
|
||||
|
||||
if(health > lastHealth && lastHealth > 0 && healTime <= -1f){
|
||||
if(wasHealed && healTime <= -1f){
|
||||
healTime = 1f;
|
||||
}
|
||||
healTime -= Time.delta / 20f;
|
||||
lastHealth = health;
|
||||
wasHealed = false;
|
||||
|
||||
//check if environment is unsupported
|
||||
if(!type.supportsEnv(state.rules.environment) && !dead){
|
||||
@@ -450,7 +457,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
float power = item().charge * stack().amount * 150f;
|
||||
|
||||
if(!spawnedByCore){
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, power, bounds() / 2f, state.rules.damageExplosions, item().flammability > 1, team);
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, power, bounds() / 2f, state.rules.damageExplosions, item().flammability > 1, team, type.deathExplosionEffect);
|
||||
}
|
||||
|
||||
float shake = hitSize / 3f;
|
||||
|
||||
@@ -134,6 +134,18 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
/** Consider using Menus.registerMenu instead. */
|
||||
public static class MenuOptionChooseEvent{
|
||||
public final Player player;
|
||||
public final int menuId, option;
|
||||
|
||||
public MenuOptionChooseEvent(Player player, int menuId, int option){
|
||||
this.player = player;
|
||||
this.option = option;
|
||||
this.menuId = menuId;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerChatEvent{
|
||||
public final Player player;
|
||||
public final String message;
|
||||
|
||||
@@ -13,7 +13,6 @@ import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.power.*;
|
||||
|
||||
@@ -142,11 +141,21 @@ public class BlockRenderer{
|
||||
Tile other = world.tile(cx, cy);
|
||||
if(other != null){
|
||||
darkEvents.add(other.pos());
|
||||
floor.recacheTile(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkChanges(){
|
||||
darkEvents.each(pos -> {
|
||||
var tile = world.tile(pos);
|
||||
if(tile != null){
|
||||
tile.data = world.getWallDarkness(tile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void drawDarkness(){
|
||||
if(!darkEvents.isEmpty()){
|
||||
Draw.flush();
|
||||
@@ -156,10 +165,9 @@ public class BlockRenderer{
|
||||
|
||||
darkEvents.each(pos -> {
|
||||
var tile = world.tile(pos);
|
||||
tile.data = world.getWallDarkness(tile);
|
||||
float darkness = world.getDarkness(tile.x, tile.y);
|
||||
//then draw the shadow
|
||||
Draw.colorl(!tile.isDarkened() || darkness <= 0f ? 1f : 1f - Math.min((darkness + 0.5f) / 4f, 1f));
|
||||
Draw.colorl(darkness <= 0f ? 1f : 1f - Math.min((darkness + 0.5f) / 4f, 1f));
|
||||
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
|
||||
});
|
||||
|
||||
@@ -186,7 +194,7 @@ public class BlockRenderer{
|
||||
}
|
||||
|
||||
if(brokenFade > 0.001f){
|
||||
for(BlockPlan block : state.teams.get(player.team()).blocks){
|
||||
for(BlockPlan block : player.team().data().blocks){
|
||||
Block b = content.block(block.block);
|
||||
if(!camera.bounds(Tmp.r1).grow(tilesize * 2f).overlaps(Tmp.r2.setSize(b.size * tilesize).setCenter(block.x * tilesize + b.offset, block.y * tilesize + b.offset))) continue;
|
||||
|
||||
|
||||
@@ -97,7 +97,6 @@ public class FloorRenderer{
|
||||
|
||||
/** Queues up a cache change for a tile. Only runs in render loop. */
|
||||
public void recacheTile(Tile tile){
|
||||
//TODO will be faster it the position also specified the layer to be recached
|
||||
//recaching all layers may not be necessary
|
||||
recacheSet.add(Point2.pack(tile.x / chunksize, tile.y / chunksize));
|
||||
}
|
||||
@@ -168,7 +167,6 @@ public class FloorRenderer{
|
||||
shader.setUniformi("u_texture", 0);
|
||||
|
||||
//only ever use the base environment texture
|
||||
//TODO show error texture for anything else
|
||||
texture.bind(0);
|
||||
|
||||
//enable all mesh attributes
|
||||
|
||||
@@ -47,7 +47,6 @@ public class MenuRenderer implements Disposable{
|
||||
Simplex s1 = new Simplex(offset);
|
||||
Simplex s2 = new Simplex(offset + 1);
|
||||
Simplex s3 = new Simplex(offset + 2);
|
||||
RidgedPerlin rid = new RidgedPerlin(1 + offset, 1);
|
||||
Block[] selected = Structs.select(
|
||||
new Block[]{Blocks.sand, Blocks.sandWall},
|
||||
new Block[]{Blocks.shale, Blocks.shaleWall},
|
||||
@@ -141,7 +140,7 @@ public class MenuRenderer implements Disposable{
|
||||
}
|
||||
|
||||
if(tendrils){
|
||||
if(rid.getValue(x, y, 1f / 17f) > 0f){
|
||||
if(RidgedPerlin.noise2d(1 + offset, x, y, 1f / 17f) > 0f){
|
||||
floor = Mathf.chance(0.2) ? Blocks.sporeMoss : Blocks.moss;
|
||||
|
||||
if(wall != Blocks.air){
|
||||
|
||||
@@ -108,7 +108,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
if(build == null || build.items == null) return;
|
||||
build.items.set(item, amount);
|
||||
}
|
||||
|
||||
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void clearItems(Building build){
|
||||
if(build == null || build.items == null) return;
|
||||
@@ -131,22 +131,24 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
@Remote(called = Loc.both, targets = Loc.both, forward = true, unreliable = true)
|
||||
public static void deletePlans(Player player, int[] positions){
|
||||
if(netServer.admins.allowAction(player, ActionType.removePlanned, a -> a.plans = positions)){
|
||||
if(net.server() && !netServer.admins.allowAction(player, ActionType.removePlanned, a -> a.plans = positions)){
|
||||
throw new ValidateException(player, "Player cannot remove plans.");
|
||||
}
|
||||
|
||||
var it = state.teams.get(player.team()).blocks.iterator();
|
||||
//O(n^2) search here; no way around it
|
||||
outer:
|
||||
while(it.hasNext()){
|
||||
BlockPlan req = it.next();
|
||||
if(player == null) return;
|
||||
|
||||
for(int pos : positions){
|
||||
if(req.x == Point2.x(pos) && req.y == Point2.y(pos)){
|
||||
it.remove();
|
||||
continue outer;
|
||||
}
|
||||
var it = player.team().data().blocks.iterator();
|
||||
//O(n^2) search here; no way around it
|
||||
outer:
|
||||
while(it.hasNext()){
|
||||
BlockPlan req = it.next();
|
||||
|
||||
for(int pos : positions){
|
||||
if(req.x == Point2.x(pos) && req.y == Point2.y(pos)){
|
||||
it.remove();
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,7 +887,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
removed.clear();
|
||||
|
||||
//remove blocks to rebuild
|
||||
Iterator<BlockPlan> broken = state.teams.get(player.team()).blocks.iterator();
|
||||
Iterator<BlockPlan> broken = player.team().data().blocks.iterator();
|
||||
while(broken.hasNext()){
|
||||
BlockPlan req = broken.next();
|
||||
Block block = content.block(req.block);
|
||||
@@ -938,9 +940,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
//check if tapped block is configurable
|
||||
if(build.block.configurable && build.interactable(player.team())){
|
||||
consumed = true;
|
||||
if(((!frag.config.isShown() && build.shouldShowConfigure(player)) //if the config fragment is hidden, show
|
||||
if((!frag.config.isShown() && build.shouldShowConfigure(player)) //if the config fragment is hidden, show
|
||||
//alternatively, the current selected block can 'agree' to switch config tiles
|
||||
|| (frag.config.isShown() && frag.config.getSelectedTile().onConfigureTileTapped(build)))){
|
||||
|| (frag.config.isShown() && frag.config.getSelectedTile().onConfigureTileTapped(build))){
|
||||
Sounds.click.at(build);
|
||||
frag.config.showConfig(build);
|
||||
}
|
||||
|
||||
@@ -588,6 +588,30 @@ public class TypeIO{
|
||||
return new TraceInfo(readString(read), readString(read), read.b() == 1, read.b() == 1, read.i(), read.i());
|
||||
}
|
||||
|
||||
public static void writeStrings(Writes write, String[][] strings){
|
||||
write.b(strings.length);
|
||||
for(String[] string : strings){
|
||||
write.b(string.length);
|
||||
for(String s : string){
|
||||
writeString(write, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String[][] readStrings(Reads read){
|
||||
int rows = read.ub();
|
||||
|
||||
String[][] strings = new String[rows][];
|
||||
for(int i = 0; i < rows; i++){
|
||||
int columns = read.ub();
|
||||
strings[i] = new String[columns];
|
||||
for(int j = 0; j < columns; j++){
|
||||
strings[i][j] = readString(read);
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
public static void writeStringData(DataOutput buffer, String string) throws IOException{
|
||||
if(string != null){
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
@@ -55,6 +57,10 @@ public class GlobalConstants{
|
||||
for(LAccess sensor : LAccess.all){
|
||||
put("@" + sensor.name(), sensor);
|
||||
}
|
||||
|
||||
for(UnitCommand cmd : UnitCommand.all){
|
||||
put("@command" + Strings.capitalize(cmd.name()), cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return a constant ID > 0 if there is a constant with this name, otherwise -1. */
|
||||
|
||||
@@ -922,6 +922,7 @@ public class LExecutor{
|
||||
v.objval instanceof Content ? "[content]" :
|
||||
v.objval instanceof Building build ? build.block.name :
|
||||
v.objval instanceof Unit unit ? unit.type.name :
|
||||
v.objval instanceof Enum<?> e ? e.name() :
|
||||
"[object]";
|
||||
|
||||
exec.textBuffer.append(strValue);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mindustry.maps.filters;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -17,7 +16,7 @@ public class BlendFilter extends GenerateFilter{
|
||||
return Structs.arr(
|
||||
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f),
|
||||
new BlockOption("block", () -> block, b -> block = b, anyOptional),
|
||||
new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly),
|
||||
new BlockOption("floor", () -> floor, b -> floor = b, anyOptional),
|
||||
new BlockOption("ignore", () -> ignore, b -> ignore = b, floorsOptional)
|
||||
);
|
||||
}
|
||||
@@ -34,7 +33,7 @@ public class BlendFilter extends GenerateFilter{
|
||||
|
||||
@Override
|
||||
public void apply(){
|
||||
if(in.floor == block || block == Blocks.air || in.floor == ignore) return;
|
||||
if(in.floor == block || block == Blocks.air || in.floor == ignore || (!floor.isFloor() && (in.block == block || in.block == ignore))) return;
|
||||
|
||||
int rad = (int)radius;
|
||||
boolean found = false;
|
||||
@@ -42,7 +41,7 @@ public class BlendFilter extends GenerateFilter{
|
||||
outer:
|
||||
for(int x = -rad; x <= rad; x++){
|
||||
for(int y = -rad; y <= rad; y++){
|
||||
if(Mathf.within(x, y, rad)) continue;
|
||||
if(x*x + y*y > rad*rad) continue;
|
||||
Tile tile = in.tile(in.x + x, in.y + y);
|
||||
|
||||
if(tile.floor() == block || tile.block() == block || tile.overlay() == block){
|
||||
@@ -53,7 +52,11 @@ public class BlendFilter extends GenerateFilter{
|
||||
}
|
||||
|
||||
if(found){
|
||||
in.floor = floor;
|
||||
if(!floor.isFloor()){
|
||||
in.block = floor;
|
||||
}else{
|
||||
in.floor = floor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import arc.scene.ui.layout.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
@@ -105,6 +104,7 @@ public abstract class FilterOption{
|
||||
if(++i % 10 == 0) dialog.cont.row();
|
||||
}
|
||||
|
||||
dialog.closeOnBack();
|
||||
dialog.show();
|
||||
}).pad(4).margin(12f);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import mindustry.world.*;
|
||||
|
||||
public abstract class GenerateFilter{
|
||||
protected transient float o = (float)(Math.random() * 10000000.0);
|
||||
protected transient long seed;
|
||||
protected transient int seed;
|
||||
protected transient GenerateInput in;
|
||||
|
||||
public void apply(Tiles tiles, GenerateInput in){
|
||||
@@ -75,11 +75,16 @@ public abstract class GenerateFilter{
|
||||
/** draw any additional guides */
|
||||
public void draw(Image image){}
|
||||
|
||||
/** localized display name */
|
||||
public String name(){
|
||||
public String simpleName(){
|
||||
Class c = getClass();
|
||||
if(c.isAnonymousClass()) c = c.getSuperclass();
|
||||
return Core.bundle.get("filter." + c.getSimpleName().toLowerCase().replace("filter", ""), c.getSimpleName().replace("Filter", ""));
|
||||
return c.getSimpleName().toLowerCase().replace("filter", "");
|
||||
}
|
||||
|
||||
/** localized display name */
|
||||
public String name(){
|
||||
var s = simpleName();
|
||||
return Core.bundle.get("filter." + s);
|
||||
}
|
||||
|
||||
public char icon(){
|
||||
@@ -112,11 +117,15 @@ public abstract class GenerateFilter{
|
||||
}
|
||||
|
||||
protected float rnoise(float x, float y, float scl, float mag){
|
||||
return in.pnoise.getValue((int)(x + o), (int)(y + o), 1f / scl) * mag;
|
||||
return RidgedPerlin.noise2d(seed + 1, (int)(x + o), (int)(y + o), 1f / scl) * mag;
|
||||
}
|
||||
|
||||
protected float rnoise(float x, float y, int octaves, float scl, float falloff, float mag){
|
||||
return RidgedPerlin.noise2d(seed + 1, (int)(x + o), (int)(y + o), octaves, falloff, 1f / scl) * mag;
|
||||
}
|
||||
|
||||
protected float chance(){
|
||||
return Mathf.randomSeed(Pack.longInt(in.x, in.y + (int)seed));
|
||||
return Mathf.randomSeed(Pack.longInt(in.x, in.y + seed));
|
||||
}
|
||||
|
||||
/** an input for generating at a certain coordinate. should only be instantiated once. */
|
||||
@@ -129,7 +138,6 @@ public abstract class GenerateFilter{
|
||||
public Block floor, block, overlay;
|
||||
|
||||
Simplex noise = new Simplex();
|
||||
RidgedPerlin pnoise = new RidgedPerlin(0, 1);
|
||||
TileProvider buffer;
|
||||
|
||||
public void apply(int x, int y, Block block, Block floor, Block overlay){
|
||||
@@ -145,7 +153,6 @@ public abstract class GenerateFilter{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
noise.setSeed(filter.seed);
|
||||
pnoise.setSeed((int)(filter.seed + 1));
|
||||
}
|
||||
|
||||
Tile tile(float x, float y){
|
||||
|
||||
@@ -10,14 +10,15 @@ import mindustry.world.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MedianFilter extends GenerateFilter{
|
||||
private static final IntSeq blocks = new IntSeq(), floors = new IntSeq();
|
||||
|
||||
float radius = 2;
|
||||
float percentile = 0.5f;
|
||||
IntSeq blocks = new IntSeq(), floors = new IntSeq();
|
||||
|
||||
@Override
|
||||
public FilterOption[] options(){
|
||||
return Structs.arr(
|
||||
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
|
||||
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f),
|
||||
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,15 +8,17 @@ import mindustry.world.*;
|
||||
import static mindustry.maps.filters.FilterOption.*;
|
||||
|
||||
public class RiverNoiseFilter extends GenerateFilter{
|
||||
float scl = 40, threshold = 0f, threshold2 = 0.1f;
|
||||
float scl = 40, threshold = 0f, threshold2 = 0.1f, octaves = 1, falloff = 0.5f;
|
||||
Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandWall;
|
||||
|
||||
@Override
|
||||
public FilterOption[] options(){
|
||||
return Structs.arr(
|
||||
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f),
|
||||
new SliderOption("threshold", () -> threshold, f -> threshold = f, -1f, 0.3f),
|
||||
new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, -1f, 0.3f),
|
||||
new SliderOption("threshold", () -> threshold, f -> threshold = f, -1f, 1f),
|
||||
new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, -1f, 1f),
|
||||
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),
|
||||
new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f),
|
||||
new BlockOption("block", () -> block, b -> block = b, wallsOnly),
|
||||
new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly),
|
||||
new BlockOption("floor2", () -> floor2, b -> floor2 = b, floorsOnly)
|
||||
@@ -30,7 +32,7 @@ public class RiverNoiseFilter extends GenerateFilter{
|
||||
|
||||
@Override
|
||||
public void apply(){
|
||||
float noise = rnoise(in.x, in.y, scl, 1f);
|
||||
float noise = rnoise(in.x, in.y, (int)octaves, scl, falloff, 1f);
|
||||
|
||||
if(noise >= threshold){
|
||||
in.floor = floor;
|
||||
|
||||
@@ -18,7 +18,6 @@ import mindustry.world.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
RidgedPerlin rid = new RidgedPerlin(1, 2);
|
||||
BaseGenerator basegen = new BaseGenerator();
|
||||
float scl = 5f;
|
||||
float waterOffset = 0.07f;
|
||||
@@ -115,7 +114,7 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
tile.floor = getBlock(position);
|
||||
tile.block = tile.floor.asFloor().wall;
|
||||
|
||||
if(rid.getValue(position.x, position.y, position.z, 22) > 0.31){
|
||||
if(RidgedPerlin.noise3d(1, position.x, position.y, position.z, 2, 22) > 0.31){
|
||||
tile.block = Blocks.air;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +121,11 @@ public class ContentParser{
|
||||
return sound;
|
||||
});
|
||||
put(Objectives.Objective.class, (type, data) -> {
|
||||
if(data.isString()){
|
||||
var cont = locateAny(data.asString());
|
||||
if(cont == null) throw new IllegalArgumentException("Unknown objective content: " + data.asString());
|
||||
return new Research((UnlockableContent)cont);
|
||||
}
|
||||
var oc = resolve(data.getString("type", ""), SectorComplete.class);
|
||||
data.remove("type");
|
||||
Objectives.Objective obj = make(oc);
|
||||
@@ -228,6 +233,7 @@ public class ContentParser{
|
||||
case "item" -> block.consumes.item(find(ContentType.item, child.asString()));
|
||||
case "items" -> block.consumes.add((Consume)parser.readValue(ConsumeItems.class, child));
|
||||
case "liquid" -> block.consumes.add((Consume)parser.readValue(ConsumeLiquid.class, child));
|
||||
case "coolant" -> block.consumes.add((Consume)parser.readValue(ConsumeCoolant.class, child));
|
||||
case "power" -> {
|
||||
if(child.isNumber()){
|
||||
block.consumes.power(child.asFloat());
|
||||
@@ -543,6 +549,16 @@ public class ContentParser{
|
||||
return first != null ? first : Vars.content.getByName(type, currentMod.name + "-" + name);
|
||||
}
|
||||
|
||||
private <T extends MappableContent> T locateAny(String name){
|
||||
for(ContentType t : ContentType.all){
|
||||
var out = locate(t, name);
|
||||
if(out != null){
|
||||
return (T)out;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
<T> T make(Class<T> type){
|
||||
try{
|
||||
Constructor<T> cons = type.getDeclaredConstructor();
|
||||
@@ -679,18 +695,26 @@ public class ContentParser{
|
||||
lastNode.remove();
|
||||
}
|
||||
|
||||
TechNode node = new TechNode(null, unlock, customRequirements == null ? unlock.researchRequirements() : customRequirements);
|
||||
TechNode node = new TechNode(null, unlock, customRequirements == null ? ItemStack.empty : customRequirements);
|
||||
LoadedMod cur = currentMod;
|
||||
|
||||
postreads.add(() -> {
|
||||
currentContent = unlock;
|
||||
currentMod = cur;
|
||||
|
||||
//add custom objectives
|
||||
if(research.has("objectives")){
|
||||
node.objectives.addAll(parser.readValue(Objective[].class, research.get("objectives")));
|
||||
}
|
||||
|
||||
//remove old node from parent
|
||||
if(node.parent != null){
|
||||
node.parent.children.remove(node);
|
||||
}
|
||||
|
||||
if(customRequirements == null){
|
||||
node.setupRequirements(unlock.researchRequirements());
|
||||
}
|
||||
|
||||
//find parent node.
|
||||
TechNode parent = TechTree.all.find(t -> t.content.name.equals(researchName) || t.content.name.equals(currentMod.name + "-" + researchName));
|
||||
|
||||
@@ -11,25 +11,23 @@ public class ModClassLoader extends ClassLoader{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
|
||||
//always try the superclass first
|
||||
try{
|
||||
return super.loadClass(name, resolve);
|
||||
}catch(ClassNotFoundException error){
|
||||
//a child may try to delegate class loading to its parent, which is *this class loader* - do not let that happen
|
||||
if(inChild) throw error;
|
||||
int size = children.size;
|
||||
//if it doesn't exist in the main class loader, try all the children
|
||||
for(int i = 0; i < size; i++){
|
||||
try{
|
||||
inChild = true;
|
||||
var out = children.get(i).loadClass(name);
|
||||
inChild = false;
|
||||
return out;
|
||||
}catch(ClassNotFoundException ignored){
|
||||
}
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException{
|
||||
//a child may try to delegate class loading to its parent, which is *this class loader* - do not let that happen
|
||||
if(inChild) throw new ClassNotFoundException(name);
|
||||
|
||||
ClassNotFoundException last = null;
|
||||
int size = children.size;
|
||||
//if it doesn't exist in the main class loader, try all the children
|
||||
for(int i = 0; i < size; i++){
|
||||
try{
|
||||
inChild = true;
|
||||
var out = children.get(i).loadClass(name);
|
||||
inChild = false;
|
||||
return out;
|
||||
}catch(ClassNotFoundException e){
|
||||
last = e;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
throw (last == null ? new ClassNotFoundException(name) : last);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class Scripts implements Disposable{
|
||||
|
||||
public String runConsole(String text){
|
||||
try{
|
||||
Object o = context.evaluateString(scope, text, "console.js", 1, null);
|
||||
Object o = context.evaluateString(scope, text, "console.js", 1);
|
||||
if(o instanceof NativeJavaObject n) o = n.unwrap();
|
||||
if(o instanceof Undefined) o = "undefined";
|
||||
return String.valueOf(o);
|
||||
@@ -172,11 +172,11 @@ public class Scripts implements Disposable{
|
||||
try{
|
||||
if(currentMod != null){
|
||||
//inject script info into file
|
||||
context.evaluateString(scope, "modName = \"" + currentMod.name + "\"\nscriptName = \"" + file + "\"", "initscript.js", 1, null);
|
||||
context.evaluateString(scope, "modName = \"" + currentMod.name + "\"\nscriptName = \"" + file + "\"", "initscript.js", 1);
|
||||
}
|
||||
context.evaluateString(scope,
|
||||
wrap ? "(function(){'use strict';\n" + script + "\n})();" : script,
|
||||
file, 0, null);
|
||||
file, 0);
|
||||
return true;
|
||||
}catch(Throwable t){
|
||||
if(currentMod != null){
|
||||
@@ -224,7 +224,7 @@ public class Scripts implements Disposable{
|
||||
if(!module.exists() || module.isDirectory()) return null;
|
||||
return new ModuleSource(
|
||||
new InputStreamReader(new ByteArrayInputStream((module.readString()).getBytes())),
|
||||
null, new URI(moduleId), root.file().toURI(), validator);
|
||||
new URI(moduleId), root.file().toURI(), validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,24 +188,22 @@ public class ArcNetProvider implements NetProvider{
|
||||
|
||||
@Override
|
||||
public void pingHost(String address, int port, Cons<Host> valid, Cons<Exception> invalid){
|
||||
executor.submit(() -> {
|
||||
try{
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
long time = Time.millis();
|
||||
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
|
||||
socket.setSoTimeout(2000);
|
||||
try{
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
long time = Time.millis();
|
||||
socket.send(new DatagramPacket(new byte[]{-2, 1}, 2, InetAddress.getByName(address), port));
|
||||
socket.setSoTimeout(2000);
|
||||
|
||||
DatagramPacket packet = packetSupplier.get();
|
||||
socket.receive(packet);
|
||||
DatagramPacket packet = packetSupplier.get();
|
||||
socket.receive(packet);
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||
Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), packet.getAddress().getHostAddress(), buffer);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(packet.getData());
|
||||
Host host = NetworkIO.readServerData((int)Time.timeSinceMillis(time), packet.getAddress().getHostAddress(), buffer);
|
||||
|
||||
Core.app.post(() -> valid.get(host));
|
||||
}catch(Exception e){
|
||||
Core.app.post(() -> invalid.get(e));
|
||||
}
|
||||
});
|
||||
Core.app.post(() -> valid.get(host));
|
||||
}catch(Exception e){
|
||||
Core.app.post(() -> invalid.get(e));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.func.*;
|
||||
import arc.net.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.net.Streamable.*;
|
||||
@@ -12,6 +13,7 @@ import mindustry.net.Streamable.*;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static arc.util.Log.*;
|
||||
import static mindustry.Vars.*;
|
||||
@@ -31,6 +33,7 @@ public class Net{
|
||||
private final ObjectMap<Class<?>, Cons> clientListeners = new ObjectMap<>();
|
||||
private final ObjectMap<Class<?>, Cons2<NetConnection, Object>> serverListeners = new ObjectMap<>();
|
||||
private final IntMap<StreamBuilder> streams = new IntMap<>();
|
||||
private final ExecutorService pingExecutor = Threads.executor(Math.max(Runtime.getRuntime().availableProcessors(), 6));
|
||||
|
||||
private final NetProvider provider;
|
||||
|
||||
@@ -316,10 +319,17 @@ public class Net{
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings a host in an new thread. If an error occured, failed() should be called with the exception.
|
||||
* Pings a host in a pooled thread. If an error occurred, failed() should be called with the exception.
|
||||
*/
|
||||
public void pingHost(String address, int port, Cons<Host> valid, Cons<Exception> failed){
|
||||
provider.pingHost(address, port, valid, failed);
|
||||
pingExecutor.submit(() -> provider.pingHost(address, port, valid, failed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings a host in an new thread. If an error occurred, failed() should be called with the exception.
|
||||
*/
|
||||
public void pingHostThread(String address, int port, Cons<Host> valid, Cons<Exception> failed){
|
||||
Threads.daemon(() -> provider.pingHost(address, port, valid, failed));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,7 +377,7 @@ public class Net{
|
||||
*/
|
||||
void discoverServers(Cons<Host> callback, Runnable done);
|
||||
|
||||
/** Ping a host. If an error occurred, failed() should be called with the exception. */
|
||||
/** Ping a host. If an error occurred, failed() should be called with the exception. This method should block. */
|
||||
void pingHost(String address, int port, Cons<Host> valid, Cons<Exception> failed);
|
||||
|
||||
/** Host a server at specified port. */
|
||||
|
||||
@@ -71,6 +71,7 @@ public class UnitType extends UnlockableContent{
|
||||
public boolean omniMovement = true;
|
||||
public Effect fallEffect = Fx.fallSmoke;
|
||||
public Effect fallThrusterEffect = Fx.fallSmoke;
|
||||
public Effect deathExplosionEffect = Fx.dynamicExplosion;
|
||||
public Seq<Ability> abilities = new Seq<>();
|
||||
public BlockFlag targetFlag = BlockFlag.generator;
|
||||
|
||||
|
||||
102
core/src/mindustry/ui/Menus.java
Normal file
102
core/src/mindustry/ui/Menus.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package mindustry.ui;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Class for handling menus and notifications across the network. Unstable API! */
|
||||
public class Menus{
|
||||
private static IntMap<MenuListener> menuListeners = new IntMap<>();
|
||||
|
||||
/** Register a *global* menu listener. If no option is chosen, the option is returned as -1. */
|
||||
public static void registerMenu(int id, MenuListener listener){
|
||||
menuListeners.put(id, listener);
|
||||
}
|
||||
|
||||
//do not invoke any of the methods below directly, use Call
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void menu(int menuId, String title, String message, String[][] options){
|
||||
if(title == null) title = "";
|
||||
if(options == null) options = new String[0][0];
|
||||
|
||||
ui.showMenu(title, message, options, (option) -> Call.menuChoose(player, menuId, option));
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.both)
|
||||
public static void menuChoose(@Nullable Player player, int menuId, int option){
|
||||
if(player != null && menuListeners.containsKey(menuId)){
|
||||
Events.fire(new MenuOptionChooseEvent(player, menuId, option));
|
||||
menuListeners.get(menuId).get(player, option);
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void setHudText(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.hudfrag.setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void hideHudText(){
|
||||
ui.hudfrag.toggleHudText(false);
|
||||
}
|
||||
|
||||
/** TCP version */
|
||||
@Remote(variants = Variant.both)
|
||||
public static void setHudTextReliable(String message){
|
||||
setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void announce(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.announce(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoMessage(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showText("", message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoPopup(String message, float duration, int align, int top, int left, int bottom, int right){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showInfoPopup(message, duration, align, top, left, bottom, right);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void label(String message, float duration, float worldx, float worldy){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showLabel(message, duration, worldx, worldy);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoToast(String message, float duration){
|
||||
if(message == null) return;
|
||||
|
||||
ui.showInfoToast(message, duration);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void warningToast(int unicode, String text){
|
||||
if(text == null || Fonts.icon.getData().getGlyph((char)unicode) == null) return;
|
||||
|
||||
ui.hudfrag.showToast(Fonts.getGlyph(Fonts.icon, (char)unicode), text);
|
||||
}
|
||||
|
||||
public interface MenuListener{
|
||||
void get(Player player, int option);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public class ContentInfoDialog extends BaseDialog{
|
||||
for(Stat stat : map.keys()){
|
||||
table.table(inset -> {
|
||||
inset.left();
|
||||
inset.add("[lightgray]" + stat.localized() + ":[] ").left();
|
||||
inset.add("[lightgray]" + stat.localized() + ":[] ").left().top();
|
||||
Seq<StatValue> arr = map.get(stat);
|
||||
for(StatValue value : arr){
|
||||
value.display(inset);
|
||||
|
||||
@@ -364,7 +364,7 @@ public class JoinDialog extends BaseDialog{
|
||||
for(String address : group.addresses){
|
||||
String resaddress = address.contains(":") ? address.split(":")[0] : address;
|
||||
int resport = address.contains(":") ? Strings.parseInt(address.split(":")[1]) : port;
|
||||
net.pingHost(resaddress, resport, res -> {
|
||||
net.pingHostThread(resaddress, resport, res -> {
|
||||
if(refreshes != cur) return;
|
||||
res.port = resport;
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ public class ModsDialog extends BaseDialog{
|
||||
float w = Math.min(Core.graphics.getWidth() / 1.1f, 520f);
|
||||
|
||||
cont.clear();
|
||||
cont.defaults().width(Math.min(Core.graphics.getWidth() / 1.2f, 520f)).pad(4);
|
||||
cont.defaults().width(Math.min(Core.graphics.getWidth() / 1.2f, 556f)).pad(4);
|
||||
cont.add("@mod.reloadrequired").visible(mods::requiresReload).center().get().setAlignment(Align.center);
|
||||
cont.row();
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ public class HudFragment extends Fragment{
|
||||
.update(label -> label.color.set(Color.orange).lerp(Color.scarlet, Mathf.absin(Time.time, 2f, 1f))), true,
|
||||
() -> {
|
||||
if(!shown || state.isPaused()) return false;
|
||||
if(state.isMenu() || !state.teams.get(player.team()).hasCore()){
|
||||
if(state.isMenu() || !player.team().data().hasCore()){
|
||||
coreAttackTime[0] = 0f;
|
||||
return false;
|
||||
}
|
||||
@@ -322,7 +322,17 @@ public class HudFragment extends Fragment{
|
||||
}
|
||||
return max == 0f ? 0f : val / max;
|
||||
}).blink(Color.white).outline(new Color(0, 0, 0, 0.6f), 7f)).grow())
|
||||
.fillX().width(320f).height(60f).name("boss").visible(() -> state.rules.waves && state.boss() != null).padTop(7);
|
||||
.fillX().width(320f).height(60f).name("boss").visible(() -> state.rules.waves && state.boss() != null).padTop(7).row();
|
||||
|
||||
t.table(Styles.black3, p -> p.margin(4).label(() -> hudText).style(Styles.outlineLabel)).touchable(Touchable.disabled).with(p -> p.visible(() -> {
|
||||
p.color.a = Mathf.lerpDelta(p.color.a, Mathf.num(showHudText), 0.2f);
|
||||
if(state.isMenu()){
|
||||
p.color.a = 0f;
|
||||
showHudText = false;
|
||||
}
|
||||
|
||||
return p.color.a >= 0.001f;
|
||||
}));
|
||||
});
|
||||
|
||||
//spawner warning
|
||||
@@ -342,20 +352,6 @@ public class HudFragment extends Fragment{
|
||||
t.add("@saving").style(Styles.outlineLabel);
|
||||
});
|
||||
|
||||
parent.fill(p -> {
|
||||
p.name = "hudtext";
|
||||
p.top().table(Styles.black3, t -> t.margin(4).label(() -> hudText)
|
||||
.style(Styles.outlineLabel)).padTop(10).visible(p.color.a >= 0.001f);
|
||||
p.update(() -> {
|
||||
p.color.a = Mathf.lerpDelta(p.color.a, Mathf.num(showHudText), 0.2f);
|
||||
if(state.isMenu()){
|
||||
p.color.a = 0f;
|
||||
showHudText = false;
|
||||
}
|
||||
});
|
||||
p.touchable = Touchable.disabled;
|
||||
});
|
||||
|
||||
//TODO DEBUG: rate table
|
||||
if(false)
|
||||
parent.fill(t -> {
|
||||
|
||||
@@ -451,6 +451,10 @@ public class Block extends UnlockableContent{
|
||||
|
||||
}
|
||||
|
||||
public boolean configSenseable(){
|
||||
return configurations.containsKey(Item.class) || configurations.containsKey(Liquid.class);
|
||||
}
|
||||
|
||||
public Object nextConfig(){
|
||||
if(saveConfig && lastConfig != null){
|
||||
return lastConfig;
|
||||
|
||||
@@ -119,11 +119,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
float result = 0f;
|
||||
|
||||
if(block.hasItems){
|
||||
result += build.items.sum((item, amount) -> item.flammability * amount) / block.itemCapacity * Mathf.clamp(block.itemCapacity / 2.4f, 1f, 3f);
|
||||
result += build.items.sum((item, amount) -> item.flammability * amount) / Math.max(block.itemCapacity, 1) * Mathf.clamp(block.itemCapacity / 2.4f, 1f, 3f);
|
||||
}
|
||||
|
||||
if(block.hasLiquids){
|
||||
result += build.liquids.sum((liquid, amount) -> liquid.flammability * amount / 1.6f) / block.liquidCapacity * Mathf.clamp(block.liquidCapacity / 30f, 1f, 2f);
|
||||
result += build.liquids.sum((liquid, amount) -> liquid.flammability * amount / 1.6f) / Math.max(block.liquidCapacity, 1) * Mathf.clamp(block.liquidCapacity / 30f, 1f, 2f);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -53,7 +53,7 @@ public class ForceProjector extends Block{
|
||||
hasItems = true;
|
||||
ambientSound = Sounds.shield;
|
||||
ambientSoundVolume = 0.08f;
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).boost().update(false);
|
||||
consumes.add(new ConsumeCoolant(0.1f)).boost().update(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,7 +37,7 @@ public class BaseTurret extends Block{
|
||||
public void init(){
|
||||
if(acceptCoolant && !consumes.has(ConsumeType.liquid)){
|
||||
hasLiquids = true;
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.2f)).update(false).boost();
|
||||
consumes.add(new ConsumeCoolant(0.2f)).update(false).boost();
|
||||
}
|
||||
|
||||
super.init();
|
||||
|
||||
@@ -19,7 +19,7 @@ public class LaserTurret extends PowerTurret{
|
||||
super(name);
|
||||
canOverdrive = false;
|
||||
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.01f)).update(false);
|
||||
consumes.add(new ConsumeCoolant(0.01f)).update(false);
|
||||
coolantMultiplier = 1f;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ public class LiquidTurret extends Turret{
|
||||
hasLiquids = true;
|
||||
loopSound = Sounds.spray;
|
||||
shootSound = Sounds.none;
|
||||
smokeEffect = Fx.none;
|
||||
shootEffect = Fx.none;
|
||||
outlinedIcon = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ public class Turret extends ReloadTurret{
|
||||
public void init(){
|
||||
if(acceptCoolant && !consumes.has(ConsumeType.liquid)){
|
||||
hasLiquids = true;
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, coolantUsage)).update(false).boost();
|
||||
consumes.add(new ConsumeCoolant(coolantUsage)).update(false).boost();
|
||||
}
|
||||
|
||||
if(shootLength < 0) shootLength = size * tilesize / 2f;
|
||||
|
||||
@@ -147,7 +147,7 @@ public class Duct extends Block implements Autotiler{
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return current == null && items.total() == 0 &&
|
||||
(source.block instanceof Duct || Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
((source.block.rotate && source.front() == this && source.block.hasItems) || Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
@@ -16,8 +17,10 @@ import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO display range
|
||||
public class DuctBridge extends Block{
|
||||
private static BuildPlan otherReq;
|
||||
private int otherDst = 0;
|
||||
|
||||
public @Load("@-bridge") TextureRegion bridgeRegion;
|
||||
public @Load("@-bridge-bottom") TextureRegion bridgeBotRegion;
|
||||
//public @Load("@-bridge-top") TextureRegion bridgeTopRegion;
|
||||
@@ -46,6 +49,26 @@ public class DuctBridge extends Block{
|
||||
Draw.rect(dirRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestConfigTop(BuildPlan req, Eachable<BuildPlan> list){
|
||||
otherReq = null;
|
||||
otherDst = range;
|
||||
Point2 d = Geometry.d4(req.rotation);
|
||||
list.each(other -> {
|
||||
if(other.block == this && req != other && Mathf.clamp(other.x - req.x, -1, 1) == d.x && Mathf.clamp(other.y - req.y, -1, 1) == d.y){
|
||||
int dst = Math.max(Math.abs(other.x - req.x), Math.abs(other.y - req.y));
|
||||
if(dst <= otherDst){
|
||||
otherReq = other;
|
||||
otherDst = dst;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(otherReq != null){
|
||||
drawBridge(req.rotation, req.drawx(), req.drawy(), otherReq.drawx(), otherReq.drawy());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, dirRegion};
|
||||
@@ -56,16 +79,14 @@ public class DuctBridge extends Block{
|
||||
Placement.calculateNodes(points, this, rotation, (point, other) -> Math.max(Math.abs(point.x - other.x), Math.abs(point.y - other.y)) <= range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid, boolean line){
|
||||
int length = range;
|
||||
Building found = null;
|
||||
int dx = Geometry.d4x(rotation), dy = Geometry.d4y(rotation);
|
||||
|
||||
//find the link
|
||||
for(int i = 1; i <= range; i++){
|
||||
Tile other = world.tile(x + Geometry.d4x(rotation) * i, y + Geometry.d4y(rotation) * i);
|
||||
Tile other = world.tile(x + dx * i, y + dy * i);
|
||||
|
||||
if(other != null && other.build instanceof DuctBridgeBuild build && build.team == player.team()){
|
||||
length = i;
|
||||
@@ -74,17 +95,50 @@ public class DuctBridge extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
Drawf.dashLine(Pal.placing,
|
||||
x * tilesize + Geometry.d4[rotation].x * (tilesize / 2f + 2),
|
||||
y * tilesize + Geometry.d4[rotation].y * (tilesize / 2f + 2),
|
||||
x * tilesize + Geometry.d4[rotation].x * (length) * tilesize,
|
||||
y * tilesize + Geometry.d4[rotation].y * (length) * tilesize
|
||||
);
|
||||
|
||||
if(found != null){
|
||||
Drawf.square(found.x, found.y, found.block.size * tilesize/2f + 2.5f, 0f);
|
||||
if(line || found != null){
|
||||
Drawf.dashLine(Pal.placing,
|
||||
x * tilesize + dx * (tilesize / 2f + 2),
|
||||
y * tilesize + dy * (tilesize / 2f + 2),
|
||||
x * tilesize + dx * (length) * tilesize,
|
||||
y * tilesize + dy * (length) * tilesize
|
||||
);
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
if(line){
|
||||
Drawf.square(found.x, found.y, found.block.size * tilesize/2f + 2.5f, 0f);
|
||||
}else{
|
||||
Drawf.square(found.x, found.y, 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
drawPlace(x, y, rotation, valid, true);
|
||||
}
|
||||
|
||||
public void drawBridge(int rotation, float x1, float y1, float x2, float y2){
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
float
|
||||
angle = Angles.angle(x1, y1, x2, y2),
|
||||
cx = (x1 + x2)/2f,
|
||||
cy = (y1 + y2)/2f,
|
||||
len = Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2)) - size * tilesize;
|
||||
|
||||
Draw.rect(bridgeRegion, cx, cy, len, tilesize, angle);
|
||||
Draw.color(0.4f, 0.4f, 0.4f, 0.4f * Renderer.bridgeOpacity);
|
||||
Draw.rect(bridgeBotRegion, cx, cy, len, tilesize, angle);
|
||||
Draw.reset();
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
|
||||
for(float i = 6f; i <= len + size * tilesize - 5f; i += 5f){
|
||||
Draw.rect(arrowRegion, x1 + Geometry.d4x(rotation) * i, y1 + Geometry.d4y(rotation) * i, angle);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public boolean positionsValid(int x1, int y1, int x2, int y2){
|
||||
@@ -108,24 +162,42 @@ public class DuctBridge extends Block{
|
||||
var link = findLink();
|
||||
if(link != null){
|
||||
Draw.z(Layer.power);
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
float
|
||||
angle = angleTo(link),
|
||||
cx = (x + link.x)/2f,
|
||||
cy = (y + link.y)/2f,
|
||||
len = Math.max(Math.abs(x - link.x), Math.abs(y - link.y)) - size * tilesize;
|
||||
drawBridge(rotation, x, y, link.x, link.y);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(bridgeRegion, cx, cy, len, tilesize, angle);
|
||||
Draw.color(0.4f, 0.4f, 0.4f, 0.4f * Renderer.bridgeOpacity);
|
||||
Draw.rect(bridgeBotRegion, cx, cy, len, tilesize, angle);
|
||||
Draw.reset();
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
drawPlace(tile.x, tile.y, rotation, true, false);
|
||||
//draw incoming bridges
|
||||
for(int dir = 0; dir < 4; dir++){
|
||||
if(dir != rotation){
|
||||
int dx = Geometry.d4x(dir), dy = Geometry.d4y(dir);
|
||||
int length = range;
|
||||
Building found = null;
|
||||
|
||||
for(float i = 6f; i <= len + size * tilesize - 5f; i += 5f){
|
||||
Draw.rect(arrowRegion, x + Geometry.d4x(rotation) * i, y + Geometry.d4y(rotation) * i, angle);
|
||||
//find the link
|
||||
for(int i = 1; i <= range; i++){
|
||||
Tile other = world.tile(tile.x + dx * i, tile.y + dy * i);
|
||||
|
||||
if(other != null && other.build instanceof DuctBridgeBuild build && build.team == player.team() && (build.rotation + 2) % 4 == dir){
|
||||
length = i;
|
||||
found = other.build;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
Drawf.dashLine(Pal.place,
|
||||
found.x - dx * (tilesize / 2f + 2),
|
||||
found.y - dy * (tilesize / 2f + 2),
|
||||
found.x - dx * (length) * tilesize,
|
||||
found.y - dy * (length) * tilesize
|
||||
);
|
||||
|
||||
Drawf.square(found.x, found.y, 2f, 45f, Pal.place);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +245,7 @@ public class DuctBridge extends Block{
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
int rel = this.relativeTo(source);
|
||||
int rel = this.relativeToEdge(source.tile);
|
||||
return items.total() < itemCapacity && rel != rotation && occupied[(rel + 2) % 4] == null;
|
||||
}
|
||||
}
|
||||
|
||||
19
core/src/mindustry/world/blocks/environment/MetalFloor.java
Normal file
19
core/src/mindustry/world/blocks/environment/MetalFloor.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
/** Class for quickly defining a floor with no water and no variants. Offers no new functionality. */
|
||||
public class MetalFloor extends Floor{
|
||||
|
||||
public MetalFloor(String name){
|
||||
super(name);
|
||||
variants = 0;
|
||||
attributes.set(Attribute.water, -1);
|
||||
}
|
||||
|
||||
public MetalFloor(String name, int variants){
|
||||
super(name);
|
||||
this.variants = variants;
|
||||
attributes.set(Attribute.water, -1);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,11 @@ public class BlockUnloader extends BlockLoader{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rotatedOutput(int x, int y){
|
||||
return false;
|
||||
}
|
||||
|
||||
public class BlockUnloaderBuild extends BlockLoaderBuild{
|
||||
|
||||
@Override
|
||||
|
||||
@@ -66,6 +66,12 @@ public class PayloadMassDriver extends PayloadBlock{
|
||||
config(Integer.class, (PayloadDriverBuild tile, Integer point) -> tile.link = point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
clipSize = Math.max(clipSize, range*2f + tilesize*size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package mindustry.world.blocks.power;
|
||||
|
||||
import arc.func.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.consumers.*;
|
||||
|
||||
/** A power consumer that uses a dynamic amount of power. */
|
||||
public class DynamicConsumePower extends ConsumePower{
|
||||
private final Floatf<Building> usage;
|
||||
|
||||
public DynamicConsumePower(Floatf<Building> usage){
|
||||
super(0, 0, false);
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float requestedPower(Building entity){
|
||||
return usage.get(entity);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class CommandCenter extends Block{
|
||||
public final int timerEffect = timers ++;
|
||||
|
||||
public TextureRegionDrawable[] commandRegions = new TextureRegionDrawable[UnitCommand.all.length];
|
||||
public Color topColor = null, bottomColor = Color.valueOf("5e5e5e");
|
||||
public Effect effect = Fx.commandSend;
|
||||
@@ -33,11 +35,17 @@ public class CommandCenter extends Block{
|
||||
solid = true;
|
||||
configurable = true;
|
||||
drawDisabled = false;
|
||||
logicConfigurable = true;
|
||||
|
||||
config(UnitCommand.class, (CommandBuild build, UnitCommand command) -> {
|
||||
build.team.data().command = command;
|
||||
effect.at(build, effectSize);
|
||||
Events.fire(new CommandIssueEvent(build, command));
|
||||
if(build.team.data().command != command){
|
||||
build.team.data().command = command;
|
||||
//do not spam effect
|
||||
if(build.timer(timerEffect, 60f)){
|
||||
effect.at(build, effectSize);
|
||||
}
|
||||
Events.fire(new CommandIssueEvent(build, command));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,6 +60,11 @@ public class CommandCenter extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configSenseable(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public class CommandBuild extends Building{
|
||||
|
||||
@Override
|
||||
|
||||
@@ -76,7 +76,7 @@ public class RepairPoint extends Block{
|
||||
public void init(){
|
||||
if(acceptCoolant){
|
||||
hasLiquids = true;
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, coolantUse)).optional(true, true);
|
||||
consumes.add(new ConsumeCoolant(coolantUse)).optional(true, true);
|
||||
}
|
||||
|
||||
consumes.powerCond(powerUse, (RepairPointBuild entity) -> entity.target != null);
|
||||
|
||||
16
core/src/mindustry/world/consumers/ConsumeCoolant.java
Normal file
16
core/src/mindustry/world/consumers/ConsumeCoolant.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
/** A ConsumeLiquidFilter that consumes specific coolant, selected based on stats. */
|
||||
public class ConsumeCoolant extends ConsumeLiquidFilter{
|
||||
public float maxTemp = 0.5f, maxFlammability = 0.1f;
|
||||
|
||||
public ConsumeCoolant(float amount){
|
||||
this.filter = liquid -> liquid.temperature <= maxTemp && liquid.flammability < maxFlammability;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
//mods
|
||||
public ConsumeCoolant(){
|
||||
this(0.1f);
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@ import mindustry.gen.*;
|
||||
|
||||
public abstract class ConsumeLiquidBase extends Consume{
|
||||
/** amount used per frame */
|
||||
public final float amount;
|
||||
public float amount;
|
||||
|
||||
public ConsumeLiquidBase(float amount){
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public ConsumeLiquidBase(){}
|
||||
|
||||
@Override
|
||||
public ConsumeType type(){
|
||||
return ConsumeType.liquid;
|
||||
|
||||
@@ -11,13 +11,17 @@ import mindustry.world.meta.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ConsumeLiquidFilter extends ConsumeLiquidBase{
|
||||
public final Boolf<Liquid> filter;
|
||||
public Boolf<Liquid> filter;
|
||||
|
||||
public ConsumeLiquidFilter(Boolf<Liquid> liquid, float amount){
|
||||
super(amount);
|
||||
this.filter = liquid;
|
||||
}
|
||||
|
||||
public ConsumeLiquidFilter(){
|
||||
this.filter = l -> false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyLiquidFilter(Bits arr){
|
||||
content.liquids().each(filter, item -> arr.set(item.id));
|
||||
|
||||
@@ -68,6 +68,11 @@ public class Consumers{
|
||||
return add(new ConditionalConsumePower(usage, (Boolf<Building>)cons));
|
||||
}
|
||||
|
||||
/** Creates a consumer that consumes a dynamic amount of power. */
|
||||
public <T extends Building> ConsumePower powerDynamic(Floatf<T> usage){
|
||||
return add(new DynamicConsumePower((Floatf<Building>)usage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a consumer which stores power.
|
||||
* @param powerCapacity The maximum capacity in power units.
|
||||
|
||||
@@ -8,10 +8,12 @@ import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
@@ -117,6 +119,51 @@ public class StatValues{
|
||||
);
|
||||
}
|
||||
|
||||
public static StatValue floors(Attribute attr, boolean floating, float scale, boolean startZero){
|
||||
return table -> table.table(c -> {
|
||||
Runnable[] rebuild = {null};
|
||||
Map[] lastMap = {null};
|
||||
|
||||
rebuild[0] = () -> {
|
||||
c.clearChildren();
|
||||
c.left();
|
||||
|
||||
if(state.isGame()){
|
||||
var blocks = Vars.content.blocks()
|
||||
.select(block -> block instanceof Floor f && indexer.isBlockPresent(block) && f.attributes.get(attr) != 0 && !(f.isLiquid && !floating))
|
||||
.<Floor>as().with(s -> s.sort(f -> f.attributes.get(attr)));
|
||||
|
||||
if(blocks.any()){
|
||||
int i = 0;
|
||||
for(var block : blocks){
|
||||
|
||||
floorEfficiency(block, block.attributes.get(attr) * scale, startZero).display(c);
|
||||
if(++i % 5 == 0){
|
||||
c.row();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
c.add("@none.found");
|
||||
}
|
||||
}else{
|
||||
c.add("@stat.showinmap");
|
||||
}
|
||||
};
|
||||
|
||||
rebuild[0].run();
|
||||
|
||||
//rebuild when map changes.
|
||||
c.update(() -> {
|
||||
Map current = state.isGame() ? state.map : null;
|
||||
|
||||
if(current != lastMap[0]){
|
||||
rebuild[0].run();
|
||||
lastMap[0] = current;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static StatValue blocks(Boolf<Block> pred){
|
||||
return blocks(content.blocks().select(pred));
|
||||
}
|
||||
@@ -246,7 +293,7 @@ public class StatValues{
|
||||
sep(bt, Core.bundle.format("bullet.splashdamage", (int)type.splashDamage, Strings.fixed(type.splashDamageRadius / tilesize, 1)));
|
||||
}
|
||||
|
||||
if(!unit && !Mathf.equal(type.ammoMultiplier, 1f)){
|
||||
if(!unit && !Mathf.equal(type.ammoMultiplier, 1f) && type.displayAmmoMultiplier){
|
||||
sep(bt, Core.bundle.format("bullet.multiplier", (int)type.ammoMultiplier));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@ package mindustry.world.meta;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
/** Hold and organizes a list of block stats. */
|
||||
public class Stats{
|
||||
@@ -68,11 +66,7 @@ public class Stats{
|
||||
}
|
||||
|
||||
public void add(Stat stat, Attribute attr, boolean floating, float scale, boolean startZero){
|
||||
for(var block : Vars.content.blocks()
|
||||
.select(block -> block instanceof Floor f && f.attributes.get(attr) != 0 && !(f.isLiquid && !floating))
|
||||
.<Floor>as().with(s -> s.sort(f -> f.attributes.get(attr)))){
|
||||
add(stat, StatValues.floorEfficiency(block, block.attributes.get(attr) * scale, startZero));
|
||||
}
|
||||
add(stat, StatValues.floors(attr, floating, scale, startZero));
|
||||
}
|
||||
|
||||
/** Adds a single string value with this stat. */
|
||||
|
||||
Reference in New Issue
Block a user