package net.minecraft.world.scores; import com.google.common.collect.Sets; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; import org.jetbrains.annotations.Nullable; public class PlayerTeam extends Team { private static final int BIT_FRIENDLY_FIRE = 0; private static final int BIT_SEE_INVISIBLES = 1; private final Scoreboard scoreboard; private final String name; private final Set players = Sets.newHashSet(); private Component displayName; private Component playerPrefix = CommonComponents.EMPTY; private Component playerSuffix = CommonComponents.EMPTY; private boolean allowFriendlyFire = true; private boolean seeFriendlyInvisibles = true; private Team.Visibility nameTagVisibility = Team.Visibility.ALWAYS; private Team.Visibility deathMessageVisibility = Team.Visibility.ALWAYS; private ChatFormatting color = ChatFormatting.RESET; private Team.CollisionRule collisionRule = Team.CollisionRule.ALWAYS; private final Style displayNameStyle; public PlayerTeam(Scoreboard scoreboard, String name) { this.scoreboard = scoreboard; this.name = name; this.displayName = Component.literal(name); this.displayNameStyle = Style.EMPTY.withInsertion(name).withHoverEvent(new HoverEvent.ShowText(Component.literal(name))); } public PlayerTeam.Packed pack() { return new PlayerTeam.Packed( this.name, Optional.of(this.displayName), this.color != ChatFormatting.RESET ? Optional.of(this.color) : Optional.empty(), this.allowFriendlyFire, this.seeFriendlyInvisibles, this.playerPrefix, this.playerSuffix, this.nameTagVisibility, this.deathMessageVisibility, this.collisionRule, List.copyOf(this.players) ); } public Scoreboard getScoreboard() { return this.scoreboard; } @Override public String getName() { return this.name; } /** * Gets the display name for this team. */ public Component getDisplayName() { return this.displayName; } public MutableComponent getFormattedDisplayName() { MutableComponent mutableComponent = ComponentUtils.wrapInSquareBrackets(this.displayName.copy().withStyle(this.displayNameStyle)); ChatFormatting chatFormatting = this.getColor(); if (chatFormatting != ChatFormatting.RESET) { mutableComponent.withStyle(chatFormatting); } return mutableComponent; } /** * Sets the display name for this team. */ public void setDisplayName(Component name) { if (name == null) { throw new IllegalArgumentException("Name cannot be null"); } else { this.displayName = name; this.scoreboard.onTeamChanged(this); } } public void setPlayerPrefix(@Nullable Component playerPrefix) { this.playerPrefix = playerPrefix == null ? CommonComponents.EMPTY : playerPrefix; this.scoreboard.onTeamChanged(this); } public Component getPlayerPrefix() { return this.playerPrefix; } public void setPlayerSuffix(@Nullable Component playerSuffix) { this.playerSuffix = playerSuffix == null ? CommonComponents.EMPTY : playerSuffix; this.scoreboard.onTeamChanged(this); } public Component getPlayerSuffix() { return this.playerSuffix; } @Override public Collection getPlayers() { return this.players; } @Override public MutableComponent getFormattedName(Component formattedName) { MutableComponent mutableComponent = Component.empty().append(this.playerPrefix).append(formattedName).append(this.playerSuffix); ChatFormatting chatFormatting = this.getColor(); if (chatFormatting != ChatFormatting.RESET) { mutableComponent.withStyle(chatFormatting); } return mutableComponent; } public static MutableComponent formatNameForTeam(@Nullable Team playerTeam, Component playerName) { return playerTeam == null ? playerName.copy() : playerTeam.getFormattedName(playerName); } @Override public boolean isAllowFriendlyFire() { return this.allowFriendlyFire; } /** * Sets whether friendly fire (PVP between members of the team) is allowed. */ public void setAllowFriendlyFire(boolean friendlyFire) { this.allowFriendlyFire = friendlyFire; this.scoreboard.onTeamChanged(this); } @Override public boolean canSeeFriendlyInvisibles() { return this.seeFriendlyInvisibles; } /** * Sets whether members of this team can see other members that are invisible. */ public void setSeeFriendlyInvisibles(boolean friendlyInvisibles) { this.seeFriendlyInvisibles = friendlyInvisibles; this.scoreboard.onTeamChanged(this); } @Override public Team.Visibility getNameTagVisibility() { return this.nameTagVisibility; } @Override public Team.Visibility getDeathMessageVisibility() { return this.deathMessageVisibility; } /** * Sets the visibility flags for player name tags. */ public void setNameTagVisibility(Team.Visibility visibility) { this.nameTagVisibility = visibility; this.scoreboard.onTeamChanged(this); } /** * Sets the visibility flags for player death messages. */ public void setDeathMessageVisibility(Team.Visibility visibility) { this.deathMessageVisibility = visibility; this.scoreboard.onTeamChanged(this); } @Override public Team.CollisionRule getCollisionRule() { return this.collisionRule; } /** * Sets the rule to be used for handling collisions with members of this team. */ public void setCollisionRule(Team.CollisionRule rule) { this.collisionRule = rule; this.scoreboard.onTeamChanged(this); } /** * Gets a bitmask containing the friendly fire and invisibles flags. */ public int packOptions() { int i = 0; if (this.isAllowFriendlyFire()) { i |= 1; } if (this.canSeeFriendlyInvisibles()) { i |= 2; } return i; } /** * Sets friendly fire and invisibles flags based off of the given bitmask. */ public void unpackOptions(int flags) { this.setAllowFriendlyFire((flags & 1) > 0); this.setSeeFriendlyInvisibles((flags & 2) > 0); } /** * Sets the color for this team. The team color is used mainly for team kill objectives and team-specific setDisplay usage. It does _not_ affect all situations (for instance, the prefix is used for the glowing effect). */ public void setColor(ChatFormatting color) { this.color = color; this.scoreboard.onTeamChanged(this); } @Override public ChatFormatting getColor() { return this.color; } public record Packed( String name, Optional displayName, Optional color, boolean allowFriendlyFire, boolean seeFriendlyInvisibles, Component memberNamePrefix, Component memberNameSuffix, Team.Visibility nameTagVisibility, Team.Visibility deathMessageVisibility, Team.CollisionRule collisionRule, List players ) { public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( Codec.STRING.fieldOf("Name").forGetter(PlayerTeam.Packed::name), ComponentSerialization.CODEC.optionalFieldOf("DisplayName").forGetter(PlayerTeam.Packed::displayName), ChatFormatting.COLOR_CODEC.optionalFieldOf("TeamColor").forGetter(PlayerTeam.Packed::color), Codec.BOOL.optionalFieldOf("AllowFriendlyFire", true).forGetter(PlayerTeam.Packed::allowFriendlyFire), Codec.BOOL.optionalFieldOf("SeeFriendlyInvisibles", true).forGetter(PlayerTeam.Packed::seeFriendlyInvisibles), ComponentSerialization.CODEC.optionalFieldOf("MemberNamePrefix", CommonComponents.EMPTY).forGetter(PlayerTeam.Packed::memberNamePrefix), ComponentSerialization.CODEC.optionalFieldOf("MemberNameSuffix", CommonComponents.EMPTY).forGetter(PlayerTeam.Packed::memberNameSuffix), Team.Visibility.CODEC.optionalFieldOf("NameTagVisibility", Team.Visibility.ALWAYS).forGetter(PlayerTeam.Packed::nameTagVisibility), Team.Visibility.CODEC.optionalFieldOf("DeathMessageVisibility", Team.Visibility.ALWAYS).forGetter(PlayerTeam.Packed::deathMessageVisibility), Team.CollisionRule.CODEC.optionalFieldOf("CollisionRule", Team.CollisionRule.ALWAYS).forGetter(PlayerTeam.Packed::collisionRule), Codec.STRING.listOf().optionalFieldOf("Players", List.of()).forGetter(PlayerTeam.Packed::players) ) .apply(instance, PlayerTeam.Packed::new) ); } }