From abfa7cd8251ab85f47790bc24130c299db8d38b6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 20 Jul 2025 02:42:16 +0300 Subject: [PATCH] Add sounds for some electric components Add multimeter tooltip translation Refactor Air intake a bit --- .../resources/assets/tfmg/lang/en_ud.json | 11 +- .../resources/assets/tfmg/lang/en_us.json | 11 +- .../content/electricity/base/IElectric.java | 10 +- .../generators/GeneratorBlockEntity.java | 21 + .../large_generator/RotorBlockEntity.java | 27 +- .../ElectricSwitchBlockEntity.java | 11 +- .../transformer/TransformerBlockEntity.java | 5 + .../misc/air_intake/AirIntakeBlockEntity.java | 570 +++++++----------- .../tfmg/registry/TFMGSoundEvents.java | 39 +- .../assets/tfmg/lang/default/interface.json | 12 +- .../resources/assets/tfmg/lang/ru_ru.json | 49 +- src/main/resources/assets/tfmg/sounds.json | 42 +- .../assets/tfmg/sounds/electric_hum.ogg | Bin 0 -> 8050 bytes .../assets/tfmg/sounds/generator_hum.ogg | Bin 0 -> 6017 bytes .../assets/tfmg/sounds/switch_off.ogg | Bin 0 -> 13362 bytes .../assets/tfmg/sounds/switch_on.ogg | Bin 0 -> 18327 bytes 16 files changed, 409 insertions(+), 399 deletions(-) create mode 100644 src/main/resources/assets/tfmg/sounds/electric_hum.ogg create mode 100644 src/main/resources/assets/tfmg/sounds/generator_hum.ogg create mode 100644 src/main/resources/assets/tfmg/sounds/switch_off.ogg create mode 100644 src/main/resources/assets/tfmg/sounds/switch_on.ogg diff --git a/src/generated/resources/assets/tfmg/lang/en_ud.json b/src/generated/resources/assets/tfmg/lang/en_ud.json index e95d32de..0f4b8360 100644 --- a/src/generated/resources/assets/tfmg/lang/en_ud.json +++ b/src/generated/resources/assets/tfmg/lang/en_ud.json @@ -606,6 +606,9 @@ "create.multimeter.energy_usage": " :ǝbɐs∩ ʎbɹǝuƎ ", "create.multimeter.group": " :dnoɹ⅁ ", "create.multimeter.header": ":ɐʇɐᗡ ɹǝʇǝɯıʇןnW", + "create.multimeter.network_power_consumption": " :uoıʇdɯnsuoɔ ɹǝʍod ʞɹoʍʇǝN", + "create.multimeter.network_power_generation": " :uoıʇɐɹǝuǝb ɹǝʍod ʞɹoʍʇǝN", + "create.multimeter.not_enough_power": "¡ᴚƎMOԀ H⅁∩ONƎ ⟘ON", "create.multimeter.power_generated": " :pǝʇɐɹǝuǝ⅁ ɹǝʍoԀ ", "create.multimeter.power_percentage": " :ɥʇbuǝɹʇS pıɹ⅁ ", "create.multimeter.power_usage": " :ǝbɐs∩ ɹǝʍoԀ ", @@ -1078,6 +1081,10 @@ "tfmg.ponder.tag.metallurgy.description": "ןɐʇǝɯ buıssǝɔoɹd oʇ pǝʇɐןǝɹ sʞɔoןᗺ", "tfmg.ponder.tag.oil_processing": "ʎɹǝuıɥɔɐW buıssǝɔoɹԀ ןıO", "tfmg.ponder.tag.oil_processing.description": "ןıo buıuıɯ puɐ buıuıɟǝɹ ɹoɟ pǝsn ʞɔoןᗺ", - "tfmg.subtitle.diesel_engine_sounds": "spunoS ǝuıbuƎ ןǝsǝıᗡ", - "tfmg.subtitle.engine_sounds": "spunoS ǝuıbuƎ" + "tfmg.subtitle.diesel_engine_sounds": "spunos ǝuıbuƎ ןǝsǝıᗡ", + "tfmg.subtitle.electric_hum": "ɯnɥ ɔıɹʇɔǝןƎ", + "tfmg.subtitle.engine_sounds": "spunos ǝuıbuƎ", + "tfmg.subtitle.generator_hum": "ɯnɥ ɹoʇɐɹǝuǝ⅁", + "tfmg.subtitle.switch_off": "buıuǝdo ɥɔʇıʍS", + "tfmg.subtitle.switch_on": "buısoןɔ ɥɔʇıʍS" } \ No newline at end of file diff --git a/src/generated/resources/assets/tfmg/lang/en_us.json b/src/generated/resources/assets/tfmg/lang/en_us.json index 5faefbe2..3b1cec23 100644 --- a/src/generated/resources/assets/tfmg/lang/en_us.json +++ b/src/generated/resources/assets/tfmg/lang/en_us.json @@ -606,6 +606,9 @@ "create.multimeter.energy_usage": " Energy Usage: ", "create.multimeter.group": " Group: ", "create.multimeter.header": "Multimeter Data:", + "create.multimeter.network_power_consumption": "Network power consumption: ", + "create.multimeter.network_power_generation": "Network power generation: ", + "create.multimeter.not_enough_power": "NOT ENOUGH POWER!", "create.multimeter.power_generated": " Power Generated: ", "create.multimeter.power_percentage": " Grid Strength: ", "create.multimeter.power_usage": " Power Usage: ", @@ -1078,6 +1081,10 @@ "tfmg.ponder.tag.metallurgy.description": "Blocks related to processing metal", "tfmg.ponder.tag.oil_processing": "Oil Processing Machinery", "tfmg.ponder.tag.oil_processing.description": "Block used for refining and mining oil", - "tfmg.subtitle.diesel_engine_sounds": "Diesel Engine Sounds", - "tfmg.subtitle.engine_sounds": "Engine Sounds" + "tfmg.subtitle.diesel_engine_sounds": "Diesel Engine sounds", + "tfmg.subtitle.electric_hum": "Electric hum", + "tfmg.subtitle.engine_sounds": "Engine sounds", + "tfmg.subtitle.generator_hum": "Generator hum", + "tfmg.subtitle.switch_off": "Switch opening", + "tfmg.subtitle.switch_on": "Switch closing" } \ No newline at end of file diff --git a/src/main/java/com/drmangotea/tfmg/content/electricity/base/IElectric.java b/src/main/java/com/drmangotea/tfmg/content/electricity/base/IElectric.java index fee7f828..c137429d 100644 --- a/src/main/java/com/drmangotea/tfmg/content/electricity/base/IElectric.java +++ b/src/main/java/com/drmangotea/tfmg/content/electricity/base/IElectric.java @@ -133,7 +133,7 @@ public interface IElectric { .forGoggles(tooltip); if (getData().notEnoughtPower) { - CreateLang.text("NOT ENOUGHT POWER") + CreateLang.translate("multimeter.not_enough_power") .color(Color.RED) .forGoggles(tooltip, 1); @@ -186,11 +186,13 @@ public interface IElectric { CreateLang.text("----------------------------") .style(ChatFormatting.WHITE) .forGoggles(tooltip); - CreateLang.text("Network Power Generation: " + TFMGUtils.formatUnits(getNetworkPowerGeneration(), "W")) + CreateLang.translate("multimeter.network_power_generation") + .add(Component.literal(TFMGUtils.formatUnits(getNetworkPowerGeneration(), "W"))) .color(0xcc4b74) .forGoggles(tooltip, 1); - CreateLang.text("Network Power Consumption: " + TFMGUtils.formatUnits(getNetworkPowerUsage(), "W")) + CreateLang.translate("multimeter.network_power_consumption") + .add(Component.literal(TFMGUtils.formatUnits(getNetworkPowerUsage(), "W"))) .color(0xcc4b74) .forGoggles(tooltip, 1); @@ -325,4 +327,4 @@ public interface IElectric { } -} \ No newline at end of file +} diff --git a/src/main/java/com/drmangotea/tfmg/content/electricity/generators/GeneratorBlockEntity.java b/src/main/java/com/drmangotea/tfmg/content/electricity/generators/GeneratorBlockEntity.java index 918f4219..3cf5c6c4 100644 --- a/src/main/java/com/drmangotea/tfmg/content/electricity/generators/GeneratorBlockEntity.java +++ b/src/main/java/com/drmangotea/tfmg/content/electricity/generators/GeneratorBlockEntity.java @@ -3,6 +3,7 @@ package com.drmangotea.tfmg.content.electricity.generators; import com.drmangotea.tfmg.TFMG; import com.drmangotea.tfmg.config.TFMGConfigs; import com.drmangotea.tfmg.content.electricity.base.KineticElectricBlockEntity; +import com.drmangotea.tfmg.registry.TFMGSoundEvents; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -32,6 +33,26 @@ public class GeneratorBlockEntity extends KineticElectricBlockEntity { updateNetwork(); data.updateNextTick = false; } + + if (level.isClientSide()) { + float speed = Math.abs(getSpeed()); + float minSpeed = TFMGConfigs.common().machines.largeGeneratorMinSpeed.getF(); + + // Only play sound if above minimum speed + if (speed > minSpeed) { + float maxSpeed = 255f; // Max expected speed + // Normalize speed between 0-1 range (clamped) + float normalizedSpeed = Math.min(1.0f, (speed - minSpeed) / (maxSpeed - minSpeed)); + + // Volume scales from 0.1 to 0.5 with speed + float volume = 0.1f + (0.4f * normalizedSpeed); + + // Pitch scales from 0.8 to 1.2 with speed (Java clamps below 0.5) + float pitch = 0.8f + (0.4f * normalizedSpeed); + + TFMGSoundEvents.GENERATOR_HUM.playAt(level, worldPosition, volume, pitch, false); + } + } } @Override diff --git a/src/main/java/com/drmangotea/tfmg/content/electricity/generators/large_generator/RotorBlockEntity.java b/src/main/java/com/drmangotea/tfmg/content/electricity/generators/large_generator/RotorBlockEntity.java index 03fc9379..68ec47f4 100644 --- a/src/main/java/com/drmangotea/tfmg/content/electricity/generators/large_generator/RotorBlockEntity.java +++ b/src/main/java/com/drmangotea/tfmg/content/electricity/generators/large_generator/RotorBlockEntity.java @@ -5,6 +5,7 @@ import com.drmangotea.tfmg.config.TFMGConfigs; import com.drmangotea.tfmg.content.electricity.base.KineticElectricBlockEntity; import com.drmangotea.tfmg.content.electricity.generators.large_generator.StatorBlock.StatorState; import com.drmangotea.tfmg.registry.TFMGBlocks; +import com.drmangotea.tfmg.registry.TFMGSoundEvents; import net.createmod.catnip.animation.LerpedFloat; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -57,6 +58,26 @@ public class RotorBlockEntity extends KineticElectricBlockEntity { findStators(); findNextTick = false; } + + if (level.isClientSide()) { + float speed = Math.abs(visualSpeed.getValue()); + float minSpeed = TFMGConfigs.common().machines.largeGeneratorMinSpeed.getF(); + + // Only play sound if above minimum speed + if (speed > minSpeed) { + float maxSpeed = 255f; // Max expected speed + // Normalize speed between 0-1 range (clamped) + float normalizedSpeed = Math.min(1.0f, (speed - minSpeed) / (maxSpeed - minSpeed)); + + // Volume scales from 0.1 to 1.0 with speed + float volume = 0.1f + (0.9f * normalizedSpeed); + + // Pitch scales from 0.5 to 1.0 with speed (Java clamps below 0.5) + float pitch = 0.5f + (0.5f * normalizedSpeed); + + TFMGSoundEvents.GENERATOR_HUM.playAt(level, worldPosition, volume, pitch, false); + } + } } @Override @@ -84,9 +105,9 @@ public class RotorBlockEntity extends KineticElectricBlockEntity { return 0; float modifier = TFMGConfigs.common().machines.largeGeneratorModifier.getF(); - float maxSpeed = TFMGConfigs.common().machines.largeGeneratorMinSpeed.getF(); + float minSpeed = TFMGConfigs.common().machines.largeGeneratorMinSpeed.getF(); - return (int) Math.max(0, ((Math.abs(getSpeed()) - maxSpeed) * modifier)); + return (int) Math.max(0, ((Math.abs(getSpeed()) - minSpeed) * modifier)); } @Override @@ -202,4 +223,4 @@ public class RotorBlockEntity extends KineticElectricBlockEntity { } -} \ No newline at end of file +} diff --git a/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/electric_switch/ElectricSwitchBlockEntity.java b/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/electric_switch/ElectricSwitchBlockEntity.java index 6b78fb13..ce0a23b8 100644 --- a/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/electric_switch/ElectricSwitchBlockEntity.java +++ b/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/electric_switch/ElectricSwitchBlockEntity.java @@ -1,7 +1,8 @@ package com.drmangotea.tfmg.content.electricity.utilities.electric_switch; -import com.drmangotea.tfmg.content.electricity.base.IElectric; import com.drmangotea.tfmg.content.electricity.utilities.diode.ElectricDiodeBlockEntity; +import com.drmangotea.tfmg.registry.TFMGSoundEvents; +import com.simibubi.create.Create; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -42,6 +43,14 @@ public class ElectricSwitchBlockEntity extends ElectricDiodeBlockEntity { updateInFrontNextTick(); updateNextTick(); + if (level.isClientSide()) { + + if (signal == 0) + TFMGSoundEvents.SWITCH_OFF.playAt(level, worldPosition, 0.8f + Create.RANDOM.nextFloat() * 0.4f, 0.8f + Create.RANDOM.nextFloat() * 0.4f, false); + else + TFMGSoundEvents.SWITCH_ON.playAt(level, worldPosition, 0.8f + Create.RANDOM.nextFloat() * 0.4f, 0.8f + Create.RANDOM.nextFloat() * 0.4f, false); + } + } public void neighbourChanged() { diff --git a/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/transformer/TransformerBlockEntity.java b/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/transformer/TransformerBlockEntity.java index 52941143..a56c5c44 100644 --- a/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/transformer/TransformerBlockEntity.java +++ b/src/main/java/com/drmangotea/tfmg/content/electricity/utilities/transformer/TransformerBlockEntity.java @@ -2,11 +2,13 @@ package com.drmangotea.tfmg.content.electricity.utilities.transformer; import com.drmangotea.tfmg.TFMG; import com.drmangotea.tfmg.base.blocks.TFMGHorizontalDirectionalBlock; +import com.drmangotea.tfmg.config.TFMGConfigs; import com.drmangotea.tfmg.content.electricity.base.IElectric; import com.drmangotea.tfmg.content.electricity.base.UpdateInFrontPacket; import com.drmangotea.tfmg.content.electricity.base.VoltageAlteringBlockEntity; import com.drmangotea.tfmg.registry.TFMGPackets; +import com.drmangotea.tfmg.registry.TFMGSoundEvents; import com.simibubi.create.foundation.utility.CreateLang; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; @@ -63,6 +65,9 @@ public class TransformerBlockEntity extends VoltageAlteringBlockEntity { updateInFront(); updateInFront = false; } + + if (this.getPowerUsage() > 0 && this.level.isClientSide()) + TFMGSoundEvents.ELECTRIC_HUM.playAt(level, worldPosition, 1.0f, 1.0f, false); } @Override diff --git a/src/main/java/com/drmangotea/tfmg/content/machinery/misc/air_intake/AirIntakeBlockEntity.java b/src/main/java/com/drmangotea/tfmg/content/machinery/misc/air_intake/AirIntakeBlockEntity.java index b61baa45..4930a269 100644 --- a/src/main/java/com/drmangotea/tfmg/content/machinery/misc/air_intake/AirIntakeBlockEntity.java +++ b/src/main/java/com/drmangotea/tfmg/content/machinery/misc/air_intake/AirIntakeBlockEntity.java @@ -4,8 +4,6 @@ import com.drmangotea.tfmg.registry.TFMGFluids; import com.simibubi.create.content.equipment.wrench.IWrenchable; import com.simibubi.create.content.kinetics.base.KineticBlockEntity; import com.simibubi.create.foundation.fluid.SmartFluidTank; - - import com.simibubi.create.foundation.utility.CreateLang; import net.createmod.catnip.animation.LerpedFloat; import net.createmod.catnip.lang.LangBuilder; @@ -35,441 +33,336 @@ import static com.drmangotea.tfmg.content.machinery.misc.air_intake.AirIntakeBlo import static com.simibubi.create.content.kinetics.base.DirectionalKineticBlock.FACING; public class AirIntakeBlockEntity extends KineticBlockEntity implements IWrenchable { + private static final int SMALL_FAN_DIAMETER = 1; + private static final int MEDIUM_FAN_DIAMETER = 2; + private static final int LARGE_FAN_DIAMETER = 3; - int diameter = 1; - - boolean isController=false; - - public boolean hasShaft=true; - + int diameter = SMALL_FAN_DIAMETER; + boolean isController = false; + public boolean hasShaft = true; boolean isUsedByController = false; - public BlockPos controller; - public List blockEntities = new ArrayList<>(); - - public float maxShaftSpeed =0; - + public float maxShaftSpeed = 0; public float angle = 0; public LerpedFloat visual_angle = LerpedFloat.angular(); protected FluidTank tankInventory; protected LazyOptional fluidCapability; - public AirIntakeBlockEntity(BlockEntityType typeIn, BlockPos pos, BlockState state) { super(typeIn, pos, state); tankInventory = createInventory(); fluidCapability = LazyOptional.of(() -> tankInventory); - - } - public void tick(){ + public void tick() { super.tick(); - //if(!level.isClientSide) { - int production = ((int) maxShaftSpeed * ((diameter * diameter))) / 40; - if (tankInventory.getFluidAmount() + production <= tankInventory.getCapacity()) { - //tankInventory.fill(new FluidStack(TFMGFluids.AIR.getSource(), production), IFluidHandler.FluidAction.EXECUTE); - tankInventory.setFluid(new FluidStack(TFMGFluids.AIR.getSource(), production + tankInventory.getFluidAmount())); - // if(controller!=null) { - // ((AirIntakeBlockEntity) level.getBlockEntity(controller)).setChanged(); - // ((AirIntakeBlockEntity) level.getBlockEntity(controller)).sendData(); - // } - } - // } - //////////////// + produceAir(); + updateVisuals(); + updateBlockState(); + validateController(); + updateShaftSpeed(); + validateMultiblock(); + } - if(isUsedByController) { + private void produceAir() { + int production = ((int) maxShaftSpeed * (diameter * diameter)) / 40; + if (tankInventory.getFluidAmount() + production <= tankInventory.getCapacity()) { + tankInventory.setFluid(new FluidStack(TFMGFluids.AIR.getSource(), production + tankInventory.getFluidAmount())); + } + + if (isUsedByController) { refreshCapability(); sendData(); setChanged(); } + } - - if(diameter == 3){ + private void updateVisuals() { + if (diameter == LARGE_FAN_DIAMETER) { visual_angle.chase(angle, 0.1f, LerpedFloat.Chaser.EXP); visual_angle.tickChaser(); } - - angle+=maxShaftSpeed/2; - - + angle += maxShaftSpeed / 2; angle %= 360; + } - - if(isUsedByController) - blockEntities.clear(); - - - - if(!this.getBlockState().getValue(INVISIBLE)){ - if(isController||isUsedByController){ - level.setBlock(this.getBlockPos(),this.getBlockState().setValue(INVISIBLE,true),2); + private void updateBlockState() { + if (!this.getBlockState().getValue(INVISIBLE)) { + if (isController || isUsedByController) { + level.setBlock(this.getBlockPos(), this.getBlockState().setValue(INVISIBLE, true), 2); } - - } - if(!isController&&!isUsedByController) - level.setBlock(this.getBlockPos(),this.getBlockState().setValue(INVISIBLE,false),2); + if (!isController && !isUsedByController) { + level.setBlock(this.getBlockPos(), this.getBlockState().setValue(INVISIBLE, false), 2); + } + } - if(controller == null) - controller = this.getBlockPos(); - - diameter =getPossibleDiameter(); - - if(controller == this.getBlockPos()) { + private void validateController() { + if (controller == null) controller = this.getBlockPos(); + diameter = getPossibleDiameter(); + if (controller == this.getBlockPos()) { isUsedByController = false; } else { isUsedByController = true; isController = false; } - if(diameter ==1) { + if (diameter == SMALL_FAN_DIAMETER) { isController = false; - } - if(!(level.getBlockEntity(controller) instanceof AirIntakeBlockEntity)) { + if (!(level.getBlockEntity(controller) instanceof AirIntakeBlockEntity)) { isUsedByController = false; controller = this.getBlockPos(); - - } else { - - if(!(((AirIntakeBlockEntity) level.getBlockEntity(controller)).isController)) - isUsedByController = false; + } else if (!(((AirIntakeBlockEntity) level.getBlockEntity(controller)).isController)) { + isUsedByController = false; } - //else - // if(!(((AirIntakeBlockEntity) level.getBlockEntity(controller)).isController)) - // controller = this.getBlockPos(); - if(controller!=null) { - if(level.getBlockEntity(controller)!=null) - if(((AirIntakeBlockEntity)level.getBlockEntity(controller)).diameter==2) { - int x = Math.abs(this.getBlockPos().getX() - controller.getX()); - int y = Math.abs(this.getBlockPos().getY() - controller.getY()); - int z = Math.abs(this.getBlockPos().getZ() - controller.getZ()); + validateControllerDistance(); + } - int distanceFromController = x + y + z; - if (x > 1 || y > 1 || z > 1) { - isUsedByController = false; - controller = this.getBlockPos(); - } - } - if(level.getBlockEntity(controller)!=null) - if(((AirIntakeBlockEntity)level.getBlockEntity(controller)).diameter==1) { + private void validateControllerDistance() { + if (controller == null || level.getBlockEntity(controller) == null) return; + + AirIntakeBlockEntity controllerBE = (AirIntakeBlockEntity) level.getBlockEntity(controller); + if (controllerBE.diameter == MEDIUM_FAN_DIAMETER) { + int x = Math.abs(this.getBlockPos().getX() - controller.getX()); + int y = Math.abs(this.getBlockPos().getY() - controller.getY()); + int z = Math.abs(this.getBlockPos().getZ() - controller.getZ()); + + if (x > 1 || y > 1 || z > 1) { isUsedByController = false; controller = this.getBlockPos(); } - + } else if (controllerBE.diameter == SMALL_FAN_DIAMETER) { + isUsedByController = false; + controller = this.getBlockPos(); } - - - - //////////////////////// - - if(diameter == 1){ - maxShaftSpeed = Math.abs(getSpeed()); - - }else { - maxShaftSpeed = Math.abs(getSpeed()); - List speeds = new ArrayList<>(); -// - for (AirIntakeBlockEntity be : blockEntities) { - speeds.add(Math.abs(be.getSpeed())); -// - } -// - for(float testedSpeed : speeds){ - if(testedSpeed> maxShaftSpeed) - maxShaftSpeed = testedSpeed; - } - // maxShaftSpeed = getSpeed(); - } - - - - if(isUsedByController) - return; - - if(diameter ==2){ - - if(blockEntities.toArray().length!=4) - return; - } - if(diameter ==3){ - if(blockEntities.toArray().length!=9) - return; - } - - - - } + + private void updateShaftSpeed() { + maxShaftSpeed = Math.abs(getSpeed()); + + if (diameter > SMALL_FAN_DIAMETER) { + for (AirIntakeBlockEntity be : blockEntities) { + float testedSpeed = Math.abs(be.getSpeed()); + if (testedSpeed > maxShaftSpeed) { + maxShaftSpeed = testedSpeed; + } + } + } + } + + private void validateMultiblock() { + if (isUsedByController) return; + + if ((diameter == MEDIUM_FAN_DIAMETER && blockEntities.size() != MEDIUM_FAN_DIAMETER * MEDIUM_FAN_DIAMETER) + || (diameter == LARGE_FAN_DIAMETER && blockEntities.size() != LARGE_FAN_DIAMETER * LARGE_FAN_DIAMETER)) return; + } + @Override public void invalidate() { super.invalidate(); - fluidCapability.invalidate(); } - public InteractionResult onWrenched(BlockState state, UseOnContext context){ + public InteractionResult onWrenched(BlockState state, UseOnContext context) { Direction direction = context.getClickedFace(); - - if(direction == getBlockState().getValue(FACING).getOpposite()) { + if (direction == getBlockState().getValue(FACING).getOpposite()) { hasShaft = !hasShaft; } return InteractionResult.SUCCESS; } public void setController(BlockPos controllerPos) { - // isUsedByController = true; - controller = controllerPos; - + controller = controllerPos; } - @Nonnull @Override @SuppressWarnings("removal") public LazyOptional getCapability(@Nonnull Capability cap, Direction side) { - - - if (!fluidCapability.isPresent()) { refreshCapability(); sendData(); setChanged(); } - - - if (cap == ForgeCapabilities.FLUID_HANDLER) - return fluidCapability.cast(); + if (cap == ForgeCapabilities.FLUID_HANDLER) return fluidCapability.cast(); return super.getCapability(cap, side); } private void refreshCapability() { - IFluidHandler handlerForCapability; - if (controller == null || controller == this.getBlockPos() - - ) { + if (controller == null || controller == this.getBlockPos()) { handlerForCapability = tankInventory; - } else - if(((AirIntakeBlockEntity) level.getBlockEntity(controller))!=null) { + } else if (level.getBlockEntity(controller) != null) { handlerForCapability = ((AirIntakeBlockEntity) level.getBlockEntity(controller)).tankInventory; - }else handlerForCapability = tankInventory; + } else { + handlerForCapability = tankInventory; + } - - LazyOptional oldCap = fluidCapability; IFluidHandler finalHandlerForCapability = handlerForCapability; fluidCapability = LazyOptional.of(() -> finalHandlerForCapability); - //oldCap.invalidate(); } + public int getPossibleDiameter() { + if (controller != this.getBlockPos()) return SMALL_FAN_DIAMETER; + boolean canBeMedium = checkMediumDiameter(); + boolean canBeLarge = checkLargeDiameter(); - - - public int getPossibleDiameter(){ - - if(controller !=this.getBlockPos()) - return 1; - - - - BlockPos checkedPos = this.getBlockPos(); - Direction direction = this.getBlockState().getValue(FACING); - - - - List checkedPosses = new ArrayList<>(); - checkedPos = this.getBlockPos(); - - boolean canBeMedium = true; - for(int x = 0;x < 2; x++){ - for(int z = 0;z < 2; z++){ - checkedPosses.add(checkedPos); - if(direction.getAxis().isHorizontal()) { - checkedPos = checkedPos.above(); - }else checkedPos = checkedPos.east(); - } - if(direction.getAxis().isHorizontal()) { - checkedPos = checkedPos.below(2); - checkedPos = checkedPos.relative(direction.getClockWise()); - } else { - checkedPos = checkedPos.west(2); - checkedPos = checkedPos.south(); - - } + if (canBeLarge) { + setupMultiblock(LARGE_FAN_DIAMETER); + return LARGE_FAN_DIAMETER; } - List checkedPossesLarge = new ArrayList<>(); - checkedPos = this.getBlockPos(); - - boolean canBeLarge = true; - for(int x = 0;x < 3; x++){ - for(int z = 0;z < 3; z++){ - checkedPossesLarge.add(checkedPos); - if(direction.getAxis().isHorizontal()) { - checkedPos = checkedPos.above(); - }else checkedPos = checkedPos.east(); - } - if(direction.getAxis().isHorizontal()) { - checkedPos = checkedPos.below(3); - checkedPos = checkedPos.relative(direction.getClockWise()); - } else { - checkedPos = checkedPos.west(3); - checkedPos = checkedPos.south(); - - } - } - //LARGE - for(BlockPos pos : checkedPossesLarge){ - if(!(level.getBlockEntity(pos) instanceof AirIntakeBlockEntity)) { - canBeLarge = false; - break; - } - - // ((AirIntakeBlockEntity) level.getBlockEntity(pos)).controller = this.getBlockPos(); - AirIntakeBlockEntity checkedBE = (AirIntakeBlockEntity) level.getBlockEntity(pos); - - //if(checkedBE.diameter<3) - // ((AirIntakeBlockEntity) level.getBlockEntity(pos)).isController = false; - - // if(pos!=this.getBlockPos()) - // if(checkedBE.isController) { -// -// - // canBeLarge = false; - // break; - // } - - - - if(checkedBE.getBlockState().getValue(FACING) != this.getBlockState().getValue(FACING)) { - canBeLarge = false; - break; - } - - //if(pos!=this.getBlockPos()) - // ((AirIntakeBlockEntity) level.getBlockEntity(pos)).isUsedByController = true; - - - } - //MEDIUM - for(BlockPos pos : checkedPosses){ - if(!(level.getBlockEntity(pos) instanceof AirIntakeBlockEntity)) { - canBeMedium = false; - break; - } - - // ((AirIntakeBlockEntity) level.getBlockEntity(pos)).controller = this.getBlockPos(); - AirIntakeBlockEntity checkedBE = (AirIntakeBlockEntity) level.getBlockEntity(pos); - - if(pos!=this.getBlockPos()) - if(checkedBE.isController) { - canBeMedium = false; - break; - } - - - if(checkedBE.getBlockState().getValue(FACING) != this.getBlockState().getValue(FACING)) { - canBeMedium = false; - break; - } - - //if(pos!=this.getBlockPos()) - // ((AirIntakeBlockEntity) level.getBlockEntity(pos)).isUsedByController = true; - - - } - - - if(canBeLarge) { - this.blockEntities.clear(); - for(BlockPos pos : checkedPossesLarge) { - //if(((AirIntakeBlockEntity) level.getBlockEntity(pos)).isUsedByController&&((AirIntakeBlockEntity) level.getBlockEntity(pos)).controller!=this.getBlockPos()&&pos!=this.getBlockPos()) { - // controller = this.getBlockPos(); - // isController = false; - // return 1; - //} - - if((((AirIntakeBlockEntity) level.getBlockEntity(pos)).isUsedByController&&((AirIntakeBlockEntity) level.getBlockEntity(pos)).controller!=this.getBlockPos()&&pos!=this.getBlockPos())||isController) { - - ((AirIntakeBlockEntity) level.getBlockEntity(pos)).isUsedByController = true; - ((AirIntakeBlockEntity) level.getBlockEntity(pos)).isController = false; - ((AirIntakeBlockEntity) level.getBlockEntity(pos)).controller =this.getBlockPos(); - - } - - ((AirIntakeBlockEntity) level.getBlockEntity(pos)).setController(this.getBlockPos()); - this.blockEntities.add((AirIntakeBlockEntity) level.getBlockEntity(pos)); - } - - controller = this.getBlockPos(); - isController = true; - return 3; - } - - - if(canBeMedium) { - this.blockEntities.clear(); - - - - for(BlockPos pos : checkedPosses) { - if(((AirIntakeBlockEntity) level.getBlockEntity(pos)).isUsedByController&&((AirIntakeBlockEntity) level.getBlockEntity(pos)).controller!=this.getBlockPos()&&pos!=this.getBlockPos()) { - controller = this.getBlockPos(); - isController = false; - return 1; - } - ((AirIntakeBlockEntity) level.getBlockEntity(pos)).setController(this.getBlockPos()); - this.blockEntities.add((AirIntakeBlockEntity) level.getBlockEntity(pos)); - } - - controller = this.getBlockPos(); - isController = true; - return 2; + if (canBeMedium) { + setupMultiblock(MEDIUM_FAN_DIAMETER); + return MEDIUM_FAN_DIAMETER; } controller = this.getBlockPos(); isController = false; - return 1; + return SMALL_FAN_DIAMETER; } + + private boolean checkMediumDiameter() { + List checkedPosses = new ArrayList<>(); + BlockPos checkedPos = this.getBlockPos(); + Direction direction = this.getBlockState().getValue(FACING); + + for (int x = 0; x < MEDIUM_FAN_DIAMETER; x++) { + for (int z = 0; z < MEDIUM_FAN_DIAMETER; z++) { + checkedPosses.add(checkedPos); + if (direction.getAxis().isHorizontal()) { + checkedPos = checkedPos.above(); + } else { + checkedPos = checkedPos.east(); + } + } + if (direction.getAxis().isHorizontal()) { + checkedPos = checkedPos.below(MEDIUM_FAN_DIAMETER); + checkedPos = checkedPos.relative(direction.getClockWise()); + } else { + checkedPos = checkedPos.west(MEDIUM_FAN_DIAMETER); + checkedPos = checkedPos.south(); + } + } + + for (BlockPos pos : checkedPosses) { + if (!(level.getBlockEntity(pos) instanceof AirIntakeBlockEntity)) return false; + + AirIntakeBlockEntity checkedBE = (AirIntakeBlockEntity) level.getBlockEntity(pos); + if (pos != this.getBlockPos() && checkedBE.isController) return false; + if (checkedBE.getBlockState().getValue(FACING) != this.getBlockState().getValue(FACING)) return false; + } + return true; + } + + private boolean checkLargeDiameter() { + List checkedPosses = new ArrayList<>(); + BlockPos checkedPos = this.getBlockPos(); + Direction direction = this.getBlockState().getValue(FACING); + + for (int x = 0; x < LARGE_FAN_DIAMETER; x++) { + for (int z = 0; z < LARGE_FAN_DIAMETER; z++) { + checkedPosses.add(checkedPos); + if (direction.getAxis().isHorizontal()) { + checkedPos = checkedPos.above(); + } else { + checkedPos = checkedPos.east(); + } + } + if (direction.getAxis().isHorizontal()) { + checkedPos = checkedPos.below(LARGE_FAN_DIAMETER); + checkedPos = checkedPos.relative(direction.getClockWise()); + } else { + checkedPos = checkedPos.west(LARGE_FAN_DIAMETER); + checkedPos = checkedPos.south(); + } + } + + for (BlockPos pos : checkedPosses) { + if (!(level.getBlockEntity(pos) instanceof AirIntakeBlockEntity)) return false; + + AirIntakeBlockEntity checkedBE = (AirIntakeBlockEntity) level.getBlockEntity(pos); + if (checkedBE.getBlockState().getValue(FACING) != this.getBlockState().getValue(FACING)) return false; + } + return true; + } + + private void setupMultiblock(int diameter) { + this.blockEntities.clear(); + List positions = getMultiblockPositions(diameter); + + for (BlockPos pos : positions) { + AirIntakeBlockEntity be = (AirIntakeBlockEntity) level.getBlockEntity(pos); + if (be.isUsedByController && be.controller != this.getBlockPos() && pos != this.getBlockPos()) { + be.isUsedByController = true; + be.isController = false; + be.controller = this.getBlockPos(); + } + + be.setController(this.getBlockPos()); + this.blockEntities.add(be); + } + + controller = this.getBlockPos(); + isController = true; + } + + private List getMultiblockPositions(int diameter) { + List positions = new ArrayList<>(); + BlockPos checkedPos = this.getBlockPos(); + Direction direction = this.getBlockState().getValue(FACING); + int size = diameter == MEDIUM_FAN_DIAMETER ? MEDIUM_FAN_DIAMETER : LARGE_FAN_DIAMETER; + + for (int x = 0; x < size; x++) { + for (int z = 0; z < size; z++) { + positions.add(checkedPos); + if (direction.getAxis().isHorizontal()) { + checkedPos = checkedPos.above(); + } else { + checkedPos = checkedPos.east(); + } + } + if (direction.getAxis().isHorizontal()) { + checkedPos = checkedPos.below(size); + checkedPos = checkedPos.relative(direction.getClockWise()); + } else { + checkedPos = checkedPos.west(size); + checkedPos = checkedPos.south(); + } + } + return positions; + } + @Override protected AABB createRenderBoundingBox() { - - return new AABB(this.getBlockPos()).inflate(3); } + @Override @SuppressWarnings("removal") public boolean addToGoggleTooltip(List tooltip, boolean isPlayerSneaking) { - - - - //--Fluid Info--// LazyOptional handler = this.getCapability(ForgeCapabilities.FLUID_HANDLER); Optional resolve = handler.resolve(); - if (!resolve.isPresent()) - return false; + if (!resolve.isPresent()) return false; IFluidHandler tank = resolve.get(); - if (tank.getTanks() == 0) - return false; + if (tank.getTanks() == 0) return false; LangBuilder mb = CreateLang.translate("generic.unit.millibuckets"); - - boolean isEmpty = true; + for (int i = 0; i < tank.getTanks(); i++) { FluidStack fluidStack = tank.getFluidInTank(i); - if (fluidStack.isEmpty()) - continue; + if (fluidStack.isEmpty()) continue; CreateLang.fluidName(fluidStack) .style(ChatFormatting.GRAY) @@ -489,13 +382,11 @@ public class AirIntakeBlockEntity extends KineticBlockEntity implements IWrencha } if (tank.getTanks() > 1) { - if (isEmpty) - tooltip.remove(tooltip.size() - 1); + if (isEmpty) tooltip.remove(tooltip.size() - 1); return true; } - if (!isEmpty) - return true; + if (!isEmpty) return true; CreateLang.translate("gui.goggles.fluid_container.capacity") .add(CreateLang.number(tank.getTankCapacity(0)) @@ -503,57 +394,40 @@ public class AirIntakeBlockEntity extends KineticBlockEntity implements IWrencha .style(ChatFormatting.DARK_GREEN)) .style(ChatFormatting.DARK_GRAY) .forGoggles(tooltip, 1); - - - return true; } + protected SmartFluidTank createInventory() { - return new SmartFluidTank(8000, this::onFluidStackChanged){ + return new SmartFluidTank(8000, this::onFluidStackChanged) { @Override public boolean isFluidValid(FluidStack stack) { return stack.getFluid().isSame(TFMGFluids.AIR.getSource()); } - // @Override - // public FluidStack drain(FluidStack resource, FluidAction action) { - // return FluidStack.EMPTY; - // } }; } protected void onFluidStackChanged(FluidStack newFluidStack) { - setChanged(); - sendData(); - //if(((AirIntakeBlockEntity) level.getBlockEntity(controller))!=null) { - // ((AirIntakeBlockEntity) level.getBlockEntity(controller)).setChanged(); - // ((AirIntakeBlockEntity) level.getBlockEntity(controller)).sendData(); - //} - + setChanged(); + sendData(); } - @Override protected void read(CompoundTag compound, boolean clientPacket) { super.read(compound, clientPacket); - diameter = compound.getInt("Diameter"); isController = compound.getBoolean("IsController"); isUsedByController = compound.getBoolean("IsUsed"); hasShaft = compound.getBoolean("HasShaft"); tankInventory.readFromNBT(compound.getCompound("TankContent")); - } @Override public void write(CompoundTag compound, boolean clientPacket) { super.write(compound, clientPacket); - - compound.putInt("Diameter", diameter); compound.putBoolean("IsController", isController); compound.putBoolean("IsUsed", isUsedByController); compound.putBoolean("HasShaft", hasShaft); compound.put("TankContent", tankInventory.writeToNBT(new CompoundTag())); - } } diff --git a/src/main/java/com/drmangotea/tfmg/registry/TFMGSoundEvents.java b/src/main/java/com/drmangotea/tfmg/registry/TFMGSoundEvents.java index da853deb..26ba73a3 100644 --- a/src/main/java/com/drmangotea/tfmg/registry/TFMGSoundEvents.java +++ b/src/main/java/com/drmangotea/tfmg/registry/TFMGSoundEvents.java @@ -44,18 +44,41 @@ public class TFMGSoundEvents { public static final TFMGSoundEvents.SoundEntry - - ENGINE = create("engine") + ENGINE = create("engine") .subtitle("Engine Sounds") .category(SoundSource.BLOCKS) .attenuationDistance(10) .build(), - DIESEL_ENGINE = create("diesel_engine") - .subtitle("Diesel Engine Sounds") - .category(SoundSource.BLOCKS) - .attenuationDistance(10) - .build(); + DIESEL_ENGINE = create("diesel_engine") + .subtitle("Diesel Engine Sounds") + .category(SoundSource.BLOCKS) + .attenuationDistance(10) + .build(), + + ELECTRIC_HUM = create("electric_hum") + .subtitle("Electric hum") + .category(SoundSource.BLOCKS) + .attenuationDistance(10) + .build(), + + GENERATOR_HUM = create("generator_hum") + .subtitle("Generator hum") + .category(SoundSource.BLOCKS) + .attenuationDistance(10) + .build(), + + SWITCH_ON = create("switch_on") + .subtitle("Switch closing") + .category(SoundSource.BLOCKS) + .attenuationDistance(10) + .build(), + + SWITCH_OFF = create("switch_off") + .subtitle("Switch opening") + .category(SoundSource.BLOCKS) + .attenuationDistance(10) + .build(); private static SoundEntryBuilder create(String name) { return create(TFMG.asResource(name)); @@ -433,4 +456,4 @@ public class TFMGSoundEvents { } -} \ No newline at end of file +} diff --git a/src/main/resources/assets/tfmg/lang/default/interface.json b/src/main/resources/assets/tfmg/lang/default/interface.json index cefe6b6b..6313668e 100644 --- a/src/main/resources/assets/tfmg/lang/default/interface.json +++ b/src/main/resources/assets/tfmg/lang/default/interface.json @@ -147,6 +147,9 @@ "tfmg.keyinfo.transmission_shift_down": "Transmission Shift Down", "create.multimeter.header": "Multimeter Data:", + "create.multimeter.network_power_consumption": "Network power consumption: ", + "create.multimeter.network_power_generation": "Network power generation: ", + "create.multimeter.not_enough_power": "NOT ENOUGH POWER!", "create.multimeter.additional_values": "Additional Values:", "create.multimeter.power_usage": " Power Usage: ", "create.multimeter.group": " Group: ", @@ -170,8 +173,11 @@ - "tfmg.subtitle.engine_sounds": "Engine Sounds", - "tfmg.subtitle.diesel_engine_sounds": "Diesel Engine Sounds" - + "tfmg.subtitle.engine_sounds": "Engine sounds", + "tfmg.subtitle.diesel_engine_sounds": "Diesel Engine sounds", + "tfmg.subtitle.electric_hum": "Electric hum", + "tfmg.subtitle.generator_hum": "Generator hum", + "tfmg.subtitle.switch_on": "Switch closing", + "tfmg.subtitle.switch_off": "Switch opening" } diff --git a/src/main/resources/assets/tfmg/lang/ru_ru.json b/src/main/resources/assets/tfmg/lang/ru_ru.json index 67d91099..8e516410 100644 --- a/src/main/resources/assets/tfmg/lang/ru_ru.json +++ b/src/main/resources/assets/tfmg/lang/ru_ru.json @@ -427,7 +427,6 @@ "block.tfmg.red_rebar_concrete_stairs": "Красные железобетонные ступеньки", "block.tfmg.red_rebar_concrete_wall": "Красная железобетонная ограда", "block.tfmg.regular_engine": "Стандартный двигатель", - "block.tfmg.reinforced_fireproof_bricks": "Укреплённые огнеупорные кирпичи", "block.tfmg.resistor": "Резистор", "block.tfmg.resistor.tooltip.behaviour1": "Добавляет сопротивление (величину можно изменить на намоточном станке) в электрическую группу подключённого блока.", "block.tfmg.resistor.tooltip.condition1": "При установке на блок в составе группы", @@ -607,12 +606,15 @@ "create.multimeter.energy_usage": " Потребление энергии: ", "create.multimeter.group": " Группа: ", "create.multimeter.header": "Данные измерений:", - "create.multimeter.power_generated": " Генерация энергии: ", + "create.multimeter.not_enough_power": "НЕДОСТАТОЧНАЯ МОЩНОСТЬ", + "create.multimeter.power_generated": " Генерируемая мощность: ", "create.multimeter.power_percentage": " Резерв мощности сети: ", "create.multimeter.power_usage": " Потребление мощности: ", "create.multimeter.transformer_ratio": " Коэффициент трансформации: ", "create.multimeter.voltage_generated": " Генерируемое напряжение: ", "create.network": "Сеть: %1$s", + "create.multimeter.network_power_consumption": "Потребление мощности в сети: ", + "create.multimeter.network_power_generation": "Генерация мощности в сети: ", "create.oil_hammer.reserves": "Резервы нефти: %1$s", "create.pumpjack_deposit_amount": "%1$s вёдер", "create.recipe.advanced_distillation": "Ректификация", @@ -646,8 +648,8 @@ "distillation_output.mode.keep_fluid": "Останавливать процесс", "distillation_output.mode.void_when_full": "Уничтожать избытки", "effect.tfmg.hellfire": "Адское пламя", - "effect.tfmg.hellfire.description": "§kОписание отсутствует§r", "entity.tfmg.blue_spark": "Синяя искра", + "entity.tfmg.dry_ice_flake": "Крошка сухого льда", "entity.tfmg.copper_grenade": "Медная граната", "entity.tfmg.green_spark": "Зелёная искра", "entity.tfmg.lithium_spark": "Литиевая искра", @@ -778,6 +780,8 @@ "item.tfmg.ethylene_bucket": "Баллон этилена", "item.tfmg.fireclay_ball": "Комок огнеупорной глины", "item.tfmg.fireproof_brick": "Огнеупорный кирпич", + "item.tfmg.fire_extinguisher": "Огнетушитель", + "item.tfmg.fire_extinguisher.tooltip.summary": "Использует углекислый газ для тушения пожаров", "item.tfmg.flamethrower": "Огнемёт", "item.tfmg.flamethrower.tooltip.behaviour1": "_Выпускает струю пламени_.", "item.tfmg.flamethrower.tooltip.behaviour2": "Заправляется _топливом_ из бака. Вид топлива влияет на _дальнобойность_ и _разброс_.", @@ -982,20 +986,12 @@ "item.tfmg.yellow_multimeter.tooltip.summary": "Отображает данные об электроприборе и сети, к которой он подключён.", "item.tfmg.zinc_electrode": "Цинковый электрод", "item.tfmg.zinc_grenade": "Цинковая граната", - "tag.fluid.tfmg.blast_stove_fuel": "Топливо для доменного воздухонагревателя", - "tag.fluid.tfmg.firebox_fuel": "Топливо для топки", - "tag.fluid.tfmg.flammable": "Горючие жидкости", - "tag.fluid.tfmg.gas": "Топливо", - "tag.item.tfmg.blast_furnace_fuel": "Топливо для доменной печи", - "tag.item.tfmg.flux": "Флюс", - "tag.item.tfmg.rods": "Стержни", - "tag.item.tfmg.spools": "Катушки", - "tag.item.tfmg.stone_types.bauxite": "Бокситовые камни", - "tag.item.tfmg.stone_types.galena": "Галенитовые камни", "tfmg.keyinfo.custom_button": "Настраиваемая кнопка контроллера двигателя", "tfmg.keyinfo.engine_start": "Запуск двигателя", "tfmg.keyinfo.transmission_shift_down": "Понижение передачи", "tfmg.keyinfo.transmission_shift_up": "Повышение передачи", + "tfmg.ponder.arc_furnace.header": "Дуговой печью", + "tfmg.ponder.arc_furnace.text_1": "Три графитовых электрода позволят создать дуговую печь — электрический аналог доменной печи", "tfmg.ponder.blast_furnace.header": "Доменным процессом", "tfmg.ponder.blast_furnace.text_1": "Основной блок доменной печи — это её лётка, то есть выход для продуктов плавки", "tfmg.ponder.blast_furnace.text_2": "Для постройки доменной печи возведите трубу из огнеупорного кирпича, с одним люком доменной печи в стенке", @@ -1011,7 +1007,7 @@ "tfmg.ponder.chemical_vat.text_4": "Вместо лопастей в промышленный смешиватель можно установить центрифугу", "tfmg.ponder.chemical_vat.text_5": "Для некоторых процессов требуется нагрев", "tfmg.ponder.chemical_vat.text_6": "Подключение к реактору двух электродных держателей с медными или цинковыми электродами превратит его в электролизёр", - "tfmg.ponder.chemical_vat.text_7": "3 графитовых электрода позволят создать дуговую печь — электрический аналог доменной печи", + "tfmg.ponder.chemical_vat.text_7": "Три графитовых электрода позволят создать дуговую печь — электрический аналог доменной печи", "tfmg.ponder.coke_oven.header": "Коксованием", "tfmg.ponder.coke_oven.text_1": "Коксовая печь производит кокс из каменного угля и древесный уголь из древесины", "tfmg.ponder.coke_oven.text_2": "Процесс коксования длительный, так что имеет смысл строить целые батареи коксовых печей", @@ -1044,6 +1040,8 @@ "tfmg.ponder.electricity_two.text_5": "Другой прибор — потенциометр, с его помощью можно задать выходное напряжение в процентах от входного", "tfmg.ponder.electricity_two.text_6": "Выключатель разрывает передачу электроэнергии при получении редстоун-сигнала", "tfmg.ponder.electricity_two.text_7": "Трансформатор повышает или понижает напряжение пропорционально соотношению витков первичной и вторичной обмоток", + "tfmg.ponder.electrolysis.header": "Электролизом", + "tfmg.ponder.electrolysis.text_1": "Подключение к реактору двух электродных держателей с медными или цинковыми электродами превратит его в электролизёр", "tfmg.ponder.engines.header": "Двигателями", "tfmg.ponder.engines.text_1": "Для постройки двигателя разместите до 5 блоков двигателя в ряд", "tfmg.ponder.engines.text_2": "Подсказки позволят узнать, какие компоненты нужно установить в процессе сборки", @@ -1054,13 +1052,18 @@ "tfmg.ponder.engines.text_7": "Каждый блок двигателя может быть улучшен установкой на него разных предметов с помощью ПКМ", "tfmg.ponder.engines.text_8": "Например, промышленные трубы позволят двигателю самостоятельно забирать топливо из расположенного рядом бака", "tfmg.ponder.engines.text_9": "Для запуска двигателя подайте редстоун-сигнал", + "tfmg.ponder.industrial_mixer.header": "Промышленным смешивателем", + "tfmg.ponder.industrial_mixer.text_1": "Промышленный смешиватель — механический компонент для химического реактора", + "tfmg.ponder.industrial_mixer.text_2": "При установке в него лопастей реактор начнёт работать как смешиватель", + "tfmg.ponder.industrial_mixer.text_3": "Вместо лопастей можно установить центрифугу", "tfmg.ponder.pumpjack.header": "Добычей нефти", "tfmg.ponder.pumpjack.text_1": "Чтобы начать добычу нефти, постройте колонну промышленных труб от месторождения до места размещения насоса", "tfmg.ponder.pumpjack.text_2": "Разместите оголовок скважины на колонне труб — он будет основой нефтяной скважины", "tfmg.ponder.pumpjack.text_3": "Разместите за ним стойку насоса-качалки", "tfmg.ponder.pumpjack.text_4": "Далее разместите над оголовком скважины головку балансира, а с противоположной стороны — шарнир балансира", "tfmg.ponder.pumpjack.text_5": "Соедините головку и шарнир, построив сам балансир из сегментов", - "tfmg.ponder.pumpjack.text_6": "Наконец, разместите снизу от шарнира балансира машинный ввод (к которому будет подведено вращение для работы), а на него — кривошип насоса. Склейте балансир суперклеем, и насос будет собран", + "tfmg.ponder.pumpjack.text_6": "Не забудьте склеить балансир суперклеем", + "tfmg.ponder.pumpjack.text_7": "Наконец, разместите снизу от шарнира балансира машинный ввод (к которому будет подведено вращение для работы), а на него — кривошип насоса", "tfmg.ponder.shared.behaviour_modify_value_panel": "Поведение можно изменить с помощью меню выбора значений", "tfmg.ponder.shared.movement_anchors": "С помощью суперклея можно перемещать крупные структуры.", "tfmg.ponder.shared.rpm16": "16 об/мин", @@ -1079,17 +1082,9 @@ "tfmg.ponder.tag.oil_processing": "Нефтепереработкой", "tfmg.ponder.tag.oil_processing.description": "Блоки, применяемые для добычи и переработки нефти", "tfmg.subtitle.diesel_engine_sounds": "Шум дизельного двигателя", + "tfmg.subtitle.electric_hum": "Электрический гул", "tfmg.subtitle.engine_sounds": "Шум двигателя", - "entity.tfmg.dry_ice_flake": "Крошка сухого льда", - "item.tfmg.fire_extinguisher": "Огнетушитель", - "item.tfmg.fire_extinguisher.tooltip.summary": "Использует углекислый газ для тушения пожаров", - "tfmg.ponder.arc_furnace.header": "Arc Furnace", - "tfmg.ponder.arc_furnace.text_1": "3 Graphite Electrodes create an Arc Furnace", - "tfmg.ponder.electrolysis.header": "Electrolysis", - "tfmg.ponder.electrolysis.text_1": "Placing 2 electrode holders with Copper or Zinc Electrodes creates an Electrolyzer", - "tfmg.ponder.industrial_mixer.header": "Industrial Mixer", - "tfmg.ponder.industrial_mixer.text_1": "The Industrial Mixer is a machine attachment for the Chemical Vat", - "tfmg.ponder.industrial_mixer.text_2": "When the Mixer Blade is inserted, the vat becomes a Mixer", - "tfmg.ponder.industrial_mixer.text_3": "The Industrial Mixer can also become a Centrifuge", - "tfmg.ponder.pumpjack.text_7": "The last step is placing a machine input (which is the power input for the pumpjack) with a pumpjack crank above it" + "tfmg.subtitle.generator_hum": "Гул генератора", + "tfmg.subtitle.switch_off": "Выключатель разомкнут", + "tfmg.subtitle.switch_on": "Выключатель замкнут" } diff --git a/src/main/resources/assets/tfmg/sounds.json b/src/main/resources/assets/tfmg/sounds.json index efa422fc..265e4406 100644 --- a/src/main/resources/assets/tfmg/sounds.json +++ b/src/main/resources/assets/tfmg/sounds.json @@ -19,5 +19,45 @@ } ], "subtitle": "tfmg.subtitle.diesel_engine_sounds" + }, + "electric_hum": { + "sounds": [ + { + "name": "tfmg:electric_hum", + "type": "file", + "attenuation_distance": 16 + } + ], + "subtitle": "tfmg.subtitle.electric_hum" + }, + "generator_hum": { + "sounds": [ + { + "name": "tfmg:generator_hum", + "type": "file", + "attenuation_distance": 16 + } + ], + "subtitle": "tfmg.subtitle.generator_hum" + }, + "switch_on": { + "sounds": [ + { + "name": "tfmg:switch_on", + "type": "file", + "attenuation_distance": 16 + } + ], + "subtitle": "tfmg.subtitle.switch_on" + }, + "switch_off": { + "sounds": [ + { + "name": "tfmg:switch_off", + "type": "file", + "attenuation_distance": 16 + } + ], + "subtitle": "tfmg.subtitle.switch_off" } -} \ No newline at end of file +} diff --git a/src/main/resources/assets/tfmg/sounds/electric_hum.ogg b/src/main/resources/assets/tfmg/sounds/electric_hum.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fd549b3507c258a71cbe7ac89f5460fbf277c02b GIT binary patch literal 8050 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 U^Y4x#5wd2el`okka*ja%4;@Pvm;e9( literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/tfmg/sounds/generator_hum.ogg b/src/main/resources/assets/tfmg/sounds/generator_hum.ogg new file mode 100644 index 0000000000000000000000000000000000000000..27277338446efb15d0c28b7d21dbdde8f978d1ba GIT binary patch literal 6017 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 bo7^ljjRk%0@@cDBgR72jg8PTu^`QR&lov$W literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/tfmg/sounds/switch_off.ogg b/src/main/resources/assets/tfmg/sounds/switch_off.ogg new file mode 100644 index 0000000000000000000000000000000000000000..aafcfec73e34652d43bf1c12832ebea058eedc85 GIT binary patch literal 13362 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 HPk{dqJ%JAs literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/tfmg/sounds/switch_on.ogg b/src/main/resources/assets/tfmg/sounds/switch_on.ogg new file mode 100644 index 0000000000000000000000000000000000000000..c5060bcebf75aea5f15b2bdcfdec8c325cdaac4d GIT binary patch literal 18327 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 literal 0 HcmV?d00001