Allow research dialog to be viewed on clients

This commit is contained in:
Anuken
2024-10-14 00:11:05 -04:00
parent 5d4ece62d0
commit e262bb8254
6 changed files with 136 additions and 114 deletions
-1
View File
@@ -676,7 +676,6 @@ requirement.capture = Capture {0}
requirement.onplanet = Control Sector On {0} requirement.onplanet = Control Sector On {0}
requirement.onsector = Land On Sector: {0} requirement.onsector = Land On Sector: {0}
launch.text = Launch launch.text = Launch
research.multiplayer = Only the host can research items.
map.multiplayer = Only the host can view sectors. map.multiplayer = Only the host can view sectors.
uncover = Uncover uncover = Uncover
configure = Configure Loadout configure = Configure Loadout
@@ -27,6 +27,7 @@ import static mindustry.Vars.*;
public class ContentLoader{ public class ContentLoader{
private ObjectMap<String, MappableContent>[] contentNameMap = new ObjectMap[ContentType.all.length]; private ObjectMap<String, MappableContent>[] contentNameMap = new ObjectMap[ContentType.all.length];
private Seq<Content>[] contentMap = new Seq[ContentType.all.length]; private Seq<Content>[] contentMap = new Seq[ContentType.all.length];
private ObjectMap<String, MappableContent> nameMap = new ObjectMap<>();
private MappableContent[][] temporaryMapper; private MappableContent[][] temporaryMapper;
private @Nullable LoadedMod currentMod; private @Nullable LoadedMod currentMod;
private @Nullable Content lastAdded; private @Nullable Content lastAdded;
@@ -188,12 +189,18 @@ public class ContentLoader{
} }
} }
contentNameMap[content.getContentType().ordinal()].put(content.name, content); contentNameMap[content.getContentType().ordinal()].put(content.name, content);
nameMap.put(content.name, content);
} }
public void setTemporaryMapper(MappableContent[][] temporaryMapper){ public void setTemporaryMapper(MappableContent[][] temporaryMapper){
this.temporaryMapper = temporaryMapper; this.temporaryMapper = temporaryMapper;
} }
/** @return the last registered content with the specified name. Note that the content loader makes no attempt to resolve name conflicts. This method can be unreliable. */
public @Nullable MappableContent byName(String name){
return nameMap.get(name);
}
public Seq<Content>[] getContentMap(){ public Seq<Content>[] getContentMap(){
return contentMap; return contentMap;
} }
+1 -1
View File
@@ -394,7 +394,7 @@ public class Logic implements ApplicationListener{
public static void researched(Content content){ public static void researched(Content content){
if(!(content instanceof UnlockableContent u)) return; if(!(content instanceof UnlockableContent u)) return;
boolean was = u.unlockedNow(); boolean was = u.unlockedNowHost();
state.rules.researched.add(u); state.rules.researched.add(u);
if(!was){ if(!was){
+4 -4
View File
@@ -19,14 +19,14 @@ public class Objectives{
@Override @Override
public boolean complete(){ public boolean complete(){
return content.unlocked(); return content.unlockedHost();
} }
@Override @Override
public String display(){ public String display(){
return Core.bundle.format("requirement.research", return Core.bundle.format("requirement.research",
//TODO broken for multi tech nodes. //TODO broken for multi tech nodes.
(content.techNode == null || content.techNode.parent == null || content.techNode.parent.content.unlocked()) ? (content.techNode == null || content.techNode.parent == null || content.techNode.parent.content.unlockedHost()) ?
(content.emoji() + " " + content.localizedName) : "???"); (content.emoji() + " " + content.localizedName) : "???");
} }
} }
@@ -42,13 +42,13 @@ public class Objectives{
@Override @Override
public boolean complete(){ public boolean complete(){
return content.unlocked(); return content.unlockedHost();
} }
@Override @Override
public String display(){ public String display(){
return Core.bundle.format("requirement.produce", return Core.bundle.format("requirement.produce",
content.unlocked() ? (content.emoji() + " " + content.localizedName) : "???"); content.unlockedHost() ? (content.emoji() + " " + content.localizedName) : "???");
} }
} }
+2 -9
View File
@@ -261,15 +261,8 @@ public class JsonIO{
public UnlockableContent read(Json json, JsonValue jsonData, Class type){ public UnlockableContent read(Json json, JsonValue jsonData, Class type){
if(jsonData.isNull()) return null; if(jsonData.isNull()) return null;
String str = jsonData.asString(); String str = jsonData.asString();
Item item = Vars.content.item(str); var map = Vars.content.byName(str);
Liquid liquid = Vars.content.liquid(str); return map instanceof UnlockableContent u ? u : null;
Block block = Vars.content.block(str);
UnitType unit = Vars.content.unit(str);
return
item != null ? item :
liquid != null ? liquid :
block != null ? block :
unit;
} }
}); });
+122 -99
View File
@@ -47,10 +47,30 @@ public class ResearchDialog extends BaseDialog{
public ItemSeq items; public ItemSeq items;
private boolean showTechSelect; private boolean showTechSelect;
private boolean needsRebuild;
public ResearchDialog(){ public ResearchDialog(){
super(""); super("");
Events.on(ResetEvent.class, e -> {
hide();
});
Events.on(UnlockEvent.class, e -> {
if(net.client() && !needsRebuild){
needsRebuild = true;
Core.app.post(() -> {
needsRebuild = false;
checkNodes(root);
view.hoverNode = null;
treeLayout();
view.rebuild();
Core.scene.act();
});
}
});
titleTable.remove(); titleTable.remove();
titleTable.clear(); titleTable.clear();
titleTable.top(); titleTable.top();
@@ -67,7 +87,7 @@ public class ResearchDialog extends BaseDialog{
t.table(Tex.button, in -> { t.table(Tex.button, in -> {
in.defaults().width(300f).height(60f); in.defaults().width(300f).height(60f);
for(TechNode node : TechTree.roots){ for(TechNode node : TechTree.roots){
if(node.requiresUnlock && !node.content.unlocked() && node != getPrefRoot()) continue; if(node.requiresUnlock && !node.content.unlockedHost() && node != getPrefRoot()) continue;
//TODO toggle //TODO toggle
in.button(node.localizedName(), node.icon(), Styles.flatTogglet, iconMed, () -> { in.button(node.localizedName(), node.icon(), Styles.flatTogglet, iconMed, () -> {
@@ -84,10 +104,11 @@ public class ResearchDialog extends BaseDialog{
addCloseButton(); addCloseButton();
}}.show(); }}.show();
}).visible(() -> showTechSelect = TechTree.roots.count(node -> !(node.requiresUnlock && !node.content.unlocked())) > 1).minWidth(300f); }).visible(() -> showTechSelect = TechTree.roots.count(node -> !(node.requiresUnlock && !node.content.unlockedHost())) > 1).minWidth(300f);
margin(0f).marginBottom(8); margin(0f).marginBottom(8);
cont.stack(titleTable, view = new View(), itemDisplay = new ItemsDisplay()).grow(); cont.stack(titleTable, view = new View(), itemDisplay = new ItemsDisplay()).grow();
itemDisplay.visible(() -> !net.client());
titleTable.toFront(); titleTable.toFront();
@@ -177,15 +198,6 @@ public class ResearchDialog extends BaseDialog{
}); });
} }
@Override
public Dialog show(){
if(net.client()){
ui.showInfo("@research.multiplayer");
return this;
}
return show(Core.scene);
}
void checkMargin(){ void checkMargin(){
if(Core.graphics.isPortrait() && showTechSelect){ if(Core.graphics.isPortrait() && showTechSelect){
itemDisplay.marginTop(60f); itemDisplay.marginTop(60f);
@@ -361,11 +373,12 @@ public class ResearchDialog extends BaseDialog{
} }
boolean selectable(TechNode node){ boolean selectable(TechNode node){
return node.content.unlocked() || !node.objectives.contains(i -> !i.complete()); //there's a desync here as far as sectors go, since the client doesn't know about that, but I'm not too concerned
return node.content.unlockedHost() || !node.objectives.contains(i -> !i.complete());
} }
boolean locked(TechNode node){ boolean locked(TechNode node){
return node.content.locked(); return !node.content.unlockedHost();
} }
class LayoutNode extends TreeNode<LayoutNode>{ class LayoutNode extends TreeNode<LayoutNode>{
@@ -418,31 +431,34 @@ public class ResearchDialog extends BaseDialog{
button.resizeImage(32f); button.resizeImage(32f);
button.getImage().setScaling(Scaling.fit); button.getImage().setScaling(Scaling.fit);
button.visible(() -> node.visible); button.visible(() -> node.visible);
button.clicked(() -> { if(!net.client()){
if(moved) return; button.clicked(() -> {
if(moved) return;
if(mobile){ if(mobile){
hoverNode = button; hoverNode = button;
rebuild(); rebuild();
float right = infoTable.getRight(); float right = infoTable.getRight();
if(right > Core.graphics.getWidth()){ if(right > Core.graphics.getWidth()){
float moveBy = right - Core.graphics.getWidth(); float moveBy = right - Core.graphics.getWidth();
addAction(new RelativeTemporalAction(){ addAction(new RelativeTemporalAction(){
{ {
setDuration(0.1f); setDuration(0.1f);
setInterpolation(Interp.fade); setInterpolation(Interp.fade);
} }
@Override @Override
protected void updateRelative(float percentDelta){ protected void updateRelative(float percentDelta){
panX -= moveBy * percentDelta; panX -= moveBy * percentDelta;
} }
}); });
}
}else if(canSpend(node.node) && locked(node.node)){
spend(node.node);
} }
}else if(canSpend(node.node) && locked(node.node)){ });
spend(node.node); }
}
});
button.hovered(() -> { button.hovered(() -> {
if(!mobile && hoverNode != button && node.visible){ if(!mobile && hoverNode != button && node.visible){
hoverNode = button; hoverNode = button;
@@ -459,6 +475,7 @@ public class ResearchDialog extends BaseDialog{
button.userObject = node.node; button.userObject = node.node;
button.setSize(nodeSize); button.setSize(nodeSize);
button.update(() -> { button.update(() -> {
button.setDisabled(net.client() && !mobile);
float offset = (Core.graphics.getHeight() % 2) / 2f; float offset = (Core.graphics.getHeight() % 2) / 2f;
button.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f + offset, Align.center); button.setPosition(node.x + panX + width / 2f, node.y + panY + height / 2f + offset, Align.center);
button.getStyle().up = !locked(node.node) ? Tex.buttonOver : !selectable(node.node) || !canSpend(node.node) ? Tex.buttonRed : Tex.button; button.getStyle().up = !locked(node.node) ? Tex.buttonOver : !selectable(node.node) || !canSpend(node.node) ? Tex.buttonRed : Tex.button;
@@ -498,7 +515,7 @@ public class ResearchDialog extends BaseDialog{
} }
boolean canSpend(TechNode node){ boolean canSpend(TechNode node){
if(!selectable(node)) return false; if(!selectable(node) || net.client()) return false;
if(node.requirements.length == 0) return true; if(node.requirements.length == 0) return true;
@@ -514,6 +531,8 @@ public class ResearchDialog extends BaseDialog{
} }
void spend(TechNode node){ void spend(TechNode node){
if(net.client()) return;
boolean complete = true; boolean complete = true;
boolean[] shine = new boolean[node.requirements.length]; boolean[] shine = new boolean[node.requirements.length];
@@ -611,86 +630,90 @@ public class ResearchDialog extends BaseDialog{
desc.left().defaults().left(); desc.left().defaults().left();
desc.add(selectable ? node.content.localizedName : "[accent]???"); desc.add(selectable ? node.content.localizedName : "[accent]???");
desc.row(); desc.row();
if(locked(node) || debugShowRequirements){ if(locked(node) || (debugShowRequirements && !net.client())){
desc.table(t -> { if(net.client()){
t.left(); desc.add("@locked").color(Pal.remove);
if(selectable){ }else{
desc.table(t -> {
t.left();
if(selectable){
//check if there is any progress, add research progress text //check if there is any progress, add research progress text
if(Structs.contains(node.finishedRequirements, s -> s.amount > 0)){ if(Structs.contains(node.finishedRequirements, s -> s.amount > 0)){
float sum = 0f, used = 0f; float sum = 0f, used = 0f;
boolean shiny = false; boolean shiny = false;
for(int i = 0; i < node.requirements.length; i++){ for(int i = 0; i < node.requirements.length; i++){
sum += node.requirements[i].item.cost * node.requirements[i].amount; sum += node.requirements[i].item.cost * node.requirements[i].amount;
used += node.finishedRequirements[i].item.cost * node.finishedRequirements[i].amount; used += node.finishedRequirements[i].item.cost * node.finishedRequirements[i].amount;
if(shine != null) shiny |= shine[i]; if(shine != null) shiny |= shine[i];
} }
Label label = t.add(Core.bundle.format("research.progress", Math.min((int)(used / sum * 100), 99))).left().get(); Label label = t.add(Core.bundle.format("research.progress", Math.min((int)(used / sum * 100), 99))).left().get();
if(shiny){
label.setColor(Pal.accent);
label.actions(Actions.color(Color.lightGray, 0.75f, Interp.fade));
}else{
label.setColor(Color.lightGray);
}
t.row();
}
for(int i = 0; i < node.requirements.length; i++){
ItemStack req = node.requirements[i];
ItemStack completed = node.finishedRequirements[i];
//skip finished stacks
if(req.amount <= completed.amount && !debugShowRequirements) continue;
boolean shiny = shine != null && shine[i];
t.table(list -> {
int reqAmount = debugShowRequirements ? req.amount : req.amount - completed.amount;
list.left();
list.image(req.item.uiIcon).size(8 * 3).padRight(3);
list.add(req.item.localizedName).color(Color.lightGray);
Label label = list.label(() -> " " +
UI.formatAmount(Math.min(items.get(req.item), reqAmount)) + " / "
+ UI.formatAmount(reqAmount)).get();
Color targetColor = items.has(req.item) ? Color.lightGray : Color.scarlet;
if(shiny){ if(shiny){
label.setColor(Pal.accent); label.setColor(Pal.accent);
label.actions(Actions.color(targetColor, 0.75f, Interp.fade)); label.actions(Actions.color(Color.lightGray, 0.75f, Interp.fade));
}else{ }else{
label.setColor(targetColor); label.setColor(Color.lightGray);
} }
}).fillX().left(); t.row();
}
for(int i = 0; i < node.requirements.length; i++){
ItemStack req = node.requirements[i];
ItemStack completed = node.finishedRequirements[i];
//skip finished stacks
if(req.amount <= completed.amount && !debugShowRequirements) continue;
boolean shiny = shine != null && shine[i];
t.table(list -> {
int reqAmount = debugShowRequirements ? req.amount : req.amount - completed.amount;
list.left();
list.image(req.item.uiIcon).size(8 * 3).padRight(3);
list.add(req.item.localizedName).color(Color.lightGray);
Label label = list.label(() -> " " +
UI.formatAmount(Math.min(items.get(req.item), reqAmount)) + " / "
+ UI.formatAmount(reqAmount)).get();
Color targetColor = items.has(req.item) ? Color.lightGray : Color.scarlet;
if(shiny){
label.setColor(Pal.accent);
label.actions(Actions.color(targetColor, 0.75f, Interp.fade));
}else{
label.setColor(targetColor);
}
}).fillX().left();
t.row();
}
}else if(node.objectives.size > 0){
t.table(r -> {
r.add("@complete").colspan(2).left();
r.row();
for(Objective o : node.objectives){
if(o.complete()) continue;
r.add("> " + o.display()).color(Color.lightGray).left();
r.image(o.complete() ? Icon.ok : Icon.cancel, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
r.row();
}
});
t.row(); t.row();
} }
}else if(node.objectives.size > 0){ });
t.table(r -> { }
r.add("@complete").colspan(2).left();
r.row();
for(Objective o : node.objectives){
if(o.complete()) continue;
r.add("> " + o.display()).color(Color.lightGray).left();
r.image(o.complete() ? Icon.ok : Icon.cancel, o.complete() ? Color.lightGray : Color.scarlet).padLeft(3);
r.row();
}
});
t.row();
}
});
}else{ }else{
desc.add("@completed"); desc.add("@completed");
} }
}).pad(9); }).pad(9);
if(mobile && locked(node)){ if(mobile && locked(node) && !net.client()){
b.row(); b.row();
b.button("@research", Icon.ok, new TextButtonStyle(){{ b.button("@research", Icon.ok, new TextButtonStyle(){{
disabled = Tex.button; disabled = Tex.button;