diff --git a/dependencies.gradle b/dependencies.gradle index de5a9b97185..5c403e6af96 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -33,6 +33,11 @@ dependencies { modCompileOnly(forge.ae2) modCompileOnly(forge.ae2wtlib) + // Create + modCompileOnly(forge.ponder) + modCompileOnly(variantOf(forge.create) { classifier("slim") }) + modCompileOnly(forge.flywheel.forge.api) + // KJS modCompileOnly(forge.kubejs) modCompileOnly(forge.architectury) @@ -89,7 +94,7 @@ dependencies { modExtraLocalRuntime(forge.trenzalore) modExtraLocalRuntime(forge.curios) // modExtraLocalRuntime(forge.worldstripper) - modExtraLocalRuntime(forge.cc.tweaked.forge.impl) +// modExtraLocalRuntime(forge.cc.tweaked.forge.impl) for some reason this breaks create modExtraLocalRuntime(forge.bundles.kjs) @@ -110,6 +115,9 @@ dependencies { modExtraLocalRuntime(forge.kotlinforforge) modExtraLocalRuntime(forge.observable) + modExtraLocalRuntime(forge.ponder) + modExtraLocalRuntime(variantOf(forge.create) { classifier("slim") }) + modExtraLocalRuntime(forge.flywheel.forge) ////////////////////////// testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") diff --git a/gradle/forge.versions.toml b/gradle/forge.versions.toml index 5218fb80f1f..ac9bf4cd7b8 100644 --- a/gradle/forge.versions.toml +++ b/gradle/forge.versions.toml @@ -22,6 +22,9 @@ ftbteams = "2001.3.0" ftbquests = "2001.4.11" ftbchunks = "2001.3.4" ccTweaked = "1.114.3" +create = "6.0.6-150" +ponder = "1.0.80" +flywheel = "1.0.4" ## modrinth maven ## jade = "11.6.3" @@ -65,6 +68,10 @@ rei-forge = { module = "me.shedaniel:RoughlyEnoughItems-forge", versio emi = { module = "dev.emi:emi-forge", version.ref = "emi" } ae2 = { module = "appeng:appliedenergistics2-forge", version.ref = "ae2" } +create = { module = "com.simibubi.create:create-1.20.1", version.ref = "create"} +ponder = { module = "net.createmod.ponder:Ponder-Forge-1.20.1", version.ref = "ponder"} +flywheel-forge-api = { module = "dev.engine-room.flywheel:flywheel-forge-api-1.20.1", version.ref = "flywheel"} +flywheel-forge = { module = "dev.engine-room.flywheel:flywheel-forge-1.20.1", version.ref = "flywheel"} kubejs = { module = "dev.latvian.mods:kubejs-forge", version.ref = "kubejs" } rhino = { module = "dev.latvian.mods:rhino-forge", version.ref = "rhino" } architectury = { module = "dev.architectury:architectury-forge", version.ref = "architectury" } diff --git a/gradle/scripts/repositories.gradle b/gradle/scripts/repositories.gradle index 3ed279995a1..27c821f0073 100644 --- a/gradle/scripts/repositories.gradle +++ b/gradle/scripts/repositories.gradle @@ -17,6 +17,15 @@ repositories { url = "https://maven.firstdarkdev.xyz/snapshots" } + + exclusiveContent { // Create, Ponder, Flywheel + forRepository { maven { url = "https://maven.createmod.net" } } + filter { + includeGroup("net.createmod.ponder") + includeGroup("com.simibubi.create") + includeGroup("dev.engine-room.flywheel") + } + } exclusiveContent { // Configuration forRepository { maven { url = "https://api.repsy.io/mvn/toma/public" } } filter { includeGroup("dev.toma.configuration")} diff --git a/src/generated/resources/assets/gtceu/blockstates/basic_data_access_hatch.json b/src/generated/resources/assets/gtceu/blockstates/basic_data_access_hatch.json new file mode 100644 index 00000000000..5506e13f583 --- /dev/null +++ b/src/generated/resources/assets/gtceu/blockstates/basic_data_access_hatch.json @@ -0,0 +1,28 @@ +{ + "variants": { + "facing=down": { + "model": "gtceu:block/machine/basic_data_access_hatch", + "x": 90 + }, + "facing=east": { + "model": "gtceu:block/machine/basic_data_access_hatch", + "y": 90 + }, + "facing=north": { + "model": "gtceu:block/machine/basic_data_access_hatch" + }, + "facing=south": { + "model": "gtceu:block/machine/basic_data_access_hatch", + "y": 180 + }, + "facing=up": { + "gtceu:z": 180, + "model": "gtceu:block/machine/basic_data_access_hatch", + "x": 270 + }, + "facing=west": { + "model": "gtceu:block/machine/basic_data_access_hatch", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/blockstates/central_monitor.json b/src/generated/resources/assets/gtceu/blockstates/central_monitor.json new file mode 100644 index 00000000000..1786db9760d --- /dev/null +++ b/src/generated/resources/assets/gtceu/blockstates/central_monitor.json @@ -0,0 +1,114 @@ +{ + "variants": { + "facing=down,upwards_facing=east": { + "gtceu:z": 90, + "model": "gtceu:block/machine/central_monitor", + "x": 90 + }, + "facing=down,upwards_facing=north": { + "model": "gtceu:block/machine/central_monitor", + "x": 90 + }, + "facing=down,upwards_facing=south": { + "gtceu:z": 180, + "model": "gtceu:block/machine/central_monitor", + "x": 90 + }, + "facing=down,upwards_facing=west": { + "gtceu:z": 270, + "model": "gtceu:block/machine/central_monitor", + "x": 90 + }, + "facing=east,upwards_facing=east": { + "gtceu:z": 270, + "model": "gtceu:block/machine/central_monitor", + "y": 90 + }, + "facing=east,upwards_facing=north": { + "model": "gtceu:block/machine/central_monitor", + "y": 90 + }, + "facing=east,upwards_facing=south": { + "gtceu:z": 180, + "model": "gtceu:block/machine/central_monitor", + "y": 90 + }, + "facing=east,upwards_facing=west": { + "gtceu:z": 90, + "model": "gtceu:block/machine/central_monitor", + "y": 90 + }, + "facing=north,upwards_facing=east": { + "gtceu:z": 270, + "model": "gtceu:block/machine/central_monitor" + }, + "facing=north,upwards_facing=north": { + "model": "gtceu:block/machine/central_monitor" + }, + "facing=north,upwards_facing=south": { + "gtceu:z": 180, + "model": "gtceu:block/machine/central_monitor" + }, + "facing=north,upwards_facing=west": { + "gtceu:z": 90, + "model": "gtceu:block/machine/central_monitor" + }, + "facing=south,upwards_facing=east": { + "gtceu:z": 270, + "model": "gtceu:block/machine/central_monitor", + "y": 180 + }, + "facing=south,upwards_facing=north": { + "model": "gtceu:block/machine/central_monitor", + "y": 180 + }, + "facing=south,upwards_facing=south": { + "gtceu:z": 180, + "model": "gtceu:block/machine/central_monitor", + "y": 180 + }, + "facing=south,upwards_facing=west": { + "gtceu:z": 90, + "model": "gtceu:block/machine/central_monitor", + "y": 180 + }, + "facing=up,upwards_facing=east": { + "gtceu:z": 90, + "model": "gtceu:block/machine/central_monitor", + "x": 270 + }, + "facing=up,upwards_facing=north": { + "gtceu:z": 180, + "model": "gtceu:block/machine/central_monitor", + "x": 270 + }, + "facing=up,upwards_facing=south": { + "model": "gtceu:block/machine/central_monitor", + "x": 270 + }, + "facing=up,upwards_facing=west": { + "gtceu:z": 270, + "model": "gtceu:block/machine/central_monitor", + "x": 270 + }, + "facing=west,upwards_facing=east": { + "gtceu:z": 270, + "model": "gtceu:block/machine/central_monitor", + "y": 270 + }, + "facing=west,upwards_facing=north": { + "model": "gtceu:block/machine/central_monitor", + "y": 270 + }, + "facing=west,upwards_facing=south": { + "gtceu:z": 180, + "model": "gtceu:block/machine/central_monitor", + "y": 270 + }, + "facing=west,upwards_facing=west": { + "gtceu:z": 90, + "model": "gtceu:block/machine/central_monitor", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/blockstates/monitor.json b/src/generated/resources/assets/gtceu/blockstates/monitor.json new file mode 100644 index 00000000000..a148abbb397 --- /dev/null +++ b/src/generated/resources/assets/gtceu/blockstates/monitor.json @@ -0,0 +1,28 @@ +{ + "variants": { + "facing=down": { + "model": "gtceu:block/machine/monitor", + "x": 90 + }, + "facing=east": { + "model": "gtceu:block/machine/monitor", + "y": 90 + }, + "facing=north": { + "model": "gtceu:block/machine/monitor" + }, + "facing=south": { + "model": "gtceu:block/machine/monitor", + "y": 180 + }, + "facing=up": { + "gtceu:z": 180, + "model": "gtceu:block/machine/monitor", + "x": 270 + }, + "facing=west": { + "model": "gtceu:block/machine/monitor", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/lang/en_ud.json b/src/generated/resources/assets/gtceu/lang/en_ud.json index 9c08af71494..f91c2058084 100644 --- a/src/generated/resources/assets/gtceu/lang/en_ud.json +++ b/src/generated/resources/assets/gtceu/lang/en_ud.json @@ -109,6 +109,7 @@ "block.gtceu.assembly_line_unit": "buısɐƆ ןoɹʇuoƆ ʎןqɯǝssⱯ", "block.gtceu.atomic_casing": "buısɐƆ ɔıɯoʇⱯ", "block.gtceu.auto_maintenance_hatch": "ɥɔʇɐH ǝɔuɐuǝʇuıɐW oʇnⱯ", + "block.gtceu.basic_data_access_hatch": "ɥɔʇɐH ssǝɔɔⱯ ɐʇɐᗡ ɔısɐᗺ", "block.gtceu.bio_hazard_sign_block": "ʞɔoןᗺ ubıS pɹɐzɐH oıᗺ", "block.gtceu.black_borderless_lamp": "dɯɐꞀ ssǝןɹǝpɹoᗺ ʞɔɐןᗺ", "block.gtceu.black_lamp": "dɯɐꞀ ʞɔɐןᗺ", @@ -138,6 +139,7 @@ "block.gtceu.casing_coke_bricks": "sʞɔıɹᗺ uǝʌO ǝʞoƆ", "block.gtceu.casing_grate": "buısɐƆ ǝuıɥɔɐW ǝʇɐɹ⅁", "block.gtceu.causality_hazard_sign_block": "ʞɔoןᗺ ubıS pɹɐzɐH ʎʇıןɐsnɐƆ", + "block.gtceu.central_monitor": "ɹoʇıuoW ןɐɹʇuǝƆ", "block.gtceu.charcoal_pile_igniter": "ɹǝʇıubI ǝןıԀ ןɐoɔɹɐɥƆ", "block.gtceu.chiseled_dark_concrete": "ǝʇǝɹɔuoƆ ʞɹɐᗡ pǝןǝsıɥƆ", "block.gtceu.chiseled_light_concrete": "ǝʇǝɹɔuoƆ ʇɥbıꞀ pǝןǝsıɥƆ", @@ -799,6 +801,7 @@ "block.gtceu.mob_infestation_hazard_sign_block": "ʞɔoןᗺ ubıS pɹɐzɐH uoıʇɐʇsǝɟuI qoW", "block.gtceu.mob_spawner_hazard_sign_block": "ʞɔoןᗺ ubıS pɹɐzɐH ɹǝuʍɐdS qoW", "block.gtceu.molybdenum_disilicide_coil_block": "ʞɔoןᗺ ןıoƆ ǝpıɔıןısıᗡ ɯnuǝpqʎןoW", + "block.gtceu.monitor": "ɹoʇıuoW", "block.gtceu.mossy_dark_concrete_bricks": "sʞɔıɹᗺ ǝʇǝɹɔuoƆ ʞɹɐᗡ ʎssoW", "block.gtceu.mossy_dark_concrete_cobblestone": "ǝuoʇsǝןqqoƆ ǝʇǝɹɔuoƆ ʞɹɐᗡ ʎssoW", "block.gtceu.mossy_light_concrete_bricks": "sʞɔıɹᗺ ǝʇǝɹɔuoƆ ʇɥbıꞀ ʎssoW", @@ -1807,6 +1810,7 @@ "config.gtceu.option.machineSounds": "spunoSǝuıɥɔɐɯ", "config.gtceu.option.machines": "sǝuıɥɔɐɯ", "config.gtceu.option.machinesEmissiveTextures": "sǝɹnʇxǝ⟘ǝʌıssıɯƎsǝuıɥɔɐɯ", + "config.gtceu.option.machinesHaveBERsByDefault": "ʇןnɐɟǝᗡʎᗺsᴚƎᗺǝʌɐHsǝuıɥɔɐɯ", "config.gtceu.option.meHatchEnergyUsage": "ǝbɐs∩ʎbɹǝuƎɥɔʇɐHǝɯ", "config.gtceu.option.minerSpeed": "pǝǝdSɹǝuıɯ", "config.gtceu.option.minimap": "dɐɯıuıɯ", @@ -2178,6 +2182,20 @@ "gtceu.cable.superconductor": "ɹoʇɔnpuoɔɹǝdnSp§ %s", "gtceu.cable.voltage": ")ɐ§%s(ɐ§ %dɐ§ ɹ§:ǝbɐʇןoΛ xɐWɐ§", "gtceu.canner": "ɹǝuuɐƆ", + "gtceu.central_monitor.gui.create_group": "dnoɹb ǝʇɐǝɹƆ", + "gtceu.central_monitor.gui.currently_editing": "%s :buıʇıpǝ ʎןʇuǝɹɹnƆ", + "gtceu.central_monitor.gui.remove_from_group": "dnoɹb ɯoɹɟ ǝʌoɯǝᴚ", + "gtceu.central_monitor.gui.set_target": "ʇǝbɹɐʇ ʇǝS", + "gtceu.central_monitor.info_tooltip.0": "˙ʇı uı ǝןnpoɯ Ɩ ǝʌɐɥ ʎןuo ʎɐɯ dnoɹb Ɐ ˙ʇsɹıɟ sdnoɹb oʇuı ɯǝɥʇ ʇıןds oʇ ǝʌɐɥ noʎ 'sɹoʇıuoɯ ǝsn oʇ ɹǝpɹo uI", + "gtceu.central_monitor.info_tooltip.1": "˙,dnoɹb ǝʇɐǝɹƆ, ʞɔıןɔ uǝɥʇ 'buıʞɔıןɔ-ʇɟǝן ʎq ɯǝɥʇ ʇɔǝןǝS", + "gtceu.central_monitor.info_tooltip.2": "˙ǝbɐd ǝɯɐs ǝɥʇ uı ʇı ǝɹnbıɟuoɔ uɐɔ noʎ 'ǝןnpoɯ ɐ ʇɹǝsuı uɐɔ noʎ dnoɹb ǝɥʇ ɹoɟ ǝbɐd sbuıʇʇǝs ǝɥʇ uı uǝɥ⟘", + "gtceu.central_monitor.info_tooltip.3": "˙,dnoɹb ɯoɹɟ ǝʌoɯǝᴚ, ʞɔıןɔ puɐ sʇuǝuodɯoɔ s,ʇı ɟo ןןɐ ʇɔǝןǝs 'dnoɹb ɐ ǝʇǝןǝp o⟘", + "gtceu.central_monitor.info_tooltip.4": "˙ʇɔǝןǝsun oʇ uıɐbɐ ʞɔıןƆ ˙ǝɯɐu s,ʇı uo buıʞɔıןɔ ʎq dnoɹb ɐ ɟo sʇuǝuodɯoɔ ןןɐ ʇɔǝןǝs ʎןʞɔınb uɐɔ noʎ", + "gtceu.central_monitor.info_tooltip.5": "˙ʇuǝuodɯoɔ ʇǝbɹɐʇ ǝɥʇ uo ʞɔıןɔ-ʇɥbıɹ puɐ dnoɹb ʇɐɥʇ ɟo ʇuǝuodɯoɔ ʎuɐ ʇɔǝןǝs dnoɹb ɐ ɹoɟ ʇǝbɹɐʇ ɐ ʇǝs oʇ 'ʇǝbɹɐʇ ʎǝɥʇ ʞɔoןq ǝɥʇ uo buıpuǝdǝp sbuıɥʇ ʎɐןdsıp ʎɐɯ sǝןnpoɯ ǝɯoS", + "gtceu.central_monitor.info_tooltip.6": "˙ʇɐɥʇ ɹoɟ ɹǝʌoɔ ɹǝʇʇıɯsuɐɹʇ ssǝןǝɹıʍ ǝɥʇ ǝsn oʇ ǝʌɐɥ noʎ 'ʞɔoןqıʇןnɯ ǝɥʇ uı ʇou sı ʇɐɥʇ ʇǝbɹɐʇ ɐ ʇɔǝןǝs oʇ ɥsıʍ ʎɐɯ noʎ", + "gtceu.central_monitor.info_tooltip.7": "˙ʞɔoןqıʇןnɯ ǝɥʇ uı ɥɔʇɐɥ ssǝɔɔɐ ɐʇɐp ɐ oʇuı ʞɔıʇs ɐʇɐp ʇɐɥʇ ʇnd puɐ ʞɔıʇs ɐʇɐp ɐ ɥʇıʍ ʇı ʞɔıןɔ-ʇɥbıɹ 'ʞɔoןq ʇǝbɹɐʇ ǝɥʇ uo ɹǝʌoɔ ǝɥʇ ǝɔɐןԀ", + "gtceu.central_monitor.info_tooltip.8": "˙pǝɹɐǝddɐ ʇɐɥʇ pןǝıɟ ɹǝqɯnu ǝɥʇ uı ʞɔıʇs ɐʇɐp ɹnoʎ ɟo xǝpuı ʇoןs ǝɥʇ ʇǝs puɐ 'ʇǝbɹɐʇ ǝɥʇ sɐ ɥɔʇɐɥ ssǝɔɔɐ ɐʇɐp ǝɥʇ ʇɔǝןǝs uǝɥ⟘", + "gtceu.central_monitor.size": ")%d+Ɩ+%d(x)%d+Ɩ+%d( :ǝzıS", "gtceu.centrifuge": "ǝbnɟıɹʇuǝƆ", "gtceu.chance_logic.and": "ᗡNⱯ", "gtceu.chance_logic.first": "⟘SᴚIℲ", @@ -2191,6 +2209,21 @@ "gtceu.coke_oven": "uǝʌO ǝʞoƆ", "gtceu.combustion_generator": "ɹoʇɐɹǝuǝ⅁ uoıʇsnqɯoƆ", "gtceu.compressor": "ɹossǝɹdɯoƆ", + "gtceu.computer_monitor_cover.error.bf_invalid": "%d ʇɐ ɹǝʇɔɐɹɐɥɔ pıןɐʌuI", + "gtceu.computer_monitor_cover.error.bf_invalid_num": "%d ɹǝqɯnu ןoqɯʎs buıssǝɔoɹd uǝɥʍ %d xǝpuı ʇɐ ɹǝqɯnu pıןɐʌuI", + "gtceu.computer_monitor_cover.error.exception": "%s :pǝɹɹnɔɔo uoıʇdǝɔxǝ pǝʇɔǝdxǝu∩", + "gtceu.computer_monitor_cover.error.invalid_args": "¡sʇuǝɯnbɹɐ pıןɐʌuI", + "gtceu.computer_monitor_cover.error.invalid_number": "¡,%s, ɹǝqɯnu pıןɐʌuI", + "gtceu.computer_monitor_cover.error.missing_item": "¡%d ʇoןs uı %s buıssıW", + "gtceu.computer_monitor_cover.error.no_ae": "¡ʞɹoʍʇǝu ᄅƎⱯ uɐ ǝʌɐɥ ʇou sǝop ɹǝpןoɥ ɹǝʌoƆ", + "gtceu.computer_monitor_cover.error.no_cover": "¡ɹǝʌoɔ oN", + "gtceu.computer_monitor_cover.error.no_placeholder": "¡,%s, :ɹǝpןoɥǝɔɐןd ɥɔns oN", + "gtceu.computer_monitor_cover.error.not_enough_args": "¡%d ʇob 'sbɹɐ %d ʇsɐǝן ʇɐ pǝʇɔǝdxƎ", + "gtceu.computer_monitor_cover.error.not_in_range": "%d ʇob ')ǝʌısnןɔuı( %d puɐ %d uǝǝʍʇǝq ǝq oʇ %s pǝʇɔǝdxƎ", + "gtceu.computer_monitor_cover.error.not_supported": "¡ɹǝʌoɔ/ʞɔoןq sıɥʇ ʎq pǝʇɹoddns ʇou sı ǝɹnʇɐǝɟ sıɥ⟘", + "gtceu.computer_monitor_cover.error.unclosed_bracket": "¡ʇǝʞɔɐɹq pǝsoןɔu∩", + "gtceu.computer_monitor_cover.error.unexpected_bracket": "¡ʇǝʞɔɐɹq buısoןɔ pǝʇɔǝdxǝu∩", + "gtceu.computer_monitor_cover.error.wrong_number_of_args": "¡%d ʇob 'sbɹɐ %d pǝʇɔǝdxƎ", "gtceu.cover.activity_detector.message_activity_inverted": "snʇɐʇS ʎʇıʌıʇɔⱯ pǝʇɹǝʌuI buıɹoʇıuoW", "gtceu.cover.activity_detector.message_activity_normal": "snʇɐʇS ʎʇıʌıʇɔⱯ ןɐɯɹoN buıɹoʇıuoW", "gtceu.cover.activity_detector_advanced.message_activity_inverted": "snʇɐʇS ssǝɹboɹԀ pǝʇɹǝʌuI buıɹoʇıuoW", @@ -2226,6 +2259,8 @@ "gtceu.direction.tooltip.left": "ʇɟǝꞀ", "gtceu.direction.tooltip.right": "ʇɥbıᴚ", "gtceu.direction.tooltip.up": "d∩", + "gtceu.display_source.computer_monitor_cover": "ɹǝʌoƆ ɹoʇıuoW ɹǝʇndɯoƆ", + "gtceu.display_target.computer_monitor_cover": "ɹǝʌoƆ ɹoʇıuoW ɹǝʇndɯoƆ", "gtceu.distillation_tower": "ɹǝʍo⟘ uoıʇɐןןıʇsıᗡ", "gtceu.distillery": "ʎɹǝןןıʇsıᗡ", "gtceu.duct_pipe.transfer_rate": "%s :ǝʇɐɹ ɹǝɟsuɐɹʇ ɹıⱯq§", @@ -2277,6 +2312,10 @@ "gtceu.gui.adv_stocking_config.ticks_per_cycle": "sǝʇɐpdn ʇsıן ɯǝʇı uǝǝʍʇǝq ʎɐןǝᗡ", "gtceu.gui.adv_stocking_config.title": "buıʞɔoʇS ɔıʇɐɯoʇnⱯ ǝɹnbıɟuoƆ", "gtceu.gui.auto_output.name": "oʇnɐ", + "gtceu.gui.central_monitor.group": "%s :dnoɹ⅁", + "gtceu.gui.central_monitor.group_default_name": "%d# dnoɹ⅁", + "gtceu.gui.central_monitor.none": "ǝuou", + "gtceu.gui.central_monitor.text_scale": "ǝןɐɔs ʇxǝ⟘", "gtceu.gui.charger_slot.tooltip.0": "ɹ§ʇoןS ɹǝbɹɐɥƆɟ§", "gtceu.gui.charger_slot.tooltip.1": "ɹ§sǝıɹǝʇʇɐq %s ɯoɹɟ ɹǝʍod sʍɐɹᗡㄥ§", "gtceu.gui.charger_slot.tooltip.2": "sǝıɹǝʇʇɐq puɐ sןooʇ %s sǝbɹɐɥƆㄥ§", @@ -2285,6 +2324,18 @@ "gtceu.gui.chunkmode.enabled.0": "˙ǝןqɐsıᗡ oʇ ʞɔıןƆ :pǝןqɐuƎ ǝpoW ʞunɥƆ", "gtceu.gui.chunkmode.enabled.1": "˙ǝuıɥɔɐɯ ǝןpı uɐ sǝɹınbǝɹ buıɥɔʇıʍSㄥ§", "gtceu.gui.circuit.title": "sbuıʇʇǝS ʇınɔɹıƆ", + "gtceu.gui.computer_monitor_cover.edit_blank_placeholders": "sɹǝpןoɥǝɔɐןd ʞuɐןq ʇıpƎ", + "gtceu.gui.computer_monitor_cover.edit_displayed_text": "ʇxǝʇ pǝʎɐןdsıp ʇıpƎ", + "gtceu.gui.computer_monitor_cover.main_textbox_tooltip.0": "˙ǝɹǝɥ %d ǝuıן uo ʎɐןdsıp oʇ buıɹʇs ʇnduI", + "gtceu.gui.computer_monitor_cover.main_textbox_tooltip.1": ",∩Ǝ }ʎʇıɔɐdɐƆʎbɹǝuǝ{/}ʎbɹǝuǝ{ :ʎbɹǝuƎ, :ǝןdɯɐxǝ ɹoɟ 'sɹǝpןoɥǝɔɐןd ǝʌɐɥ uɐɔ ʇI", + "gtceu.gui.computer_monitor_cover.main_textbox_tooltip.2": "˙sɹǝpןoɥǝɔɐןd ɹǝɥʇo ǝpısuı ǝq osןɐ uɐɔ sɹǝpןoɥǝɔɐןԀ", + "gtceu.gui.computer_monitor_cover.placeholder_reference.0": ":sɹǝpןoɥǝɔɐןd ןןⱯ", + "gtceu.gui.computer_monitor_cover.placeholder_reference.1": ")oɟuı ǝɹoɯ ɹoɟ ɹǝʌoɥ(", + "gtceu.gui.computer_monitor_cover.second_page_textbox_tooltip.0": "˙ǝɹǝɥ ,}{, %s ɟo ǝɔɐןd uı pǝsn ǝq oʇ ɹǝpןoɥǝɔɐןd ʇnduI", + "gtceu.gui.computer_monitor_cover.second_page_textbox_tooltip.1": "˙sǝxoq ʇxǝʇ ǝsǝɥʇ uı ,ʎʇıɔɐdɐƆʎbɹǝuǝ, puɐ ,ʎbɹǝuǝ, puɐ ,∩Ǝ }{/}{ :ʎbɹǝuƎ, buıɹʇs ɐ ǝʌɐɥ uɐɔ noʎ 'ǝןdɯɐxǝ ɹoℲ", + "gtceu.gui.computer_monitor_cover.slot_tooltip.0": "ǝɔuǝɹǝɟǝɹ uɐɔ sɹǝpןoɥǝɔɐןd ǝɯos ʇɐɥʇ sɯǝʇı ɹoɟ ʇoןs Ɐ", + "gtceu.gui.computer_monitor_cover.slot_tooltip.1": "%d :ɹǝqɯnu ʇoןS", + "gtceu.gui.computer_monitor_cover.update_interval": ")sʞɔıʇ uı( ןɐʌɹǝʇuı ǝʇɐpd∩", "gtceu.gui.config_slot": "ɹ§ʇoןS bıɟuoƆɟ§", "gtceu.gui.config_slot.auto_pull_managed": "ןןnԀ-oʇnⱯ ʎq pǝbɐuɐW ㄥ§:pǝןqɐsıᗡㄣ§", "gtceu.gui.config_slot.remove": "ɹ§˙ʇoןs bıɟuoɔ ㄥ§ɹɐǝןɔㄣ§ oʇ ʞɔıןɔ ʇɥbıᴚㄥ§", @@ -3403,6 +3454,171 @@ "gtceu.packer": "ɹǝʞɔɐԀ", "gtceu.part_sharing.disabled": "pǝןqɐsıᗡㄣ§ buıɹɐɥS ʞɔoןqıʇןnW", "gtceu.part_sharing.enabled": "pǝןqɐuƎɐ§ buıɹɐɥS ʞɔoןqıʇןnW", + "gtceu.placeholder_info.active.0": "˙ǝsıʍɹǝɥʇo 0 'ǝdıɔǝɹ ɐ buıuunɹ ʎןʇuǝɹɹnɔ sı oʇ pǝɥɔɐʇʇɐ sı ɹǝʌoɔ ǝɥʇ ʞɔoןq ǝɥʇ ɟı Ɩ ɐ suɹnʇǝᴚ", + "gtceu.placeholder_info.active.1": ":ǝbɐs∩", + "gtceu.placeholder_info.active.2": "ǝdıɔǝɹ buıuunɹ ʎןʇuǝɹɹnɔ ɐ s,ǝɹǝɥʇ ɹǝɥʇǝɥʍ >- }ǝʌıʇɔɐ{ ", + "gtceu.placeholder_info.ae2crafting.0": "˙uo sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ uı buıʇɟɐɹɔ-oʇnɐ ʇnoqɐ uoıʇɐɯɹoɟuı suɹnʇǝᴚ", + "gtceu.placeholder_info.ae2crafting.1": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2crafting.10": "ǝןpı sı ∩ԀƆ ǝɥʇ ɟı 0 ɹo ')spuoɔǝsouɐu uı( ʇɟɐɹɔ ǝɥʇ ɟo ʇɹɐʇs ǝɥʇ ɯoɹɟ pǝsdɐןǝ ǝɯıʇ ɟo ʇunoɯɐ ǝɥʇ >- }ǝɯıʇ >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.2": "ʞɹoʍʇǝu ƎW ǝɥʇ uı s∩ԀƆ buıʇɟɐɹɔ ɟo ʇunoɯɐ ǝɥʇ >- }ʇunoɯɐ ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.3": "sɐɥ ∩ԀƆ pǝıɟıɔǝds ǝɥʇ ǝbɐɹoʇs buıʇɟɐɹɔ ɟo ʇunoɯɐ ǝɥʇ >- }ǝbɐɹoʇs >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.4": "sɐɥ ∩ԀƆ pǝıɟıɔǝds ǝɥʇ sɹossǝɔoɹd-oɔ ɟo ʇunoɯɐ ǝɥʇ >- }spɐǝɹɥʇ >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.5": "∩ԀƆ buıʇɟɐɹɔ pǝıɟıɔǝds ǝɥʇ ɟo ǝɯɐu ǝɥʇ >- }ǝɯɐu >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.6": ")sʇsǝnbǝɹ ɥʇoq ɹo ɔıʇɐɯoʇnɐ 'ןɐnuɐɯ ɹoɟ pǝsn( ∩ԀƆ buıʇɟɐɹɔ pǝıɟıɔǝds ǝɥʇ ɟo ǝpoɯ uoıʇɔǝןǝs ǝɥʇ >- }ǝpoWuoıʇɔǝןǝs >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.7": "ǝןpı sı ∩ԀƆ ǝɥʇ ɟı 0 ɹo 'pǝʇsǝnbǝɹ sɐʍ ʇɐɥʇ ɯǝʇı ǝɥʇ ɟo ʇunoɯɐ ǝɥʇ >- }ʇunoɯɐ >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.8": "ǝןpı sı ∩ԀƆ ǝɥʇ ɟı 0 ɹo 'pǝʇsǝnbǝɹ sɐʍ ʇɐɥʇ ɯǝʇı ǝɥʇ ɟo ǝɯɐu ʎɐןdsıp ǝɥʇ >- }ɯǝʇı >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2crafting.9": "ǝןpı sı ∩ԀƆ ǝɥʇ ɟı 0 ɹo 'ssǝɹboɹd qoظ buıʇɟɐɹɔ ǝɥʇ >- }ssǝɹboɹd >xǝpuı< ʇǝb buıʇɟɐɹɔᄅǝɐ{ ", + "gtceu.placeholder_info.ae2energy.0": "˙uo sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ uı pǝɹoʇs ʎןʇuǝɹɹnɔ ʎbɹǝuǝ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.ae2energy.1": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2energy.2": ")sʇıun ƎⱯ uı( ʞɹoʍʇǝu ƎW ǝɥʇ uı ʎbɹǝuǝ ǝɥʇ >- }ʎbɹǝuǝᄅǝɐ{ ", + "gtceu.placeholder_info.ae2fluidCount.0": "˙oʇ pǝɥɔɐʇʇɐ sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ uı sɯǝʇı sʇunoɔ ʇnq 'ʇunoƆpınןɟ sɐ ǝɯɐS", + "gtceu.placeholder_info.ae2fluidCount.1": "¡bɐן ǝsnɐɔ ʎɐɯ spınןɟ ןןɐ buıʇunoɔ ʇɐɥʇ ǝʇoN", + "gtceu.placeholder_info.ae2fluidCount.2": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2fluidCount.3": "pǝıɟıɔǝds ɟı pIpınןɟ ɥʇıʍ pınןɟ ǝɥʇ ɹo 'spınןɟ ןןɐ ɟo ʇunoɯɐ ǝɥʇ >- }]pIpınןɟ[ ʇunoƆpınןɟ{ ", + "gtceu.placeholder_info.ae2itemCount.0": "˙oʇ pǝɥɔɐʇʇɐ sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ uı sɯǝʇı sʇunoɔ ʇnq 'ʇunoƆɯǝʇı sɐ ǝɯɐS", + "gtceu.placeholder_info.ae2itemCount.1": "¡bɐן ǝsnɐɔ ʎɐɯ sɯǝʇı ןןɐ ɹo ɹǝʇןıɟ ʎq buıʇunoɔ ʇɐɥʇ ǝʇoN", + "gtceu.placeholder_info.ae2itemCount.2": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2itemCount.3": "ʇunoɯɐ ɯǝʇı ןɐʇoʇ >- }ʇunoƆɯǝʇı{ ", + "gtceu.placeholder_info.ae2itemCount.4": "pı‾ɯǝʇı oʇ ןɐnbǝ spı ɥʇıʍ sɯǝʇı ɟo ʇunoɯɐ >- }>pı‾ɯǝʇı< ʇunoƆɯǝʇı{ ", + "gtceu.placeholder_info.ae2itemCount.5": "ɹǝʌoɔ sıɥʇ ɟo ʇoןs pǝıɟıɔǝds uı ɹǝʇןıɟ buıɥɔʇɐɯ sɯǝʇı ɟo ʇunoɯɐ >- }>pı‾ʇoןs< ɹǝʇןıɟ ʇunoƆɯǝʇı{ ", + "gtceu.placeholder_info.ae2maxPower.0": "˙uo sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ ɟo ʎʇıɔɐdɐɔ ʎbɹǝuǝ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.ae2maxPower.1": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2maxPower.2": "ʞɹoʍʇǝu ƎW ǝɥʇ ɟo ʎʇıɔɐdɐɔ ʎbɹǝuǝ ǝɥʇ >- }ɹǝʍoԀxɐɯᄅǝɐ{ ", + "gtceu.placeholder_info.ae2powerUsage.0": "˙uo sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ ɟo uoıʇdɯnsuoɔ ʎbɹǝuǝ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.ae2powerUsage.1": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2powerUsage.2": "ʞɹoʍʇǝu ƎW ǝɥʇ ɟo uoıʇdɯnsuoɔ ʎbɹǝuǝ ǝɥʇ >- }ǝbɐs∩ɹǝʍodᄅǝɐ{ ", + "gtceu.placeholder_info.ae2spatial.0": "˙uo sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ʞɹoʍʇǝu ƎW ǝɥʇ uı O/I ןɐıʇɐds ʇnoqɐ uoıʇɐɯɹoɟuı suɹnʇǝᴚ", + "gtceu.placeholder_info.ae2spatial.1": ":ǝbɐs∩", + "gtceu.placeholder_info.ae2spatial.2": "O/I ןɐıʇɐds ǝʇɐıʇıuı oʇ pǝɹınbǝɹ ɹǝʍod ɟo ʇunoɯɐ ǝɥʇ >- }ɹǝʍod ןɐıʇɐdsᄅǝɐ{ ", + "gtceu.placeholder_info.ae2spatial.3": ")SԀS( ǝɹnʇɔnɹʇS ʇuǝɯuıɐʇuoƆ ןɐıʇɐdS ǝɥʇ ɟo ʎɔuǝıɔıɟɟǝ ǝɥʇ >- }ʎɔuǝıɔıɟɟǝ ןɐıʇɐdsᄅǝɐ{ ", + "gtceu.placeholder_info.ae2spatial.4": "),}Zǝzıs{x}ʎǝzıs{x}Xǝzıs{ :ǝzıS, :ǝןdɯɐxǝ( sıxɐ pǝıɟıɔǝds ǝɥʇ buoןɐ SԀS ǝɥʇ ɟo ǝzıs ǝɥʇ >- }>Z|ʎ|X<ǝzıs ןɐıʇɐdsᄅǝɐ{ ", + "gtceu.placeholder_info.amperage.0": "˙uo sı ɹǝʌoɔ ǝɥʇ ǝןqɐɔ/ǝɹıʍ ǝɥʇ uı ǝbɐɹǝdɯɐ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.amperage.1": ":ǝbɐs∩", + "gtceu.placeholder_info.amperage.2": "ǝןqɐɔ/ǝɹıʍ ǝɥʇ uı ǝʇɐɹǝdɯɐ ǝɥʇ >- }ǝbɐɹǝdɯɐ{ ", + "gtceu.placeholder_info.bf.0": ":ǝbɐs∩", + "gtceu.placeholder_info.bf.1": "buıɹʇs ʎʇdɯǝ >- }>ǝpoɔ< >xǝpuı‾ʇoןs‾ɯǝʇı‾ɐʇɐp< ɟq{ ", + "gtceu.placeholder_info.block.0": "˙)█( ןoqɯʎs ʞɔoןq ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.block.1": ":ǝbɐs∩", + "gtceu.placeholder_info.block.2": ",█, >- }ʞɔoןq{ ", + "gtceu.placeholder_info.calc.0": "˙uoıʇɐɹǝdo ɹo uoıʇɔunɟ ɥʇɐɯ ɐ ɟo ʇןnsǝɹ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.calc.1": ":ǝbɐs∩", + "gtceu.placeholder_info.calc.2": "buıɹʇs‾ʎuɐ >- }>buıɹʇs‾ʎuɐ< ɔןɐɔ{ ", + "gtceu.placeholder_info.calc.3": "uoıʇɐɹǝdo pǝıɟıɔǝds ǝɥʇ ɟo ʇןnsǝɹ ǝɥʇ >- }>bɹɐ< >~|ʇɹbs|ןıǝɔ|ɹooןɟ|punoɹ< ɔןɐɔ{ ", + "gtceu.placeholder_info.calc.4": "uoıʇɐɹǝdo pǝıɟıɔǝds ǝɥʇ ɟo ʇןnsǝɹ ǝɥʇ >- }>bɹɐ‾puoɔǝs< >%|<<|>>|//|/|*|-|+< >bɹɐ‾ʇsɹıɟ< ɔןɐɔ{ ", + "gtceu.placeholder_info.cmd.0": "˙ʇndʇno ɹıǝɥʇ suɹnʇǝɹ puɐ spuɐɯɯoɔ ʇɟɐɹɔǝuıW sǝʇnɔǝxƎ", + "gtceu.placeholder_info.cmd.1": "˙ʇı ɥʇıʍ buıʞɔıןɔ-ʇɥbıɹ ʎq ɟןǝsɹnoʎ oʇ ɯǝʇı ɐʇɐp ʎuɐ puıq 'ɹǝʎɐןd ɐ oʇ punoq ɯǝʇı ɐʇɐp ɐ sǝɹınbǝᴚ", + "gtceu.placeholder_info.cmd.2": ":ǝbɐs∩", + "gtceu.placeholder_info.cmd.3": "ʇndʇno puɐɯɯoɔ >- }>puɐɯɯoɔ< >xǝpuı‾ʇoןs< pɯɔ{ ", + "gtceu.placeholder_info.cmp.0": "sʇuǝɯnbɹɐ s,ʇı uı uoıssǝɹdxǝ ǝɥʇ uo pǝsɐq 0 ɹo Ɩ ɐ suɹnʇǝᴚ", + "gtceu.placeholder_info.cmp.1": ":ǝbɐs∩", + "gtceu.placeholder_info.cmp.2": "=¡ '== '=< '=> '< '> ɟo ǝuo sı ɹoʇɐɹǝdo '0 ɹo Ɩ >- }>q< >ɹoʇɐɹǝdo< >ɐ< dɯɔ{ ", + "gtceu.placeholder_info.color.0": "˙pǝsn ǝq uɐɔ sɹoןoɔ ʇɐɥɔ ʇɟɐɹɔǝuıɯ ʇןnɐɟǝp ןןⱯ ˙ʇuǝɯnbɹɐ ʇsɹıɟ ǝɥʇ ɯoɹɟ ɹoןoɔ ǝɥʇ ɥʇıʍ pǝɹoןoɔ 'ʇuǝɯnbɹɐ puoɔǝs ǝɥʇ ɯoɹɟ ʇxǝʇ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.color.1": ":ǝbɐs∩", + "gtceu.placeholder_info.color.2": "ʇxǝʇ pǝɹoןoɔ >- }>ʇxǝʇ< >ɹoןoɔ< ɹoןoɔ{ ", + "gtceu.placeholder_info.combine.0": ")sʇuǝɯnbɹɐ ǝɥʇ uǝǝʍʇǝq sǝɔɐds ןןɐ buıdɐɔsǝ ʎq( buıɹʇs ǝןbuıs ɐ oʇuı sʇuǝɯnbɹɐ s,ʇı ɟo ןןɐ sǝuıqɯoƆ", + "gtceu.placeholder_info.combine.1": "\"ouɯ \\ןʞظ \\ıɥb \\ɟǝp \\ɔqɐ\" >- }ouɯ ןʞظ ıɥb ɟǝp ɔqɐ ǝuıqɯoɔ{ :ǝןdɯɐxƎ", + "gtceu.placeholder_info.combine.2": ":ǝbɐs∩", + "gtceu.placeholder_info.combine.3": "sɹǝpןoɥǝɔɐןd ɹǝɥʇɹnɟ uı ʇuǝɯnbɹɐ ǝןbuıs ɐ sɐ pǝʇɐǝɹʇ ǝq ןןıʍ ʇɐɥʇ buıɹʇs ɐ >- }˙˙˙ ]Ɛbɹɐ[ ]ᄅbɹɐ[ ]Ɩbɹɐ[ ǝuıqɯoɔ{ ", + "gtceu.placeholder_info.count.0": ")\"0˙0\" =¡ \"0\" os 'sbuıɹʇs sɐ pǝɹɐdɯoɔ( ʇsɹıɟ ǝɥʇ oʇ ןɐnbǝ ǝɹɐ sʇuǝɯnbɹɐ pǝpıʌoɹd ǝɥʇ ɟo ʎuɐɯ ʍoɥ suɹnʇǝᴚ", + "gtceu.placeholder_info.count.1": ":ǝbɐs∩", + "gtceu.placeholder_info.count.2": "ʇsɹıɟ ǝɥʇ oʇ ןɐnbǝ ǝɹɐ ʇɐɥʇ sʇuǝɯnbɹɐ ɟo ʇunoɯɐ ǝɥʇ >- }˙˙˙ ]ㄣbɹɐ[ ]Ɛbɹɐ[ ]ᄅbɹɐ[ >Ɩbɹɐ< ʇunoɔ{ ", + "gtceu.placeholder_info.data.0": "˙sʇoןs ǝɥʇ ɟo ǝuo uı )ǝןnpoɯ/qɹo/ʞɔıʇs ɐʇɐp( ɯǝʇı ɐʇɐp ɐ ɯoɹɟ ɐʇɐp ǝɯos sǝʌǝıɹʇǝɹ ɹo sǝɹoʇS", + "gtceu.placeholder_info.data.1": "˙)ʇqu ɯǝʇı ɐʇɐp ǝɥʇ uı pǝɹoʇs sı ʇɐɥʇ )Ɩ - ʎʇıɔɐdɐɔ( oʇ 0 ɯoɹɟ ɹǝbǝʇuı uɐ sı d( d ǝnןɐʌ ǝɥʇ ɥʇıʍ pǝɔɐןdǝɹ ǝq ןןıʍ ʇı 'ʎʇdɯǝ ʇuǝɯnbɹɐ >xǝpuı< ǝɥʇ ǝʌɐǝן noʎ ɟI", + "gtceu.placeholder_info.data.2": ":ǝbɐs∩", + "gtceu.placeholder_info.data.3": "ʇoןs pǝıɟıɔǝds ǝɥʇ uı ɯǝʇı ǝɥʇ uı pǝɹoʇs ɐʇɐp ǝɥʇ >- }>xǝpuı< >ʇoןs< ʇǝb ɐʇɐp{ ", + "gtceu.placeholder_info.data.4": "buıɹʇs ʎʇdɯǝ uɐ suɹnʇǝɹ 'ʇoןs pǝıɟıɔǝds ǝɥʇ uı ɯǝʇı ǝɥʇ uı pǝɹoʇs ɐʇɐp ǝɥʇ sʇǝs >- }>ǝnןɐʌ< >xǝpuı< >ʇoןs< ʇǝs ɐʇɐp{ ", + "gtceu.placeholder_info.data.5": "d >- }>ʇoןs< dʇǝb ɐʇɐp{ ", + "gtceu.placeholder_info.data.6": "buıɹʇs ʎʇdɯǝ uɐ suɹnʇǝɹ 'd sʇǝs >- }>ǝnןɐʌ< >ʇoןs< dʇǝs ɐʇɐp{ ", + "gtceu.placeholder_info.data.7": "0 oʇ d sʇǝs 'ʎʇıɔɐdɐɔ oʇ ןɐnbǝ ɹo uɐɥʇ ǝɹoɯ sǝɯoɔǝq d ɟı 'Ɩ ʎq d sʇuǝɯǝɹɔuı >- }>ʇoןs< ɔuı ɐʇɐp{ ", + "gtceu.placeholder_info.data.8": ")Ɩ - ʎʇıɔɐdɐɔ( oʇ d sʇǝs '0 uɐɥʇ ssǝן sǝɯoɔǝq d ɟı 'Ɩ ʎq d sʇuǝɯǝɹɔǝp >- }>ʇoןs< ɔǝp ɐʇɐp{ ", + "gtceu.placeholder_info.displayTarget.0": "˙ʞuıן ʎɐןdsıp ɐ buısn ɹǝʌoɔ sıɥʇ oʇ pǝʇʇıɯsuɐɹʇ sɐʍ ʇɐɥʇ ǝuıן pǝıɟıɔǝds ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.displayTarget.1": ":ǝbɐs∩", + "gtceu.placeholder_info.displayTarget.2": ")00Ɩ-Ɩ sı ɹǝqɯnu ǝuıן( ǝuıן pǝıɟıɔǝds ǝɥʇ uo ʇxǝʇ ǝɥʇ >- }>ɹǝqɯnu‾ǝuıן< ʇǝbɹɐ⟘ʎɐןdsıp{ ", + "gtceu.placeholder_info.energy.0": "˙pǝɹoʇs ʎbɹǝuǝ ɟo ʇunoɯɐ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.energy.1": ":ǝbɐs∩", + "gtceu.placeholder_info.energy.2": "pǝɹoʇs ʎbɹǝuǝ ɟo ʇunoɯɐ ǝɥʇ >- }ʎbɹǝuǝ{ ", + "gtceu.placeholder_info.energyCapacity.0": "pǝɹoʇs ǝq uɐɔ ʇɐɥʇ ʎbɹǝuǝ ɟo ʇunoɯɐ xɐɯ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.energyCapacity.1": ":ǝbɐs∩", + "gtceu.placeholder_info.energyCapacity.2": "ʎʇıɔɐdɐɔ ʎbɹǝuǝ ǝɥʇ >- }ʎʇıɔɐdɐƆʎbɹǝuǝ{", + "gtceu.placeholder_info.fluidCount.0": "˙)pǝɹǝʇןıɟ ǝq uɐɔ( spınןɟ ɟo ʇunoɯɐ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.fluidCount.1": ":ǝbɐs∩", + "gtceu.placeholder_info.fluidCount.2": "pǝıɟıɔǝds ɟı pIpınןɟ ɥʇıʍ pınןɟ ǝɥʇ ɹo 'spınןɟ ןןɐ ɟo ʇunoɯɐ ǝɥʇ >- }]pIpınןɟ[ ʇunoƆpınןɟ{ ", + "gtceu.placeholder_info.formatInt.0": "ɹǝbǝʇuı pǝpıʌoɹd ǝɥʇ ɟo uoıʇɐʇuǝsǝɹdǝɹ buıɹʇs ɐ suɹnʇǝᴚ", + "gtceu.placeholder_info.formatInt.1": "Wㄣᄅ˙Ɩ >- }ㄥϛㄣ9ƐᄅƖ ʇuIʇɐɯɹoɟ{ :ǝןdɯɐxƎ", + "gtceu.placeholder_info.formatInt.2": ":ǝbɐs∩", + "gtceu.placeholder_info.formatInt.3": "ʇuı ǝɥʇ ɟo uoıʇɐʇuǝsǝɹdǝɹ buıɹʇs >- }>bɹɐ< ʇuIʇɐɯɹoɟ{ ", + "gtceu.placeholder_info.fromAscii.0": "ǝpoɔ IIƆSⱯ pǝpıʌoɹd ǝɥʇ ʎq pǝʇuǝsǝɹdǝɹ ɹǝʇɔɐɹɐɥɔ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.fromAscii.1": ":ǝbɐs∩", + "gtceu.placeholder_info.fromAscii.2": "ɹǝʇɔɐɹɐɥɔ ɐ >- }>ǝpoɔ‾ɹɐɥɔ< ııɔsⱯɯoɹɟ{ ", + "gtceu.placeholder_info.if.0": "˙0 oʇ ןɐnbǝ ʇou sı puɐ buıɹʇs ʎʇdɯǝ uɐ ʇou sı ʇı ɟı ǝnɹʇ pǝɹǝpısuoɔ sı uoıʇıpuoɔ ǝɥ⟘ ˙uoıʇıpuoɔ ǝɥʇ uo buıpuǝdǝp sʇuǝɯnbɹɐ ǝɥʇ ɟo ǝuo suɹnʇǝᴚ", + "gtceu.placeholder_info.if.1": ":ǝbɐs∩", + "gtceu.placeholder_info.if.2": "}]ǝsןɐɟ‾ɟı‾pǝuɹnʇǝɹ[ >ǝnɹʇ‾ɟı‾pǝuɹnʇǝɹ< >uoıʇıpuoɔ< ɟı{ ", + "gtceu.placeholder_info.itemCount.0": "˙)pǝɹǝʇןıɟ ǝq uɐɔ( sɯǝʇı ɟo ʇunoɯɐ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.itemCount.1": ":ǝbɐs∩", + "gtceu.placeholder_info.itemCount.2": "ʇunoɯɐ ɯǝʇı ןɐʇoʇ >- }ʇunoƆɯǝʇı{ ", + "gtceu.placeholder_info.itemCount.3": "pı‾ɯǝʇı oʇ ןɐnbǝ spı ɥʇıʍ sɯǝʇı ɟo ʇunoɯɐ >- }>pı‾ɯǝʇı< ʇunoƆɯǝʇı{ ", + "gtceu.placeholder_info.itemCount.4": "ɹǝʌoɔ sıɥʇ ɟo ʇoןs pǝıɟıɔǝds uı ɹǝʇןıɟ buıɥɔʇɐɯ sɯǝʇı ɟo ʇunoɯɐ >- }>pı‾ʇoןs< ɹǝʇןıɟ ʇunoƆɯǝʇı{ ", + "gtceu.placeholder_info.maintenance.0": "˙ǝsıʍɹǝɥʇo 0 'oʇ pǝɥɔɐʇʇɐ sı ɹǝʌoɔ ǝɥʇ ʞɔoןq ǝɥʇ uı sɯǝןqoɹd ǝɔuɐuǝʇuıɐɯ ǝɹɐ ǝɹǝɥʇ ɟı Ɩ ɐ suɹnʇǝᴚ", + "gtceu.placeholder_info.maintenance.1": ",}ʞO ᗡƎᴚI∩ὉƎᴚ \\⅁NIXIℲ }ǝɔuɐuǝʇuıɐɯ{ ɟı{ :snʇɐʇs ǝɔuɐuǝʇuıɐW, :ǝןdɯɐxƎ", + "gtceu.placeholder_info.maintenance.2": ":ǝbɐs∩", + "gtceu.placeholder_info.maintenance.3": "sɯǝןqoɹd ǝɔuɐuǝʇuıɐɯ ǝɹɐ ǝɹǝɥʇ ɹǝɥʇǝɥʍ >- }ǝɔuɐuǝʇuıɐɯ{ ", + "gtceu.placeholder_info.maxProgress.0": "˙oʇ pǝɥɔɐʇʇɐ sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ǝdıɔǝɹ buıuunɹ ʎןʇuǝɹɹnɔ ǝɥʇ ɟo ssǝɹboɹd ɯnɯıxɐɯ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.maxProgress.1": ",%}00Ɩ * }}ssǝɹboɹԀxɐɯ{ / }ssǝɹboɹd{ ɔןɐɔ{ ɔןɐɔ{ :ssǝɹboɹԀ, :ǝןdɯɐxƎ", + "gtceu.placeholder_info.maxProgress.2": ":ǝbɐs∩", + "gtceu.placeholder_info.maxProgress.3": "ǝdıɔǝɹ buıuunɹ ʎןʇuǝɹɹnɔ ǝɥʇ ɟo ssǝɹboɹd xɐɯ ǝɥʇ >- }ssǝɹboɹԀxɐɯ{ ", + "gtceu.placeholder_info.nbt.0": "ʇoןs pǝıɟıɔǝds ǝɥʇ uı ɯǝʇı ǝɥʇ ɟo ɐʇɐp ʇqu ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.nbt.1": ":ǝbɐs∩", + "gtceu.placeholder_info.nbt.2": "ɐʇɐp ʇqu >- }>ʇoןs< ʇqu{ ", + "gtceu.placeholder_info.obf.0": "˙pǝʇɐɔsnɟqo 'ʇuǝɯnbɹɐ ʇsɹıɟ ǝɥʇ ɯoɹɟ ʇxǝʇ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.obf.1": ":ǝbɐs∩", + "gtceu.placeholder_info.obf.2": "ʇxǝʇ pǝʇɐɔsnɟqo >- }>ʇxǝʇ< ɟqo{ ", + "gtceu.placeholder_info.previousText.0": "˙)buıddɐɹʍ-ǝuıן ǝɹoɟǝq( ǝuıן pǝıɟıɔǝds ǝɥʇ ʇɐ ɹǝʌoɔ sıɥʇ ʎq pǝʎɐןdsıp ʎןsnoıʌǝɹd sɐʍ ʇɐɥʇ ʇxǝʇ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.previousText.1": ":ǝbɐs∩", + "gtceu.placeholder_info.previousText.2": ")Ɩ ʇɐ sʇɹɐʇs xǝpuı( ǝuıן pǝıɟıɔǝds ǝɥʇ uo pǝʎɐןdsıp ʎןsnoıʌǝɹd ʇxǝʇ ǝɥʇ >- }>ǝuıן< ʇxǝ⟘snoıʌǝɹd{ ", + "gtceu.placeholder_info.progress.0": "˙oʇ pǝɥɔɐʇʇɐ sı ɹǝʌoɔ sıɥʇ ʞɔoןq ǝɥʇ ɟo ǝdıɔǝɹ buıuunɹ ʎןʇuǝɹɹnɔ ǝɥʇ ɟo ssǝɹboɹd ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.progress.1": "}ssǝɹboɹԀxɐɯ{ puɐ 0 uǝǝʍʇǝq ɹǝbǝʇuı uɐ sı ssǝɹboɹd ʇɐɥʇ ǝʇoN", + "gtceu.placeholder_info.progress.2": ":ǝbɐs∩", + "gtceu.placeholder_info.progress.3": "ǝdıɔǝɹ buıuunɹ ʎןʇuǝɹɹnɔ ǝɥʇ ɟo ssǝɹboɹd ǝɥʇ >- }ssǝɹboɹd{ ", + "gtceu.placeholder_info.random.0": "˙)ǝʌısnןɔuı( ןɐʌɹǝʇuı pǝıɟıɔǝds ǝɥʇ uı ɹǝqɯnu ɯopuɐɹ ɐ suɹnʇǝᴚ", + "gtceu.placeholder_info.random.1": ":ǝbɐs∩", + "gtceu.placeholder_info.random.2": ")ǝʌısnןɔuı( xɐɯ puɐ uıɯ uǝǝʍʇǝq ɹǝqɯnu ɯopuɐɹ ɐ >- }>xɐɯ< >uıɯ< ɯopuɐɹ{ ", + "gtceu.placeholder_info.redstone.0": "ɥʇbuǝɹʇs ʇndʇno ǝuoʇspǝɹ ǝɥʇ sʇǝs ɹo ɥʇbuǝɹʇs ןɐubıs ǝuoʇspǝɹ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.redstone.1": ":ǝbɐs∩", + "gtceu.placeholder_info.redstone.2": "ǝpıs pǝıɟıɔǝds ǝɥʇ ʇɐ )ϛƖ-0( ɥʇbuǝɹʇs ןɐubıs ǝuoʇspǝɹ >- }>ʇsǝʍ|ʇsɐǝ|ɥʇnos|ɥʇɹou|uʍop|dn< ʇǝb ǝuoʇspǝɹ{ ", + "gtceu.placeholder_info.redstone.3": ")9-0 'ʇɥbıɹ oʇ ʇɟǝן ɯoɹɟ( ɹǝןןoɹʇuoɔ ǝɥʇ ǝpısuı ʎɔuǝnbǝɹɟ ǝɥʇ ɟo xǝpuı ǝɥʇ sı xǝpuı‾ʇoןs‾bǝɹɟ ˙xǝpuı‾ʇoןs# ʇoןs uı ɹǝןןoɹʇuoɔ pǝʞuıן ɐ ʎq pǝıɟıɔǝds ʎɔuǝnbǝɹɟ ʞuıן ǝuoʇspǝɹ ǝʇɐǝɹƆ ɐ ɟo ɥʇbuǝɹʇs ןɐubıs ǝuoʇspǝɹ >- }>xǝpuı‾ʇoןs‾bǝɹɟ< >xǝpuı‾ʇoןs< ʞuıן ʇǝb ǝuoʇspǝɹ{ ", + "gtceu.placeholder_info.redstone.4": "ǝpıs s,ɹǝʌoɔ sıɥʇ ɯoɹɟ ɥʇbuǝɹʇs ʇndʇno ǝuoʇspǝɹ ǝɥʇ sʇǝs 'buıɹʇs ʎʇdɯǝ >- }>ɹǝʍod< ʇǝs ǝuoʇspǝɹ{ ", + "gtceu.placeholder_info.redstone.5": "ʎɔuǝnbǝɹɟ ʞuıן ǝuoʇspǝɹ ǝʇɐǝɹƆ pǝıɟıɔǝds ǝɥʇ uo ɹǝʍod ǝuoʇspǝɹ pǝıɟıɔǝds ǝɥʇ sʇsɐɔpɐoɹq 'buıɹʇs ʎʇdɯǝ >- }>ɹǝʍod< >xǝpuı‾ʇoןs‾bǝɹɟ< >xǝpuı‾ʇoןs< ʞuıן ʇǝs ǝuoʇspǝɹ{ ", + "gtceu.placeholder_info.repeat.0": "˙ʇuǝɯnbɹɐ ʇsɹıɟ ǝɥʇ uı pǝıɟıɔǝds sǝɯıʇ ɟo ʇunoɯɐ ǝɥʇ pǝʇɐǝdǝɹ 'sʇuǝɯnbɹɐ puoɔǝs ǝɥʇ ɯoɹɟ ʇxǝʇ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.repeat.1": ":ǝbɐs∩", + "gtceu.placeholder_info.repeat.2": "sǝɯıʇ ɟo ʇunoɯɐ pǝıɟıɔǝds ǝɥʇ pǝʇɐǝdǝɹ ʇxǝʇ >- }>ʇxǝʇ< >ʇunoɯɐ< ʇɐǝdǝɹ{ ", + "gtceu.placeholder_info.select.0": ")0 ɯoɹɟ buıʇɹɐʇs( xǝpuı pǝıɟıɔǝds ǝɥʇ ʇɐ ʇuǝɯnbɹɐ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.select.1": ":ǝbɐs∩", + "gtceu.placeholder_info.select.2": "xǝpuı pǝıɟıɔǝds ǝɥʇ ʇɐ ʇuǝɯnbɹɐ >- ˙˙˙ ]Ɛbɹɐ[ ]ᄅbɹɐ[ ]Ɩbɹɐ[ >xǝpuı< ʇɔǝןǝs{ ", + "gtceu.placeholder_info.strike.0": "ʇno pǝssoɹɔ sɐʍ ʇı ɟı sɐ ʇı buıʎɐןdsıp 'ʇxǝʇ ʇsɹıɟ ǝɥʇ ɯoɹɟ ʇxǝʇ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.strike.1": ":ǝbɐs∩", + "gtceu.placeholder_info.strike.2": "ʇxǝʇ ʇno-pǝssoɹɔ >- }>ʇxǝʇ< ǝʞıɹʇs{ ", + "gtceu.placeholder_info.subList.0": ")0 ɯoɹɟ buıʇɹɐʇs( )ǝʌısnןɔxǝ( ɹ oʇ )ǝʌısnןɔuı( ן ɯoɹɟ sǝxǝpuı ɥʇıʍ ɯoɹɟ sʇuǝɯnbɹɐ suɹnʇǝᴚ", + "gtceu.placeholder_info.subList.1": ":ǝbɐs∩", + "gtceu.placeholder_info.subList.2": "sǝɔɐds ʎq pǝʇɐɹɐdǝs ɹ oʇ ן ɯoɹɟ sǝxǝpuı ɥʇıʍ sʇuǝɯnbɹɐ ןןɐ >- }˙˙˙ ]Ɩbɹɐ[ ]0bɹɐ[ >ʇɥbıɹ< >ʇɟǝן< ʇsıꞀqns{ ", + "gtceu.placeholder_info.tick.0": "˙pǝɔɐןd sɐʍ ɹǝʌoɔ sıɥʇ uǝɥʍ ɯoɹɟ pǝssɐd sʞɔıʇ ɟo ʇunoɯɐ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.tick.1": ":ǝbɐs∩", + "gtceu.placeholder_info.tick.2": "sʞɔıʇ ɟo ʇunoɯɐ ǝɥʇ >- }ʞɔıʇ{ ", + "gtceu.placeholder_info.tm.0": "ןoqɯʎs ™ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.tm.1": ":ǝbɐs∩", + "gtceu.placeholder_info.tm.2": "ןoqɯʎs ™ ǝɥʇ >- }ɯʇ{ ", + "gtceu.placeholder_info.toAscii.0": "ɹǝʇɔɐɹɐɥɔ pǝpıʌoɹd ǝɥʇ ɟo ǝpoɔ IIƆSⱯ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.toAscii.1": ":ǝbɐs∩", + "gtceu.placeholder_info.toAscii.2": "ɹǝʇɔɐɹɐɥɔ ǝɥʇ ɟo ǝpoɔ IIƆSⱯ >- }>ɹǝʇɔɐɹɐɥɔ< ııɔsⱯoʇ{ ", + "gtceu.placeholder_info.toChars.0": "ɯǝɥʇ uǝǝʍʇǝq sǝɔɐds ɥʇıʍ buıɹʇs pǝpıʌoɹd ǝɥʇ ɟo sɹǝʇɔɐɹɐɥɔ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.toChars.1": ",ǝ ן d ɯ ɐ x ǝ, >- }ǝןdɯɐxǝ sɹɐɥƆoʇ{ :ǝןdɯɐxƎ", + "gtceu.placeholder_info.toChars.2": ":ǝbɐs∩", + "gtceu.placeholder_info.toChars.3": "sɹǝʇɔɐɹɐɥɔ >- }>bɹɐ< sɹɐɥƆoʇ{ ", + "gtceu.placeholder_info.underline.0": "pǝuıןɹǝpun 'ʇuǝɯnbɹɐ ʇsɹıɟ ǝɥʇ ɯoɹɟ ʇxǝʇ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.underline.1": ":ǝbɐs∩", + "gtceu.placeholder_info.underline.2": "ʇxǝʇ pǝuıןɹǝpun >- }>ʇxǝʇ< ǝuıןɹǝpun{ ", + "gtceu.placeholder_info.voltage.0": "˙uo sı ɹǝʌoɔ ǝɥʇ ǝןqɐɔ/ǝɹıʍ ǝɥʇ uı ǝbɐʇןoʌ ǝɥʇ suɹnʇǝᴚ", + "gtceu.placeholder_info.voltage.1": ":ǝbɐs∩", + "gtceu.placeholder_info.voltage.2": "ǝןqɐɔ/ǝɹıʍ ǝɥʇ uı ǝbɐʇןoʌ ǝɥʇ >- }ǝbɐʇןoʌ{ ", "gtceu.plasma_generator": "ɹoʇɐɹǝuǝ⅁ ɐɯsɐןԀ", "gtceu.polarizer": "ɹǝzıɹɐןoԀ", "gtceu.primitive_blast_furnace": "ǝɔɐuɹnℲ ʇsɐןᗺ ǝʌıʇıɯıɹԀ", @@ -3537,9 +3753,12 @@ "gtceu.tool_action.wire_cutter.connect": "suoıʇɔǝuuoƆ ʇǝs oʇ sɹǝʇʇnƆ ǝɹıM ǝs∩8§", "gtceu.tool_action.wrench.connect": "suoıʇɔǝuuoƆ ʞɔoןq oʇ ʞɐǝus 'suoıʇɔǝuuoƆ ʇǝs oʇ ɥɔuǝɹM ǝs∩8§", "gtceu.tool_action.wrench.set_facing": "buıɔɐℲ ʇǝs oʇ ɥɔuǝɹM ǝs∩8§", + "gtceu.tooltip.computer_monitor_config": "ɐʇɐp uoıʇɐɹnbıɟuoɔ ɹǝʌoɔ ɹoʇıuoɯ ɹǝʇndɯoɔ buıɹoʇS", + "gtceu.tooltip.computer_monitor_data": "%s :ɐʇɐp buıɹoʇS", "gtceu.tooltip.fluid_pipe_hold_shift": "oɟuI ʇuǝɯuıɐʇuoƆ pınןℲ ʍoɥs oʇ ⟘ℲIHS pןoHㄥ§", "gtceu.tooltip.hold_ctrl": "oɟuı ǝɹoɯ ɹoɟ Ꞁᴚ⟘Ɔ pןoHㄥ§", "gtceu.tooltip.hold_shift": "oɟuı ǝɹoɯ ɹoɟ ⟘ℲIHS pןoHㄥ§", + "gtceu.tooltip.player_bind": "%s :ɹǝʎɐןd oʇ punoᗺ", "gtceu.tooltip.potion.each": "ɹ§buıuǝddɐɥ ɟo ǝɔuɐɥɔㄥ§ %s%% ɹ§ɐ ɥʇıʍ sʞɔıʇㄥ§ %s ɹ§ɹoɟㄥ§ %s %s", "gtceu.tooltip.potion.header": ":sʇɔǝɟɟǝ suıɐʇuoƆ9§", "gtceu.tooltip.proxy_bind": "%s %s %s ʇɐ ɹǝɟɟnᗺ uɹǝʇʇɐԀ ɐ oʇ buıpuıᗺɟ§", @@ -3547,6 +3766,7 @@ "gtceu.tooltip.status.trinary.true": "ǝnɹ⟘", "gtceu.tooltip.status.trinary.unknown": "uʍouʞu∩", "gtceu.tooltip.tool_fluid_hold_shift": "oɟuI ןoo⟘ puɐ ʇuǝɯuıɐʇuoƆ pınןℲ ʍoɥs oʇ ⟘ℲIHS pןoHㄥ§", + "gtceu.tooltip.wireless_transmitter_bind": "%s buıɔɐɟ %s %s %s ʇɐ ɹǝʌoɔ ɹǝʇʇıɯsuɐɹʇ ɐ oʇ buıpuıᗺ", "gtceu.top.allow_output_input": "ʇnduI ʍoןןⱯ", "gtceu.top.auto_output": "ʇndʇnO oʇnⱯ", "gtceu.top.buffer_bound_pos": "%s :Z '%s :ʎ '%s :X - o⟘ punoᗺ", @@ -3967,6 +4187,7 @@ "item.gtceu.ilc_chip.tooltip": "ʇınɔɹıƆ ɔıboꞀ pǝʇɐɹbǝʇuIㄥ§", "item.gtceu.ilc_wafer": "ɹǝɟɐM ƆꞀI", "item.gtceu.ilc_wafer.tooltip": "ʇınɔɹıƆ pǝʇɐɹbǝʇuI ʍɐᴚㄥ§", + "item.gtceu.image_module": "ǝןnpoW ǝbɐɯI", "item.gtceu.impure_bentonite_dust": "ǝʇıuoʇuǝᗺ ɟo ǝןıԀ ǝɹndɯI", "item.gtceu.impure_cassiterite_sand_dust": "puɐS ǝʇıɹǝʇıssɐƆ ɟo ǝןıԀ ǝɹndɯI", "item.gtceu.impure_pitchblende_dust": "ǝpuǝןqɥɔʇıԀ ɟo ǝןıԀ ǝɹndɯI", @@ -4401,6 +4622,7 @@ "item.gtceu.tantalum_capacitor": "ɹoʇıɔɐdɐƆ ɯnןɐʇuɐ⟘", "item.gtceu.terminal": "ןɐuıɯɹǝ⟘", "item.gtceu.terminal.tooltip": "ʞɔoןq-ıʇןnɯ ǝɥʇ pןınq ʎןןɐɔıʇɐɯoʇnɐ oʇ ɹǝןןoɹʇuoɔ ɐ uo ʞɔıןƆ-ᴚ + ʇɟıɥS", + "item.gtceu.text_module": "ǝןnpoW ʇxǝ⟘", "item.gtceu.tiny_ash_dust": "sǝɥsⱯ ɟo ǝןıԀ ʎuı⟘", "item.gtceu.tiny_basaltic_mineral_sand_dust": "puɐS ןɐɹǝuıW ɔıʇןɐsɐᗺ ɟo ǝןıԀ ʎuı⟘", "item.gtceu.tiny_bentonite_dust": "ǝʇıuoʇuǝᗺ ɟo ǝןıԀ ʎuı⟘", @@ -4633,6 +4855,7 @@ "item.gtceu.white_dye_spray_can": ")ǝʇıɥM( uɐƆ ʎɐɹdS", "item.gtceu.wire_extruder_mold": ")ǝɹıM( pןoW ɹǝpnɹʇxƎ", "item.gtceu.wire_extruder_mold.tooltip": "sǝɹıM buıʞɐɯ ɹoɟ ǝdɐɥS ɹǝpnɹʇxƎㄥ§", + "item.gtceu.wireless_transmitter_cover": "ɹǝʇʇıɯsuɐɹ⟘ ssǝןǝɹıM", "item.gtceu.wood_bolt": "ʞɔıʇS pooM ʇɹoɥS", "item.gtceu.wood_dust": "dןnԀ pooM", "item.gtceu.wood_plate": "ʞuɐןԀ pooM", diff --git a/src/generated/resources/assets/gtceu/lang/en_us.json b/src/generated/resources/assets/gtceu/lang/en_us.json index 62950800596..acc5ccda0f3 100644 --- a/src/generated/resources/assets/gtceu/lang/en_us.json +++ b/src/generated/resources/assets/gtceu/lang/en_us.json @@ -109,6 +109,7 @@ "block.gtceu.assembly_line_unit": "Assembly Control Casing", "block.gtceu.atomic_casing": "Atomic Casing", "block.gtceu.auto_maintenance_hatch": "Auto Maintenance Hatch", + "block.gtceu.basic_data_access_hatch": "Basic Data Access Hatch", "block.gtceu.bio_hazard_sign_block": "Bio Hazard Sign Block", "block.gtceu.black_borderless_lamp": "Black Borderless Lamp", "block.gtceu.black_lamp": "Black Lamp", @@ -138,6 +139,7 @@ "block.gtceu.casing_coke_bricks": "Coke Oven Bricks", "block.gtceu.casing_grate": "Grate Machine Casing", "block.gtceu.causality_hazard_sign_block": "Causality Hazard Sign Block", + "block.gtceu.central_monitor": "Central Monitor", "block.gtceu.charcoal_pile_igniter": "Charcoal Pile Igniter", "block.gtceu.chiseled_dark_concrete": "Chiseled Dark Concrete", "block.gtceu.chiseled_light_concrete": "Chiseled Light Concrete", @@ -799,6 +801,7 @@ "block.gtceu.mob_infestation_hazard_sign_block": "Mob Infestation Hazard Sign Block", "block.gtceu.mob_spawner_hazard_sign_block": "Mob Spawner Hazard Sign Block", "block.gtceu.molybdenum_disilicide_coil_block": "Molybdenum Disilicide Coil Block", + "block.gtceu.monitor": "Monitor", "block.gtceu.mossy_dark_concrete_bricks": "Mossy Dark Concrete Bricks", "block.gtceu.mossy_dark_concrete_cobblestone": "Mossy Dark Concrete Cobblestone", "block.gtceu.mossy_light_concrete_bricks": "Mossy Light Concrete Bricks", @@ -1807,6 +1810,7 @@ "config.gtceu.option.machineSounds": "machineSounds", "config.gtceu.option.machines": "machines", "config.gtceu.option.machinesEmissiveTextures": "machinesEmissiveTextures", + "config.gtceu.option.machinesHaveBERsByDefault": "machinesHaveBERsByDefault", "config.gtceu.option.meHatchEnergyUsage": "meHatchEnergyUsage", "config.gtceu.option.minerSpeed": "minerSpeed", "config.gtceu.option.minimap": "minimap", @@ -2178,6 +2182,20 @@ "gtceu.cable.superconductor": "%s §dSuperconductor", "gtceu.cable.voltage": "§aMax Voltage:§r §a%d §a(%s§a)", "gtceu.canner": "Canner", + "gtceu.central_monitor.gui.create_group": "Create group", + "gtceu.central_monitor.gui.currently_editing": "Currently editing: %s", + "gtceu.central_monitor.gui.remove_from_group": "Remove from group", + "gtceu.central_monitor.gui.set_target": "Set target", + "gtceu.central_monitor.info_tooltip.0": "In order to use monitors, you have to split them into groups first. A group may only have 1 module in it.", + "gtceu.central_monitor.info_tooltip.1": "Select them by left-clicking, then click 'Create group'.", + "gtceu.central_monitor.info_tooltip.2": "Then in the settings page for the group you can insert a module, you can configure it in the same page.", + "gtceu.central_monitor.info_tooltip.3": "To delete a group, select all of it's components and click 'Remove from group'.", + "gtceu.central_monitor.info_tooltip.4": "You can quickly select all components of a group by clicking on it's name. Click again to unselect.", + "gtceu.central_monitor.info_tooltip.5": "Some modules may display things depending on the block they target, to set a target for a group select any component of that group and right-click on the target component.", + "gtceu.central_monitor.info_tooltip.6": "You may wish to select a target that is not in the multiblock, you have to use the wireless transmitter cover for that.", + "gtceu.central_monitor.info_tooltip.7": "Place the cover on the target block, right-click it with a data stick and put that data stick into a data access hatch in the multiblock.", + "gtceu.central_monitor.info_tooltip.8": "Then select the data access hatch as the target, and set the slot index of your data stick in the number field that appeared.", + "gtceu.central_monitor.size": "Size: (%d+1+%d)x(%d+1+%d)", "gtceu.centrifuge": "Centrifuge", "gtceu.chance_logic.and": "AND", "gtceu.chance_logic.first": "FIRST", @@ -2191,6 +2209,21 @@ "gtceu.coke_oven": "Coke Oven", "gtceu.combustion_generator": "Combustion Generator", "gtceu.compressor": "Compressor", + "gtceu.computer_monitor_cover.error.bf_invalid": "Invalid character at %d", + "gtceu.computer_monitor_cover.error.bf_invalid_num": "Invalid number at index %d when processing symbol number %d", + "gtceu.computer_monitor_cover.error.exception": "Unexpected exception occurred: %s", + "gtceu.computer_monitor_cover.error.invalid_args": "Invalid arguments!", + "gtceu.computer_monitor_cover.error.invalid_number": "Invalid number '%s'!", + "gtceu.computer_monitor_cover.error.missing_item": "Missing %s in slot %d!", + "gtceu.computer_monitor_cover.error.no_ae": "Cover holder does not have an AE2 network!", + "gtceu.computer_monitor_cover.error.no_cover": "No cover!", + "gtceu.computer_monitor_cover.error.no_placeholder": "No such placeholder: '%s'!", + "gtceu.computer_monitor_cover.error.not_enough_args": "Expected at least %d args, got %d!", + "gtceu.computer_monitor_cover.error.not_in_range": "Expected %s to be between %d and %d (inclusive), got %d", + "gtceu.computer_monitor_cover.error.not_supported": "This feature is not supported by this block/cover!", + "gtceu.computer_monitor_cover.error.unclosed_bracket": "Unclosed bracket!", + "gtceu.computer_monitor_cover.error.unexpected_bracket": "Unexpected closing bracket!", + "gtceu.computer_monitor_cover.error.wrong_number_of_args": "Expected %d args, got %d!", "gtceu.cover.activity_detector.message_activity_inverted": "Monitoring Inverted Activity Status", "gtceu.cover.activity_detector.message_activity_normal": "Monitoring Normal Activity Status", "gtceu.cover.activity_detector_advanced.message_activity_inverted": "Monitoring Inverted Progress Status", @@ -2226,6 +2259,8 @@ "gtceu.direction.tooltip.left": "Left", "gtceu.direction.tooltip.right": "Right", "gtceu.direction.tooltip.up": "Up", + "gtceu.display_source.computer_monitor_cover": "Computer Monitor Cover", + "gtceu.display_target.computer_monitor_cover": "Computer Monitor Cover", "gtceu.distillation_tower": "Distillation Tower", "gtceu.distillery": "Distillery", "gtceu.duct_pipe.transfer_rate": "§bAir transfer rate: %s", @@ -2277,6 +2312,10 @@ "gtceu.gui.adv_stocking_config.ticks_per_cycle": "Delay between item list updates", "gtceu.gui.adv_stocking_config.title": "Configure Automatic Stocking", "gtceu.gui.auto_output.name": "auto", + "gtceu.gui.central_monitor.group": "Group: %s", + "gtceu.gui.central_monitor.group_default_name": "Group #%d", + "gtceu.gui.central_monitor.none": "none", + "gtceu.gui.central_monitor.text_scale": "Text scale", "gtceu.gui.charger_slot.tooltip.0": "§fCharger Slot§r", "gtceu.gui.charger_slot.tooltip.1": "§7Draws power from %s batteries§r", "gtceu.gui.charger_slot.tooltip.2": "§7Charges %s tools and batteries", @@ -2285,6 +2324,18 @@ "gtceu.gui.chunkmode.enabled.0": "Chunk Mode Enabled: Click to Disable.", "gtceu.gui.chunkmode.enabled.1": "§7Switching requires an idle machine.", "gtceu.gui.circuit.title": "Circuit Settings", + "gtceu.gui.computer_monitor_cover.edit_blank_placeholders": "Edit blank placeholders", + "gtceu.gui.computer_monitor_cover.edit_displayed_text": "Edit displayed text", + "gtceu.gui.computer_monitor_cover.main_textbox_tooltip.0": "Input string to display on line %d here.", + "gtceu.gui.computer_monitor_cover.main_textbox_tooltip.1": "It can have placeholders, for example: 'Energy: {energy}/{energyCapacity} EU'", + "gtceu.gui.computer_monitor_cover.main_textbox_tooltip.2": "Placeholders can also be inside other placeholders.", + "gtceu.gui.computer_monitor_cover.placeholder_reference.0": "All placeholders:", + "gtceu.gui.computer_monitor_cover.placeholder_reference.1": "(hover for more info)", + "gtceu.gui.computer_monitor_cover.second_page_textbox_tooltip.0": "Input placeholder to be used in place of %s '{}' here.", + "gtceu.gui.computer_monitor_cover.second_page_textbox_tooltip.1": "For example, you can have a string 'Energy: {}/{} EU' and 'energy' and 'energyCapacity' in these text boxes.", + "gtceu.gui.computer_monitor_cover.slot_tooltip.0": "A slot for items that some placeholders can reference", + "gtceu.gui.computer_monitor_cover.slot_tooltip.1": "Slot number: %d", + "gtceu.gui.computer_monitor_cover.update_interval": "Update interval (in ticks)", "gtceu.gui.config_slot": "§fConfig Slot§r", "gtceu.gui.config_slot.auto_pull_managed": "§4Disabled:§7 Managed by Auto-Pull", "gtceu.gui.config_slot.remove": "§7Right click to §4clear§7 config slot.§r", @@ -3403,6 +3454,171 @@ "gtceu.packer": "Packer", "gtceu.part_sharing.disabled": "Multiblock Sharing §4Disabled", "gtceu.part_sharing.enabled": "Multiblock Sharing §aEnabled", + "gtceu.placeholder_info.active.0": "Returns a 1 if the block the cover is attached to is currently running a recipe, 0 otherwise.", + "gtceu.placeholder_info.active.1": "Usage:", + "gtceu.placeholder_info.active.2": " {active} -> whether there's a currently running recipe", + "gtceu.placeholder_info.ae2crafting.0": "Returns information about auto-crafting in the ME network of the block this cover is on.", + "gtceu.placeholder_info.ae2crafting.1": "Usage:", + "gtceu.placeholder_info.ae2crafting.10": " {ae2crafting get time} -> the amount of time elapsed from the start of the craft (in nanoseconds), or 0 if the CPU is idle", + "gtceu.placeholder_info.ae2crafting.2": " {ae2crafting get amount} -> the amount of crafting CPUs in the ME network", + "gtceu.placeholder_info.ae2crafting.3": " {ae2crafting get storage} -> the amount of crafting storage the specified CPU has", + "gtceu.placeholder_info.ae2crafting.4": " {ae2crafting get threads} -> the amount of co-processors the specified CPU has", + "gtceu.placeholder_info.ae2crafting.5": " {ae2crafting get name} -> the name of the specified crafting CPU", + "gtceu.placeholder_info.ae2crafting.6": " {ae2crafting get selectionMode} -> the selection mode of the specified crafting CPU (used for manual, automatic or both requests)", + "gtceu.placeholder_info.ae2crafting.7": " {ae2crafting get amount} -> the amount of the item that was requested, or 0 if the CPU is idle", + "gtceu.placeholder_info.ae2crafting.8": " {ae2crafting get item} -> the display name of the item that was requested, or 0 if the CPU is idle", + "gtceu.placeholder_info.ae2crafting.9": " {ae2crafting get progress} -> the crafting job progress, or 0 if the CPU is idle", + "gtceu.placeholder_info.ae2energy.0": "Returns the energy currently stored in the ME network of the block this cover is on.", + "gtceu.placeholder_info.ae2energy.1": "Usage:", + "gtceu.placeholder_info.ae2energy.2": " {ae2energy} -> the energy in the ME network (in AE units)", + "gtceu.placeholder_info.ae2fluidCount.0": "Same as fluidCount, but counts items in the ME network of the block this cover is attached to.", + "gtceu.placeholder_info.ae2fluidCount.1": "Note that counting all fluids may cause lag!", + "gtceu.placeholder_info.ae2fluidCount.2": "Usage:", + "gtceu.placeholder_info.ae2fluidCount.3": " {fluidCount [fluidId]} -> the amount of all fluids, or the fluid with fluidId if specified", + "gtceu.placeholder_info.ae2itemCount.0": "Same as itemCount, but counts items in the ME network of the block this cover is attached to.", + "gtceu.placeholder_info.ae2itemCount.1": "Note that counting by filter or all items may cause lag!", + "gtceu.placeholder_info.ae2itemCount.2": "Usage:", + "gtceu.placeholder_info.ae2itemCount.3": " {itemCount} -> total item amount", + "gtceu.placeholder_info.ae2itemCount.4": " {itemCount } -> amount of items with ids equal to item_id", + "gtceu.placeholder_info.ae2itemCount.5": " {itemCount filter } -> amount of items matching filter in specified slot of this cover", + "gtceu.placeholder_info.ae2maxPower.0": "Returns the energy capacity of the ME network of the block this cover is on.", + "gtceu.placeholder_info.ae2maxPower.1": "Usage:", + "gtceu.placeholder_info.ae2maxPower.2": " {ae2maxPower} -> the energy capacity of the ME network", + "gtceu.placeholder_info.ae2powerUsage.0": "Returns the energy consumption of the ME network of the block this cover is on.", + "gtceu.placeholder_info.ae2powerUsage.1": "Usage:", + "gtceu.placeholder_info.ae2powerUsage.2": " {ae2powerUsage} -> the energy consumption of the ME network", + "gtceu.placeholder_info.ae2spatial.0": "Returns information about spatial I/O in the ME network of the block this cover is on.", + "gtceu.placeholder_info.ae2spatial.1": "Usage:", + "gtceu.placeholder_info.ae2spatial.2": " {ae2spatial power} -> the amount of power required to initiate spatial I/O", + "gtceu.placeholder_info.ae2spatial.3": " {ae2spatial efficiency} -> the efficiency of the Spatial Containment Structure (SPS)", + "gtceu.placeholder_info.ae2spatial.4": " {ae2spatial size} -> the size of the SPS along the specified axis (example: 'Size: {sizeX}x{sizeY}x{sizeZ}')", + "gtceu.placeholder_info.amperage.0": "Returns the amperage in the wire/cable the cover is on.", + "gtceu.placeholder_info.amperage.1": "Usage:", + "gtceu.placeholder_info.amperage.2": " {amperage} -> the amperate in the wire/cable", + "gtceu.placeholder_info.bf.0": "Usage:", + "gtceu.placeholder_info.bf.1": " {bf } -> empty string", + "gtceu.placeholder_info.block.0": "Returns the block symbol (█).", + "gtceu.placeholder_info.block.1": "Usage:", + "gtceu.placeholder_info.block.2": " {block} -> '█'", + "gtceu.placeholder_info.calc.0": "Returns the result of a math function or operation.", + "gtceu.placeholder_info.calc.1": "Usage:", + "gtceu.placeholder_info.calc.2": " {calc } -> any_string", + "gtceu.placeholder_info.calc.3": " {calc } -> the result of the specified operation", + "gtceu.placeholder_info.calc.4": " {calc <+|-|*|/|//|>>|<<|%> } -> the result of the specified operation", + "gtceu.placeholder_info.cmd.0": "Executes Minecraft commands and returns their output.", + "gtceu.placeholder_info.cmd.1": "Requires a data item bound to a player, bind any data item to yourself by right-clicking with it.", + "gtceu.placeholder_info.cmd.2": "Usage:", + "gtceu.placeholder_info.cmd.3": " {cmd } -> command output", + "gtceu.placeholder_info.cmp.0": "Returns a 1 or 0 based on the expression in it's arguments", + "gtceu.placeholder_info.cmp.1": "Usage:", + "gtceu.placeholder_info.cmp.2": " {cmp } -> 1 or 0, operator is one of >, <, >=, <=, ==, !=", + "gtceu.placeholder_info.color.0": "Returns the text from the second argument, colored with the color from the first argument. All default minecraft chat colors can be used.", + "gtceu.placeholder_info.color.1": "Usage:", + "gtceu.placeholder_info.color.2": " {color } -> colored text", + "gtceu.placeholder_info.combine.0": "Combines all of it's arguments into a single string (by escaping all spaces between the arguments)", + "gtceu.placeholder_info.combine.1": "Example: {combine abc def ghi jkl mno} -> \"abc\\ def\\ ghi\\ jkl\\ mno\"", + "gtceu.placeholder_info.combine.2": "Usage:", + "gtceu.placeholder_info.combine.3": " {combine [arg1] [arg2] [arg3] ...} -> a string that will be treated as a single argument in further placeholders", + "gtceu.placeholder_info.count.0": "Returns how many of the provided arguments are equal to the first (compared as strings, so \"0\" != \"0.0\")", + "gtceu.placeholder_info.count.1": "Usage:", + "gtceu.placeholder_info.count.2": " {count [arg2] [arg3] [arg4] ...} -> the amount of arguments that are equal to the first", + "gtceu.placeholder_info.data.0": "Stores or retrieves some data from a data item (data stick/orb/module) in one of the slots.", + "gtceu.placeholder_info.data.1": "If you leave the argument empty, it will be replaced with the value p (p is an integer from 0 to (capacity - 1) that is stored in the data item nbt).", + "gtceu.placeholder_info.data.2": "Usage:", + "gtceu.placeholder_info.data.3": " {data get } -> the data stored in the item in the specified slot", + "gtceu.placeholder_info.data.4": " {data set } -> sets the data stored in the item in the specified slot, returns an empty string", + "gtceu.placeholder_info.data.5": " {data getp } -> p", + "gtceu.placeholder_info.data.6": " {data setp } -> sets p, returns an empty string", + "gtceu.placeholder_info.data.7": " {data inc } -> increments p by 1, if p becomes more than or equal to capacity, sets p to 0", + "gtceu.placeholder_info.data.8": " {data dec } -> decrements p by 1, if p becomes less than 0, sets p to (capacity - 1)", + "gtceu.placeholder_info.displayTarget.0": "Returns the specified line that was transmitted to this cover using a display link.", + "gtceu.placeholder_info.displayTarget.1": "Usage:", + "gtceu.placeholder_info.displayTarget.2": " {displayTarget } -> the text on the specified line (line number is 1-100)", + "gtceu.placeholder_info.energy.0": "Returns the amount of energy stored.", + "gtceu.placeholder_info.energy.1": "Usage:", + "gtceu.placeholder_info.energy.2": " {energy} -> the amount of energy stored", + "gtceu.placeholder_info.energyCapacity.0": "Returns the max amount of energy that can be stored", + "gtceu.placeholder_info.energyCapacity.1": "Usage:", + "gtceu.placeholder_info.energyCapacity.2": "{energyCapacity} -> the energy capacity", + "gtceu.placeholder_info.fluidCount.0": "Returns the amount of fluids (can be filtered).", + "gtceu.placeholder_info.fluidCount.1": "Usage:", + "gtceu.placeholder_info.fluidCount.2": " {fluidCount [fluidId]} -> the amount of all fluids, or the fluid with fluidId if specified", + "gtceu.placeholder_info.formatInt.0": "Returns a string representation of the provided integer", + "gtceu.placeholder_info.formatInt.1": "Example: {formatInt 1236457} -> 1.24M", + "gtceu.placeholder_info.formatInt.2": "Usage:", + "gtceu.placeholder_info.formatInt.3": " {formatInt } -> string representation of the int", + "gtceu.placeholder_info.fromAscii.0": "Returns the character represented by the provided ASCII code", + "gtceu.placeholder_info.fromAscii.1": "Usage:", + "gtceu.placeholder_info.fromAscii.2": " {fromAscii } -> a character", + "gtceu.placeholder_info.if.0": "Returns one of the arguments depending on the condition. The condition is considered true if it is not an empty string and is not equal to 0.", + "gtceu.placeholder_info.if.1": "Usage:", + "gtceu.placeholder_info.if.2": " {if [returned_if_false]}", + "gtceu.placeholder_info.itemCount.0": "Returns the amount of items (can be filtered).", + "gtceu.placeholder_info.itemCount.1": "Usage:", + "gtceu.placeholder_info.itemCount.2": " {itemCount} -> total item amount", + "gtceu.placeholder_info.itemCount.3": " {itemCount } -> amount of items with ids equal to item_id", + "gtceu.placeholder_info.itemCount.4": " {itemCount filter } -> amount of items matching filter in specified slot of this cover", + "gtceu.placeholder_info.maintenance.0": "Returns a 1 if there are maintenance problems in the block the cover is attached to, 0 otherwise.", + "gtceu.placeholder_info.maintenance.1": "Example: 'Maintenance status: {if {maintenance} FIXING\\ REQUIRED OK}'", + "gtceu.placeholder_info.maintenance.2": "Usage:", + "gtceu.placeholder_info.maintenance.3": " {maintenance} -> whether there are maintenance problems", + "gtceu.placeholder_info.maxProgress.0": "Returns the maximum progress of the currently running recipe of the block this cover is attached to.", + "gtceu.placeholder_info.maxProgress.1": "Example: 'Progress: {calc {calc {progress} / {maxProgress}} * 100}%'", + "gtceu.placeholder_info.maxProgress.2": "Usage:", + "gtceu.placeholder_info.maxProgress.3": " {maxProgress} -> the max progress of the currently running recipe", + "gtceu.placeholder_info.nbt.0": "Returns the nbt data of the item in the specified slot", + "gtceu.placeholder_info.nbt.1": "Usage:", + "gtceu.placeholder_info.nbt.2": " {nbt } -> nbt data", + "gtceu.placeholder_info.obf.0": "Returns the text from the first argument, obfuscated.", + "gtceu.placeholder_info.obf.1": "Usage:", + "gtceu.placeholder_info.obf.2": " {obf } -> obfuscated text", + "gtceu.placeholder_info.previousText.0": "Returns the text that was previously displayed by this cover at the specified line (before line-wrapping).", + "gtceu.placeholder_info.previousText.1": "Usage:", + "gtceu.placeholder_info.previousText.2": " {previousText } -> the text previously displayed on the specified line (index starts at 1)", + "gtceu.placeholder_info.progress.0": "Returns the progress of the currently running recipe of the block this cover is attached to.", + "gtceu.placeholder_info.progress.1": "Note that progress is an integer between 0 and {maxProgress}", + "gtceu.placeholder_info.progress.2": "Usage:", + "gtceu.placeholder_info.progress.3": " {progress} -> the progress of the currently running recipe", + "gtceu.placeholder_info.random.0": "Returns a random number in the specified interval (inclusive).", + "gtceu.placeholder_info.random.1": "Usage:", + "gtceu.placeholder_info.random.2": " {random } -> a random number between min and max (inclusive)", + "gtceu.placeholder_info.redstone.0": "Returns the redstone signal strength or sets the redstone output strength", + "gtceu.placeholder_info.redstone.1": "Usage:", + "gtceu.placeholder_info.redstone.2": " {redstone get } -> redstone signal strength (0-15) at the specified side", + "gtceu.placeholder_info.redstone.3": " {redstone get link } -> redstone signal strength of a Create redstone link frequency specified by a linked controller in slot #slot_index. freq_slot_index is the index of the frequency inside the controller (from left to right, 0-6)", + "gtceu.placeholder_info.redstone.4": " {redstone set } -> empty string, sets the redstone output strength from this cover's side", + "gtceu.placeholder_info.redstone.5": " {redstone set link } -> empty string, broadcasts the specified redstone power on the specified Create redstone link frequency", + "gtceu.placeholder_info.repeat.0": "Returns the text from the second arguments, repeated the amount of times specified in the first argument.", + "gtceu.placeholder_info.repeat.1": "Usage:", + "gtceu.placeholder_info.repeat.2": " {repeat } -> text repeated the specified amount of times", + "gtceu.placeholder_info.select.0": "Returns the argument at the specified index (starting from 0)", + "gtceu.placeholder_info.select.1": "Usage:", + "gtceu.placeholder_info.select.2": " {select [arg1] [arg2] [arg3] ... -> argument at the specified index", + "gtceu.placeholder_info.strike.0": "Returns the text from the first text, displaying it as if it was crossed out", + "gtceu.placeholder_info.strike.1": "Usage:", + "gtceu.placeholder_info.strike.2": " {strike } -> crossed-out text", + "gtceu.placeholder_info.subList.0": "Returns arguments from with indexes from l (inclusive) to r (exclusive) (starting from 0)", + "gtceu.placeholder_info.subList.1": "Usage:", + "gtceu.placeholder_info.subList.2": " {subList [arg0] [arg1] ...} -> all arguments with indexes from l to r separated by spaces", + "gtceu.placeholder_info.tick.0": "Returns the amount of ticks passed from when this cover was placed.", + "gtceu.placeholder_info.tick.1": "Usage:", + "gtceu.placeholder_info.tick.2": " {tick} -> the amount of ticks", + "gtceu.placeholder_info.tm.0": "Returns the ™ symbol", + "gtceu.placeholder_info.tm.1": "Usage:", + "gtceu.placeholder_info.tm.2": " {tm} -> the ™ symbol", + "gtceu.placeholder_info.toAscii.0": "Returns the ASCII code of the provided character", + "gtceu.placeholder_info.toAscii.1": "Usage:", + "gtceu.placeholder_info.toAscii.2": " {toAscii } -> ASCII code of the character", + "gtceu.placeholder_info.toChars.0": "Returns the characters of the provided string with spaces between them", + "gtceu.placeholder_info.toChars.1": "Example: {toChars example} -> 'e x a m p l e'", + "gtceu.placeholder_info.toChars.2": "Usage:", + "gtceu.placeholder_info.toChars.3": " {toChars } -> characters", + "gtceu.placeholder_info.underline.0": "Returns the text from the first argument, underlined", + "gtceu.placeholder_info.underline.1": "Usage:", + "gtceu.placeholder_info.underline.2": " {underline } -> underlined text", + "gtceu.placeholder_info.voltage.0": "Returns the voltage in the wire/cable the cover is on.", + "gtceu.placeholder_info.voltage.1": "Usage:", + "gtceu.placeholder_info.voltage.2": " {voltage} -> the voltage in the wire/cable", "gtceu.plasma_generator": "Plasma Generator", "gtceu.polarizer": "Polarizer", "gtceu.primitive_blast_furnace": "Primitive Blast Furnace", @@ -3537,9 +3753,12 @@ "gtceu.tool_action.wire_cutter.connect": "§8Use Wire Cutters to set Connections", "gtceu.tool_action.wrench.connect": "§8Use Wrench to set Connections, sneak to block Connections", "gtceu.tool_action.wrench.set_facing": "§8Use Wrench to set Facing", + "gtceu.tooltip.computer_monitor_config": "Storing computer monitor cover configuration data", + "gtceu.tooltip.computer_monitor_data": "Storing data: %s", "gtceu.tooltip.fluid_pipe_hold_shift": "§7Hold SHIFT to show Fluid Containment Info", "gtceu.tooltip.hold_ctrl": "§7Hold CTRL for more info", "gtceu.tooltip.hold_shift": "§7Hold SHIFT for more info", + "gtceu.tooltip.player_bind": "Bound to player: %s", "gtceu.tooltip.potion.each": "%s %s §7for§r %s §7ticks with a§r %s%% §7chance of happening§r", "gtceu.tooltip.potion.header": "§6Contains effects:", "gtceu.tooltip.proxy_bind": "§fBinding to a Pattern Buffer at %s %s %s", @@ -3547,6 +3766,7 @@ "gtceu.tooltip.status.trinary.true": "True", "gtceu.tooltip.status.trinary.unknown": "Unknown", "gtceu.tooltip.tool_fluid_hold_shift": "§7Hold SHIFT to show Fluid Containment and Tool Info", + "gtceu.tooltip.wireless_transmitter_bind": "Binding to a transmitter cover at %s %s %s facing %s", "gtceu.top.allow_output_input": "Allow Input", "gtceu.top.auto_output": "Auto Output", "gtceu.top.buffer_bound_pos": "Bound To - X: %s, Y: %s, Z: %s", @@ -3967,6 +4187,7 @@ "item.gtceu.ilc_chip.tooltip": "§7Integrated Logic Circuit", "item.gtceu.ilc_wafer": "ILC Wafer", "item.gtceu.ilc_wafer.tooltip": "§7Raw Integrated Circuit", + "item.gtceu.image_module": "Image Module", "item.gtceu.impure_bentonite_dust": "Impure Pile of Bentonite", "item.gtceu.impure_cassiterite_sand_dust": "Impure Pile of Cassiterite Sand", "item.gtceu.impure_pitchblende_dust": "Impure Pile of Pitchblende", @@ -4401,6 +4622,7 @@ "item.gtceu.tantalum_capacitor": "Tantalum Capacitor", "item.gtceu.terminal": "Terminal", "item.gtceu.terminal.tooltip": "Shift + R-Click on a controller to automatically build the multi-block", + "item.gtceu.text_module": "Text Module", "item.gtceu.tiny_ash_dust": "Tiny Pile of Ashes", "item.gtceu.tiny_basaltic_mineral_sand_dust": "Tiny Pile of Basaltic Mineral Sand", "item.gtceu.tiny_bentonite_dust": "Tiny Pile of Bentonite", @@ -4633,6 +4855,7 @@ "item.gtceu.white_dye_spray_can": "Spray Can (White)", "item.gtceu.wire_extruder_mold": "Extruder Mold (Wire)", "item.gtceu.wire_extruder_mold.tooltip": "§7Extruder Shape for making Wires", + "item.gtceu.wireless_transmitter_cover": "Wireless Transmitter", "item.gtceu.wood_bolt": "Short Wood Stick", "item.gtceu.wood_dust": "Wood Pulp", "item.gtceu.wood_plate": "Wood Plank", diff --git a/src/generated/resources/assets/gtceu/models/block/machine/basic_data_access_hatch.json b/src/generated/resources/assets/gtceu/models/block/machine/basic_data_access_hatch.json new file mode 100644 index 00000000000..9c1ec49c439 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/block/machine/basic_data_access_hatch.json @@ -0,0 +1,22 @@ +{ + "parent": "minecraft:block/block", + "loader": "gtceu:machine", + "machine": "gtceu:basic_data_access_hatch", + "replaceable_textures": [ + "bottom", + "top", + "side" + ], + "variants": { + "": { + "model": { + "parent": "gtceu:block/machine/part/data_access_hatch", + "textures": { + "bottom": "gtceu:block/casings/voltage/hv/bottom", + "side": "gtceu:block/casings/voltage/hv/side", + "top": "gtceu:block/casings/voltage/hv/top" + } + } + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/machine/central_monitor.json b/src/generated/resources/assets/gtceu/models/block/machine/central_monitor.json new file mode 100644 index 00000000000..04b093f56b3 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/block/machine/central_monitor.json @@ -0,0 +1,91 @@ +{ + "parent": "minecraft:block/block", + "dynamic_renders": [ + { + "type": "gtceu:central_monitor" + } + ], + "loader": "gtceu:machine", + "machine": "gtceu:central_monitor", + "texture_overrides": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof" + }, + "variants": { + "is_formed=false,recipe_logic_status=idle": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front" + } + } + }, + "is_formed=false,recipe_logic_status=suspend": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front" + } + } + }, + "is_formed=false,recipe_logic_status=waiting": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/central_monitor/overlay_front_active_emissive" + } + } + }, + "is_formed=false,recipe_logic_status=working": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/central_monitor/overlay_front_active_emissive" + } + } + }, + "is_formed=true,recipe_logic_status=idle": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front" + } + } + }, + "is_formed=true,recipe_logic_status=suspend": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front" + } + } + }, + "is_formed=true,recipe_logic_status=waiting": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/central_monitor/overlay_front_active_emissive" + } + } + }, + "is_formed=true,recipe_logic_status=working": { + "model": { + "parent": "gtceu:block/machine/template/cube_all/sided", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof", + "overlay_front": "gtceu:block/multiblock/central_monitor/overlay_front_active", + "overlay_front_emissive": "gtceu:block/multiblock/central_monitor/overlay_front_active_emissive" + } + } + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/machine/monitor.json b/src/generated/resources/assets/gtceu/models/block/machine/monitor.json new file mode 100644 index 00000000000..a299f6adee3 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/block/machine/monitor.json @@ -0,0 +1,18 @@ +{ + "parent": "minecraft:block/block", + "loader": "gtceu:machine", + "machine": "gtceu:monitor", + "replaceable_textures": [ + "all" + ], + "variants": { + "": { + "model": { + "parent": "gtceu:block/machine/part/computer_monitor", + "textures": { + "all": "gtceu:block/casings/solid/machine_casing_frost_proof" + } + } + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/basic_data_access_hatch.json b/src/generated/resources/assets/gtceu/models/item/basic_data_access_hatch.json new file mode 100644 index 00000000000..184b9a346f3 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/basic_data_access_hatch.json @@ -0,0 +1,3 @@ +{ + "parent": "gtceu:block/machine/basic_data_access_hatch" +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/central_monitor.json b/src/generated/resources/assets/gtceu/models/item/central_monitor.json new file mode 100644 index 00000000000..f2f5c4f89cd --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/central_monitor.json @@ -0,0 +1,3 @@ +{ + "parent": "gtceu:block/machine/central_monitor" +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/image_module.json b/src/generated/resources/assets/gtceu/models/item/image_module.json new file mode 100644 index 00000000000..f9729b7b19c --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/image_module.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "gtceu:item/image_module" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/monitor.json b/src/generated/resources/assets/gtceu/models/item/monitor.json new file mode 100644 index 00000000000..c223679dd8e --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/monitor.json @@ -0,0 +1,3 @@ +{ + "parent": "gtceu:block/machine/monitor" +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/text_module.json b/src/generated/resources/assets/gtceu/models/item/text_module.json new file mode 100644 index 00000000000..87136145bae --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/text_module.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "gtceu:item/text_module" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/wireless_transmitter_cover.json b/src/generated/resources/assets/gtceu/models/item/wireless_transmitter_cover.json new file mode 100644 index 00000000000..0f1ab172aa1 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/wireless_transmitter_cover.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "gtceu:item/wireless_transmitter_cover" + } +} \ No newline at end of file diff --git a/src/main/java/com/gregtechceu/gtceu/GTCEu.java b/src/main/java/com/gregtechceu/gtceu/GTCEu.java index b572a9f53db..fee2fc2b308 100644 --- a/src/main/java/com/gregtechceu/gtceu/GTCEu.java +++ b/src/main/java/com/gregtechceu/gtceu/GTCEu.java @@ -221,5 +221,9 @@ public static boolean isGameStagesLoaded() { public static boolean isCCTweakedLoaded() { return isModLoaded(GTValues.MODID_CCTWEAKED); } + + public static boolean isCreateLoaded() { + return isModLoaded(GTValues.MODID_CREATE); + } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java index 9690dc48fc5..ebbac1d88ff 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java @@ -264,6 +264,14 @@ public static LazyOptional getCapability(MetaMachine machine, @NotNull Ca if (!list.isEmpty()) { return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } + } else if (cap == GTCapability.CAPABILITY_MONITOR_COMPONENT) { + if (machine instanceof IMonitorComponent monitorComponent) { + return GTCapability.CAPABILITY_MONITOR_COMPONENT.orEmpty(cap, LazyOptional.of(() -> monitorComponent)); + } + var list = getCapabilitiesFromTraits(machine.getTraits(), side, IMonitorComponent.class); + if (!list.isEmpty()) { + return GTCapability.CAPABILITY_MONITOR_COMPONENT.orEmpty(cap, LazyOptional.of(() -> list.get(0))); + } } if (GTCEu.Mods.isAE2Loaded()) { LazyOptional opt = AE2CallWrapper.getGridNodeHostCapability(cap, machine, side); diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java b/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java index 37a2038fced..49ba637ac4d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java @@ -12,6 +12,8 @@ import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.energy.IEnergyStorage; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,6 +30,16 @@ public static IEnergyStorage getForgeEnergyItem(ItemStack itemStack) { return itemStack.getCapability(ForgeCapabilities.ENERGY).resolve().orElse(null); } + @Nullable + public static IItemHandler getItemHandler(Level level, BlockPos pos, @Nullable Direction side) { + return getBlockEntityCapability(ForgeCapabilities.ITEM_HANDLER, level, pos, side); + } + + @Nullable + public static IFluidHandler getFluidHandler(Level level, BlockPos pos, @Nullable Direction side) { + return getBlockEntityCapability(ForgeCapabilities.FLUID_HANDLER, level, pos, side); + } + @Nullable public static IEnergyContainer getEnergyContainer(Level level, BlockPos pos, @Nullable Direction side) { return getBlockEntityCapability(GTCapability.CAPABILITY_ENERGY_CONTAINER, level, pos, side); @@ -105,6 +117,11 @@ public static IHazardParticleContainer getHazardContainer(Level level, BlockPos return getBlockEntityCapability(GTCapability.CAPABILITY_HAZARD_CONTAINER, level, pos, side); } + @Nullable + public static IMonitorComponent getMonitorComponent(Level level, BlockPos pos, @Nullable Direction side) { + return getBlockEntityCapability(GTCapability.CAPABILITY_MONITOR_COMPONENT, level, pos, side); + } + @Nullable private static T getBlockEntityCapability(Capability capability, Level level, BlockPos pos, @Nullable Direction side) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java b/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java index eaa8de82d9a..92dd3520740 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java @@ -212,6 +212,14 @@ static Direction rayTraceCoverableSide(ICoverable coverable, Player player) { return traceCoverSide(rayTrace); } + default boolean hasDynamicCovers() { + for (Direction face : GTUtil.DIRECTIONS) { + CoverBehavior cover = this.getCoverAtSide(face); + if (cover != null && cover.getDynamicRenderer().get() != null) return true; + } + return false; + } + class PrimaryBoxData { public final boolean usePlacementGrid; diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IMonitorComponent.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IMonitorComponent.java new file mode 100644 index 00000000000..aa9d1cb61fe --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/IMonitorComponent.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.api.capability; + +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; + +import net.minecraft.core.BlockPos; +import net.minecraftforge.items.IItemHandler; + +import org.jetbrains.annotations.Nullable; + +public interface IMonitorComponent { + + default boolean isMonitor() { + return false; + } + + IGuiTexture getComponentIcon(); + + BlockPos getPos(); + + default @Nullable IItemHandler getDataItems() { + return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java b/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java index dcd5a4dd44d..7be5f37b866 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java @@ -38,6 +38,8 @@ public class GTCapability { .get(new CapabilityToken<>() {}); public static final Capability CAPABILITY_HAZARD_CONTAINER = CapabilityManager .get(new CapabilityToken<>() {}); + public static final Capability CAPABILITY_MONITOR_COMPONENT = CapabilityManager + .get(new CapabilityToken<>() {}); public static final Capability CAPABILITY_MEDICAL_CONDITION_TRACKER = CapabilityManager .get(new CapabilityToken<>() {}); diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java b/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java index f1e1865a34c..d290f8bb306 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java @@ -9,6 +9,7 @@ import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; import com.gregtechceu.gtceu.client.renderer.cover.ICoverRenderer; +import com.gregtechceu.gtceu.client.renderer.cover.IDynamicCoverRenderer; import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; @@ -219,6 +220,10 @@ public BlockState getAppearance(BlockState sourceState, BlockPos sourcePos) { return null; } + public Supplier getDynamicRenderer() { + return () -> null; + } + ////////////////////////////////////// // ******* Capabilities *******// ////////////////////////////////////// diff --git a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/NumberInputWidget.java b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/NumberInputWidget.java index 53c6c49327e..fdcd8bf9884 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/gui/widget/NumberInputWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/api/gui/widget/NumberInputWidget.java @@ -10,6 +10,7 @@ import com.lowdragmc.lowdraglib.gui.util.ClickData; import com.lowdragmc.lowdraglib.gui.widget.ButtonWidget; import com.lowdragmc.lowdraglib.gui.widget.TextFieldWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.utils.Position; import com.lowdragmc.lowdraglib.utils.Size; @@ -111,6 +112,12 @@ public void readInitialData(FriendlyByteBuf buffer) { textField.setCurrentString(buffer.readUtf()); } + @Override + public Widget setHoverTooltips(String... tooltipText) { + textField.setHoverTooltips(tooltipText); + return super.setHoverTooltips(tooltipText); + } + private void buildUI() { int buttonWidth = Mth.clamp(this.getSize().width / 5, 15, 40); int textFieldWidth = this.getSize().width - (2 * buttonWidth) - 4; diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/IDataItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/IDataItem.java index b4c15f8a294..c4778df3637 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/IDataItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/IDataItem.java @@ -3,4 +3,6 @@ public interface IDataItem { boolean requireDataBank(); + + int getCapacity(); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/IMonitorModuleItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/IMonitorModuleItem.java new file mode 100644 index 00000000000..d7c01b2f671 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/IMonitorModuleItem.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.api.item.component; + +import com.gregtechceu.gtceu.client.renderer.monitor.IMonitorRenderer; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; + +import com.lowdragmc.lowdraglib.gui.widget.Widget; + +import net.minecraft.world.item.ItemStack; + +public interface IMonitorModuleItem extends IItemComponent { + + default void tick(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group) {} + + IMonitorRenderer getRenderer(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group); + + Widget createUIWidget(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/misc/ImageCache.java b/src/main/java/com/gregtechceu/gtceu/api/misc/ImageCache.java new file mode 100644 index 00000000000..de9b8410f25 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/misc/ImageCache.java @@ -0,0 +1,62 @@ +package com.gregtechceu.gtceu.api.misc; + +import com.gregtechceu.gtceu.GTCEu; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class ImageCache { + + public static final long REFRESH_SECS = 120; + public static final long EXPIRE_SECS = 300; + private static final byte[] NULL_MARKER = new byte[0]; + + private static boolean downloading = false; + + private static final LoadingCache CACHE = CacheBuilder.newBuilder() + .refreshAfterWrite(REFRESH_SECS, TimeUnit.SECONDS) + .expireAfterAccess(EXPIRE_SECS, TimeUnit.SECONDS) + .concurrencyLevel(3) + .build(CacheLoader.from(url -> { + if (downloading) return NULL_MARKER; + downloading = true; + + try (InputStream stream = new URL(url).openStream()) { + return stream.readAllBytes(); + } catch (IOException e) { + GTCEu.LOGGER.error("Could not load image {}", url, e); + downloading = false; + return NULL_MARKER; + } finally { + GTCEu.LOGGER.debug("Downloaded image {}! Executing callback", url); + downloading = false; + } + })); + + public static void queryServerImage(String url, Consumer callback) { + try { + if (downloading) return; + + byte[] image = CACHE.get(url); + if (image != NULL_MARKER) { + callback.accept(image); + } else { + CACHE.invalidate(url); + } + } catch (ExecutionException e) { + Throwable t = e; + if (t.getCause() != null) { + t = t.getCause(); + } + GTCEu.LOGGER.error("Could not load image {}", url, t); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java b/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java index 5eebbb030a9..eca90ad7f7c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java @@ -63,14 +63,14 @@ public MultiblockState(Level world, BlockPos controllerPos) { this.matchContext = new PatternMatchContext(); } - protected void clean() { + public void clean() { this.matchContext.reset(); this.globalCount = new Object2IntOpenHashMap<>(); this.layerCount = new Object2IntOpenHashMap<>(); cache = new LongOpenHashSet(); } - protected boolean update(BlockPos posIn, TraceabilityPredicate predicate) { + public boolean update(BlockPos posIn, TraceabilityPredicate predicate) { this.pos = posIn; this.blockState = null; this.tileEntity = null; diff --git a/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java b/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java index ff23ecc2fc4..87047c98ef9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java @@ -8,6 +8,7 @@ import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.machine.MachineDefinition; import com.gregtechceu.gtceu.api.machine.multiblock.IBatteryData; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; import com.gregtechceu.gtceu.api.pattern.error.PatternStringError; @@ -65,6 +66,14 @@ public static TraceabilityPredicate blocks(IMachineBlock... blocks) { new PredicateBlocks(Arrays.stream(blocks).map(IMachineBlock::self).toArray(Block[]::new))); } + public static TraceabilityPredicate machines(MachineDefinition... definitions) { + IMachineBlock[] machineBlocks = new IMachineBlock[definitions.length]; + for (int i = 0; i < machineBlocks.length; i++) { + machineBlocks[i] = definitions[i].get(); + } + return blocks(machineBlocks); + } + public static TraceabilityPredicate blockTag(TagKey tag) { return new TraceabilityPredicate(new PredicateBlockTag(tag)); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/IPlaceholderInfoProviderCover.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/IPlaceholderInfoProviderCover.java new file mode 100644 index 00000000000..586cb41c17b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/IPlaceholderInfoProviderCover.java @@ -0,0 +1,15 @@ +package com.gregtechceu.gtceu.api.placeholder; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; + +import java.util.List; + +public interface IPlaceholderInfoProviderCover { + + long getTicksSincePlaced(); + + List getCreateDisplayTargetBuffer(); + + void setDisplayTargetBufferLine(int line, MutableComponent component); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/MultiLineComponent.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/MultiLineComponent.java new file mode 100644 index 00000000000..dcaacaa61e4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/MultiLineComponent.java @@ -0,0 +1,145 @@ +package com.gregtechceu.gtceu.api.placeholder; + +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.ChatFormatting; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.*; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class MultiLineComponent extends ArrayList { + + public MultiLineComponent(List components) { + super(); + this.addAll(components); + } + + public static MultiLineComponent of(Component c) { + return new MultiLineComponent(List.of(c.copy())); + } + + public static MultiLineComponent literal(String s) { + return MultiLineComponent.of(Component.literal(s)); + } + + public static MultiLineComponent literal(long n) { + return MultiLineComponent.literal(String.valueOf(n)); + } + + public static MultiLineComponent literal(double n) { + return MultiLineComponent.literal(String.valueOf(n)); + } + + public static MultiLineComponent empty() { + return MultiLineComponent.of(CommonComponents.EMPTY); + } + + @Override + public boolean equals(Object o) { + if (o instanceof MultiLineComponent) + return Objects.equals(this.toString(), o.toString()); + return false; + } + + public boolean equalsString(String s) { + return Objects.equals(this.toString(), s); + } + + public String toString() { + StringBuilder out = new StringBuilder(); + if (this.isEmpty()) return out.toString(); + for (Component component : this) { + out.append(component.getString()); + out.append('\n'); + } + return out.substring(0, out.length() - 1); + } + + public double toDouble() { + if (this.isEmpty()) return 0; + if (this.size() > 1) throw new NumberFormatException(this.toString()); + return Double.parseDouble(this.get(0).getString()); + } + + public int toInt() { + if (this.isEmpty()) return 0; + if (this.size() > 1) throw new NumberFormatException(this.toString()); + return Integer.parseInt(this.get(0).getString()); + } + + public void append(@Nullable String s) { + if (s != null) + GTUtil.getLast(this).append(s); + } + + public void append(char c) { + append(String.valueOf(c)); + } + + public MultiLineComponent append(@Nullable List lines) { + if (lines == null) return this; + if (lines.isEmpty()) return this; + for (Component line : lines) { + GTUtil.getLast(this).append(line); + this.add(MutableComponent.create(ComponentContents.EMPTY)); + } + this.remove(this.size() - 1); + return this; + } + + public void appendNewline() { + this.add(MutableComponent.create(ComponentContents.EMPTY)); + } + + public MultiLineComponent withStyle(Style style) { + MultiLineComponent out = MultiLineComponent.empty(); + for (MutableComponent c : this) { + out.append(MultiLineComponent.of(c.withStyle(style))); + out.appendNewline(); + } + return out; + } + + public MultiLineComponent withStyle(ChatFormatting... style) { + MultiLineComponent out = MultiLineComponent.empty(); + for (MutableComponent c : this) { + out.append(MultiLineComponent.of(c.withStyle(style))); + out.appendNewline(); + } + return out; + } + + public List toImmutable() { + return new ArrayList<>(this); + } + + public Tag toTag() { + ListTag tag = new ListTag(); + for (MutableComponent component : this) { + tag.add(StringTag.valueOf(Component.Serializer.toJson(component))); + } + return tag; + } + + public static MultiLineComponent fromTag(ListTag tag) { + MultiLineComponent out = MultiLineComponent.empty(); + out.clear(); + for (Tag i : tag) { + out.add(Component.Serializer.fromJson(i.getAsString())); + } + return out; + } + + public long toLong() { + if (this.isEmpty()) return 0; + if (this.size() > 1) throw new NumberFormatException(this.toString()); + return Long.parseLong(this.get(0).getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/Placeholder.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/Placeholder.java new file mode 100644 index 00000000000..7f153477389 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/Placeholder.java @@ -0,0 +1,27 @@ +package com.gregtechceu.gtceu.api.placeholder; + +import com.gregtechceu.gtceu.api.placeholder.exceptions.PlaceholderException; + +import lombok.Getter; + +import java.util.List; + +public abstract class Placeholder { + + @Getter + private final String name; + @Getter + private final int priority; + + public abstract MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException; + + public Placeholder(String name) { + this(name, 0); + } + + public Placeholder(String name, int priority) { + this.name = name; + this.priority = priority; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderContext.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderContext.java new file mode 100644 index 00000000000..ab88492f62e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderContext.java @@ -0,0 +1,17 @@ +package com.gregtechceu.gtceu.api.placeholder; + +import com.gregtechceu.gtceu.api.cover.CoverBehavior; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraftforge.items.ItemStackHandler; + +import org.jetbrains.annotations.Nullable; + +public record PlaceholderContext(Level level, + BlockPos pos, + Direction side, + @Nullable ItemStackHandler itemStackHandler, + @Nullable CoverBehavior cover, + @Nullable MultiLineComponent previousText) {} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderHandler.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderHandler.java new file mode 100644 index 00000000000..6b8ba7e41b5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderHandler.java @@ -0,0 +1,161 @@ +package com.gregtechceu.gtceu.api.placeholder; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.placeholder.exceptions.PlaceholderException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.UnclosedBracketException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.UnexpectedBracketException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.UnknownPlaceholderException; +import com.gregtechceu.gtceu.data.lang.LangHandler; +import com.gregtechceu.gtceu.utils.GTStringUtils; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.texture.TextTexture; +import com.lowdragmc.lowdraglib.gui.widget.DraggableScrollableWidgetGroup; +import com.lowdragmc.lowdraglib.gui.widget.TextTextureWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; + +import net.minecraft.ChatFormatting; +import net.minecraft.MethodsReturnNonnullByDefault; + +import java.util.*; +import java.util.function.Consumer; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class PlaceholderHandler { + + private static final char ARG_SEPARATOR = ' '; + private static final char PLACEHOLDER_BEGIN = '{'; + private static final char PLACEHOLDER_END = '}'; + private static final char ESCAPE = '\\'; + private static final char LITERAL_ESCAPE = '"'; + private static final char NEWLINE = '\n'; + private static final char ESCAPED_NEWLINE = 'n'; + + private static final Map placeholders = new HashMap<>(); + + public static void addPlaceholder(Placeholder placeholder) { + if (placeholders.containsKey(placeholder.getName())) { + if (placeholders.get(placeholder.getName()).getPriority() <= placeholder.getPriority()) { + placeholders.put(placeholder.getName(), placeholder); + } + } else placeholders.put(placeholder.getName(), placeholder); + } + + public static boolean placeholderExists(MultiLineComponent placeholder) { + return placeholders.containsKey(placeholder.toString()); + } + + public static MultiLineComponent processPlaceholder(List placeholder, + PlaceholderContext context) throws PlaceholderException { + if (!placeholderExists(placeholder.get(0))) + throw new UnknownPlaceholderException(placeholder.get(0).toString()); + return placeholders.get(placeholder.get(0).toString()).apply(context, + placeholder.subList(1, placeholder.size())); + } + + public static MultiLineComponent processPlaceholders(String s, PlaceholderContext ctx) { + if (ctx.level().isClientSide) + GTCEu.LOGGER.warn("Placeholder processing is running on client instead of server!"); + List exceptions = new ArrayList<>(); + boolean escape = false; + boolean escapeNext = false; + boolean literalEscape = false; + int line = 1; + int symbol = 1; + Stack> stack = new Stack<>(); + stack.push(GTUtil.list(MultiLineComponent.empty())); + for (char c : s.toCharArray()) { + if (escape || (literalEscape && c != LITERAL_ESCAPE)) { + if (c == ESCAPED_NEWLINE && !literalEscape) { + GTUtil.getLast(stack.peek()).appendNewline(); + line++; + symbol = 0; + } else if (c == NEWLINE) continue; + else GTUtil.getLast(stack.peek()).append(c); + } else { + switch (c) { + case ESCAPE -> escapeNext = true; + case LITERAL_ESCAPE -> literalEscape = !literalEscape; + case NEWLINE -> { + GTUtil.getLast(stack.peek()).appendNewline(); + line++; + symbol = 0; + } + case ARG_SEPARATOR -> { + if (stack.size() == 1) GTUtil.getLast(stack.peek()).append(c); + else stack.peek().add(MultiLineComponent.empty()); + } + case PLACEHOLDER_BEGIN -> stack.push(GTUtil.list(MultiLineComponent.empty())); + case PLACEHOLDER_END -> { + List placeholder = stack.pop(); + try { + if (stack.isEmpty()) throw new UnexpectedBracketException(); + MultiLineComponent result = processPlaceholder(placeholder, ctx); + GTUtil.getLast(stack.peek()).append(result); + } catch (PlaceholderException e) { + e.setLineInfo(line, symbol); + exceptions.add(e); + } catch (RuntimeException e) { + exceptions.add(e); + } + } + default -> GTUtil.getLast(stack.peek()).append(c); + } + } + escape = escapeNext; + escapeNext = false; + symbol++; + } + if (stack.size() > 1) { + PlaceholderException exception = new UnclosedBracketException(); + exception.setLineInfo(line, symbol); + exceptions.add(exception); + } + if (exceptions.isEmpty()) + return stack.peek().stream().reduce(MultiLineComponent.empty(), MultiLineComponent::append); + MultiLineComponent out = MultiLineComponent.empty(); + exceptions.forEach(exception -> { + out.append(exception.getMessage()); + out.appendNewline(); + }); + return out.withStyle(ChatFormatting.DARK_RED); + } + + public static Set getAllPlaceholderNames() { + return placeholders.keySet(); + } + + public static Widget getPlaceholderHandlerUI(String filter) { + DraggableScrollableWidgetGroup placeholderReference = new DraggableScrollableWidgetGroup(280, 15, 100, 200); + Consumer onSearch = (newSearch) -> { + placeholderReference.clearAllWidgets(); + int y = 2; + ArrayList placeholders = new ArrayList<>(getAllPlaceholderNames()); + placeholders.removeIf(s -> s == null || !s.contains(newSearch)); + placeholders.sort(String::compareTo); + for (String placeholder : placeholders) { + TextTextureWidget placeholderName = new TextTextureWidget(0, y, 80, 15, placeholder); + placeholderName.getTextTexture().type = TextTexture.TextType.LEFT; + placeholderName.setHoverTooltips(GTStringUtils + .toImmutable(LangHandler.getSingleOrMultiLang("gtceu.placeholder_info." + placeholder))); + placeholderReference.addWidget(placeholderName); + y += 15; + } + }; + onSearch.accept(filter); + TextTextureWidget placeholderReferenceLabel = new TextTextureWidget( + 280, 0, + 160, 15, + GTStringUtils.componentsToString( + LangHandler.getMultiLang("gtceu.gui.computer_monitor_cover.placeholder_reference"))); + placeholderReferenceLabel.getTextTexture().type = TextTexture.TextType.LEFT; + WidgetGroup out = new WidgetGroup(); + out.addWidget(placeholderReferenceLabel); + out.addWidget(placeholderReference); + return out; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderUtils.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderUtils.java new file mode 100644 index 00000000000..eeb8f00cff3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/PlaceholderUtils.java @@ -0,0 +1,49 @@ +package com.gregtechceu.gtceu.api.placeholder; + +import com.gregtechceu.gtceu.api.placeholder.exceptions.InvalidNumberException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.NotEnoughArgsException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.OutOfRangeException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.WrongNumberOfArgsException; + +import java.util.List; + +public class PlaceholderUtils { + + public static void checkRange(String what, int min, int max, int n) throws OutOfRangeException { + if (n < min || n > max) throw new OutOfRangeException(what, min, max, n); + } + + public static int toInt(MultiLineComponent component) throws InvalidNumberException { + try { + return component.toInt(); + } catch (NumberFormatException e) { + throw new InvalidNumberException(component.toString()); + } + } + + public static double toDouble(MultiLineComponent component) throws InvalidNumberException { + try { + return component.toDouble(); + } catch (NumberFormatException e) { + throw new InvalidNumberException(component.toString()); + } + } + + public static void checkArgs(List args, int args_num) throws WrongNumberOfArgsException { + if (args.size() != args_num) throw new WrongNumberOfArgsException(args_num, args.size()); + } + + public static void checkArgs(List args, int args_num, + boolean allowMore) throws NotEnoughArgsException, WrongNumberOfArgsException { + if (!allowMore) checkArgs(args, args_num); + if (args.size() < args_num) throw new NotEnoughArgsException(args_num, args.size()); + } + + public static long toLong(MultiLineComponent component) throws InvalidNumberException { + try { + return component.toLong(); + } catch (NumberFormatException e) { + throw new InvalidNumberException(component.toString()); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/InvalidArgsException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/InvalidArgsException.java new file mode 100644 index 00000000000..2eec004295c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/InvalidArgsException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class InvalidArgsException extends PlaceholderException { + + public InvalidArgsException() { + super(Component.translatable("gtceu.computer_monitor_cover.error.invalid_args").getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/InvalidNumberException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/InvalidNumberException.java new file mode 100644 index 00000000000..ca2a5895ebf --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/InvalidNumberException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class InvalidNumberException extends PlaceholderException { + + public InvalidNumberException(String number) { + super(Component.translatable("gtceu.computer_monitor_cover.error.invalid_number", number).getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/MissingItemException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/MissingItemException.java new file mode 100644 index 00000000000..fa31f6c7206 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/MissingItemException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class MissingItemException extends PlaceholderException { + + public MissingItemException(String item, int slot) { + super(Component.translatable("gtceu.computer_monitor_cover.error.missing_item", item, slot).getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NoMENetworkException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NoMENetworkException.java new file mode 100644 index 00000000000..aecc8cfeba4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NoMENetworkException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class NoMENetworkException extends PlaceholderException { + + public NoMENetworkException() { + super(Component.translatable("gtceu.computer_monitor_cover.error.no_ae").getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NotEnoughArgsException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NotEnoughArgsException.java new file mode 100644 index 00000000000..b12b30db941 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NotEnoughArgsException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class NotEnoughArgsException extends PlaceholderException { + + public NotEnoughArgsException(int expected, int got) { + super(Component.translatable("gtceu.computer_monitor_cover.error.not_enough_args", expected, got).getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NotSupportedException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NotSupportedException.java new file mode 100644 index 00000000000..7218faaaf40 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/NotSupportedException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class NotSupportedException extends PlaceholderException { + + public NotSupportedException() { + super(Component.translatable("gtceu.computer_monitor_cover.error.not_supported").getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/OutOfRangeException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/OutOfRangeException.java new file mode 100644 index 00000000000..76c047abad7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/OutOfRangeException.java @@ -0,0 +1,11 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class OutOfRangeException extends PlaceholderException { + + public OutOfRangeException(String what, int min, int max, int provided) { + super(Component.translatable("gtceu.computer_monitor_cover.error.not_in_range", what, min, max, provided) + .getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/PlaceholderException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/PlaceholderException.java new file mode 100644 index 00000000000..30882b11f89 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/PlaceholderException.java @@ -0,0 +1,22 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +public class PlaceholderException extends Exception { + + private String message; + private int line = 0, symbol = 0; + + public PlaceholderException(String message) { + super(message); + this.message = message; + } + + public void setLineInfo(int line, int symbol) { + this.line = line; + this.symbol = symbol; + } + + @Override + public String getMessage() { + return "%d:%d: %s".formatted(line, symbol, message); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnclosedBracketException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnclosedBracketException.java new file mode 100644 index 00000000000..17ad606f6cc --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnclosedBracketException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class UnclosedBracketException extends PlaceholderException { + + public UnclosedBracketException() { + super(Component.translatable("gtceu.computer_monitor_cover.error.unclosed_bracket").getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnexpectedBracketException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnexpectedBracketException.java new file mode 100644 index 00000000000..df9db034f83 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnexpectedBracketException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class UnexpectedBracketException extends RuntimeException { + + public UnexpectedBracketException() { + super(Component.translatable("gtceu.computer_monitor_cover.error.unexpected_bracket").getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnknownPlaceholderException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnknownPlaceholderException.java new file mode 100644 index 00000000000..fc9efe4916b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/UnknownPlaceholderException.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class UnknownPlaceholderException extends PlaceholderException { + + public UnknownPlaceholderException(String name) { + super(Component.translatable("gtceu.computer_monitor_cover.error.no_placeholder", name).getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/WrongNumberOfArgsException.java b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/WrongNumberOfArgsException.java new file mode 100644 index 00000000000..88e3f6f6da2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/placeholder/exceptions/WrongNumberOfArgsException.java @@ -0,0 +1,11 @@ +package com.gregtechceu.gtceu.api.placeholder.exceptions; + +import net.minecraft.network.chat.Component; + +public class WrongNumberOfArgsException extends PlaceholderException { + + public WrongNumberOfArgsException(int expected, int got) { + super(Component.translatable("gtceu.computer_monitor_cover.error.wrong_number_of_args", expected, got) + .getString()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java index 2c9b20e235c..ba4a22eb513 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/registry/registrate/MachineBuilder.java @@ -115,7 +115,7 @@ public class MachineBuilder extends Builde @Setter private boolean allowExtendedFacing = false; @Setter - private boolean hasBER; + private boolean hasBER = ConfigHolder.INSTANCE.client.machinesHaveBERsByDefault; @Setter private boolean renderMultiblockWorldPreview = true; @Setter diff --git a/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java b/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java index 04b746146db..75b8029e9cb 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java +++ b/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java @@ -140,6 +140,8 @@ public static void initializeDynamicRenders() { DynamicRenderManager.register(GTCEu.id("boiler_multi_parts"), BoilerMultiPartRender.TYPE); DynamicRenderManager.register(GTCEu.id("fluid_area"), FluidAreaRender.TYPE); + + DynamicRenderManager.register(GTCEu.id("central_monitor"), CentralMonitorRender.TYPE); } @SubscribeEvent diff --git a/src/main/java/com/gregtechceu/gtceu/client/model/machine/MachineModel.java b/src/main/java/com/gregtechceu/gtceu/client/model/machine/MachineModel.java index 71dfd1343f8..c20eb618e68 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/model/machine/MachineModel.java +++ b/src/main/java/com/gregtechceu/gtceu/client/model/machine/MachineModel.java @@ -382,11 +382,7 @@ private List renderPartOverrides(MachineModel controllerModel, IMulti @Override public boolean isCustomRenderer() { - if (dynamicRenders.isEmpty()) return false; - for (DynamicRender render : dynamicRenders) { - if (render.isBlockEntityRenderer()) return true; - } - return false; + return true; } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -396,6 +392,9 @@ public void render(@NotNull BlockEntity blockEntity, float partialTick, int packedLight, int packedOverlay) { if (!(blockEntity instanceof IMachineBlockEntity machineBE)) return; if (machineBE.getDefinition() != getDefinition()) return; + ICoverableRenderer.super.renderDynamicCovers(machineBE.getMetaMachine(), partialTick, poseStack, buffer, + packedLight, + packedOverlay); if (dynamicRenders.isEmpty()) return; MetaMachine machine = machineBE.getMetaMachine(); @@ -453,6 +452,7 @@ public boolean shouldRenderOffScreen(BlockEntity blockEntity) { public boolean shouldRender(BlockEntity blockEntity, @NotNull Vec3 cameraPos) { if (!(blockEntity instanceof IMachineBlockEntity machineBE)) return false; if (machineBE.getDefinition() != getDefinition()) return false; + if (machineBE.getMetaMachine().getCoverContainer().hasDynamicCovers()) return true; if (dynamicRenders.isEmpty()) return false; MetaMachine machine = machineBE.getMetaMachine(); @@ -466,8 +466,6 @@ public boolean shouldRender(BlockEntity blockEntity, @NotNull Vec3 cameraPos) { @Override public int getViewDistance() { - if (dynamicRenders.isEmpty()) return 0; - int distance = 0; for (DynamicRender model : dynamicRenders) { distance = Math.max(distance, model.getViewDistance()); diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java index 7e0fcbd09aa..c36a41a91e6 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java @@ -1,13 +1,17 @@ package com.gregtechceu.gtceu.client.renderer; +import net.minecraft.Util; import net.minecraft.client.renderer.RenderStateShard; import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; +import java.util.function.Function; + @OnlyIn(Dist.CLIENT) public class GTRenderTypes extends RenderType { @@ -18,6 +22,16 @@ public class GTRenderTypes extends RenderType { .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) .createCompositeState(false)); + private static final Function GUI_TEXTURE = Util.memoize((texture) -> { + return create("gui_texture", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, + RenderType.TRANSIENT_BUFFER_SIZE, false, true, + RenderType.CompositeState.builder() + .setShaderState(RENDERTYPE_TEXT_SHADER) + .setTextureState(new RenderStateShard.TextureStateShard(texture, false, false)) + .setTransparencyState(TRANSLUCENT_TRANSPARENCY) + .setLightmapState(LIGHTMAP) + .createCompositeState(false)); + }); private GTRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, int bufferSize, boolean affectsCrumbling, boolean sortOnUpload, Runnable setupState, Runnable clearState) { @@ -27,4 +41,8 @@ private GTRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, public static RenderType getLightRing() { return LIGHT_RING; } + + public static RenderType guiTexture(ResourceLocation texture) { + return GUI_TEXTURE.apply(texture); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/CoverTextRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/CoverTextRenderer.java new file mode 100644 index 00000000000..95efa90be76 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/CoverTextRenderer.java @@ -0,0 +1,56 @@ +package com.gregtechceu.gtceu.client.renderer.cover; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.util.FormattedCharSequence; + +import com.mojang.blaze3d.vertex.PoseStack; +import lombok.Setter; + +import java.util.List; +import java.util.function.Supplier; + +public class CoverTextRenderer implements IDynamicCoverRenderer { + + private static final float TEXT_SCALE = 1 / 144f; + + @Setter + private Supplier> text; + + public CoverTextRenderer(Supplier> text) { + this.text = text; + } + + @Override + public void render(MetaMachine machine, Direction face, float partialTick, PoseStack poseStack, + MultiBufferSource buffer, int packedLight, int packedOverlay) { + poseStack.translate(3 / 16f, 3 / 16f, 0); + poseStack.scale(TEXT_SCALE, TEXT_SCALE, TEXT_SCALE); + int y = 0; + for (Component s : text.get()) { + boolean didAnything = false; + for (FormattedCharSequence line : Minecraft.getInstance().font.split(s, 90)) { + if (y >= 90) return; + Minecraft.getInstance().font.drawInBatch( + line, + 0, y, + 0x72e500, + false, + poseStack.last().pose(), + buffer, + Font.DisplayMode.NORMAL, + 0, + LightTexture.FULL_BRIGHT); + y += Minecraft.getInstance().font.lineHeight; + didAnything = true; + } + if (!didAnything) y += Minecraft.getInstance().font.lineHeight; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index aac37b2f697..041ae027db0 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -2,10 +2,14 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.client.util.RenderUtil; import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.client.bakedpipeline.FaceQuad; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlas; @@ -19,6 +23,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.model.data.ModelData; +import com.mojang.blaze3d.vertex.PoseStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -68,4 +73,22 @@ default void renderCovers(List quads, @NotNull ICoverable coverable, } } } + + @OnlyIn(Dist.CLIENT) + default void renderDynamicCovers(MetaMachine machine, float partialTick, PoseStack poseStack, + MultiBufferSource buffer, int packedLight, int packedOverlay) { + ICoverable coverable = machine.getCoverContainer(); + for (Direction face : GTUtil.DIRECTIONS) { + CoverBehavior cover = coverable.getCoverAtSide(face); + IDynamicCoverRenderer renderer = cover != null ? cover.getDynamicRenderer().get() : null; + if (renderer != null) { + poseStack.pushPose(); + RenderUtil.moveToFace(poseStack, .5f, .5f, .5f, face); + RenderUtil.rotateToFace(poseStack, face, Direction.NORTH); + poseStack.translate(-.5f, -.5f, .01f); + renderer.render(machine, face, partialTick, poseStack, buffer, packedLight, packedOverlay); + poseStack.popPose(); + } + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/IDynamicCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/IDynamicCoverRenderer.java new file mode 100644 index 00000000000..87e3b07670b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/IDynamicCoverRenderer.java @@ -0,0 +1,14 @@ +package com.gregtechceu.gtceu.client.renderer.cover; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; + +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.Direction; + +import com.mojang.blaze3d.vertex.PoseStack; + +public interface IDynamicCoverRenderer { + + void render(MetaMachine machine, Direction face, float partialTick, PoseStack poseStack, MultiBufferSource buffer, + int packedLight, int packedOverlay); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/DynamicRenderHelper.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/DynamicRenderHelper.java index 705dd9b9ae3..c6cd2f5c598 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/DynamicRenderHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/DynamicRenderHelper.java @@ -53,4 +53,8 @@ public class DynamicRenderHelper { public static DynamicRender createQuantumTankRender() { return new QuantumTankFluidRender(); } + + public static DynamicRender createCentralMonitorRender() { + return new CentralMonitorRender(); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/impl/CentralMonitorRender.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/impl/CentralMonitorRender.java new file mode 100644 index 00000000000..8ce0183cafc --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/impl/CentralMonitorRender.java @@ -0,0 +1,94 @@ +package com.gregtechceu.gtceu.client.renderer.machine.impl; + +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; +import com.gregtechceu.gtceu.api.item.ComponentItem; +import com.gregtechceu.gtceu.api.item.component.IItemComponent; +import com.gregtechceu.gtceu.api.item.component.IMonitorModuleItem; +import com.gregtechceu.gtceu.client.renderer.machine.DynamicRender; +import com.gregtechceu.gtceu.client.renderer.machine.DynamicRenderType; +import com.gregtechceu.gtceu.client.util.RenderUtil; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; + +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.BlockPos; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.serialization.Codec; + +public class CentralMonitorRender extends DynamicRender { + + // spotless:off + public static final Codec CODEC = Codec.unit(CentralMonitorRender::new); + public static final DynamicRenderType TYPE = new DynamicRenderType<>(CODEC); + // spotless:on + private static final float SCREEN_OFFSET_Z = 0.01f; + + public CentralMonitorRender() {} + + @Override + public DynamicRenderType getType() { + return TYPE; + } + + @Override + public void render(CentralMonitorMachine machine, float partialTick, PoseStack poseStack, MultiBufferSource buffer, + int packedLight, int packedOverlay) { + poseStack.pushPose(); + RenderUtil.moveToFace(poseStack, 0.5f, 0.5f, 0.5f, machine.getFrontFacing()); + RenderUtil.rotateToFace(poseStack, machine.getFrontFacing(), machine.getUpwardsFacing()); + poseStack.translate(-machine.getRightDist() - 0.5f, -machine.getUpDist() - 0.5f, SCREEN_OFFSET_Z); + + if (machine.getRecipeLogic().isActive()) { + for (MonitorGroup group : machine.getMonitorGroups()) { + ItemStack itemStack = group.getItemStackHandler().getStackInSlot(0); + if (!(itemStack.getItem() instanceof ComponentItem item)) { + continue; + } + for (IItemComponent component : item.getComponents()) { + if (!(component instanceof IMonitorModuleItem module)) { + continue; + } + poseStack.pushPose(); + module.getRenderer(group.getItemStackHandler().getStackInSlot(0), machine, group) + .render(machine, group, partialTick, poseStack, buffer, packedLight, packedOverlay); + poseStack.popPose(); + } + } + } + poseStack.popPose(); + } + + @Override + public boolean shouldRenderOffScreen(CentralMonitorMachine machine) { + return true; + } + + @Override + public boolean shouldRender(CentralMonitorMachine machine, Vec3 cameraPos) { + return machine.isFormed(); + } + + @Override + public AABB getRenderBoundingBox(CentralMonitorMachine machine) { + BlockPos pos = machine.getPos(); + BoundingBox bounds = new BoundingBox( + pos.getX() - 1, pos.getY() - 1, pos.getZ() - 1, + pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1); + + for (int row = 0; row <= machine.getUpDist() + machine.getDownDist(); row++) { + for (int col = 0; col <= machine.getLeftDist() + machine.getRightDist(); col++) { + IMonitorComponent component = machine.getComponent(row, col); + if (component != null && component.isMonitor()) { + // noinspection deprecation + bounds.encapsulate(component.getPos()); + } + } + } + return AABB.of(bounds); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/IMonitorRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/IMonitorRenderer.java new file mode 100644 index 00000000000..b217dc6c35c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/IMonitorRenderer.java @@ -0,0 +1,17 @@ +package com.gregtechceu.gtceu.client.renderer.monitor; + +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; + +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.mojang.blaze3d.vertex.PoseStack; + +public interface IMonitorRenderer { + + @OnlyIn(Dist.CLIENT) + void render(CentralMonitorMachine machine, MonitorGroup group, float partialTick, PoseStack poseStack, + MultiBufferSource buffer, int packedLight, int packedOverlay); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/MonitorImageRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/MonitorImageRenderer.java new file mode 100644 index 00000000000..a76b37ccf7d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/MonitorImageRenderer.java @@ -0,0 +1,49 @@ +package com.gregtechceu.gtceu.client.renderer.monitor; + +import com.gregtechceu.gtceu.client.renderer.GTRenderTypes; +import com.gregtechceu.gtceu.client.util.ClientImageCache; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import org.joml.Matrix4f; + +public class MonitorImageRenderer implements IMonitorRenderer { + + private final String url; + + public MonitorImageRenderer(String url) { + this.url = url; + } + + @Override + public void render(CentralMonitorMachine machine, MonitorGroup group, float partialTick, PoseStack poseStack, + MultiBufferSource buffer, int packedLight, int packedOverlay) { + BlockPos rel = group.getRow(0, machine::toRelative).get(0); + BlockPos size = GTUtil.getLast(group.getRow(-1, machine::toRelative)) + .offset(-rel.getX() + 1, -rel.getY() + 1, -rel.getZ() + 1); + + poseStack.translate(rel.getX(), rel.getY(), rel.getZ()); + + ResourceLocation textureId = ClientImageCache.getOrLoadTexture(url); + if (textureId == null) return; + + VertexConsumer consumer = buffer.getBuffer(GTRenderTypes.guiTexture(textureId)); + Matrix4f pose = poseStack.last().pose(); + + float minX = 0, maxX = size.getX(); + float minY = 0, maxY = size.getY(); + + consumer.vertex(pose, minX, maxY, 0).color(0xFFFFFFFF).uv(0, 1).uv2(LightTexture.FULL_BRIGHT).endVertex(); + consumer.vertex(pose, maxX, maxY, 0).color(0xFFFFFFFF).uv(1, 1).uv2(LightTexture.FULL_BRIGHT).endVertex(); + consumer.vertex(pose, maxX, minY, 0).color(0xFFFFFFFF).uv(1, 0).uv2(LightTexture.FULL_BRIGHT).endVertex(); + consumer.vertex(pose, minX, minY, 0).color(0xFFFFFFFF).uv(0, 0).uv2(LightTexture.FULL_BRIGHT).endVertex(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/MonitorTextRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/MonitorTextRenderer.java new file mode 100644 index 00000000000..029a85b5223 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/monitor/MonitorTextRenderer.java @@ -0,0 +1,76 @@ +package com.gregtechceu.gtceu.client.renderer.monitor; + +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.util.FormattedCharSequence; + +import com.mojang.blaze3d.vertex.PoseStack; + +import java.util.List; + +public class MonitorTextRenderer implements IMonitorRenderer { + + private static final float TEXT_SCALE = 1 / 144f; + private final List text; + private final float scale; + + public MonitorTextRenderer(List text, double scale) { + this.text = text; + this.scale = (float) scale; + } + + @Override + public void render(CentralMonitorMachine machine, MonitorGroup group, float partialTick, PoseStack poseStack, + MultiBufferSource buffer, int packedLight, int packedOverlay) { + try { + BlockPos rel = group.getRow(0, machine::toRelative).get(0); + int row = 0; + int columns = group.getRow(0, machine::toRelative).size(); + poseStack.translate(rel.getX(), rel.getY(), rel.getZ()); + poseStack.scale(TEXT_SCALE * scale, TEXT_SCALE * scale, TEXT_SCALE * scale); + float y = 9; + for (Component s : text) { + boolean didAnything = false; + for (FormattedCharSequence line : Minecraft.getInstance().font.split(s, + Math.round(columns * 135 / scale))) { + if (y >= 144) { + try { + row++; + columns = group.getRow(row, machine::toRelative).size(); + y -= 144; + poseStack.translate(-rel.getX() / (TEXT_SCALE * scale), -rel.getY() / (TEXT_SCALE * scale), + -rel.getZ() / (TEXT_SCALE * scale)); + rel = group.getRow(row, machine::toRelative).get(0); + poseStack.translate(rel.getX() / (TEXT_SCALE * scale), rel.getY() / (TEXT_SCALE * scale), + rel.getZ() / (TEXT_SCALE * scale)); + } catch (IndexOutOfBoundsException e) { + return; + } + } + Minecraft.getInstance().font.drawInBatch( + line, + 9, y, + 0xFFFFFF, + false, + poseStack.last().pose(), + buffer, + Font.DisplayMode.NORMAL, + 0, + LightTexture.FULL_BRIGHT); + y += Minecraft.getInstance().font.lineHeight * scale; + didAnything = true; + } + if (!didAnything) { + y += Minecraft.getInstance().font.lineHeight * scale; + } + } + } catch (IndexOutOfBoundsException ignored) {} + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/ClientImageCache.java b/src/main/java/com/gregtechceu/gtceu/client/util/ClientImageCache.java new file mode 100644 index 00000000000..1c93aafc26c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/util/ClientImageCache.java @@ -0,0 +1,105 @@ +package com.gregtechceu.gtceu.client.util; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.misc.ImageCache; +import com.gregtechceu.gtceu.common.network.GTNetwork; +import com.gregtechceu.gtceu.common.network.packets.CPacketImageRequest; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.AbstractTexture; +import net.minecraft.client.renderer.texture.DynamicTexture; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.mojang.blaze3d.platform.NativeImage; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +@OnlyIn(Dist.CLIENT) +public class ClientImageCache { + + private static final Map imageParts = new HashMap<>(); + + private static boolean downloading = false; + // TODO make some kind of loading icon for this + private static final AbstractTexture LOADING_TEXTURE_MARKER = new SimpleTexture( + GTCEu.id("textures/block/void.png")); + private static final LoadingCache CACHE = CacheBuilder.newBuilder() + .refreshAfterWrite(ImageCache.REFRESH_SECS, TimeUnit.SECONDS) + .expireAfterAccess(ImageCache.EXPIRE_SECS, TimeUnit.SECONDS) + .build(CacheLoader.from(url -> { + if (!downloading) { + downloading = true; + GTCEu.LOGGER.debug("Requesting image {}", url); + GTNetwork.sendToServer(new CPacketImageRequest(url)); + } + return LOADING_TEXTURE_MARKER; + })); + + private static @NotNull ResourceLocation getUrlTextureId(String url) { + return GTCEu.id("textures/central_monitor/image_" + url.hashCode()); + } + + public static @Nullable ResourceLocation getOrLoadTexture(String url) { + AbstractTexture texture = null; + + try { + texture = CACHE.get(url); + } catch (ExecutionException e) { + Throwable t = e; + if (t.getCause() != null) { + t = t.getCause(); + } + GTCEu.LOGGER.error("Could not load image {}", url, t); + } + if (texture == null || texture == LOADING_TEXTURE_MARKER) { + return null; + } + + return getUrlTextureId(url); + } + + @ApiStatus.Internal + public static void receiveImagePart(String url, byte[] imagePart, int index, + final int totalParts) throws IOException { + byte[][] parts = imageParts.computeIfAbsent(url, $ -> new byte[totalParts][]); + parts[index] = imagePart; + + if (index == totalParts - 1) { + byte[] imageBytes = new byte[imagePart.length]; + int currentIndex = 0; + for (byte[] part : parts) { + imageBytes = ArrayUtils.insert(currentIndex, imageBytes, part); + currentIndex += part.length; + } + + saveTexture(url, imageBytes); + imageParts.remove(url); + downloading = false; + } + } + + private static void saveTexture(String url, byte[] imageBytes) throws IOException { + ByteBuffer buffer = ByteBuffer.allocateDirect(imageBytes.length); + buffer.put(imageBytes).flip(); + DynamicTexture texture = new DynamicTexture(NativeImage.read(buffer)); + + Minecraft.getInstance().getTextureManager().register(getUrlTextureId(url), texture); + + CACHE.put(url, texture); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java index 84e6774efd7..21be19fddee 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java +++ b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java @@ -26,6 +26,7 @@ import com.gregtechceu.gtceu.api.recipe.lookup.ingredient.item.*; import com.gregtechceu.gtceu.api.registry.GTRegistries; import com.gregtechceu.gtceu.common.data.*; +import com.gregtechceu.gtceu.common.data.GTPlaceholders; import com.gregtechceu.gtceu.common.data.machines.GTMachineUtils; import com.gregtechceu.gtceu.common.data.materials.GTFoods; import com.gregtechceu.gtceu.common.item.tool.rotation.CustomBlockRotations; @@ -45,7 +46,9 @@ import com.gregtechceu.gtceu.data.pack.GTPackSource; import com.gregtechceu.gtceu.data.recipe.GTCraftingComponents; import com.gregtechceu.gtceu.forge.AlloyBlastPropertyAddition; +import com.gregtechceu.gtceu.integration.ae2.GTAEPlaceholders; import com.gregtechceu.gtceu.integration.cctweaked.CCTweakedPlugin; +import com.gregtechceu.gtceu.integration.create.GTCreateIntegration; import com.gregtechceu.gtceu.integration.kjs.GTCEuStartupEvents; import com.gregtechceu.gtceu.integration.kjs.GTRegistryInfo; import com.gregtechceu.gtceu.integration.kjs.events.MaterialModificationEventJS; @@ -132,6 +135,11 @@ public static void init() { TagPrefix.init(); GTSoundEntries.init(); GTDamageTypes.init(); + GTPlaceholders.initPlaceholders(); + if (GTCEu.Mods.isCreateLoaded()) { + GTCreateIntegration.init(); + GTAEPlaceholders.init(); + } GTCovers.init(); GTFluids.init(); GTCreativeModeTabs.init(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java index 86b0837504c..23b38c2f1fc 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java @@ -1,15 +1,104 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.api.cover.IUICover; +import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.api.machine.TickableSubscription; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; +import com.gregtechceu.gtceu.api.placeholder.IPlaceholderInfoProviderCover; +import com.gregtechceu.gtceu.api.placeholder.MultiLineComponent; +import com.gregtechceu.gtceu.api.placeholder.PlaceholderContext; +import com.gregtechceu.gtceu.api.placeholder.PlaceholderHandler; +import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.client.renderer.cover.CoverTextRenderer; +import com.gregtechceu.gtceu.client.renderer.cover.IDynamicCoverRenderer; +import com.gregtechceu.gtceu.data.lang.LangHandler; +import com.gregtechceu.gtceu.integration.create.GTCreateIntegration; +import com.gregtechceu.gtceu.utils.GTStringUtils; +import com.gregtechceu.gtceu.utils.GTUtil; +import com.lowdragmc.lowdraglib.gui.texture.ResourceBorderTexture; +import com.lowdragmc.lowdraglib.gui.widget.*; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Supplier; + +import javax.annotation.ParametersAreNonnullByDefault; + +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +public class ComputerMonitorCover extends CoverBehavior + implements IUICover, IDataStickInteractable, IPlaceholderInfoProviderCover { -public class ComputerMonitorCover extends CoverBehavior { + public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(ComputerMonitorCover.class, + CoverBehavior.MANAGED_FIELD_HOLDER); + + private TickableSubscription subscription; + private final CoverTextRenderer renderer; + @Persisted + private final List formatStringArgs = new ArrayList<>(8); + @Persisted + private final List formatStringLines = new ArrayList<>(8); + @Persisted + @DescSynced + @Getter + private List text = new ArrayList<>(); + @Persisted + public final CustomItemStackHandler itemStackHandler = new CustomItemStackHandler(8); + @Setter + private String placeholderSearch = ""; + @Setter + @Getter + @Persisted + private int updateInterval = 100; + @Getter + @Persisted + private long ticksSincePlaced = 0; + @Persisted + @Getter + private final List createDisplayTargetBuffer = new ArrayList<>(); public ComputerMonitorCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { super(definition, coverHolder, attachedSide); + renderer = new CoverTextRenderer(this::getText); + for (int i = 0; i < 100; i++) createDisplayTargetBuffer.add(MutableComponent.create(ComponentContents.EMPTY)); + } + + public List getRenderedText() { + String s = formatStringLines.stream().reduce((a, b) -> a + "\n" + b).orElse(""); + List tmp = new ArrayList<>(formatStringArgs); + tmp = tmp.stream().map(str -> '{' + str + '}').toList(); + return PlaceholderHandler.processPlaceholders( + GTStringUtils.replace(s, "\\{}", tmp), + new PlaceholderContext(coverHolder.getLevel(), coverHolder.getPos(), attachedSide, itemStackHandler, + this, new MultiLineComponent(text))); + } + + public void setDisplayTargetBufferLine(int line, MutableComponent component) { + createDisplayTargetBuffer.set(line, component); } @Override @@ -17,5 +106,169 @@ public boolean canPipePassThrough() { return false; } - // No implementation here, this cover is just for decorative purposes + @Override + public Supplier getDynamicRenderer() { + return () -> renderer; + } + + @Override + public @NotNull ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public Widget createUIWidget() { + int textFieldWidth = 160, horizontalPadding = 10, verticalPadding = 2; + final WidgetGroup group = new WidgetGroup(0, 0, 2 * textFieldWidth + 3 * horizontalPadding, 150); + final WidgetGroup mainPage = new WidgetGroup(0, 0, 2 * textFieldWidth + 3 * horizontalPadding, 150); + final WidgetGroup formatStringArgsPage = new WidgetGroup(0, 0, 2 * textFieldWidth + 3 * horizontalPadding, 150); + for (int i = 0; i < 8; i++) { + TextFieldWidget formatStringInput = new TextFieldWidget(); + formatStringInput.setSize(textFieldWidth, 15); + formatStringInput.setSelfPosition(horizontalPadding + textFieldWidth / 2, + 10 + verticalPadding + i * (15 + verticalPadding)); + formatStringInput.setHoverTooltips(GTStringUtils.toImmutable( + LangHandler.getMultiLang("gtceu.gui.computer_monitor_cover.main_textbox_tooltip", i + 1))); + int finalI = i; + if (i >= formatStringLines.size()) formatStringLines.add(""); + formatStringInput.setCurrentString(formatStringLines.get(i)); + formatStringInput.setTextResponder((s) -> formatStringLines.set(finalI, s)); + mainPage.addWidget(formatStringInput); + SlotWidget slot = new com.gregtechceu.gtceu.api.gui.widget.SlotWidget( + itemStackHandler, + i, + horizontalPadding + 50, + 20 * i); + slot.setBackgroundTexture(SlotWidget.ITEM_SLOT_TEXTURE); + slot.setHoverTooltips(GTStringUtils + .toImmutable(LangHandler.getMultiLang("gtceu.gui.computer_monitor_cover.slot_tooltip", i + 1))); + mainPage.addWidget(slot); + } + for (int i = 0; i < 8; i++) { + TextFieldWidget formatStringArgsInput = new TextFieldWidget(); + formatStringArgsInput.setSize(textFieldWidth, 15); + formatStringArgsInput.setSelfPosition(textFieldWidth / 2 + horizontalPadding, + 10 + verticalPadding + i * (15 + verticalPadding)); + formatStringArgsInput.setHoverTooltips(GTStringUtils.toImmutable( + LangHandler.getMultiLang("gtceu.gui.computer_monitor_cover.second_page_textbox_tooltip", + GTStringUtils.getIntOrderingSuffix(i + 1)))); + + int finalI = i; + if (i >= formatStringArgs.size()) formatStringArgs.add(""); + formatStringArgsInput.setCurrentString(formatStringArgs.get(i)); + formatStringArgsInput.setTextResponder((s) -> formatStringArgs.set(finalI, s)); + formatStringArgsPage.addWidget(formatStringArgsInput); + } + ButtonWidget switchToFormatStringArgsPageButton = new ButtonWidget( + horizontalPadding + 50, + 10 * (15 + verticalPadding) + verticalPadding, + 20, 20, + new ResourceBorderTexture(), + clickData -> { + group.clearAllWidgets(); + group.addWidget(formatStringArgsPage); + }); + ButtonWidget switchBack = new ButtonWidget( + horizontalPadding + 50, + 10 * (15 + verticalPadding) + verticalPadding, + 20, 20, + new ResourceBorderTexture(), + clickData -> { + group.clearAllWidgets(); + group.addWidget(mainPage); + }); + mainPage.addWidget(PlaceholderHandler.getPlaceholderHandlerUI("")); + // TextFieldWidget searchBox = new TextFieldWidget(280, 0, 80, 15, null, onSearch); + // searchBox.setHoverTooltips("Search"); + // mainPage.addWidget(searchBox); + IntInputWidget updateIntervalInput = new IntInputWidget(0, 0, 60, 20, this::getUpdateInterval, + this::setUpdateInterval); + updateIntervalInput.setMin(1); + updateIntervalInput.setMax(60 * 20); + updateIntervalInput + .setHoverTooltips(Component.translatable("gtceu.gui.computer_monitor_cover.update_interval")); + mainPage.addWidget(updateIntervalInput); + switchToFormatStringArgsPageButton + .setHoverTooltips(Component.translatable("gtceu.gui.computer_monitor_cover.edit_blank_placeholders")); + switchBack.setHoverTooltips(Component.translatable("gtceu.gui.computer_monitor_cover.edit_displayed_text")); + mainPage.addWidget(switchToFormatStringArgsPageButton); + formatStringArgsPage.addWidget(switchBack); + group.addWidget(mainPage); + return group; + } + + @Override + public void onLoad() { + super.onLoad(); + subscription = coverHolder.subscribeServerTick(subscription, this::update); + } + + private void update() { + ticksSincePlaced++; + if (coverHolder.getOffsetTimer() % updateInterval == 0) { + try { + if (GTCEu.Mods.isCreateLoaded()) + GTCreateIntegration.TemporaryRedstoneLinkTransmitter.destroyAll(); + setRedstoneSignalOutput(0); + text = getRenderedText(); + } catch (RuntimeException e) { + text = GTUtil + .list(Component.translatable("gtceu.computer_monitor_cover.error.exception", e.getMessage())); + } + } + } + + @Override + public void onRemoved() { + super.onRemoved(); + if (subscription != null) { + subscription.unsubscribe(); + } + } + + @Override + public boolean canConnectRedstone() { + return true; + } + + @Override + public List getAdditionalDrops() { + List drops = super.getAdditionalDrops(); + for (int i = 0; i < 8; i++) { + if (!itemStackHandler.getStackInSlot(i).isEmpty()) + drops.add(itemStackHandler.getStackInSlot(i)); + } + return drops; + } + + @Override + public InteractionResult onDataStickUse(Player player, ItemStack dataStick) { + CompoundTag tag = dataStick.getTagElement("computer_monitor_cover_config"); + if (tag == null) return InteractionResult.FAIL; + List stringLines = new ArrayList<>(); + ListTag stringLinesTag = tag.getList("lines", Tag.TAG_STRING); + for (int i = 0; i < stringLinesTag.size(); i++) stringLines.add(stringLinesTag.getString(i)); + formatStringLines.clear(); + formatStringLines.addAll(stringLines); + List stringArgs = new ArrayList<>(); + ListTag stringArgsTag = tag.getList("args", Tag.TAG_STRING); + for (int i = 0; i < stringArgsTag.size(); i++) stringArgs.add(stringArgsTag.getString(i)); + formatStringArgs.clear(); + formatStringArgs.addAll(stringArgs); + updateInterval = tag.getInt("updateInterval"); + return InteractionResult.SUCCESS; + } + + @Override + public InteractionResult onDataStickShiftUse(Player player, ItemStack dataStick) { + CompoundTag tag = dataStick.getOrCreateTagElement("computer_monitor_cover_config"); + ListTag stringLinesTag = new ListTag(); + formatStringLines.forEach(line -> stringLinesTag.add(StringTag.valueOf(line))); + tag.put("lines", stringLinesTag); + ListTag stringArgsTag = new ListTag(); + formatStringArgs.forEach(line -> stringArgsTag.add(StringTag.valueOf(line))); + tag.put("args", stringArgsTag); + tag.putInt("updateInterval", updateInterval); + return InteractionResult.SUCCESS; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/WirelessTransmitterCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/WirelessTransmitterCover.java new file mode 100644 index 00000000000..b66b219ca89 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/WirelessTransmitterCover.java @@ -0,0 +1,65 @@ +package com.gregtechceu.gtceu.common.cover; + +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; +import com.gregtechceu.gtceu.api.placeholder.IPlaceholderInfoProviderCover; + +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +public class WirelessTransmitterCover extends CoverBehavior + implements IDataStickInteractable, IPlaceholderInfoProviderCover { + + private static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + WirelessTransmitterCover.class, CoverBehavior.MANAGED_FIELD_HOLDER); + + @Getter + private final List createDisplayTargetBuffer = new ArrayList<>(); + + public WirelessTransmitterCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { + super(definition, coverHolder, attachedSide); + for (int i = 0; i < 100; i++) createDisplayTargetBuffer.add(MutableComponent.create(ComponentContents.EMPTY)); + } + + @Override + public InteractionResult onDataStickUse(Player player, ItemStack dataStick) { + dataStick.getOrCreateTag().putInt("targetX", coverHolder.getPos().getX()); + dataStick.getOrCreateTag().putInt("targetY", coverHolder.getPos().getY()); + dataStick.getOrCreateTag().putInt("targetZ", coverHolder.getPos().getZ()); + dataStick.getOrCreateTag().putString("face", attachedSide.getName()); + return InteractionResult.SUCCESS; + } + + @Override + public long getTicksSincePlaced() { + return coverHolder.getOffsetTimer(); + } + + @Override + public void setDisplayTargetBufferLine(int line, MutableComponent component) { + createDisplayTargetBuffer.set(line, component); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java index f59a1717e15..76f3b41e123 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java @@ -73,6 +73,10 @@ public class GTCovers { public final static CoverDefinition MACHINE_CONTROLLER = register("machine_controller", MachineControllerCover::new); + public final static CoverDefinition WIRELESS_TRANSMITTER = register( + "wireless_transmitter", + WirelessTransmitterCover::new, + () -> () -> new SimpleCoverRenderer(GTCEu.id("block/cover/wireless_transmitter"))); // Voiding public final static CoverDefinition ITEM_VOIDING = register("item_voiding", ItemVoidingCover::new); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java index 338be46274f..52264be6a38 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java @@ -26,6 +26,8 @@ import com.gregtechceu.gtceu.common.entity.GTBoat; import com.gregtechceu.gtceu.common.item.*; import com.gregtechceu.gtceu.common.item.armor.*; +import com.gregtechceu.gtceu.common.item.modules.ImageModuleBehaviour; +import com.gregtechceu.gtceu.common.item.modules.TextModuleBehaviour; import com.gregtechceu.gtceu.common.item.tool.behavior.LighterBehavior; import com.gregtechceu.gtceu.common.item.tool.behavior.MetaMachineConfigCopyBehaviour; import com.gregtechceu.gtceu.config.ConfigHolder; @@ -1423,13 +1425,13 @@ public static ItemEntry createFluidCell(Material mat, int capacit .register() : null; public static ItemEntry TOOL_DATA_STICK = REGISTRATE.item("data_stick", ComponentItem::create) - .lang("Data Stick").onRegister(attach(new DataItemBehavior())) + .lang("Data Stick").onRegister(attach(new DataItemBehavior(false, 8))) .register(); public static ItemEntry TOOL_DATA_ORB = REGISTRATE.item("data_orb", ComponentItem::create) - .lang("Data Orb").onRegister(attach(new DataItemBehavior())) + .lang("Data Orb").onRegister(attach(new DataItemBehavior(false, 64))) .register(); public static ItemEntry TOOL_DATA_MODULE = REGISTRATE.item("data_module", ComponentItem::create) - .lang("Data Module").onRegister(attach(new DataItemBehavior(true))) + .lang("Data Module").onRegister(attach(new DataItemBehavior(true, 256))) .register(); public static final Map> GLASS_LENSES = new HashMap<>(); @@ -1791,6 +1793,11 @@ public static ItemEntry createFluidCell(Material mat, int capacit new CoverPlaceBehavior(GTCovers.FLUID_FILTER))) .onRegister(materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.Zinc, GTValues.M * 3 / 2)))) .register(); + public static ItemEntry COVER_WIRELESS_TRANSMITTER = REGISTRATE + .item("wireless_transmitter_cover", ComponentItem::create) + .lang("Wireless Transmitter") + .onRegister(attach(new CoverPlaceBehavior(GTCovers.WIRELESS_TRANSMITTER))) + .register(); public static ItemEntry COVER_MACHINE_CONTROLLER = REGISTRATE .item("machine_controller_cover", ComponentItem::create) @@ -2501,6 +2508,14 @@ public static ItemEntry createFluidCell(Material mat, int capacit .lang("Treated Wood Boat with Chest") .register(); + public static ItemEntry TEXT_MODULE = REGISTRATE.item("text_module", ComponentItem::create) + .onRegister(attach(new TextModuleBehaviour())) + .register(); + + public static ItemEntry IMAGE_MODULE = REGISTRATE.item("image_module", ComponentItem::create) + .onRegister(attach(new ImageModuleBehaviour())) + .register(); + public static void init() { GTMaterialItems.generateMaterialItems(); GTMaterialItems.generateTools(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java index bf35e656b54..ed780c94e37 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java @@ -23,6 +23,7 @@ import com.gregtechceu.gtceu.common.data.models.GTModels; import com.gregtechceu.gtceu.common.machine.electric.*; import com.gregtechceu.gtceu.common.machine.multiblock.part.*; +import com.gregtechceu.gtceu.common.machine.multiblock.part.monitor.MonitorPartMachine; import com.gregtechceu.gtceu.common.machine.steam.SteamLiquidBoilerMachine; import com.gregtechceu.gtceu.common.machine.steam.SteamMinerMachine; import com.gregtechceu.gtceu.common.machine.steam.SteamSolarBoiler; @@ -1038,6 +1039,12 @@ public class GTMachines { PartAbility.INPUT_LASER); public static final MachineDefinition[] LASER_OUTPUT_HATCH_4096 = registerLaserHatch(OUT, 4096, PartAbility.OUTPUT_LASER); + public static final MachineDefinition MONITOR = REGISTRATE.machine("monitor", MonitorPartMachine::new) + .rotationState(RotationState.ALL) + .model(createOverlayCasingMachineModel(GTCEu.id("block/casings/solid/machine_casing_frost_proof"), + GTCEu.id("block/machine/part/computer_monitor"))) + .tier(MV) + .register(); public static void init() { GTMultiMachines.init(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTPlaceholders.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTPlaceholders.java new file mode 100644 index 00000000000..4b83e859957 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTPlaceholders.java @@ -0,0 +1,661 @@ +package com.gregtechceu.gtceu.common.data; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.IEnergyContainer; +import com.gregtechceu.gtceu.api.capability.IWorkable; +import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; +import com.gregtechceu.gtceu.api.item.ComponentItem; +import com.gregtechceu.gtceu.api.item.component.IDataItem; +import com.gregtechceu.gtceu.api.item.component.IItemComponent; +import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; +import com.gregtechceu.gtceu.api.placeholder.*; +import com.gregtechceu.gtceu.api.placeholder.exceptions.*; +import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; +import com.gregtechceu.gtceu.utils.GTStringUtils; + +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.Direction; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.registries.ForgeRegistries; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class GTPlaceholders { + + public static int countItems(String id, @Nullable IItemHandler itemHandler) { + if (itemHandler == null) return 0; + int cnt = 0; + for (int i = 0; i < itemHandler.getSlots(); i++) { + ItemStack itemStack = itemHandler.getStackInSlot(i); + String itemId = "%s:%s".formatted(itemStack.getItem().getCreatorModId(itemStack), + itemStack.getItem().toString()); + if (itemId.equals(id)) cnt += itemStack.getCount(); + } + return cnt; + } + + public static int countFluids(@Nullable String id, @Nullable IFluidHandler fluidHandler) { + if (fluidHandler == null) return 0; + int cnt = 0; + for (int i = 0; i < fluidHandler.getTanks(); i++) { + FluidStack fluidStack = fluidHandler.getFluidInTank(i); + String fluidId = Objects.requireNonNull(ForgeRegistries.FLUIDS.getKey(fluidStack.getFluid())).toString(); + if (id == null || fluidId.equals(id)) cnt += fluidStack.getAmount(); + } + return cnt; + } + + public static int countItems(@Nullable ItemFilter filter, @Nullable IItemHandler itemHandler) { + if (itemHandler == null) + return -1; + int cnt = 0; + for (int i = 0; i < itemHandler.getSlots(); i++) { + if (filter == null || filter.test(itemHandler.getStackInSlot(i))) + cnt += itemHandler.getStackInSlot(i).getCount(); + } + return cnt; + } + + public static void initPlaceholders() { + PlaceholderHandler.addPlaceholder(new Placeholder("energy") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + IEnergyContainer energy = GTCapabilityHelper.getEnergyContainer(ctx.level(), ctx.pos(), ctx.side()); + return MultiLineComponent.literal(energy != null ? energy.getEnergyStored() : 0); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("energyCapacity") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + IEnergyContainer energy = GTCapabilityHelper.getEnergyContainer(ctx.level(), ctx.pos(), ctx.side()); + return MultiLineComponent.literal(energy != null ? energy.getEnergyCapacity() : 0); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("calc") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, List args) { + List stringArgs = new ArrayList<>(); + args.forEach((components) -> stringArgs.add(GTStringUtils.componentsToString(components))); + return MultiLineComponent.literal(GTStringUtils.calc(stringArgs)); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("itemCount") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IItemHandler itemHandler = GTCapabilityHelper.getItemHandler(ctx.level(), ctx.pos(), ctx.side()); + if (args.isEmpty()) return MultiLineComponent.literal(countItems((ItemFilter) null, itemHandler)); + if (args.size() == 1) return MultiLineComponent + .literal(countItems(GTStringUtils.componentsToString(args.get(0)), itemHandler)); + if (GTStringUtils.equals(args.get(0), "filter")) { + int slot = PlaceholderUtils.toInt(args.get(1)); + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + try { + if (ctx.itemStackHandler() == null) + throw new NotSupportedException(); + return MultiLineComponent.literal(countItems( + ItemFilter.loadFilter(ctx.itemStackHandler().getStackInSlot(slot - 1)), itemHandler)); + } catch (NullPointerException e) { + throw new MissingItemException("filter", slot); + } + } + throw new InvalidArgsException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("fluidCount") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IFluidHandler fluidHandler = GTCapabilityHelper.getFluidHandler(ctx.level(), ctx.pos(), ctx.side()); + if (args.isEmpty()) return MultiLineComponent.literal(countFluids(null, fluidHandler)); + if (args.size() == 1) + return MultiLineComponent + .literal(countFluids(GTStringUtils.componentsToString(args.get(0)), fluidHandler)); + PlaceholderUtils.checkArgs(args, 1); + return MultiLineComponent.empty(); // unreachable + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("if") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2, true); + try { + if (GTStringUtils.toDouble(args.get(0)) != 0) { + return args.get(1); + } else if (args.size() > 2) return args.get(2); + else return MultiLineComponent.empty(); + } catch (NumberFormatException e) { + return args.get(1); + } + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("color") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2); + ChatFormatting color = ChatFormatting.getByName(GTStringUtils.componentsToString(args.get(0))); + if (color == null) throw new InvalidArgsException(); + return new MultiLineComponent(args.get(1).stream().map(c -> c.withStyle(color)).toList()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("underline") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + return new MultiLineComponent( + args.get(0).stream().map(c -> c.withStyle(ChatFormatting.UNDERLINE)).toList()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("strike") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + return new MultiLineComponent( + args.get(0).stream().map(c -> c.withStyle(ChatFormatting.STRIKETHROUGH)).toList()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("obf") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + return new MultiLineComponent( + args.get(0).stream().map(c -> c.withStyle(ChatFormatting.OBFUSCATED)).toList()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("random") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2); + return MultiLineComponent.literal(GTValues.RNG.nextIntBetweenInclusive( + PlaceholderUtils.toInt(args.get(0)), PlaceholderUtils.toInt(args.get(1)))); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("repeat") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2); + int count = PlaceholderUtils.toInt(args.get(0)); + MultiLineComponent out = MultiLineComponent.empty(); + for (int i = 0; i < count; i++) out.append(args.get(1)); + return out; + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("block") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + return MultiLineComponent.literal("█"); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("tick") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + if (ctx.cover() instanceof IPlaceholderInfoProviderCover cover) + return MultiLineComponent.literal(cover.getTicksSincePlaced()); + throw new NotSupportedException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("select") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1, true); + int i = PlaceholderUtils.toInt(args.get(0)); + PlaceholderUtils.checkArgs(args, i + 2); + return args.get(i + 1); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("redstone") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2, true); + if (GTStringUtils.equals(args.get(0), "get")) { + Direction direction = Direction.byName(GTStringUtils.componentsToString(args.get(1))); + if (direction == null) + throw new InvalidArgsException(); + return MultiLineComponent.literal(ctx.level() + .getSignal(ctx.pos().relative(direction), direction)); + } else if (GTStringUtils.equals(args.get(1), "set")) { + int power = PlaceholderUtils.toInt(args.get(1)); + PlaceholderUtils.checkRange("redstone power", 0, 15, power); + if (ctx.cover() == null) throw new NotSupportedException(); + ctx.cover().setRedstoneSignalOutput(power); + return MultiLineComponent.empty(); + } + throw new InvalidArgsException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("previousText") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + int i = PlaceholderUtils.toInt(args.get(0)); + if (ctx.previousText() == null) throw new NotSupportedException(); + PlaceholderUtils.checkRange("line", 1, ctx.previousText().size(), i); + return MultiLineComponent.of(ctx.previousText().get(i - 1)); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("progress") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + IWorkable workable = GTCapabilityHelper.getWorkable(ctx.level(), + ctx.pos(), ctx.side()); + if (workable == null) throw new NotSupportedException(); + return MultiLineComponent.literal(workable.getProgress()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("maxProgress") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + IWorkable workable = GTCapabilityHelper.getWorkable(ctx.level(), + ctx.pos(), ctx.side()); + if (workable == null) throw new NotSupportedException(); + return MultiLineComponent.literal(workable.getMaxProgress()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("maintenance") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + IMaintenanceMachine maintenance = GTCapabilityHelper.getMaintenanceMachine(ctx.level(), + ctx.pos(), ctx.side()); + if (maintenance == null) throw new NotSupportedException(); + return MultiLineComponent.literal(maintenance.hasMaintenanceProblems() ? 1 : 0); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("active") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + IWorkable workable = GTCapabilityHelper.getWorkable(ctx.level(), + ctx.pos(), ctx.side()); + if (workable == null) throw new NotSupportedException(); + return MultiLineComponent.literal(workable.isActive() ? 1 : 0); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("voltage") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + if (ctx.level().getBlockEntity(ctx.pos()) instanceof CableBlockEntity cable) { + return MultiLineComponent.literal(cable.getAverageVoltage()); + } + throw new NotSupportedException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("amperage") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 0); + if (ctx.level().getBlockEntity(ctx.pos()) instanceof CableBlockEntity cable) { + return MultiLineComponent.literal(cable.getAverageAmperage()); + } + throw new NotSupportedException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("count") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1, true); + String arg1 = GTStringUtils.componentsToString(args.get(0)); + int cnt = -1; + for (List arg : args) { + if (GTStringUtils.equals(arg, arg1)) cnt++; + } + return MultiLineComponent.literal(cnt); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("data") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2, true); + try { + int slot = PlaceholderUtils.toInt(args.get(1)); + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + ItemStack stack = ctx.itemStackHandler().getStackInSlot(slot - 1); + int capacity = -1; + if (stack.getItem() instanceof ComponentItem componentItem) { + for (IItemComponent component : componentItem.getComponents()) { + if (component instanceof IDataItem dataComponent) { + capacity = dataComponent.getCapacity(); + break; + } + } + } + if (capacity == -1) throw new MissingItemException("any data item", slot); + PlaceholderUtils.checkRange("index", 0, capacity - 1, PlaceholderUtils.toInt(args.get(2))); + ListTag data = stack.getOrCreateTag().getList("computer_monitor_cover_data", Tag.TAG_STRING); + while (data.size() <= PlaceholderUtils.toInt(args.get(2))) data.add(StringTag.valueOf("")); + int p = stack.getOrCreateTag().getInt("computer_monitor_cover_p"); + if (GTStringUtils.equals(args.get(2), "")) args.set(2, MultiLineComponent.literal(p)); + if (GTStringUtils.equals(args.get(0), "get")) + return MultiLineComponent + .literal(data.getString(PlaceholderUtils.toInt(args.get(2)) % capacity)); + else if (args.get(0).equalsString("set")) { + data.set(PlaceholderUtils.toInt(args.get(2)) % capacity, + StringTag.valueOf(args.get(3).toString())); + stack.getOrCreateTag().put("computer_monitor_cover_data", data); + return MultiLineComponent.empty(); + } else if (args.get(0).equalsString("setp")) { + stack.getOrCreateTag().putInt("computer_monitor_cover_p", + PlaceholderUtils.toInt(args.get(3)) % capacity); + return MultiLineComponent.empty(); + } else if (args.get(0).equalsString("inc")) { + stack.getOrCreateTag().putInt("computer_monitor_cover_p", (p + 1) % capacity); + return MultiLineComponent.empty(); + } else if (args.get(0).equalsString("dec")) { + stack.getOrCreateTag().putInt("computer_monitor_cover_p", p == 0 ? capacity - 1 : p - 1); + return MultiLineComponent.empty(); + } else throw new InvalidArgsException(); + } catch (IndexOutOfBoundsException e) { + throw new InvalidArgsException(); + } + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("combine") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, List args) { + MultiLineComponent out = MultiLineComponent.empty(); + for (int i = 0; i < args.size(); i++) { + out.append(args.get(i)); + if (i != args.size() - 1) out.append(" "); + } + return out; + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("nbt") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + int slot = GTStringUtils.toInt(args.get(0)); + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + return MultiLineComponent + .literal(ctx.itemStackHandler().getStackInSlot(slot - 1).getOrCreateTag().toString()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("toChars") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + if (args.get(0).isEmpty()) return MultiLineComponent.empty(); + StringBuilder out = new StringBuilder(); + for (char c : GTStringUtils.componentsToString(args.get(0)).toCharArray()) out.append(c).append(' '); + return MultiLineComponent.literal(out.substring(0, out.length() - 2)); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("toAscii") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + String arg = args.get(0).toString(); + if (arg.length() != 1) throw new InvalidArgsException(); + return MultiLineComponent.literal((int) arg.toCharArray()[0]); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("fromAscii") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + return MultiLineComponent.literal((char) PlaceholderUtils.toInt(args.get(0))); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("subList") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2, true); + int l = PlaceholderUtils.toInt(args.get(0)); + int r = PlaceholderUtils.toInt(args.get(1)); + PlaceholderUtils.checkRange("start index", 0, args.size(), l); + PlaceholderUtils.checkRange("end index", 0, args.size(), r); + MultiLineComponent out = MultiLineComponent.empty(); + for (int i = l; i < r - 1; i++) out.append(args.get(i)).append(' '); + out.append(args.get(r - 1)); + return out; + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("cmp") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 3); + double a = PlaceholderUtils.toDouble(args.get(0)); + double b = PlaceholderUtils.toDouble(args.get(2)); + return switch (args.get(1).toString()) { + case ">" -> MultiLineComponent.literal(a > b ? 1 : 0); + case "<" -> MultiLineComponent.literal(a < b ? 1 : 0); + case ">=" -> MultiLineComponent.literal(a >= b ? 1 : 0); + case "<=" -> MultiLineComponent.literal(a <= b ? 1 : 0); + case "==" -> MultiLineComponent.literal(a == b ? 1 : 0); + case "!=" -> MultiLineComponent.literal(a != b ? 1 : 0); + default -> throw new InvalidArgsException(); + }; + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("bf") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2); + int slot = PlaceholderUtils.toInt(args.get(0)); + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + ItemStack stack = ctx.itemStackHandler().getStackInSlot(slot - 1); + int capacity = -1; + if (stack.getItem() instanceof ComponentItem componentItem) { + for (IItemComponent component : componentItem.getComponents()) { + if (component instanceof IDataItem dataComponent) { + capacity = dataComponent.getCapacity(); + break; + } + } + } + if (capacity == -1) throw new MissingItemException("any data item", slot); + ListTag tag = stack.getOrCreateTag().getList("computer_monitor_cover_data", Tag.TAG_STRING); + int operationsLeft = 1000; + int p = 0; + String code = args.get(1).toString(); + Stack loops = new Stack<>(); + for (int i = 0; i < code.length() && operationsLeft > 0; i++) { + while (tag.size() <= p) tag.add(StringTag.valueOf("0")); + if (tag.getString(p).isEmpty()) tag.set(i, StringTag.valueOf("0")); + try { + switch (code.charAt(i)) { + case '+' -> tag.set(p, + StringTag.valueOf(String.valueOf(Integer.parseInt(tag.getString(p)) + 1))); + case '-' -> tag.set(p, + StringTag.valueOf(String.valueOf(Integer.parseInt(tag.getString(p)) - 1))); + case '>' -> p++; + case '<' -> p--; + case '[' -> loops.push(i); + case ']' -> { + if (Integer.parseInt(tag.getString(p)) == 0) loops.pop(); + else i = loops.peek(); + } + default -> throw new PlaceholderException(Component + .translatable("gtceu.computer_monitor_cover.error.bf_invalid", i).getString()); + } + } catch (InvalidNumberException e) { + throw new PlaceholderException(Component + .translatable("gtceu.computer_monitor_cover.error.bf_invalid_num", p, i).getString()); + } + operationsLeft--; + } + return MultiLineComponent.empty(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("cmd") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 2); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + int slot = PlaceholderUtils.toInt(args.get(0)); + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + ItemStack stack = ctx.itemStackHandler().getStackInSlot(slot - 1); + if (!stack.getOrCreateTag().contains("boundPlayerPermLevel")) + throw new MissingItemException("any data item bound to player", slot); + int perm = stack.getOrCreateTag().getInt("boundPlayerPermLevel"); + Component displayName = Component.Serializer + .fromJson(stack.getOrCreateTag().getString("boundPlayerName")); + if (displayName == null) displayName = Component.literal("Placeholder processor"); + if (ctx.level() instanceof ServerLevel serverLevel) { + MinecraftServer server = serverLevel.getServer(); + MultiLineComponent output = MultiLineComponent.empty(); + UUID playerUUID = null; + try { + playerUUID = UUID.fromString(stack.getOrCreateTag().getString("boundPlayerUUID")); + } catch (RuntimeException ignored) {} + ServerPlayer player = playerUUID == null ? null : server.getPlayerList().getPlayer(playerUUID); + CommandSource customSource = new CommandSource() { + + @Override + public void sendSystemMessage(@NotNull Component message) { + output.append(List.of(message)); + output.appendNewline(); + } + + @Override + public boolean acceptsSuccess() { + return true; + } + + @Override + public boolean acceptsFailure() { + return true; + } + + @Override + public boolean shouldInformAdmins() { + return false; + } + }; + CommandSourceStack source = new CommandSourceStack( + customSource, + ctx.pos() == null ? Vec3.ZERO : ctx.pos().getCenter(), + Vec2.ZERO, + serverLevel, + perm, + displayName.getString(), + displayName, + server, + player); + server.getCommands().performPrefixedCommand(source, args.get(1).toString()); + return output; + } else throw new NotSupportedException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("tm") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, List args) { + return MultiLineComponent.literal("™"); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("formatInt") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + long n = PlaceholderUtils.toLong(args.get(0)); + Map suffixes = Map.of( + 1L, "", + 1000L, "K", + 1000000L, "M", + 1000000000L, "B", + 1000000000000L, "T"); + long max = 1; + for (Long i : suffixes.keySet()) { + if (n >= i && max < i) max = i; + } + return MultiLineComponent.literal("%.2f%s".formatted(((double) n) / max, suffixes.get(max))); + } + }); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTSyncedFieldAccessors.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTSyncedFieldAccessors.java index afdd68f8a89..833631ab2e8 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTSyncedFieldAccessors.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTSyncedFieldAccessors.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.client.model.machine.MachineRenderState; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; import com.gregtechceu.gtceu.syncdata.*; import com.lowdragmc.lowdraglib.syncdata.IAccessor; @@ -25,5 +26,6 @@ public static void init() { registerSimple(MaterialPayload.class, MaterialPayload::new, Material.class, 1); registerSimple(GTRecipePayload.class, GTRecipePayload::new, GTRecipe.class, 100); registerSimple(FluidStackPayload.class, FluidStackPayload::new, FluidStack.class, -1); + registerSimple(MonitorGroupPayload.class, MonitorGroupPayload::new, MonitorGroup.class, 1); } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java index 5fb406aef77..b49dae74de2 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java @@ -23,6 +23,7 @@ import com.gregtechceu.gtceu.common.block.BoilerFireboxType; import com.gregtechceu.gtceu.common.data.*; import com.gregtechceu.gtceu.common.machine.multiblock.electric.*; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.BedrockOreMinerMachine; import com.gregtechceu.gtceu.common.machine.multiblock.primitive.CharcoalPileIgniterMachine; import com.gregtechceu.gtceu.common.machine.multiblock.primitive.CokeOvenMachine; import com.gregtechceu.gtceu.common.machine.multiblock.primitive.PrimitiveBlastFurnaceMachine; @@ -1126,5 +1127,23 @@ public class GTMultiMachines { (builder, overlay) -> builder.workableCasingModel( GTCEu.id("block/casings/solid/machine_casing_solid_steel"), overlay)); + public static final MultiblockMachineDefinition CENTRAL_MONITOR = REGISTRATE + .multiblock("central_monitor", CentralMonitorMachine::new) + .rotationState(RotationState.ALL) + .recipeType(DUMMY_RECIPES) + .appearanceBlock(CASING_ALUMINIUM_FROSTPROOF) + .pattern((definition) -> FactoryBlockPattern.start() + .aisle("BCB", "BBB", "BBB", "BBB") + .where('C', Predicates.controller(Predicates.blocks(definition.get()))) + .where('B', CentralMonitorMachine.BLOCK_PREDICATE) + .build()) + .modelProperty(RecipeLogic.STATUS_PROPERTY, RecipeLogic.Status.IDLE) + .model(createWorkableCasingMachineModel( + GTCEu.id("block/casings/solid/machine_casing_frost_proof"), + GTCEu.id("block/multiblock/central_monitor")) + .andThen(b -> b.addDynamicRenderer(DynamicRenderHelper::createCentralMonitorRender))) + .hasBER(true) + .register(); + public static void init() {} } diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java index 40dbfb79bec..453436cd61f 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java @@ -324,6 +324,18 @@ public class GTResearchMachines { .tooltips(Component.translatable("gtceu.part_sharing.disabled")) .register(); + public static final MachineDefinition BASIC_DATA_ACCESS_HATCH = REGISTRATE + .machine("basic_data_access_hatch", (holder) -> new DataAccessHatchMachine(holder, HV, false)) + .langValue("Basic Data Access Hatch") + .tier(HV) + .rotationState(RotationState.ALL) + .abilities(PartAbility.DATA_ACCESS) + .tooltips(Component.translatable("gtceu.machine.data_access_hatch.tooltip.0"), + Component.translatable("gtceu.machine.data_access_hatch.tooltip.1", 4), + Component.translatable("gtceu.part_sharing.disabled")) + .overlayTieredHullModel("data_access_hatch") + .register(); + public static final MachineDefinition DATA_ACCESS_HATCH = REGISTRATE .machine("data_access_hatch", (holder) -> new DataAccessHatchMachine(holder, EV, false)) .langValue("Data Access Hatch") diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/models/GTMachineModels.java b/src/main/java/com/gregtechceu/gtceu/common/data/models/GTMachineModels.java index 0d3eee9218e..1e2b6b4b43e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/models/GTMachineModels.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/models/GTMachineModels.java @@ -103,6 +103,18 @@ public static MachineBuilder.ModelInitializer createOverlayTieredHullMachineMode }; } + public static MachineBuilder.ModelInitializer createOverlayCasingMachineModel(ResourceLocation baseCasingTexture, + ResourceLocation overlayModel) { + return (ctx, prov, builder) -> { + BlockModelBuilder model = prov.models().nested() + .parent(prov.models().getExistingFile(overlayModel)); + model.texture("all", baseCasingTexture); + + builder.forAllStatesModels(state -> model); + builder.addReplaceableTextures("all"); + }; + } + public static MachineBuilder.ModelInitializer createColorOverlayTieredHullMachineModel(ResourceLocation overlay, @Nullable ResourceLocation pipeOverlay, @Nullable ResourceLocation emissiveOverlay) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/DataItemBehavior.java b/src/main/java/com/gregtechceu/gtceu/common/item/DataItemBehavior.java index 349f3c506a7..86f46137601 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/DataItemBehavior.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/DataItemBehavior.java @@ -1,6 +1,8 @@ package com.gregtechceu.gtceu.common.item; import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDataItem; @@ -8,33 +10,54 @@ import com.gregtechceu.gtceu.api.machine.feature.IDataStickInteractable; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.common.machine.owner.MachineOwner; +import com.gregtechceu.gtceu.utils.GTStringUtils; import com.gregtechceu.gtceu.utils.ResearchManager; import net.minecraft.ChatFormatting; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import lombok.Getter; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; +@SuppressWarnings("ClassCanBeRecord") public class DataItemBehavior implements IInteractionItem, IAddInformation, IDataItem { private final boolean requireDataBank; + @Getter + private final int capacity; - public DataItemBehavior() { - this.requireDataBank = false; + public DataItemBehavior(boolean requireDataBank, int capacity) { + this.requireDataBank = requireDataBank; + this.capacity = capacity; } - public DataItemBehavior(boolean requireDataBank) { - this.requireDataBank = requireDataBank; + @Override + public InteractionResultHolder use(Item item, Level level, Player player, InteractionHand usedHand) { + if (player.isShiftKeyDown()) { + ItemStack stack = player.getItemInHand(usedHand); + stack.getOrCreateTag().putString("boundPlayerName", Component.Serializer.toJson(player.getDisplayName())); + int perm = 0; + while (player.hasPermissions(perm)) perm++; + stack.getOrCreateTag().putInt("boundPlayerPermLevel", perm - 1); + stack.getOrCreateTag().putString("boundPlayerUUID", player.getStringUUID()); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); + } + return IInteractionItem.super.use(item, level, player, usedHand); } @Override @@ -45,6 +68,27 @@ public boolean requireDataBank() { @Override public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, TooltipFlag isAdvanced) { + if (stack.getOrCreateTag().contains("boundPlayerName")) { + MutableComponent name = Component.Serializer.fromJson(stack.getOrCreateTag().getString("boundPlayerName")); + tooltipComponents.add(Component.translatable("gtceu.tooltip.player_bind", name)); + } + if (stack.getOrCreateTag().contains("targetX")) { + tooltipComponents.add(Component.translatable( + "gtceu.tooltip.wireless_transmitter_bind", + Component.literal("" + stack.getOrCreateTag().getInt("targetX")).withStyle(ChatFormatting.GOLD), + Component.literal("" + stack.getOrCreateTag().getInt("targetY")).withStyle(ChatFormatting.GOLD), + Component.literal("" + stack.getOrCreateTag().getInt("targetZ")).withStyle(ChatFormatting.GOLD), + Component.literal(stack.getOrCreateTag().getString("face")).withStyle(ChatFormatting.DARK_PURPLE))); + } + if (stack.getOrCreateTag().contains("computer_monitor_cover_config")) { + tooltipComponents.add(Component.translatable("gtceu.tooltip.computer_monitor_config")); + } + if (stack.getOrCreateTag().contains("computer_monitor_cover_data")) { + tooltipComponents.add( + Component.translatable("gtceu.tooltip.computer_monitor_data", + GTStringUtils.toComponent( + stack.getOrCreateTag().getList("computer_monitor_cover_data", Tag.TAG_STRING)))); + } ResearchManager.ResearchItem researchData = ResearchManager.readResearchId(stack); if (researchData == null) { if (stack.getOrCreateTag().contains("pos", Tag.TAG_INT_ARRAY) && stack.hasTag()) { @@ -79,6 +123,18 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List { + if (!click.isRemote) return; + + stack.getOrCreateTag().putString("url", textField.getCurrentString()); + GTNetwork.sendToServer(new SCPacketMonitorGroupNBTChange(stack, group, machine)); + }); + saveButton.setButtonTexture(GuiTextures.BUTTON_CHECK); + builder.addWidget(textField); + builder.addWidget(saveButton); + return builder; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/modules/TextModuleBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/modules/TextModuleBehaviour.java new file mode 100644 index 00000000000..aed515a5ce6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/item/modules/TextModuleBehaviour.java @@ -0,0 +1,112 @@ +package com.gregtechceu.gtceu.common.item.modules; + +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.item.component.IMonitorModuleItem; +import com.gregtechceu.gtceu.api.placeholder.MultiLineComponent; +import com.gregtechceu.gtceu.api.placeholder.PlaceholderContext; +import com.gregtechceu.gtceu.api.placeholder.PlaceholderHandler; +import com.gregtechceu.gtceu.client.renderer.monitor.IMonitorRenderer; +import com.gregtechceu.gtceu.client.renderer.monitor.MonitorTextRenderer; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; +import com.gregtechceu.gtceu.common.network.GTNetwork; +import com.gregtechceu.gtceu.common.network.packets.SCPacketMonitorGroupNBTChange; + +import com.lowdragmc.lowdraglib.gui.widget.ButtonWidget; +import com.lowdragmc.lowdraglib.gui.widget.TextFieldWidget; +import com.lowdragmc.lowdraglib.gui.widget.Widget; +import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; +import com.lowdragmc.lowdraglib.gui.widget.codeeditor.CodeEditorWidget; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class TextModuleBehaviour implements IMonitorModuleItem { + + private void updateText(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group) { + StringBuilder formatStringLines = new StringBuilder(); + ListTag tag = stack.getOrCreateTag().getList("formatStringLines", StringTag.TAG_STRING); + for (Tag value : tag) { + formatStringLines.append(value.getAsString()).append('\n'); + } + MultiLineComponent text = PlaceholderHandler.processPlaceholders( + formatStringLines.toString(), + new PlaceholderContext( + machine.getLevel(), + group.getTarget(machine.getLevel()), + group.getTargetCoverSide(), + group.getPlaceholderSlotsHandler(), + group.getTargetCover(machine.getLevel()), + null)); + stack.getOrCreateTag().put("text", text.toTag()); + } + + @Override + public void tick(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group) { + this.updateText(stack, machine, group); + } + + @Override + public IMonitorRenderer getRenderer(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group) { + return new MonitorTextRenderer( + MultiLineComponent.fromTag(stack.getOrCreateTag().getList("text", Tag.TAG_STRING)).toImmutable(), + Math.max(stack.getOrCreateTag().getDouble("scale"), .0001)); + } + + @Override + public Widget createUIWidget(ItemStack stack, CentralMonitorMachine machine, MonitorGroup group) { + WidgetGroup builder = new WidgetGroup(); + CodeEditorWidget editor = new CodeEditorWidget(0, 0, 120, 80); + TextFieldWidget scaleInput = new TextFieldWidget( + -50, 47, + 40, 10, + null, + null); + ButtonWidget saveButton = new ButtonWidget(-40, 22, 20, 20, click -> { + if (!click.isRemote) return; + ListTag listTag = new ListTag(); + editor.getLines().forEach(line -> listTag.add(StringTag.valueOf(line))); + CompoundTag tag2 = stack.getOrCreateTag(); + tag2.put("formatStringLines", listTag); + try { + tag2.putDouble("scale", Double.parseDouble(scaleInput.getCurrentString())); + } catch (NumberFormatException ignored) {} + stack.setTag(tag2); + GTNetwork.sendToServer(new SCPacketMonitorGroupNBTChange(stack, group, machine)); + }); + saveButton.setButtonTexture(GuiTextures.BUTTON_CHECK); + List tmp = new ArrayList<>(); + Supplier scaleInputSupplier = () -> { + if (tmp.isEmpty()) tmp.add(true); + else scaleInput.setTextSupplier(null); + if (!stack.getOrCreateTag().contains("scale")) { + stack.getOrCreateTag().putDouble("scale", 1); + GTNetwork.sendToServer(new SCPacketMonitorGroupNBTChange(stack, group, machine)); + return "1"; + } + return String.valueOf(Mth.clamp(stack.getOrCreateTag().getDouble("scale"), .0001, 1000)); + }; + scaleInput.setTextSupplier(scaleInputSupplier); + scaleInput.setHoverTooltips(Component.translatable("gtceu.gui.central_monitor.text_scale")); + ListTag tag = stack.getOrCreateTag().getList("formatStringLines", Tag.TAG_STRING); + List formatStringLines = new ArrayList<>(); + for (Tag line : tag) formatStringLines.add(line.getAsString()); + editor.setLines(formatStringLines); + builder.addWidget(editor); + builder.addWidget(saveButton); + Widget placeholderReference = PlaceholderHandler.getPlaceholderHandlerUI(""); + builder.addWidget(scaleInput); + placeholderReference.setSelfPosition(-100, -50); + builder.addWidget(placeholderReference); + return builder; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java index e43e3d8ab82..2b2ea3648d9 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.IControllable; import com.gregtechceu.gtceu.api.capability.IElectricItem; +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; import com.gregtechceu.gtceu.api.capability.compat.FeCompat; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.SlotWidget; @@ -17,6 +18,7 @@ import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; @@ -38,7 +40,7 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public class BatteryBufferMachine extends TieredEnergyMachine - implements IControllable, IFancyUIMachine, IMachineLife { + implements IControllable, IFancyUIMachine, IMachineLife, IMonitorComponent { public static final long AMPS_PER_BATTERY = 2L; @@ -204,6 +206,11 @@ public void onMachineRemoved() { clearInventory(batteryInventory); } + @Override + public IGuiTexture getComponentIcon() { + return GuiTextures.BUTTON_CHECK; // temporary + } + protected class EnergyBatteryTrait extends NotifiableEnergyContainer { protected EnergyBatteryTrait(int inventorySize) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java index e68d1bf7951..4f5c9d06f9e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/HullMachine.java @@ -2,12 +2,15 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; +import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredPartMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableEnergyContainer; import com.gregtechceu.gtceu.integration.ae2.machine.trait.GridNodeHostTrait; +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; @@ -24,7 +27,7 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class HullMachine extends TieredPartMachine { +public class HullMachine extends TieredPartMachine implements IMonitorComponent { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(HullMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); @@ -110,4 +113,9 @@ public int tintColor(int index) { } return super.tintColor(index); } + + @Override + public IGuiTexture getComponentIcon() { + return GuiTextures.BUTTON_CHECK; // temporary (until there's a texture that is not fully 16x16 for this) + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java new file mode 100644 index 00000000000..fad6955e38d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CentralMonitorMachine.java @@ -0,0 +1,671 @@ +package com.gregtechceu.gtceu.common.machine.multiblock.electric; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; +import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.api.gui.widget.SlotWidget; +import com.gregtechceu.gtceu.api.item.IComponentItem; +import com.gregtechceu.gtceu.api.item.component.IItemComponent; +import com.gregtechceu.gtceu.api.item.component.IMonitorModuleItem; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.feature.IDataInfoProvider; +import com.gregtechceu.gtceu.api.machine.feature.IMachineLife; +import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockDisplayText; +import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableElectricMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.multiblock.WorkableMultiblockMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.misc.EnergyContainerList; +import com.gregtechceu.gtceu.api.pattern.*; +import com.gregtechceu.gtceu.api.pattern.util.RelativeDirection; +import com.gregtechceu.gtceu.common.data.GTBlocks; +import com.gregtechceu.gtceu.common.data.GTMachines; +import com.gregtechceu.gtceu.common.item.PortableScannerBehavior; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; +import com.gregtechceu.gtceu.common.machine.trait.CentralMonitorLogic; +import com.gregtechceu.gtceu.common.network.GTNetwork; +import com.gregtechceu.gtceu.common.network.packets.SCPacketMonitorGroupNBTChange; +import com.gregtechceu.gtceu.data.lang.LangHandler; +import com.gregtechceu.gtceu.utils.GTStringUtils; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.texture.*; +import com.lowdragmc.lowdraglib.gui.widget.*; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.annotation.RequireRerender; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraftforge.items.IItemHandler; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; +import java.util.*; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class CentralMonitorMachine extends WorkableElectricMultiblockMachine + implements IMonitorComponent, IDataInfoProvider, IMachineLife { + + public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(CentralMonitorMachine.class, + WorkableMultiblockMachine.MANAGED_FIELD_HOLDER); + + public static final TraceabilityPredicate BLOCK_PREDICATE = Predicates.abilities(PartAbility.INPUT_ENERGY) + .setMinGlobalLimited(1).setMaxGlobalLimited(2).setPreviewCount(1) + .or(Predicates.abilities(PartAbility.DATA_ACCESS).setPreviewCount(1) + .or(Predicates.machines(GTMachines.BATTERY_BUFFER_4).setPreviewCount(0)) + .or(Predicates.machines(GTMachines.BATTERY_BUFFER_16).setPreviewCount(0)) + .setMaxGlobalLimited(4)) + .or(Predicates.machines(GTMachines.HULL)) + .or(Predicates.machines(GTMachines.MONITOR)) + .or(Predicates.blocks(GTBlocks.CASING_ALUMINIUM_FROSTPROOF.get())); + + @Persisted + @DescSynced + @Getter + private int leftDist = 0, rightDist = 0, upDist = 0, downDist = 0; + @Persisted + @DescSynced + @Getter + @RequireRerender + private final List monitorGroups = new ArrayList<>(); + private final Set selectedComponents = new HashSet<>(); + private final List selectedTargets = new ArrayList<>(); + + private MultiblockState patternFindingState; + + public CentralMonitorMachine(IMachineBlockEntity holder) { + super(holder); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public void onStructureInvalid() { + super.onStructureInvalid(); + this.clearPatternFindingState(); + } + + @Override + public CentralMonitorLogic getRecipeLogic() { + return (CentralMonitorLogic) super.getRecipeLogic(); + } + + @Override + protected RecipeLogic createRecipeLogic(Object... args) { + return new CentralMonitorLogic(this); + } + + public @Nullable EnergyContainerList getFormedEnergyContainer() { + return this.energyContainer; + } + + public void tick() { + Level level = getLevel(); + if (level == null) { + return; + } + + for (MonitorGroup group : monitorGroups) { + ItemStack stack = group.getItemStackHandler().getStackInSlot(0); + if (stack.isEmpty() || !(stack.getItem() instanceof IComponentItem componentItem)) { + continue; + } + + for (IItemComponent component : componentItem.getComponents()) { + if (!(component instanceof IMonitorModuleItem module)) { + continue; + } + module.tick(stack, this, group); + GTNetwork.sendToAllPlayersTrackingChunk(level.getChunkAt(getPos()), + new SCPacketMonitorGroupNBTChange(stack, group, this)); + } + } + } + + @Override + public void onUnload() { + super.onUnload(); + this.clearPatternFindingState(); + } + + protected void clearPatternFindingState() { + if (this.patternFindingState != null) + this.patternFindingState.clean(); + this.patternFindingState = null; + } + + protected MultiblockState getPatternFindingState() { + if (this.patternFindingState == null) { + this.patternFindingState = new MultiblockState(getLevel(), getPos()); + this.patternFindingState.clean(); + } + return this.patternFindingState; + } + + public boolean isValidMonitorBlock(Level level, BlockPos pos) { + if (level.isOutsideBuildHeight(pos)) return false; + + MultiblockState state = getPatternFindingState(); + if (!state.update(pos, BLOCK_PREDICATE)) { + return false; + } + state.io = IO.BOTH; + + return Stream.concat(state.predicate.common.stream(), state.predicate.limited.stream()) + .anyMatch(predicate -> predicate.test(state)); + } + + public void updateStructureDimensions() { + Level level = getLevel(); + if (level == null) return; + + Direction front = getFrontFacing(); + Direction spin = getUpwardsFacing(); + + Direction left = RelativeDirection.LEFT.getRelative(front, spin, false); + Direction right = RelativeDirection.RIGHT.getRelative(front, spin, false); + Direction up = RelativeDirection.UP.getRelative(front, spin, false); + Direction down = RelativeDirection.DOWN.getRelative(front, spin, false); + BlockPos.MutableBlockPos posLeft = getPos().mutable().move(left); + BlockPos.MutableBlockPos posRight = getPos().mutable().move(right); + BlockPos.MutableBlockPos posUp = getPos().mutable().move(up); + BlockPos.MutableBlockPos posDown = getPos().mutable().move(down); + this.leftDist = 0; + this.rightDist = 0; + this.upDist = 0; + this.downDist = 0; + + while (isValidMonitorBlock(level, posLeft)) { + posLeft.move(left); + leftDist++; + } + while (isValidMonitorBlock(level, posRight)) { + posRight.move(right); + rightDist++; + } + while (isValidMonitorBlockRow(level, posUp, leftDist, rightDist, left, right)) { + posUp.move(up); + upDist++; + } + while (isValidMonitorBlockRow(level, posDown, leftDist, rightDist, left, right)) { + posDown.move(down); + downDist++; + } + } + + private boolean isValidMonitorBlockRow(Level level, BlockPos pos, int leftDist, int rightDist, Direction left, + Direction right) { + BlockPos.MutableBlockPos mutable = pos.mutable(); + mutable.move(left, leftDist); + for (int i = 0; i < leftDist + rightDist; i++) { + if (!isValidMonitorBlock(level, mutable)) return false; + mutable.move(right); + } + return isValidMonitorBlock(level, mutable); + } + + @Override + public BlockPattern getPattern() { + updateStructureDimensions(); + if (leftDist + rightDist < 1 || upDist + downDist < 1) { + leftDist = 3; + rightDist = 0; + upDist = 1; + downDist = 1; + } + + StringBuilder[] pattern = new StringBuilder[upDist + downDist + 1]; + for (int i = 0; i < upDist + downDist + 1; i++) { + pattern[i] = new StringBuilder(leftDist + rightDist + 1); + for (int j = 0; j < leftDist + rightDist + 1; j++) { + if (i == downDist && j == rightDist) + pattern[i].append('C'); // controller + else + pattern[i].append('B'); // any valid block + } + } + + String[] aisle = new String[upDist + downDist + 1]; + for (int i = 0; i < upDist + downDist + 1; i++) { + aisle[i] = pattern[i].toString(); + } + + return FactoryBlockPattern.start() + .aisle(aisle) + .where('B', BLOCK_PREDICATE) + .where('C', Predicates.controller(Predicates.blocks(this.getDefinition().get()))) + .build(); + } + + public BlockPos toRelative(BlockPos pos) { + Direction front = getFrontFacing(); + Direction spin = getUpwardsFacing(); + boolean flipped = isFlipped(); + Direction right = RelativeDirection.RIGHT.getRelative(front, spin, flipped); + Direction up = RelativeDirection.UP.getRelative(front, spin, flipped); + + BlockPos tmp = getPos().mutable().move(right, rightDist).move(up, upDist); + + return new BlockPos(Math.abs(tmp.get(right.getAxis()) - pos.get(right.getAxis())), + Math.abs(tmp.get(up.getAxis()) - pos.get(up.getAxis())), + 0); + } + + @Nullable + public IMonitorComponent getComponent(int row, int col) { + Level level = getLevel(); + if (level == null) return null; + + Direction front = getFrontFacing(); + Direction spin = getUpwardsFacing(); + boolean flipped = isFlipped(); + + Direction left = RelativeDirection.LEFT.getRelative(front, spin, flipped); + Direction up = RelativeDirection.UP.getRelative(front, spin, flipped); + + col = leftDist + rightDist - col; + BlockPos pos = getPos().relative(left, leftDist - col).relative(up, upDist - row); + + return GTCapabilityHelper.getMonitorComponent(level, pos, null); + } + + public boolean isMonitor(int row, int col) { + IMonitorComponent component = this.getComponent(row, col); + if (component == null) return false; + return component.isMonitor(); + } + + private IGuiTexture getComponentTexture(int row, int col) { + if (row < 0 || col < 0 || row > downDist + upDist + 1 || col > leftDist + rightDist + 1) + return GuiTextures.BLANK_TRANSPARENT; + IMonitorComponent component = getComponent(row, col); + if (component == null) return GuiTextures.BLANK_TRANSPARENT; + return component.getComponentIcon(); + } + + private boolean isInAnyGroup(IMonitorComponent component) { + return monitorGroups.stream().anyMatch(group -> group.contains(component.getPos())); + } + + @Override + public void addDisplayText(List textList) { + MultiblockDisplayText.builder(textList, isFormed()) + .addWorkingStatusLine(); + getDefinition().getAdditionalDisplay().accept(this, textList); + } + + @Override + public Widget createUIWidget() { + updateStructureDimensions(); + selectedComponents.clear(); + WidgetGroup builder = (WidgetGroup) super.createUIWidget(); + + WidgetGroup main = new WidgetGroup(); + DraggableScrollableWidgetGroup componentSelection = new DraggableScrollableWidgetGroup(0, 10, 200, 110); + main.addWidget(componentSelection); + WidgetGroup options = new WidgetGroup(-100, 20, 60, 20); + WidgetGroup groupConfig = new WidgetGroup(10, 30, 100, 100); + groupConfig.setVisible(false); + + ButtonWidget infoWidget = new ButtonWidget(200, 10, 20, 20, null); + infoWidget.setButtonTexture(GuiTextures.INFO_ICON); + infoWidget.setHoverTooltips( + GTStringUtils.toImmutable(LangHandler.getSingleOrMultiLang("gtceu.central_monitor.info_tooltip"))); + builder.addWidget(infoWidget); + List configGroup = new ArrayList<>(); + configGroup.add(null); + + Consumer openGroupConfig = (group) -> { + configGroup.set(0, group); + if (group == null) { + main.setVisible(true); + groupConfig.setVisible(false); + return; + } + groupConfig.clearAllWidgets(); + groupConfig.addWidget(new LabelWidget(0, 5, () -> { + String currentName = ""; + if (configGroup.get(0) != null) { + currentName = configGroup.get(0).getName(); + } + return Component.translatable("gtceu.central_monitor.gui.currently_editing", currentName).getString(); + })); + for (int i = 0; i < 8; i++) { + SlotWidget slot = new SlotWidget(group.getPlaceholderSlotsHandler(), i, -38, 16 * i + 46); + slot.setHoverTooltips(GTStringUtils + .toImmutable(LangHandler.getMultiLang("gtceu.gui.computer_monitor_cover.slot_tooltip", i + 1))); + groupConfig.addWidget(slot); + } + SlotWidget slot = new SlotWidget( + group.getItemStackHandler(), 0, + 0, 20); + WidgetGroup itemUI = new WidgetGroup(40, 20, 100, 100); + Runnable changeListener = () -> { + if (slot.getLastItem().is(slot.getItem().getItem())) return; + itemUI.clearAllWidgets(); + if (slot.getItem().getItem() instanceof IComponentItem item) { + for (IItemComponent component : item.getComponents()) { + if (component instanceof IMonitorModuleItem module) { + itemUI.addWidget(module.createUIWidget(slot.getItem(), this, group)); + } + } + } + }; + slot.setChangeListener(changeListener); + changeListener.run(); + groupConfig.addWidget(itemUI); + groupConfig.addWidget(slot); + main.setVisible(false); + groupConfig.setVisible(true); + }; + builder.addWidget(groupConfig); + DraggableScrollableWidgetGroup groupList = new DraggableScrollableWidgetGroup(-100, 50, 70, 80); + + List>>> imageButtons = new ArrayList<>(); + Map rightClickCallbacks = new HashMap<>(); + int[] dataSlot = new int[2]; // list to be able to modify it in lambdas + dataSlot[0] = 1; // the slot (index starts from 1) + dataSlot[1] = 9; // amount of slots + IntInputWidget dataSlotInput = new IntInputWidget(120, 20, 60, -20, () -> dataSlot[0], + n -> dataSlot[0] = Mth.clamp(n, 1, dataSlot[1])); + dataSlotInput.setVisible(false); + builder.addWidget(dataSlotInput); + + Consumer addGroupToList = group -> { + ButtonWidget label = new ButtonWidget(20, groupList.widgets.size() * 15 + 5, 60, 10, null); + TextTexture text = new TextTexture(group.getName()); + text.setType(TextTexture.TextType.LEFT); + label.setButtonTexture(text); + label.setOnPressCallback(click -> { + group.getRelativePositions().forEach(pos -> { + BlockPos rel = toRelative(pos); + imageButtons.get(rel.getY()).get(rel.getX()).accept(null); + }); + if (group.getTargetRaw() != null) { + rightClickCallbacks.getOrDefault(group.getTargetRaw(), () -> {}).run(); + } + }); + groupList.addWidget(label); + + ButtonWidget configButton = new ButtonWidget( + 0, label.getSelfPositionY() - 3, + 16, 16, + GuiTextures.IO_CONFIG_COVER_SETTINGS, + click -> { + if (configGroup.get(0) == null) { + openGroupConfig.accept(group); + } else { + openGroupConfig.accept(null); + } + }); + groupList.addWidget(configButton); + }; + + monitorGroups.forEach(addGroupToList); + builder.addWidget(groupList); + main.addWidget(options); + ButtonWidget removeFromGroupButton = new ButtonWidget(0, 0, 60, 20, null); + removeFromGroupButton.setButtonTexture(new TextTexture("gtceu.central_monitor.gui.remove_from_group")); + removeFromGroupButton.setVisible(false); + ButtonWidget setTargetButton = new ButtonWidget(0, 15, 60, 20, null); + setTargetButton.setButtonTexture(new TextTexture("gtceu.central_monitor.gui.set_target")); + setTargetButton.setVisible(false); + ButtonWidget createGroupButton = new ButtonWidget(0, 0, 60, 20, null); + createGroupButton.setOnPressCallback(click -> { + MonitorGroup group = new MonitorGroup( + Component.translatable("gtceu.gui.central_monitor.group_default_name", monitorGroups.size() + 1) + .getString()); + for (IMonitorComponent component : selectedComponents) { + if (isInAnyGroup(component)) return; + group.add(component.getPos()); + } + monitorGroups.add(group); + addGroupToList.accept(group); + + createGroupButton.setVisible(false); + removeFromGroupButton.setVisible(true); + Iterator it = selectedComponents.iterator(); + while (it.hasNext()) { + IMonitorComponent c = it.next(); + BlockPos rel = toRelative(c.getPos()); + imageButtons.get(rel.getY()).get(rel.getX()).accept(it); + } + if (!selectedTargets.isEmpty()) { + rightClickCallbacks.getOrDefault(selectedTargets.get(0).getPos(), () -> {}).run(); + } + }); + setTargetButton.setOnPressCallback(click -> { + MonitorGroup group = null; + for (MonitorGroup group2 : monitorGroups) { + for (IMonitorComponent component : selectedComponents) { + if (group2.contains(component.getPos())) { + group = group2; + break; + } + } + if (group != null) break; + } + if (group == null) return; + if (selectedTargets.isEmpty()) group.setTarget(null); + else { + group.setTarget(selectedTargets.get(0).getPos()); + group.setDataSlot(dataSlot[0] - 1); + } + }); + removeFromGroupButton.setOnPressCallback(click -> { + for (MonitorGroup group : monitorGroups) { + for (IMonitorComponent component : selectedComponents) group.remove(component.getPos()); + } + Iterator itg = monitorGroups.iterator(); + while (itg.hasNext()) { + MonitorGroup group = itg.next(); + if (group.isEmpty()) { + clearInventory(group.getItemStackHandler()); + clearInventory(group.getPlaceholderSlotsHandler()); + itg.remove(); + } + } + groupList.clearAllWidgets(); + monitorGroups.forEach(addGroupToList); + + removeFromGroupButton.setVisible(false); + createGroupButton.setVisible(true); + Iterator it = selectedComponents.iterator(); + while (it.hasNext()) { + IMonitorComponent c = it.next(); + BlockPos rel = toRelative(c.getPos()); + imageButtons.get(rel.getY()).get(rel.getX()).accept(it); + } + if (!selectedTargets.isEmpty()) { + rightClickCallbacks.getOrDefault(selectedTargets.get(0).getPos(), () -> {}).run(); + } + }); + createGroupButton.setButtonTexture(new TextTexture("gtceu.central_monitor.gui.create_group")); + createGroupButton.setVisible(false); + options.addWidget(removeFromGroupButton); + options.addWidget(createGroupButton); + options.addWidget(setTargetButton); + int startX = 20; + int startY = 30; + for (int row = 0; row <= downDist + upDist; row++) { + imageButtons.add(new ArrayList<>()); + for (int col = 0; col <= leftDist + rightDist; col++) { + IGuiTexture texture = getComponentTexture(row, col); + GuiTextureGroup textures = new GuiTextureGroup(texture, new ColorBorderTexture(2, 0xFFFFFF)); + IMonitorComponent component = getComponent(row, col); + if (component == null) { + GTUtil.getLast(imageButtons).add(it -> {}); + continue; + } + ButtonWidget img = new ButtonWidget(startX + (16 * col), startY + (16 * row), 16, 16, textures, null); + Consumer> callback = (it) -> { + if (!component.isMonitor()) return; + if (selectedComponents.contains(component)) { + if (it == null) { + selectedComponents.remove(component); + } else { + it.remove(); + } + + if (!selectedTargets.isEmpty() && selectedTargets.get(0) == component) { + ColorRectTexture rect = new ColorRectTexture(Color.BLUE); + textures.setTextures(rect, texture); + } else { + textures.setTextures(texture); + } + + createGroupButton.setVisible(selectedComponents.stream().noneMatch(this::isInAnyGroup)); + removeFromGroupButton.setVisible(selectedComponents.stream().allMatch(this::isInAnyGroup)); + setTargetButton.setVisible(removeFromGroupButton.isVisible()); + + if (selectedComponents.isEmpty()) { + createGroupButton.setVisible(false); + removeFromGroupButton.setVisible(false); + setTargetButton.setVisible(false); + } + } else { + boolean inAnyGroup = isInAnyGroup(component); + // yes I know this is terrible but if it works don't touch it :) + if (selectedComponents.isEmpty() && !inAnyGroup) createGroupButton.setVisible(true); + if (inAnyGroup) createGroupButton.setVisible(false); + if (selectedComponents.isEmpty() && inAnyGroup) { + removeFromGroupButton.setVisible(true); + setTargetButton.setVisible(true); + } + if (!inAnyGroup) { + removeFromGroupButton.setVisible(false); + setTargetButton.setVisible(false); + } + selectedComponents.add(component); + ColorRectTexture rect = new ColorRectTexture( + (selectedTargets.isEmpty() || selectedTargets.get(0) != component) ? Color.RED : + Color.PINK); + textures.setTextures(rect, texture); + } + if (isInAnyGroup(component)) { + monitorGroups.forEach(group -> { + if (group.contains(component.getPos())) { + img.setHoverTooltips( + Component.translatable("gtceu.gui.central_monitor.group", group.getName())); + } + }); + } else { + img.setHoverTooltips(Component.translatable("gtceu.gui.central_monitor.group", + Component.translatable("gtceu.gui.central_monitor.none"))); + } + }; + Runnable rightClickCallback = () -> { + if (!selectedTargets.isEmpty()) { + if (selectedTargets.get(0) == component) { + selectedTargets.clear(); + if (selectedComponents.contains(component)) { + ColorRectTexture rect = new ColorRectTexture(Color.RED); + textures.setTextures(rect, texture); + } else { + textures.setTextures(texture); + } + dataSlotInput.setVisible(false); + return; + } else { + rightClickCallbacks.get(selectedTargets.get(0).getPos()).run(); + } + } + selectedTargets.add(component); + ColorRectTexture rect; + if (selectedComponents.contains(component)) { + rect = new ColorRectTexture(Color.PINK); + } else { + rect = new ColorRectTexture(Color.BLUE); + } + textures.setTextures(rect, texture); + if (component.getDataItems() != null) { + IItemHandler dataItems = component.getDataItems(); + MonitorGroup selectedGroup = null; + for (MonitorGroup group : monitorGroups) { + for (IMonitorComponent c : selectedComponents) { + if (group.contains(c.getPos())) { + if (selectedGroup == null || selectedGroup == group) { + selectedGroup = group; + } else { + selectedGroup = null; + break; + } + } + } + } + if (selectedGroup != null) { + dataSlot[0] = selectedGroup.getDataSlot() + 1; + } + dataSlot[1] = dataItems.getSlots(); + dataSlotInput.setVisible(true); + } + }; + if (isInAnyGroup(component)) { + monitorGroups.forEach(group -> { + if (group.contains(component.getPos())) img.setHoverTooltips( + Component.translatable("gtceu.gui.central_monitor.group", group.getName())); + }); + } else { + img.setHoverTooltips(Component.translatable("gtceu.gui.central_monitor.group", + Component.translatable("gtceu.gui.central_monitor.none"))); + } + img.setOnPressCallback(click -> { + if (click.button == 0) callback.accept(null); + else if (click.button == 1) rightClickCallback.run(); + }); + componentSelection.addWidget(img); + GTUtil.getLast(imageButtons).add(callback); + rightClickCallbacks.put(component.getPos(), rightClickCallback); + } + } + builder.addWidget(main); + return builder; + } + + @Override + public IGuiTexture getComponentIcon() { + return ResourceTexture.fromSpirit(GTCEu.id("block/multiblock/network_switch/overlay_front_active")); + } + + @Override + public @NotNull List getDebugInfo(Player player, int logLevel, + PortableScannerBehavior.DisplayMode mode) { + return List.of(Component.translatable("gtceu.central_monitor.size", leftDist, rightDist, upDist, downDist)); + } + + @Override + public @NotNull List getDataInfo(PortableScannerBehavior.DisplayMode mode) { + return List.of(Component.translatable("gtceu.central_monitor.size", leftDist, rightDist, upDist, downDist)); + } + + @Override + public void onMachineRemoved() { + for (MonitorGroup group : monitorGroups) { + clearInventory(group.getItemStackHandler()); + clearInventory(group.getPlaceholderSlotsHandler()); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/monitor/MonitorGroup.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/monitor/MonitorGroup.java new file mode 100644 index 00000000000..96a74ea2164 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/monitor/MonitorGroup.java @@ -0,0 +1,124 @@ +package com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor; + +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.UnaryOperator; + +public class MonitorGroup { + + private final Set monitorPositions = new HashSet<>(); + @Getter + private final String name; + @Getter + private final CustomItemStackHandler itemStackHandler; + @Getter + private final CustomItemStackHandler placeholderSlotsHandler; + @Setter + private @Nullable BlockPos target; + @Setter + @Getter + private @Nullable Direction targetCoverSide; + @Setter + @Getter + private int dataSlot = 0; + + public MonitorGroup(String name) { + this(name, new CustomItemStackHandler(1), new CustomItemStackHandler(8)); + } + + public MonitorGroup(String name, CustomItemStackHandler handler, CustomItemStackHandler placeholderSlotsHandler) { + this.name = name; + this.itemStackHandler = handler; + this.placeholderSlotsHandler = placeholderSlotsHandler; + } + + public void add(BlockPos pos) { + monitorPositions.add(pos); + } + + public void remove(BlockPos pos) { + monitorPositions.remove(pos); + } + + public List getRow(int row, UnaryOperator toRelative) throws IndexOutOfBoundsException { + IntSet yLevelsSet = new IntOpenHashSet(); + for (BlockPos pos : monitorPositions) { + yLevelsSet.add(toRelative.apply(pos).getY()); + } + if (row < 0) row += yLevelsSet.size(); + int y = yLevelsSet.intStream().sorted().toArray()[row]; + List rowPositions = new ArrayList<>(); + for (BlockPos pos : monitorPositions) { + if (toRelative.apply(pos).getY() == y) { + rowPositions.add(toRelative.apply(pos)); + } + } + rowPositions.sort(Comparator.comparingInt(Vec3i::getX)); + return rowPositions; + } + + public boolean contains(BlockPos pos) { + return monitorPositions.contains(pos); + } + + public boolean isEmpty() { + return monitorPositions.isEmpty(); + } + + public Set getRelativePositions() { + return monitorPositions; + } + + public @Nullable CoverBehavior getTargetCover(Level level) { + if (getTarget(level) != null && targetCoverSide != null) { + ICoverable coverable = GTCapabilityHelper.getCoverable(level, getTarget(level), targetCoverSide); + if (coverable != null) return coverable.getCoverAtSide(targetCoverSide); + } + return null; + } + + public @Nullable BlockPos getTargetRaw() { + return target; + } + + public @Nullable BlockPos getTarget(Level level) { + if (target == null) return null; + + IMonitorComponent component = GTCapabilityHelper.getMonitorComponent(level, target, null); + if (component != null && component.getDataItems() != null) { + ItemStack stack = component.getDataItems().getStackInSlot(dataSlot); + CompoundTag tag = stack.getTag(); + if (tag == null) { + return null; + } + int x = tag.getInt("targetX"); + int y = tag.getInt("targetY"); + int z = tag.getInt("targetZ"); + Direction face = Direction.byName(tag.getString("face")); + if (face == null) { + return null; + } + setTargetCoverSide(face); + return new BlockPos(x, y, z); + } + return target; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java index 5ebe9a88ee2..e14e6cf198c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java @@ -1,7 +1,9 @@ package com.gregtechceu.gtceu.common.machine.multiblock.part; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.gui.GuiTextures; @@ -20,6 +22,8 @@ import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; import com.gregtechceu.gtceu.utils.ResearchManager; +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; import com.lowdragmc.lowdraglib.gui.widget.Widget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; @@ -31,6 +35,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.items.IItemHandler; import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; @@ -44,7 +49,7 @@ @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault public class DataAccessHatchMachine extends TieredPartMachine - implements IMachineLife, IDataAccessHatch, IDataInfoProvider { + implements IMachineLife, IDataAccessHatch, IDataInfoProvider, IMonitorComponent { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( DataAccessHatchMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); @@ -76,8 +81,7 @@ public void onContentsChanged() { @Override public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { boolean isDataBank = isFormed() && getControllers().first() instanceof DataBankMachine; - if (ResearchManager.isStackDataItem(stack, isDataBank) && - ResearchManager.hasResearchTag(stack)) { + if (ResearchManager.isStackDataItem(stack, isDataBank)) { return super.insertItem(slot, stack, simulate); } return stack; @@ -108,7 +112,12 @@ public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult } protected int getInventorySize() { - return getTier() == GTValues.LuV ? 16 : 9; + return switch (getTier()) { + case GTValues.LuV -> 16; + case GTValues.EV -> 9; + case GTValues.HV -> 4; + default -> 1; + }; } @Override @@ -184,4 +193,14 @@ public GTRecipe modifyRecipe(GTRecipe recipe) { public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } + + @Override + public IGuiTexture getComponentIcon() { + return new ResourceTexture(GTCEu.id("textures/item/data_module.png")).getSubTexture(0, 0, 1, 1 / 13f); + } + + @Override + public IItemHandler getDataItems() { + return importItems.storage; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/monitor/MonitorComponentPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/monitor/MonitorComponentPartMachine.java new file mode 100644 index 00000000000..220bd378673 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/monitor/MonitorComponentPartMachine.java @@ -0,0 +1,12 @@ +package com.gregtechceu.gtceu.common.machine.multiblock.part.monitor; + +import com.gregtechceu.gtceu.api.capability.IMonitorComponent; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; + +public abstract class MonitorComponentPartMachine extends MultiblockPartMachine implements IMonitorComponent { + + public MonitorComponentPartMachine(IMachineBlockEntity holder) { + super(holder); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/monitor/MonitorPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/monitor/MonitorPartMachine.java new file mode 100644 index 00000000000..4f999166910 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/monitor/MonitorPartMachine.java @@ -0,0 +1,33 @@ +package com.gregtechceu.gtceu.common.machine.multiblock.part.monitor; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; + +import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.BlockHitResult; + +public class MonitorPartMachine extends MonitorComponentPartMachine { + + public MonitorPartMachine(IMachineBlockEntity holder) { + super(holder); + } + + @Override + public boolean isMonitor() { + return true; + } + + @Override + public IGuiTexture getComponentIcon() { + return ResourceTexture.fromSpirit(GTCEu.id("item/computer_monitor_cover")); + } + + @Override + public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { + return false; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/trait/CentralMonitorLogic.java b/src/main/java/com/gregtechceu/gtceu/common/machine/trait/CentralMonitorLogic.java new file mode 100644 index 00000000000..0d4b9cec954 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/trait/CentralMonitorLogic.java @@ -0,0 +1,75 @@ +package com.gregtechceu.gtceu.common.machine.trait; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.IWorkable; +import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; +import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; +import com.gregtechceu.gtceu.api.misc.EnergyContainerList; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; + +import net.minecraft.util.Mth; + +public class CentralMonitorLogic extends RecipeLogic implements IWorkable { + + private static final int BASE_UPDATE_INTERVAL = 8 * 20; + + public CentralMonitorLogic(IRecipeLogicMachine machine) { + super(machine); + } + + public CentralMonitorMachine getMachine() { + return (CentralMonitorMachine) machine; + } + + private boolean consumeEnergy() { + int tier = Mth.clamp(getMachine().getTier(), GTValues.ULV, GTValues.MAX); + long energyToDrain = GTValues.VA[tier]; + EnergyContainerList energyContainer = getMachine().getFormedEnergyContainer(); + if (energyContainer == null) { + return false; + } + + long resultEnergy = energyContainer.getEnergyStored() - energyToDrain; + if (resultEnergy >= 0L && resultEnergy <= energyContainer.getEnergyCapacity()) { + energyContainer.removeEnergy(energyToDrain); + return true; + } + return false; + } + + private int getUpdateInterval() { + int interval = BASE_UPDATE_INTERVAL; + for (int i = 1; i < getMachine().getTier(); i++) { + interval /= 2; + } + return Math.max(interval, 1); + } + + @Override + public void serverTick() { + if (!getMachine().isFormed() || !isWorkingEnabled()) { + setStatus(Status.IDLE); + } else if (consumeEnergy()) { + setStatus(Status.WORKING); + isActive = true; + progress = (progress + 1) % getUpdateInterval(); + if (progress == 0) { + getMachine().tick(); + } + } else { + setStatus(Status.WAITING); + isActive = false; + progress = Math.max(progress - 2, 1); + } + } + + @Override + public int getMaxProgress() { + return getUpdateInterval(); + } + + @Override + public boolean isActive() { + return getMachine().isFormed() && this.isActive; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java b/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java index 4a3c79b1cf4..02a559b2b5f 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java +++ b/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java @@ -62,6 +62,10 @@ public static void sendToPlayer(ServerPlayer player, INetPacket packet) { INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet); } + public static void reply(NetworkEvent.Context context, INetPacket packet) { + INSTANCE.reply(packet, context); + } + public interface INetPacket { void encode(FriendlyByteBuf buffer); @@ -78,6 +82,10 @@ public static void register(Class cls, Function { + try { + SPacketImageResponse.sendImage(url, image, context); + } catch (IOException ignored) {} + }); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/network/packets/SCPacketMonitorGroupNBTChange.java b/src/main/java/com/gregtechceu/gtceu/common/network/packets/SCPacketMonitorGroupNBTChange.java new file mode 100644 index 00000000000..9b7aa42c088 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/network/packets/SCPacketMonitorGroupNBTChange.java @@ -0,0 +1,64 @@ +package com.gregtechceu.gtceu.common.network.packets; + +import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.CentralMonitorMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; +import com.gregtechceu.gtceu.common.network.GTNetwork; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.util.LogicalSidedProvider; +import net.minecraftforge.network.NetworkEvent; + +import java.util.Optional; + +public class SCPacketMonitorGroupNBTChange implements GTNetwork.INetPacket { + + private final ItemStack stack; + private final int monitorGroupId; + private final BlockPos pos; + + public SCPacketMonitorGroupNBTChange(ItemStack stack, MonitorGroup group, CentralMonitorMachine machine) { + this.stack = stack; + this.monitorGroupId = machine.getMonitorGroups().indexOf(group); + this.pos = machine.getPos(); + } + + public SCPacketMonitorGroupNBTChange(FriendlyByteBuf buf) { + this.stack = buf.readItem(); + this.monitorGroupId = buf.readVarInt(); + this.pos = buf.readBlockPos(); + } + + @Override + public void encode(FriendlyByteBuf buffer) { + buffer.writeItemStack(stack, false); + buffer.writeVarInt(monitorGroupId); + buffer.writeBlockPos(pos); + } + + @Override + public void execute(NetworkEvent.Context context) { + Level level = LogicalSidedProvider.CLIENTWORLD.get(context.getDirection().getReceptionSide()) + .or(() -> Optional.ofNullable(context.getSender()).map(ServerPlayer::level)) + .orElse(null); + if (level == null) return; + + MetaMachine machine = MetaMachine.getMachine(level, pos); + if (machine instanceof CentralMonitorMachine centralMonitor) { + centralMonitor.getMonitorGroups().get(monitorGroupId) + .getItemStackHandler().setStackInSlot(0, stack); + } + } + + private static class ClientCallWrapper { + + private static Level getClientLevel() { + return Minecraft.getInstance().level; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/network/packets/SPacketImageResponse.java b/src/main/java/com/gregtechceu/gtceu/common/network/packets/SPacketImageResponse.java new file mode 100644 index 00000000000..7a1ecb7071c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/network/packets/SPacketImageResponse.java @@ -0,0 +1,75 @@ +package com.gregtechceu.gtceu.common.network.packets; + +import com.gregtechceu.gtceu.client.util.ClientImageCache; +import com.gregtechceu.gtceu.common.network.GTNetwork; +import com.gregtechceu.gtceu.utils.GTMath; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.network.NetworkEvent; + +import org.apache.commons.lang3.ArrayUtils; + +import java.io.IOException; + +public class SPacketImageResponse implements GTNetwork.INetPacket { + + private static final int MAX_BYTES_PER_PACKET = 120000; + + private final byte[] imagePart; + private final String url; + private final int index; + private final int totalSize; + + public SPacketImageResponse(String url, byte[] imagePart, int index, int totalSize) { + this.url = url; + this.imagePart = imagePart; + this.index = index; + this.totalSize = totalSize; + } + + public SPacketImageResponse(FriendlyByteBuf buf) { + this.index = buf.readInt(); + this.totalSize = buf.readInt(); + this.url = buf.readUtf(); + this.imagePart = buf.readByteArray(); + } + + @Override + public void encode(FriendlyByteBuf buffer) { + buffer.writeInt(index); + buffer.writeInt(totalSize); + buffer.writeUtf(url); + buffer.writeByteArray(imagePart); + } + + @Override + public void execute(NetworkEvent.Context context) { + if (imagePart == null) { + return; + } + try { + ClientImageCache.receiveImagePart(url, imagePart, index, totalSize); + } catch (IOException ignored) {} + } + + public static void sendImage(String url, byte[] imageBytes, NetworkEvent.Context context) throws IOException { + if (imageBytes.length < MAX_BYTES_PER_PACKET) { + GTNetwork.reply(context, new SPacketImageResponse(url, imageBytes, 0, 1)); + } else { + int packetCount = GTMath.ceilDiv(imageBytes.length, MAX_BYTES_PER_PACKET); + int arrayIndex = 0; + + for (int i = 0; i < packetCount; i++) { + int remaining = imageBytes.length - arrayIndex; + if (remaining <= 0) { + break; + } + + byte[] part = ArrayUtils.subarray(imageBytes, arrayIndex, arrayIndex + MAX_BYTES_PER_PACKET); + GTNetwork.reply(context, new SPacketImageResponse(url, part, i, packetCount)); + + arrayIndex += MAX_BYTES_PER_PACKET; + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java b/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java index fff0c21eb61..68450f99ea0 100644 --- a/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java @@ -784,6 +784,11 @@ public static class ClientConfigs { @Configurable.Comment({ "Whether or not to enable Emissive Textures for GregTech Machines.", "Default: true" }) public boolean machinesEmissiveTextures = true; @Configurable + @Configurable.Comment({ + "Whether most machines will have block entity renderers, mainly used for rendering certain covers. (Restart required)", + "Disable if experiencing performance issues.", "Default: true" }) + public boolean machinesHaveBERsByDefault = true; + @Configurable @Configurable.Comment({ "Whether or not sounds should be played when using tools outside of crafting.", "Default: true" }) public boolean toolUseSounds = true; diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java index 001e53959ad..91f07ccae4f 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java @@ -1420,6 +1420,275 @@ public static void init(RegistrateLangProvider provider) { provider.add("gtceu.tooltip.status.trinary.false", "False"); provider.add("gtceu.tooltip.status.trinary.true", "True"); provider.add("gtceu.tooltip.status.trinary.unknown", "Unknown"); + + provider.add("gtceu.tooltip.wireless_transmitter_bind", "Binding to a transmitter cover at %s %s %s facing %s"); + provider.add("gtceu.tooltip.computer_monitor_config", "Storing computer monitor cover configuration data"); + provider.add("gtceu.tooltip.computer_monitor_data", "Storing data: %s"); + + provider.add("gtceu.display_source.computer_monitor_cover", "Computer Monitor Cover"); + provider.add("gtceu.display_target.computer_monitor_cover", "Computer Monitor Cover"); + multiLang(provider, "gtceu.placeholder_info.energy", + "Returns the amount of energy stored.", + "Usage:", + " {energy} -> the amount of energy stored"); + multiLang(provider, "gtceu.placeholder_info.energyCapacity", + "Returns the max amount of energy that can be stored", + "Usage:", + "{energyCapacity} -> the energy capacity"); + multiLang(provider, "gtceu.placeholder_info.itemCount", + "Returns the amount of items (can be filtered).", + "Usage:", + " {itemCount} -> total item amount", + " {itemCount } -> amount of items with ids equal to item_id", + " {itemCount filter } -> amount of items matching filter in specified slot of this cover"); + multiLang(provider, "gtceu.placeholder_info.calc", + "Returns the result of a math function or operation.", + "Usage:", + " {calc } -> any_string", + " {calc } -> the result of the specified operation", + " {calc <+|-|*|/|//|>>|<<|%> } -> the result of the specified operation"); + multiLang(provider, "gtceu.placeholder_info.if", + "Returns one of the arguments depending on the condition. The condition is considered true if it is not an empty string and is not equal to 0.", + "Usage:", + " {if [returned_if_false]}"); + multiLang(provider, "gtceu.placeholder_info.obf", + "Returns the text from the first argument, obfuscated.", + "Usage:", + " {obf } -> obfuscated text"); + multiLang(provider, "gtceu.placeholder_info.underline", + "Returns the text from the first argument, underlined", + "Usage:", + " {underline } -> underlined text"); + multiLang(provider, "gtceu.placeholder_info.strike", + "Returns the text from the first text, displaying it as if it was crossed out", + "Usage:", + " {strike } -> crossed-out text"); + multiLang(provider, "gtceu.placeholder_info.color", + "Returns the text from the second argument, colored with the color from the first argument. All default minecraft chat colors can be used.", + "Usage:", + " {color } -> colored text"); + multiLang(provider, "gtceu.placeholder_info.tick", + "Returns the amount of ticks passed from when this cover was placed.", + "Usage:", + " {tick} -> the amount of ticks"); + multiLang(provider, "gtceu.placeholder_info.block", "Returns the block symbol (█).", + "Usage:", + " {block} -> '█'"); + multiLang(provider, "gtceu.placeholder_info.repeat", + "Returns the text from the second arguments, repeated the amount of times specified in the first argument.", + "Usage:", + " {repeat } -> text repeated the specified amount of times"); + multiLang(provider, "gtceu.placeholder_info.random", + "Returns a random number in the specified interval (inclusive).", + "Usage:", + " {random } -> a random number between min and max (inclusive)"); + multiLang(provider, "gtceu.placeholder_info.select", + "Returns the argument at the specified index (starting from 0)", + "Usage:", + " {select [arg1] [arg2] [arg3] ... -> argument at the specified index"); + multiLang(provider, "gtceu.placeholder_info.redstone", + "Returns the redstone signal strength or sets the redstone output strength", + "Usage:", + " {redstone get } -> redstone signal strength (0-15) at the specified side", + " {redstone get link } -> redstone signal strength of a Create redstone link frequency specified by a linked controller in slot #slot_index. freq_slot_index is the index of the frequency inside the controller (from left to right, 0-6)", + " {redstone set } -> empty string, sets the redstone output strength from this cover's side", + " {redstone set link } -> empty string, broadcasts the specified redstone power on the specified Create redstone link frequency"); + multiLang(provider, "gtceu.placeholder_info.fluidCount", + "Returns the amount of fluids (can be filtered).", + "Usage:", + " {fluidCount [fluidId]} -> the amount of all fluids, or the fluid with fluidId if specified"); + multiLang(provider, "gtceu.placeholder_info.displayTarget", + "Returns the specified line that was transmitted to this cover using a display link.", + "Usage:", + " {displayTarget } -> the text on the specified line (line number is 1-100)"); + multiLang(provider, "gtceu.placeholder_info.previousText", + "Returns the text that was previously displayed by this cover at the specified line (before line-wrapping).", + "Usage:", + " {previousText } -> the text previously displayed on the specified line (index starts at 1)"); + multiLang(provider, "gtceu.placeholder_info.ae2itemCount", + "Same as itemCount, but counts items in the ME network of the block this cover is attached to.", + "Note that counting by filter or all items may cause lag!", + "Usage:", + " {itemCount} -> total item amount", + " {itemCount } -> amount of items with ids equal to item_id", + " {itemCount filter } -> amount of items matching filter in specified slot of this cover"); + multiLang(provider, "gtceu.placeholder_info.ae2fluidCount", + "Same as fluidCount, but counts items in the ME network of the block this cover is attached to.", + "Note that counting all fluids may cause lag!", + "Usage:", + " {fluidCount [fluidId]} -> the amount of all fluids, or the fluid with fluidId if specified"); + multiLang(provider, "gtceu.placeholder_info.progress", + "Returns the progress of the currently running recipe of the block this cover is attached to.", + "Note that progress is an integer between 0 and {maxProgress}", + "Usage:", + " {progress} -> the progress of the currently running recipe"); + multiLang(provider, "gtceu.placeholder_info.maxProgress", + "Returns the maximum progress of the currently running recipe of the block this cover is attached to.", + "Example: 'Progress: {calc {calc {progress} / {maxProgress}} * 100}%'", + "Usage:", + " {maxProgress} -> the max progress of the currently running recipe"); + multiLang(provider, "gtceu.placeholder_info.maintenance", + "Returns a 1 if there are maintenance problems in the block the cover is attached to, 0 otherwise.", + "Example: 'Maintenance status: {if {maintenance} FIXING\\ REQUIRED OK}'", + "Usage:", + " {maintenance} -> whether there are maintenance problems"); + multiLang(provider, "gtceu.placeholder_info.active", + "Returns a 1 if the block the cover is attached to is currently running a recipe, 0 otherwise.", + "Usage:", + " {active} -> whether there's a currently running recipe"); + multiLang(provider, "gtceu.placeholder_info.voltage", + "Returns the voltage in the wire/cable the cover is on.", + "Usage:", + " {voltage} -> the voltage in the wire/cable"); + multiLang(provider, "gtceu.placeholder_info.amperage", + "Returns the amperage in the wire/cable the cover is on.", + "Usage:", + " {amperage} -> the amperate in the wire/cable"); + multiLang(provider, "gtceu.placeholder_info.ae2energy", + "Returns the energy currently stored in the ME network of the block this cover is on.", + "Usage:", + " {ae2energy} -> the energy in the ME network (in AE units)"); + multiLang(provider, "gtceu.placeholder_info.ae2maxPower", + "Returns the energy capacity of the ME network of the block this cover is on.", + "Usage:", + " {ae2maxPower} -> the energy capacity of the ME network"); + multiLang(provider, "gtceu.placeholder_info.ae2powerUsage", + "Returns the energy consumption of the ME network of the block this cover is on.", + "Usage:", + " {ae2powerUsage} -> the energy consumption of the ME network"); + multiLang(provider, "gtceu.placeholder_info.ae2spatial", + "Returns information about spatial I/O in the ME network of the block this cover is on.", + "Usage:", + " {ae2spatial power} -> the amount of power required to initiate spatial I/O", + " {ae2spatial efficiency} -> the efficiency of the Spatial Containment Structure (SPS)", + " {ae2spatial size} -> the size of the SPS along the specified axis (example: 'Size: {sizeX}x{sizeY}x{sizeZ}')"); + multiLang(provider, "gtceu.placeholder_info.ae2crafting", + "Returns information about auto-crafting in the ME network of the block this cover is on.", + "Usage:", + " {ae2crafting get amount} -> the amount of crafting CPUs in the ME network", + " {ae2crafting get storage} -> the amount of crafting storage the specified CPU has", + " {ae2crafting get threads} -> the amount of co-processors the specified CPU has", + " {ae2crafting get name} -> the name of the specified crafting CPU", + " {ae2crafting get selectionMode} -> the selection mode of the specified crafting CPU (used for manual, automatic or both requests)", + " {ae2crafting get amount} -> the amount of the item that was requested, or 0 if the CPU is idle", + " {ae2crafting get item} -> the display name of the item that was requested, or 0 if the CPU is idle", + " {ae2crafting get progress} -> the crafting job progress, or 0 if the CPU is idle", + " {ae2crafting get time} -> the amount of time elapsed from the start of the craft (in nanoseconds), or 0 if the CPU is idle"); + multiLang(provider, "gtceu.placeholder_info.count", + "Returns how many of the provided arguments are equal to the first (compared as strings, so \"0\" != \"0.0\")", + "Usage:", + " {count [arg2] [arg3] [arg4] ...} -> the amount of arguments that are equal to the first"); + multiLang(provider, "gtceu.placeholder_info.data", + "Stores or retrieves some data from a data item (data stick/orb/module) in one of the slots.", + "If you leave the argument empty, it will be replaced with the value p (p is an integer from 0 to (capacity - 1) that is stored in the data item nbt).", + "Usage:", + " {data get } -> the data stored in the item in the specified slot", + " {data set } -> sets the data stored in the item in the specified slot, returns an empty string", + " {data getp } -> p", + " {data setp } -> sets p, returns an empty string", + " {data inc } -> increments p by 1, if p becomes more than or equal to capacity, sets p to 0", + " {data dec } -> decrements p by 1, if p becomes less than 0, sets p to (capacity - 1)"); + multiLang(provider, "gtceu.placeholder_info.combine", + "Combines all of it's arguments into a single string (by escaping all spaces between the arguments)", + "Example: {combine abc def ghi jkl mno} -> \"abc\\ def\\ ghi\\ jkl\\ mno\"", + "Usage:", + " {combine [arg1] [arg2] [arg3] ...} -> a string that will be treated as a single argument in further placeholders"); + multiLang(provider, "gtceu.placeholder_info.nbt", + "Returns the nbt data of the item in the specified slot", + "Usage:", + " {nbt } -> nbt data"); + multiLang(provider, "gtceu.placeholder_info.toChars", + "Returns the characters of the provided string with spaces between them", + "Example: {toChars example} -> 'e x a m p l e'", + "Usage:", + " {toChars } -> characters"); + multiLang(provider, "gtceu.placeholder_info.toAscii", + "Returns the ASCII code of the provided character", + "Usage:", + " {toAscii } -> ASCII code of the character"); + multiLang(provider, "gtceu.placeholder_info.fromAscii", + "Returns the character represented by the provided ASCII code", + "Usage:", + " {fromAscii } -> a character"); + multiLang(provider, "gtceu.gui.computer_monitor_cover.placeholder_reference", + "All placeholders:", + "(hover for more info)"); + multiLang(provider, "gtceu.placeholder_info.subList", + "Returns arguments from with indexes from l (inclusive) to r (exclusive) (starting from 0)", + "Usage:", + " {subList [arg0] [arg1] ...} -> all arguments with indexes from l to r separated by spaces"); + multiLang(provider, "gtceu.placeholder_info.cmp", + "Returns a 1 or 0 based on the expression in it's arguments", + "Usage:", + " {cmp } -> 1 or 0, operator is one of >, <, >=, <=, ==, !="); + multiLang(provider, "gtceu.placeholder_info.bf", + "Usage:", + " {bf } -> empty string"); + multiLang(provider, "gtceu.placeholder_info.cmd", + "Executes Minecraft commands and returns their output.", + "Requires a data item bound to a player, bind any data item to yourself by right-clicking with it.", + "Usage:", + " {cmd } -> command output"); + multiLang(provider, "gtceu.placeholder_info.tm", + "Returns the ™ symbol", + "Usage:", + " {tm} -> the ™ symbol"); + multiLang(provider, "gtceu.placeholder_info.formatInt", + "Returns a string representation of the provided integer", + "Example: {formatInt 1236457} -> 1.24M", + "Usage:", + " {formatInt } -> string representation of the int"); + provider.add("gtceu.gui.computer_monitor_cover.update_interval", "Update interval (in ticks)"); + provider.add("gtceu.gui.computer_monitor_cover.edit_blank_placeholders", "Edit blank placeholders"); + provider.add("gtceu.gui.computer_monitor_cover.edit_displayed_text", "Edit displayed text"); + provider.add("gtceu.gui.central_monitor.text_scale", "Text scale"); + provider.add("gtceu.gui.central_monitor.group", "Group: %s"); + provider.add("gtceu.gui.central_monitor.group_default_name", "Group #%d"); + provider.add("gtceu.gui.central_monitor.none", "none"); + provider.add("gtceu.central_monitor.size", "Size: (%d+1+%d)x(%d+1+%d)"); + provider.add("gtceu.computer_monitor_cover.error.invalid_number", "Invalid number '%s'!"); + provider.add("gtceu.computer_monitor_cover.error.wrong_number_of_args", "Expected %d args, got %d!"); + provider.add("gtceu.computer_monitor_cover.error.not_enough_args", "Expected at least %d args, got %d!"); + provider.add("gtceu.computer_monitor_cover.error.no_cover", "No cover!"); + provider.add("gtceu.computer_monitor_cover.error.exception", "Unexpected exception occurred: %s"); + provider.add("gtceu.computer_monitor_cover.error.not_in_range", + "Expected %s to be between %d and %d (inclusive), got %d"); + provider.add("gtceu.computer_monitor_cover.error.invalid_args", "Invalid arguments!"); + provider.add("gtceu.computer_monitor_cover.error.missing_item", "Missing %s in slot %d!"); + provider.add("gtceu.computer_monitor_cover.error.bf_invalid_num", + "Invalid number at index %d when processing symbol number %d"); + provider.add("gtceu.computer_monitor_cover.error.bf_invalid", "Invalid character at %d"); + multiLang(provider, "gtceu.gui.computer_monitor_cover.main_textbox_tooltip", + "Input string to display on line %d here.", + "It can have placeholders, for example: 'Energy: {energy}/{energyCapacity} EU'", + "Placeholders can also be inside other placeholders."); + multiLang(provider, "gtceu.gui.computer_monitor_cover.slot_tooltip", + "A slot for items that some placeholders can reference", + "Slot number: %d"); + multiLang(provider, "gtceu.gui.computer_monitor_cover.second_page_textbox_tooltip", + "Input placeholder to be used in place of %s '{}' here.", + "For example, you can have a string 'Energy: {}/{} EU' and 'energy' and 'energyCapacity' in these text boxes."); + provider.add("gtceu.computer_monitor_cover.error.no_placeholder", "No such placeholder: '%s'!"); + provider.add("gtceu.computer_monitor_cover.error.unclosed_bracket", "Unclosed bracket!"); + provider.add("gtceu.computer_monitor_cover.error.unexpected_bracket", "Unexpected closing bracket!"); + provider.add("gtceu.computer_monitor_cover.error.no_ae", "Cover holder does not have an AE2 network!"); + provider.add("gtceu.computer_monitor_cover.error.not_supported", + "This feature is not supported by this block/cover!"); + provider.add("gtceu.central_monitor.gui.create_group", "Create group"); + provider.add("gtceu.central_monitor.gui.remove_from_group", "Remove from group"); + provider.add("gtceu.central_monitor.gui.set_target", "Set target"); + provider.add("gtceu.central_monitor.gui.currently_editing", "Currently editing: %s"); + multiLang(provider, "gtceu.central_monitor.info_tooltip", + "In order to use monitors, you have to split them into groups first. A group may only have 1 module in it.", + "Select them by left-clicking, then click 'Create group'.", + "Then in the settings page for the group you can insert a module, you can configure it in the same page.", + "To delete a group, select all of it's components and click 'Remove from group'.", + "You can quickly select all components of a group by clicking on it's name. Click again to unselect.", + "Some modules may display things depending on the block they target, to set a target for a group select any component of that group and right-click on the target component.", + "You may wish to select a target that is not in the multiblock, you have to use the wireless transmitter cover for that.", + "Place the cover on the target block, right-click it with a data stick and put that data stick into a data access hatch in the multiblock.", + "Then select the data access hatch as the target, and set the slot index of your data stick in the number field that appeared."); + provider.add("gtceu.tooltip.player_bind", "Bound to player: %s"); } /** diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java index 33a20188682..7e5894bea69 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java @@ -22,6 +22,16 @@ public class ComputerRecipes { public static void init(Consumer provider) { + ASSEMBLER_RECIPES.recipeBuilder("basic_data_access_hatch") + .inputItems(ITEM_IMPORT_BUS[HV]) + .inputItems(TOOL_DATA_STICK, 4) + .inputItems(CustomTags.HV_CIRCUITS, 4) + .outputItems(BASIC_DATA_ACCESS_HATCH) + .inputFluids(Polyethylene, L * 2) + .cleanroom(CleanroomType.CLEANROOM) + .duration(200).EUt(VA[HV]) + .addMaterialInfo(true).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("data_access_hatch") .inputItems(ITEM_IMPORT_BUS[EV]) .inputItems(TOOL_DATA_STICK, 4) @@ -43,6 +53,46 @@ public static void init(Consumer provider) { .duration(400).EUt(6000) .addMaterialInfo(true, true).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("wireless_transmitter_cover") + .inputItems(plate, EnderPearl) + .inputItems(foil, AnnealedCopper) + .inputItems(EMITTER_MV) + .inputItems(wireFine, Platinum) + .inputFluids(SolderingAlloy, L) + .outputItems(COVER_WIRELESS_TRANSMITTER) + .duration(1000).EUt(VA[MV]) + .addMaterialInfo(true).save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("text_module") + .inputItems(PLASTIC_CIRCUIT_BOARD) + .inputItems(foil, Steel, 4) + .inputItems(wireFine, RedAlloy, 4) + .inputItems(CustomTags.MV_CIRCUITS) + .inputFluids(SolderingAlloy, L) + .outputItems(TEXT_MODULE) + .duration(1000).EUt(VA[MV]) + .addMaterialInfo(true).save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("image_module") + .inputItems(PLASTIC_CIRCUIT_BOARD) + .inputItems(foil, Electrum, 4) + .inputItems(wireFine, Silver, 4) + .inputItems(CustomTags.MV_CIRCUITS) + .inputFluids(SolderingAlloy, L) + .outputItems(IMAGE_MODULE) + .duration(1000).EUt(VA[MV]) + .addMaterialInfo(true).save(provider); + + ASSEMBLER_RECIPES.recipeBuilder("monitor_casing") + .inputItems(HULL[MV]) + .inputItems(COVER_SCREEN) + .inputItems(plate, Glass, 4) + .inputItems(wireFine, RedAlloy, 4) + .inputFluids(Glowstone, L) + .outputItems(MONITOR, ConfigHolder.INSTANCE.recipes.casingsPerCraft) + .duration(1000).EUt(VA[MV]) + .addMaterialInfo(true).save(provider); + ASSEMBLER_RECIPES.recipeBuilder("high_power_casing") .inputItems(frameGt, Iridium) .inputItems(plate, Iridium, 6) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java index c871f64d3c7..2fa34fc4490 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java @@ -641,6 +641,11 @@ public static void init(Consumer provider) { new MaterialEntry(TagPrefix.gear, GTMaterials.TungstenSteel), 'P', CustomTags.LuV_CIRCUITS, 'A', GTMachines.HULL[GTValues.LuV].asStack(), 'C', new MaterialEntry(TagPrefix.pipeLargeFluid, GTMaterials.TungstenSteel)); + VanillaRecipeHelper.addShapedRecipe(provider, true, "central_monitor", + GTMultiMachines.CENTRAL_MONITOR.asStack(), "CMC", "SAE", "FFF", 'C', + CustomTags.MV_CIRCUITS, 'M', GTItems.COVER_SCREEN, 'S', GTItems.SENSOR_MV, + 'A', GTMachines.HULL[GTValues.MV].asStack(), 'E', GTItems.EMITTER_MV, + 'F', GTBlocks.CASING_ALUMINIUM_FROSTPROOF.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_bronze_boiler", GTMultiMachines.LARGE_BOILER_BRONZE.asStack(), "PSP", "SAS", "PSP", 'P', diff --git a/src/main/java/com/gregtechceu/gtceu/integration/ae2/GTAEPlaceholders.java b/src/main/java/com/gregtechceu/gtceu/integration/ae2/GTAEPlaceholders.java new file mode 100644 index 00000000000..fc5d0466e2e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/ae2/GTAEPlaceholders.java @@ -0,0 +1,240 @@ +package com.gregtechceu.gtceu.integration.ae2; + +import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; +import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.placeholder.*; +import com.gregtechceu.gtceu.api.placeholder.exceptions.*; +import com.gregtechceu.gtceu.utils.GTStringUtils; + +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.registries.ForgeRegistries; + +import appeng.api.networking.GridHelper; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNode; +import appeng.api.networking.IInWorldGridNodeHost; +import appeng.api.networking.crafting.CraftingJobStatus; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingService; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.GenericStack; +import appeng.api.stacks.KeyCounter; +import appeng.me.helpers.IGridConnectedBlockEntity; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3i; + +import java.util.List; + +import javax.annotation.ParametersAreNonnullByDefault; + +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +public class GTAEPlaceholders { + + private GTAEPlaceholders() {} + + private static IGrid getGrid(PlaceholderContext ctx) throws PlaceholderException { + if (ctx.pos() == null) throw new NotSupportedException(); + IInWorldGridNodeHost nodeHost = GridHelper.getNodeHost(ctx.level(), ctx.pos()); + if (nodeHost != null) { + IGridNode node = nodeHost.getGridNode(ctx.side()); + if (node != null) return node.getGrid(); + } ; + BlockEntity blockEntity = ctx.level().getBlockEntity(ctx.pos()); + if (blockEntity instanceof IMachineBlockEntity machineBlockEntity) { + if (machineBlockEntity.getMetaMachine() instanceof IGridConnectedBlockEntity gridMachine) { + IGrid nullable = gridMachine.getMainNode().getGrid(); + if (nullable == null) throw new NoMENetworkException(); + return nullable; + } + } + if (blockEntity instanceof IGridConnectedBlockEntity gridBlockEntity) { + IGridNode node = gridBlockEntity.getGridNode(); + if (node != null) return gridBlockEntity.getGridNode().getGrid(); + } + throw new NoMENetworkException(); + } + + private static long countItems(String id, IGrid grid) { + Item item = ForgeRegistries.ITEMS.getValue(new ResourceLocation(id)); + if (item == null) return 0; + GenericStack stack = GenericStack.fromItemStack(new ItemStack(item, 1)); + if (stack == null) return 0; + return grid.getStorageService().getInventory().getAvailableStacks().get(stack.what()); + } + + private static long countItems(@Nullable ItemFilter filter, IGrid grid) { + KeyCounter stacks = grid.getStorageService().getCachedInventory(); + long count = 0; + for (var stack : stacks) { + if (stack.getKey() instanceof AEItemKey && + (filter == null || filter.test(stack.getKey().wrapForDisplayOrFilter()))) + count += stack.getLongValue(); + } + return count; + } + + private static long countFluids(@Nullable String id, IGrid grid) { + if (id == null) { + KeyCounter stacks = grid.getStorageService().getCachedInventory(); + long count = 0; + for (var stack : stacks) { + if (stack.getKey() instanceof AEFluidKey) count += stack.getLongValue(); + } + return count; + } + Fluid fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(id)); + if (fluid == null) return 0; + GenericStack stack = GenericStack.fromFluidStack(new FluidStack(fluid, 1)); + if (stack == null) return 0; + return grid.getStorageService().getInventory().getAvailableStacks().get(stack.what()); + } + + private static Vector3i getSpatialSize(IGrid grid) { + BlockPos start = grid.getSpatialService().getMin(); + BlockPos end = grid.getSpatialService().getMax(); + BlockPos tmp = end.subtract(start); + return new Vector3i(tmp.getX(), tmp.getY(), tmp.getZ()).absolute(); + } + + public static void init() { + PlaceholderHandler.addPlaceholder(new Placeholder("ae2itemCount") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + if (args.isEmpty()) return MultiLineComponent.literal(countItems((ItemFilter) null, grid)); + if (args.size() == 1) + return MultiLineComponent.literal(countItems(GTStringUtils.componentsToString(args.get(0)), grid)); + if (GTStringUtils.equals(args.get(0), "filter")) { + int slot = PlaceholderUtils.toInt(args.get(1)); + try { + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + return MultiLineComponent.literal(countItems( + ItemFilter.loadFilter(ctx.itemStackHandler().getStackInSlot(slot - 1)), grid)); + } catch (NullPointerException e) { + throw new MissingItemException("filter", slot); + } + } + throw new InvalidArgsException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("ae2fluidCount") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + if (args.isEmpty()) return MultiLineComponent.literal(countFluids(null, grid)); + if (args.size() == 1) + return MultiLineComponent.literal(countFluids(GTStringUtils.componentsToString(args.get(0)), grid)); + throw new WrongNumberOfArgsException(1, args.size()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("ae2power") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + PlaceholderUtils.checkArgs(args, 0); + return MultiLineComponent.literal(grid.getEnergyService().getStoredPower()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("ae2maxPower") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + PlaceholderUtils.checkArgs(args, 0); + return MultiLineComponent.literal(grid.getEnergyService().getMaxStoredPower()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("ae2powerUsage") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + PlaceholderUtils.checkArgs(args, 0); + return MultiLineComponent.literal(grid.getEnergyService().getAvgPowerUsage()); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("ae2spatial") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + PlaceholderUtils.checkArgs(args, 1); + if (GTStringUtils.equals(args.get(0), "power")) { + return MultiLineComponent.literal(grid.getSpatialService().requiredPower()); + } else if (GTStringUtils.equals(args.get(0), "efficiency")) { + return MultiLineComponent.literal(grid.getSpatialService().currentEfficiency()); + } else if (GTStringUtils.equals(args.get(0), "sizeX")) { + return MultiLineComponent.literal(getSpatialSize(grid).x); + } else if (GTStringUtils.equals(args.get(0), "sizeY")) { + return MultiLineComponent.literal(getSpatialSize(grid).y); + } else if (GTStringUtils.equals(args.get(0), "sizeZ")) { + return MultiLineComponent.literal(getSpatialSize(grid).z); + } else throw new InvalidArgsException(); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("ae2crafting") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + IGrid grid = getGrid(ctx); + PlaceholderUtils.checkArgs(args, 1, true); + ICraftingService crafting = grid.getCraftingService(); + if (GTStringUtils.equals(args.get(0), "get")) { + if (GTStringUtils.equals(args.get(1), "amount")) + return MultiLineComponent.literal(crafting.getCpus().size()); + int index = PlaceholderUtils.toInt(args.get(1)); + int i = 0; + for (ICraftingCPU cpu : crafting.getCpus()) { + if (index - 1 == i) { + CraftingJobStatus job = cpu.getJobStatus(); + if (GTStringUtils.equals(args.get(2), "storage")) + return MultiLineComponent.literal(cpu.getAvailableStorage()); + else if (GTStringUtils.equals(args.get(2), "threads")) + return MultiLineComponent.literal(cpu.getCoProcessors()); + else if (GTStringUtils.equals(args.get(2), "name")) + return MultiLineComponent + .of(cpu.getName() == null ? Component.literal("Crafting CPU " + i) : + cpu.getName().copy()); + else if (GTStringUtils.equals(args.get(2), "selectionMode")) + return MultiLineComponent.literal(cpu.getSelectionMode().name()); + else if (job == null) return MultiLineComponent.literal(0); + else if (GTStringUtils.equals(args.get(2), "amount")) + return MultiLineComponent.literal(job.crafting().amount()); + else if (GTStringUtils.equals(args.get(2), "item")) + return MultiLineComponent.of(job.crafting().what().getDisplayName().copy()); + else if (GTStringUtils.equals(args.get(2), "progress")) + return MultiLineComponent.literal(job.progress()); + else if (GTStringUtils.equals(args.get(2), "time")) + return MultiLineComponent.literal(job.elapsedTimeNanos()); + else throw new InvalidArgsException(); + } + i++; + } + throw new OutOfRangeException("cpu number", 1, crafting.getCpus().size(), index); + } // else if (GTStringUtils.equals(args.get(0), "request")) {} gonna implement that someday :) + throw new InvalidArgsException(); + } + }); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/CCTweakedPlugin.java b/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/CCTweakedPlugin.java index b71737b29af..2bcced8d85d 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/CCTweakedPlugin.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/CCTweakedPlugin.java @@ -13,9 +13,11 @@ public static void init() { ComputerCraftAPI.registerGenericSource(new EnergyInfoPeripheral()); ComputerCraftAPI.registerGenericSource(new TurbineMachinePeripheral()); ComputerCraftAPI.registerGenericSource(new WorkablePeripheral()); + ComputerCraftAPI.registerGenericSource(new CoverHolderPeripheral()); ForgeComputerCraftAPI.registerGenericCapability(GTCapability.CAPABILITY_CONTROLLABLE); ForgeComputerCraftAPI.registerGenericCapability(GTCapability.CAPABILITY_ENERGY_INFO_PROVIDER); ForgeComputerCraftAPI.registerGenericCapability(GTCapability.CAPABILITY_TURBINE_MACHINE); ForgeComputerCraftAPI.registerGenericCapability(GTCapability.CAPABILITY_WORKABLE); + ForgeComputerCraftAPI.registerGenericCapability(GTCapability.CAPABILITY_COVERABLE); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/peripherals/CoverHolderPeripheral.java b/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/peripherals/CoverHolderPeripheral.java new file mode 100644 index 00000000000..18ba54ae798 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/cctweaked/peripherals/CoverHolderPeripheral.java @@ -0,0 +1,30 @@ +package com.gregtechceu.gtceu.integration.cctweaked.peripherals; + +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.placeholder.IPlaceholderInfoProviderCover; + +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; + +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.GenericPeripheral; + +public class CoverHolderPeripheral implements GenericPeripheral { + + @Override + public String id() { + return "gtceu:coverable"; + } + + @LuaFunction + public static MethodResult setBufferedText(ICoverable coverable, String face, int line, String text) { + Direction direction = Direction.byName(face); + if (direction == null) return MethodResult.of(false, "invalid face"); + if (line < 1 || line > 100) return MethodResult.of(false, "line must be from 1 to 100 (inclusive)"); + if (coverable.getCoverAtSide(direction) instanceof IPlaceholderInfoProviderCover cover) { + cover.setDisplayTargetBufferLine(line, Component.literal(text)); + return MethodResult.of(true, "success"); + } else return MethodResult.of(false, "invalid cover"); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateDisplaySources.java b/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateDisplaySources.java new file mode 100644 index 00000000000..55e9e0aa1c2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateDisplaySources.java @@ -0,0 +1,36 @@ +package com.gregtechceu.gtceu.integration.create; + +import com.gregtechceu.gtceu.api.registry.GTRegistries; +import com.gregtechceu.gtceu.api.registry.registrate.GTRegistrate; +import com.gregtechceu.gtceu.common.registry.GTRegistration; +import com.gregtechceu.gtceu.integration.create.display.ComputerMonitorCoverDisplaySource; + +import net.minecraft.core.registries.Registries; + +import com.simibubi.create.api.behaviour.display.DisplaySource; +import com.simibubi.create.api.registry.registrate.SimpleBuilder; +import com.tterrag.registrate.util.entry.RegistryEntry; + +import java.util.function.Supplier; + +@SuppressWarnings("unused") +public class GTCreateDisplaySources { + + public static final RegistryEntry COMPUTER_MONITOR_COVER = registerToAllMachines( + "computer_monitor_cover", ComputerMonitorCoverDisplaySource::new); + + @SuppressWarnings("SameParameterValue") + private static RegistryEntry registerToAllMachines(String name, Supplier supplier) { + SimpleBuilder builder = GTCreateIntegration + .displaySource(GTRegistration.REGISTRATE, name, supplier); + builder.onRegisterAfter( + Registries.BLOCK_ENTITY_TYPE, + source -> GTRegistries.MACHINES.entries().forEach( + (entry) -> DisplaySource.BY_BLOCK_ENTITY.add( + entry.getValue().getBlockEntityType(), + source))); + return builder.register(); + } + + public static void init() {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateDisplayTargets.java b/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateDisplayTargets.java new file mode 100644 index 00000000000..9c7c0ad62f4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateDisplayTargets.java @@ -0,0 +1,35 @@ +package com.gregtechceu.gtceu.integration.create; + +import com.gregtechceu.gtceu.api.registry.GTRegistries; +import com.gregtechceu.gtceu.api.registry.registrate.GTRegistrate; +import com.gregtechceu.gtceu.common.registry.GTRegistration; +import com.gregtechceu.gtceu.integration.create.display.ComputerMonitorCoverDisplayTarget; + +import net.minecraft.core.registries.Registries; + +import com.simibubi.create.api.behaviour.display.DisplayTarget; +import com.simibubi.create.api.registry.registrate.SimpleBuilder; +import com.tterrag.registrate.util.entry.RegistryEntry; + +import java.util.function.Supplier; + +public class GTCreateDisplayTargets { + + public static final RegistryEntry COMPUTER_MONITOR_COVER = registerToAllMachines( + "computer_monitor_cover", ComputerMonitorCoverDisplayTarget::new); + + @SuppressWarnings("SameParameterValue") + private static RegistryEntry registerToAllMachines(String name, Supplier supplier) { + SimpleBuilder builder = GTCreateIntegration + .displayTarget(GTRegistration.REGISTRATE, name, supplier); + builder.onRegisterAfter( + Registries.BLOCK_ENTITY_TYPE, + target -> GTRegistries.MACHINES.entries().forEach( + (entry) -> DisplayTarget.BY_BLOCK_ENTITY.register( + entry.getValue().getBlockEntityType(), + target))); + return builder.register(); + } + + public static void init() {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateIntegration.java b/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateIntegration.java new file mode 100644 index 00000000000..523e95a6ed1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/create/GTCreateIntegration.java @@ -0,0 +1,243 @@ +package com.gregtechceu.gtceu.integration.create; + +import com.gregtechceu.gtceu.api.placeholder.*; +import com.gregtechceu.gtceu.api.placeholder.exceptions.InvalidArgsException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.MissingItemException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.NotSupportedException; +import com.gregtechceu.gtceu.api.placeholder.exceptions.PlaceholderException; +import com.gregtechceu.gtceu.api.registry.registrate.GTRegistrate; +import com.gregtechceu.gtceu.utils.GTStringUtils; + +import net.createmod.catnip.data.Couple; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import com.simibubi.create.AllItems; +import com.simibubi.create.Create; +import com.simibubi.create.api.behaviour.display.DisplaySource; +import com.simibubi.create.api.behaviour.display.DisplayTarget; +import com.simibubi.create.api.registry.CreateRegistries; +import com.simibubi.create.api.registry.registrate.SimpleBuilder; +import com.simibubi.create.content.redstone.link.IRedstoneLinkable; +import com.simibubi.create.content.redstone.link.RedstoneLinkNetworkHandler; +import com.simibubi.create.content.redstone.link.controller.LinkedControllerItem; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +public class GTCreateIntegration { + + private GTCreateIntegration() {} + + public static void init() { + GTCreateDisplaySources.init(); + GTCreateDisplayTargets.init(); + + PlaceholderHandler.addPlaceholder(new Placeholder("redstone", 1) { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + return processRedstonePlaceholder(ctx, args); + } + }); + PlaceholderHandler.addPlaceholder(new Placeholder("displayTarget") { + + @Override + public MultiLineComponent apply(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1); + if (!(ctx.cover() instanceof IPlaceholderInfoProviderCover cover)) throw new NotSupportedException(); + int i = PlaceholderUtils.toInt(args.get(0)); + PlaceholderUtils.checkRange("line number", 1, 100, i); + return MultiLineComponent.of(cover.getCreateDisplayTargetBuffer().get(i - 1)); + } + }); + } + + private static int getRedstoneLinkPower(PlaceholderContext ctx, + Couple freq) { + IRedstoneLinkable linkable = new IRedstoneLinkable() { + + @Override + public int getTransmittedStrength() { + return 0; + } + + @Override + public void setReceivedStrength(int power) {} + + @Override + public boolean isListening() { + return true; + } + + @Override + public boolean isAlive() { + return true; + } + + @Override + public Couple getNetworkKey() { + return freq; + } + + @Override + public BlockPos getLocation() { + return ctx.pos(); + } + }; + Set network = Create.REDSTONE_LINK_NETWORK_HANDLER.getNetworkOf(ctx.level(), + linkable); + int power = 0; + for (IRedstoneLinkable i : network) { + if (!i.isAlive()) continue; + if (!RedstoneLinkNetworkHandler.withinRange(i, linkable)) continue; + power = Math.max(power, i.getTransmittedStrength()); + } + return power; + } + + private static void setRedstoneLinkPower(PlaceholderContext ctx, + Couple freq, int power) { + TemporaryRedstoneLinkTransmitter linkable = new TemporaryRedstoneLinkTransmitter(freq, power, + ctx.pos(), ctx.level()); + Create.REDSTONE_LINK_NETWORK_HANDLER.addToNetwork(ctx.level(), linkable); + } + + private static MultiLineComponent processRedstonePlaceholder(PlaceholderContext ctx, + List args) throws PlaceholderException { + PlaceholderUtils.checkArgs(args, 1, true); + if (GTStringUtils.equals(args.get(0), "get")) { + PlaceholderUtils.checkArgs(args, 2, true); + if (GTStringUtils.equals(args.get(1), "link")) { + PlaceholderUtils.checkArgs(args, 4); + int slot = PlaceholderUtils.toInt(args.get(2)); + int freq_slot = PlaceholderUtils.toInt(args.get(3)); + PlaceholderUtils.checkRange("slot index", 1, 8, slot); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + ItemStack item = ctx.itemStackHandler().getStackInSlot(slot - 1); + if (item.is(AllItems.LINKED_CONTROLLER.get())) { + Couple freq = LinkedControllerItem.toFrequency(item, + freq_slot); + return MultiLineComponent.literal(getRedstoneLinkPower(ctx, freq)); + } else throw new MissingItemException("redstone link", slot); + } else { + Direction direction = Direction.byName(args.get(1).toString()); + if (direction == null) + throw new InvalidArgsException(); + return MultiLineComponent.literal(ctx.level().getSignal(ctx.pos().relative(direction), direction)); + } + } else if (GTStringUtils.equals(args.get(0), "set")) { + PlaceholderUtils.checkArgs(args, 2, true); + if (GTStringUtils.equals(args.get(1), "link")) { + PlaceholderUtils.checkArgs(args, 5); + int slot = PlaceholderUtils.toInt(args.get(2)); + int freq_slot = PlaceholderUtils.toInt(args.get(3)); + int power = PlaceholderUtils.toInt(args.get(4)); + PlaceholderUtils.checkRange("redstone power", 0, 15, power); + PlaceholderUtils.checkRange("slot", 1, 8, slot); + if (ctx.itemStackHandler() == null) throw new NotSupportedException(); + ItemStack item = ctx.itemStackHandler().getStackInSlot(slot - 1); + if (item.is(AllItems.LINKED_CONTROLLER.get())) { + Couple freq = LinkedControllerItem.toFrequency(item, + freq_slot); + setRedstoneLinkPower(ctx, freq, power); + return MultiLineComponent.empty(); + } else throw new MissingItemException("redstone link", slot); + } else { + int power = PlaceholderUtils.toInt(args.get(1)); + PlaceholderUtils.checkRange("redstone power", 0, 15, power); + if (ctx.cover() == null) throw new NotSupportedException(); + ctx.cover().setRedstoneSignalOutput(power); + return MultiLineComponent.empty(); + } + } else { + throw new InvalidArgsException(); + } + } + + public static < + T extends DisplaySource> SimpleBuilder displaySource(GTRegistrate registrate, + String name, + Supplier supplier) { + return registrate.entry(name, callback -> new SimpleBuilder<>( + registrate, registrate, name, callback, CreateRegistries.DISPLAY_SOURCE, supplier) + .byBlock(DisplaySource.BY_BLOCK) + .byBlockEntity(DisplaySource.BY_BLOCK_ENTITY)); + } + + public static < + T extends DisplayTarget> SimpleBuilder displayTarget(GTRegistrate registrate, + String name, + Supplier supplier) { + return registrate.entry(name, callback -> new SimpleBuilder<>( + registrate, registrate, name, callback, CreateRegistries.DISPLAY_TARGET, supplier) + .byBlock(DisplayTarget.BY_BLOCK) + .byBlockEntity(DisplayTarget.BY_BLOCK_ENTITY)); + } + + public static class TemporaryRedstoneLinkTransmitter implements IRedstoneLinkable { + + private static final ArrayList transmitters = new ArrayList<>(); + private final int power; + private final Couple freq; + private final BlockPos pos; + private final Level level; + private boolean alive; + + public TemporaryRedstoneLinkTransmitter(Couple frequency, int power, + BlockPos pos, Level level) { + this.power = power; + this.freq = frequency; + this.alive = true; + this.pos = pos; + this.level = level; + transmitters.add(this); + } + + @Override + public int getTransmittedStrength() { + return power; + } + + @Override + public void setReceivedStrength(int power) {} + + @Override + public boolean isListening() { + return false; + } + + @Override + public boolean isAlive() { + return alive; + } + + @Override + public Couple getNetworkKey() { + return freq; + } + + @Override + public BlockPos getLocation() { + return pos; + } + + public void destroy() { + this.alive = false; + Create.REDSTONE_LINK_NETWORK_HANDLER.updateNetworkOf(level, this); + } + + public static void destroyAll() { + while (!transmitters.isEmpty()) { + transmitters.get(transmitters.size() - 1).destroy(); + transmitters.remove(transmitters.size() - 1); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/create/display/ComputerMonitorCoverDisplaySource.java b/src/main/java/com/gregtechceu/gtceu/integration/create/display/ComputerMonitorCoverDisplaySource.java new file mode 100644 index 00000000000..5853ef40337 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/create/display/ComputerMonitorCoverDisplaySource.java @@ -0,0 +1,38 @@ +package com.gregtechceu.gtceu.integration.create.display; + +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.common.cover.ComputerMonitorCover; +import com.gregtechceu.gtceu.utils.GTStringUtils; + +import net.minecraft.network.chat.MutableComponent; + +import com.simibubi.create.api.behaviour.display.DisplaySource; +import com.simibubi.create.content.redstone.displayLink.DisplayLinkContext; +import com.simibubi.create.content.redstone.displayLink.target.DisplayTargetStats; + +import java.util.List; + +public class ComputerMonitorCoverDisplaySource extends DisplaySource { + + private int refreshTicks = 100; + + @Override + public List provideText(DisplayLinkContext context, DisplayTargetStats stats) { + ICoverable coverable = GTCapabilityHelper.getCoverable(context.level(), context.getSourcePos(), + context.blockEntity().getDirection().getOpposite()); + if (coverable != null) { + if (coverable.getCoverAtSide( + context.blockEntity().getDirection().getOpposite()) instanceof ComputerMonitorCover cover) { + refreshTicks = cover.getUpdateInterval(); + return cover.getText(); + } + } + return GTStringUtils.literalLine("No cover!"); + } + + @Override + public int getPassiveRefreshTicks() { + return refreshTicks; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/create/display/ComputerMonitorCoverDisplayTarget.java b/src/main/java/com/gregtechceu/gtceu/integration/create/display/ComputerMonitorCoverDisplayTarget.java new file mode 100644 index 00000000000..1599ea4ce04 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/create/display/ComputerMonitorCoverDisplayTarget.java @@ -0,0 +1,37 @@ +package com.gregtechceu.gtceu.integration.create.display; + +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.placeholder.IPlaceholderInfoProviderCover; + +import net.minecraft.core.Direction; +import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.MutableComponent; + +import com.simibubi.create.api.behaviour.display.DisplayTarget; +import com.simibubi.create.content.redstone.displayLink.DisplayLinkContext; +import com.simibubi.create.content.redstone.displayLink.target.DisplayTargetStats; + +import java.util.List; + +public class ComputerMonitorCoverDisplayTarget extends DisplayTarget { + + @Override + public void acceptText(int line, List text, DisplayLinkContext context) { + ICoverable coverable = GTCapabilityHelper.getCoverable(context.level(), context.getTargetPos(), Direction.DOWN); + MutableComponent component = MutableComponent.create(ComponentContents.EMPTY); + text.forEach(component::append); + if (coverable != null) { + for (Direction face : Direction.values()) { + if (coverable.getCoverAtSide(face) instanceof IPlaceholderInfoProviderCover cover) { + cover.setDisplayTargetBufferLine(line, component); + } + } + } + } + + @Override + public DisplayTargetStats provideStats(DisplayLinkContext context) { + return new DisplayTargetStats(100, 1000, this); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/syncdata/MonitorGroupPayload.java b/src/main/java/com/gregtechceu/gtceu/syncdata/MonitorGroupPayload.java new file mode 100644 index 00000000000..acfa684cc4f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/syncdata/MonitorGroupPayload.java @@ -0,0 +1,62 @@ +package com.gregtechceu.gtceu.syncdata; + +import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.common.machine.multiblock.electric.monitor.MonitorGroup; + +import com.lowdragmc.lowdraglib.syncdata.payload.ObjectTypedPayload; + +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.Tag; + +import org.jetbrains.annotations.Nullable; + +public class MonitorGroupPayload extends ObjectTypedPayload { + + @Override + public @Nullable Tag serializeNBT() { + CompoundTag tag = new CompoundTag(); + tag.putString("name", payload.getName()); + ListTag list = new ListTag(); + payload.getRelativePositions().forEach(pos -> { + list.add(NbtUtils.writeBlockPos(pos)); + }); + if (payload.getTargetRaw() != null) { + tag.put("targetPos", NbtUtils.writeBlockPos(payload.getTargetRaw())); + if (payload.getTargetCoverSide() != null) { + tag.putString("targetSide", payload.getTargetCoverSide().getSerializedName()); + } + } + tag.put("positions", list); + tag.putInt("dataSlot", payload.getDataSlot()); + tag.put("items", payload.getItemStackHandler().serializeNBT()); + tag.put("placeholderSlots", payload.getPlaceholderSlotsHandler().serializeNBT()); + return tag; + } + + @Override + public void deserializeNBT(Tag tag) { + if (tag instanceof CompoundTag compoundTag) { + CustomItemStackHandler handler = new CustomItemStackHandler(), + placeholderSlotsHandler = new CustomItemStackHandler(); + handler.deserializeNBT(compoundTag.getCompound("items")); + placeholderSlotsHandler.deserializeNBT(compoundTag.getCompound("placeholderSlots")); + payload = new MonitorGroup(compoundTag.getString("name"), handler, placeholderSlotsHandler); + ListTag list = compoundTag.getList("positions", Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + payload.add(NbtUtils.readBlockPos(list.getCompound(i))); + } + if (compoundTag.contains("targetPos", Tag.TAG_COMPOUND)) { + payload.setTarget(NbtUtils.readBlockPos(compoundTag.getCompound("targetPos"))); + if (compoundTag.contains("targetSide", Tag.TAG_STRING)) { + payload.setTargetCoverSide(Direction.byName(compoundTag.getString("targetSide"))); + } + if (compoundTag.contains("dataSlot", Tag.TAG_INT)) { + payload.setDataSlot(compoundTag.getInt("dataSlot")); + } + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/GTMath.java b/src/main/java/com/gregtechceu/gtceu/utils/GTMath.java index 05aee21f19e..6cae4eed2a3 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/GTMath.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/GTMath.java @@ -62,4 +62,13 @@ public static int hashLongs(long... vals) { public static float ratio(BigInteger a, BigInteger b) { return new BigDecimal(a).divide(new BigDecimal(b), MathContext.DECIMAL32).floatValue(); } + + public static int ceilDiv(int x, int y) { + final int q = x / y; + // if the signs are the same and modulo not zero, round up + if ((x ^ y) >= 0 && (q * y != x)) { + return q + 1; + } + return q; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/GTStringUtils.java b/src/main/java/com/gregtechceu/gtceu/utils/GTStringUtils.java index 7ff60bee2da..70912110719 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/GTStringUtils.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/GTStringUtils.java @@ -1,11 +1,21 @@ package com.gregtechceu.gtceu.utils; +import net.minecraft.ChatFormatting; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; public class GTStringUtils { @@ -27,4 +37,189 @@ public static String fluidStackToString(@NotNull FluidStack stack) { ResourceLocation fluidId = BuiltInRegistries.FLUID.getKey(stack.getFluid()); return stack.getAmount() + "x_" + fluidId.getNamespace() + "_" + fluidId.getPath(); } + + /** + * This function does this: + *
    + *
  • {@code 1} -> {@code "1st"}
  • + *
  • {@code 2} -> {@code "2nd"}
  • + *
  • {@code 3} -> {@code "3rd"}
  • + *
  • {@code 4} -> {@code "4th"}
  • + *
  • ...
  • + *
+ */ + @NotNull + public static String getIntOrderingSuffix(int x) { + if ((x % 100) / 10 == 1) return x + "th"; + if (x % 10 == 1) return x + "st"; + if (x % 10 == 2) return x + "nd"; + if (x % 10 == 3) return x + "rd"; + return x + "th"; + } + + /** + * Returns a string with the result of the provided expression. + * This function is intended for use with user input. + * For example: + *
    + *
  • {@code {"12", "+", "34"}} -> {@code "46"}
  • + *
  • {@code {"sqrt", "16"}} -> {@code "4"}
  • + *
  • {@code {"round", "4.5"}} -> {@code "5"}
  • + *
  • {@code {"literally any string"}} -> {@code "literally any string"}
  • + *
  • {@code {"~", "0"}} -> {@code "-1"} // signed bitwise inversion
  • + *
+ * Currently the operations are: {@code {"+", "-", "*", "/", "%", ">>", "<<", "~", "round", "ceil", "floor", + * "sqrt"}} + * + * @param args the arguments, including operands and operation to calculate + * @return the result of the calculation, {@code "Invalid number!"} or {@code "Invalid expression!"} + */ + @NotNull + public static String calc(@NotNull List args) { + // yes I know this is terrible code, but I want to be able to do math in placeholders + // not going to do anything crazy like including lua or python here + if (args.size() == 3) { + try { + long a = Long.parseLong(args.get(0)); + long b = Long.parseLong(args.get(2)); + return switch (args.get(1)) { + case "+" -> String.valueOf(a + b); + case "-" -> String.valueOf(a - b); + case "*" -> String.valueOf(a * b); + case "/" -> String.valueOf(a / b); + case "%" -> String.valueOf(a % b); + case "<<" -> String.valueOf(a << b); + case ">>" -> String.valueOf(a >> b); + default -> "No such operation: '%s'".formatted(args.get(1)); + }; + } catch (NumberFormatException e) { + try { + double a = Double.parseDouble(args.get(0)); + double b = Double.parseDouble(args.get(2)); + return switch (args.get(1)) { + case "/" -> String.valueOf(a / b); + case "+" -> String.valueOf(a + b); + case "-" -> String.valueOf(a - b); + case "*" -> String.valueOf(a * b); + default -> "Invalid number: '%s' or operation '%s'".formatted(e.getMessage(), args.get(1)); + }; + } catch (NumberFormatException ex) { + return "Invalid number '%s'!".formatted(ex.getMessage()); + } + } + } else if (args.size() == 2) { + try { + long a = Long.parseLong(args.get(1)); + return switch (args.get(0)) { + case "~" -> String.valueOf(~a); + case "sqrt" -> String.valueOf(Math.sqrt(a)); + default -> "No such operation: '%s'".formatted(args.get(0)); + }; + } catch (NumberFormatException e) { + try { + double a = Double.parseDouble(args.get(1)); + return switch (args.get(0)) { + case "round" -> String.valueOf(Math.round(a)); + case "ceil" -> String.valueOf(Math.ceil(a)); + case "floor" -> String.valueOf(Math.floor(a)); + case "sqrt" -> String.valueOf(Math.sqrt(a)); + default -> "Invalid number '%s' or operation '%s'!".formatted(e.getMessage(), args.get(0)); + }; + } catch (NumberFormatException e2) { + return "Invalid number '%s'!".formatted(e2.getMessage()); + } + } + } else if (args.size() == 1) return args.get(0); + return "Invalid expression!"; + } + + public static List literalLine(String s) { + return new ArrayList<>(List.of(Component.literal(s))); + } + + public static List literalLine(long n) { + return literalLine(String.valueOf(n)); + } + + public static boolean equals(List components, String s) { + return Objects.equals(componentsToString(components), s); + } + + public static double toDouble(List components) throws NumberFormatException { + if (components.isEmpty()) return 0; + if (components.size() > 1) throw new NumberFormatException(componentsToString(components)); + return Double.parseDouble(components.get(0).getString()); + } + + public static int toInt(List components) throws NumberFormatException { + if (components.isEmpty()) return 0; + if (components.size() > 1) throw new NumberFormatException(componentsToString(components)); + return Integer.parseInt(components.get(0).getString()); + } + + public static String componentsToString(List components) { + StringBuilder out = new StringBuilder(); + if (components.isEmpty()) return out.toString(); + for (Component component : components) { + out.append(component.getString()); + out.append('\n'); + } + return out.substring(0, out.length() - 1); + } + + public static void append(List components, @Nullable String s) { + if (s != null) + GTUtil.getLast(components).append(s); + } + + public static void append(List components, char c) { + append(components, String.valueOf(c)); + } + + public static void append(List components, @Nullable List lines) { + if (lines == null) return; + if (lines.isEmpty()) return; + for (Component line : lines) { + GTUtil.getLast(components).append(line); + components.add(MutableComponent.create(ComponentContents.EMPTY)); + } + components.remove(components.size() - 1); + } + + public static List toImmutable(List singleOrMultiLang) { + return singleOrMultiLang.stream().map((c) -> (Component) c).toList(); + } + + public static List literalLine(double d) { + return literalLine(String.valueOf(d)); + } + + public static String replace(String s, String regex, List replacements) { + List out = new ArrayList<>(); + out.add(s); + replacements.forEach(replacement -> out.set(0, out.get(0).replaceFirst(regex, replacement))); + return out.get(0); + } + + public static Component toComponent(ListTag arr) { + MutableComponent component = Component.literal("["); + if (arr.size() <= 5) { + for (int i = 0; i < arr.size(); i++) { + component.append(Component.literal('"' + arr.getString(i) + '"').withStyle(ChatFormatting.DARK_AQUA)); + if (i != arr.size() - 1) component.append(", "); + } + } else { + for (int i = 0; i < 2; i++) { + component.append(Component.literal('"' + arr.getString(i) + '"').withStyle(ChatFormatting.DARK_AQUA)); + component.append(", "); + } + component.append("..., "); + for (int i = arr.size() - 2; i < arr.size(); i++) { + component.append(Component.literal('"' + arr.getString(i) + '"').withStyle(ChatFormatting.DARK_AQUA)); + if (i != arr.size() - 1) component.append(", "); + } + } + component.append("]"); + return component; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java b/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java index 3e8f210740c..7a2eebe1fbc 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java @@ -580,4 +580,12 @@ public static void addPotionTooltip(List> effects .setStyle(Style.EMPTY.withColor(ChatFormatting.GREEN)))); }); } + + public static T getLast(List list) { + return list.get(list.size() - 1); + } + + public static ArrayList list(T obj) { + return new ArrayList<>(List.of(obj)); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/input/SyncedKeyMapping.java b/src/main/java/com/gregtechceu/gtceu/utils/input/SyncedKeyMapping.java index f6b3a753386..fb011054de7 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/input/SyncedKeyMapping.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/input/SyncedKeyMapping.java @@ -59,7 +59,7 @@ private SyncedKeyMapping(Supplier> mcKeyMapping) { } private SyncedKeyMapping(int keyCode) { - if (GTCEu.isClientSide()) { + if (GTCEu.isClientSide() && !GTCEu.isDataGen()) { this.keyCode = keyCode; } // Does not need to be registered, is not a configurable key mapping @@ -69,7 +69,7 @@ private SyncedKeyMapping(int keyCode) { } private SyncedKeyMapping(String nameKey, IKeyConflictContext ctx, int keyCode, String category) { - if (GTCEu.isClientSide()) { + if (GTCEu.isClientSide() && !GTCEu.isDataGen()) { this.keyMapping = (KeyMapping) createKeyMapping(nameKey, ctx, keyCode, category); } this.needsRegister = true; diff --git a/src/main/resources/assets/gtceu/models/block/machine/part/computer_monitor.json b/src/main/resources/assets/gtceu/models/block/machine/part/computer_monitor.json new file mode 100644 index 00000000000..73c914a3038 --- /dev/null +++ b/src/main/resources/assets/gtceu/models/block/machine/part/computer_monitor.json @@ -0,0 +1,6 @@ +{ + "parent": "gtceu:block/overlay/front_all", + "textures": { + "overlay": "gtceu:block/overlay/machine/overlay_monitor" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/models/block/overlay/front_all.json b/src/main/resources/assets/gtceu/models/block/overlay/front_all.json new file mode 100644 index 00000000000..5f89a979bf0 --- /dev/null +++ b/src/main/resources/assets/gtceu/models/block/overlay/front_all.json @@ -0,0 +1,24 @@ +{ + "parent": "gtceu:block/cube/tinted/all", + "elements": [ + { + "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "down": { "texture": "#all", "cullface": "down", "tintindex": 1 }, + "up": { "texture": "#all", "cullface": "up", "tintindex": 1 }, + "north": { "texture": "#all", "cullface": "north", "tintindex": 1 }, + "south": { "texture": "#all", "cullface": "south", "tintindex": 1 }, + "west": { "texture": "#all", "cullface": "west", "tintindex": 1 }, + "east": { "texture": "#all", "cullface": "east", "tintindex": 1 } + } + }, + { + "from": [ 0, 0, -0.01 ], + "to": [ 16, 16, -0.01 ], + "faces": { + "north": { "uv": [0, 0, 16, 16], "texture": "#overlay", "cullface": "north" } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/gtceu/textures/block/cover/computer_monitor_emissive.png b/src/main/resources/assets/gtceu/textures/block/cover/computer_monitor_emissive.png deleted file mode 100644 index 39f7ce433dd..00000000000 Binary files a/src/main/resources/assets/gtceu/textures/block/cover/computer_monitor_emissive.png and /dev/null differ diff --git a/src/main/resources/assets/gtceu/textures/block/cover/wireless_transmitter.png b/src/main/resources/assets/gtceu/textures/block/cover/wireless_transmitter.png new file mode 100644 index 00000000000..68ddbb87707 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/cover/wireless_transmitter.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front.png b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front.png new file mode 100644 index 00000000000..d6fcdf974a8 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active.png b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active.png new file mode 100644 index 00000000000..d6fcdf974a8 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active_emissive.png b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active_emissive.png new file mode 100644 index 00000000000..a556f63bc84 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active_emissive.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active_emissive.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active_emissive.png.mcmeta new file mode 100644 index 00000000000..3de4a0bdd81 --- /dev/null +++ b/src/main/resources/assets/gtceu/textures/block/multiblock/central_monitor/overlay_front_active_emissive.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 8 + } +} diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor.png b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor.png new file mode 100644 index 00000000000..af74a983e27 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor.png.mcmeta new file mode 100644 index 00000000000..38c2c75c408 --- /dev/null +++ b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor.png.mcmeta @@ -0,0 +1,5 @@ +{ + "ldlib": { + "connection": "gtceu:block/overlay/machine/overlay_monitor_ctm" + } +} diff --git a/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor_ctm.png b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor_ctm.png new file mode 100644 index 00000000000..1e6e270cc34 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/overlay/machine/overlay_monitor_ctm.png differ diff --git a/src/main/resources/assets/gtceu/textures/item/image_module.png b/src/main/resources/assets/gtceu/textures/item/image_module.png new file mode 100644 index 00000000000..8b532f20234 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/item/image_module.png differ diff --git a/src/main/resources/assets/gtceu/textures/item/text_module.png b/src/main/resources/assets/gtceu/textures/item/text_module.png new file mode 100644 index 00000000000..ad997f1a0ab Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/item/text_module.png differ diff --git a/src/main/resources/assets/gtceu/textures/item/digital_interface_cover.png b/src/main/resources/assets/gtceu/textures/item/wireless_transmitter_cover.png similarity index 100% rename from src/main/resources/assets/gtceu/textures/item/digital_interface_cover.png rename to src/main/resources/assets/gtceu/textures/item/wireless_transmitter_cover.png