This commit is contained in:
Anuken
2024-08-24 10:18:47 -04:00
75 changed files with 270 additions and 130 deletions

View File

@@ -62,7 +62,9 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
}
long ram = Runtime.getRuntime().maxMemory();
boolean gb = ram >= 1024 * 1024 * 1024;
Log.info("[RAM] Available: @ @", Strings.fixed(gb ? ram / 1024f / 1024 / 1024f : ram / 1024f / 1024f, 1), gb ? "GB" : "MB");
if(!OS.isIos){
Log.info("[RAM] Available: @ @", Strings.fixed(gb ? ram / 1024f / 1024 / 1024f : ram / 1024f / 1024f, 1), gb ? "GB" : "MB");
}
Time.setDeltaProvider(() -> {
float result = Core.graphics.getDeltaTime() * 60f;

View File

@@ -1513,27 +1513,23 @@ public class Blocks{
requirements(Category.defense, with(Items.copper, 6));
health = 80 * wallHealthMultiplier;
researchCostMultiplier = 0.1f;
envDisabled |= Env.scorching;
}};
copperWallLarge = new Wall("copper-wall-large"){{
requirements(Category.defense, ItemStack.mult(copperWall.requirements, 4));
health = 80 * 4 * wallHealthMultiplier;
size = 2;
envDisabled |= Env.scorching;
}};
titaniumWall = new Wall("titanium-wall"){{
requirements(Category.defense, with(Items.titanium, 6));
health = 110 * wallHealthMultiplier;
envDisabled |= Env.scorching;
}};
titaniumWallLarge = new Wall("titanium-wall-large"){{
requirements(Category.defense, ItemStack.mult(titaniumWall.requirements, 4));
health = 110 * wallHealthMultiplier * 4;
size = 2;
envDisabled |= Env.scorching;
}};
plastaniumWall = new Wall("plastanium-wall"){{
@@ -1542,7 +1538,6 @@ public class Blocks{
insulated = true;
absorbLasers = true;
schematicPriority = 10;
envDisabled |= Env.scorching;
}};
plastaniumWallLarge = new Wall("plastanium-wall-large"){{
@@ -1552,20 +1547,17 @@ public class Blocks{
insulated = true;
absorbLasers = true;
schematicPriority = 10;
envDisabled |= Env.scorching;
}};
thoriumWall = new Wall("thorium-wall"){{
requirements(Category.defense, with(Items.thorium, 6));
health = 200 * wallHealthMultiplier;
envDisabled |= Env.scorching;
}};
thoriumWallLarge = new Wall("thorium-wall-large"){{
requirements(Category.defense, ItemStack.mult(thoriumWall.requirements, 4));
health = 200 * wallHealthMultiplier * 4;
size = 2;
envDisabled |= Env.scorching;
}};
phaseWall = new Wall("phase-wall"){{
@@ -1573,7 +1565,6 @@ public class Blocks{
health = 150 * wallHealthMultiplier;
chanceDeflect = 10f;
flashHit = true;
envDisabled |= Env.scorching;
}};
phaseWallLarge = new Wall("phase-wall-large"){{
@@ -1582,14 +1573,12 @@ public class Blocks{
size = 2;
chanceDeflect = 10f;
flashHit = true;
envDisabled |= Env.scorching;
}};
surgeWall = new Wall("surge-wall"){{
requirements(Category.defense, with(Items.surgeAlloy, 6));
health = 230 * wallHealthMultiplier;
lightningChance = 0.05f;
envDisabled |= Env.scorching;
}};
surgeWallLarge = new Wall("surge-wall-large"){{
@@ -1597,13 +1586,11 @@ public class Blocks{
health = 230 * 4 * wallHealthMultiplier;
size = 2;
lightningChance = 0.05f;
envDisabled |= Env.scorching;
}};
door = new Door("door"){{
requirements(Category.defense, with(Items.titanium, 6, Items.silicon, 4));
health = 100 * wallHealthMultiplier;
envDisabled |= Env.scorching;
}};
doorLarge = new Door("door-large"){{
@@ -1612,14 +1599,12 @@ public class Blocks{
closefx = Fx.doorcloselarge;
health = 100 * 4 * wallHealthMultiplier;
size = 2;
envDisabled |= Env.scorching;
}};
scrapWall = new Wall("scrap-wall"){{
requirements(Category.defense, BuildVisibility.sandboxOnly, with(Items.scrap, 6));
health = 60 * wallHealthMultiplier;
variants = 5;
envDisabled |= Env.scorching;
}};
scrapWallLarge = new Wall("scrap-wall-large"){{
@@ -1627,7 +1612,6 @@ public class Blocks{
health = 60 * 4 * wallHealthMultiplier;
size = 2;
variants = 4;
envDisabled |= Env.scorching;
}};
scrapWallHuge = new Wall("scrap-wall-huge"){{
@@ -1635,21 +1619,18 @@ public class Blocks{
health = 60 * 9 * wallHealthMultiplier;
size = 3;
variants = 3;
envDisabled |= Env.scorching;
}};
scrapWallGigantic = new Wall("scrap-wall-gigantic"){{
requirements(Category.defense, BuildVisibility.sandboxOnly, ItemStack.mult(scrapWall.requirements, 16));
health = 60 * 16 * wallHealthMultiplier;
size = 4;
envDisabled |= Env.scorching;
}};
thruster = new Thruster("thruster"){{
requirements(Category.defense, BuildVisibility.sandboxOnly, with(Items.scrap, 96));
health = 55 * 16 * wallHealthMultiplier;
size = 4;
envDisabled |= Env.scorching;
}};
berylliumWall = new Wall("beryllium-wall"){{

View File

@@ -65,7 +65,6 @@ public class Planets{
clearSectorOnLose = true;
defaultCore = Blocks.coreBastion;
iconColor = Color.valueOf("ff9266");
hiddenItems.addAll(Items.serpuloItems).removeAll(Items.erekirItems);
enemyBuildSpeedMultiplier = 0.4f;
//TODO disallowed for now
@@ -152,7 +151,6 @@ public class Planets{
startSector = 15;
alwaysUnlocked = true;
landCloudColor = Pal.spore.cpy().a(0.5f);
hiddenItems.addAll(Items.erekirItems).removeAll(Items.serpuloItems);
}};
verilus = makeAsteroid("verlius", sun, Blocks.stoneWall, Blocks.iceWall, 0.5f, 12, 2f, gen -> {

View File

@@ -139,6 +139,16 @@ public class TechTree{
}
}
/** Adds the specified database tab to all the content in this tree. */
public void addDatabaseTab(UnlockableContent tab){
each(node -> node.content.databaseTabs.add(tab));
}
/** Adds the specified planet to the shownPlanets of all the content in this tree. */
public void addPlanet(Planet planet){
each(node -> node.content.shownPlanets.add(planet));
}
public Drawable icon(){
return icon == null ? new TextureRegionDrawable(content.uiIcon) : icon;
}

View File

@@ -88,6 +88,7 @@ public class ContentLoader{
/** Calls Content#init() on everything. Use only after all modules have been created. */
public void init(){
initialize(Content::init);
initialize(Content::postInit);
if(logicVars != null) logicVars.init();
Events.fire(new ContentInitEvent());
}

View File

@@ -78,7 +78,7 @@ public class GameState{
}
public @Nullable Planet getPlanet(){
return rules.sector != null ? rules.sector.planet : null;
return rules.sector != null ? rules.sector.planet : rules.planet;
}
public boolean isEditor(){

View File

@@ -140,10 +140,6 @@ public class Logic implements ApplicationListener{
core.items.set(item, core.block.itemCapacity);
}
}
//set up hidden items
state.rules.hiddenBuildItems.clear();
state.rules.hiddenBuildItems.addAll(state.rules.sector.planet.hiddenItems);
}
//save settings

View File

@@ -158,7 +158,7 @@ public class UI implements ApplicationListener, Loadable{
Core.scene.draw();
if(Core.input.keyTap(KeyCode.mouseLeft) && Core.scene.hasField()){
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(!(e instanceof TextField)){
Core.scene.setKeyboardFocus(null);
}

View File

@@ -321,8 +321,6 @@ public class World{
state.rules.cloudColor = sector.planet.landCloudColor;
state.rules.env = sector.planet.defaultEnv;
state.rules.planet = sector.planet;
state.rules.hiddenBuildItems.clear();
state.rules.hiddenBuildItems.addAll(sector.planet.hiddenItems);
sector.planet.applyRules(state.rules);
sector.info.resources = content.toSeq();
sector.info.resources.sort(Structs.comps(Structs.comparing(Content::getContentType), Structs.comparingInt(c -> c.id)));

View File

@@ -25,6 +25,9 @@ public abstract class Content implements Comparable<Content>{
/** Called after all content and modules are created. Do not use to load regions or texture data! */
public void init(){}
/** Called after init(). */
public void postInit(){}
/**
* Called after all content is created, only on non-headless versions.
* Use for loading regions or other image data.

View File

@@ -35,8 +35,6 @@ public abstract class UnlockableContent extends MappableContent{
public boolean hideDetails = true;
/** If false, all icon generation is disabled for this content; createIcons is not called. */
public boolean generateIcons = true;
/** Special logic icon ID. */
public int iconId = 0;
/** How big the content appears in certain selection menus */
public float selectionSize = 24f;
/** Icon of the content to use in UI. */
@@ -45,11 +43,24 @@ public abstract class UnlockableContent extends MappableContent{
public TextureRegion fullIcon;
/** Override for the full icon. Useful for mod content with duplicate icons. Overrides any other full icon.*/
public String fullOverride = "";
/** If true, this content will appear in all database tabs. */
public boolean allDatabaseTabs = false;
/**
* Planets that this content is made for. If empty, a planet is decided based on item requirements.
* Currently, this is only meaningful for blocks.
* */
public ObjectSet<Planet> shownPlanets = new ObjectSet<>();
/**
* Content - usually a planet - that dictates which database tab(s) this content will appear in.
* If nothing is defined, it will use the values in shownPlanets.
* If shownPlanets is also empty, it will use Serpulo as the "default" tab.
* */
public ObjectSet<UnlockableContent> databaseTabs = new ObjectSet<>();
/** 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. */
public Seq<TechNode> techNodes = new Seq<>();
/** Unlock state. Loaded from settings. Do not modify outside of the constructor. */
/** Unlock state. Loaded from settings. Do not modify outside the constructor. */
protected boolean unlocked;
public UnlockableContent(String name){
@@ -61,6 +72,13 @@ public abstract class UnlockableContent extends MappableContent{
this.unlocked = Core.settings != null && Core.settings.getBool(this.name + "-unlocked", false);
}
@Override
public void postInit(){
super.postInit();
databaseTabs.addAll(shownPlanets);
}
@Override
public void loadIcon(){
fullIcon =
@@ -74,6 +92,10 @@ public abstract class UnlockableContent extends MappableContent{
uiIcon = Core.atlas.find(getContentType().name() + "-" + name + "-ui", fullIcon);
}
public boolean isOnPlanet(@Nullable Planet planet){
return planet == null || shownPlanets.isEmpty() || shownPlanets.contains(planet);
}
public int getLogicId(){
return logicVars.lookupLogicId(this);
}

View File

@@ -329,7 +329,7 @@ public class MapView extends Element implements GestureListener{
return Core.scene != null && Core.scene.getKeyboardFocus() != null
&& Core.scene.getKeyboardFocus().isDescendantOf(ui.editor)
&& ui.editor.isShown() && tool == EditorTool.zoom &&
Core.scene.hit(Core.input.mouse().x, Core.input.mouse().y, true) == this;
Core.scene.getHoverElement() == this;
}
@Override

View File

@@ -1370,6 +1370,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
/** Called when the block is destroyed. The tile is still intact at this stage. */
public void onDestroyed(){
if(sound != null){
sound.stop();
}
float explosiveness = block.baseExplosiveness;
float flammability = 0f;
float power = 0f;

View File

@@ -153,8 +153,6 @@ public class Rules{
public ObjectSet<Block> revealedBlocks = new ObjectSet<>();
/** Unlocked content names. Only used in multiplayer when the campaign is enabled. */
public ObjectSet<String> researched = new ObjectSet<>();
/** Block containing these items as requirements are hidden. */
public ObjectSet<Item> hiddenBuildItems = Items.erekirOnlyItems.asSet();
/** In-map objective executor. */
public MapObjectives objectives = new MapObjectives();
/** Flags set by objectives. Used in world processors. */

View File

@@ -8,6 +8,7 @@ import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.ai.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
@@ -359,6 +360,10 @@ public class Teams{
//TODO this may cause a lot of packet spam, optimize?
Call.setTeam(build, Team.derelict);
if(build.getPayload() instanceof UnitPayload){
Call.destroyPayload(build);
}
if(Mathf.chance(0.25)){
Time.run(Mathf.random(0f, 60f * 6f), build::kill);
}
@@ -425,6 +430,14 @@ public class Teams{
}
}
@Remote(called = Loc.server, unreliable = true)
public static void destroyPayload(Building build){
if(build != null && build.getPayload() instanceof UnitPayload && build.takePayload() instanceof UnitPayload unit){
unit.dump();
unit.unit.killed();
}
}
/** Represents a block made by this team that was destroyed somewhere on the map.
* This does not include deconstructed blocks.*/
public static class BlockPlan{

View File

@@ -132,7 +132,9 @@ public class GlobalVars{
}
for(UnitType type : Vars.content.units()){
put("@" + type.name, type);
if(!type.internal){
put("@" + type.name, type);
}
}
for(Weather weather : Vars.content.weathers()){

View File

@@ -33,8 +33,9 @@ public class LAssembler{
Seq<LStatement> st = read(data, privileged);
asm.instructions = st.map(l -> l.build(asm)).retainAll(l -> l != null).toArray(LInstruction.class);
asm.privileged = privileged;
asm.instructions = st.map(l -> l.build(asm)).retainAll(l -> l != null).toArray(LInstruction.class);
return asm;
}
@@ -58,7 +59,7 @@ public class LAssembler{
/** @return a variable by name.
* This may be a constant variable referring to a number or object. */
public LVar var(String symbol){
LVar constVar = Vars.logicVars.get(symbol);
LVar constVar = Vars.logicVars.get(symbol, privileged);
if(constVar != null) return constVar;
symbol = symbol.trim();

View File

@@ -171,7 +171,7 @@ public class LCanvas extends Table{
}
StatementElem checkHovered(){
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(e != null){
while(e != null && !(e instanceof StatementElem)){
e = e.parent;

View File

@@ -555,7 +555,7 @@ public class LExecutor{
int address = position.numi();
Building from = target.building();
if(from instanceof MemoryBuild mem && (exec.privileged || from.team == exec.team)){
if(from instanceof MemoryBuild mem && (exec.privileged || (from.team == exec.team && !mem.block.privileged))){
output.setnum(address < 0 || address >= mem.memory.length ? 0 : mem.memory[address]);
}
}
@@ -662,7 +662,7 @@ public class LExecutor{
LogicAI ai = null;
if(base instanceof Ranged r && (exec.privileged || r.team() == exec.team) &&
(base instanceof Building || (ai = UnitControlI.checkLogicAI(exec, base)) != null)){ //must be a building or a controllable unit
((base instanceof Building b && (!b.block.privileged || exec.privileged)) || (ai = UnitControlI.checkLogicAI(exec, base)) != null)){ //must be a building or a controllable unit
float range = r.range();
Healthc targeted;
@@ -1050,7 +1050,7 @@ public class LExecutor{
@Override
public void run(LExecutor exec){
if(target.building() instanceof MessageBuild d && (d.team == exec.team || exec.privileged)){
if(target.building() instanceof MessageBuild d && (exec.privileged || (d.team == exec.team && !d.block.privileged))){
d.message.setLength(0);
d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer));
@@ -1242,7 +1242,8 @@ public class LExecutor{
result.setobj(units == null || i < 0 || i >= units.size ? null : units.get(i));
}
}
case player -> result.setobj(i < 0 || i >= data.players.size ? null : data.players.get(i).unit());
case player -> result.setobj(i < 0 || i >= data.players.size ? null :
data.players.get(i).unit() instanceof BlockUnitc block ? block.tile() : data.players.get(i).unit());
case core -> result.setobj(i < 0 || i >= data.cores.size ? null : data.cores.get(i));
case build -> {
Block block = extra.obj() instanceof Block b ? b : null;
@@ -1295,7 +1296,7 @@ public class LExecutor{
@Override
public void run(LExecutor exec){
Tile tile = world.tile(x.numi(), y.numi());
Tile tile = world.tile(Mathf.round(x.numf()), Mathf.round(y.numf()));
if(tile == null){
dest.setobj(null);
}else{
@@ -1380,7 +1381,7 @@ public class LExecutor{
Team t = team.team();
if(type.obj() instanceof UnitType type && !type.hidden && t != null && Units.canCreate(t, type)){
if(type.obj() instanceof UnitType type && !type.internal && !type.hidden && t != null && Units.canCreate(t, type)){
//random offset to prevent stacking
var unit = type.spawn(t, World.unconv(x.numf()) + Mathf.range(0.01f), World.unconv(y.numf()) + Mathf.range(0.01f));
spawner.spawnEffect(unit, rotation.numf());

View File

@@ -34,7 +34,7 @@ public class BaseGenerator{
Seq<Block> wallsSmall = content.blocks().select(b -> b instanceof Wall && b.isVanilla() && b.size == size
&& !b.insulated && b.buildVisibility == BuildVisibility.shown
&& !(b instanceof Door)
&& !(Structs.contains(b.requirements, i -> state.rules.hiddenBuildItems.contains(i.item))));
&& b.isOnPlanet(state.getPlanet()));
wallsSmall.sort(b -> b.buildCost);
return wallsSmall.getFrac(difficulty * 0.91f);
}

View File

@@ -1097,29 +1097,41 @@ public class ContentParser{
}
Field field = metadata.field;
try{
boolean isMap = ObjectMap.class.isAssignableFrom(field.getType()) || ObjectIntMap.class.isAssignableFrom(field.getType()) || ObjectFloatMap.class.isAssignableFrom(field.getType());
boolean mergeMap = isMap && child.has("add") && child.get("add").isBoolean() && child.getBoolean("add", false);
if(child.isObject() && child.has("add") && (Seq.class.isAssignableFrom(field.getType()) || ObjectSet.class.isAssignableFrom(field.getType()))){
Object readField = parser.readValue(field.getType(), metadata.elementType, child.get("add"), metadata.keyType);
Object fieldObj = field.get(object);
if(mergeMap){
child.remove("add");
}
Object readField = parser.readValue(field.getType(), metadata.elementType, child, metadata.keyType);
Object fieldObj = field.get(object);
//if a map has add: true, add its contents to the map instead
if(mergeMap && (fieldObj instanceof ObjectMap<?,?> || fieldObj instanceof ObjectIntMap<?> || fieldObj instanceof ObjectFloatMap<?>)){
if(field.get(object) instanceof ObjectMap<?,?> baseMap){
baseMap.putAll((ObjectMap)readField);
}else if(field.get(object) instanceof ObjectIntMap<?> baseMap){
baseMap.putAll((ObjectIntMap)readField);
}else if(field.get(object) instanceof ObjectFloatMap<?> baseMap){
baseMap.putAll((ObjectFloatMap)readField);
if(fieldObj instanceof ObjectSet set){
set.addAll((ObjectSet)readField);
}else if(fieldObj instanceof Seq seq){
seq.addAll((Seq)readField);
}else{
throw new SerializationException("This should be impossible");
}
}else{
field.set(object, readField);
}
boolean isMap = ObjectMap.class.isAssignableFrom(field.getType()) || ObjectIntMap.class.isAssignableFrom(field.getType()) || ObjectFloatMap.class.isAssignableFrom(field.getType());
boolean mergeMap = isMap && 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);
Object fieldObj = field.get(object);
//if a map has add: true, add its contents to the map instead
if(mergeMap && (fieldObj instanceof ObjectMap<?,?> || fieldObj instanceof ObjectIntMap<?> || fieldObj instanceof ObjectFloatMap<?>)){
if(field.get(object) instanceof ObjectMap<?,?> baseMap){
baseMap.putAll((ObjectMap)readField);
}else if(field.get(object) instanceof ObjectIntMap<?> baseMap){
baseMap.putAll((ObjectIntMap)readField);
}else if(field.get(object) instanceof ObjectFloatMap<?> baseMap){
baseMap.putAll((ObjectFloatMap)readField);
}
}else{
field.set(object, readField);
}
}
}catch(IllegalAccessException ex){
throw new SerializationException("Error accessing field: " + field.getName() + " (" + type.getName() + ")", ex);
}catch(SerializationException ex){

View File

@@ -81,12 +81,12 @@ public class GameService{
}
private void registerEvents(){
allTransportSerpulo = content.blocks().select(b -> b.category == Category.distribution && b.isVisibleOn(Planets.serpulo) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
allTransportErekir = content.blocks().select(b -> b.category == Category.distribution && b.isVisibleOn(Planets.erekir) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
allTransportSerpulo = content.blocks().select(b -> b.category == Category.distribution && b.isOnPlanet(Planets.serpulo) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
allTransportErekir = content.blocks().select(b -> b.category == Category.distribution && b.isOnPlanet(Planets.erekir) && b.isVanilla() && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
//cores are ignored since they're upgrades and can be skipped
allSerpuloBlocks = content.blocks().select(b -> b.synthetic() && b.isVisibleOn(Planets.serpulo) && b.isVanilla() && !(b instanceof CoreBlock) && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
allErekirBlocks = content.blocks().select(b -> b.synthetic() && b.isVisibleOn(Planets.erekir) && b.isVanilla() && !(b instanceof CoreBlock) && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
allSerpuloBlocks = content.blocks().select(b -> b.synthetic() && b.isOnPlanet(Planets.serpulo) && b.isVanilla() && !(b instanceof CoreBlock) && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
allErekirBlocks = content.blocks().select(b -> b.synthetic() && b.isOnPlanet(Planets.erekir) && b.isVanilla() && !(b instanceof CoreBlock) && b.buildVisibility == BuildVisibility.shown).toArray(Block.class);
unitsBuilt = Core.settings.getJson("units-built" , ObjectSet.class, String.class, ObjectSet::new);
blocksBuilt = Core.settings.getJson("blocks-built" , ObjectSet.class, String.class, ObjectSet::new);
@@ -526,7 +526,7 @@ public class GameService{
}
for(Building entity : player.team().cores()){
if(!content.items().contains(i -> !state.rules.hiddenBuildItems.contains(i) && entity.items.get(i) < entity.block.itemCapacity)){
if(!content.items().contains(i -> i.isOnPlanet(state.getPlanet()) && entity.items.get(i) < entity.block.itemCapacity)){
fillCoreAllCampaign.complete();
break;
}

View File

@@ -47,7 +47,9 @@ public class Item extends UnlockableContent implements Senseable{
/** If true, this material is used by buildings. If false, this material will be incinerated in certain cores. */
public boolean buildable = true;
public boolean hidden = false;
/** For mods. Adds this item to the listed planets' hidden items Seq. */
/** @deprecated no-op, do not use. */
@Deprecated
public @Nullable Planet[] hiddenOnPlanets;
public Item(String name, Color color){
@@ -60,14 +62,9 @@ public class Item extends UnlockableContent implements Senseable{
}
@Override
public void init(){
super.init();
if(hiddenOnPlanets != null){
for(Planet planet : hiddenOnPlanets){
planet.hiddenItems.add(this);
}
}
public boolean isOnPlanet(Planet planet){
//hidden items should not appear on any planet's resource selection screen
return super.isOnPlanet(planet) && !hidden;
}
@Override

View File

@@ -15,7 +15,6 @@ import mindustry.content.*;
import mindustry.content.TechTree.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.game.EventType.ContentInitEvent;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.graphics.g3d.*;
@@ -146,10 +145,8 @@ public class Planet extends UnlockableContent{
public @Nullable TechNode techTree;
/** TODO remove? Planets that can be launched to from this one. Made mutual in init(). */
public Seq<Planet> launchCandidates = new Seq<>();
/** Items not available on this planet. Left out for backwards compatibility. */
public Seq<Item> hiddenItems = new Seq<>();
/** The only items available on this planet, if defined. */
public Seq<Item> itemWhitelist = new Seq<>();
/** If true, all content in this planet's tech tree will be assigned this planet in their shownPlanets. */
public boolean autoAssignPlanet = true;
/** Content (usually planet-specific) that is unlocked upon landing here. */
public Seq<UnlockableContent> unlockedOnLand = new Seq<>();
/** Loads the mesh. Clientside only. Defaults to a boring sphere mesh. */
@@ -157,6 +154,11 @@ public class Planet extends UnlockableContent{
/** Loads the planet grid outline mesh. Clientside only. */
public Prov<Mesh> gridMeshLoader = () -> MeshBuilder.buildPlanetGrid(grid, outlineColor, outlineRad * radius);
/** @deprecated no-op, do not use. */
@Deprecated
public Seq<Item> itemWhitelist = new Seq<>(), hiddenItems = new Seq<>();
public Planet(String name, Planet parent, float radius){
super(name);
@@ -179,13 +181,6 @@ public class Planet extends UnlockableContent{
parent.updateTotalRadius();
}
//if an item whitelist exists, add everything else not in that whitelist to hidden items
Events.on(ContentInitEvent.class, e -> {
if(itemWhitelist.size > 0){
hiddenItems.addAll(content.items().select(i -> !itemWhitelist.contains(i)));
}
});
//calculate solar system
for(solarSystem = this; solarSystem.parent != null; solarSystem = solarSystem.parent);
}
@@ -216,8 +211,6 @@ public class Planet extends UnlockableContent{
rules.attributes.add(defaultAttributes);
rules.env = defaultEnv;
rules.planet = this;
rules.hiddenBuildItems.clear();
rules.hiddenBuildItems.addAll(hiddenItems);
}
public @Nullable Sector getLastSector(){
@@ -338,6 +331,11 @@ public class Planet extends UnlockableContent{
techTree = TechTree.roots.find(n -> n.planet == this);
}
if(techTree != null && autoAssignPlanet){
techTree.addDatabaseTab(this);
techTree.addPlanet(this);
}
for(Sector sector : sectors){
sector.loadInfo();
}

View File

@@ -68,10 +68,12 @@ public class StatusEffect extends UnlockableContent{
public StatusEffect(String name){
super(name);
allDatabaseTabs = true;
}
@Override
public void init(){
super.init();
if(initblock != null){
initblock.run();
}

View File

@@ -675,6 +675,8 @@ public class UnitType extends UnlockableContent implements Senseable{
@CallSuper
@Override
public void init(){
super.init();
if(constructor == null) throw new IllegalArgumentException("no constructor set up for unit '" + name + "'");
Unit example = constructor.get();

View File

@@ -111,7 +111,7 @@ public class Minimap extends Table{
update(() -> {
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(e != null && e.isDescendantOf(this)){
requestScroll();
}else if(hasScroll()){

View File

@@ -229,19 +229,8 @@ public class CustomRulesDialog extends BaseDialog{
}
number("@rules.dropzoneradius", false, f -> rules.dropZoneRadius = f * tilesize, () -> rules.dropZoneRadius / tilesize, () -> rules.waves);
category("resourcesbuilding");
check("@rules.infiniteresources", b -> {
rules.infiniteResources = b;
//reset to serpulo if any env was enabled
if(!b && rules.hiddenBuildItems.isEmpty()){
rules.env = Planets.serpulo.defaultEnv;
rules.hiddenBuildItems.clear();
rules.hiddenBuildItems.addAll(Planets.serpulo.hiddenItems);
setup();
}
}, () -> rules.infiniteResources);
check("@rules.infiniteresources", b -> rules.infiniteResources = b, () -> rules.infiniteResources);
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);
@@ -349,7 +338,6 @@ public class CustomRulesDialog extends BaseDialog{
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();

View File

@@ -5,11 +5,13 @@ import arc.graphics.*;
import arc.input.*;
import arc.math.*;
import arc.scene.event.*;
import arc.scene.style.*;
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.gen.*;
import mindustry.graphics.*;
@@ -24,16 +26,30 @@ public class DatabaseDialog extends BaseDialog{
private TextField search;
private Table all = new Table();
private @Nullable Seq<UnlockableContent> allTabs;
//sun means "all content"
private UnlockableContent tab = Planets.sun;
public DatabaseDialog(){
super("@database");
shouldPause = true;
addCloseButton();
shown(this::rebuild);
shown(() -> {
checkTabList();
if(state.isCampaign() && allTabs.contains(state.getPlanet())){
tab = state.getPlanet();
}else if(state.isGame() && state.rules.planet != null && allTabs.contains(state.rules.planet)){
tab = state.rules.planet;
}
rebuild();
});
onResize(this::rebuild);
all.margin(20).marginTop(0f);
cont.top();
cont.table(s -> {
s.image(Icon.zoom).padRight(8);
search = s.field(null, text -> rebuild()).growX().get();
@@ -43,18 +59,51 @@ public class DatabaseDialog extends BaseDialog{
cont.pane(all).scrollX(false);
}
void checkTabList(){
if(allTabs == null){
Seq<Content>[] allContent = Vars.content.getContentMap();
ObjectSet<UnlockableContent> all = new ObjectSet<>();
for(var contents : allContent){
for(var content : contents){
if(content instanceof UnlockableContent u){
all.addAll(u.databaseTabs);
}
}
}
allTabs = all.toSeq().sort();
allTabs.insert(0, Planets.sun);
}
}
void rebuild(){
checkTabList();
all.clear();
var text = search.getText().toLowerCase();
Seq<Content>[] allContent = Vars.content.getContentMap();
all.table(t -> {
int i = 0;
for(var content : allTabs){
t.button(content == Planets.sun ? Icon.eyeSmall : content instanceof Planet ? Icon.planet : new TextureRegionDrawable(content.uiIcon), Styles.clearNoneTogglei, iconMed, () -> {
tab = content;
rebuild();
}).size(50f).checked(b -> tab == content).tooltip(content == Planets.sun ? "@all" : content.localizedName).with(but -> {
but.getStyle().imageUpColor = content instanceof Planet p ? p.iconColor : Color.white.cpy();
});
if(++i % 10 == 0) t.row();
}
}).row();;
for(int j = 0; j < allContent.length; j++){
ContentType type = ContentType.all[j];
Seq<UnlockableContent> array = allContent[j]
.select(c -> c instanceof UnlockableContent u && !u.isHidden() &&
.select(c -> c instanceof UnlockableContent u && !u.isHidden() && (tab == Planets.sun || u.allDatabaseTabs || u.databaseTabs.contains(tab)) &&
(text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as();
if(array.size == 0) continue;
all.add("@content." + type.name() + ".name").growX().left().color(Pal.accent);

View File

@@ -48,7 +48,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
ItemSeq launch = universe.getLaunchResources();
if(sector.planet.allowLaunchLoadout){
for(var item : content.items()){
if(sector.planet.hiddenItems.contains(item)){
if(!item.isOnPlanet(sector.planet)){
launch.set(item, 0);
}
}
@@ -72,7 +72,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
if(destination.preset != null){
var rules = destination.preset.generator.map.rules();
for(var stack : rules.loadout){
if(!sector.planet.hiddenItems.contains(stack.item)){
if(stack.item.isOnPlanet(sector.planet)){
resources.add(stack.item, stack.amount);
}
}
@@ -136,7 +136,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
ItemSeq realItems = sitems.copy();
selected.requirements().each(realItems::remove);
loadout.show(lastCapacity, realItems, out, i -> i.unlocked() && !sector.planet.hiddenItems.contains(i), out::clear, () -> {}, () -> {
loadout.show(lastCapacity, realItems, out, i -> i.unlocked() && i.isOnPlanet(sector.planet), out::clear, () -> {}, () -> {
universe.updateLaunchResources(new ItemSeq(out));
update.run();
rebuildItems.run();
@@ -172,7 +172,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
Cons<Schematic> handler = s -> {
if(s.tiles.contains(tile -> !tile.block.supportsEnv(sector.planet.defaultEnv) ||
//make sure block can be built here.
(!sector.planet.hiddenItems.isEmpty() && Structs.contains(tile.block.requirements, stack -> sector.planet.hiddenItems.contains(stack.item))))){
!tile.block.isOnPlanet(sector.planet))){
return;
}

View File

@@ -587,7 +587,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
@Override
public void act(float delta){
if(scene.getDialog() == PlanetDialog.this && !scene.hit(input.mouseX(), input.mouseY(), true).isDescendantOf(e -> e instanceof ScrollPane)){
if(scene.getDialog() == PlanetDialog.this && (scene.getHoverElement() == null || !scene.getHoverElement().isDescendantOf(e -> e instanceof ScrollPane))){
scene.setScrollFocus(PlanetDialog.this);
}

View File

@@ -472,7 +472,7 @@ public class ResearchDialog extends BaseDialog{
if(mobile){
tapped(() -> {
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(e == this){
hoverNode = null;
rebuild();

View File

@@ -67,7 +67,7 @@ public class BlockConfigFragment{
}
public boolean hasConfigMouse(){
Element e = Core.scene.hit(Core.input.mouseX(), Core.graphics.getHeight() - Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
return e != null && (e == table || e.isDescendantOf(table));
}

View File

@@ -615,7 +615,7 @@ public class PlacementFragment{
blocksSelect.margin(4).marginTop(0);
blockPane = blocksSelect.pane(blocks -> blockTable = blocks).height(194f).update(pane -> {
if(pane.hasScroll()){
Element result = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element result = Core.scene.getHoverElement();
if(result == null || !result.isDescendantOf(pane)){
Core.scene.setScrollFocus(null);
}

View File

@@ -899,10 +899,6 @@ public class Block extends UnlockableContent implements Senseable{
return !isHidden() && (state.rules.editor || (!state.rules.hideBannedBlocks || !state.rules.isBanned(this)));
}
public boolean isVisibleOn(Planet planet){
return !Structs.contains(requirements, i -> planet.hiddenItems.contains(i.item));
}
public boolean isPlaceable(){
return isVisible() && (!state.rules.isBanned(this) || state.rules.editor) && supportsEnv(state.rules.env);
}
@@ -948,7 +944,7 @@ public class Block extends UnlockableContent implements Senseable{
}
public boolean environmentBuildable(){
return (state.rules.hiddenBuildItems.isEmpty() || !Structs.contains(requirements, i -> state.rules.hiddenBuildItems.contains(i.item)));
return isOnPlanet(state.getPlanet());
}
public boolean isStatic(){
@@ -1164,10 +1160,28 @@ public class Block extends UnlockableContent implements Senseable{
return buildVisibility != BuildVisibility.hidden;
}
@Override
public void postInit(){
//usually, an empty set of planets is a configuration error. auto-assign based on requirements
if(requirements.length > 0 && shownPlanets.isEmpty()){
for(Planet planet : content.planets()){
if(planet.isLandable()){
if(!Structs.contains(requirements, s -> !s.item.isOnPlanet(planet))){
shownPlanets.add(planet);
}
}
}
}
super.postInit();
}
/** Called after all blocks are created. */
@Override
@CallSuper
public void init(){
super.init();
//disable standard shadow
if(customShadow){
hasShadow = false;

View File

@@ -9,7 +9,6 @@ import arc.struct.*;
import arc.util.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
@@ -65,7 +64,7 @@ public class ItemSelection{
Seq<T> list = items.select(u -> (text.isEmpty() || u.localizedName.toLowerCase().contains(text.toLowerCase())));
for(T item : list){
if(!item.unlockedNow() || (item instanceof Item checkVisible && state.rules.hiddenBuildItems.contains(checkVisible)) || item.isHidden()) continue;
if(!item.unlockedNow() || !item.isOnPlanet(state.getPlanet()) || item.isHidden()) continue;
ImageButton button = cont.button(Tex.whiteui, Styles.clearNoneTogglei, Mathf.clamp(item.selectionSize, 0f, 40f), () -> {
if(closeSelect) control.input.config.hideConfig();

View File

@@ -6,6 +6,7 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
@@ -266,6 +267,14 @@ public class ForceProjector extends Block{
Draw.reset();
}
@Override
public void overwrote(Seq<Building> previous){
if(previous.size > 0 && previous.first().block == block && previous.first() instanceof ForceBuild b){
broken = b.broken;
buildup = b.buildup;
}
}
@Override
public void write(Writes write){
super.write(write);

View File

@@ -53,6 +53,7 @@ public class BuildPayload implements Payload{
@Override
public void destroyed(){
build.dead = true;
build.onDestroyed();
}