Skip to content
408 changes: 228 additions & 180 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ public boolean tryStartResearch(String researchId, Set<String> completedResearch
this.startTime = level.getGameTime();
this.researchKey = researchKey;
setChanged();
// Sync block entity state to client so GUI can display progress view
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);

// Play start sound (subtle click/buzz)
level.playSound(null, worldPosition, SoundEvents.UI_BUTTON_CLICK.value(), SoundSource.BLOCKS, 0.6f, 1.2f);
Expand Down Expand Up @@ -539,6 +541,8 @@ public void clearResearch() {
private void clearResearchAndNotify(Level level) {
String key = this.researchKey;
clearResearch();
// Sync block entity state to client so GUI can switch back to list view
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
if (key != null && level instanceof ServerLevel serverLevel) {
sendClearPacket(serverLevel, key);
}
Expand Down
817 changes: 537 additions & 280 deletions src/main/java/com/researchcube/client/screen/ResearchTableScreen.java

Large diffs are not rendered by default.

343 changes: 239 additions & 104 deletions src/main/java/com/researchcube/client/screen/ResearchTreeScreen.java

Large diffs are not rendered by default.

140 changes: 123 additions & 17 deletions src/main/java/com/researchcube/menu/ResearchTableMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,129 @@ public class ResearchTableMenu extends AbstractContainerMenu {
public static final int DATA_FLUID_TYPE = 3; // 0=empty, 1=thinking, 2=pondering, 3=reasoning, 4=imagination
public static final int DATA_COUNT = 4;

// Layout coordinates shared by screens
public static final int DRIVE_X = 26;
public static final int DRIVE_Y = 42;
public static final int CUBE_X = 26;
public static final int CUBE_Y = 78;
public static final int COST_X = 70;
public static final int COST_Y = 42;
public static final int BUCKET_IN_X = 70;
public static final int BUCKET_IN_Y = 86;
public static final int BUCKET_OUT_X = 92;
public static final int BUCKET_OUT_Y = 86;
public static final int IDEA_CHIP_X = 134;
public static final int IDEA_CHIP_Y = 60;
public static final int PLAYER_INV_X = 179;
public static final int PLAYER_INV_Y = 164;
public static final int HOTBAR_X = 179;
public static final int HOTBAR_Y = 222;
// ══════════════════════════════════════════════════════════════
// Layout coordinates (shared by screens)
// ══════════════════════════════════════════════════════════════
//
// GUI Dimensions: 470 x 280
//
// TOP SECTION: Research Browser (centered, y=8 to y=140)
// - Search box + Tree button
// - Research list (5 visible rows)
// - Detail pane
//
// BOTTOM SECTION: Machine Panel + Player Inventory (side by side, y=148)
// - Machine Panel (left): Drive, Cube, Cost grid, Buckets, Idea, Gauge, Progress, Buttons
// - Player Inventory (right): 9x3 main + hotbar

public static final int GUI_WIDTH = 470;
public static final int GUI_HEIGHT = 260;

// ═══════════════════════════════════════════════════════════════
// UPPER PANEL (top, centered) - shows List/Tree/Progress views
// Panel contains: search/controls bar + main content area
// ═══════════════════════════════════════════════════════════════
public static final int UPPER_PANEL_X = 8;
public static final int UPPER_PANEL_Y = 4;
public static final int UPPER_PANEL_W = 454;
public static final int UPPER_PANEL_H = 140; // extended to fit detail pane

// Controls bar (search + buttons)
public static final int SEARCH_X = 16;
public static final int SEARCH_Y = 10;
public static final int SEARCH_W = 340;
public static final int SEARCH_H = 14;

public static final int TREE_BTN_X = 364;
public static final int TREE_BTN_Y = 8;
public static final int TREE_BTN_W = 46;
public static final int TREE_BTN_H = 16;

public static final int LIST_BTN_X = 414;
public static final int LIST_BTN_Y = 8;
public static final int LIST_BTN_W = 40;
public static final int LIST_BTN_H = 16;

// List view components
public static final int LIST_X = 16;
public static final int LIST_Y = 28;
public static final int LIST_W = 438;
public static final int LIST_ROW_H = 18;
public static final int LIST_VISIBLE_ROWS = 4;

public static final int DETAIL_X = 16;
public static final int DETAIL_Y = 102;
public static final int DETAIL_W = 438;
public static final int DETAIL_H = 38;

// Progress view components (replaces list when researching)
public static final int PROGRESS_VIEW_X = 16;
public static final int PROGRESS_VIEW_Y = 28;
public static final int PROGRESS_VIEW_W = 438;
public static final int PROGRESS_VIEW_H = 112; // full content area for progress info

public static final int PROGRESS_BAR_X = 16;
public static final int PROGRESS_BAR_Y = 120; // near bottom of upper panel
public static final int PROGRESS_BAR_W = 438;
public static final int PROGRESS_BAR_H = 12;

// ═══════════════════════════════════════════════════════════════
// MACHINE PANEL (bottom left) - HORIZONTAL LAYOUT
// Row 1: Drive, Cube, Idea | Cost grid top | Fluid gauge, Bucket In
// Row 2: | Cost grid bot | Fluid gauge, Bucket Out
// Row 3: Buttons
// ═══════════════════════════════════════════════════════════════
public static final int MACHINE_PANEL_X = 12;
public static final int MACHINE_PANEL_Y = 148;
public static final int MACHINE_PANEL_W = 200;

// Labels row
public static final int LABEL_Y = 156;
public static final int LABEL_HEIGHT = 10;

// Slot row 1 & 2 (y=168 and y=186)
public static final int SLOT_ROW_1_Y = 168;
public static final int SLOT_ROW_2_Y = 186;

// Drive/Cube/Idea - horizontal row
public static final int DRIVE_X = 20;
public static final int DRIVE_Y = 168;
public static final int CUBE_X = 40;
public static final int CUBE_Y = 168;
public static final int IDEA_CHIP_X = 60;
public static final int IDEA_CHIP_Y = 168;

// Cost grid (3x2) - center
public static final int COST_X = 96;
public static final int COST_Y = 168;
// Grid: x=96,114,132 ; y=168,186

// Fluid gauge + Buckets - right side
public static final int FLUID_GAUGE_X = 168;
public static final int FLUID_GAUGE_Y = 168;
public static final int FLUID_GAUGE_W = 18;
public static final int FLUID_GAUGE_H = 36; // 2 slots tall

public static final int BUCKET_IN_X = 190;
public static final int BUCKET_IN_Y = 168;
public static final int BUCKET_OUT_X = 190;
public static final int BUCKET_OUT_Y = 186;

// Buttons (single row, moved up)
public static final int BUTTON_Y = 210;
public static final int BUTTON_W = 42;
public static final int BUTTON_H = 14;
public static final int START_BTN_X = 20;
public static final int STOP_BTN_X = 66;
public static final int WIPE_BTN_X = 112;

// ═══════════════════════════════════════════════════════════════
// PLAYER INVENTORY (bottom right) - aligned with machine panel
// ═══════════════════════════════════════════════════════════════
public static final int PLAYER_INV_X = 229;
public static final int PLAYER_INV_Y = 168; // same as slot row 1
public static final int HOTBAR_X = 229;
public static final int HOTBAR_Y = 230; // below main inv

// ── Constructor from server (block entity available) ──
public ResearchTableMenu(int containerId, Inventory playerInv, ResearchTableBlockEntity be) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public ResearchDefinition(ResourceLocation id, ResearchTier tier, int duration,
Prerequisite prerequisites, List<ItemCost> itemCosts,
List<WeightedRecipe> weightedRecipePool,
@Nullable String name, @Nullable String description,
@Nullable String category, @Nullable FluidCost fluidCost,
@Nullable String category,
@Nullable FluidCost fluidCost,
Optional<ItemStack> ideaChip) {
this.id = id;
this.tier = tier;
Expand All @@ -72,7 +73,8 @@ public ResearchDefinition(ResourceLocation id, ResearchTier tier, int duration,
Prerequisite prerequisites, List<ItemCost> itemCosts,
List<WeightedRecipe> weightedRecipePool,
@Nullable String name, @Nullable String description,
@Nullable String category, @Nullable FluidCost fluidCost) {
@Nullable String category,
@Nullable FluidCost fluidCost) {
this(id, tier, duration, prerequisites, itemCosts, weightedRecipePool,
name, description, category, fluidCost, Optional.empty());
}
Expand All @@ -97,7 +99,7 @@ public ResearchDefinition(ResourceLocation id, ResearchTier tier, int duration,
List<ResourceLocation> recipePool) {
this(id, tier, duration, prerequisites, itemCosts,
recipePool.stream().map(rl -> new WeightedRecipe(rl, 1)).toList(),
null, null, null, null);
null, null, null, null, null);
}

public ResourceLocation getId() {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/researchcube/research/ResearchManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ private ResearchDefinition parseDefinition(ResourceLocation id, JsonObject json)
}

return new ResearchDefinition(id, tier, duration, prerequisites, itemCosts, weightedRecipePool,
parseName(json), parseDescription(json), parseCategory(json), parseFluidCost(json),
parseIdeaChip(json));
parseName(json), parseDescription(json), parseCategory(json),
parseFluidCost(json), parseIdeaChip(json));
}

@Nullable
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/researchcube/util/TierUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ private TierUtil() {}
/**
* Validates whether a research operation is allowed based on tier rules:
* - cube.tier >= research.tier
* - drive.tier == research.tier
* - drive.tier >= research.tier (higher tier drives can research lower tier research)
*
* @param cubeTier the tier of the cube in the Research Table
* @param driveTier the tier of the drive in the Research Table
Expand All @@ -31,7 +31,7 @@ public static boolean canResearch(ResearchTier cubeTier, ResearchTier driveTier,
if (!cubeTier.isAtLeast(researchTier)) {
return false;
}
// Drive must exactly match the research tier
return driveTier == researchTier;
// Drive must be at least the research tier (higher tier drives can research lower tier)
return driveTier.isAtLeast(researchTier);
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Cosmic Assembly",
"description": "The ultimate capstone research — assemble cosmic-tier items. Demonstrates a 3-recipe weighted pool at the highest tier. Requires both star_forging and void_synthesis.",
"flavor_text": "When star and void converge, creation itself bends to your will.",
"category": "endgame",
"tier": "SELF_AWARE",
"duration": 48000,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Crystal Growing",
"description": "Grow crystals in a controlled environment using the Processing Station. A processing-only recipe at PRECISE tier.",
"flavor_text": "Patience is the seed from which all great crystals grow.",
"category": "processing",
"tier": "PRECISE",
"duration": 3200,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Ender Weaving",
"description": "Weave ender energy into items. Requires an Irrecoverable Drive as an idea chip and AND prerequisites (lapis_infusion AND potion_brewing).",
"flavor_text": "Between the cracks of reality, the Ender threads wait to be spun.",
"category": "magic",
"tier": "FLAWLESS",
"duration": 8000,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Iron Working",
"description": "Master iron crafting techniques. Demonstrates a simple prerequisite and unlocks both a shaped drive crafting recipe and a processing recipe.",
"flavor_text": "The forge remembers every hammer blow. Make each one count.",
"category": "materials",
"tier": "BASIC",
"duration": 1000,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Redstone Basics",
"description": "Unlock fundamental redstone mechanisms. Demonstrates the idea chip feature — requires an Irrecoverable Drive as an idea chip to begin.",
"flavor_text": "A single pulse of redstone can awaken an entire machine.",
"category": "redstone",
"tier": "BASIC",
"duration": 1200,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Soul Extraction",
"description": "Extract soul energy from soul sand for crafting. A shaped drive crafting recipe from the crystal growing branch.",
"flavor_text": "The sand whispers of lives long past. Listen carefully.",
"category": "magic",
"tier": "FLAWLESS",
"duration": 8000,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Star Forging",
"description": "Forge nether stars into powerful components. Demonstrates a weighted recipe pool at the SELF_AWARE tier with two drive crafting recipes.",
"flavor_text": "To hold a star is to hold the memory of a world that burned.",
"category": "endgame",
"tier": "SELF_AWARE",
"duration": 24000,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Stone Refinement",
"description": "Learn to process raw stone into refined building materials. A simple entry-level research with no prerequisites.",
"flavor_text": "Even the humblest cobblestone holds secrets for those who dare to look.",
"category": "materials",
"tier": "UNSTABLE",
"duration": 600,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "Void Synthesis",
"description": "Synthesize void energy into tangible form. Requires an Irrecoverable Drive as an idea chip — demonstrating endgame idea chip gating. Uses AND prerequisites.",
"flavor_text": "Nothing is never truly empty. The void itself has substance.",
"category": "endgame",
"tier": "SELF_AWARE",
"duration": 24000,
Expand Down
6 changes: 6 additions & 0 deletions todo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ Tasks marked as [DONE] have been completed and should not be suggested for new w
[END AI INSTRUCTIONS]


Known issues:
[RESEARCH STATION]
- (none — all known issues resolved)


Todo:
[DONE — Phase 1: Foundation]
- [DONE] Build system: build.gradle, settings.gradle, gradle.properties, neoforge.mods.toml, pack.mcmeta
- [DONE] ResearchTier enum (7 values: IRRECOVERABLE → SELF_AWARE), StringRepresentable
Expand Down
Loading