Allow research dialog to be viewed on clients
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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){
|
||||||
|
|||||||
@@ -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) : "???");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user