Files
SteamWar/LobbySystem/src/de/steamwar/lobby/boatrace/BoatRace.java
T

183 lines
7.5 KiB
Java

/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.lobby.boatrace;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.util.LeaderboardManager;
import de.steamwar.sql.Leaderboard;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.event.vehicle.VehicleMoveEvent;
import org.bukkit.scheduler.BukkitTask;
import java.util.EventListener;
import static de.steamwar.lobby.util.LeaderboardManager.renderTime;
public class BoatRace implements EventListener, Listener {
private static final String CONFIG_KEY = "lobby@boatrace";
private static final double MIN_HEIGHT = 4.3;
public static final REntityServer boatNpcServer;
private static boolean oneNotStarted = false;
private static final LeaderboardManager leaderboard;
static {
boatNpcServer = new REntityServer();
REntity starter = new REntity(boatNpcServer, EntityType.VILLAGER, BoatRacePositions.NPC);
boatNpcServer.setCallback((player, rEntity, entityAction) -> {
if (rEntity != starter) return;
Bukkit.getWorlds().get(0).getEntities().stream().filter(entity -> entity.getType() == EntityType.ENDER_CRYSTAL).forEach(Entity::remove);
if (entityAction == REntityServer.EntityAction.INTERACT && !oneNotStarted) {
oneNotStarted = true;
new BoatRace(player);
}
});
leaderboard = new LeaderboardManager(boatNpcServer, CONFIG_KEY, BoatRacePositions.LEADERBOARD);
}
private final Player player;
private Boat boat;
private int nextCheckpoint = 0;
private long startTime;
private final BukkitTask task;
private final BossBar bossBar;
private boolean hasBacked = false;
private double lastDistance;
@EventHandler
public void onBoatMove(VehicleMoveEvent event) {
if (event.getVehicle() != boat) return;
lastDistance = event.getFrom().distance(event.getTo());
if(nextCheckpoint == 0 && inRegion(player, BoatRacePositions.BACKWARDS[0], BoatRacePositions.BACKWARDS[1])) {
player.eject();
player.teleport(BoatRacePositions.END);
oneNotStarted = false;
return;
}
if(player.getLocation().getY() < MIN_HEIGHT) {
Location[] backTo = BoatRacePositions.CHECKPOINTS[nextCheckpoint - 1];
Location avg = new Location(backTo[0].getWorld(), (backTo[0].getX() + backTo[1].getX()) / 2, Math.max(backTo[0].getY(), backTo[1].getY()), (backTo[0].getZ() + backTo[1].getZ()) / 2, backTo[0].getYaw(), backTo[0].getPitch());
Boat nboat = Bukkit.getWorlds().get(0).spawn(avg, Boat.class);
nboat.setBoatType(boat.getBoatType());
hasBacked = true;
player.eject();
boat.remove();
boat = nboat;
boat.addPassenger(player);
player.playSound(avg, Sound.ENTITY_ENDERMAN_TELEPORT, 1, 1);
return;
}
Location[] checkpoint = BoatRacePositions.CHECKPOINTS[nextCheckpoint];
if(inRegion(player, checkpoint[0], checkpoint[1])) {
if(nextCheckpoint == 0) {
oneNotStarted = false;
startTime = System.currentTimeMillis();
bossBar.addPlayer(player);
bossBar.setVisible(true);
}
nextCheckpoint++;
if (nextCheckpoint == BoatRacePositions.CHECKPOINTS.length) {
long time = System.currentTimeMillis() - startTime;
boat.remove();
player.eject();
player.teleport(BoatRacePositions.END);
player.playSound(player.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1, 1);
bossBar.removeAll();
HandlerList.unregisterAll(this);
task.cancel();
LobbySystem.getMessage().send("BOAT_RACE_TIME", player, renderTime(time));
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
long best = leaderboard.getPlayerTime(user);
if (time < best) {
LobbySystem.getMessage().send("BOAT_RACE_NEW_BEST", player);
leaderboard.updateBestTime(user, time);
}
} else {
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1);
}
}
}
@EventHandler
public void onVehicleExit(VehicleExitEvent event) {
if (event.getVehicle() != boat) return;
if (event.getExited() != player) return;
if (hasBacked) return;
HandlerList.unregisterAll(this);
task.cancel();
bossBar.removeAll();
boat.remove();
player.teleport(BoatRacePositions.END);
oneNotStarted = false;
}
public BoatRace(Player player) {
this.player = player;
boat = Bukkit.getWorlds().get(0).spawn(BoatRacePositions.START, Boat.class);
// boat.setBoatType(Boat.Type.values()[new Random().nextInt(Boat.Type.values().length)]);
boat.addPassenger(player);
bossBar = Bukkit.createBossBar("", BarColor.BLUE, BarStyle.SOLID);
task = Bukkit.getScheduler().runTaskTimer(LobbySystem.getInstance(), () -> {
hasBacked = false;
if (nextCheckpoint != 0) {
double kmh = lastDistance * 20 * 3.6;
bossBar.setProgress((nextCheckpoint - 1d) / (BoatRacePositions.CHECKPOINTS.length - 1d));
bossBar.setTitle(LobbySystem.getMessage().parse("BOAT_RACE_TITLE", player, nextCheckpoint, renderTime(System.currentTimeMillis() - startTime), (int) kmh));
}
}, 0, 1);
Bukkit.getPluginManager().registerEvents(this, LobbySystem.getInstance());
}
private boolean inRegion(Player p, Location loc1, Location loc2) {
double x1 = Math.min(loc1.getX(), loc2.getX());
double y1 = Math.min(loc1.getY(), loc2.getY());
double z1 = Math.min(loc1.getZ(), loc2.getZ());
double x2 = Math.max(loc1.getX(), loc2.getX()) + 1;
double y2 = Math.max(loc1.getY(), loc2.getY()) + 1;
double z2 = Math.max(loc1.getZ(), loc2.getZ()) + 1;
return p.getLocation().getX() >= x1 && p.getLocation().getY() >= y1 && p.getLocation().getZ() >= z1 && p.getLocation().getX() < x2 && p.getLocation().getY() < y2 && p.getLocation().getZ() < z2;
}
}