added boost mechanic

This commit is contained in:
2025-10-05 00:13:56 +02:00
parent 75314748da
commit 39fb7f4956
2 changed files with 148 additions and 40 deletions

View File

@@ -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.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; 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 eu.mhsl.minenet.minigames.score.PointsWinScore;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component; 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.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.*; import net.minestom.server.entity.*;
import net.minestom.server.entity.attribute.Attribute;
import net.minestom.server.entity.metadata.other.FallingBlockMeta; 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.event.player.PlayerTickEvent;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
@@ -28,20 +29,21 @@ import java.util.concurrent.CompletableFuture;
class TurtleGame extends StatelessGame { class TurtleGame extends StatelessGame {
private final int radius; private final int radius;
private final Map<Player, EntityCreature> turtlePlayerMap = new WeakHashMap<>(); private final Map<Player, Turtle> turtlePlayerMap = new WeakHashMap<>();
private final Map<Player, Integer> scoreMap = new WeakHashMap<>();
private final ArrayList<Entity> snacks = new ArrayList<>(); private final ArrayList<Entity> snacks = new ArrayList<>();
private final ArrayList<Entity> bombs = new ArrayList<>(); private final ArrayList<Entity> bombs = new ArrayList<>();
private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper"); private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper");
private double speed; private final double startSpeed;
public TurtleGame(int radius, int startSpeed) { public TurtleGame(int radius, int startSpeed) {
super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore()); super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore());
this.radius = radius; this.radius = radius;
this.speed = startSpeed; this.startSpeed = startSpeed;
this.eventNode() this.eventNode()
.addListener(PlayerTickEvent.class, this::onPlayerTick); .addListener(PlayerTickEvent.class, this::onPlayerTick)
.addListener(PlayerStartSneakingEvent.class, this::onPlayerStartSneak)
.addListener(PlayerStopSneakingEvent.class, this::onPlayerStopSneak);
} }
@Override @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) { protected void onPlayerTick(@NotNull PlayerTickEvent event) {
Player p = event.getPlayer(); Player p = event.getPlayer();
if(p.getGameMode() == GameMode.SPECTATOR) return; if(p.getGameMode() == GameMode.SPECTATOR) return;
EntityCreature turtle = this.turtlePlayerMap.get(p); Turtle turtle = this.turtlePlayerMap.get(p);
turtle.teleport(turtle.getPosition().withView(p.getPosition())); turtle.adaptView();
Vec direction = p.getPosition().direction();
if(this.isRunning()) { if(this.isRunning()) {
Vec targetDirection = direction.withY(0).normalize().mul(this.speed); turtle.move();
turtle.setVelocity(targetDirection);
this.snacks.stream() this.snacks.stream()
.filter(snack -> snack.getBoundingBox().intersectBox(turtle.getPosition().sub(snack.getPosition()), turtle.getBoundingBox())) .filter(turtle::checkCollisionWithEntity)
.toList() .toList()
.forEach(snack -> { .forEach(snack -> {
this.eat(p, snack); this.eat(p, snack);
this.generateNewSnack(); this.generateNewSnack();
if(this.scoreMap.values().stream().mapToInt(Integer::intValue).sum() % 4 == 0) { if(this.turtlePlayerMap.get(p).getScore() % 5 == 0) {
this.generateNewBomb(); this.generateNewBomb();
this.speed += 0.4; this.addSpeed(0.4, p);
} }
}); });
this.bombs.stream() this.bombs.stream()
.filter(bomb -> bomb.getBoundingBox().intersectBox(turtle.getPosition().sub(bomb.getPosition()), turtle.getBoundingBox())) .filter(turtle::checkCollisionWithEntity)
.toList() .toList()
.forEach(bomb -> { .forEach(bomb -> {
this.explode(p, bomb); this.explode(p, bomb);
this.generateNewBomb(2); 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) { protected void eat(Player p, Entity snack) {
p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition()); p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition());
Material snackMaterial = this.snackBlock.registry().material(); 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)); 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); this.snacks.remove(snack);
snack.remove(); snack.remove();
this.scoreMap.put(p, this.scoreMap.get(p) + 1); this.turtlePlayerMap.get(p).increaseScore();
p.setLevel(this.scoreMap.get(p));
} }
protected void explode(Player p, Entity bomb) { 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.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.sendPacket(new ParticlePacket(Particle.EXPLOSION, p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8));
p.setGameMode(GameMode.SPECTATOR); p.setGameMode(GameMode.SPECTATOR);
p.setFlying(true); p.setFlying(true);
turtle.removePassenger(p); turtle.destroy();
turtle.remove(); this.getScore().insertResult(p, this.turtlePlayerMap.get(p).getScore());
turtle.kill();
this.getScore().insertResult(p, this.scoreMap.get(p));
this.bombs.remove(bomb); this.bombs.remove(bomb);
bomb.remove(); bomb.remove();
} }
@Override @Override
protected boolean onPlayerJoin(Player p) { protected boolean onPlayerJoin(Player p) {
if(this.turtlePlayerMap.get(p) == null) { this.turtlePlayerMap.putIfAbsent(p, new Turtle(p, this.startSpeed));
EntityCreature turtle = new EntityCreature(EntityType.TURTLE); p.setLevel(this.turtlePlayerMap.get(p).getScore());
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);
});
Turtle turtle = this.turtlePlayerMap.get(p);
MinecraftServer.getSchedulerManager().scheduleNextTick(turtle::spawnTurtle);
return super.onPlayerJoin(p); return super.onPlayerJoin(p);
} }
@Override @Override
protected void onPlayerLeave(Player p) { protected void onPlayerLeave(Player p) {
EntityCreature turtle = this.turtlePlayerMap.get(p); Turtle turtle = this.turtlePlayerMap.get(p);
turtle.remove(); turtle.remove();
} }
@@ -236,5 +240,6 @@ class TurtleGame extends StatelessGame {
protected void onStart() { protected void onStart() {
this.generateNewSnack((int) Math.ceil(this.turtlePlayerMap.size() * 1.5)); this.generateNewSnack((int) Math.ceil(this.turtlePlayerMap.size() * 1.5));
this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5)); this.generateNewBomb((int) Math.ceil(this.snacks.size() * 0.5));
this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill);
} }
} }

View File

@@ -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);
}
}