Core Database Content Classification Subdivison (#11506)

* test

* test

* test

* Core database content subdivision

* Core database content subdivision

* undo some old change

* Revert "test"

This reverts commit ada9dca787.

* Revert "test"

This reverts commit d7c81185

* undo some old change

* cleanup

* Update core/assets/bundles/bundle.properties

* Format and Cleanup

* Format and Cleanup

* gradle toggle

* Update core/assets/bundles/bundle.properties

* Update core/assets/bundles/bundle.properties

* Update core/assets/bundles/bundle.properties

---------

Co-authored-by: Anuken <arnukren@gmail.com>
This commit is contained in:
酪桦姬
2025-12-30 14:08:00 +08:00
committed by GitHub
parent b143b05ad7
commit 4981300476
5 changed files with 165 additions and 54 deletions

View File

@@ -1538,13 +1538,27 @@ rules.randomwaveai.info = Makes units spawned in waves target random structures
rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy. rules.placerangecheck.info = Prevents players from placing anything near enemy buildings. When trying to place a turret, the range is increased, so the turret will not be able to reach the enemy.
rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores. rules.onlydepositcore.info = Prevents units from depositing items into any buildings except cores.
content.item.name = Items database-category.item = Items
content.liquid.name = Fluids database-category.liquid = Fluids
content.unit.name = Units database-category.unit = Units
content.block.name = Blocks database-category.block = Blocks
content.status.name = Status Effects database-category.status = Status Effects
content.sector.name = Sectors database-category.sector = Sectors
content.team.name = Factions database-category.team = Factions
database-tag.turret = Turret
database-tag.production = Production
database-tag.distribution = Distribution
database-tag.liquid = Liquid
database-tag.power = Power
database-tag.defense = Defense
database-tag.crafting = Crafting
database-tag.units = Units
database-tag.effect = Effect
database-tag.logic = Logic
database-tag.unit-air = Air
database-tag.unit-naval = Naval
database-tag.unit-ground = Ground
wallore = (Wall) wallore = (Wall)

View File

@@ -61,6 +61,19 @@ public abstract class UnlockableContent extends MappableContent{
* If shownPlanets is also empty, it will use Serpulo as the "default" tab. * If shownPlanets is also empty, it will use Serpulo as the "default" tab.
* */ * */
public ObjectSet<UnlockableContent> databaseTabs = new ObjectSet<>(); public ObjectSet<UnlockableContent> databaseTabs = new ObjectSet<>();
/**
* Content category. Defines the primary category of content classification in core database.
* For example, "block", "liquid", "unit".
* Uses getContentType().name() as a fallback when the value is null or empty.
* */
public @Nullable String databaseCategory;
/**
* Category tags. Secondary category of content classification in core database.
* For example, "turret", "wall" under databaseCategory "block", "core-unit", "ground-unit" under databaseCategory "units".
* Uses "default" as a fallback when the value is null or empty. When using "default", no extra tag label are displayed.
* */
public @Nullable String databaseTag;
/** The tech tree node for this content, if applicable. Null if not part of a tech tree. */ /** The tech tree node for this content, if applicable. Null if not part of a tech tree. */
@NoPatch @NoPatch
public @Nullable TechNode techNode; public @Nullable TechNode techNode;
@@ -84,6 +97,9 @@ public abstract class UnlockableContent extends MappableContent{
public void postInit(){ public void postInit(){
super.postInit(); super.postInit();
if(databaseCategory == null || databaseCategory.isEmpty()) databaseCategory = getContentType().name();
if(databaseTag == null || databaseTag.isEmpty()) databaseTag = "default";
databaseTabs.addAll(shownPlanets); databaseTabs.addAll(shownPlanets);
} }

View File

@@ -529,6 +529,21 @@ public class UnitType extends UnlockableContent implements Senseable{
selectionSize = 30f; selectionSize = 30f;
} }
@Override
public void postInit(){
if(databaseTag == null || databaseTag.isEmpty()){
if(flying){
databaseTag = "unit-air";
}else if(naval){
databaseTag = "unit-naval";
}else{
databaseTag = "unit-ground";
}
}
super.postInit();
}
public UnitController createController(Unit unit){ public UnitController createController(Unit unit){
return controller.get(unit); return controller.get(unit);
} }

View File

@@ -22,6 +22,9 @@ import static arc.Core.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
public class DatabaseDialog extends BaseDialog{ public class DatabaseDialog extends BaseDialog{
private final OrderedMap<String, OrderedMap<String, Seq<UnlockableContent>>> sortedContents = new OrderedMap<>();
private final OrderedMap<String, Seq<UnlockableContent>> tmpCategory = new OrderedMap<>();
private TextField search; private TextField search;
private Table all = new Table(); private Table all = new Table();
@@ -36,6 +39,7 @@ public class DatabaseDialog extends BaseDialog{
addCloseButton(); addCloseButton();
shown(() -> { shown(() -> {
checkTabList(); checkTabList();
sortContents();
if(state.isCampaign() && allTabs.contains(state.getPlanet())){ if(state.isCampaign() && allTabs.contains(state.getPlanet())){
tab = state.getPlanet(); tab = state.getPlanet();
}else if(state.isGame() && state.rules.planet != null && allTabs.contains(state.rules.planet)){ }else if(state.isGame() && state.rules.planet != null && allTabs.contains(state.rules.planet)){
@@ -74,14 +78,28 @@ public class DatabaseDialog extends BaseDialog{
} }
} }
void sortContents(){
Seq<Content>[] allContent = Vars.content.getContentMap();
sortedContents.clear();
for(var contents : allContent){
for(var content : contents){
if(content instanceof UnlockableContent u){
var categoryContents = sortedContents.get(u.databaseCategory, new OrderedMap<>());
var taggedContents = categoryContents.get(u.databaseTag, new Seq<>());
taggedContents.add(u);
categoryContents.put(u.databaseTag, taggedContents);
sortedContents.put(u.databaseCategory, categoryContents);
}
}
}
}
void rebuild(){ void rebuild(){
checkTabList(); checkTabList();
all.clear(); all.clear();
var text = search.getText().toLowerCase(); var text = search.getText().toLowerCase();
Seq<Content>[] allContent = Vars.content.getContentMap();
all.table(t -> { all.table(t -> {
int i = 0; int i = 0;
for(var content : allTabs){ for(var content : allTabs){
@@ -96,71 +114,115 @@ public class DatabaseDialog extends BaseDialog{
} }
}).row(); }).row();
for(int j = 0; j < allContent.length; j++){ boolean hasResult = false;
ContentType type = ContentType.all[j];
Seq<UnlockableContent> array = allContent[j] for(int i = 0; i < sortedContents.size; i++){
.select(c -> c instanceof UnlockableContent u && !u.isHidden() && !u.hideDatabase && (tab == Planets.sun || u.allDatabaseTabs || u.databaseTabs.contains(tab)) && String categoryName = sortedContents.orderedKeys().get(i);
(text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as(); OrderedMap<String, Seq<UnlockableContent>> categoryContents = sortedContents.get(categoryName);
if(array.size == 0) continue; tmpCategory.clear();
//sorting only makes sense when in-game; otherwise, banned blocks can't exist boolean categoryHasResult = false;
if(state.isGame()){
array.sort(Structs.comps(Structs.comparingBool(UnlockableContent::isBanned), Structs.comparingInt(u -> u.id))); for(int j = 0; j < categoryContents.size; j++){
String tagName = categoryContents.orderedKeys().get(j);
Seq<UnlockableContent> array = categoryContents.get(tagName).select(u ->
!u.isHidden() && !u.hideDatabase &&
(tab == Planets.sun || u.allDatabaseTabs || u.databaseTabs.contains(tab)) &&
(text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as();
if(array.isEmpty()) continue;
hasResult = true;
categoryHasResult = true;
//sorting only makes sense when in-game; otherwise, banned blocks can't exist
if(state.isGame()){
array.sort(Structs.comps(Structs.comparingBool(UnlockableContent::isBanned), Structs.comparingInt(u -> u.id)));
}
tmpCategory.put(tagName, array);
} }
all.add("@content." + type.name() + ".name").growX().left().color(Pal.accent); if(tmpCategory.isEmpty() || !categoryHasResult) continue;
all.add("@database-category." + categoryName).growX().left().color(Pal.accent);
all.row(); all.row();
all.image().growX().pad(5).padLeft(0).padRight(0).height(3).color(Pal.accent); all.image().pad(5).padLeft(0).padRight(0).height(3).color(Pal.accent).growX();
all.row(); all.row();
all.table(list -> {
list.left();
int cols = (int)Mathf.clamp((Core.graphics.getWidth() - Scl.scl(30)) / Scl.scl(32 + 12), 1, 22); all.table(sub -> {
int count = 0; for(int j = 0; j < tmpCategory.size; j++){
String tagName = categoryContents.orderedKeys().get(j);
Seq<UnlockableContent> array = tmpCategory.get(tagName);
if(array == null || array.isEmpty()) continue;
for(var unlock : array){ if(!"default".equals(tagName)){
Image image = unlocked(unlock) ? new Image(new TextureRegionDrawable(unlock.uiIcon), mobile ? Color.white : Color.lightGray).setScaling(Scaling.fit) : new Image(Icon.lock, Pal.gray); sub.table(tag -> {
tag.add("@database-tag." + tagName).left().color(Pal.gray);
//banned cross tag.image().growX().pad(5).height(3).color(Pal.gray);
if(state.isGame() && unlock.isBanned()){ }).pad(4, 8, 4, 8).growX();
list.stack(image, new Image(Icon.cancel){{ sub.row();
setColor(Color.scarlet);
touchable = Touchable.disabled;
}}).size(8 * 4).pad(3);
}else{
list.add(image).size(8 * 4).pad(3);
} }
ClickListener listener = new ClickListener(); sub.table(list -> {
image.addListener(listener); list.left();
if(!mobile && unlocked(unlock)){
image.addListener(new HandCursorListener());
image.update(() -> image.color.lerp(!listener.isOver() ? Color.lightGray : Color.white, Mathf.clamp(0.4f * Time.delta)));
}
if(unlocked(unlock)){ int cols = (int)Mathf.clamp((Core.graphics.getWidth() - Scl.scl(30)) / Scl.scl(32 + 12), 1, 22);
image.clicked(() -> { int count = 0;
if(Core.input.keyDown(KeyCode.shiftLeft) && Fonts.getUnicode(unlock.name) != 0){
Core.app.setClipboardText((char)Fonts.getUnicode(unlock.name) + ""); for(var unlock : array){
ui.showInfoFade("@copied"); Image image = unlocked(unlock) ? new Image(new TextureRegionDrawable(unlock.uiIcon), mobile ? Color.white : Color.lightGray).setScaling(Scaling.fit) : new Image(Icon.lock, Pal.gray);
//banned cross
if(state.isGame() && unlock.isBanned()){
list.stack(image, new Image(Icon.cancel){{
setColor(Color.scarlet);
touchable = Touchable.disabled;
}}).size(8 * 4).pad(3);
}else{ }else{
ui.content.show(unlock); list.add(image).size(8 * 4).pad(3);
} }
});
image.addListener(new Tooltip(t -> t.background(Tex.button).add(unlock.localizedName + (settings.getBool("console") ? "\n[gray]" + unlock.name : ""))));
}
if((++count) % cols == 0){ ClickListener listener = new ClickListener();
list.row(); image.addListener(listener);
} if(!mobile && unlocked(unlock)){
image.addListener(new HandCursorListener());
image.update(() -> image.color.lerp(!listener.isOver() ? Color.lightGray : Color.white, Mathf.clamp(0.4f * Time.delta)));
}
if(unlocked(unlock)){
image.clicked(() -> {
if(Core.input.keyDown(KeyCode.shiftLeft) && Fonts.getUnicode(unlock.name) != 0){
Core.app.setClipboardText((char)Fonts.getUnicode(unlock.name) + "");
ui.showInfoFade("@copied");
}else{
ui.content.show(unlock);
}
});
image.addListener(new Tooltip(t -> t.background(Tex.button).add(unlock.localizedName + (settings.getBool("console") ? "\n[gray]" + unlock.name : ""))));
}
if((++count) % cols == 0){
list.row();
}
}
for(int k = 0; k < cols - count; k++){
Image image = new Image();
image.setColor(Color.clear);
list.add(image).size(8 * 4).pad(3);
}
});
sub.row();
} }
}).growX().left().padBottom(10); }).growX().left().padBottom(10);
all.row(); all.row();
} }
if(all.getChildren().isEmpty()){ if(!hasResult){
all.add("@none.found"); all.add("@none.found");
} }
} }

View File

@@ -1251,6 +1251,10 @@ public class Block extends UnlockableContent implements Senseable{
} }
} }
if(databaseTag == null || databaseTag.isEmpty()){
databaseTag = category.name();
}
super.postInit(); super.postInit();
} }