Add Mage character class with snowball duplication ability

Introduced the Mage class as a playable character along with its creation logic, tools, and armor setup. Mages can duplicate snowballs upon throwing, creating multiple projectiles with varying directions and velocities to enhance gameplay dynamics. Updated relevant systems to accommodate this new class.
This commit is contained in:
Teriuihi 2025-03-01 00:05:57 +01:00
parent e8c015790f
commit d11cf25fc8
5 changed files with 136 additions and 9 deletions

View File

@ -3,6 +3,7 @@ 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.game_class.implementations.Mage;
import com.alttd.ctf.stats.Stat;
import com.alttd.ctf.team.TeamPlayer;
import lombok.extern.slf4j.Slf4j;
@ -35,7 +36,7 @@ public class SnowballEvent implements Listener {
@FunctionalInterface
private interface SnowballThrownConsumer {
void apply(Player shooter, TeamPlayer shooterTeamPlayer);
void apply(Player shooter, TeamPlayer shooterTeamPlayer, Snowball snowball);
}
@EventHandler
@ -61,10 +62,13 @@ public class SnowballEvent implements Listener {
@EventHandler
public void onSnowballThrown(ProjectileLaunchEvent event) {
handleSnowballThrown(event, (shooter, shooterTeamPlayer) -> {
handleSnowballThrown(event, (shooter, shooterTeamPlayer, snowball) -> {
GameClass shooterClass = shooterTeamPlayer.getGameClass();
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
shooterTeamPlayer.increaseStat(Stat.SNOWBALLS_THROWN);
if (shooterClass instanceof Mage mage) {
mage.duplicateSnowBalls(shooter, snowball);
}
});
}
@ -101,7 +105,7 @@ public class SnowballEvent implements Listener {
log.debug("The shooter that threw a snowball was not a team player");
return;
}
consumer.apply(shooter, teamPlayer.get());
consumer.apply(shooter, teamPlayer.get(), snowball);
}
private void handleSnowballHit(EntityDamageByEntityEvent event, SnowballHitConsumer consumer) {

View File

@ -1,10 +1,7 @@
package com.alttd.ctf.game_class;
import com.alttd.ctf.game.GameManager;
import com.alttd.ctf.game_class.creation.EngineerCreator;
import com.alttd.ctf.game_class.creation.FighterCreator;
import com.alttd.ctf.game_class.creation.TankCreator;
import com.alttd.ctf.game_class.creation.TrapperCreator;
import com.alttd.ctf.game_class.creation.*;
import com.alttd.ctf.team.Team;
import java.util.ArrayList;
@ -27,6 +24,7 @@ public class GameClassRetrieval {
gameClasses.add(TankCreator.createTank(team.getColor()));
gameClasses.add(TrapperCreator.createTrapper(team.getColor()));
gameClasses.add(EngineerCreator.createEngineer(team.getColor()));
gameClasses.add(MageCreator.createMage(team.getColor()));
return gameClasses;
}

View File

@ -0,0 +1,63 @@
package com.alttd.ctf.game_class.creation;
import com.alttd.ctf.game_class.GameClass;
import com.alttd.ctf.game_class.implementations.Fighter;
import com.alttd.ctf.game_class.implementations.Mage;
import com.alttd.ctf.team.TeamColor;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.List;
@Slf4j
public class MageCreator {
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
@Contract("_ -> new")
public static @NotNull GameClass createMage(@NotNull TeamColor teamColor) {
return new Mage(getArmor(), getTools(teamColor), getDisplayItem(teamColor),
20, 100, 5);
}
@Contract(value = " -> new", pure = true)
private static @NotNull @Unmodifiable List<Material> getArmor() {
return (List.of(Material.CHAINMAIL_BOOTS, Material.AIR, Material.AIR, Material.LEATHER_HELMET));
}
@Contract("_ -> new")
private static @NotNull @Unmodifiable List<ItemStack> getTools(@NotNull TeamColor teamColor) {
return (List.of(getShovel(teamColor)));
}
private static @NotNull ItemStack getShovel(@NotNull TeamColor teamColor) {
ItemStack shovel = new ItemStack(Material.WOODEN_SHOVEL);
ItemMeta meta = shovel.getItemMeta();
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex())));
meta.setUnbreakable(true);
meta.addEnchant(Enchantment.EFFICIENCY, 1, false);
shovel.setItemMeta(meta);
return shovel;
}
private static @NotNull ItemStack getDisplayItem(@NotNull TeamColor teamColor) {
ItemStack itemStack = new ItemStack(Material.IRON_SWORD);
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.displayName(miniMessage.deserialize(String.format("<color:%s>Mage</color>", teamColor.hex())));
itemMeta.lore(List.of(
miniMessage.deserialize("<gold>The Mage can throw many snowballs at once</gold>"),
miniMessage.deserialize("<gold>But it has a long cooldown.</gold>")
));
itemStack.setItemMeta(itemMeta);
return itemStack;
}
}

View File

@ -0,0 +1,62 @@
package com.alttd.ctf.game_class.implementations;
import com.alttd.ctf.game_class.GameClass;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.entity.Snowball;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@Slf4j
public class Mage extends GameClass {
public Mage(@NotNull List<Material> armor, @NotNull List<ItemStack> tools, @NotNull ItemStack displayItem,
double health, int throwTickSpeed, int damage) {
super(armor, tools, displayItem, health, throwTickSpeed, damage);
}
public void duplicateSnowBalls(Player shooter, Snowball snowball) {
Location location = snowball.getLocation();
PlayerInventory inventory = shooter.getInventory();
ItemStack itemStack = inventory.getItemInMainHand();
if (itemStack.getType() != Material.SNOWBALL) {
ItemStack itemInOffHand = inventory.getItemInOffHand();
if (itemInOffHand.getType() != Material.SNOWBALL) {
log.warn("Unable to find snowballs in main/off hand");
return;
}
itemStack = itemInOffHand;
}
if (itemStack.getAmount() <= 1) {
return;
}
duplicate(shooter, location, snowball.getVelocity(), itemStack.getAmount() - 1);
itemStack.setAmount(0);
}
private void duplicate(@NotNull Player shooter, @NotNull Location location, @NotNull Vector velocity, int count) {
for (int i = 0; i < count; i++) {
double offsetX = (Math.random() - 0.5) * 2;
double offsetY = (Math.random() - 0.5) * 2;
double offsetZ = (Math.random() - 0.5) * 2;
Location newLocation = location.clone().add(offsetX, offsetY, offsetZ);
Snowball snowball = location.getWorld().spawn(newLocation, Snowball.class);
snowball.setShooter(shooter);
double velocityChangeX = (Math.random() - 0.5) * 0.2;
double velocityChangeY = (Math.random() - 0.5) * 0.2;
double velocityChangeZ = (Math.random() - 0.5) * 0.2;
Vector newVelocity = velocity.clone().add(new Vector(velocityChangeX, velocityChangeY, velocityChangeZ));
snowball.setVelocity(newVelocity);
}
}
}

View File

@ -1,3 +1,3 @@
#Fri Feb 28 22:11:49 CET 2025
buildNumber=80
#Fri Feb 28 23:59:35 CET 2025
buildNumber=83
version=0.1