diff --git a/src/api/java/me/desht/pneumaticcraft/api/lib/Names.java b/src/api/java/me/desht/pneumaticcraft/api/lib/Names.java index 6f19c0535..aafac5044 100644 --- a/src/api/java/me/desht/pneumaticcraft/api/lib/Names.java +++ b/src/api/java/me/desht/pneumaticcraft/api/lib/Names.java @@ -34,6 +34,7 @@ public class Names { public static final ResourceLocation MODULE_LOGISTICS = RL("logistics_module"); public static final ResourceLocation MODULE_REDSTONE = RL("redstone_module"); public static final ResourceLocation MODULE_VACUUM = RL("vacuum_module"); + public static final ResourceLocation MODULE_THERMOSTAT = RL("thermostat_module"); public static final String PNEUMATIC_KEYBINDING_CATEGORY_MAIN = "key.pneumaticcraft.category.main"; public static final String PNEUMATIC_KEYBINDING_CATEGORY_UPGRADE_TOGGLES = "key.pneumaticcraft.category.upgrade_toggles"; diff --git a/src/generated/resources/data/pneumaticcraft/advancements/recipes/misc/thermostat_module.json b/src/generated/resources/data/pneumaticcraft/advancements/recipes/misc/thermostat_module.json new file mode 100644 index 000000000..08db87bf0 --- /dev/null +++ b/src/generated/resources/data/pneumaticcraft/advancements/recipes/misc/thermostat_module.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_ingot_iron_compressed": { + "conditions": { + "items": [ + { + "items": [ + "pneumaticcraft:ingot_iron_compressed" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "pneumaticcraft:thermostat_module" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_ingot_iron_compressed", + "has_the_recipe" + ] + ], + "rewards": { + "recipes": [ + "pneumaticcraft:thermostat_module" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated/resources/data/pneumaticcraft/recipes/thermostat_module.json b/src/generated/resources/data/pneumaticcraft/recipes/thermostat_module.json new file mode 100644 index 000000000..68ec5623f --- /dev/null +++ b/src/generated/resources/data/pneumaticcraft/recipes/thermostat_module.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "D": { + "tag": "forge:dusts/redstone" + }, + "H": { + "item": "pneumaticcraft:heat_pipe" + }, + "M": { + "item": "pneumaticcraft:manometer" + }, + "T": { + "item": "pneumaticcraft:pressure_tube" + } + }, + "pattern": [ + " M ", + " H ", + "TDT" + ], + "result": { + "item": "pneumaticcraft:thermostat_module" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/main/java/me/desht/pneumaticcraft/client/ClientSetup.java b/src/main/java/me/desht/pneumaticcraft/client/ClientSetup.java index a46790f54..6f4faee1c 100644 --- a/src/main/java/me/desht/pneumaticcraft/client/ClientSetup.java +++ b/src/main/java/me/desht/pneumaticcraft/client/ClientSetup.java @@ -12,6 +12,7 @@ import me.desht.pneumaticcraft.client.gui.tubemodule.LogisticsModuleScreen; import me.desht.pneumaticcraft.client.gui.tubemodule.PressureGaugeModuleScreen; import me.desht.pneumaticcraft.client.gui.tubemodule.RedstoneModuleScreen; +import me.desht.pneumaticcraft.client.gui.tubemodule.ThermostatModuleScreen; import me.desht.pneumaticcraft.client.gui.upgrademanager.*; import me.desht.pneumaticcraft.client.model.ModelMinigun; import me.desht.pneumaticcraft.client.model.PNCModelLayers; @@ -206,6 +207,7 @@ public static void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDe event.registerLayerDefinition(PNCModelLayers.REGULATOR_MODULE, RegulatorRenderer::createBodyLayer); event.registerLayerDefinition(PNCModelLayers.SAFETY_VALVE_MODULE, SafetyValveRenderer::createBodyLayer); event.registerLayerDefinition(PNCModelLayers.VACUUM_MODULE, VacuumRenderer::createBodyLayer); + event.registerLayerDefinition(PNCModelLayers.THERMOSTAT_MODULE, ThermostatRenderer::createBodyLayer); } private static void registerItemModelProperties() { @@ -416,6 +418,7 @@ private static void registerTubeModuleFactories() { TubeModuleClientRegistry.registerTubeModuleGUI(Names.MODULE_SAFETY_VALVE, PressureGaugeModuleScreen::createGUI); TubeModuleClientRegistry.registerTubeModuleGUI(Names.MODULE_REDSTONE, RedstoneModuleScreen::new); TubeModuleClientRegistry.registerTubeModuleGUI(Names.MODULE_LOGISTICS, LogisticsModuleScreen::new); + TubeModuleClientRegistry.registerTubeModuleGUI(Names.MODULE_THERMOSTAT, ThermostatModuleScreen::createGUI); TubeModuleClientRegistry.registerTubeModuleRenderer(Names.MODULE_AIR_GRATE, AirGrateRenderer::new); TubeModuleClientRegistry.registerTubeModuleRenderer(Names.MODULE_REDSTONE, RedstoneRenderer::new); @@ -426,6 +429,7 @@ private static void registerTubeModuleFactories() { TubeModuleClientRegistry.registerTubeModuleRenderer(Names.MODULE_FLOW_DETECTOR, FlowDetectorRenderer::new); TubeModuleClientRegistry.registerTubeModuleRenderer(Names.MODULE_LOGISTICS, LogisticsRenderer::new); TubeModuleClientRegistry.registerTubeModuleRenderer(Names.MODULE_VACUUM, VacuumRenderer::new); + TubeModuleClientRegistry.registerTubeModuleRenderer(Names.MODULE_THERMOSTAT, ThermostatRenderer::new); } private static void registerArmorClientUpgradeHandlers() { diff --git a/src/main/java/me/desht/pneumaticcraft/client/gui/tubemodule/SimpleThermostatModuleScreen.java b/src/main/java/me/desht/pneumaticcraft/client/gui/tubemodule/SimpleThermostatModuleScreen.java new file mode 100644 index 000000000..99269d960 --- /dev/null +++ b/src/main/java/me/desht/pneumaticcraft/client/gui/tubemodule/SimpleThermostatModuleScreen.java @@ -0,0 +1,116 @@ +/* + * This file is part of pnc-repressurized. + * + * pnc-repressurized is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * pnc-repressurized is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pnc-repressurized. If not, see . + */ + +package me.desht.pneumaticcraft.client.gui.tubemodule; + +import me.desht.pneumaticcraft.client.gui.widget.WidgetCheckBox; +import me.desht.pneumaticcraft.client.gui.widget.WidgetColorSelector; +import me.desht.pneumaticcraft.client.gui.widget.WidgetLabel; +import me.desht.pneumaticcraft.client.gui.widget.WidgetTextField; +import me.desht.pneumaticcraft.client.gui.widget.WidgetTextFieldNumber; +import me.desht.pneumaticcraft.common.network.NetworkHandler; +import me.desht.pneumaticcraft.common.network.PacketSyncThermostatModuleToServer; +import me.desht.pneumaticcraft.common.network.PacketUpdatePressureModule; +import me.desht.pneumaticcraft.common.network.PacketTubeModuleColor; +import me.desht.pneumaticcraft.common.tubemodules.AbstractTubeModule; +import me.desht.pneumaticcraft.common.tubemodules.ThermostatModule; +import me.desht.pneumaticcraft.lib.Textures; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; + +import static me.desht.pneumaticcraft.common.util.PneumaticCraftUtils.xlate; + +public class SimpleThermostatModuleScreen extends AbstractTubeModuleScreen { + private int color; + private int threshold; + private WidgetTextFieldNumber thresholdField; + + public SimpleThermostatModuleScreen(ThermostatModule module) { + super(module); + + ySize = 57; + } + + @Override + public void init() { + super.init(); + + color = module.getColorChannel(); + threshold = module.getThreshold(); + + addLabel(getTitle(), guiLeft + xSize / 2, guiTop + 5, WidgetLabel.Alignment.CENTRE); + + int x = guiLeft + 10; + int y = guiTop + 24; + WidgetLabel colorLabel; + addRenderableWidget(colorLabel = new WidgetLabel(x, y, xlate("pneumaticcraft.gui.tubeModule.channel"))); + + x = guiLeft + 10 + colorLabel.getWidth() + 10; + WidgetColorSelector colorSelector = new WidgetColorSelector(x, y-4, w -> color = w.getColor().getId()) + .withInitialColor(DyeColor.byId(color)); + addRenderableWidget(colorSelector); + + if (module.isUpgraded()) { + x = guiLeft + 10 + colorLabel.getWidth() + 10 + colorSelector.getWidth() + 10; + WidgetCheckBox advancedMode = new WidgetCheckBox(x, y, 0xFF404040, Component.literal("Advanced"), b -> { + module.advancedConfig = b.checked; + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + }).setChecked(false); + advancedMode.setTooltip(Tooltip.create(xlate("pneumaticcraft.gui.tubeModule.advancedConfig.tooltip"))); + addRenderableWidget(advancedMode); + } + + x = guiLeft + 10; + y = guiTop + 22 + colorLabel.getHeight() + 10; + WidgetLabel thresholdLabel; + addRenderableWidget(thresholdLabel = new WidgetLabel(x, y, xlate("pneumaticcraft.gui.tubeModule.simpleConfig.threshold"))); + + x = guiLeft + 10 + thresholdLabel.getWidth() + 5; + thresholdField = new WidgetTextFieldNumber(font, x, y-1, 30, font.lineHeight + 2).setDecimals(0); + addRenderableWidget(thresholdField); + thresholdField.setWidth(40); + setInitialFocus(thresholdField); + thresholdField.setValue(threshold); + + x = guiLeft + 10 + thresholdLabel.getWidth() + thresholdField.getWidth() + 10; + addRenderableWidget(new WidgetLabel(x, y, xlate("pneumaticcraft.gui.tubeModule.celsius"))); + } + + @Override + public void tick() { + super.tick(); + if (module.advancedConfig) { + minecraft.setScreen(new ThermostatModuleScreen(module)); + } + } + + @Override + protected ResourceLocation getTexture() { + return Textures.GUI_MODULE_SIMPLE; + } + + @Override + public void removed() { + super.removed(); + + module.setColorChannel(color); + module.setThreshold(thresholdField.getIntValue()); + NetworkHandler.sendToServer(new PacketSyncThermostatModuleToServer(module)); + } +} diff --git a/src/main/java/me/desht/pneumaticcraft/client/gui/tubemodule/ThermostatModuleScreen.java b/src/main/java/me/desht/pneumaticcraft/client/gui/tubemodule/ThermostatModuleScreen.java new file mode 100644 index 000000000..a85d1621d --- /dev/null +++ b/src/main/java/me/desht/pneumaticcraft/client/gui/tubemodule/ThermostatModuleScreen.java @@ -0,0 +1,308 @@ +/* + * This file is part of pnc-repressurized. + * + * pnc-repressurized is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * pnc-repressurized is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pnc-repressurized. If not, see . + */ + +package me.desht.pneumaticcraft.client.gui.tubemodule; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import me.desht.pneumaticcraft.client.gui.widget.WidgetAnimatedStat; +import me.desht.pneumaticcraft.client.gui.widget.WidgetCheckBox; +import me.desht.pneumaticcraft.client.gui.widget.WidgetColorSelector; +import me.desht.pneumaticcraft.client.gui.widget.WidgetLabel; +import me.desht.pneumaticcraft.client.gui.widget.WidgetTooltipArea; +import me.desht.pneumaticcraft.common.network.NetworkHandler; +import me.desht.pneumaticcraft.common.network.PacketSyncThermostatModuleToServer; +import me.desht.pneumaticcraft.common.network.PacketUpdatePressureModule; +import me.desht.pneumaticcraft.common.tubemodules.AbstractTubeModule; +import me.desht.pneumaticcraft.common.tubemodules.ThermostatModule; +import me.desht.pneumaticcraft.common.util.PneumaticCraftUtils; +import me.desht.pneumaticcraft.lib.Textures; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.item.DyeColor; + +import org.joml.Matrix4f; + +import static me.desht.pneumaticcraft.common.util.PneumaticCraftUtils.xlate; + +public class ThermostatModuleScreen extends AbstractTubeModuleScreen { + private int color; + WidgetColorSelector colorSelector; + private EditBox lowerBoundField; + private EditBox higherBoundField; + private int graphLowY; + private int graphHighY; + private int graphLeft; + private int graphRight; + private Rect2i lowerBoundArea, higherBoundArea; + private boolean grabLower, grabHigher; + + public static AbstractTubeModuleScreen createGUI(AbstractTubeModule module) { + return module.isUpgraded() ? new ThermostatModuleScreen((ThermostatModule)module) + : new SimpleThermostatModuleScreen((ThermostatModule)module); + } + + public ThermostatModuleScreen(ThermostatModule module) { + super(module); + ySize = 191; + } + + @Override + public void init() { + super.init(); + + color = module.getColorChannel(); + + addLabel(getTitle(), guiLeft + xSize / 2, guiTop + 5, WidgetLabel.Alignment.CENTRE); + + int xStart = (width - xSize) / 2; + int yStart = (height - ySize) / 2; + int x = guiLeft + 10; + int y = guiTop + 22; + + WidgetLabel colorLabel; + addRenderableWidget(colorLabel = new WidgetLabel(x, y, xlate("pneumaticcraft.gui.tubeModule.channel"))); + + x = guiLeft + 10 + colorLabel.getWidth() + 7; + colorSelector = new WidgetColorSelector(x, y-2, w -> { + color = w.getColor().getId(); + module.setColorChannel(color); + NetworkHandler.sendToServer(new PacketSyncThermostatModuleToServer(module)); + }) + .withInitialColor(DyeColor.byId(color)); + addRenderableWidget(colorSelector); + + x = guiLeft + 10 + colorLabel.getWidth() + 7 + colorSelector.getWidth() + 10; + WidgetCheckBox advancedMode = new WidgetCheckBox(x, y, 0xFF404040, Component.literal("Advanced"), b -> { + module.advancedConfig = b.checked; + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + }).setChecked(true); + advancedMode.setTooltip(Tooltip.create(xlate("pneumaticcraft.gui.tubeModule.advancedConfig.tooltip"))); + addRenderableWidget(advancedMode); + + addLabel(Component.literal("lower"), guiLeft + 15, guiTop + 33); + addLabel(Component.literal("°C"), guiLeft + 60, guiTop + 44); + addLabel(Component.literal("higher"), guiLeft + 140, guiTop + 33); + + addLabel(title, width / 2 - font.width(title) / 2, guiTop + 5); + + lowerBoundField = new EditBox(font, xStart + 15, yStart + 43, 40, 10, + Component.literal(PneumaticCraftUtils.roundNumberTo(module.lowerBound, 0))); + lowerBoundField.setResponder(s -> updateBoundFromTextfield(0)); + addRenderableWidget(lowerBoundField); + + higherBoundField = new EditBox(font, xStart + 130, yStart + 43, 40, 10, + Component.literal(PneumaticCraftUtils.roundNumberTo(module.higherBound, 0))); + higherBoundField.setResponder(s -> updateBoundFromTextfield(1)); + addRenderableWidget(higherBoundField); + + graphLowY = guiTop + 158; + graphHighY = guiTop + 98; + graphLeft = guiLeft + 22; + graphRight = guiLeft + 172; + + addRenderableWidget(new WidgetTooltipArea(graphLeft - 20, graphHighY, 25, graphLowY - graphHighY, xlate("pneumaticcraft.gui.redstone"))); + addRenderableWidget(new WidgetTooltipArea(graphLeft, graphLowY - 5, graphRight - graphLeft, 25, xlate("pneumaticcraft.gui.temperature"))); + + WidgetAnimatedStat stat = new WidgetAnimatedStat(this, xlate("pneumaticcraft.gui.tab.info"), + WidgetAnimatedStat.StatIcon.of(Textures.GUI_INFO_LOCATION), xStart, yStart + 5, 0xFF8888FF, null, true); + stat.setText(xlate("pneumaticcraft.gui.tab.info.tubeModule")); + stat.setBeveled(true); + addRenderableWidget(stat); + + higherBoundArea = new Rect2i(guiLeft + 11, guiTop + 59, 158, 15); + lowerBoundArea = new Rect2i(guiLeft + 11, guiTop + 73, 158, 15); + } + + @Override + protected ResourceLocation getTexture() { + return Textures.GUI_TUBE_MODULE; + } + + @Override + public void drawForeground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + int scrollbarLowerBoundX = temperatureToX((int)module.lowerBound); + int scrollbarHigherBoundX = temperatureToX((int)module.higherBound); + + graphics.blit(getTexture(), scrollbarLowerBoundX, guiTop + 73, 183, 0, 15, 12); + graphics.blit(getTexture(), scrollbarHigherBoundX, guiTop + 59, 183, 0, 15, 12); + + renderGraph(graphics); + + // Update bounds and advancedConfig state + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + // Update channel + NetworkHandler.sendToServer(new PacketSyncThermostatModuleToServer(module)); + + graphics.hLine(graphLeft + 4, graphRight, graphHighY + (graphLowY - graphHighY) * (15 - module.getLevel()) / 15, 0xFFFF0000); + String status = I18n.get("pneumaticcraft.gui.tubeModule.simpleConfig.temperature") + + " " + PneumaticCraftUtils.roundNumberTo(module.getTemperature(), 0) + " °C"; + graphics.drawString(font, status, guiLeft + xSize / 2f - font.width(status) / 2f, guiTop + 175, 0xFF404040, false); + + // the actual graph data + RenderSystem.setShader(GameRenderer::getPositionColorShader); + BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); + bufferBuilder.begin(VertexFormat.Mode.DEBUG_LINE_STRIP, DefaultVertexFormat.POSITION_COLOR); + RenderSystem.enableBlend(); + RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); + Matrix4f posMat = graphics.pose().last().pose(); + float temperatureRange = ThermostatModule.MAX_VALUE - ThermostatModule.MIN_VALUE; + for (int i = 0; i < 16; i++) { + float y = graphHighY + (graphLowY - graphHighY) * (15 - i) / 15f; + float x = graphLeft + (graphRight - graphLeft) * (module.getTemperatureForLevel(i) - ThermostatModule.MIN_VALUE) / temperatureRange; + bufferBuilder.vertex(posMat, x, y, 90f).color(0.25f + i * 0.05f, 0f, 0f, 1.0f).endVertex(); + } + Tesselator.getInstance().end(); + RenderSystem.disableBlend(); + + } + + private void renderGraph(GuiGraphics graphics) { + graphics.vLine(graphLeft, graphHighY, graphLowY, 0xFF303030); + for (int i = 0; i < 16; i++) { + boolean longer = i % 5 == 0; + if (longer) { + String txt = String.valueOf(i); + graphics.drawString(font, txt, graphLeft - 5 - font.width(txt), graphHighY + (graphLowY - graphHighY) * (15 - i) / 15f - 3, 0xFF303030, false); + graphics.hLine(graphLeft + 4, graphRight, graphHighY + (graphLowY - graphHighY) * (15 - i) / 15, i == 0 ? 0xFF303030 : 0x33000000); + + } + graphics.hLine(graphLeft - (longer ? 5 : 3), graphLeft + 3, graphHighY + (graphLowY - graphHighY) * (15 - i) / 15, 0xFF303030); + } + + int[] temps = { ThermostatModule.MIN_VALUE, 0, 1000, ThermostatModule.MAX_VALUE }; + int[] adjusts = { -3, 1, 1, -5 }; + for (int i = 0; i < 4; i++) { + int offset = (int)((temps[i] - ThermostatModule.MIN_VALUE) * (100f / (ThermostatModule.MAX_VALUE - ThermostatModule.MIN_VALUE))); + String txt = String.valueOf(temps[i]); + graphics.drawString(font, txt, graphLeft + (graphRight - graphLeft) * offset / 100f - font.width(txt) / 2f + adjusts[i], + graphLowY + 6, 0xFF303030, false); + graphics.vLine(graphLeft + (graphRight - graphLeft) * offset / 100, graphHighY, graphLowY - 2, 0x33000000); + graphics.vLine(graphLeft + (graphRight - graphLeft) * offset / 100, graphLowY - 5, graphLowY + 3, 0xFF303030); + } + } + + private void updateBoundFromTextfield(int fieldId) { + try { + float prev; + switch (fieldId) { + case 0 -> { + prev = module.lowerBound; + module.lowerBound = Mth.clamp(Integer.parseInt(lowerBoundField.getValue()), + ThermostatModule.MIN_VALUE, ThermostatModule.MAX_VALUE); + if (!Mth.equal(module.lowerBound, prev)) { + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + } + } + case 1 -> { + prev = module.higherBound; + module.higherBound = Mth.clamp(Integer.parseInt(higherBoundField.getValue()), + ThermostatModule.MIN_VALUE, ThermostatModule.MAX_VALUE); + if (!Mth.equal(module.higherBound, prev)) { + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + } + } + default -> throw new IllegalArgumentException("unknown field id " + fieldId); + } + } catch (NumberFormatException ignored) { + } + } + + private int xToTemperature(double mouseX) { + float sliderWidth = 158 - 12; + int sliderLeft = guiLeft + 11; + float temperatureRange = ThermostatModule.MAX_VALUE - ThermostatModule.MIN_VALUE; + float xNormalized = Math.max(0f, Math.min(1f, ((float)mouseX - sliderLeft) / sliderWidth)); + int temperature = (int)(xNormalized * temperatureRange) + ThermostatModule.MIN_VALUE; + return temperature; + } + + private int temperatureToX(int temperature) { + float sliderWidth = 158 - 12; + int sliderLeft = guiLeft + 11; + float temperatureRange = ThermostatModule.MAX_VALUE - ThermostatModule.MIN_VALUE; + float tempNormalized = Math.max(ThermostatModule.MIN_VALUE, + Math.min(ThermostatModule.MAX_VALUE, ((float)temperature - ThermostatModule.MIN_VALUE) / temperatureRange)); + int x = (int)(tempNormalized * sliderWidth) + sliderLeft; + return x; + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + if (!colorSelector.isExpanded() && lowerBoundArea.contains((int)mouseX, (int)mouseY)) { + module.lowerBound = xToTemperature(mouseX - 7); + grabLower = true; + return true; + } else if (!colorSelector.isExpanded() && higherBoundArea.contains((int)mouseX, (int)mouseY)) { + module.higherBound = xToTemperature(mouseX - 7); + grabHigher = true; + return true; + } + return super.mouseClicked(mouseX, mouseY, mouseButton); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int clickedMouseButton, double dx, double dy) { + if (grabLower) { + module.lowerBound = xToTemperature(mouseX - 7); + return true; + } else if (grabHigher) { + module.higherBound = xToTemperature(mouseX - 7); + return true; + } else { + return super.mouseDragged(mouseX, mouseY, clickedMouseButton, dx, dy); + } + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int state) { + if (grabLower) { + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + grabLower = false; + return true; + } else if (grabHigher) { + NetworkHandler.sendToServer(new PacketUpdatePressureModule(module)); + grabHigher = false; + return true; + } else { + return super.mouseReleased(mouseX, mouseY, state); + } + } + + @Override + public void tick() { + super.tick(); + + if (!module.advancedConfig) minecraft.setScreen(new SimpleThermostatModuleScreen(module)); + + if (!lowerBoundField.isFocused()) + lowerBoundField.setValue(PneumaticCraftUtils.roundNumberTo(module.lowerBound, 0)); + if (!higherBoundField.isFocused()) + higherBoundField.setValue(PneumaticCraftUtils.roundNumberTo(module.higherBound, 0)); + } +} diff --git a/src/main/java/me/desht/pneumaticcraft/client/gui/widget/WidgetColorSelector.java b/src/main/java/me/desht/pneumaticcraft/client/gui/widget/WidgetColorSelector.java index 21709a61d..a0f6d8e76 100644 --- a/src/main/java/me/desht/pneumaticcraft/client/gui/widget/WidgetColorSelector.java +++ b/src/main/java/me/desht/pneumaticcraft/client/gui/widget/WidgetColorSelector.java @@ -50,6 +50,10 @@ public WidgetColorSelector withInitialColor(DyeColor color) { return this; } + public boolean isExpanded() { + return expanded; + } + public DyeColor getColor() { return color; } diff --git a/src/main/java/me/desht/pneumaticcraft/client/model/PNCModelLayers.java b/src/main/java/me/desht/pneumaticcraft/client/model/PNCModelLayers.java index 3a9ef0074..d70f9de90 100644 --- a/src/main/java/me/desht/pneumaticcraft/client/model/PNCModelLayers.java +++ b/src/main/java/me/desht/pneumaticcraft/client/model/PNCModelLayers.java @@ -48,6 +48,7 @@ public class PNCModelLayers { public static final ModelLayerLocation REGULATOR_MODULE = register("regulator_module"); public static final ModelLayerLocation SAFETY_VALVE_MODULE = register("safety_valve_module"); public static final ModelLayerLocation VACUUM_MODULE = register("vacuum_module"); + public static final ModelLayerLocation THERMOSTAT_MODULE = register("thermostat_module"); // ===================================================================== diff --git a/src/main/java/me/desht/pneumaticcraft/client/render/tube_module/ThermostatRenderer.java b/src/main/java/me/desht/pneumaticcraft/client/render/tube_module/ThermostatRenderer.java new file mode 100644 index 000000000..39ed92c40 --- /dev/null +++ b/src/main/java/me/desht/pneumaticcraft/client/render/tube_module/ThermostatRenderer.java @@ -0,0 +1,124 @@ +package me.desht.pneumaticcraft.client.render.tube_module; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import me.desht.pneumaticcraft.client.model.PNCModelLayers; +import me.desht.pneumaticcraft.client.util.RenderUtils; +import me.desht.pneumaticcraft.common.tubemodules.ThermostatModule; +import me.desht.pneumaticcraft.lib.Textures; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.CubeListBuilder; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; +import net.minecraft.client.model.geom.builders.PartDefinition; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.item.DyeColor; + +public class ThermostatRenderer extends AbstractTubeModuleRenderer { + private final ModelPart faceplate; + private final ModelPart tubeConnector1; + private final ModelPart tubeConnector2; + private final ModelPart tubeConnector3; + private final ModelPart tubeConnector4; + private final ModelPart tubeConnector5; + private final ModelPart tubeConnector6; + private final ModelPart frame1; + private final ModelPart frame2; + private final ModelPart frame3; + private final ModelPart frame4; + + private static final String FACEPLATE = "faceplate"; + private static final String TUBECONNECTOR1 = "tubeConnector1"; + private static final String TUBECONNECTOR2 = "tubeConnector2"; + private static final String TUBECONNECTOR3 = "tubeConnector3"; + private static final String TUBECONNECTOR4 = "tubeConnector4"; + private static final String TUBECONNECTOR5 = "tubeConnector5"; + private static final String TUBECONNECTOR6 = "tubeConnector6"; + private static final String FRAME1 = "frame1"; + private static final String FRAME2 = "frame2"; + private static final String FRAME3 = "frame3"; + private static final String FRAME4 = "frame4"; + + public ThermostatRenderer(BlockEntityRendererProvider.Context ctx) { + ModelPart root = ctx.bakeLayer(PNCModelLayers.THERMOSTAT_MODULE); + faceplate = root.getChild(FACEPLATE); + tubeConnector1 = root.getChild(TUBECONNECTOR1); + tubeConnector2 = root.getChild(TUBECONNECTOR2); + tubeConnector3 = root.getChild(TUBECONNECTOR3); + tubeConnector4 = root.getChild(TUBECONNECTOR4); + tubeConnector5 = root.getChild(TUBECONNECTOR5); + tubeConnector6 = root.getChild(TUBECONNECTOR6); + frame1 = root.getChild(FRAME1); + frame2 = root.getChild(FRAME2); + frame3 = root.getChild(FRAME3); + frame4 = root.getChild(FRAME4); + } + + public static LayerDefinition createBodyLayer() { + MeshDefinition meshdefinition = new MeshDefinition(); + PartDefinition partdefinition = meshdefinition.getRoot(); + + partdefinition.addOrReplaceChild(FACEPLATE, CubeListBuilder.create().texOffs(12, 0) + .addBox("faceplate_0", 0.0F, 0.0F, -1.0F, 8, 8, 2), + PartPose.offset(-4.0F, 12.0F, 5.0F)); + partdefinition.addOrReplaceChild(TUBECONNECTOR1, CubeListBuilder.create().texOffs(12, 10) + .addBox("tubeConnector1_0", -2.0F, -2.0F, 1.0F, 7, 7, 1), + PartPose.offset(-1.5F, 14.5F, 2.0F)); + partdefinition.addOrReplaceChild(TUBECONNECTOR2, CubeListBuilder.create().texOffs(12, 18) + .addBox("tubeConnector2_0", -1.0F, -1.0F, 0.0F, 5, 5, 1), + PartPose.offset(-1.5F, 14.5F, 2.0F)); + partdefinition.addOrReplaceChild(TUBECONNECTOR3, CubeListBuilder.create().texOffs(28, 12) + .addBox("tubeConnector3_0", 4.0F, 0.0F, 0.0F, 1, 3, 1), + PartPose.offset(-1.5F, 14.5F, 2.0F)); + partdefinition.addOrReplaceChild(TUBECONNECTOR4, CubeListBuilder.create().texOffs(28, 16) + .addBox("tubeConnector4_0", 0.0F, 4.0F, 0.0F, 3, 1, 1), + PartPose.offset(-1.5F, 14.5F, 2.0F)); + partdefinition.addOrReplaceChild(TUBECONNECTOR5, CubeListBuilder.create().texOffs(32, 12) + .addBox("tubeConnector5_0", -2.0F, 0.0F, 0.0F, 1, 3, 1), + PartPose.offset(-1.5F, 14.5F, 2.0F)); + partdefinition.addOrReplaceChild(TUBECONNECTOR6, CubeListBuilder.create().texOffs(28, 10) + .addBox("tubeConnector6_0", 0.0F, -2.0F, 0.0F, 3, 1, 1), + PartPose.offset(-1.5F, 14.5F, 2.0F)); + partdefinition.addOrReplaceChild(FRAME1, CubeListBuilder.create().texOffs(32, 0) + .addBox("frame1_0", 2.0F, 1.5F, -3.75F, 4, 1, 4), + PartPose.offset(-4.0F, 11.5F, 6.0F)); + partdefinition.addOrReplaceChild(FRAME2, CubeListBuilder.create().texOffs(32, 5) + .addBox("frame2_0", 2.0F, -1.5F, -3.75F, 4, 1, 4), + PartPose.offset(-4.0F, 19.5F, 6.0F)); + partdefinition.addOrReplaceChild(FRAME3, CubeListBuilder.create().texOffs(0, 6) + .addBox("frame3_0", -1.5F, 0.5F, -3.75F, 1, 6, 4), + PartPose.offset(3.5F, 12.5F, 6.0F)); + partdefinition.addOrReplaceChild(FRAME4, CubeListBuilder.create().texOffs(0, 16) + .addBox("frame4_0", 1.5F, 0.5F, -3.75F, 1, 6, 4), + PartPose.offset(-4.5F, 12.5F, 6.0F)); + + return LayerDefinition.create(meshdefinition, 64, 32); + } + + + @Override + protected void render(ThermostatModule module, PoseStack matrixStack, VertexConsumer builder, float partialTicks, int combinedLight, int combinedOverlay, float alpha) { + tubeConnector1.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + tubeConnector2.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + tubeConnector3.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + tubeConnector4.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + tubeConnector5.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + tubeConnector6.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + faceplate.render(matrixStack, builder, combinedLight, combinedOverlay, 1f, 1f, 1f, alpha); + + float[] cols = { 1f, 1f, 1f, 1f }; + cols = DyeColor.byId(module.getColorChannel()).getTextureDiffuseColors(); + frame1.render(matrixStack, builder, combinedLight, combinedOverlay, cols[0], cols[1], cols[2], alpha); + frame2.render(matrixStack, builder, combinedLight, combinedOverlay, cols[0], cols[1], cols[2], alpha); + frame3.render(matrixStack, builder, combinedLight, combinedOverlay, cols[0], cols[1], cols[2], alpha); + frame4.render(matrixStack, builder, combinedLight, combinedOverlay, cols[0], cols[1], cols[2], alpha); + } + + @Override + protected ResourceLocation getTexture(boolean isUpgraded) { + return isUpgraded ? Textures.MODEL_THERMOSTAT_MODULE_UPGRADED : Textures.MODEL_THERMOSTAT_MODULE; + } +} diff --git a/src/main/java/me/desht/pneumaticcraft/common/core/ModItems.java b/src/main/java/me/desht/pneumaticcraft/common/core/ModItems.java index 3236a80bf..0540cea53 100644 --- a/src/main/java/me/desht/pneumaticcraft/common/core/ModItems.java +++ b/src/main/java/me/desht/pneumaticcraft/common/core/ModItems.java @@ -215,6 +215,8 @@ public class ModItems { () -> new TubeModuleItem(RedstoneModule::new)); public static final RegistryObject VACUUM_MODULE = register("vacuum_module", () -> new TubeModuleItem(VacuumModule::new)); + public static final RegistryObject THERMOSTAT_MODULE = register("thermostat_module", + () -> new TubeModuleItem(ThermostatModule::new)); public static final RegistryObject OIL_BUCKET = registerBucket("oil_bucket", ModFluids.OIL); diff --git a/src/main/java/me/desht/pneumaticcraft/common/network/NetworkHandler.java b/src/main/java/me/desht/pneumaticcraft/common/network/NetworkHandler.java index 79d91ed4e..a17f0a6d4 100644 --- a/src/main/java/me/desht/pneumaticcraft/common/network/NetworkHandler.java +++ b/src/main/java/me/desht/pneumaticcraft/common/network/NetworkHandler.java @@ -143,6 +143,10 @@ public static void init() { PacketSyncRedstoneModuleToClient::toBytes, PacketSyncRedstoneModuleToClient::new, PacketSyncRedstoneModuleToClient::handle, PLAY_TO_CLIENT); registerMessage(PacketSyncRedstoneModuleToServer.class, PacketSyncRedstoneModuleToServer::toBytes, PacketSyncRedstoneModuleToServer::new, PacketSyncRedstoneModuleToServer::handle, PLAY_TO_SERVER); + registerMessage(PacketSyncThermostatModuleToClient.class, + PacketSyncThermostatModuleToClient::toBytes, PacketSyncThermostatModuleToClient::new, PacketSyncThermostatModuleToClient::handle, PLAY_TO_CLIENT); + registerMessage(PacketSyncThermostatModuleToServer.class, + PacketSyncThermostatModuleToServer::toBytes, PacketSyncThermostatModuleToServer::new, PacketSyncThermostatModuleToServer::handle, PLAY_TO_SERVER); registerMessage(PacketHackingBlockStart.class, PacketHackingBlockStart::toBytes, PacketHackingBlockStart::new, PacketHackingBlockStart::handle); registerMessage(PacketHackingBlockFinish.class, diff --git a/src/main/java/me/desht/pneumaticcraft/common/network/PacketSyncThermostatModuleToClient.java b/src/main/java/me/desht/pneumaticcraft/common/network/PacketSyncThermostatModuleToClient.java new file mode 100644 index 000000000..cb6af0994 --- /dev/null +++ b/src/main/java/me/desht/pneumaticcraft/common/network/PacketSyncThermostatModuleToClient.java @@ -0,0 +1,77 @@ +/* + * This file is part of pnc-repressurized. + * + * pnc-repressurized is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * pnc-repressurized is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pnc-repressurized. If not, see . + */ + +package me.desht.pneumaticcraft.common.network; + +import me.desht.pneumaticcraft.client.util.ClientUtils; +import me.desht.pneumaticcraft.common.block.entity.PressureTubeBlockEntity; +import me.desht.pneumaticcraft.common.tubemodules.ThermostatModule; +import me.desht.pneumaticcraft.common.util.PneumaticCraftUtils; +import net.minecraft.core.Direction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +/** + * Received on: CLIENT + * Sent by server to sync up the settings of a redstone module + */ +public class PacketSyncThermostatModuleToClient extends LocationIntPacket { + private final Direction side; + private final int channel; + private final int level; + private final int temperature; + + public PacketSyncThermostatModuleToClient(ThermostatModule module) { + super(module.getTube().getBlockPos()); + + this.channel = module.getColorChannel(); + this.side = module.getDirection(); + this.level = module.getLevel(); + this.temperature = module.getTemperature(); + } + + PacketSyncThermostatModuleToClient(FriendlyByteBuf buffer) { + super(buffer); + channel = buffer.readByte(); + side = buffer.readEnum(Direction.class); + level = buffer.readInt(); + temperature = buffer.readInt(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + super.toBytes(buf); + buf.writeByte(channel); + buf.writeEnum(side); + buf.writeInt(level); + buf.writeInt(temperature); + } + + public void handle(Supplier ctx) { + ctx.get().enqueueWork(() -> + PneumaticCraftUtils.getTileEntityAt(ClientUtils.getClientLevel(), pos, PressureTubeBlockEntity.class).ifPresent(te -> { + if (te.getModule(side) instanceof ThermostatModule mr) { + mr.setColorChannel(channel); + mr.setLevel(level); + mr.setTemperature(temperature); + } + })); + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/me/desht/pneumaticcraft/common/network/PacketSyncThermostatModuleToServer.java b/src/main/java/me/desht/pneumaticcraft/common/network/PacketSyncThermostatModuleToServer.java new file mode 100644 index 000000000..142db2eab --- /dev/null +++ b/src/main/java/me/desht/pneumaticcraft/common/network/PacketSyncThermostatModuleToServer.java @@ -0,0 +1,78 @@ +/* + * This file is part of pnc-repressurized. + * + * pnc-repressurized is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * pnc-repressurized is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pnc-repressurized. If not, see . + */ + +package me.desht.pneumaticcraft.common.network; + +import me.desht.pneumaticcraft.common.block.entity.PressureTubeBlockEntity; +import me.desht.pneumaticcraft.common.tubemodules.ThermostatModule; +import me.desht.pneumaticcraft.common.util.PneumaticCraftUtils; +import net.minecraft.core.Direction; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Player; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +/** + * Received on: SERVER + * Sent by client to update server-side settings when redstone module GUI is closed + */ +public class PacketSyncThermostatModuleToServer extends LocationIntPacket { + private final Direction side; + private final byte channel; + private final int threshold; + + public PacketSyncThermostatModuleToServer(ThermostatModule module) { + super(module.getTube().getBlockPos()); + + this.side = module.getDirection(); + this.channel = (byte) module.getColorChannel(); + this.threshold = module.getThreshold(); + } + + PacketSyncThermostatModuleToServer(FriendlyByteBuf buffer) { + super(buffer); + side = buffer.readEnum(Direction.class); + channel = buffer.readByte(); + threshold = buffer.readInt(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + super.toBytes(buf); + buf.writeEnum(side); + buf.writeByte(channel); + buf.writeInt(threshold); + } + + public void handle(Supplier ctx) { + ctx.get().enqueueWork(() -> { + Player player = ctx.get().getSender(); + if (PneumaticCraftUtils.canPlayerReach(player, pos)) { + PneumaticCraftUtils.getTileEntityAt(player.level(), pos, PressureTubeBlockEntity.class).ifPresent(tube -> { + if (tube.getModule(side) instanceof ThermostatModule mr) { + mr.setColorChannel(channel); + mr.setThreshold(threshold); + mr.updateNeighbors(); + mr.setUpdate(true); // Force recalc + } + }); + } + }); + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/AbstractTubeModule.java b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/AbstractTubeModule.java index 8881a14e8..dc6cfe25c 100644 --- a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/AbstractTubeModule.java +++ b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/AbstractTubeModule.java @@ -288,4 +288,8 @@ public void onRemoved() { public boolean isInlineAndFocused(PressureTubeBlock.TubeHitInfo hitInfo) { return false; } + + public boolean canConnectTo(AbstractTubeModule other) { + return this.getClass() == other.getClass(); + } } diff --git a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ModuleNetworkManager.java b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ModuleNetworkManager.java index bd05430fe..d297bdb92 100644 --- a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ModuleNetworkManager.java +++ b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ModuleNetworkManager.java @@ -60,7 +60,7 @@ private Set computeConnections(AbstractTubeModule module) { while (!pendingPositions.isEmpty()) { BlockPos pos = pendingPositions.pop(); PneumaticCraftUtils.getTileEntityAt(level, pos, PressureTubeBlockEntity.class).ifPresent(tube -> tube.tubeModules() - .filter(tm -> tm instanceof INetworkedModule && module.getClass() == tm.getClass()) + .filter(tm -> tm instanceof INetworkedModule && tm.canConnectTo(module)) .forEach(modules::add)); for (Direction dir : DirectionUtil.VALUES) { BlockPos pos1 = pos.relative(dir); diff --git a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/RedstoneModule.java b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/RedstoneModule.java index 3816dc8b1..6ce7f810a 100644 --- a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/RedstoneModule.java +++ b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/RedstoneModule.java @@ -23,8 +23,10 @@ import me.desht.pneumaticcraft.common.network.NetworkHandler; import me.desht.pneumaticcraft.common.network.PacketSyncRedstoneModuleToClient; import me.desht.pneumaticcraft.common.thirdparty.ModdedWrenchUtils; +import me.desht.pneumaticcraft.common.tubemodules.ThermostatModule; import me.desht.pneumaticcraft.common.util.ITranslatableEnum; import me.desht.pneumaticcraft.common.util.PneumaticCraftUtils; +import me.desht.pneumaticcraft.lib.Log; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -108,6 +110,10 @@ public void tickServer() { if (mr.getRedstoneDirection() == EnumRedstoneDirection.INPUT && mr.getInputLevel() > levels[mr.getColorChannel()]) levels[mr.getColorChannel()] = (byte) mr.inputLevel; } + if (module instanceof ThermostatModule mr) { + if (mr.getLevel() > levels[mr.getColorChannel()]) + levels[mr.getColorChannel()] = (byte) mr.getLevel(); + } } int out = computeOutputSignal(outputLevel, levels); diff --git a/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ThermostatModule.java b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ThermostatModule.java new file mode 100644 index 000000000..5fba9ab71 --- /dev/null +++ b/src/main/java/me/desht/pneumaticcraft/common/tubemodules/ThermostatModule.java @@ -0,0 +1,240 @@ +/* + * This file is part of pnc-repressurized. + * + * pnc-repressurized is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * pnc-repressurized is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with pnc-repressurized. If not, see . + */ + +package me.desht.pneumaticcraft.common.tubemodules; + +import me.desht.pneumaticcraft.common.block.entity.PressureTubeBlockEntity; +import me.desht.pneumaticcraft.common.config.ConfigHelper; +import me.desht.pneumaticcraft.common.core.ModItems; +import me.desht.pneumaticcraft.common.heat.HeatExchangerManager; +import me.desht.pneumaticcraft.common.heat.HeatUtil; +import me.desht.pneumaticcraft.common.heat.TemperatureData; +import me.desht.pneumaticcraft.common.network.NetworkHandler; +import me.desht.pneumaticcraft.common.network.PacketSyncThermostatModuleToClient; +import me.desht.pneumaticcraft.common.network.PacketSyncThermostatModuleToServer; +import me.desht.pneumaticcraft.common.thirdparty.ModdedWrenchUtils; +import me.desht.pneumaticcraft.common.util.DirectionUtil; +import me.desht.pneumaticcraft.common.util.ITranslatableEnum; +import me.desht.pneumaticcraft.common.util.PneumaticCraftUtils; +import me.desht.pneumaticcraft.lib.Log; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.IntStream; + +public class ThermostatModule extends AbstractTubeModule implements INetworkedModule { + + public static final int MIN_VALUE = -273; + public static final int MAX_VALUE = 2000; + + private int colorChannel; + private int temperature = 0; + private int level; + private int threshold; + private boolean update = true; + + public ThermostatModule(Direction dir, PressureTubeBlockEntity pressureTube) { + super(dir, pressureTube); + lowerBound = ThermostatModule.MIN_VALUE; + higherBound = ThermostatModule.MAX_VALUE; + } + + public int getTemperature() { + return this.temperature; + } + + public int getTemperatureForLevel(int level) { + float temperatureRange = higherBound - lowerBound; + float levelNormalized = (float)level / 15f; + float temperature = levelNormalized * temperatureRange + lowerBound; + return (int)temperature; + } + + public void setTemperature(int temperature) { + this.temperature = temperature; + } + + public int getThreshold() { + return this.threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public int getLevel() { + return this.level; + } + + public void setLevel(int level) { + this.level = level; + } + + public void setUpdate(boolean update) { + this.update = update; + } + + @Override + public boolean hasGui() { + return true; + } + + @Override + public Item getItem() { + return ModItems.THERMOSTAT_MODULE.get(); + } + + @Override + public int getColorChannel() { + return colorChannel; + } + + @Override + public void setColorChannel(int channel) { + this.colorChannel = channel; + setChanged(); + } + + @Override + public double getWidth() { + return 9D; + } + + @Override + protected double getHeight() { + return 5D; + } + + @Override + public void addInfo(List curInfo) { + super.addInfo(curInfo); + if (advancedConfig) { + curInfo.add(PneumaticCraftUtils.xlate("pneumaticcraft.waila.tubeModule.threshold_temp_bounds", + PneumaticCraftUtils.roundNumberTo(lowerBound, 0), PneumaticCraftUtils.roundNumberTo(higherBound, 0))); + } else { + curInfo.add(PneumaticCraftUtils.xlate("pneumaticcraft.waila.tubeModule.threshold_temp", + PneumaticCraftUtils.roundNumberTo(getThreshold(), 1))); + } + curInfo.add(PneumaticCraftUtils.xlate("pneumaticcraft.waila.thermostatModule.temperature", temperature)); + curInfo.add(PneumaticCraftUtils.xlate("pneumaticcraft.waila.thermostatModule.level", level)); + } + + @Override + public boolean onActivated(Player player, InteractionHand hand) { + ItemStack heldStack = player.getItemInHand(hand); + DyeColor dyeColor = DyeColor.getColor(heldStack); + if (dyeColor != null) { + int colorId = dyeColor.getId(); + setColorChannel(colorId); + if (ConfigHelper.common().general.useUpDyesWhenColoring.get() && !player.isCreative()) { + heldStack.shrink(1); + } + return true; + } else { + return super.onActivated(player, hand); + } + } + + @Override + public void onNeighborBlockUpdate() { + updateInputLevel(); + } + + @Override + public void onNeighborTileUpdate() { + updateInputLevel(); + } + + @Override + public boolean canConnectTo(AbstractTubeModule other) { + return true; + } + + @Override + public void tickServer() { + super.tickServer(); + + // Forced recalc when client GUI updated + if (this.update) { + this.update = false; + updateInputLevel(); + } + } + + public void updateInputLevel() { + Level world = Objects.requireNonNull(pressureTube.getLevel()); + BlockPos pos = pressureTube.getBlockPos().relative(getDirection()); + + HeatExchangerManager.getInstance().getLogic(world, pos, null) + .ifPresent(logic -> setTemperature((int)logic.getTemperature() - 273)); + + int level = 0; + if (advancedConfig) { + float temperatureRange = higherBound - lowerBound; + float temperatureNormalized = (temperature - lowerBound) / temperatureRange; + level = (int)(15f * temperatureNormalized); + level = Math.max(0, Math.min(15, level)); + } else { + if (temperature >= threshold) { + level = 15; + } + } + + if (this.level != level) { + this.level = level; + NetworkHandler.sendToAllTracking(new PacketSyncThermostatModuleToClient(this), getTube()); + } + } + + @Override + public CompoundTag writeToNBT(CompoundTag tag) { + super.writeToNBT(tag); + + tag.putByte("channel", (byte) colorChannel); + tag.putByte("level", (byte) level); + tag.putInt("temperature", temperature); + tag.putInt("threshold", threshold); + + return tag; + } + + @Override + public void readFromNBT(CompoundTag tag) { + super.readFromNBT(tag); + + colorChannel = tag.getByte("channel"); + level = tag.getByte("level"); + temperature = tag.getInt("temperature"); + threshold = tag.getInt("threshold"); + } + +} diff --git a/src/main/java/me/desht/pneumaticcraft/datagen/ModRecipeProvider.java b/src/main/java/me/desht/pneumaticcraft/datagen/ModRecipeProvider.java index 878fc9c52..80ce65c8d 100644 --- a/src/main/java/me/desht/pneumaticcraft/datagen/ModRecipeProvider.java +++ b/src/main/java/me/desht/pneumaticcraft/datagen/ModRecipeProvider.java @@ -652,6 +652,14 @@ protected void buildRecipes(Consumer consumer) { 'T', ModBlocks.PRESSURE_TUBE.get() ).save(consumer); + shaped(ModItems.THERMOSTAT_MODULE.get(), ModItems.COMPRESSED_IRON_INGOT.get(), + " M / H /TDT", + 'M', ModItems.MANOMETER.get(), + 'H', ModBlocks.HEAT_PIPE.get(), + 'T', ModBlocks.PRESSURE_TUBE.get(), + 'D', Tags.Items.DUSTS_REDSTONE + ).save(consumer); + shaped(ModBlocks.PRESSURE_TUBE.get(), 8, ModItems.COMPRESSED_IRON_INGOT.get(), "IGI", 'G', Tags.Items.GLASS, diff --git a/src/main/java/me/desht/pneumaticcraft/lib/Textures.java b/src/main/java/me/desht/pneumaticcraft/lib/Textures.java index 8dbc80267..4d7777ef9 100644 --- a/src/main/java/me/desht/pneumaticcraft/lib/Textures.java +++ b/src/main/java/me/desht/pneumaticcraft/lib/Textures.java @@ -62,6 +62,8 @@ public class Textures { public static final ResourceLocation MODEL_REDSTONE_MODULE_UPGRADED = tubeModuleTexture("redstone_upgraded.png"); public static final ResourceLocation MODEL_VACUUM_MODULE = tubeModuleTexture("vacuum.png"); public static final ResourceLocation MODEL_VACUUM_MODULE_UPGRADED = tubeModuleTexture("vacuum_upgraded.png"); + public static final ResourceLocation MODEL_THERMOSTAT_MODULE = tubeModuleTexture("thermostat_module.png"); + public static final ResourceLocation MODEL_THERMOSTAT_MODULE_UPGRADED = tubeModuleTexture("thermostat_module_upgraded.png"); // Progwidget textures public static final ResourceLocation PROG_WIDGET_COMMENT = progWidgetTexture("comment_piece.png"); diff --git a/src/main/resources/assets/pneumaticcraft/lang/en_us.json b/src/main/resources/assets/pneumaticcraft/lang/en_us.json index 7eda27edc..c2a54c9db 100644 --- a/src/main/resources/assets/pneumaticcraft/lang/en_us.json +++ b/src/main/resources/assets/pneumaticcraft/lang/en_us.json @@ -355,6 +355,7 @@ "gui.tooltip.item.pneumaticcraft.programming_puzzle" : "Used by a Programmer when writing programs to a Drone or Network API. Either keep these pieces in your inventory or put them in an inventory adjacent to the Programmer.", "gui.tooltip.item.pneumaticcraft.raw_salmon_tempura" : "Needs some deep frying!", "gui.tooltip.item.pneumaticcraft.redstone_module" : "§bThis module transmits redstone signals to other Redstone Modules on connected tubes. Right-click with any dye to set the channel; right-click with a wrench to toggle between input and output.\n\nUpgrade with a Module Expansion Card for extra signal processing operations.", + "gui.tooltip.item.pneumaticcraft.thermostat_module" : "§bThis module transmits redstone signals to Redstone Modules on connected tubes based on temperature of facing block. Right-click with any dye to set the channel.\n\nUpgrade with a Module Expansion Card for extra signal processing operations.", "gui.tooltip.item.pneumaticcraft.regulator_tube_module" : "§bThis module limits the pressure allowed on its low side based on the redstone signal it receives. No signal = 4.9 bar, a full signal = 0 bar (no air allowed through), and other signal levels are interpolated. Air is always allowed to travel \"backwards\" through the Regulator, making it usable as a one-way valve.\nWhen upgraded with a Module Expansion Card, the threshold can be configured precisely via GUI.", "gui.tooltip.item.pneumaticcraft.reinforced_chest_kit" : "Sneak+Right-click on any wooden chest to upgrade it to a Reinforced Chest, keeping the contents intact.\nThe old chest will be returned to you as an item drop.", "gui.tooltip.item.pneumaticcraft.reinforced_air_canister" : "A stronger version of the basic Air Canister. In conjunction with a couple of Aerial Interfaces, Charging Modules and an Ender Chest, this could have some wireless pressure transfer possibilities...", @@ -496,6 +497,7 @@ "item.pneumaticcraft.radiation_shielding_upgrade" : "Radiation Shielding Upgrade", "item.pneumaticcraft.range_upgrade" : "Range Upgrade", "item.pneumaticcraft.redstone_module" : "Redstone Module", + "item.pneumaticcraft.thermostat_module" : "Thermostat Module", "item.pneumaticcraft.regulator_tube_module" : "Regulator Tube Module", "item.pneumaticcraft.reinforced_air_canister" : "Reinforced Air Canister", "item.pneumaticcraft.reinforced_chest_kit" : "Reinforced Chest Upgrade Kit", @@ -1478,7 +1480,7 @@ "pneumaticcraft.gui.tab.info.smart_chest.push_pull.title" : "Push/Pull Modes", "pneumaticcraft.gui.tab.info.smart_chest.slots" : "§eAlt + Left-Click\n§f• Empty slot: mark slot (and all following slots) closed\n• Empty closed or filtered slot: remove filter\n• Item in slot or on cursor: filter on that item\n• Also hold Shift to set item limit to max\n\n§eAlt + Mouse Wheel\n§f• Filtered slot: adjust item limit\n• Also hold Shift for fast adjust\n\n§eAlt + Cursor Up/Down\n§f• Adjust item limit, like Alt + Mouse Wheel", "pneumaticcraft.gui.tab.info.smart_chest.slots.title" : "Slot Interaction", - "pneumaticcraft.gui.tab.info.tubeModule" : "§0In this interface you can define exactly how a module should behave, dependent on the redstone signal. The signal will be proportional to the pressure interpolated between the lower and higher bounds.", + "pneumaticcraft.gui.tab.info.tubeModule" : "§0In this interface you can define exactly how a module should behave, dependent on the redstone signal. The signal will be proportional to the pressure or temperature interpolated between the lower and higher bounds.", "pneumaticcraft.gui.tab.liquidCompressor.fuel" : "Available Fuels", "pneumaticcraft.gui.tab.liquidHopper.mode.empty" : "Empty out tank.", "pneumaticcraft.gui.tab.liquidHopper.mode.leaveLiquid" : "Leave 1000mB in tank, to filter.", @@ -1711,6 +1713,7 @@ "pneumaticcraft.gui.thermopneumatic.dumpInput" : "Dump Input Tank\n§7Void any unwanted / excess fluids from the input tank", "pneumaticcraft.gui.thermopneumatic.moveInput" : "Move Fluid\n§7Move any fluid from the input tank to the output tank, if possible\n§oHold Shift to dump fluid", "pneumaticcraft.gui.threshold" : "Threshold(bar)", + "pneumaticcraft.gui.temperature" : "Temperature (°C)", "pneumaticcraft.gui.tooltip.aerial_interface.xpDisabled" : "XP handling disabled", "pneumaticcraft.gui.tooltip.air" : "Air: %s mL", "pneumaticcraft.gui.tooltip.airUsage" : "Using: %s mL/t", @@ -1812,6 +1815,8 @@ "pneumaticcraft.gui.tubeModule.simpleConfig.higherThan" : "Higher than", "pneumaticcraft.gui.tubeModule.simpleConfig.lowerThan" : "Lower than", "pneumaticcraft.gui.tubeModule.simpleConfig.threshold" : "Threshold:", + "pneumaticcraft.gui.tubeModule.simpleConfig.temperature" : "Temperature:", + "pneumaticcraft.gui.tubeModule.celsius" : "°C", "pneumaticcraft.gui.tubeModule.simpleConfig.turn" : "Emit when", "pneumaticcraft.gui.universalSensor.desc.block_comparator" : "This sensor setting simulates a Redstone Comparator at the position(s) marked by the GPS (Area) Tool, so the output redstone signal is proportional to the contents of inventories at those positions. If the comparator output would be side dependant, the highest signal will be emitted.\nIn the case of multiple positions, the highest comparator value from any position will be emitted.", "pneumaticcraft.gui.universalSensor.desc.block_heat" : "The block position(s) marked by the GPS (Area) Tool are monitored for heat. When the temperature of the monitored block is higher than the temperature in the text box (in °C) the sensor will emit a redstone level of 15, and 0 if not.\nIf the textfield is empty, the output level is proportional to the temperature, scaling from 0°C (redstone=0) through 400°C (redstone=15).\nIn the case of multiple positions, the position with the highest temperature is used.", @@ -1940,6 +1945,8 @@ "pneumaticcraft.waila.redstoneModule.inverted" : "§cOutput inverted", "pneumaticcraft.waila.redstoneModule.op" : "Operation: §e%s", "pneumaticcraft.waila.redstoneModule.receiving" : "Receiving redstone: §e%d", + "pneumaticcraft.waila.thermostatModule.level" : "Redstone level: §e%d", + "pneumaticcraft.waila.thermostatModule.temperature" : "Temperature: §e%d °C", "pneumaticcraft.waila.tank" : "Tank #%d: %s", "pneumaticcraft.waila.temperature.down" : "Bottom Temperature: ", "pneumaticcraft.waila.temperature.east" : "East Temperature: ", @@ -1951,6 +1958,8 @@ "pneumaticcraft.waila.temperatureGain" : "§7Heat Absorption: §f%d%%", "pneumaticcraft.waila.temperatureLoss" : "§7Heat Extraction: §f%d%%", "pneumaticcraft.waila.tubeModule.threshold" : "Threshold: §e%d bar", + "pneumaticcraft.waila.tubeModule.threshold_temp" : "Threshold: §e%d °C", + "pneumaticcraft.waila.tubeModule.threshold_temp_bounds" : "Threshold bounds: §e%d - §e%d °C", "programmingPuzzle.pneumaticcraft.area.name" : "Area", "programmingPuzzle.pneumaticcraft.block_right_click.name" : "Right Click", "programmingPuzzle.pneumaticcraft.comment.name" : "Comment", diff --git a/src/main/resources/assets/pneumaticcraft/models/item/thermostat_module.json b/src/main/resources/assets/pneumaticcraft/models/item/thermostat_module.json new file mode 100644 index 000000000..4129486e5 --- /dev/null +++ b/src/main/resources/assets/pneumaticcraft/models/item/thermostat_module.json @@ -0,0 +1,243 @@ +{ + "credit": "Made with Blockbench", + "texture_size": [64, 32], + "textures": { + "0": "pneumaticcraft:pnc_model/modules/thermostat_module" + }, + "elements": [ + { + "name": "faceplate", + "from": [4, 0, 8], + "to": [12, 8, 10], + "faces": { + "north": {"uv": [3.5, 1, 5.5, 5], "texture": "#0"}, + "east": {"uv": [3, 1, 3.5, 5], "texture": "#0"}, + "south": {"uv": [6, 1, 8, 5], "texture": "#0"}, + "west": {"uv": [5.5, 1, 6, 5], "texture": "#0"}, + "up": {"uv": [5.5, 1, 3.5, 0], "texture": "#0"}, + "down": {"uv": [7.5, 0, 5.5, 1], "texture": "#0"} + } + }, + { + "name": "tubeConnector1", + "from": [4.5, 0.5, 7], + "to": [11.5, 7.5, 8], + "faces": { + "north": {"uv": [3.25, 5.5, 5, 9], "texture": "#0"}, + "east": {"uv": [3, 5.5, 3.25, 9], "texture": "#0"}, + "south": {"uv": [5.25, 5.5, 7, 9], "texture": "#0"}, + "west": {"uv": [5, 5.5, 5.25, 9], "texture": "#0"}, + "up": {"uv": [5, 5.5, 3.25, 5], "texture": "#0"}, + "down": {"uv": [6.75, 5, 5, 5.5], "texture": "#0"} + } + }, + { + "name": "tubeConnector2", + "from": [5.5, 1.5, 6], + "to": [10.5, 6.5, 7], + "faces": { + "north": {"uv": [3.25, 9.5, 4.5, 12], "texture": "#0"}, + "east": {"uv": [3, 9.5, 3.25, 12], "texture": "#0"}, + "south": {"uv": [4.75, 9.5, 6, 12], "texture": "#0"}, + "west": {"uv": [4.5, 9.5, 4.75, 12], "texture": "#0"}, + "up": {"uv": [4.5, 9.5, 3.25, 9], "texture": "#0"}, + "down": {"uv": [5.75, 9, 4.5, 9.5], "texture": "#0"} + } + }, + { + "name": "tubeConnector3", + "from": [4.5, 2.5, 6], + "to": [5.5, 5.5, 7], + "faces": { + "north": {"uv": [7.25, 6.5, 7.5, 8], "texture": "#0"}, + "east": {"uv": [7, 6.5, 7.25, 8], "texture": "#0"}, + "south": {"uv": [7.75, 6.5, 8, 8], "texture": "#0"}, + "west": {"uv": [7.5, 6.5, 7.75, 8], "texture": "#0"}, + "up": {"uv": [7.5, 6.5, 7.25, 6], "texture": "#0"}, + "down": {"uv": [7.75, 6, 7.5, 6.5], "texture": "#0"} + } + }, + { + "name": "tubeConnector4", + "from": [6.5, 0.5, 6], + "to": [9.5, 1.5, 7], + "faces": { + "north": {"uv": [7.25, 8.5, 8, 9], "texture": "#0"}, + "east": {"uv": [7, 8.5, 7.25, 9], "texture": "#0"}, + "south": {"uv": [8.25, 8.5, 9, 9], "texture": "#0"}, + "west": {"uv": [8, 8.5, 8.25, 9], "texture": "#0"}, + "up": {"uv": [8, 8.5, 7.25, 8], "texture": "#0"}, + "down": {"uv": [8.75, 8, 8, 8.5], "texture": "#0"} + } + }, + { + "name": "tubeConnector5", + "from": [10.5, 2.5, 6], + "to": [11.5, 5.5, 7], + "faces": { + "north": {"uv": [8.25, 6.5, 8.5, 8], "texture": "#0"}, + "east": {"uv": [8, 6.5, 8.25, 8], "texture": "#0"}, + "south": {"uv": [8.75, 6.5, 9, 8], "texture": "#0"}, + "west": {"uv": [8.5, 6.5, 8.75, 8], "texture": "#0"}, + "up": {"uv": [8.5, 6.5, 8.25, 6], "texture": "#0"}, + "down": {"uv": [8.75, 6, 8.5, 6.5], "texture": "#0"} + } + }, + { + "name": "tubeConnector6", + "from": [6.5, 6.5, 6], + "to": [9.5, 7.5, 7], + "faces": { + "north": {"uv": [7.25, 5.5, 8, 6], "texture": "#0"}, + "east": {"uv": [7, 5.5, 7.25, 6], "texture": "#0"}, + "south": {"uv": [8.25, 5.5, 9, 6], "texture": "#0"}, + "west": {"uv": [8, 5.5, 8.25, 6], "texture": "#0"}, + "up": {"uv": [8, 5.5, 7.25, 5], "texture": "#0"}, + "down": {"uv": [8.75, 5, 8, 5.5], "texture": "#0"} + } + }, + { + "name": "frame1", + "from": [6, 6, 6.25], + "to": [10, 7, 10.25], + "faces": { + "north": {"uv": [9, 2, 10, 2.5], "texture": "#0"}, + "east": {"uv": [8, 2, 9, 2.5], "texture": "#0"}, + "south": {"uv": [11, 2, 12, 2.5], "texture": "#0"}, + "west": {"uv": [10, 2, 11, 2.5], "texture": "#0"}, + "up": {"uv": [10, 2, 9, 0], "texture": "#0"}, + "down": {"uv": [11, 0, 10, 2], "texture": "#0"} + } + }, + { + "name": "frame2", + "from": [6, 1, 6.25], + "to": [10, 2, 10.25], + "faces": { + "north": {"uv": [9, 4.5, 10, 5], "texture": "#0"}, + "east": {"uv": [8, 4.5, 9, 5], "texture": "#0"}, + "south": {"uv": [11, 4.5, 12, 5], "texture": "#0"}, + "west": {"uv": [10, 4.5, 11, 5], "texture": "#0"}, + "up": {"uv": [10, 4.5, 9, 2.5], "texture": "#0"}, + "down": {"uv": [11, 2.5, 10, 4.5], "texture": "#0"} + } + }, + { + "name": "frame3", + "from": [5, 1, 6.25], + "to": [6, 7, 10.25], + "faces": { + "north": {"uv": [1, 5, 1.25, 8], "texture": "#0"}, + "east": {"uv": [0, 5, 1, 8], "texture": "#0"}, + "south": {"uv": [2.25, 5, 2.5, 8], "texture": "#0"}, + "west": {"uv": [1.25, 5, 2.25, 8], "texture": "#0"}, + "up": {"uv": [1.25, 5, 1, 3], "texture": "#0"}, + "down": {"uv": [1.5, 3, 1.25, 5], "texture": "#0"} + } + }, + { + "name": "frame4", + "from": [10, 1, 6.25], + "to": [11, 7, 10.25], + "faces": { + "north": {"uv": [1, 10, 1.25, 13], "texture": "#0"}, + "east": {"uv": [0, 10, 1, 13], "texture": "#0"}, + "south": {"uv": [2.25, 10, 2.5, 13], "texture": "#0"}, + "west": {"uv": [1.25, 10, 2.25, 13], "texture": "#0"}, + "up": {"uv": [1.25, 10, 1, 8], "texture": "#0"}, + "down": {"uv": [1.5, 8, 1.25, 10], "texture": "#0"} + } + } + ], + "display": { + "thirdperson_righthand": { + "rotation": [75, -135, 0], + "translation": [-1.25, 1.25, 1.25], + "scale": [0.5, 0.5, 0.5] + }, + "thirdperson_lefthand": { + "rotation": [75, -135, 0], + "translation": [-1.25, 1.25, 1.25], + "scale": [0.5, 0.5, 0.48] + }, + "firstperson_righthand": { + "rotation": [0, -45, 0], + "translation": [0, 3.5, 0.5], + "scale": [0.6, 0.6, 0.6] + }, + "firstperson_lefthand": { + "rotation": [0, -45, 0], + "translation": [0, 3.5, 0.5], + "scale": [0.6, 0.6, 0.6] + }, + "ground": { + "translation": [0, 3, 0], + "scale": [0.5, 0.5, 0.5] + }, + "gui": { + "rotation": [30, 45, 0], + "translation": [0, 4.75, 0], + "scale": [1.25, 1.25, 1.25] + }, + "fixed": { + "translation": [0, 5.25, -2.5] + } + }, + "groups": [ + { + "name": "faceplate", + "origin": [4, 12, 5], + "children": [1] + }, + { + "name": "tubeConnector1", + "origin": [1.5, 9.5, 2], + "children": [2] + }, + { + "name": "tubeConnector2", + "origin": [1.5, 9.5, 2], + "children": [3] + }, + { + "name": "tubeConnector3", + "origin": [1.5, 9.5, 2], + "children": [4] + }, + { + "name": "tubeConnector4", + "origin": [1.5, 9.5, 2], + "children": [5] + }, + { + "name": "tubeConnector5", + "origin": [1.5, 9.5, 2], + "children": [6] + }, + { + "name": "tubeConnector6", + "origin": [1.5, 9.5, 2], + "children": [7] + }, + { + "name": "frame1", + "origin": [4, 12.5, 6], + "children": [8] + }, + { + "name": "frame2", + "origin": [4, 4.5, 6], + "children": [9] + }, + { + "name": "frame3", + "origin": [-3.5, 11.5, 6], + "children": [10, 11] + }, + { + "name": "frame4", + "origin": [4.5, 11.5, 6], + "children": [] + } + ] +} diff --git a/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/thermostat_module.json b/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/thermostat_module.json new file mode 100644 index 000000000..53763d344 --- /dev/null +++ b/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/thermostat_module.json @@ -0,0 +1,30 @@ +{ + "name": "Thermostat Module", + "icon": "pneumaticcraft:thermostat_module", + "category": "pneumaticcraft:tubes", + "advancement": "pneumaticcraft:pressure_tube", + "pages": [ + { + "type": "text", + "text": "This tube module measures the temperature of the block it's facing and emits a redstone signal on configured channel if the temperature is higher than the set threshold." + }, + { + "type": "spotlight", + "item": "pneumaticcraft:module_expansion_card", + "text": "Applying a $(l:tubes/module_expansion_card)Module Expansion Card/$ to the Thermostat Module unlocks the advanced GUI to provide much more control over $(#f00)redstone signal/$ emission. By default, you can provide a threshold level: the signal will be off below that level and fully on above it (or vice versa). If you select the $(bold)Advanced Config/$ toggle in the GUI, you gain even more control (see over)." + }, + { + "type": "image", + "images": [ + "pneumaticcraft:textures/patchouli/thermostat_gui.png" + ], + "text": "$(italic)<0°C = 0 redstone, >1000°C = 15 redstone, 0-1000°C = interpolate (e.g. 200°C = 3 redstone)/$", + "anchor": "img" + }, + { + "type": "crafting", + "text": "Crafting a Thermostat Module", + "recipe": "pneumaticcraft:thermostat_module" + } + ] +} diff --git a/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/tube_modules.json b/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/tube_modules.json index a49c4d969..4e151b57a 100644 --- a/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/tube_modules.json +++ b/src/main/resources/assets/pneumaticcraft/patchouli_books/book/en_us/entries/tubes/tube_modules.json @@ -26,6 +26,7 @@ "pneumaticcraft:tubes/charging_module", "pneumaticcraft:tubes/logistics_module", "pneumaticcraft:tubes/redstone_module", + "pneumaticcraft:tubes/thermostat_module", "pneumaticcraft:tubes/vacuum_module" ] } diff --git a/src/main/resources/assets/pneumaticcraft/textures/patchouli/thermostat_gui.png b/src/main/resources/assets/pneumaticcraft/textures/patchouli/thermostat_gui.png new file mode 100644 index 000000000..ef33a4258 Binary files /dev/null and b/src/main/resources/assets/pneumaticcraft/textures/patchouli/thermostat_gui.png differ diff --git a/src/main/resources/assets/pneumaticcraft/textures/pnc_model/modules/thermostat_module.png b/src/main/resources/assets/pneumaticcraft/textures/pnc_model/modules/thermostat_module.png new file mode 100644 index 000000000..4c174444b Binary files /dev/null and b/src/main/resources/assets/pneumaticcraft/textures/pnc_model/modules/thermostat_module.png differ diff --git a/src/main/resources/assets/pneumaticcraft/textures/pnc_model/modules/thermostat_module_upgraded.png b/src/main/resources/assets/pneumaticcraft/textures/pnc_model/modules/thermostat_module_upgraded.png new file mode 100644 index 000000000..6e16bdafd Binary files /dev/null and b/src/main/resources/assets/pneumaticcraft/textures/pnc_model/modules/thermostat_module_upgraded.png differ