Add player statistics tracking and integration.
Introduced a comprehensive system for tracking player stats, including kills, flags captured, damage dealt, and more. Integrated stat recording into related game events and ensured proper handling for both individual and team actions. This enhances gameplay analysis and future feature potential.
This commit is contained in:
parent
8386e773ce
commit
eeed5b4c54
|
|
@ -5,12 +5,14 @@ import com.alttd.ctf.config.GameConfig;
|
|||
import com.alttd.ctf.flag.Flag;
|
||||
import com.alttd.ctf.game.GameManager;
|
||||
import com.alttd.ctf.game.GamePhase;
|
||||
import com.alttd.ctf.stats.Stat;
|
||||
import com.alttd.ctf.team.TeamPlayer;
|
||||
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.damage.DamageEffect;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
|
@ -47,6 +49,15 @@ public class OnPlayerDeath implements Listener {
|
|||
gameManager.getTeamPlayer(player)
|
||||
.ifPresent(TeamPlayer::setDead);
|
||||
flag.handleCarrierDeathOrDisconnect(player);
|
||||
|
||||
try {
|
||||
if (event.getDamageSource().getDamageType().getDamageEffect().equals(DamageEffect.FREEZING)) {
|
||||
gameManager.getTeamPlayer(player)
|
||||
.ifPresent(teamPlayer -> teamPlayer.increaseStat(Stat.DEATHS_IN_POWDERED_SNOW));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to check for death cause due to exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
|
|||
86
src/main/java/com/alttd/ctf/events/OtherGameEvents.java
Normal file
86
src/main/java/com/alttd/ctf/events/OtherGameEvents.java
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package com.alttd.ctf.events;
|
||||
|
||||
import com.alttd.ctf.game.GameManager;
|
||||
import com.alttd.ctf.stats.Stat;
|
||||
import com.alttd.ctf.team.TeamPlayer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.attribute.AttributeInstance;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.ThrownPotion;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.entity.PotionSplashEvent;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class OtherGameEvents implements Listener {
|
||||
|
||||
private final GameManager gameManager;
|
||||
|
||||
public OtherGameEvents(GameManager gameManager) {
|
||||
this.gameManager = gameManager;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
if (gameManager.getGamePhase().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!Tag.SNOW.isTagged(event.getBlock().getType())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
gameManager.getTeamPlayer(event.getPlayer())
|
||||
.ifPresent(teamPlayer -> teamPlayer.increaseStat(Stat.SNOW_MINED));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPlace(BlockBreakEvent event) {
|
||||
if (gameManager.getGamePhase().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!Tag.SNOW.isTagged(event.getBlock().getType())) {
|
||||
event.setCancelled(true);
|
||||
log.warn("Player {} placed a block that wasn't snow: {}",
|
||||
event.getPlayer().getName(), event.getBlock().getType());
|
||||
return;
|
||||
}
|
||||
gameManager.getTeamPlayer(event.getPlayer())
|
||||
.ifPresent(teamPlayer -> teamPlayer.increaseStat(Stat.BLOCKS_PLACED));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPotionSplash(PotionSplashEvent event) {
|
||||
ThrownPotion thrownPotion = event.getPotion();
|
||||
if (!(thrownPotion.getShooter() instanceof Player player))
|
||||
return;
|
||||
|
||||
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player);
|
||||
if (optionalTeamPlayer.isEmpty())
|
||||
return;
|
||||
|
||||
double totalHealing = thrownPotion.getEffects().stream()
|
||||
.filter(effect -> effect.getType() == PotionEffectType.INSTANT_HEALTH)
|
||||
.flatMapToDouble(effect -> event.getAffectedEntities().stream()
|
||||
.filter(livingEntity -> livingEntity instanceof Player)
|
||||
.map(livingEntity -> (Player) livingEntity)
|
||||
.mapToDouble(target -> {
|
||||
AttributeInstance playerMaxHealth = target.getAttribute(Attribute.GENERIC_MAX_HEALTH);
|
||||
if (playerMaxHealth == null) {
|
||||
return 0;
|
||||
}
|
||||
double missingHealth = playerMaxHealth.getValue() - target.getHealth();
|
||||
double potentialHealing = (effect.getAmplifier() + 1) * event.getIntensity(target);
|
||||
return Math.min(potentialHealing, missingHealth);
|
||||
}))
|
||||
.sum();
|
||||
|
||||
optionalTeamPlayer.get().increaseStat(Stat.DAMAGE_HEALED, totalHealing);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,16 +3,15 @@ package com.alttd.ctf.events;
|
|||
import com.alttd.ctf.game.GameManager;
|
||||
import com.alttd.ctf.game.GamePhase;
|
||||
import com.alttd.ctf.game_class.GameClass;
|
||||
import com.alttd.ctf.stats.Stat;
|
||||
import com.alttd.ctf.team.TeamPlayer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.entity.Snowball;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
|
@ -48,8 +47,13 @@ public class SnowballEvent implements Listener {
|
|||
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
||||
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
||||
|
||||
double newHealth = hitPlayer.getHealth() - shooterClass.getDamage();
|
||||
hitPlayer.setHealth(Math.max(newHealth, 0));
|
||||
double newHealth = Math.max(hitPlayer.getHealth() - shooterClass.getDamage(), 0);
|
||||
hitPlayer.setHealth(newHealth);
|
||||
|
||||
shooterTeamPlayer.increaseStat(Stat.DAMAGE_DONE, shooterClass.getDamage());
|
||||
if (newHealth <= 0) {
|
||||
shooterTeamPlayer.increaseStat(Stat.KILLS);
|
||||
}
|
||||
log.debug("{} health was set to {} because of a snowball thrown by {}",
|
||||
hitPlayer.getName(), Math.max(newHealth, 0), shooter.getName());
|
||||
});
|
||||
|
|
@ -60,19 +64,10 @@ public class SnowballEvent implements Listener {
|
|||
handleSnowballThrown(event, (shooter, shooterTeamPlayer) -> {
|
||||
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
||||
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
||||
shooterTeamPlayer.increaseStat(Stat.SNOWBALLS_THROWN);
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
if (gameManager.getGamePhase().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (Tag.SNOW.isTagged(event.getBlock().getType()))
|
||||
return;
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
private boolean blockedAttack(@NotNull Player hitPlayer, @NotNull Snowball snowball) {
|
||||
if (!hitPlayer.isBlocking()) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.alttd.ctf.flag;
|
|||
import com.alttd.ctf.Main;
|
||||
import com.alttd.ctf.config.GameConfig;
|
||||
import com.alttd.ctf.game.GameManager;
|
||||
import com.alttd.ctf.stats.Stat;
|
||||
import com.alttd.ctf.team.Team;
|
||||
import com.alttd.ctf.team.TeamColor;
|
||||
import com.alttd.ctf.team.TeamPlayer;
|
||||
|
|
@ -166,7 +167,8 @@ public class Flag implements Runnable {
|
|||
Placeholder.component("player", flagCarrier.displayName())));
|
||||
Bukkit.getOnlinePlayers().forEach(player ->
|
||||
gameManager.getTeam(player).ifPresent(team ->
|
||||
player.showTitle(team.getId() == winningTeam.getId() ? capturingTeamTitle : huntingTeamTitle)));
|
||||
player.showTitle(team.getId().intValue() == winningTeam.getId().intValue()
|
||||
? capturingTeamTitle : huntingTeamTitle)));
|
||||
}
|
||||
|
||||
private void spawnParticlesOnSquareBorder(Location center, double size) {
|
||||
|
|
@ -222,7 +224,10 @@ public class Flag implements Runnable {
|
|||
|
||||
flagCarrier.getInventory().setItem(EquipmentSlot.HEAD, null);
|
||||
gameManager.getTeamPlayer(flagCarrier)
|
||||
.ifPresent(teamPlayer -> teamPlayer.getGameClass().setArmor(flagCarrier, teamPlayer));
|
||||
.ifPresent(teamPlayer -> {
|
||||
teamPlayer.getGameClass().setArmor(flagCarrier, teamPlayer);
|
||||
teamPlayer.increaseStat(Stat.FLAGS_CAPTURED);
|
||||
});
|
||||
|
||||
resetFlagCarrier();
|
||||
}
|
||||
|
|
@ -319,6 +324,7 @@ public class Flag implements Runnable {
|
|||
return Math.max(updatedValue, 0);
|
||||
});
|
||||
});
|
||||
nearbyPlayers.forEach(teamPlayer -> teamPlayer.increaseStat(Stat.TIME_SPEND_CAPTURING_FLAG));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
58
src/main/java/com/alttd/ctf/stats/PlayerStat.java
Normal file
58
src/main/java/com/alttd/ctf/stats/PlayerStat.java
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package com.alttd.ctf.stats;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
public final class PlayerStat {
|
||||
private static final CommandSender commandSender = Bukkit.getConsoleSender();
|
||||
private final UUID uuid;
|
||||
private final String inGameName;
|
||||
|
||||
private boolean completedGame = false;
|
||||
private int flagsCaptured = 0;
|
||||
private int kills = 0;
|
||||
private double damageDone = 0;
|
||||
private double damageHealed = 0;
|
||||
private int snowMined = 0;
|
||||
private int blocksPlaced = 0;
|
||||
private int snowballsThrown = 0;
|
||||
private long timeSpendCapturingFlag = 0;
|
||||
private int deathsInPowderedSnow = 0;
|
||||
|
||||
public PlayerStat(UUID uuid, String inGameName) {
|
||||
this.uuid = uuid;
|
||||
this.inGameName = inGameName;
|
||||
}
|
||||
|
||||
public void increaseStat(Stat stat) throws IllegalArgumentException {
|
||||
switch (stat) {
|
||||
case COMPLETED_GAME -> {
|
||||
if (!completedGame) {
|
||||
Bukkit.dispatchCommand(commandSender, String.format("lp user %s permission set ctf.game.completed", inGameName));
|
||||
}
|
||||
completedGame = true;
|
||||
}
|
||||
case FLAGS_CAPTURED -> flagsCaptured++;
|
||||
case KILLS -> kills++;
|
||||
case SNOW_MINED -> snowMined++;
|
||||
case BLOCKS_PLACED -> blocksPlaced++;
|
||||
case SNOWBALLS_THROWN -> snowballsThrown++;
|
||||
case TIME_SPEND_CAPTURING_FLAG -> timeSpendCapturingFlag++;
|
||||
case DEATHS_IN_POWDERED_SNOW -> deathsInPowderedSnow++; //TODO announce if they are the first person to do this and save they are the first
|
||||
case DAMAGE_DONE, DAMAGE_HEALED -> throw new IllegalArgumentException(String.format("%s requires a number", stat.name()));
|
||||
}
|
||||
}
|
||||
|
||||
public void increaseStat(Stat stat, double value) throws IllegalArgumentException {
|
||||
switch (stat) {
|
||||
case DAMAGE_DONE -> damageDone += value;
|
||||
case DAMAGE_HEALED -> damageHealed += value;
|
||||
default -> throw new IllegalArgumentException(String.format("%s cannot be passed with a number", stat.name()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/java/com/alttd/ctf/stats/Stat.java
Normal file
14
src/main/java/com/alttd/ctf/stats/Stat.java
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package com.alttd.ctf.stats;
|
||||
|
||||
public enum Stat {
|
||||
COMPLETED_GAME,
|
||||
FLAGS_CAPTURED,
|
||||
KILLS,
|
||||
DAMAGE_DONE,
|
||||
DAMAGE_HEALED,
|
||||
SNOW_MINED,
|
||||
BLOCKS_PLACED,
|
||||
SNOWBALLS_THROWN,
|
||||
TIME_SPEND_CAPTURING_FLAG,
|
||||
DEATHS_IN_POWDERED_SNOW
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ public class Team {
|
|||
public TeamPlayer addPlayer(Player player) {
|
||||
removeFromScoreBoard(player);
|
||||
UUID uuid = player.getUniqueId();
|
||||
TeamPlayer teamPlayer = new TeamPlayer(uuid, this);
|
||||
TeamPlayer teamPlayer = new TeamPlayer(player, this);
|
||||
players.put(uuid, teamPlayer);
|
||||
addToScoreboard(player);
|
||||
if (discordTeam != null) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.alttd.ctf.game.GamePhase;
|
|||
import com.alttd.ctf.game_class.GameClass;
|
||||
import com.alttd.ctf.game_class.GameClassRetrieval;
|
||||
import com.alttd.ctf.gui.ClassSelectionGUI;
|
||||
import com.alttd.ctf.stats.PlayerStat;
|
||||
import com.alttd.ctf.stats.Stat;
|
||||
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -21,15 +23,17 @@ import java.util.*;
|
|||
@Getter
|
||||
public class TeamPlayer {
|
||||
|
||||
private final PlayerStat playerStat;
|
||||
private final UUID uuid;
|
||||
private final Team team;
|
||||
@Setter
|
||||
private GameClass gameClass;
|
||||
private boolean isDead = false;
|
||||
|
||||
protected TeamPlayer(UUID uuid, Team team) {
|
||||
this.uuid = uuid;
|
||||
protected TeamPlayer(Player player, Team team) {
|
||||
this.uuid = player.getUniqueId();
|
||||
this.team = team;
|
||||
this.playerStat = new PlayerStat(uuid, player.getName());
|
||||
}
|
||||
|
||||
public void respawn(@NotNull Player player, @NotNull WorldBorderApi worldBorderApi, @NotNull GamePhase gamePhase) {
|
||||
|
|
@ -87,4 +91,12 @@ public class TeamPlayer {
|
|||
public int hashCode() {
|
||||
return Objects.hash(uuid);
|
||||
}
|
||||
|
||||
public void increaseStat(Stat stat) {
|
||||
playerStat.increaseStat(stat);
|
||||
}
|
||||
|
||||
public void increaseStat(Stat stat, double amount) {
|
||||
playerStat.increaseStat(stat, amount);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#Sat Feb 15 22:27:11 CET 2025
|
||||
buildNumber=66
|
||||
#Sun Feb 23 01:14:21 CET 2025
|
||||
buildNumber=70
|
||||
version=0.1
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user