diff --git a/src/generated/resources/assets/anvilcraft/lang/en_ud.json b/src/generated/resources/assets/anvilcraft/lang/en_ud.json index 34b39868b..fb25854d6 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_ud.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_ud.json @@ -236,6 +236,7 @@ "block.anvilcraft.stamping_platform": "ɯɹoɟʇɐןԀ buıdɯɐʇS", "block.anvilcraft.supercritical_nesting_shulker_box": "xoᗺ ɹǝʞןnɥS buıʇsǝN ןɐɔıʇıɹɔɹǝdnS", "block.anvilcraft.tempering_glass": "ssɐן⅁ buıɹǝdɯǝ⟘", + "block.anvilcraft.tesla_tower": "ɹǝʍo⟘ ɐןsǝ⟘", "block.anvilcraft.thermoelectric_converter": "ɹǝʇɹǝʌuoƆ ɔıɹʇɔǝןǝoɯɹǝɥ⟘", "block.anvilcraft.tin_block": "ʞɔoןᗺ uı⟘", "block.anvilcraft.tin_pressure_plate": "ǝʇɐןԀ ǝɹnssǝɹԀ uı⟘", @@ -442,7 +443,9 @@ "pack.anvilcraft.builtin_pack": "ʞɔɐԀǝɔɹnosǝᴚ uıʇןınᗺ ʇɟɐɹƆןıʌuⱯ", "pack.anvilcraft.transparent_cauldron.description": "uoɹpןnɐƆ ʇuǝɹɐdsuɐɹ⟘", "patchouli.anvilcraft.landing_text": "ʇɟɐɹƆןıʌuⱯ oʇ ǝɯoɔןǝM", + "screen.anvilcraft.active_silencer.search": "ɥɔɹɐǝs oʇ pɹoʍʎǝʞ ɹǝʇuǝ", "screen.anvilcraft.active_silencer.title": "ɹǝɔuǝןıS ǝʌıʇɔⱯ", + "screen.anvilcraft.anvil_hammer.title": "ʞɔoןᗺ buıʎɟıpoW", "screen.anvilcraft.button.direction": "%s :uoıʇɔǝɹıᗡ ʇndʇnO", "screen.anvilcraft.button.direction.down": "uʍoᗡ", "screen.anvilcraft.button.direction.east": "ʇsɐƎ", @@ -474,6 +477,15 @@ "screen.anvilcraft.structure_tool.to_data_gen": "uǝ⅁ ɐʇɐᗡ o⟘", "screen.anvilcraft.structure_tool.to_json": "NOSſ o⟘", "screen.anvilcraft.structure_tool.to_kubejs": "Sſǝqnʞ o⟘", + "screen.anvilcraft.tesla_tower.filter.has_custom_name": "ɹǝʇןıℲ ʎʇıʇuƎ pǝɯɐN ɯoʇsnƆ", + "screen.anvilcraft.tesla_tower.filter.is_baby_friendly": "ɹǝʇןıℲ ʎʇıʇuƎ ʎןpuǝıɹℲ ʎqɐᗺ", + "screen.anvilcraft.tesla_tower.filter.is_entity_id": "ɹǝʇןıℲ ʎʇıʇuƎ", + "screen.anvilcraft.tesla_tower.filter.is_friendly": "ɹǝʇןıℲ ʎʇıʇuƎ ʎןpuǝıɹℲ", + "screen.anvilcraft.tesla_tower.filter.is_on_vehicle": "ɹǝʇןıℲ ǝןɔıɥǝΛ uO", + "screen.anvilcraft.tesla_tower.filter.is_pet": "ɹǝʇןıℲ ʇǝԀ", + "screen.anvilcraft.tesla_tower.filter.is_player": "ɹǝʇןıℲ ɹǝʎɐןԀ", + "screen.anvilcraft.tesla_tower.filter.is_player_id": "ɹǝʇןıℲ pI ɹǝʎɐןԀ", + "screen.anvilcraft.tesla_tower.filter.unknown": "ɹǝʇןıℲ uʍouʞu∩", "text.autoconfig.anvilcraft.option.anvilEfficiency": "ʎɔuǝıɔıɟɟƎ ןıʌuⱯ", "text.autoconfig.anvilcraft.option.anvilEfficiency.@Tooltip": "ǝɯıʇ ǝɯɐs ǝɥʇ ʇɐ ןıʌuɐ ǝɥʇ ʎq pǝssǝɔoɹd sɯǝʇı ɟo ɹǝqɯnu ɯnɯıxɐW", "text.autoconfig.anvilcraft.option.batchCrafterCooldown": "uʍopןooƆ ɹǝʇɟɐɹƆ ɥɔʇɐᗺ", diff --git a/src/generated/resources/assets/anvilcraft/lang/en_us.json b/src/generated/resources/assets/anvilcraft/lang/en_us.json index c0b1ab3e0..6d757bc00 100644 --- a/src/generated/resources/assets/anvilcraft/lang/en_us.json +++ b/src/generated/resources/assets/anvilcraft/lang/en_us.json @@ -236,6 +236,7 @@ "block.anvilcraft.stamping_platform": "Stamping Platform", "block.anvilcraft.supercritical_nesting_shulker_box": "Supercritical Nesting Shulker Box", "block.anvilcraft.tempering_glass": "Tempering Glass", + "block.anvilcraft.tesla_tower": "Tesla Tower", "block.anvilcraft.thermoelectric_converter": "Thermoelectric Converter", "block.anvilcraft.tin_block": "Tin Block", "block.anvilcraft.tin_pressure_plate": "Tin Pressure Plate", @@ -442,7 +443,9 @@ "pack.anvilcraft.builtin_pack": "AnvilCraft Builtin ResourcePack", "pack.anvilcraft.transparent_cauldron.description": "Transparent Cauldron", "patchouli.anvilcraft.landing_text": "Welcome to AnvilCraft", + "screen.anvilcraft.active_silencer.search": "enter keyword to search", "screen.anvilcraft.active_silencer.title": "Active Silencer", + "screen.anvilcraft.anvil_hammer.title": "Modifying Block", "screen.anvilcraft.button.direction": "Output Direction: %s", "screen.anvilcraft.button.direction.down": "Down", "screen.anvilcraft.button.direction.east": "East", @@ -474,6 +477,15 @@ "screen.anvilcraft.structure_tool.to_data_gen": "To Data Gen", "screen.anvilcraft.structure_tool.to_json": "To JSON", "screen.anvilcraft.structure_tool.to_kubejs": "To KubeJS", + "screen.anvilcraft.tesla_tower.filter.has_custom_name": "Custom Named Entity Filter", + "screen.anvilcraft.tesla_tower.filter.is_baby_friendly": "Baby Friendly Entity Filter", + "screen.anvilcraft.tesla_tower.filter.is_entity_id": "Entity Filter", + "screen.anvilcraft.tesla_tower.filter.is_friendly": "Friendly Entity Filter", + "screen.anvilcraft.tesla_tower.filter.is_on_vehicle": "On Vehicle Filter", + "screen.anvilcraft.tesla_tower.filter.is_pet": "Pet Filter", + "screen.anvilcraft.tesla_tower.filter.is_player": "Player Filter", + "screen.anvilcraft.tesla_tower.filter.is_player_id": "Player Id Filter", + "screen.anvilcraft.tesla_tower.filter.unknown": "Unknown Filter", "text.autoconfig.anvilcraft.option.anvilEfficiency": "Anvil Efficiency", "text.autoconfig.anvilcraft.option.anvilEfficiency.@Tooltip": "Maximum number of items processed by the anvil at the same time", "text.autoconfig.anvilcraft.option.batchCrafterCooldown": "Batch Crafter Cooldown", diff --git a/src/generated/resources/assets/anvilcraft/models/item/tesla_tower.json b/src/generated/resources/assets/anvilcraft/models/item/tesla_tower.json new file mode 100644 index 000000000..70eb75ad8 --- /dev/null +++ b/src/generated/resources/assets/anvilcraft/models/item/tesla_tower.json @@ -0,0 +1,3 @@ +{ + "parent": "anvilcraft:block/tesla_tower_overall" +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/advancement/recipes/misc/tesla_tower.json b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/tesla_tower.json new file mode 100644 index 000000000..de1c781ca --- /dev/null +++ b/src/generated/resources/data/anvilcraft/advancement/recipes/misc/tesla_tower.json @@ -0,0 +1,54 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_circuit_board": { + "conditions": { + "items": [ + { + "items": "anvilcraft:circuit_board" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "anvilcraft:tesla_tower" + }, + "trigger": "minecraft:recipe_unlocked" + }, + "has_topaz_block": { + "conditions": { + "items": [ + { + "items": "anvilcraft:topaz_block" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_transmission_pole": { + "conditions": { + "items": [ + { + "items": "anvilcraft:transmission_pole" + } + ] + }, + "trigger": "minecraft:inventory_changed" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_circuit_board", + "has_transmission_pole", + "has_topaz_block" + ] + ], + "rewards": { + "recipes": [ + "anvilcraft:tesla_tower" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/loot_table/blocks/tesla_tower.json b/src/generated/resources/data/anvilcraft/loot_table/blocks/tesla_tower.json new file mode 100644 index 000000000..440ea8914 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/loot_table/blocks/tesla_tower.json @@ -0,0 +1,30 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "conditions": [ + { + "block": "anvilcraft:tesla_tower", + "condition": "minecraft:block_state_property", + "properties": { + "half": "bottom" + } + } + ], + "name": "anvilcraft:tesla_tower" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "anvilcraft:blocks/tesla_tower" +} \ No newline at end of file diff --git a/src/generated/resources/data/anvilcraft/recipe/tesla_tower.json b/src/generated/resources/data/anvilcraft/recipe/tesla_tower.json new file mode 100644 index 000000000..f1164a478 --- /dev/null +++ b/src/generated/resources/data/anvilcraft/recipe/tesla_tower.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "A": { + "item": "anvilcraft:royal_steel_ingot" + }, + "B": { + "item": "anvilcraft:topaz_block" + }, + "C": { + "item": "anvilcraft:transmission_pole" + }, + "D": { + "item": "anvilcraft:circuit_board" + } + }, + "pattern": [ + "ABA", + "ACA", + "ADA" + ], + "result": { + "count": 1, + "id": "anvilcraft:tesla_tower" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json index 5c4989f68..691c7952d 100644 --- a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json @@ -17,6 +17,7 @@ "anvilcraft:heater", "anvilcraft:transmission_pole", "anvilcraft:remote_transmission_pole", + "anvilcraft:tesla_tower", "anvilcraft:induction_light", "anvilcraft:charge_collector", "anvilcraft:power_converter_small", diff --git a/src/main/java/dev/dubhe/anvilcraft/AnvilCraft.java b/src/main/java/dev/dubhe/anvilcraft/AnvilCraft.java index 4d382b6b3..ac37d4b2a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/AnvilCraft.java +++ b/src/main/java/dev/dubhe/anvilcraft/AnvilCraft.java @@ -1,5 +1,6 @@ package dev.dubhe.anvilcraft; +import dev.dubhe.anvilcraft.api.taslatower.TeslaFilter; import dev.dubhe.anvilcraft.api.tooltip.ItemTooltipManager; import dev.dubhe.anvilcraft.config.AnvilCraftConfig; import dev.dubhe.anvilcraft.data.AnvilCraftDatagen; @@ -7,7 +8,6 @@ import dev.dubhe.anvilcraft.init.ModBlocks; import dev.dubhe.anvilcraft.init.ModCommands; import dev.dubhe.anvilcraft.init.ModComponents; -import dev.dubhe.anvilcraft.init.ModDamageTypes; import dev.dubhe.anvilcraft.init.ModDispenserBehavior; import dev.dubhe.anvilcraft.init.ModEnchantmentEffectComponents; import dev.dubhe.anvilcraft.init.ModEnchantmentEffects; @@ -84,11 +84,14 @@ public AnvilCraft(IEventBus modEventBus, ModContainer container) { ModLootContextParamSets.registerAll(); ModEnchantmentEffectComponents.register(modEventBus); ModEnchantmentEffects.register(modEventBus); + + TeslaFilter.init(); // datagen AnvilCraftDatagen.init(); registerEvents(modEventBus); logger.info("Ciallo~(∠・ω< )⌒★"); + logger.info("let's 0721!"); } private static void registerEvents(IEventBus eventBus) { diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/HasCustomNameFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/HasCustomNameFilter.java new file mode 100644 index 000000000..0940a3131 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/HasCustomNameFilter.java @@ -0,0 +1,18 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; + +public class HasCustomNameFilter extends TeslaFilter{ + @Getter + private final String id = "HasCustomNameFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { return entity.getCustomName() != null;} + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.has_custom_name"); + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsBabyFriendlyFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsBabyFriendlyFilter.java new file mode 100644 index 000000000..20989f9c7 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsBabyFriendlyFilter.java @@ -0,0 +1,21 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.Animal; + +public class IsBabyFriendlyFilter extends TeslaFilter{ + @Getter + private final String id = "IsBabyFriendlyFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { + return entity.getType().getCategory().isFriendly() && entity instanceof Animal animal && animal.isBaby(); + } + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_baby_friendly"); + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsEntityIdFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsEntityIdFilter.java new file mode 100644 index 000000000..dd810ce66 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsEntityIdFilter.java @@ -0,0 +1,26 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; + +public class IsEntityIdFilter extends TeslaFilter{ + @Getter + private final String id = "IsEntityIdFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { return entity.getType().getDescriptionId().equals(arg); } + + @Override + public boolean needArg() { return true; } + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_entity_id"); + } + + @Override + public String tooltip(String arg) { + return arg; + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsFriendlyFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsFriendlyFilter.java new file mode 100644 index 000000000..a83c8b218 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsFriendlyFilter.java @@ -0,0 +1,18 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; + +public class IsFriendlyFilter extends TeslaFilter{ + @Getter + private final String id = "IsFriendlyFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { return entity.getType().getCategory().isFriendly();} + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_friendly"); + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsOnVehicleFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsOnVehicleFilter.java new file mode 100644 index 000000000..e8a9a2eb7 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsOnVehicleFilter.java @@ -0,0 +1,18 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; + +public class IsOnVehicleFilter extends TeslaFilter{ + @Getter + private final String id = "IsOnVehicleFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { return entity.getVehicle() != null;} + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_on_vehicle"); + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPetFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPetFilter.java new file mode 100644 index 000000000..9af3f8213 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPetFilter.java @@ -0,0 +1,21 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.TamableAnimal; + +public class IsPetFilter extends TeslaFilter{ + @Getter + private final String id = "IsPetFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { + return entity instanceof TamableAnimal tamableAnimal && tamableAnimal.getOwner() != null; + } + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_pet"); + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPlayerFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPlayerFilter.java new file mode 100644 index 000000000..41fbe56cc --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPlayerFilter.java @@ -0,0 +1,21 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; + +public class IsPlayerFilter extends TeslaFilter{ + @Getter + private final String id = "IsPlayerFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { + return entity instanceof Player; + } + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_player"); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPlayerIdFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPlayerIdFilter.java new file mode 100644 index 000000000..9c9f913d0 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/IsPlayerIdFilter.java @@ -0,0 +1,29 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import lombok.Getter; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; + +public class IsPlayerIdFilter extends TeslaFilter{ + @Getter + private final String id = "IsPlayerIdFilter"; + + @Override + public boolean match(LivingEntity entity, String arg) { + return entity instanceof Player player && player.getName().getString().equals(arg); + } + + @Override + public boolean needArg() { return true; } + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.is_player_id"); + } + + @Override + public String tooltip(String arg) { + return arg; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/taslatower/TeslaFilter.java b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/TeslaFilter.java new file mode 100644 index 000000000..fbbf5b227 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/taslatower/TeslaFilter.java @@ -0,0 +1,51 @@ +package dev.dubhe.anvilcraft.api.taslatower; + +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.LivingEntity; + +import java.util.Collection; +import java.util.HashMap; + +public abstract class TeslaFilter { + static TeslaFilter emptyFilter = new TeslaFilter() { + @Override + public String getId() { + return ""; + } + + @Override + public boolean match(LivingEntity entity, String arg) { + return false; + } + + @Override + public Component title() { + return Component.translatable("screen.anvilcraft.tesla_tower.filter.unknown"); + } + }; + private static final HashMap FILTER_MAP = new HashMap<>(); + public static void register(TeslaFilter filter) { + FILTER_MAP.put(filter.getId(), filter); + } + public static TeslaFilter getFilter(String id) { + return FILTER_MAP.getOrDefault(id, emptyFilter); + } + public static Collection all() { return FILTER_MAP.values(); } + abstract public String getId(); + abstract public boolean match(LivingEntity entity, String arg); + public boolean needArg() { return false; } + abstract public Component title(); + public String tooltip(String arg) { return ""; } + + public static void init() { + FILTER_MAP.clear(); + register(new IsPlayerFilter()); + register(new IsPlayerIdFilter()); + register(new IsPetFilter()); + register(new IsOnVehicleFilter()); + register(new IsFriendlyFilter()); + register(new IsEntityIdFilter()); + register(new IsBabyFriendlyFilter()); + register(new HasCustomNameFilter()); + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/block/TeslaTowerBlock.java b/src/main/java/dev/dubhe/anvilcraft/block/TeslaTowerBlock.java new file mode 100644 index 000000000..b96e2da15 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/block/TeslaTowerBlock.java @@ -0,0 +1,204 @@ +package dev.dubhe.anvilcraft.block; + +import dev.dubhe.anvilcraft.api.IHasMultiBlock; +import dev.dubhe.anvilcraft.api.hammer.IHammerRemovable; +import dev.dubhe.anvilcraft.api.power.IPowerComponent; +import dev.dubhe.anvilcraft.block.entity.TeslaTowerBlockEntity; +import dev.dubhe.anvilcraft.block.state.Vertical4PartHalf; +import dev.dubhe.anvilcraft.init.ModBlocks; +import dev.dubhe.anvilcraft.init.ModMenuTypes; +import dev.dubhe.anvilcraft.network.TeslaFilterSyncPacket; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class TeslaTowerBlock + extends AbstractMultiplePartBlock + implements IHammerRemovable, IHasMultiBlock, EntityBlock { + public static final EnumProperty HALF = EnumProperty.create("half", Vertical4PartHalf.class); + public static final BooleanProperty OVERLOAD = IPowerComponent.OVERLOAD; + public static final EnumProperty SWITCH = IPowerComponent.SWITCH; + public static final VoxelShape BOTTOM_SHAPE = Shapes.join(Block.box(0, 0, 0, 16, 4, 16), Block.box(1, 4, 1, 15, 16, 15), BooleanOp.OR); + public static final VoxelShape LOWER_SHAPE = Shapes.join(Block.box(5, 8, 5, 11, 16, 11), Block.box(1, 0, 1, 15, 8, 15), BooleanOp.OR); + public static final VoxelShape UPPER_SHAPE = Shapes.join(Block.box(6, 8, 6, 10, 16, 10), Block.box(4, 0, 4, 12, 8, 12), BooleanOp.OR); + public static final VoxelShape TOP_SHAPE = Shapes.join(Block.box(3, 6, 3, 13, 16, 13), Block.box(6, 0, 6, 10, 8, 10), BooleanOp.OR); + + /** + * @param properties 属性 + */ + public TeslaTowerBlock(Properties properties) { + super(properties); + this.registerDefaultState(this.stateDefinition + .any() + .setValue(HALF, Vertical4PartHalf.BOTTOM) + .setValue(OVERLOAD, true) + .setValue(SWITCH, IPowerComponent.Switch.ON)); + } + + @Override + public Property getPart() { + return TeslaTowerBlock.HALF; + } + + @Override + public Vertical4PartHalf[] getParts() { + return Vertical4PartHalf.values(); + } + + @Override + @Nullable + public BlockState getPlacementState(BlockPlaceContext context) { + Level level = context.getLevel(); + BlockPos pos = context.getClickedPos(); + IPowerComponent.Switch sw = + level.hasNeighborSignal(pos) ? IPowerComponent.Switch.OFF : IPowerComponent.Switch.ON; + return this.defaultBlockState() + .setValue(HALF, Vertical4PartHalf.BOTTOM) + .setValue(OVERLOAD, true) + .setValue(SWITCH, sw); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(HALF).add(OVERLOAD).add(SWITCH); + } + + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + + @Override + public VoxelShape getShape( + BlockState state, + BlockGetter level, + BlockPos pos, + CollisionContext context) { + return switch (state.getValue(HALF)){ + case BOTTOM -> BOTTOM_SHAPE; + case MID_LOWER -> LOWER_SHAPE; + case MID_UPPER -> UPPER_SHAPE; + case TOP -> TOP_SHAPE; + }; + } + + @Override + protected BlockState placedState(Vertical4PartHalf part, BlockState state) { + return super.placedState(part, state).setValue(SWITCH, IPowerComponent.Switch.ON); + } + + @Override + public BlockState playerWillDestroy( + Level level, BlockPos pos, BlockState state, Player player) { + if (level.isClientSide) return state; + onRemove(level, pos, state); + super.playerWillDestroy(level, pos, state, player); + return state; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new TeslaTowerBlockEntity(pos, state); + } + + @Nullable + @Override + public BlockEntityTicker getTicker( + Level level, BlockState state, BlockEntityType type) { + if (level.isClientSide) return null; + return (level1, pos, state1, entity) -> { + if (entity instanceof TeslaTowerBlockEntity entity1) entity1.tick(); + }; + } + + @Override + public void neighborChanged( + BlockState state, + Level level, + BlockPos pos, + Block neighborBlock, + BlockPos neighborPos, + boolean movedByPiston) { + if (level.isClientSide) { + return; + } + if (state.getValue(HALF) != Vertical4PartHalf.BOTTOM) return; + BlockPos topPos = pos.above(3); + BlockState topState = level.getBlockState(topPos); + if (!topState.is(ModBlocks.TESLA_TOWER.get())) return; + if (topState.getValue(HALF) != Vertical4PartHalf.TOP) return; + IPowerComponent.Switch sw = state.getValue(SWITCH); + boolean bl = sw == IPowerComponent.Switch.ON; + if (bl == level.hasNeighborSignal(pos)) { + if (bl) { + state = state.setValue(SWITCH, IPowerComponent.Switch.OFF); + topState = topState.setValue(SWITCH, IPowerComponent.Switch.OFF); + } else { + state = state.setValue(SWITCH, IPowerComponent.Switch.ON); + topState = topState.setValue(SWITCH, IPowerComponent.Switch.ON); + } + level.setBlockAndUpdate(pos, state); + level.setBlockAndUpdate(topPos, topState); + } + } + + @Override + public void onRemove(Level level, BlockPos pos, BlockState state) { + } + + @Override + public void onPlace(Level level, BlockPos pos, BlockState state) { + } + + @Override + protected InteractionResult useWithoutItem( + BlockState pState, + Level pLevel, + BlockPos pPos, + Player pPlayer, + BlockHitResult pHitResult + ) { + if (pLevel.isClientSide) { + return InteractionResult.SUCCESS; + } + BlockEntity be = pLevel.getBlockEntity(pPos); + if (be instanceof TeslaTowerBlockEntity teslaTowerBlockEntity && pPlayer instanceof ServerPlayer sp) { + if (sp.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) return InteractionResult.PASS; + ModMenuTypes.open(sp, teslaTowerBlockEntity, pPos); + PacketDistributor.sendToPlayer(sp, new TeslaFilterSyncPacket(teslaTowerBlockEntity.getWhiteList())); + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } +} \ No newline at end of file diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/TeslaTowerBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/TeslaTowerBlockEntity.java new file mode 100644 index 000000000..b1dba8b42 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/TeslaTowerBlockEntity.java @@ -0,0 +1,251 @@ +package dev.dubhe.anvilcraft.block.entity; + +import dev.dubhe.anvilcraft.api.power.IPowerConsumer; +import dev.dubhe.anvilcraft.api.power.PowerGrid; +import dev.dubhe.anvilcraft.api.taslatower.HasCustomNameFilter; +import dev.dubhe.anvilcraft.api.taslatower.IsEntityIdFilter; +import dev.dubhe.anvilcraft.api.taslatower.IsFriendlyFilter; +import dev.dubhe.anvilcraft.api.taslatower.IsOnVehicleFilter; +import dev.dubhe.anvilcraft.api.taslatower.IsPetFilter; +import dev.dubhe.anvilcraft.api.taslatower.IsPlayerIdFilter; +import dev.dubhe.anvilcraft.api.taslatower.TeslaFilter; +import dev.dubhe.anvilcraft.block.TeslaTowerBlock; +import dev.dubhe.anvilcraft.block.state.Vertical4PartHalf; +import dev.dubhe.anvilcraft.init.ModBlockEntities; +import dev.dubhe.anvilcraft.init.ModBlocks; +import dev.dubhe.anvilcraft.init.ModMenuTypes; +import dev.dubhe.anvilcraft.inventory.TeslaTowerMenu; +import it.unimi.dsi.fastutil.Pair; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LightningBolt; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@Slf4j +public class TeslaTowerBlockEntity extends BlockEntity + implements IPowerConsumer, MenuProvider { + private final Comparator ENTITY_SORTER = new Comparator<>() { + private final Vec3 blockPosVec = getBlockPos().getCenter(); + + @Override + public int compare(Entity entity, Entity t1) { + double d1 = entity.position().distanceTo(blockPosVec); + double d2 = t1.position().distanceTo(blockPosVec); + if (d1 == d2) + return 0; + else return d1 < d2 ? -1 : 1; + } + }; + private final Comparator BLOCK_SORTED = new Comparator<>() { + private final Vec3 blockPosVec = getBlockPos().getCenter(); + + @Override + public int compare(BlockPos blockPos, BlockPos t1) { + double d1 = blockPos.getCenter().distanceTo(blockPosVec); + double d2 = t1.getCenter().distanceTo(blockPosVec); + if (d1 == d2) + return 0; + else return d1 < d2 ? -1 : 1; + } + }; + private final ArrayList> whiteList = new ArrayList<>(); + private int tickCount = 0; + @Setter + @Getter + private PowerGrid grid; + + public TeslaTowerBlockEntity(BlockPos pos, BlockState blockState) { + super(ModBlockEntities.TESLA_TOWER.get(), pos, blockState); + } + + private TeslaTowerBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } + + public static TeslaTowerBlockEntity createBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + return new TeslaTowerBlockEntity(type, pos, blockState); + } + + @Override + public int getInputPower() { + if (level == null) return 0; + return level.getBlockState(getBlockPos()).getValue(TeslaTowerBlock.HALF) == Vertical4PartHalf.BOTTOM ? + 128 : 0; + } + + @Override + public Level getCurrentLevel() { + return this.level; + } + + @Override + public @NotNull BlockPos getPos() { + return this.getBlockPos(); + } + + @Override + protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider provider) { + int index = 0; + for (Pair entry : whiteList) { + tag.putString(entry.first().getId()+"_-_"+index, entry.second()); + index++; + } + } + + @Override + public void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider provider) { + whiteList.clear(); + for (String key : tag.getAllKeys()) { + if (key.split("_-_").length != 2) continue; + String id = key.split("_-_")[0]; + whiteList.add(Pair.of(TeslaFilter.getFilter(id), tag.getString(key))); + } + } + + public void tick() { + if (level == null) return; + BlockState state = level.getBlockState(getBlockPos()); + if (!state.is(ModBlocks.TESLA_TOWER.get())) return; + if (state.getValue(TeslaTowerBlock.HALF) != Vertical4PartHalf.BOTTOM) return; + if (state.getValue(TeslaTowerBlock.SWITCH) == Switch.OFF && this.getGrid() != null) { + this.getGrid().remove(this); + } else if (state.getValue(TeslaTowerBlock.SWITCH) == Switch.ON && this.getGrid() == null) { + PowerGrid.addComponent(this); + } + this.flushState(level, getBlockPos()); + this.flushState(level, getBlockPos().above(1)); + this.flushState(level, getBlockPos().above(2)); + this.flushState(level, getBlockPos().above(3)); + if (level.isClientSide) return; + if (state.getValue(TeslaTowerBlock.OVERLOAD) || state.getValue(TeslaTowerBlock.SWITCH) == Switch.OFF) return; + if (tickCount > 0) { + tickCount--; + return; + } + tickCount = 80; + tickCount--; + BlockPos pos = getBlockPos().above(3); + BlockPos pos1 = pos.below(8).west(8).north(8); + BlockPos pos2 = pos.above(8).east(8).south(8); + AABB aabb = new AABB(pos1.getX(), pos1.getY(), pos1.getZ(), pos2.getX() + 1, pos2.getY() + 1, pos2.getZ() + 1); + Optional target = level.getEntitiesOfClass(LivingEntity.class, aabb) + .stream() + .filter(it -> whiteList.stream().noneMatch(it2 -> it2.left().match(it, it2.right()))).min(ENTITY_SORTER); + Vec3 targetPos; + if (target.isPresent()) { + targetPos = target.get().position(); + } else { + ArrayList lightingRods = new ArrayList<>(); + BlockPos.betweenClosedStream(aabb) + .forEach(it -> { + if (level.getBlockState(it).is(Blocks.LIGHTNING_ROD)) + lightingRods.add(it.above(0)); + }); + Optional targetBlock = lightingRods.stream().min(BLOCK_SORTED); + if (targetBlock.isPresent()) + targetPos = targetBlock.get().getCenter(); + else + return; + } + LightningBolt lightningBolt = new LightningBolt(EntityType.LIGHTNING_BOLT, level); + lightningBolt.setDamage(10f); + lightningBolt.setPos(targetPos); + level.addFreshEntity(lightningBolt); + } + + public void initWhiteList(Player player) { + whiteList.add(Pair.of(new IsPlayerIdFilter(), player.getName().getString())); + whiteList.add(Pair.of(new IsPetFilter(), "")); + whiteList.add(Pair.of(new HasCustomNameFilter(), "")); + whiteList.add(Pair.of(new IsEntityIdFilter(), "entity.minecraft.villager")); + whiteList.add(Pair.of(new IsEntityIdFilter(), "entity.minecraft.wandering_trader")); + whiteList.add(Pair.of(new IsFriendlyFilter(), "")); + whiteList.add(Pair.of(new IsOnVehicleFilter(), "")); + } + + public void addFilter(String id, String arg) { + if (level == null) return; + BlockState blockState = level.getBlockState(getBlockPos()); + int yOffset = blockState.getValue(TeslaTowerBlock.HALF).getOffsetY(); + if (level.getBlockEntity(getBlockPos().above(-yOffset)) instanceof TeslaTowerBlockEntity teslaTowerBlockEntity) + teslaTowerBlockEntity._addFilter(id, arg); + } + + private void _addFilter(String id, String arg) { + whiteList.add(Pair.of(TeslaFilter.getFilter(id), arg)); + } + + public void removeFilter(String id, String arg) { + if (level == null) return; + BlockState blockState = level.getBlockState(getBlockPos()); + int yOffset = blockState.getValue(TeslaTowerBlock.HALF).getOffsetY(); + if (level.getBlockEntity(getBlockPos().above(-yOffset)) instanceof TeslaTowerBlockEntity teslaTowerBlockEntity) + teslaTowerBlockEntity._removeFilter(id, arg); + } + + private void _removeFilter(String id, String arg) { + whiteList.removeIf(pair -> pair.first().getId().equals(id) && pair.second().equals(arg)); + } + + public void handleSync(List> filters) { + if (level == null) return; + BlockState blockState = level.getBlockState(getBlockPos()); + int yOffset = blockState.getValue(TeslaTowerBlock.HALF).getOffsetY(); + if (level.getBlockEntity(getBlockPos().above(-yOffset)) instanceof TeslaTowerBlockEntity teslaTowerBlockEntity) + teslaTowerBlockEntity._handleSync(filters); + } + + private void _handleSync(List> filters) { + whiteList.clear(); + whiteList.addAll(filters); + } + + @Override + public @NotNull Component getDisplayName() { + return Component.translatable("block.anvilcraft.tesla_tower"); + } + + @Override + public @Nullable AbstractContainerMenu createMenu(int i, @NotNull Inventory inventory, @NotNull Player player) { + if (level == null) return null; + BlockState blockState = level.getBlockState(getBlockPos()); + int yOffset = blockState.getValue(TeslaTowerBlock.HALF).getOffsetY(); + if (level.getBlockEntity(getBlockPos().above(-yOffset)) instanceof TeslaTowerBlockEntity teslaTowerBlockEntity) + return new TeslaTowerMenu(ModMenuTypes.TESLA_TOWER.get(), i, inventory, teslaTowerBlockEntity); + return null; + } + + public List> getWhiteList() { + if (level == null) return List.of(); + BlockState blockState = level.getBlockState(getBlockPos()); + int yOffset = blockState.getValue(TeslaTowerBlock.HALF).getOffsetY(); + if (level.getBlockEntity(getBlockPos().above(-yOffset)) instanceof TeslaTowerBlockEntity teslaTowerBlockEntity) + return teslaTowerBlockEntity.whiteList; + return List.of(); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TeslaTowerButton.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TeslaTowerButton.java new file mode 100644 index 000000000..9906ac4d5 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/component/TeslaTowerButton.java @@ -0,0 +1,122 @@ +package dev.dubhe.anvilcraft.client.gui.component; + +import com.mojang.blaze3d.systems.RenderSystem; +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.client.gui.screen.TeslaTowerScreen; +import lombok.Getter; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentUtils; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class TeslaTowerButton extends Button { + + private final ResourceLocation texture; + + @Getter + private final int index; + + private final TeslaTowerScreen parent; + private final int variant; + + /** + * 主动静音器 screen 的按钮 + */ + public TeslaTowerButton( + int x, + int y, + int index, + int variant, + OnPress onPress, + TeslaTowerScreen parent, + String textureVariant + ) { + super( + x, + y, + 10, + 10, + Component.literal(""), + onPress, + (var) -> parent.getFilterTitle(index, variant).copy() + ); + this.height = 15; + this.width = 112; + this.index = index; + texture = AnvilCraft.of("textures/gui/container/machine/active_silencer_button_%s.png".formatted(textureVariant)); + this.parent = parent; + this.variant = variant; + } + + @Override + public void renderWidget(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + String searchText = parent.getFilterText(); + String id = parent.getFilterToolTipAt(index, variant); + if (id == null) return; + this.renderTexture(guiGraphics, texture, this.getX(), this.getY(), 0, 0, 15, this.width, this.height, 112, 30); + Component message; + if (searchText.startsWith("#") || searchText.startsWith("~")) { + message = parent.getFilterTitle(index, variant); + } else { + message = highlighted( + parent.getFilterTitle(index, variant).getString(), + searchText, + ChatFormatting.WHITE + ); + } + this.setMessage(message); + this.renderString(guiGraphics, Minecraft.getInstance().font, 16777215 | Mth.ceil(this.alpha * 255.0F) << 24); + if (this.isHovered()) { + Component filterText = highlighted( + id, searchText.replaceFirst("#", ""), ChatFormatting.GRAY); + guiGraphics.renderTooltip( + Minecraft.getInstance().font, + filterText.getString().isEmpty() ? List.of(message.getVisualOrderText()) : List.of(message.getVisualOrderText(), filterText.getVisualOrderText()), + mouseX, + mouseY); + } + } + + private static Component highlighted( + String original, + String hightlighted, + ChatFormatting originalFormatting + ) { + String[] parts = original.split(hightlighted); + List components = new ArrayList<>(); + for (String s : parts) { + components.add(Component.literal(s).copy().setStyle(Style.EMPTY.applyFormat(originalFormatting))); + } + return ComponentUtils.formatList( + components, Component.literal(hightlighted).withStyle(ChatFormatting.YELLOW)); + } + + public void renderTexture( + @NotNull GuiGraphics guiGraphics, + @NotNull ResourceLocation texture, + int x, + int y, + int puOffset, + int pvOffset, + int textureDifference, + int width, + int height, + int textureWidth, + int textureHeight) { + int i = pvOffset; + if (this.isHovered()) { + i += textureDifference; + } + RenderSystem.enableDepthTest(); + guiGraphics.blit(texture, x, y, puOffset, i, width, height, textureWidth, textureHeight); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/ActiveSilencerScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/ActiveSilencerScreen.java index de65b0013..e2b558e89 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/ActiveSilencerScreen.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/ActiveSilencerScreen.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.regex.Pattern; +@SuppressWarnings("DuplicatedCode") public class ActiveSilencerScreen extends AbstractContainerScreen { private static final ResourceLocation CONTAINER_LOCATION = @@ -105,6 +106,7 @@ private void onSearchTextChange(String text) { @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + assert this.minecraft != null; if (this.minecraft.options.keyInventory.matches(keyCode, scanCode)) { return this.getFocused() != null && this.getFocused().keyPressed(keyCode, scanCode, modifiers); } else { @@ -393,6 +395,7 @@ public void handleSync(List sounds) { if (events == null || events.getSubtitle() == null) return; mutedSounds.add(Pair.of(sound, events.getSubtitle())); } + onSearchTextChange(""); menu.handleSync(sounds); } diff --git a/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/TeslaTowerScreen.java b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/TeslaTowerScreen.java new file mode 100644 index 000000000..eb558277e --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/client/gui/screen/TeslaTowerScreen.java @@ -0,0 +1,415 @@ +package dev.dubhe.anvilcraft.client.gui.screen; + +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.api.taslatower.TeslaFilter; +import dev.dubhe.anvilcraft.client.gui.component.TeslaTowerButton; +import dev.dubhe.anvilcraft.inventory.TeslaTowerMenu; +import dev.dubhe.anvilcraft.network.AddTeslaFilterPacket; +import dev.dubhe.anvilcraft.network.RemoveTeslaFilterPacket; +import it.unimi.dsi.fastutil.Pair; +import lombok.Getter; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.player.Inventory; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +@SuppressWarnings({"MismatchedReadAndWriteOfArray", "FieldCanBeLocal"}) +public class TeslaTowerScreen extends AbstractContainerScreen { + + private static final ResourceLocation CONTAINER_LOCATION = + AnvilCraft.of("textures/gui/container/machine/background/tesla_tower.png"); + + public static final ResourceLocation ACTIVE_SILENCER_SLIDER = + AnvilCraft.of("textures/gui/container/machine/active_silencer_slider.png"); + + private static final int SCROLL_BAR_HEIGHT = 120; + private static final int SCROLL_BAR_TOP_POS_Y = 35; + private static final int START_LEFT_X = 6; + private static final int START_RIGHT_X = 132; + private static final int SCROLL_BAR_START_LEFT_X = 120; + private static final int SCROLL_BAR_START_RIGHT_X = 245; + private static final int SCROLL_BAR_WIDTH = 5; + private static final int SCROLLER_HEIGHT = 9; + + public static final int FILTER_FILTERED = 0; + public static final int SOUND_MUTED = 1; + + private final TeslaTowerMenu menu; + private final TeslaTowerButton[] allFilterButtons = new TeslaTowerButton[8]; + private final TeslaTowerButton[] mutedSoundButtons = new TeslaTowerButton[8]; + private EditBox editBox; + private int leftScrollOff; + private int rightScrollOff; + + @Getter + private String filterText = ""; + + private boolean isDraggingLeft; + private boolean isDraggingRight; + private final List> allFilter = new ArrayList<>(); + private final List> filteredFilters = new ArrayList<>(); + private final List> whiteFilters = new ArrayList<>(); + + private void onSearchTextChange(String text) { + leftScrollOff = 0; + filteredFilters.clear(); + if (text == null || text.isEmpty()) { + this.filterText = ""; + filteredFilters.addAll(allFilter); + filteredFilters.removeIf(it -> whiteFilters.stream().anyMatch(it2 -> it.left().getId().equals(it2.left().getId()) && it.right().equals(it2.right()))); + return; + } else { + this.filterText = text; + } + + if (text.startsWith("#")) { + String search = text.replaceFirst("#", ""); + allFilter.stream() + .filter(it -> it.right().contains(search)) + .filter(it -> whiteFilters.stream().anyMatch(it2 -> it.left().getId().equals(it2.left().getId()) && it.right().equals(it2.right()))) + .forEach(filteredFilters::add); + } else { + if (text.startsWith("~")) { + try { + Pattern search = Pattern.compile(text.replaceFirst("~", "")); + allFilter.stream() + .filter(it -> search.matcher(it.left().getId()).matches()) + .filter(it -> whiteFilters.stream().anyMatch(it2 -> it.left().getId().equals(it2.left().getId()) && it.right().equals(it2.right()))) + .forEach(filteredFilters::add); + } catch (Exception ignored) { + // intentionally empty + } + } + allFilter.stream() + .filter(it -> it.left().title().getString().contains(filterText)) + .filter(it -> + whiteFilters.stream().noneMatch(it1 -> it1.left().equals(it.first()))) + .forEach(filteredFilters::add); + } + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + assert this.minecraft != null; + if (this.minecraft.options.keyInventory.matches(keyCode, scanCode)) { + return this.getFocused() != null && this.getFocused().keyPressed(keyCode, scanCode, modifiers); + } else { + return super.keyPressed(keyCode, scanCode, modifiers); + } + } + + private void refreshFilterList() { + onSearchTextChange(filterText); + } + + private void onAllFilterButtonClick(int selectedIndex) { + int actualIndex = selectedIndex; + actualIndex += leftScrollOff; + if (filteredFilters.isEmpty() || actualIndex >= filteredFilters.size()) return; + String id = filteredFilters.get(actualIndex).left().getId(); + String arg = filteredFilters.get(actualIndex).right(); + addWhiteFilter(id, arg); + PacketDistributor.sendToServer(new AddTeslaFilterPacket(id, arg)); + refreshFilterList(); + } + + private void onWhiteListFilterButtonClick(int selectedIndex) { + int actualIndex = selectedIndex; + actualIndex += rightScrollOff; + if (whiteFilters.isEmpty() || actualIndex >= whiteFilters.size()) return; + String id = whiteFilters.get(actualIndex).left().getId(); + String arg = whiteFilters.get(actualIndex).right(); + removeWhiteFilter(id, arg); + PacketDistributor.sendToServer(new RemoveTeslaFilterPacket(id, arg)); + refreshFilterList(); + } + + void addWhiteFilter(String id, String arg) { + this.menu.addFilter(id, arg); + this.whiteFilters.add(Pair.of(TeslaFilter.getFilter(id), arg)); + } + + void removeWhiteFilter(String id, String arg) { + this.menu.removeFilter(id, arg); + this.whiteFilters.removeIf(it -> it.left().getId().equals(id) && it.right().equals(arg)); + } + + public Component getFilterTitle(int index, int variant) { + int actualIndex = index; + if (variant == FILTER_FILTERED) { + actualIndex += leftScrollOff; + if (filteredFilters.isEmpty() || actualIndex >= filteredFilters.size()) return Component.empty(); + return filteredFilters.get(actualIndex).left().title(); + } else { + actualIndex += rightScrollOff; + if (whiteFilters.isEmpty() || actualIndex >= whiteFilters.size()) return Component.empty(); + return whiteFilters.get(actualIndex).left().title(); + } + } + + public String getFilterToolTipAt(int index, int variant) { + int actualIndex = index; + if (variant == FILTER_FILTERED) { + actualIndex += leftScrollOff; + if (filteredFilters.isEmpty() || actualIndex >= filteredFilters.size()) return null; + Pair filter = filteredFilters.get(actualIndex); + return filter.left().tooltip(filter.right()); + } else { + actualIndex += rightScrollOff; + if (whiteFilters.isEmpty() || actualIndex >= whiteFilters.size()) return null; + Pair filter = whiteFilters.get(actualIndex); + return filter.left().tooltip(filter.right()); + } + } + + /** + * 主动消音器gui + */ + public TeslaTowerScreen(TeslaTowerMenu menu, Inventory playerInventory, Component title) { + super(menu, playerInventory, title); + this.menu = menu; + this.imageWidth = 256; + this.imageHeight = 166; + } + + @SuppressWarnings("ExtractMethodRecommender") + @Override + protected void init() { + super.init(); + + int buttonTop = topPos + 35; + for (int l = 0; l < 8; ++l) { + TeslaTowerButton button = new TeslaTowerButton( + leftPos + START_LEFT_X, + buttonTop, + l, + FILTER_FILTERED, + b -> { + if (b instanceof TeslaTowerButton silencerButton) { + onAllFilterButtonClick(silencerButton.getIndex()); + } + }, + this, + "add"); + button.setWidth(112); + this.allFilterButtons[l] = this.addRenderableWidget(button); + buttonTop += 15; + } + + buttonTop = topPos + 35; + for (int l = 0; l < 8; ++l) { + TeslaTowerButton button = new TeslaTowerButton( + leftPos + START_RIGHT_X, + buttonTop, + l, + SOUND_MUTED, + b -> { + if (b instanceof TeslaTowerButton silencerButton) { + onWhiteListFilterButtonClick(silencerButton.getIndex()); + } + }, + this, + "remove"); + this.mutedSoundButtons[l] = this.addRenderableWidget(button); + buttonTop += 15; + } + + assert this.minecraft != null; + editBox = new EditBox( + this.minecraft.font, + leftPos + 78, + topPos + 19, + 100, + 12, + Component.translatable("screen.anvilcraft.active_silencer.search")); + editBox.setResponder(this::onSearchTextChange); + addRenderableWidget(editBox); + + allFilter.addAll(TeslaFilter.all() + .stream() + .filter(it -> !it.needArg()) + .map(it -> Pair.of(it, "")) + .toList() + ); + assert Minecraft.getInstance().player != null; + allFilter.addAll(Minecraft.getInstance().player.connection.getOnlinePlayers().stream() + .map(it -> Pair.of(TeslaFilter.getFilter("IsPlayerIdFilter"), it.getProfile().getName())) + .toList() + ); + allFilter.addAll(BuiltInRegistries.ENTITY_TYPE.stream() + .map(it -> Pair.of(TeslaFilter.getFilter("IsEntityIdFilter"), it.getDescriptionId())) + .toList() + ); + filteredFilters.addAll(allFilter); + } + + private boolean mouseInLeft(double mouseX, double mouseY, int leftPos, int topPos) { + return mouseX >= leftPos + START_LEFT_X + && mouseX <= leftPos + SCROLL_BAR_START_LEFT_X + SCROLL_BAR_WIDTH + && mouseY >= topPos + SCROLL_BAR_TOP_POS_Y + && mouseY <= topPos + SCROLL_BAR_TOP_POS_Y + SCROLL_BAR_HEIGHT; + } + + private boolean mouseInRight(double mouseX, double mouseY, int leftPos, int topPos) { + return mouseX >= leftPos + START_RIGHT_X + && mouseX <= leftPos + SCROLL_BAR_START_RIGHT_X + SCROLL_BAR_WIDTH + && mouseY >= topPos + SCROLL_BAR_TOP_POS_Y + && mouseY <= topPos + SCROLL_BAR_TOP_POS_Y + SCROLL_BAR_HEIGHT; + } + + private boolean mouseInLeftSlider(double mouseX, double mouseY, int leftPos, int topPos) { + return mouseX >= leftPos + SCROLL_BAR_START_LEFT_X + && mouseX <= leftPos + SCROLL_BAR_START_LEFT_X + SCROLL_BAR_WIDTH + && mouseY >= topPos + SCROLL_BAR_TOP_POS_Y + && mouseY <= topPos + SCROLL_BAR_TOP_POS_Y + SCROLL_BAR_HEIGHT; + } + + private boolean mouseInRightSlider(double mouseX, double mouseY, int leftPos, int topPos) { + return mouseX >= leftPos + SCROLL_BAR_START_RIGHT_X + && mouseX <= leftPos + SCROLL_BAR_START_RIGHT_X + SCROLL_BAR_WIDTH + && mouseY >= topPos + SCROLL_BAR_TOP_POS_Y + && mouseY <= topPos + SCROLL_BAR_TOP_POS_Y + SCROLL_BAR_HEIGHT; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double pScrollX, double pScrollY) { + int leftPos = (this.width - this.imageWidth) / 2; + int topPos = (this.height - this.imageHeight) / 2; + if (mouseInLeft(mouseX, mouseY, leftPos, topPos)) { + if (this.filteredFilters.size() > 8) { + this.leftScrollOff = (int) Mth.clamp(this.leftScrollOff - pScrollY, 0, this.filteredFilters.size() - 7); + } + } else { + if (mouseInRight(mouseX, mouseY, leftPos, topPos)) { + if (this.whiteFilters.size() > 8) { + this.rightScrollOff = + (int) Mth.clamp(this.rightScrollOff - pScrollY, 0, this.whiteFilters.size() - 7); + } + } + } + return true; + } + + /** + * 鼠标拖动事件 + */ + @SuppressWarnings("DuplicatedCode") + public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) { + int leftPos = (this.width - this.imageWidth) / 2; + int topPos = (this.height - this.imageHeight) / 2; + if (mouseInLeftSlider(mouseX, mouseY, leftPos, topPos)) { + int i = filteredFilters.size(); + if (this.isDraggingLeft) { + int j = this.topPos + SCROLL_BAR_TOP_POS_Y; + int k = j + SCROLL_BAR_HEIGHT; + int dragMax = i - 7; + float scroll = (float) ((mouseY - j - 13.5F) / ((k - j) - 27.0F)); + scroll = scroll * dragMax + 0.5F; + this.leftScrollOff = Mth.clamp((int) scroll, 0, dragMax); + return true; + } else { + return super.mouseDragged(mouseX, mouseY, button, dragX, dragY); + } + } else { + if (mouseInRightSlider(mouseX, mouseY, leftPos, topPos)) { + int i = whiteFilters.size(); + if (this.isDraggingRight) { + int j = this.topPos + SCROLL_BAR_TOP_POS_Y; + int k = j + SCROLL_BAR_HEIGHT; + int dragMax = i - 7; + float scroll = (float) ((mouseY - j - 13.5F) / ((k - j) - 27.0F)); + scroll = scroll * dragMax + 0.5F; + this.rightScrollOff = Mth.clamp((int) scroll, 0, dragMax); + return true; + } else { + return super.mouseDragged(mouseX, mouseY, button, dragX, dragY); + } + } + } + return super.mouseDragged(mouseX, mouseY, button, dragX, dragY); + } + + /** + * 鼠标点击 + */ + public boolean mouseClicked(double mouseX, double mouseY, int button) { + isDraggingLeft = false; + isDraggingRight = false; + int leftPos = (this.width - this.imageWidth) / 2; + int topPos = (this.height - this.imageHeight) / 2; + if (mouseInLeftSlider(mouseX, mouseY, leftPos, topPos) && filteredFilters.size() > 8) { + this.isDraggingLeft = true; + } + if (mouseInRightSlider(mouseX, mouseY, leftPos, topPos) && whiteFilters.size() > 8) { + this.isDraggingRight = true; + } + return super.mouseClicked(mouseX, mouseY, button); + } + + private void renderScroller(GuiGraphics guiGraphics, int posX, int posY, int totalCount, int scrollOff) { + int i = totalCount + 1 - 8; + if (i > 1) { + int maxY = posY + SCROLL_BAR_HEIGHT - SCROLLER_HEIGHT; + int scrollY = (int) (posY + (scrollOff / (float) totalCount) * SCROLL_BAR_HEIGHT); + scrollY = Mth.clamp(scrollY, posY, maxY); + + guiGraphics.blit(ACTIVE_SILENCER_SLIDER, posX, scrollY, 0, 0, 5, 9, 10, 9); + } else { + guiGraphics.blit(ACTIVE_SILENCER_SLIDER, posX, posY, 0, 0, 5, 9, 10, 9); + } + } + + /** + * 渲染 + */ + public void render(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + int leftPos = (this.width - this.imageWidth) / 2; + int topPos = (this.height - this.imageHeight) / 2; + + super.render(guiGraphics, mouseX, mouseY, partialTick); + this.renderScroller(guiGraphics, leftPos + 119, topPos + 35, filteredFilters.size(), leftScrollOff); + + this.renderScroller(guiGraphics, leftPos + 245, topPos + 35, whiteFilters.size(), rightScrollOff); + + this.renderTooltip(guiGraphics, mouseX, mouseY); + } + + /** + * 处理同步包 + */ + public void handleSync(List> filters) { + rightScrollOff = 0; + whiteFilters.clear(); + whiteFilters.addAll(filters); + onSearchTextChange(""); + menu.handleSync(filters); + } + + @Override + protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { + guiGraphics.drawString(this.font, this.title, this.titleLabelX, this.titleLabelY, 0x404040, false); + } + + @Override + protected void renderBg(@NotNull GuiGraphics guiGraphics, float partialTick, int mouseX, int mouseY) { + int i = (this.width - this.imageWidth) / 2; + int j = (this.height - this.imageHeight) / 2; + guiGraphics.blit(CONTAINER_LOCATION, i, j, 0, 0, this.imageWidth, this.imageHeight); + } + + @Override + protected void renderTooltip(@NotNull GuiGraphics guiGraphics, int x, int y) { + super.renderTooltip(guiGraphics, x, y); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java index c24e35a6e..b7a68a74e 100644 --- a/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java +++ b/src/main/java/dev/dubhe/anvilcraft/data/lang/ScreenLang.java @@ -86,5 +86,17 @@ public static void init(RegistrateLangProvider provider) { provider.add("screen.anvilcraft.structure_tool.to_json", "To JSON"); provider.add("screen.anvilcraft.anvil_hammer.title", "Modifying Block"); + + provider.add("screen.anvilcraft.active_silencer.search", "enter keyword to search"); + + provider.add("screen.anvilcraft.tesla_tower.filter.unknown", "Unknown Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_player_id", "Player Id Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_player", "Player Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_pet", "Pet Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_on_vehicle", "On Vehicle Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_friendly", "Friendly Entity Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_entity_id", "Entity Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.is_baby_friendly", "Baby Friendly Entity Filter"); + provider.add("screen.anvilcraft.tesla_tower.filter.has_custom_name", "Custom Named Entity Filter"); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModBlockEntities.java b/src/main/java/dev/dubhe/anvilcraft/init/ModBlockEntities.java index a2f30a74b..f22f7c79d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModBlockEntities.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModBlockEntities.java @@ -23,6 +23,7 @@ import dev.dubhe.anvilcraft.block.entity.RubyLaserBlockEntity; import dev.dubhe.anvilcraft.block.entity.RubyPrismBlockEntity; import dev.dubhe.anvilcraft.block.entity.SimpleChuteBlockEntity; +import dev.dubhe.anvilcraft.block.entity.TeslaTowerBlockEntity; import dev.dubhe.anvilcraft.block.entity.ThermoelectricConverterBlockEntity; import dev.dubhe.anvilcraft.block.entity.TransmissionPoleBlockEntity; import dev.dubhe.anvilcraft.client.renderer.blockentity.BatchCrafterRenderer; @@ -171,6 +172,11 @@ public class ModBlockEntities { .renderer(() -> HeliostatsRenderer::new) .register(); + public static final BlockEntityEntry TESLA_TOWER = REGISTRATE + .blockEntity("tesla_tower", TeslaTowerBlockEntity::createBlockEntity) + .validBlocks(ModBlocks.TESLA_TOWER) + .register(); + public static void register() { } } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModBlocks.java b/src/main/java/dev/dubhe/anvilcraft/init/ModBlocks.java index 2d46ea535..473ec2026 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModBlocks.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModBlocks.java @@ -27,6 +27,7 @@ import dev.dubhe.anvilcraft.block.HeavyIronWallBlock; import dev.dubhe.anvilcraft.block.SlidingRailBlock; import dev.dubhe.anvilcraft.block.SlidingRailStopBlock; +import dev.dubhe.anvilcraft.block.TeslaTowerBlock; import dev.dubhe.anvilcraft.block.TransparentCraftingTableBlock; import dev.dubhe.anvilcraft.block.DischargerBlock; import dev.dubhe.anvilcraft.block.EmberAnvilBlock; @@ -99,6 +100,7 @@ import dev.dubhe.anvilcraft.item.HeliostatsItem; import dev.dubhe.anvilcraft.item.PlaceInWaterBlockItem; import dev.dubhe.anvilcraft.item.ResinBlockItem; +import dev.dubhe.anvilcraft.item.TeslaTowerItem; import dev.dubhe.anvilcraft.util.DangerUtil; import dev.dubhe.anvilcraft.util.ModelProviderUtil; @@ -503,6 +505,43 @@ public class ModBlocks { .save(provider); }) .register(); + public static final BlockEntry TESLA_TOWER = REGISTRATE + .block("tesla_tower", TeslaTowerBlock::new) + .initialProperties(ModBlocks.MAGNET_BLOCK) + .loot(AbstractMultiplePartBlock::loot) + .properties(properties -> properties.noOcclusion().lightLevel(state -> { + if (state.getValue(TeslaTowerBlock.HALF) != Vertical4PartHalf.TOP) return 0; + if (state.getValue(SWITCH) == Switch.OFF) return 0; + if (state.getValue(OVERLOAD)) return 6; + return 15; + })) + .blockstate((ctx, provider) -> { + }) + .item(TeslaTowerItem::new) + .model((ctx, provider) -> { provider.blockItem(ctx, "_overall"); }) + .build() + .tag(BlockTags.MINEABLE_WITH_PICKAXE) + .recipe((ctx, provider) -> { + ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ctx.get()) + .pattern("ABA") + .pattern("ACA") + .pattern("ADA") + .define('A', ModItems.ROYAL_STEEL_INGOT) + .define('B', ModBlocks.TOPAZ_BLOCK) + .define('C', ModBlocks.TRANSMISSION_POLE) + .define('D', ModItems.CIRCUIT_BOARD) + .unlockedBy( + AnvilCraftDatagen.hasItem(ModItems.CIRCUIT_BOARD), + AnvilCraftDatagen.has(ModItems.CIRCUIT_BOARD)) + .unlockedBy( + AnvilCraftDatagen.hasItem(ModBlocks.TRANSMISSION_POLE), + AnvilCraftDatagen.has(ModBlocks.TRANSMISSION_POLE)) + .unlockedBy( + AnvilCraftDatagen.hasItem(ModBlocks.TOPAZ_BLOCK), + AnvilCraftDatagen.has(ModBlocks.TOPAZ_BLOCK)) + .save(provider); + }) + .register(); public static final BlockEntry INDUCTION_LIGHT = REGISTRATE .block("induction_light", InductionLightBlock::new) .initialProperties(ModBlocks.MAGNET_BLOCK) diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java b/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java index 84745f327..1aafe16fc 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModMenuTypes.java @@ -14,6 +14,7 @@ import dev.dubhe.anvilcraft.client.gui.screen.RoyalSmithingScreen; import dev.dubhe.anvilcraft.client.gui.screen.SliderScreen; import dev.dubhe.anvilcraft.client.gui.screen.StructureToolScreen; +import dev.dubhe.anvilcraft.client.gui.screen.TeslaTowerScreen; import dev.dubhe.anvilcraft.inventory.ActiveSilencerMenu; import dev.dubhe.anvilcraft.inventory.BatchCrafterMenu; import dev.dubhe.anvilcraft.inventory.ChuteMenu; @@ -29,15 +30,16 @@ import dev.dubhe.anvilcraft.inventory.SliderMenu; import dev.dubhe.anvilcraft.inventory.StructureToolMenu; +import dev.dubhe.anvilcraft.inventory.TeslaTowerMenu; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.MenuProvider; import com.tterrag.registrate.util.entry.MenuEntry; -import net.minecraft.world.level.GameType; import static dev.dubhe.anvilcraft.AnvilCraft.REGISTRATE; +@SuppressWarnings("DataFlowIssue") public class ModMenuTypes { @SuppressWarnings("DataFlowIssue") public static final MenuEntry BATCH_CRAFTER = REGISTRATE @@ -104,6 +106,10 @@ public class ModMenuTypes { .menu("jewel_crafting", (type, id, inv) -> new JewelCraftingMenu(type, id, inv), () -> JewelCraftingScreen::new) .register(); + public static final MenuEntry TESLA_TOWER = REGISTRATE + .menu("tesla_tower", TeslaTowerMenu::new, () -> TeslaTowerScreen::new) + .register(); + public static void register() { } diff --git a/src/main/java/dev/dubhe/anvilcraft/init/ModNetworks.java b/src/main/java/dev/dubhe/anvilcraft/init/ModNetworks.java index e79dd36f0..bef21cfee 100644 --- a/src/main/java/dev/dubhe/anvilcraft/init/ModNetworks.java +++ b/src/main/java/dev/dubhe/anvilcraft/init/ModNetworks.java @@ -1,6 +1,7 @@ package dev.dubhe.anvilcraft.init; import dev.dubhe.anvilcraft.network.AddMutedSoundPacket; +import dev.dubhe.anvilcraft.network.AddTeslaFilterPacket; import dev.dubhe.anvilcraft.network.ChargeCollectorIncomingChargePacket; import dev.dubhe.anvilcraft.network.CyclingValueSyncPacket; import dev.dubhe.anvilcraft.network.HammerChangeBlockPacket; @@ -15,12 +16,14 @@ import dev.dubhe.anvilcraft.network.PowerGridSyncPacket; import dev.dubhe.anvilcraft.network.RecipeCacheSyncPacket; import dev.dubhe.anvilcraft.network.RemoveMutedSoundPacket; +import dev.dubhe.anvilcraft.network.RemoveTeslaFilterPacket; import dev.dubhe.anvilcraft.network.RocketJumpPacket; import dev.dubhe.anvilcraft.network.SliderInitPacket; import dev.dubhe.anvilcraft.network.SliderUpdatePacket; import dev.dubhe.anvilcraft.network.SlotDisableChangePacket; import dev.dubhe.anvilcraft.network.SlotFilterChangePacket; import dev.dubhe.anvilcraft.network.StructureDataSyncPacket; +import dev.dubhe.anvilcraft.network.TeslaFilterSyncPacket; import dev.dubhe.anvilcraft.network.UpdateDisplayItemPacket; import net.neoforged.neoforge.network.registration.PayloadRegistrar; @@ -138,5 +141,20 @@ public static void init(PayloadRegistrar registrar) { RecipeCacheSyncPacket.STREAM_CODEC, RecipeCacheSyncPacket::acceptClient ); + registrar.playToClient( + TeslaFilterSyncPacket.TYPE, + TeslaFilterSyncPacket.STREAM_CODEC, + TeslaFilterSyncPacket.HANDLER + ); + registrar.playToServer( + AddTeslaFilterPacket.TYPE, + AddTeslaFilterPacket.STREAM_CODEC, + AddTeslaFilterPacket.HANDLER + ); + registrar.playToServer( + RemoveTeslaFilterPacket.TYPE, + RemoveTeslaFilterPacket.STREAM_CODEC, + RemoveTeslaFilterPacket.HANDLER + ); } } diff --git a/src/main/java/dev/dubhe/anvilcraft/inventory/TeslaTowerMenu.java b/src/main/java/dev/dubhe/anvilcraft/inventory/TeslaTowerMenu.java new file mode 100644 index 000000000..04d03de3d --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/inventory/TeslaTowerMenu.java @@ -0,0 +1,67 @@ +package dev.dubhe.anvilcraft.inventory; + +import dev.dubhe.anvilcraft.api.taslatower.TeslaFilter; +import dev.dubhe.anvilcraft.block.entity.TeslaTowerBlockEntity; +import dev.dubhe.anvilcraft.init.ModBlocks; +import it.unimi.dsi.fastutil.Pair; +import lombok.Getter; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +public class TeslaTowerMenu extends AbstractContainerMenu { + + @Getter + private final TeslaTowerBlockEntity blockEntity; + + private final Level level; + + public TeslaTowerMenu( + @Nullable MenuType menuType, int containerId, Inventory inventory, @NotNull BlockEntity machine) { + super(menuType, containerId); + blockEntity = (TeslaTowerBlockEntity) machine; + this.level = inventory.player.level(); + } + + public TeslaTowerMenu( + @Nullable MenuType menuType, int containerId, Inventory inventory, @NotNull FriendlyByteBuf extraData) { + this(menuType, containerId, inventory, Objects.requireNonNull(inventory.player.level().getBlockEntity(extraData.readBlockPos()))); + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) { + return ItemStack.EMPTY; + } + + @Override + public boolean stillValid(@NotNull Player player) { + return stillValid( + ContainerLevelAccess.create(level, blockEntity.getBlockPos()), + player, + ModBlocks.TESLA_TOWER.get() + ); + } + + public void addFilter(String id, String arg) { + blockEntity.addFilter(id, arg); + } + + public void removeFilter(String id, String arg) { + blockEntity.removeFilter(id, arg); + } + + public void handleSync(List> filters) { + blockEntity.handleSync(filters); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/item/TeslaTowerItem.java b/src/main/java/dev/dubhe/anvilcraft/item/TeslaTowerItem.java new file mode 100644 index 000000000..09b912272 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/item/TeslaTowerItem.java @@ -0,0 +1,34 @@ +package dev.dubhe.anvilcraft.item; + +import dev.dubhe.anvilcraft.block.AbstractMultiplePartBlock; +import dev.dubhe.anvilcraft.block.entity.TeslaTowerBlockEntity; +import dev.dubhe.anvilcraft.block.state.Vertical4PartHalf; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +public class TeslaTowerItem extends AbstractMultiplePartBlockItem { + public TeslaTowerItem(AbstractMultiplePartBlock block, Properties properties) { + super(block, properties); + } + + @Override + protected boolean updateCustomBlockEntityTag( + @NotNull BlockPos pos, + @NotNull Level level, + @Nullable Player player, + @NotNull ItemStack stack, + @NotNull BlockState state) { + if (!(level.getBlockEntity(pos) instanceof TeslaTowerBlockEntity teslaTowerBlockEntity)) return false; + if (player == null) return false; + teslaTowerBlockEntity.initWhiteList(player); + return true; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/network/AddTeslaFilterPacket.java b/src/main/java/dev/dubhe/anvilcraft/network/AddTeslaFilterPacket.java new file mode 100644 index 000000000..24d4b5bac --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/network/AddTeslaFilterPacket.java @@ -0,0 +1,53 @@ +package dev.dubhe.anvilcraft.network; + +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.inventory.TeslaTowerMenu; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import org.jetbrains.annotations.NotNull; + +public class AddTeslaFilterPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>(AnvilCraft.of("tesla_filter_add")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.ofMember(AddTeslaFilterPacket::encode, AddTeslaFilterPacket::new); + public static final IPayloadHandler HANDLER = AddTeslaFilterPacket::serverHandler; + + private final String id; + private final String arg; + + public AddTeslaFilterPacket(String id, String arg) { + this.id = id; + this.arg = arg; + } + + public AddTeslaFilterPacket(RegistryFriendlyByteBuf buf) { + this.id = buf.readUtf(); + this.arg = buf.readUtf(); + } + + public void encode(@NotNull RegistryFriendlyByteBuf buf) { + buf.writeUtf(id); + buf.writeUtf(arg); + } + + @Override + public @NotNull Type type() { + return TYPE; + } + + /** + * + */ + public static void serverHandler(AddTeslaFilterPacket data, IPayloadContext context) { + ServerPlayer player = (ServerPlayer) context.player(); + context.enqueueWork(() -> { + if (player.containerMenu instanceof TeslaTowerMenu menu) { + menu.addFilter(data.id, data.arg); + } + }); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/network/RemoveTeslaFilterPacket.java b/src/main/java/dev/dubhe/anvilcraft/network/RemoveTeslaFilterPacket.java new file mode 100644 index 000000000..3de898c47 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/network/RemoveTeslaFilterPacket.java @@ -0,0 +1,50 @@ +package dev.dubhe.anvilcraft.network; + +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.inventory.TeslaTowerMenu; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import org.jetbrains.annotations.NotNull; + +public class RemoveTeslaFilterPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>(AnvilCraft.of("tesla_filter_remove")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.ofMember(RemoveTeslaFilterPacket::encode, RemoveTeslaFilterPacket::new); + public static final IPayloadHandler HANDLER = RemoveTeslaFilterPacket::serverHandler; + + private final String id; + private final String arg; + + public RemoveTeslaFilterPacket(String id, String arg) { + this.id = id; + this.arg = arg; + } + + public RemoveTeslaFilterPacket(RegistryFriendlyByteBuf buf) { + this.id = buf.readUtf(); + this.arg = buf.readUtf(); + } + + public void encode(@NotNull RegistryFriendlyByteBuf buf) { + buf.writeUtf(id); + buf.writeUtf(arg); + } + + @Override + public @NotNull Type type() { + return TYPE; + } + + public static void serverHandler(RemoveTeslaFilterPacket data, IPayloadContext context) { + ServerPlayer player = (ServerPlayer) context.player(); + context.enqueueWork(() -> { + if (player.containerMenu instanceof TeslaTowerMenu menu) { + menu.removeFilter(data.id, data.arg); + } + }); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/network/TeslaFilterSyncPacket.java b/src/main/java/dev/dubhe/anvilcraft/network/TeslaFilterSyncPacket.java new file mode 100644 index 000000000..e76df7faf --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/network/TeslaFilterSyncPacket.java @@ -0,0 +1,61 @@ +package dev.dubhe.anvilcraft.network; + +import dev.dubhe.anvilcraft.AnvilCraft; +import dev.dubhe.anvilcraft.api.taslatower.TeslaFilter; +import dev.dubhe.anvilcraft.client.gui.screen.TeslaTowerScreen; +import it.unimi.dsi.fastutil.Pair; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class TeslaFilterSyncPacket implements CustomPacketPayload { + public static final Type TYPE = new Type<>(AnvilCraft.of("tesla_filter_sync")); + public static final StreamCodec STREAM_CODEC = + StreamCodec.ofMember(TeslaFilterSyncPacket::encode, TeslaFilterSyncPacket::new); + public static final IPayloadHandler HANDLER = TeslaFilterSyncPacket::clientHandler; + + private final List> filters; + + public TeslaFilterSyncPacket(List> filters) { + this.filters = filters; + } + + public TeslaFilterSyncPacket(RegistryFriendlyByteBuf buf) { + List ids = buf.readList(FriendlyByteBuf::readUtf); + List args = buf.readList(FriendlyByteBuf::readUtf); + ArrayList> filters = new ArrayList<>(); + for (int i = 0; i < args.size(); i++) { + filters.add(Pair.of(TeslaFilter.getFilter(ids.get(i)), args.get(i))); + } + this.filters = filters; + } + + public void encode(@NotNull FriendlyByteBuf buf) { + buf.writeCollection(filters.stream().map(it -> it.left().getId()).toList(), FriendlyByteBuf::writeUtf); + buf.writeCollection(filters.stream().map(Pair::right).toList(), FriendlyByteBuf::writeUtf); + } + + @Override + public @NotNull Type type() { + return TYPE; + } + + /** + * + */ + public static void clientHandler(TeslaFilterSyncPacket data, IPayloadContext context) { + context.enqueueWork(() -> { + if (Minecraft.getInstance().screen instanceof TeslaTowerScreen screen) { + screen.handleSync(data.filters); + } + }); + } +} diff --git a/src/main/resources/assets/anvilcraft/blockstates/tesla_tower.json b/src/main/resources/assets/anvilcraft/blockstates/tesla_tower.json index 6e6f50c1a..86a4af34b 100644 --- a/src/main/resources/assets/anvilcraft/blockstates/tesla_tower.json +++ b/src/main/resources/assets/anvilcraft/blockstates/tesla_tower.json @@ -3,10 +3,10 @@ "half=bottom,overload=false,switch=on": { "model": "anvilcraft:block/tesla_tower_base" }, - "half=middown,overload=false,switch=on": { + "half=lower,overload=false,switch=on": { "model": "anvilcraft:block/tesla_tower_middown" }, - "half=midup,overload=false,switch=on": { + "half=upper,overload=false,switch=on": { "model": "anvilcraft:block/tesla_tower_midup" }, "half=top,overload=false,switch=on": { @@ -15,10 +15,10 @@ "half=bottom,overload=true,switch=on": { "model": "anvilcraft:block/tesla_tower_base" }, - "half=middown,overload=true,switch=on": { + "half=lower,overload=true,switch=on": { "model": "anvilcraft:block/tesla_tower_middown" }, - "half=midup,overload=true,switch=on": { + "half=upper,overload=true,switch=on": { "model": "anvilcraft:block/tesla_tower_midup" }, "half=top,overload=true,switch=on": { @@ -27,10 +27,10 @@ "half=bottom,overload=false,switch=off": { "model": "anvilcraft:block/tesla_tower_base" }, - "half=middown,overload=false,switch=off": { + "half=lower,overload=false,switch=off": { "model": "anvilcraft:block/tesla_tower_middown" }, - "half=midup,overload=false,switch=off": { + "half=upper,overload=false,switch=off": { "model": "anvilcraft:block/tesla_tower_midup" }, "half=top,overload=false,switch=off": { @@ -39,10 +39,10 @@ "half=bottom,overload=true,switch=off": { "model": "anvilcraft:block/tesla_tower_base" }, - "half=middown,overload=true,switch=off": { + "half=lower,overload=true,switch=off": { "model": "anvilcraft:block/tesla_tower_middown" }, - "half=midup,overload=true,switch=off": { + "half=upper,overload=true,switch=off": { "model": "anvilcraft:block/tesla_tower_midup" }, "half=top,overload=true,switch=off": { diff --git a/src/main/resources/assets/anvilcraft/models/block/tesla_tower_top_off.json b/src/main/resources/assets/anvilcraft/models/block/tesla_tower_top_off.json index ee2493a16..0a5591a7d 100644 --- a/src/main/resources/assets/anvilcraft/models/block/tesla_tower_top_off.json +++ b/src/main/resources/assets/anvilcraft/models/block/tesla_tower_top_off.json @@ -1,7 +1,7 @@ { "parent": "anvilcraft:block/tesla_tower_top", "textures": { - "particle": "anvilcraft:block/tesla_tower_head_off", - "head": "anvilcraft:block/tesla_tower_head_off" + "particle": "anvilcraft:block/tesla_tower_head_overload_off", + "head": "anvilcraft:block/tesla_tower_head_overload_off" } } \ No newline at end of file