From 39fb7f4956bd4a0c9c8fefd6f1cafe64b4378c1f Mon Sep 17 00:00:00 2001 From: lars Date: Sun, 5 Oct 2025 00:13:56 +0200 Subject: [PATCH] added boost mechanic --- .../types/turtleGame/TurtleGame.java | 85 ++++++++------- .../types/turtleGame/gameObjects/Turtle.java | 103 ++++++++++++++++++ 2 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java index 015b920..39699ed 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/TurtleGame.java @@ -2,6 +2,7 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame; import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects.Turtle; import eu.mhsl.minenet.minigames.score.PointsWinScore; import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; @@ -9,10 +10,10 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; -import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.*; -import net.minestom.server.entity.attribute.Attribute; import net.minestom.server.entity.metadata.other.FallingBlockMeta; +import net.minestom.server.event.player.PlayerStartSneakingEvent; +import net.minestom.server.event.player.PlayerStopSneakingEvent; import net.minestom.server.event.player.PlayerTickEvent; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; @@ -28,20 +29,21 @@ import java.util.concurrent.CompletableFuture; class TurtleGame extends StatelessGame { private final int radius; - private final Map turtlePlayerMap = new WeakHashMap<>(); - private final Map scoreMap = new WeakHashMap<>(); + private final Map turtlePlayerMap = new WeakHashMap<>(); private final ArrayList snacks = new ArrayList<>(); private final ArrayList bombs = new ArrayList<>(); private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper"); - private double speed; + private final double startSpeed; public TurtleGame(int radius, int startSpeed) { super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); this.radius = radius; - this.speed = startSpeed; + this.startSpeed = startSpeed; this.eventNode() - .addListener(PlayerTickEvent.class, this::onPlayerTick); + .addListener(PlayerTickEvent.class, this::onPlayerTick) + .addListener(PlayerStartSneakingEvent.class, this::onPlayerStartSneak) + .addListener(PlayerStopSneakingEvent.class, this::onPlayerStopSneak); } @Override @@ -65,38 +67,53 @@ class TurtleGame extends StatelessGame { } } + private void onPlayerStartSneak(@NotNull PlayerStartSneakingEvent event) { + Player p = event.getPlayer(); + this.turtlePlayerMap.get(p).boostSpeed(); + } + + private void onPlayerStopSneak(@NotNull PlayerStopSneakingEvent event) { + Player p = event.getPlayer(); + this.turtlePlayerMap.get(p).cancelBoost(); + } + protected void onPlayerTick(@NotNull PlayerTickEvent event) { Player p = event.getPlayer(); if(p.getGameMode() == GameMode.SPECTATOR) return; - EntityCreature turtle = this.turtlePlayerMap.get(p); - turtle.teleport(turtle.getPosition().withView(p.getPosition())); - Vec direction = p.getPosition().direction(); + Turtle turtle = this.turtlePlayerMap.get(p); + turtle.adaptView(); if(this.isRunning()) { - Vec targetDirection = direction.withY(0).normalize().mul(this.speed); - turtle.setVelocity(targetDirection); - + turtle.move(); this.snacks.stream() - .filter(snack -> snack.getBoundingBox().intersectBox(turtle.getPosition().sub(snack.getPosition()), turtle.getBoundingBox())) + .filter(turtle::checkCollisionWithEntity) .toList() .forEach(snack -> { this.eat(p, snack); this.generateNewSnack(); - if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 4 == 0) { + if(this.turtlePlayerMap.get(p).getScore() % 5 == 0) { this.generateNewBomb(); - this.speed += 0.4; + this.addSpeed(0.4, p); } }); this.bombs.stream() - .filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) + .filter(turtle::checkCollisionWithEntity) .toList() .forEach(bomb -> { this.explode(p, bomb); this.generateNewBomb(2); - this.speed += 0.3; + this.addGlobalSpeed(0.3); }); } } + protected void addSpeed(double amount, Player p) { + this.turtlePlayerMap.get(p).addSpeed(amount); + } + + protected void addGlobalSpeed(double amount) { + this.turtlePlayerMap.values().forEach(turtle -> turtle.addSpeed(amount)); + } + protected void eat(Player p, Entity snack) { p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition()); Material snackMaterial = this.snackBlock.registry().material(); @@ -104,48 +121,35 @@ class TurtleGame extends StatelessGame { p.sendPacket(new ParticlePacket(Particle.ITEM.withItem(ItemStack.of(snackMaterial)), p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); this.snacks.remove(snack); snack.remove(); - this.scoreMap.put(p, this.scoreMap.get(p) + 1); - p.setLevel(this.scoreMap.get(p)); + this.turtlePlayerMap.get(p).increaseScore(); } protected void explode(Player p, Entity bomb) { - EntityCreature turtle = this.turtlePlayerMap.get(p); + Turtle turtle = this.turtlePlayerMap.get(p); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition()); p.sendPacket(new ParticlePacket(Particle.EXPLOSION, p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8)); p.setGameMode(GameMode.SPECTATOR); p.setFlying(true); - turtle.removePassenger(p); - turtle.remove(); - turtle.kill(); - this.getScore().insertResult(p, this.scoreMap.get(p)); + turtle.destroy(); + this.getScore().insertResult(p, this.turtlePlayerMap.get(p).getScore()); this.bombs.remove(bomb); bomb.remove(); } @Override protected boolean onPlayerJoin(Player p) { - if(this.turtlePlayerMap.get(p) == null) { - EntityCreature turtle = new EntityCreature(EntityType.TURTLE); - this.turtlePlayerMap.put(p, turtle); - } - this.scoreMap.putIfAbsent(p, 0); - p.setLevel(this.scoreMap.get(p)); - - EntityCreature turtle = this.turtlePlayerMap.get(p); - MinecraftServer.getSchedulerManager().scheduleNextTick(() -> { - turtle.setInstance(this); - turtle.teleport(p.getPosition()); - turtle.addPassenger(p); - turtle.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15); - }); + this.turtlePlayerMap.putIfAbsent(p, new Turtle(p, this.startSpeed)); + p.setLevel(this.turtlePlayerMap.get(p).getScore()); + Turtle turtle = this.turtlePlayerMap.get(p); + MinecraftServer.getSchedulerManager().scheduleNextTick(turtle::spawnTurtle); return super.onPlayerJoin(p); } @Override protected void onPlayerLeave(Player p) { - EntityCreature turtle = this.turtlePlayerMap.get(p); + Turtle turtle = this.turtlePlayerMap.get(p); turtle.remove(); } @@ -236,5 +240,6 @@ class TurtleGame extends StatelessGame { protected void onStart() { this.generateNewSnack((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5)); + this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java new file mode 100644 index 0000000..e7f0434 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/turtleGame/gameObjects/Turtle.java @@ -0,0 +1,103 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityCreature; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.Player; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.timer.Task; +import net.minestom.server.timer.TaskSchedule; + +public class Turtle extends EntityCreature { + private final Player player; + private int score = 0; + private double speed; + private final double boostSpeedAmount = 4; + private float boostChargeLevel = 0f; + private Task boostTask; + private Task boostRefillTask; + + public Turtle(Player player, double speed) { + super(EntityType.TURTLE); + this.player = player; + this.speed = speed; + + this.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15); + this.getAttribute(Attribute.MAX_HEALTH).setBaseValue(1); + this.player.setExp(this.boostChargeLevel); + } + + public void spawnTurtle() { + this.setInstance(this.player.getInstance()); + this.teleport(this.player.getPosition()); + this.addPassenger(this.player); + } + + public void startBoostRefill() { + this.boostRefillTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { + if(this.boostChargeLevel >= 1f) return; + this.boostChargeLevel = Math.min(1f, this.boostChargeLevel + 0.025f); + this.player.setExp(this.boostChargeLevel); + }, TaskSchedule.seconds(1), TaskSchedule.seconds(1)); + } + + public void destroy() { + this.removePassenger(this.player); + this.remove(); + this.kill(); + this.boostRefillTask.cancel(); + this.boostTask.cancel(); + } + + public void adaptView() { + this.teleport(this.getPosition().withView(this.player.getPosition().withPitch(this.getPosition().pitch()))); + Vec lookingVector = this.player.getPosition().direction().withY(0).mul(100); + this.lookAt(this.getPosition().add(lookingVector.asPosition())); + } + + public void move() { + this.adaptView(); + Vec direction = this.player.getPosition().direction(); + Vec movementVector = direction.withY(0).normalize().mul(this.speed); + this.setVelocity(movementVector); + } + + public boolean checkCollisionWithEntity(Entity other) { + return this.getBoundingBox().intersectBox(other.getPosition().sub(this.getPosition()), other.getBoundingBox()); + } + + public void boostSpeed() { + if(this.boostChargeLevel <= 0f) return; + this.speed += this.boostSpeedAmount; + this.boostTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { + if(this.boostChargeLevel <= 0f) { + this.cancelBoost(); + return; + } + this.boostChargeLevel = Math.max(0f, this.boostChargeLevel - 0.025f); + System.out.println(this.boostChargeLevel); + this.player.setExp(this.boostChargeLevel); + }, TaskSchedule.millis(30), TaskSchedule.millis(30)); + } + + public void cancelBoost() { + if(!this.boostTask.isAlive()) return; + this.boostTask.cancel(); + this.speed -= this.boostSpeedAmount; + } + + public void addSpeed(double amount) { + this.speed += amount; + } + + public int getScore() { + return this.score; + } + + public void increaseScore() { + this.score += 1; + this.player.setLevel(this.score); + } +}