Skip to content

Commit 46d138d

Browse files
committed
refactor(gui): add control buttons for research actions in ResearchTreeScreen
1 parent 9f039cf commit 46d138d

1 file changed

Lines changed: 188 additions & 44 deletions

File tree

src/main/java/com/researchcube/client/screen/ResearchTreeScreen.java

Lines changed: 188 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import com.researchcube.ResearchCubeMod;
44
import com.researchcube.block.ResearchTableBlockEntity;
55
import com.researchcube.menu.ResearchTableMenu;
6+
import com.researchcube.network.CancelResearchPacket;
67
import com.researchcube.network.StartResearchPacket;
8+
import com.researchcube.network.WipeTankPacket;
79
import com.researchcube.registry.ModFluids;
810
import com.researchcube.research.FluidCost;
911
import com.researchcube.research.ItemCost;
@@ -14,13 +16,15 @@
1416
import com.researchcube.research.prerequisite.OrPrerequisite;
1517
import com.researchcube.research.prerequisite.Prerequisite;
1618
import com.researchcube.research.prerequisite.SinglePrerequisite;
19+
import com.researchcube.util.IdeaChipMatcher;
1720
import net.minecraft.client.Minecraft;
1821
import net.minecraft.client.gui.GuiGraphics;
1922
import net.minecraft.client.gui.components.Button;
2023
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
2124
import net.minecraft.network.chat.Component;
2225
import net.minecraft.resources.ResourceLocation;
2326
import net.minecraft.world.entity.player.Inventory;
27+
import net.minecraft.world.item.Item;
2428
import net.minecraft.world.item.ItemStack;
2529
import net.neoforged.neoforge.network.PacketDistributor;
2630

@@ -103,10 +107,9 @@ private record Dependency(ResourceLocation sourceId, EdgeStyle style) {}
103107
private double dragLastY;
104108

105109
private Button startButton;
110+
private Button cancelButton;
111+
private Button wipeButton;
106112
private Button listButton;
107-
private Button fitButton;
108-
private Button zoomInButton;
109-
private Button zoomOutButton;
110113

111114
private int graphMinX;
112115
private int graphMinY;
@@ -128,29 +131,28 @@ public ResearchTreeScreen(ResearchTableMenu menu, Inventory playerInv, Component
128131
protected void init() {
129132
super.init();
130133

131-
// Buttons positioned in upper panel controls area
132-
int btnY = topPos + ResearchTableMenu.TREE_BTN_Y;
133-
134+
// Start button
134135
this.startButton = addRenderableWidget(Button.builder(Component.literal("Start"), b -> onStartResearch())
135136
.bounds(leftPos + ResearchTableMenu.START_BTN_X, topPos + ResearchTableMenu.BUTTON_Y,
136137
ResearchTableMenu.BUTTON_W, ResearchTableMenu.BUTTON_H)
137138
.build());
138139

139-
this.listButton = addRenderableWidget(Button.builder(Component.literal("List"), b -> openListView())
140-
.bounds(leftPos + ResearchTableMenu.LIST_BTN_X, btnY,
141-
ResearchTableMenu.LIST_BTN_W, ResearchTableMenu.LIST_BTN_H)
142-
.build());
143-
144-
this.fitButton = addRenderableWidget(Button.builder(Component.literal("Fit"), b -> fitGraphToViewport())
145-
.bounds(leftPos + ResearchTableMenu.SEARCH_X, btnY, 30, 16)
140+
// Cancel/Stop button
141+
this.cancelButton = addRenderableWidget(Button.builder(Component.literal("Stop"), b -> onCancelResearch())
142+
.bounds(leftPos + ResearchTableMenu.STOP_BTN_X, topPos + ResearchTableMenu.BUTTON_Y,
143+
ResearchTableMenu.BUTTON_W, ResearchTableMenu.BUTTON_H)
146144
.build());
147145

148-
this.zoomOutButton = addRenderableWidget(Button.builder(Component.literal("-"), b -> adjustZoom(-0.12f))
149-
.bounds(leftPos + ResearchTableMenu.SEARCH_X + 34, btnY, 20, 16)
146+
// Wipe tank button
147+
this.wipeButton = addRenderableWidget(Button.builder(Component.literal("Wipe"), b -> onWipeTank())
148+
.bounds(leftPos + ResearchTableMenu.WIPE_BTN_X, topPos + ResearchTableMenu.BUTTON_Y,
149+
ResearchTableMenu.BUTTON_W, ResearchTableMenu.BUTTON_H)
150150
.build());
151151

152-
this.zoomInButton = addRenderableWidget(Button.builder(Component.literal("+"), b -> adjustZoom(0.12f))
153-
.bounds(leftPos + ResearchTableMenu.SEARCH_X + 58, btnY, 20, 16)
152+
// List view button (switches back to list view)
153+
this.listButton = addRenderableWidget(Button.builder(Component.literal("List"), b -> openListView())
154+
.bounds(leftPos + ResearchTableMenu.LIST_BTN_X, topPos + ResearchTableMenu.TREE_BTN_Y,
155+
ResearchTableMenu.LIST_BTN_W, ResearchTableMenu.LIST_BTN_H)
154156
.build());
155157

156158
buildGraph();
@@ -174,12 +176,12 @@ public void containerTick() {
174176
clampPan();
175177
}
176178

177-
boolean canStart = !menu.isResearching() && selectedId != null;
178-
if (canStart) {
179-
ResearchDefinition selected = ResearchRegistry.get(selectedId);
180-
canStart = selected != null && isPrerequisiteMet(selected);
181-
}
179+
// Update button states (same logic as ResearchTableScreen)
180+
ResearchDefinition selectedDef = getSelectedDefinition();
181+
boolean canStart = !menu.isResearching() && selectedDef != null && canStartResearch(selectedDef);
182182
startButton.active = canStart;
183+
cancelButton.active = menu.isResearching();
184+
wipeButton.active = menu.getFluidAmount() > 0;
183185
}
184186

185187
private void openListView() {
@@ -191,12 +193,70 @@ private void openListView() {
191193
private void onStartResearch() {
192194
if (selectedId == null || menu.isResearching()) return;
193195
ResearchDefinition def = ResearchRegistry.get(selectedId);
194-
if (def == null || !isPrerequisiteMet(def)) return;
196+
if (def == null || !canStartResearch(def)) return;
195197

196198
ResearchTableBlockEntity be = menu.getBlockEntity();
197199
PacketDistributor.sendToServer(new StartResearchPacket(be.getBlockPos(), selectedId.toString()));
198200
}
199201

202+
private void onCancelResearch() {
203+
if (!menu.isResearching()) return;
204+
ResearchTableBlockEntity be = menu.getBlockEntity();
205+
PacketDistributor.sendToServer(new CancelResearchPacket(be.getBlockPos()));
206+
}
207+
208+
private void onWipeTank() {
209+
ResearchTableBlockEntity be = menu.getBlockEntity();
210+
PacketDistributor.sendToServer(new WipeTankPacket(be.getBlockPos()));
211+
}
212+
213+
private boolean canStartResearch(ResearchDefinition def) {
214+
if (!isPrerequisiteMet(def)) return false;
215+
if (!isIdeaChipSatisfied(def)) return false;
216+
if (!hasRequiredItems(def)) return false;
217+
if (!hasRequiredFluid(def)) return false;
218+
return true;
219+
}
220+
221+
private boolean hasRequiredItems(ResearchDefinition def) {
222+
if (def.getItemCosts().isEmpty()) return true;
223+
Map<Item, Integer> available = new HashMap<>();
224+
for (int i = 0; i < 6; i++) {
225+
int slotIndex = ResearchTableBlockEntity.COST_SLOT_START + i;
226+
ItemStack stack = menu.getSlot(slotIndex).getItem();
227+
if (!stack.isEmpty()) {
228+
available.merge(stack.getItem(), stack.getCount(), Integer::sum);
229+
}
230+
}
231+
for (ItemCost cost : def.getItemCosts()) {
232+
int availableCount = available.getOrDefault(cost.getItem(), 0);
233+
if (availableCount < cost.count()) return false;
234+
}
235+
return true;
236+
}
237+
238+
private boolean hasRequiredFluid(ResearchDefinition def) {
239+
FluidCost fluidCost = def.getFluidCost();
240+
if (fluidCost == null) return true;
241+
int tankFluidType = menu.getFluidType();
242+
int tankAmount = menu.getFluidAmount();
243+
int requiredType = ModFluids.getFluidIndex(fluidCost.getFluid());
244+
if (tankFluidType != requiredType) return false;
245+
return tankAmount >= fluidCost.amount();
246+
}
247+
248+
private boolean isIdeaChipSatisfied(ResearchDefinition def) {
249+
if (def.getIdeaChip().isEmpty()) return true;
250+
ItemStack required = def.getIdeaChip().get();
251+
ItemStack candidate = menu.getSlot(ResearchTableBlockEntity.SLOT_IDEA_CHIP).getItem();
252+
return IdeaChipMatcher.matches(required, candidate);
253+
}
254+
255+
private ResearchDefinition getSelectedDefinition() {
256+
if (selectedId == null) return null;
257+
return ResearchRegistry.get(selectedId);
258+
}
259+
200260
private boolean isPrerequisiteMet(ResearchDefinition def) {
201261
return def.getPrerequisites().isSatisfied(menu.getCompletedResearch());
202262
}
@@ -418,6 +478,18 @@ protected void renderBg(GuiGraphics g, float partialTick, int mouseX, int mouseY
418478
// ── Static background from texture (same as ResearchTableScreen) ──
419479
g.blit(TEXTURE, x, y, 0, 0, imageWidth, imageHeight, TEX_W, TEX_H);
420480

481+
// ── Slot labels (above slot row) ──
482+
int labelY = y + ResearchTableMenu.LABEL_Y;
483+
g.drawString(font, "Dr", x + ResearchTableMenu.DRIVE_X + 2, labelY, 0xFFD3D7E5, false);
484+
g.drawString(font, "Cb", x + ResearchTableMenu.CUBE_X + 2, labelY, 0xFFD3D7E5, false);
485+
g.drawString(font, "Id", x + ResearchTableMenu.IDEA_CHIP_X + 2, labelY, 0xFFD3D7E5, false);
486+
g.drawString(font, "Costs", x + ResearchTableMenu.COST_X, labelY, 0xFFD3D7E5, false);
487+
g.drawString(font, "Fl", x + ResearchTableMenu.FLUID_GAUGE_X + 3, labelY, 0xFFD3D7E5, false);
488+
g.drawString(font, "I/O", x + ResearchTableMenu.BUCKET_IN_X, labelY, 0xFFD3D7E5, false);
489+
490+
// ── Idea chip dynamic overlay ──
491+
renderIdeaChipOverlay(g, x + ResearchTableMenu.IDEA_CHIP_X, y + ResearchTableMenu.IDEA_CHIP_Y);
492+
421493
// ── Fluid gauge (dynamic) ──
422494
drawFluidGauge(g, x + ResearchTableMenu.FLUID_GAUGE_X, y + ResearchTableMenu.FLUID_GAUGE_Y,
423495
ResearchTableMenu.FLUID_GAUGE_W, ResearchTableMenu.FLUID_GAUGE_H);
@@ -435,6 +507,28 @@ protected void renderBg(GuiGraphics g, float partialTick, int mouseX, int mouseY
435507
drawEdges(g, gx, gy);
436508
drawNodes(g, gx, gy);
437509
g.disableScissor();
510+
511+
// ── Edge legend (inside graph viewport, top-left corner) ──
512+
int legendX = gx + 4;
513+
int legendY = gy + 4;
514+
g.fill(legendX - 2, legendY - 2, legendX + 62, legendY + 12, 0xAA1A1E2A);
515+
g.drawString(font, "AND", legendX, legendY, EDGE_AND, false);
516+
g.drawString(font, "OR", legendX + 22, legendY, EDGE_OR, false);
517+
g.drawString(font, "S", legendX + 38, legendY, EDGE_SINGLE, false);
518+
}
519+
520+
private void renderIdeaChipOverlay(GuiGraphics g, int sx, int sy) {
521+
ResearchDefinition selected = getSelectedDefinition();
522+
if (selected == null || selected.getIdeaChip().isEmpty()) {
523+
g.fill(sx, sy, sx + 16, sy + 16, 0x88000000);
524+
} else if (!isIdeaChipSatisfied(selected)) {
525+
int x0 = sx - 1;
526+
int y0 = sy - 1;
527+
g.fill(x0, y0, x0 + 18, y0 + 1, 0xFFFF3333);
528+
g.fill(x0, y0 + 17, x0 + 18, y0 + 18, 0xFFFF3333);
529+
g.fill(x0, y0, x0 + 1, y0 + 18, 0xFFFF3333);
530+
g.fill(x0 + 17, y0, x0 + 18, y0 + 18, 0xFFFF3333);
531+
}
438532
}
439533

440534
private void drawFluidGauge(GuiGraphics g, int gx, int gy, int gw, int gh) {
@@ -641,12 +735,81 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl
641735
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTick) {
642736
super.render(graphics, mouseX, mouseY, partialTick);
643737

738+
// Node tooltip
644739
Optional<NodeBox> hovered = getHoveredNode(mouseX, mouseY);
645740
hovered.ifPresent(node -> renderNodeTooltip(graphics, node, mouseX, mouseY));
646741

742+
// Fluid gauge tooltip
743+
renderFluidGaugeTooltip(graphics, mouseX, mouseY);
744+
745+
// Idea chip tooltip
746+
renderIdeaChipTooltip(graphics, mouseX, mouseY);
747+
647748
renderTooltip(graphics, mouseX, mouseY);
648749
}
649750

751+
private void renderFluidGaugeTooltip(GuiGraphics graphics, int mouseX, int mouseY) {
752+
int gx = leftPos + ResearchTableMenu.FLUID_GAUGE_X;
753+
int gy = topPos + ResearchTableMenu.FLUID_GAUGE_Y;
754+
int gw = ResearchTableMenu.FLUID_GAUGE_W;
755+
int gh = ResearchTableMenu.FLUID_GAUGE_H;
756+
757+
if (mouseX < gx - 1 || mouseX >= gx + gw + 1 || mouseY < gy - 1 || mouseY >= gy + gh + 1) {
758+
return;
759+
}
760+
761+
List<Component> tooltip = new ArrayList<>();
762+
int fluidAmount = menu.getFluidAmount();
763+
int fluidType = menu.getFluidType();
764+
765+
if (fluidAmount > 0 && fluidType > 0) {
766+
int color = ModFluids.getFluidColor(fluidType);
767+
tooltip.add(Component.literal(ModFluids.getFluidName(fluidType))
768+
.withStyle(s -> s.withColor(color & 0x00FFFFFF)));
769+
tooltip.add(Component.literal(fluidAmount + " / " + ResearchTableBlockEntity.TANK_CAPACITY + " mB")
770+
.withStyle(s -> s.withColor(0xBBBBBB)));
771+
} else {
772+
tooltip.add(Component.literal("Empty")
773+
.withStyle(s -> s.withColor(0x888888)));
774+
tooltip.add(Component.literal("0 / " + ResearchTableBlockEntity.TANK_CAPACITY + " mB")
775+
.withStyle(s -> s.withColor(0xBBBBBB)));
776+
}
777+
tooltip.add(Component.literal("Insert fluid buckets below")
778+
.withStyle(s -> s.withColor(0x666666).withItalic(true)));
779+
780+
graphics.renderTooltip(font, tooltip, Optional.empty(), mouseX, mouseY);
781+
}
782+
783+
private void renderIdeaChipTooltip(GuiGraphics graphics, int mouseX, int mouseY) {
784+
int sx = leftPos + ResearchTableMenu.IDEA_CHIP_X;
785+
int sy = topPos + ResearchTableMenu.IDEA_CHIP_Y;
786+
787+
if (mouseX < sx - 1 || mouseX >= sx + 17 || mouseY < sy - 1 || mouseY >= sy + 17) {
788+
return;
789+
}
790+
791+
ItemStack slotStack = menu.getSlot(ResearchTableBlockEntity.SLOT_IDEA_CHIP).getItem();
792+
if (!slotStack.isEmpty()) return;
793+
794+
List<Component> tooltip = new ArrayList<>();
795+
ResearchDefinition selected = getSelectedDefinition();
796+
797+
if (selected == null || selected.getIdeaChip().isEmpty()) {
798+
tooltip.add(Component.literal("Idea Chip Slot")
799+
.withStyle(s -> s.withColor(0x888888)));
800+
tooltip.add(Component.literal("No idea chip required for this research.")
801+
.withStyle(s -> s.withColor(0x666666).withItalic(true)));
802+
} else {
803+
ItemStack required = selected.getIdeaChip().get();
804+
tooltip.add(Component.literal("Idea Chip Slot")
805+
.withStyle(s -> s.withColor(0xFF5555)));
806+
tooltip.add(Component.literal("Requires: " + required.getHoverName().getString())
807+
.withStyle(s -> s.withColor(0xFFAAAA)));
808+
}
809+
810+
graphics.renderTooltip(font, tooltip, Optional.empty(), mouseX, mouseY);
811+
}
812+
650813
private void renderNodeTooltip(GuiGraphics graphics, NodeBox node, int mouseX, int mouseY) {
651814
List<Component> lines = new ArrayList<>();
652815
boolean completed = menu.getCompletedResearch().contains(node.def.getId().toString());
@@ -692,32 +855,13 @@ private void renderNodeTooltip(GuiGraphics graphics, NodeBox node, int mouseX, i
692855

693856
@Override
694857
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
695-
graphics.drawString(this.font, this.title, this.titleLabelX, this.titleLabelY, 0xFF202020, false);
858+
graphics.drawString(this.font, this.title, this.titleLabelX, this.titleLabelY, 0xFF343841, false);
696859
graphics.drawString(this.font, this.playerInventoryTitle, this.inventoryLabelX, this.inventoryLabelY, 0xFFE6EAF5, false);
697860

698-
// Tree view controls info
699-
graphics.drawString(this.font, "Tree View | scroll=zoom, R-drag=pan",
700-
ResearchTableMenu.SEARCH_X + 84, ResearchTableMenu.SEARCH_Y, 0xFFE5E7EB, false);
701-
702-
// Edge legend
703-
graphics.drawString(this.font, "AND", GRAPH_X + GRAPH_W - 70, ResearchTableMenu.SEARCH_Y, EDGE_AND, false);
704-
graphics.drawString(this.font, "OR", GRAPH_X + GRAPH_W - 46, ResearchTableMenu.SEARCH_Y, EDGE_OR, false);
705-
graphics.drawString(this.font, "S", GRAPH_X + GRAPH_W - 22, ResearchTableMenu.SEARCH_Y, EDGE_SINGLE, false);
706-
707-
// Slot labels in machine panel
708-
int labelY = ResearchTableMenu.LABEL_Y;
709-
graphics.drawString(this.font, "Dr", ResearchTableMenu.DRIVE_X + 2, labelY, 0xFFD3D7E5, false);
710-
graphics.drawString(this.font, "Cb", ResearchTableMenu.CUBE_X + 2, labelY, 0xFFD3D7E5, false);
711-
graphics.drawString(this.font, "Id", ResearchTableMenu.IDEA_CHIP_X + 2, labelY, 0xFFD3D7E5, false);
712-
graphics.drawString(this.font, "Costs", ResearchTableMenu.COST_X, labelY, 0xFFD3D7E5, false);
713-
graphics.drawString(this.font, "Fl", ResearchTableMenu.FLUID_GAUGE_X + 3, labelY, 0xFFD3D7E5, false);
714-
graphics.drawString(this.font, "I/O", ResearchTableMenu.BUCKET_IN_X, labelY, 0xFFD3D7E5, false);
715-
861+
// Researching indicator (same as ResearchTableScreen)
716862
if (menu.isResearching()) {
717863
graphics.drawString(this.font, "\u25CF Researching",
718864
ResearchTableMenu.MACHINE_PANEL_X + 4, ResearchTableMenu.BUTTON_Y + 18, 0xFF77DD77, false);
719865
}
720-
graphics.drawString(this.font, Math.round(zoom * 100f) + "%",
721-
ResearchTableMenu.SEARCH_X + 82, ResearchTableMenu.TREE_BTN_Y, 0xFFD9DDE7, false);
722866
}
723867
}

0 commit comments

Comments
 (0)