diff --git a/core/assets/planets/serpulo.json b/core/assets/planets/serpulo.json new file mode 100644 index 0000000000..1692f024a0 --- /dev/null +++ b/core/assets/planets/serpulo.json @@ -0,0 +1 @@ +{presets:{windsweptIslands:97,stainedMountains:20,weatheredChannels:39,craters:175,extractionOutpost:165,coastline:108,navalFortress:216,frontier:41,groundZero:15,mycelialBastion:23,atolls:40,overgrowth:220,testingGrounds:3,frozenForest:170,fungalPass:219,taintedWoods:221,infestedCanyons:210,desolateRift:123,nuclearComplex:130,facility32m:222,planetaryTerminal:93,impact0078:227,seaPort:98,geothermalStronghold:264,cruxscape:54,tarFields:101,biomassFacility:63},attackSectors:[0,1,2,5,6,10,11,12,13,16,19,24,25,27,28,30,31,33,34,36,38,47,48,49,51,56,57,59,60,66,67,68,70,71,75,76,78,84,90,104,106,110,114,115,121,124,125,127,128,129,133,138,148,149,154,158,180,182,200,202,214,224,225,229,233,234,235,241,243,248,254,255,257,259,265]} \ No newline at end of file diff --git a/core/src/mindustry/content/Planets.java b/core/src/mindustry/content/Planets.java index c0e20a2b9a..38f5193fe2 100644 --- a/core/src/mindustry/content/Planets.java +++ b/core/src/mindustry/content/Planets.java @@ -125,7 +125,7 @@ public class Planets{ }}; serpulo = new Planet("serpulo", sun, 1f, 3){{ - attackSectorBitString = "YzyZ01aASxvcUBAgAAUMsicEMEQgAFAAAAAEASsOCsEKAg=="; + loadPlanetData = true; generator = new SerpuloPlanetGenerator(); meshLoader = () -> new HexMesh(this, 6); cloudMeshLoader = () -> new MultiMesh( diff --git a/core/src/mindustry/content/SectorPresets.java b/core/src/mindustry/content/SectorPresets.java index 7c61c02bdd..f201bc515b 100644 --- a/core/src/mindustry/content/SectorPresets.java +++ b/core/src/mindustry/content/SectorPresets.java @@ -1,6 +1,5 @@ package mindustry.content; -import mindustry.*; import mindustry.type.*; import static mindustry.content.Planets.*; @@ -40,12 +39,12 @@ public class SectorPresets{ captureWave = 33; }}; - frozenForest = new SectorPreset("frozenForest", serpulo, 86){{ + frozenForest = new SectorPreset("frozenForest", serpulo, 170){{ captureWave = 15; difficulty = 2; }}; - biomassFacility = new SectorPreset("biomassFacility", serpulo, 81){{ + biomassFacility = new SectorPreset("biomassFacility", serpulo, 63){{ captureWave = 20; difficulty = 3; }}; @@ -55,17 +54,17 @@ public class SectorPresets{ difficulty = 5; }}; - craters = new SectorPreset("craters", serpulo, 18){{ + craters = new SectorPreset("craters", serpulo, 175){{ captureWave = 20; difficulty = 2; }}; - ruinousShores = new SectorPreset("ruinousShores", serpulo, 213){{ + ruinousShores = new SectorPreset("ruinousShores", serpulo, 41){{ captureWave = 30; difficulty = 3; }}; - seaPort = new SectorPreset("seaPort", serpulo, 47){{ + seaPort = new SectorPreset("seaPort", serpulo, 98){{ difficulty = 4; }}; @@ -74,7 +73,7 @@ public class SectorPresets{ difficulty = 4; }}; - windsweptIslands = new SectorPreset("windsweptIslands", serpulo, 246){{ + windsweptIslands = new SectorPreset("windsweptIslands", serpulo, 97){{ captureWave = 30; difficulty = 4; }}; @@ -107,11 +106,11 @@ public class SectorPresets{ difficulty = 8; }}; - frontier = new SectorPreset("frontier", serpulo, 50){{ + frontier = new SectorPreset("frontier", serpulo, 41){{ difficulty = 4; }}; - fungalPass = new SectorPreset("fungalPass", serpulo, 21){{ + fungalPass = new SectorPreset("fungalPass", serpulo, 219){{ difficulty = 4; }}; @@ -119,19 +118,19 @@ public class SectorPresets{ difficulty = 4; }}; - atolls = new SectorPreset("atolls", serpulo, 1){{ + atolls = new SectorPreset("atolls", serpulo, 40){{ difficulty = 7; }}; - mycelialBastion = new SectorPreset("mycelialBastion", serpulo, 260){{ + mycelialBastion = new SectorPreset("mycelialBastion", serpulo, 23){{ difficulty = 8; }}; - overgrowth = new SectorPreset("overgrowth", serpulo, 134){{ + overgrowth = new SectorPreset("overgrowth", serpulo, 220){{ difficulty = 5; }}; - tarFields = new SectorPreset("tarFields", serpulo, 23){{ + tarFields = new SectorPreset("tarFields", serpulo, 101){{ captureWave = 40; difficulty = 5; }}; @@ -164,6 +163,7 @@ public class SectorPresets{ difficulty = 10; }}; + /* registerHiddenSectors(serpulo, 68, //Winter Forest by wpx: https://discord.com/channels/391020510269669376/1165421701362897000/1235654407006322700 241,//River Bastion by wpx: https://discord.com/channels/391020510269669376/1165421701362897000/1232658317126402050 @@ -179,7 +179,7 @@ public class SectorPresets{ Vars.content.sector("sector-serpulo-173").captureWave = 17; Vars.content.sector("sector-serpulo-240").captureWave = 40; serpulo.sectors.get(173).generateEnemyBase = false; - serpulo.sectors.get(240).generateEnemyBase = false; + serpulo.sectors.get(240).generateEnemyBase = false;*/ //endregion //region erekir diff --git a/core/src/mindustry/content/SerpuloTechTree.java b/core/src/mindustry/content/SerpuloTechTree.java index f9c02f2642..2d8a395b3c 100644 --- a/core/src/mindustry/content/SerpuloTechTree.java +++ b/core/src/mindustry/content/SerpuloTechTree.java @@ -475,6 +475,19 @@ public class SerpuloTechTree{ new Research(mace), new Research(mono) ), () -> { + node(seaPort, Seq.with( + new SectorComplete(biomassFacility), + new SectorComplete(frontier), + new Research(navalFactory), + new Research(risso), + new Research(retusa), + new Research(steamGenerator), + new Research(cultivator), + new Research(coalCentrifuge) + ), () -> { + + }); + node(overgrowth, Seq.with( new SectorComplete(frontier), new SectorComplete(windsweptIslands), @@ -519,30 +532,6 @@ public class SerpuloTechTree{ new Research(siliconSmelter), new Research(steamGenerator) ), () -> { - node(taintedWoods, Seq.with( - new SectorComplete(biomassFacility), - new SectorComplete(fungalPass), - new SectorComplete(windsweptIslands), - new Research(Items.sporePod), - new Research(Items.plastanium), - new Research(wave) - ), () -> { - - }); - - node(seaPort, Seq.with( - new SectorComplete(biomassFacility), - new SectorComplete(frontier), - new SectorComplete(fungalPass), - new Research(navalFactory), - new Research(risso), - new Research(retusa), - new Research(steamGenerator), - new Research(cultivator), - new Research(coalCentrifuge) - ), () -> { - - }); node(tarFields, Seq.with( new SectorComplete(windsweptIslands), @@ -729,6 +718,15 @@ public class SerpuloTechTree{ ), () -> { }); + + node(taintedWoods, Seq.with( + new SectorComplete(infestedCanyons), + new Research(Items.sporePod), + new Research(Items.plastanium), + new Research(wave) + ), () -> { + + }); }); diff --git a/core/src/mindustry/type/Planet.java b/core/src/mindustry/type/Planet.java index 9653fa21d5..c12e0a912b 100644 --- a/core/src/mindustry/type/Planet.java +++ b/core/src/mindustry/type/Planet.java @@ -2,6 +2,7 @@ package mindustry.type; import arc.*; import arc.audio.*; +import arc.files.*; import arc.func.*; import arc.graphics.*; import arc.graphics.g3d.*; @@ -11,7 +12,6 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import arc.util.noise.*; -import arc.util.serialization.*; import mindustry.content.*; import mindustry.content.TechTree.*; import mindustry.ctype.*; @@ -153,8 +153,6 @@ public class Planet extends UnlockableContent{ public boolean allowSelfSectorLaunch; /** If true, all content in this planet's tech tree will be assigned this planet in their shownPlanets. */ public boolean autoAssignPlanet = true; - /** Base64 encoded string to use as data for setting generateAttackSector status. See {@link #writeAttackSectorBits()}}*/ - public @Nullable String attackSectorBitString; /** Content (usually planet-specific) that is unlocked upon landing here. */ public Seq unlockedOnLand = new Seq<>(); /** Loads the mesh. Clientside only. Defaults to a boring sphere mesh. */ @@ -171,6 +169,11 @@ public class Planet extends UnlockableContent{ /** If true, RTS AI can be customized. */ public boolean showRtsAIRule = false; + /** If true, planet data is loaded as 'planets/{name}.json'. This is only tested/functional in vanilla! */ + public boolean loadPlanetData = false; + /** Data indicating attack sector positions and sector mappings. */ + public @Nullable PlanetData data; + public Planet(String name, Planet parent, float radius){ super(name); @@ -386,20 +389,28 @@ public class Planet extends UnlockableContent{ generator.generateSector(sector); } - if(attackSectorBitString != null){ - try{ - loadAttackBits(attackSectorBitString); - }catch(Exception e){ - Log.err(e); - } - } - updateBaseCoverage(); } clipRadius = Math.max(clipRadius, radius + atmosphereRadOut + 0.5f); } + public PlanetData getData(){ + if(loadPlanetData && data == null){ + Fi file = tree.get("planets/" + name + ".json"); + if(file.exists()){ + data = JsonIO.read(PlanetData.class, file.readString()); + for(int i : data.attackSectors){ + if(i >= 0 && i < sectors.size){ + sectors.get(i).generateEnemyBase = true; + } + } + } + } + + return data; + } + /** Gets a sector a tile position. */ public Sector getSector(Ptile tile){ return sectors.get(tile.id); @@ -592,24 +603,8 @@ public class Planet extends UnlockableContent{ ); } - public String writeAttackSectorBits(){ - byte[] bits = new byte[Mathf.ceil(sectors.size / 8f)]; - for(int i = 0; i < sectors.size; i++){ - int bit = (i >> 3), mask = (i & 0b111); - if(sectors.get(i).generateEnemyBase){ - bits[bit] |= (1 << mask); - } - } - return new String(Base64Coder.encode(bits)); - } - - public void loadAttackBits(String str){ - byte[] bits = Base64Coder.decode(str); - for(int i = 0; i < sectors.size; i++){ - int bit = (i >> 3), mask = (i & 0b111); - if(bit < bits.length && (bits[bit] & (1 << mask)) != 0){ - sectors.get(i).generateEnemyBase = true; - } - } + public static class PlanetData{ + public ObjectIntMap presets = new ObjectIntMap<>(); + public int[] attackSectors = {}; } } diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index 1c2079e289..93c52a2b54 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -152,7 +152,7 @@ public class Sector{ @Nullable public TextureRegion icon(){ - return info.contentIcon != null ? info.contentIcon.uiIcon : info.icon == null ? null : Fonts.getLargeIcon(info.icon); + return info.contentIcon != null ? info.contentIcon.uiIcon : info.icon == null ? (preset != null && preset.uiIcon.found() ? preset.uiIcon : null) : Fonts.getLargeIcon(info.icon); } @Nullable diff --git a/core/src/mindustry/type/SectorPreset.java b/core/src/mindustry/type/SectorPreset.java index 958856633c..ea00349fa2 100644 --- a/core/src/mindustry/type/SectorPreset.java +++ b/core/src/mindustry/type/SectorPreset.java @@ -61,6 +61,11 @@ public class SectorPreset extends UnlockableContent{ public void initialize(Planet planet, int sector){ this.planet = planet; + //auto remap based on data + var data = planet.getData(); + if(data != null){ + sector = data.presets.get(name, sector); + } sector %= planet.sectors.size; this.sector = planet.sectors.get(sector); diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index ce23e66abc..8436b3930a 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -31,8 +31,10 @@ import mindustry.graphics.*; import mindustry.graphics.g3d.PlanetGrid.*; import mindustry.graphics.g3d.*; import mindustry.input.*; +import mindustry.io.*; import mindustry.maps.*; import mindustry.type.*; +import mindustry.type.Planet.*; import mindustry.ui.*; import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.CoreBlock.*; @@ -71,6 +73,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ private Texture[] planetTextures; private Element mainView; private CampaignRulesDialog campaignRules = new CampaignRulesDialog(); + private SectorSelectDialog selectDialog = new SectorSelectDialog(); public PlanetDialog(){ super("", Styles.fullDialog); @@ -532,7 +535,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ @Override public void renderProjections(Planet planet){ - float iw = 48f/4f; + float iw = 64f/4f; for(Sector sec : planet.sectors){ if(sec != hovered){ @@ -540,10 +543,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ var icon = sec.isAttacked() ? Fonts.getLargeIcon("warning") : !sec.hasBase() && sec.preset != null && sec.preset.requireUnlock && sec.preset.unlocked() && preficon == null ? - Fonts.getLargeIcon("terrain") : + sec.preset != null ? sec.preset.uiIcon : Fonts.getLargeIcon("terrain") : sec.preset != null && sec.preset.requireUnlock && sec.preset.locked() && sec.preset.techNode != null && (sec.preset.techNode.parent == null || !sec.preset.techNode.parent.content.locked()) ? Fonts.getLargeIcon("lock") : preficon; - var color = sec.preset != null && sec.preset.requireUnlock && !sec.hasBase() ? Team.derelict.color : Team.sharded.color; + var color = sec.isAttacked() ? Team.sharded.color : Color.white; if(icon != null){ planets.drawPlane(sec, () -> { @@ -622,8 +625,24 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ public void touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){ super.touchDown(event, x, y, pointer, button); - if(debugSectorAttackEdit && button == KeyCode.mouseRight && hovered != null){ - hovered.generateEnemyBase = !hovered.generateEnemyBase; + var hovered = PlanetDialog.this.hovered; + + if(debugSectorAttackEdit && hovered != null){ + if(button == KeyCode.mouseRight){ + if(input.shift()){ + hovered.generateEnemyBase = !hovered.generateEnemyBase; + }else{ + selectDialog.show(state.planet, result -> { + for(var other : state.planet.sectors){ + if(other.preset == result){ + other.preset = null; + } + } + hovered.preset = result; + }); + } + } + } } }); @@ -634,9 +653,26 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ if(scene.getDialog() == PlanetDialog.this && (scene.getHoverElement() == null || !scene.getHoverElement().isDescendantOf(e -> e instanceof ScrollPane))){ scene.setScrollFocus(PlanetDialog.this); - if(debugSectorAttackEdit && input.ctrl() && input.keyTap(KeyCode.c)){ - Core.app.setClipboardText(state.planet.writeAttackSectorBits()); - Vars.ui.showInfoFade("@copied"); + if(debugSectorAttackEdit && input.ctrl() && input.keyTap(KeyCode.s)){ + try{ + PlanetData data = new PlanetData(); + IntSeq attack = new IntSeq(); + for(var sector : state.planet.sectors){ + if(sector.preset == null && sector.generateEnemyBase){ + attack.add(sector.id); + } + + if(sector.preset != null && sector.preset.requireUnlock){ + data.presets.put(sector.preset.name, sector.id); + } + } + data.attackSectors = attack.toArray(); + files.local("planets/" + state.planet.name + ".json").writeString(JsonIO.write(data)); + + Vars.ui.showInfoFade("@editor.saved"); + }catch(Exception e){ + Log.err(e); + } } } @@ -1123,7 +1159,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ stable.background(Styles.black6); stable.table(title -> { - title.add("[accent]" + sector.name()).padLeft(3); + title.add("[accent]" + sector.name() + (debugSelect && (sector.info.name != null || sector.preset != null) ? " [lightgray](" + sector.id + ")" : "")).padLeft(3); if(sector.preset == null){ title.add().growX(); diff --git a/core/src/mindustry/ui/dialogs/SectorSelectDialog.java b/core/src/mindustry/ui/dialogs/SectorSelectDialog.java new file mode 100644 index 0000000000..7624f8005b --- /dev/null +++ b/core/src/mindustry/ui/dialogs/SectorSelectDialog.java @@ -0,0 +1,83 @@ +package mindustry.ui.dialogs; + +import arc.*; +import arc.func.*; +import arc.input.*; +import arc.scene.style.*; +import arc.scene.ui.*; +import arc.scene.ui.layout.*; +import mindustry.*; +import mindustry.content.*; +import mindustry.gen.*; +import mindustry.type.*; +import mindustry.ui.*; + +import java.util.*; + +//internal use only! +public class SectorSelectDialog extends BaseDialog{ + Table sectors = new Table(); + Planet planet = Planets.serpulo; + Cons cons = s -> {}; + TextField search; + + public SectorSelectDialog(){ + super("@content.sector.name"); + + cont.top(); + cont.table(s -> { + s.image(Icon.zoom); + search = s.field("", ignored -> { + rebuild(); + }).width(300f).get(); + search.keyDown(KeyCode.enter, () -> { + String text = search.getText().toLowerCase(Locale.ROOT); + var found = Vars.content.sectors().find(sec -> matches(sec, text)); + if(found != null){ + cons.get(found); + hide(); + } + }); + }); + cont.row(); + + cont.pane(sectors).grow().top(); + sectors.top(); + + addCloseButton(); + + shown(() -> { + search.clearText(); + search.requestKeyboard(); + Core.app.post(() -> search.requestKeyboard()); + rebuild(); + }); + } + + public void show(Planet planet, Cons cons){ + this.planet = planet; + this.cons = cons; + + show(); + } + + void rebuild(){ + sectors.clear(); + + String text = search.getText().toLowerCase(Locale.ROOT); + + for(var sector : Vars.content.sectors()){ + if(matches(sector, text)){ + sectors.button(sector.localizedName, new TextureRegionDrawable(sector.uiIcon), Styles.grayt, 32f, () -> { + cons.get(sector); + hide(); + }).size(400f, 50f).margin(4f).pad(3f); + sectors.row(); + } + } + } + + boolean matches(SectorPreset sector, String text){ + return sector.planet == planet && (text.isEmpty() || sector.name.toLowerCase(Locale.ROOT).contains(text) || sector.localizedName.toLowerCase(Locale.ROOT).contains(text)); + } +}