Colored flags, flags command, end portal tp fix
All checks were successful
Build / build (push) Successful in 4m11s
All checks were successful
Build / build (push) Successful in 4m11s
This commit is contained in:
parent
6e32233f4b
commit
755473cf4e
28 changed files with 651 additions and 97 deletions
|
@ -4,7 +4,6 @@ org.gradle.daemon=true
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.configuration-cache=true
|
org.gradle.configuration-cache=true
|
||||||
|
|
||||||
#read more on this at https://github.com/neoforged/ModDevGradle?tab=readme-ov-file#better-minecraft-parameter-names--javadoc-parchment
|
#read more on this at https://github.com/neoforged/ModDevGradle?tab=readme-ov-file#better-minecraft-parameter-names--javadoc-parchment
|
||||||
# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started
|
# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started
|
||||||
parchment_minecraft_version=1.21.1
|
parchment_minecraft_version=1.21.1
|
||||||
|
@ -18,14 +17,12 @@ minecraft_version=1.21.1
|
||||||
# as they do not follow standard versioning conventions.
|
# as they do not follow standard versioning conventions.
|
||||||
minecraft_version_range=[1.21.1]
|
minecraft_version_range=[1.21.1]
|
||||||
# The Neo version must agree with the Minecraft version to get a valid artifact
|
# The Neo version must agree with the Minecraft version to get a valid artifact
|
||||||
neo_version=21.1.186
|
neo_version=21.1.173
|
||||||
# The Neo version range can use any version of Neo as bounds
|
# The Neo version range can use any version of Neo as bounds
|
||||||
neo_version_range=[21.1.186,)
|
neo_version_range=[21.1.173,)
|
||||||
# The loader version range can only use the major version of FML as bounds
|
# The loader version range can only use the major version of FML as bounds
|
||||||
loader_version_range=[1,)
|
loader_version_range=[1,)
|
||||||
|
|
||||||
## Mod Properties
|
## Mod Properties
|
||||||
|
|
||||||
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}
|
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}
|
||||||
# Must match the String constant located in the main mod class annotated with @Mod.
|
# Must match the String constant located in the main mod class annotated with @Mod.
|
||||||
mod_id=riseofblocks
|
mod_id=riseofblocks
|
||||||
|
@ -40,6 +37,6 @@ mod_version=1.0.0
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
mod_group_id=nl.kallestruik.riseofblocks
|
mod_group_id=nl.kallestruik.riseofblocks
|
||||||
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
|
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
|
||||||
mod_authors=YourNameHere, OtherNameHere
|
mod_authors=Kalle Struik
|
||||||
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
|
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
|
||||||
mod_description=Example mod description.\nNewline characters can be used and will be replaced properly.
|
mod_description=Mod for the Rise of Blocks event
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
package nl.kallestruik.riseofblocks.blockentities;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
||||||
import net.minecraft.ChatFormatting;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.world.scores.Team;
|
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
|
|
||||||
public class FlagBlockRenderer implements BlockEntityRenderer<FlagBlockEntity> {
|
|
||||||
@Override
|
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
public void render(FlagBlockEntity be, float v, PoseStack poseStack, MultiBufferSource multiBufferSource, int packedLight, int packedOverlay) {
|
|
||||||
if (be.getLevel() == null) return;
|
|
||||||
|
|
||||||
Team team = be.getLevel().getScoreboard().getPlayersTeam(be.getTeamName());
|
|
||||||
int color = 0xFFFFFF; // default white
|
|
||||||
|
|
||||||
if (team != null) {
|
|
||||||
ChatFormatting textColor = team.getColor();
|
|
||||||
color = textColor.getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
float r = ((color >> 16) & 0xFF) / 255f;
|
|
||||||
float g = ((color >> 8) & 0xFF) / 255f;
|
|
||||||
float b = (color & 0xFF) / 255f;
|
|
||||||
|
|
||||||
poseStack.pushPose();
|
|
||||||
|
|
||||||
// Translate to the block position center (optional)
|
|
||||||
poseStack.translate(0.5, 0.5, 0.5);
|
|
||||||
|
|
||||||
// Get model and vertex consumer
|
|
||||||
BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer();
|
|
||||||
BakedModel model = dispatcher.getBlockModel(be.getBlockState());
|
|
||||||
|
|
||||||
VertexConsumer vertexConsumer = multiBufferSource.getBuffer(RenderType.cutout());
|
|
||||||
|
|
||||||
// Render model with tint
|
|
||||||
dispatcher.getModelRenderer().renderModel(
|
|
||||||
poseStack.last(),
|
|
||||||
vertexConsumer,
|
|
||||||
be.getBlockState(),
|
|
||||||
model,
|
|
||||||
r, g, b,
|
|
||||||
packedLight,
|
|
||||||
packedOverlay
|
|
||||||
);
|
|
||||||
|
|
||||||
poseStack.popPose();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,22 +3,59 @@ package nl.kallestruik.riseofblocks.blocks;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
import nl.kallestruik.riseofblocks.blockentities.FlagBlockEntity;
|
import nl.kallestruik.riseofblocks.blockentities.FlagBlockEntity;
|
||||||
import nl.kallestruik.riseofblocks.registry.FlagRegistry;
|
import nl.kallestruik.riseofblocks.registry.FlagRegistry;
|
||||||
|
import nl.kallestruik.riseofblocks.util.TeamUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
|
||||||
public class FlagBlock extends Block implements EntityBlock {
|
public class FlagBlock extends Block implements EntityBlock {
|
||||||
|
public static final EnumProperty<DyeColor> COLOR = EnumProperty.create("color", DyeColor.class);
|
||||||
|
private static final VoxelShape SHAPE = Shapes.or(
|
||||||
|
Block.box(2, 0, 2, 14, 1, 14),
|
||||||
|
Block.box(7, 1, 7, 9, 24, 9),
|
||||||
|
Block.box(8, 17, 7, 16, 23, 9)
|
||||||
|
);
|
||||||
|
|
||||||
public FlagBlock(Properties properties) {
|
public FlagBlock(Properties properties) {
|
||||||
super(properties);
|
super(properties.noCollission().noOcclusion().explosionResistance(1000).noLootTable());
|
||||||
|
this.registerDefaultState(this.stateDefinition.any().setValue(COLOR, DyeColor.WHITE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
Player player = context.getPlayer();
|
||||||
|
if (player == null) {
|
||||||
|
return this.defaultBlockState();
|
||||||
|
}
|
||||||
|
DyeColor color = TeamUtil.getDyeColorForTeam(player.getTeam());
|
||||||
|
return this.stateDefinition.any().setValue(COLOR, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||||
|
return SHAPE;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ParametersAreNonnullByDefault
|
@ParametersAreNonnullByDefault
|
||||||
|
@ -34,7 +71,7 @@ public class FlagBlock extends Block implements EntityBlock {
|
||||||
if (be instanceof FlagBlockEntity blockEntity) {
|
if (be instanceof FlagBlockEntity blockEntity) {
|
||||||
String teamName = blockEntity.getTeamName();
|
String teamName = blockEntity.getTeamName();
|
||||||
if (teamName != null) {
|
if (teamName != null) {
|
||||||
FlagRegistry.get(serverLevel).remove(teamName);
|
FlagRegistry.get(serverLevel.getServer()).remove(teamName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package nl.kallestruik.riseofblocks.commands;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.commands.CommandBuildContext;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.Commands;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.chat.ClickEvent;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.HoverEvent;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.world.scores.Team;
|
||||||
|
import nl.kallestruik.riseofblocks.registry.FlagRegistry;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FlagCommand {
|
||||||
|
|
||||||
|
public static void registerCommands(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext context, Commands.CommandSelection selection) {
|
||||||
|
dispatcher.register(
|
||||||
|
Commands.literal("flags")
|
||||||
|
.executes(FlagCommand::listFlags)
|
||||||
|
.then(Commands.literal("admin")
|
||||||
|
.requires(src -> src.hasPermission(2)) // OP only
|
||||||
|
.executes(FlagCommand::listFlagsAdmin))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int listFlags(CommandContext<CommandSourceStack> context) {
|
||||||
|
context.getSource().sendSystemMessage(Component.literal("Not available during the grace period."));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int listFlagsAdmin(CommandContext<CommandSourceStack> context) {
|
||||||
|
MinecraftServer server = context.getSource().getServer();
|
||||||
|
for (Map.Entry<String, FlagRegistry.FlagPosition> flags : FlagRegistry.get(server).getAll()) {
|
||||||
|
Team team = server.getScoreboard().getPlayerTeam(flags.getKey());
|
||||||
|
if (team == null) continue;
|
||||||
|
Component flagTeamComponent = Component.literal(team.getName()).withStyle(team.getColor());
|
||||||
|
BlockPos pos = flags.getValue().getPos();
|
||||||
|
|
||||||
|
String posStr = pos.getX() + " " + pos.getY() + " " + pos.getZ();
|
||||||
|
|
||||||
|
MutableComponent coordComponent = Component.literal("[" + posStr + "]")
|
||||||
|
.withStyle(style -> style
|
||||||
|
.withColor(ChatFormatting.AQUA)
|
||||||
|
.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tp @s " + posStr))
|
||||||
|
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
|
Component.literal("Click to teleport to " + posStr)))
|
||||||
|
);
|
||||||
|
|
||||||
|
context.getSource().sendSystemMessage(Component.empty().append(flagTeamComponent).append(Component.literal(": ")).append(coordComponent));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
package nl.kallestruik.riseofblocks.handlers;
|
|
||||||
|
|
||||||
import net.neoforged.api.distmarker.Dist;
|
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
|
||||||
import net.neoforged.fml.common.EventBusSubscriber;
|
|
||||||
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
|
|
||||||
import nl.kallestruik.riseofblocks.RiseOfBlocks;
|
|
||||||
import nl.kallestruik.riseofblocks.blockentities.FlagBlockRenderer;
|
|
||||||
import nl.kallestruik.riseofblocks.registry.BlockEntityRegistry;
|
|
||||||
|
|
||||||
|
|
||||||
@EventBusSubscriber(value = Dist.CLIENT, modid = RiseOfBlocks.MODID)
|
|
||||||
public class BlockEntityRendererRegister {
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) {
|
|
||||||
event.registerBlockEntityRenderer(BlockEntityRegistry.FLAG.get(),
|
|
||||||
// Pass the context to an empty (default) constructor call
|
|
||||||
context -> new FlagBlockRenderer()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +1,41 @@
|
||||||
package nl.kallestruik.riseofblocks.handlers;
|
package nl.kallestruik.riseofblocks.handlers;
|
||||||
|
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.TickTask;
|
import net.minecraft.server.TickTask;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.scores.Team;
|
import net.minecraft.world.scores.Team;
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
import net.neoforged.fml.common.EventBusSubscriber;
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
|
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
||||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||||
import net.neoforged.neoforge.event.level.BlockEvent;
|
import net.neoforged.neoforge.event.level.BlockEvent;
|
||||||
import nl.kallestruik.riseofblocks.RiseOfBlocks;
|
import nl.kallestruik.riseofblocks.RiseOfBlocks;
|
||||||
import nl.kallestruik.riseofblocks.blockentities.FlagBlockEntity;
|
import nl.kallestruik.riseofblocks.blockentities.FlagBlockEntity;
|
||||||
|
import nl.kallestruik.riseofblocks.commands.FlagCommand;
|
||||||
import nl.kallestruik.riseofblocks.registry.FlagRegistry;
|
import nl.kallestruik.riseofblocks.registry.FlagRegistry;
|
||||||
|
import nl.kallestruik.riseofblocks.registry.ItemRegistry;
|
||||||
|
|
||||||
@EventBusSubscriber(modid = RiseOfBlocks.MODID)
|
@EventBusSubscriber(modid = RiseOfBlocks.MODID)
|
||||||
public class FlagEventHandler {
|
public class FlagEventHandler {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRegisterCommands(RegisterCommandsEvent event) {
|
||||||
|
FlagCommand.registerCommands(event.getDispatcher(), event.getBuildContext(), event.getCommandSelection());
|
||||||
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onPlayerRespawn(PlayerEvent.Clone event) {
|
public static void onPlayerRespawn(PlayerEvent.Clone event) {
|
||||||
System.out.println("onPlayerRespawn: " + event.getEntity().getScoreboardName() + " is " + (event.isWasDeath() ? "dead" : "alive") + " and is respawning.");
|
|
||||||
if (!event.isWasDeath()) return; // Only handle actual death-respawns
|
if (!event.isWasDeath()) return; // Only handle actual death-respawns
|
||||||
|
|
||||||
ServerPlayer player = (ServerPlayer) event.getEntity();
|
ServerPlayer player = (ServerPlayer) event.getEntity();
|
||||||
|
@ -33,12 +44,10 @@ public class FlagEventHandler {
|
||||||
|
|
||||||
// Get player's team
|
// Get player's team
|
||||||
Team team = player.getScoreboard().getPlayersTeam(player.getScoreboardName());
|
Team team = player.getScoreboard().getPlayersTeam(player.getScoreboardName());
|
||||||
System.out.println("Team: " + (team == null ? "null" : team.getName()));
|
|
||||||
if (team == null) return;
|
if (team == null) return;
|
||||||
|
|
||||||
// Get position from registry
|
// Get position from registry
|
||||||
FlagRegistry.FlagPosition flagPos = FlagRegistry.get(world).get(team.getName());
|
FlagRegistry.FlagPosition flagPos = FlagRegistry.get(server).get(team.getName());
|
||||||
System.out.println("FlagPos: " + flagPos);
|
|
||||||
if (flagPos == null || flagPos.getPos() == null) return;
|
if (flagPos == null || flagPos.getPos() == null) return;
|
||||||
BlockPos pos = flagPos.getPos();
|
BlockPos pos = flagPos.getPos();
|
||||||
ResourceKey<Level> dimensionKey = ResourceKey.create(Registries.DIMENSION, flagPos.getDimension());
|
ResourceKey<Level> dimensionKey = ResourceKey.create(Registries.DIMENSION, flagPos.getDimension());
|
||||||
|
@ -51,6 +60,43 @@ public class FlagEventHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||||
|
if (event.getEntity() instanceof ServerPlayer serverPlayer) {
|
||||||
|
// Don't ever play end credits, this fixes not teleporting to the flag the first time
|
||||||
|
serverPlayer.seenCredits = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPlayerChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) {
|
||||||
|
System.out.println("from: " + event.getFrom() + ", to: " + event.getTo());
|
||||||
|
if (event.getFrom() != Level.END || event.getTo() != Level.OVERWORLD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServerPlayer player = (ServerPlayer) event.getEntity();
|
||||||
|
ServerLevel world = player.serverLevel();
|
||||||
|
MinecraftServer server = world.getServer();
|
||||||
|
|
||||||
|
// Get player's team
|
||||||
|
Team team = player.getScoreboard().getPlayersTeam(player.getScoreboardName());
|
||||||
|
if (team == null) return;
|
||||||
|
|
||||||
|
// Get position from registry
|
||||||
|
FlagRegistry.FlagPosition flagPos = FlagRegistry.get(server).get(team.getName());
|
||||||
|
if (flagPos == null || flagPos.getPos() == null) return;
|
||||||
|
BlockPos pos = flagPos.getPos();
|
||||||
|
ResourceKey<Level> dimensionKey = ResourceKey.create(Registries.DIMENSION, flagPos.getDimension());
|
||||||
|
ServerLevel dimension = server.getLevel(dimensionKey);
|
||||||
|
if (dimension == null) return;
|
||||||
|
|
||||||
|
// Teleport after short delay (avoid spawn conflict)
|
||||||
|
server.execute(() -> {
|
||||||
|
player.teleportTo(dimension, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, player.getYRot(), player.getXRot());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onBlockPlace(BlockEvent.EntityPlaceEvent event) {
|
public static void onBlockPlace(BlockEvent.EntityPlaceEvent event) {
|
||||||
if (!(event.getLevel() instanceof ServerLevel world)) return;
|
if (!(event.getLevel() instanceof ServerLevel world)) return;
|
||||||
|
@ -61,7 +107,7 @@ public class FlagEventHandler {
|
||||||
if (be instanceof FlagBlockEntity flagBe) {
|
if (be instanceof FlagBlockEntity flagBe) {
|
||||||
Team team = player.getScoreboard().getPlayersTeam(player.getScoreboardName());
|
Team team = player.getScoreboard().getPlayersTeam(player.getScoreboardName());
|
||||||
if (team != null) {
|
if (team != null) {
|
||||||
FlagRegistry flagRegistry = FlagRegistry.get(world);
|
FlagRegistry flagRegistry = FlagRegistry.get(world.getServer());
|
||||||
FlagRegistry.FlagPosition oldFlagPos = flagRegistry.get(team.getName());
|
FlagRegistry.FlagPosition oldFlagPos = flagRegistry.get(team.getName());
|
||||||
if (oldFlagPos != null) {
|
if (oldFlagPos != null) {
|
||||||
// Break the old flag if it exists
|
// Break the old flag if it exists
|
||||||
|
@ -75,4 +121,37 @@ public class FlagEventHandler {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onBlockBreak(BlockEvent.BreakEvent event) {
|
||||||
|
if (!(event.getLevel() instanceof ServerLevel world)) return;
|
||||||
|
BlockPos pos = event.getPos();
|
||||||
|
BlockEntity be = world.getBlockEntity(pos);
|
||||||
|
if (!(be instanceof FlagBlockEntity flagBe)) return;
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Team playerTeam = player.getTeam();
|
||||||
|
Team flagTeam = world.getServer().getScoreboard().getPlayerTeam(flagBe.getTeamName());
|
||||||
|
if (flagTeam == null) return;
|
||||||
|
|
||||||
|
Component flagTeamComponent = Component.literal(flagBe.getTeamName()).withStyle(flagTeam.getColor());
|
||||||
|
if (playerTeam == null) {
|
||||||
|
world.getServer().sendSystemMessage(Component.literal("Flag for team ").append(flagTeamComponent).append(" was destroyed by an unassigned player!"));
|
||||||
|
world.getServer().sendSystemMessage(Component.literal("Player: ").append(player.getName()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flagTeam.equals(playerTeam)) {
|
||||||
|
world.getServer().sendSystemMessage(Component.literal("Flag for team ").append(flagTeamComponent).append(" was destroyed by a member of the team!"));
|
||||||
|
world.getServer().sendSystemMessage(Component.literal("Player: ").append(player.getName()));
|
||||||
|
|
||||||
|
// Spawn the flag item in the world if it was broken by the same team.
|
||||||
|
ItemStack stack = new ItemStack(ItemRegistry.FLAG.get());
|
||||||
|
ItemEntity itemEntity = new ItemEntity(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, stack);
|
||||||
|
world.addFreshEntity(itemEntity);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component playerTeamComponent = Component.literal(playerTeam.getName()).withStyle(playerTeam.getColor());
|
||||||
|
world.getServer().getPlayerList().broadcastSystemMessage(Component.empty().append(Component.literal("[Rise of Blocks] ").withStyle(ChatFormatting.GOLD)).append("Flag for team ").append(flagTeamComponent).append(" has been broken by ").append(player.getName().copy().withStyle(playerTeam.getColor())).append(" from team ").append(playerTeamComponent).append("!"), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,22 @@ import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.saveddata.SavedData;
|
import net.minecraft.world.level.saveddata.SavedData;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class FlagRegistry extends SavedData {
|
public class FlagRegistry extends SavedData {
|
||||||
private static final String DATA_NAME = "flag_registry";
|
private static final String DATA_NAME = "flag_registry";
|
||||||
private final HashMap<String, FlagPosition> flagPositions = new HashMap<>();
|
private final HashMap<String, FlagPosition> flagPositions = new HashMap<>();
|
||||||
private static final Factory<FlagRegistry> FACTORY = new Factory<FlagRegistry>(FlagRegistry::new, FlagRegistry::load);
|
private static final Factory<FlagRegistry> FACTORY = new Factory<FlagRegistry>(FlagRegistry::new, FlagRegistry::load);
|
||||||
|
|
||||||
public static FlagRegistry get(ServerLevel world) {
|
public static FlagRegistry get(MinecraftServer server) {
|
||||||
return world.getDataStorage().computeIfAbsent(
|
return server.getLevel(ServerLevel.OVERWORLD).getDataStorage().computeIfAbsent(
|
||||||
FACTORY,
|
FACTORY,
|
||||||
DATA_NAME
|
DATA_NAME
|
||||||
);
|
);
|
||||||
|
@ -28,6 +31,10 @@ public class FlagRegistry extends SavedData {
|
||||||
return flagPositions.get(teamName);
|
return flagPositions.get(teamName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Map.Entry<String, FlagPosition>> getAll() {
|
||||||
|
return flagPositions.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
public void put(String teamName, FlagPosition pos) {
|
public void put(String teamName, FlagPosition pos) {
|
||||||
flagPositions.put(teamName, pos);
|
flagPositions.put(teamName, pos);
|
||||||
setDirty();
|
setDirty();
|
||||||
|
|
55
src/main/java/nl/kallestruik/riseofblocks/util/TeamUtil.java
Normal file
55
src/main/java/nl/kallestruik/riseofblocks/util/TeamUtil.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package nl.kallestruik.riseofblocks.util;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import net.minecraft.world.scores.Team;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class TeamUtil {
|
||||||
|
|
||||||
|
public static DyeColor getDyeColorForTeam(@Nullable Team team) {
|
||||||
|
if (team == null) return DyeColor.WHITE;
|
||||||
|
switch (team.getColor()) {
|
||||||
|
case BLACK -> {
|
||||||
|
return DyeColor.BLACK;
|
||||||
|
}
|
||||||
|
case DARK_BLUE, BLUE -> {
|
||||||
|
return DyeColor.BLUE;
|
||||||
|
}
|
||||||
|
case DARK_GREEN -> {
|
||||||
|
return DyeColor.GREEN;
|
||||||
|
}
|
||||||
|
case DARK_AQUA, AQUA -> {
|
||||||
|
return DyeColor.CYAN;
|
||||||
|
}
|
||||||
|
case DARK_RED, RED -> {
|
||||||
|
return DyeColor.RED;
|
||||||
|
}
|
||||||
|
case DARK_PURPLE -> {
|
||||||
|
return DyeColor.PURPLE;
|
||||||
|
}
|
||||||
|
case GOLD -> {
|
||||||
|
return DyeColor.ORANGE;
|
||||||
|
}
|
||||||
|
case GRAY -> {
|
||||||
|
return DyeColor.LIGHT_GRAY;
|
||||||
|
}
|
||||||
|
case DARK_GRAY -> {
|
||||||
|
return DyeColor.GRAY;
|
||||||
|
}
|
||||||
|
case GREEN -> {
|
||||||
|
return DyeColor.LIME;
|
||||||
|
}
|
||||||
|
case LIGHT_PURPLE -> {
|
||||||
|
return DyeColor.PINK;
|
||||||
|
}
|
||||||
|
case YELLOW -> {
|
||||||
|
return DyeColor.YELLOW;
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
return DyeColor.WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
src/main/resources/assets/riseofblocks/blockstates/flag.json
Normal file
52
src/main/resources/assets/riseofblocks/blockstates/flag.json
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"color=white": {
|
||||||
|
"model": "riseofblocks:block/flag_white"
|
||||||
|
},
|
||||||
|
"color=orange": {
|
||||||
|
"model": "riseofblocks:block/flag_orange"
|
||||||
|
},
|
||||||
|
"color=magenta": {
|
||||||
|
"model": "riseofblocks:block/flag_magenta"
|
||||||
|
},
|
||||||
|
"color=light_blue": {
|
||||||
|
"model": "riseofblocks:block/flag_light_blue"
|
||||||
|
},
|
||||||
|
"color=yellow": {
|
||||||
|
"model": "riseofblocks:block/flag_yellow"
|
||||||
|
},
|
||||||
|
"color=lime": {
|
||||||
|
"model": "riseofblocks:block/flag_lime"
|
||||||
|
},
|
||||||
|
"color=pink": {
|
||||||
|
"model": "riseofblocks:block/flag_pink"
|
||||||
|
},
|
||||||
|
"color=gray": {
|
||||||
|
"model": "riseofblocks:block/flag_gray"
|
||||||
|
},
|
||||||
|
"color=light_gray": {
|
||||||
|
"model": "riseofblocks:block/flag_light_gray"
|
||||||
|
},
|
||||||
|
"color=cyan": {
|
||||||
|
"model": "riseofblocks:block/flag_cyan"
|
||||||
|
},
|
||||||
|
"color=purple": {
|
||||||
|
"model": "riseofblocks:block/flag_purple"
|
||||||
|
},
|
||||||
|
"color=blue": {
|
||||||
|
"model": "riseofblocks:block/flag_blue"
|
||||||
|
},
|
||||||
|
"color=brown": {
|
||||||
|
"model": "riseofblocks:block/flag_brown"
|
||||||
|
},
|
||||||
|
"color=green": {
|
||||||
|
"model": "riseofblocks:block/flag_green"
|
||||||
|
},
|
||||||
|
"color=red": {
|
||||||
|
"model": "riseofblocks:block/flag_red"
|
||||||
|
},
|
||||||
|
"color=black": {
|
||||||
|
"model": "riseofblocks:block/flag_black"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"itemGroup.riseofblocks": "Example Mod Tab",
|
"itemGroup.riseofblocks": "Rise of Blocks",
|
||||||
"block.riseofblocks.example_block": "Example Block"
|
"block.riseofblocks.flag": "Flag"
|
||||||
}
|
}
|
||||||
|
|
248
src/main/resources/assets/riseofblocks/models/block/flag.json
Normal file
248
src/main/resources/assets/riseofblocks/models/block/flag.json
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
{
|
||||||
|
"credit": "Made with Blockbench",
|
||||||
|
"textures": {
|
||||||
|
"particle": "minecraft:block/stone",
|
||||||
|
"base": "minecraft:block/stone",
|
||||||
|
"pole": "minecraft:block/oak_planks",
|
||||||
|
"flag": "minecraft:block/white_wool"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"name": "base",
|
||||||
|
"from": [
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
14,
|
||||||
|
1,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"rotation": {
|
||||||
|
"angle": 0,
|
||||||
|
"axis": "y",
|
||||||
|
"origin": [
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"color": 4,
|
||||||
|
"faces": {
|
||||||
|
"north": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"texture": "#base"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"texture": "#base"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"texture": "#base"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"texture": "#base"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"texture": "#base"
|
||||||
|
},
|
||||||
|
"down": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"texture": "#base"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pole",
|
||||||
|
"from": [
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
9,
|
||||||
|
24,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"rotation": {
|
||||||
|
"angle": 0,
|
||||||
|
"axis": "y",
|
||||||
|
"origin": [
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"color": 1,
|
||||||
|
"faces": {
|
||||||
|
"north": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
16
|
||||||
|
],
|
||||||
|
"texture": "#pole"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
16
|
||||||
|
],
|
||||||
|
"texture": "#pole"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
16
|
||||||
|
],
|
||||||
|
"texture": "#pole"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
16
|
||||||
|
],
|
||||||
|
"texture": "#pole"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"texture": "#pole"
|
||||||
|
},
|
||||||
|
"down": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"texture": "#pole"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "flag",
|
||||||
|
"from": [
|
||||||
|
8,
|
||||||
|
17,
|
||||||
|
7.5
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
16,
|
||||||
|
23,
|
||||||
|
8.5
|
||||||
|
],
|
||||||
|
"rotation": {
|
||||||
|
"angle": 0,
|
||||||
|
"axis": "y",
|
||||||
|
"origin": [
|
||||||
|
8,
|
||||||
|
21,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"color": 3,
|
||||||
|
"faces": {
|
||||||
|
"north": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"texture": "#flag"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"texture": "#flag"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"texture": "#flag"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"texture": "#flag"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"texture": "#flag"
|
||||||
|
},
|
||||||
|
"down": {
|
||||||
|
"uv": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"texture": "#flag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/black_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/blue_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/brown_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/cyan_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/gray_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/green_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/light_blue_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/light_gray_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/lime_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/magenta_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/orange_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/pink_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/purple_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/red_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/white_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag",
|
||||||
|
"textures": {
|
||||||
|
"flag": "minecraft:block/yellow_wool"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"parent": "riseofblocks:block/flag_white"
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue