From 4f85bc21e054949b5275d1495e7414436cc2f001 Mon Sep 17 00:00:00 2001 From: thepigcat Date: Sun, 12 Jan 2025 14:27:19 +0100 Subject: [PATCH] wip graph --- .../103d9f3f36b01595f1aa5172191e60eff02e6924 | 9 +- .../researchd/researchd/research/example.json | 9 -- .../portingdeadmods/researchd/Researchd.java | 4 +- .../researchd/ResearchdRegistries.java | 2 +- .../researchd/api/research/Research.java | 3 +- .../researchd/client/ResearchManager.java | 95 +++++++++++++++++++ .../client/screens/ResearchScreen.java | 30 ++---- .../client/screens/graph/ResearchGraph.java | 18 ++-- .../client/screens/graph/ResearchNode.java | 20 +++- .../client/screens/list/TechListEntry.java | 13 +++ .../impl/research/SimpleResearch.java | 33 ++++--- .../researchd/registries/Researches.java | 39 ++++++-- test.txt | 1 + 13 files changed, 215 insertions(+), 61 deletions(-) delete mode 100644 src/generated/resources/data/researchd/researchd/research/example.json create mode 100644 src/main/java/com/portingdeadmods/researchd/client/ResearchManager.java create mode 100644 test.txt diff --git a/src/generated/resources/.cache/103d9f3f36b01595f1aa5172191e60eff02e6924 b/src/generated/resources/.cache/103d9f3f36b01595f1aa5172191e60eff02e6924 index 4fce1ba..ef24b44 100644 --- a/src/generated/resources/.cache/103d9f3f36b01595f1aa5172191e60eff02e6924 +++ b/src/generated/resources/.cache/103d9f3f36b01595f1aa5172191e60eff02e6924 @@ -1,5 +1,10 @@ -// 1.21.1 2025-01-12T01:35:51.697973695 Registries -a2254160c3b757cc65c156058b73a4d7fcfee7af data/researchd/researchd/research/example.json +// 1.21.1 2025-01-12T11:42:27.249407057 Registries +1c90d9b45c6886c69a5e05f3b5b8f43ed409246f data/researchd/researchd/research/coal.json +5675612b592e247a9090712849c235fedca9bcc8 data/researchd/researchd/research/copper.json +9228586f51e932a81cbe989082a1395abf460d5e data/researchd/researchd/research/stick.json +5d59ad6b8434e563bab7b543ab6e1b90e1dc5bfc data/researchd/researchd/research/stone.json +19ef9527cf9f4017e6f1abe9717cbb27ee522af9 data/researchd/researchd/research/wood.json +aa3e8393680f785192c41c83fc3f389967312dfc data/researchd/researchd/research/wooden_pickaxe.json 1cdd93f8a6bb79a07d1657ef616b5bd4fbdb247d data/researchd/researchd/research_pack/end.json 89ac98c63d706e87b221fe288b9947b8018976ca data/researchd/researchd/research_pack/nether.json f077b1491836c0ad25f25653e259fb15df317440 data/researchd/researchd/research_pack/overworld.json diff --git a/src/generated/resources/data/researchd/researchd/research/example.json b/src/generated/resources/data/researchd/researchd/research/example.json deleted file mode 100644 index e59b498..0000000 --- a/src/generated/resources/data/researchd/researchd/research/example.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "researchd:simple", - "parent": {}, - "icon": "minecraft:stick", - "requires_parent": false, - "research_points": { - "researchd:overworld": 3 - } -} \ No newline at end of file diff --git a/src/main/java/com/portingdeadmods/researchd/Researchd.java b/src/main/java/com/portingdeadmods/researchd/Researchd.java index 730ee55..043a4d3 100644 --- a/src/main/java/com/portingdeadmods/researchd/Researchd.java +++ b/src/main/java/com/portingdeadmods/researchd/Researchd.java @@ -63,8 +63,8 @@ private void registerRegistries(NewRegistryEvent event) { } private void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) { - event.dataPackRegistry(ResearchdRegistries.RESEARCH_KEY, Research.CODEC); - event.dataPackRegistry(ResearchdRegistries.RESEARCH_PACK_KEY, ResearchPack.CODEC); + event.dataPackRegistry(ResearchdRegistries.RESEARCH_KEY, Research.CODEC, Research.CODEC); + event.dataPackRegistry(ResearchdRegistries.RESEARCH_PACK_KEY, ResearchPack.CODEC, ResearchPack.CODEC); } public static ResourceLocation rl(String path) { diff --git a/src/main/java/com/portingdeadmods/researchd/ResearchdRegistries.java b/src/main/java/com/portingdeadmods/researchd/ResearchdRegistries.java index cd65c34..3a7a79d 100644 --- a/src/main/java/com/portingdeadmods/researchd/ResearchdRegistries.java +++ b/src/main/java/com/portingdeadmods/researchd/ResearchdRegistries.java @@ -23,7 +23,7 @@ public class ResearchdRegistries { public static final ResourceKey>> RESEARCH_PREDICATE_SERIALIZER_KEY = ResourceKey.createRegistryKey(Researchd.rl("research_predicate_serializer")); - public static final Registry> RESEARCH_SERIALIZER = new RegistryBuilder<>(RESEARCH_SERIALIZER_KEY).create(); + public static final Registry> RESEARCH_SERIALIZER = new RegistryBuilder<>(RESEARCH_SERIALIZER_KEY).sync(true).create(); public static final Registry> RESEARCH_PACK_SERIALIZER = new RegistryBuilder<>(RESEARCH_PACK_SERIALIZER_KEY).create(); public static final Registry> RESEARCH_PREDICATE_SERIALIZER = new RegistryBuilder<>(RESEARCH_PREDICATE_SERIALIZER_KEY).create(); diff --git a/src/main/java/com/portingdeadmods/researchd/api/research/Research.java b/src/main/java/com/portingdeadmods/researchd/api/research/Research.java index bda6dc2..d043631 100644 --- a/src/main/java/com/portingdeadmods/researchd/api/research/Research.java +++ b/src/main/java/com/portingdeadmods/researchd/api/research/Research.java @@ -7,6 +7,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -28,7 +29,7 @@ public interface Research { /** * @return An {@link Optional} {@link ResourceKey} which represents the parent of this research. */ - Optional> parent(); + List> parents(); /** * @return whether the parent needs to researched to research this research diff --git a/src/main/java/com/portingdeadmods/researchd/client/ResearchManager.java b/src/main/java/com/portingdeadmods/researchd/client/ResearchManager.java new file mode 100644 index 0000000..c35b762 --- /dev/null +++ b/src/main/java/com/portingdeadmods/researchd/client/ResearchManager.java @@ -0,0 +1,95 @@ +package com.portingdeadmods.researchd.client; + +import com.portingdeadmods.researchd.Researchd; +import com.portingdeadmods.researchd.ResearchdRegistries; +import com.portingdeadmods.researchd.api.research.Research; +import com.portingdeadmods.researchd.client.screens.graph.ResearchNode; +import com.portingdeadmods.researchd.registries.Researches; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.world.level.Level; + +import java.util.*; + +public class ResearchManager { + private Set> researches = new HashSet<>(); + private Set nodes = new HashSet<>(); + private Set rootNodes; + + public ResearchManager(Level level) { + RegistryAccess registryAccess = level.registryAccess(); + HolderLookup.RegistryLookup registry = registryAccess.lookupOrThrow(ResearchdRegistries.RESEARCH_KEY); + + // Collect researches + registry.listElements().forEach(research -> { + this.researches.add(research); + this.nodes.add(new ResearchNode(research)); + }); + + for (ResearchNode node : this.nodes) { + List> parents = node.getHolder().value().parents().stream() + .map(registryAccess::holderOrThrow).toList(); + + for (Holder parentResearch : parents) { + ResearchNode parentNode = getNodeByResearch(parentResearch); + if (parentNode != null) { + parentNode.addNext(node); + } + } + } + + Set referencedNodes = new HashSet<>(); + for (ResearchNode node : this.nodes) { + referencedNodes.addAll(node.getNext()); + } + + HashSet researchNodesCopy = new HashSet<>(this.nodes); + researchNodesCopy.removeAll(referencedNodes); + + this.rootNodes = new HashSet<>(researchNodesCopy); + } + + public void setCoordinates(int paddingX, int paddingY) { + int i = 0; + for (ResearchNode node : this.rootNodes) { + Researchd.LOGGER.debug("root: {}", node.getHolder().getKey().location()); + setCoordinate(node, paddingX + i * 40, paddingY, 0); + i++; + } + } + + public void setCoordinate(ResearchNode node, int x, int y, int nesting) { + Researchd.LOGGER.debug("node: {}, nesting: {}", node.getHolder().getKey().location(), nesting); + node.setX(x); + node.setY(y); + Researchd.LOGGER.debug("y: {}", y); + List next = node.getNext(); + for (int i = 0; i < next.size(); i++) { + ResearchNode nextNode = next.get(i); + int newNesting = nesting + 1; + setCoordinate(nextNode, x + i * 30, y + newNesting * 30, newNesting + 1); + } + } + + public ResearchNode getNodeByResearch(Holder research) { + for (ResearchNode node : nodes) { + if (node.getHolder().is(research.getKey())) { + return node; + } + } + return null; + } + + public Set getNodes() { + return nodes; + } + + public Set> getResearches() { + return researches; + } + + public Set getRootNodes() { + return rootNodes; + } +} diff --git a/src/main/java/com/portingdeadmods/researchd/client/screens/ResearchScreen.java b/src/main/java/com/portingdeadmods/researchd/client/screens/ResearchScreen.java index 28f1885..2ac85fc 100644 --- a/src/main/java/com/portingdeadmods/researchd/client/screens/ResearchScreen.java +++ b/src/main/java/com/portingdeadmods/researchd/client/screens/ResearchScreen.java @@ -1,30 +1,15 @@ package com.portingdeadmods.researchd.client.screens; import com.portingdeadmods.researchd.Researchd; -import com.portingdeadmods.researchd.api.research.Research; +import com.portingdeadmods.researchd.client.ResearchManager; import com.portingdeadmods.researchd.client.screens.graph.ResearchGraph; -import com.portingdeadmods.researchd.client.screens.graph.ResearchNode; -import com.portingdeadmods.researchd.client.screens.list.EntryType; import com.portingdeadmods.researchd.client.screens.list.TechList; -import com.portingdeadmods.researchd.client.screens.list.TechListEntry; import com.portingdeadmods.researchd.client.screens.queue.ResearchQueue; -import com.portingdeadmods.researchd.impl.research.SimpleResearch; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.ContainerObjectSelectionList; -import net.minecraft.client.gui.components.StringWidget; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.layouts.GridLayout; -import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Items; - -import java.util.List; -import java.util.Map; -import java.util.Optional; public class ResearchScreen extends Screen { public static final ResourceLocation BACKGROUND_TEXTURE = Researchd.rl("textures/gui/research_screen.png"); @@ -39,13 +24,16 @@ public ResearchScreen() { this.techList.fillList(); this.researchQueue = new ResearchQueue(0, 0); this.researchQueue.fillList(); - int width = Minecraft.getInstance().getWindow().getGuiScaledWidth(); + Minecraft mc = Minecraft.getInstance(); + int width = mc.getWindow().getGuiScaledWidth(); Researchd.LOGGER.debug("Width: {}", width); int x = 174; - this.researchGraph = new ResearchGraph(x, 0, 300, 253); - ResearchNode node = new ResearchNode(SimpleResearch.debug(Items.IRON_NUGGET), EntryType.RESEARCHED, x + 60, 60); - this.researchGraph.setNode(node); - node.addNext(new ResearchNode(SimpleResearch.debug(Items.IRON_BARS), EntryType.RESEARCHABLE, x + 70, 100)); + ResearchManager manager = new ResearchManager(mc.level); + manager.setCoordinates(174, 10); + this.researchGraph = new ResearchGraph(manager, x, 0, 300, 253); + //ResearchNode node = new ResearchNode(SimpleResearch.debug(Items.IRON_NUGGET), EntryType.RESEARCHED, x + 60, 60); + //this.researchGraph.setNode(node); + //node.addNext(new ResearchNode(SimpleResearch.debug(Items.IRON_BARS), EntryType.RESEARCHABLE, x + 70, 100)); } @Override diff --git a/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchGraph.java b/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchGraph.java index 9d556b6..d027e85 100644 --- a/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchGraph.java +++ b/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchGraph.java @@ -1,5 +1,6 @@ package com.portingdeadmods.researchd.client.screens.graph; +import com.portingdeadmods.researchd.client.ResearchManager; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.narration.NarrationElementOutput; @@ -7,24 +8,29 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; public class ResearchGraph extends AbstractWidget { - private @Nullable ResearchNode node; - private final List nodes; + private Set rootNodes; + private Set nodes; public ResearchGraph(int x, int y, int width, int height) { super(x, y, width, height, Component.empty()); - this.nodes = new ArrayList<>(); + this.nodes = new HashSet<>(); + this.rootNodes = new HashSet<>(); } - public void setNode(@Nullable ResearchNode node) { - this.node = node; + public ResearchGraph(ResearchManager manager, int x, int y, int width, int height) { + super(x, y, width, height, Component.empty()); + this.nodes = manager.getNodes(); + this.rootNodes = manager.getRootNodes(); } @Override protected void renderWidget(GuiGraphics guiGraphics, int i, int i1, float v) { - if (node != null) { + for (ResearchNode node : this.rootNodes) { renderNode(node, guiGraphics, i, i1, v); } } diff --git a/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchNode.java b/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchNode.java index b114691..732eb14 100644 --- a/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchNode.java +++ b/src/main/java/com/portingdeadmods/researchd/client/screens/graph/ResearchNode.java @@ -7,17 +7,25 @@ import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; import java.util.ArrayList; import java.util.List; public class ResearchNode extends TechListEntry { private final List next; + private final Holder holder; - public ResearchNode(Research research, EntryType type, int x, int y) { - super(research, type, x, y); + public ResearchNode(Holder research) { + super(research.value(), EntryType.LOCKED, 0, 0); this.next = new ArrayList<>(); + this.holder = research; + } + + public Holder getHolder() { + return holder; } public void addNext(ResearchNode next) { @@ -31,4 +39,12 @@ public void removeNext(ResearchNode toRemove) { public List getNext() { return next; } + + @Override + public String toString() { + return "ResearchNode{" + + "next=" + next + + ", holder=" + holder + + '}'; + } } diff --git a/src/main/java/com/portingdeadmods/researchd/client/screens/list/TechListEntry.java b/src/main/java/com/portingdeadmods/researchd/client/screens/list/TechListEntry.java index f32f2bb..1e13c1e 100644 --- a/src/main/java/com/portingdeadmods/researchd/client/screens/list/TechListEntry.java +++ b/src/main/java/com/portingdeadmods/researchd/client/screens/list/TechListEntry.java @@ -11,6 +11,8 @@ import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; +import java.util.Objects; + public class TechListEntry extends AbstractWidget { public static final int WIDTH = 20; public static final int HEIGHT = 24; @@ -57,4 +59,15 @@ protected void renderWidget(GuiGraphics guiGraphics, int mouseX, int mouseY, flo @Override protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) { } + + @Override + public boolean equals(Object o) { + if (!(o instanceof TechListEntry entry)) return false; + return Objects.equals(research, entry.research) && type == entry.type; + } + + @Override + public int hashCode() { + return Objects.hash(research, type); + } } diff --git a/src/main/java/com/portingdeadmods/researchd/impl/research/SimpleResearch.java b/src/main/java/com/portingdeadmods/researchd/impl/research/SimpleResearch.java index 3462522..0f7bd96 100644 --- a/src/main/java/com/portingdeadmods/researchd/impl/research/SimpleResearch.java +++ b/src/main/java/com/portingdeadmods/researchd/impl/research/SimpleResearch.java @@ -19,17 +19,16 @@ import net.minecraft.world.level.ItemLike; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; // TODO: Change icon to Ingredient -public record SimpleResearch(Item icon, Map, Integer> researchPoints, Optional> parent, boolean requiresParent) implements Research { +public record SimpleResearch(Item icon, Map, Integer> researchPoints, + List> parents, boolean requiresParent) implements Research { public static SimpleResearch debug(ItemLike icon) { - return new SimpleResearch(icon.asItem(), Map.of(), Optional.empty(), false); + return new SimpleResearch(icon.asItem(), Map.of(), Collections.emptyList(), false); } - private static SimpleResearch fromStringMap(Item icon, Map researchPoints, Optional> parent, boolean requiresParent) { + private static SimpleResearch fromStringMap(Item icon, Map researchPoints, List> parent, boolean requiresParent) { Map, Integer> map = new HashMap<>(); for (Map.Entry researchPoint : researchPoints.entrySet()) { ResourceKey researchPack = ResourceKey.create(ResearchdRegistries.RESEARCH_PACK_KEY, ResourceLocation.parse(researchPoint.getKey())); @@ -51,12 +50,23 @@ public ResearchSerializer getSerializer() { return Serializer.INSTANCE; } + @Override + public boolean equals(Object o) { + if (!(o instanceof SimpleResearch that)) return false; + return requiresParent == that.requiresParent && Objects.equals(icon, that.icon) && Objects.equals(parents, that.parents) && Objects.equals(researchPoints, that.researchPoints); + } + + @Override + public int hashCode() { + return Objects.hash(icon, researchPoints, parents, requiresParent); + } + public static class Serializer implements ResearchSerializer { public static final Serializer INSTANCE = new Serializer(); public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( CodecUtils.registryCodec(BuiltInRegistries.ITEM).fieldOf("icon").forGetter(SimpleResearch::icon), Codec.unboundedMap(Codec.STRING, Codec.INT).fieldOf("research_points").forGetter(SimpleResearch::getStringMap), - ExtraCodecs.optionalEmptyMap(Research.RESOURCE_KEY_CODEC).fieldOf("parent").forGetter(SimpleResearch::parent), + Research.RESOURCE_KEY_CODEC.listOf().fieldOf("parents").forGetter(SimpleResearch::parents), Codec.BOOL.fieldOf("requires_parent").forGetter(SimpleResearch::requiresParent) ).apply(instance, SimpleResearch::fromStringMap)); @@ -77,7 +87,7 @@ public StreamCodec streamCodec() { public static class Builder implements Research.Builder { private Item icon = Items.AIR; private Map, Integer> researchPacks = Map.of(); - private ResourceKey parent = null; + private List> parents = Collections.emptyList(); private boolean requiresParent = false; public static Builder of() { @@ -97,8 +107,9 @@ public Builder researchPacks(Map, Integer> researchPac return this; } - public Builder parent(@Nullable ResourceKey parent) { - this.parent = parent; + @SafeVarargs + public final Builder parents(ResourceKey... parents) { + this.parents = List.of(parents); return this; } @@ -109,7 +120,7 @@ public Builder requiresParent(boolean requiresParent) { @Override public SimpleResearch build() { - return new SimpleResearch(this.icon, this.researchPacks, Optional.ofNullable(this.parent), this.requiresParent); + return new SimpleResearch(this.icon, this.researchPacks, this.parents, this.requiresParent); } } } diff --git a/src/main/java/com/portingdeadmods/researchd/registries/Researches.java b/src/main/java/com/portingdeadmods/researchd/registries/Researches.java index 3c1939e..baf18cd 100644 --- a/src/main/java/com/portingdeadmods/researchd/registries/Researches.java +++ b/src/main/java/com/portingdeadmods/researchd/registries/Researches.java @@ -19,7 +19,7 @@ public final class Researches { private static final Map, Research.Builder> RESEARCHES = new HashMap<>(); - private static final ResourceKey WOOD = register("wood", builder -> builder + public static final ResourceKey WOOD = register("wood", builder -> builder .icon(Items.OAK_LOG) .researchPacks(Map.of( ResearchPacks.OVERWORLD, 3 @@ -28,13 +28,40 @@ public final class Researches { .icon(Items.STICK) .researchPacks(Map.of( ResearchPacks.OVERWORLD, 5 - ))); + )) + .parents(Researches.WOOD) + .requiresParent(false)); private static final ResourceKey WOODEN_PICKAXE = register("wooden_pickaxe", builder -> builder .icon(Items.WOODEN_PICKAXE) - .researchPacks(Map.of(ResearchPacks.OVERWORLD, 6, ResearchPacks.NETHER, 3))); - private static final ResourceKey STONE = register("stone", null); - private static final ResourceKey COPPER = register("copper", null); - private static final ResourceKey COAL = register("coal", null); + .researchPacks(Map.of( + ResearchPacks.OVERWORLD, 6, + ResearchPacks.NETHER, 3 + )) + .parents(WOOD, STICK) + .requiresParent(true)); + private static final ResourceKey STONE = register("stone", builder -> builder + .icon(Items.STONE) + .researchPacks(Map.of( + ResearchPacks.OVERWORLD, 3, + ResearchPacks.END, 4 + )) + .parents(WOODEN_PICKAXE) + .requiresParent(true)); + private static final ResourceKey COPPER = register("copper", builder -> builder + .icon(Items.RAW_COPPER) + .researchPacks(Map.of( + ResearchPacks.NETHER, 5 + )) + .parents(WOODEN_PICKAXE) + .requiresParent(true)); + private static final ResourceKey COAL = register("coal", builder -> builder + .icon(Items.COAL) + .researchPacks(Map.of( + ResearchPacks.NETHER, 7, + ResearchPacks.END, 4 + )) + .parents(WOODEN_PICKAXE) + .requiresParent(true)); public static void bootstrap(BootstrapContext context) { for (Map.Entry, Research.Builder> research : RESEARCHES.entrySet()) { diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..2e7c674 --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +node: ResearchNode{next=ResearchNode{next=[], holder=Reference{ResourceKey[researchd:research / researchd:coal]=SimpleResearch[icon=minecraft:coal, researchPoints={ResourceKey[researchd:research_pack / researchd:nether]=7, ResourceKey[researchd:research_pack / researchd:end]=4}, parents=[ResourceKey[researchd:research / researchd:wooden_pickaxe]], requiresParent=true]}}, ResearchNode{next=[], holder=Reference{ResourceKey[researchd:research / researchd:copper]=SimpleResearch[icon=minecraft:raw_copper, researchPoints={ResourceKey[researchd:research_pack / researchd:nether]=5}, parents=[ResourceKey[researchd:research / researchd:wooden_pickaxe]], requiresParent=true]}}], holder=Reference{ResourceKey[researchd:research / researchd:wooden_pickaxe]=SimpleResearch[icon=minecraft:wooden_pickaxe, researchPoints={ResourceKey[researchd:research_pack / researchd:nether]=3, ResourceKey[researchd:research_pack / researchd:overworld]=6}, parents=[ResourceKey[researchd:research / researchd:wood], ResourceKey[researchd:research / researchd:stick]], requiresParent=true]}}, ResearchNode{next=[ResearchNode{next=[ResearchNode{next=[], holder=Reference{ResourceKey[researchd:research / researchd:stone]=SimpleResearch[icon=minecraft:stone, researchPoints={ResourceKey[researchd:research_pack / researchd:end]=4, ResourceKey[researchd:research_pack / researchd:overworld]=3}, parents=[ResourceKey[researchd:research / researchd:wooden_pickaxe]], requiresParent=true]}}, ResearchNode{next=[], holder=Reference{ResourceKey[researchd:research / researchd:coal]=SimpleResearch[icon=minecraft:coal, researchPoints={ResourceKey[researchd:research_pack / researchd:nether]=7, ResourceKey[researchd:research_pack / researchd:end]=4}, parents=[ResourceKey[researchd:research / researchd:wooden_pickaxe]], requiresParent=true]}}, ResearchNode{next=[], holder=Reference{ResourceKey[researchd:research / researchd:copper]=SimpleResearch[icon=minecraft:raw_copper, researchPoints={ResourceKey[researchd:research_pack / researchd:nether]=5}, parents=[ResourceKey[researchd:research / researchd:wooden_pickaxe]], requiresParent=true]}}], holder=Reference{ResourceKey[researchd:research / researchd:wooden_pickaxe]=SimpleResearch[icon=minecraft:wooden_pickaxe, researchPoints={ResourceKey[researchd:research_pack / researchd:nether]=3, ResourceKey[researchd:research_pack / researchd:overworld]=6}, parents=[ResourceKey[researchd:research / researchd:wood], ResourceKey[researchd:research / researchd:stick]], requiresParent=true]}}], holder=Reference{ResourceKey[researchd:research / researchd:stick]=SimpleResearch[icon=minecraft:stick, researchPoints={ResourceKey[researchd:research_pack / researchd:overworld]=5}, parents=[ResourceKey[researchd:research / researchd:wood]], requiresParent=false]}}], holder=Reference{ResourceKey[researchd:research / researchd:wood]=SimpleResearch[icon=minecraft:oak_log, researchPoints={ResourceKey[researchd:research_pack / researchd:overworld]=3}, parents=[], requiresParent=false]}} \ No newline at end of file