Functional tech tree
This commit is contained in:
@@ -42,6 +42,7 @@ public class Recipes implements ContentList{
|
||||
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.launchPad, new ItemStack(Items.copper, 500));
|
||||
new Recipe(effect, Blocks.core, new ItemStack(Items.titanium, 2000)).setHidden(true).setAlwaysUnlocked(true);
|
||||
|
||||
//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));
|
||||
@@ -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));
|
||||
|
||||
//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
|
||||
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));
|
||||
|
||||
@@ -13,7 +13,7 @@ public class TechTree implements ContentList{
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
root = node(null, with(), () -> {
|
||||
root = node(Blocks.core, with(), () -> {
|
||||
|
||||
node(Blocks.conveyor, 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);
|
||||
}
|
||||
|
||||
private TechNode node(Block block, ItemStack[] requirements){
|
||||
return new TechNode(block, requirements, () -> {});
|
||||
}
|
||||
|
||||
private TechNode node(Block block){
|
||||
return new TechNode(block, with(), () -> {});
|
||||
private void node(Block block, ItemStack[] requirements){
|
||||
new TechNode(block, requirements, () -> {});
|
||||
}
|
||||
|
||||
public static class TechNode{
|
||||
@@ -281,5 +277,9 @@ public class TechTree implements ContentList{
|
||||
children.run();
|
||||
context = last;
|
||||
}
|
||||
|
||||
public Recipe recipe(){
|
||||
return Recipe.getByResult(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ public class GlobalData{
|
||||
modified = true;
|
||||
}
|
||||
|
||||
public boolean has(Item item, int amount){
|
||||
return items.get(item, 0) >= amount;
|
||||
}
|
||||
|
||||
public ObjectIntMap<Item> items(){
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public class Palette{
|
||||
heal = Color.valueOf("98ffa9"),
|
||||
bar = Color.SLATE,
|
||||
accent = Color.valueOf("ffd37f"),
|
||||
locked = Color.valueOf("6b6b6b"),
|
||||
accentBack = Color.valueOf("d4816b"),
|
||||
place = Color.valueOf("6335f8"),
|
||||
remove = Color.valueOf("e55454"),
|
||||
|
||||
@@ -8,11 +8,15 @@ import io.anuke.arc.util.Pack;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.entities.traits.SaveTrait;
|
||||
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.type.ContentType;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
@@ -120,7 +124,7 @@ public abstract class SaveFileVersion{
|
||||
tile.entity.readConfig(stream);
|
||||
tile.entity.read(stream);
|
||||
|
||||
if(tile.block() == Blocks.core){
|
||||
if(tile.block() instanceof CoreBlock){
|
||||
state.teams.get(t).cores.add(tile);
|
||||
}
|
||||
}else if(wallid == 0){
|
||||
|
||||
@@ -34,7 +34,7 @@ public class DeployDialog extends FloatingDialog{
|
||||
ObjectIntMap<Item> items = data.items();
|
||||
for(Item item : Vars.content.items()){
|
||||
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);
|
||||
add("[LIGHT_GRAY]" + item.localizedName()).left();
|
||||
row();
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
package io.anuke.mindustry.ui.dialogs;
|
||||
|
||||
import io.anuke.arc.Core;
|
||||
import io.anuke.arc.collection.Array;
|
||||
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.ScissorStack;
|
||||
import io.anuke.arc.input.KeyCode;
|
||||
import io.anuke.arc.math.Interpolation;
|
||||
import io.anuke.arc.math.geom.Rectangle;
|
||||
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.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.layout.Table;
|
||||
import io.anuke.arc.util.Align;
|
||||
import io.anuke.arc.util.Log;
|
||||
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.TechNode;
|
||||
import io.anuke.mindustry.graphics.Palette;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
import io.anuke.mindustry.type.Recipe;
|
||||
import io.anuke.mindustry.type.Recipe.RecipeVisibility;
|
||||
import io.anuke.mindustry.ui.TreeLayout;
|
||||
import io.anuke.mindustry.ui.TreeLayout.TreeNode;
|
||||
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{
|
||||
private ObjectSet<TechTreeNode> nodes = new ObjectSet<>();
|
||||
private TechTreeNode root = new TechTreeNode(TechTree.root, null);
|
||||
private static final float nodeSize = 60f;
|
||||
private int toasts;
|
||||
|
||||
public TechTreeDialog(){
|
||||
super("$techtree");
|
||||
@@ -36,14 +44,14 @@ public class TechTreeDialog extends FloatingDialog{
|
||||
TreeLayout layout = new TreeLayout();
|
||||
layout.gapBetweenLevels = 60f;
|
||||
layout.gapBetweenNodes = 40f;
|
||||
layout.layout(new TechTreeNode(TechTree.root, null));
|
||||
layout.layout(root);
|
||||
|
||||
cont.add(new View()).grow();
|
||||
|
||||
{ //debug code; TODO remove
|
||||
ObjectSet<Recipe> used = new ObjectSet<Recipe>().select(t -> true);
|
||||
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));
|
||||
recipes.sort(Structs.comparing(r -> r.cost));
|
||||
@@ -55,11 +63,44 @@ public class TechTreeDialog extends FloatingDialog{
|
||||
}
|
||||
}
|
||||
|
||||
shown(() -> checkNodes(root));
|
||||
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{
|
||||
final TechNode node;
|
||||
boolean visible = true;
|
||||
|
||||
public TechTreeNode(TechNode node, TreeNode parent){
|
||||
this.node = node;
|
||||
@@ -77,21 +118,47 @@ public class TechTreeDialog extends FloatingDialog{
|
||||
|
||||
class View extends Group{
|
||||
float panX = 0, panY = 0;
|
||||
Rectangle clip = new Rectangle();
|
||||
boolean moved = false;
|
||||
Rectangle clip = new Rectangle();
|
||||
ImageButton hoverNode;
|
||||
Table infoTable = new Table();
|
||||
|
||||
{
|
||||
infoTable.touchable(Touchable.enabled);
|
||||
|
||||
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(() -> {
|
||||
if(moved) return;
|
||||
Vars.ui.content.show(Recipe.getByResult(node.node.block == null ? Blocks.conveyor : node.node.block));
|
||||
if(mobile){
|
||||
hoverNode = button;
|
||||
rebuild();
|
||||
}else if(data.hasItems(node.node.requirements) && locked(node)){
|
||||
unlock(node.node);
|
||||
}
|
||||
});
|
||||
button.tapped(() -> {
|
||||
moved = false;
|
||||
button.hovered(() -> {
|
||||
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.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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
public void draw(){
|
||||
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;
|
||||
|
||||
Lines.stroke(3f, Palette.accent);
|
||||
|
||||
for(TreeNode node : nodes){
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user