Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0c80918
Backport dupefix
PLASMAchicken Dec 23, 2025
3ed7be1
Spotless :(
PLASMAchicken Dec 23, 2025
0a6335e
Remove slotClick that doesnt work anyhow
PLASMAchicken Dec 24, 2025
28ea0fc
Make ItemPlan behave like AE2 Patterns when holding Shift
PLASMAchicken Dec 24, 2025
2a1d111
remove useless )
PLASMAchicken Dec 24, 2025
14807a2
Fix wrong OreDict matching
PLASMAchicken Dec 24, 2025
3051015
Add Recipe Validation before allowing Output
PLASMAchicken Dec 24, 2025
1c18b6c
Move onCrafting() to correct Position
PLASMAchicken Dec 24, 2025
733fe30
Use tile.invCrafting
PLASMAchicken Dec 24, 2025
80579f8
Port #16 to Project Bench
PLASMAchicken Dec 24, 2025
a0e3868
Port #16 to Project Bench Part 2: Move back Remainders
PLASMAchicken Dec 24, 2025
0bba7da
Spotless
PLASMAchicken Dec 24, 2025
950adea
Fix Auto Crafter able to craft items with different NBT
Worive Dec 13, 2025
72f7a31
Port #60 to ProjectBench
PLASMAchicken Dec 25, 2025
37c6b4e
Make ItemPlan behave like AE2 Patterns when holding Shift
PLASMAchicken Dec 25, 2025
f0da73e
spotless
PLASMAchicken Dec 25, 2025
4449645
Prefer Crafting Grid Materials when not PlanRecipe
PLASMAchicken Dec 25, 2025
f810218
Disabe OreDict because it ignores NBT check
PLASMAchicken Dec 25, 2025
5e6813d
Merge remote-tracking branch 'upstream/master' into rework-projectred…
PLASMAchicken Jun 3, 2026
9e44e12
remove duplicate code
PLASMAchicken Jun 3, 2026
ceb682b
Update dependencies.gradle
PLASMAchicken Jun 3, 2026
5047abc
AutoCrafter no Ore Matching due to ignoring NBT
PLASMAchicken Jun 3, 2026
a4e55a5
Revert "Update dependencies.gradle"
PLASMAchicken Jun 3, 2026
ecab4bb
Apply suggestions from code review
PLASMAchicken Jun 4, 2026
4a1e6e0
Update en_US.lang
PLASMAchicken Jun 4, 2026
a19b346
Merge branch 'master' into rework-projectred-crafting
Dream-Master Jun 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/main/resources/assets/projectred/lang/en_US.lang
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ item.projectred.expansion.electric_screwdriver.name=Electric Screwdriver
item.projectred.expansion.jetpack.name=Jetpack
item.projectred.expansion.infused_ender_pearl.name=Infused Ender Pearl
item.projectred.expansion.plan.name=Recipe Plan
item.projectred.expansion.plan.tooltip.output=Output: %s
item.projectred.expansion.plan.tooltip.inputs=Inputs:
item.projectred.expansion.plan.tooltip.empty=Empty
tile.projectred.expansion.machine1.name=Machine
tile.projectred.expansion.machine1|0.name=Inductive Furnace
tile.projectred.expansion.machine1|1.name=Electrotine Generator
Expand Down
15 changes: 2 additions & 13 deletions src/main/scala/mrtjp/projectred/expansion/TileAutoCrafter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@ class TileAutoCrafter
recipeEquality.matchNBT = true
recipeEquality.matchMeta =
!currentOutput.key.makeStack(0).isItemStackDamageable
recipeEquality.matchOre = currentRecipe.isInstanceOf[ShapedOreRecipe] ||
currentRecipe.isInstanceOf[ShapelessOreRecipe]
recipeEquality.matchOre = false
}
}

Expand Down Expand Up @@ -210,8 +209,7 @@ class TileAutoCrafter
val eq = new ItemEquality
eq.matchMeta = !item.makeStack(0).isItemStackDamageable
eq.matchNBT = true
eq.matchOre = currentRecipe.isInstanceOf[ShapedOreRecipe] || currentRecipe
.isInstanceOf[ShapelessOreRecipe]
eq.matchOre = false

var found = 0
for (i <- 9 until 27) {
Expand Down Expand Up @@ -400,15 +398,6 @@ class GuiAutoCrafter(tile: TileAutoCrafter, c: ContainerAutoCrafter)
false
)
}

override def drawFront_Impl(mouse: Point, rframe: Float) {
if (
Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(
Keyboard.KEY_RSHIFT
)
)
GuiProjectBench.drawPlanOutputOverlay(c.slots)
}
}

object GuiAutoCrafter extends TGuiBuilder {
Expand Down
167 changes: 92 additions & 75 deletions src/main/scala/mrtjp/projectred/expansion/TileProjectBench.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package mrtjp.projectred.expansion

import java.util.{List => JList}

import codechicken.lib.data.MCDataInput
import codechicken.lib.gui.GuiDraw
import codechicken.lib.render.uv.{MultiIconTransformation, UVTransformation}
Expand Down Expand Up @@ -37,7 +36,6 @@ import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.IIcon
import net.minecraft.world.{IBlockAccess, World}
import net.minecraftforge.oredict.{ShapedOreRecipe, ShapelessOreRecipe}
import org.lwjgl.input.Keyboard

import scala.collection.JavaConversions._

Expand Down Expand Up @@ -201,15 +199,17 @@ class SlotProjectCrafting(
tile.currentRecipe,
tile.currentInputs,
storage
)
) && tile.currentRecipe.matches(tile.invCrafting, tile.world)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears this matches logic could be lifted as an early out to avoid duplicating it

}

// copied from super for obfuscation bug
canRemoveDelegate()
canRemoveDelegate() && tile.currentRecipe.matches(
tile.invCrafting,
tile.world
)
}

override def onPickupFromSlot(player: EntityPlayer, stack: ItemStack) {
onCrafting(stack)

val storage = ((9 until 27) ++ (0 until 9)).map { i =>
val s = tile.getStackInSlot(i)
Expand All @@ -233,14 +233,75 @@ class SlotProjectCrafting(
}
}

val invCrafting = new InventoryCrafting(new NodeContainer, 3, 3)
for (i <- 0 until 9)
invCrafting.setInventorySlotContents(i, tile.currentInputs(i))
FMLCommonHandler
.instance()
.firePlayerCraftingEvent(player, stack, invCrafting)
FMLCommonHandler.instance.firePlayerCraftingEvent(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did the () go?

TIL Scala is totally okay without parens here but I think it should be consistent with the rest of the file.

player,
stack,
tile.invCrafting
)
onCrafting(stack)

for (i <- 0 until 9) {

val gridStack = tile.invCrafting.getStackInSlot(
i
) // current real item in grid (maybe null or decreased)
val remainder = getRemaining(i, tile.invCrafting)

if (remainder != null && gridStack != null) {
// Case 1: No plan active AND this grid slot is now empty → put remainder back in grid
if (!tile.isPlanRecipe && (gridStack.isItemEqual(remainder))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how gridStack.isItemEqual(remainder) means it is now empty

tile.setInventorySlotContents(i, remainder)
}
// Case 2: Try to merge remainder into storage slots (9-26) or player inventory
else if (
!tryAddToStorageSlots(remainder) &&
!player.inventory.addItemStackToInventory(remainder)
) {
// If no space anywhere, drop it
player.dropPlayerItemWithRandomChoice(remainder, false)
}
}
}
tile.updateRecipe()

}
def tryAddToStorageSlots(stack: ItemStack): Boolean = {
// Try to merge into storage slots 9-26
for (j <- 9 until 27) {
val slotStack = tile.getStackInSlot(j)
if (
slotStack != null && slotStack.isItemEqual(stack) &&
ItemStack.areItemStackTagsEqual(slotStack, stack) &&
slotStack.stackSize < slotStack.getMaxStackSize
) {
val space = slotStack.getMaxStackSize - slotStack.stackSize
val toAdd = Math.min(space, stack.stackSize)
slotStack.stackSize += toAdd
stack.stackSize -= toAdd
if (stack.stackSize <= 0) return true
}
}

// Try to place in empty storage slot
for (j <- 9 until 27) {
if (tile.getStackInSlot(j) == null) {
tile.setInventorySlotContents(j, stack)
return true
}
}

false
}

def getRemaining(i: Int, invCrafting: InventoryCrafting): ItemStack = {
val stack = invCrafting.getStackInSlot(i)

if (stack != null) {
val item = stack.getItem
if (item != null && item.hasContainerItem(stack)) {
item.getContainerItem(stack)
} else null
} else null
}

def searchFor(
Expand All @@ -249,54 +310,53 @@ class SlotProjectCrafting(
inputs: Array[ItemStack],
storage: Array[ItemStack]
): Boolean = {
i = 0
val invCrafting = new InventoryCrafting(new NodeContainer, 3, 3)
if (tile.isPlanRecipe) i = 0 else i = 17
for (i <- 0 until 9) {
val item = inputs(i)
if (item != null) {
if (!eatResource(recipe, item, storage)) return false
invCrafting.setInventorySlotContents(i, item)
val eatenItem = eatResource(recipe, item, storage)
if (eatenItem == null) return false
tile.invCrafting.setInventorySlotContents(i, eatenItem)
}
}
recipe.matches(invCrafting, world)
recipe.matches(tile.invCrafting, world)
}

private var i = 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holly shadowing, I know it is old code but this should be renamed

private def eatResource(
recipe: IRecipe,
stack1: ItemStack,
storage: Array[ItemStack]
): Boolean = {
): ItemStack = {
def increment() = { i = (i + 1) % storage.length; i }
if (i < 18) i = 0 else increment()
if (!tile.isPlanRecipe) increment()
val start = i
do {
val stack2 = storage(i)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming these from 1/2 to something more descriptive would also be nice

if (stack2 != null && ingredientMatch(recipe, stack1, stack2)) {
if (stack2.getItem.hasContainerItem(stack2)) {
val cStack = stack2.getItem.getContainerItem(stack2)
storage(i) =
if (cStack.getItemDamage < cStack.getMaxDamage) cStack else null
return true
} else if (stack2.stackSize >= 1) {
stack2.stackSize -= 1
return true
stack2.stackSize -= 1;
if (stack2.stackSize <= 0) {
storage(i) = null;
}

val copy = stack2.copy();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it is worth it but would skipping the copy when the item is deleted be beneficial?

copy.stackSize = 1;
return copy;
}
} while (increment() != start)
false
null
}

private def ingredientMatch(
recipe: IRecipe,
stack1: ItemStack,
stack2: ItemStack
) = {

val eq = new ItemEquality
eq.matchMeta = !stack1.isItemStackDamageable
eq.matchNBT = false
eq.matchOre = recipe.isInstanceOf[ShapedOreRecipe] || recipe
.isInstanceOf[ShapelessOreRecipe]
eq.matchNBT = true
eq.matchOre = false
eq.matches(ItemKey.get(stack1), ItemKey.get(stack2))
}

Expand Down Expand Up @@ -338,17 +398,6 @@ class ContainerProjectBench(player: EntityPlayer, tile: TileProjectBench)
detectAndSendChanges()
}

override def slotClick(
id: Int,
mouse: Int,
shift: Int,
player: EntityPlayer
) = {
var mode = shift
if (id == 28 && mode == 6) mode = 0
super.slotClick(id, mouse, mode, player)
}

override def transferStackInSlot(player: EntityPlayer, i: Int): ItemStack = {
if (i == 28 && !getSlot(28).canTakeStack(player))
null
Expand All @@ -362,7 +411,7 @@ class ContainerProjectBench(player: EntityPlayer, tile: TileProjectBench)
if (tryMergeItemStack(stack, 9, 27, false))
return true // merge to storage
if (tryMergeItemStack(stack, 29, 65, false))
return true // merge to inventory)
return true // merge to inventory
} else if (9 until 27 contains from) // storage
{
if (stack.getItem.isInstanceOf[ItemPlan]) {
Expand All @@ -383,7 +432,7 @@ class ContainerProjectBench(player: EntityPlayer, tile: TileProjectBench)
if (tryMergeItemStack(stack, 9, 27, true))
return true // merge to storage
if (tryMergeItemStack(stack, 29, 65, false))
return true // merge to inventory)
return true // merge to inventory
} else if (from == 28) // output slot
{
if (tryMergeItemStack(stack, 29, 65, true))
Expand Down Expand Up @@ -475,15 +524,6 @@ class GuiProjectBench(tile: TileProjectBench, c: ContainerProjectBench)
false
)
}

override def drawFront_Impl(mouse: Point, rframe: Float) {
if (
Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(
Keyboard.KEY_RSHIFT
)
)
GuiProjectBench.drawPlanOutputOverlay(c.slots)
}
}

object GuiProjectBench extends TGuiBuilder {
Expand All @@ -497,29 +537,6 @@ object GuiProjectBench extends TGuiBuilder {
case _ => null
}
}

def drawPlanOutputOverlay(slots: Iterable[TSlot3]) {
for (slot <- slots) if (slot.getHasStack) {
val stack = slot.getStack
if (ItemPlan.hasRecipeInside(stack)) {
val output = ItemPlan.loadPlanOutput(stack)
GuiDraw.drawRect(
slot.xDisplayPosition,
slot.yDisplayPosition,
16,
16,
Colors.LIGHT_BLUE.argb(0xcc)
)
ItemDisplayNode.renderItem(
Point(slot.xDisplayPosition + 1, slot.yDisplayPosition + 1),
Size(14, 14),
0,
true,
output
)
}
}
}
}

object RenderProjectBench extends TCubeMapRender {
Expand Down
Loading