diff --git a/src/main/java/xyz/nucleoid/spleef/Spleef.java b/src/main/java/xyz/nucleoid/spleef/Spleef.java index 01d4aa4..89cf2e5 100644 --- a/src/main/java/xyz/nucleoid/spleef/Spleef.java +++ b/src/main/java/xyz/nucleoid/spleef/Spleef.java @@ -10,6 +10,7 @@ import xyz.nucleoid.plasmid.game.GameType; import xyz.nucleoid.spleef.game.SpleefConfig; import xyz.nucleoid.spleef.game.SpleefWaiting; +import xyz.nucleoid.spleef.game.action.*; import xyz.nucleoid.spleef.game.map.shape.renderer.*; public final class Spleef implements ModInitializer { @@ -31,5 +32,11 @@ public void onInitialize() { MapShapeRenderer.REGISTRY.register(new Identifier(Spleef.ID, "square"), SquareShapeRenderer.CODEC); MapShapeRenderer.REGISTRY.register(new Identifier(Spleef.ID, "sierpinski_carpet"), SierpinskiCarpetShapeRenderer.CODEC); MapShapeRenderer.REGISTRY.register(new Identifier(Spleef.ID, "pattern"), PatternShapeRenderer.CODEC); + + BlockAction.REGISTRY.register(new Identifier(Spleef.ID, "add_status_effect"), AddStatusEffectBlockAction.CODEC); + BlockAction.REGISTRY.register(new Identifier(Spleef.ID, "give_item_stack"), GiveItemStackBlockAction.CODEC); + BlockAction.REGISTRY.register(new Identifier(Spleef.ID, "restock_projectile"), RestockProjectileBlockAction.CODEC); + BlockAction.REGISTRY.register(new Identifier(Spleef.ID, "send_message"), SendMessageBlockAction.CODEC); + BlockAction.REGISTRY.register(new Identifier(Spleef.ID, "sequence"), SequenceBlockAction.CODEC); } } diff --git a/src/main/java/xyz/nucleoid/spleef/game/SpleefActive.java b/src/main/java/xyz/nucleoid/spleef/game/SpleefActive.java index f2e48c4..54a086d 100644 --- a/src/main/java/xyz/nucleoid/spleef/game/SpleefActive.java +++ b/src/main/java/xyz/nucleoid/spleef/game/SpleefActive.java @@ -26,7 +26,9 @@ import xyz.nucleoid.plasmid.game.player.PlayerOfferResult; import xyz.nucleoid.plasmid.game.rule.GameRuleType; import xyz.nucleoid.spleef.Spleef; +import xyz.nucleoid.spleef.game.action.BlockAction; import xyz.nucleoid.spleef.game.map.SpleefMap; +import xyz.nucleoid.stimuli.event.block.BlockBreakEvent; import xyz.nucleoid.stimuli.event.player.PlayerDamageEvent; import xyz.nucleoid.stimuli.event.player.PlayerDeathEvent; import xyz.nucleoid.stimuli.event.projectile.ProjectileHitEvent; @@ -84,6 +86,7 @@ public static void open(GameSpace gameSpace, ServerWorld world, SpleefMap map, S activity.listen(GamePlayerEvents.OFFER, active::offerPlayer); + activity.listen(BlockBreakEvent.EVENT, active::onBlockBreak); activity.listen(ProjectileHitEvent.BLOCK, active::onBlockHit); activity.listen(PlayerDamageEvent.EVENT, active::onPlayerDamage); @@ -156,7 +159,7 @@ private void tick() { var projectiles = this.config.projectile(); if (time > this.restockTime && projectiles != null) { if (this.restockTime != -1) { - this.restockProjectiles(projectiles); + this.restockProjectiles(); } this.restockTime = time + projectiles.restockInterval(); @@ -175,16 +178,22 @@ private void tickClosing(GameSpace gameSpace, long time) { } } - private void restockProjectiles(ProjectileConfig projectileConfig) { + private void restockProjectiles() { + for (var player : this.gameSpace.getPlayers()) { + this.restockProjectileFor(player); + } + } + + public void restockProjectileFor(ServerPlayerEntity player) { + if (player.isSpectator()) return; + + var projectileConfig = this.config.projectile(); var projectileStack = projectileConfig.stack(); - for (var player : this.gameSpace.getPlayers()) { - if (player.isSpectator()) continue; - if (player.getInventory().count(projectileStack.getItem()) >= projectileConfig.maximum()) continue; + if (player.getInventory().count(projectileStack.getItem()) >= projectileConfig.maximum()) return; - player.getInventory().insertStack(projectileStack.copy()); - player.playSound(SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1); - } + player.getInventory().insertStack(projectileStack.copy()); + player.playSound(SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 1, 1); } private void breakFloorBlock(BlockPos pos) { @@ -193,6 +202,17 @@ private void breakFloorBlock(BlockPos pos) { } } + private ActionResult onBlockBreak(ServerPlayerEntity player, ServerWorld world, BlockPos pos) { + var block = world.getBlockState(pos).getBlock(); + var action = this.config.blockBreakActions().get(block); + + if (action != null) { + BlockAction.apply(action, player, this.gameSpace.getPlayers(), this); + } + + return ActionResult.PASS; + } + private ActionResult onBlockHit(ProjectileEntity entity, BlockHitResult hitResult) { var projectiles = this.config.projectile(); if (projectiles == null) return ActionResult.FAIL; diff --git a/src/main/java/xyz/nucleoid/spleef/game/SpleefConfig.java b/src/main/java/xyz/nucleoid/spleef/game/SpleefConfig.java index 233ec28..1243c5c 100644 --- a/src/main/java/xyz/nucleoid/spleef/game/SpleefConfig.java +++ b/src/main/java/xyz/nucleoid/spleef/game/SpleefConfig.java @@ -3,12 +3,17 @@ import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.block.Block; +import net.minecraft.registry.Registries; import net.minecraft.util.dynamic.Codecs; import org.jetbrains.annotations.Nullable; import xyz.nucleoid.plasmid.game.common.config.PlayerConfig; +import xyz.nucleoid.spleef.game.action.BlockAction; import xyz.nucleoid.spleef.game.map.SpleefGeneratedMapConfig; import xyz.nucleoid.spleef.game.map.SpleefTemplateMapConfig; +import java.util.Collections; +import java.util.Map; import java.util.Optional; public record SpleefConfig( @@ -17,6 +22,7 @@ public record SpleefConfig( ToolConfig tool, @Nullable ProjectileConfig projectile, @Nullable LavaRiseConfig lavaRise, + Map blockBreakActions, long levelBreakInterval, int decay, int timeOfDay, @@ -28,6 +34,7 @@ public record SpleefConfig( ToolConfig.CODEC.optionalFieldOf("tool", ToolConfig.DEFAULT).forGetter(SpleefConfig::tool), ProjectileConfig.CODEC.optionalFieldOf("projectile").forGetter(config -> Optional.ofNullable(config.projectile())), LavaRiseConfig.CODEC.optionalFieldOf("lava_rise").forGetter(config -> Optional.ofNullable(config.lavaRise())), + Codec.unboundedMap(Registries.BLOCK.getCodec(), BlockAction.REGISTRY_CODEC).optionalFieldOf("block_break_actions", Collections.emptyMap()).forGetter(SpleefConfig::blockBreakActions), Codec.LONG.optionalFieldOf("level_break_interval", 20L * 60).forGetter(SpleefConfig::levelBreakInterval), Codec.INT.optionalFieldOf("decay", -1).forGetter(SpleefConfig::decay), Codec.INT.optionalFieldOf("time_of_day", 6000).forGetter(SpleefConfig::timeOfDay), @@ -40,6 +47,7 @@ private SpleefConfig( ToolConfig tool, Optional projectile, Optional lavaRise, + Map blockBreakActions, long levelBreakInterval, int decay, int timeOfDay, @@ -49,6 +57,7 @@ private SpleefConfig( map, players, tool, projectile.orElse(null), lavaRise.orElse(null), + blockBreakActions, levelBreakInterval, decay, timeOfDay, unstableTnt diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/AddStatusEffectBlockAction.java b/src/main/java/xyz/nucleoid/spleef/game/action/AddStatusEffectBlockAction.java new file mode 100644 index 0000000..1aea545 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/AddStatusEffectBlockAction.java @@ -0,0 +1,42 @@ +package xyz.nucleoid.spleef.game.action; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.network.ServerPlayerEntity; +import xyz.nucleoid.codecs.MoreCodecs; + +public record AddStatusEffectBlockAction(StatusEffectInstance effect, BlockActionTarget target) implements BlockAction { + private static final Codec STATUS_EFFECT_CODEC = MoreCodecs.withNbt(effect -> effect.writeNbt(new NbtCompound()), nbt -> { + if (nbt instanceof NbtCompound compound) { + var effect = StatusEffectInstance.fromNbt(compound); + + if (effect == null) { + return DataResult.error(() -> "Unknown status effect ID"); + } else { + return DataResult.success(effect); + } + } + + return DataResult.error(() -> "Expected compound tag"); + }); + + public static final Codec CODEC = RecordCodecBuilder.create(instance -> { + return instance.group( + STATUS_EFFECT_CODEC.fieldOf("effect").forGetter(AddStatusEffectBlockAction::effect), + BlockActionTarget.CODEC.fieldOf("target").forGetter(AddStatusEffectBlockAction::target) + ).apply(instance, AddStatusEffectBlockAction::new); + }); + + @Override + public void apply(ServerPlayerEntity player, BlockActionContext context) { + player.addStatusEffect(new StatusEffectInstance(this.effect), player); + } + + @Override + public Codec getCodec() { + return CODEC; + } +} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/BlockAction.java b/src/main/java/xyz/nucleoid/spleef/game/action/BlockAction.java new file mode 100644 index 0000000..378f382 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/BlockAction.java @@ -0,0 +1,28 @@ +package xyz.nucleoid.spleef.game.action; + +import com.mojang.serialization.Codec; +import net.minecraft.server.network.ServerPlayerEntity; +import xyz.nucleoid.plasmid.game.player.PlayerSet; +import xyz.nucleoid.plasmid.registry.TinyRegistry; +import xyz.nucleoid.spleef.game.SpleefActive; + +import java.util.function.Function; + +public interface BlockAction { + TinyRegistry> REGISTRY = TinyRegistry.create(); + Codec REGISTRY_CODEC = REGISTRY.dispatchMap(BlockAction::getCodec, Function.identity()).codec(); + + public void apply(ServerPlayerEntity player, BlockActionContext context); + + public BlockActionTarget target(); + + Codec getCodec(); + + public static void apply(BlockAction action, ServerPlayerEntity self, PlayerSet players, SpleefActive game) { + var context = new BlockActionContext(self, players, game); + + action.target().forEachTarget(context, player -> { + action.apply(player, context); + }); + } +} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/BlockActionContext.java b/src/main/java/xyz/nucleoid/spleef/game/action/BlockActionContext.java new file mode 100644 index 0000000..81e6cc0 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/BlockActionContext.java @@ -0,0 +1,7 @@ +package xyz.nucleoid.spleef.game.action; + +import net.minecraft.server.network.ServerPlayerEntity; +import xyz.nucleoid.plasmid.game.player.PlayerSet; +import xyz.nucleoid.spleef.game.SpleefActive; + +public record BlockActionContext(ServerPlayerEntity self, PlayerSet players, SpleefActive game) {} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/BlockActionTarget.java b/src/main/java/xyz/nucleoid/spleef/game/action/BlockActionTarget.java new file mode 100644 index 0000000..d6de289 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/BlockActionTarget.java @@ -0,0 +1,42 @@ +package xyz.nucleoid.spleef.game.action; + +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.StringIdentifiable; + +import java.util.function.Consumer; + +public enum BlockActionTarget implements StringIdentifiable { + SELF("self"), + OTHERS("others"), + ALL("all") + ; + + public static final com.mojang.serialization.Codec CODEC = StringIdentifiable.createCodec(BlockActionTarget::values); + + public final String name; + + BlockActionTarget(String name) { + this.name = name; + } + + public void forEachTarget(BlockActionContext context, Consumer consumer) { + if (this == SELF) { + consumer.accept(context.self()); + return; + } + + context.players().stream() + .filter(player -> { + if (this == OTHERS && player == context.self()) { + return false; + } + + return !player.isSpectator(); + }) + .forEach(consumer); + } + + public String asString() { + return this.name; + } +} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/GiveItemStackBlockAction.java b/src/main/java/xyz/nucleoid/spleef/game/action/GiveItemStackBlockAction.java new file mode 100644 index 0000000..49aec1c --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/GiveItemStackBlockAction.java @@ -0,0 +1,25 @@ +package xyz.nucleoid.spleef.game.action; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; + +public record GiveItemStackBlockAction(ItemStack stack, BlockActionTarget target) implements BlockAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> { + return instance.group( + ItemStack.CODEC.fieldOf("stack").forGetter(GiveItemStackBlockAction::stack), + BlockActionTarget.CODEC.fieldOf("target").forGetter(GiveItemStackBlockAction::target) + ).apply(instance, GiveItemStackBlockAction::new); + }); + + @Override + public void apply(ServerPlayerEntity player, BlockActionContext context) { + player.giveItemStack(this.stack.copy()); + } + + @Override + public Codec getCodec() { + return CODEC; + } +} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/RestockProjectileBlockAction.java b/src/main/java/xyz/nucleoid/spleef/game/action/RestockProjectileBlockAction.java new file mode 100644 index 0000000..0ada980 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/RestockProjectileBlockAction.java @@ -0,0 +1,23 @@ +package xyz.nucleoid.spleef.game.action; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.server.network.ServerPlayerEntity; + +public record RestockProjectileBlockAction(BlockActionTarget target) implements BlockAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> { + return instance.group( + BlockActionTarget.CODEC.fieldOf("target").forGetter(RestockProjectileBlockAction::target) + ).apply(instance, RestockProjectileBlockAction::new); + }); + + @Override + public void apply(ServerPlayerEntity player, BlockActionContext context) { + context.game().restockProjectileFor(player); + } + + @Override + public Codec getCodec() { + return CODEC; + } +} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/SendMessageBlockAction.java b/src/main/java/xyz/nucleoid/spleef/game/action/SendMessageBlockAction.java new file mode 100644 index 0000000..99d0bb5 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/SendMessageBlockAction.java @@ -0,0 +1,27 @@ +package xyz.nucleoid.spleef.game.action; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import xyz.nucleoid.plasmid.util.PlasmidCodecs; + +public record SendMessageBlockAction(Text message, boolean overlay, BlockActionTarget target) implements BlockAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> { + return instance.group( + PlasmidCodecs.TEXT.fieldOf("message").forGetter(SendMessageBlockAction::message), + Codec.BOOL.optionalFieldOf("overlay", false).forGetter(SendMessageBlockAction::overlay), + BlockActionTarget.CODEC.fieldOf("target").forGetter(SendMessageBlockAction::target) + ).apply(instance, SendMessageBlockAction::new); + }); + + @Override + public void apply(ServerPlayerEntity player, BlockActionContext context) { + player.sendMessage(this.message, this.overlay); + } + + @Override + public Codec getCodec() { + return CODEC; + } +} diff --git a/src/main/java/xyz/nucleoid/spleef/game/action/SequenceBlockAction.java b/src/main/java/xyz/nucleoid/spleef/game/action/SequenceBlockAction.java new file mode 100644 index 0000000..33870c8 --- /dev/null +++ b/src/main/java/xyz/nucleoid/spleef/game/action/SequenceBlockAction.java @@ -0,0 +1,33 @@ +package xyz.nucleoid.spleef.game.action; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.List; + +public record SequenceBlockAction(List actions) implements BlockAction { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> { + return instance.group( + BlockAction.REGISTRY_CODEC.listOf().fieldOf("actions").forGetter(SequenceBlockAction::actions) + ).apply(instance, SequenceBlockAction::new); + }); + + @Override + public void apply(ServerPlayerEntity player, BlockActionContext context) { + for (var action : this.actions) { + action.target().forEachTarget(context, playerx -> { + action.apply(player, context); + }); + } + } + + public BlockActionTarget target() { + return BlockActionTarget.SELF; + } + + @Override + public Codec getCodec() { + return CODEC; + } +}