diff --git a/build.gradle b/build.gradle index 4f31cdb05..c403b7683 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ import java.time.Instant plugins { id 'java' id 'maven-publish' + id 'com.github.johnrengelman.shadow' } // Get mod version from CI, else suffix a timestamp (calculated here bc timestamp can change if done separately in each subproject) @@ -61,6 +62,11 @@ subprojects { p -> } } +// Defined explicitly so publishing can access shadowJar property +project(':fabrication') { + apply plugin: 'com.github.johnrengelman.shadow' +} + publishing { repositories { maven { @@ -76,6 +82,7 @@ publishing { artifact project(':core').jar artifact project(':expansion').jar artifact project(':exploration').jar + artifact project(':fabrication').shadowJar artifact project(':illumination').jar artifact project(':integration').jar artifact project(':transmission').jar diff --git a/core/src/main/java/mrtjp/projectred/lib/Rect.java b/core/src/main/java/mrtjp/projectred/lib/Rect.java index e12e5e79e..8e75091be 100644 --- a/core/src/main/java/mrtjp/projectred/lib/Rect.java +++ b/core/src/main/java/mrtjp/projectred/lib/Rect.java @@ -91,9 +91,15 @@ public Rect union(Rect rect) { return new Rect(x, y, w, h); } + public Rect expand(int x, int y) { + int dw = width() + x < 0 ? -width() : x; + int dh = height() + y < 0 ? -height() : y; + return new Rect(origin.subtract(dw / 2, dh / 2), new Size(width() + dw, height() + dh)); + } + public Rect trap(Rect rect) { - int dx = (rect.x() < x() ? x() - rect.x() : 0) + (rect.maxX() > maxX() ? rect.maxX() - maxX() : 0); - int dy = (rect.y() < y() ? y() - rect.y() : 0) + (rect.maxY() > maxY() ? rect.maxY() - maxY() : 0); + int dx = (rect.x() < x() ? x() - rect.x() : 0) - (rect.maxX() > maxX() ? rect.maxX() - maxX() : 0); + int dy = (rect.y() < y() ? y() - rect.y() : 0) - (rect.maxY() > maxY() ? rect.maxY() - maxY() : 0); return new Rect(rect.x() + dx, rect.y() + dy, rect.width(), rect.height()); } diff --git a/core/src/main/java/mrtjp/projectred/redui/ButtonNode.java b/core/src/main/java/mrtjp/projectred/redui/ButtonNode.java index ca2f92ff6..99ec3c686 100644 --- a/core/src/main/java/mrtjp/projectred/redui/ButtonNode.java +++ b/core/src/main/java/mrtjp/projectred/redui/ButtonNode.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.function.Consumer; +import java.util.function.Supplier; import static net.minecraft.client.gui.GuiComponent.drawCenteredString; @@ -15,6 +16,8 @@ public class ButtonNode extends AbstractButtonNode { private Runnable clickFunction = () -> { }; private Consumer> tooltipBuilder = c -> { }; + private Supplier isSelectedFunction = () -> false; + private Supplier isEnabledFunction = () -> true; private String buttonText = ""; @@ -26,6 +29,14 @@ public void setTooltipBuilder(Consumer> tooltipBuilder) { this.tooltipBuilder = tooltipBuilder; } + public void setIsSelectedFunction(Supplier isSelectedFunction) { + this.isSelectedFunction = isSelectedFunction; + } + + public void setIsEnabledFunction(Supplier isEnabledFunction) { + this.isEnabledFunction = isEnabledFunction; + } + public void setButtonText(String buttonText) { this.buttonText = buttonText; } @@ -37,7 +48,14 @@ protected void onButtonClicked() { @Override protected boolean isButtonDisabled() { - return false; + return !isEnabledFunction.get(); + } + + @Override + protected int getButtonState(boolean mouseover) { + if (isSelectedFunction.get()) + return BUTTON_STATE_HIGHLIGHT; + return super.getButtonState(mouseover); } @Override diff --git a/core/src/main/java/mrtjp/projectred/redui/ItemStackNode.java b/core/src/main/java/mrtjp/projectred/redui/ItemStackNode.java new file mode 100644 index 000000000..6a988f32e --- /dev/null +++ b/core/src/main/java/mrtjp/projectred/redui/ItemStackNode.java @@ -0,0 +1,59 @@ +package mrtjp.projectred.redui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.List; + +import static net.minecraft.client.gui.GuiComponent.fillGradient; + +public class ItemStackNode extends AbstractGuiNode { + + private ItemStack itemStack; + + public ItemStackNode(ItemStack itemStack) { + this.itemStack = itemStack; + setSize(16, 16); + } + + public ItemStackNode() { + this(ItemStack.EMPTY); + } + + public void setItemStack(ItemStack itemStack) { + this.itemStack = itemStack; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + + // Would be nice if renderGuiItem can take the matrix stack... + Point screenPos = convertParentPointToScreen(getPosition()); + getRoot().getItemRenderer().renderGuiItem(itemStack, screenPos.x, screenPos.y); + + if (isFirstHit(mouse)) { + int slotColor = -2130706433; + RenderSystem.disableDepthTest(); + RenderSystem.colorMask(true, true, true, false); + Rect frame = getFrame(); + fillGradient(stack, frame.x(), frame.y(), frame.x() + frame.width(), frame.y() + frame.height(), slotColor, slotColor, 0); + RenderSystem.colorMask(true, true, true, true); + RenderSystem.enableDepthTest(); + } + } + + @Override + public void drawFront(PoseStack stack, Point mouse, float partialFrame) { + if (isFirstHit(mouse)) { + Minecraft minecraft = getRoot().getMinecraft(); + List tooltip = itemStack.getTooltipLines(minecraft.player, minecraft.options.advancedItemTooltips ? TooltipFlag.Default.ADVANCED : TooltipFlag.Default.NORMAL); + renderTooltip(stack, mouse, tooltip); + } + } +} diff --git a/core/src/main/java/mrtjp/projectred/redui/RedUINode.java b/core/src/main/java/mrtjp/projectred/redui/RedUINode.java index 638849289..2f7310e1d 100644 --- a/core/src/main/java/mrtjp/projectred/redui/RedUINode.java +++ b/core/src/main/java/mrtjp/projectred/redui/RedUINode.java @@ -222,6 +222,14 @@ default Point convertPointTo(Point p, RedUINode to) { return p.add(difference); } + default Point convertPointToParent(Point p) { + return p.add(getPosition()); + } + + default Point convertPointFromParent(Point p) { + return p.subtract(getPosition()); + } + /** * Convert a given point p in this node's coordinate system to screen space * @@ -270,6 +278,14 @@ default Point getScreenOffset() { */ default Rect convertRectTo(Rect r, RedUINode to) { return new Rect(convertPointTo(r.origin, to), r.size); } + default Rect convertRectToParent(Rect r) { + return new Rect(convertPointToParent(r.origin), r.size); + } + + default Rect convertRectFromParent(Rect r) { + return new Rect(convertPointFromParent(r.origin), r.size); + } + /** * Converts a rectangle from this node's coordinate system to screen space * @@ -361,26 +377,25 @@ default Rect calculateGL11Frame() { } /** - * Calculate a frame encompassing all frames in the entire subtree. Hidden nodes and all its - * descendants are excluded. + * Combines frame encompassing all children with frame of this node. * * @return Rect in the coordinate system of this node's parent */ default Rect calculateAccumulatedFrame() { - Rect screenSpaceFrame = getSubTree(n -> !n.isHidden()).stream() - .map(n -> n.convertParentRectToScreen(n.getFrame())) - .reduce(Rect.ZERO, Rect::union); - return convertScreenRectToParent(screenSpaceFrame); + return getFrame().union(calculateChildrenFrame()); } + /** + * Calculate a frame encompassing all visible children and their subtrees. + * + * @return Rect in the coordinate system of this node's parent + */ default Rect calculateChildrenFrame() { - List subTree = getSubTree(n -> !n.isHidden()); - subTree.remove(0); // drop this node - - Rect screenSpaceFrame = subTree.stream() - .map(n -> n.convertParentRectToScreen(n.getFrame())) + Rect childrenFrame = getOurChildren().stream() + .filter(n -> !n.isHidden()) + .map(RedUINode::calculateAccumulatedFrame) .reduce(Rect.ZERO, Rect::union); - return convertScreenRectToParent(screenSpaceFrame); + return convertRectToParent(childrenFrame); } /** diff --git a/core/src/main/java/mrtjp/projectred/redui/RedUIRootNode.java b/core/src/main/java/mrtjp/projectred/redui/RedUIRootNode.java index e80b6fc9b..560eff9f1 100644 --- a/core/src/main/java/mrtjp/projectred/redui/RedUIRootNode.java +++ b/core/src/main/java/mrtjp/projectred/redui/RedUIRootNode.java @@ -7,7 +7,6 @@ import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.network.chat.Component; -import net.minecraft.util.FormattedCharSequence; import javax.annotation.Nullable; import java.util.List; diff --git a/core/src/main/java/mrtjp/projectred/redui/ScrollBarNode.java b/core/src/main/java/mrtjp/projectred/redui/ScrollBarNode.java index 3db506774..8f3f1b5ad 100644 --- a/core/src/main/java/mrtjp/projectred/redui/ScrollBarNode.java +++ b/core/src/main/java/mrtjp/projectred/redui/ScrollBarNode.java @@ -12,7 +12,8 @@ public abstract class ScrollBarNode extends AbstractGuiNode { private final ScrollAxis axis; - private Point lastDragPosition = Point.ZERO; + private Point initialClickPosition = Point.ZERO; + private Rect initialSliderFrame = new Rect(0, 0, 0, 0); private boolean isDraggingSlider = false; private Rect sliderFrame = new Rect(0, 0, 0, 0); @@ -34,19 +35,13 @@ public void drawBack(PoseStack stack, Point mouse, float partialFrame) { drawSlider(stack, sliderFrame); } - @Override - public void drawFront(PoseStack stack, Point mouse, float partialFrame) { - if (!isFirstHit(mouse)) return; - - renderTooltip(stack, mouse, Collections.singletonList(new TextComponent("Scroll (" + scrollPercentage + ")"))); - } - @Override public boolean mouseClicked(Point p, int glfwMouseButton, boolean consumed) { if (isFirstHit(p)) { if (sliderFrame.contains(p)) { isDraggingSlider = true; - lastDragPosition = p; + initialClickPosition = p; + initialSliderFrame = sliderFrame; return true; } @@ -71,10 +66,8 @@ public boolean mouseDragged(Point p, int glfwMouseButton, long timeHeld, boolean if (!isDraggingSlider) return false; - Point delta = p.subtract(lastDragPosition).multiply(axis.vec); - lastDragPosition = p; - - Rect targetSliderFrame = new Rect(sliderFrame.origin.add(delta), sliderFrame.size); + Point delta = p.subtract(initialClickPosition).multiply(axis.vec); + Rect targetSliderFrame = new Rect(initialSliderFrame.origin.add(delta), sliderFrame.size); sliderFrame = getFrame().trap(targetSliderFrame); recalcScrollPercentage(); diff --git a/core/src/main/java/mrtjp/projectred/redui/TextBoxNode.java b/core/src/main/java/mrtjp/projectred/redui/TextBoxNode.java new file mode 100644 index 000000000..beb4ea2df --- /dev/null +++ b/core/src/main/java/mrtjp/projectred/redui/TextBoxNode.java @@ -0,0 +1,194 @@ +package mrtjp.projectred.redui; + +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.lib.Point; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.screens.Screen; + +import static net.minecraft.client.gui.GuiComponent.fill; +import static org.lwjgl.glfw.GLFW.*; + +public abstract class TextBoxNode extends AbstractGuiNode { + + private static final int BORDER_COLOR = 0xFFA0A0A0; + private static final int BACKGROUND_COLOR = 0xFF000000; + private static final int SUGGESTION_COLOR = 0xFFA0A0A0; + private static final int ENABLED_COLOR = 0xE0E0E0; + private static final int DISABLED_COLOR = 0x707070; + + protected String currentText = ""; + + protected int characterLimit = -1; // by default, width based limit + protected boolean focused = false; + protected boolean enabled = true; + + public TextBoxNode(String initialText) { + this.currentText = initialText; + } + + public String getText() { + return currentText; + } + + public void setText(String text) { + this.currentText = text; + } + + public boolean isEditing() { + return enabled && focused; + } + + public void setCharacterLimit(int limit) { + this.characterLimit = limit; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + // Border + int x = getPosition().x; + int y = getPosition().y; + int width = getFrame().width(); + int height = getFrame().height(); + + fill(stack, x, y, x + width, y + height, BORDER_COLOR); + fill(stack, x + 1, y + 1, x + width - 1, y + height - 1, BACKGROUND_COLOR); + + Font fontRenderer = getRoot().getFontRenderer(); + String suggestion = getSuggestionString(); + int lineHeight = fontRenderer.lineHeight; + int leftPad = 4; + + int textX = x + leftPad; + int textY = y + height/2 - lineHeight/2; + + if (currentText.isEmpty() && !suggestion.isEmpty()) { + fontRenderer.draw(stack, suggestion, textX, textY, SUGGESTION_COLOR); + } + + if (!currentText.isEmpty()) { + String s = currentText; + if (enabled && focused && System.currentTimeMillis() % 1000 > 1000/2) { + s += "_"; + } + + fontRenderer.drawShadow(stack, s, textX, textY, enabled ? ENABLED_COLOR : DISABLED_COLOR); + } + } + + @Override + public boolean mouseClicked(Point p, int glfwMouseButton, boolean consumed) { + if (enabled && !consumed && isFirstHit(p)) { + setFocused(true); + if (glfwMouseButton == GLFW_MOUSE_BUTTON_RIGHT) { + currentText = ""; + } + return true; + } + + setFocused(false); + return false; + } + + @Override + public boolean onKeyPressed(int glfwKeyCode, int glfwScanCode, int glfwFlags, boolean consumed) { + + if (!focused || !enabled) return false; + + boolean changed = false; + + if (Screen.isPaste(glfwKeyCode)) { + String clipboard = Minecraft.getInstance().keyboardHandler.getClipboard(); + changed = appendText(clipboard); + } else { + switch (glfwKeyCode) { + case GLFW_KEY_BACKSPACE: + changed = backspaceText(); + break; + case GLFW_KEY_ESCAPE: + setFocused(false); + break; + case GLFW_KEY_ENTER: + setFocused(false); + onReturnPressed(); + break; + } + } + + if (changed) { + onTextChanged(); + return true; + } + return false; + } + + @Override + public boolean onCharTyped(char ch, int glfwFlags, boolean consumed) { + if (!focused || !enabled) return false; + + if (appendChar(ch)) { + onTextChanged(); + return true; + } + return false; + } + + private void setFocused(boolean focused) { + this.focused = focused; + } + + private boolean appendText(String text) { + StringBuilder builder = new StringBuilder(currentText); + + char[] chars = text.toCharArray(); + int i = 0; + + while (builder.length() < getCharacterLimit() && i < chars.length) { + char next = chars[i++]; + if (isCharAllowed(next)) { + builder.append(next); + } + } + + String newText = builder.toString(); + + if (!newText.equals(currentText)) { + currentText = newText; + return true; + } + return false; + } + + private boolean appendChar(char c) { + if (currentText.length() < getCharacterLimit() && isCharAllowed(c)) { + currentText += c; + return true; + } + return false; + } + + private boolean backspaceText() { + if (currentText.length() > 0) { + currentText = currentText.substring(0, currentText.length() - 1); + return true; + } + return false; + } + + private boolean isCharAllowed(char c) { + return true; + } + + private int getCharacterLimit() { + if (characterLimit == -1) { + return getFrame().width() / 6; + } + return characterLimit; + } + + protected abstract String getSuggestionString(); + + protected abstract void onTextChanged(); + + protected abstract void onReturnPressed(); +} diff --git a/core/src/main/java/mrtjp/projectred/redui/ViewportRenderNode.java b/core/src/main/java/mrtjp/projectred/redui/ViewportRenderNode.java index 6a7891996..5efdad449 100644 --- a/core/src/main/java/mrtjp/projectred/redui/ViewportRenderNode.java +++ b/core/src/main/java/mrtjp/projectred/redui/ViewportRenderNode.java @@ -58,6 +58,16 @@ protected Vector3 ndcMouseToWorld(Vec2 ndcMouse) { return pvMatrix.ndcToWorldCoordinates(ndcMouse.dx, ndcMouse.dy, getTargetPlaneDistance()); } + protected double distanceToEncloseRect(double width, double height) { + double fovY = getVerticalFOV(); + double fovX = fovY * getFrame().width() / getFrame().height(); + + double xDist = width / (2 * Math.tan(fovX / 2)); + double yDist = height / (2 * Math.tan(fovY / 2)); + + return Math.max(xDist, yDist); + } + @Override public void drawBack(PoseStack stack, Point mouse, float partialFrame) { diff --git a/core/src/main/resources/META-INF/accesstransformer.cfg b/core/src/main/resources/META-INF/accesstransformer.cfg index aac0cabee..127413484 100644 --- a/core/src/main/resources/META-INF/accesstransformer.cfg +++ b/core/src/main/resources/META-INF/accesstransformer.cfg @@ -5,6 +5,8 @@ public net.minecraft.client.renderer.PostChain * # All fields public net.minecraft.client.renderer.LevelRenderer m_109501_(IIIZ)V # setSectionDirty +public net.minecraft.client.gui.GuiComponent m_168740_(Lcom/mojang/blaze3d/vertex/PoseStack;IIIIIII)V # fillGradient + public net.minecraft.world.level.lighting.LevelLightEngine f_75802_ # blockEngine public net.minecraft.world.level.lighting.LevelLightEngine f_75803_ # skyEngine diff --git a/fabrication/build.gradle b/fabrication/build.gradle new file mode 100644 index 000000000..e16737b9e --- /dev/null +++ b/fabrication/build.gradle @@ -0,0 +1,81 @@ +plugins { + id 'net.minecraftforge.gradle' + id 'com.matthewprenger.cursegradle' + id 'com.github.johnrengelman.shadow' +} + +String mod_id = 'projectred_fabrication' + +minecraft { + mappings channel: mcp_mappings, version: mc_version + accessTransformer = file("../core/src/main/resources/META-INF/accesstransformer.cfg") + runs { + data { + property 'mixin.env.remapRefMap', 'true' + property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg" + + ideaModule "${rootProject.name}.${project.name}.main" + + workingDirectory file('run') + args '--mod', mod_id, '--all', '--output', file("src/main/generated"), '--existing', file("src/main/resources") + mods { + '${mod_id}' { source sourceSets.main } + } + } + } +} + +dependencies { + minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" + + implementation fg.deobf("codechicken:CodeChickenLib:${mc_version}-${ccl_version}:universal") + implementation fg.deobf("codechicken:CBMultipart:${mc_version}-${cbm_version}:universal") + + implementation project(":core") + + // Note: Fails if transitives are included. Possible ForgeGradle bug + implementation(project(":integration")) { transitive = false } + implementation(project(":transmission")) { transitive = false } + + // Dependencies to be packed into jar + shadow("io.github.mrtjp:fabrication-engine:${fabrication_version}") { transitive = false } +} + +// Jar settings +jar.enabled = false +shadowJar { + // Jar configurations + archiveClassifier = jar.archiveClassifier + configurations = [project.configurations.shadow] + + // Attach reobf task + finalizedBy 'reobfShadowJar' +} + +build.dependsOn shadowJar + +reobf { + jar { enabled = false } // Disable unshaded jar + shadowJar { } // Enable shadow jar reobf +} + +curseforge { + apiKey = System.getenv('CURSE_TOKEN') ?: 'XXX' + + // Fabrication + project { + id = '230111' + releaseType = System.getenv('CURSE_RELEASE_TYPE') ?: 'alpha' + changelogType = 'markdown' + changelog = rootProject.file('CHANGELOG.md') + relations { + requiredDependency 'project-red-core' + requiredDependency 'project-red-integration' + requiredDependency 'project-red-transmission' + } + + // Java/ForgeGradle integrations don't work after 1.18.2 port + addGameVersion "${mc_version}" + addGameVersion "Java ${java_lang_version}" + } +} diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/ic_workbench.json b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/ic_workbench.json new file mode 100644 index 000000000..7f5b01928 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/ic_workbench.json @@ -0,0 +1,10 @@ +{ + "variants": { + "blueprint=false": { + "model": "projectred_fabrication:block/ic_workbench_empty" + }, + "blueprint=true": { + "model": "projectred_fabrication:block/ic_workbench" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/lithography_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/lithography_table.json new file mode 100644 index 000000000..0efd13726 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/lithography_table.json @@ -0,0 +1,64 @@ +{ + "variants": { + "charged=false,rotation=0,working=false": { + "model": "projectred_fabrication:block/lithography_table" + }, + "charged=true,rotation=0,working=false": { + "model": "projectred_fabrication:block/lithography_table_state1" + }, + "charged=false,rotation=1,working=false": { + "model": "projectred_fabrication:block/lithography_table", + "y": 90 + }, + "charged=true,rotation=1,working=false": { + "model": "projectred_fabrication:block/lithography_table_state1", + "y": 90 + }, + "charged=false,rotation=2,working=false": { + "model": "projectred_fabrication:block/lithography_table", + "y": 180 + }, + "charged=true,rotation=2,working=false": { + "model": "projectred_fabrication:block/lithography_table_state1", + "y": 180 + }, + "charged=false,rotation=3,working=false": { + "model": "projectred_fabrication:block/lithography_table", + "y": 270 + }, + "charged=true,rotation=3,working=false": { + "model": "projectred_fabrication:block/lithography_table_state1", + "y": 270 + }, + "charged=false,rotation=0,working=true": { + "model": "projectred_fabrication:block/lithography_table" + }, + "charged=true,rotation=0,working=true": { + "model": "projectred_fabrication:block/lithography_table_state2" + }, + "charged=false,rotation=1,working=true": { + "model": "projectred_fabrication:block/lithography_table", + "y": 90 + }, + "charged=true,rotation=1,working=true": { + "model": "projectred_fabrication:block/lithography_table_state2", + "y": 90 + }, + "charged=false,rotation=2,working=true": { + "model": "projectred_fabrication:block/lithography_table", + "y": 180 + }, + "charged=true,rotation=2,working=true": { + "model": "projectred_fabrication:block/lithography_table_state2", + "y": 180 + }, + "charged=false,rotation=3,working=true": { + "model": "projectred_fabrication:block/lithography_table", + "y": 270 + }, + "charged=true,rotation=3,working=true": { + "model": "projectred_fabrication:block/lithography_table_state2", + "y": 270 + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/packaging_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/packaging_table.json new file mode 100644 index 000000000..083843757 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/packaging_table.json @@ -0,0 +1,64 @@ +{ + "variants": { + "charged=false,rotation=0,working=false": { + "model": "projectred_fabrication:block/packaging_table" + }, + "charged=true,rotation=0,working=false": { + "model": "projectred_fabrication:block/packaging_table_state1" + }, + "charged=false,rotation=1,working=false": { + "model": "projectred_fabrication:block/packaging_table", + "y": 90 + }, + "charged=true,rotation=1,working=false": { + "model": "projectred_fabrication:block/packaging_table_state1", + "y": 90 + }, + "charged=false,rotation=2,working=false": { + "model": "projectred_fabrication:block/packaging_table", + "y": 180 + }, + "charged=true,rotation=2,working=false": { + "model": "projectred_fabrication:block/packaging_table_state1", + "y": 180 + }, + "charged=false,rotation=3,working=false": { + "model": "projectred_fabrication:block/packaging_table", + "y": 270 + }, + "charged=true,rotation=3,working=false": { + "model": "projectred_fabrication:block/packaging_table_state1", + "y": 270 + }, + "charged=false,rotation=0,working=true": { + "model": "projectred_fabrication:block/packaging_table" + }, + "charged=true,rotation=0,working=true": { + "model": "projectred_fabrication:block/packaging_table_state2" + }, + "charged=false,rotation=1,working=true": { + "model": "projectred_fabrication:block/packaging_table", + "y": 90 + }, + "charged=true,rotation=1,working=true": { + "model": "projectred_fabrication:block/packaging_table_state2", + "y": 90 + }, + "charged=false,rotation=2,working=true": { + "model": "projectred_fabrication:block/packaging_table", + "y": 180 + }, + "charged=true,rotation=2,working=true": { + "model": "projectred_fabrication:block/packaging_table_state2", + "y": 180 + }, + "charged=false,rotation=3,working=true": { + "model": "projectred_fabrication:block/packaging_table", + "y": 270 + }, + "charged=true,rotation=3,working=true": { + "model": "projectred_fabrication:block/packaging_table_state2", + "y": 270 + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/plotting_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/plotting_table.json new file mode 100644 index 000000000..06593b8ce --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/blockstates/plotting_table.json @@ -0,0 +1,64 @@ +{ + "variants": { + "charged=false,rotation=0,working=false": { + "model": "projectred_fabrication:block/plotting_table" + }, + "charged=true,rotation=0,working=false": { + "model": "projectred_fabrication:block/plotting_table_state1" + }, + "charged=false,rotation=1,working=false": { + "model": "projectred_fabrication:block/plotting_table", + "y": 90 + }, + "charged=true,rotation=1,working=false": { + "model": "projectred_fabrication:block/plotting_table_state1", + "y": 90 + }, + "charged=false,rotation=2,working=false": { + "model": "projectred_fabrication:block/plotting_table", + "y": 180 + }, + "charged=true,rotation=2,working=false": { + "model": "projectred_fabrication:block/plotting_table_state1", + "y": 180 + }, + "charged=false,rotation=3,working=false": { + "model": "projectred_fabrication:block/plotting_table", + "y": 270 + }, + "charged=true,rotation=3,working=false": { + "model": "projectred_fabrication:block/plotting_table_state1", + "y": 270 + }, + "charged=false,rotation=0,working=true": { + "model": "projectred_fabrication:block/plotting_table" + }, + "charged=true,rotation=0,working=true": { + "model": "projectred_fabrication:block/plotting_table_state2" + }, + "charged=false,rotation=1,working=true": { + "model": "projectred_fabrication:block/plotting_table", + "y": 90 + }, + "charged=true,rotation=1,working=true": { + "model": "projectred_fabrication:block/plotting_table_state2", + "y": 90 + }, + "charged=false,rotation=2,working=true": { + "model": "projectred_fabrication:block/plotting_table", + "y": 180 + }, + "charged=true,rotation=2,working=true": { + "model": "projectred_fabrication:block/plotting_table_state2", + "y": 180 + }, + "charged=false,rotation=3,working=true": { + "model": "projectred_fabrication:block/plotting_table", + "y": 270 + }, + "charged=true,rotation=3,working=true": { + "model": "projectred_fabrication:block/plotting_table_state2", + "y": 270 + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/lang/en_us.json b/fabrication/src/main/generated/assets/projectred_fabrication/lang/en_us.json new file mode 100644 index 000000000..39cba5c16 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/lang/en_us.json @@ -0,0 +1,103 @@ +{ + "block.projectred_fabrication.ic_workbench": "IC Workbench", + "block.projectred_fabrication.lithography_table": "Lithography Table", + "block.projectred_fabrication.packaging_table": "Packaging Table", + "block.projectred_fabrication.plotting_table": "Plotting Table", + "item.projectred_fabrication.blank_photomask": "Blank Photomask", + "item.projectred_fabrication.etched_silicon_wafer": "Etched Silicon Wafer", + "item.projectred_fabrication.fabricated_gate": "Fabricated Gate", + "item.projectred_fabrication.ic_blueprint": "IC Blueprint", + "item.projectred_fabrication.invalid_die": "Invalid Die", + "item.projectred_fabrication.photomask_set": "Photomask Set", + "item.projectred_fabrication.rough_silicon_wafer": "Rough Silicon Wafer", + "item.projectred_fabrication.valid_die": "Valid Die", + "itemGroup.projectred_fabrication": "Project Red: Fabrication", + "projectred_fabrication.dimensions.dies": "%d dies x %d dies", + "projectred_fabrication.dimensions.dies_total": "%d dies x %d dies (%d dies^2)", + "projectred_fabrication.dimensions.nm": "%d nm x %d nm", + "projectred_fabrication.dimensions.nm_total": "%d nm x %d nm (%d nm^2)", + "projectred_fabrication.dimensions.tiles": "%d tiles x %d tiles", + "projectred_fabrication.dimensions.tiles_total": "%d tiles x %d tiles (%d tiles^2)", + "projectred_fabrication.interact.io_mode.input": "Input", + "projectred_fabrication.interact.io_mode.output": "Output", + "projectred_fabrication.interact.side_disabled": "Side disabled", + "projectred_fabrication.interact.side_enabled": "Side enabled", + "projectred_fabrication.interact.toggle_colour": "Toggle colour", + "projectred_fabrication.interact.toggle_delay": "Toggle delay", + "projectred_fabrication.interact.toggle_io_mode": "Toggle IO mode", + "projectred_fabrication.interact.toggle_state": "Toggle state", + "projectred_fabrication.interface.bundled": "Bundled", + "projectred_fabrication.interface.nc": "Not connected", + "projectred_fabrication.interface.redstone": "Redstone", + "projectred_fabrication.problems.dead_gate.desc": "No signals drive this gate", + "projectred_fabrication.problems.dead_gate.title": "Dead gate", + "projectred_fabrication.problems.dead_wire.desc": "No signals pass through this wire", + "projectred_fabrication.problems.dead_wire.title": "Dead wire", + "projectred_fabrication.problems.io_dir_mismatch.desc": "Side has IO gates with conflicting directions", + "projectred_fabrication.problems.io_dir_mismatch.title": "IO direction mismatch", + "projectred_fabrication.problems.multiple_drivers.desc": "Multiple registers connected to an input:", + "projectred_fabrication.problems.multiple_drivers.title": "Multiple drivers", + "projectred_fabrication.problems.no_errors": "No errors", + "projectred_fabrication.problems.no_inputs.desc": "Design has no inputs", + "projectred_fabrication.problems.no_inputs.title": "No inputs", + "projectred_fabrication.problems.no_outputs.desc": "Design has no outputs", + "projectred_fabrication.problems.no_outputs.title": "No outputs", + "projectred_fabrication.problems.no_warnings": "No warnings", + "projectred_fabrication.tab.compile": "Compile", + "projectred_fabrication.tab.edit": "Edit", + "projectred_fabrication.tab.info": "Info", + "projectred_fabrication.tab.problems": "Problems", + "projectred_fabrication.tab.stack": "Stack", + "projectred_fabrication.tab.tree": "Tree", + "projectred_fabrication.tilegroup.basic": "Basic", + "projectred_fabrication.tilegroup.bundled": "Bundled", + "projectred_fabrication.tilegroup.io": "IO", + "projectred_fabrication.tilegroup.memory": "Memory", + "projectred_fabrication.tilegroup.redwire": "Redwire", + "projectred_fabrication.tilegroup.timing": "Timing", + "projectred_fabrication.tiles.io_gate": "IO Gate", + "projectred_fabrication.tool.eraser": "Erase", + "projectred_fabrication.tool.gates": "Gates", + "projectred_fabrication.tool.interact": "Interact", + "projectred_fabrication.tool.wires": "Wires", + "projectred_fabrication.tooltip.bottom": "Bottom", + "projectred_fabrication.tooltip.bundled_input": "Bundled input", + "projectred_fabrication.tooltip.bundled_output": "Bundled output", + "projectred_fabrication.tooltip.cannot_fabricate": "Cannot fabricate", + "projectred_fabrication.tooltip.corrupted_discard": "Corrupted NBT data, please discard", + "projectred_fabrication.tooltip.defect_chance": "Defect chance", + "projectred_fabrication.tooltip.input_mask": "Input mask", + "projectred_fabrication.tooltip.io_none": "None", + "projectred_fabrication.tooltip.io_types": "IO types", + "projectred_fabrication.tooltip.left": "Left", + "projectred_fabrication.tooltip.name": "Name", + "projectred_fabrication.tooltip.output_mask": "Output mask", + "projectred_fabrication.tooltip.right": "Right", + "projectred_fabrication.tooltip.size": "Size", + "projectred_fabrication.tooltip.tile_count": "Tile count", + "projectred_fabrication.tooltip.top": "Top", + "projectred_fabrication.ui.blueprint_dim": "Dimensions", + "projectred_fabrication.ui.blueprint_info": "Blueprint Info", + "projectred_fabrication.ui.blueprint_layers": "Layers", + "projectred_fabrication.ui.blueprint_name": "Name", + "projectred_fabrication.ui.blueprint_owner": "Owner", + "projectred_fabrication.ui.compile": "Compile", + "projectred_fabrication.ui.compile_done": "Done (%d/%d)", + "projectred_fabrication.ui.compile_failed": "Compile failed", + "projectred_fabrication.ui.compile_progress": "Compiling (%d/%d)", + "projectred_fabrication.ui.compile_ready": "Ready to compile", + "projectred_fabrication.ui.die_size": "Die size", + "projectred_fabrication.ui.dies_per_wafer": "Dies per wafer", + "projectred_fabrication.ui.lithography_pipeline": "Lithography pipeline", + "projectred_fabrication.ui.place_blueprint": "Place blueprint", + "projectred_fabrication.ui.process_node": "Process node", + "projectred_fabrication.ui.sim_running": "Simulation running", + "projectred_fabrication.ui.single_layer_yield": "Single layer yield", + "projectred_fabrication.ui.wafer_size": "Wafer size", + "projectred_fabrication.ui.wafer_type": "Wafer type", + "projectred_fabrication.ui.yield": "Yield", + "projectred_fabrication.ui.yield_calculator": "Yield Calculator", + "projectred_fabrication.unit.errors": "%d errors", + "projectred_fabrication.unit.ticks": "%d ticks", + "projectred_fabrication.unit.warnings": "%d warnings" +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/ic_workbench.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/ic_workbench.json new file mode 100644 index 000000000..ade716fc6 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/ic_workbench.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "projectred_fabrication:block/ic_workbench_bottom", + "up": "projectred_fabrication:block/ic_workbench_top", + "north": "projectred_fabrication:block/ic_workbench_front", + "south": "projectred_fabrication:block/ic_workbench_front", + "east": "projectred_fabrication:block/ic_workbench_side", + "west": "projectred_fabrication:block/ic_workbench_side", + "particle": "projectred_fabrication:block/ic_workbench_front" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/ic_workbench_empty.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/ic_workbench_empty.json new file mode 100644 index 000000000..f4e51e324 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/ic_workbench_empty.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "down": "projectred_fabrication:block/ic_workbench_bottom", + "up": "projectred_fabrication:block/ic_workbench_top_empty", + "north": "projectred_fabrication:block/ic_workbench_front_empty", + "south": "projectred_fabrication:block/ic_workbench_front_empty", + "east": "projectred_fabrication:block/ic_workbench_side_empty", + "west": "projectred_fabrication:block/ic_workbench_side_empty", + "particle": "projectred_fabrication:block/ic_workbench_front_empty" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table.json new file mode 100644 index 000000000..fb25e6820 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/lithography_table_bottom", + "up": "projectred_fabrication:block/lithography_table_top", + "north": "projectred_fabrication:block/lithography_table_front_0", + "south": "projectred_fabrication:block/lithography_table_side", + "west": "projectred_fabrication:block/lithography_table_side", + "east": "projectred_fabrication:block/lithography_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table_state1.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table_state1.json new file mode 100644 index 000000000..bb9a8f32d --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table_state1.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/lithography_table_bottom", + "up": "projectred_fabrication:block/lithography_table_top", + "north": "projectred_fabrication:block/lithography_table_front_1", + "south": "projectred_fabrication:block/lithography_table_side", + "west": "projectred_fabrication:block/lithography_table_side", + "east": "projectred_fabrication:block/lithography_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table_state2.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table_state2.json new file mode 100644 index 000000000..10a1cf2d7 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/lithography_table_state2.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/lithography_table_bottom", + "up": "projectred_fabrication:block/lithography_table_top", + "north": "projectred_fabrication:block/lithography_table_front_2", + "south": "projectred_fabrication:block/lithography_table_side", + "west": "projectred_fabrication:block/lithography_table_side", + "east": "projectred_fabrication:block/lithography_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table.json new file mode 100644 index 000000000..fcacfaa76 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/packaging_table_bottom", + "up": "projectred_fabrication:block/packaging_table_top", + "north": "projectred_fabrication:block/packaging_table_front_0", + "south": "projectred_fabrication:block/packaging_table_side", + "west": "projectred_fabrication:block/packaging_table_side", + "east": "projectred_fabrication:block/packaging_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table_state1.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table_state1.json new file mode 100644 index 000000000..d985a4bb7 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table_state1.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/packaging_table_bottom", + "up": "projectred_fabrication:block/packaging_table_top", + "north": "projectred_fabrication:block/packaging_table_front_1", + "south": "projectred_fabrication:block/packaging_table_side", + "west": "projectred_fabrication:block/packaging_table_side", + "east": "projectred_fabrication:block/packaging_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table_state2.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table_state2.json new file mode 100644 index 000000000..66772ed43 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/packaging_table_state2.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/packaging_table_bottom", + "up": "projectred_fabrication:block/packaging_table_top", + "north": "projectred_fabrication:block/packaging_table_front_2", + "south": "projectred_fabrication:block/packaging_table_side", + "west": "projectred_fabrication:block/packaging_table_side", + "east": "projectred_fabrication:block/packaging_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table.json new file mode 100644 index 000000000..b2bb9b870 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/plotting_table_bottom", + "up": "projectred_fabrication:block/plotting_table_top", + "north": "projectred_fabrication:block/plotting_table_front_0", + "south": "projectred_fabrication:block/plotting_table_side", + "west": "projectred_fabrication:block/plotting_table_side", + "east": "projectred_fabrication:block/plotting_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table_state1.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table_state1.json new file mode 100644 index 000000000..fe0ae0b29 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table_state1.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/plotting_table_bottom", + "up": "projectred_fabrication:block/plotting_table_top", + "north": "projectred_fabrication:block/plotting_table_front_1", + "south": "projectred_fabrication:block/plotting_table_side", + "west": "projectred_fabrication:block/plotting_table_side", + "east": "projectred_fabrication:block/plotting_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table_state2.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table_state2.json new file mode 100644 index 000000000..a807658f0 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/block/plotting_table_state2.json @@ -0,0 +1,11 @@ +{ + "parent": "projectred_fabrication:block/domed_machine", + "textures": { + "down": "projectred_fabrication:block/plotting_table_bottom", + "up": "projectred_fabrication:block/plotting_table_top", + "north": "projectred_fabrication:block/plotting_table_front_2", + "south": "projectred_fabrication:block/plotting_table_side", + "west": "projectred_fabrication:block/plotting_table_side", + "east": "projectred_fabrication:block/plotting_table_side" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/blank_photomask.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/blank_photomask.json new file mode 100644 index 000000000..a318d7cdf --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/blank_photomask.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/blank_photomask" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/etched_silicon_wafer.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/etched_silicon_wafer.json new file mode 100644 index 000000000..69cbe5a84 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/etched_silicon_wafer.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/etched_silicon_wafer" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/fabricated_gate.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/fabricated_gate.json new file mode 100644 index 000000000..1bcbb312f --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/fabricated_gate.json @@ -0,0 +1,3 @@ +{ + "parent": "minecraft:item/generated" +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/ic_blueprint.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/ic_blueprint.json new file mode 100644 index 000000000..225226ad0 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/ic_blueprint.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/ic_blueprint" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/ic_workbench.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/ic_workbench.json new file mode 100644 index 000000000..01abc7198 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/ic_workbench.json @@ -0,0 +1,3 @@ +{ + "parent": "projectred_fabrication:block/ic_workbench" +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/invalid_die.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/invalid_die.json new file mode 100644 index 000000000..46649c024 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/invalid_die.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/invalid_die" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/lithography_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/lithography_table.json new file mode 100644 index 000000000..c95bda678 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/lithography_table.json @@ -0,0 +1,3 @@ +{ + "parent": "projectred_fabrication:block/lithography_table" +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/packaging_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/packaging_table.json new file mode 100644 index 000000000..e4241aca7 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/packaging_table.json @@ -0,0 +1,3 @@ +{ + "parent": "projectred_fabrication:block/packaging_table" +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/photomask_set.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/photomask_set.json new file mode 100644 index 000000000..96317cdcc --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/photomask_set.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/photomask_set" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/plotting_table.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/plotting_table.json new file mode 100644 index 000000000..46a0db2e5 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/plotting_table.json @@ -0,0 +1,3 @@ +{ + "parent": "projectred_fabrication:block/plotting_table" +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/rough_silicon_wafer.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/rough_silicon_wafer.json new file mode 100644 index 000000000..d005df176 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/rough_silicon_wafer.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/rough_silicon_wafer" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/assets/projectred_fabrication/models/item/valid_die.json b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/valid_die.json new file mode 100644 index 000000000..3f7d01b80 --- /dev/null +++ b/fabrication/src/main/generated/assets/projectred_fabrication/models/item/valid_die.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "projectred_fabrication:item/valid_die" + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/blank_photomask.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/blank_photomask.json new file mode 100644 index 000000000..c19cec02a --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/blank_photomask.json @@ -0,0 +1,19 @@ +{ + "type": "minecraft:crafting_shaped", + "result": { + "item": "projectred_fabrication:blank_photomask" + }, + "pattern": [ + "ggg", + "gqg", + "ggg" + ], + "key": { + "g": { + "tag": "forge:glass_panes" + }, + "q": { + "tag": "forge:gems/quartz" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/ic_blueprint.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/ic_blueprint.json new file mode 100644 index 000000000..47b026e6f --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/ic_blueprint.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "result": { + "item": "projectred_fabrication:ic_blueprint" + }, + "pattern": [ + "pbp", + "brb", + "pbp" + ], + "key": { + "r": { + "tag": "forge:dusts/redstone" + }, + "p": { + "item": "minecraft:paper" + }, + "b": { + "tag": "forge:dyes/blue" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/ic_workbench.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/ic_workbench.json new file mode 100644 index 000000000..ed95bc33e --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/ic_workbench.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "result": { + "item": "projectred_fabrication:ic_workbench" + }, + "pattern": [ + "sss", + "wbw", + "www" + ], + "key": { + "w": { + "tag": "minecraft:planks" + }, + "s": { + "item": "minecraft:stone" + }, + "b": { + "item": "projectred_fabrication:ic_blueprint" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/lithography_table.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/lithography_table.json new file mode 100644 index 000000000..1a1bb51b2 --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/lithography_table.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "result": { + "item": "projectred_fabrication:lithography_table" + }, + "pattern": [ + "gtg", + "oio", + "wbw" + ], + "key": { + "g": { + "item": "minecraft:glass" + }, + "o": { + "tag": "forge:obsidian" + }, + "t": { + "tag": "forge:gems/emerald" + }, + "w": { + "tag": "minecraft:planks" + }, + "b": { + "tag": "forge:ingots/electrotine_alloy" + }, + "i": { + "tag": "forge:ingots/iron" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/packaging_table.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/packaging_table.json new file mode 100644 index 000000000..3293e192b --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/packaging_table.json @@ -0,0 +1,28 @@ +{ + "type": "minecraft:crafting_shaped", + "result": { + "item": "projectred_fabrication:packaging_table" + }, + "pattern": [ + "gtg", + "ppp", + "wbw" + ], + "key": { + "g": { + "item": "minecraft:glass" + }, + "t": { + "tag": "forge:dusts/redstone" + }, + "w": { + "tag": "minecraft:planks" + }, + "p": { + "item": "projectred_core:plate" + }, + "b": { + "tag": "forge:ingots/electrotine_alloy" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/plotting_table.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/plotting_table.json new file mode 100644 index 000000000..ed8c915cd --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/plotting_table.json @@ -0,0 +1,31 @@ +{ + "type": "minecraft:crafting_shaped", + "result": { + "item": "projectred_fabrication:plotting_table" + }, + "pattern": [ + "gtg", + "sps", + "wbw" + ], + "key": { + "g": { + "item": "minecraft:glass" + }, + "t": { + "tag": "forge:gems/sapphire" + }, + "w": { + "tag": "minecraft:planks" + }, + "p": { + "item": "projectred_fabrication:blank_photomask" + }, + "s": { + "item": "minecraft:stone" + }, + "b": { + "tag": "forge:ingots/electrotine_alloy" + } + } +} \ No newline at end of file diff --git a/fabrication/src/main/generated/data/projectred_fabrication/recipes/rough_silicon_wafer.json b/fabrication/src/main/generated/data/projectred_fabrication/recipes/rough_silicon_wafer.json new file mode 100644 index 000000000..71b434438 --- /dev/null +++ b/fabrication/src/main/generated/data/projectred_fabrication/recipes/rough_silicon_wafer.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:smelting", + "result": { + "item": "projectred_fabrication:rough_silicon_wafer" + }, + "ingredient": { + "item": "projectred_core:silicon" + }, + "experience": 0.0, + "cookingtime": 200 +} \ No newline at end of file diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/ProjectRedFabrication.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/ProjectRedFabrication.java new file mode 100644 index 000000000..be535a819 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/ProjectRedFabrication.java @@ -0,0 +1,85 @@ +package mrtjp.projectred.fabrication; + +import codechicken.lib.gui.SimpleCreativeTab; +import codechicken.multipart.api.MultipartType; +import mrtjp.projectred.fabrication.data.FabricationBlockStateModelProvider; +import mrtjp.projectred.fabrication.data.FabricationItemModelProvider; +import mrtjp.projectred.fabrication.data.FabricationLanguageProvider; +import mrtjp.projectred.fabrication.data.FabricationRecipeProvider; +import mrtjp.projectred.fabrication.init.*; +import net.minecraft.data.DataGenerator; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.forge.event.lifecycle.GatherDataEvent; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.MOD_ID; + +@Mod (MOD_ID) +public class ProjectRedFabrication { + + public static final String MOD_ID = "projectred_fabrication"; + + public static final Logger LOGGER = LogManager.getLogger(MOD_ID); + + public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MOD_ID); + public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID); + public static final DeferredRegister> TILE_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITIES, MOD_ID); + public static final DeferredRegister> CONTAINERS = DeferredRegister.create(ForgeRegistries.CONTAINERS, MOD_ID); + public static final DeferredRegister> PARTS = DeferredRegister.create(MultipartType.MULTIPART_TYPES, MOD_ID); + + public static final SimpleCreativeTab FABRICATION_GROUP = new SimpleCreativeTab(MOD_ID, () -> new ItemStack(FabricationReferences.IC_WORKBENCH_BLOCK)); + + static { + FabricationBlocks.register(); + FabricationContainers.register(); + FabricationItems.register(); + FabricationParts.register(); + } + + public ProjectRedFabrication() { + final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + + modEventBus.addListener(this::commonSetup); + modEventBus.addListener(this::onGatherDataEvent); + + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> FabricationClientInit::init); + + BLOCKS.register(modEventBus); + ITEMS.register(modEventBus); + TILE_ENTITIES.register(modEventBus); + CONTAINERS.register(modEventBus); + PARTS.register(modEventBus); + } + + private void commonSetup(final FMLCommonSetupEvent event) { + + } + + private void onGatherDataEvent(final GatherDataEvent event) { + DataGenerator generator = event.getGenerator(); + ExistingFileHelper fileHelper = event.getExistingFileHelper(); + + if (event.includeClient()) { + generator.addProvider(new FabricationBlockStateModelProvider(generator, fileHelper)); + generator.addProvider(new FabricationItemModelProvider(generator, fileHelper)); + generator.addProvider(new FabricationLanguageProvider(generator)); + } + if (event.includeServer()) { + generator.addProvider(new FabricationRecipeProvider(generator)); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/block/FabricationMachineBlock.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/FabricationMachineBlock.java new file mode 100644 index 000000000..0e05517ea --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/FabricationMachineBlock.java @@ -0,0 +1,44 @@ +package mrtjp.projectred.fabrication.block; + +import codechicken.lib.vec.Rotation; +import mrtjp.projectred.core.block.ProjectRedBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.phys.shapes.VoxelShape; + +import javax.annotation.Nullable; + +public abstract class FabricationMachineBlock extends ProjectRedBlock { + + protected static final VoxelShape BOTTOM_AABB = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 10.0D, 16.0D); + + public FabricationMachineBlock(Properties properties) { + super(properties); + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + int r = Rotation.rotationTo(0, context.getHorizontalDirection().ordinal()); + return this.defaultBlockState() + .setValue(ROTATION, r) + .setValue(CHARGED, false) + .setValue(WORKING, false); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(ROTATION); + builder.add(CHARGED); + builder.add(WORKING); + } + + @Override + public VoxelShape getOcclusionShape(BlockState state, BlockGetter world, BlockPos pos) { + return BOTTOM_AABB; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/block/ICWorkbenchBlock.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/ICWorkbenchBlock.java new file mode 100644 index 000000000..629cfff28 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/ICWorkbenchBlock.java @@ -0,0 +1,46 @@ +package mrtjp.projectred.fabrication.block; + +import mrtjp.projectred.core.block.ProjectRedBlock; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.tile.ICWorkbenchTile; +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import org.jetbrains.annotations.Nullable; + +public class ICWorkbenchBlock extends ProjectRedBlock { + + public static final BooleanProperty BLUEPRINT_PROPERTY = BooleanProperty.create("blueprint"); + + public ICWorkbenchBlock() { + super(STONE_PROPERTIES); + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState() + .setValue(BLUEPRINT_PROPERTY, false); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new ICWorkbenchTile(pos, state); + } + + @Override + protected BlockEntityType getBlockEntityType() { + return FabricationReferences.IC_WORKBENCH_TILE; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(BLUEPRINT_PROPERTY); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/block/LithographyTableBlock.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/LithographyTableBlock.java new file mode 100644 index 000000000..517a75f97 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/LithographyTableBlock.java @@ -0,0 +1,27 @@ +package mrtjp.projectred.fabrication.block; + +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.tile.LithographyTableTile; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public class LithographyTableBlock extends FabricationMachineBlock { + + public LithographyTableBlock() { + super(STONE_PROPERTIES); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new LithographyTableTile(pos, state); + } + + @Override + protected BlockEntityType getBlockEntityType() { + return FabricationReferences.LITHOGRAPHY_TABLE_TILE; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/block/PackagingTableBlock.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/PackagingTableBlock.java new file mode 100644 index 000000000..e8ec75cc3 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/PackagingTableBlock.java @@ -0,0 +1,27 @@ +package mrtjp.projectred.fabrication.block; + +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.tile.PackagingTableTile; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public class PackagingTableBlock extends FabricationMachineBlock { + + public PackagingTableBlock() { + super(STONE_PROPERTIES); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new PackagingTableTile(pos, state); + } + + @Override + protected BlockEntityType getBlockEntityType() { + return FabricationReferences.PACKAGING_TABLE_TILE; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/block/PlottingTableBlock.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/PlottingTableBlock.java new file mode 100644 index 000000000..a4257d119 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/block/PlottingTableBlock.java @@ -0,0 +1,28 @@ +package mrtjp.projectred.fabrication.block; + +import mrtjp.projectred.core.block.ProjectRedBlock; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.tile.PlottingTableTile; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; + +public class PlottingTableBlock extends FabricationMachineBlock { + + public PlottingTableBlock() { + super(ProjectRedBlock.STONE_PROPERTIES); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new PlottingTableTile(pos, state); + } + + @Override + protected BlockEntityType getBlockEntityType() { + return FabricationReferences.PLOTTING_TABLE_TILE; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationBlockStateModelProvider.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationBlockStateModelProvider.java new file mode 100644 index 000000000..c23b9d3e6 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationBlockStateModelProvider.java @@ -0,0 +1,97 @@ +package mrtjp.projectred.fabrication.data; + +import mrtjp.projectred.core.block.ProjectRedBlock; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.block.FabricationMachineBlock; +import mrtjp.projectred.fabrication.block.ICWorkbenchBlock; +import net.minecraft.data.DataGenerator; +import net.minecraft.world.level.block.Block; +import net.minecraftforge.client.model.generators.BlockModelBuilder; +import net.minecraftforge.client.model.generators.BlockStateProvider; +import net.minecraftforge.client.model.generators.ConfiguredModel; +import net.minecraftforge.client.model.generators.ModelFile; +import net.minecraftforge.common.data.ExistingFileHelper; + +import static mrtjp.projectred.fabrication.init.FabricationReferences.*; + +public class FabricationBlockStateModelProvider extends BlockStateProvider { + + public FabricationBlockStateModelProvider(DataGenerator gen, ExistingFileHelper exFileHelper) { + super(gen, ProjectRedFabrication.MOD_ID, exFileHelper); + } + + @Override + public String getName() { + return "ProjectRed-Fabrication Block Models"; + } + + @Override + protected void registerStatesAndModels() { + + addICWorkbenchVariants(IC_WORKBENCH_BLOCK); + + addFabricationMachineVariants(PLOTTING_TABLE_BLOCK); + addFabricationMachineVariants(LITHOGRAPHY_TABLE_BLOCK); + addFabricationMachineVariants(PACKAGING_TABLE_BLOCK); + } + + private void addICWorkbenchVariants(Block block) { + getVariantBuilder(block) + .partialState().with(ICWorkbenchBlock.BLUEPRINT_PROPERTY, false) + .modelForState().modelFile(createICWorkbenchModel(block, false)).addModel(); + + getVariantBuilder(block) + .partialState().with(ICWorkbenchBlock.BLUEPRINT_PROPERTY, true) + .modelForState().modelFile(createICWorkbenchModel(block, true)).addModel(); + } + + private void addFabricationMachineVariants(Block block) { + addRotatableDomedMachineVariants(block, + createDomedMachineModelFileForBlock(block, 2), + createDomedMachineModelFileForBlock(block, 1), + createDomedMachineModelFileForBlock(block, 0)); + } + + private void addRotatableDomedMachineVariants(Block block, ModelFile workingModel, ModelFile chargedModel, ModelFile idleModel) { + getVariantBuilder(block) + .forAllStates(state -> { + int r = state.getValue(ProjectRedBlock.ROTATION); + boolean isWorking = state.getValue(FabricationMachineBlock.WORKING); + boolean isCharged = state.getValue(FabricationMachineBlock.CHARGED); + + ModelFile modelFile = isWorking && isCharged ? workingModel : isCharged ? chargedModel : idleModel; + + return ConfiguredModel.builder() + .modelFile(modelFile) + .rotationY(r * 90) + .build(); + }); + } + + private BlockModelBuilder createDomedMachineModelFileForBlock(Block block, int chargeState) { + String textureName = block.getRegistryName().getPath(); + String modelName = textureName + (chargeState > 0 ? "_state" + chargeState : ""); + return models() + .withExistingParent(modelName, modLoc("block/domed_machine")) + .texture("down", modLoc("block/" + textureName + "_bottom")) + .texture("up", modLoc("block/" + textureName + "_top")) + .texture("north", modLoc("block/" + textureName + "_front_" + chargeState)) + .texture("south", modLoc("block/" + textureName + "_side")) + .texture("west", modLoc("block/" + textureName + "_side")) + .texture("east", modLoc("block/" + textureName + "_side")); + } + + private BlockModelBuilder createICWorkbenchModel(Block block, boolean hasBlueprint) { + String textureName = block.getRegistryName().getPath(); + String suffix = hasBlueprint ? "" : "_empty"; + String modelName = textureName + suffix; + return models().cube(modelName, + modLoc("block/" + textureName + "_bottom"), + modLoc("block/" + textureName + "_top" + suffix), + modLoc("block/" + textureName + "_front" + suffix), + modLoc("block/" + textureName + "_front" + suffix), + modLoc("block/" + textureName + "_side" + suffix), + modLoc("block/" + textureName + "_side" + suffix)) + .texture("particle", modLoc("block/" + textureName + "_front" + suffix)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationItemModelProvider.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationItemModelProvider.java new file mode 100644 index 000000000..3ac91c622 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationItemModelProvider.java @@ -0,0 +1,40 @@ +package mrtjp.projectred.fabrication.data; + +import codechicken.lib.datagen.ItemModelProvider; +import net.minecraft.data.DataGenerator; +import net.minecraftforge.common.data.ExistingFileHelper; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.MOD_ID; +import static mrtjp.projectred.fabrication.init.FabricationReferences.*; + +public class FabricationItemModelProvider extends ItemModelProvider { + + public FabricationItemModelProvider(DataGenerator generator, ExistingFileHelper existingFileHelper) { + super(generator, MOD_ID, existingFileHelper); + } + + @Override + public String getName() { + return "ProjectRed-Fabrication Item Models"; + } + + @Override + protected void registerModels() { + + simpleItemBlock(IC_WORKBENCH_BLOCK); + + simpleItemBlock(PLOTTING_TABLE_BLOCK); + simpleItemBlock(LITHOGRAPHY_TABLE_BLOCK); + simpleItemBlock(PACKAGING_TABLE_BLOCK); + + generated(IC_BLUEPRINT_ITEM); + generated(BLANK_PHOTOMASK_ITEM); + generated(PHOTOMASK_SET_ITEM); + generated(ROUGH_SILICON_WAFER_ITEM); + generated(ETCHED_SILICON_WAFER_ITEM); + generated(VALID_DIE_ITEM); + generated(INVALID_DIE_ITEM); + + generated(FABRICATED_GATE_ITEM).noTexture(); // Dummy model to suppress warnings (actually rendered runtime via IItemRenderer) + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationLanguageProvider.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationLanguageProvider.java new file mode 100644 index 000000000..3cd2b138d --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationLanguageProvider.java @@ -0,0 +1,141 @@ +package mrtjp.projectred.fabrication.data; + +import net.minecraft.data.DataGenerator; +import net.minecraftforge.common.data.LanguageProvider; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.MOD_ID; +import static mrtjp.projectred.fabrication.init.FabricationReferences.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class FabricationLanguageProvider extends LanguageProvider { + + public FabricationLanguageProvider(DataGenerator gen) { + super(gen, MOD_ID, "en_us"); + } + + @Override + public String getName() { + return "ProjectRed-Fabrication Languages: en_us"; + } + + @Override + protected void addTranslations() { + + // Creative tab + add("itemGroup." + MOD_ID, "Project Red: Fabrication"); + + add(IC_WORKBENCH_BLOCK, "IC Workbench"); + add(PLOTTING_TABLE_BLOCK, "Plotting Table"); + add(LITHOGRAPHY_TABLE_BLOCK, "Lithography Table"); + add(PACKAGING_TABLE_BLOCK, "Packaging Table"); + + add(IC_BLUEPRINT_ITEM, "IC Blueprint"); + add(BLANK_PHOTOMASK_ITEM, "Blank Photomask"); + add(PHOTOMASK_SET_ITEM, "Photomask Set"); + add(ROUGH_SILICON_WAFER_ITEM, "Rough Silicon Wafer"); + add(ETCHED_SILICON_WAFER_ITEM, "Etched Silicon Wafer"); + add(VALID_DIE_ITEM, "Valid Die"); + add(INVALID_DIE_ITEM, "Invalid Die"); + + add(FABRICATED_GATE_ITEM, "Fabricated Gate"); + + add(UL_TAB_INFO, "Info"); + add(UL_TAB_EDIT, "Edit"); + add(UL_TAB_COMPILE, "Compile"); + add(UL_TAB_STACK, "Stack"); + add(UL_TAB_TREE, "Tree"); + add(UL_TAB_PROBLEMS, "Problems"); + + add(UL_ERASER_TOOL, "Erase"); + add(UL_INTERACT_TOOL, "Interact"); + add(UL_WIRE_TOOL, "Wires"); + add(UL_GATE_TOOL, "Gates"); + + add(UL_TOGGLE_STATE, "Toggle state"); + add(UL_TOGGLE_IO_MODE, "Toggle IO mode"); + add(UL_IO_MODE_INPUT, "Input"); + add(UL_IO_MODE_OUTPUT, "Output"); + add(UL_TOGGLE_COLOUR, "Toggle colour"); + add(UL_TOGGLE_DELAY, "Toggle delay"); + add(UL_SIDE_ENABLED, "Side enabled"); + add(UL_SIDE_DISABLED, "Side disabled"); + + add(UL_MULTIPLE_DRIVERS_TITLE, "Multiple drivers"); + add(UL_MULTIPLE_DRIVERS_DESC, "Multiple registers connected to an input:"); + add(UL_DEAD_WIRE_TITLE, "Dead wire"); + add(UL_DEAD_WIRE_DESC, "No signals pass through this wire"); + add(UL_DEAD_GATE_TITLE, "Dead gate"); + add(UL_DEAD_GATE_DESC, "No signals drive this gate"); + add(UL_IO_DIR_MISMATCH_TITLE, "IO direction mismatch"); + add(UL_IO_DIR_MISMATCH_DESC, "Side has IO gates with conflicting directions"); + add(UL_NO_INPUTS_TITLE, "No inputs"); + add(UL_NO_INPUTS_DESC, "Design has no inputs"); + add(UL_NO_OUTPUTS_TITLE, "No outputs"); + add(UL_NO_OUTPUTS_DESC, "Design has no outputs"); + add(UL_NO_ERRORS, "No errors"); + add(UL_NO_WARNINGS, "No warnings"); + + add(UL_PLACE_BLUEPRINT, "Place blueprint"); + add(UL_BLUEPRINT_INFO, "Blueprint Info"); + add(UL_BLUEPRINT_NAME, "Name"); + add(UL_BLUEPRINT_OWNER, "Owner"); + add(UL_BLUEPRINT_DIM, "Dimensions"); + add(UL_BLUEPRINT_LAYERS, "Layers"); + add(UL_COMPILE, "Compile"); + add(UL_COMPILE_PROGRESS, "Compiling (%d/%d)"); + add(UL_COMPILE_DONE, "Done (%d/%d)"); + add(UL_COMPILE_READY, "Ready to compile"); + add(UL_COMPILE_FAILED, "Compile failed"); + add(UL_SIM_RUNNING, "Simulation running"); + + add(UL_YIELD_CALCULATOR, "Yield Calculator"); + add(UL_LITHOGRAPHY_PIPELINE, "Lithography pipeline"); + add(UL_PROCESS_NODE, "Process node"); + add(UL_WAFER_TYPE, "Wafer type"); + add(UL_DIE_SIZE, "Die size"); + add(UL_WAFER_SIZE, "Wafer size"); + add(UL_DIES_PER_WAFER, "Dies per wafer"); + add(UL_SINGLE_LAYER_YIELD, "Single layer yield"); + add(UL_YIELD, "Yield"); + + add(UL_SIZE, "Size"); + add(UL_DEFECT_CHANCE, "Defect chance"); + add(UL_CORRUPTED_DISCARD, "Corrupted NBT data, please discard"); + add(UL_NAME, "Name"); + add(UL_TILE_COUNT, "Tile count"); + add(UL_IO_TYPES, "IO types"); + add(UL_INPUT_MASK, "Input mask"); + add(UL_OUTPUT_MASK, "Output mask"); + add(UL_TOP, "Top"); + add(UL_RIGHT, "Right"); + add(UL_BOTTOM, "Bottom"); + add(UL_LEFT, "Left"); + add(UL_BUNDLED_INPUT, "Bundled input"); + add(UL_BUNDLED_OUTPUT, "Bundled output"); + add(UL_IO_NONE, "None"); + add(UL_CANNOT_FABRICATE, "Cannot fabricate"); + + add(UL_IO_GATE_TILE, "IO Gate"); + + add(UL_TILEGROUP_REDWIRE, "Redwire"); + add(UL_TILEGROUP_BUNDLED, "Bundled"); + add(UL_TILEGROUP_IO, "IO"); + add(UL_TILEGROUP_BASIC, "Basic"); + add(UL_TILEGROUP_TIMING, "Timing"); + add(UL_TILEGROUP_MEMORY, "Memory"); + + add(UL_INTERFACE_NC, "Not connected"); + add(UL_INTERFACE_REDSTONE, "Redstone"); + add(UL_INTERFACE_BUNDLED, "Bundled"); + + add(UL_UNIT_WARNINGS, "%d warnings"); + add(UL_UNIT_ERRORS, "%d errors"); + add(UL_UNIT_TICKS, "%d ticks"); + add(UL_DIMENSIONS_NM, "%d nm x %d nm"); + add(UL_DIMENSIONS_TILES, "%d tiles x %d tiles"); + add(UL_DIMENSIONS_DIES, "%d dies x %d dies"); + add(UL_DIMENSIONS_NM_TOTAL, "%d nm x %d nm (%d nm^2)"); + add(UL_DIMENSIONS_TILES_TOTAL, "%d tiles x %d tiles (%d tiles^2)"); + add(UL_DIMENSIONS_DIES_TOTAL, "%d dies x %d dies (%d dies^2)"); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationRecipeProvider.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationRecipeProvider.java new file mode 100644 index 000000000..8575328ce --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/data/FabricationRecipeProvider.java @@ -0,0 +1,88 @@ +package mrtjp.projectred.fabrication.data; + +import codechicken.lib.datagen.recipe.RecipeProvider; +import net.minecraft.data.DataGenerator; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Blocks; +import net.minecraftforge.common.Tags; + +import static mrtjp.projectred.core.init.CoreReferences.PLATE_ITEM; +import static mrtjp.projectred.core.init.CoreReferences.SILICON_ITEM; +import static mrtjp.projectred.core.init.CoreTags.ELECTROTINE_ALLOY_INGOT_TAG; +import static mrtjp.projectred.core.init.CoreTags.SAPPHIRE_GEM_TAG; +import static mrtjp.projectred.fabrication.init.FabricationReferences.*; + +public class FabricationRecipeProvider extends RecipeProvider { + + public FabricationRecipeProvider(DataGenerator dataGenerator) { + super(dataGenerator); + } + + @Override + public String getName() { + return "ProjectRed-Fabrication Recipes"; + } + + @Override + protected void registerRecipes() { + + shapedRecipe(IC_WORKBENCH_BLOCK) + .key('s', Blocks.STONE) + .key('w', ItemTags.PLANKS) + .key('b', IC_BLUEPRINT_ITEM) + .patternLine("sss") + .patternLine("wbw") + .patternLine("www"); + + shapedRecipe(PLOTTING_TABLE_BLOCK) + .key('g', Blocks.GLASS) + .key('t', SAPPHIRE_GEM_TAG) + .key('s', Blocks.STONE) + .key('p', BLANK_PHOTOMASK_ITEM) + .key('w', ItemTags.PLANKS) + .key('b', ELECTROTINE_ALLOY_INGOT_TAG) + .patternLine("gtg") + .patternLine("sps") + .patternLine("wbw"); + + shapedRecipe(LITHOGRAPHY_TABLE_BLOCK) + .key('g', Blocks.GLASS) + .key('t', Tags.Items.GEMS_EMERALD) + .key('o', Tags.Items.OBSIDIAN) + .key('i', Tags.Items.INGOTS_IRON) + .key('w', ItemTags.PLANKS) + .key('b', ELECTROTINE_ALLOY_INGOT_TAG) + .patternLine("gtg") + .patternLine("oio") + .patternLine("wbw"); + + shapedRecipe(PACKAGING_TABLE_BLOCK) + .key('g', Blocks.GLASS) + .key('t', Tags.Items.DUSTS_REDSTONE) + .key('p', PLATE_ITEM) + .key('w', ItemTags.PLANKS) + .key('b', ELECTROTINE_ALLOY_INGOT_TAG) + .patternLine("gtg") + .patternLine("ppp") + .patternLine("wbw"); + + shapedRecipe(IC_BLUEPRINT_ITEM) + .key('p', Items.PAPER) + .key('b', Tags.Items.DYES_BLUE) + .key('r', Tags.Items.DUSTS_REDSTONE) + .patternLine("pbp") + .patternLine("brb") + .patternLine("pbp"); + + shapedRecipe(BLANK_PHOTOMASK_ITEM) + .key('g', Tags.Items.GLASS_PANES) + .key('q', Tags.Items.GEMS_QUARTZ) + .patternLine("ggg") + .patternLine("gqg") + .patternLine("ggg"); + + smelting(ROUGH_SILICON_WAFER_ITEM) + .ingredient(SILICON_ITEM); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/EditorDataUtils.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/EditorDataUtils.java new file mode 100644 index 000000000..312f46590 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/EditorDataUtils.java @@ -0,0 +1,118 @@ +package mrtjp.projectred.fabrication.editor; + +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.engine.InterfaceSpec; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +import java.util.List; + +public class EditorDataUtils { + + // ICEditor + public static final String KEY_FORMAT = "format"; // int + public static final String KEY_ACTIVE = "active"; // boolean + public static final String KEY_IC_NAME = "ic_name"; // String + public static final String KEY_TILE_MAP = "tile_map"; // CompoundTag + public static final String KEY_TILE_COUNT = "tile_count"; // int + public static final String KEY_IS_BUILT = "is_built"; // boolean + public static final String KEY_IO_SPEC = "io_spec"; + + // ICEditorStateMachine + public static final String KEY_COMP_STATE = "state"; // byte + public static final String KEY_FLAT_MAP = "flat_map"; // String + public static final String KEY_SIMULATION = "sim_cont"; // CompoundTag + public static final String KEY_COMPILER_LOG = "compiler_log"; // CompoundTag + + // ICIssuesLog + public static final String KEY_COMPLETED_STEPS = "completed_steps"; // int + public static final String KEY_CURRENT_PATH = "current_path"; // int array + public static final String KEY_PROBLEMS_LIST = "problems"; // ListTag + public static final String KEY_ERROR_COUNT = "error_count"; // int + public static final String KEY_WARNING_COUNT = "warning_count"; // int + + public static int getFormat(CompoundTag tag) { + return tag.getInt(KEY_FORMAT); + } + + public static boolean hasEditorData(CompoundTag tag) { + return tag != null && + tag.contains(KEY_FORMAT) && + tag.contains(KEY_ACTIVE) && + tag.contains(KEY_TILE_MAP); + } + + // Minimum subset of data required to fabricate gate (i.e. create photomask) + public static boolean hasFabricationTarget(CompoundTag tag) { + return tag != null && + tag.contains(KEY_IS_BUILT) && + tag.contains(KEY_FLAT_MAP); + } + + public static boolean canFabricate(CompoundTag tag) { + return hasFabricationTarget(tag) && tag.getBoolean(KEY_IS_BUILT) && getErrorCount(tag) == 0; + } + + public static int getErrorCount(CompoundTag tag) { + return tag.getCompound(KEY_COMPILER_LOG).getInt(KEY_ERROR_COUNT); + } + + public static int getWarningCount(CompoundTag tag) { + return tag.getCompound(KEY_COMPILER_LOG).getInt(KEY_WARNING_COUNT); + } + + // Creates copy of editor tag with only the data required to fabricate a gate + public static CompoundTag createFabricationCopy(CompoundTag editorTag) { + CompoundTag copy = new CompoundTag(); + copy.putBoolean(KEY_IS_BUILT, editorTag.getBoolean(KEY_IS_BUILT)); + copy.putString(KEY_IC_NAME, editorTag.getString(KEY_IC_NAME)); + copy.putInt(KEY_TILE_COUNT, editorTag.getInt(KEY_TILE_COUNT)); + copy.put(KEY_IO_SPEC, editorTag.getCompound(KEY_IO_SPEC)); + copy.putString(KEY_FLAT_MAP, editorTag.getString(KEY_FLAT_MAP)); + return copy; + } + + public static InterfaceSpec getInterfaceSpec(CompoundTag tag) { + return InterfaceSpec.createFrom(tag, KEY_IO_SPEC); + } + + public static void saveTileCoord(CompoundTag tag, String key, TileCoord coord) { + tag.putByte(key + "_x", (byte) coord.x); + tag.putByte(key + "_y", (byte) coord.y); + tag.putByte(key + "_z", (byte) coord.z); + } + + public static TileCoord loadTileCoord(CompoundTag tag, String key) { + return new TileCoord( + tag.getByte(key + "_x"), + tag.getByte(key + "_y"), + tag.getByte(key + "_z") + ); + } + + public static void saveTileCoordList(CompoundTag tag, String key, Iterable coordList) { + ListTag list = new ListTag(); + for (TileCoord coord : coordList) { + list.add(tileCoordToNBT(coord)); + } + tag.put(key, list); + } + + public static void loadTileCoordList(CompoundTag tag, String key, List coordList) { + ListTag list = tag.getList(key, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + coordList.add(tileCoordFromNBT(list.getCompound(i))); + } + } + + public static CompoundTag tileCoordToNBT(TileCoord coord) { + CompoundTag tag = new CompoundTag(); + saveTileCoord(tag, "", coord); + return tag; + } + + public static TileCoord tileCoordFromNBT(CompoundTag tag) { + return loadTileCoord(tag, ""); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/GatePlacementType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/GatePlacementType.java new file mode 100644 index 000000000..71ebfec80 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/GatePlacementType.java @@ -0,0 +1,7 @@ +package mrtjp.projectred.fabrication.editor; + +public enum GatePlacementType { + INTERNAL, + IO_EDGE, + TOP_LAYER +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICEditorStateMachine.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICEditorStateMachine.java new file mode 100644 index 000000000..99e1ede23 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICEditorStateMachine.java @@ -0,0 +1,445 @@ +package mrtjp.projectred.fabrication.editor; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.TileCoord; +import mrtjp.fengine.api.ICFlatMap; +import mrtjp.fengine.api.ICStepThroughAssembler; +import mrtjp.projectred.fabrication.engine.BaseTile; +import mrtjp.projectred.fabrication.engine.ICSimulationContainer; +import mrtjp.projectred.fabrication.engine.IIOConnectionTile; +import mrtjp.projectred.fabrication.engine.PRFabricationEngine; +import mrtjp.projectred.fabrication.engine.log.ICCompilerLog; +import mrtjp.projectred.fabrication.engine.log.IODirectionMismatchError; +import mrtjp.projectred.fabrication.engine.log.NoInputsError; +import mrtjp.projectred.fabrication.engine.log.NoOutputsError; +import net.minecraft.nbt.CompoundTag; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.*; + +public class ICEditorStateMachine { + + public static final int KEY_STATE_CHANGED = 0; + public static final int KEY_COMPILER_LOG_CLEARED = 1; + public static final int KEY_COMPILER_LOG_NODE_ADDED = 2; + public static final int KEY_COMPILER_LOG_NODE_EXECUTED = 3; + public static final int KEY_COMPILER_LOG_PROBLEM_ADDED = 4; + + public static final int KEY_CLIENT_COMPILE_CLICKED = 10; + + private final ICWorkbenchEditor editor; + + private static final int STATE_INITIAL = 0; + private static final int STATE_AWAITING_COMPILE = 1; + private static final int STATE_COMPILING = 2; + private static final int STATE_SIMULATING = 3; + private static final int STATE_COMPILE_FAILED = 4; + + private final State[] states = { + new StateInitial(), + new StateAwaitingCompile(), + new StateCompiling(), + new StateSimulating(), + new CompileFailed(), + }; + + private int currentState = STATE_INITIAL; + + private final StateMachineCallback callback; + + private final ICSimulationContainer simulationContainer = new ICSimulationContainer(); + private final ICCompilerLog compilerLog = new ICCompilerLog(this); + + private String lastCompiledFlatMap = PRFabricationEngine.EMPTY_FLAT_MAP_SERIALIZED; + + private boolean autoCompileOnChange = false; //TODO client-side toggle + + public ICEditorStateMachine(ICWorkbenchEditor editor, StateMachineCallback callback) { + this.editor = editor; + this.callback = callback; + } + + public ICCompilerLog getCompilerLog() { + return compilerLog; + } + + public void save(CompoundTag tag) { + tag.putByte(KEY_COMP_STATE, (byte) currentState); + tag.putString(KEY_FLAT_MAP, lastCompiledFlatMap); + + CompoundTag simTag = new CompoundTag(); + simulationContainer.save(simTag); + tag.put(KEY_SIMULATION, simTag); + + CompoundTag logTag = new CompoundTag(); + compilerLog.save(logTag); + tag.put(KEY_COMPILER_LOG, logTag); + } + + public void load(CompoundTag tag) { + currentState = tag.getByte(KEY_COMP_STATE) & 0xFF; + lastCompiledFlatMap = tag.getString(KEY_FLAT_MAP); + simulationContainer.load(tag.getCompound(KEY_SIMULATION)); + compilerLog.load(tag.getCompound(KEY_COMPILER_LOG)); + } + + public void writeDesc(MCDataOutput out) { + out.writeByte(currentState); + simulationContainer.writeDesc(out); + compilerLog.writeDesc(out); + } + + public void readDesc(MCDataInput in) { + currentState = in.readUByte(); + simulationContainer.readDesc(in); + compilerLog.readDesc(in); + } + + public void reset() { + enterState(STATE_INITIAL, true); + } + + public void readStateMachineStream(MCDataInput in, int key) { + switch (key) { + case KEY_STATE_CHANGED: + enterStateOnClient(in.readUByte()); + break; + case KEY_COMPILER_LOG_CLEARED: + case KEY_COMPILER_LOG_NODE_ADDED: + case KEY_COMPILER_LOG_NODE_EXECUTED: + case KEY_COMPILER_LOG_PROBLEM_ADDED: + compilerLog.readLogStream(in, key); + break; + case KEY_CLIENT_COMPILE_CLICKED: + onCompileTriggered(); + break; + default: + throw new IllegalArgumentException("Unknown compiler stream key: " + key); + } + } + + public MCDataOutput getStateMachineStream(int key) { + return editor.getStateMachineStream(key); + } + + //region State Machine events + public void onTick(long time) { + states[currentState].onTick(time); + } + + public void onTileMapChanged() { + states[currentState].onTileMapChanged(); + } + + public void onCompileTriggered() { + states[currentState].onCompileTriggered(); + } + + public void onInputRegistersChanged(int rotation, Function changeFunction) { + states[currentState].onInputRegistersChanged(rotation, changeFunction); + } + //endregion + + //region Client-side utilities + public void sendCompileButtonClicked() { + // Notifies server to call onCompileTriggered + getStateMachineStream(KEY_CLIENT_COMPILE_CLICKED); + } + public boolean canTriggerCompile() { + return states[currentState].canTransitionTo(STATE_COMPILING); + } + public boolean isCompiling() { + return currentState == STATE_COMPILING; + } + public boolean isSimulating() { + return currentState == STATE_SIMULATING; + } + public boolean didLastCompileFailed() { + return getCompilerLog().getErrorCount() > 0; + } + //endregion + + private void enterState(int id, boolean force) { + if (currentState == id) return; + + if (!force && !states[currentState].canTransitionTo(id)) + throw new RuntimeException("Illegal state change requested"); + + int oldState = currentState; + + states[currentState].onStateLeaving(id); + currentState = id; + states[currentState].onStateEntered(oldState); + + LOGGER.info("State transition: " + oldState + " -> " + currentState); + editor.markDirty(); + } + + private void enterStateAndSend(int id) { + // Shift state + enterState(id, false); + + // Notify clients to also shift states + getStateMachineStream(KEY_STATE_CHANGED).writeByte(currentState); + } + + private void enterStateOnClient(int id) { + int oldState = currentState; + + states[currentState].onClientStateLeaving(id); + currentState = id; + states[currentState].onClientStateEntered(oldState); + + LOGGER.info("Client state transition: " + oldState + " -> " + currentState); + } + + public interface StateMachineCallback { + + void onCompileStart(); + + void onCompileComplete(); + + void onCompileFailed(); + + void onSimulationComplete(int changeMask, ICSimulationContainer container); + } + + private interface State { + + //region Server-side events + default void onTick(long time) { } + + default void onTileMapChanged() { } + + default void onCompileTriggered() { } + + default void onInputRegistersChanged(int rotation, Function changeFunction) { } + + boolean canTransitionTo(int id); + + default void onStateEntered(int previousStateId) { } + + default void onStateLeaving(int nextStateId) { } + //endregion + + //region Client-side events + default void onClientStateEntered(int previousStateId) { } + + default void onClientStateLeaving(int nextStateId) { } + //endregion + } + + private class StateInitial implements State { + + @Override + public void onTileMapChanged() { + enterStateAndSend(STATE_AWAITING_COMPILE); + } + + @Override + public boolean canTransitionTo(int id) { + return id == STATE_AWAITING_COMPILE; + } + + @Override + public void onStateEntered(int previousStateId) { + compilerLog.clearAndSend(); + } + } + + private class StateAwaitingCompile implements State { + + @Override + public void onTick(long time) { + if (autoCompileOnChange) { + enterStateAndSend(STATE_COMPILING); + } + } + + @Override + public void onCompileTriggered() { + enterStateAndSend(STATE_COMPILING); + } + + @Override + public boolean canTransitionTo(int id) { + return id == STATE_COMPILING; + } + } + + private class StateCompiling implements State { + + private ICStepThroughAssembler assembler = null; + + @Override + public void onTick(long time) { + if (assembler == null) { + LOGGER.warn("Compiler assembler is null!"); + restartAssembly(); + return; + } + + if (!assembler.isDone()) { + long nanoTime = System.nanoTime(); + long elapsedTime; + do { + assembler.stepIn(); + elapsedTime = System.nanoTime() - nanoTime; + } while (elapsedTime < 500000L && !assembler.isDone()); // Use up to 0.5ms to compile + } + + if (assembler.isDone()) { + ICFlatMap map = assembler.result(); + assembler = null; //TODO make assemblers clearable + lastCompiledFlatMap = PRFabricationEngine.instance.serializeFlatMap(map); + simulationContainer.setFlatMap(map); + + if (compilerLog.getErrorCount() > 0) { + enterStateAndSend(STATE_COMPILE_FAILED); + if (callback != null) callback.onCompileFailed(); + } else { + enterStateAndSend(STATE_SIMULATING); + if (callback != null) callback.onCompileComplete(); + } + } + } + + @Override + public void onTileMapChanged() { + restartAssembly(); + } + + @Override + public boolean canTransitionTo(int id) { + return id == STATE_SIMULATING || id == STATE_COMPILE_FAILED; + } + + @Override + public void onStateEntered(int previousStateId) { + restartAssembly(); + } + + @Override + public void onStateLeaving(int nextStateId) { + assembler = null; + } + + private void restartAssembly() { + assembler = PRFabricationEngine.instance.newStepThroughAssembler(); + assembler.setEventReceiver(compilerLog); + compilerLog.clearAndSend(); + + assembler.addTileMap(editor.getTileMap(), Collections.emptyMap()); + if (callback != null) callback.onCompileStart(); + + // Check for problems detectable before compilation + + int ioMask = 0; // 2 bits per side, 0x1 = input, 0x2 = output, lookup with 0x3 << side*2 + Collection ioTiles = editor.getTileMap().getIOTiles(); + for (var io : ioTiles) { + int s = io.getIOSide(); + int m = io.isInputIOMode() ? 0x1 : 0x2; + ioMask |= m << s*2; + } + + // Check for sides marked as both input and output + for (int r = 0; r < 4; r++) { + int m = ioMask >> r*2 & 0x3; + if (m == 0x3) { + int finalR = r; + List coordList = ioTiles.stream() + .filter(io -> io.getIOSide() == finalR) + .map(io -> ((BaseTile)io).getPos()) + .toList(); + + compilerLog.addProblem(new IODirectionMismatchError(coordList)); + } + } + + if ((ioMask & 0x55) == 0) { + compilerLog.addProblem(new NoInputsError()); + } + + if ((ioMask & 0xAA) == 0) { + compilerLog.addProblem(new NoOutputsError()); + } + } + } + + private class StateSimulating implements State { + + private long lastTime = -1; + + @Override + public void onTick(long time) { + + // Note: Because this is always -1 initially, 1 tick will be missed on every chunk load. Not a big deal for + // IC Workbench tile, because there are no external interaction. But FabricatedGateParts get around this + // by saving the elapsed time to NBT, and then use IChunkLoadTile hook to convert the elapsed to new lastTime. + // May be worth implementing IChunkLoadTile on workbench tile, but this is okay for now. + if (lastTime == -1) { + lastTime = time; + return; + } + + long elapsedTime = time - lastTime; + lastTime = time; + + simulationContainer.progressTime(elapsedTime); + simulationContainer.pushTime(); + propagateAndNotify(); + } + + @Override + public void onInputRegistersChanged(int rotation, Function changeFunction) { + short oldInput = simulationContainer.getInput(rotation); + short newInput = changeFunction.apply(oldInput); + LOGGER.info("oldInput: " + oldInput + ", newInput: " + newInput); + if (oldInput != newInput) { + simulationContainer.setInput(rotation, newInput); + simulationContainer.pushInputs(1 << rotation); + propagateAndNotify(); + } + } + + private void propagateAndNotify() { + simulationContainer.simulate(); + int changeMask = simulationContainer.pullOutputs(); + if (callback != null) callback.onSimulationComplete(changeMask, simulationContainer); + } + + @Override + public void onTileMapChanged() { + enterStateAndSend(STATE_AWAITING_COMPILE); //TODO enter initial if tile map was emptied + } + + @Override + public boolean canTransitionTo(int id) { + return id == STATE_AWAITING_COMPILE; + } + + @Override + public void onStateEntered(int previousStateId) { + lastTime = -1; + } + } + + private class CompileFailed implements State { + + @Override + public boolean canTransitionTo(int id) { + return id == STATE_AWAITING_COMPILE; + } + + @Override + public void onTileMapChanged() { + enterStateAndSend(STATE_AWAITING_COMPILE); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICEditorToolType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICEditorToolType.java new file mode 100644 index 000000000..ae549faf2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICEditorToolType.java @@ -0,0 +1,34 @@ +package mrtjp.projectred.fabrication.editor; + +import mrtjp.projectred.fabrication.editor.tools.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public enum ICEditorToolType { + INTERACT_TOOL(InteractTool::new), + ERASE_TOOL(EraseTool::new), + GATE_PLACER_TOOL(GatePlacerTool::new), + + WIRE_PLACER_TOOL(WirePlacerTool::new), + + ; + + private final Supplier factory; + + ICEditorToolType(Supplier factory) { + this.factory = factory; + } + + public IICEditorTool createTool() { + return factory.get(); + } + + public static ArrayList createToolList() { + return Arrays.stream(ICEditorToolType.values()) + .map(ICEditorToolType::createTool) + .collect(Collectors.toCollection(ArrayList::new)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICWorkbenchEditor.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICWorkbenchEditor.java new file mode 100644 index 000000000..533a127b1 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/ICWorkbenchEditor.java @@ -0,0 +1,333 @@ +package mrtjp.projectred.fabrication.editor; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.tools.IICEditorTool; +import mrtjp.projectred.fabrication.engine.BaseTile; +import mrtjp.projectred.fabrication.engine.BaseTileMap; +import mrtjp.projectred.fabrication.engine.ICSimulationContainer; +import mrtjp.projectred.fabrication.engine.ICTileType; +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceLocation; + +import javax.annotation.Nullable; +import java.util.*; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.*; + +public class ICWorkbenchEditor implements ICEditorStateMachine.StateMachineCallback { + + public static final Style UNIFORM = Style.EMPTY.withFont(new ResourceLocation("minecraft", "uniform")); + public static final Style UNIFORM_DARK_GRAY = UNIFORM.withColor(ChatFormatting.DARK_GRAY); + public static final Style UNIFORM_GRAY = UNIFORM.withColor(ChatFormatting.GRAY); + public static final Style UNIFORM_RED = UNIFORM.withColor(ChatFormatting.RED); + public static final Style UNIFORM_YELLOW = UNIFORM.withColor(ChatFormatting.YELLOW); + + public static final int EDITOR_FORMAT = 1; + + private static final int STREAM_ID_GENERAL = 0; + private static final int STREAM_ID_TILE_UPDATES = 1; + private static final int STREAM_ID_FSM = 2; + + // Server to client keys + private static final int KEY_ADD_TILE = 1; + private static final int KEY_REMOVE_TILE = 2; + private static final int KEY_SET_IC_NAME = 3; + + // Client to server keys + private static final int KEY_TOOL = 10; + private static final int KEY_CLIENT_SEND_IC_NAME = 11; + + private final IICWorkbenchEditorNetwork network; + private final BaseTileMap tileMap = new BaseTileMap(this); + private final ICEditorStateMachine stateMachine = new ICEditorStateMachine(this, this); + + private final ArrayList toolList = ICEditorToolType.createToolList(); + private final List neighborChangeList = new LinkedList<>(); + + private boolean isActive = false; + private String icName = "untitled"; + + public ICWorkbenchEditor(IICWorkbenchEditorNetwork network) { + this.network = network; + for (IICEditorTool t : toolList) t.bindEditor(this); + } + + public String getIcName() { + return icName; + } + + public ArrayList getToolList() { + return toolList; + } + + public BaseTileMap getTileMap() { + return tileMap; + } + + public ICEditorStateMachine getStateMachine() { + return stateMachine; + } + + public boolean isActive() { + return isActive; + } + + //region ICWorkbenchTile utilities + public void save(CompoundTag tag) { + LOGGER.info("ICWorkbenchEditor: saving to NBT"); + tag.putInt(KEY_FORMAT, EDITOR_FORMAT); + tag.putBoolean(KEY_ACTIVE, isActive); + tag.putString(KEY_IC_NAME, icName); + + CompoundTag tileMapTag = new CompoundTag(); + tileMap.save(tileMapTag); + tag.put(KEY_TILE_MAP, tileMapTag); + + stateMachine.save(tag); + } + + public void load(CompoundTag tag) { + LOGGER.info("ICWorkbenchEditor: reading form NBT"); + isActive = tag.getBoolean(KEY_ACTIVE); + icName = tag.getString(KEY_IC_NAME); + tileMap.load(tag.getCompound(KEY_TILE_MAP)); + stateMachine.load(tag); + } + + public void writeDesc(MCDataOutput out) { + LOGGER.info("ICWorkbenchEditor: writing description"); + out.writeBoolean(isActive); + out.writeString(icName); + tileMap.writeDesc(out); + stateMachine.writeDesc(out); + } + + public void readDesc(MCDataInput in) { + LOGGER.info("ICWorkbenchEditor: Reading description"); + isActive = in.readBoolean(); + icName = in.readString(); + tileMap.readDesc(in); + stateMachine.readDesc(in); + } + + private void clear() { + LOGGER.info("ICWorkbenchEditor: Preparing load of initial data (Should be server only)"); + tileMap.removeAll(); + stateMachine.reset(); + icName = "untitled"; + } + + public void readBlueprintTagAndActivate(@Nullable CompoundTag tag) { + // Clear the session + clear(); + + // Save editor contents + if (tag != null) load(tag); + + // Activate editor + isActive = true; + } + + public void writeBlueprintTagAndDeactivate(CompoundTag tag) { + // Save all editor contents + save(tag); + + // Save additional metadata for blueprints + tileMap.getInterfaceSpec().saveTo(tag, KEY_IO_SPEC); + tag.putInt(KEY_TILE_COUNT, tileMap.getTileCount()); + tag.putBoolean(KEY_IS_BUILT, stateMachine.isSimulating()); + + // Deactivate editor + isActive = false; + clear(); + } + //endregion + + public void readBufferedStream(MCDataInput in, int streamKey, int frameKey) { + switch (streamKey) { + case STREAM_ID_GENERAL: + readGeneralStream(in, frameKey); + break; + case STREAM_ID_TILE_UPDATES: + readTileStream(in, frameKey); + break; + case STREAM_ID_FSM: + stateMachine.readStateMachineStream(in, frameKey); + break; + default: + LOGGER.error("Unknown stream key " + streamKey); + } + } + + private void readTileStream(MCDataInput in, int frameKey) { + + TileCoord positionToUpdate = new TileCoord(in.readByte(), in.readByte(), in.readByte()); + int key = in.readUByte(); + Optional tileToUpdate = tileMap.getBaseTile(positionToUpdate); + if (!tileToUpdate.isPresent() || tileToUpdate.get().getTileType().getID() != frameKey) { + LOGGER.error("Tile Update error: No tile with id " + frameKey + " at position " + positionToUpdate + ". Reading into temporary tile"); + BaseTile tmp = ICTileType.createFromId(frameKey); + if (tmp == null) { + LOGGER.error("Unknown tile id " + frameKey + " in tile update stream"); + } else { + tmp.read(in, key); + } + return; + } + tileToUpdate.get().read(in, key); + } + + private void readGeneralStream(MCDataInput in, int frameKey) { + switch (frameKey) { + case KEY_ADD_TILE: + BaseTile tile = ICTileType.createFromId(in.readUByte()); //TODO check if not null? + tileMap.addTile(new TileCoord(in.readByte(), in.readByte(), in.readByte()), tile); + tile.readDesc(in); + break; + case KEY_REMOVE_TILE: + tileMap.removeTile(new TileCoord(in.readByte(), in.readByte(), in.readByte())); //TODO check if removed? + break; + case KEY_SET_IC_NAME: + icName = in.readString(); + break; + case KEY_TOOL: + toolList.get(in.readUByte()).readPacket(in); + break; + case KEY_CLIENT_SEND_IC_NAME: + //TODO validate name + icName = in.readString(); + network.getBufferedStream(STREAM_ID_GENERAL, KEY_SET_IC_NAME).writeString(icName); + break; + default: + LOGGER.error("Unknown key " + frameKey + " in general stream"); + } + } + + public void tick() { + + // Alert neighbors of changes if necessary + if (!neighborChangeList.isEmpty()) { + Queue changesRemaining = new LinkedList<>(neighborChangeList); + neighborChangeList.clear(); + + Set tilesNotified = new HashSet<>(); + TileCoord next = changesRemaining.poll(); + while (next != null) { + if (!tilesNotified.contains(next)) { + tilesNotified.add(next); + Optional tile = tileMap.getBaseTile(next); + tile.ifPresent(BaseTile::onNeighborChanged); + } + next = changesRemaining.poll(); + } + } + + // Run compiler or simulator + if (!network.isClientSide()) stateMachine.onTick(network.getGameTime()); + } + + public MCDataOutput getToolStream(IICEditorTool tool) { + MCDataOutput out = network.getBufferedStream(STREAM_ID_GENERAL, KEY_TOOL); + out.writeByte(tool.getToolType().ordinal()); + return out; + } + + public MCDataOutput getTileStream(BaseTile tile, int key) { + // Use tile ID as frame key + MCDataOutput out = network.getBufferedStream(STREAM_ID_TILE_UPDATES, tile.getTileType().getID()); + out.writeByte(tile.getPos().x).writeByte(tile.getPos().y).writeByte(tile.getPos().z); + out.writeByte(key); + return out; + } + + public MCDataOutput getStateMachineStream(int key) { + return network.getBufferedStream(STREAM_ID_FSM, key); + } + + //region Server Utils + public void addTile(BaseTile tile, TileCoord pos) { + if (network.isClientSide()) throw new RuntimeException("Tiles can only be added server-side"); + + if (!tileMap.addTile(pos, tile)) { + LOGGER.error("Failed to add tile to pos " + pos); + return; + } + tile.onAdded(); + + // Send the new tile to clients + MCDataOutput out = network.getBufferedStream(STREAM_ID_GENERAL, KEY_ADD_TILE); + out.writeByte(tile.getTileType().getID()); + out.writeByte(pos.x).writeByte(pos.y).writeByte(pos.z); + tile.writeDesc(out); + + markTileChange(); + } + + public void removeTile(TileCoord pos) { + if (network.isClientSide()) throw new RuntimeException("Tiles can only be removed server-side"); + + Optional tileToRemove = tileMap.getBaseTile(pos); + if (!tileToRemove.isPresent()) { + LOGGER.error("No tile present to remove at pos " + pos); + return; + } + tileToRemove.get().onRemoved(); + tileMap.removeTile(pos); + + // Send the tile removed action to clients + MCDataOutput out = network.getBufferedStream(STREAM_ID_GENERAL, KEY_REMOVE_TILE); + out.writeByte(pos.x).writeByte(pos.y).writeByte(pos.z); + + markTileChange(); + } + + public void queueNeighborChange(TileCoord pos) { + neighborChangeList.add(pos); + } + + public void markTileChange() { + network.markSave(); + stateMachine.onTileMapChanged(); + } + + public void markDirty() { + network.markSave(); + } + //endregion + + //region Client Utils + public void sendNewICName(String name) { + // Notifies server to set a new IC name + network.getBufferedStream(STREAM_ID_GENERAL, KEY_CLIENT_SEND_IC_NAME).writeString(name); + } + //endregion + + //region State Machine callbacks + @Override + public void onCompileStart() { + LOGGER.info("Compiling..."); + } + + @Override + public void onCompileComplete() { + LOGGER.info("Compilation complete"); + } + + @Override + public void onCompileFailed() { + LOGGER.info("Compilation failed"); + } + + @Override + public void onSimulationComplete(int changeMask, ICSimulationContainer container) { + for (var entry : tileMap.getBaseTileEntries()) { + entry.getValue().onSimRegistersChanged(changeMask, container); + } + } + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/IICWorkbenchEditorNetwork.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/IICWorkbenchEditorNetwork.java new file mode 100644 index 000000000..894ec3f70 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/IICWorkbenchEditorNetwork.java @@ -0,0 +1,40 @@ +package mrtjp.projectred.fabrication.editor; + +import codechicken.lib.data.MCDataOutput; + +/** + * Interface implemented on an IC Workbench tile containing network-related methods that the editor uses + * to send data to and from clients that have the editor open. + */ +public interface IICWorkbenchEditorNetwork { + + /** + * Returns a buffered output stream that can be freely written. It is automatically + * sent at regular intervals. If client, packet is sent to server. If server, packets are + * sent to all clients watching the editor UI. + * + * The stream is always terminated with a frame of value 255 before being sent. The other side + * must read through the stream until a terminator frame byte of 255 is read. + * + * @param streamKey Identifier for the buffered stream (between 0 and 255) + * @param frameKey Frame byte for each group of data. Cannot be 255, as that is used for end-of-packet terminator. + * @return The buffered stream + */ + MCDataOutput getBufferedStream(int streamKey, int frameKey); + + /** + * @return True if this is the client + */ + boolean isClientSide(); + + /** + * Called by the editor when something inside this editor has changed that would cause the tile's + * NBT data to change + */ + void markSave(); + + /** + * @return The world game time + */ + long getGameTime(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/BaseICEditorTool.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/BaseICEditorTool.java new file mode 100644 index 000000000..b29a9bb9f --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/BaseICEditorTool.java @@ -0,0 +1,54 @@ +package mrtjp.projectred.fabrication.editor.tools; + +import codechicken.lib.vec.Vector3; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.List; + +public abstract class BaseICEditorTool implements IICEditorTool { + + protected ICWorkbenchEditor editor; + + @Override + public void bindEditor(ICWorkbenchEditor editor) { + this.editor = editor; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void buildTooltip(Vector3 mousePosition, boolean isFirstHit, List tooltip) { + + if (!isFirstHit) return; + + TileCoord pos = IICEditorTool.toNearestPosition(mousePosition); + editor.getTileMap().getBaseTile(pos).ifPresent(tile -> tile.buildToolTip(tooltip)); + } + + protected boolean isInBody(TileCoord coord) { + TileCoord minBounds = editor.getTileMap().getMinBounds(); + TileCoord maxBounds = editor.getTileMap().getMaxBounds(); + + return coord.x > minBounds.x && coord.x < maxBounds.x && + coord.y >= minBounds.y && coord.y <= maxBounds.y && + coord.z > minBounds.z && coord.z < maxBounds.z; + } + + protected boolean isOnIOEdge(TileCoord coord) { + TileCoord minBounds = editor.getTileMap().getMinBounds(); + TileCoord maxBounds = editor.getTileMap().getMaxBounds(); + + if (coord.y != 0) return false; + + int edges = 0; + if (coord.x == minBounds.x) edges++; + if (coord.x == maxBounds.x) edges++; + if (coord.z == minBounds.z) edges++; + if (coord.z == maxBounds.z) edges++; + + return edges == 1; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/EraseTool.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/EraseTool.java new file mode 100644 index 000000000..db7eb13b8 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/EraseTool.java @@ -0,0 +1,132 @@ +package mrtjp.projectred.fabrication.editor.tools; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.glfw.GLFW; + +import static mrtjp.projectred.fabrication.editor.tools.IICEditorTool.toNearestPosition; + +public class EraseTool extends BaseICEditorTool { + + private final Vector3 initialMouseDown = new Vector3(); + private boolean leftMouseDown; + + @Override + public ICEditorToolType getToolType() { + return ICEditorToolType.ERASE_TOOL; + } + + @Override + public void readPacket(MCDataInput input) { + + TileCoord start = new TileCoord(input.readByte(), input.readByte(), input.readByte()); + TileCoord end = new TileCoord(input.readByte(), input.readByte(), input.readByte()); + + TileCoord min = start.min(end); + TileCoord max = start.max(end); + + // Delete all tiles in range + for (int x = min.x; x <= max.x; x++) { + for (int z = min.z; z <= max.z; z++) { + TileCoord pos = new TileCoord(x, start.y, z); + if (editor.getTileMap().getBaseTile(pos).isPresent()) editor.removeTile(pos); + } + } + } + + private void executeTool(Vector3 startMouseDown, Vector3 endMouseDown) { + MCDataOutput out = editor.getToolStream(this); + + TileCoord start = toNearestPosition(startMouseDown); + TileCoord end = toNearestPosition(endMouseDown); + + out.writeByte(start.x).writeByte(start.y).writeByte(start.z); + out.writeByte(end.x).writeByte(end.y).writeByte(end.z); + } + + @Override + public boolean toolStart(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + leftMouseDown = true; + initialMouseDown.set(mousePosition); + return true; + } + return false; + } + + @Override + public boolean toolReleased(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT && leftMouseDown) { + leftMouseDown = false; + executeTool(initialMouseDown, mousePosition); + return true; + } + return false; + } + + @Override + public void toolLayerChanged(int previousLayer, int newLayer) { + if (leftMouseDown) { + initialMouseDown.y = newLayer; + } + } + + @Override + public boolean toolDragged(Vector3 mousePosition, Vector3 delta, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + return leftMouseDown; + } + return false; + } + + @Override + public boolean toolScrolled(Vector3 mousePosition, double scroll) { + return false; + } + + @Override + public boolean toolCanceled() { + if (leftMouseDown) { + leftMouseDown = false; + return true; + } + return false; + } + + @Override + public void toolActivated() { + leftMouseDown = false; + } + + @Override + public void toolDeactivated() { + leftMouseDown = false; + } + + @Override + @OnlyIn (Dist.CLIENT) + public void renderOverlay(Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + if (!isFirstHit) return; + + Vector3 a = leftMouseDown ? initialMouseDown : mousePosition; + Vector3 b = mousePosition; + + ccrs.reset(); + ccrs.bind(ICRenderTypes.selectionRenderType, Minecraft.getInstance().renderBuffers().bufferSource(), matrixStack); + ccrs.baseColour = EnumColour.PINK.rgba(leftMouseDown ? 200 : 32); + + ICRenderTypes.renderSelection(ccrs, a, b, 3 / 16D, 2 / 16D); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/GatePlacerTool.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/GatePlacerTool.java new file mode 100644 index 000000000..224349930 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/GatePlacerTool.java @@ -0,0 +1,228 @@ +package mrtjp.projectred.fabrication.editor.tools; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.math.MathHelper; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Translation; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.GatePlacementType; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.engine.gates.GateTile; +import mrtjp.projectred.fabrication.engine.gates.ICGateTileType; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import mrtjp.projectred.integration.client.GateModelRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.glfw.GLFW; + +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.tools.IICEditorTool.toNearestPosition; + +public class GatePlacerTool extends BaseICEditorTool { + + private final Vector3 initialMouseDown = new Vector3(); + private boolean leftMouseDown; + + // Tool config + private boolean overwrite = false; + private ICGateTileType gateType = ICGateTileType.OR; + + public ICGateTileType getGateType() { + return gateType; + } + + public void setGateType(ICGateTileType gateType) { + this.gateType = gateType; + leftMouseDown = false; + } + + @Override + public ICEditorToolType getToolType() { + return ICEditorToolType.GATE_PLACER_TOOL; + } + + @Override + public void readPacket(MCDataInput input) { + + boolean overwrite = input.readBoolean(); + ICGateTileType gateType = ICGateTileType.values()[input.readUByte()]; + int x = input.readByte(); + int y = input.readByte(); + int z = input.readByte(); + int r = input.readUByte(); + + TileCoord pos = new TileCoord(x, y, z); + if (!editor.getTileMap().isInBounds(pos)) return; + if (editor.getTileMap().getTile(pos).isPresent()) { + if (!overwrite) return; + editor.getTileMap().removeTile(pos); + } + + GateTile t = (GateTile) gateType.tileType.create(); + t.preparePlacement(r); + editor.addTile(t, pos); + } + + private void sendPlaceGatePacket(TileCoord pos, int r) { + MCDataOutput out = editor.getToolStream(this); + out.writeBoolean(overwrite); + out.writeByte(gateType.ordinal()); + + out.writeByte(pos.x).writeByte(pos.y).writeByte(pos.z); + out.writeByte(r); + } + + private boolean isValidStart(TileCoord pos) { + + if (!editor.getTileMap().isInBounds(pos)) return false; + if (editor.getTileMap().getTile(pos).isPresent() && !overwrite) return false; + + if (gateType.placementType == GatePlacementType.INTERNAL && !isInBody(pos)) return false; + if (gateType.placementType == GatePlacementType.IO_EDGE && !isOnIOEdge(pos)) return false; + + return true; + } + + @Override + public boolean toolStart(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + TileCoord start = toNearestPosition(mousePosition); + if (isValidStart(start)) { + leftMouseDown = true; + initialMouseDown.set(mousePosition); + return true; + } + } + return false; + } + + @Override + public boolean toolReleased(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT && leftMouseDown) { + leftMouseDown = false; + TileCoord posA = toNearestPosition(initialMouseDown); + sendPlaceGatePacket(posA, getPlacementRotation(initialMouseDown, mousePosition)); + return true; + } + return false; + } + + @Override + public boolean toolDragged(Vector3 mousePosition, Vector3 delta, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + return leftMouseDown; + } + return false; + } + + @Override + public boolean toolScrolled(Vector3 mousePosition, double scroll) { + return false; + } + + @Override + public void toolLayerChanged(int previousLayer, int newLayer) { + if (leftMouseDown) { + initialMouseDown.y = newLayer; + } + } + + @Override + public boolean toolCanceled() { + if (leftMouseDown) { + leftMouseDown = false; + return true; + } + return false; + } + + @Override + public void toolActivated() { + leftMouseDown = false; + } + + @Override + public void toolDeactivated() { + leftMouseDown = false; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void renderOverlay(Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + if (!isFirstHit) return; + + ccrs.reset(); + ccrs.brightness = LightTexture.pack(15, 15); + ccrs.bind(ICRenderTypes.layersRenderType, Minecraft.getInstance().renderBuffers().bufferSource(), matrixStack); + + if (!leftMouseDown) { + ccrs.alphaOverride = 64; + TileCoord mousePos = toNearestPosition(mousePosition); + int rot = getHoverRotation(mousePosition); + if (isValidStart(mousePos)) renderHover(mousePos, rot, ccrs); + } else { + ccrs.alphaOverride = 128; + TileCoord placementPos = toNearestPosition(initialMouseDown); + int rot = getPlacementRotation(initialMouseDown, mousePosition); + renderHover(placementPos, rot, ccrs); + } + } + + @Override + @OnlyIn(Dist.CLIENT) + public void buildTooltip(Vector3 mousePosition, boolean isFirstHit, List tooltip) { + super.buildTooltip(mousePosition, isFirstHit, tooltip); + } + + public void renderHover(TileCoord pos, int r, CCRenderState ccrs) { + GateModelRenderer.instance().renderInventory(ccrs, null, gateType.renderIndex, r, new Translation(pos.x, pos.y, pos.z)); + } + + private int getHoverRotation(Vector3 hoverPos) { + if (gateType.placementType == GatePlacementType.IO_EDGE) { + return getEdgeRotation(toNearestPosition(hoverPos)); + } + return 0; + } + + private int getPlacementRotation(Vector3 initialPos, Vector3 mousePos) { + if (gateType.placementType == GatePlacementType.IO_EDGE) { + return getEdgeRotation(toNearestPosition(initialPos)); + } + // TODO this can be done by comparing x and z components of the initial -> mouse vector + + Vector3 i = initialPos.copy().floor().add(0.5); // Move initial to center of its closest block space + Vector3 a = mousePos.copy().subtract(i); + a.y = 0; // Project onto XY + Vector3 b = new Vector3(1, 0, 0); + + if (a.mag() < 1.2) return 0; // Don't rotate if mouse is too close + + double angle = b.angle(a); // Angle between 0 - pi + if (a.z > 0) angle = (2 * MathHelper.pi) - angle; // Full circular angle 0 - 2*pi + angle = (angle + 1 / 4D * MathHelper.pi) % (2 * MathHelper.pi); // Add 45 degrees + angle = 2 * MathHelper.pi - angle; // Invert, because rotations are counter-clockwise + + // Convert to rotation + double percentage = angle / (2 * MathHelper.pi); + return ((int) (4 * percentage) + 2) % 4; + } + + private int getEdgeRotation(TileCoord pos) { + if (pos.z == editor.getTileMap().getMinBounds().z) return 0; + if (pos.x == editor.getTileMap().getMaxBounds().x) return 1; + if (pos.z == editor.getTileMap().getMaxBounds().z) return 2; + if (pos.x == editor.getTileMap().getMinBounds().x) return 3; + + return 0; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/IICEditorTool.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/IICEditorTool.java new file mode 100644 index 000000000..e7984c6f6 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/IICEditorTool.java @@ -0,0 +1,80 @@ +package mrtjp.projectred.fabrication.editor.tools; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.List; + +public interface IICEditorTool { + + ICEditorToolType getToolType(); + + void bindEditor(ICWorkbenchEditor editor); + + void readPacket(MCDataInput input); + + boolean toolStart(Vector3 mousePosition, int glfwMouseButton); + + boolean toolReleased(Vector3 mousePosition, int glfwMouseButton); + + boolean toolDragged(Vector3 mousePosition, Vector3 delta, int glfwMouseButton); + + boolean toolScrolled(Vector3 mousePosition, double scroll); + + boolean toolCanceled(); + + void toolLayerChanged(int previousLayer, int newLayer); + + void toolActivated(); + + void toolDeactivated(); + + @OnlyIn(Dist.CLIENT) + void renderOverlay(Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack); + + @OnlyIn(Dist.CLIENT) + void buildTooltip(Vector3 mousePosition, boolean isFirstHit, List tooltip); + + static TileCoord toNearestPosition(Vector3 v) { + return new TileCoord((int) Math.floor(v.x), (int) Math.floor(v.y), (int) Math.floor(v.z)); + } + + static boolean isSamePosition(Vector3 a, Vector3 b) { + return Math.floor(a.x) == Math.floor(b.x) && + Math.floor(a.y) == Math.floor(b.y) && + Math.floor(a.z) == Math.floor(b.z); + } + + static Cuboid6 toNearestCuboid(Vector3 v) { + return new Cuboid6(v.copy().floor(), v.copy().ceil()); + } + + static Cuboid6 toSelectionCuboid(Vector3 a, Vector3 b) { + return toNearestCuboid(a).enclose(toNearestCuboid(b)); + } + + /** + * Converts Cuboid from internal tile space to world space + * + * @param pos Position of the tile + * @param c Internal cuboid within bounds [0,0,0 -> 6,16,16] + * @return Cuboid in world space + */ + static Cuboid6 internalToGlobalCuboid(TileCoord pos, Cuboid6 c) { + Cuboid6 box = c.copy(); + box.min.multiply(1/16D); + box.max.multiply(1/16D); + box.add(pos.x, pos.y, pos.z); + return box; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/InteractTool.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/InteractTool.java new file mode 100644 index 000000000..f24bd2b20 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/InteractTool.java @@ -0,0 +1,222 @@ +package mrtjp.projectred.fabrication.editor.tools; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.data.MCDataInput; +import codechicken.lib.render.BlockRenderer; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.render.RenderUtils; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Matrix4; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.engine.BaseTile; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.glfw.GLFW; + +import java.util.List; +import java.util.Optional; + +import static mrtjp.projectred.fabrication.editor.tools.IICEditorTool.*; + +public class InteractTool extends BaseICEditorTool { + + private final Vector3 initialMouseDown = new Vector3(); + private boolean leftMouseDown; + + @Override + public ICEditorToolType getToolType() { + return ICEditorToolType.INTERACT_TOOL; + } + + @Override + public void readPacket(MCDataInput input) { + + TileCoord pos = new TileCoord(input.readByte(), input.readByte(), input.readByte()); + byte b = input.readByte(); + + int z = b & 0x7F; + boolean leftClick = (b & 0x80) != 0; + + Optional tile = editor.getTileMap().getBaseTile(pos); + tile.ifPresent(t -> { + if (leftClick) + t.onInteractionZoneClicked(z); + else + t.onInteractionZoneActivated(z); + }); + } + + private void executeTool(Vector3 startMouseDown, Vector3 endMouseDown) { + TileCoord start = toNearestPosition(startMouseDown); + TileCoord end = toNearestPosition(endMouseDown); + if (!start.equals(end)) return; + + InteractionZone zone = getTargetInteractionZone(endMouseDown); + if (zone == null) return; + + byte b = (byte) ((zone.index & 0x7F) | 0x80); //TODO right clicky + + editor.getToolStream(this) + .writeByte(start.x).writeByte(start.y).writeByte(start.z) + .writeByte(b); + } + + @Override + public boolean toolStart(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + leftMouseDown = true; + initialMouseDown.set(mousePosition); + return true; + } + return false; + } + + @Override + public boolean toolReleased(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT && leftMouseDown) { + leftMouseDown = false; + executeTool(initialMouseDown, mousePosition); + return true; + } + return false; + } + + @Override + public boolean toolDragged(Vector3 mousePosition, Vector3 delta, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + return leftMouseDown; + } + return false; + } + + @Override + public boolean toolScrolled(Vector3 mousePosition, double scroll) { + return false; + } + + @Override + public void toolLayerChanged(int previousLayer, int newLayer) { + if (leftMouseDown) { + initialMouseDown.y = newLayer; + } + } + + @Override + public boolean toolCanceled() { + if (leftMouseDown) { + leftMouseDown = false; + return true; + } + return false; + } + + @Override + public void toolActivated() { + leftMouseDown = false; + } + + @Override + public void toolDeactivated() { + leftMouseDown = false; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void buildTooltip(Vector3 mousePosition, boolean isFirstHit, List tooltip) { + + if (!isFirstHit) return; + + InteractionZone zone = getTargetInteractionZone(mousePosition); + if (zone == null) { + super.buildTooltip(mousePosition, isFirstHit, tooltip); + return; + } + + TileCoord pos = toNearestPosition(mousePosition); + editor.getTileMap().getBaseTile(pos).ifPresent(tile -> + tile.buildInteractionToolTip(tooltip, zone.index)); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void renderOverlay(Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + if (!isFirstHit) return; + + // Selection box for entire tile area + Vector3 a = leftMouseDown ? initialMouseDown : mousePosition; + Vector3 b = mousePosition; + int alpha = leftMouseDown ? isSamePosition(a, b) ? 255 : 128 : 32; + + ccrs.reset(); + ccrs.bind(ICRenderTypes.selectionRenderType, Minecraft.getInstance().renderBuffers().bufferSource(), matrixStack); + ccrs.baseColour = EnumColour.LIGHT_BLUE.rgba(alpha); + +// ICRenderTypes.renderSelection(ccrs, a, a, 3 / 16D, 2 / 16D); // TODO Selection box that sits AROUND the tile + + // Render tile interaction zone + TileCoord coord = toNearestPosition(b); + editor.getTileMap().getBaseTile(coord).ifPresent(tile -> { + List zones = tile.getInteractionZones(); + + Cuboid6 currentZoneBox = null; + + // Render the interaction zone overlay + for (Cuboid6 zone : zones) { + Cuboid6 box = internalToGlobalCuboid(coord, zone); + + boolean isInitialInZone = box.contains(a); + boolean isCurrentInZone = box.contains(b); + + if (isCurrentInZone) currentZoneBox = box; + + ccrs.baseColour = EnumColour.LIGHT_BLUE.rgba(leftMouseDown && isInitialInZone ? isCurrentInZone ? 255 : 128 : 32); + BlockRenderer.renderCuboid(ccrs, box, 1); + } + + // Render bounding box for interaction zone + if (currentZoneBox != null) { + RenderUtils.bufferHitBox(new Matrix4(matrixStack.last().pose()), Minecraft.getInstance().renderBuffers().bufferSource(), currentZoneBox); + } + }); + } + + private TileCoord getTargetTileCoord(Vector3 mousePosition) { + return toNearestPosition(leftMouseDown ? initialMouseDown : mousePosition); + } + + private InteractionZone getTargetInteractionZone(Vector3 mousePosition) { + TileCoord coord = getTargetTileCoord(mousePosition); + BaseTile tile = editor.getTileMap().getBaseTile(coord).orElse(null); + + if (tile == null) return null; + + int i = 0; + for (Cuboid6 zone : tile.getInteractionZones()) { + Cuboid6 box = internalToGlobalCuboid(coord, zone); + if (box.contains(mousePosition)) return new InteractionZone(coord, zone, i); + i++; + } + + return null; + } + + private static class InteractionZone { + public final TileCoord tileCoord; + public final Cuboid6 bounds; + public final int index; + + public InteractionZone(TileCoord tileCoord, Cuboid6 bounds, int index) { + this.tileCoord = tileCoord; + this.bounds = bounds; + this.index = index; + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/WirePlacerTool.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/WirePlacerTool.java new file mode 100644 index 000000000..55265fced --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/editor/tools/WirePlacerTool.java @@ -0,0 +1,226 @@ +package mrtjp.projectred.fabrication.editor.tools; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Translation; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.engine.IRotatableICTile; +import mrtjp.projectred.fabrication.engine.wires.ICWireTileType; +import mrtjp.projectred.fabrication.engine.wires.WireTile; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import mrtjp.projectred.transmission.client.WireModelRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.glfw.GLFW; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import static mrtjp.projectred.fabrication.editor.tools.IICEditorTool.toNearestPosition; + +public class WirePlacerTool extends BaseICEditorTool { + + private final Vector3 initialMouseDown = new Vector3(); + private final List selectedPositionsList = new LinkedList<>(); + private final Set selectedPositionsSet = new HashSet<>(); + + private boolean leftMouseDown; + + // Tool config + private boolean overwrite = false; + private ICWireTileType wireType = ICWireTileType.RED_ALLOY; + + public void setWireType(ICWireTileType wireType) { + this.wireType = wireType; + } + + public ICWireTileType getWireType() { + return wireType; + } + + @Override + public ICEditorToolType getToolType() { + return ICEditorToolType.WIRE_PLACER_TOOL; + } + + @Override + public void readPacket(MCDataInput in) { + + boolean overwrite = in.readBoolean(); + ICWireTileType wireType = ICWireTileType.values()[in.readUByte()]; + int numPositions = in.readUByte(); + int y = in.readByte(); + + for (int i = 0; i < numPositions; i++) { + TileCoord pos = new TileCoord(in.readByte(), y, in.readByte()); + + if (!canPlaceAt(pos, overwrite)) continue; + + WireTile tile = (WireTile) wireType.tileType.create(); + if (editor.getTileMap().getTile(pos).isPresent()) { + editor.removeTile(pos); + } + editor.addTile(tile, pos); + } + } + + protected void sendPlaceWires() { + if (selectedPositionsList.isEmpty()) return; + + MCDataOutput out = editor.getToolStream(this); + out.writeBoolean(overwrite); + out.writeByte(wireType.ordinal()); + out.writeByte(selectedPositionsList.size()); + out.writeByte(selectedPositionsList.get(0).y); // All on same layer + + for (TileCoord t : selectedPositionsList) { + out.writeByte(t.x).writeByte(t.z); + } + } + + protected boolean canPlaceAt(TileCoord pos, boolean overwrite) { + if (!isInBody(pos) || isOnIOEdge(pos)) return false; + return editor.getTileMap().getTile(pos).isEmpty() || overwrite; + } + + protected void addToSelection(Vector3 mousePosition) { + TileCoord pos = toNearestPosition(mousePosition); + if (canPlaceAt(pos, overwrite) && !selectedPositionsSet.contains(pos)) { + selectedPositionsSet.add(pos); + selectedPositionsList.add(pos); + } + } + + @Override + public boolean toolStart(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT) { + leftMouseDown = true; + initialMouseDown.set(mousePosition); + addToSelection(mousePosition); + return true; + } + return false; + } + + @Override + public boolean toolReleased(Vector3 mousePosition, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT && leftMouseDown) { + addToSelection(mousePosition); + sendPlaceWires(); + + leftMouseDown = false; + selectedPositionsSet.clear(); + selectedPositionsList.clear(); + + return true; + } + return false; + } + + @Override + public boolean toolDragged(Vector3 mousePosition, Vector3 delta, int glfwMouseButton) { + if (glfwMouseButton == GLFW.GLFW_MOUSE_BUTTON_LEFT && leftMouseDown) { + addToSelection(mousePosition); + return true; + } + return false; + } + + @Override + public boolean toolScrolled(Vector3 mousePosition, double scroll) { + return false; + } + + @Override + public void toolLayerChanged(int previousLayer, int newLayer) { + if (leftMouseDown) { + int diff = newLayer - previousLayer; + + // Shift entire selection to the new layer + initialMouseDown.y += diff; + + List oldSelection = new LinkedList<>(selectedPositionsList); + selectedPositionsSet.clear(); + selectedPositionsList.clear(); + + for (TileCoord t : oldSelection) { + TileCoord newPos = t.add(0, diff, 0); + selectedPositionsSet.add(newPos); + selectedPositionsList.add(newPos); + } + } + } + + @Override + public boolean toolCanceled() { + if (leftMouseDown) { + leftMouseDown = false; + selectedPositionsSet.clear(); + selectedPositionsList.clear(); + return true; + } + return false; + } + + @Override + public void toolActivated() { + leftMouseDown = false; + selectedPositionsSet.clear(); + selectedPositionsList.clear(); + } + + @Override + public void toolDeactivated() { + leftMouseDown = false; + selectedPositionsSet.clear(); + selectedPositionsList.clear(); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void renderOverlay(Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + if (!isFirstHit && selectedPositionsList.isEmpty()) return; + + ccrs.reset(); + ccrs.bind(ICRenderTypes.layersRenderType, Minecraft.getInstance().renderBuffers().bufferSource(), matrixStack); + ccrs.alphaOverride = leftMouseDown ? 128 : 64; + ccrs.brightness = LightTexture.pack(15, 15); + + for (TileCoord pos : selectedPositionsList) { + int rmask = 0; + for (int r = 0; r < 4; r++) { + int s = IRotatableICTile.rotationToDir(r); + if (selectedPositionsSet.contains(pos.offset(s))) { + rmask |= 1 << r; + } + } + renderWireAt(pos, ccrs, rmask); + } + + // If just hovering, render overlay + if (!leftMouseDown) { + TileCoord pos = toNearestPosition(mousePosition); + if (canPlaceAt(pos, overwrite)) { + renderWireAt(pos, ccrs, 0); + } + } + } + + private void renderWireAt(TileCoord pos, CCRenderState ccrs, int rmask) { + int wireConnMask = rmask << 4; + int modelKey = WireModelRenderer.modelKey(0, wireType.multipartType.getThickness(), wireConnMask); +// RenderWire.render(modelKey, (255 & 0xFF) / 2 + 60 << 24 | 0xFF, renderType.getTextures().get(0), ccrs, new Translation(pos.x, pos.y, pos.z)); + WireModelRenderer.render(ccrs, modelKey, -1, wireType.multipartType.getTextures().get(0), new Translation(pos.x, pos.y, pos.z)); + } + +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/BaseTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/BaseTile.java new file mode 100644 index 000000000..87887c56f --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/BaseTile.java @@ -0,0 +1,113 @@ +package mrtjp.projectred.fabrication.engine; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Transformation; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.fengine.tiles.FETile; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.Collections; +import java.util.List; + +public abstract class BaseTile implements FETile { + + private final ICTileType tileType; + + private BaseTileMap map; + private TileCoord pos; + + public BaseTile(ICTileType tileType) { + this.tileType = tileType; + } + + public void bindMap(BaseTileMap map, TileCoord pos) { + this.map = map; + this.pos = pos; + } + + public void unbindMap() { + map = null; + pos = null; + } + + public BaseTileMap getMap() { + return map; + } + + public ICWorkbenchEditor getEditor() { + return map.getEditor(); + } + + public TileCoord getPos() { + return pos; + } + + public ICTileType getTileType() { + return tileType; + } + + public abstract void save(CompoundTag tag); + + public abstract void load(CompoundTag tag); + + public abstract void writeDesc(MCDataOutput out); + + public abstract void readDesc(MCDataInput in); + + public MCDataOutput getWriteStream(int key) { + return getEditor().getTileStream(this, key); + } + + public void read(MCDataInput in, int key) { + switch (key) { + case 0: + readDesc(in); + default: + // Unknown key + } + } + + public void sendDescUpdate() { + writeDesc(getWriteStream(0)); + } + + public void update() { } + + public void onAdded() { } + + public void onRemoved() { } + + public void onNeighborChanged() { } + + public void onSimRegistersChanged(int rMask, ICSimulationContainer container) { } + + public void onInteractionZoneClicked(int i) { } + + public void onInteractionZoneActivated(int i) { } + + @OnlyIn(Dist.CLIENT) + public void buildToolTip(List toolTip) { + toolTip.add(new TranslatableComponent(tileType.getUnlocalizedName())); + } + + @OnlyIn(Dist.CLIENT) + public void renderTile(CCRenderState ccrs, Transformation t, float partialFrame) { } + + @OnlyIn(Dist.CLIENT) + public List getInteractionZones() { + return Collections.emptyList(); + } + + @OnlyIn(Dist.CLIENT) + public void buildInteractionToolTip(List toolTip, int i) { } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/BaseTileMap.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/BaseTileMap.java new file mode 100644 index 000000000..c7a4443ee --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/BaseTileMap.java @@ -0,0 +1,221 @@ +package mrtjp.projectred.fabrication.engine; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.TileCoord; +import mrtjp.fengine.tiles.FETile; +import mrtjp.fengine.tiles.FETileMap; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.covers1624.quack.collection.FastStream; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import java.util.*; +import java.util.stream.Collectors; + +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.loadTileCoord; +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.saveTileCoord; + +public class BaseTileMap implements FETileMap { + + private static final TileCoord defaultMinBounds = new TileCoord(-8, 0, -8); + private static final TileCoord defaultMaxBounds = new TileCoord(7, 0, 7); + + private final Map tileMap = new HashMap<>(); + private final ICWorkbenchEditor editor; + + private final Set ioTiles = new HashSet<>(); + private final Map> layerToTileMap = new HashMap<>(); + + private TileCoord minBounds = defaultMinBounds; + private TileCoord maxBounds = defaultMaxBounds; + + public BaseTileMap(ICWorkbenchEditor editor) { + this.editor = editor; + } + + public ICWorkbenchEditor getEditor() { + return editor; + } + + public TileCoord getMinBounds() { + return minBounds; + } + + public TileCoord getMaxBounds() { + return maxBounds; + } + + public TileCoord getDimensions() { + return maxBounds.subtract(minBounds).add(1, 1, 1); + } + + public void setBounds(TileCoord minBounds, TileCoord maxBounds) { + this.minBounds = minBounds; + this.maxBounds = maxBounds; + } + + public boolean isInBounds(TileCoord coord) { + return coord.x >= minBounds.x && coord.x <= maxBounds.x && + coord.y >= minBounds.y && coord.y <= maxBounds.y && + coord.z >= minBounds.z && coord.z <= maxBounds.z; + } + + public boolean addTile(TileCoord coord, BaseTile tile) { + + if (!tileMap.containsKey(coord)) { + tileMap.put(coord, tile); + tile.bindMap(this, coord); + cacheType(coord, tile); + return true; + } + return false; + } + + public Optional removeTile(TileCoord coord) { + + BaseTile removed = tileMap.remove(coord); + if (removed != null) { + removed.unbindMap(); + uncacheType(coord, removed); + } + return Optional.ofNullable(removed); + } + + public Optional getBaseTile(TileCoord coord) { + return Optional.ofNullable(tileMap.get(coord)); + } + + public int getTileCount() { + return tileMap.size(); + } + + public Collection> getBaseTileEntries() { + return tileMap.entrySet(); + } + + public Collection> getTilesOnLayer(int y) { + return layerToTileMap.computeIfAbsent(y, k -> new HashMap<>()).entrySet(); + } + + public Collection getIOTiles() { + return ioTiles; + } + + public void removeAll() { + ioTiles.clear(); + + for (BaseTile t : tileMap.values()) t.unbindMap(); + tileMap.clear(); + + minBounds = defaultMinBounds; + maxBounds = defaultMaxBounds; + } + + private void cacheType(TileCoord coord, BaseTile tile) { + // Type caching + if (tile instanceof IIOConnectionTile) ioTiles.add((IIOConnectionTile) tile); + + // Layer caching + layerToTileMap.computeIfAbsent(coord.y, k -> new HashMap<>()).put(coord, tile); + } + + private void uncacheType(TileCoord coord, BaseTile tile) { + // Type caching + if (tile instanceof IIOConnectionTile) ioTiles.remove(tile); + + // Layer caching + layerToTileMap.get(coord.y).remove(coord); + } + + public InterfaceSpec getInterfaceSpec() { + InterfaceSpec spec = new InterfaceSpec(); + spec.setFromIOTiles(ioTiles); + return spec; + } + + public void save(CompoundTag tag) { + + ListTag tileList = new ListTag(); + for (Map.Entry entry : tileMap.entrySet()) { + CompoundTag tileTag = new CompoundTag(); + tileTag.putByte("_id", (byte) entry.getValue().getTileType().getID()); + tileTag.putByte("_x", (byte) entry.getKey().x); + tileTag.putByte("_y", (byte) entry.getKey().y); + tileTag.putByte("_z", (byte) entry.getKey().z); + entry.getValue().save(tileTag); + tileList.add(tileTag); + } + tag.put("tiles", tileList); + + saveTileCoord(tag, "maxBounds", maxBounds); + saveTileCoord(tag, "minBounds", minBounds); + } + + public void load(CompoundTag tag) { + removeAll(); + + ListTag tileList = tag.getList("tiles", 10); + for (int i = 0; i < tileList.size(); i++) { + CompoundTag tileTag = tileList.getCompound(i); + int id = tileTag.getByte("_id") & 0xFF; + int x = tileTag.getByte("_x"); + int y = tileTag.getByte("_y"); + int z = tileTag.getByte("_z"); + BaseTile tile = ICTileType.createFromId(id); + addTile(new TileCoord(x, y, z), tile); + tile.load(tileTag); + } + + maxBounds = loadTileCoord(tag, "maxBounds"); + minBounds = loadTileCoord(tag, "minBounds"); + } + + public void writeDesc(MCDataOutput out) { + + out.writeByte(minBounds.x).writeByte(minBounds.y).writeByte(minBounds.z); + out.writeByte(maxBounds.x).writeByte(maxBounds.y).writeByte(maxBounds.z); + + for (Map.Entry entry : tileMap.entrySet()) { + out.writeByte(entry.getValue().getTileType().getID()); + out.writeByte(entry.getKey().x); + out.writeByte(entry.getKey().y); + out.writeByte(entry.getKey().z); + entry.getValue().writeDesc(out); + } + out.writeByte(255); + } + + public void readDesc(MCDataInput in) { + removeAll(); + + minBounds = new TileCoord(in.readByte(), in.readByte(), in.readByte()); + maxBounds = new TileCoord(in.readByte(), in.readByte(), in.readByte()); + + int id = in.readUByte(); + while (id != 255) { + BaseTile tile = ICTileType.createFromId(id); + TileCoord coord = new TileCoord(in.readByte(), in.readByte(), in.readByte()); + addTile(coord, tile); + tile.readDesc(in); + + id = in.readUByte(); + } + } + + @Override + public Optional getTile(TileCoord coord) { + return Optional.ofNullable(tileMap.get(coord)); + } + + @Override + public Collection getEntries() { + + return FastStream.of(tileMap.entrySet()).map(e -> (TileMapEntry) new TileMapEntry() { + //@formatter:off + @Override public TileCoord getCoord() { return e.getKey(); } + @Override public FETile getTile() { return e.getValue(); } + //@formatter:on + }).toImmutableList(); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IBundledConnectableICTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IBundledConnectableICTile.java new file mode 100644 index 000000000..513e1f4dd --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IBundledConnectableICTile.java @@ -0,0 +1,5 @@ +package mrtjp.projectred.fabrication.engine; + +public interface IBundledConnectableICTile { + int getBundledColour(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICInterfaceType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICInterfaceType.java new file mode 100644 index 000000000..c6e16ecbb --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICInterfaceType.java @@ -0,0 +1,28 @@ +package mrtjp.projectred.fabrication.engine; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public enum ICInterfaceType { + NC(UL_INTERFACE_NC), + REDSTONE(UL_INTERFACE_REDSTONE), + BUNDLED(UL_INTERFACE_BUNDLED), + ; + + private final String unlocalName; + + ICInterfaceType(String unlocalName) { + this.unlocalName = unlocalName; + } + + public int getId() { + return ordinal(); + } + + public String getUnlocalName() { + return unlocalName; + } + + public static ICInterfaceType fromId(int id) { + return values()[id]; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICSimulationContainer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICSimulationContainer.java new file mode 100644 index 000000000..004ab2f80 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICSimulationContainer.java @@ -0,0 +1,186 @@ +package mrtjp.projectred.fabrication.engine; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.api.ICFlatMap; +import mrtjp.fengine.simulate.ICSimulation; +import net.minecraft.nbt.CompoundTag; + +public class ICSimulationContainer { + + private ICSimulation simulation = PRFabricationEngine.EMPTY_SIMULATION; + + /** + * Inputs to the simulation indexed by rotation (0-3). + *

+ * Each input is a mask of 16 bits, which represents one of the + * 16 bundled colours inside the tile map. + */ + private final short[] inputs = new short[] { 0, 0, 0, 0 }; + + /** + * Outputs from the simulation indexed by rotation (0-3). + */ + private final short[] outputs = new short[] { 0, 0, 0, 0 }; + + private long systemTime = 0L; + + public void setInput(int rotation, short mask) { + inputs[rotation] = mask; + } + + public int setInputs(short[] inputs) { + + int changeMask = 0; + for (int r = 0; r < 4; r++) { + if (this.inputs[r] != inputs[r]) { + this.inputs[r] = inputs[r]; + changeMask |= 1 << r; + } + } + + return changeMask; + } + + public short getInput(int rotation) { + return inputs[rotation]; + } + + public void setSystemTime(long systemTime) { + this.systemTime = systemTime; + } + + public void progressTime(long time) { + systemTime += time; + } + + public short getOutput(int rotation) { + return outputs[rotation]; + } + + public void setFlatMap(ICFlatMap flatMap) { + this.simulation = new ICSimulation(flatMap); + + pushInputs(0xF); + pushTime(); + + simulation.computeAll(null); + } + + public void save(CompoundTag tag) { + tag.putString("simulation", PRFabricationEngine.instance.serializeSimulation(simulation)); + for (int i = 0; i < 4; i++) { + tag.putShort("in" + i, inputs[i]); + tag.putShort("out" + i, outputs[i]); + } + tag.putLong("systemTime", systemTime); + } + + public void load(CompoundTag tag) { + ICSimulation simulation = PRFabricationEngine.instance.deserializeSimulation(tag.getString("simulation")); + if (simulation != null) { + this.simulation = simulation; + } + for (int i = 0; i < 4; i++) { + inputs[i] = tag.getShort("in" + i); + outputs[i] = tag.getShort("out" + i); + } + systemTime = tag.getLong("systemTime"); + } + + public void writeDesc(MCDataOutput out) { + //TODO write simulation log + } + + public void readDesc(MCDataInput in) { + // TODO read simulation log + } + + public void pushInputs(int rmask) { + + for (int r = 0; r < 4; r++) { + if ((rmask & (1 << r)) != 0) { + for (int i = 0; i < 16; i++) { + int regId = PRFabricationEngine.inputRegisterId(r, i); + byte value = (inputs[r] & (1 << i)) != 0 ? (byte) 1 : 0; + simulation.queueRegByteVal(regId, value); + } + } + } + } + + public void pullInputs(int rmask) { + + for (int r = 0; r < 4; r++) { + if ((rmask & (1 << r)) != 0) { + inputs[r] = 0; + for (int i = 0; i < 16; i++) { + int regId = PRFabricationEngine.inputRegisterId(r, i); + if (simulation.getRegByteVal(regId) != 0) inputs[r] |= (1 << i); + } + } + } + } + + public void pushTime() { + + simulation.queueRegByteVal(PRFabricationEngine.REG_TIME_3, (byte) (systemTime >>> 24)); + simulation.queueRegByteVal(PRFabricationEngine.REG_TIME_2, (byte) (systemTime >>> 16)); + simulation.queueRegByteVal(PRFabricationEngine.REG_TIME_1, (byte) (systemTime >>> 8)); + simulation.queueRegByteVal(PRFabricationEngine.REG_TIME_0, (byte) (systemTime)); + } + + public void pullTime() { + + systemTime = 0; + systemTime |= (long) (simulation.getRegByteVal(PRFabricationEngine.REG_TIME_3) & 0xFF) << 24; + systemTime |= (simulation.getRegByteVal(PRFabricationEngine.REG_TIME_2) & 0xFF) << 16; + systemTime |= (simulation.getRegByteVal(PRFabricationEngine.REG_TIME_1) & 0xFF) << 8; + systemTime |= (simulation.getRegByteVal(PRFabricationEngine.REG_TIME_0) & 0xFF); + } + + public int pullOutputs() { + + short[] newOutputs = new short[4]; + for (int r = 0; r < 4; r++) { + for (int i = 0; i < 16; i++) { + int regId = PRFabricationEngine.outputRegisterId(r, i); + int value = simulation.getRegByteVal(regId) != 0 ? 1 : 0; + newOutputs[r] |= value << i; + } + } + + int changeMask = 0; + for (int r = 0; r < 4; r++) { + if (outputs[r] != newOutputs[r]) { + outputs[r] = newOutputs[r]; + changeMask |= (1 << r); + } + } + return changeMask; + } + + public int outputMask() { + int mask = 0; + for (int r = 0; r < 4; r++) { + if (outputs[r] != 0) mask |= 1 << r; + } + return mask; + } + + public int inputMask() { + int mask = 0; + for (int r = 0; r < 4; r++) { + if (inputs[r] != 0) mask |= 1 << r; + } + return mask; + } + + public byte pullRegisterValue(int regId) { + return simulation.getRegByteVal(regId); + } + + public void simulate() { + simulation.propagate(null); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICTileType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICTileType.java new file mode 100644 index 000000000..391446ac7 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICTileType.java @@ -0,0 +1,117 @@ +package mrtjp.projectred.fabrication.engine; + +import mrtjp.projectred.fabrication.engine.gates.*; +import mrtjp.projectred.fabrication.engine.wires.BundledWireTile; +import mrtjp.projectred.fabrication.engine.wires.InsulatedWireTile; +import mrtjp.projectred.fabrication.engine.wires.RedAlloyWireTile; +import mrtjp.projectred.integration.GateType; +import mrtjp.projectred.transmission.WireType; + +import java.util.function.Supplier; + +import static mrtjp.projectred.fabrication.engine.ICTileTypeOffsets.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_IO_GATE_TILE; +import static mrtjp.projectred.integration.GateType.*; +import static mrtjp.projectred.transmission.WireType.*; + +public enum ICTileType { + + //@formatter:off + IO_GATE(ID_OFFSET_IOGATE, UL_IO_GATE_TILE, IOGateTile::new), + + OR_GATE (ID_OFFSET_GATE, OR, ORGateTile::new), + NOR_GATE (ID_OFFSET_GATE + 1, NOR, NORGateTile::new), + NOT_GATE (ID_OFFSET_GATE + 2, NOT, NOTGateTile::new), + AND_GATE (ID_OFFSET_GATE + 3, AND, ANDGateTile::new), + NAND_GATE (ID_OFFSET_GATE + 4, NAND, NANDGateTile::new), + XOR_GATE (ID_OFFSET_GATE + 5, XOR, XORGateTile::new), + XNOR_GATE (ID_OFFSET_GATE + 6, XNOR, XNORGateTile::new), + BUFFER_GATE (ID_OFFSET_GATE + 7, BUFFER, BufferGateTile::new), + MULTIPLEXER_GATE (ID_OFFSET_GATE + 8, MULTIPLEXER, MultiplexerGateTile::new), + PULSE_GATE (ID_OFFSET_GATE + 9, PULSE, PulseGateTile::new), + REPEATER_GATE (ID_OFFSET_GATE + 10, REPEATER, RepeaterGateTile::new), + RANDOMIZER_GATE (ID_OFFSET_GATE + 11, RANDOMIZER, RandomizerGateTile::new), + SR_LATCH_GATE (ID_OFFSET_GATE + 12, SR_LATCH, SRLatchGateTile::new), + TOGGLE_LATCH_GATE (ID_OFFSET_GATE + 13, TOGGLE_LATCH, ToggleLatchGateTile::new), + TRANSPARENT_LATCH_GATE(ID_OFFSET_GATE + 14, TRANSPARENT_LATCH, TransparentLatchGateTile::new), + + RED_ALLOY_WIRE (ID_OFFSET_WIRE_RED, RED_ALLOY, RedAlloyWireTile::new), + INSULATED_WHITE_WIRE (ID_OFFSET_WIRE_RED + 1, INSULATED_WHITE, () -> new InsulatedWireTile(0)), + INSULATED_ORANGE_WIRE (ID_OFFSET_WIRE_RED + 2, INSULATED_ORANGE, () -> new InsulatedWireTile(1)), + INSULATED_MAGENTA_WIRE (ID_OFFSET_WIRE_RED + 3, INSULATED_MAGENTA, () -> new InsulatedWireTile(2)), + INSULATED_LIGHT_BLUE_WIRE(ID_OFFSET_WIRE_RED + 4, INSULATED_BLUE, () -> new InsulatedWireTile(3)), + INSULATED_YELLOW_WIRE (ID_OFFSET_WIRE_RED + 5, INSULATED_YELLOW, () -> new InsulatedWireTile(4)), + INSULATED_LIME_WIRE (ID_OFFSET_WIRE_RED + 6, INSULATED_LIME, () -> new InsulatedWireTile(5)), + INSULATED_PINK_WIRE (ID_OFFSET_WIRE_RED + 7, INSULATED_PINK, () -> new InsulatedWireTile(6)), + INSULATED_GRAY_WIRE (ID_OFFSET_WIRE_RED + 8, INSULATED_GRAY, () -> new InsulatedWireTile(7)), + INSULATED_LIGHT_GRAY_WIRE(ID_OFFSET_WIRE_RED + 9, INSULATED_LIGHT_GRAY, () -> new InsulatedWireTile(8)), + INSULATED_CYAN_WIRE (ID_OFFSET_WIRE_RED + 10, INSULATED_CYAN, () -> new InsulatedWireTile(9)), + INSULATED_PURPLE_WIRE (ID_OFFSET_WIRE_RED + 11, INSULATED_PURPLE, () -> new InsulatedWireTile(10)), + INSULATED_BLUE_WIRE (ID_OFFSET_WIRE_RED + 12, INSULATED_BLUE, () -> new InsulatedWireTile(11)), + INSULATED_BROWN_WIRE (ID_OFFSET_WIRE_RED + 13, INSULATED_BROWN, () -> new InsulatedWireTile(12)), + INSULATED_GREEN_WIRE (ID_OFFSET_WIRE_RED + 14, INSULATED_GREEN, () -> new InsulatedWireTile(13)), + INSULATED_RED_WIRE (ID_OFFSET_WIRE_RED + 15, INSULATED_RED, () -> new InsulatedWireTile(14)), + INSULATED_BLACK_WIRE (ID_OFFSET_WIRE_RED + 16, INSULATED_BLACK, () -> new InsulatedWireTile(15)), + + BUNDLED_NEUTRAL_WIRE (ID_OFFSET_WIRE_BUNDLED, BUNDLED_NEUTRAL, () -> new BundledWireTile(-1)), + BUNDLED_WHITE_WIRE (ID_OFFSET_WIRE_BUNDLED + 1, BUNDLED_WHITE, () -> new BundledWireTile(0)), + BUNDLED_ORANGE_WIRE (ID_OFFSET_WIRE_BUNDLED + 2, BUNDLED_ORANGE, () -> new BundledWireTile(1)), + BUNDLED_MAGENTA_WIRE (ID_OFFSET_WIRE_BUNDLED + 3, BUNDLED_MAGENTA, () -> new BundledWireTile(2)), + BUNDLED_LIGHT_BLUE_WIRE(ID_OFFSET_WIRE_BUNDLED + 4, BUNDLED_LIGHT_BLUE, () -> new BundledWireTile(3)), + BUNDLED_YELLOW_WIRE (ID_OFFSET_WIRE_BUNDLED + 5, BUNDLED_YELLOW, () -> new BundledWireTile(4)), + BUNDLED_LIME_WIRE (ID_OFFSET_WIRE_BUNDLED + 6, BUNDLED_LIME, () -> new BundledWireTile(5)), + BUNDLED_PINK_WIRE (ID_OFFSET_WIRE_BUNDLED + 7, BUNDLED_PINK, () -> new BundledWireTile(6)), + BUNDLED_GRAY_WIRE (ID_OFFSET_WIRE_BUNDLED + 8, BUNDLED_GRAY, () -> new BundledWireTile(7)), + BUNDLED_LIGHT_GRAY_WIRE(ID_OFFSET_WIRE_BUNDLED + 9, BUNDLED_LIGHT_GRAY, () -> new BundledWireTile(8)), + BUNDLED_CYAN_WIRE (ID_OFFSET_WIRE_BUNDLED + 10, BUNDLED_CYAN, () -> new BundledWireTile(9)), + BUNDLED_PURPLE_WIRE (ID_OFFSET_WIRE_BUNDLED + 11, BUNDLED_PURPLE, () -> new BundledWireTile(10)), + BUNDLED_BLUE_WIRE (ID_OFFSET_WIRE_BUNDLED + 12, BUNDLED_BLUE, () -> new BundledWireTile(11)), + BUNDLED_BROWN_WIRE (ID_OFFSET_WIRE_BUNDLED + 13, BUNDLED_BROWN, () -> new BundledWireTile(12)), + BUNDLED_GREEN_WIRE (ID_OFFSET_WIRE_BUNDLED + 14, BUNDLED_GREEN, () -> new BundledWireTile(13)), + BUNDLED_RED_WIRE (ID_OFFSET_WIRE_BUNDLED + 15, BUNDLED_RED, () -> new BundledWireTile(14)), + BUNDLED_BLACK_WIRE (ID_OFFSET_WIRE_BUNDLED + 16, BUNDLED_BLACK, () -> new BundledWireTile(15)); + //@formatter:on + + private static final ICTileType[] VALUES_BY_ID; + + static { + VALUES_BY_ID = new ICTileType[ID_OFFSET_MAX + 1]; + for (ICTileType type : ICTileType.values()) { + VALUES_BY_ID[type.getID()] = type; + } + } + + private final int id; + private final String unlocalizedName; + private final Supplier factory; + + ICTileType(int id, String unlocalizedName, Supplier factory) { + this.id = id; + this.unlocalizedName = unlocalizedName; + this.factory = factory; + } + + ICTileType(int id, GateType gateType, Supplier factory) { + this(id, gateType.getItem().getDescriptionId(), factory); + } + + ICTileType(int id, WireType wireType, Supplier factory) { + this(id, wireType.getItem().getDescriptionId(), factory); + } + + public String getUnlocalizedName() { + return unlocalizedName; + } + + public BaseTile create() { + return factory.get(); + } + + public int getID() { + return id; + } + + public static BaseTile createFromId(int id) { + return VALUES_BY_ID[id].create(); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICTileTypeOffsets.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICTileTypeOffsets.java new file mode 100644 index 000000000..8753a60c0 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/ICTileTypeOffsets.java @@ -0,0 +1,15 @@ +package mrtjp.projectred.fabrication.engine; + +public class ICTileTypeOffsets { + public static final int MAX_IO_GATES = 16; + public static final int MAX_GATES = 64; + public static final int MAX_REDSTONE_WIRES = 20; + public static final int MAX_BUNDLED_WIRES = 20; + + public static final int ID_OFFSET_IOGATE = 0; + public static final int ID_OFFSET_GATE = ID_OFFSET_IOGATE + MAX_IO_GATES; + public static final int ID_OFFSET_WIRE_RED = ID_OFFSET_GATE + MAX_GATES; + public static final int ID_OFFSET_WIRE_BUNDLED = ID_OFFSET_WIRE_RED + MAX_REDSTONE_WIRES; + + public static final int ID_OFFSET_MAX = ID_OFFSET_WIRE_BUNDLED + MAX_BUNDLED_WIRES; +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IConnectableICTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IConnectableICTile.java new file mode 100644 index 000000000..94d5c2bf3 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IConnectableICTile.java @@ -0,0 +1,54 @@ +package mrtjp.projectred.fabrication.engine; + +public interface IConnectableICTile { + + int getConnMask(); + + void setConnMask(int connMask); + + IConnectableICTile getTileTowardsDir(int dir); + + default int dirFrom(int dir) { + return dir^1; + } + + default boolean maskConnectsToDir(int dir) { + return (getConnMask() & 1 << dir) != 0; + } + + default boolean updateConns() { + int newConn = 0; + for (int s = 0; s < 6; s++) { + if (discoverAndConnect(s)) newConn |= 1 << s; + } + if (newConn != getConnMask()) { + setConnMask(newConn); + onMaskChanged(); + return true; + } + return false; + } + + default boolean discoverAndConnect(int dir) { + IConnectableICTile tileTowardsDir = getTileTowardsDir(dir); + if (tileTowardsDir == null) return false; + + return canConnectTo(tileTowardsDir, dir) && tileTowardsDir.connect(this, dirFrom(dir)); + } + + default boolean connect(IConnectableICTile target, int towardsDir) { + if (canConnectTo(target, towardsDir)) { + int newConn = getConnMask() | 1 << towardsDir; + if (newConn != getConnMask()) { + setConnMask(newConn); + onMaskChanged(); + } + return true; + } + return false; + } + + boolean canConnectTo(IConnectableICTile target, int towardsDir); + + void onMaskChanged(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IIOConnectionTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IIOConnectionTile.java new file mode 100644 index 000000000..3f72e4d60 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IIOConnectionTile.java @@ -0,0 +1,19 @@ +package mrtjp.projectred.fabrication.engine; + +public interface IIOConnectionTile { + + /** + * @return True if this tile is an input + */ + boolean isInputIOMode(); + + /** + * @return Rotation index (0-3) representing the edge through which this IO signal is passing + */ + int getIOSide(); + + /** + * @return The edge interface type for this IO connection tile. + */ + ICInterfaceType getInterfaceType(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IInsulatedConnectableICTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IInsulatedConnectableICTile.java new file mode 100644 index 000000000..6765e4c70 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IInsulatedConnectableICTile.java @@ -0,0 +1,5 @@ +package mrtjp.projectred.fabrication.engine; + +public interface IInsulatedConnectableICTile { + int getInsulatedColour(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IRedstoneConnectableICTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IRedstoneConnectableICTile.java new file mode 100644 index 000000000..05abfd701 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IRedstoneConnectableICTile.java @@ -0,0 +1,4 @@ +package mrtjp.projectred.fabrication.engine; + +public interface IRedstoneConnectableICTile { +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IRotatableICTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IRotatableICTile.java new file mode 100644 index 000000000..35a74aede --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/IRotatableICTile.java @@ -0,0 +1,63 @@ +package mrtjp.projectred.fabrication.engine; + +public interface IRotatableICTile { + + int getRotation(); + + void setRotation(int r); + + default int toInternalRotation(int absRot) { + return (absRot + 6 - getRotation()) % 4; + } + + default int toAbsoluteRotation(int r) { + return (r + 2 + getRotation()) % 4; + } + + default int toInternalMask(int absMask) { + return shiftMask(absMask, toInternalRotation(0)); + } + + default int toAbsoluteMask(int intMask) { + return shiftMask(intMask, toAbsoluteRotation(0)); + } + + //@formatter:off + static int rotationToDir(int r) { + switch (r) { + case 0: return 3; + case 1: return 4; + case 2: return 2; + case 3: return 5; + default: return -1; + } + } + + static int dirToRotation(int dir) { + switch (dir) { + case 2: return 2; + case 3: return 0; + case 4: return 1; + case 5: return 3; + default: return -1; + } + } + //@formatter:on + + static int dirMaskToRotationMask(int connMask) { + int rMask = 0; + for (int r = 0; r < 4; r++) { + int s = rotationToDir(r); + if ((connMask & 1 << s) != 0) rMask |= 1 << r; + } + return rMask; + } + + static int shiftMask(int mask, int r) { + return (mask & ~0xF) | (mask << r | mask >> 4-r) & 0xF; + } + + static int flipMaskZ(int mask) { + return mask & 5 | mask << 2 & 8 | mask >> 2 & 2; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/InterfaceSpec.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/InterfaceSpec.java new file mode 100644 index 000000000..210ac69ad --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/InterfaceSpec.java @@ -0,0 +1,130 @@ +package mrtjp.projectred.fabrication.engine; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import net.minecraft.nbt.CompoundTag; + +import java.util.Arrays; +import java.util.Set; + +public class InterfaceSpec { + + private final ICInterfaceType[] sideInterfaces = new ICInterfaceType[4]; + private int redstoneMask = 0; // OOOO IIII + private int bundledMask = 0; // OOOO IIII + + { + Arrays.fill(sideInterfaces, ICInterfaceType.NC); + } + + //region Save/load + public void save(CompoundTag tag) { + for (int r = 0; r < 4; r++) { + tag.putByte("if" + r, (byte) sideInterfaces[r].getId()); + } + tag.putByte("rmask", (byte) redstoneMask); + tag.putByte("bmask", (byte) bundledMask); + } + + public void load(CompoundTag tag) { + for (int r = 0; r < 4; r++) { + sideInterfaces[r] = ICInterfaceType.fromId(tag.getByte("if" + r) & 0xFF); + } + redstoneMask = tag.getByte("rmask") & 0xFF; + bundledMask = tag.getByte("bmask") & 0xFF; + } + + public void writeDesc(MCDataOutput packet) { + for (int r = 0; r < 4; r++) { + packet.writeByte(sideInterfaces[r].getId()); + } + packet.writeByte(bundledMask); + packet.writeByte(redstoneMask); + } + + public void readDesc(MCDataInput packet) { + for (int r = 0; r < 4; r++) { + sideInterfaces[r] = ICInterfaceType.fromId(packet.readByte() & 0xFF); + } + bundledMask = packet.readByte(); + redstoneMask = packet.readByte(); + } + //endregion + + public void setFromIOTiles(Set ioTiles) { + + Arrays.fill(sideInterfaces, ICInterfaceType.NC); + bundledMask = 0; + redstoneMask = 0; + + for (IIOConnectionTile t : ioTiles) { + int side = t.getIOSide(); + ICInterfaceType type = t.getInterfaceType(); + int dir = t.isInputIOMode() ? 0x1 : 0x10; + + sideInterfaces[side] = type; + + switch (type) { + case REDSTONE -> redstoneMask |= dir << side; + case BUNDLED -> bundledMask |= dir << side; + } + } + } + + public ICInterfaceType getInterfaceType(int side) { + return sideInterfaces[side]; + } + + public int getInputMask() { + return (redstoneMask | bundledMask) & 0xF; + } + + public int getOutputMask() { + return (redstoneMask | bundledMask) >> 4 & 0xF; + } + + public boolean isInput(int side) { + return ((redstoneMask | bundledMask) & 1 << side) != 0; + } + + public boolean isOutput(int side) { + return ((redstoneMask | bundledMask) & 0x10 << side) != 0; + } + + public int getRedstoneInputMask() { + return redstoneMask & 0xF; + } + + public int getRedstoneOutputMask() { + return redstoneMask >> 4 & 0xF; + } + + public int getBundledInputMask() { + return bundledMask & 0xF; + } + + public int getBundledOutputMask() { + return bundledMask >> 4 & 0xF; + } + + //region Utilities + public void saveTo(CompoundTag tag, String key) { + CompoundTag tag1 = new CompoundTag(); + save(tag1); + tag.put(key, tag1); + } + + public void loadFrom(CompoundTag tag, String key) { + CompoundTag tag1 = tag.getCompound(key); + load(tag1); + } + + public static InterfaceSpec createFrom(CompoundTag tag, String key) { + CompoundTag tag1 = tag.getCompound(key); + InterfaceSpec spec = new InterfaceSpec(); + spec.load(tag1); + return spec; + } + + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/PRFabricationEngine.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/PRFabricationEngine.java new file mode 100644 index 000000000..0c820e352 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/PRFabricationEngine.java @@ -0,0 +1,102 @@ +package mrtjp.projectred.fabrication.engine; + +import mrtjp.fengine.api.FabricationEngine; +import mrtjp.fengine.api.ICAssembler; +import mrtjp.fengine.api.ICFlatMap; +import mrtjp.fengine.api.ICStepThroughAssembler; +import mrtjp.fengine.simulate.ByteRegister; +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; +import mrtjp.fengine.simulate.StaticByteRegister; +import mrtjp.projectred.fabrication.engine.gates.*; + +import java.util.HashMap; +import java.util.Map; + +public class PRFabricationEngine extends FabricationEngine { + + // Static register allocations + public static final int REG_IN_BASE = 0; + public static final int REG_OUT_BASE = 64; + + public static final int REG_TIME_3 = 128; // Upper 8 bits of time + public static final int REG_TIME_2 = 129; + public static final int REG_TIME_1 = 130; + public static final int REG_TIME_0 = 131; // Lower 8 bits of time + + public static final int REG_ZERO = 132; + public static final int REG_ONE = 133; + + public static final PRFabricationEngine instance = new PRFabricationEngine(); + + public static final ICFlatMap EMPTY_FLAT_MAP = instance.newAssembler().result(); + public static final ICSimulation EMPTY_SIMULATION = new ICSimulation(EMPTY_FLAT_MAP); + + public static final String EMPTY_FLAT_MAP_SERIALIZED = instance.serializeFlatMap(EMPTY_FLAT_MAP); + public static final String EMPTY_SIMULATION_SERIALIZED = instance.serializeSimulation(EMPTY_SIMULATION); + + public static int inputRegisterId(int r, int i) { + return REG_IN_BASE + r * 16 + i; + } + + public static int outputRegisterId(int r, int i) { + return REG_OUT_BASE + r * 16 + i; + } + + @Override + public ICAssembler newAssembler() { + ICAssembler assembler = super.newAssembler(); + addStaticRegisters(assembler); + return assembler; + } + + @Override + public ICStepThroughAssembler newStepThroughAssembler() { + ICStepThroughAssembler assembler = super.newStepThroughAssembler(); + addStaticRegisters(assembler); + return assembler; + } + + private void addStaticRegisters(ICAssembler assembler) { + // Add Static registers + for (int i = 0; i < 16; i++) { + for (int r = 0; r < 4; r++) { + assembler.addRegister(inputRegisterId(r, i), new ByteRegister()); + assembler.addRegister(outputRegisterId(r, i), new ByteRegister()); + } + } + + // Add time registers + assembler.addRegister(REG_TIME_3, new ByteRegister()); + assembler.addRegister(REG_TIME_2, new ByteRegister()); + assembler.addRegister(REG_TIME_1, new ByteRegister()); + assembler.addRegister(REG_TIME_0, new ByteRegister()); + + // Add zero and one registers + assembler.addRegister(REG_ZERO, new StaticByteRegister((byte) 0)); + assembler.addRegister(REG_ONE, new StaticByteRegister((byte) 1)); + } + + @Override + public Map, String> getGateSerializationMap() { + Map, String> map = new HashMap<>(); + + map.put(ORGateTile.ORGate.class, "pr_or"); //TODO create an enum to keep track of these. Label must be permanent to avoid breaking saves. + map.put(NORGateTile.NORGate.class, "pr_nor"); + map.put(NOTGateTile.NOTGate.class, "pr_not"); + map.put(ANDGateTile.ANDGate.class, "pr_and"); + map.put(NANDGateTile.NANDGate.class, "pr_nand"); + map.put(XORGateTile.XORGate.class, "pr_xor"); + map.put(XNORGateTile.XNORGate.class, "pr_xnor"); + map.put(BufferGateTile.BufferGate.class, "pr_buff"); + map.put(MultiplexerGateTile.MultiplexerGate.class, "pr_mlpx"); + map.put(PulseGateTile.PulseGate.class, "pr_pulse"); + map.put(RepeaterGateTile.RepeaterGate.class, "pr_repeater"); + map.put(RandomizerGateTile.RandomizerGate.class, "pr_rand"); + map.put(SRLatchGateTile.SRLatchGate.class, "pr_srlatch"); + map.put(ToggleLatchGateTile.ToggleLatchGate.class, "pr_tglatch"); + map.put(TransparentLatchGateTile.TransparentLatchGate.class, "pr_trlatch"); + + return map; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ANDGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ANDGateTile.java new file mode 100644 index 000000000..a0035e581 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ANDGateTile.java @@ -0,0 +1,45 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class ANDGateTile extends SimpleGateTile { + + public ANDGateTile() { + super(ICGateTileType.AND); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return ~getShape() << 1 & 0xE; + } + + @Override + protected int getDeadSides() { + return 3; + } + + @Override + public ICGate createGate() { + return new ANDGate(); + } + + public static class ANDGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + for (int i = 0; i < inputs.length; i++) { + if (ic.getRegByteVal(inputs[i]) == 0) { + ic.queueRegByteVal(outputs[0], (byte) 0); + return; + } + } + ic.queueRegByteVal(outputs[0], (byte) 1); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/BufferGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/BufferGateTile.java new file mode 100644 index 000000000..78da9fc63 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/BufferGateTile.java @@ -0,0 +1,45 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class BufferGateTile extends SimpleGateTile { + + public BufferGateTile() { + super(ICGateTileType.BUFFER); + } + + @Override + protected int redstoneOutputMask() { + return ~((getShape() & 1) << 1 | (getShape() & 2) << 2) & 0xB; + } + + @Override + protected int redstoneInputMask() { + return 4; + } + + @Override + protected int getDeadSides() { + return 2; + } + + @Override + protected int getMaxDeadSides() { + return 2; + } + + @Override + public ICGate createGate() { + return new BufferGate(); + } + + public static class BufferGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + for (int i = 0; i < outputs.length; i++) + ic.queueRegByteVal(outputs[i], ic.getRegByteVal(inputs[0]) != 0 ? (byte) 1 : (byte) 0); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/GateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/GateTile.java new file mode 100644 index 000000000..e0ea9dbf1 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/GateTile.java @@ -0,0 +1,218 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Transformation; +import mrtjp.projectred.fabrication.engine.BaseTile; +import mrtjp.projectred.fabrication.engine.IConnectableICTile; +import mrtjp.projectred.fabrication.engine.IRotatableICTile; +import mrtjp.projectred.integration.client.GateModelRenderer; +import mrtjp.projectred.integration.part.IGateRenderData; +import net.minecraft.nbt.CompoundTag; + +import java.util.Optional; + +public abstract class GateTile extends BaseTile implements IConnectableICTile, IRotatableICTile, IGateRenderData { + + public static final int PACKET_ROTATION = 1; + public static final int PACKET_SHAPE = 2; + public static final int PACKET_CLIENT_ROTATE_REQUEST = 3; + public static final int PACKET_CLIENT_CONFIGURE_REQUEST = 4; + + private byte connMask = 0; + private byte gateShape = 0; + private byte gateRotation = 0; + + private final ICGateTileType gateTileType; + + public GateTile(ICGateTileType gateTileType) { + super(gateTileType.tileType); + this.gateTileType = gateTileType; + } + + public int getShape() { + return gateShape & 0xFF; + } + + public void setShape(int shape) { + gateShape = (byte) shape; + } + + public void preparePlacement(int r) { + setRotation(r); + } + + @Override + public void save(CompoundTag tag) { + tag.putByte("orient", gateRotation); + tag.putByte("shape", gateShape); + tag.putByte("connMask", connMask); + } + + @Override + public void load(CompoundTag tag) { + gateRotation = tag.getByte("orient"); + gateShape = tag.getByte("shape"); + connMask = tag.getByte("connMask"); + } + + @Override + public void writeDesc(MCDataOutput out) { + out.writeByte(gateRotation); + out.writeByte(gateShape); + } + + @Override + public void readDesc(MCDataInput in) { + gateRotation = in.readByte(); + gateShape = in.readByte(); + } + + @Override + public void read(MCDataInput in, int key) { + switch (key) { + case PACKET_ROTATION: + gateRotation = in.readByte(); + break; + case PACKET_SHAPE: + gateShape = in.readByte(); + break; + case PACKET_CLIENT_ROTATE_REQUEST: + rotateAndSend(); + break; + case PACKET_CLIENT_CONFIGURE_REQUEST: + configureAndSend(); + break; + default: + super.read(in, key); + } + } + + protected void sendRotationUpdate() { + getWriteStream(PACKET_ROTATION).writeByte(gateRotation); + } + + protected void sendShapeUpdate() { + getWriteStream(PACKET_SHAPE).writeByte(gateShape); + } + + protected void rotateAndSend() { + setRotation((getRotation()+1)%4); + updateConns(); + sendRotationUpdate(); + notifyNeighbors(0xF); + getEditor().markTileChange(); + } + + protected void configureAndSend() { + if (cycleShape()) { + updateConns(); + sendShapeUpdate(); + notifyNeighbors(0xF); + getEditor().markTileChange(); + } + } + + @Override + public void onNeighborChanged() { + super.onNeighborChanged(); + if (updateConns()) + getEditor().markTileChange(); + } + + @Override + public void onAdded() { + super.onAdded(); + updateConns(); + onGatePlaced(); + } + + @Override + public void onRemoved() { + super.onRemoved(); + notifyNeighbors(0xF); + } + + @Override + public void renderTile(CCRenderState ccrs, Transformation t, float partialFrame) { + GateModelRenderer r = GateModelRenderer.instance(); + r.renderStatic(ccrs, this, t); + r.renderDynamic(ccrs, this, t, partialFrame); + } + + protected void notifyNeighbors(int rMask) { + int absRMask = toAbsoluteMask(rMask); + for (int r = 0; r < 4; r++) if ((absRMask & 1 << r) != 0) { + int absDir = IRotatableICTile.rotationToDir(r); + getEditor().queueNeighborChange(getPos().offset(absDir)); + } + } + + //region IRotatableICTile implementation + @Override + public int getRotation() { + return gateRotation & 0x3; + } + + @Override + public void setRotation(int r) { + gateRotation = (byte) (gateRotation & 0xFC | r); + } + //endregion + + //region IConnectableICTile implementation + @Override + public int getConnMask() { + return connMask & 0xFF; + } + + @Override + public void setConnMask(int connMask) { + this.connMask = (byte) connMask; + } + + @Override + public IConnectableICTile getTileTowardsDir(int dir) { + Optional tile = getMap().getBaseTile(getPos().offset(dir)); + if (tile.isPresent() && tile.get() instanceof IConnectableICTile) return (IConnectableICTile) tile.get(); + return null; + } + + @Override + public boolean canConnectTo(IConnectableICTile target, int towardsDir) { + return canGateConnectTo(target, toInternalRotation(IRotatableICTile.dirToRotation(towardsDir))); + } + + @Override + public void onMaskChanged() { + // Gate rendering does not require connMask, so don't send to clients + } + //endregion + + //region IGateRenderKey implementation + + @Override + public int getRenderIndex() { + return gateTileType.renderIndex; + } + + @Override + public int getOrientation() { + return getRotation() & 0x3; + } + + @Override + public int shape() { + return this.gateShape; + } + //endregion + + //region GateTile logic override points + protected void onGatePlaced() { } + + protected abstract boolean canGateConnectTo(IConnectableICTile target, int r); + + protected abstract boolean cycleShape(); + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ICGateTileType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ICGateTileType.java new file mode 100644 index 000000000..0f72ee247 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ICGateTileType.java @@ -0,0 +1,46 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.projectred.fabrication.editor.GatePlacementType; +import mrtjp.projectred.fabrication.engine.ICTileType; +import mrtjp.projectred.integration.GateType; +import mrtjp.projectred.integration.client.GateModelRenderer; + +import static mrtjp.projectred.fabrication.editor.GatePlacementType.INTERNAL; +import static mrtjp.projectred.fabrication.editor.GatePlacementType.IO_EDGE; + +public enum ICGateTileType { + IO(ICTileType.IO_GATE, GateModelRenderer.getNonPartRenderIndex(0), IO_EDGE), + + + OR(ICTileType.OR_GATE, GateModelRenderer.getRenderIndex(GateType.OR)), + NOR(ICTileType.NOR_GATE, GateModelRenderer.getRenderIndex(GateType.NOR)), + NOT(ICTileType.NOT_GATE, GateModelRenderer.getRenderIndex(GateType.NOT)), + AND(ICTileType.AND_GATE, GateModelRenderer.getRenderIndex(GateType.AND)), + NAND(ICTileType.NAND_GATE, GateModelRenderer.getRenderIndex(GateType.NAND)), + XOR(ICTileType.XOR_GATE, GateModelRenderer.getRenderIndex(GateType.XOR)), + XNOR(ICTileType.XNOR_GATE, GateModelRenderer.getRenderIndex(GateType.XNOR)), + BUFFER(ICTileType.BUFFER_GATE, GateModelRenderer.getRenderIndex(GateType.BUFFER)), + MULTIPLEXER(ICTileType.MULTIPLEXER_GATE, GateModelRenderer.getRenderIndex(GateType.MULTIPLEXER)), + PULSE(ICTileType.PULSE_GATE, GateModelRenderer.getRenderIndex(GateType.PULSE)), + REPEATER(ICTileType.REPEATER_GATE, GateModelRenderer.getRenderIndex(GateType.REPEATER)), + RANDOMIZER(ICTileType.RANDOMIZER_GATE, GateModelRenderer.getRenderIndex(GateType.RANDOMIZER)), + SR_LATCH(ICTileType.SR_LATCH_GATE, GateModelRenderer.getRenderIndex(GateType.SR_LATCH)), + TOGGLE_LATCH(ICTileType.TOGGLE_LATCH_GATE, GateModelRenderer.getRenderIndex(GateType.TOGGLE_LATCH)), + TRANSPARENT_LATCH(ICTileType.TRANSPARENT_LATCH_GATE, GateModelRenderer.getRenderIndex(GateType.TRANSPARENT_LATCH)); + + ; + + public final ICTileType tileType; + public final int renderIndex; + public final GatePlacementType placementType; + + ICGateTileType(ICTileType tileType, int renderIndex) { + this(tileType, renderIndex, INTERNAL); + } + + ICGateTileType(ICTileType tileType, int renderIndex, GatePlacementType placementType) { + this.tileType = tileType; + this.renderIndex = renderIndex; + this.placementType = placementType; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/IOGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/IOGateTile.java new file mode 100644 index 000000000..0b321e006 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/IOGateTile.java @@ -0,0 +1,273 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Rotation; +import codechicken.lib.vec.Transformation; +import codechicken.lib.vec.Vector3; +import mrtjp.fengine.api.IPathFinder; +import mrtjp.fengine.assemble.PathFinderResult; +import mrtjp.projectred.fabrication.engine.ICInterfaceType; +import mrtjp.projectred.fabrication.engine.ICSimulationContainer; +import mrtjp.projectred.fabrication.engine.IIOConnectionTile; +import mrtjp.projectred.fabrication.engine.IRotatableICTile; +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.engine.PRFabricationEngine.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class IOGateTile extends RedstoneGateTile implements IIOConnectionTile { + + public static final int COLOUR_PACKET = 6; + + private int regId = REG_ZERO; + private byte colour = 0; + + public IOGateTile() { + super(ICGateTileType.IO); + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + tag.putInt("reg", regId); + tag.putByte("colour", colour); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + regId = tag.getInt("reg"); + colour = tag.getByte("colour"); + } + + @Override + public void writeDesc(MCDataOutput out) { + super.writeDesc(out); + out.writeByte(colour); + } + + @Override + public void readDesc(MCDataInput in) { + super.readDesc(in); + colour = in.readByte(); + } + + @Override + public void read(MCDataInput in, int key) { + switch (key) { + case COLOUR_PACKET: + colour = in.readByte(); + break; + default: + super.read(in, key); + } + } + + protected void sendColourUpdate() { + getWriteStream(COLOUR_PACKET).writeByte(colour); + } + + @Override + public boolean isInputIOMode() { + return getShape() == 0; + } + + @Override + public int getIOSide() { + return getRotation(); + } + + @Override + public ICInterfaceType getInterfaceType() { + return ICInterfaceType.BUNDLED; + } + + protected void toggleWorldInput() { + LOGGER.info("Toggling world input"); + getEditor().getStateMachine().onInputRegistersChanged(getIOSide(), i -> (short) (i ^ (1< getInteractionZones() { + List zones = new LinkedList<>(); + + zones.add(new Cuboid6(1, 2, 0, 15, 3, 5)); // Toggle state of IO register + zones.add(new Cuboid6(2, 2, 5, 6, 3, 8)); // Toggle IO mode + zones.add(new Cuboid6(1, 2, 8.5, 5, 4, 12.5)); // Toggle colour + + Transformation rotation = Rotation.quarterRotations[getRotation()].at(new Vector3(8, 8, 8)); + zones.forEach(c -> c.apply(rotation)); + + return zones; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void buildInteractionToolTip(List toolTip, int i) { + + switch (i) { + case 0: + toolTip.add(new TranslatableComponent(UL_TOGGLE_STATE)); + toolTip.add(new TextComponent(((getState() & 0x44) != 0 ? "0x1" : "0x0")).withStyle(ChatFormatting.GRAY)); + break; + case 1: + toolTip.add(new TranslatableComponent(UL_TOGGLE_IO_MODE)); + toolTip.add(new TranslatableComponent((isInputIOMode() ? UL_IO_MODE_INPUT : UL_IO_MODE_OUTPUT)).withStyle(ChatFormatting.GRAY)); + break; + case 2: + toolTip.add(new TranslatableComponent(UL_TOGGLE_COLOUR)); + toolTip.add(new TranslatableComponent(EnumColour.values()[colour & 0xFF].getUnlocalizedName()).withStyle(ChatFormatting.GRAY)); + break; + default: + } + } + + @Override + public void onInteractionZoneClicked(int i) { + + switch (i) { + case 0: + toggleWorldInput(); + break; + case 1: + configureAndSend(); + break; + case 2: + incrementColour(); + break; + default: + } + } + + // IGateRenderKey overrides + + @Override + public int state2() { + return colour & 0xFF; + } + + // RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + setShape((getShape() + 1) % 2); + return true; + } + + @Override + protected int redstoneOutputMask() { + return isInputIOMode() ? 0x0 : 0x4; + } + + @Override + protected int redstoneInputMask() { + return isInputIOMode() ? 0x4 : 0x0; + } + + // BaseTile overrides + + @Override + public void onSimRegistersChanged(int rMask, ICSimulationContainer container) { + int oldState = getState(); + int newState = pullInputMask(container) & 0xF | pullOutputMask(container) << 4; + if (oldState != newState) { + setState(newState); + sendStateUpdate(); + } + } + + protected int pullInputMask(ICSimulationContainer container) { + return !isInputIOMode() && container.pullRegisterValue(regId) != 0 ? 0x4 : 0; + } + + protected int pullOutputMask(ICSimulationContainer container) { + return isInputIOMode() && container.pullRegisterValue(regId) != 0 ? 0x4 : 0; + } + + // FETile overrides + + @Override + public void allocate(Allocator allocator) { + if (isInputIOMode()) { // Input from world, output into simulation + regId = allocator.allocRegisterID(getStaticInputRegister(colour)); + } else { // Input from simulation, output into world + regId = REG_ZERO; // Will be located, then remapped to target static register + } + } + + @Override + public void locate(IPathFinder pathFinder) { + if (!isInputIOMode()) { + int absR = toAbsoluteRotation(2); + int absDir = IRotatableICTile.rotationToDir(absR); + PathFinderResult pfr = pathFinder.doPathFinding((d, p) -> d == absDir); + if (pfr.outputRegisters.size() > 1) { + // TODO log this somewhere + System.out.println("ERR: Unexpected multiple drivers: " + pfr.outputRegisters); + } + if (!pfr.outputRegisters.isEmpty()) { + regId = pfr.outputRegisters.get(0); + } + } + } + + @Override + public void registerRemaps(RemapRegistry remapRegistry) { + if (!isInputIOMode() && regId != REG_ZERO) { + remapRegistry.addRemap(regId, getStaticOutputRegister(colour)); + } + } + + @Override + public void consumeRemaps(RemapProvider remapProvider) { + regId = remapProvider.getRemappedRegisterID(regId); + } + + @Override + public void collect(Collector collector) { + // Static registers are pre-added during assembler instantiation +// if (isInputIOMode()) { +// collector.addRegister(regId, new ByteRegister()); +// } + } + + @Override + public Optional getOutputRegister(int outDir, int outPort) { + int gateOutputDir = IRotatableICTile.rotationToDir(toAbsoluteRotation(2)); + return isInputIOMode() && outDir == gateOutputDir ? Optional.of(regId) : Optional.empty(); + } + + @Override + public Optional getInputRegister(int inDir, int inPort) { + int gateInputDir = IRotatableICTile.rotationToDir(toAbsoluteRotation(2)); + return !isInputIOMode() && inDir == gateInputDir ? Optional.of(regId) : Optional.empty(); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/InternalStateGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/InternalStateGateTile.java new file mode 100644 index 000000000..102a7ef97 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/InternalStateGateTile.java @@ -0,0 +1,53 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ByteRegister; +import net.minecraft.nbt.CompoundTag; + +public abstract class InternalStateGateTile extends SidedRedstoneGateTile { + + protected int stateReg = -1; + + public InternalStateGateTile(ICGateTileType gateType) { + super(gateType); + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + tag.putInt("regS", stateReg); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + stateReg = tag.getInt("regS"); + } + + @Override + protected void clearRegisterIds() { + super.clearRegisterIds(); + stateReg = -1; + } + + //region FETile overrides + + @Override + public void allocate(Allocator allocator) { + super.allocate(allocator); + stateReg = allocator.allocRegisterID(); + } + + @Override + public void consumeRemaps(RemapProvider remapProvider) { + super.consumeRemaps(remapProvider); + stateReg = remapProvider.getRemappedRegisterID(stateReg); + } + + @Override + public void collect(Collector collector) { + super.collect(collector); + collector.addRegister(stateReg, new ByteRegister()); + } + + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/MultiplexerGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/MultiplexerGateTile.java new file mode 100644 index 000000000..bb6f59c75 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/MultiplexerGateTile.java @@ -0,0 +1,41 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class MultiplexerGateTile extends SimpleGateTile { + + public MultiplexerGateTile() { + super(ICGateTileType.MULTIPLEXER); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return 0xE; + } + + @Override + public ICGate createGate() { + return new MultiplexerGate(); + } + + public static class MultiplexerGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + + boolean in0 = ic.getRegByteVal(inputs[0]) != 0; + boolean in1 = ic.getRegByteVal(inputs[1]) != 0; + boolean in2 = ic.getRegByteVal(inputs[2]) != 0; + + ic.queueRegByteVal(outputs[0], in0 ? + (byte) (in1 ? 1 : 0) : + (byte) (in2 ? 1 : 0)); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NANDGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NANDGateTile.java new file mode 100644 index 000000000..dc62f098d --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NANDGateTile.java @@ -0,0 +1,45 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class NANDGateTile extends SimpleGateTile { + + public NANDGateTile() { + super(ICGateTileType.NAND); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return ~getShape() << 1 & 0xE; + } + + @Override + protected int getDeadSides() { + return 3; + } + + @Override + public ICGate createGate() { + return new NANDGate(); + } + + public static class NANDGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + for (int i = 0; i < inputs.length; i++) { + if (ic.getRegByteVal(inputs[i]) == 0) { + ic.queueRegByteVal(outputs[0], (byte) 1); + return; + } + } + ic.queueRegByteVal(outputs[0], (byte) 0); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NORGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NORGateTile.java new file mode 100644 index 000000000..72c2e8754 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NORGateTile.java @@ -0,0 +1,45 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class NORGateTile extends SimpleGateTile { + + public NORGateTile() { + super(ICGateTileType.NOR); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return ~getShape() << 1 & 0xE; + } + + @Override + protected int getDeadSides() { + return 3; + } + + @Override + public ICGate createGate() { + return new NORGate(); + } + + public static class NORGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + for (int i = 0; i < inputs.length; i++) { + if (ic.getRegByteVal(inputs[i]) != 0) { + ic.queueRegByteVal(outputs[0], (byte) 0); + return; + } + } + ic.queueRegByteVal(outputs[0], (byte) 1); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NOTGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NOTGateTile.java new file mode 100644 index 000000000..0a0a90e87 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/NOTGateTile.java @@ -0,0 +1,42 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class NOTGateTile extends SimpleGateTile { + + public NOTGateTile() { + super(ICGateTileType.NOT); + } + + @Override + protected int redstoneOutputMask() { + return ~((getShape() & 1) << 1 | (getShape() & 2) >> 1 | (getShape() & 4) << 1) & 0xB; + } + + @Override + protected int redstoneInputMask() { + return 4; + } + + @Override + protected int getDeadSides() { + return 3; + } + + @Override + public ICGate createGate() { + return new NOTGate(); + } + + public static class NOTGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + boolean isHigh = ic.getRegByteVal(inputs[0]) != 0; + for (int i = 0; i < outputs.length; i++) { + ic.queueRegByteVal(outputs[i], isHigh ? 0 : (byte) 1); + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ORGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ORGateTile.java new file mode 100644 index 000000000..357dfb014 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ORGateTile.java @@ -0,0 +1,45 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class ORGateTile extends SimpleGateTile { + + public ORGateTile() { + super(ICGateTileType.OR); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return ~getShape() << 1 & 0xE; + } + + @Override + protected int getDeadSides() { + return 3; + } + + @Override + public ICGate createGate() { + return new ORGate(); + } + + public static class ORGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + for (int i = 0; i < inputs.length; i++) { + if (ic.getRegByteVal(inputs[i]) != 0) { + ic.queueRegByteVal(outputs[0], (byte) 1); + return; + } + } + ic.queueRegByteVal(outputs[0], (byte) 0); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/PulseGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/PulseGateTile.java new file mode 100644 index 000000000..0bc644343 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/PulseGateTile.java @@ -0,0 +1,101 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +import java.util.ArrayList; +import java.util.List; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.engine.PRFabricationEngine.*; + +public class PulseGateTile extends TimedStateGateTile { + + public PulseGateTile() { + super(ICGateTileType.PULSE); + } + + //region RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + return false; + } + + @Override + protected int redstoneInputMask() { + return 4; + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + //endregion + + @Override + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegs = new ArrayList<>(); + List outputRegs = new ArrayList<>(); + + inputRegs.add(stateReg); + inputRegs.add(timeReg3); + inputRegs.add(timeReg2); + inputRegs.add(timeReg1); + inputRegs.add(timeReg0); + inputRegs.add(inputRegisters[2]); + + inputRegs.add(REG_TIME_3); + inputRegs.add(REG_TIME_2); + inputRegs.add(REG_TIME_1); + inputRegs.add(REG_TIME_0); + + outputRegs.add(stateReg); + outputRegs.add(timeReg3); + outputRegs.add(timeReg2); + outputRegs.add(timeReg1); + outputRegs.add(timeReg0); + outputRegs.add(outputRegisters[0]); + + collector.addGate(gateId, new PulseGate(), inputRegs, outputRegs); + } + + public static class PulseGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + + int stateVal = ic.getRegByteVal(inputs[0]); + switch (stateVal) { + case 0: { // Waiting for high input + if (ic.getRegByteVal(inputs[5]) != 0) { // if input is high + ic.queueRegByteVal(outputs[0], (byte) 1); // go to state 1 + ic.queueRegByteVal(outputs[5], (byte) 1); // set output high + ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], + ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]) + 2); // set time to t + 2 + } + break; + } + case 1: { // Waiting for timer to expire + if (ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]) >= ic.getRegLongVal(inputs[1], inputs[2], inputs[3], inputs[4])) { // if t is at scheduled time + ic.queueRegByteVal(outputs[0], (byte) (ic.getRegByteVal(inputs[5]) == 0 ? 0 : 2)); // if input high, got to state 2, else state 0 + ic.queueRegByteVal(outputs[5], (byte) 0); // set output low + ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], -1); // disable timer + } + break; + } + case 2: { // Waiting for low input state + if (ic.getRegByteVal(inputs[5]) == 0) { // if input is low + ic.queueRegByteVal(outputs[0], (byte) 0); // go to state 0 + } + break; + } + default: + LOGGER.error("Invalid state: " + stateVal); + ic.queueRegByteVal(outputs[0], (byte) 0); // go to state 0 + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RandomizerGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RandomizerGateTile.java new file mode 100644 index 000000000..c7a7f6111 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RandomizerGateTile.java @@ -0,0 +1,122 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.engine.PRFabricationEngine.*; + +public class RandomizerGateTile extends TimedStateGateTile { + + public RandomizerGateTile() { + super(ICGateTileType.RANDOMIZER); + } + + //region GateTile overrides + + //TODO interaction zones + + //endregion + + //region RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + return false; + } + + @Override + protected int redstoneInputMask() { + return 4; + } + + @Override + protected int redstoneOutputMask() { + return 0xB; + } + + //endregion + + @Override + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegs = new ArrayList<>(); + List outputRegs = new ArrayList<>(); + + inputRegs.add(stateReg); + inputRegs.add(timeReg3); + inputRegs.add(timeReg2); + inputRegs.add(timeReg1); + inputRegs.add(timeReg0); + inputRegs.add(inputRegisters[2]); + + inputRegs.add(REG_TIME_3); + inputRegs.add(REG_TIME_2); + inputRegs.add(REG_TIME_1); + inputRegs.add(REG_TIME_0); + + outputRegs.add(stateReg); + outputRegs.add(timeReg3); + outputRegs.add(timeReg2); + outputRegs.add(timeReg1); + outputRegs.add(timeReg0); + outputRegs.add(outputRegisters[3] != -1 ? outputRegisters[3] : REG_ZERO); + outputRegs.add(outputRegisters[0] != -1 ? outputRegisters[0] : REG_ZERO); + outputRegs.add(outputRegisters[1] != -1 ? outputRegisters[1] : REG_ZERO); + + collector.addGate(gateId, new RandomizerGateTile.RandomizerGate(), inputRegs, outputRegs); + } + + public static class RandomizerGate implements ICGate { + + private static final Random RAND = new Random(); + + private static byte readState(ICSimulation ic, int[] inputs) { return ic.getRegByteVal(inputs[0]); } + private static long readSchedTime(ICSimulation ic, int[] inputs) { return ic.getRegLongVal(inputs[1], inputs[2], inputs[3], inputs[4]); } + private static byte readInput(ICSimulation ic, int[] inputs) { return ic.getRegByteVal(inputs[5]); } + private static long readSysTime(ICSimulation ic, int[] inputs) { return ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]); } + + private static void writeState(ICSimulation ic, int[] outputs, byte state) { ic.queueRegByteVal(outputs[0], state); } + private static void writeSchedTime(ICSimulation ic, int[] outputs, long time) { ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], time); } + private static void writeOutMask(ICSimulation ic, int[] outputs, int mask) { + ic.queueRegByteVal(outputs[5], (byte) ((mask & 1) != 0 ? 1 : 0)); + ic.queueRegByteVal(outputs[6], (byte) ((mask & 2) != 0 ? 1 : 0)); + ic.queueRegByteVal(outputs[7], (byte) ((mask & 4) != 0 ? 1 : 0)); + } + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + + switch (readState(ic, inputs)) { + case 0: { // Waiting for high input + if (readInput(ic, inputs) != 0) { // if input is high + writeState(ic, outputs, (byte) 1); // set state to 1 + writeOutMask(ic, outputs, RAND.nextInt(8)); // set outputs to random state + writeSchedTime(ic, outputs, readSysTime(ic, inputs) + 2); // set scheduled time to t + 2 + } + break; + } + case 1: { // Wait for timer state + if (readSysTime(ic, inputs) >= readSchedTime(ic, inputs)) { // if time is up + if (readInput(ic, inputs) != 0) { // if input is high + // stay in state 1 + writeOutMask(ic, outputs, RAND.nextInt(8)); // set outputs to random state + writeSchedTime(ic, outputs, readSysTime(ic, inputs) + 2); // set scheduled time to t + 2 + } else { + writeState(ic, outputs, (byte) 0); // set state to 0 + writeOutMask(ic, outputs, 0); // set outputs to 0 + } + } + break; + } + default: + LOGGER.error("Invalid state: " + readState(ic, inputs)); + writeState(ic, outputs, (byte) 0); // go to state 0 + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RedstoneGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RedstoneGateTile.java new file mode 100644 index 000000000..932626ba5 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RedstoneGateTile.java @@ -0,0 +1,107 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.projectred.fabrication.engine.IConnectableICTile; +import mrtjp.projectred.fabrication.engine.IRedstoneConnectableICTile; +import net.minecraft.nbt.CompoundTag; + +public abstract class RedstoneGateTile extends GateTile implements IRedstoneConnectableICTile { + + public static final int STATE_PACKET = 5; + + /** + * Mapped inputs and outputs of the gate. + * OOOO IIII + * High nybble is output. + * Low nybble is input + */ + private byte gateState = 0; + + public RedstoneGateTile(ICGateTileType gateType) { + super(gateType); + } + + public void setState(int state) { + gateState = (byte) state; + } + + public int getState() { + return gateState & 0xFF; + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + tag.putByte("state", gateState); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + gateState = tag.getByte("state"); + } + + @Override + public void writeDesc(MCDataOutput out) { + super.writeDesc(out); + out.writeByte(gateState); + } + + @Override + public void readDesc(MCDataInput in) { + super.readDesc(in); + gateState = in.readByte(); + } + + @Override + public void read(MCDataInput in, int key) { + switch (key) { + case STATE_PACKET: + gateState = in.readByte(); + break; + default: + super.read(in, key); + } + } + + protected void sendStateUpdate() { + getWriteStream(STATE_PACKET).writeByte(gateState); + } + + //region IGateRenderKey implementation + @Override + public int state() { + return gateState & 0xFF; + } + //endregion + + //region RedstoneGateTile logic override points + @Override + protected boolean canGateConnectTo(IConnectableICTile target, int r) { + if (target instanceof IRedstoneConnectableICTile) + return canConnectRedstone(r); + return false; + } + + protected boolean canConnectRedstone(int r) { + return canInputRedstone(r) || canOutputRedstone(r); + } + + protected boolean canOutputRedstone(int r) { + return (redstoneOutputMask() & 1 << r) != 0; + } + + protected boolean canInputRedstone(int r) { + return (redstoneInputMask() & 1 << r) != 0; + } + + protected int redstoneOutputMask() { + return 0; + } + + protected int redstoneInputMask() { + return 0; + } + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RepeaterGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RepeaterGateTile.java new file mode 100644 index 000000000..9a63c48bb --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/RepeaterGateTile.java @@ -0,0 +1,157 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Rotation; +import codechicken.lib.vec.Transformation; +import codechicken.lib.vec.Vector3; +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.engine.PRFabricationEngine.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_TOGGLE_DELAY; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_UNIT_TICKS; + +public class RepeaterGateTile extends TimedStateGateTile { + + private static final int[] DELAYS = { 2, 4, 6, 8, 16, 32, 64, 128, 256 }; + + public RepeaterGateTile() { + super(ICGateTileType.REPEATER); + } + + //region GateTile overrides + + @Override + public List getInteractionZones() { + List zones = new LinkedList<>(); + zones.add(new Cuboid6(1, 2, 1, 15, 3, 15)); + Transformation rotation = Rotation.quarterRotations[getRotation()].at(new Vector3(8, 8, 8)); + zones.forEach(c -> c.apply(rotation)); + return zones; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void buildInteractionToolTip(List toolTip, int i) { + + toolTip.add(new TranslatableComponent(UL_TOGGLE_DELAY)); + toolTip.add(new TranslatableComponent(UL_UNIT_TICKS, DELAYS[getShape()]).withStyle(ChatFormatting.GRAY)); + } + + @Override + public void onInteractionZoneClicked(int i) { + configureAndSend(); + } + + //endregion + + //region RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + setShape((getShape() + 1) % DELAYS.length); + return true; + } + + @Override + protected int redstoneInputMask() { + return 4; + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + //endregion + + @Override + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegs = new ArrayList<>(); + List outputRegs = new ArrayList<>(); + + inputRegs.add(stateReg); + inputRegs.add(timeReg3); + inputRegs.add(timeReg2); + inputRegs.add(timeReg1); + inputRegs.add(timeReg0); + inputRegs.add(inputRegisters[2]); + + inputRegs.add(REG_TIME_3); + inputRegs.add(REG_TIME_2); + inputRegs.add(REG_TIME_1); + inputRegs.add(REG_TIME_0); + + outputRegs.add(stateReg); + outputRegs.add(timeReg3); + outputRegs.add(timeReg2); + outputRegs.add(timeReg1); + outputRegs.add(timeReg0); + outputRegs.add(outputRegisters[0]); + + collector.addGate(gateId, new RepeaterGate(DELAYS[getShape()]), inputRegs, outputRegs); + } + + public static class RepeaterGate implements ICGate { + + private final int delay; + + public RepeaterGate(int delay) { + this.delay = delay; + } + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + + int stateVal = ic.getRegByteVal(inputs[0]); + switch (stateVal) { + case 0: { // Waiting for high input + if (ic.getRegByteVal(inputs[5]) != 0) { // if input is high + ic.queueRegByteVal(outputs[0], (byte) 1); // go to state 1 + ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], + ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]) + delay); // set timer to t + delay + } + break; + } + case 1: { // Waiting for timer to go high + if (ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]) >= ic.getRegLongVal(inputs[1], inputs[2], inputs[3], inputs[4])) { // if timer expired + ic.queueRegByteVal(outputs[0], (byte) 2); // go to state 2 + ic.queueRegByteVal(outputs[5], (byte) 1); // set output high + ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], -1); // disable timer + } + break; + } + case 2: { // Waiting for low input + if (ic.getRegByteVal(inputs[5]) == 0) { // if input is low + ic.queueRegByteVal(outputs[0], (byte) 3); // go to state 3 + ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], + ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]) + delay); // set timer to t + delay + } + break; + } + case 3: { // Waiting for timer to go low + if (ic.getRegLongVal(inputs[6], inputs[7], inputs[8], inputs[9]) >= ic.getRegLongVal(inputs[1], inputs[2], inputs[3], inputs[4])) { // if timer expired + ic.queueRegByteVal(outputs[0], (byte) 0); // go to state 0 + ic.queueRegByteVal(outputs[5], (byte) 0); // set output low + ic.queueRegLongVal(outputs[1], outputs[2], outputs[3], outputs[4], -1); // disable timer + } + break; + } + default: + LOGGER.error("Invalid state: " + stateVal); + ic.queueRegByteVal(outputs[0], (byte) 0); // go to state 0 + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SRLatchGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SRLatchGateTile.java new file mode 100644 index 000000000..624a443a8 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SRLatchGateTile.java @@ -0,0 +1,157 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.engine.PRFabricationEngine.REG_ZERO; + +public class SRLatchGateTile extends InternalStateGateTile { + + public SRLatchGateTile() { + super(ICGateTileType.SR_LATCH); + } + + //region GateTile overrides + + //TODO interaction zones + + //endregion + + //region RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + setShape((getShape() + 1) % 4); + return true; + } + + @Override + protected int redstoneInputMask() { + return 0xA; + } + + @Override + protected int redstoneOutputMask() { + return (getShape() >> 1) == 0 ? 0xF : 5; + } + + //endregion + + @Override + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegs = new ArrayList<>(); + List outputRegs = new ArrayList<>(); + + boolean reflected = (getShape() & 1) != 0; + boolean backfeed = (getShape() & 2) == 0; + + inputRegs.add(stateReg); + inputRegs.add(inputRegisters[reflected ? 3 : 1]); //inA + inputRegs.add(inputRegisters[reflected ? 1 : 3]); //inB + + outputRegs.add(stateReg); + outputRegs.add(outputRegisters[2]); //outA + outputRegs.add(outputRegisters[0]); //outB + outputRegs.add(backfeed ? outputRegisters[reflected ? 3 : 1] : REG_ZERO); //backfeedA + outputRegs.add(backfeed ? outputRegisters[reflected ? 1 : 3] : REG_ZERO); //backfeedB + + collector.addGate(gateId, new SRLatchGate(), inputRegs, outputRegs); + } + + public static class SRLatchGate implements ICGate { + + private static final Random RAND = new Random(); + + private static byte readState(ICSimulation ic, int[] inputs) { return ic.getRegByteVal(inputs[0]); } + + private static int readInputMask(ICSimulation ic, int[] inputs) { + int mask = 0; + if (ic.getRegByteVal(inputs[1]) != 0) mask |= 1; + if (ic.getRegByteVal(inputs[2]) != 0) mask |= 2; + return mask; + } + + private static void writeState(ICSimulation ic, int[] outputs, byte state) { ic.queueRegByteVal(outputs[0], state); } + + private static void writeOutputMask(ICSimulation ic, int[] outputs, int mask) { + ic.queueRegByteVal(outputs[1], (byte) ((mask & 1) != 0 ? 1 : 0)); // outA + ic.queueRegByteVal(outputs[3], (byte) ((mask & 1) != 0 ? 1 : 0)); // backfeedA + ic.queueRegByteVal(outputs[2], (byte) ((mask & 2) != 0 ? 1 : 0)); // outB + ic.queueRegByteVal(outputs[4], (byte) ((mask & 2) != 0 ? 1 : 0)); // backfeedB + } + + private static void enterStateA(ICSimulation ic, int[] outputs) { + writeState(ic, outputs, (byte) 1); + writeOutputMask(ic, outputs, 0x1); + } + + private static void enterStateB(ICSimulation ic, int[] outputs) { + writeState(ic, outputs, (byte) 2); + writeOutputMask(ic, outputs, 0x2); + } + + private static void enterUndefState(ICSimulation ic, int[] outputs) { + writeState(ic, outputs, (byte) 3); + writeOutputMask(ic, outputs, 0x0); + } + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + int inputMask = readInputMask(ic, inputs); + switch (readState(ic, inputs)) { + case 0: { + if (inputMask == 2) { + enterStateB(ic, outputs); + } else if (inputMask == 3) { + enterUndefState(ic, outputs); + } else { + enterStateA(ic, outputs); + } + } + + case 1: { // A state + if (inputMask == 2) { + enterStateB(ic, outputs); + } else if (inputMask == 3) { + enterUndefState(ic, outputs); + } + break; + } + + case 2: { // B state + if (inputMask == 1) { + enterStateA(ic, outputs); + } else if (inputMask == 3) { + enterUndefState(ic, outputs); + } + break; + } + + case 3: { // Undefined state + if (inputMask == 0) { + if (RAND.nextBoolean()) { + enterStateA(ic, outputs); + } else { + enterStateB(ic, outputs); + } + } else if (inputMask == 1) { + enterStateA(ic, outputs); + } else if (inputMask == 2) { + enterStateB(ic, outputs); + } + break; + } + + default: + LOGGER.error("Invalid state: " + readState(ic, inputs)); + writeState(ic, outputs, (byte) 0); // go to state 0 + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SidedRedstoneGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SidedRedstoneGateTile.java new file mode 100644 index 000000000..e0dd87d51 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SidedRedstoneGateTile.java @@ -0,0 +1,173 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.api.IPathFinder; +import mrtjp.fengine.assemble.PathFinderResult; +import mrtjp.fengine.simulate.ByteRegister; +import mrtjp.projectred.fabrication.engine.ICSimulationContainer; +import mrtjp.projectred.fabrication.engine.IRotatableICTile; +import mrtjp.projectred.fabrication.engine.log.DeadGateWarning; +import mrtjp.projectred.fabrication.engine.log.MultipleDriversError; +import net.minecraft.nbt.CompoundTag; + +import java.util.Arrays; +import java.util.Optional; + +import static mrtjp.projectred.fabrication.engine.PRFabricationEngine.REG_ZERO; + +public abstract class SidedRedstoneGateTile extends RedstoneGateTile { + + private final int[] inputRegisters = new int[] { -1, -1, -1, -1 }; + private final int[] outputRegisters = new int[] { -1, -1, -1, -1 }; + private int gateId = -1; + + public SidedRedstoneGateTile(ICGateTileType gateType) { + super(gateType); + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + for (int i = 0; i < 4; i++) { + tag.putInt("regIn" + i, inputRegisters[i]); + tag.putInt("regOut" + i, outputRegisters[i]); + } + tag.putInt("gate", gateId); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + for (int i = 0; i < 4; i++) { + inputRegisters[i] = tag.getInt("regIn" + i); + outputRegisters[i] = tag.getInt("regOut" + i); + } + gateId = tag.getInt("gate"); + } + + @Override + public void onSimRegistersChanged(int rMask, ICSimulationContainer container) { + int oldState = getState(); + int newState = pullInputMask(container) & 0xF | pullOutputMask(container) << 4; + if (oldState != newState) { + setState(newState); + sendStateUpdate(); + } + } + + protected int pullInputMask(ICSimulationContainer container) { + int mask = 0; + for (int r = 0; r < 4; r++) { + if (canInputRedstone(r)) { + int regId = inputRegisters[r]; + if (container.pullRegisterValue(regId) != 0) mask |= 1 << r; + } + } + return mask; + } + + protected int pullOutputMask(ICSimulationContainer container) { + int mask = 0; + for (int r = 0; r < 4; r++) { + if (canOutputRedstone(r)) { + int regId = outputRegisters[r]; + if (container.pullRegisterValue(regId) != 0) mask |= 1 << r; + } + } + return mask; + } + + //region FETile overrides + + @Override + public void allocate(Allocator allocator) { + clearRegisterIds(); + + for (int r = 0; r < 4; r++) { + if (canOutputRedstone(r)) outputRegisters[r] = allocator.allocRegisterID(); + } + + gateId = allocator.allocGateID(); + } + + @Override + public void locate(IPathFinder pathFinder) { + + int req = 0; + int found = 0; + for (int r = 0; r < 4; r++) { + if (canInputRedstone(r)) { + req++; + inputRegisters[r] = searchInputRegister(r, pathFinder); + if (inputRegisters[r] != REG_ZERO) found++; + } + } + + if (req > 0 && found == 0) { + getEditor().getStateMachine().getCompilerLog().addProblem(new DeadGateWarning(getPos())); + } + } + + @Override + public void consumeRemaps(RemapProvider remapProvider) { + + for (int r = 0; r < 4; r++) { + if (inputRegisters[r] != -1) inputRegisters[r] = remapProvider.getRemappedRegisterID(inputRegisters[r]); + if (outputRegisters[r] != -1) outputRegisters[r] = remapProvider.getRemappedRegisterID(outputRegisters[r]); + } + } + + @Override + public void collect(Collector collector) { + + for (int r = 0; r < 4; r++) { + if (outputRegisters[r] != -1) collector.addRegister(outputRegisters[r], new ByteRegister()); + } + + collectGate(collector, gateId, inputRegisters, outputRegisters); + } + + @Override + public Optional getOutputRegister(int outDir, int outPort) { + int absR = IRotatableICTile.dirToRotation(outDir); + int r = toInternalRotation(absR); + + if (r == -1 || outputRegisters[r] == -1) return Optional.empty(); + return Optional.of(outputRegisters[r]); + } + + @Override + public Optional getInputRegister(int inDir, int inPort) { + int absR = IRotatableICTile.dirToRotation(inDir); + int r = toInternalRotation(absR); + + if (r == -1 || inputRegisters[r] == -1) return Optional.empty(); + return Optional.of(inputRegisters[r]); + } + + //endregion + + protected void clearRegisterIds() { + Arrays.fill(inputRegisters, -1); + Arrays.fill(outputRegisters, -1); + } + + private int searchInputRegister(int r, IPathFinder pathFinder) { + int absR = toAbsoluteRotation(r); + int absDir = IRotatableICTile.rotationToDir(absR); + PathFinderResult pfr = pathFinder.doPathFinding((d, p) -> d == absDir); + if (pfr.outputRegisters.size() > 1) { + getEditor().getStateMachine().getCompilerLog().addProblem(new MultipleDriversError(getPos(), pfr.outputRegisters)); + } + if (!pfr.outputRegisters.isEmpty()) { + return pfr.outputRegisters.get(0); + } + + return REG_ZERO; + } + + //region SidedRedstoneGateTile logic override points + + protected abstract void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters); + + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SimpleGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SimpleGateTile.java new file mode 100644 index 000000000..66f8d13fc --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/SimpleGateTile.java @@ -0,0 +1,130 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import codechicken.lib.vec.*; +import mrtjp.fengine.simulate.ICGate; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_SIDE_DISABLED; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_SIDE_ENABLED; + +public abstract class SimpleGateTile extends SidedRedstoneGateTile { + + public static int[] DEAD_SIDES_MASKS = new int[] { 1, 2, 3, 0, 5, 6, 3 }; + + public SimpleGateTile(ICGateTileType gateType) { + super(gateType); + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + } + + @Override + protected boolean cycleShape() { + int oldShape = getShape(); + setShape(progressDeadSideShape(oldShape)); + return oldShape != getShape(); + } + + private int progressDeadSideShape(int shape) { + if (getDeadSides() == 0) return shape; + + int s = DEAD_SIDES_MASKS[shape]; + s = ensureMaxDeadSides(s); + return s; + } + + private int ensureMaxDeadSides(int s) { + while (Integer.bitCount(s) > getMaxDeadSides() || 32 - Integer.numberOfLeadingZeros(s) > getDeadSides()) { + s = DEAD_SIDES_MASKS[s]; + } + return s; + } + + //region BaseTile overrides + + @Override + public List getInteractionZones() { + List zones = new LinkedList<>(); + if (getDeadSides() > 0) { + zones.add(new Cuboid6(10, 2, 6, 16, 2.5, 10)); + zones.add(new Cuboid6(6, 2, 10, 10, 2.5, 16)); + zones.add(new Cuboid6(0, 2, 6, 6, 2.5, 10)); + + Transformation rotation = Rotation.quarterRotations[getRotation()].at(new Vector3(8, 8, 8)); + Transformation t = isReflected() ? new Scale(1, -1, 1).with(rotation) : rotation; + zones.forEach(c -> c.apply(t)); + } + + return zones; + } + + @Override + @OnlyIn(Dist.CLIENT) + public void buildInteractionToolTip(List toolTip, int i) { + + boolean isEnabled = (getShape() & (1 << (i-1))) == 0; + toolTip.add(new TranslatableComponent(isEnabled ? UL_SIDE_ENABLED : UL_SIDE_DISABLED)); + } + + @Override + public void onInteractionZoneClicked(int i) { + if (getDeadSides() == 0) return; + + int oldShape = getShape(); + int shape = oldShape ^ (1 << i); + shape = ensureMaxDeadSides(shape); + + if (oldShape != shape) { + setShape(shape); + sendShapeUpdate(); + notifyNeighbors(0xF); + getEditor().markTileChange(); + } + } + //endregion + + //region SimpleGateTile logic override points + + protected int getDeadSides() { + return 0; + } + + protected int getMaxDeadSides() { + return getDeadSides() - 1; + } + + protected boolean isReflected() { + return false; + } + + protected abstract ICGate createGate(); + + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegistersList = new ArrayList<>(); + List outputRegistersList = new ArrayList<>(); + for (int r = 0; r < 4; r++) { + if (inputRegisters[r] != -1) inputRegistersList.add(inputRegisters[r]); + if (outputRegisters[r] != -1) outputRegistersList.add(outputRegisters[r]); + } + + collector.addGate(gateId, createGate(), inputRegistersList, outputRegistersList); + } + + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/TimedStateGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/TimedStateGateTile.java new file mode 100644 index 000000000..b599f62b1 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/TimedStateGateTile.java @@ -0,0 +1,82 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ByteRegister; +import net.minecraft.nbt.CompoundTag; + +public abstract class TimedStateGateTile extends SidedRedstoneGateTile { + + protected int stateReg = -1; + protected int timeReg3 = -1; + protected int timeReg2 = -1; + protected int timeReg1 = -1; + protected int timeReg0 = -1; + + public TimedStateGateTile(ICGateTileType gateType) { + super(gateType); + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + tag.putInt("regS", stateReg); + tag.putInt("regT3", timeReg3); + tag.putInt("regT2", timeReg2); + tag.putInt("regT1", timeReg1); + tag.putInt("regT0", timeReg0); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + stateReg = tag.getInt("regS"); + timeReg3 = tag.getInt("regT3"); + timeReg2 = tag.getInt("regT2"); + timeReg1 = tag.getInt("regT1"); + timeReg0 = tag.getInt("regT0"); + } + + @Override + protected void clearRegisterIds() { + super.clearRegisterIds(); + stateReg = -1; + timeReg3 = -1; + timeReg2 = -1; + timeReg1 = -1; + timeReg0 = -1; + } + + //region FETile overrides + + @Override + public void allocate(Allocator allocator) { + super.allocate(allocator); + stateReg = allocator.allocRegisterID(); + timeReg3 = allocator.allocRegisterID(); + timeReg2 = allocator.allocRegisterID(); + timeReg1 = allocator.allocRegisterID(); + timeReg0 = allocator.allocRegisterID(); + } + + @Override + public void consumeRemaps(RemapProvider remapProvider) { + super.consumeRemaps(remapProvider); + stateReg = remapProvider.getRemappedRegisterID(stateReg); + timeReg3 = remapProvider.getRemappedRegisterID(timeReg3); + timeReg2 = remapProvider.getRemappedRegisterID(timeReg2); + timeReg1 = remapProvider.getRemappedRegisterID(timeReg1); + timeReg0 = remapProvider.getRemappedRegisterID(timeReg0); + } + + @Override + public void collect(Collector collector) { + super.collect(collector); + + collector.addRegister(stateReg, new ByteRegister()); + collector.addRegister(timeReg3, new ByteRegister()); + collector.addRegister(timeReg2, new ByteRegister()); + collector.addRegister(timeReg1, new ByteRegister()); + collector.addRegister(timeReg0, new ByteRegister()); + } + + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ToggleLatchGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ToggleLatchGateTile.java new file mode 100644 index 000000000..f9061826c --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/ToggleLatchGateTile.java @@ -0,0 +1,129 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +import java.util.ArrayList; +import java.util.List; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; + +public class ToggleLatchGateTile extends InternalStateGateTile { + + public ToggleLatchGateTile() { + super(ICGateTileType.TOGGLE_LATCH); + } + + //region GateTile overrides + + //TODO interaction zones + + //endregion + + //region RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + setShape((getShape() + 1) % 2); + return true; + } + + @Override + protected int redstoneInputMask() { + return 0xA; + } + + @Override + protected int redstoneOutputMask() { + return 5; + } + + //endregion + + @Override + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegs = new ArrayList<>(); + List outputRegs = new ArrayList<>(); + + inputRegs.add(stateReg); + inputRegs.add(inputRegisters[3]); //inA + inputRegs.add(inputRegisters[1]); //inB + + outputRegs.add(stateReg); + outputRegs.add(outputRegisters[0]); //outA + outputRegs.add(outputRegisters[2]); //outB + + collector.addGate(gateId, new ToggleLatchGate(getShape()), inputRegs, outputRegs); + } + + public static class ToggleLatchGate implements ICGate { + + private final int defaultState; + + public ToggleLatchGate(int defaultState) { + this.defaultState = defaultState; + } + + private static byte readState(ICSimulation ic, int[] inputs) { return ic.getRegByteVal(inputs[0]); } + + private static int readInputMask(ICSimulation ic, int[] inputs) { + int mask = 0; + if (ic.getRegByteVal(inputs[1]) != 0) mask |= 1; //inA + if (ic.getRegByteVal(inputs[2]) != 0) mask |= 2; //inB + return mask; + } + + private static void writeState(ICSimulation ic, int[] outputs, byte state) { ic.queueRegByteVal(outputs[0], state); } + + private static void writeOutputMask(ICSimulation ic, int[] outputs, int mask) { + ic.queueRegByteVal(outputs[1], (byte) ((mask & 1) != 0 ? 1 : 0)); // outA + ic.queueRegByteVal(outputs[2], (byte) ((mask & 2) != 0 ? 1 : 0)); // outB + } + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + int s = readState(ic, inputs); + int state = s & 0xF; + int inputMask = readInputMask(ic, inputs); + int prevInputMask = (s >> 4) & 0xF; + int highMask = inputMask & ~prevInputMask; + int nextState = state; + + switch (state) { + case 0: { // Initial state + if (defaultState == 0) { + nextState = 1; // Enter A state + writeOutputMask(ic, outputs, 0x1); + } else { + nextState = 2; // Enter B state + writeOutputMask(ic, outputs, 0x2); + } + break; + } + + case 1: { // A state + if (highMask == 1 || highMask == 2) { + nextState = 2; // Enter B state + writeOutputMask(ic, outputs, 0x2); + } + break; + } + + case 2: { // B state + if (highMask == 1 || highMask == 2) { + nextState = 1; // Enter A state + writeOutputMask(ic, outputs, 0x1); + } + break; + } + + default: + LOGGER.error("Invalid state: " + readState(ic, inputs)); + nextState = 0; + } + + writeState(ic, outputs, (byte) (inputMask << 4 | nextState)); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/TransparentLatchGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/TransparentLatchGateTile.java new file mode 100644 index 000000000..712d950e8 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/TransparentLatchGateTile.java @@ -0,0 +1,114 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +import java.util.ArrayList; +import java.util.List; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; + +public class TransparentLatchGateTile extends InternalStateGateTile { + + public TransparentLatchGateTile() { + super(ICGateTileType.TRANSPARENT_LATCH); + } + + //region GateTile overrides + + //TODO interaction zones + + //endregion + + //region RedstoneGateTile overrides + + @Override + protected boolean cycleShape() { + setShape((getShape() + 1) % 2); + return true; + } + + @Override + protected int redstoneInputMask() { + return getShape() == 0 ? 0xC : 0x6; + } + + @Override + protected int redstoneOutputMask() { + return getShape() == 0 ? 0x3 : 0x9; + } + + //endregion + + @Override + protected void collectGate(Collector collector, int gateId, int[] inputRegisters, int[] outputRegisters) { + + List inputRegs = new ArrayList<>(); + List outputRegs = new ArrayList<>(); + + inputRegs.add(stateReg); + inputRegs.add(inputRegisters[getShape() == 0 ? 3 : 1]); //dataIn + inputRegs.add(inputRegisters[2]); //writeEnable + + outputRegs.add(stateReg); + outputRegs.add(outputRegisters[0]); //out1 + outputRegs.add(outputRegisters[getShape() == 0 ? 1 : 3]); //out2 + + collector.addGate(gateId, new TransparentLatchGate(), inputRegs, outputRegs); + } + + public static class TransparentLatchGate implements ICGate { + + private static byte readState(ICSimulation ic, int[] inputs) { return ic.getRegByteVal(inputs[0]); } + + private static byte readData(ICSimulation ic, int[] inputs) { + return ic.getRegByteVal(inputs[1]); + } + + private static boolean writeEnabled(ICSimulation ic, int[] inputs) { + return ic.getRegByteVal(inputs[2]) != 0; + } + + private static void writeState(ICSimulation ic, int[] outputs, byte state) { ic.queueRegByteVal(outputs[0], state); } + + private static void writeData(ICSimulation ic, int[] outputs, byte data) { + ic.queueRegByteVal(outputs[1], data); + ic.queueRegByteVal(outputs[2], data); + } + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + + switch (readState(ic, inputs)) { + case 0: { // Initial state + if (writeEnabled(ic, inputs)) { + writeState(ic, outputs, (byte) 2); // Enter write state + } else { + writeState(ic, outputs, (byte) 1); // Enter lock state + } + break; + } + + case 1: { // Lock state + if (writeEnabled(ic, inputs)) { + writeState(ic, outputs, (byte) 2); // Enter write state + } + break; + } + + case 2: { // Write state + if (writeEnabled(ic, inputs)) { + writeData(ic, outputs, readData(ic, inputs)); + } else { + writeState(ic, outputs, (byte) 1); // Enter lock state + } + break; + } + + default: + LOGGER.error("Invalid state: " + readState(ic, inputs)); + writeState(ic, outputs, (byte) 0); + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/XNORGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/XNORGateTile.java new file mode 100644 index 000000000..4d989efbe --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/XNORGateTile.java @@ -0,0 +1,36 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class XNORGateTile extends SimpleGateTile { + + public XNORGateTile() { + super(ICGateTileType.XNOR); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return 10; + } + + @Override + public ICGate createGate() { + return new XNORGate(); + } + + public static class XNORGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + boolean in0 = ic.getRegByteVal(inputs[0]) != 0; + boolean in1 = ic.getRegByteVal(inputs[1]) != 0; + ic.queueRegByteVal(outputs[0], in0 == in1 ? (byte) 1 : (byte) 0); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/XORGateTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/XORGateTile.java new file mode 100644 index 000000000..f111958fd --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/gates/XORGateTile.java @@ -0,0 +1,36 @@ +package mrtjp.projectred.fabrication.engine.gates; + +import mrtjp.fengine.simulate.ICGate; +import mrtjp.fengine.simulate.ICSimulation; + +public class XORGateTile extends SimpleGateTile { + + public XORGateTile() { + super(ICGateTileType.XOR); + } + + @Override + protected int redstoneOutputMask() { + return 1; + } + + @Override + protected int redstoneInputMask() { + return 10; + } + + @Override + public ICGate createGate() { + return new XORGate(); + } + + public static class XORGate implements ICGate { + + @Override + public void compute(ICSimulation ic, int[] inputs, int[] outputs) { + boolean in0 = ic.getRegByteVal(inputs[0]) != 0; + boolean in1 = ic.getRegByteVal(inputs[1]) != 0; + ic.queueRegByteVal(outputs[0], in0 != in1 ? (byte) 1 : (byte) 0); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblem.java new file mode 100644 index 000000000..ac9387e1d --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblem.java @@ -0,0 +1,44 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.gui.ICRenderNode; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.List; + +public abstract class CompileProblem { + + public final CompileProblemType type; + public final CompileProblemSeverity severity; + + public CompileProblem(CompileProblemType type, CompileProblemSeverity severity) { + this.type = type; + this.severity = severity; + } + + public abstract void save(CompoundTag tag); + + public abstract void load(CompoundTag tag); + + public abstract void writeDesc(MCDataOutput out); + + public abstract void readDesc(MCDataInput in); + + public abstract Component getName(); + + public abstract void buildToolTip(List tooltip, TileCoord hoverPosition); + + public abstract void buildToolTip(List tooltip); + + @OnlyIn(Dist.CLIENT) + public abstract void renderOverlay(Vector3 mousePosition, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblemSeverity.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblemSeverity.java new file mode 100644 index 000000000..b8730c4f5 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblemSeverity.java @@ -0,0 +1,6 @@ +package mrtjp.projectred.fabrication.engine.log; + +public enum CompileProblemSeverity { + ERROR, + WARNING +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblemType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblemType.java new file mode 100644 index 000000000..4dc79ed14 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/CompileProblemType.java @@ -0,0 +1,33 @@ +package mrtjp.projectred.fabrication.engine.log; + +import java.util.function.Supplier; + +public enum CompileProblemType { + MULTIPLE_DRIVERS(MultipleDriversError::new), + DEAD_WIRE(DeadWireWarning::new), + DEAD_GATE(DeadGateWarning::new), + IO_DIR_MISMATCH(IODirectionMismatchError::new), + NO_INPUTS(NoInputsError::new), + NO_OUTPUTS(NoOutputsError::new), + ; + + public static final CompileProblemType[] VALUES = values(); + + private final Supplier issueSupplier; + + CompileProblemType(Supplier issueSupplier) { + this.issueSupplier = issueSupplier; + } + + public CompileProblem newInstance() { + return issueSupplier.get(); + } + + public int getID() { + return ordinal(); + } + + public static CompileProblem createById(int type) { + return VALUES[type].newInstance(); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/DeadGateWarning.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/DeadGateWarning.java new file mode 100644 index 000000000..4451ca4c3 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/DeadGateWarning.java @@ -0,0 +1,33 @@ +package mrtjp.projectred.fabrication.engine.log; + +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DEAD_GATE_DESC; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DEAD_GATE_TITLE; + +public class DeadGateWarning extends SimpleLocatableProblem { + + public DeadGateWarning() { + super(CompileProblemType.DEAD_GATE, CompileProblemSeverity.WARNING); + } + + public DeadGateWarning(TileCoord coord) { + super(CompileProblemType.DEAD_GATE, CompileProblemSeverity.WARNING, coord); + } + + @Override + public Component getName() { + return new TranslatableComponent(UL_DEAD_GATE_TITLE); + } + + @Override + public void buildToolTip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_DEAD_GATE_DESC).withStyle(ICWorkbenchEditor.UNIFORM_GRAY)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/DeadWireWarning.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/DeadWireWarning.java new file mode 100644 index 000000000..6ad09807b --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/DeadWireWarning.java @@ -0,0 +1,34 @@ +package mrtjp.projectred.fabrication.engine.log; + +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DEAD_WIRE_DESC; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DEAD_WIRE_TITLE; + +public class DeadWireWarning extends SimpleLocatableProblem { + + public DeadWireWarning() { + super(CompileProblemType.DEAD_WIRE, CompileProblemSeverity.WARNING); + } + + public DeadWireWarning(TileCoord coord) { + super(CompileProblemType.DEAD_WIRE, CompileProblemSeverity.WARNING, coord); + } + + @Override + public Component getName() { + return new TranslatableComponent(UL_DEAD_WIRE_TITLE); + } + + @Override + public void buildToolTip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_DEAD_WIRE_DESC).withStyle(ICWorkbenchEditor.UNIFORM_GRAY)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/ICCompilerLog.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/ICCompilerLog.java new file mode 100644 index 000000000..b6c5dc7d2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/ICCompilerLog.java @@ -0,0 +1,512 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.TileCoord; +import mrtjp.fengine.api.ICStepThroughAssembler; +import mrtjp.projectred.fabrication.editor.ICEditorStateMachine; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +import java.util.*; +import java.util.stream.Collectors; + +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.*; +import static mrtjp.projectred.fabrication.editor.ICEditorStateMachine.*; + +public class ICCompilerLog implements ICStepThroughAssembler.EventReceiver { + + private final ICEditorStateMachine stateMachine; + + // Compile steps tracking + private final CompileTree compileTree = new CompileTree(); + private final List currentPath = new LinkedList<>(); + private int completedSteps = 0; //TODO ideally this would be a feature in the compile tree object + + // Compile problems + private final List problems = new ArrayList<>(); + private int warningCount = 0; + private int errorCount = 0; + + public ICCompilerLog(ICEditorStateMachine stateMachine) { + this.stateMachine = stateMachine; + } + + public void save(CompoundTag tag) { + tag.putInt(KEY_COMPLETED_STEPS, completedSteps); + tag.putIntArray(KEY_CURRENT_PATH, currentPath); + compileTree.save(tag); + + ListTag list = new ListTag(); + for (CompileProblem problem : problems) { + CompoundTag problemTag = new CompoundTag(); + problemTag.putByte("_type", (byte) problem.type.getID()); + problem.save(problemTag); + list.add(problemTag); + } + tag.put(KEY_PROBLEMS_LIST, list); + tag.putInt(KEY_WARNING_COUNT, warningCount); + tag.putInt(KEY_ERROR_COUNT, errorCount); + } + + public void load(CompoundTag tag) { + completedSteps = tag.getInt(KEY_COMPLETED_STEPS); + currentPath.clear(); + currentPath.addAll(Arrays.stream(tag.getIntArray(KEY_CURRENT_PATH)).boxed().toList()); + compileTree.load(tag); + + ListTag list = tag.getList(KEY_PROBLEMS_LIST, 10); + for (int i = 0; i < list.size(); i++) { + CompoundTag problemTag = list.getCompound(i); + CompileProblem problem = CompileProblemType.createById(problemTag.getByte("_type") & 0xFF); + problem.load(problemTag); + addProblemInternal(problem); + } + } + + public void writeDesc(MCDataOutput out) { + compileTree.writeDesc(out); + out.writeInt(completedSteps); + out.writeInt(currentPath.size()); + for (int i : currentPath) out.writeInt(i); + + out.writeVarInt(problems.size()); + for (CompileProblem problem : problems) { + out.writeByte(problem.type.getID()); + problem.writeDesc(out); + } + } + + public void readDesc(MCDataInput in) { + clear(); + compileTree.readDesc(in); + completedSteps = in.readInt(); + int size = in.readInt(); + for (int i = 0; i < size; i++) currentPath.add(in.readInt()); + + size = in.readVarInt(); + for (int i = 0; i < size; i++) { + CompileProblem problem = CompileProblemType.createById(in.readUByte()); + problem.readDesc(in); + addProblemInternal(problem); + } + } + + public void clear() { + compileTree.clear(); + currentPath.clear(); + completedSteps = 0; + problems.clear(); + warningCount = 0; + errorCount = 0; + } + + public int getTotalSteps() { + return compileTree.size(); + } + + public int getCompletedSteps() { + return completedSteps; + } + + public List getProblems() { + return problems; + } + + public int getErrorCount() { + return errorCount; + } + + public int getWarningCount() { + return warningCount; + } + + //region ICStepThroughAssembler.EventReceiver + @Override + public void onStepAdded(ICStepThroughAssembler.AssemblerStepDescriptor descriptor) { + CompileTreeNode node = compileTree.findOrCreateNode(descriptor.getTreePath()); + node.step = descriptor.getStepType(); + + sendNodeAdded(node, descriptor.getTreePath()); + } + + @Override + public void onStepExecuted(ICStepThroughAssembler.AssemblerStepResult result) { + CompileTreeNode node = compileTree.findOrCreateNode(result.getTreePath()); + node.tileCoords.addAll(result.getTileCoords()); + node.registerIds.addAll(result.getRegisterIds()); + node.gateIds.addAll(result.getGateIds()); + node.registerRemaps.putAll(result.getRemappedRegisterIds()); + + currentPath.clear(); + currentPath.addAll(result.getTreePath()); + completedSteps++; + + sendNodeExecuted(node, result.getTreePath()); + } + //endregion + + //region Compile-time logging + public void clearAndSend() { + clear(); + sendClear(); + } + + public void addProblem(CompileProblem problem) { + addProblemInternal(problem); + sendProblemAdded(problem); + } + //endregion + + //region Packet handling + public void readLogStream(MCDataInput in, int key) { + switch (key) { + case KEY_COMPILER_LOG_CLEARED: + clear(); + break; + case KEY_COMPILER_LOG_NODE_ADDED: + readNodeAdded(in); + break; + case KEY_COMPILER_LOG_NODE_EXECUTED: + readNodeExecuted(in); + break; + case KEY_COMPILER_LOG_PROBLEM_ADDED: + readProblemAdded(in); + break; + default: + throw new IllegalArgumentException("Unknown compiler stream key: " + key); + } + } + //endregion + + //region Server-side utilities + private void sendClear() { + stateMachine.getStateMachineStream(KEY_COMPILER_LOG_CLEARED); + } + + private void sendNodeAdded(CompileTreeNode node, List treePath) { + MCDataOutput out = stateMachine.getStateMachineStream(KEY_COMPILER_LOG_NODE_ADDED); + out.writeByte(treePath.size()); + for (int i : treePath) { + out.writeByte(i); + } + out.writeByte(node.step.ordinal()); + } + + private void sendNodeExecuted(CompileTreeNode node, List treePath) { + MCDataOutput out = stateMachine.getStateMachineStream(KEY_COMPILER_LOG_NODE_EXECUTED); + out.writeByte(treePath.size()); + for (int i : treePath) { + out.writeByte(i); + } + node.write(out); + } + + private void sendProblemAdded(CompileProblem problem) { + MCDataOutput out = stateMachine.getStateMachineStream(KEY_COMPILER_LOG_PROBLEM_ADDED); + out.writeByte(problem.type.ordinal()); + problem.writeDesc(out); + } + + private void addProblemInternal(CompileProblem problem) { + problems.add(problem); + switch (problem.severity) { + case WARNING -> warningCount++; + case ERROR -> errorCount++; + } + } + //endregion + + //region Client-side utilities + private void readNodeAdded(MCDataInput in) { + int pathLength = in.readUByte(); + List path = new ArrayList<>(pathLength); + for (int i = 0; i < pathLength; i++) { + path.add((int) in.readUByte()); + } + CompileTreeNode node = compileTree.findOrCreateNode(path); + node.step = ICStepThroughAssembler.AssemblerStepType.values()[in.readUByte()]; + } + + private void readNodeExecuted(MCDataInput in) { + int pathLength = in.readUByte(); + List path = new ArrayList<>(pathLength); + for (int i = 0; i < pathLength; i++) { + path.add((int) in.readUByte()); + } + CompileTreeNode node = compileTree.findOrCreateNode(path); + node.read(in); + + currentPath.clear(); + currentPath.addAll(path); + completedSteps++; + } + + private void readProblemAdded(MCDataInput in) { + CompileProblem problem = CompileProblemType.createById(in.readUByte()); + problem.readDesc(in); + addProblemInternal(problem); + } + + public List getCurrentStack() { + return compileTree.getStack(currentPath); + } + + public int getProgressScaled(int scale) { + int size = compileTree.size(); + return size == 0 ? 0 : completedSteps * scale / size; + } + //endregion + + public static class CompileTreeNode { + + public final List children = new ArrayList<>(); + + public ICStepThroughAssembler.AssemblerStepType step; + public final List tileCoords = new ArrayList<>(); + public final List registerIds = new ArrayList<>(); + public final List gateIds = new ArrayList<>(); + public final Map registerRemaps = new HashMap<>(); + + public void save(CompoundTag tag) { + tag.putByte("step", (byte) step.ordinal()); + + ListTag tileCoordsTag = new ListTag(); + for (TileCoord coord : tileCoords) { + CompoundTag coordTag = new CompoundTag(); + coordTag.putByte("x", (byte) coord.x); + coordTag.putByte("y", (byte) coord.y); + coordTag.putByte("z", (byte) coord.z); + tileCoordsTag.add(coordTag); + } + tag.put("tileCoords", tileCoordsTag); + + tag.putIntArray("registerIds", registerIds); + tag.putIntArray("gateIds", gateIds); + + ListTag remapsTag = new ListTag(); + for (Map.Entry entry : registerRemaps.entrySet()) { + CompoundTag remapTag = new CompoundTag(); + remapTag.putInt("k", entry.getKey()); + remapTag.putInt("v", entry.getValue()); + remapsTag.add(remapTag); + } + tag.put("registerRemaps", remapsTag); + + ListTag childrenTag = new ListTag(); + for (CompileTreeNode child : children) { + CompoundTag childTag = new CompoundTag(); + child.save(childTag); + childrenTag.add(childTag); + } + tag.put("children", childrenTag); + } + + public void load(CompoundTag tag) { + step = ICStepThroughAssembler.AssemblerStepType.values()[tag.getByte("step")]; + + // Note: this is only called on fresh instance. No need to clear lists + + ListTag tileCoordsTag = tag.getList("tileCoords", Tag.TAG_COMPOUND); + for (Tag coordTag : tileCoordsTag) { + CompoundTag coordTagCompound = (CompoundTag) coordTag; + tileCoords.add(new TileCoord( + coordTagCompound.getByte("x"), + coordTagCompound.getByte("y"), + coordTagCompound.getByte("z") + )); + } + + registerIds.addAll(Arrays.stream(tag.getIntArray("registerIds")).boxed().collect(Collectors.toList())); + gateIds.addAll(Arrays.stream(tag.getIntArray("gateIds")).boxed().collect(Collectors.toList())); + + ListTag remapsTag = tag.getList("registerRemaps", Tag.TAG_COMPOUND); + for (Tag remapTag : remapsTag) { + CompoundTag remapTagCompound = (CompoundTag) remapTag; + registerRemaps.put(remapTagCompound.getInt("k"), remapTagCompound.getInt("v")); + } + + children.clear(); + ListTag childrenTag = tag.getList("children", Tag.TAG_COMPOUND); + for (Tag childTag : childrenTag) { + CompileTreeNode child = new CompileTreeNode(); + child.load((CompoundTag) childTag); + children.add(child); + } + } + + public void write(MCDataOutput out) { + out.writeByte(step.ordinal()); + out.writeByte(tileCoords.size()); + for (TileCoord coord : tileCoords) { + out.writeByte(coord.x).writeByte(coord.y).writeByte(coord.z); + } + out.writeByte(registerIds.size()); + for (int i : registerIds) { + out.writeVarInt(i); + } + out.writeByte(gateIds.size()); + for (int i : gateIds) { + out.writeVarInt(i); + } + out.writeByte(registerRemaps.size()); + registerRemaps.forEach((k, v) -> { + out.writeVarInt(k); + out.writeVarInt(v); + }); + } + + public void read(MCDataInput in) { + step = ICStepThroughAssembler.AssemblerStepType.values()[in.readUByte()]; + int size = in.readUByte(); + tileCoords.clear(); + for (int i = 0; i < size; i++) { + tileCoords.add(new TileCoord(in.readByte(), in.readByte(), in.readByte())); + } + size = in.readUByte(); + registerIds.clear(); + for (int i = 0; i < size; i++) { + registerIds.add(in.readVarInt()); + } + size = in.readVarInt(); + gateIds.clear(); + for (int i = 0; i < size; i++) { + gateIds.add(in.readVarInt()); + } + size = in.readVarInt(); + registerRemaps.clear(); + for (int i = 0; i < size; i++) { + registerRemaps.put(in.readVarInt(), in.readVarInt()); + } + } + + protected void writeDesc(MCDataOutput out) { + write(out); + out.writeByte(children.size()); + for (CompileTreeNode child : children) { + child.writeDesc(out); + } + } + + protected void readDesc(MCDataInput in) { + read(in); + children.clear(); + int size = in.readUByte(); + for (int i = 0; i < size; i++) { + CompileTreeNode child = new CompileTreeNode(); + child.readDesc(in); + children.add(child); + } + } + } + + public static class CompileTree { + + private final List rootNodes = new ArrayList<>(); + + private int size = 0; + + /** + * Find the node at the given path, creating empty nodes when necessary + */ + public CompileTreeNode findOrCreateNode(List path) { + Iterator pathIterator = path.iterator(); + + int ri = pathIterator.next(); + CompileTreeNode node = ri < rootNodes.size() ? rootNodes.get(ri) : null; + if (node == null) { + node = new CompileTreeNode(); + rootNodes.add(ri, node); + size++; + } + + while (pathIterator.hasNext()) { + int i = pathIterator.next(); + CompileTreeNode next = i < node.children.size() ? node.children.get(i) : null; + if (next == null) { + next = new CompileTreeNode(); + node.children.add(i, next); + size++; + } + node = next; + } + return node; + } + + public List getStack(List path) { + if (path.isEmpty()) return Collections.emptyList(); + + List stack = new ArrayList<>(path.size()); + Iterator pathIterator = path.iterator(); + + int ri = pathIterator.next(); + CompileTreeNode node = ri < rootNodes.size() ? rootNodes.get(ri) : null; + if (node == null) { + return stack; + } + stack.add(node); + + while (pathIterator.hasNext()) { + int i = pathIterator.next(); + CompileTreeNode next = i < node.children.size() ? node.children.get(i) : null; + if (next == null) { + return stack; + } + stack.add(next); + node = next; + } + return stack; + } + + public int size() { + return size; + } + + public void clear() { + rootNodes.clear(); + size = 0; + } + + public void save(CompoundTag tag) { + tag.putInt("size", size); + ListTag rootNodesTag = new ListTag(); + for (CompileTreeNode rootNode : rootNodes) { + CompoundTag rootNodeTag = new CompoundTag(); + rootNode.save(rootNodeTag); + rootNodesTag.add(rootNodeTag); + } + tag.put("rootNodes", rootNodesTag); + } + + public void load(CompoundTag tag) { + size = tag.getInt("size"); + rootNodes.clear(); + ListTag rootNodesTag = tag.getList("rootNodes", Tag.TAG_COMPOUND); + for (Tag rootNodeTag : rootNodesTag) { + CompileTreeNode rootNode = new CompileTreeNode(); + rootNode.load((CompoundTag) rootNodeTag); + rootNodes.add(rootNode); + } + } + + public void writeDesc(MCDataOutput out) { + out.writeVarInt(size); + out.writeByte(rootNodes.size()); + for (CompileTreeNode node : rootNodes) { + node.writeDesc(out); + } + } + + public void readDesc(MCDataInput in) { + size = in.readVarInt(); + int size = in.readUByte(); + rootNodes.clear(); + for (int i = 0; i < size; i++) { + CompileTreeNode node = new CompileTreeNode(); + node.readDesc(in); + rootNodes.add(node); + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/IODirectionMismatchError.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/IODirectionMismatchError.java new file mode 100644 index 000000000..26be91d2d --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/IODirectionMismatchError.java @@ -0,0 +1,33 @@ +package mrtjp.projectred.fabrication.engine.log; + +import mrtjp.fengine.TileCoord; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.Collection; +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.ICWorkbenchEditor.UNIFORM_GRAY; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_IO_DIR_MISMATCH_DESC; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_IO_DIR_MISMATCH_TITLE; + +public class IODirectionMismatchError extends MultiPositionProblem { + + public IODirectionMismatchError(Collection coordList) { + super(CompileProblemType.IO_DIR_MISMATCH, CompileProblemSeverity.ERROR, coordList); + } + + public IODirectionMismatchError() { + super(CompileProblemType.IO_DIR_MISMATCH, CompileProblemSeverity.ERROR); + } + + @Override + public Component getName() { + return new TranslatableComponent(UL_IO_DIR_MISMATCH_TITLE); + } + + @Override + public void buildToolTip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_IO_DIR_MISMATCH_DESC).withStyle(UNIFORM_GRAY)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/MultiPositionProblem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/MultiPositionProblem.java new file mode 100644 index 000000000..87de47b58 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/MultiPositionProblem.java @@ -0,0 +1,79 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.EditorDataUtils; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import static mrtjp.projectred.fabrication.engine.log.CompileProblemSeverity.ERROR; + +public abstract class MultiPositionProblem extends CompileProblem { + + public final List coordList = new LinkedList<>(); + + public MultiPositionProblem(CompileProblemType type, CompileProblemSeverity severity) { + super(type, severity); + } + + public MultiPositionProblem(CompileProblemType type, CompileProblemSeverity severity, Collection coordList) { + this(type, severity); + this.coordList.addAll(coordList); + } + + @Override + public void save(CompoundTag tag) { + EditorDataUtils.saveTileCoordList(tag, "coordList", coordList); + } + + @Override + public void load(CompoundTag tag) { + coordList.clear(); + EditorDataUtils.loadTileCoordList(tag, "coordList", coordList); + } + + @Override + public void writeDesc(MCDataOutput out) { + out.writeShort(coordList.size()); + for (TileCoord coord : coordList) { + out.writeByte(coord.x).writeByte(coord.y).writeByte(coord.z); + } + } + + @Override + public void readDesc(MCDataInput in) { + coordList.clear(); + int size = in.readUShort(); + for (int i = 0; i < size; i++) { + coordList.add(new TileCoord(in.readByte(), in.readByte(), in.readByte())); + } + } + + @Override + public void buildToolTip(List tooltip, TileCoord hoverPosition) { + if (coordList.contains(hoverPosition)) { + buildToolTip(tooltip); + } + } + + @Override + public void renderOverlay(Vector3 mousePosition, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + ccrs.baseColour = severity == ERROR ? EnumColour.RED.rgba(200) : EnumColour.YELLOW.rgba(200); + Vector3 vec = new Vector3(); + for (TileCoord coord : coordList) { + vec.set(coord.x, coord.y, coord.z).add(0.5); + ICRenderTypes.renderSelection(ccrs, vec, vec, 3 / 16D, 2 / 16D); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/MultipleDriversError.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/MultipleDriversError.java new file mode 100644 index 000000000..467050440 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/MultipleDriversError.java @@ -0,0 +1,108 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.EditorDataUtils; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import net.minecraft.ChatFormatting; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.ArrayList; +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.ICWorkbenchEditor.UNIFORM_GRAY; +import static mrtjp.projectred.fabrication.engine.log.CompileProblemSeverity.ERROR; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_MULTIPLE_DRIVERS_DESC; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_MULTIPLE_DRIVERS_TITLE; + +public class MultipleDriversError extends CompileProblem { + + public TileCoord coord; + public final List registerList = new ArrayList<>(); + + public MultipleDriversError() { + super(CompileProblemType.MULTIPLE_DRIVERS, CompileProblemSeverity.ERROR); + } + + public MultipleDriversError(TileCoord coord, List registerList) { + super(CompileProblemType.MULTIPLE_DRIVERS, CompileProblemSeverity.ERROR); + this.coord = coord; + this.registerList.addAll(registerList); + } + + @Override + public void save(CompoundTag tag) { + tag.put("coord", EditorDataUtils.tileCoordToNBT(coord)); + tag.putIntArray("registers", registerList.stream().mapToInt(i -> i).toArray()); + } + + @Override + public void load(CompoundTag tag) { + coord = EditorDataUtils.tileCoordFromNBT(tag.getCompound("coord")); + registerList.clear(); + for (int i : tag.getIntArray("registers")) { + registerList.add(i); + } + } + + @Override + public void writeDesc(MCDataOutput out) { + out.writeByte(coord.x).writeByte(coord.y).writeByte(coord.z); + out.writeShort(registerList.size()); + for (int i : registerList) { + out.writeVarInt(i); + } + } + + @Override + public void readDesc(MCDataInput in) { + coord = new TileCoord(in.readByte(), in.readByte(), in.readByte()); + registerList.clear(); + int size = in.readUShort(); + for (int i = 0; i < size; i++) { + registerList.add(in.readVarInt()); + } + } + + @Override + public Component getName() { + return new TranslatableComponent(UL_MULTIPLE_DRIVERS_TITLE); + } + + @Override + public void buildToolTip(List tooltip, TileCoord hoverPosition) { + if (coord.equals(hoverPosition)) { + buildToolTip(tooltip); + } + } + + @Override + public void buildToolTip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_MULTIPLE_DRIVERS_DESC).withStyle(UNIFORM_GRAY)); + + StringBuilder s = new StringBuilder(); + for (int r : registerList) { + s.append("R").append(r).append(", "); + } + s.delete(s.length() - 2, s.length()); // remove trailing comma + tooltip.add(new TextComponent(" ").withStyle(UNIFORM_GRAY).append( + new TextComponent(s.toString()).withStyle(UNIFORM_GRAY))); + } + + @Override + public void renderOverlay(Vector3 mousePosition, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + ccrs.baseColour = severity == ERROR ? EnumColour.RED.rgba(200) : EnumColour.YELLOW.rgba(200); + Vector3 vec = new Vector3(coord.x, coord.y, coord.z).add(0.5); + ICRenderTypes.renderSelection(ccrs, vec, vec, 3 / 16D, 2 / 16D); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/NoInputsError.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/NoInputsError.java new file mode 100644 index 000000000..e36736876 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/NoInputsError.java @@ -0,0 +1,61 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.ICWorkbenchEditor.UNIFORM_GRAY; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_NO_INPUTS_DESC; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_NO_INPUTS_TITLE; + +public class NoInputsError extends CompileProblem { + + public NoInputsError() { + super(CompileProblemType.NO_INPUTS, CompileProblemSeverity.ERROR); + } + + @Override + public void save(CompoundTag tag) { + } + + @Override + public void load(CompoundTag tag) { + } + + @Override + public void writeDesc(MCDataOutput out) { + } + + @Override + public void readDesc(MCDataInput in) { + } + + @Override + public Component getName() { + return new TranslatableComponent(UL_NO_INPUTS_TITLE); + } + + @Override + public void buildToolTip(List tooltip, TileCoord hoverPosition) { + + } + + @Override + public void buildToolTip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_NO_INPUTS_DESC).withStyle(UNIFORM_GRAY)); + } + + @Override + public void renderOverlay(Vector3 mousePosition, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/NoOutputsError.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/NoOutputsError.java new file mode 100644 index 000000000..41b3ed6f9 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/NoOutputsError.java @@ -0,0 +1,61 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.ICWorkbenchEditor.UNIFORM_GRAY; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class NoOutputsError extends CompileProblem { + + public NoOutputsError() { + super(CompileProblemType.NO_OUTPUTS, CompileProblemSeverity.ERROR); + } + + @Override + public void save(CompoundTag tag) { + } + + @Override + public void load(CompoundTag tag) { + } + + @Override + public void writeDesc(MCDataOutput out) { + } + + @Override + public void readDesc(MCDataInput in) { + } + + @Override + public Component getName() { + return new TranslatableComponent(UL_NO_OUTPUTS_TITLE); + } + + @Override + public void buildToolTip(List tooltip, TileCoord hoverPosition) { + + } + + @Override + public void buildToolTip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_NO_OUTPUTS_DESC).withStyle(UNIFORM_GRAY)); + } + + @Override + public void renderOverlay(Vector3 mousePosition, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/SimpleLocatableProblem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/SimpleLocatableProblem.java new file mode 100644 index 000000000..60db2f7b1 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/log/SimpleLocatableProblem.java @@ -0,0 +1,66 @@ +package mrtjp.projectred.fabrication.engine.log; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.EditorDataUtils; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; + +import java.util.List; + +import static mrtjp.projectred.fabrication.engine.log.CompileProblemSeverity.ERROR; + +public abstract class SimpleLocatableProblem extends CompileProblem { + + public TileCoord coord; + + public SimpleLocatableProblem(CompileProblemType type, CompileProblemSeverity severity) { + super(type, severity); + } + + public SimpleLocatableProblem(CompileProblemType type, CompileProblemSeverity severity, TileCoord coord) { + this(type, severity); + this.coord = coord; + } + + @Override + public void save(CompoundTag tag) { + tag.put("coord", EditorDataUtils.tileCoordToNBT(coord)); + } + + @Override + public void load(CompoundTag tag) { + coord = EditorDataUtils.tileCoordFromNBT(tag.getCompound("coord")); + } + + @Override + public void writeDesc(MCDataOutput out) { + out.writeByte(coord.x).writeByte(coord.y).writeByte(coord.z); + } + + @Override + public void readDesc(MCDataInput in) { + coord = new TileCoord(in.readByte(), in.readByte(), in.readByte()); + } + + @Override + public void buildToolTip(List tooltip, TileCoord hoverPosition) { + if (coord.equals(hoverPosition)) { + buildToolTip(tooltip); + } + } + + @Override + public void renderOverlay(Vector3 mousePosition, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + ccrs.baseColour = severity == ERROR ? EnumColour.RED.rgba(200) : EnumColour.YELLOW.rgba(200); + Vector3 vec = new Vector3(coord.x, coord.y, coord.z).add(0.5); + ICRenderTypes.renderSelection(ccrs, vec, vec, 3 / 16D, 2 / 16D); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/BundledWireTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/BundledWireTile.java new file mode 100644 index 000000000..e54f414e8 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/BundledWireTile.java @@ -0,0 +1,43 @@ +package mrtjp.projectred.fabrication.engine.wires; + +import mrtjp.fengine.api.PropagationFunction; +import mrtjp.projectred.fabrication.engine.IBundledConnectableICTile; +import mrtjp.projectred.fabrication.engine.ICTileType; +import mrtjp.projectred.fabrication.engine.IConnectableICTile; +import mrtjp.projectred.fabrication.engine.IInsulatedConnectableICTile; +import mrtjp.projectred.transmission.WireType; + +public class BundledWireTile extends WireTile implements IBundledConnectableICTile { + + public BundledWireTile(int colour) { + super(colour == -1 ? ICTileType.BUNDLED_NEUTRAL_WIRE : ICWireTileType.BUNDLED_COLOURED[colour].tileType, + WireType.values()[WireType.BUNDLED_WHITE.ordinal() + colour]); + } + + @Override + public int getBundledColour() { + return getWireType().getColourIdx(); + } + + @Override + public boolean canConnectTo(IConnectableICTile target, int towardsDir) { + + if (target instanceof IBundledConnectableICTile) { + int targetColour = ((IBundledConnectableICTile) target).getBundledColour(); + int thisColour = getBundledColour(); + return targetColour == thisColour || targetColour == -1 || thisColour == -1; // -1 means neutral + } + + if (target instanceof IInsulatedConnectableICTile) + return true; + + return false; + } + + @Override + public PropagationFunction propagationFunc(int inDir, int inPort) { + // Accept any colour, but only forward to that specific colour + return ((outDir, outPort) -> + outPort == inPort && maskConnectsToDir(inDir) && maskConnectsToDir(outDir)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/ICWireTileType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/ICWireTileType.java new file mode 100644 index 000000000..52916c64f --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/ICWireTileType.java @@ -0,0 +1,92 @@ +package mrtjp.projectred.fabrication.engine.wires; + +import mrtjp.projectred.fabrication.engine.ICTileType; +import mrtjp.projectred.transmission.WireType; + +public enum ICWireTileType { + + //@formatter:off + RED_ALLOY(ICTileType.RED_ALLOY_WIRE, WireType.RED_ALLOY), + + INSULATED_WHITE (ICTileType.INSULATED_WHITE_WIRE, WireType.INSULATED_WHITE), + INSULATED_ORANGE (ICTileType.INSULATED_ORANGE_WIRE, WireType.INSULATED_ORANGE), + INSULATED_MAGENTA (ICTileType.INSULATED_MAGENTA_WIRE, WireType.INSULATED_MAGENTA), + INSULATED_LIGHT_BLUE(ICTileType.INSULATED_LIGHT_BLUE_WIRE, WireType.INSULATED_LIGHT_BLUE), + INSULATED_YELLOW (ICTileType.INSULATED_YELLOW_WIRE, WireType.INSULATED_YELLOW), + INSULATED_LIME (ICTileType.INSULATED_LIME_WIRE, WireType.INSULATED_LIME), + INSULATED_PINK (ICTileType.INSULATED_PINK_WIRE, WireType.INSULATED_PINK), + INSULATED_GRAY (ICTileType.INSULATED_GRAY_WIRE, WireType.INSULATED_GRAY), + INSULATED_LIGHT_GRAY(ICTileType.INSULATED_LIGHT_GRAY_WIRE, WireType.INSULATED_LIGHT_GRAY), + INSULATED_CYAN (ICTileType.INSULATED_CYAN_WIRE, WireType.INSULATED_CYAN), + INSULATED_PURPLE (ICTileType.INSULATED_PURPLE_WIRE, WireType.INSULATED_PURPLE), + INSULATED_BLUE (ICTileType.INSULATED_BLUE_WIRE, WireType.INSULATED_BLUE), + INSULATED_BROWN (ICTileType.INSULATED_BROWN_WIRE, WireType.INSULATED_BROWN), + INSULATED_GREEN (ICTileType.INSULATED_GREEN_WIRE, WireType.INSULATED_GREEN), + INSULATED_RED (ICTileType.INSULATED_RED_WIRE, WireType.INSULATED_RED), + INSULATED_BLACK (ICTileType.INSULATED_BLACK_WIRE, WireType.INSULATED_BLACK), + + BUNDLED_NEUTRAL (ICTileType.BUNDLED_NEUTRAL_WIRE, WireType.BUNDLED_NEUTRAL), + + BUNDLED_WHITE (ICTileType.BUNDLED_WHITE_WIRE, WireType.BUNDLED_WHITE), + BUNDLED_ORANGE (ICTileType.BUNDLED_ORANGE_WIRE, WireType.BUNDLED_ORANGE), + BUNDLED_MAGENTA (ICTileType.BUNDLED_MAGENTA_WIRE, WireType.BUNDLED_MAGENTA), + BUNDLED_LIGHT_BLUE(ICTileType.BUNDLED_LIGHT_BLUE_WIRE, WireType.BUNDLED_LIGHT_BLUE), + BUNDLED_YELLOW (ICTileType.BUNDLED_YELLOW_WIRE, WireType.BUNDLED_YELLOW), + BUNDLED_LIME (ICTileType.BUNDLED_LIME_WIRE, WireType.BUNDLED_LIME), + BUNDLED_PINK (ICTileType.BUNDLED_PINK_WIRE, WireType.BUNDLED_PINK), + BUNDLED_GRAY (ICTileType.BUNDLED_GRAY_WIRE, WireType.BUNDLED_GRAY), + BUNDLED_LIGHT_GRAY(ICTileType.BUNDLED_LIGHT_GRAY_WIRE, WireType.BUNDLED_LIGHT_GRAY), + BUNDLED_CYAN (ICTileType.BUNDLED_CYAN_WIRE, WireType.BUNDLED_CYAN), + BUNDLED_PURPLE (ICTileType.BUNDLED_PURPLE_WIRE, WireType.BUNDLED_PURPLE), + BUNDLED_BLUE (ICTileType.BUNDLED_BLUE_WIRE, WireType.BUNDLED_BLUE), + BUNDLED_BROWN (ICTileType.BUNDLED_BROWN_WIRE, WireType.BUNDLED_BROWN), + BUNDLED_GREEN (ICTileType.BUNDLED_GREEN_WIRE, WireType.BUNDLED_GREEN), + BUNDLED_RED (ICTileType.BUNDLED_RED_WIRE, WireType.BUNDLED_RED), + BUNDLED_BLACK (ICTileType.BUNDLED_BLACK_WIRE, WireType.BUNDLED_BLACK), + ; + //@formatter:on + + public static final ICWireTileType[] INSULATED = { + INSULATED_WHITE, + INSULATED_ORANGE, + INSULATED_MAGENTA, + INSULATED_LIGHT_BLUE, + INSULATED_YELLOW, + INSULATED_LIME, + INSULATED_PINK, + INSULATED_GRAY, + INSULATED_LIGHT_GRAY, + INSULATED_CYAN, + INSULATED_PURPLE, + INSULATED_BLUE, + INSULATED_BROWN, + INSULATED_GREEN, + INSULATED_RED, + INSULATED_BLACK }; + + public static final ICWireTileType[] BUNDLED_COLOURED = { + BUNDLED_WHITE, + BUNDLED_ORANGE, + BUNDLED_MAGENTA, + BUNDLED_LIGHT_BLUE, + BUNDLED_YELLOW, + BUNDLED_LIME, + BUNDLED_PINK, + BUNDLED_GRAY, + BUNDLED_LIGHT_GRAY, + BUNDLED_CYAN, + BUNDLED_PURPLE, + BUNDLED_BLUE, + BUNDLED_BROWN, + BUNDLED_GREEN, + BUNDLED_RED, + BUNDLED_BLACK }; + + public final ICTileType tileType; + public final WireType multipartType; + + ICWireTileType(ICTileType tileType, WireType multipartType) { + this.tileType = tileType; + this.multipartType = multipartType; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/InsulatedWireTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/InsulatedWireTile.java new file mode 100644 index 000000000..2551da506 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/InsulatedWireTile.java @@ -0,0 +1,49 @@ +package mrtjp.projectred.fabrication.engine.wires; + +import mrtjp.fengine.api.PropagationFunction; +import mrtjp.projectred.fabrication.engine.IBundledConnectableICTile; +import mrtjp.projectred.fabrication.engine.IConnectableICTile; +import mrtjp.projectred.fabrication.engine.IInsulatedConnectableICTile; +import mrtjp.projectred.fabrication.engine.IRedstoneConnectableICTile; +import mrtjp.projectred.transmission.WireType; + +public class InsulatedWireTile extends RedstoneWireTile implements IInsulatedConnectableICTile, IRedstoneConnectableICTile { + + public InsulatedWireTile(int colour) { + super(ICWireTileType.INSULATED[colour].tileType, WireType.values()[WireType.INSULATED_WHITE.ordinal() + colour]); + } + + @Override + protected int getRenderHue() { + return -1; // Insulated wires don't render hue + } + + @Override + public int getInsulatedColour() { + return getWireType().getColourIdx(); + } + + @Override + protected int getTextureIndex() { + return signal != 0 ? 1 : 0; + } + + @Override + public boolean canConnectTo(IConnectableICTile target, int towardsDir) { + if (target instanceof IInsulatedConnectableICTile) + return ((IInsulatedConnectableICTile) target).getInsulatedColour() == getInsulatedColour(); + + if (target instanceof IBundledConnectableICTile) + return true; + + return super.canConnectTo(target, towardsDir); + } + + @Override + public PropagationFunction propagationFunc(int inDir, int inPort) { + // Accept only matching colour, and forward only to that colour + return ((outDir, outPort) -> + inPort == getInsulatedColour() && outPort == inPort && + maskConnectsToDir(inDir) && maskConnectsToDir(outDir)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/RedAlloyWireTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/RedAlloyWireTile.java new file mode 100644 index 000000000..58a4992b3 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/RedAlloyWireTile.java @@ -0,0 +1,29 @@ +package mrtjp.projectred.fabrication.engine.wires; + +import mrtjp.fengine.api.PropagationFunction; +import mrtjp.projectred.fabrication.engine.ICTileType; +import mrtjp.projectred.transmission.WireType; + +public class RedAlloyWireTile extends RedstoneWireTile { + + public RedAlloyWireTile() { + super(ICTileType.RED_ALLOY_WIRE, WireType.RED_ALLOY); + } + + @Override + protected int getRenderHue() { + return (signal&0xFF)/2+60<<24|0xFF; + } + + @Override + protected int getTextureIndex() { + return 0; + } + + @Override + public PropagationFunction propagationFunc(int inDir, int inPort) { + // Accept any colour, forward to all colours + return ((outDir, outPort) -> maskConnectsToDir(inDir) && maskConnectsToDir(outDir)); + } + +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/RedstoneWireTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/RedstoneWireTile.java new file mode 100644 index 000000000..dbd88a16f --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/RedstoneWireTile.java @@ -0,0 +1,120 @@ +package mrtjp.projectred.fabrication.engine.wires; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.api.IPathFinderManifest; +import mrtjp.projectred.fabrication.engine.ICSimulationContainer; +import mrtjp.projectred.fabrication.engine.ICTileType; +import mrtjp.projectred.fabrication.engine.IConnectableICTile; +import mrtjp.projectred.fabrication.engine.IRedstoneConnectableICTile; +import mrtjp.projectred.fabrication.engine.log.DeadWireWarning; +import mrtjp.projectred.transmission.WireType; +import net.minecraft.nbt.CompoundTag; + +import java.util.HashSet; +import java.util.Set; + +public abstract class RedstoneWireTile extends WireTile implements IRedstoneConnectableICTile { + + private static final int PACKET_SIGNAL = 2; + + protected final Set inputRegisters = new HashSet<>(); + + protected byte signal = 0; + + public RedstoneWireTile(ICTileType tileType, WireType renderType) { + super(tileType, renderType); + } + @Override + public void save(CompoundTag tag) { + super.save(tag); + tag.putByte("signal", signal); + tag.putByte("numInputReg", (byte) inputRegisters.size()); + + int i = 0; + for (Integer inputReg : inputRegisters) tag.putInt("inR" + (i++), inputReg); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + signal = tag.getByte("signal"); + + int numInputReg = tag.getByte("numInputReg") & 0xFF; + for (int i = 0; i < numInputReg; i++) inputRegisters.add(tag.getInt("inR" + i)); + } + + @Override + public void writeDesc(MCDataOutput out) { + super.writeDesc(out); + out.writeByte(signal); + } + + @Override + public void readDesc(MCDataInput in) { + super.readDesc(in); + signal = in.readByte(); + } + + @Override + public void read(MCDataInput in, int key) { + switch (key) { + case PACKET_SIGNAL: + signal = in.readByte(); + break; + default: + super.read(in, key); + } + } + + protected void sendSignalUpdate() { + getWriteStream(PACKET_SIGNAL).writeByte(signal); + } + + @Override + public boolean canConnectTo(IConnectableICTile target, int towardsDir) { + if (target instanceof IRedstoneConnectableICTile) + return true; + + return false; + } + + @Override + public void onSimRegistersChanged(int rMask, ICSimulationContainer container) { + + byte oldSignal = signal; + signal = 0; + + for (Integer inputReg : inputRegisters) { + boolean isHigh = container.pullRegisterValue(inputReg) > 0; + signal = isHigh ? (byte) 255 : 0; + if (isHigh) break; + } + + if (oldSignal != signal) sendSignalUpdate(); + } + + + @Override + public void searchManifest(IPathFinderManifest manifest) { + inputRegisters.clear(); + inputRegisters.addAll(manifest.getOutputRegisters()); + + if (inputRegisters.isEmpty()) { + getEditor().getStateMachine().getCompilerLog().addProblem(new DeadWireWarning(getPos())); + } + } + + @Override + public void consumeRemaps(RemapProvider remapProvider) { + + Set remappedInputs = new HashSet<>(); + for (Integer inputReg : inputRegisters) { + int remapped = remapProvider.getRemappedRegisterID(inputReg); + remappedInputs.add(remapped); + } + + inputRegisters.clear(); + inputRegisters.addAll(remappedInputs); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/WireTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/WireTile.java new file mode 100644 index 000000000..39ed80f95 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/engine/wires/WireTile.java @@ -0,0 +1,127 @@ +package mrtjp.projectred.fabrication.engine.wires; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Transformation; +import mrtjp.projectred.fabrication.engine.BaseTile; +import mrtjp.projectred.fabrication.engine.ICTileType; +import mrtjp.projectred.fabrication.engine.IConnectableICTile; +import mrtjp.projectred.fabrication.engine.IRotatableICTile; +import mrtjp.projectred.transmission.WireType; +import mrtjp.projectred.transmission.client.WireModelRenderer; +import net.minecraft.nbt.CompoundTag; + +import java.util.Optional; + +public abstract class WireTile extends BaseTile implements IConnectableICTile { + + private static final int PACKET_CONN_MASK = 1; + + private final WireType renderType; + + public WireTile(ICTileType tileType, WireType renderType) { + super(tileType); + this.renderType = renderType; + } + + private byte connMask = 0; + + public WireType getWireType() { + return renderType; + } + + @Override + public int getConnMask() { + return connMask & 0xFF; + } + + @Override + public void setConnMask(int connMask) { + this.connMask = (byte) connMask; + } + + @Override + public void save(CompoundTag tag) { + tag.putByte("connMask", connMask); + } + + @Override + public void load(CompoundTag tag) { + connMask = tag.getByte("connMask"); + } + + @Override + public void writeDesc(MCDataOutput out) { + out.writeByte(connMask); + } + + @Override + public void readDesc(MCDataInput in) { + connMask = in.readByte(); + } + + @Override + public void read(MCDataInput in, int key) { + switch (key) { + case PACKET_CONN_MASK: + connMask = in.readByte(); + break; + default: + super.read(in, key); + } + } + + protected void sendConnUpdate() { + getWriteStream(PACKET_CONN_MASK).writeByte(connMask); + } + + @Override + public void onMaskChanged() { + sendConnUpdate(); + getEditor().markTileChange(); + } + + @Override + public void onNeighborChanged() { + super.onNeighborChanged(); + updateConns(); + } + + @Override + public void onAdded() { + super.onAdded(); + updateConns(); + } + + @Override + public void onRemoved() { + super.onRemoved(); + for (int s = 0; s < 6; s++) { + getEditor().queueNeighborChange(getPos().offset(s)); + } + } + + @Override + public IConnectableICTile getTileTowardsDir(int dir) { + Optional tile = getMap().getBaseTile(getPos().offset(dir)); + if (tile.isPresent() && tile.get() instanceof IConnectableICTile) return (IConnectableICTile) tile.get(); + return null; + } + + protected int getRenderHue() { + return -1; + } + + protected int getTextureIndex() { + return 0; + } + + @Override + public void renderTile(CCRenderState ccrs, Transformation t, float partialFrame) { + int rmask = IRotatableICTile.dirMaskToRotationMask(getConnMask()); + int wireConnMask = rmask << 4; + int modelKey = WireModelRenderer.modelKey(0, getWireType().getThickness(), wireConnMask); + WireModelRenderer.render(ccrs, modelKey, getRenderHue(), getWireType().getTextures().get(getTextureIndex()), t); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ButtonArrayNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ButtonArrayNode.java new file mode 100644 index 000000000..34b30e619 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ButtonArrayNode.java @@ -0,0 +1,86 @@ +package mrtjp.projectred.fabrication.gui; + +import mrtjp.projectred.redui.AbstractButtonNode; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.ButtonNode; + +import java.util.ArrayList; + +public class ButtonArrayNode extends AbstractGuiNode { + + private final Listener listener; + + private final int rows; + private final int columns; + private final int spacing; + + private final ArrayList buttons = new ArrayList<>(); + + public ButtonArrayNode(Listener listener, int rows, int columns, int spacing) { + this.listener = listener; + this.rows = rows; + this.columns = columns; + this.spacing = spacing; + + initButtons(); + } + + private void initButtons() { + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + AbstractButtonNode button = createButton(i * columns + j); + addChild(button); + buttons.add(button); + } + } + } + + private void resizeButtons(int bw, int bh, int spacing) { + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < columns; j++) { + AbstractButtonNode button = buttons.get(i * columns + j); + button.setSize(bw, bh); + button.setPosition(j * (bw + spacing), i * (bh + spacing)); + } + } + + setSize(columns * (bw + spacing) - spacing, rows * (bh + spacing) - spacing); + } + + @Override + public void setSize(int width, int height) { + super.setSize(width, height); + } + + public void setGridSize(int width, int height) { + int bw = (width + spacing) / columns - spacing; // width = columns * (bw + spacing) - spacing + int bh = (height + spacing) / rows - spacing; // height = rows * (bh + spacing) - spacing + resizeButtons(bw, bh, spacing); + } + + public void setButtonSize(int buttonWidth, int buttonHeight) { + resizeButtons(buttonWidth, buttonHeight, spacing); + } + + protected AbstractButtonNode createButton(int index) { + + ButtonNode button = new ButtonNode(); + button.setButtonText(listener.getButtonText(index)); + button.setClickFunction(() -> listener.onButtonClicked(index)); + button.setIsSelectedFunction(() -> listener.isButtonSelected(index)); + button.setIsEnabledFunction(() -> listener.isButtonEnabled(index)); + + return button; + } + + public interface Listener { + String getButtonText(int index); + + void onButtonClicked(int index); + + boolean isButtonEnabled(int index); + + boolean isButtonSelected(int index); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CTNListNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CTNListNode.java new file mode 100644 index 000000000..700f8916c --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CTNListNode.java @@ -0,0 +1,110 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.engine.log.ICCompilerLog.CompileTreeNode; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchCompileTab; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; + +import java.util.LinkedList; +import java.util.List; + +public class CTNListNode extends AbstractGuiNode { + + private final List nodeList = new LinkedList<>(); + + private final AbstractGuiNode listParent = new AbstractGuiNode() { + + }; + + public CTNListNode() { + initSubNodes(); + } + + private void initSubNodes() { + addChild(listParent); + } + + public void setNodeList(List nodeList) { + this.nodeList.clear(); + this.nodeList.addAll(nodeList); + refreshListItems(); + } + + private void refreshListItems() { + listParent.removeAllChildren(); + listParent.setPosition(0, 0); + + int y = 0; + for (CompileTreeNode node : nodeList) { + CompileTreeNodeListItem item = new CompileTreeNodeListItem(node); + item.setPosition(0, y); + listParent.addChild(item); + y += item.calculateAccumulatedFrame().height(); + } + } + + @Override + public void onSubTreePreDrawBack() { + // This node's frame converted to GL11 window coordinates + Rect gl11Rect = calculateGL11Frame(); + + // Enable scissor using the calculated rect + RenderSystem.enableScissor(gl11Rect.x(), gl11Rect.y(), gl11Rect.width(), gl11Rect.height()); + } + + @Override + public void onSubTreePostDrawBack() { + // Disable scissor + RenderSystem.disableScissor(); + } + + private class CompileTreeNodeListItem extends AbstractGuiNode { + + private final CompileTreeNode node; + + public CompileTreeNodeListItem(CompileTreeNode node) { + this.node = node; + + setSize(67, 16); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 1, 358, getFrame().width(), getFrame().height(), 512, 512); + + String s = node.step.toString(); + if (s.length() > 10) { + s = s.substring(s.length() - 10); + } + getRoot().getFontRenderer().draw(stack, s, getFrame().x() + 2, getFrame().y() + 2, 0xFFFFFF); + } + + @Override + public void drawFront(PoseStack stack, Point mouse, float partialFrame) { + + if (!isFirstHit(mouse)) return; + + List toolTip = new LinkedList<>(); + toolTip.add(new TextComponent(node.step.toString())); //TODO localize + toolTip.add(new TextComponent("Positions: " + node.tileCoords.size())); + toolTip.add(new TextComponent("Registers: " + node.registerIds.size())); + toolTip.add(new TextComponent("Gates: " + node.gateIds.size())); + toolTip.add(new TextComponent("Remaps: " + node.registerRemaps.size())); + + renderTooltip(stack, mouse, toolTip); + } + + @Override + public boolean checkHit(Point absPoint) { + return super.checkHit(absPoint) && CTNListNode.this.convertParentRectToScreen(CTNListNode.this.getFrame()).contains(absPoint); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileProblemsTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileProblemsTab.java new file mode 100644 index 000000000..e63e1f686 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileProblemsTab.java @@ -0,0 +1,101 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.editor.tools.IICEditorTool; +import mrtjp.projectred.fabrication.engine.log.CompileProblem; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchCompileTab; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.ScrollBarNode; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; + +import java.util.List; + +public class CompileProblemsTab extends AbstractGuiNode implements ICompileOverlayRenderer { + + private final ICWorkbenchEditor editor; + private final ProblemListNode issueListNode = new ProblemListNode(); + + public CompileProblemsTab(ICWorkbenchEditor editor) { + this.editor = editor; + setSize(91, 134); + initSubNodes(); + } + + private void initSubNodes() { + + // Stack + issueListNode.setPosition(6, 31); + issueListNode.setSize(67, 95); + addChild(issueListNode); + + // Scrollbar //TODO + ScrollBar scrollBar = new ScrollBar(); + scrollBar.setPosition(77, 31); + scrollBar.setZPosition(0.2); + scrollBar.setSize(8, 95); + scrollBar.setSliderSize(8, 16); + addChild(scrollBar); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 184, 223, getFrame().width(), getFrame().height(), 512, 512); + } + + @Override + public void update() { + if (!isHidden()) { + // TODO only do this when issue log changes + issueListNode.setProblemList(editor.getStateMachine().getCompilerLog().getProblems()); + } + } + + //region ICompileTabOverlayRenderer + public void renderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + ccrs.reset(); + ccrs.bind(ICRenderTypes.selectionRenderType, Minecraft.getInstance().renderBuffers().bufferSource(), matrixStack); + + for (CompileProblem problem : editor.getStateMachine().getCompilerLog().getProblems()) { + problem.renderOverlay(mousePosition, ccrs, getter, matrixStack); + } + } + + @Override + public void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, List tooltip) { + + TileCoord pos = IICEditorTool.toNearestPosition(mousePosition); + for (CompileProblem issue : editor.getStateMachine().getCompilerLog().getProblems()) { + issue.buildToolTip(tooltip, pos); + } + } + //endregion + + private class ScrollBar extends ScrollBarNode { + + public ScrollBar() { + super(ScrollAxis.VERTICAL); + } + + @Override + protected void drawSlider(PoseStack stack, Rect sliderFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + GuiComponent.blit(stack, sliderFrame.x(), sliderFrame.y(), 305, 58, sliderFrame.width(), sliderFrame.height(), 512, 512); + } + + @Override + protected void adjustContent(double scrollPercentage) { + issueListNode.setScrollPercentage(scrollPercentage); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileStackTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileStackTab.java new file mode 100644 index 000000000..d095abcf4 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileStackTab.java @@ -0,0 +1,109 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.engine.log.ICCompilerLog; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchCompileTab; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.ScrollBarNode; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; + +import java.util.List; + +public class CompileStackTab extends AbstractGuiNode implements ICompileOverlayRenderer { + + private final ICWorkbenchEditor editor; + + private final CTNListNode ctnListNode = new CTNListNode(); + + public CompileStackTab(ICWorkbenchEditor editor) { + this.editor = editor; + + setSize(91, 134); + initSubNodes(); + } + + private void initSubNodes() { + + // Stack + ctnListNode.setPosition(6, 31); + ctnListNode.setSize(67, 95); + addChild(ctnListNode); + + // Scrollbar //TODO + ScrollBar scrollBar = new ScrollBar(); + scrollBar.setPosition(77, 31); + scrollBar.setZPosition(0.2); + scrollBar.setSize(8, 95); + scrollBar.setSliderSize(8, 16); + addChild(scrollBar); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 0, 223, getFrame().width(), getFrame().height(), 512, 512); + } + + @Override + public void update() { + if (!isHidden()) { + // TODO only do this when the stack changes + List execStack = editor.getStateMachine().getCompilerLog().getCurrentStack(); + ctnListNode.setNodeList(execStack); + } + } + + private void renderCompileTreeNode(ICCompilerLog.CompileTreeNode node, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + ccrs.reset(); + ccrs.bind(ICRenderTypes.selectionRenderType, Minecraft.getInstance().renderBuffers().bufferSource(), matrixStack); + ccrs.baseColour = EnumColour.LIGHT_BLUE.rgba(200); + + for (TileCoord pos : node.tileCoords) { + Vector3 p = new Vector3(pos.x, pos.y, pos.z); + ICRenderTypes.renderSelection(ccrs, p, p.copy().add(0.01), 3 / 16D, 2 / 16D); + } + } + + //region ICompileTabOverlayRenderer + @Override + public void renderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + for (ICCompilerLog.CompileTreeNode node : editor.getStateMachine().getCompilerLog().getCurrentStack()) { + renderCompileTreeNode(node, ccrs, getter, matrixStack); + } + } + + @Override + public void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, List tooltip) { + + } + //endregion + + private class ScrollBar extends ScrollBarNode { + + public ScrollBar() { + super(ScrollAxis.VERTICAL); + } + + @Override + protected void drawSlider(PoseStack stack, Rect sliderFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + GuiComponent.blit(stack, sliderFrame.x(), sliderFrame.y(), 305, 58, sliderFrame.width(), sliderFrame.height(), 512, 512); + } + + @Override + protected void adjustContent(double scrollPercentage) { + //TODO adjust scroll + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileTreeTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileTreeTab.java new file mode 100644 index 000000000..555cd1eef --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/CompileTreeTab.java @@ -0,0 +1,47 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchCompileTab; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; + +import java.util.List; + +public class CompileTreeTab extends AbstractGuiNode implements ICompileOverlayRenderer { + + private final ICWorkbenchEditor editor; + + public CompileTreeTab(ICWorkbenchEditor editor) { + this.editor = editor; + setSize(91, 134); + initSubNodes(); + } + + private void initSubNodes() { + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 92, 223, getFrame().width(), getFrame().height(), 512, 512); + } + + //region ICompileTabOverlayRenderer + @Override + public void renderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + + } + + @Override + public void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, List tooltip) { + + } + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/EraserToolTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/EraserToolTab.java new file mode 100644 index 000000000..a3222bfe2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/EraserToolTab.java @@ -0,0 +1,50 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.tools.EraseTool; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_ERASER_TOOL; + +public class EraserToolTab extends ICEditorToolTab { + + private final EraseTool tool; + + public EraserToolTab(ICEditorToolManager manager, EraseTool tool) { + super(manager, tool); + this.tool = tool; + construct(); + } + + private void construct() { + + } + + @Override + public TabButtonNode createButtonNode() { + return new TabButtonNode(this, TabButtonNode.TabSide.LEFT) { + @Override + public void renderIcon(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + GuiComponent.blit(stack, getFrame().x() + 3, getFrame().y() + 3, 390, 16, 14, 14, 512, 512); + } + + @Override + public void buildTooltip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_ERASER_TOOL)); + } + }; + } + + @Override + public boolean hasBody() { + return false; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/GatePlacerToolTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/GatePlacerToolTab.java new file mode 100644 index 000000000..552f91b65 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/GatePlacerToolTab.java @@ -0,0 +1,112 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.math.MathHelper; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Rotation; +import codechicken.lib.vec.Scale; +import codechicken.lib.vec.TransformationList; +import codechicken.lib.vec.Translation; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.tools.GatePlacerTool; +import mrtjp.projectred.fabrication.engine.gates.ICGateTileType; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.integration.client.GateModelRenderer; +import mrtjp.projectred.lib.Point; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class GatePlacerToolTab extends ICEditorToolTab { + + private final GatePlacerTool tool; + + public GatePlacerToolTab(ICEditorToolManager manager, GatePlacerTool tool) { + super(manager, tool); + this.tool = tool; + construct(); + } + + private void addGateButton(ICGateTileType type) { + ButtonController buttonController = new ButtonController() { + @Override public void getTooltip(List tooltip) { tooltip.add(new TranslatableComponent(type.tileType.getUnlocalizedName())); } + @Override public void onClick() { tool.setGateType(type); } + @Override public boolean isSelected() { return tool.getGateType() == type; } + + @Override + public void renderIcon(PoseStack stack, Point absPos, float partialFrame) { + MultiBufferSource.BufferSource getter = Minecraft.getInstance().renderBuffers().bufferSource(); + CCRenderState ccrs = CCRenderState.instance(); + ccrs.reset(); + ccrs.bind(RenderType.cutout(), getter, stack); + ccrs.overlay = OverlayTexture.NO_OVERLAY; + ccrs.brightness = 0xF000F0; + + double scale = 10/16D; + TransformationList t = new TransformationList( + new Rotation(90.0F * MathHelper.torad, 1.0F, 0.0F, 0.0F), + new Scale(16.0F * scale, -16.0F * scale, 16.0F * scale), + new Translation(absPos.x + 8 - scale*8, absPos.y + 8 - scale*8, 0.0F) + ); + + //TODO dont use null? + GateModelRenderer.instance().renderInventory(ccrs, null, type.renderIndex, 0, t); + + getter.endBatch(); + } + }; + + this.addSingleButton(buttonController); + } + + private void construct() { + + addGroup(UL_TILEGROUP_IO); + addGateButton(ICGateTileType.IO); + + addGroup(UL_TILEGROUP_BASIC); + addGateButton(ICGateTileType.OR); + addGateButton(ICGateTileType.NOR); + addGateButton(ICGateTileType.NOT); + addGateButton(ICGateTileType.AND); + addGateButton(ICGateTileType.NAND); + addGateButton(ICGateTileType.XOR); + addGateButton(ICGateTileType.XNOR); + addGateButton(ICGateTileType.BUFFER); + addGateButton(ICGateTileType.MULTIPLEXER); + + addGroup(UL_TILEGROUP_TIMING); + addGateButton(ICGateTileType.PULSE); + addGateButton(ICGateTileType.REPEATER); + addGateButton(ICGateTileType.RANDOMIZER); + + addGroup(UL_TILEGROUP_MEMORY); + addGateButton(ICGateTileType.SR_LATCH); + addGateButton(ICGateTileType.TOGGLE_LATCH); + addGateButton(ICGateTileType.TRANSPARENT_LATCH); + } + + @Override + public TabButtonNode createButtonNode() { + return new TabButtonNode(this, TabButtonNode.TabSide.LEFT) { + @Override + public void renderIcon(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + GuiComponent.blit(stack, getFrame().x() + 3, getFrame().y() + 3, 390, 31, 14, 14, 512, 512); + } + + @Override + public void buildTooltip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_GATE_TOOL)); + } + }; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICEditorToolManager.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICEditorToolManager.java new file mode 100644 index 000000000..1628cf4ba --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICEditorToolManager.java @@ -0,0 +1,208 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.editor.tools.IICEditorTool; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; + +public class ICEditorToolManager implements ICRenderNode.IICRenderNodeEventReceiver { + + private final Vector3 initialLeftMousePosition = new Vector3(); + private final Vector3 initialRightMousePosition = new Vector3(); + + private boolean leftMouseDown = false; + private boolean rightMouseDown = false; + + private final ArrayList toolList; + private IICEditorTool selectedTool; + + private boolean upPressed = false; + private boolean rightPressed = false; + private boolean downPressed = false; + private boolean leftPressed = false; + private boolean layerUpPressed = false; + private boolean layerDownPressed = false; + + private final List> toolSwappedListeners = new LinkedList<>(); + + public ICEditorToolManager(ArrayList toolList) { + this.toolList = toolList; + selectedTool = toolList.get(0); + } + + public void swapTools(ICEditorToolType nextToolType) { + IICEditorTool nextTool = toolList.get(nextToolType.ordinal()); + if (nextTool != selectedTool) { + selectedTool.toolDeactivated(); + selectedTool = nextTool; + selectedTool.toolActivated(); + + for (Consumer listener : toolSwappedListeners) { + listener.accept(nextToolType); + } + } + } + + public void addToolSwappedListener(Consumer listener) { + toolSwappedListeners.add(listener); + } + + public boolean keyPressed(int glfwKeyCode, int glfwFlags) { + switch (glfwKeyCode) { + case GLFW.GLFW_KEY_ESCAPE: + // Try to cancel current tool + if (selectedTool.toolCanceled()) break; + + // Otherwise, try to swap to the interact tool + if (selectedTool.getToolType() != ICEditorToolType.INTERACT_TOOL) { + swapTools(ICEditorToolType.INTERACT_TOOL); + break; + } + return false; + case GLFW.GLFW_KEY_W: + upPressed = true; + break; + case GLFW.GLFW_KEY_A: + leftPressed = true; + break; + case GLFW.GLFW_KEY_S: + downPressed = true; + break; + case GLFW.GLFW_KEY_D: + rightPressed = true; + break; + case GLFW.GLFW_KEY_UP: + layerUpPressed = true; + break; + case GLFW.GLFW_KEY_DOWN: + layerDownPressed = true; + break; + default: + return false; + } + return true; + } + + public boolean keyReleased(int glfwKeyCode, int glfwFlags) { + switch (glfwKeyCode) { + case GLFW.GLFW_KEY_W: + upPressed = false; + break; + case GLFW.GLFW_KEY_A: + leftPressed = false; + break; + case GLFW.GLFW_KEY_S: + downPressed = false; + break; + case GLFW.GLFW_KEY_D: + rightPressed = false; + break; + case GLFW.GLFW_KEY_UP: + layerUpPressed = false; + break; + case GLFW.GLFW_KEY_DOWN: + layerDownPressed = false; + break; + default: + return false; + } + + return true; + } + + public void update(ICRenderNode renderNode) { + // Pan camera + if (upPressed || downPressed || leftPressed || rightPressed) { + Vector3 panDelta = new Vector3(); + double deltaPerTick = 0.05D; + panDelta.z = (upPressed ? -deltaPerTick : 0) + (downPressed ? deltaPerTick : 0); + panDelta.x = (leftPressed ? -deltaPerTick : 0) + (rightPressed ? deltaPerTick : 0); + renderNode.applyPanningDelta(panDelta); + } + + // Shift Layers + if (layerUpPressed) { + renderNode.setLayer(renderNode.getLayer() + 1); + } else if (layerDownPressed) { + renderNode.setLayer(renderNode.getLayer() - 1); + } + layerUpPressed = false; + layerDownPressed = false; + } + + @Override + public void mouseButtonPressed(ICRenderNode renderNode, Vector3 mousePosition, int glfwMouseButton) { + switch (glfwMouseButton) { + case GLFW.GLFW_MOUSE_BUTTON_LEFT: + leftMouseDown = true; + initialLeftMousePosition.set(mousePosition); + break; + case GLFW.GLFW_MOUSE_BUTTON_RIGHT: + rightMouseDown = true; + initialRightMousePosition.set(mousePosition); + break; + default: + // ignore + } + + selectedTool.toolStart(mousePosition, glfwMouseButton); + } + + @Override + public void mouseButtonReleased(ICRenderNode renderNode, Vector3 mousePosition, int glfwMouseButton) { + switch (glfwMouseButton) { + case GLFW.GLFW_MOUSE_BUTTON_LEFT: + leftMouseDown = false; + break; + case GLFW.GLFW_MOUSE_BUTTON_RIGHT: + rightMouseDown = false; + break; + default: + // ignore + } + + selectedTool.toolReleased(mousePosition, glfwMouseButton); + } + + @Override + public void mouseButtonDragged(ICRenderNode renderNode, Vector3 mousePosition, Vector3 delta, int glfwMouseButton) { + if (!selectedTool.toolDragged(mousePosition, delta, glfwMouseButton)) { + // No default handling of mouse drag events + } + } + + @Override + public void mouseScrolled(ICRenderNode renderNode, Vector3 mousePosition, double scroll) { + if (!selectedTool.toolScrolled(mousePosition, scroll)) { + renderNode.moveZoomAt(mousePosition, scroll * 0.3D); + } + } + + @Override + public void layerChanged(ICRenderNode renderNode, int previousLayer, int newLayer) { + // Update bump the initial position up or down if a drag is in progress + if (leftMouseDown) initialLeftMousePosition.y = newLayer; + if (rightMouseDown) initialRightMousePosition.y = newLayer; + + selectedTool.toolLayerChanged(previousLayer, newLayer); + } + + @Override + public void onRenderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + selectedTool.renderOverlay(mousePosition, isFirstHit, ccrs, getter, matrixStack); + } + + @Override + public void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, List tooltip) { + selectedTool.buildTooltip(mousePosition, isFirstHit, tooltip); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICEditorToolTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICEditorToolTab.java new file mode 100644 index 000000000..cc6b8431e --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICEditorToolTab.java @@ -0,0 +1,256 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.tools.IICEditorTool; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.sounds.SoundEvents; + +import java.util.LinkedList; +import java.util.List; + +public class ICEditorToolTab extends AbstractGuiNode implements TabControllerNode.IToolbarTab { + + private static final int GROUP_U = 390; + private static final int GROUP_V = 61; + private static final int GROUP_WIDTH = 64; + private static final int GROUP_HEIGHT = 16; + + private static final int FULL_BUTTON_U = 390; + private static final int FULL_BUTTON_V = 77; + private static final int FULL_BUTTON_WIDTH = 64; + private static final int FULL_BUTTON_HEIGHT = 16; + + private static final int SINGLE_BUTTON_U = FULL_BUTTON_U + 64; + private static final int SINGLE_BUTTON_V = FULL_BUTTON_V; + private static final int SINGLE_BUTTON_WIDTH = 16; + private static final int SINGLE_BUTTON_HEIGHT = 16; + + private static final int BUTTON_SELECTED_SHIFT_U = 0; + private static final int BUTTON_SELECTED_SHIFT_V = 16; + private static final int BUTTON_MOUSEOVER_SHIFT_U = 0; + private static final int BUTTON_MOUSEOVER_SHIFT_V = 32; + + protected final ICEditorToolManager manager; + protected final IICEditorTool tool; + + private int rowIndex = 0; + private int columnIndex = 0; + + public ICEditorToolTab(ICEditorToolManager manager, IICEditorTool tool) { + this.manager = manager; + this.tool = tool; + + this.setSize(84, 222); + } + + private void setAndIncrGridPos(AbstractGuiNode node, int cellWidth) { + if (columnIndex + cellWidth > 4) { + columnIndex = 0; + rowIndex++; + } + + node.setPosition(7 + columnIndex * SINGLE_BUTTON_WIDTH, 18 + rowIndex * SINGLE_BUTTON_HEIGHT); + + columnIndex += cellWidth; + if (columnIndex > 4) { + columnIndex = 0; + rowIndex++; + } + } + + protected void addGroup(String unlocal) { + addGroup(new TranslatableComponent(unlocal)); + } + + protected void addGroup(Component groupName) { + + GroupHeaderNode header = new GroupHeaderNode(groupName); + setAndIncrGridPos(header, 4); + addChild(header); + } + + protected void addFullRowButton(ButtonController controller) { + + FullRowButtonNode button = new FullRowButtonNode(controller); + setAndIncrGridPos(button, 4); + addChild(button); + } + + protected void addSingleButton(ButtonController controller) { + + SingleColumnButtonNode button = new SingleColumnButtonNode(controller); + setAndIncrGridPos(button, 1); + addChild(button); + } + + public IICEditorTool getTool() { + return tool; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + + GuiComponent.blit(stack, getPosition().x, getPosition().y, 305, 0, 84, 222, 512, 512); + + //TODO: Render tool name on header + } + + @Override + public boolean hasBody() { + return true; + } + + @Override + public void onTabClosed() { + setHidden(true); + } + + @Override + public void onTabOpened() { + setHidden(false); + manager.swapTools(tool.getToolType()); + } + + @Override + public void onTabMinimized() { + setHidden(true); + } + + @Override + public TabButtonNode createButtonNode() { + return new TabButtonNode(this, TabButtonNode.TabSide.LEFT) { + + @Override + public void renderIcon(PoseStack stack, Point mouse, float partialFrame) { + } + + @Override + public void buildTooltip(List tooltip) { + tooltip.add(new TextComponent("//TODO implement tab")); + } + }; + } + + interface ButtonController { + void getTooltip(List tooltip); + void onClick(); + boolean isSelected(); + void renderIcon(PoseStack stack, Point absPos, float partialFrame); + } + + private static class GroupHeaderNode extends AbstractGuiNode { + + private final Component title; + + public GroupHeaderNode(Component title) { + this.title = title; + this.setSize(GROUP_WIDTH, GROUP_HEIGHT); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + + GuiComponent.blit(stack, getPosition().x, getPosition().y, GROUP_U, GROUP_V, getFrame().width(), getFrame().height(), 512, 512); + + Font fontRenderer = getRoot().getFontRenderer(); + fontRenderer.draw(stack, title, getPosition().x + 2, getPosition().y + GROUP_HEIGHT / 2 - fontRenderer.lineHeight/2, 0xFFFFFF); + } + } + + private static class AbstractButtonNode extends AbstractGuiNode { + + private final ButtonController controller; + private final Point uvBg; + private final Point uvBgSelectedShift; + private final Point uvMouseOverShift; + + public AbstractButtonNode(ButtonController controller, int width, int height, Point uvBg, Point uvBgSelectedShift, Point uvMouseOverShift) { + this.controller = controller; + this.setSize(width, height); + this.uvBg = uvBg; + this.uvBgSelectedShift = uvBgSelectedShift; + this.uvMouseOverShift = uvMouseOverShift; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.enableBlend(); + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + + boolean mouseover = getFrame().contains(mouse) && isFirstHit(mouse); + boolean selected = controller.isSelected(); + + int x = getPosition().x; + int y = getPosition().y; + int w = getFrame().width(); + int h = getFrame().height(); + + // Background + int uBackground = uvBg.x + (selected ? uvBgSelectedShift.x : 0); + int vBackground = uvBg.y + (selected ? uvBgSelectedShift.y : 0); + GuiComponent.blit(stack, x, y, uBackground, vBackground, w, h, 512, 512); + + // Mouseover layer + if (selected || mouseover) { + int uMouseOver = uvBg.x + uvMouseOverShift.x; + int vMouseOver = uvBg.y + uvMouseOverShift.y; + GuiComponent.blit(stack, x, y, uMouseOver, vMouseOver, w, h, 512, 512); + } + + RenderSystem.disableBlend(); + + // Icon + controller.renderIcon(stack, getPosition(), partialFrame); + } + + @Override + public void drawFront(PoseStack stack, Point mouse, float partialFrame) { + + if (!isFirstHit(mouse)) return; + + List tooltip = new LinkedList<>(); + controller.getTooltip(tooltip); + + renderTooltip(stack, mouse, tooltip); + } + + @Override + public boolean mouseClicked(Point p, int glfwMouseButton, boolean consumed) { + if (!consumed && isFirstHit(p)) { + getRoot().getMinecraft().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1)); + controller.onClick(); + return true; + } + return false; + } + } + + private static class SingleColumnButtonNode extends AbstractButtonNode { + public SingleColumnButtonNode(ButtonController controller) { + super(controller, SINGLE_BUTTON_WIDTH, SINGLE_BUTTON_HEIGHT, + new Point(SINGLE_BUTTON_U, SINGLE_BUTTON_V), + new Point(BUTTON_SELECTED_SHIFT_U, BUTTON_SELECTED_SHIFT_V), + new Point(BUTTON_MOUSEOVER_SHIFT_U, BUTTON_MOUSEOVER_SHIFT_V)); + } + } + + private static class FullRowButtonNode extends AbstractButtonNode { + public FullRowButtonNode(ButtonController controller) { + super(controller, FULL_BUTTON_WIDTH, FULL_BUTTON_HEIGHT, + new Point(FULL_BUTTON_U, FULL_BUTTON_V), + new Point(BUTTON_SELECTED_SHIFT_U, BUTTON_SELECTED_SHIFT_V), + new Point(BUTTON_MOUSEOVER_SHIFT_U, BUTTON_MOUSEOVER_SHIFT_V)); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICRenderNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICRenderNode.java new file mode 100644 index 000000000..2befba91b --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICRenderNode.java @@ -0,0 +1,291 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.math.MathHelper; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.RedundantTransformation; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.engine.BaseTileMap; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Vec2; +import mrtjp.projectred.redui.ViewportRenderNode; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class ICRenderNode extends ViewportRenderNode { + + private static final int ZOOM_ANIMATION_TIME_MS = 100; + private static final int LAYER_ANIMATION_TIME_MS = 200; + private static final int CAMERA_ANIMATION_TIME_MS = 100; + private static final int FOCUS_ANIMATION_TIME_MS = 400; + private static final int ZOOM_DIST_MAX = 18; + private static final int ZOOM_DIST_MIN = 3; + + private final ICWorkbenchEditor editor; + private final IICRenderNodeEventReceiver eventReceiver; + + private final Vector3 cameraPosition = new Vector3(); + + private final LinearVectorAnimation cameraLayerAnimator = new LinearVectorAnimation(); + private final LinearVectorAnimation cameraZoomAnimator = new LinearVectorAnimation(0, 8, 0); + + private int currentLayer = 0; + + private Point lastMousePos = Point.ZERO; + + public ICRenderNode(ICWorkbenchEditor editor, IICRenderNodeEventReceiver eventReceiver) { + this.editor = editor; + this.eventReceiver = eventReceiver; + } + + public void setLayer(int layer) { + int previousLayer = currentLayer; + this.currentLayer = layer; + cameraLayerAnimator.addDeltaWithNewDuration(new Vector3(0, layer-previousLayer, 0), LAYER_ANIMATION_TIME_MS); + eventReceiver.layerChanged(this, previousLayer, currentLayer); + } + + public int getLayer() { + return currentLayer; + } + + public void moveZoomAt(Vector3 zoomPos, double zoomDelta) { + + Vector3 zoomVec = zoomPos.copy().subtract(cameraPosition).normalize(); + zoomVec.multiply(zoomDelta); + cameraZoomAnimator.addDeltaWithNewDuration(zoomVec, ZOOM_ANIMATION_TIME_MS); + } + + public void applyCameraDelta(Vector3 delta) { + cameraZoomAnimator.addDeltaWithNewDuration(delta, CAMERA_ANIMATION_TIME_MS); + } + + /** + * Apply a camera delta based on NDC delta + * @param delta Amount to pan camera in frame NDC units + */ + public void applyPanningDelta(Vector3 delta) { + + // Calculate scale based on how many tiles are visible in the viewport across its largest dimension + double scale; + if (getFrame().width() > getFrame().height()) { + scale = ndcMouseToWorld(new Vec2(1, 0)).subtract(ndcMouseToWorld(new Vec2(-1, 0))).x; + } else { + scale = ndcMouseToWorld(new Vec2(0, -1)).subtract(ndcMouseToWorld(new Vec2(0, 1))).z; // Z because top-down view + } + + // Scale delta to world units + Vector3 scaledDelta = delta.copy().multiply(scale); + + // Move camera + applyCameraDelta(scaledDelta); + } + + public void focusCameraAtTiles(List positions) { + + if (positions.isEmpty()) return; + + // Create cuboid enclosing all tiles + Cuboid6 bounds = new Cuboid6(); + + Iterator it = positions.iterator(); + TileCoord first = it.next(); + bounds.set(first.x, first.y, first.z, first.x+1, first.y+1, first.z+1); + while (it.hasNext()) { + TileCoord next = it.next(); + bounds.enclose(next.x, next.y, next.z, next.x+1, next.y+1, next.z+1); + } + bounds.expand(1.0); // Expand by 1 tile in all directions + + // X and Z will be at midpoints + double midX = (bounds.min.x + bounds.max.x) / 2D; + double midZ = (bounds.min.z + bounds.max.z) / 2D; + + // Y will be far enough away to enclose all tiles inside FOV + double dist = distanceToEncloseRect(bounds.max.x - bounds.min.x, bounds.max.z - bounds.min.z); + + // Set camera position + cameraZoomAnimator.moveToTargetWithDuration(new Vector3(midX, dist, midZ), FOCUS_ANIMATION_TIME_MS); + } + + @Override + public void frameUpdate(Point mouse, float partialFrame) { + + // Set camera location bounds + TileCoord minBounds = editor.getTileMap().getMinBounds(); + TileCoord maxBounds = editor.getTileMap().getMaxBounds(); + cameraZoomAnimator.setBounds( + minBounds.x, + cameraLayerAnimator.vector.y + ZOOM_DIST_MIN, // Y bounds follows zoom + minBounds.z, + maxBounds.x + 1, + cameraLayerAnimator.vector.y + ZOOM_DIST_MAX, + maxBounds.z + 1); + + long t = System.currentTimeMillis(); + cameraZoomAnimator.tick(t); + cameraLayerAnimator.tick(t); + + // Store mouse position for mouse drag calculations + lastMousePos = mouse; + + // Combine components to calculate final position + cameraPosition.set(0); + cameraZoomAnimator.apply(cameraPosition); + cameraLayerAnimator.apply(cameraPosition); + } + + @Override + protected double getTargetPlaneDistance() { + return cameraPosition.y - currentLayer - 2/16D; + } + + @Override + protected double getVerticalFOV() { + return 70D * MathHelper.torad; + } + + @Override + protected double getMaxRenderDist() { + return 20D; + } + + @Override + protected Vector3 getCameraPosition() { + return cameraPosition; + } + + @Override + protected void renderInViewport(PoseStack renderStack, Vec2 ndcMouse, float partialFrame, boolean isFirstHit) { + + CCRenderState ccrs = CCRenderState.instance(); + MultiBufferSource.BufferSource getter = Minecraft.getInstance().renderBuffers().bufferSource(); + + Vector3 worldPos = ndcMouseToWorld(ndcMouse); + + // At most, render 1 layer below and 1 render above (for transition effects) + for (int y = currentLayer-1; y <= currentLayer+1; y++) { + + ccrs.reset(); + // Linearly ramp down alpha as we move away from the current layer + ccrs.alphaOverride = 255 - (int) (255 * Math.min(Math.abs(y - cameraLayerAnimator.vector.y), 1)); + ccrs.brightness = LightTexture.pack(15, 15); + + // Render grid + renderStack.pushPose(); + renderStack.translate(0, y, 0); + TileCoord minBounds = editor.getTileMap().getMinBounds(); + TileCoord maxBounds = editor.getTileMap().getMaxBounds(); + Cuboid6 bounds = new Cuboid6(minBounds.x, minBounds.y, minBounds.z, maxBounds.x + 1, maxBounds.y + 1, maxBounds.z + 1); + + ICRenderTypes.renderICGrid(renderStack, getter, bounds, ccrs); //TODO the cuboid going in here is kinda awkward + + renderStack.popPose(); + + // Render tiles + for (var entry : editor.getTileMap().getTilesOnLayer(y)) { + renderStack.pushPose(); + renderStack.translate(entry.getKey().x, y, entry.getKey().z); + ccrs.bind(ICRenderTypes.layersRenderType, getter, renderStack); + + entry.getValue().renderTile(ccrs, RedundantTransformation.INSTANCE, partialFrame); + + renderStack.popPose(); + } + + if (y == currentLayer) { + eventReceiver.onRenderOverlay(this, worldPos, isFirstHit, ccrs, getter, renderStack); + } + } + + // Force-end the batch to make sure it happens before the custom viewport is altered + getter.endBatch(); + } + + @Override + public void onAddedToParent() { + // Set initial zoom to enclose entire tile map + focusCameraAtTiles(List.of(editor.getTileMap().getMinBounds(), editor.getTileMap().getMaxBounds())); + } + + @Override + public void update() { + super.update(); + eventReceiver.update(this); + } + + @Override + protected List getToolTip(Point mousePosition, boolean isFirstHit) { + + List tooltip = new LinkedList<>(); + eventReceiver.buildTooltip(this, mouseToWorld(mousePosition), isFirstHit, tooltip); + + return tooltip; + } + + @Override + public boolean mouseClicked(Point p, int glfwMouseButton, boolean consumed) { + lastMousePos = p; + if (!consumed && isFirstHit(p)) { + eventReceiver.mouseButtonPressed(this, mouseToWorld(p), glfwMouseButton); + return true; + } + return false; + } + + @Override + public boolean mouseReleased(Point p, int glfwMouseButton, long timeHeld, boolean consumed) { + lastMousePos = p; + eventReceiver.mouseButtonReleased(this, mouseToWorld(p), glfwMouseButton); + return true; + } + + @Override + public boolean mouseDragged(Point p, int glfwMouseButton, long timeHeld, boolean consumed) { + Vector3 worldPos = mouseToWorld(p); + Vector3 delta = worldPos.copy().subtract(mouseToWorld(lastMousePos)); + + eventReceiver.mouseButtonDragged(this, worldPos, delta, glfwMouseButton); + + lastMousePos = p; + return true; + } + + @Override + public boolean mouseScrolled(Point p, double scroll, boolean consumed) { + lastMousePos = p; + if (!consumed && isFirstHit(p)) { + eventReceiver.mouseScrolled(this, mouseToWorld(p), scroll); + return true; + } + return false; + } + + public interface IICRenderNodeEventReceiver { + + default void update(ICRenderNode renderNode) { } + + default void mouseButtonPressed(ICRenderNode renderNode, Vector3 mousePosition, int glfwMouseButton) { } + + default void mouseButtonReleased(ICRenderNode renderNode, Vector3 mousePosition, int glfwMouseButton) { } + + default void mouseButtonDragged(ICRenderNode renderNode, Vector3 mousePosition, Vector3 delta, int glfwMouseButton) { } + + default void mouseScrolled(ICRenderNode renderNode, Vector3 mousePosition, double scroll) { } + + default void layerChanged(ICRenderNode renderNode, int previousLayer, int newLayer) { } + + void onRenderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack); + + void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, List tooltip); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICRenderTypes.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICRenderTypes.java new file mode 100644 index 000000000..26dfcb015 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICRenderTypes.java @@ -0,0 +1,243 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.render.BlockRenderer; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.texture.AtlasRegistrar; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.math.Vector3f; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.ResourceLocation; + +import java.util.HashMap; +import java.util.Map; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.MOD_ID; + +public class ICRenderTypes { + + public static ResourceLocation PERFBOARD_TEXTURE = new ResourceLocation(MOD_ID, "textures/block/perfboard.png"); + public static ResourceLocation PERFBOARD_EDGE_TEXTURE = new ResourceLocation(MOD_ID, "textures/block/perfboard_edge.png"); + public static ResourceLocation PERFBOARD_CORNER_TEXTURE = new ResourceLocation(MOD_ID, "textures/block/perfboard_corner.png"); + + public static RenderType layersRenderType = RenderType.create(MOD_ID + ":ic_block", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 10000, false, true, + RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.RENDERTYPE_CUTOUT_SHADER) //TODO only default one that discards alpha frags + .setTextureState(RenderStateShard.BLOCK_SHEET) + .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST) + .setCullState(RenderStateShard.CULL) + .setLightmapState(RenderStateShard.LIGHTMAP) + .setOverlayState(RenderStateShard.NO_OVERLAY) + .setLayeringState(RenderStateShard.NO_LAYERING) + .setOutputState(RenderStateShard.MAIN_TARGET) + .setTexturingState(RenderStateShard.DEFAULT_TEXTURING) + .setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE) + .setLineState(RenderStateShard.DEFAULT_LINE) + .createCompositeState(true)); + + public static RenderType selectionRenderType = RenderType.create(MOD_ID + ":ic_selection", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false, + RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) + .setTextureState(RenderStateShard.NO_TEXTURE) + .setTransparencyState(RenderStateShard.LIGHTNING_TRANSPARENCY) + .setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST) + .setCullState(RenderStateShard.CULL) + .setLightmapState(RenderStateShard.NO_LIGHTMAP) + .setOverlayState(RenderStateShard.NO_OVERLAY) + .setLayeringState(RenderStateShard.NO_LAYERING) + .setOutputState(RenderStateShard.MAIN_TARGET) + .setTexturingState(RenderStateShard.DEFAULT_TEXTURING) + .setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE) + .setLineState(RenderStateShard.DEFAULT_LINE) + .createCompositeState(true)); + + public static RenderType gridRenderType = RenderType.create(MOD_ID + ":ic_grid", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false, + RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.BLOCK_SHADER) + .setTextureState(new RenderStateShard.TextureStateShard(PERFBOARD_TEXTURE, false, false)) // Mipped: Strange artifacts on render. Our normal world rendering is not mipped + .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST) + .setCullState(RenderStateShard.CULL) + .setLightmapState(RenderStateShard.NO_LIGHTMAP) + .setOverlayState(RenderStateShard.NO_OVERLAY) + .setLayeringState(RenderStateShard.NO_LAYERING) + .setOutputState(RenderStateShard.MAIN_TARGET) + .setTexturingState(RenderStateShard.DEFAULT_TEXTURING) + .setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE) + .setLineState(RenderStateShard.DEFAULT_LINE) + .createCompositeState(true)); + + public static RenderType gridEdgeRenderType = RenderType.create(MOD_ID + ":ic_grid_edge", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false, + RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.BLOCK_SHADER) + .setTextureState(new RenderStateShard.TextureStateShard(PERFBOARD_EDGE_TEXTURE, false, false)) + .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST) + .setCullState(RenderStateShard.CULL) + .setLightmapState(RenderStateShard.NO_LIGHTMAP) + .setOverlayState(RenderStateShard.NO_OVERLAY) + .setLayeringState(RenderStateShard.NO_LAYERING) + .setOutputState(RenderStateShard.MAIN_TARGET) + .setTexturingState(RenderStateShard.DEFAULT_TEXTURING) + .setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE) + .setLineState(RenderStateShard.DEFAULT_LINE) + .createCompositeState(true)); + + public static RenderType gridCornerRenderType = RenderType.create(MOD_ID + ":ic_grid_corner", DefaultVertexFormat.BLOCK, VertexFormat.Mode.QUADS, 256, false, false, + RenderType.CompositeState.builder() + .setShaderState(RenderStateShard.BLOCK_SHADER) + .setTextureState(new RenderStateShard.TextureStateShard(PERFBOARD_CORNER_TEXTURE, false, false)) + .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST) + .setCullState(RenderStateShard.CULL) + .setLightmapState(RenderStateShard.NO_LIGHTMAP) + .setOverlayState(RenderStateShard.NO_OVERLAY) + .setLayeringState(RenderStateShard.NO_LAYERING) + .setOutputState(RenderStateShard.MAIN_TARGET) + .setTexturingState(RenderStateShard.DEFAULT_TEXTURING) + .setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE) + .setLineState(RenderStateShard.DEFAULT_LINE) + .createCompositeState(true)); + + public static TextureAtlasSprite icSurfaceIcon; + public static TextureAtlasSprite icSurfaceBorderIcon; + public static TextureAtlasSprite icSurfaceCornerIcon; + + public static void renderICGrid(PoseStack renderStack, MultiBufferSource getter, Cuboid6 bounds, CCRenderState ccrs) { + // Main + double ymin = -2/16D; + Cuboid6 box = new Cuboid6(bounds.min.x, ymin, bounds.min.z, bounds.max.x, 0, bounds.max.z); + ccrs.bind(ICRenderTypes.gridRenderType, getter, renderStack); + BlockRenderer.renderCuboid(ccrs, box, 1); + + // Edges + double h = 0.01D; + ccrs.bind(ICRenderTypes.gridEdgeRenderType, getter, renderStack); + // Top + box.set(bounds.min.x, ymin, bounds.min.z, bounds.max.x, h, bounds.min.z + 1); + BlockRenderer.renderCuboid(ccrs, box, 1); + // bottom + box.set(bounds.min.x, ymin, bounds.max.z - 1, bounds.max.x, h, bounds.max.z); + BlockRenderer.renderCuboid(ccrs, box, 1); + // left + box.set(bounds.min.x, ymin, bounds.min.z, bounds.min.x + 1, h, bounds.max.z); + BlockRenderer.renderCuboid(ccrs, box, 1); + // right + box.set(bounds.max.x - 1, ymin, bounds.min.z, bounds.max.x, h, bounds.max.z); + BlockRenderer.renderCuboid(ccrs, box, 1); + + // Corners + h = 0.02D; + ccrs.bind(ICRenderTypes.gridCornerRenderType, getter, renderStack); + // Top left + box.set(bounds.min.x, ymin, bounds.min.z, bounds.min.x + 1, h, bounds.min.z + 1); + BlockRenderer.renderCuboid(ccrs, box, 1); + // Top right + box.set(bounds.max.x - 1, ymin, bounds.min.z, bounds.max.x, h, bounds.min.z + 1); + BlockRenderer.renderCuboid(ccrs, box, 1); + // bottom right + box.set(bounds.max.x - 1, ymin, bounds.max.z - 1, bounds.max.x, h, bounds.max.z); + BlockRenderer.renderCuboid(ccrs, box, 1); + // bottom left + box.set(bounds.min.x, ymin, bounds.max.z - 1, bounds.min.x + 1, h, bounds.max.z); + BlockRenderer.renderCuboid(ccrs, box, 1); + + } + + public static void renderSelection(CCRenderState ccrs, Vector3 a, Vector3 b, double height, double th) { + + Cuboid6 ac = new Cuboid6(a.copy().floor(), a.copy().ceil()); + ac.enclose(b.copy().floor()); + ac.enclose(b.copy().ceil()); + ac.expand(0.002); + + Cuboid6 box = new Cuboid6(); + + //Top + box.min.x = ac.min.x; + box.min.y = ac.min.y; + box.min.z = ac.min.z; + box.max.x = ac.max.x; + box.max.y = ac.min.y + height; + box.max.z = ac.min.z + th; + BlockRenderer.renderCuboid(ccrs, box, 1); + + // Bottom + box.min.x = ac.min.x; + box.min.y = ac.min.y; + box.min.z = ac.max.z - th; + box.max.x = ac.max.x; + box.max.y = ac.min.y + height; + box.max.z = ac.max.z; + BlockRenderer.renderCuboid(ccrs, box, 1); + + // Left + box.min.x = ac.min.x; + box.min.y = ac.min.y; + box.min.z = ac.min.z + th; + box.max.x = ac.min.x + th; + box.max.y = ac.min.y + height; + box.max.z = ac.max.z - th; + BlockRenderer.renderCuboid(ccrs, box, 1 | 1<<2 | 1<<3); + + // Right + box.min.x = ac.max.x - th; + box.min.y = ac.min.y; + box.min.z = ac.min.z + th; + box.max.x = ac.max.x; + box.max.y = ac.min.y + height; + box.max.z = ac.max.z - th; + BlockRenderer.renderCuboid(ccrs, box, 1 | 1<<2 | 1<<3); + } + + public static void renderTextCenteredAt(PoseStack stack, Vector3 pos, String text, int bgColor, int textColor) { + Font fontRenderer = Minecraft.getInstance().font; + + stack.pushPose(); + + stack.translate(pos.x, pos.y, pos.z); + stack.scale(1.0f/fontRenderer.lineHeight, 1, 1.0f/fontRenderer.lineHeight); + stack.mulPose(Vector3f.XP.rotationDegrees(90.0F)); + + fontRenderer.draw(stack, text, (float) (0 - fontRenderer.width(text) / 2), (float) (0 - fontRenderer.lineHeight / 2), textColor); + + stack.popPose(); + } + + public void sortComponents(Cuboid6 c) { + + if (c.min.x > c.max.x) { + double tmp = c.max.x; + c.max.x = c.min.x; + c.min.x = tmp; + } + + if (c.min.y > c.max.y) { + double tmp = c.max.y; + c.max.y = c.min.y; + c.min.y = tmp; + } + + if (c.min.z > c.max.z) { + double tmp = c.max.z; + c.max.z = c.min.z; + c.min.z = tmp; + } + } + + public static void registerIcons(AtlasRegistrar registrar) { + registrar.registerSprite(new ResourceLocation(MOD_ID, "block/perfboard"), i -> icSurfaceIcon = i); + registrar.registerSprite(new ResourceLocation(MOD_ID, "block/perfboard_edge"), i -> icSurfaceBorderIcon = i); + registrar.registerSprite(new ResourceLocation(MOD_ID, "block/perfboard_corner"), i -> icSurfaceCornerIcon = i); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICompileOverlayRenderer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICompileOverlayRenderer.java new file mode 100644 index 000000000..c7c447676 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ICompileOverlayRenderer.java @@ -0,0 +1,16 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; + +import java.util.List; + +public interface ICompileOverlayRenderer { + + void renderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack); + + void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, List tooltip); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/InteractToolTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/InteractToolTab.java new file mode 100644 index 000000000..7ea2edde5 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/InteractToolTab.java @@ -0,0 +1,50 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.tools.InteractTool; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_INTERACT_TOOL; + +public class InteractToolTab extends ICEditorToolTab { + + private final InteractTool tool; + + public InteractToolTab(ICEditorToolManager manager, InteractTool tool) { + super(manager, tool); + this.tool = tool; + construct(); + } + + private void construct() { + + } + + @Override + public TabButtonNode createButtonNode() { + return new TabButtonNode(this, TabButtonNode.TabSide.LEFT) { + @Override + public void renderIcon(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + GuiComponent.blit(stack, getFrame().x() + 3, getFrame().y() + 3, 390, 1, 14, 14, 512, 512); + } + + @Override + public void buildTooltip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_INTERACT_TOOL)); + } + }; + } + + @Override + public boolean hasBody() { + return false; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/LinearVectorAnimation.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/LinearVectorAnimation.java new file mode 100644 index 000000000..7eb95abae --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/LinearVectorAnimation.java @@ -0,0 +1,72 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Vector3; + +public class LinearVectorAnimation { + + public final Vector3 vector; + + private final Cuboid6 bounds = new Cuboid6(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + private final Vector3 step = new Vector3(); + + private long lastTime = -1; + private long stepsRemaining = 0; + + public LinearVectorAnimation() { + this(0, 0, 0); + } + + public LinearVectorAnimation(double x, double y, double z) { + vector = new Vector3(x, y, z); + } + + public void setBounds(double xmin, double ymin, double zmin, double xmax, double ymax, double zmax) { + bounds.min.set(xmin, ymin, zmin); + bounds.max.set(xmax, ymax, zmax); + // If now outside, jump into bounds + if (!bounds.contains(vector)) { + vector.x = Math.max(bounds.min.x, Math.min(bounds.max.x, vector.x)); + vector.y = Math.max(bounds.min.y, Math.min(bounds.max.y, vector.y)); + vector.z = Math.max(bounds.min.z, Math.min(bounds.max.z, vector.z)); + } + } + + public void addDeltaWithNewDuration(Vector3 delta, long duration) { + if (delta.equals(Vector3.ZERO) || duration == 0) { + return; + } + + // Calculate the new step size to satisfy new delta plus previous delta within new duration + step.multiply(stepsRemaining); + step.add(delta); + stepsRemaining = duration; + step.divide(stepsRemaining); + } + + public void moveToTargetWithDuration(Vector3 target, long duration) { + stepsRemaining = duration; + step.set(target).subtract(vector).divide(stepsRemaining); + } + + public void tick(long time) { + + long dt = lastTime == -1 ? 0 : time - lastTime; + lastTime = time; + + if (stepsRemaining > 0) { + long stepsToTake = Math.min(stepsRemaining, dt); + vector.add(step.x * stepsToTake, step.y * stepsToTake, step.z * stepsToTake); + stepsRemaining -= stepsToTake; + + // Contain within bounds + vector.x = Math.max(bounds.min.x, Math.min(bounds.max.x, vector.x)); + vector.y = Math.max(bounds.min.y, Math.min(bounds.max.y, vector.y)); + vector.z = Math.max(bounds.min.z, Math.min(bounds.max.z, vector.z)); + } + } + + public void apply(Vector3 target) { + target.add(vector); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/PipelineDiagramNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/PipelineDiagramNode.java new file mode 100644 index 000000000..3904aed62 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/PipelineDiagramNode.java @@ -0,0 +1,105 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchInfoTab; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.lithography.LithographyPipeline; +import mrtjp.projectred.fabrication.lithography.YieldCalculator; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.ItemStackNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; + +public class PipelineDiagramNode extends AbstractGuiNode { + + private final YieldCalculator yieldCalculator; + + private BasicPipelineItems basicPipelineItems; + private AbstractGuiNode advancedPipelineItems; + + public PipelineDiagramNode(YieldCalculator yieldCalculator) { + this.yieldCalculator = yieldCalculator; + setSize(280, 56); + initSubNodes(); + } + + private void initSubNodes() { + + basicPipelineItems = new BasicPipelineItems(); + addChild(basicPipelineItems); + + advancedPipelineItems = new AbstractGuiNode() { }; //TODO + addChild(advancedPipelineItems); + + updatePipelines(); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + + } + + @Override + public void update() { + updatePipelines(); + } + + private void updatePipelines() { + boolean isBasic = yieldCalculator.getPipeline() == LithographyPipeline.BASIC; + basicPipelineItems.setHidden(!isBasic); + advancedPipelineItems.setHidden(isBasic); + } + + private class BasicPipelineItems extends AbstractGuiNode { + + private final ArrayList items = new ArrayList<>(); + + public BasicPipelineItems() { + addItem(new ItemStack(FabricationReferences.IC_BLUEPRINT_ITEM), 30, 5); + addItem(new ItemStack(FabricationReferences.PLOTTING_TABLE_BLOCK), 64, 5); + addItem(new ItemStack(FabricationReferences.BLANK_PHOTOMASK_ITEM), 64, 39); + addItem(new ItemStack(FabricationReferences.PHOTOMASK_SET_ITEM), 98, 5); + addItem(new ItemStack(FabricationReferences.LITHOGRAPHY_TABLE_BLOCK), 132, 5); + addItem(new ItemStack(FabricationReferences.ROUGH_SILICON_WAFER_ITEM), 132, 39); + addItem(new ItemStack(FabricationReferences.VALID_DIE_ITEM), 166, 5); + addItem(new ItemStack(FabricationReferences.PACKAGING_TABLE_BLOCK), 200, 5); + addItem(new ItemStack(FabricationReferences.FABRICATED_GATE_ITEM), 234, 5); + } + + private void addItem(ItemStack stack, int x, int y) { + ItemStackNode stackNode = new ItemStackNode(stack); + stackNode.setPosition(x, y); + items.add(stackNode); + addChild(stackNode); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchInfoTab.TAB_BACKGROUND); + + // Draw the diagram centered in this node + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 1, 223, 280, 56, 512, 512); + } + + @Override + public void update() { + // Update the wafer input slot to the Lithography table + ItemStackNode node = items.get(5); //Wafer slot + switch (yieldCalculator.getWaferType()) { + case ROUGH_WAFER: + node.setItemStack(new ItemStack(FabricationReferences.ROUGH_SILICON_WAFER_ITEM)); + break; + case POLISHED_WAFER: + case PURIFIED_WAFER: + node.setItemStack(ItemStack.EMPTY); //TODO + break; + } + } + + //TODO update final Gate stack to reflect proper IO + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ProblemListNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ProblemListNode.java new file mode 100644 index 000000000..0ab125ecd --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/ProblemListNode.java @@ -0,0 +1,118 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.engine.log.CompileProblem; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchCompileTab; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; + +import java.util.LinkedList; +import java.util.List; + +public class ProblemListNode extends AbstractGuiNode { + + private final List problemList = new LinkedList<>(); + + private final AbstractGuiNode listParent = new AbstractGuiNode() { + }; + + private double scroll = 0; + + public ProblemListNode() { + initSubNodes(); + } + + private void initSubNodes() { + addChild(listParent); + } + + public void setProblemList(List list) { + this.problemList.clear(); + this.problemList.addAll(list); + refreshListItems(); + } + + private void refreshListItems() { + + listParent.removeAllChildren(); + listParent.setPosition(0, 0); + + int y = 0; + for (CompileProblem issue : problemList) { + IssueListItemNode item = new IssueListItemNode(issue); + item.setPosition(0, y); + listParent.addChild(item); + y += item.calculateAccumulatedFrame().height(); + } + + moveListToScroll(); + } + + public void setScrollPercentage(double scrollPercentage) { + this.scroll = scrollPercentage; + moveListToScroll(); + } + + private void moveListToScroll() { + Rect subFrame = calculateChildrenFrame(); + if (subFrame.height() <= getFrame().height()) return; + + int totalScroll = subFrame.height() - getFrame().height(); // How much scroll is possible + int dist = (int) (totalScroll * scroll); // How much we want to scroll + + listParent.setPosition(0, -dist); + } + + @Override + public void onSubTreePreDrawBack() { + // This node's frame converted to GL11 window coordinates + Rect gl11Rect = calculateGL11Frame(); + + // Enable scissor using the calculated rect + RenderSystem.enableScissor(gl11Rect.x(), gl11Rect.y(), gl11Rect.width(), gl11Rect.height()); + } + + @Override + public void onSubTreePostDrawBack() { + // Disable scissor + RenderSystem.disableScissor(); + } + + private class IssueListItemNode extends AbstractGuiNode { + + private final CompileProblem issue; + + public IssueListItemNode(CompileProblem issue) { + this.issue = issue; + setSize(67, 12); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchCompileTab.TAB_BACKGROUND); + + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 1, 375, getFrame().width(), getFrame().height(), 512, 512); + + Component s = issue.getName(); + getRoot().getFontRenderer().draw(stack, s, getFrame().x() + 2, getFrame().y() + 2, 0xFFFFFF); + } + + @Override + public void drawFront(PoseStack stack, Point mouse, float partialFrame) { + if (!isFirstHit(mouse)) return; + + List toolTip = new LinkedList<>(); + issue.buildToolTip(toolTip); + renderTooltip(stack, mouse, toolTip); + } + + @Override + public boolean checkHit(Point absPoint) { + return super.checkHit(absPoint) && ProblemListNode.this.convertParentRectToScreen(ProblemListNode.this.getFrame()).contains(absPoint); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/SelectionGridNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/SelectionGridNode.java new file mode 100644 index 000000000..a0f546195 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/SelectionGridNode.java @@ -0,0 +1,117 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class SelectionGridNode extends AbstractGuiNode { + + private static final int BUTTON_WIDTH = 16; + private static final int BUTTON_HEIGHT = 16; + private static final int BUTTON_U = 495; + private static final int BUTTON_V = 1; + private static final int BUTTON_V_SHIFT = 17; + + private final int gridWidth; + private final int gridHeight; + + private Consumer clickFunction = i -> { }; + private Predicate selectionCheckFunction = i -> false; + private BiConsumer> tooltipBuilder = (i, l) -> { }; + + public SelectionGridNode(int gridWidth, int gridHeight) { + this.gridWidth = gridWidth; + this.gridHeight = gridHeight; + setSize(gridWidth * BUTTON_WIDTH, gridHeight * BUTTON_HEIGHT); + addButtons(); + } + + public void setClickFunction(Consumer clickFunction) { + this.clickFunction = clickFunction; + } + + public void setTooltipBuilder(BiConsumer> tooltipBuilder) { + this.tooltipBuilder = tooltipBuilder; + } + + public void setSelectionCheckFunction(Predicate selectionCheckFunction) { + this.selectionCheckFunction = selectionCheckFunction; + } + + private void addButtons() { + + for (int y = 0; y < gridHeight; y++) { + for (int x = 0; x < gridWidth; x++) { + + SelectionGridButtonNode button = new SelectionGridButtonNode(x + y * gridHeight); + button.setPosition(x * button.getFrame().width(), y * button.getFrame().height()); + addChild(button); + } + } + } + + private class SelectionGridButtonNode extends AbstractGuiNode { + + private final int i; + + public SelectionGridButtonNode(int i) { + this.i = i; + this.setSize(BUTTON_WIDTH, BUTTON_HEIGHT); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + + boolean mouseover = getFrame().contains(mouse) && isFirstHit(mouse); + + int state = SelectionGridNode.this.selectionCheckFunction.test(i) ? 2 : + mouseover ? 1 : 0; + + int x = getPosition().x; + int y = getPosition().y; + int w = getFrame().width(); + int h = getFrame().height(); + int u = BUTTON_U; + int v = BUTTON_V + BUTTON_V_SHIFT * state; + + GuiComponent.blit(stack, x, y, u, v, w, h, 512, 512); + + + } + + @Override + public void drawFront(PoseStack stack, Point mouse, float partialFrame) { + + if (!isFirstHit(mouse)) return; + + List tooltip = new LinkedList<>(); + tooltipBuilder.accept(i, tooltip); + + renderTooltip(stack, mouse, tooltip); + } + + @Override + public boolean mouseClicked(Point p, int glfwMouseButton, boolean consumed) { + if (!consumed && isFirstHit(p)) { + getRoot().getMinecraft().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1)); + clickFunction.accept(i); + return true; + } + return false; + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/SimpleUVTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/SimpleUVTab.java new file mode 100644 index 000000000..03ae5fe81 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/SimpleUVTab.java @@ -0,0 +1,111 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; + +import java.util.List; + +public class SimpleUVTab implements TabControllerNode.IToolbarTab { + + private final AbstractGuiNode tabBodyNode; + private final Component tabName; + private final TabButtonNode.TabSide tabSide; + + private final int u; + private final int v; + private final ResourceLocation texture; + + public SimpleUVTab(AbstractGuiNode tabBodyNode, String unlocalTabName, TabButtonNode.TabSide side, int u, int v, ResourceLocation texture) { //TODO Icon width/height? + this.tabBodyNode = tabBodyNode; + this.tabName = new TranslatableComponent(unlocalTabName); + this.tabSide = side; + this.u = u; + this.v = v; + this.texture = texture; + } + + public SimpleUVTab(AbstractGuiNode tabBodyNode, String tabName, TabButtonNode.TabSide side, int u, int v) { + this(tabBodyNode, tabName, side, u, v, ICWorkbenchScreen.BACKGROUND); + } + + public AbstractGuiNode getTabBodyNode() { + return tabBodyNode; + } + + //region Customization + protected StatusDot getStatusDot() { + return StatusDot.NONE; + } + + protected void buildTooltip(List tooltip) { + tooltip.add(tabName); + } + //endregion + + //region TabControllerNode.IToolbarTab + @Override + public boolean hasBody() { + return true; + } + + @Override + public void onTabClosed() { + tabBodyNode.setHidden(true); + } + + @Override + public void onTabOpened() { + tabBodyNode.setHidden(false); + } + + @Override + public void onTabMinimized() { + tabBodyNode.setHidden(true); + } + + @Override + public TabButtonNode createButtonNode() { + return new TabButtonNode(this, tabSide) { + @Override + public void renderIcon(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, texture); + GuiComponent.blit(stack, getFrame().x() + 3, getFrame().y() + 3, u, v, 14, 14, 512, 512); + + StatusDot statusDot = SimpleUVTab.this.getStatusDot(); + if (statusDot != StatusDot.NONE) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + GuiComponent.blit(stack, getFrame().x() + 3 + 7, getFrame().y() + 3, 7, 7, statusDot.u, statusDot.v, 14, 14, 512, 512); + } + } + + @Override + public void buildTooltip(List tooltip) { + SimpleUVTab.this.buildTooltip(tooltip); + } + }; + } + //endregion + + public enum StatusDot { + NONE(0, 0), + GREEN(435, 1), + YELLOW(450, 1), + RED(465, 1) + ; + + public final int u; + public final int v; + + StatusDot(int u, int v) { + this.u = u; + this.v = v; + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/TabButtonNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/TabButtonNode.java new file mode 100644 index 000000000..1812ecee2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/TabButtonNode.java @@ -0,0 +1,170 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.resources.sounds.SimpleSoundInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; + +import java.util.LinkedList; +import java.util.List; + +public abstract class TabButtonNode extends AbstractGuiNode { + + private TabState renderState; + private final TabSide side; + + protected final TabControllerNode.IToolbarTab tab; + + public TabButtonNode(TabControllerNode.IToolbarTab tab, TabSide side) { + this(tab, side, TabState.ALL_CLOSED); + } + + public TabButtonNode(TabControllerNode.IToolbarTab tab, TabSide side, TabState renderState) { + this.tab = tab; + this.side = side; + this.renderState = renderState; + + setSize(side.w, side.h); + } + + + public TabState getRenderState() { + return renderState; + } + + public void setTabState(TabState renderState) { + + if (renderState != this.renderState) { + TabState prevState = this.renderState; + this.renderState = renderState; + + onTabStateChanged(prevState, renderState); + } + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + + int x = getPosition().x; + int y = getPosition().y; + int u = side.u; + int v = side.v; + int w = side.w; + int h = side.h; + + int ex = x + side.extOffsetX; + int ey = y + side.extOffsetY; + int eu = side.extU; + int ev = side.extV; + int ew = side.extW; + int eh = side.extH; + + // If inactive, render greyed out version of the sprite + if (renderState == TabState.ALL_CLOSED || renderState == TabState.CLOSED) { + u += side.inactiveOffsetU; + v += side.inactiveOffsetV; + eu += side.inactiveOffsetU; + ev += side.inactiveOffsetV; + } + + GuiComponent.blit(stack, x, y, u, v, w, h, 512, 512); + + // Render extension if needed + if (renderState == TabState.CLOSED || renderState == TabState.OPEN) { + GuiComponent.blit(stack, ex, ey, eu, ev, ew, eh, 512, 512); + } + + renderIcon(stack, mouse, partialFrame); + } + + @Override + public void drawFront(PoseStack stack, Point mouse, float partialFrame) { + + if (!isFirstHit(mouse)) + return; + + List tooltip = new LinkedList<>(); + buildTooltip(tooltip); + + renderTooltip(stack, mouse, tooltip); + } + + @Override + public boolean mouseClicked(Point p, int glfwMouseButton, boolean consumed) { + if (!consumed && isFirstHit(p)) { + getRoot().getMinecraft().getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1)); + onClicked(); + return true; + } + return false; + } + + public void onClicked() { + TabControllerNode tabControllerNode = (TabControllerNode) getParent(); + tabControllerNode.openTab(tab); + } + + public void onTabStateChanged(TabState prevState, TabState newState) { + switch (newState) { + case OPEN: + tab.onTabOpened(); + break; + case CLOSED: + case ALL_CLOSED: + tab.onTabClosed(); + break; + case MINIMIZED: + tab.onTabMinimized(); + } + } + + public abstract void renderIcon(PoseStack stack, Point mouse, float partialFrame); + + public abstract void buildTooltip(List tooltip); + + enum TabState { + ALL_CLOSED, // Unselected with no extension + CLOSED, // Unselected with extension + MINIMIZED, // Selected with no extension + OPEN // Selected with extension + } + + public enum TabSide { + LEFT(482, 179, 20, 20, 503, 179, 6, 20, 16, 0, 0, 21), + BOTTOM(466, 234, 20, 20, 466, 227, 20, 6, 0, -2, 21, 0); + + public final int u; + public final int v; + public final int w; + public final int h; + public final int extU; + public final int extV; + public final int extW; + public final int extH; + public final int extOffsetX; + public final int extOffsetY; + public final int inactiveOffsetU; + public final int inactiveOffsetV; + + TabSide(int u, int v, int w, int h, int extU, int extV, int extW, int extH, int extOffsetX, int extOffsetY, int inactiveOffsetU, int inactiveOffsetV) { + this.u = u; + this.v = v; + this.w = w; + this.h = h; + this.extU = extU; + this.extV = extV; + this.extW = extW; + this.extH = extH; + this.extOffsetX = extOffsetX; + this.extOffsetY = extOffsetY; + this.inactiveOffsetU = inactiveOffsetU; + this.inactiveOffsetV = inactiveOffsetV; + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/TabControllerNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/TabControllerNode.java new file mode 100644 index 000000000..9a1bb4c2d --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/TabControllerNode.java @@ -0,0 +1,167 @@ +package mrtjp.projectred.fabrication.gui; + +import mrtjp.projectred.redui.AbstractGuiNode; +import net.covers1624.quack.collection.FastStream; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import static mrtjp.projectred.fabrication.gui.TabButtonNode.TabState.*; + +public class TabControllerNode extends AbstractGuiNode { + + private final List tabButtonList = new LinkedList<>(); + private final List tabs = new LinkedList<>(); + + public void addButtonForTab(IToolbarTab tab) { + + int i = tabs.size(); + + TabButtonNode button = tab.createButtonNode(); + button.setZPosition(0.1); + button.setTabState(TabButtonNode.TabState.ALL_CLOSED); + addChild(button); + + tabs.add(new TabEntry(i, button, tab)); + tabButtonList.add(button); + } + + public Optional getSelectedTab() { + for (TabEntry entry : tabs) { + TabButtonNode.TabState state = entry.buttonNode.getRenderState(); + if (state == OPEN || state == MINIMIZED) { + return Optional.ofNullable(entry.tab); + } + } + return Optional.empty(); + } + + public IToolbarTab getTab(int index) { + return tabs.get(index).tab; + } + + public void spreadButtonsVertically(int padding) { + int y = 0; + for (TabButtonNode button : tabButtonList) { + button.setPosition(0, y); + y += button.getFrame().height() + padding; + } + } + + public void spreadButtonsHorizontally(int padding) { + int x = 0; + for (TabButtonNode button : tabButtonList) { + button.setPosition(x, 0); + x += button.getFrame().width() + padding; + } + } + + public void selectInitialTab(int tabIndex) { + openTab(tabs.get(tabIndex).tab); + } + + public void openTab(IToolbarTab tab) { + + boolean tabsOpen = false; + + for (TabEntry entry : tabs) { + if (entry.tab == tab) { + entry.buttonNode.setTabState(OPEN); + if (!tab.hasBody()) { + entry.buttonNode.setTabState(MINIMIZED); + } else { + tabsOpen = true; + } + } else { + entry.buttonNode.setTabState(CLOSED); + } + } + + if (!tabsOpen) { + for (TabEntry entry : tabs) { + if (entry.tab != tab) entry.buttonNode.setTabState(ALL_CLOSED); + } + } + } + + public void openTab(Predicate selector) { + for (TabEntry entry : tabs) { + if (selector.test(entry.tab)) { + openTab(entry.tab); + return; + } + } + } + + public void closeTab(IToolbarTab tab) { + + boolean tabsOpen = false; + + for (TabEntry entry : tabs) { + if (entry.tab == tab) { + entry.buttonNode.setTabState(CLOSED); + } + + if (entry.buttonNode.getRenderState() == OPEN) { + tabsOpen = true; + } + } + + if (!tabsOpen) { + for (TabEntry entry : tabs) entry.buttonNode.setTabState(ALL_CLOSED); + } + } + + public void minimizeTab(IToolbarTab tab) { + boolean tabsOpen = false; + + for (TabEntry entry : tabs) { + if (entry.tab == tab) { + entry.buttonNode.setTabState(MINIMIZED); + } + + if (entry.buttonNode.getRenderState() == OPEN) { + tabsOpen = true; + } + } + + if (!tabsOpen) { + for (TabEntry entry : tabs) { + if (entry.tab != tab) entry.buttonNode.setTabState(ALL_CLOSED); + } + } + } + + public void closeAllTabs() { + for (TabEntry entry : tabs) { + entry.buttonNode.setTabState(ALL_CLOSED); + } + } + + private static class TabEntry { + public final int i; + public final TabButtonNode buttonNode; + public final IToolbarTab tab; + + public TabEntry(int i, TabButtonNode buttonNode, IToolbarTab tab) { + this.i = i; + this.buttonNode = buttonNode; + this.tab = tab; + } + } + + public interface IToolbarTab { + + boolean hasBody(); // Tabs without bodies will be immediately minimized after open + + void onTabClosed(); + + void onTabOpened(); + + void onTabMinimized(); + + TabButtonNode createButtonNode(); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/VerticalListNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/VerticalListNode.java new file mode 100644 index 000000000..3ee60a5e0 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/VerticalListNode.java @@ -0,0 +1,257 @@ +package mrtjp.projectred.fabrication.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.RedUINode; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; + +import java.util.function.Supplier; + +public class VerticalListNode extends AbstractGuiNode { + + private static final int TITLE_LINE_HEIGHT = 16; + private static final int TEXT_LINE_HEIGHT = 12; + private static final int TEXT_LEFT_PAD = 5; + private static final int NODE_ROW_VERTICAL_PAD = 2; + + private int listHeight = 0; + + @Override + public void onSubTreePreDrawBack() { + // This node's frame converted to GL11 window coordinates + Rect gl11Rect = calculateGL11Frame(); + + // Enable scissor using the calculated rect + RenderSystem.enableScissor(gl11Rect.x(), gl11Rect.y(), gl11Rect.width(), gl11Rect.height()); + } + + @Override + public void onSubTreePostDrawBack() { + // Disable scissor + RenderSystem.disableScissor(); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + //Background + GuiComponent.fill(stack, getFrame().x(), getFrame().y(), getFrame().x() + getFrame().width(), getFrame().y() + getFrame().height(), 0x7F000000); + + //TODO render top/bottom gradients + } + + public void sortElementsVertically() { + positionElementsAt(0); + } + + public boolean canScroll() { + Rect subFrame = calculateChildrenFrame(); + return subFrame.height() <= calculateAccumulatedFrame().height(); + } + + public double getScrollPercentage() { + Rect subFrame = calculateChildrenFrame(); + if (subFrame.height() <= calculateAccumulatedFrame().height()) { + return 0; + } + + int totalScroll = subFrame.height() - getFrame().height(); // How much scroll is possible + int dist = subFrame.maxY() - getFrame().maxY(); // How close to the bottom we are + + return (double) (totalScroll - dist) / totalScroll; + } + + public void setScrollPercentage(double scrollPercentage) { + Rect subFrame = calculateChildrenFrame(); + if (subFrame.height() <= getFrame().height()) return; + + int totalScroll = subFrame.height() - getFrame().height(); // How much scroll is possible + int dist = (int) (totalScroll * scrollPercentage); // How much we want to scroll + + positionElementsAt(-dist); + } + + protected void positionChildNode(RedUINode node, int x, int y) { + if (node instanceof AbstractGuiNode) { + ((AbstractGuiNode) node).setPosition(x, y); + } + } + + private void positionElementsAt(int y) { + for (RedUINode node : getOurChildren()) { + positionChildNode(node, 0, y); + y += node.calculateAccumulatedFrame().height(); + } + } + + private void positionAndAddChildListElement(RedUINode node) { + positionChildNode(node, 0, listHeight); + listHeight += node.calculateAccumulatedFrame().height(); + addChild(node); + } + + public void addTitleRow(Component title) { + TitleRow titleNode = new TitleRow(title); + titleNode.setSize(getFrame().width(), TITLE_LINE_HEIGHT); + positionAndAddChildListElement(new RowNode(0, titleNode)); + } + + public void addKeyValueRow(Component key, Supplier valueSupplier) { + + TextNode keyNode = new TextNode(key); + keyNode.setSize(getFrame().width() / 2, TEXT_LINE_HEIGHT); + keyNode.setPadding(TEXT_LEFT_PAD); + + TextNode valueNode = new TextNode(valueSupplier); + valueNode.setSize(getFrame().width() / 2, TEXT_LINE_HEIGHT); + valueNode.setPadding(TEXT_LEFT_PAD); + + positionAndAddChildListElement(new RowNode(0, keyNode, valueNode)); + } + + public void addTextWithNodeRow(Component key, RedUINode node) { + TextNode textNode = new TextNode(key); + textNode.setSize(getFrame().width() / 2, Math.max(TEXT_LINE_HEIGHT, node.calculateAccumulatedFrame().height())); + textNode.setPadding(TEXT_LEFT_PAD); + + positionAndAddChildListElement(new RowNode(NODE_ROW_VERTICAL_PAD, textNode, node)); + } + + public void addSingleNodeRow(RedUINode node) { + positionAndAddChildListElement(new RowNode(NODE_ROW_VERTICAL_PAD, node)); + } + + private class RowNode extends AbstractGuiNode { + + public RowNode(int verticalPadding, RedUINode... rowElements) { + int x = 0; + int maxH = 0; + for (RedUINode node : rowElements) { + VerticalListNode.this.positionChildNode(node, x, verticalPadding); + Rect childFrame = node.calculateAccumulatedFrame(); + maxH = Math.max(maxH, childFrame.height()); + x += childFrame.width(); + addChild(node); + } + + setSize(x, maxH + verticalPadding * 2); + } + } + + // TODO move to redui lib + private static class TextNode extends AbstractGuiNode { + + private final Supplier textSupplier; + + private HorizontalAlignment horizontalAlignment = HorizontalAlignment.LEFT; + private VerticalAlignment verticalAlignment = VerticalAlignment.CENTER; + + private int padding = 4; + private int textColor = 0xFFFFFFFF; + + public TextNode(Supplier textSupplier) { + this.textSupplier = textSupplier; + } + + public TextNode(Component text) { + this(() -> text); + } + + public void setTextColor(int textColor) { + this.textColor = textColor; + } + + public void setPadding(int padding) { + this.padding = padding; + } + + public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) { + this.horizontalAlignment = horizontalAlignment; + } + + public void setVerticalAlignment(VerticalAlignment verticalAlignment) { + this.verticalAlignment = verticalAlignment; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + Font fontRenderer = getRoot().getFontRenderer(); + Component text = textSupplier.get(); + + int x = getXForAlignment(fontRenderer, text); + int y = getYForAlignment(fontRenderer); + fontRenderer.drawShadow(stack, text, x, y, textColor); + } + + private Rect getPaddedFrame() { + return getFrame().expand(-padding, -padding); + } + + private int getXForAlignment(Font fontRenderer, Component text) { + switch (horizontalAlignment) { + case LEFT: + return getPaddedFrame().x(); + case CENTER: + return getPaddedFrame().midX() - fontRenderer.width(text) / 2; + case RIGHT: + return getPaddedFrame().maxX() - fontRenderer.width(text); + default: + return getPaddedFrame().x(); + } + } + + private int getYForAlignment(Font fontRenderer) { + switch (verticalAlignment) { + case TOP: + return getPaddedFrame().y(); + case CENTER: + return getPaddedFrame().midY() - fontRenderer.lineHeight / 2; + case BOTTOM: + return getPaddedFrame().maxY() - fontRenderer.lineHeight; + default: + return getPaddedFrame().y(); + } + } + + public enum HorizontalAlignment { + LEFT, + CENTER, + RIGHT + } + + public enum VerticalAlignment { + TOP, + CENTER, + BOTTOM + } + } + + private static class TitleRow extends AbstractGuiNode { + + private static final int TITLE_COLOR = 0xFFFFFFFF; + private final Component title; + + public TitleRow(Component title) { + this.title = title; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + //Line + GuiComponent.fill(stack, getFrame().x(), getFrame().y(), getFrame().x() + getFrame().width(), getFrame().y() + 1, 0xFF000000); + + Font fontRenderer = getRoot().getFontRenderer(); + + int x = getFrame().midX() - fontRenderer.width(title) / 2; + int y = getFrame().midY() - fontRenderer.lineHeight / 2; + fontRenderer.drawShadow(stack, title, x, y, TITLE_COLOR); + + //Line + GuiComponent.fill(stack, getFrame().x(), getFrame().y() + getFrame().height() - 1, getFrame().x() + getFrame().width(), getFrame().y() + getFrame().height(), 0xFF000000); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/WirePlacerToolTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/WirePlacerToolTab.java new file mode 100644 index 000000000..013b10312 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/WirePlacerToolTab.java @@ -0,0 +1,112 @@ +package mrtjp.projectred.fabrication.gui; + +import codechicken.lib.math.MathHelper; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Rotation; +import codechicken.lib.vec.Scale; +import codechicken.lib.vec.TransformationList; +import codechicken.lib.vec.Translation; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.tools.WirePlacerTool; +import mrtjp.projectred.fabrication.engine.wires.ICWireTileType; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.transmission.client.WireModelRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; + +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class WirePlacerToolTab extends ICEditorToolTab { + + private final WirePlacerTool tool; + + public WirePlacerToolTab(ICEditorToolManager manager, WirePlacerTool tool) { + super(manager, tool); + this.tool = tool; + construct(); + } + + private void addWireButton(ICWireTileType type, boolean fullRow) { + ButtonController buttonController = new ButtonController() { + @Override public void getTooltip(List tooltip) { tooltip.add(new TranslatableComponent(type.tileType.getUnlocalizedName())); } + @Override public void onClick() { tool.setWireType(type); } + @Override public boolean isSelected() { return tool.getWireType() == type; } + + @Override + public void renderIcon(PoseStack stack, Point absPos, float partialFrame) { + MultiBufferSource.BufferSource getter = Minecraft.getInstance().renderBuffers().bufferSource(); + CCRenderState ccrs = CCRenderState.instance(); + ccrs.reset(); + ccrs.bind(RenderType.cutout(), getter, stack); + ccrs.overlay = OverlayTexture.NO_OVERLAY; + ccrs.brightness = 0xF000F0; + + double scale = 10/16D; + TransformationList t = new TransformationList( + new Rotation(90.0F * MathHelper.torad, 1.0F, 0.0F, 0.0F), + new Scale(16.0F * scale, -16.0F * scale, 16.0F * scale), + new Translation(absPos.x + 8 - scale*8, absPos.y + 8 - scale*8, 0.0F) + ); + + WireModelRenderer.renderInventory(ccrs, type.multipartType.getThickness(), type.multipartType.getItemColour() << 8 | 0xFF, type.multipartType.getTextures().get(0), t); + + getter.endBatch(); + } + }; + + if (fullRow) { + addFullRowButton(buttonController); + } else { + addSingleButton(buttonController); + } + } + + private void construct() { + + addGroup(UL_TILEGROUP_REDWIRE); + + // Alloy wire + addWireButton(ICWireTileType.RED_ALLOY, true); + + // Insulated wires + for (ICWireTileType type : ICWireTileType.INSULATED) + addWireButton(type, false); + + addGroup(UL_TILEGROUP_BUNDLED); + + // Bundled neutral wire + addWireButton(ICWireTileType.BUNDLED_NEUTRAL, true); + + // Bundled coloured wires + for (ICWireTileType type : ICWireTileType.BUNDLED_COLOURED) + addWireButton(type, false); + + } + + @Override + public TabButtonNode createButtonNode() { + return new TabButtonNode(this, TabButtonNode.TabSide.LEFT) { + @Override + public void renderIcon(PoseStack stack, Point mouse, float partialFrame) { + + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + GuiComponent.blit(stack, getFrame().x() + 3, getFrame().y() + 3, 390, 46, 14, 14, 512, 512); + } + + @Override + public void buildTooltip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_WIRE_TOOL)); + } + }; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchCompileTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchCompileTab.java new file mode 100644 index 000000000..f2eea7329 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchCompileTab.java @@ -0,0 +1,343 @@ +package mrtjp.projectred.fabrication.gui.screen; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.editor.tools.IICEditorTool; +import mrtjp.projectred.fabrication.engine.log.ICCompilerLog; +import mrtjp.projectred.fabrication.gui.*; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractButtonNode; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import org.lwjgl.glfw.GLFW; + +import java.util.LinkedList; +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.ICWorkbenchEditor.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class ICWorkbenchCompileTab extends AbstractGuiNode implements ICRenderNode.IICRenderNodeEventReceiver { + + public static final ResourceLocation TAB_BACKGROUND = new ResourceLocation(ProjectRedFabrication.MOD_ID, "textures/gui/compile_tab.png"); + + private final ICWorkbenchEditor editor; + + private List overlays = new LinkedList<>(); + + private boolean upPressed = false; + private boolean rightPressed = false; + private boolean downPressed = false; + private boolean leftPressed = false; + private boolean layerUpPressed = false; + private boolean layerDownPressed = false; + + public ICWorkbenchCompileTab(ICWorkbenchEditor editor) { + this.editor = editor; + + setSize(304, 222); + initSubNodes(); + } + + private void initSubNodes() { + + CompileButton compileButton = new CompileButton(); + compileButton.setPosition(208, 16); + compileButton.setSize(18, 18); + addChild(compileButton); + + ICRenderNode icRenderNode = new ICRenderNode(editor, this); + icRenderNode.setPosition(7, 18); + icRenderNode.setSize(197, 197); + addChild(icRenderNode); + + // Tab nodes + CompileStackTab compileStackTab = new CompileStackTab(editor); + compileStackTab.setPosition(208, 77); + addChild(compileStackTab); + + CompileTreeTab compileTreeTab = new CompileTreeTab(editor); + compileTreeTab.setPosition(208, 77); + addChild(compileTreeTab); + + CompileProblemsTab compileProblemsTab = new CompileProblemsTab(editor); + compileProblemsTab.setPosition(208, 77); + addChild(compileProblemsTab); + + // Keep track of overlays + overlays.add(compileStackTab); + overlays.add(compileTreeTab); + overlays.add(compileProblemsTab); + + // Bottom tabs + TabControllerNode tabControllerNode = new TabControllerNode(); + tabControllerNode.setPosition(212, 210); + tabControllerNode.setZPosition(0.1); + addChild(tabControllerNode); + + tabControllerNode.addButtonForTab(new SimpleUVTab(compileStackTab, UL_TAB_STACK, TabButtonNode.TabSide.BOTTOM, 350, 11, TAB_BACKGROUND)); + tabControllerNode.addButtonForTab(new SimpleUVTab(compileTreeTab, UL_TAB_TREE, TabButtonNode.TabSide.BOTTOM, 365, 11, TAB_BACKGROUND)); + tabControllerNode.addButtonForTab(new ProblemsTab(compileProblemsTab, UL_TAB_PROBLEMS, TabButtonNode.TabSide.BOTTOM, 380, 11, TAB_BACKGROUND)); + + tabControllerNode.selectInitialTab(0); + tabControllerNode.spreadButtonsHorizontally(1); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, TAB_BACKGROUND); + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 0, 0, getFrame().width(), getFrame().height(), 512, 512); + + // Blueprint name in top left corner + getRoot().getFontRenderer().draw(stack, editor.getIcName(), getFrame().x() + 8, getFrame().y() + 6, EnumColour.GRAY.argb()); + + // Progress bar + RenderSystem.setShaderTexture(0, TAB_BACKGROUND); + int barWidth = 91; + int progress = editor.getStateMachine().getCompilerLog().getProgressScaled(barWidth); + GuiComponent.blit(stack, getFrame().x() + 208, getFrame().y() + 36, 304, 0, barWidth, 5, 512, 512); + GuiComponent.blit(stack, getFrame().x() + 208, getFrame().y() + 36, 304, 5, progress, 5, 512, 512); + + // Progress text + ICCompilerLog log = editor.getStateMachine().getCompilerLog(); + getRoot().getFontRenderer().draw(stack, + new TranslatableComponent(editor.getStateMachine().isCompiling() ? UL_COMPILE_PROGRESS : UL_COMPILE_DONE, log.getCompletedSteps(), log.getTotalSteps()) + .withStyle(UNIFORM_DARK_GRAY), + getFrame().x() + 208, getFrame().y() + 42, EnumColour.GRAY.argb()); + + // Error Count + Component errorText = log.getErrorCount() == 0 ? + new TextComponent(" ") + .append(new TranslatableComponent(UL_NO_ERRORS)) + .withStyle(UNIFORM_DARK_GRAY) : + new TextComponent(" ") + .withStyle(ICWorkbenchEditor.UNIFORM_RED) + .append(new TranslatableComponent(UL_UNIT_ERRORS, log.getErrorCount()) + .withStyle(UNIFORM_DARK_GRAY)); + + Component warningText = log.getWarningCount() == 0 ? + new TextComponent(" ") + .append(new TranslatableComponent(UL_NO_WARNINGS)) + .withStyle(UNIFORM_DARK_GRAY) : + new TextComponent(" ") + .withStyle(ICWorkbenchEditor.UNIFORM_YELLOW) + .append(new TranslatableComponent(UL_UNIT_WARNINGS, log.getWarningCount()) + .withStyle(UNIFORM_DARK_GRAY)); + + getRoot().getFontRenderer().draw(stack, errorText, getFrame().x() + 208, getFrame().y() + 52, EnumColour.GRAY.argb()); + getRoot().getFontRenderer().draw(stack, warningText, getFrame().x() + 208, getFrame().y() + 60, EnumColour.GRAY.argb()); + } + + //TODO Reduce this reused code (ICEditorToolManager) + @Override + public boolean onKeyPressed(int glfwKeyCode, int glfwScanCode, int glfwFlags, boolean consumed) { + if (consumed || isHidden()) + return false; + + switch (glfwKeyCode) { + case GLFW.GLFW_KEY_W: + upPressed = true; + break; + case GLFW.GLFW_KEY_A: + leftPressed = true; + break; + case GLFW.GLFW_KEY_S: + downPressed = true; + break; + case GLFW.GLFW_KEY_D: + rightPressed = true; + break; + case GLFW.GLFW_KEY_UP: + layerUpPressed = true; + break; + case GLFW.GLFW_KEY_DOWN: + layerDownPressed = true; + break; + default: + return false; + } + return true; + } + + @Override + public boolean onKeyReleased(int glfwKeyCode, int glfwScanCode, int glfwFlags, boolean consumed) { + switch (glfwKeyCode) { + case GLFW.GLFW_KEY_W: + upPressed = false; + break; + case GLFW.GLFW_KEY_A: + leftPressed = false; + break; + case GLFW.GLFW_KEY_S: + downPressed = false; + break; + case GLFW.GLFW_KEY_D: + rightPressed = false; + break; + case GLFW.GLFW_KEY_UP: + layerUpPressed = false; + break; + case GLFW.GLFW_KEY_DOWN: + layerDownPressed = false; + break; + default: + return false; + } + return true; + } + + //region ICRenderNode.IICRenderNodeEventReceiver + + @Override + public void update(ICRenderNode renderNode) { + // Pan camera + if (upPressed || downPressed || leftPressed || rightPressed) { + Vector3 panDelta = new Vector3(); + double deltaPerTick = 0.05D; + panDelta.z = (upPressed ? -deltaPerTick : 0) + (downPressed ? deltaPerTick : 0); + panDelta.x = (leftPressed ? -deltaPerTick : 0) + (rightPressed ? deltaPerTick : 0); + renderNode.applyPanningDelta(panDelta); + } + + // Shift Layers + if (layerUpPressed) { + renderNode.setLayer(renderNode.getLayer() + 1); + } else if (layerDownPressed) { + renderNode.setLayer(renderNode.getLayer() - 1); + } + layerUpPressed = false; + layerDownPressed = false; + } + + @Override + public void mouseScrolled(ICRenderNode renderNode, Vector3 mousePosition, double scroll) { + renderNode.moveZoomAt(mousePosition, scroll * 0.3D); + } + + @Override + public void buildTooltip(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, List tooltip) { + if (!isFirstHit) return; + + TileCoord pos = IICEditorTool.toNearestPosition(mousePosition); + editor.getTileMap().getBaseTile(pos).ifPresent(tile -> tile.buildToolTip(tooltip)); + + for (ICompileOverlayRenderer overlay : overlays) { + overlay.buildTooltip(renderNode, mousePosition, tooltip); + } + } + + @Override + public void onRenderOverlay(ICRenderNode renderNode, Vector3 mousePosition, boolean isFirstHit, CCRenderState ccrs, MultiBufferSource getter, PoseStack matrixStack) { + for (ICompileOverlayRenderer overlay : overlays) { + overlay.renderOverlay(renderNode, mousePosition, isFirstHit, ccrs, getter, matrixStack); + } + } + + //endregion + + //region Utilities + public static void appendProblemsInfo(ICCompilerLog log, List tooltip) { + Component errorText = log.getErrorCount() == 0 ? + new TextComponent(" ") + .append(new TranslatableComponent(UL_NO_ERRORS)) + .withStyle(UNIFORM_GRAY) : + new TextComponent(" ") + .withStyle(UNIFORM_RED) + .append(new TranslatableComponent(UL_UNIT_ERRORS, log.getErrorCount()) + .withStyle(UNIFORM_GRAY)); + + Component warningText = log.getWarningCount() == 0 ? + new TextComponent(" ") + .append(new TranslatableComponent(UL_NO_WARNINGS)) + .withStyle(UNIFORM_GRAY) : + new TextComponent(" ") + .withStyle(ICWorkbenchEditor.UNIFORM_YELLOW) + .append(new TranslatableComponent(UL_UNIT_WARNINGS, log.getWarningCount()) + .withStyle(UNIFORM_GRAY)); + + tooltip.add(errorText); + tooltip.add(warningText); + } + //endregion + + public class CompileButton extends AbstractButtonNode { + + @Override + protected void onButtonClicked() { + editor.getStateMachine().sendCompileButtonClicked(); + } + @Override + protected boolean isButtonDisabled() { + return !editor.getStateMachine().canTriggerCompile(); + } + + @Override + protected void drawButtonBody(PoseStack stack, boolean mouseover) { + RenderSystem.setShaderTexture(0, TAB_BACKGROUND); + + if (editor.getStateMachine().isCompiling()) { + // Spinner + long time = Minecraft.getInstance().level.getGameTime(); + int progress = (int) (time / 2) % 8; + int u = 305 + (15 * progress); + int v = 26; + blitCentered(stack, u, v, 14, 14); + } else { + // Hammer icon + blitCentered(stack, 305, 11, 14, 14); + } + } + + private void blitCentered(PoseStack stack, int u, int v, int width, int height) { + GuiComponent.blit(stack, + getFrame().x() + (getFrame().width() - width) / 2, + getFrame().y() + (getFrame().height() - height) / 2, u, v, + width, height, 512, 512); + } + + @Override + protected void buildTooltip(List tooltip) { + tooltip.add(new TranslatableComponent(UL_COMPILE)); + if (editor.getStateMachine().canTriggerCompile()) { + tooltip.add(new TranslatableComponent(UL_COMPILE_READY).withStyle(UNIFORM_GRAY)); + } + } + } + + private class ProblemsTab extends SimpleUVTab { + + public ProblemsTab(AbstractGuiNode tabBodyNode, String unlocalTabName, TabButtonNode.TabSide side, int u, int v, ResourceLocation texture) { + super(tabBodyNode, unlocalTabName, side, u, v, texture); + } + + @Override + protected StatusDot getStatusDot() { + if (editor.getStateMachine().getCompilerLog().getErrorCount() > 0) { + return StatusDot.RED; + } else if (editor.getStateMachine().getCompilerLog().getWarningCount() > 0) { + return StatusDot.YELLOW; + } else { + return StatusDot.NONE; + } + } + + @Override + protected void buildTooltip(List tooltip) { + super.buildTooltip(tooltip); + ICCompilerLog log = editor.getStateMachine().getCompilerLog(); + appendProblemsInfo(log, tooltip); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchEditTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchEditTab.java new file mode 100644 index 000000000..c1dbad28d --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchEditTab.java @@ -0,0 +1,103 @@ +package mrtjp.projectred.fabrication.gui.screen; + +import codechicken.lib.colour.EnumColour; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.editor.ICEditorToolType; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.editor.tools.*; +import mrtjp.projectred.fabrication.gui.*; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiComponent; + +public class ICWorkbenchEditTab extends AbstractGuiNode { + + private final ICWorkbenchEditor editor; + private final ICEditorToolManager toolManager; + + private final TabControllerNode toolbarNode = new TabControllerNode(); + + public ICWorkbenchEditTab(ICWorkbenchEditor editor) { + this.editor = editor; + this.toolManager = new ICEditorToolManager(editor.getToolList()); + + toolManager.addToolSwappedListener(this::onToolSwapped); + + setSize(304, 222); + initSubNodes(); + } + + private void initSubNodes() { + + toolbarNode.setPosition(286, 24); + toolbarNode.setZPosition(0.1); + addChild(toolbarNode); + + for (IICEditorTool tool : editor.getToolList()) { + + ICEditorToolTab tab = getTabForTool(tool); + tab.setPosition(305, 0); + tab.setHidden(true); + + toolbarNode.addButtonForTab(tab); + addChild(tab); + } + toolbarNode.selectInitialTab(0); + toolbarNode.spreadButtonsVertically(1); + + ICRenderNode icRenderNode = new ICRenderNode(editor, toolManager); + icRenderNode.setPosition(7, 18); + icRenderNode.setSize(290, 197); + addChild(icRenderNode); + } + + private ICEditorToolTab getTabForTool(IICEditorTool tool) { + + if (tool instanceof InteractTool) + return new InteractToolTab(toolManager, (InteractTool) tool); + + if (tool instanceof EraseTool) + return new EraserToolTab(toolManager, (EraseTool) tool); + + if (tool instanceof GatePlacerTool) + return new GatePlacerToolTab(toolManager, (GatePlacerTool) tool); + + if (tool instanceof WirePlacerTool) + return new WirePlacerToolTab(toolManager, (WirePlacerTool) tool); + + return new ICEditorToolTab(toolManager, tool); + } + + private void onToolSwapped(ICEditorToolType newToolType) { + toolbarNode.openTab(tab -> ((ICEditorToolTab) tab).getTool().getToolType() == newToolType); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, ICWorkbenchScreen.BACKGROUND); + + Rect frame = getFrame(); + Font fontRenderer = getRoot().getFontRenderer(); + + GuiComponent.blit(stack, frame.x(), frame.y(), 0, 0, frame.width(), frame.height(), 512, 512); + + // Blueprint name in top left corner + fontRenderer.draw(stack, editor.getIcName(), frame.x() + 8, frame.y() + 6, EnumColour.GRAY.argb()); + } + + @Override + public boolean onKeyPressed(int glfwKeyCode, int glfwScanCode, int glfwFlags, boolean consumed) { + if (!consumed && !isHidden()) { + return toolManager.keyPressed(glfwKeyCode, glfwFlags); + } + return false; + } + + @Override + public boolean onKeyReleased(int glfwKeyCode, int glfwScanCode, int glfwFlags, boolean consumed) { + return toolManager.keyReleased(glfwKeyCode, glfwFlags); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchInfoTab.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchInfoTab.java new file mode 100644 index 000000000..5fe8caca4 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchInfoTab.java @@ -0,0 +1,239 @@ +package mrtjp.projectred.fabrication.gui.screen; + +import codechicken.lib.colour.EnumColour; +import codechicken.lib.texture.TextureUtils; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.fengine.TileCoord; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.gui.ButtonArrayNode; +import mrtjp.projectred.fabrication.gui.PipelineDiagramNode; +import mrtjp.projectred.fabrication.gui.VerticalListNode; +import mrtjp.projectred.fabrication.lithography.LithographyPipeline; +import mrtjp.projectred.fabrication.lithography.ProcessNode; +import mrtjp.projectred.fabrication.lithography.WaferType; +import mrtjp.projectred.fabrication.lithography.YieldCalculator; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.lib.Rect; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.ScrollBarNode; +import mrtjp.projectred.redui.TextBoxNode; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class ICWorkbenchInfoTab extends AbstractGuiNode { + + public static final ResourceLocation TAB_BACKGROUND = new ResourceLocation(ProjectRedFabrication.MOD_ID, "textures/gui/info_tab.png"); + + private final ICWorkbenchEditor editor; + + private final YieldCalculator yieldCalculator = new YieldCalculator(); + + private VerticalListNode listNode; + + public ICWorkbenchInfoTab(ICWorkbenchEditor editor) { + this.editor = editor; + + setSize(304, 222); + initSubNodes(); + } + + private void initSubNodes() { + + listNode = new VerticalListNode(); + listNode.setPosition(6, 17); + listNode.setSize(280, 198); + addChild(listNode); + + NameTextBox nameTextBox = new NameTextBox(); + nameTextBox.setSize(80, 18); + + listNode.addTitleRow(new TranslatableComponent(UL_BLUEPRINT_INFO)); + listNode.addTextWithNodeRow(new TranslatableComponent(UL_BLUEPRINT_NAME), nameTextBox); + listNode.addKeyValueRow(new TranslatableComponent(UL_BLUEPRINT_OWNER), () -> new TranslatableComponent("//TODO")); + + listNode.addKeyValueRow(new TranslatableComponent(UL_BLUEPRINT_DIM), () -> { + TileCoord dimensions = editor.getTileMap().getDimensions(); + return new TranslatableComponent(UL_DIMENSIONS_TILES, dimensions.x, dimensions.z); + }); + + listNode.addKeyValueRow(new TranslatableComponent(UL_BLUEPRINT_LAYERS), () -> { + TileCoord dimensions = editor.getTileMap().getDimensions(); + return new TextComponent("" + dimensions.y); + }); + + listNode.addTitleRow(new TranslatableComponent(UL_YIELD_CALCULATOR)); + + ButtonArrayNode pipelineButtonGrid = createPipelineButtons(); + pipelineButtonGrid.setGridSize(128, 18); + listNode.addTextWithNodeRow(new TranslatableComponent(UL_LITHOGRAPHY_PIPELINE), pipelineButtonGrid); + + ButtonArrayNode processNodeButtonGrid = createProcessNodeButtons(); + processNodeButtonGrid.setGridSize(128, 18); + listNode.addTextWithNodeRow(new TranslatableComponent(UL_PROCESS_NODE), processNodeButtonGrid); + + ButtonArrayNode waferTypeButtonGrid = createWaferTypeButtons(); + waferTypeButtonGrid.setGridSize(128, 18); + listNode.addTextWithNodeRow(new TranslatableComponent(UL_WAFER_TYPE), waferTypeButtonGrid); + + PipelineDiagramNode diagramNode = new PipelineDiagramNode(yieldCalculator); + listNode.addSingleNodeRow(diagramNode); + + ScrollBarNode scrollBarNode = new ScrollBarNode(ScrollBarNode.ScrollAxis.VERTICAL) { + @Override + protected void drawSlider(PoseStack stack, Rect sliderFrame) { + RenderSystem.setShaderTexture(0, TAB_BACKGROUND); + GuiComponent.blit(stack, sliderFrame.x(), sliderFrame.y(), 305, 58, sliderFrame.width(), sliderFrame.height(), 512, 512); + } + + @Override + protected void adjustContent(double scrollPercentage) { + listNode.setScrollPercentage(scrollPercentage); + } + }; + + listNode.addKeyValueRow(new TranslatableComponent(UL_DIE_SIZE), yieldCalculator::getDieDimensionsText); + listNode.addKeyValueRow(new TranslatableComponent(UL_WAFER_SIZE), yieldCalculator::getWaferDimensionsText); + listNode.addKeyValueRow(new TranslatableComponent(UL_DIES_PER_WAFER), yieldCalculator::getDieCountDimensionsText); + listNode.addKeyValueRow(new TranslatableComponent(UL_SINGLE_LAYER_YIELD), yieldCalculator::getSingleLayerYieldText); + listNode.addKeyValueRow(new TranslatableComponent(UL_YIELD), yieldCalculator::getYieldText); + + scrollBarNode.setPosition(290, 17); + scrollBarNode.setSize(8, 198); + scrollBarNode.setSliderSize(8, 16); + addChild(scrollBarNode); + } + + @Override + public void update() { + super.update(); + TileCoord dimensions = editor.getTileMap().getDimensions(); + yieldCalculator.setTileMapSize(dimensions.x, dimensions.z, dimensions.y); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, TAB_BACKGROUND); + GuiComponent.blit(stack, getFrame().x(), getFrame().y(), 0, 0, getFrame().width(), getFrame().height(), 512, 512); + + // Blueprint name in top left corner + getRoot().getFontRenderer().draw(stack, editor.getIcName(), getFrame().x() + 8, getFrame().y() + 6, EnumColour.GRAY.argb()); + } + + private ButtonArrayNode createPipelineButtons() { + ButtonArrayNode.Listener listener = new ButtonArrayNode.Listener() { + @Override + public String getButtonText(int index) { + return LithographyPipeline.values()[index].getUnlocalizedName(); + } + + @Override + public void onButtonClicked(int index) { + LithographyPipeline pipeline = LithographyPipeline.values()[index]; + yieldCalculator.setPipeline(pipeline); + if (!pipeline.isProcessNodeValid(yieldCalculator.getProcessNode())) { + yieldCalculator.setProcessNode(ProcessNode.PROCESS_64NM); + } + if (!pipeline.isWaferTypeValid(yieldCalculator.getWaferType())) { + yieldCalculator.setWaferType(WaferType.ROUGH_WAFER); + } + } + + @Override + public boolean isButtonEnabled(int index) { + return true; + } + + @Override + public boolean isButtonSelected(int index) { + return yieldCalculator.getPipeline().ordinal() == index; + } + }; + return new ButtonArrayNode(listener, 1, LithographyPipeline.values().length, 2); + } + + private ButtonArrayNode createProcessNodeButtons() { + ButtonArrayNode.Listener listener = new ButtonArrayNode.Listener() { + @Override + public String getButtonText(int index) { + return ProcessNode.values()[index].getDisplayName(); + } + + @Override + public void onButtonClicked(int index) { + yieldCalculator.setProcessNode(ProcessNode.values()[index]); + } + + @Override + public boolean isButtonEnabled(int index) { + return yieldCalculator.getPipeline().isProcessNodeValid(ProcessNode.values()[index]); + } + + @Override + public boolean isButtonSelected(int index) { + return yieldCalculator.getProcessNode().ordinal() == index; + } + }; + return new ButtonArrayNode(listener, 1, ProcessNode.values().length, 2); + } + + private ButtonArrayNode createWaferTypeButtons() { + ButtonArrayNode.Listener listener = new ButtonArrayNode.Listener() { + @Override + public String getButtonText(int index) { + return WaferType.values()[index].getUnlocalizedName(); + } + + @Override + public void onButtonClicked(int index) { + yieldCalculator.setWaferType(WaferType.values()[index]); + } + + @Override + public boolean isButtonEnabled(int index) { + return yieldCalculator.getPipeline().isWaferTypeValid(WaferType.values()[index]); + } + + @Override + public boolean isButtonSelected(int index) { + return yieldCalculator.getWaferType().ordinal() == index; + } + }; + return new ButtonArrayNode(listener, 1, WaferType.values().length, 2); + } + + private class NameTextBox extends TextBoxNode { + + public NameTextBox() { + super(editor.getIcName()); + } + + @Override + public void update() { + super.update(); + if (!getText().equals(editor.getIcName()) && !isEditing()) { + setText(editor.getIcName()); + } + } + + @Override + protected String getSuggestionString() { + return editor.getIcName(); + } + + @Override + protected void onTextChanged() { + } + + @Override + protected void onReturnPressed() { + editor.sendNewICName(getText()); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchScreen.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchScreen.java new file mode 100644 index 000000000..4388fd4cc --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/ICWorkbenchScreen.java @@ -0,0 +1,237 @@ +package mrtjp.projectred.fabrication.gui.screen; + +import codechicken.lib.colour.EnumColour; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.engine.log.ICCompilerLog; +import mrtjp.projectred.fabrication.gui.SimpleUVTab; +import mrtjp.projectred.fabrication.gui.TabButtonNode; +import mrtjp.projectred.fabrication.gui.TabControllerNode; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.tile.ICWorkbenchTile; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.AbstractGuiNode; +import mrtjp.projectred.redui.ItemStackNode; +import mrtjp.projectred.redui.RedUIScreen; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.lwjgl.glfw.GLFW; + +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.ICWorkbenchEditor.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class ICWorkbenchScreen extends RedUIScreen { + + public static final ResourceLocation BACKGROUND = new ResourceLocation(ProjectRedFabrication.MOD_ID, "textures/gui/ic_workbench.png"); + + private final ICWorkbenchTile tile; + private final ICWorkbenchEditor editor; + + private AbstractGuiNode contentNode; + private InactiveOverlayNode overlayNode; + + public ICWorkbenchScreen(ICWorkbenchTile tile) { + super(304, 222, new TextComponent(tile.getType().getRegistryName().toString())); + + this.tile = tile; + this.editor = tile.getEditor(); + + initSubNodes(); + } + + public static void openGuiOnClient(ICWorkbenchTile tile) { + Minecraft.getInstance().setScreen(new ICWorkbenchScreen(tile)); + } + + private void initSubNodes() { + + //organizational node to make hiding everything easy + contentNode = new AbstractGuiNode() { }; + addChild(contentNode); + + ICWorkbenchInfoTab infoTab = new ICWorkbenchInfoTab(editor); + contentNode.addChild(infoTab); + + ICWorkbenchEditTab editTab = new ICWorkbenchEditTab(editor); + contentNode.addChild(editTab); + + ICWorkbenchCompileTab compileTab = new ICWorkbenchCompileTab(editor); + contentNode.addChild(compileTab); + + TabControllerNode tabControllerNode = new TabControllerNode(); + tabControllerNode.setPosition(-19, 4); + tabControllerNode.setZPosition(0.1); + contentNode.addChild(tabControllerNode); + + tabControllerNode.addButtonForTab(new SimpleUVTab(infoTab, UL_TAB_INFO, TabButtonNode.TabSide.LEFT, 420, 1)); + tabControllerNode.addButtonForTab(new EditTab(editTab, UL_TAB_EDIT, TabButtonNode.TabSide.LEFT, 420, 16)); + tabControllerNode.addButtonForTab(new CompileTab(compileTab, UL_TAB_COMPILE, TabButtonNode.TabSide.LEFT, 420, 31)); + + tabControllerNode.selectInitialTab(1); + tabControllerNode.spreadButtonsVertically(1); + + // Overlay for no blueprint + overlayNode = new InactiveOverlayNode(); + overlayNode.setPosition( + getFrame().midX() - overlayNode.getFrame().width() / 2, + getFrame().midY() - overlayNode.getFrame().height() / 2); + addChild(overlayNode); + + refreshOverlay(); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + super.drawBack(stack, mouse, partialFrame); + } + + @Override + public void removed() { + super.removed(); + tile.closeGuiFromClient(); + ProjectRedFabrication.LOGGER.info("ICWorkbenchScreen REMOVED"); + } + + @Override + public void onClose() { + super.onClose(); + ProjectRedFabrication.LOGGER.info("ICWorkbenchScreen ONCLOSE"); + } + + @Override + public void update() { + refreshOverlay(); + } + + @Override + public boolean shouldCloseOnEsc() { + // Suppress Vanilla logic to always close screen on ESC press + return false; + } + + @Override + public boolean keyPressed(int glfwKeyCode, int glfwScanCode, int glfwFlags) { + // This gives our UI elements a chance to consume ESC before we close the screen + if (super.keyPressed(glfwKeyCode, glfwScanCode, glfwFlags)) return true; + + // Since we didn't consume ESC, we'll close the screen if it was pressed + if (glfwKeyCode == GLFW.GLFW_KEY_ESCAPE) { + onClose(); + return true; + } + + return false; + } + + private void refreshOverlay() { + // If active, hide overlay and show every thing else. Otherwise, do opposite + boolean isActive = editor.isActive(); + overlayNode.setHidden(isActive); + contentNode.setHidden(!isActive); + } + + private static class InactiveOverlayNode extends AbstractGuiNode { + + public InactiveOverlayNode() { + setSize(132, 92); + initSubNodes(); + } + + private void initSubNodes() { + ItemStackNode blueprintNode = new ItemStackNode(new ItemStack(FabricationReferences.IC_BLUEPRINT_ITEM)); + blueprintNode.setPosition(58, 24); + addChild(blueprintNode); + + ItemStackNode workbenchNode = new ItemStackNode(new ItemStack(FabricationReferences.IC_WORKBENCH_BLOCK)); + workbenchNode.setPosition(58, 64); + addChild(workbenchNode); + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + RenderSystem.setShaderTexture(0, BACKGROUND); + blit(stack, getFrame().x(), getFrame().y(), 0, 222, getFrame().width(), getFrame().height(), 512, 512); + + Font fontRenderer = getRoot().getFontRenderer(); + Component text = new TranslatableComponent(UL_PLACE_BLUEPRINT); + + fontRenderer.draw(stack, text, + getFrame().midX() - fontRenderer.width(text) / 2f, + getFrame().y() + 6, EnumColour.GRAY.argb()); + } + } + + private class EditTab extends SimpleUVTab { + + public EditTab(AbstractGuiNode tabBodyNode, String unlocalTabName, TabButtonNode.TabSide side, int u, int v) { + super(tabBodyNode, unlocalTabName, side, u, v); + } + + @Override + protected StatusDot getStatusDot() { + if (editor.getStateMachine().isSimulating()) { + return StatusDot.GREEN; + } + return StatusDot.NONE; + } + + @Override + protected void buildTooltip(List tooltip) { + super.buildTooltip(tooltip); + if (editor.getStateMachine().isSimulating()) { + tooltip.add(new TranslatableComponent(UL_SIM_RUNNING).withStyle(UNIFORM_GRAY)); + } + } + } + + private class CompileTab extends SimpleUVTab { + + public CompileTab(AbstractGuiNode tabBodyNode, String unlocalTabName, TabButtonNode.TabSide side, int u, int v) { + super(tabBodyNode, unlocalTabName, side, u, v); + } + + @Override + protected StatusDot getStatusDot() { + if (editor.getStateMachine().isCompiling()) { + return StatusDot.GREEN; + } + if (editor.getStateMachine().didLastCompileFailed()) { + return StatusDot.RED; + } + if (editor.getStateMachine().canTriggerCompile()) { + return StatusDot.YELLOW; + } + return StatusDot.NONE; + } + + @Override + protected void buildTooltip(List tooltip) { + super.buildTooltip(tooltip); + + ICCompilerLog log = editor.getStateMachine().getCompilerLog(); + if (editor.getStateMachine().canTriggerCompile()) { + tooltip.add(new TranslatableComponent(UL_COMPILE_READY).withStyle(UNIFORM_GRAY)); + } + + if (editor.getStateMachine().isCompiling()) { + tooltip.add(new TranslatableComponent(UL_COMPILE_PROGRESS, log.getCompletedSteps(), log.getTotalSteps()).withStyle(UNIFORM_GRAY)); + } + + if (editor.getStateMachine().didLastCompileFailed()) { + tooltip.add(new TranslatableComponent(UL_COMPILE_FAILED).withStyle(UNIFORM_RED)); + } + + ICWorkbenchCompileTab.appendProblemsInfo(log, tooltip); + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/LithographyTableScreen.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/LithographyTableScreen.java new file mode 100644 index 000000000..4adde857b --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/LithographyTableScreen.java @@ -0,0 +1,48 @@ +package mrtjp.projectred.fabrication.gui.screen.inventory; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.inventory.container.LithographyTableContainer; +import mrtjp.projectred.lib.GuiLib; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.RedUIContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class LithographyTableScreen extends RedUIContainerScreen { + + public static final ResourceLocation BACKGROUND = new ResourceLocation(ProjectRedFabrication.MOD_ID, "textures/gui/lithography_table.png"); + + public LithographyTableScreen(LithographyTableContainer container, Inventory playerInventory, Component title) { + super(176, 171, container, playerInventory, title); //TODO size + + inventoryLabelX = 8; + inventoryLabelY = 79; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + super.drawBack(stack, mouse, partialFrame); + + RenderSystem.setShaderTexture(0, BACKGROUND); + int x = getFrame().x(); + int y = getFrame().y(); + + blit(stack, x, y, 0, 0, getFrame().width(), getFrame().height()); + + int s = getMenu().getProgressScaled(24); + blit(stack, x + 80, y + 40, 176, 0, s + 1, 16); + + if (getMenu().canConductorWork()) + blit(stack, x + 16, y + 16, 177, 18, 7, 9); + + GuiLib.drawVerticalTank(stack, this, x + 16, y + 26, 177, 27, 7, 48, getMenu().getChargeScaled(48)); + + if (getMenu().isFlowFull()) + blit(stack, x + 27, y + 16, 185, 18, 7, 9); + + GuiLib.drawVerticalTank(stack, this, x + 27, y + 26, 185, 27, 7, 48, getMenu().getFlowScaled(48)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/PackagingTableScreen.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/PackagingTableScreen.java new file mode 100644 index 000000000..fa9e8c124 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/PackagingTableScreen.java @@ -0,0 +1,64 @@ +package mrtjp.projectred.fabrication.gui.screen.inventory; + +import codechicken.lib.colour.EnumColour; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.inventory.container.PackagingTableContainer; +import mrtjp.projectred.lib.GuiLib; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.RedUIContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.Slot; + +public class PackagingTableScreen extends RedUIContainerScreen { + + public static final ResourceLocation BACKGROUND = new ResourceLocation(ProjectRedFabrication.MOD_ID, "textures/gui/packaging_table.png"); + + public PackagingTableScreen(PackagingTableContainer container, Inventory playerInventory, Component title) { + super(176, 171, container, playerInventory, title); //TODO size + + inventoryLabelX = 8; + inventoryLabelY = 79; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + super.drawBack(stack, mouse, partialFrame); + + RenderSystem.setShaderTexture(0, BACKGROUND); + int x = getFrame().x(); + int y = getFrame().y(); + + blit(stack, x, y, 0, 0, getFrame().width(), getFrame().height()); + + int s = getMenu().getProgressScaled(20); + blit(stack, x + 104, y + 41, 176, 0, s + 1, 16); + + if (getMenu().canConductorWork()) + blit(stack, x + 16, y + 16, 177, 18, 7, 9); + + GuiLib.drawVerticalTank(stack, this, x + 16, y + 26, 177, 27, 7, 48, getMenu().getChargeScaled(48)); + + if (getMenu().isFlowFull()) + blit(stack, x + 27, y + 16, 185, 18, 7, 9); + + GuiLib.drawVerticalTank(stack, this, x + 27, y + 26, 185, 27, 7, 48, getMenu().getFlowScaled(48)); + + // Highlight problematic slots red. + int mask = getMenu().getProblematicSlotMask(); + for (int i = 0; i < 9; i++) { + if ((mask & 1 << i) != 0) { + Slot slot = getMenu().getSlot(i + 36); // Offset past player inventory + int colour = EnumColour.RED.argb(0x55); + fillGradient(stack, + x + slot.x, + y + slot.y, + x + slot.x + 16, + y + slot.y + 16, colour, colour); + } + } + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/PlottingTableScreen.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/PlottingTableScreen.java new file mode 100644 index 000000000..db1ca597b --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/gui/screen/inventory/PlottingTableScreen.java @@ -0,0 +1,48 @@ +package mrtjp.projectred.fabrication.gui.screen.inventory; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.inventory.container.PlottingTableContainer; +import mrtjp.projectred.lib.GuiLib; +import mrtjp.projectred.lib.Point; +import mrtjp.projectred.redui.RedUIContainerScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; + +public class PlottingTableScreen extends RedUIContainerScreen { + + public static final ResourceLocation BACKGROUND = new ResourceLocation(ProjectRedFabrication.MOD_ID, "textures/gui/plotting_table.png"); + + public PlottingTableScreen(PlottingTableContainer container, Inventory playerInventory, Component title) { + super(176, 171, container, playerInventory, title); //TODO size + + inventoryLabelX = 8; + inventoryLabelY = 79; + } + + @Override + public void drawBack(PoseStack stack, Point mouse, float partialFrame) { + super.drawBack(stack, mouse, partialFrame); + + RenderSystem.setShaderTexture(0, BACKGROUND); + int x = getFrame().x(); + int y = getFrame().y(); + + blit(stack, x, y, 0, 0, getFrame().width(), getFrame().height()); + + int s = getMenu().getProgressScaled(24); + blit(stack, x + 80, y + 40, 176, 0, s + 1, 16); + + if (getMenu().canConductorWork()) + blit(stack, x + 16, y + 16, 177, 18, 7, 9); + + GuiLib.drawVerticalTank(stack, this, x + 16, y + 26, 177, 27, 7, 48, getMenu().getChargeScaled(48)); + + if (getMenu().isFlowFull()) + blit(stack, x + 27, y + 16, 185, 18, 7, 9); + + GuiLib.drawVerticalTank(stack, this, x + 27, y + 26, 185, 27, 7, 48, getMenu().getFlowScaled(48)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationBlocks.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationBlocks.java new file mode 100644 index 000000000..19980b623 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationBlocks.java @@ -0,0 +1,45 @@ +package mrtjp.projectred.fabrication.init; + +import mrtjp.projectred.fabrication.block.ICWorkbenchBlock; +import mrtjp.projectred.fabrication.block.LithographyTableBlock; +import mrtjp.projectred.fabrication.block.PackagingTableBlock; +import mrtjp.projectred.fabrication.block.PlottingTableBlock; +import mrtjp.projectred.fabrication.tile.ICWorkbenchTile; +import mrtjp.projectred.fabrication.tile.LithographyTableTile; +import mrtjp.projectred.fabrication.tile.PackagingTableTile; +import mrtjp.projectred.fabrication.tile.PlottingTableTile; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.entity.BlockEntityType; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.*; +import static mrtjp.projectred.fabrication.init.FabricationReferences.*; + +public class FabricationBlocks { + + public static final String ID_IC_WORKBENCH = "ic_workbench"; + public static final String ID_PLOTTING_TABLE = "plotting_table"; + public static final String ID_LITHOGRAPHY_TABLE = "lithography_table"; + public static final String ID_PACKAGING_TABLE = "packaging_table"; + + public static void register() { + + // Blocks + BLOCKS.register(ID_IC_WORKBENCH, ICWorkbenchBlock::new); + BLOCKS.register(ID_PLOTTING_TABLE, PlottingTableBlock::new); + BLOCKS.register(ID_LITHOGRAPHY_TABLE, LithographyTableBlock::new); + BLOCKS.register(ID_PACKAGING_TABLE, PackagingTableBlock::new); + + // Block Items + ITEMS.register(ID_IC_WORKBENCH, () -> new BlockItem(IC_WORKBENCH_BLOCK, new Item.Properties().tab(FABRICATION_GROUP))); + ITEMS.register(ID_PLOTTING_TABLE, () -> new BlockItem(PLOTTING_TABLE_BLOCK, new Item.Properties().tab(FABRICATION_GROUP))); + ITEMS.register(ID_LITHOGRAPHY_TABLE, () -> new BlockItem(LITHOGRAPHY_TABLE_BLOCK, new Item.Properties().tab(FABRICATION_GROUP))); + ITEMS.register(ID_PACKAGING_TABLE, () -> new BlockItem(PACKAGING_TABLE_BLOCK, new Item.Properties().tab(FABRICATION_GROUP))); + + // Tiles + TILE_ENTITIES.register(ID_IC_WORKBENCH, () -> BlockEntityType.Builder.of(ICWorkbenchTile::new, IC_WORKBENCH_BLOCK).build(null)); + TILE_ENTITIES.register(ID_PLOTTING_TABLE, () -> BlockEntityType.Builder.of(PlottingTableTile::new, PLOTTING_TABLE_BLOCK).build(null)); + TILE_ENTITIES.register(ID_LITHOGRAPHY_TABLE, () -> BlockEntityType.Builder.of(LithographyTableTile::new, LITHOGRAPHY_TABLE_BLOCK).build(null)); + TILE_ENTITIES.register(ID_PACKAGING_TABLE, () -> BlockEntityType.Builder.of(PackagingTableTile::new, PACKAGING_TABLE_BLOCK).build(null)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationClientInit.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationClientInit.java new file mode 100644 index 000000000..1f1a44c41 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationClientInit.java @@ -0,0 +1,41 @@ +package mrtjp.projectred.fabrication.init; + +import codechicken.lib.model.ModelRegistryHelper; +import codechicken.lib.texture.SpriteRegistryHelper; +import mrtjp.projectred.fabrication.gui.ICRenderTypes; +import mrtjp.projectred.fabrication.gui.screen.inventory.LithographyTableScreen; +import mrtjp.projectred.fabrication.gui.screen.inventory.PackagingTableScreen; +import mrtjp.projectred.fabrication.gui.screen.inventory.PlottingTableScreen; +import mrtjp.projectred.integration.client.GatePartItemRenderer; +import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; + +import static mrtjp.projectred.fabrication.init.FabricationReferences.*; + +public class FabricationClientInit { + + public static void init() { + final IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + + modEventBus.addListener(FabricationClientInit::clientSetup); + } + + private static void clientSetup(final FMLClientSetupEvent event) { + + // Register sprites + SpriteRegistryHelper iconRegister = new SpriteRegistryHelper(); + iconRegister.addIIconRegister(ICRenderTypes::registerIcons); + + // Register models + ModelRegistryHelper modelRegistryHelper = new ModelRegistryHelper(); + modelRegistryHelper.register(new ModelResourceLocation(FabricationReferences.FABRICATED_GATE_ITEM.getRegistryName(), "inventory"), GatePartItemRenderer.INSTANCE); + + // Register screens + MenuScreens.register(PLOTTING_TABLE_CONTAINER, PlottingTableScreen::new); + MenuScreens.register(LITHOGRAPHY_TABLE_CONTAINER, LithographyTableScreen::new); + MenuScreens.register(PACKAGING_TABLE_CONTAINER, PackagingTableScreen::new); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationContainers.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationContainers.java new file mode 100644 index 000000000..9a7f1f0d9 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationContainers.java @@ -0,0 +1,19 @@ +package mrtjp.projectred.fabrication.init; + +import codechicken.lib.inventory.container.ICCLContainerType; +import mrtjp.projectred.fabrication.inventory.container.LithographyTableContainer; +import mrtjp.projectred.fabrication.inventory.container.PackagingTableContainer; +import mrtjp.projectred.fabrication.inventory.container.PlottingTableContainer; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.CONTAINERS; +import static mrtjp.projectred.fabrication.init.FabricationBlocks.*; + +public class FabricationContainers { + + public static void register() { + + CONTAINERS.register(ID_PLOTTING_TABLE, () -> ICCLContainerType.create(PlottingTableContainer.FACTORY)); + CONTAINERS.register(ID_LITHOGRAPHY_TABLE, () -> ICCLContainerType.create(LithographyTableContainer.FACTORY)); + CONTAINERS.register(ID_PACKAGING_TABLE, () -> ICCLContainerType.create(PackagingTableContainer.FACTORY)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationItems.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationItems.java new file mode 100644 index 000000000..b7846b863 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationItems.java @@ -0,0 +1,28 @@ +package mrtjp.projectred.fabrication.init; + +import mrtjp.projectred.fabrication.item.*; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.ITEMS; + +public class FabricationItems { + + public static final String ID_IC_BLUEPRINT = "ic_blueprint"; + public static final String ID_BLANK_PHOTOMASK = "blank_photomask"; + public static final String ID_PHOTOMASK_SET = "photomask_set"; + public static final String ID_ROUGH_SILICON_WAFER = "rough_silicon_wafer"; + public static final String ID_ETCHED_SILICON_WAFER = "etched_silicon_wafer"; + public static final String ID_VALID_DIE = "valid_die"; + public static final String ID_INVALID_DIE = "invalid_die"; + + public static void register() { + + // Items + ITEMS.register(ID_IC_BLUEPRINT, ICBlueprintItem::new); + ITEMS.register(ID_BLANK_PHOTOMASK, BlankPhotomaskItem::new); + ITEMS.register(ID_PHOTOMASK_SET, PhotomaskSetItem::new); + ITEMS.register(ID_ROUGH_SILICON_WAFER, RoughSiliconWaferItem::new); + ITEMS.register(ID_ETCHED_SILICON_WAFER, EtchedSiliconWaferItem::new); + ITEMS.register(ID_VALID_DIE, ValidDieItem::new); + ITEMS.register(ID_INVALID_DIE, InvalidDieItem::new); + } +} \ No newline at end of file diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationParts.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationParts.java new file mode 100644 index 000000000..5e93d0579 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationParts.java @@ -0,0 +1,30 @@ +package mrtjp.projectred.fabrication.init; + +import codechicken.multipart.api.MultipartType; +import codechicken.multipart.api.SimpleMultipartType; +import mrtjp.projectred.fabrication.item.FabricatedGatePartItem; +import mrtjp.projectred.fabrication.part.FabricatedGatePart; +import mrtjp.projectred.integration.GateType; +import mrtjp.projectred.integration.part.GatePart; +import net.minecraft.world.item.Item; +import net.minecraftforge.registries.RegistryObject; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.ITEMS; +import static mrtjp.projectred.fabrication.ProjectRedFabrication.PARTS; + +public class FabricationParts { + + public static final String ID_FABRICATED_GATE = "fabricated_gate"; + + public static void register() { + + // Items + RegistryObject registeredItem = ITEMS.register(ID_FABRICATED_GATE, () -> new FabricatedGatePartItem(GateType.FABRICATED_GATE)); + + // Parts + RegistryObject> registeredPartType = PARTS.register(ID_FABRICATED_GATE, () -> new SimpleMultipartType<>(b -> new FabricatedGatePart())); + + // Add to GateType enum + GateType.FABRICATED_GATE.inject(ID_FABRICATED_GATE, isClient -> new FabricatedGatePart(), registeredItem, registeredPartType); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationReferences.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationReferences.java new file mode 100644 index 000000000..921d99732 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationReferences.java @@ -0,0 +1,78 @@ +package mrtjp.projectred.fabrication.init; + +import codechicken.multipart.api.MultipartType; +import mrtjp.projectred.fabrication.block.ICWorkbenchBlock; +import mrtjp.projectred.fabrication.block.LithographyTableBlock; +import mrtjp.projectred.fabrication.block.PackagingTableBlock; +import mrtjp.projectred.fabrication.block.PlottingTableBlock; +import mrtjp.projectred.fabrication.inventory.container.LithographyTableContainer; +import mrtjp.projectred.fabrication.inventory.container.PackagingTableContainer; +import mrtjp.projectred.fabrication.inventory.container.PlottingTableContainer; +import mrtjp.projectred.fabrication.item.*; +import mrtjp.projectred.fabrication.tile.ICWorkbenchTile; +import mrtjp.projectred.fabrication.tile.LithographyTableTile; +import mrtjp.projectred.fabrication.tile.PackagingTableTile; +import mrtjp.projectred.fabrication.tile.PlottingTableTile; +import mrtjp.projectred.integration.item.GatePartItem; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraftforge.registries.ObjectHolder; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.MOD_ID; +import static mrtjp.projectred.fabrication.init.FabricationBlocks.*; +import static mrtjp.projectred.fabrication.init.FabricationItems.*; +import static mrtjp.projectred.fabrication.init.FabricationParts.ID_FABRICATED_GATE; + +@ObjectHolder(MOD_ID) +public class FabricationReferences { + + // Blocks + @ObjectHolder(ID_IC_WORKBENCH) + public static ICWorkbenchBlock IC_WORKBENCH_BLOCK = null; + @ObjectHolder(ID_PLOTTING_TABLE) + public static PlottingTableBlock PLOTTING_TABLE_BLOCK = null; + @ObjectHolder(ID_LITHOGRAPHY_TABLE) + public static LithographyTableBlock LITHOGRAPHY_TABLE_BLOCK = null; + @ObjectHolder(ID_PACKAGING_TABLE) + public static PackagingTableBlock PACKAGING_TABLE_BLOCK = null; + + // Tiles + @ObjectHolder(ID_IC_WORKBENCH) + public static BlockEntityType IC_WORKBENCH_TILE = null; + @ObjectHolder(ID_PLOTTING_TABLE) + public static BlockEntityType PLOTTING_TABLE_TILE = null; + @ObjectHolder(ID_LITHOGRAPHY_TABLE) + public static BlockEntityType LITHOGRAPHY_TABLE_TILE = null; + @ObjectHolder(ID_PACKAGING_TABLE) + public static BlockEntityType PACKAGING_TABLE_TILE = null; + + // Containers + @ObjectHolder(ID_PLOTTING_TABLE) + public static MenuType PLOTTING_TABLE_CONTAINER = null; + @ObjectHolder(ID_LITHOGRAPHY_TABLE) + public static MenuType LITHOGRAPHY_TABLE_CONTAINER = null; + @ObjectHolder(ID_PACKAGING_TABLE) + public static MenuType PACKAGING_TABLE_CONTAINER = null; + + // Items + @ObjectHolder(ID_IC_BLUEPRINT) + public static ICBlueprintItem IC_BLUEPRINT_ITEM = null; + @ObjectHolder(ID_BLANK_PHOTOMASK) + public static BlankPhotomaskItem BLANK_PHOTOMASK_ITEM = null; + @ObjectHolder(ID_PHOTOMASK_SET) + public static PhotomaskSetItem PHOTOMASK_SET_ITEM = null; + @ObjectHolder(ID_ROUGH_SILICON_WAFER) + public static RoughSiliconWaferItem ROUGH_SILICON_WAFER_ITEM = null; + @ObjectHolder(ID_ETCHED_SILICON_WAFER) + public static EtchedSiliconWaferItem ETCHED_SILICON_WAFER_ITEM = null; + @ObjectHolder(ID_VALID_DIE) + public static ValidDieItem VALID_DIE_ITEM = null; + @ObjectHolder(ID_INVALID_DIE) + public static InvalidDieItem INVALID_DIE_ITEM = null; + + // Parts + @ObjectHolder(ID_FABRICATED_GATE) + public static GatePartItem FABRICATED_GATE_ITEM = null; + @ObjectHolder(ID_FABRICATED_GATE) + public static MultipartType FABRICATED_GATE_PART = null; +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationUnlocal.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationUnlocal.java new file mode 100644 index 000000000..2488fe874 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/init/FabricationUnlocal.java @@ -0,0 +1,117 @@ +package mrtjp.projectred.fabrication.init; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.MOD_ID; + +public class FabricationUnlocal { + + private static final String PREFIX = MOD_ID + "."; + + // ICWorkbench tabs + public static final String UL_TAB_INFO = PREFIX + "tab.info"; + public static final String UL_TAB_EDIT = PREFIX + "tab.edit"; + public static final String UL_TAB_COMPILE = PREFIX + "tab.compile"; + public static final String UL_TAB_STACK = PREFIX + "tab.stack"; + public static final String UL_TAB_TREE = PREFIX + "tab.tree"; + public static final String UL_TAB_PROBLEMS = PREFIX + "tab.problems"; + + // Workbench tools + public static final String UL_ERASER_TOOL = PREFIX + "tool.eraser"; + public static final String UL_INTERACT_TOOL = PREFIX + "tool.interact"; + public static final String UL_WIRE_TOOL = PREFIX + "tool.wires"; + public static final String UL_GATE_TOOL = PREFIX + "tool.gates"; + + // Tile interactions + public static final String UL_TOGGLE_STATE = PREFIX + "interact.toggle_state"; + public static final String UL_TOGGLE_IO_MODE = PREFIX + "interact.toggle_io_mode"; + public static final String UL_IO_MODE_INPUT = PREFIX + "interact.io_mode.input"; + public static final String UL_IO_MODE_OUTPUT = PREFIX + "interact.io_mode.output"; + public static final String UL_TOGGLE_COLOUR = PREFIX + "interact.toggle_colour"; + public static final String UL_TOGGLE_DELAY = PREFIX + "interact.toggle_delay"; + public static final String UL_SIDE_ENABLED = PREFIX + "interact.side_enabled"; + public static final String UL_SIDE_DISABLED = PREFIX + "interact.side_disabled"; + + // Problems + public static final String UL_MULTIPLE_DRIVERS_TITLE = PREFIX + "problems.multiple_drivers.title"; + public static final String UL_MULTIPLE_DRIVERS_DESC = PREFIX + "problems.multiple_drivers.desc"; + public static final String UL_DEAD_WIRE_TITLE = PREFIX + "problems.dead_wire.title"; + public static final String UL_DEAD_WIRE_DESC = PREFIX + "problems.dead_wire.desc"; + public static final String UL_DEAD_GATE_TITLE = PREFIX + "problems.dead_gate.title"; + public static final String UL_DEAD_GATE_DESC = PREFIX + "problems.dead_gate.desc"; + public static final String UL_IO_DIR_MISMATCH_TITLE = PREFIX + "problems.io_dir_mismatch.title"; + public static final String UL_IO_DIR_MISMATCH_DESC = PREFIX + "problems.io_dir_mismatch.desc"; + public static final String UL_NO_INPUTS_TITLE = PREFIX + "problems.no_inputs.title"; + public static final String UL_NO_INPUTS_DESC = PREFIX + "problems.no_inputs.desc"; + public static final String UL_NO_OUTPUTS_TITLE = PREFIX + "problems.no_outputs.title"; + public static final String UL_NO_OUTPUTS_DESC = PREFIX + "problems.no_outputs.desc"; + public static final String UL_NO_ERRORS = PREFIX + "problems.no_errors"; + public static final String UL_NO_WARNINGS = PREFIX + "problems.no_warnings"; + + // General workbench UI + public static final String UL_PLACE_BLUEPRINT = PREFIX + "ui.place_blueprint"; + public static final String UL_BLUEPRINT_INFO = PREFIX + "ui.blueprint_info"; + public static final String UL_BLUEPRINT_NAME = PREFIX + "ui.blueprint_name"; + public static final String UL_BLUEPRINT_OWNER = PREFIX + "ui.blueprint_owner"; + public static final String UL_BLUEPRINT_DIM = PREFIX + "ui.blueprint_dim"; + public static final String UL_BLUEPRINT_LAYERS = PREFIX + "ui.blueprint_layers"; + public static final String UL_COMPILE = PREFIX + "ui.compile"; + public static final String UL_COMPILE_PROGRESS = PREFIX + "ui.compile_progress"; + public static final String UL_COMPILE_DONE = PREFIX + "ui.compile_done"; + public static final String UL_COMPILE_READY = PREFIX + "ui.compile_ready"; + public static final String UL_COMPILE_FAILED = PREFIX + "ui.compile_failed"; + public static final String UL_SIM_RUNNING = PREFIX + "ui.sim_running"; + + public static final String UL_YIELD_CALCULATOR = PREFIX + "ui.yield_calculator"; + public static final String UL_LITHOGRAPHY_PIPELINE = PREFIX + "ui.lithography_pipeline"; + public static final String UL_PROCESS_NODE = PREFIX + "ui.process_node"; + public static final String UL_WAFER_TYPE = PREFIX + "ui.wafer_type"; + public static final String UL_DIE_SIZE = PREFIX + "ui.die_size"; + public static final String UL_WAFER_SIZE = PREFIX + "ui.wafer_size"; + public static final String UL_DIES_PER_WAFER = PREFIX + "ui.dies_per_wafer"; + public static final String UL_SINGLE_LAYER_YIELD = PREFIX + "ui.single_layer_yield"; + public static final String UL_YIELD = PREFIX + "ui.yield"; + + // IC Tiles + public static final String UL_IO_GATE_TILE = PREFIX + "tiles.io_gate"; + + // Tile Groups + public static final String UL_TILEGROUP_REDWIRE = PREFIX + "tilegroup.redwire"; + public static final String UL_TILEGROUP_BUNDLED = PREFIX + "tilegroup.bundled"; + public static final String UL_TILEGROUP_IO = PREFIX + "tilegroup.io"; + public static final String UL_TILEGROUP_BASIC = PREFIX + "tilegroup.basic"; + public static final String UL_TILEGROUP_TIMING = PREFIX + "tilegroup.timing"; + public static final String UL_TILEGROUP_MEMORY = PREFIX + "tilegroup.memory"; + + // Item tooltips + public static final String UL_SIZE = PREFIX + "tooltip.size"; + public static final String UL_DEFECT_CHANCE = PREFIX + "tooltip.defect_chance"; + public static final String UL_CORRUPTED_DISCARD = PREFIX + "tooltip.corrupted_discard"; //"Corrupted NBT data, please discard" + public static final String UL_NAME = PREFIX + "tooltip.name"; + public static final String UL_TILE_COUNT = PREFIX + "tooltip.tile_count"; + public static final String UL_IO_TYPES = PREFIX + "tooltip.io_types"; + public static final String UL_INPUT_MASK = PREFIX + "tooltip.input_mask"; + public static final String UL_OUTPUT_MASK = PREFIX + "tooltip.output_mask"; + public static final String UL_TOP = PREFIX + "tooltip.top"; + public static final String UL_RIGHT = PREFIX + "tooltip.right"; + public static final String UL_BOTTOM = PREFIX + "tooltip.bottom"; + public static final String UL_LEFT = PREFIX + "tooltip.left"; + public static final String UL_BUNDLED_INPUT = PREFIX + "tooltip.bundled_input"; + public static final String UL_BUNDLED_OUTPUT = PREFIX + "tooltip.bundled_output"; + public static final String UL_IO_NONE = PREFIX + "tooltip.io_none"; + public static final String UL_CANNOT_FABRICATE = PREFIX + "tooltip.cannot_fabricate"; + + // Interfaces + public static final String UL_INTERFACE_NC = PREFIX + "interface.nc"; + public static final String UL_INTERFACE_REDSTONE = PREFIX + "interface.redstone"; + public static final String UL_INTERFACE_BUNDLED = PREFIX + "interface.bundled"; ; + + // Numbers and measurements + public static final String UL_UNIT_WARNINGS = PREFIX + "unit.warnings"; + public static final String UL_UNIT_ERRORS = PREFIX + "unit.errors"; + public static final String UL_UNIT_TICKS = PREFIX + "unit.ticks"; + public static final String UL_DIMENSIONS_NM = PREFIX + "dimensions.nm"; + public static final String UL_DIMENSIONS_TILES = PREFIX + "dimensions.tiles"; + public static final String UL_DIMENSIONS_DIES = PREFIX + "dimensions.dies"; + public static final String UL_DIMENSIONS_NM_TOTAL = PREFIX + "dimensions.nm_total"; + public static final String UL_DIMENSIONS_TILES_TOTAL = PREFIX + "dimensions.tiles_total"; + public static final String UL_DIMENSIONS_DIES_TOTAL = PREFIX + "dimensions.dies_total"; +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/FabricationMachineContainer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/FabricationMachineContainer.java new file mode 100644 index 000000000..4117d64cc --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/FabricationMachineContainer.java @@ -0,0 +1,28 @@ +package mrtjp.projectred.fabrication.inventory.container; + +import mrtjp.projectred.core.inventory.container.BasePoweredTileContainer; +import mrtjp.projectred.core.inventory.container.SimpleDataSlot; +import mrtjp.projectred.fabrication.tile.FabricationMachineTile; +import net.minecraft.world.inventory.MenuType; + +import javax.annotation.Nullable; + +public class FabricationMachineContainer extends BasePoweredTileContainer { + + private final FabricationMachineTile tile; + + private int remainingWork; + private int totalWork; + + public FabricationMachineContainer(@Nullable MenuType containerType, int windowId, FabricationMachineTile tile) { + super(containerType, windowId, tile); + this.tile = tile; + + addDataSlot(new SimpleDataSlot(tile::getRemainingWork, value -> remainingWork = value)); + addDataSlot(new SimpleDataSlot(tile::getTotalWork, value -> totalWork = value)); + } + + public int getProgressScaled(int scale) { + return totalWork == 0 ? 0 : scale * (totalWork - remainingWork) / totalWork; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/LithographyTableContainer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/LithographyTableContainer.java new file mode 100644 index 000000000..0e93ed0c1 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/LithographyTableContainer.java @@ -0,0 +1,126 @@ +package mrtjp.projectred.fabrication.inventory.container; + +import codechicken.lib.inventory.container.ICCLContainerFactory; +import mrtjp.projectred.core.inventory.container.TakeOnlySlot; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.item.PhotomaskSetItem; +import mrtjp.projectred.fabrication.item.RoughSiliconWaferItem; +import mrtjp.projectred.fabrication.tile.LithographyTableTile; +import mrtjp.projectred.lib.InventoryLib; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class LithographyTableContainer extends FabricationMachineContainer { + + public static ICCLContainerFactory FACTORY = (windowId, inventory, packet) -> { + BlockEntity tile = inventory.player.level.getBlockEntity(packet.readPos()); + if (!(tile instanceof LithographyTableTile)) return null; + + return new LithographyTableContainer(inventory, (LithographyTableTile) tile, windowId); + }; + + private final Inventory playerInventory; + private final LithographyTableTile tile; + + public LithographyTableContainer(Inventory playerInventory, LithographyTableTile tile, int windowId) { + super(FabricationReferences.LITHOGRAPHY_TABLE_CONTAINER, windowId, tile); + this.playerInventory = playerInventory; + this.tile = tile; + + InventoryLib.addPlayerInventory(playerInventory, 8, 89, this::addSlot); + addLithographyTableInventory(); + } + + private void addLithographyTableInventory() { + addSlot(new Slot(tile.getInventory(), 0, 56, 31)); // photomask set input + addSlot(new Slot(tile.getInventory(), 1, 56, 49)); // wafer input + addSlot(new TakeOnlySlot(tile.getInventory(), 2, 110, 31)); // valid die output + addSlot(new TakeOnlySlot(tile.getInventory(), 3, 110, 49)); // invalid die output + } + + public ItemStack quickMoveStack(Player player, int slotIndex) { + + Slot slot = slots.get(slotIndex); + if (slot == null || !slot.hasItem()) return ItemStack.EMPTY; + + ItemStack stack = slot.getItem(); + ItemStack originalStack = stack.copy(); + + if (isOutputs(slotIndex)) { + if (!moveToEntireInventory(stack, true)) return ItemStack.EMPTY; + + } else if (isHotbar(slotIndex) || isPlayerInventory(slotIndex)) { + if (isPhotomaskSet(stack)) { + if (!moveToPhotomaskInput(stack, false)) return ItemStack.EMPTY; + + } else if (isSiliconWafer(stack)) { + if (!moveToWaferInput(stack, false)) return ItemStack.EMPTY; + + } else if (isPlayerInventory(slotIndex)) { + if (!moveToHotbar(stack, false)) return ItemStack.EMPTY; + + } else { // Hot bar + if (!moveToPlayerInventory(stack, false)) return ItemStack.EMPTY; + } + + } else if (isInputs(slotIndex)) { + if (!moveToEntireInventory(stack, true)) return ItemStack.EMPTY; + } + + if (stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (stack.getCount() == originalStack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(player, stack); + + return originalStack; + } + + //@formatter:off + private boolean isHotbar(int slotIndex) { + return slotIndex >= 27 && slotIndex < 36; + } + private boolean isPlayerInventory(int slotIndex) { + return slotIndex >= 0 && slotIndex < 27; + } + private boolean isInputs(int slotIndex) { + return slotIndex >= 36 && slotIndex < 38; + } + private boolean isOutputs(int slotIndex) { + return slotIndex >= 38 && slotIndex < 40; + } + + private boolean moveToHotbar(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 27, 36, reverse); + } + private boolean moveToPlayerInventory(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 0, 27, reverse); + } + private boolean moveToEntireInventory(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 0, 36, reverse); + } + private boolean moveToPhotomaskInput(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 36, 37, reverse); + } + private boolean moveToWaferInput(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 37, 38, reverse); + } + //@formatter:on + + private boolean isPhotomaskSet(ItemStack stack) { + return stack.getItem() instanceof PhotomaskSetItem; //TODO check tag + } + + private boolean isSiliconWafer(ItemStack stack) { + return stack.getItem() instanceof RoughSiliconWaferItem; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/PackagingTableContainer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/PackagingTableContainer.java new file mode 100644 index 000000000..3869cb6d7 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/PackagingTableContainer.java @@ -0,0 +1,125 @@ +package mrtjp.projectred.fabrication.inventory.container; + +import codechicken.lib.inventory.container.ICCLContainerFactory; +import mrtjp.projectred.core.inventory.container.SimpleDataSlot; +import mrtjp.projectred.core.inventory.container.TakeOnlySlot; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.item.ValidDieItem; +import mrtjp.projectred.fabrication.tile.PackagingTableTile; +import mrtjp.projectred.lib.InventoryLib; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class PackagingTableContainer extends FabricationMachineContainer { + + public static ICCLContainerFactory FACTORY = (windowId, inventory, packet) -> { + BlockEntity tile = inventory.player.level.getBlockEntity(packet.readPos()); + if (!(tile instanceof PackagingTableTile)) return null; + + return new PackagingTableContainer(inventory, (PackagingTableTile) tile, windowId); + }; + + private final Inventory playerInventory; + private final PackagingTableTile tile; + + protected int problematicSlotMask; + + public PackagingTableContainer(Inventory playerInventory, PackagingTableTile tile, int windowId) { + super(FabricationReferences.PACKAGING_TABLE_CONTAINER, windowId, tile); + this.playerInventory = playerInventory; + this.tile = tile; + + InventoryLib.addPlayerInventory(playerInventory, 8, 89, this::addSlot); + addPackagingTableInventory(); + + addDataSlot(new SimpleDataSlot(tile::getProblematicSlotMask, value -> problematicSlotMask = value)); + } + + private void addPackagingTableInventory() { + // Input slots + InventoryLib.addInventory(tile.getInventory(), 0, 46, 22, 3, 3, this::addSlot); + + // Output slot + addSlot(new TakeOnlySlot(tile.getInventory(), 9, 135, 40)); + } + + @Override + public ItemStack quickMoveStack(Player player, int slotIndex) { + + Slot slot = slots.get(slotIndex); + if (slot == null || !slot.hasItem()) return ItemStack.EMPTY; + + ItemStack stack = slot.getItem(); + ItemStack originalStack = stack.copy(); + + if (isOutput(slotIndex)) { + if (!moveToEntireInventory(stack, true)) return ItemStack.EMPTY; + + } else if (isHotbar(slotIndex) || isPlayerInventory(slotIndex)) { + if (isValidDie(stack)) { + if (!moveToDieInput(stack, false)) return ItemStack.EMPTY; + + } else if (isPlayerInventory(slotIndex)) { + if (!moveToHotbar(stack, false)) return ItemStack.EMPTY; + + } else { // Hot bar + if (!moveToPlayerInventory(stack, false)) return ItemStack.EMPTY; + } + + } else if (isInputs(slotIndex)) { + if (!moveToEntireInventory(stack, false)) return ItemStack.EMPTY; + } + + if (stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (stack.getCount() == originalStack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(player, stack); + return originalStack; + } + + public int getProblematicSlotMask() { + return problematicSlotMask; + } + + //@formatter:off + private boolean isHotbar(int slotIndex) { + return slotIndex >= 27 && slotIndex < 36; + } + private boolean isPlayerInventory(int slotIndex) { + return slotIndex >= 0 && slotIndex < 27; + } + private boolean isInputs(int slotIndex) { + return slotIndex >= 36 && slotIndex < 45; + } + private boolean isOutput(int slotIndex) { + return slotIndex == 45; + } + + private boolean moveToHotbar(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 27, 36, reverse); + } + private boolean moveToPlayerInventory(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 0, 27, reverse); + } + private boolean moveToEntireInventory(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 0, 36, reverse); + } + private boolean moveToDieInput(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 40, 41, reverse); + } + //@formatter:on + + private boolean isValidDie(ItemStack stack) { + return stack.getItem() instanceof ValidDieItem; //TODO check tag + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/PlottingTableContainer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/PlottingTableContainer.java new file mode 100644 index 000000000..0fec1a2c3 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/inventory/container/PlottingTableContainer.java @@ -0,0 +1,125 @@ +package mrtjp.projectred.fabrication.inventory.container; + +import codechicken.lib.inventory.container.ICCLContainerFactory; +import mrtjp.projectred.core.inventory.container.TakeOnlySlot; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.item.BlankPhotomaskItem; +import mrtjp.projectred.fabrication.item.ICBlueprintItem; +import mrtjp.projectred.fabrication.tile.PlottingTableTile; +import mrtjp.projectred.lib.InventoryLib; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; + +public class PlottingTableContainer extends FabricationMachineContainer { + + public static ICCLContainerFactory FACTORY = (windowId, inventory, packet) -> { + BlockEntity tile = inventory.player.level.getBlockEntity(packet.readPos()); + if (!(tile instanceof PlottingTableTile)) return null; + + return new PlottingTableContainer(inventory, (PlottingTableTile) tile, windowId); + }; + + private final Inventory playerInventory; + private final PlottingTableTile tile; + + public PlottingTableContainer(Inventory playerInventory, PlottingTableTile tile, int windowId) { + super(FabricationReferences.PLOTTING_TABLE_CONTAINER, windowId, tile); + this.playerInventory = playerInventory; + this.tile = tile; + + InventoryLib.addPlayerInventory(playerInventory, 8, 89, this::addSlot); + addPlottingTableInventory(); + } + + private void addPlottingTableInventory() { + addSlot(new Slot(tile.getInventory(), 0, 56, 31)); // blueprint input + addSlot(new Slot(tile.getInventory(), 1, 56, 49)); // empty photo mask input + addSlot(new TakeOnlySlot(tile.getInventory(), 2, 116, 40)); // output + } + + @Override + public ItemStack quickMoveStack(Player player, int slotIndex) { + + Slot slot = slots.get(slotIndex); + if (slot == null || !slot.hasItem()) return ItemStack.EMPTY; + + ItemStack stack = slot.getItem(); + ItemStack originalStack = stack.copy(); + + if (isOutput(slotIndex)) { + if (!moveToEntireInventory(stack, true)) return ItemStack.EMPTY; + + } else if (isHotbar(slotIndex) || isPlayerInventory(slotIndex)) { + if (isBlueprint(stack)) { + if (!moveToBlueprintInput(stack, false)) return ItemStack.EMPTY; + + } else if (isBlankPhotomask(stack)) { + if (!moveToPhotomaskInput(stack, false)) return ItemStack.EMPTY; + + } else if (isPlayerInventory(slotIndex)) { + if (!moveToHotbar(stack, false)) return ItemStack.EMPTY; + + } else { // Hot bar + if (!moveToPlayerInventory(stack, false)) return ItemStack.EMPTY; // Move to player inventory + } + + } else if (isInputs(slotIndex)) { + if (!moveToEntireInventory(stack, false)) return ItemStack.EMPTY; + } + + if (stack.isEmpty()) { + slot.set(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (stack.getCount() == originalStack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(player, stack); + return originalStack; + } + + //@formatter:off + private boolean isHotbar(int slotIndex) { + return slotIndex >= 27 && slotIndex < 36; + } + private boolean isPlayerInventory(int slotIndex) { + return slotIndex >= 0 && slotIndex < 27; + } + private boolean isInputs(int slotIndex) { + return slotIndex >= 36 && slotIndex < 38; + } + private boolean isOutput(int slotIndex) { + return slotIndex == 38; + } + + private boolean moveToHotbar(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 27, 36, reverse); + } + private boolean moveToPlayerInventory(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 0, 27, reverse); + } + private boolean moveToEntireInventory(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 0, 36, reverse); + } + private boolean moveToBlueprintInput(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 36, 37, reverse); + } + private boolean moveToPhotomaskInput(ItemStack stack, boolean reverse) { + return moveItemStackTo(stack, 37, 38, reverse); + } + //@formatter:on + + private boolean isBlueprint(ItemStack stack) { + return stack.getItem() instanceof ICBlueprintItem; //TODO check tag + } + + private boolean isBlankPhotomask(ItemStack stack) { + return stack.getItem() instanceof BlankPhotomaskItem; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/BaseSiliconWaferItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/BaseSiliconWaferItem.java new file mode 100644 index 000000000..246f43f6f --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/BaseSiliconWaferItem.java @@ -0,0 +1,40 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.lithography.WaferType; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; + +import javax.annotation.Nullable; +import java.util.List; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DEFECT_CHANCE; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_SIZE; + +public class BaseSiliconWaferItem extends Item { + + private final WaferType waferType; + + public BaseSiliconWaferItem(WaferType waferType) { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP)); + this.waferType = waferType; + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltipList, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, world, tooltipList, tooltipFlag); + + tooltipList.add(new TranslatableComponent(UL_SIZE).append(": " + waferType.getWaferWidth() + "nm x " + waferType.getWaferHeight() + "nm").withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TranslatableComponent(UL_DEFECT_CHANCE).append(": " + waferType.getDefectRatePerUnitArea()*100 + "% / nm^2").withStyle(ChatFormatting.GRAY)); + } + + public WaferType getWaferType() { + return waferType; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/BlankPhotomaskItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/BlankPhotomaskItem.java new file mode 100644 index 000000000..cb85d930c --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/BlankPhotomaskItem.java @@ -0,0 +1,12 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import net.minecraft.world.item.Item; + +public class BlankPhotomaskItem extends Item { + + public BlankPhotomaskItem() { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/EtchedSiliconWaferItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/EtchedSiliconWaferItem.java new file mode 100644 index 000000000..c02494de2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/EtchedSiliconWaferItem.java @@ -0,0 +1,87 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; + +import javax.annotation.Nullable; +import java.util.List; + +public class EtchedSiliconWaferItem extends Item { + + public EtchedSiliconWaferItem() { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP) + .stacksTo(1)); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltipList, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, world, tooltipList, tooltipFlag); + + if (stack.getTag() == null) + return; + + CompoundTag tag = stack.getTag(); + + // Blueprint data + tooltipList.add(new TextComponent("Name: " + tag.getString("ic_name")).withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TextComponent("Tile count: " + tag.getInt("tilecount")).withStyle(ChatFormatting.GRAY)); + byte bmask = tag.getByte("bmask"); + tooltipList.add(new TextComponent("Input mask: " + "0x" + Integer.toHexString(bmask & 0xF)).withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TextComponent("Output mask: " + "0x" + Integer.toHexString((bmask >> 4) & 0xF)).withStyle(ChatFormatting.GRAY)); + + // Wafer etching data + tooltipList.add(new TextComponent("Yield: " + tag.getDouble("yield")*100 + "%").withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TextComponent("Defects: " + tag.getInt("defectCount")).withStyle(ChatFormatting.GRAY)); + byte[] defects = tag.getByteArray("defects"); + int gridLen = tag.getInt("gridLen"); + for (int y = 0; y < gridLen; y++) { + StringBuilder s = new StringBuilder(); + for (int x = 0; x < gridLen; x++) { + int i = y * gridLen + x; + s.append(defects[i] == 0 ? "[-]" : "[X]"); + } + tooltipList.add(new TextComponent(" " + s).withStyle(ChatFormatting.GRAY)); + } + } + + public static ItemStack createFromPhotomaskSet(ItemStack photomaskSet, int waferLen, int dieLen, double defectChancePerLen) { + + // Create copy of photomask tag + CompoundTag tag = photomaskSet.getTag().copy(); + + int gridLen = waferLen / dieLen; + double defectChancePerDie = dieLen * defectChancePerLen; + int totalDesigns = gridLen * gridLen; + int totalDefects = 0; + + byte[] defects = new byte[totalDesigns]; + for (int i = 0; i < totalDesigns; i++) { + if (Math.random() < defectChancePerDie) { + defects[i] = 1; + totalDefects++; + } + } + + double yield = (totalDesigns - totalDefects) / (double) totalDesigns; + + tag.putInt("gridLen", gridLen); + tag.putInt("designCount", totalDesigns); + tag.putInt("defectCount", totalDefects); + tag.putDouble("yield", yield); + tag.putByteArray("defects", defects); + + // Put NBT on new item stack and return + ItemStack output = new ItemStack(FabricationReferences.ETCHED_SILICON_WAFER_ITEM); + output.setTag(tag); + return output; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/FabricatedGatePartItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/FabricatedGatePartItem.java new file mode 100644 index 000000000..3b2e1f5ad --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/FabricatedGatePartItem.java @@ -0,0 +1,23 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.integration.GateType; +import mrtjp.projectred.integration.item.GatePartItem; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class FabricatedGatePartItem extends GatePartItem { + + public FabricatedGatePartItem(GateType gateType) { + super(gateType); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipList, TooltipFlag flag) { + ICBlueprintItem.buildTooltip(stack.getTag(), tooltipList); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/ICBlueprintItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/ICBlueprintItem.java new file mode 100644 index 000000000..72bb024e8 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/ICBlueprintItem.java @@ -0,0 +1,118 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.engine.InterfaceSpec; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.integration.GateType; +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +import javax.annotation.Nullable; +import java.util.List; + +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.*; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.*; + +public class ICBlueprintItem extends Item { + + public ICBlueprintItem() { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP) + .stacksTo(1)); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level p_77624_2_, List tooltipList, TooltipFlag tooltipFlag) { + buildTooltip(stack.getTag(), tooltipList); + } + + @Override + public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) { + + if (!context.getPlayer().isCreative()) return InteractionResult.PASS; + + // Creative mode bypass: Convert blueprint directly to gate block + BlockState blockState = context.getLevel().getBlockState(context.getClickedPos()); + if (blockState.getBlock() == FabricationReferences.IC_WORKBENCH_BLOCK) { return InteractionResult.PASS; } + + if (!canFabricate(stack.getTag())) { + return InteractionResult.PASS; + } + + ItemStack gate = GateType.FABRICATED_GATE.makeStack(); + gate.setTag(createFabricationCopy(stack.getTag())); + + context.getPlayer().addItem(gate); + return InteractionResult.SUCCESS; + } + + public static void buildTooltip(CompoundTag blueprintTag, List tooltipList) { + + if (blueprintTag == null) return; + + if (!hasFabricationTarget(blueprintTag)) { + tooltipList.add(new TextComponent(" ").withStyle(ChatFormatting.RED) + .append(new TranslatableComponent(UL_CORRUPTED_DISCARD).withStyle(ChatFormatting.GRAY))); + return; + } + + tooltipList.add(new TranslatableComponent(UL_NAME).append(": " + blueprintTag.getString(KEY_IC_NAME)).withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TranslatableComponent(UL_TILE_COUNT).append(": " + blueprintTag.getInt(KEY_TILE_COUNT)).withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TranslatableComponent(UL_IO_TYPES).append(": ").withStyle(ChatFormatting.GRAY)); + + InterfaceSpec spec = getInterfaceSpec(blueprintTag); + TextComponent indent = new TextComponent(" "); + tooltipList.add(indent.copy().append(new TranslatableComponent(UL_TOP)).append(": ").append(new TranslatableComponent(spec.getInterfaceType(0).getUnlocalName())).withStyle(ChatFormatting.GRAY)); + tooltipList.add(indent.copy().append(new TranslatableComponent(UL_RIGHT)).append(": ").append(new TranslatableComponent(spec.getInterfaceType(1).getUnlocalName())).withStyle(ChatFormatting.GRAY)); + tooltipList.add(indent.copy().append(new TranslatableComponent(UL_BOTTOM)).append(": ").append(new TranslatableComponent(spec.getInterfaceType(2).getUnlocalName())).withStyle(ChatFormatting.GRAY)); + tooltipList.add(indent.copy().append(new TranslatableComponent(UL_LEFT)).append(": ").append(new TranslatableComponent(spec.getInterfaceType(3).getUnlocalName())).withStyle(ChatFormatting.GRAY)); + + tooltipList.add(new TranslatableComponent(UL_INPUT_MASK).append(String.format(": 0x%X", spec.getInputMask())).withStyle(ChatFormatting.GRAY)); + tooltipList.add(new TranslatableComponent(UL_OUTPUT_MASK).append(String.format(": 0x%X", spec.getOutputMask())).withStyle(ChatFormatting.GRAY)); + + int warningCount = getWarningCount(blueprintTag); + int errorCount = getErrorCount(blueprintTag); + if (warningCount > 0) { + tooltipList.add(new TextComponent(" ").withStyle(ChatFormatting.YELLOW) + .append(new TranslatableComponent(UL_UNIT_WARNINGS, warningCount).withStyle(ChatFormatting.GRAY))); + } + if (errorCount > 0) { + tooltipList.add(new TextComponent(" ").withStyle(ChatFormatting.RED) + .append(new TranslatableComponent(UL_UNIT_ERRORS, errorCount).withStyle(ChatFormatting.GRAY))); + } + + if (!canFabricate(blueprintTag)) { + tooltipList.add(new TextComponent(" - ") + .append(new TranslatableComponent(UL_CANNOT_FABRICATE)).withStyle(ChatFormatting.RED)); + } + } + + private static TranslatableComponent getBundledIOTextComponent(byte bmask, int r) { + int i = 0x01 << r; + int o = 0x10 << r; + return new TranslatableComponent((bmask & i) != 0 ? UL_BUNDLED_INPUT : (bmask & o) != 0 ? UL_BUNDLED_OUTPUT : UL_IO_NONE); + } + + public static ItemStack createPhotomaskStack(ItemStack blueprintStack) { + + ItemStack photomaskStack = new ItemStack(FabricationReferences.PHOTOMASK_SET_ITEM); + CompoundTag blueprintTag = blueprintStack.getTag(); + + if (!hasFabricationTarget(blueprintTag)) return photomaskStack; + + CompoundTag photomaskTag = createFabricationCopy(blueprintTag); + photomaskStack.setTag(photomaskTag); + + return photomaskStack; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/InvalidDieItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/InvalidDieItem.java new file mode 100644 index 000000000..87eba8c31 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/InvalidDieItem.java @@ -0,0 +1,12 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import net.minecraft.world.item.Item; + +public class InvalidDieItem extends Item { + + public InvalidDieItem() { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/PhotomaskSetItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/PhotomaskSetItem.java new file mode 100644 index 000000000..8c3e8d8cc --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/PhotomaskSetItem.java @@ -0,0 +1,49 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.integration.GateType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; + +import javax.annotation.Nullable; +import java.util.List; + +public class PhotomaskSetItem extends Item { + + public PhotomaskSetItem() { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP)); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level p_77624_2_, List tooltipList, TooltipFlag tooltipFlag) { + + ICBlueprintItem.buildTooltip(stack.getTag(), tooltipList); + } + + @Override + public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext context) { + + if (stack.getTag() != null) { + ItemStack gate = GateType.FABRICATED_GATE.makeStack(); + gate.setTag(stack.getTag()); + + context.getPlayer().addItem(gate); + return InteractionResult.SUCCESS; + } + + return InteractionResult.PASS; + } + + public static ItemStack createDieStack(ItemStack photomask, int count) { + ItemStack validDieStack = new ItemStack(FabricationReferences.VALID_DIE_ITEM, count); + validDieStack.setTag(photomask.getTag().copy()); //Nothing additional to add yet + return validDieStack; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/RoughSiliconWaferItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/RoughSiliconWaferItem.java new file mode 100644 index 000000000..dcef72bab --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/RoughSiliconWaferItem.java @@ -0,0 +1,10 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.lithography.WaferType; + +public class RoughSiliconWaferItem extends BaseSiliconWaferItem { + + public RoughSiliconWaferItem() { + super(WaferType.ROUGH_WAFER); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/item/ValidDieItem.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/ValidDieItem.java new file mode 100644 index 000000000..bf9fe7ab2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/item/ValidDieItem.java @@ -0,0 +1,31 @@ +package mrtjp.projectred.fabrication.item; + +import mrtjp.projectred.fabrication.ProjectRedFabrication; +import mrtjp.projectred.integration.GateType; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; + +import javax.annotation.Nullable; +import java.util.List; + +public class ValidDieItem extends Item { + + public ValidDieItem() { + super(new Item.Properties() + .tab(ProjectRedFabrication.FABRICATION_GROUP)); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level world, List tooltipList, TooltipFlag tooltipFlag) { + ICBlueprintItem.buildTooltip(stack.getTag(), tooltipList); + } + + public static ItemStack createGatePart(ItemStack die) { + ItemStack gate = GateType.FABRICATED_GATE.makeStack(); + gate.setTag(die.getTag().copy()); + return gate; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/ILithographerNetwork.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/ILithographerNetwork.java new file mode 100644 index 000000000..f0bb954c2 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/ILithographerNetwork.java @@ -0,0 +1,12 @@ +package mrtjp.projectred.fabrication.lithography; + +import codechicken.lib.data.MCDataOutput; + +public interface ILithographerNetwork { + + MCDataOutput getBufferedStream(int frameKey); + + boolean isClientSide(); + + void markSave(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/Lithographer.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/Lithographer.java new file mode 100644 index 000000000..f9e19bb5c --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/Lithographer.java @@ -0,0 +1,31 @@ +package mrtjp.projectred.fabrication.lithography; + +import codechicken.lib.data.MCDataInput; + +public class Lithographer { + + // Size of wafer in nanometers + private int waferSize; + // Size of each tile in nanometers + private int tileSize; + // Chance of defect per nanometer + private double defectChance; + + private final ILithographerNetwork network; + + public Lithographer(ILithographerNetwork network) { + this.network = network; + } + + public void clear() { + + } + + public void setup() { + + } + + public void readBufferedStream(MCDataInput in, int frameKey) { + + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/LithographyPipeline.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/LithographyPipeline.java new file mode 100644 index 000000000..2669b5862 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/LithographyPipeline.java @@ -0,0 +1,36 @@ +package mrtjp.projectred.fabrication.lithography; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +public enum LithographyPipeline { + BASIC(new ProcessNode[] { ProcessNode.PROCESS_64NM }, + new WaferType[] { WaferType.ROUGH_WAFER }, + "basic"), + ADVANCED(new ProcessNode[] { ProcessNode.PROCESS_64NM, ProcessNode.PROCESS_32NM, ProcessNode.PROCESS_16NM, ProcessNode.PROCESS_8NM }, + new WaferType[] { WaferType.ROUGH_WAFER, WaferType.PURIFIED_WAFER, WaferType.POLISHED_WAFER }, + "advanced"); + + private final Set compatibleProcesses; + private final Set compatibleWafers; + private final String unlocalizedName; + + LithographyPipeline(ProcessNode[] compatibleProcesses, WaferType[] compatibleWafers, String unlocalizedName) { + this.compatibleProcesses = Arrays.stream(compatibleProcesses).collect(Collectors.toSet()); + this.compatibleWafers = Arrays.stream(compatibleWafers).collect(Collectors.toSet()); + this.unlocalizedName = unlocalizedName; + } + + public boolean isProcessNodeValid(ProcessNode process) { + return compatibleProcesses.contains(process); + } + + public boolean isWaferTypeValid(WaferType wafer) { + return compatibleWafers.contains(wafer); + } + + public String getUnlocalizedName() { + return unlocalizedName; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/ProcessNode.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/ProcessNode.java new file mode 100644 index 000000000..eca41ba5b --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/ProcessNode.java @@ -0,0 +1,33 @@ +package mrtjp.projectred.fabrication.lithography; + +public enum ProcessNode { + PROCESS_64NM(64), + PROCESS_32NM(32), + PROCESS_16NM(16), + PROCESS_8NM(8), + ; + + private final int tileLen; + private final int tileArea; + + ProcessNode(int tileLen) { + this.tileLen = tileLen; + this.tileArea = tileLen * tileLen; + } + + public int getTileWidth() { + return tileLen; + } + + public int getTileHeight() { + return tileLen; + } + + public int getTileArea() { + return tileArea; + } + + public String getDisplayName() { + return "" + tileLen + "nm"; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/WaferType.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/WaferType.java new file mode 100644 index 000000000..0093e84a3 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/WaferType.java @@ -0,0 +1,46 @@ +package mrtjp.projectred.fabrication.lithography; + +public enum WaferType { + + //@formatter:off + ROUGH_WAFER (4096, 0.5, "roughWafer"), + PURIFIED_WAFER (4096, 0.1, "purifiedWafer"), + POLISHED_WAFER (4096, 0.01, "polishedWafer"); + //@formatter:on + + private final String unlocalizedName; + private final int waferLen; + private final int waferArea; + private final double defectRatePerUnitArea; + + // Note: standard die defect rate is rate of defect of 16x16 design in 64nm process + WaferType(int waferLen, double standardDieDefectRate, String unlocalizedName) { + this.waferLen = waferLen; + this.waferArea = waferLen * waferLen; // square wafers only atm + + // back-calculate standard die defect rate to defect per unit area + this.defectRatePerUnitArea = standardDieDefectRate / (Math.pow(64 * 16, 2)); + + this.unlocalizedName = unlocalizedName; + } + + public int getWaferArea() { + return waferArea; + } + + public int getWaferWidth() { + return waferLen; + } + + public int getWaferHeight() { + return waferLen; + } + + public double getDefectRatePerUnitArea() { + return defectRatePerUnitArea; + } + + public String getUnlocalizedName() { + return unlocalizedName; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/YieldCalculator.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/YieldCalculator.java new file mode 100644 index 000000000..aab7ca107 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/lithography/YieldCalculator.java @@ -0,0 +1,97 @@ +package mrtjp.projectred.fabrication.lithography; + +import mrtjp.projectred.lib.Size; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; + +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DIMENSIONS_DIES_TOTAL; +import static mrtjp.projectred.fabrication.init.FabricationUnlocal.UL_DIMENSIONS_NM; + +public class YieldCalculator { + + private int tileMapWidth; + private int tileMapHeight; + private int tileMapLayers; + + private LithographyPipeline pipeline = LithographyPipeline.BASIC; + private ProcessNode processNode = ProcessNode.PROCESS_64NM; + private WaferType waferType = WaferType.ROUGH_WAFER; + + public LithographyPipeline getPipeline() { + return pipeline; + } + + public void setPipeline(LithographyPipeline pipeline) { + this.pipeline = pipeline; + } + + public ProcessNode getProcessNode() { + return processNode; + } + + public void setProcessNode(ProcessNode processNode) { + this.processNode = processNode; + } + + public WaferType getWaferType() { + return waferType; + } + + public void setWaferType(WaferType waferType) { + this.waferType = waferType; + } + + public void setTileMapSize(int width, int height, int layers) { + this.tileMapWidth = width; + this.tileMapHeight = height; + this.tileMapLayers = layers; + } + + public Size getTileMapSize() { + return new Size(tileMapWidth, tileMapHeight); + } + + public Size getDieSize() { + return new Size(tileMapWidth * processNode.getTileWidth(), tileMapHeight * processNode.getTileHeight()); + } + + public Size getWaferSize() { + return new Size(waferType.getWaferWidth(), waferType.getWaferHeight()); + } + + public Size getDieCount() { + return getWaferSize().divide(getDieSize()); + } + + public double getSingleLayerYield() { + Size dieSize = getDieSize(); + return 1.0 - waferType.getDefectRatePerUnitArea() * dieSize.width * dieSize.height; + } + + public double getMultiLayerYield() { + return Math.pow(getSingleLayerYield(), tileMapLayers); + } + + public Component getDieDimensionsText() { + Size dieSize = getDieSize(); + return new TranslatableComponent(UL_DIMENSIONS_NM, dieSize.width, dieSize.height); + } + + public Component getWaferDimensionsText() { + return new TranslatableComponent(UL_DIMENSIONS_NM, waferType.getWaferWidth(), waferType.getWaferHeight()); + } + + public Component getDieCountDimensionsText() { + Size dieCount = getDieCount(); + return new TranslatableComponent(UL_DIMENSIONS_DIES_TOTAL, dieCount.width, dieCount.height, dieCount.width * dieCount.height); + } + + public Component getSingleLayerYieldText() { + return new TextComponent(String.format("%.2f%%", getSingleLayerYield() * 100)); + } + + public Component getYieldText() { + return new TextComponent(String.format("%.2f%%", getMultiLayerYield() * 100)); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/part/FabricatedGatePart.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/part/FabricatedGatePart.java new file mode 100644 index 000000000..9c1306746 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/part/FabricatedGatePart.java @@ -0,0 +1,227 @@ +package mrtjp.projectred.fabrication.part; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import mrtjp.fengine.api.ICFlatMap; +import mrtjp.projectred.core.BundledSignalsLib; +import mrtjp.projectred.fabrication.engine.ICInterfaceType; +import mrtjp.projectred.fabrication.engine.ICSimulationContainer; +import mrtjp.projectred.fabrication.engine.InterfaceSpec; +import mrtjp.projectred.fabrication.engine.PRFabricationEngine; +import mrtjp.projectred.integration.GateType; +import mrtjp.projectred.integration.part.BundledGatePart; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.*; + +public class FabricatedGatePart extends BundledGatePart { + + private final ICSimulationContainer simulationContainer = new ICSimulationContainer(); + private String icName = "untitled"; + private final InterfaceSpec ifSpec = new InterfaceSpec(); + + private long simulationTimeStart = -1L; + + public FabricatedGatePart() { + super(GateType.FABRICATED_GATE); + } + + @Override + public void preparePlacement(Player player, BlockPos pos, int side) { + super.preparePlacement(player, pos, side); + + if (player.level.isClientSide) return; + + ItemStack stack = player.getItemInHand(InteractionHand.MAIN_HAND); // TODO handle offhand + if (stack.isEmpty() || !stack.hasTag()) { + LOGGER.warn("Gate placement issue: no NBT on gate item"); + return; + } + + CompoundTag tag = stack.getTag(); + icName = tag.getString(KEY_IC_NAME); + ICFlatMap flatMap = PRFabricationEngine.instance.deserializeFlatMap(tag.getString(KEY_FLAT_MAP)); + simulationContainer.setFlatMap(flatMap); + + ifSpec.loadFrom(tag, KEY_IO_SPEC); + } + + @Override + public void save(CompoundTag tag) { + super.save(tag); + tag.putString("ic_name", icName); + tag.putLong("sim_time", level().getGameTime() - simulationTimeStart); + simulationContainer.save(tag); + ifSpec.saveTo(tag, "io_spec"); + } + + @Override + public void load(CompoundTag tag) { + super.load(tag); + icName = tag.getString("ic_name"); + simulationTimeStart = tag.getLong("sim_time"); + simulationContainer.load(tag); + ifSpec.loadFrom(tag, "io_spec"); + } + + @Override + public void writeDesc(MCDataOutput packet) { + super.writeDesc(packet); + packet.writeString(icName); + ifSpec.writeDesc(packet); + } + + @Override + public void readDesc(MCDataInput packet) { + super.readDesc(packet); + icName = packet.readString(); + ifSpec.readDesc(packet); + } + + @Override + public ItemStack getItem() { + return super.getItem(); //TODO set nbt on this item + } + + //region RedstoneGatePart overrides + @Override + protected int outputMask(int shape) { + return ifSpec.getRedstoneOutputMask(); + } + + @Override + protected int inputMask(int shape) { + return ifSpec.getRedstoneInputMask(); + } + + @Override + protected int getOutput(int r) { + if (!ifSpec.isOutput(r)) return 0; + + return switch (ifSpec.getInterfaceType(r)) { + case NC, BUNDLED -> 0; // Bundled output handled by getBundledOutput + case REDSTONE -> (simulationContainer.getOutput(r) & 1) != 0 ? 15 : 0; + }; + } + + //endregion + + //region BundledGatePart overrides + @Override + protected int bundledInputMask(int shape) { + return ifSpec.getBundledInputMask(); + } + + @Override + protected int bundledOutputMask(int shape) { + return ifSpec.getBundledOutputMask(); + } + + @Override + protected byte[] getBundledOutput(int r) { + if (!ifSpec.isOutput(r)) return null; + + return switch (ifSpec.getInterfaceType(r)) { + case NC, REDSTONE -> null; // Redstone output handled by getOutput + case BUNDLED -> BundledSignalsLib.unpackDigital(null, simulationContainer.getOutput(r)); //TODO reuse an array + }; + } + //endregion + + //region IGateRenderKey overrides + @Override + public int state2() { + // TODO Temporary: state2 contains IO details for rendering + // TODO May let ifSpec pack this? + int rsMask = ifSpec.getRedstoneInputMask() | ifSpec.getRedstoneOutputMask(); + int analogMask = 0; //TODO + int bundledMask = ifSpec.getBundledInputMask() | ifSpec.getBundledOutputMask(); + return (rsMask & 0xF) | (analogMask & 0xF) << 4 | (bundledMask & 0xF) << 8; + } + + @Override + public String getGateName() { + return icName; + } + //endregion + + @Override + protected void gateLogicOnWorldLoad() { + simulationTimeStart = level().getGameTime() - simulationTimeStart; + } + + @Override + protected void gateLogicSetup() { + if (simulationTimeStart == -1L) { + simulationTimeStart = level().getGameTime(); + tile().setChanged(); + } + } + + @Override + protected void gateLogicOnChange() { + + short[] newInputs = new short[4]; + for (int r = 0; r < 4; r++) newInputs[r] = getModeBasedInput(r); + int changeMask = simulationContainer.setInputs(newInputs); + + // Schedule update if inputs changed + if (changeMask != 0) { + setState(state() & 0xF0 | simulationContainer.inputMask()); + onInputChange(); + scheduleTick(2); + } + } + + @Override + protected void gateLogicOnScheduledTick() { + + // Push latched inputs into simulation registers + simulationContainer.pushInputs(0xF); + + // Run simulation + simulationContainer.simulate(); + int changeMask = simulationContainer.pullOutputs(); + if (changeMask != 0) { + setState(state() & 0xF | simulationContainer.outputMask() << 4); + onOutputChange(changeMask); + } + + // Re-check inputs in case they changed during scheduled tick + gateLogicOnChange(); + } + + @Override + protected void gateLogicOnTick() { + if (!level().isClientSide) { + long simTimeElapsed = level().getGameTime() - simulationTimeStart; + + // Push new simulation time register + simulationContainer.setSystemTime(simTimeElapsed); + simulationContainer.pushTime(); + + // Run simulation + simulationContainer.simulate(); + int changeMask = simulationContainer.pullOutputs(); + if (changeMask != 0) { + setState(state() & 0xF | simulationContainer.outputMask() << 4); + onOutputChange(changeMask); + } + } + } + + private short getModeBasedInput(int r) { + if (!ifSpec.isInput(r)) return 0; + + return switch (ifSpec.getInterfaceType(r)) { + case NC -> 0; + case REDSTONE -> (short) (getRedstoneInput(r) != 0 ? 0xFFFF : 0); + case BUNDLED -> (short) BundledSignalsLib.packDigital(getBundledInput(r)); + }; + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/FabricationMachineTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/FabricationMachineTile.java new file mode 100644 index 000000000..89beb47c0 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/FabricationMachineTile.java @@ -0,0 +1,135 @@ +package mrtjp.projectred.fabrication.tile; + +import mrtjp.projectred.api.IConnectable; +import mrtjp.projectred.core.power.ILowLoadMachine; +import mrtjp.projectred.core.power.ILowLoadPowerLine; +import mrtjp.projectred.core.power.PowerConductor; +import mrtjp.projectred.core.tile.BasePoweredTile; +import mrtjp.projectred.fabrication.block.FabricationMachineBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +public abstract class FabricationMachineTile extends BasePoweredTile implements ILowLoadMachine { + + protected final PowerConductor conductor = new PowerConductor(this, 0.01, 160); + + private int chargeFlow = 0; + private boolean isWorking = false; + private boolean isCharged = false; + private int remainingWork = 0; + private int totalWork = 0; + + public FabricationMachineTile(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Override + public void saveToNBT(CompoundTag tag) { + super.saveToNBT(tag); + conductor.save(tag); + tag.putBoolean("isWorking", isWorking); + tag.putBoolean("isCharged", isCharged); + } + + @Override + public void loadFromNBT(CompoundTag tag) { + super.loadFromNBT(tag); + conductor.load(tag); + isWorking = tag.getBoolean("isWorking"); + isCharged = tag.getBoolean("isCharged"); + } + + @Override + public BlockState storeBlockState(BlockState defaultState) { + return super.storeBlockState(defaultState) + .setValue(FabricationMachineBlock.CHARGED, isCharged) + .setValue(FabricationMachineBlock.WORKING, isWorking); + } + + @Override + public void tick() { + if (getLevel().isClientSide) return; + + boolean wasCharged = isCharged; + boolean wasWorking = isWorking; + + conductor.tick(); + + chargeFlow <<= 1; + if (canConductorWork()) chargeFlow |= 1; + isCharged = canConductorWork(); + + if (!isWorking) { + if (canStartWork()) { + isWorking = true; + remainingWork = totalWork = startWork(); + } + + } else { + int workDone = tickWork(remainingWork); + remainingWork -= workDone; + if (remainingWork <= 0) { + isWorking = false; + remainingWork = 0; + totalWork = 0; + finishWork(); + } + } + + if (isCharged != wasCharged || isWorking != wasWorking) { + pushBlockState(); + } + } + + @Override + public boolean canConnectPart(IConnectable part, int s, int edgeRot) { + if (part instanceof ILowLoadMachine) return true; + if (part instanceof ILowLoadPowerLine) return true; + + return false; + } + + @Override + public PowerConductor getConductor(int dir) { + return conductor; + } + + @Override + public int getConductorCharge() { + return (int) (conductor.getVoltage() * 10); + } + + @Override + public int getConductorFlow() { + return chargeFlow; + } + + @Override + public boolean canConductorWork() { + return getConductorCharge() > 600; + } + + protected void cancelWorkIfNeeded() { + if (isWorking && !canStartWork()) { + isWorking = false; + remainingWork = 0; + totalWork = 0; + pushBlockState(); + } + } + + public int getRemainingWork() { + return remainingWork; + } + + public int getTotalWork() { + return totalWork; + } + + protected abstract boolean canStartWork(); + protected abstract int startWork(); + protected abstract int tickWork(int remainingWork); + protected abstract void finishWork(); +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/ICWorkbenchTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/ICWorkbenchTile.java new file mode 100644 index 000000000..5d269ebcf --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/ICWorkbenchTile.java @@ -0,0 +1,247 @@ +package mrtjp.projectred.fabrication.tile; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.packet.PacketCustom; +import codechicken.lib.vec.Vector3; +import mrtjp.projectred.core.CoreNetwork; +import mrtjp.projectred.core.tile.IPacketReceiverTile; +import mrtjp.projectred.core.tile.ProjectRedTile; +import mrtjp.projectred.fabrication.block.ICWorkbenchBlock; +import mrtjp.projectred.fabrication.editor.ICWorkbenchEditor; +import mrtjp.projectred.fabrication.editor.IICWorkbenchEditorNetwork; +import mrtjp.projectred.fabrication.gui.screen.ICWorkbenchScreen; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static mrtjp.projectred.fabrication.ProjectRedFabrication.LOGGER; + +public class ICWorkbenchTile extends ProjectRedTile implements IPacketReceiverTile, IICWorkbenchEditorNetwork { + + private static final int KEY_CLIENT_OPENED_SCREEN = 0; + private static final int KEY_CLIENT_CLOSED_SCREEN = 1; + private static final int KEY_EDITOR_DESCRIPTION_UPDATE = 50; + private static final int KEY_EDITOR_PACKET = 100; + + private final ICWorkbenchEditor editor = new ICWorkbenchEditor(this); + + private final Map editorBufferedStreams = new HashMap<>(); + private final Set playersWatchingScreen = new HashSet<>(); + + public ICWorkbenchTile(BlockPos pos, BlockState state) { + super(FabricationReferences.IC_WORKBENCH_TILE, pos, state); + } + + public ICWorkbenchEditor getEditor() { + return editor; + } + + @Override + public void writeDesc(MCDataOutput out) { + editor.writeDesc(out); + } + + @Override + public void readDesc(MCDataInput in) { + editor.readDesc(in); + } + + @Override + public void saveToNBT(CompoundTag tag) { + editor.save(tag); + } + + @Override + public void loadFromNBT(CompoundTag tag) { + editor.load(tag); + } + + private Set filterAndGetWatchers() { + Set toRemove = playersWatchingScreen.stream() + .filter(ServerPlayer::hasDisconnected) + .collect(Collectors.toSet()); + playersWatchingScreen.removeAll(toRemove); + return playersWatchingScreen; + } + + @Override + public void tick() { + + editor.tick(); + + flushEditorStreams(); + } + + @Override + public InteractionResult onBlockActivated(Player player, InteractionHand hand, BlockHitResult hit) { + + if (!getLevel().isClientSide()) { + + ItemStack stackInHand = player.getItemInHand(hand); + + boolean blueprintOnTable = getBlockState().getValue(ICWorkbenchBlock.BLUEPRINT_PROPERTY); + boolean blueprintInHand = !stackInHand.isEmpty() && stackInHand.getItem() == FabricationReferences.IC_BLUEPRINT_ITEM; + + if (!blueprintOnTable && blueprintInHand) { + // load blueprint and activate editor + editor.readBlueprintTagAndActivate(stackInHand.getTag()); + stackInHand.shrink(1); + setBlueprintBlockState(true); + sendEditorDescription(); + + } else if (blueprintOnTable && player.isCrouching()) { + // save/drop blueprint and deactivate editor + ItemStack blueprintToDrop = createBlueprintStack(); + dropBlueprintStack(blueprintToDrop); + setBlueprintBlockState(false); + sendEditorDescription(); + + } else { + // open editor GUI + openGuiFromServer(player); + } + } + + return InteractionResult.SUCCESS; + } + + @Override + public void onBlockRemoved() { + if (getEditor().isActive()) { + ItemStack blueprintToDrop = createBlueprintStack(); + ProjectRedTile.dropItem(blueprintToDrop, getLevel(), Vector3.fromBlockPos(getBlockPos())); + } + } + + private ItemStack createBlueprintStack() { + ItemStack stack = new ItemStack(FabricationReferences.IC_BLUEPRINT_ITEM); + editor.writeBlueprintTagAndDeactivate(stack.getOrCreateTag()); + return stack; + } + + private void dropBlueprintStack(ItemStack blueprintToDrop) { + BlockPos pos = getBlockPos().offset(0, 1, 0); + ItemEntity itemEntity = new ItemEntity(getLevel(), pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, blueprintToDrop); + itemEntity.setPickUpDelay(10); + itemEntity.setDeltaMovement(0, 0.15D, 0); + getLevel().addFreshEntity(itemEntity); + } + + private void setBlueprintBlockState(boolean blueprintOnTable) { + BlockState newState = getBlockState().setValue(ICWorkbenchBlock.BLUEPRINT_PROPERTY, blueprintOnTable); + getLevel().setBlockAndUpdate(getBlockPos(), newState); + } + + private void openGuiFromServer(Player player) { + if (getLevel().isClientSide || !(player instanceof ServerPlayer)) { throw new RuntimeException("Server only"); } + filterAndGetWatchers().add((ServerPlayer) player); + LOGGER.info("Watcher added. Size: " + playersWatchingScreen.size()); + sendUpdateToPlayer(KEY_CLIENT_OPENED_SCREEN, editor::writeDesc, (ServerPlayer) player); + } + + private void sendEditorDescription() { + + sendUpdateToPlayerList(KEY_EDITOR_DESCRIPTION_UPDATE, editor::writeDesc, playersWatchingScreen); + } + + public void closeGuiFromClient() { + sendUpdateToServer(1, n -> { }); + } + + @Override + public void receiveUpdateFromServer(int key, MCDataInput input) { + switch (key) { + case KEY_CLIENT_OPENED_SCREEN: // Client opened screen + editor.readDesc(input); + ICWorkbenchScreen.openGuiOnClient(this); + break; + case KEY_EDITOR_DESCRIPTION_UPDATE: // Editor description update + editor.readDesc(input); + break; + case KEY_EDITOR_PACKET: // Some packet for the editor + receiveBufferedStream(input); + break; + default: + LOGGER.error("Unknown packet key from server: " + key); + } + } + + @Override + public void receiveUpdateFromClient(int key, MCDataInput input, ServerPlayer player) { + switch (key) { + case KEY_CLIENT_CLOSED_SCREEN: // Client closed screen + filterAndGetWatchers().remove(player); + LOGGER.info("Watcher removed. Size: " + playersWatchingScreen.size()); + break; + case KEY_EDITOR_PACKET: // Some packet for the editor + receiveBufferedStream(input); + break; + default: + LOGGER.error("Unknown packet key from client: " + key); + } + } + + private void receiveBufferedStream(MCDataInput in) { + int streamKey = in.readUByte(); + int frameKey = in.readUByte(); + while (frameKey != 255) { + editor.readBufferedStream(in, streamKey, frameKey); + frameKey = in.readUByte(); + } + } + + @Override + public MCDataOutput getBufferedStream(int streamKey, int frameKey) { + MCDataOutput out = editorBufferedStreams.computeIfAbsent(streamKey, k -> { + PacketCustom packet = getLevel().isClientSide ? + CoreNetwork.createTileServerPacket(this, (byte) KEY_EDITOR_PACKET) : + CoreNetwork.createTileClientPacket(this, (byte) KEY_EDITOR_PACKET); + packet.writeByte(k); // One-time key that identifies the entire stream + return packet; + }); + + return out.writeByte(frameKey); // Frame byte inserted between chunks of data + } + + private void flushEditorStreams() { + for (PacketCustom packet : editorBufferedStreams.values()) { + packet.writeByte(255); // Terminator frame byte + if (getLevel().isClientSide) { + packet.sendToServer(); + } else { + for (ServerPlayer player : filterAndGetWatchers()) { packet.sendToPlayer(player); } + } + } + editorBufferedStreams.clear(); + } + + @Override + public boolean isClientSide() { + return getLevel().isClientSide(); + } + + @Override + public void markSave() { + this.setChanged(); + } + + @Override + public long getGameTime() { + return getLevel().getGameTime(); + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/LithographyTableTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/LithographyTableTile.java new file mode 100644 index 000000000..326b22ee0 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/LithographyTableTile.java @@ -0,0 +1,171 @@ +package mrtjp.projectred.fabrication.tile; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.util.ServerUtils; +import codechicken.lib.vec.Vector3; +import mrtjp.projectred.core.inventory.BaseInventory; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.inventory.container.LithographyTableContainer; +import mrtjp.projectred.fabrication.item.BaseSiliconWaferItem; +import mrtjp.projectred.fabrication.item.PhotomaskSetItem; +import mrtjp.projectred.fabrication.lithography.ProcessNode; +import mrtjp.projectred.fabrication.lithography.WaferType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class LithographyTableTile extends FabricationMachineTile { + + private final BaseInventory inventory = new BaseInventory(4) { + + @Override + public boolean canPlaceItem(int slot, ItemStack stack) { + switch (slot) { + case 0: + return stack.getItem() instanceof PhotomaskSetItem; + case 1: + return stack.getItem() instanceof BaseSiliconWaferItem; + default: + return false; + } + } + + @Override + public void setChanged() { + super.setChanged(); + cancelWorkIfNeeded(); + } + }; + + public LithographyTableTile(BlockPos pos, BlockState state) { + super(FabricationReferences.LITHOGRAPHY_TABLE_TILE, pos, state); + inventory.addListener(c -> setChanged()); + } + + public Container getInventory() { + return inventory; + } + + @Override + public void saveToNBT(CompoundTag tag) { + super.saveToNBT(tag); + tag.put("inventory", inventory.createTag()); + } + + @Override + public void loadFromNBT(CompoundTag tag) { + super.loadFromNBT(tag); + inventory.loadFrom(tag, "inventory"); + } + + @Override + public void writeDesc(MCDataOutput out) { + } + + @Override + public void readDesc(MCDataInput in) { + } + + @Override + public InteractionResult onBlockActivated(Player player, InteractionHand hand, BlockHitResult hit) { + if (!getLevel().isClientSide) { + ServerUtils.openContainer((ServerPlayer) player, new SimpleMenuProvider((id, inv, p) -> new LithographyTableContainer(inv, this, id), new TranslatableComponent(getBlockState().getBlock().getDescriptionId())), p -> p.writePos(getBlockPos())); + } + + return InteractionResult.SUCCESS; + } + + @Override + public void onBlockRemoved() { + super.onBlockRemoved(); + dropInventory(inventory, getLevel(), Vector3.fromBlockPos(getBlockPos())); + } + + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + return super.getCapability(cap, side); //TODO add capabilities + } + + @Override + protected boolean canStartWork() { + + ItemStack slot0 = inventory.getItem(0); + ItemStack slot1 = inventory.getItem(1); + + if (slot0.isEmpty() || slot1.isEmpty()) { + return false; + } + + if (!(slot0.getItem() instanceof PhotomaskSetItem)) return false; + if (!(slot1.getItem() instanceof BaseSiliconWaferItem)) return false; + + return inventory.getItem(2).isEmpty() && inventory.getItem(3).isEmpty(); + } + + @Override + protected int startWork() { + return 20 * 60; // 60 seconds worth of work + } + + @Override + protected int tickWork(int remainingWork) { + if (canConductorWork()) { + conductor.applyPower(-100); // draw at rate of 100W + return 1; + } + return 0; + } + + @Override + protected void finishWork() { + BaseSiliconWaferItem wafer = (BaseSiliconWaferItem) inventory.getItem(1).getItem(); + + WaferType waferType = wafer.getWaferType(); + ProcessNode processNode = ProcessNode.PROCESS_64NM; + int dieWidth = processNode.getTileWidth() * 16; //TODO Source from photomask itemstack instead of assuming 16x16 + int dieHeight = processNode.getTileHeight() * 16; + int gridWidth = waferType.getWaferWidth() / dieWidth; + int gridHeight = waferType.getWaferHeight() / dieHeight; + double defectChancePerDie = dieWidth * dieHeight * waferType.getDefectRatePerUnitArea(); + + // Calculate number of good and bad dies + int totalDesigns = gridWidth * gridHeight; + int totalDefectiveDies = 0; + for (int i = 0; i < totalDesigns; i++) { + if (Math.random() < defectChancePerDie) { + totalDefectiveDies++; + } + } + int totalValidDies = totalDesigns - totalDefectiveDies; + + // Set the output stacks + if (totalValidDies > 0) { + ItemStack validDieStack = PhotomaskSetItem.createDieStack(inventory.getItem(0), totalValidDies); + inventory.setItem(2, validDieStack); + } + if (totalDefectiveDies > 0) { + ItemStack defectiveDieStack = new ItemStack(FabricationReferences.INVALID_DIE_ITEM, totalDefectiveDies); + inventory.setItem(3, defectiveDieStack); + } + + // Consume inputs + inventory.removeItem(1, 1); // Consume wafer + } +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/PackagingTableTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/PackagingTableTile.java new file mode 100644 index 000000000..55d33a75b --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/PackagingTableTile.java @@ -0,0 +1,191 @@ +package mrtjp.projectred.fabrication.tile; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.util.ServerUtils; +import codechicken.lib.vec.Vector3; +import mrtjp.projectred.core.init.CoreReferences; +import mrtjp.projectred.core.inventory.BaseInventory; +import mrtjp.projectred.core.tile.IPacketReceiverTile; +import mrtjp.projectred.fabrication.engine.ICInterfaceType; +import mrtjp.projectred.fabrication.engine.InterfaceSpec; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.inventory.container.PackagingTableContainer; +import mrtjp.projectred.fabrication.item.ValidDieItem; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.getInterfaceSpec; +import static mrtjp.projectred.fabrication.editor.EditorDataUtils.hasFabricationTarget; + +public class PackagingTableTile extends FabricationMachineTile implements IPacketReceiverTile { + + private static final int DIE_SLOT = 4; + private static final int OUTPUT_SLOT = 9; + + private final BaseInventory inventory = new BaseInventory(10) { + + @Override + public boolean canPlaceItem(int slot, @NotNull ItemStack stack) { + return switch (slot) { + case DIE_SLOT -> stack.getItem() instanceof ValidDieItem; + case OUTPUT_SLOT -> false; + default -> true; + }; + } + + @Override + public void setChanged() { + super.setChanged(); + cancelWorkIfNeeded(); + } + }; + + private int problematicSlotMask = 0; // Masks of slots that client should render red highlights + + public PackagingTableTile(BlockPos pos, BlockState state) { + super(FabricationReferences.PACKAGING_TABLE_TILE, pos, state); + inventory.addListener(c -> setChanged()); + } + + public Container getInventory() { + return inventory; + } + + @Override + public void saveToNBT(CompoundTag tag) { + super.saveToNBT(tag); + tag.put("inventory", inventory.createTag()); + } + + @Override + public void loadFromNBT(CompoundTag tag) { + super.loadFromNBT(tag); + inventory.loadFrom(tag, "inventory"); + } + + @Override + public void writeDesc(MCDataOutput out) { + } + + @Override + public void readDesc(MCDataInput in) { + } + + @Override + public InteractionResult onBlockActivated(Player player, InteractionHand hand, BlockHitResult hit) { + if (!getLevel().isClientSide) { + ServerUtils.openContainer((ServerPlayer) player, + new SimpleMenuProvider((id, inv, p) -> new PackagingTableContainer(inv, this, id), new TranslatableComponent(getBlockState().getBlock().getDescriptionId())), + p -> p.writePos(getBlockPos())); + } + + return InteractionResult.SUCCESS; + } + + @Override + public void onBlockRemoved() { + super.onBlockRemoved(); + dropInventory(inventory, getLevel(), Vector3.fromBlockPos(getBlockPos())); + } + + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + return super.getCapability(cap, side); //TODO add capabilities + } + + @Override + protected boolean canStartWork() { + + problematicSlotMask = 0; + + ItemStack dieItem = inventory.getItem(DIE_SLOT); + CompoundTag dieTag = dieItem.getTag(); + + if (dieItem.isEmpty() || !(dieItem.getItem() instanceof ValidDieItem) || !hasFabricationTarget(dieTag)) { + problematicSlotMask |= 1 << DIE_SLOT; + return false; // Fail-fast. Can't do ingredient checks without valid die item + } + + if (!inventory.getItem(OUTPUT_SLOT).isEmpty()) { + problematicSlotMask |= 1 << OUTPUT_SLOT; + } + + // Check IO ingredients + InterfaceSpec iospec = getInterfaceSpec(dieTag); + int[] slotMap = { 1, 5, 7, 3 }; // Maps rotation to grid slot + for (int r = 0; r < 4; r++) { + ICInterfaceType type = iospec.getInterfaceType(r); + + // Each type of IO corresponds to a particular ingredient + boolean match = inventory.getItem(slotMap[r]).is(switch (type) { + case NC -> CoreReferences.PLATE_ITEM; + case REDSTONE -> CoreReferences.CONDUCTIVE_PLATE_ITEM; + case BUNDLED -> CoreReferences.BUNDLED_PLATE_ITEM; + }); + + if (!match) { + problematicSlotMask |= 1 << slotMap[r]; + } + } + + // Check corner slots + int[] cornerSlots = { 0, 2, 6, 8 }; + for (int slot : cornerSlots) { + if (!inventory.getItem(slot).is(CoreReferences.PLATE_ITEM)) { + problematicSlotMask |= 1 << slot; + } + } + + return problematicSlotMask == 0; + } + + @Override + protected int startWork() { + return 20 * 20; // 20 seconds worth of work + } + + @Override + protected int tickWork(int remainingWork) { + if (canConductorWork()) { + conductor.applyPower(-100); // draw at rate of 100W + return 1; + } + return 0; + } + + @Override + protected void finishWork() { + ItemStack gatePart = ValidDieItem.createGatePart(inventory.getItem(DIE_SLOT)); + inventory.setItem(OUTPUT_SLOT, gatePart); //TODO or stack + + // Consume inputs + for (int i = 0; i < 9; i++) { + inventory.removeItem(i, 1); + } + } + + //region Container data + public int getProblematicSlotMask() { + return problematicSlotMask; + } + //endregion +} diff --git a/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/PlottingTableTile.java b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/PlottingTableTile.java new file mode 100644 index 000000000..bd92cbc76 --- /dev/null +++ b/fabrication/src/main/java/mrtjp/projectred/fabrication/tile/PlottingTableTile.java @@ -0,0 +1,146 @@ +package mrtjp.projectred.fabrication.tile; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutput; +import codechicken.lib.util.ServerUtils; +import codechicken.lib.vec.Vector3; +import mrtjp.projectred.core.inventory.BaseInventory; +import mrtjp.projectred.fabrication.editor.EditorDataUtils; +import mrtjp.projectred.fabrication.init.FabricationReferences; +import mrtjp.projectred.fabrication.inventory.container.PlottingTableContainer; +import mrtjp.projectred.fabrication.item.BlankPhotomaskItem; +import mrtjp.projectred.fabrication.item.ICBlueprintItem; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PlottingTableTile extends FabricationMachineTile { + + private final BaseInventory inventory = new BaseInventory(3) { + + @Override + public boolean canPlaceItem(int slot, ItemStack stack) { + switch (slot) { + case 0: + return stack.getItem() instanceof ICBlueprintItem; + case 1: + return stack.getItem() instanceof BlankPhotomaskItem; + default: + return false; + } + } + + @Override + public void setChanged() { + super.setChanged(); + cancelWorkIfNeeded(); + } + }; + + public PlottingTableTile(BlockPos pos, BlockState state) { + super(FabricationReferences.PLOTTING_TABLE_TILE, pos, state); + inventory.addListener(c -> setChanged()); + } + + public Container getInventory() { + return inventory; + } + + @Override + public void saveToNBT(CompoundTag tag) { + super.saveToNBT(tag); + tag.put("inventory", inventory.createTag()); + } + + @Override + public void loadFromNBT(CompoundTag tag) { + super.loadFromNBT(tag); + inventory.loadFrom(tag, "inventory"); + } + + @Override + public void writeDesc(MCDataOutput out) { + } + + @Override + public void readDesc(MCDataInput in) { + } + + @Override + public InteractionResult onBlockActivated(Player player, InteractionHand hand, BlockHitResult hit) { + if (!getLevel().isClientSide) { + ServerUtils.openContainer((ServerPlayer) player, + new SimpleMenuProvider((id, inv, p) -> new PlottingTableContainer(inv, this, id), new TranslatableComponent(getBlockState().getBlock().getDescriptionId())), + p -> p.writePos(getBlockPos())); + } + + return InteractionResult.SUCCESS; + } + + @Override + public void onBlockRemoved() { + super.onBlockRemoved(); + dropInventory(inventory, getLevel(), Vector3.fromBlockPos(getBlockPos())); + } + + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + return super.getCapability(cap, side); //TODO add capabilities + } + + @Override + protected boolean canStartWork() { + + ItemStack slot0 = inventory.getItem(0); + ItemStack slot1 = inventory.getItem(1); + + if (slot0.isEmpty() || slot1.isEmpty()) { + return false; + } + + if (!(slot0.getItem() instanceof ICBlueprintItem)) return false; + + if (!(slot1.getItem() instanceof BlankPhotomaskItem)) return false; + + if (!EditorDataUtils.canFabricate(slot0.getTag())) return false; + + return inventory.getItem(2).isEmpty(); + } + + @Override + protected int startWork() { + return 20 * 10; // 10 seconds worth of work + } + + @Override + protected int tickWork(int remainingWork) { + if (canConductorWork()) { + conductor.applyPower(-100); // draw at rate of 100W + return 1; + } + return 0; + } + + @Override + protected void finishWork() { + ItemStack output = ICBlueprintItem.createPhotomaskStack(inventory.getItem(0)); + inventory.setItem(2, output); + inventory.removeItem(1, 1); // delete 1 blank photomask + } +} diff --git a/fabrication/src/main/resources/META-INF/mods.toml b/fabrication/src/main/resources/META-INF/mods.toml new file mode 100644 index 000000000..78528e2f9 --- /dev/null +++ b/fabrication/src/main/resources/META-INF/mods.toml @@ -0,0 +1,58 @@ +modLoader="javafml" +loaderVersion="[${lang_version},)" +issueTrackerURL="https://github.com/MrTJP/ProjectRed" +license="MIT" + +[[mods]] + modId="projectred_fabrication" + displayName="ProjectRed Fabrication" + version="${file.jarVersion}" + + updateJSONURL="https://version-check.covers1624.net/check/?mod=ProjectRed&mc=${mc_version}" + displayURL="https://github.com/MrTJP/ProjectRed" + authors="MrTJP, Chicken Bones, covers1624" + description=''' +Redstone. The way it was meant to be. + ''' +[[dependencies.projectred_fabrication]] + modId="forge" + mandatory=true + versionRange="[${forge_version},)" + ordering="NONE" + side="BOTH" +[[dependencies.projectred_fabrication]] + modId="minecraft" + mandatory=true + versionRange="[1.18.2]" + ordering="NONE" + side="BOTH" +[[dependencies.projectred_fabrication]] + modId="codechickenlib" + mandatory=true + versionRange="[${ccl_version},)" + ordering="AFTER" + side="BOTH" +[[dependencies.projectred_fabrication]] + modId="cb_multipart" + mandatory=true + versionRange="[${cbm_version},)" + ordering="AFTER" + side="BOTH" +[[dependencies.projectred_fabrication]] + modId="projectred_core" + mandatory=true + versionRange="[${file.jarVersion},)" + ordering="AFTER" + side="BOTH" +[[dependencies.projectred_fabrication]] + modId="projectred_integration" + mandatory=true + versionRange="[${file.jarVersion},)" + ordering="AFTER" + side="BOTH" +[[dependencies.projectred_fabrication]] + modId="projectred_transmission" + mandatory=true + versionRange="[${file.jarVersion},)" + ordering="AFTER" + side="BOTH" diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/models/block/domed_machine.json b/fabrication/src/main/resources/assets/projectred_fabrication/models/block/domed_machine.json new file mode 100644 index 000000000..b6b609955 --- /dev/null +++ b/fabrication/src/main/resources/assets/projectred_fabrication/models/block/domed_machine.json @@ -0,0 +1,18 @@ +{ "parent": "block/block", + "textures": { + "particle": "#south" + }, + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 16, 10, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#down", "cullface": "down" }, + "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#up" }, + "north": { "uv": [ 0, 6, 16, 16 ], "texture": "#north", "cullface": "north" }, + "south": { "uv": [ 0, 6, 16, 16 ], "texture": "#south", "cullface": "south" }, + "west": { "uv": [ 0, 6, 16, 16 ], "texture": "#west", "cullface": "west" }, + "east": { "uv": [ 0, 6, 16, 16 ], "texture": "#east", "cullface": "east" } + } + } + ] +} diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_bottom.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_bottom.png new file mode 100644 index 000000000..d57893eb3 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_bottom.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front.png new file mode 100644 index 000000000..a9ebf9b22 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front_bp.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front_bp.png new file mode 100644 index 000000000..d9d37a872 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front_bp.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front_empty.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front_empty.png new file mode 100644 index 000000000..d9d37a872 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_front_empty.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side.png new file mode 100644 index 000000000..0f4a27086 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side_bp.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side_bp.png new file mode 100644 index 000000000..8b5d991b1 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side_bp.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side_empty.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side_empty.png new file mode 100644 index 000000000..8b5d991b1 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_side_empty.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top.png new file mode 100644 index 000000000..d6696a84b Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top_bp.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top_bp.png new file mode 100644 index 000000000..8ffbdb9a8 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top_bp.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top_empty.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top_empty.png new file mode 100644 index 000000000..8ffbdb9a8 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/ic_workbench_top_empty.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_bottom.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_bottom.png new file mode 100644 index 000000000..a90603cd1 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_bottom.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_0.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_0.png new file mode 100644 index 000000000..6a543ea73 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_0.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_1.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_1.png new file mode 100644 index 000000000..11b592a53 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_1.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_2.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_2.png new file mode 100644 index 000000000..345b83c63 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_front_2.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_side.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_side.png new file mode 100644 index 000000000..8f2151ef3 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_side.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_top.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_top.png new file mode 100644 index 000000000..a85f47f79 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/lithography_table_top.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_bottom.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_bottom.png new file mode 100644 index 000000000..a90603cd1 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_bottom.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_0.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_0.png new file mode 100644 index 000000000..b1c449a1c Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_0.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_1.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_1.png new file mode 100644 index 000000000..b968968c4 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_1.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_2.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_2.png new file mode 100644 index 000000000..f2877413e Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_front_2.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_side.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_side.png new file mode 100644 index 000000000..ec1a168dd Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_side.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_top.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_top.png new file mode 100644 index 000000000..f64c42d14 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/packaging_table_top.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard.png new file mode 100644 index 000000000..d3006bcf3 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard_corner.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard_corner.png new file mode 100644 index 000000000..2599f25a7 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard_corner.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard_edge.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard_edge.png new file mode 100644 index 000000000..2baefe17c Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/perfboard_edge.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_bottom.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_bottom.png new file mode 100644 index 000000000..d57893eb3 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_bottom.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_0.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_0.png new file mode 100644 index 000000000..438868085 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_0.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_1.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_1.png new file mode 100644 index 000000000..c6f84dbe5 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_1.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_2.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_2.png new file mode 100644 index 000000000..5ac7cd56a Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_front_2.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_printhead.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_printhead.png new file mode 100644 index 000000000..7062e2ddb Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_printhead.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_side.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_side.png new file mode 100644 index 000000000..7f46458aa Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_side.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_top.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_top.png new file mode 100644 index 000000000..b212bf3b0 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/block/plotting_table_top.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/compile_tab.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/compile_tab.png new file mode 100644 index 000000000..22f3fb547 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/compile_tab.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/ic_workbench.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/ic_workbench.png new file mode 100644 index 000000000..29e205d6f Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/ic_workbench.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/info_tab.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/info_tab.png new file mode 100644 index 000000000..6f38483e1 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/info_tab.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/lithography_table.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/lithography_table.png new file mode 100755 index 000000000..ba9a9b670 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/lithography_table.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/packaging_table.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/packaging_table.png new file mode 100755 index 000000000..b9a597440 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/packaging_table.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/plotting_table.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/plotting_table.png new file mode 100755 index 000000000..417725c70 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/gui/plotting_table.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/blank_photomask.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/blank_photomask.png new file mode 100755 index 000000000..ac0df1107 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/blank_photomask.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/etched_silicon_wafer.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/etched_silicon_wafer.png new file mode 100755 index 000000000..d159e9fb0 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/etched_silicon_wafer.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_active.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_active.png new file mode 100755 index 000000000..8b6787255 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_active.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_active.png.mcmeta b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_active.png.mcmeta new file mode 100755 index 000000000..24f9c2fae --- /dev/null +++ b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_active.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 1 + } +} diff --git a/src/fabrication/resources/assets/projectred-fabrication/textures/gui/ic_workbench.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_blueprint.png old mode 100644 new mode 100755 similarity index 68% rename from src/fabrication/resources/assets/projectred-fabrication/textures/gui/ic_workbench.png rename to fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_blueprint.png index c87f13a1c..b55df3437 Binary files a/src/fabrication/resources/assets/projectred-fabrication/textures/gui/ic_workbench.png and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_blueprint.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_inert.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_inert.png new file mode 100644 index 000000000..58a798c99 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/ic_inert.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/invalid_die.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/invalid_die.png new file mode 100644 index 000000000..b3ef1c949 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/invalid_die.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/photomask_set.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/photomask_set.png new file mode 100755 index 000000000..b86fae9ce Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/photomask_set.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/rough_silicon_wafer copy.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/rough_silicon_wafer copy.png new file mode 100755 index 000000000..4ae83653b Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/rough_silicon_wafer copy.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/rough_silicon_wafer.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/rough_silicon_wafer.png new file mode 100755 index 000000000..0176927a3 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/rough_silicon_wafer.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die.png new file mode 100755 index 000000000..638c8d11a Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die.png.mcmeta b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die.png.mcmeta new file mode 100755 index 000000000..24f9c2fae --- /dev/null +++ b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 1 + } +} diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die_template.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die_template.png new file mode 100755 index 000000000..6554fd3f6 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die_template.png differ diff --git a/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die_v1.png b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die_v1.png new file mode 100755 index 000000000..9adf56175 Binary files /dev/null and b/fabrication/src/main/resources/assets/projectred_fabrication/textures/item/valid_die_v1.png differ diff --git a/fabrication/src/main/resources/pack.mcmeta b/fabrication/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..a4e32f532 --- /dev/null +++ b/fabrication/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 4, + "description": "Project Red resource pack" + } +} diff --git a/integration/src/main/java/mrtjp/projectred/integration/GateType.java b/integration/src/main/java/mrtjp/projectred/integration/GateType.java index 62957f126..c008a03ec 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/GateType.java +++ b/integration/src/main/java/mrtjp/projectred/integration/GateType.java @@ -49,10 +49,11 @@ public enum GateType TRANSPARENT_LATCH_CELL(ID_STACKING_LATCH, ArrayGatePart.TransparentLatchCell::new), SEGMENT_DISPLAY (ID_SEGMENT_DISPLAY, BundledGatePart.SegmentDisplay::new), DEC_RANDOMIZER (ID_DEC_RANDOMIZER, SimpleGatePart.DecodingRandomizer::new), + FABRICATED_GATE (null, null), // Will be injected if applicable ; - private final String unlocalName; - private final Function partFactory; + private String unlocalName; + private Function partFactory; private RegistryObject itemSupplier; private RegistryObject> partSupplier; @@ -62,6 +63,10 @@ public enum GateType this.partFactory = partFactory; } + public boolean isEnabled() { + return partFactory != null; + } + public String getUnlocalizedName() { return unlocalName; } @@ -86,4 +91,15 @@ public void registerParts(DeferredRegister> partRegistry, Defer itemSupplier = itemRegistry.register(unlocalName, () -> new GatePartItem(this)); partSupplier = partRegistry.register(unlocalName, () -> new SimpleMultipartType<>(isClient -> partFactory.apply(this))); } + + // TODO: Add proper gate registering mechanism + public void inject(String unlocalName, Function partFactory, RegistryObject itemSupplier, RegistryObject> partSupplier) { + if (this.itemSupplier != null || this.partSupplier != null) { + throw new RuntimeException("GateType " + name() + " already registered!"); + } + this.unlocalName = unlocalName; + this.partFactory = partFactory; + this.itemSupplier = itemSupplier; + this.partSupplier = partSupplier; + } } diff --git a/integration/src/main/java/mrtjp/projectred/integration/client/GateComponentModels.java b/integration/src/main/java/mrtjp/projectred/integration/client/GateComponentModels.java index fb4e22f2e..f684304c8 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/client/GateComponentModels.java +++ b/integration/src/main/java/mrtjp/projectred/integration/client/GateComponentModels.java @@ -20,8 +20,15 @@ import mrtjp.projectred.core.BundledSignalsLib; import mrtjp.projectred.core.client.HaloRenderer; import mrtjp.projectred.integration.part.GatePart; +import mrtjp.projectred.integration.part.IGateRenderData; import mrtjp.projectred.lib.VecLib; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.network.chat.TextComponent; import net.minecraft.resources.ResourceLocation; import java.util.*; @@ -56,9 +63,9 @@ public class GateComponentModels { public static Map sixteenSeg = loadModels("array/16seg"); public static CCModel segbus = loadModel("array/segbus"); - public static CCModel icChip = loadModel("icchip").apply(new Translation(8 / 16D, 2 / 16D, 8 / 16D)); - public static CCModel icGlass = loadModel("icglass").apply(new Translation(8 / 16D, 0, 8 / 16D)); - public static CCModel icHousing = loadModel("ichousing").apply(new Translation(8 / 16D, 0, 8 / 16D)); + public static Map fabIC = loadModels("fab_ic", (k, v) -> v.apply(new Translation(0.5, 0, 0.5))); + public static Map ioCrimp = loadModels("io_crimp"); + public static Map ioColourBox = loadModels("io_colour_box"); public static IconTransformation baseIcon; public static IconTransformation wireBorderIcon; @@ -93,6 +100,8 @@ public class GateComponentModels { public static IconTransformation icChipIcon; public static IconTransformation icChipIconOff; public static IconTransformation icHousingIcon; + public static IconTransformation ioCrimpConnectorIcon; + public static IconTransformation ioColourBoxIcon; public static void registerIcons(AtlasRegistrar registrar) { //@formatter:off @@ -133,6 +142,8 @@ public static void registerIcons(AtlasRegistrar registrar) { registrar.registerSprite(new ResourceLocation(MOD_ID, "block/ic_active"), i -> icChipIcon = new IconTransformation(i)); registrar.registerSprite(new ResourceLocation(MOD_ID, "block/ic_inert"), i -> icChipIconOff = new IconTransformation(i)); registrar.registerSprite(new ResourceLocation(MOD_ID, "block/ic_housing"), i -> icHousingIcon = new IconTransformation(i)); + registrar.registerSprite(new ResourceLocation(MOD_ID, "block/io_crimp"), i -> ioCrimpConnectorIcon = new IconTransformation(i)); + registrar.registerSprite(new ResourceLocation(MOD_ID, "block/io_colour_box"), i -> ioColourBoxIcon = new IconTransformation(i)); //@formatter:on } @@ -1066,8 +1077,8 @@ public void renderModel(Transformation t, int orient, CCRenderState ccrs) { } } - public void renderLights(CCRenderState ccrs, GatePart gate, PoseStack mStack, MultiBufferSource buffers, Transformation t) { - Transformation t2 = t.with(new Translation(gate.pos())); + public void renderLights(CCRenderState ccrs, BlockPos lightPos, PoseStack mStack, MultiBufferSource buffers, Transformation t) { + Transformation t2 = t.with(new Translation(lightPos)); for (int i = 0; i < 16; i++) { if ((pressMask & 1 << i) != 0) { HaloRenderer.addLight(t2, i, LIGHT_BOXES[i]); @@ -1428,6 +1439,165 @@ protected UVTransformation getUVT() { } } + public static class SidedICBundledCableModel extends BundledCableModel { + + public int sidemask = 0; + + public SidedICBundledCableModel() { + super(icBundled, new Vector3(8, 0, 8), 7 / 32D, 12 / 32D); + } + + @Override + protected UVTransformation getUVT() { + return busConvIcon; + } + + @Override + public void renderModel(Transformation t, int orient, CCRenderState ccrs) { + for (int r = 0; r < 4; r++) { + if ((sidemask & 1 << r) != 0) { + super.renderModel(t, orient & 0xFC | ((orient & 3) + r) % 4, ccrs); + } + } + } + } + + public static class SidedWireModel extends ComponentModel { + + public int sidemask = 0; + + public final WireModel[] wires; + + public SidedWireModel(WireModel[] wires) { + this.wires = wires; + } + + @Override + public void renderModel(Transformation t, int orient, CCRenderState ccrs) { + for (int r = 0; r < 4; r++) { + if ((sidemask & 1 << r) != 0) { + wires[r].renderModel(t, orient, ccrs); + } + } + } + } + + public static class FabricatedICModel extends ComponentModel { + + public static final FabricatedICModel INSTANCE = new FabricatedICModel(); + + private static final Style UNIFORM = Style.EMPTY.withFont(new ResourceLocation("minecraft", "uniform")); + private static final CCModel[] platformModel = bakeOrients(fabIC.get("platform")); + private static final CCModel[] icChipModel = bakeOrients(fabIC.get("ic")); + + @Override + public void renderModel(Transformation t, int orient, CCRenderState ccrs) { + platformModel[orient].render(ccrs, t, icHousingIcon); + icChipModel[orient].render(ccrs, t, icChipIcon); + } + + public void renderName(String name, PoseStack mStack, Transformation t1) { + + Component nameComponent = new TextComponent(name).withStyle(UNIFORM); + Font fr = Minecraft.getInstance().font; + + // Calculate font scale + int tw = fr.width(nameComponent); + int th = fr.lineHeight; + double wScale = 8/16D * (1.0/tw); // Cap width to 8/16 block + double hScale = 2/16D * (1.0/th); // Cap height to 2/16 block + double scale = Math.min(wScale, hScale); // Use the limiting scale + + // Create the transform + Transformation t = new Rotation(90 * MathHelper.torad, 1, 0, 0) + .with(new Scale(scale, 1, scale)) + .with(new Translation(8/16D, 2.2501/16D, 11.5/16D)) + .with(new Translation(-(tw / 2.0) * scale, 0, -(th / 2.0) * scale)) + .with(t1); + Matrix4 m = new Matrix4(); + t.apply(m); + + // Draw text + mStack.pushPose(); + mStack.mulPoseMatrix(m.toMatrix4f()); + fr.draw(mStack, nameComponent, 0, 0, 0xFFFFFFFF); + mStack.popPose(); + } + + public void renderGlass(Transformation t, CCRenderState ccrs) { + fabIC.get("glass").render(ccrs, t, icHousingIcon); + } + } + + public static class IOCrimpConnectorModel extends StaticComponentModel { + + public static final IOCrimpConnectorModel INSTANCE = new IOCrimpConnectorModel(); + + private IOCrimpConnectorModel() { + super(ioCrimp.get("crimp") + .copy() + .apply(new Translation(0.5, 2/16D, 0.5))); + } + + @Override + protected UVTransformation getUVT() { + return ioCrimpConnectorIcon; + } + } + + public static class IOCrimpWireModel extends CellWireModel { + + private static final CCModel[] models = bakeOrients(ioCrimp.get("redalloy").copy().apply(new Translation(0.5, 2/16D, 0.5))); + + private IconTransformation getUVT() { + return ioCrimpConnectorIcon; + } + + @Override + public void renderModel(Transformation t, int orient, CCRenderState ccrs) { + models[orient].render(ccrs, t, getUVT(), colourMult()); + } + } + + public static class IOCrimpColourBoxModel extends ComponentModel { + + public int colour = EnumColour.WHITE.ordinal(); + public boolean isInput = true; + + private final CCModel[] boxModels; + private final CCModel[] inputArrowModels; + private final CCModel[] outputArrowModels; + + public IOCrimpColourBoxModel(double x, double z) { + + CCModel m = ioColourBox.get("box").copy().apply(new Translation(x/16D, 2/16D, z/16D)); + boxModels = bakeOrients(m); + + m = ioColourBox.get("arrow").copy().apply(new Translation(x/16D, 2/16D, z/16D)); + inputArrowModels = bakeOrients(m); + + m = ioColourBox.get("arrow").copy() + .apply(new Rotation(180 * MathHelper.torad, 0, 1, 0)) + .apply(new Translation(x/16D, 2/16D, z/16D)); + outputArrowModels = bakeOrients(m); + } + + private UVTransformation getColourUVT() { + return new UVTranslation((colour%2) * 4 / 32D, (colour/2) * 4 / 32D).with(ioColourBoxIcon); + } + + private UVTransformation getBoxUVT() { + return ioColourBoxIcon; + } + + @Override + public void renderModel(Transformation t, int orient, CCRenderState ccrs) { + boxModels[orient].render(ccrs, t, getBoxUVT()); + (isInput ? inputArrowModels : outputArrowModels)[orient].render(ccrs, t, getColourUVT()); + } + } + + private static class RedundantUVTransformation extends UVTransformation { public static final RedundantUVTransformation INSTANCE = new RedundantUVTransformation(); @@ -1469,5 +1639,4 @@ public UVTransformation copy() { return this; } } - } diff --git a/integration/src/main/java/mrtjp/projectred/integration/client/GateModelRenderer.java b/integration/src/main/java/mrtjp/projectred/integration/client/GateModelRenderer.java index 672a50764..e08cd8481 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/client/GateModelRenderer.java +++ b/integration/src/main/java/mrtjp/projectred/integration/client/GateModelRenderer.java @@ -3,10 +3,11 @@ import codechicken.lib.colour.EnumColour; import codechicken.lib.math.MathHelper; import codechicken.lib.render.CCRenderState; +import codechicken.lib.render.buffer.TransformingVertexConsumer; import codechicken.lib.texture.AtlasRegistrar; -import codechicken.lib.vec.RedundantTransformation; import codechicken.lib.vec.Transformation; import codechicken.lib.vec.Vector3; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Vector3f; import mrtjp.projectred.integration.GateType; @@ -14,9 +15,13 @@ import mrtjp.projectred.integration.part.IGateRenderData; import mrtjp.projectred.lib.VecLib; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.BlockPos; import net.minecraft.core.particles.DustParticleOptions; +import net.minecraft.nbt.CompoundTag; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -66,63 +71,54 @@ public static GateModelRenderer instance() { new RenderTransparentLatchCell(), new RenderSegmentDisplay(), new RenderDecodingRandomizer(), + new RenderFabricatedGate(), }; - //region Static rendering - public void renderStatic(CCRenderState ccrs, GatePart gate) { - renderStatic(ccrs, gate.getGateType(), gate, gate.getOrientation(), RedundantTransformation.INSTANCE); - } - - public void renderStatic(CCRenderState ccrs, GateType type, IGateRenderData key, int orientation, Transformation t) { - renderStatic(ccrs, type.ordinal(), key, orientation, t); - } + private final GateRenderer[] nonPartRenderers = new GateRenderer[] { + new RenderIOGate(), + }; - public void renderStatic(CCRenderState ccrs, int renderIndex, IGateRenderData key, int orientation, Transformation t) { - GateRenderer r = getRenderer(renderIndex); + //region Static rendering + public void renderStatic(CCRenderState ccrs, IGateRenderData key, Transformation t) { + GateRenderer r = getRenderer(key.getRenderIndex()); r.prepare(key); - r.renderStatic(ccrs, orientation, t); + r.renderStatic(ccrs, key.getOrientation(), t); } //endregion //region Dynamic rendering - public void renderDynamic(CCRenderState ccrs, GatePart gate, float partialFrame) { - renderDynamic(ccrs, gate.getGateType(), gate, gate.getOrientation(), RedundantTransformation.INSTANCE, partialFrame); + public void renderDynamic(CCRenderState ccrs, IGateRenderData key, Transformation t, float partialFrame) { + renderDynamic(ccrs, key, t, null, null, 0, 0, partialFrame); } - public void renderDynamic(CCRenderState ccrs, GateType type, IGateRenderData key, int orientation, Transformation t, float partialTicks) { - renderDynamic(ccrs, type.ordinal(), key, orientation, t, partialTicks); - } - - public void renderDynamic(CCRenderState ccrs, int renderIndex, IGateRenderData key, int orientation, Transformation t, float partialTicks) { - GateRenderer r = getRenderer(renderIndex); + public void renderDynamic(CCRenderState ccrs, IGateRenderData key, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { + GateRenderer r = getRenderer(key.getRenderIndex()); if (r.hasSpecials()) { + Transformation t2 = VecLib.orientT(key.getOrientation()).with(t); r.prepareDynamic(key, partialTicks); - r.renderDynamic(ccrs, VecLib.orientT(orientation).with(t)); + r.renderDynamic(ccrs, t2); + if (mStack != null) { + r.renderCustomDynamic(ccrs, t2, mStack, buffers, packedLight, packedOverlay, partialTicks); + } } } //endregion - //region Custom dynamic rendering - public void renderCustomDynamic(CCRenderState ccrs, GatePart gate, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { - GateRenderer r = getRenderer(gate.getGateType().ordinal()); - r.renderCustomDynamic(ccrs, gate, VecLib.orientT(gate.getOrientation()), mStack, buffers, packedLight, packedOverlay, partialTicks); - } - //endregion - //region Inventory rendering - public void renderInventory(CCRenderState ccrs, ItemStack stack, Transformation t, GateType type) { - renderInventory(ccrs, stack, type, 0, t); - } - - public void renderInventory(CCRenderState ccrs, ItemStack stack, GateType type, int orient, Transformation t) { - renderInventory(ccrs, stack, type.ordinal(), orient, t); + public void renderInventory(CCRenderState ccrs, @Nullable ItemStack stack, int renderIndex, int orient, Transformation t) { + renderInventory(ccrs, stack, renderIndex, orient, t, null, null, 0, 0); } - public void renderInventory(CCRenderState ccrs, ItemStack stack, int renderIndex, int orient, Transformation t) { + public void renderInventory(CCRenderState ccrs, @Nullable ItemStack stack, int renderIndex, int orient, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay) { GateRenderer r = getRenderer(renderIndex); - r.prepareInventory(); + r.prepareInventory(stack); r.renderStatic(ccrs, orient, t); - if (r.hasSpecials()) r.renderDynamic(ccrs, t); + if (r.hasSpecials()) { + r.renderDynamic(ccrs, t); + if (mStack != null) { + r.renderCustomDynamic(ccrs, t, mStack, buffers, packedLight, packedOverlay, 0); + } + } } //endregion @@ -132,10 +128,17 @@ public void spawnParticles(GatePart part, Random random) { r.spawnParticles(part, random); } + public static int getRenderIndex(GateType type) { + return type.ordinal(); + } + + public static int getNonPartRenderIndex(int i) { + return 0x100 | i; + } + private GateRenderer getRenderer(int renderIndex) { if ((renderIndex & 0x100) != 0) { - //TODO return non-gate renders - return null; + return nonPartRenderers[renderIndex & 0xFF]; } else { return renderers[renderIndex]; } @@ -157,7 +160,7 @@ public static abstract class GateRenderer { protected abstract List getModels(); - protected abstract void prepareInventory(); + protected abstract void prepareInventory(@Nullable ItemStack stack); protected abstract void prepare(IGateRenderData gate); @@ -205,7 +208,7 @@ public boolean hasSpecials() { public void renderDynamic(CCRenderState ccrs, Transformation t) { } - public void renderCustomDynamic(CCRenderState ccrs, GatePart gate, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { + public void renderCustomDynamic(CCRenderState ccrs, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { } } @@ -228,7 +231,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; wires[2].on = false; @@ -273,7 +276,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; wires[2].on = false; @@ -316,7 +319,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = true; wires[2].on = false; @@ -361,7 +364,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; wires[2].on = false; @@ -412,7 +415,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; wires[2].on = false; @@ -461,7 +464,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[3].on = false; wires[2].on = false; @@ -504,7 +507,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[3].on = false; wires[2].on = false; @@ -549,7 +552,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; wires[2].on = false; @@ -594,7 +597,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[1].on = true; wires[2].on = true; @@ -643,7 +646,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; wires[2].on = false; @@ -696,7 +699,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = false; endTorch.on = false; @@ -734,7 +737,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[1].on = false; wires[2].on = false; @@ -808,7 +811,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { reflect = false; shape = 0; wires1[0].on = false; @@ -861,7 +864,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[1].on = false; torches[0].on = true; @@ -900,7 +903,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { reflect = false; wires[0].on = true; wires[1].on = false; @@ -950,7 +953,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; solar.state = 0; } @@ -981,7 +984,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; } @@ -1012,7 +1015,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[1].on = false; wires[2].on = false; @@ -1065,7 +1068,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { torches[0].on = true; torches[1].on = true; torches[2].on = false; @@ -1124,7 +1127,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { reflect = false; wires[0].on = false; wires[1].on = false; @@ -1183,7 +1186,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { reflect = false; wires[0].on = false; wires[1].on = false; @@ -1252,7 +1255,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = true; wires[1].on = true; wires[2].on = false; @@ -1301,7 +1304,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { reflect = false; wires[0].on = false; wires[1].on = false; @@ -1343,7 +1346,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { bottomWire.signal = 0; topWire.signal = 0; topWire.conn = 0; @@ -1378,8 +1381,8 @@ public RenderInvertCell() { } @Override - protected void prepareInventory() { - super.prepareInventory(); + protected void prepareInventory(@Nullable ItemStack stack) { + super.prepareInventory(stack); topWire.signal = (byte) 255; wires[0].on = false; torch.on = true; @@ -1406,8 +1409,8 @@ public RenderBufferCell() { } @Override - protected void prepareInventory() { - super.prepareInventory(); + protected void prepareInventory(@Nullable ItemStack stack) { + super.prepareInventory(stack); wires[0].on = false; wires[1].on = true; torches[0].on = true; @@ -1445,7 +1448,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { topWire.signal = 0; topWire.conn = 0; torches[0].on = true; @@ -1489,7 +1492,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { topWire.signal = 0; topWire.conn = 0; wires[0].on = true; @@ -1505,14 +1508,14 @@ protected void prepareInventory() { @Override protected void prepare(IGateRenderData gate) { - boolean on = (gate.state()&0x10) != 0; + boolean on = (gate.state() & 0x10) != 0; topWire.signal = gate.topSignal(); topWire.conn = gate.topSignalConnMask() & ~0x2; // Always render left side wires[0].on = !on; wires[1].on = gate.topSignal() != 0; wires[2].on = gate.topSignal() == 0; wires[3].on = on; - wires[4].on = (gate.state()&4) != 0; + wires[4].on = (gate.state() & 4) != 0; torches[0].on = wires[2].on; torches[1].on = !wires[2].on && !wires[4].on; torches[2].on = !wires[1].on && !wires[3].on; @@ -1540,7 +1543,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { reflect = false; wires[0].on = true; wires[1].on = false; @@ -1614,7 +1617,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { shape = 0; panel.signal = 0; panel.disableMask = 0; @@ -1658,7 +1661,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[1].on = false; wires[2].on = false; @@ -1683,6 +1686,8 @@ public static class RenderBusInputPanel extends GateRenderer { private final WireModel[] wires = generateWireModels("businput", 1); private final InputPanelButtonsModel buttons = new InputPanelButtonsModel(); + private BlockPos.MutableBlockPos lightPos = new BlockPos.MutableBlockPos(); + public RenderBusInputPanel() { models.add(BaseComponentModel.INSTANCE); models.add(BusInputPanelCableModel.INSTANCE); @@ -1696,9 +1701,10 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; buttons.pressMask = 0; + lightPos.set(0, 0, 0); } @Override @@ -1708,9 +1714,19 @@ protected void prepare(IGateRenderData gate) { } @Override - public void renderCustomDynamic(CCRenderState ccrs, GatePart gate, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { + public boolean hasSpecials() { + return true; + } + + @Override + protected void prepareDynamic(IGateRenderData gate, float partialFrame) { buttons.pressMask = gate.bInput0(); - buttons.renderLights(ccrs, gate, mStack, buffers, t); + lightPos.set(gate.worldPos()); + } + + @Override + public void renderCustomDynamic(CCRenderState ccrs, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { + buttons.renderLights(ccrs, lightPos, mStack, buffers, t); } } @@ -1746,7 +1762,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { shape = 0; sevenSeg1.signal = 64; sevenSeg0.signal = 64; @@ -1793,7 +1809,7 @@ protected List getModels() { } @Override - protected void prepareInventory() { + protected void prepareInventory(@Nullable ItemStack stack) { wires[0].on = false; wires[1].on = false; wires[2].on = false; @@ -1831,4 +1847,155 @@ protected void prepare(IGateRenderData gate) { chips[2].on = true; } } + + public static class RenderFabricatedGate extends GateRenderer { + + private final List models = new LinkedList<>(); + + private final SidedWireModel simpleWires = new SidedWireModel(generateWireModels("ic1", 4)); + private final SidedWireModel analogWires = new SidedWireModel(generateWireModels("ic2", 4)); + private final SidedICBundledCableModel bundledWires = new SidedICBundledCableModel(); + private final FabricatedICModel icHousing = FabricatedICModel.INSTANCE; + + //**** Copied from EditorDataUtils.class ****/ + public static final String KEY_FORMAT = "format"; // int + public static final String KEY_ACTIVE = "active"; // boolean + public static final String KEY_IC_NAME = "ic_name"; // String + public static final String KEY_TILE_MAP = "tile_map"; // CompoundTag + public static final String KEY_IS_BUILT = "is_built"; // boolean + public static final String KEY_IO_SPEC = "io_spec"; + public static final String KEY_COMP_STATE = "state"; // byte + public static final String KEY_FLAT_MAP = "flat_map"; // String + public static final String KEY_SIMULATION = "sim_cont"; // CompoundTag + public static final String KEY_COMPILER_LOG = "compiler_log"; // CompoundTag + + // Minimum subset of data required to fabricate gate (i.e. create photomask) + public static boolean hasFabricationTarget(CompoundTag tag) { + return tag != null && + tag.contains(KEY_IS_BUILT) && + tag.contains(KEY_FLAT_MAP); + } + + private String name = "untitled"; + + public RenderFabricatedGate() { + models.add(BaseComponentModel.INSTANCE); + models.add(simpleWires); + models.add(analogWires); + models.add(bundledWires); + models.add(icHousing); + } + + @Override + protected List getModels() { + return models; + } + + @Override + protected void prepareInventory(@Nullable ItemStack stack) { + if (stack == null || !hasFabricationTarget(stack.getTag())) { + name = "ERROR!"; + simpleWires.sidemask = 0; + analogWires.sidemask = 0; + bundledWires.sidemask = 0; + return; + } + + //TODO use EditorDataUtils helpers once this class is moved to Fabrication + + CompoundTag tag = stack.getTag(); + name = tag.getString("ic_name"); + + CompoundTag ifspecTag = tag.getCompound("io_spec"); + byte rMask = ifspecTag.getByte("rmask"); + byte aMask = 0; //TODO analog stuff + byte bMask = ifspecTag.getByte("bmask"); + + simpleWires.sidemask = rMask & 0xF | (rMask >> 4) & 0xF; + analogWires.sidemask = aMask; + bundledWires.sidemask = bMask & 0xF | (bMask >> 4) & 0xF; + } + + @Override + protected void prepare(IGateRenderData gate) { + + simpleWires.sidemask = gate.state2() & 0xF; + analogWires.sidemask = (gate.state2() >> 4) & 0xF; + bundledWires.sidemask = (gate.state2() >> 8) & 0xF; + + simpleWires.wires[0].on = (gate.state() & 0x11) != 0; + simpleWires.wires[1].on = (gate.state() & 0x22) != 0; + simpleWires.wires[2].on = (gate.state() & 0x44) != 0; + simpleWires.wires[3].on = (gate.state() & 0x88) != 0; + + analogWires.wires[0].on = simpleWires.wires[0].on; + analogWires.wires[1].on = simpleWires.wires[1].on; + analogWires.wires[2].on = simpleWires.wires[2].on; + analogWires.wires[3].on = simpleWires.wires[3].on; + } + + @Override + public boolean hasSpecials() { + return true; + } + + @Override + protected void prepareDynamic(IGateRenderData gate, float partialFrame) { + name = gate.getGateName(); + } + + @Override + public void renderDynamic(CCRenderState ccrs, Transformation t) { + } + + @Override + public void renderCustomDynamic(CCRenderState ccrs, Transformation t, PoseStack mStack, MultiBufferSource buffers, int packedLight, int packedOverlay, float partialTicks) { + + // Render name + icHousing.renderName(name, mStack, t); + + // Render glass + ccrs.reset(); + ccrs.brightness = packedLight; + ccrs.overlay = packedOverlay; + ccrs.bind(new TransformingVertexConsumer(buffers.getBuffer(RenderType.translucentMovingBlock()), mStack), DefaultVertexFormat.BLOCK); + icHousing.renderGlass(t, ccrs); + } + } + + public static class RenderIOGate extends GateRenderer { + + private final List models = new LinkedList<>(); + + private final WireModel[] wires = generateWireModels("fabio", 1); + private final IOCrimpWireModel crimpWire = new IOCrimpWireModel(); + private final IOCrimpColourBoxModel colourBox = new IOCrimpColourBoxModel(3, 10.5); + + public RenderIOGate() { + models.add(BaseComponentModel.INSTANCE); + models.addAll(Arrays.asList(wires)); + models.add(IOCrimpConnectorModel.INSTANCE); + models.add(crimpWire); + models.add(colourBox); + } + + @Override + protected List getModels() { + return models; + } + + @Override + protected void prepareInventory(@Nullable ItemStack stack) { + crimpWire.signal = 0; + colourBox.colour = 0; + } + + @Override + protected void prepare(IGateRenderData gate) { + wires[0].on = (gate.state() & 0x44) != 0; + crimpWire.signal = (byte) (wires[0].on ? 255 : 0); + colourBox.colour = gate.state2() & 0xF; + colourBox.isInput = gate.shape() == 0; + } + } } diff --git a/integration/src/main/java/mrtjp/projectred/integration/client/GatePartItemRenderer.java b/integration/src/main/java/mrtjp/projectred/integration/client/GatePartItemRenderer.java index 39a62df04..d423efb5e 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/client/GatePartItemRenderer.java +++ b/integration/src/main/java/mrtjp/projectred/integration/client/GatePartItemRenderer.java @@ -48,6 +48,6 @@ public void renderItem(ItemStack stack, ItemTransforms.TransformType transformTy ccrs.brightness = packedLight; ccrs.overlay = packedOverlay; ccrs.bind(RenderType.cutout(), getter, mStack); - GateModelRenderer.instance().renderInventory(ccrs, stack, RedundantTransformation.INSTANCE, gateItem.getGateType()); + GateModelRenderer.instance().renderInventory(ccrs, stack, gateItem.getGateType().ordinal(), 0, RedundantTransformation.INSTANCE, mStack, getter, packedLight, packedOverlay); } } diff --git a/integration/src/main/java/mrtjp/projectred/integration/client/GatePartRenderer.java b/integration/src/main/java/mrtjp/projectred/integration/client/GatePartRenderer.java index b1b0d304c..b7d750cab 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/client/GatePartRenderer.java +++ b/integration/src/main/java/mrtjp/projectred/integration/client/GatePartRenderer.java @@ -2,6 +2,7 @@ import codechicken.lib.render.CCRenderState; import codechicken.lib.render.buffer.TransformingVertexConsumer; +import codechicken.lib.vec.RedundantTransformation; import codechicken.multipart.api.part.render.PartRenderer; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; @@ -23,7 +24,7 @@ private GatePartRenderer() { public boolean renderStatic(GatePart part, @Nullable RenderType layer, CCRenderState ccrs) { if (layer == null || (layer == RenderType.cutout() && Configurator.staticGates)) { ccrs.setBrightness(part.level(), part.pos()); - GateModelRenderer.instance().renderStatic(ccrs, part); + GateModelRenderer.instance().renderStatic(ccrs, part, RedundantTransformation.INSTANCE); return true; } return false; @@ -36,7 +37,6 @@ public void renderDynamic(GatePart part, PoseStack mStack, MultiBufferSource buf ccrs.brightness = packedLight; ccrs.overlay = packedOverlay; ccrs.bind(new TransformingVertexConsumer(buffers.getBuffer(RenderType.cutout()), mStack), DefaultVertexFormat.BLOCK); - GateModelRenderer.instance().renderDynamic(ccrs, part, partialTicks); - GateModelRenderer.instance().renderCustomDynamic(ccrs, part, mStack, buffers, packedLight, packedOverlay, partialTicks); + GateModelRenderer.instance().renderDynamic(ccrs, part, RedundantTransformation.INSTANCE, mStack, buffers, packedLight, packedOverlay, partialTicks); } } diff --git a/integration/src/main/java/mrtjp/projectred/integration/data/IntegrationItemModelProvider.java b/integration/src/main/java/mrtjp/projectred/integration/data/IntegrationItemModelProvider.java index 27fe3d2b5..a78c6b28f 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/data/IntegrationItemModelProvider.java +++ b/integration/src/main/java/mrtjp/projectred/integration/data/IntegrationItemModelProvider.java @@ -28,6 +28,7 @@ protected void registerModels() { ModelFile.ExistingModelFile gate = getExistingFile(new ResourceLocation(MOD_ID, "item/gate")); for (GateType type : GateType.values()) { + if (!type.isEnabled()) continue; getSimple(type.getItem()).noTexture().parent(gate); } } diff --git a/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationClientInit.java b/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationClientInit.java index 8eb0ac12d..ce8428ea8 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationClientInit.java +++ b/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationClientInit.java @@ -49,6 +49,7 @@ private static void clientSetup(final FMLClientSetupEvent event) { // Register part item renderers for (GateType type : GateType.values()) { + if (!type.isEnabled()) continue; MultipartClientRegistry.register(type.getPartType(), GatePartRenderer.INSTANCE); } diff --git a/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationParts.java b/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationParts.java index ac25e86ce..fd8878c35 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationParts.java +++ b/integration/src/main/java/mrtjp/projectred/integration/init/IntegrationParts.java @@ -44,6 +44,7 @@ public class IntegrationParts { public static void register() { for (GateType type : GateType.values()) { + if (!type.isEnabled()) continue; type.registerParts(PARTS, ITEMS); } } diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/BundledGatePart.java b/integration/src/main/java/mrtjp/projectred/integration/part/BundledGatePart.java index e2b68d207..3adb3ef0a 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/BundledGatePart.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/BundledGatePart.java @@ -606,7 +606,7 @@ protected int inputMask(int shape) { } @Override - int getOutput(int r) { + protected int getOutput(int r) { return shape() != 0 && r == 2 ? rsOut : (state() & 0x10 << r) != 0 ? 0xF : 0; } @@ -781,6 +781,11 @@ public short bOutput2() { public short bInput0() { return pressMask; } + + @Override + public BlockPos worldPos() { + return pos(); + } //endregion //region Gate logic @@ -805,7 +810,7 @@ protected int inputMask(int shape) { } @Override - int getOutput(int r) { + protected int getOutput(int r) { //TODO same as super. Dont override? return (state() & 0x10 << r) != 0 ? 15 : 0; } @@ -953,7 +958,7 @@ protected int bundledInputMask(int shape) { } @Override - int getOutput(int r) { + protected int getOutput(int r) { return 0; // Super derives output from state, but we store colour in that } diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/ComplexGatePart.java b/integration/src/main/java/mrtjp/projectred/integration/part/ComplexGatePart.java index f47f961fb..1a2671917 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/ComplexGatePart.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/ComplexGatePart.java @@ -1054,7 +1054,7 @@ protected boolean gateLogicCanConnect(int r) { } @Override - int getOutput(int r) { + protected int getOutput(int r) { return r == 0 ? state2() & 0xF : 0; } //endregion diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/GatePart.java b/integration/src/main/java/mrtjp/projectred/integration/part/GatePart.java index fa0fae968..dbe453844 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/GatePart.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/GatePart.java @@ -9,7 +9,6 @@ import codechicken.lib.vec.Vector3; import codechicken.microblock.part.face.FaceMicroblockPart; import codechicken.multipart.api.MultipartType; -import codechicken.multipart.api.NormalOcclusionTest; import codechicken.multipart.api.part.*; import codechicken.multipart.block.TileMultipart; import codechicken.multipart.util.PartRayTraceResult; @@ -21,7 +20,6 @@ import mrtjp.projectred.core.part.IConnectableFacePart; import mrtjp.projectred.integration.GateType; import mrtjp.projectred.integration.client.GateComponentModels; -import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -31,7 +29,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.phys.shapes.CollisionContext; @@ -91,10 +88,16 @@ public final GateType getGateType() { return type; } + @Override public int getOrientation() { return orientation & 0xFF; } + @Override + public int getRenderIndex() { + return type.ordinal(); + } + @Override public int shape() { return gateShape; @@ -357,12 +360,16 @@ public MultipartType getType() { //region Items and drops @Override public ItemStack getCloneStack(PartRayTraceResult hit) { - return getGateType().makeStack(); + return getItem(); } @Override public Iterable getDrops() { - return Collections.singleton(getGateType().makeStack()); + return Collections.singleton(getItem()); + } + + protected ItemStack getItem() { + return getGateType().makeStack(); } //endregion diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/IGateRenderData.java b/integration/src/main/java/mrtjp/projectred/integration/part/IGateRenderData.java index 9149fbe34..b8a956197 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/IGateRenderData.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/IGateRenderData.java @@ -1,8 +1,14 @@ package mrtjp.projectred.integration.part; +import net.minecraft.core.BlockPos; + public interface IGateRenderData { //@formatter:off + // Render type + int getRenderIndex(); // Unique identifier that selects exact model to render + int getOrientation(); // Packed orientation data. See {@link VecLib.orientT} + // General stuff int shape(); int state(); @@ -30,9 +36,15 @@ public interface IGateRenderData { default int bInHigh() { return 0; } //TODO this is just bundled input side 0 default byte segmentColour() { return 0; } // TODO this can be a state2 + // Bus input panel + default BlockPos worldPos() { return BlockPos.ZERO; } + // Array Cells default byte bottomSignal() { return 0; } default byte topSignal() { return 0; } default int topSignalConnMask() { return 0; } + + // Fabricated Gate + default String getGateName() { return "???"; } //@formatter:on } diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/RedstoneGatePart.java b/integration/src/main/java/mrtjp/projectred/integration/part/RedstoneGatePart.java index 3d91951fa..9aeec01b4 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/RedstoneGatePart.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/RedstoneGatePart.java @@ -239,11 +239,11 @@ protected int inputMask(int shape) { return 0; } - int getOutput(int r) { + protected int getOutput(int r) { return (getState() & 0x10 << r) != 0 ? 15 : 0; } - int getInput(int mask) { + protected int getInput(int mask) { int input = 0; for (int r = 0; r < 4; r++) { if ((mask & 1 << r) != 0 && getRedstoneInput(r) > 0) { diff --git a/integration/src/main/java/mrtjp/projectred/integration/part/SimpleGatePart.java b/integration/src/main/java/mrtjp/projectred/integration/part/SimpleGatePart.java index 0fce8fce4..a72c6c9bb 100644 --- a/integration/src/main/java/mrtjp/projectred/integration/part/SimpleGatePart.java +++ b/integration/src/main/java/mrtjp/projectred/integration/part/SimpleGatePart.java @@ -548,7 +548,7 @@ protected int cycleShape(int shape) { } @Override - int getOutput(int r) { + protected int getOutput(int r) { return r == 2 ? state() >> 4 : 0; } diff --git a/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.blend b/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.blend index e6d6533b7..56b9b4273 100644 Binary files a/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.blend and b/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.blend differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.obj b/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.obj index c8b8fc770..6ab2cc15d 100644 --- a/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.obj +++ b/integration/src/main/resources/assets/projectred_integration/obj/array/icbundled.obj @@ -1,4 +1,4 @@ -# Blender v2.71 (sub 5) OBJ File: 'icbundled.blend' +# Blender v3.1.2 OBJ File: 'icbundled.blend' # www.blender.org g base.002 v -0.187500 0.000000 -0.312600 @@ -9,23 +9,23 @@ v -0.187500 0.250000 -0.312600 v -0.187500 0.250000 -0.500010 v 0.187500 0.250000 -0.500010 v 0.187500 0.250000 -0.312600 -vt 0.312500 0.375000 -vt 0.312500 0.468750 -vt 0.437500 0.468750 -vt 0.437500 0.375000 -vt 0.125000 0.375000 -vt 0.125000 0.250000 -vt 0.312500 0.250000 -vt 0.125000 0.468750 -vt 0.000000 0.468750 -vt 0.000000 0.375000 +vt 0.125000 0.781250 vt 0.125000 0.875000 +vt -0.000000 0.875000 +vt -0.000000 0.781250 vt 0.312500 0.875000 vt 0.312500 1.000000 vt 0.125000 1.000000 +vt 0.312500 0.781250 +vt 0.437500 0.781250 +vt 0.437500 0.875000 +vt 0.312500 0.375000 +vt 0.125000 0.375000 +vt 0.125000 0.250000 +vt 0.312500 0.250000 s off f 5/1 6/2 2/3 1/4 -f 6/1 7/5 3/6 2/7 +f 6/2 7/5 3/6 2/7 f 7/5 8/8 4/9 3/10 f 8/11 5/12 1/13 4/14 -f 8/8 7/5 6/1 5/2 +f 8/8 7/5 6/2 5/1 diff --git a/integration/src/main/resources/assets/projectred_integration/obj/fab_ic.blend b/integration/src/main/resources/assets/projectred_integration/obj/fab_ic.blend new file mode 100644 index 000000000..cc79a9bec Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/obj/fab_ic.blend differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/fab_ic.obj b/integration/src/main/resources/assets/projectred_integration/obj/fab_ic.obj new file mode 100644 index 000000000..50124709b --- /dev/null +++ b/integration/src/main/resources/assets/projectred_integration/obj/fab_ic.obj @@ -0,0 +1,287 @@ +# Blender v3.1.2 OBJ File: 'fab_ic.blend' +# www.blender.org +g ic +v -0.080277 0.125000 -0.169473 +v -0.080277 0.125000 0.169473 +v -0.169473 0.125000 0.169473 +v -0.169473 0.125000 -0.169473 +v -0.080277 0.171875 -0.169473 +v -0.080277 0.171875 0.169473 +v -0.169473 0.171875 0.169473 +v -0.169473 0.171875 -0.169473 +v 0.169473 0.125000 -0.169473 +v 0.169473 0.125000 0.169473 +v 0.080277 0.125000 0.169473 +v 0.080277 0.125000 -0.169473 +v 0.169473 0.171875 -0.169473 +v 0.169473 0.171875 0.169473 +v 0.080277 0.171875 0.169473 +v 0.080277 0.171875 -0.169473 +v 0.044598 0.125000 -0.169473 +v 0.044598 0.125000 0.169473 +v -0.044598 0.125000 0.169473 +v -0.044598 0.125000 -0.169473 +v 0.044598 0.171875 -0.169473 +v 0.044598 0.171875 0.169473 +v -0.044598 0.171875 0.169473 +v -0.044598 0.171875 -0.169473 +v 0.187312 0.125000 -0.133795 +v 0.187313 0.125000 0.133795 +v -0.187312 0.125000 0.133795 +v -0.187313 0.125000 -0.133795 +v 0.187312 0.187500 -0.133795 +v 0.187313 0.187500 0.133795 +v -0.187312 0.187500 0.133795 +v -0.187313 0.187500 -0.133795 +vt 0.343750 0.812500 +vt 0.343750 0.218750 +vt 0.375000 0.218750 +vt 0.375000 0.812500 +vt 0.375000 0.250000 +vt 0.218750 0.250000 +vt 0.218750 0.218750 +vt 0.250000 0.218750 +vt 0.250000 0.812500 +vt 0.218750 0.812500 +vt 0.218750 0.812500 +vt 0.375000 0.812500 +vt 0.375000 0.781250 +vt 0.218750 0.781250 +vt 0.218750 0.218750 +vt 0.375000 0.218750 +vt 0.718750 0.812500 +vt 0.718750 0.218750 +vt 0.750000 0.218750 +vt 0.750000 0.812500 +vt 0.750000 0.250000 +vt 0.593750 0.250000 +vt 0.593750 0.218750 +vt 0.625000 0.218750 +vt 0.625000 0.812500 +vt 0.593750 0.812500 +vt 0.593750 0.812500 +vt 0.750000 0.812500 +vt 0.750000 0.781250 +vt 0.593750 0.781250 +vt 0.593750 0.218750 +vt 0.750000 0.218750 +vt 0.531250 0.812500 +vt 0.531250 0.218750 +vt 0.562500 0.218750 +vt 0.562500 0.812500 +vt 0.562500 0.250000 +vt 0.406250 0.250000 +vt 0.406250 0.218750 +vt 0.437500 0.218750 +vt 0.437500 0.812500 +vt 0.406250 0.812500 +vt 0.406250 0.812500 +vt 0.562500 0.812500 +vt 0.562500 0.781250 +vt 0.406250 0.781250 +vt 0.406250 0.218750 +vt 0.562500 0.218750 +vt 0.781250 0.750000 +vt 0.781250 0.281250 +vt 0.812500 0.281250 +vt 0.812500 0.750000 +vt 0.812500 0.312500 +vt 0.156250 0.312500 +vt 0.156250 0.281250 +vt 0.187500 0.281250 +vt 0.187500 0.750000 +vt 0.156250 0.750000 +vt 0.812500 0.750000 +vt 0.156250 0.750000 +vt 0.156250 0.718750 +vt 0.812500 0.718750 +vt 0.156250 0.750000 +vt 0.156250 0.281250 +vt 0.812500 0.281250 +vt 0.812500 0.750000 +s off +f 5/1 6/2 2/3 1/4 +f 6/5 7/6 3/7 2/3 +f 7/8 8/9 4/10 3/7 +f 8/11 5/12 1/13 4/14 +f 8/11 7/15 6/16 5/12 +f 13/17 14/18 10/19 9/20 +f 14/21 15/22 11/23 10/19 +f 15/24 16/25 12/26 11/23 +f 16/27 13/28 9/29 12/30 +f 16/27 15/31 14/32 13/28 +f 21/33 22/34 18/35 17/36 +f 22/37 23/38 19/39 18/35 +f 23/40 24/41 20/42 19/39 +f 24/43 21/44 17/45 20/46 +f 24/43 23/47 22/48 21/44 +f 29/49 30/50 26/51 25/52 +f 30/53 31/54 27/55 26/51 +f 31/56 32/57 28/58 27/55 +f 32/59 29/60 25/61 28/62 +f 32/63 31/64 30/65 29/66 +g glass +v -0.312500 0.000010 0.312500 +v -0.312500 0.000010 -0.312500 +v 0.312500 0.000010 -0.312500 +v 0.312500 0.000010 0.312500 +v -0.312500 0.251000 0.312500 +v -0.312500 0.251000 -0.312500 +v 0.312500 0.251000 -0.312500 +v 0.312500 0.251000 0.312500 +v -0.312500 0.000010 0.312500 +v -0.312500 0.000010 -0.312500 +v 0.312500 0.000010 -0.312500 +v 0.312500 0.000010 0.312500 +v -0.312500 0.251000 0.312500 +v -0.312500 0.251000 -0.312500 +v 0.312500 0.251000 -0.312500 +v 0.312500 0.251000 0.312500 +vt 0.031250 0.468750 +vt 0.531250 0.468750 +vt 0.531250 0.593750 +vt 0.031250 0.593750 +vt 0.031250 0.468750 +vt 0.531250 0.468750 +vt 0.531250 0.593750 +vt 0.031250 0.593750 +vt 0.031250 0.468750 +vt 0.531250 0.468750 +vt 0.531250 0.593750 +vt 0.031250 0.593750 +vt 0.031250 0.468750 +vt 0.531250 0.468750 +vt 0.531250 0.593750 +vt 0.031250 0.593750 +vt 0.531250 0.968750 +vt 0.031250 0.968750 +vt 0.031250 0.468750 +vt 0.031250 0.593750 +vt 0.531250 0.593750 +vt 0.531250 0.468750 +vt 0.031250 0.468750 +vt 0.031250 0.593750 +vt 0.531250 0.593750 +vt 0.531250 0.468750 +vt 0.031250 0.468750 +vt 0.031250 0.593750 +vt 0.531250 0.593750 +vt 0.531250 0.468750 +vt 0.031250 0.468750 +vt 0.031250 0.593750 +vt 0.531250 0.593750 +vt 0.531250 0.468750 +vt 0.031250 0.968750 +vt 0.531250 0.968750 +s off +f 37/67 38/68 34/69 33/70 +f 38/71 39/72 35/73 34/74 +f 39/75 40/76 36/77 35/78 +f 40/79 37/80 33/81 36/82 +f 40/79 39/72 38/83 37/84 +f 45/85 41/86 42/87 46/88 +f 46/89 42/90 43/91 47/92 +f 47/93 43/94 44/95 48/96 +f 48/97 44/98 41/99 45/100 +f 48/97 45/101 46/102 47/92 +g platform +v -0.343750 0.000010 0.343750 +v -0.343750 0.000010 -0.343750 +v -0.281250 0.000010 -0.343750 +v -0.281250 0.000010 0.343750 +v -0.343750 0.153125 0.343750 +v -0.343750 0.153125 -0.343750 +v -0.281250 0.153125 -0.343750 +v -0.281250 0.153125 0.343750 +v 0.281250 0.000010 0.343750 +v 0.281250 0.000010 -0.343750 +v 0.343750 0.000010 -0.343750 +v 0.343750 0.000010 0.343750 +v 0.281250 0.153125 0.343750 +v 0.281250 0.153125 -0.343750 +v 0.343750 0.153125 -0.343750 +v 0.343750 0.153125 0.343750 +v -0.343750 0.000010 0.343750 +v -0.343750 0.000010 0.281250 +v 0.343750 0.000010 0.281250 +v 0.343750 0.000010 0.343750 +v -0.343750 0.153125 0.343750 +v -0.343750 0.153125 0.281250 +v 0.343750 0.153125 0.281250 +v 0.343750 0.153125 0.343750 +v -0.343750 0.000010 -0.281250 +v -0.343750 0.000010 -0.343750 +v 0.343750 0.000010 -0.343750 +v 0.343750 0.000010 -0.281250 +v -0.343750 0.153125 -0.281250 +v -0.343750 0.153125 -0.343750 +v 0.343750 0.153125 -0.343750 +v 0.343750 0.153125 -0.281250 +v -0.312500 0.140625 0.312500 +v 0.312500 0.140625 0.312500 +v -0.312500 0.140625 -0.312500 +v 0.312500 0.140625 -0.312500 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.093750 +vt 0.093750 0.093750 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.093750 +vt 0.093750 0.093750 +vt 0.125000 0.031250 +vt 0.125000 0.375000 +vt 0.093750 0.375000 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.062500 +vt 0.093750 0.062500 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.062500 +vt 0.093750 0.062500 +vt 0.437500 0.375000 +vt 0.406250 0.375000 +vt 0.406250 0.031250 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.093750 +vt 0.093750 0.093750 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.093750 +vt 0.093750 0.093750 +vt 0.437500 0.031250 +vt 0.437500 0.062500 +vt 0.093750 0.062500 +vt 0.093750 0.031250 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.062500 +vt 0.093750 0.062500 +vt 0.093750 0.031250 +vt 0.437500 0.031250 +vt 0.437500 0.062500 +vt 0.093750 0.062500 +vt 0.437500 0.343750 +vt 0.437500 0.375000 +vt 0.093750 0.375000 +vt 0.093750 0.343750 +vt 0.437500 0.093750 +vt 0.718750 0.093750 +vt 0.718750 0.375000 +vt 0.437500 0.375000 +s off +f 53/103 54/104 50/105 49/106 +f 55/107 56/108 52/109 51/110 +f 56/111 55/112 54/113 53/103 +f 61/114 62/115 58/116 57/117 +f 63/118 64/119 60/120 59/121 +f 64/119 63/122 62/123 61/124 +f 70/125 71/126 67/127 66/128 +f 72/129 69/130 65/131 68/132 +f 72/133 71/134 70/135 69/136 +f 78/137 79/138 75/139 74/140 +f 80/141 77/142 73/143 76/144 +f 80/145 79/146 78/147 77/148 +f 81/149 82/150 84/151 83/152 diff --git a/integration/src/main/resources/assets/projectred_integration/obj/icchip.blend b/integration/src/main/resources/assets/projectred_integration/obj/icchip.blend deleted file mode 100644 index 5865978ca..000000000 Binary files a/integration/src/main/resources/assets/projectred_integration/obj/icchip.blend and /dev/null differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/icchip.obj b/integration/src/main/resources/assets/projectred_integration/obj/icchip.obj deleted file mode 100644 index 3f2e44558..000000000 --- a/integration/src/main/resources/assets/projectred_integration/obj/icchip.obj +++ /dev/null @@ -1,104 +0,0 @@ -# Blender v2.71 (sub 5) OBJ File: 'icchip.blend' -# www.blender.org -g base -v -0.080277 0.000000 -0.169473 -v -0.080277 0.000000 0.169473 -v -0.169473 0.000000 0.169473 -v -0.169473 0.000000 -0.169473 -v -0.080277 0.046875 -0.169473 -v -0.080277 0.046875 0.169473 -v -0.169473 0.046875 0.169473 -v -0.169473 0.046875 -0.169473 -v 0.169473 0.000000 -0.169473 -v 0.169473 0.000000 0.169473 -v 0.080277 0.000000 0.169473 -v 0.080277 0.000000 -0.169473 -v 0.169473 0.046875 -0.169473 -v 0.169473 0.046875 0.169473 -v 0.080277 0.046875 0.169473 -v 0.080277 0.046875 -0.169473 -v 0.044598 0.000000 -0.169473 -v 0.044598 0.000000 0.169473 -v -0.044598 0.000000 0.169473 -v -0.044598 0.000000 -0.169473 -v 0.044598 0.046875 -0.169473 -v 0.044598 0.046875 0.169473 -v -0.044598 0.046875 0.169473 -v -0.044598 0.046875 -0.169473 -v 0.187312 0.000000 -0.133795 -v 0.187313 0.000000 0.133795 -v -0.187312 0.000000 0.133795 -v -0.187313 0.000000 -0.133795 -v 0.187312 0.062500 -0.133795 -v 0.187313 0.062500 0.133795 -v -0.187312 0.062500 0.133795 -v -0.187313 0.062500 -0.133795 -vt 0.343750 0.812500 -vt 0.343750 0.218750 -vt 0.375000 0.218750 -vt 0.375000 0.812500 -vt 0.375000 0.250000 -vt 0.218750 0.250000 -vt 0.218750 0.218750 -vt 0.250000 0.218750 -vt 0.250000 0.812500 -vt 0.218750 0.812500 -vt 0.375000 0.781250 -vt 0.218750 0.781250 -vt 0.718750 0.812500 -vt 0.718750 0.218750 -vt 0.750000 0.218750 -vt 0.750000 0.812500 -vt 0.750000 0.250000 -vt 0.593750 0.250000 -vt 0.593750 0.218750 -vt 0.625000 0.218750 -vt 0.625000 0.812500 -vt 0.593750 0.812500 -vt 0.750000 0.781250 -vt 0.593750 0.781250 -vt 0.531250 0.812500 -vt 0.531250 0.218750 -vt 0.562500 0.218750 -vt 0.562500 0.812500 -vt 0.562500 0.250000 -vt 0.406250 0.250000 -vt 0.406250 0.218750 -vt 0.437500 0.218750 -vt 0.437500 0.812500 -vt 0.406250 0.812500 -vt 0.562500 0.781250 -vt 0.406250 0.781250 -vt 0.781250 0.750000 -vt 0.781250 0.281250 -vt 0.812500 0.281250 -vt 0.812500 0.750000 -vt 0.812500 0.312500 -vt 0.156250 0.312500 -vt 0.156250 0.281250 -vt 0.187500 0.281250 -vt 0.187500 0.750000 -vt 0.156250 0.750000 -vt 0.156250 0.718750 -vt 0.812500 0.718750 -s off -f 5/1 6/2 2/3 1/4 -f 6/5 7/6 3/7 2/3 -f 7/8 8/9 4/10 3/7 -f 8/10 5/4 1/11 4/12 -f 8/10 7/7 6/3 5/4 -f 13/13 14/14 10/15 9/16 -f 14/17 15/18 11/19 10/15 -f 15/20 16/21 12/22 11/19 -f 16/22 13/16 9/23 12/24 -f 16/22 15/19 14/15 13/16 -f 21/25 22/26 18/27 17/28 -f 22/29 23/30 19/31 18/27 -f 23/32 24/33 20/34 19/31 -f 24/34 21/28 17/35 20/36 -f 24/34 23/31 22/27 21/28 -f 29/37 30/38 26/39 25/40 -f 30/41 31/42 27/43 26/39 -f 31/44 32/45 28/46 27/43 -f 32/40 29/46 25/47 28/48 -f 32/46 31/43 30/39 29/40 diff --git a/integration/src/main/resources/assets/projectred_integration/obj/icglass.blend b/integration/src/main/resources/assets/projectred_integration/obj/icglass.blend deleted file mode 100644 index bab8d54d8..000000000 Binary files a/integration/src/main/resources/assets/projectred_integration/obj/icglass.blend and /dev/null differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/icglass.obj b/integration/src/main/resources/assets/projectred_integration/obj/icglass.obj deleted file mode 100644 index dbdae5b93..000000000 --- a/integration/src/main/resources/assets/projectred_integration/obj/icglass.obj +++ /dev/null @@ -1,44 +0,0 @@ -# Blender v2.71 (sub 5) OBJ File: 'icglass.blend' -# www.blender.org -g base.001_base.002 -v -0.312500 0.000010 0.312500 -v -0.312500 0.000010 -0.312500 -v 0.312500 0.000010 -0.312500 -v 0.312500 0.000010 0.312500 -v -0.312500 0.251000 0.312500 -v -0.312500 0.251000 -0.312500 -v 0.312500 0.251000 -0.312500 -v 0.312500 0.251000 0.312500 -vt 0.031250 0.468750 -vt 0.031250 0.593750 -vt 0.531250 0.593750 -vt 0.531250 0.468750 -vt 0.031250 0.968750 -vt 0.531250 0.968750 -s off -f 5/1 1/2 2/3 6/4 -f 6/1 2/2 3/3 7/4 -f 7/1 3/2 4/3 8/4 -f 8/1 4/2 1/3 5/4 -f 8/1 5/5 6/6 7/4 -g base -v -0.312500 0.000010 0.312500 -v -0.312500 0.000010 -0.312500 -v 0.312500 0.000010 -0.312500 -v 0.312500 0.000010 0.312500 -v -0.312500 0.251000 0.312500 -v -0.312500 0.251000 -0.312500 -v 0.312500 0.251000 -0.312500 -v 0.312500 0.251000 0.312500 -vt 0.031250 0.468750 -vt 0.531250 0.468750 -vt 0.531250 0.593750 -vt 0.031250 0.593750 -vt 0.531250 0.968750 -vt 0.031250 0.968750 -s off -f 13/7 14/8 10/9 9/10 -f 14/7 15/8 11/9 10/10 -f 15/7 16/8 12/9 11/10 -f 16/7 13/8 9/9 12/10 -f 16/7 15/8 14/11 13/12 diff --git a/integration/src/main/resources/assets/projectred_integration/obj/ichousing.blend b/integration/src/main/resources/assets/projectred_integration/obj/ichousing.blend deleted file mode 100644 index 09c62922b..000000000 Binary files a/integration/src/main/resources/assets/projectred_integration/obj/ichousing.blend and /dev/null differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/ichousing.obj b/integration/src/main/resources/assets/projectred_integration/obj/ichousing.obj deleted file mode 100644 index 856b99a88..000000000 --- a/integration/src/main/resources/assets/projectred_integration/obj/ichousing.obj +++ /dev/null @@ -1,73 +0,0 @@ -# Blender v2.71 (sub 5) OBJ File: 'ichousing.blend' -# www.blender.org -g Plane -v -0.312500 0.140625 0.312500 -v 0.312500 0.140625 0.312500 -v -0.312500 0.140625 -0.312500 -v 0.312500 0.140625 -0.312500 -vt 0.437500 0.093750 -vt 0.718750 0.093750 -vt 0.718750 0.375000 -vt 0.437500 0.375000 -s off -f 1/1 2/2 4/3 3/4 -g base.005 -v -0.343750 0.000010 0.343750 -v -0.343750 0.000010 -0.343750 -v -0.281250 0.000010 -0.343750 -v -0.281250 0.000010 0.343750 -v -0.343750 0.153125 0.343750 -v -0.343750 0.153125 -0.343750 -v -0.281250 0.153125 -0.343750 -v -0.281250 0.153125 0.343750 -v 0.281250 0.000010 0.343750 -v 0.281250 0.000010 -0.343750 -v 0.343750 0.000010 -0.343750 -v 0.343750 0.000010 0.343750 -v 0.281250 0.153125 0.343750 -v 0.281250 0.153125 -0.343750 -v 0.343750 0.153125 -0.343750 -v 0.343750 0.153125 0.343750 -v -0.343750 0.000010 0.343750 -v -0.343750 0.000010 0.281250 -v 0.343750 0.000010 0.281250 -v 0.343750 0.000010 0.343750 -v -0.343750 0.153125 0.343750 -v -0.343750 0.153125 0.281250 -v 0.343750 0.153125 0.281250 -v 0.343750 0.153125 0.343750 -v -0.343750 0.000010 -0.281250 -v -0.343750 0.000010 -0.343750 -v 0.343750 0.000010 -0.343750 -v 0.343750 0.000010 -0.281250 -v -0.343750 0.153125 -0.281250 -v -0.343750 0.153125 -0.343750 -v 0.343750 0.153125 -0.343750 -v 0.343750 0.153125 -0.281250 -vt 0.093750 0.031250 -vt 0.437500 0.031250 -vt 0.437500 0.093750 -vt 0.093750 0.093750 -vt 0.125000 0.031250 -vt 0.125000 0.375000 -vt 0.093750 0.375000 -vt 0.437500 0.062500 -vt 0.093750 0.062500 -vt 0.437500 0.375000 -vt 0.406250 0.375000 -vt 0.406250 0.031250 -vt 0.437500 0.343750 -vt 0.093750 0.343750 -s off -f 9/5 10/6 6/7 5/8 -f 11/5 12/6 8/7 7/8 -f 12/9 11/10 10/11 9/5 -f 17/5 18/6 14/12 13/13 -f 19/5 20/6 16/12 15/13 -f 20/6 19/14 18/15 17/16 -f 26/5 27/6 23/7 22/8 -f 28/5 25/6 21/7 24/8 -f 28/6 27/12 26/13 25/5 -f 34/5 35/6 31/12 30/13 -f 36/5 33/6 29/12 32/13 -f 36/17 35/14 34/11 33/18 diff --git a/integration/src/main/resources/assets/projectred_integration/obj/io_colour_box.blend b/integration/src/main/resources/assets/projectred_integration/obj/io_colour_box.blend new file mode 100644 index 000000000..5d05f00aa Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/obj/io_colour_box.blend differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/io_colour_box.obj b/integration/src/main/resources/assets/projectred_integration/obj/io_colour_box.obj new file mode 100644 index 000000000..794e94787 --- /dev/null +++ b/integration/src/main/resources/assets/projectred_integration/obj/io_colour_box.obj @@ -0,0 +1,54 @@ +# Blender v3.1.2 OBJ File: 'io_colour_box.blend' +# www.blender.org +g box +v -0.125000 0.125000 -0.125000 +v -0.125000 0.000000 -0.125000 +v 0.125000 0.125000 -0.125000 +v 0.125000 0.000000 -0.125000 +v -0.125000 0.125000 0.125000 +v -0.125000 -0.000000 0.125000 +v 0.125000 0.125000 0.125000 +v 0.125000 -0.000000 0.125000 +vt 0.437500 0.875000 +vt 0.437500 1.000000 +vt 0.125000 1.000000 +vt 0.125000 0.875000 +vt 0.437500 0.562500 +vt 0.125000 0.562500 +vt 0.125000 0.437500 +vt 0.437500 0.437500 +vt 0.562500 0.562500 +vt 0.562500 0.875000 +vt 0.000000 0.875000 +vt 0.000000 0.562500 +s off +f 3/1 4/2 2/3 1/4 +f 7/5 5/6 6/7 8/8 +f 3/1 7/5 8/9 4/10 +f 2/11 6/12 5/6 1/4 +f 1/4 5/6 7/5 3/1 +g arrow +v -0.100000 0.165063 -0.100000 +v -0.100000 0.124000 -0.100000 +v 0.100000 0.165063 -0.100000 +v 0.100000 0.124000 -0.100000 +v 0.000000 0.165062 0.100000 +v 0.000000 0.124000 0.100000 +vt 0.875000 0.968750 +vt 0.875000 1.000000 +vt 0.750000 1.000000 +vt 0.750000 0.968750 +vt 0.843750 1.000000 +vt 0.843750 0.875000 +vt 0.875000 0.875000 +vt 0.750000 0.875000 +vt 0.781250 0.875000 +vt 0.781250 1.000000 +vt 0.750000 1.000000 +vt 0.812500 0.875000 +vt 0.875000 1.000000 +s off +f 11/13 12/14 10/15 9/16 +f 11/17 13/18 14/19 12/14 +f 10/15 14/20 13/21 9/22 +f 9/23 13/24 11/25 diff --git a/integration/src/main/resources/assets/projectred_integration/obj/io_crimp.blend b/integration/src/main/resources/assets/projectred_integration/obj/io_crimp.blend new file mode 100644 index 000000000..0a8a16ee2 Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/obj/io_crimp.blend differ diff --git a/integration/src/main/resources/assets/projectred_integration/obj/io_crimp.obj b/integration/src/main/resources/assets/projectred_integration/obj/io_crimp.obj new file mode 100644 index 000000000..bcd58d242 --- /dev/null +++ b/integration/src/main/resources/assets/projectred_integration/obj/io_crimp.obj @@ -0,0 +1,233 @@ +# Blender v3.1.2 OBJ File: 'io_crimp.blend' +# www.blender.org +g crimp +v 0.437500 0.031250 -0.187510 +v 0.437500 -0.031250 -0.187510 +v 0.000000 0.031250 -0.187510 +v 0.000000 -0.031250 -0.187510 +v 0.437500 0.031250 0.000010 +v 0.437500 -0.031250 0.000010 +v 0.000000 0.031250 0.000010 +v 0.000000 -0.031250 0.000010 +v 0.000010 -0.031250 -0.062500 +v 0.000010 0.156250 -0.062500 +v 0.125010 -0.031250 -0.062500 +v 0.125010 0.156250 -0.062500 +v 0.000010 -0.031250 0.000000 +v 0.000010 0.156250 0.000000 +v 0.125010 -0.031250 0.000000 +v 0.125010 0.156250 -0.000000 +v 0.000010 -0.031250 -0.187500 +v 0.000010 0.156250 -0.187500 +v 0.125010 -0.031250 -0.187500 +v 0.125010 0.156250 -0.187500 +v 0.000010 -0.031250 -0.125000 +v 0.000010 0.156250 -0.125000 +v 0.125010 -0.031250 -0.125000 +v 0.125010 0.156250 -0.125000 +v -0.406250 0.062500 -0.468750 +v -0.406250 -0.031250 -0.468750 +v 0.406250 0.062500 -0.468750 +v 0.406250 -0.031250 -0.468750 +v -0.406250 0.062500 -0.218750 +v -0.406250 -0.031250 -0.218750 +v 0.406250 0.062500 -0.218750 +v 0.406250 -0.031250 -0.218750 +v 0.212500 0.062500 -0.218750 +v 0.212500 -0.031250 -0.218750 +v 0.406250 0.062500 -0.218750 +v 0.406250 -0.031250 -0.218750 +v 0.212500 0.062500 0.031250 +v 0.212500 -0.031250 0.031250 +v 0.406250 0.062500 0.031250 +v 0.406250 -0.031250 0.031250 +v 0.281250 0.093750 -0.125000 +v 0.281250 -0.031250 -0.125000 +v 0.343750 0.093750 -0.125000 +v 0.343750 -0.031250 -0.125000 +v 0.281250 0.093750 -0.062500 +v 0.281250 -0.031250 -0.062500 +v 0.343750 0.093750 -0.062500 +v 0.343750 -0.031250 -0.062500 +vt 0.000000 0.968750 +vt 0.031250 0.968750 +vt 0.031250 0.750000 +vt 0.000000 0.750000 +vt 0.031250 0.750000 +vt 0.031250 0.718750 +vt 0.125000 0.718750 +vt 0.125000 0.750000 +vt 0.156250 0.750000 +vt 0.156250 0.968750 +vt 0.125000 0.968750 +vt 0.031250 0.968750 +vt 0.281250 0.531250 +vt 0.375000 0.531250 +vt 0.375000 0.593750 +vt 0.281250 0.593750 +vt 0.375000 0.625000 +vt 0.281250 0.625000 +vt 0.531250 0.593750 +vt 0.437500 0.593750 +vt 0.437500 0.531250 +vt 0.531250 0.531250 +vt 0.281250 0.500000 +vt 0.375000 0.500000 +vt 0.000000 0.531250 +vt 0.093750 0.531250 +vt 0.093750 0.593750 +vt 0.000000 0.593750 +vt 0.250000 0.625000 +vt 0.156250 0.625000 +vt 0.156250 0.593750 +vt 0.250000 0.593750 +vt 0.156250 0.531250 +vt 0.250000 0.531250 +vt 0.156250 0.500000 +vt 0.250000 0.500000 +vt 0.093750 0.531250 +vt 0.156250 0.531250 +vt 0.156250 0.593750 +vt 0.093750 0.593750 +vt 0.593750 0.968750 +vt 0.593750 1.000000 +vt 0.187500 1.000000 +vt 0.187500 0.968750 +vt 0.593750 0.843750 +vt 0.187500 0.843750 +vt 0.187500 0.812500 +vt 0.593750 0.812500 +vt 0.625000 0.843750 +vt 0.625000 0.968750 +vt 0.156250 0.968750 +vt 0.156250 0.843750 +vt 0.125000 1.000000 +vt 0.031250 1.000000 +vt 0.593750 0.656250 +vt 0.500000 0.656250 +vt 0.500000 0.625000 +vt 0.593750 0.625000 +vt 0.593750 0.781250 +vt 0.625000 0.656250 +vt 0.625000 0.781250 +vt 0.468750 0.781250 +vt 0.468750 0.656250 +vt 0.500000 0.781250 +vt 0.406250 0.750000 +vt 0.406250 0.781250 +vt 0.375000 0.781250 +vt 0.375000 0.750000 +vt 0.406250 0.718750 +vt 0.375000 0.718750 +vt 0.375000 0.687500 +vt 0.406250 0.687500 +vt 0.437500 0.718750 +vt 0.437500 0.750000 +vt 0.343750 0.750000 +vt 0.343750 0.718750 +s off +f 1/1 2/2 4/3 3/4 +f 3/5 4/6 8/7 7/8 +f 7/8 8/9 6/10 5/11 +f 3/5 7/8 5/11 1/12 +f 9/13 10/14 12/15 11/16 +f 11/16 12/15 16/17 15/18 +f 15/19 16/20 14/21 13/22 +f 13/23 14/24 10/14 9/13 +f 16/20 12/15 10/14 14/21 +f 17/25 18/26 20/27 19/28 +f 19/29 20/30 24/31 23/32 +f 23/32 24/31 22/33 21/34 +f 21/34 22/33 18/35 17/36 +f 24/37 20/38 18/39 22/40 +f 27/41 28/42 26/43 25/44 +f 31/45 29/46 30/47 32/48 +f 27/41 31/45 32/49 28/50 +f 26/51 30/52 29/46 25/44 +f 25/44 29/46 31/45 27/41 +f 1/12 5/11 6/53 2/54 +f 39/55 37/56 38/57 40/58 +f 35/59 39/55 40/60 36/61 +f 34/62 38/63 37/56 33/64 +f 33/64 37/56 39/55 35/59 +f 43/65 44/66 42/67 41/68 +f 47/69 45/70 46/71 48/72 +f 43/65 47/69 48/73 44/74 +f 42/75 46/76 45/70 41/68 +f 41/68 45/70 47/69 43/65 +g redalloy +v -0.125000 -0.062500 -0.156250 +v -0.125000 0.125000 -0.156250 +v 0.187500 -0.062500 -0.156250 +v 0.187500 0.125000 -0.156250 +v -0.125000 -0.062500 -0.031250 +v -0.125000 0.125000 -0.031250 +v 0.187500 -0.062500 -0.031250 +v 0.187500 0.125000 -0.031250 +v -0.375100 -0.109375 -0.156250 +v -0.375100 0.015625 -0.156250 +v -0.125000 -0.109375 -0.156250 +v -0.125000 0.015625 -0.156250 +v -0.375100 -0.109375 -0.031250 +v -0.375100 0.015625 -0.031250 +v -0.125000 -0.109375 -0.031250 +v -0.125000 0.015625 -0.031250 +v 0.336056 -0.000000 0.180101 +v -0.375000 -0.109375 0.093750 +v -0.375000 0.015625 0.093750 +v -0.375000 -0.109375 -0.031250 +v -0.375000 0.015625 -0.031250 +v -0.250000 -0.109375 0.093750 +v -0.250000 0.015625 0.093750 +v -0.250000 -0.109375 -0.031250 +v -0.250000 0.015625 -0.031250 +vt 0.281250 0.468750 +vt 0.281250 0.375000 +vt 0.437500 0.375000 +vt 0.437500 0.468750 +vt 0.531250 0.375000 +vt 0.437500 0.312500 +vt 0.531250 0.312500 +vt 0.437500 0.218750 +vt 0.281250 0.312500 +vt 0.281250 0.218750 +vt 0.187500 0.312500 +vt 0.187500 0.375000 +vt 0.062500 0.437500 +vt 0.062500 0.375000 +vt 0.187500 0.375000 +vt 0.187500 0.437500 +vt 0.187500 0.250000 +vt 0.187500 0.312500 +vt 0.062500 0.312500 +vt 0.062500 0.250000 +vt 0.000000 0.312500 +vt 0.000000 0.375000 +vt 0.000000 0.125000 +vt 0.062500 0.125000 +vt 0.062500 0.187500 +vt 0.000000 0.187500 +vt 0.187500 0.187500 +vt 0.125000 0.187500 +vt 0.125000 0.125000 +vt 0.187500 0.125000 +vt 0.125000 0.062500 +vt 0.062500 0.062500 +vt 0.125000 0.250000 +vt 0.062500 0.250000 +vt 0.062500 0.187500 +vt 0.125000 0.187500 +s off +f 49/77 50/78 52/79 51/80 +f 51/81 52/79 56/82 55/83 +f 55/84 56/82 54/85 53/86 +f 53/87 54/85 50/78 49/88 +f 57/89 58/90 60/91 59/92 +f 56/82 52/79 50/78 54/85 +f 63/93 64/94 62/95 61/96 +f 61/97 62/95 58/90 57/98 +f 64/94 60/91 58/90 62/95 +f 66/99 67/100 69/101 68/102 +f 72/103 73/104 71/105 70/106 +f 70/107 71/105 67/100 66/108 +f 73/109 69/110 67/111 71/112 diff --git a/integration/src/main/resources/assets/projectred_integration/textures/block/io_colour_box.png b/integration/src/main/resources/assets/projectred_integration/textures/block/io_colour_box.png new file mode 100644 index 000000000..f309202de Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/textures/block/io_colour_box.png differ diff --git a/integration/src/main/resources/assets/projectred_integration/textures/block/io_crimp.png b/integration/src/main/resources/assets/projectred_integration/textures/block/io_crimp.png new file mode 100644 index 000000000..dae560584 Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/textures/block/io_crimp.png differ diff --git a/integration/src/main/resources/assets/projectred_integration/textures/block/io_crimp_old.png b/integration/src/main/resources/assets/projectred_integration/textures/block/io_crimp_old.png new file mode 100644 index 000000000..073b4b944 Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/textures/block/io_crimp_old.png differ diff --git a/integration/src/main/resources/assets/projectred_integration/textures/block/surface/fabio-0.png b/integration/src/main/resources/assets/projectred_integration/textures/block/surface/fabio-0.png new file mode 100644 index 000000000..491449348 Binary files /dev/null and b/integration/src/main/resources/assets/projectred_integration/textures/block/surface/fabio-0.png differ diff --git a/runtime/build.gradle b/runtime/build.gradle index 44746bc31..a7380ca6f 100644 --- a/runtime/build.gradle +++ b/runtime/build.gradle @@ -18,6 +18,7 @@ minecraft { 'projectred_core' { source project(":core").sourceSets.main } 'projectred_expansion' { source project(":expansion").sourceSets.main } 'projectred_exploration' { source project(":exploration").sourceSets.main } + 'projectred_fabrication' { source project(":fabrication").sourceSets.main } 'projectred_illumination' { source project(":illumination").sourceSets.main } 'projectred_integration' { source project(":integration").sourceSets.main } 'projectred_transmission' { source project(":transmission").sourceSets.main } @@ -40,6 +41,7 @@ dependencies { runtimeOnly project(":core") runtimeOnly project(":expansion") runtimeOnly project(":exploration") + runtimeOnly project(":fabrication") runtimeOnly project(":illumination") runtimeOnly project(":integration") runtimeOnly project(":transmission") diff --git a/settings.gradle b/settings.gradle index 62215fe71..7e20c0c7a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,13 +12,13 @@ pluginManagement { id 'com.matthewprenger.cursegradle' version '1.4.0' apply false id 'net.minecraftforge.gradle' version '5.1.+' apply false id 'org.spongepowered.mixin' version '0.7.+' apply false + id 'com.github.johnrengelman.shadow' version '7.1.+' apply false } } rootProject.name = 'ProjectRed' -// Mod modules -include 'core', 'expansion', 'exploration', 'illumination', 'integration', 'transmission' +include 'core', 'expansion', 'exploration', 'fabrication', 'illumination', 'integration', 'transmission' // Empty module for client/server run configurations include 'runtime'