Functional tech tree

This commit is contained in:
Anuken
2019-01-19 22:16:28 -05:00
parent 093043750b
commit d34f228d2f
18 changed files with 1217 additions and 1079 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

View File

@@ -51,6 +51,9 @@ name = Name:
noname = Pick a[accent] player name[] first. noname = Pick a[accent] player name[] first.
filename = File Name: filename = File Name:
unlocked = New content unlocked! unlocked = New content unlocked!
completed = [accent]Completed
research = Research
researched = [LIGHT_GRAY]{0} researched.
players = {0} players online players = {0} players online
players.single = {0} player online players.single = {0} player online
server.closing = [accent]Closing server... server.closing = [accent]Closing server...

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 963 KiB

After

Width:  |  Height:  |  Size: 967 KiB

View File

@@ -24,6 +24,7 @@ ButtonStyle: {
}, },
TextButtonStyle: { TextButtonStyle: {
default: {over: button-over, disabled: button, font: default-font, fontColor: white, disabledFontColor: gray, down: button-down, up: button}, default: {over: button-over, disabled: button, font: default-font, fontColor: white, disabledFontColor: gray, down: button-down, up: button},
node: {disabled: content-background, font: default-font, fontColor: white, disabledFontColor: gray, up: content-background, over: content-background-over},
right: {over: button-right-over, font: default-font, fontColor: white, disabledFontColor: gray, down: button-right-down, up: button-right}, right: {over: button-right-over, font: default-font, fontColor: white, disabledFontColor: gray, down: button-right-down, up: button-right},
wave: {font: default-font, fontColor: white, disabledFontColor: gray, up: button-edge-4}, wave: {font: default-font, fontColor: white, disabledFontColor: gray, up: button-edge-4},
clear: {over: flat-over, font: default-font, fontColor: white, disabledFontColor: gray, down: flat-over, up: flat}, clear: {over: flat-over, font: default-font, fontColor: white, disabledFontColor: gray, down: flat-over, up: flat},
@@ -37,7 +38,7 @@ TextButtonStyle: {
}, },
ImageButtonStyle: { ImageButtonStyle: {
default: {down: button-down, up: button, over: button-over, imageDisabledColor: gray, imageUpColor: white }, default: {down: button-down, up: button, over: button-over, imageDisabledColor: gray, imageUpColor: white },
node: {down: content-background-over, up: content-background, over: content-background-over}, node: {up: content-background, over: content-background-over},
right: {over: button-right-over, down: button-right-down, up: button-right}, right: {over: button-right-over, down: button-right-down, up: button-right},
empty: { imageDownColor: accent, imageUpColor: white}, empty: { imageDownColor: accent, imageUpColor: white},
emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: gray}, emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: gray},

View File

@@ -42,6 +42,7 @@ public class Recipes implements ContentList{
new Recipe(effect, Blocks.container, new ItemStack(Items.titanium, 200)); new Recipe(effect, Blocks.container, new ItemStack(Items.titanium, 200));
new Recipe(effect, Blocks.vault, new ItemStack(Items.titanium, 500), new ItemStack(Items.thorium, 250)); new Recipe(effect, Blocks.vault, new ItemStack(Items.titanium, 500), new ItemStack(Items.thorium, 250));
new Recipe(effect, Blocks.launchPad, new ItemStack(Items.copper, 500)); new Recipe(effect, Blocks.launchPad, new ItemStack(Items.copper, 500));
new Recipe(effect, Blocks.core, new ItemStack(Items.titanium, 2000)).setHidden(true).setAlwaysUnlocked(true);
//projectors //projectors
new Recipe(effect, Blocks.mendProjector, new ItemStack(Items.lead, 200), new ItemStack(Items.titanium, 150), new ItemStack(Items.titanium, 50), new ItemStack(Items.silicon, 180)); new Recipe(effect, Blocks.mendProjector, new ItemStack(Items.lead, 200), new ItemStack(Items.titanium, 150), new ItemStack(Items.titanium, 50), new ItemStack(Items.silicon, 180));
@@ -160,9 +161,6 @@ public class Recipes implements ContentList{
new Recipe(units, Blocks.repairPoint, new ItemStack(Items.lead, 30), new ItemStack(Items.copper, 30), new ItemStack(Items.silicon, 30)); new Recipe(units, Blocks.repairPoint, new ItemStack(Items.lead, 30), new ItemStack(Items.copper, 30), new ItemStack(Items.silicon, 30));
//removed for testing MOBA-style unit production
//new Recipe(units, Blocks.commandCenter, new ItemStack(Items.lead, 100), new ItemStack(Items.densealloy, 100), new ItemStack(Items.silicon, 200));
//LIQUIDS //LIQUIDS
new Recipe(liquid, Blocks.conduit, new ItemStack(Items.lead, 1)); new Recipe(liquid, Blocks.conduit, new ItemStack(Items.lead, 1));
new Recipe(liquid, Blocks.pulseConduit, new ItemStack(Items.titanium, 1), new ItemStack(Items.lead, 1)); new Recipe(liquid, Blocks.pulseConduit, new ItemStack(Items.titanium, 1), new ItemStack(Items.lead, 1));

View File

@@ -13,7 +13,7 @@ public class TechTree implements ContentList{
@Override @Override
public void load(){ public void load(){
root = node(null, with(), () -> { root = node(Blocks.core, with(), () -> {
node(Blocks.conveyor, with(Items.copper, 100), () -> { node(Blocks.conveyor, with(Items.copper, 100), () -> {
node(Blocks.launchPad, with(Items.copper, 100), () -> { node(Blocks.launchPad, with(Items.copper, 100), () -> {
@@ -252,12 +252,8 @@ public class TechTree implements ContentList{
return new TechNode(block, requirements, children); return new TechNode(block, requirements, children);
} }
private TechNode node(Block block, ItemStack[] requirements){ private void node(Block block, ItemStack[] requirements){
return new TechNode(block, requirements, () -> {}); new TechNode(block, requirements, () -> {});
}
private TechNode node(Block block){
return new TechNode(block, with(), () -> {});
} }
public static class TechNode{ public static class TechNode{
@@ -281,5 +277,9 @@ public class TechTree implements ContentList{
children.run(); children.run();
context = last; context = last;
} }
public Recipe recipe(){
return Recipe.getByResult(block);
}
} }
} }

View File

@@ -48,6 +48,10 @@ public class GlobalData{
modified = true; modified = true;
} }
public boolean has(Item item, int amount){
return items.get(item, 0) >= amount;
}
public ObjectIntMap<Item> items(){ public ObjectIntMap<Item> items(){
return items; return items;
} }

View File

@@ -41,6 +41,7 @@ public class Palette{
heal = Color.valueOf("98ffa9"), heal = Color.valueOf("98ffa9"),
bar = Color.SLATE, bar = Color.SLATE,
accent = Color.valueOf("ffd37f"), accent = Color.valueOf("ffd37f"),
locked = Color.valueOf("6b6b6b"),
accentBack = Color.valueOf("d4816b"), accentBack = Color.valueOf("d4816b"),
place = Color.valueOf("6335f8"), place = Color.valueOf("6335f8"),
remove = Color.valueOf("e55454"), remove = Color.valueOf("e55454"),

View File

@@ -8,11 +8,15 @@ import io.anuke.arc.util.Pack;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.traits.SaveTrait; import io.anuke.mindustry.entities.traits.SaveTrait;
import io.anuke.mindustry.entities.traits.TypeTrait; import io.anuke.mindustry.entities.traits.TypeTrait;
import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Serialization; import io.anuke.mindustry.gen.Serialization;
import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart; import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -120,7 +124,7 @@ public abstract class SaveFileVersion{
tile.entity.readConfig(stream); tile.entity.readConfig(stream);
tile.entity.read(stream); tile.entity.read(stream);
if(tile.block() == Blocks.core){ if(tile.block() instanceof CoreBlock){
state.teams.get(t).cores.add(tile); state.teams.get(t).cores.add(tile);
} }
}else if(wallid == 0){ }else if(wallid == 0){

View File

@@ -34,7 +34,7 @@ public class DeployDialog extends FloatingDialog{
ObjectIntMap<Item> items = data.items(); ObjectIntMap<Item> items = data.items();
for(Item item : Vars.content.items()){ for(Item item : Vars.content.items()){
if(item.type == ItemType.material && data.isUnlocked(item)){ if(item.type == ItemType.material && data.isUnlocked(item)){
add(items.get(item, 0) + "").left(); label(() -> items.get(item, 0) + "").left();
addImage(item.region).size(8*4).pad(4); addImage(item.region).size(8*4).pad(4);
add("[LIGHT_GRAY]" + item.localizedName()).left(); add("[LIGHT_GRAY]" + item.localizedName()).left();
row(); row();

View File

@@ -1,34 +1,42 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet; import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Lines; import io.anuke.arc.graphics.g2d.Lines;
import io.anuke.arc.graphics.g2d.ScissorStack; import io.anuke.arc.graphics.g2d.ScissorStack;
import io.anuke.arc.input.KeyCode; import io.anuke.arc.input.KeyCode;
import io.anuke.arc.math.Interpolation;
import io.anuke.arc.math.geom.Rectangle; import io.anuke.arc.math.geom.Rectangle;
import io.anuke.arc.scene.Group; import io.anuke.arc.scene.Group;
import io.anuke.arc.scene.actions.Actions;
import io.anuke.arc.scene.event.InputEvent; import io.anuke.arc.scene.event.InputEvent;
import io.anuke.arc.scene.event.InputListener; import io.anuke.arc.scene.event.InputListener;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.style.TextureRegionDrawable;
import io.anuke.arc.scene.ui.ImageButton; import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Align; import io.anuke.arc.util.Align;
import io.anuke.arc.util.Log; import io.anuke.arc.util.Log;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.TechTree; import io.anuke.mindustry.content.TechTree;
import io.anuke.mindustry.content.TechTree.TechNode; import io.anuke.mindustry.content.TechTree.TechNode;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.type.Recipe.RecipeVisibility; import io.anuke.mindustry.type.Recipe.RecipeVisibility;
import io.anuke.mindustry.ui.TreeLayout; import io.anuke.mindustry.ui.TreeLayout;
import io.anuke.mindustry.ui.TreeLayout.TreeNode; import io.anuke.mindustry.ui.TreeLayout.TreeNode;
import io.anuke.mindustry.world.Block.Icon; import io.anuke.mindustry.world.Block.Icon;
import static io.anuke.mindustry.Vars.content; import static io.anuke.mindustry.Vars.*;
public class TechTreeDialog extends FloatingDialog{ public class TechTreeDialog extends FloatingDialog{
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>(); private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
private static final float nodeSize = 60f; private static final float nodeSize = 60f;
private int toasts;
public TechTreeDialog(){ public TechTreeDialog(){
super("$techtree"); super("$techtree");
@@ -36,14 +44,14 @@ public class TechTreeDialog extends FloatingDialog{
TreeLayout layout = new TreeLayout(); TreeLayout layout = new TreeLayout();
layout.gapBetweenLevels = 60f; layout.gapBetweenLevels = 60f;
layout.gapBetweenNodes = 40f; layout.gapBetweenNodes = 40f;
layout.layout(new TechTreeNode(TechTree.root, null)); layout.layout(root);
cont.add(new View()).grow(); cont.add(new View()).grow();
{ //debug code; TODO remove { //debug code; TODO remove
ObjectSet<Recipe> used = new ObjectSet<Recipe>().select(t -> true); ObjectSet<Recipe> used = new ObjectSet<Recipe>().select(t -> true);
for(TechTreeNode node : nodes){ for(TechTreeNode node : nodes){
if(node.node.block != null) used.add(Recipe.getByResult(node.node.block)); used.add(node.node.recipe());
} }
Array<Recipe> recipes = content.recipes().select(r -> r.visibility == RecipeVisibility.all && !used.contains(r)); Array<Recipe> recipes = content.recipes().select(r -> r.visibility == RecipeVisibility.all && !used.contains(r));
recipes.sort(Structs.comparing(r -> r.cost)); recipes.sort(Structs.comparing(r -> r.cost));
@@ -55,11 +63,44 @@ public class TechTreeDialog extends FloatingDialog{
} }
} }
shown(() -> checkNodes(root));
addCloseButton(); addCloseButton();
} }
void checkNodes(TechTreeNode node){
boolean locked = locked(node);
if(!locked) node.visible = true;
for(TreeNode child : node.children){
TechTreeNode l = (TechTreeNode)child;
l.visible = !locked;
checkNodes(l);
}
}
void showToast(String info){
toasts ++;
int t = toasts;
Table table = new Table();
table.actions(Actions.fadeOut(7f, Interpolation.fade), Actions.run(() -> toasts --), Actions.removeActor());
table.top().add(info);
table.update(() -> {
table.toFront();
table.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight() - 21 - t*20f, Align.top);
});
Core.scene.add(table);
}
boolean locked(TreeNode node){
return locked(((TechTreeNode)node).node);
}
boolean locked(TechNode node){
return !data.isUnlocked(node.recipe());
}
class TechTreeNode extends TreeNode{ class TechTreeNode extends TreeNode{
final TechNode node; final TechNode node;
boolean visible = true;
public TechTreeNode(TechNode node, TreeNode parent){ public TechTreeNode(TechNode node, TreeNode parent){
this.node = node; this.node = node;
@@ -77,21 +118,47 @@ public class TechTreeDialog extends FloatingDialog{
class View extends Group{ class View extends Group{
float panX = 0, panY = 0; float panX = 0, panY = 0;
Rectangle clip = new Rectangle();
boolean moved = false; boolean moved = false;
Rectangle clip = new Rectangle();
ImageButton hoverNode;
Table infoTable = new Table();
{ {
infoTable.touchable(Touchable.enabled);
for(TechTreeNode node : nodes){ for(TechTreeNode node : nodes){
ImageButton button = new ImageButton(node.node.block == null ? Blocks.core.icon(Icon.medium) : node.node.block.icon(Icon.medium), "node"); ImageButton button = new ImageButton(node.node.block.icon(Icon.medium), "node");
button.clicked(() -> { button.clicked(() -> {
if(moved) return; if(mobile){
Vars.ui.content.show(Recipe.getByResult(node.node.block == null ? Blocks.conveyor : node.node.block)); hoverNode = button;
rebuild();
}else if(data.hasItems(node.node.requirements) && locked(node)){
unlock(node.node);
}
}); });
button.tapped(() -> { button.hovered(() -> {
moved = false; if(!mobile && hoverNode != button && node.visible){
hoverNode = button;
rebuild();
}
}); });
button.exited(() -> {
if(hoverNode == button && !infoTable.hasMouse() && !hoverNode.hasMouse()){
hoverNode = null;
rebuild();
}
});
button.touchable(() -> !node.visible ? Touchable.disabled : Touchable.enabled);
button.setUserObject(node.node);
button.tapped(() -> moved = false);
button.setSize(nodeSize, nodeSize); button.setSize(nodeSize, nodeSize);
button.update(() -> button.setPosition(node.x + panX + width/2f, node.y + panY + height/2f - 0.5f, Align.center)); button.update(() -> {
button.setPosition(node.x + panX + width/2f, node.y + panY + height/2f - 0.5f, Align.center);
button.getStyle().up = Core.scene.skin.getDrawable(!locked(node) ? "content-background" : "content-background-locked");
((TextureRegionDrawable)button.getStyle().imageUp)
.setRegion(node.visible ? node.node.block.icon(Icon.medium) : Core.atlas.find("icon-tree-locked"));
button.getImage().setColor(!locked(node) ? Color.WHITE : Color.GRAY);
});
addChild(button); addChild(button);
} }
@@ -115,6 +182,75 @@ public class TechTreeDialog extends FloatingDialog{
}); });
} }
void unlock(TechNode node){
data.unlockContent(node.recipe());
data.removeItems(node.requirements);
showToast(Core.bundle.format("researched", node.block.formalName));
checkNodes(root);
hoverNode = null;
rebuild();
}
void rebuild(){
ImageButton button = hoverNode;
infoTable.remove();
infoTable.clear();
infoTable.update(null);
if(button == null) return;
TechNode node = (TechNode)button.getUserObject();
infoTable.exited(() -> {
if(hoverNode == button && !infoTable.hasMouse() && !hoverNode.hasMouse()){
hoverNode = null;
rebuild();
}
});
infoTable.background("content-background");
infoTable.update(() -> infoTable.setPosition(button.getX() + button.getWidth(), button.getY() + button.getHeight(), Align.topLeft));
infoTable.margin(0).left().defaults().left();
infoTable.addImageButton("icon-info", "node", 14*2, () -> ui.content.show(node.recipe())).growY().width(50f);
infoTable.add().grow();
infoTable.table(desc -> {
desc.left().defaults().left();
desc.add(node.block.formalName);
desc.row();
if(locked(node)){
desc.table(t -> {
t.left();
for(ItemStack req : node.requirements){
t.table(list -> {
list.left();
list.addImage(req.item.getContentIcon()).size(8 * 3).padRight(3);
list.add(req.item.localizedName()).color(Color.LIGHT_GRAY);
list.add(" " + Math.min(data.items().get(req.item, 0), req.amount) + " / " + req.amount)
.color(data.has(req.item, req.amount) ? Color.LIGHT_GRAY : Color.SCARLET);
}).fillX().left();
t.row();
}
});
}else{
desc.add("$completed");
}
}).pad(9);
if(mobile && locked(node)){
infoTable.row();
infoTable.addImageTextButton("$research", "icon-check", "node", 16*2, () -> unlock(node))
.disabled(b -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3);
}
addChild(infoTable);
infoTable.pack();
}
@Override @Override
public void draw(){ public void draw(){
if(!ScissorStack.pushScissors(clip.set(x, y, width, height))){ if(!ScissorStack.pushScissors(clip.set(x, y, width, height))){
@@ -123,26 +259,16 @@ public class TechTreeDialog extends FloatingDialog{
float offsetX = panX + width/2f + x, offsetY = panY + height/2f + y; float offsetX = panX + width/2f + x, offsetY = panY + height/2f + y;
Lines.stroke(3f, Palette.accent);
for(TreeNode node : nodes){ for(TreeNode node : nodes){
for(TreeNode child : node.children){ for(TreeNode child : node.children){
Lines.stroke(3f, locked(node) || locked(child) ? Palette.locked : Palette.accent);
Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY); Lines.line(node.x + offsetX, node.y + offsetY, child.x + offsetX, child.y + offsetY);
} }
} }
super.draw(); super.draw();
/*
Draw.color();
for(TechTreeNode node : nodes){
Draw.drawable("content-background", node.x + offsetX - nodeSize/2f, node.y + offsetY - nodeSize/2f, nodeSize, nodeSize);
TextureRegion region = node.node.block == null ? Blocks.core.icon(Icon.medium) : node.node.block.icon(Icon.medium);
Draw.rect(region, node.x + offsetX, node.y + offsetY - 0.5f, region.getWidth(), region.getHeight());
}*/
ScissorStack.popScissors(); ScissorStack.popScissors();
} }
} }